Lullabot News, Podcasts, and Contenthttps://www.lullabot.com/
The latest news, articles, podcasts, and more from your friends at Lullabot.enDebugging Jobs in GitLab CIhttps://www.lullabot.com/articles/debugging-jobs-gitlab-ci
<p><em>A follow up to </em><a href="https://www.lullabot.com/articles/installer-drupal-8-and-gitlab-ci" target="_blank"><em>An Installer for Drupal 8 and GitLab CI</em></a></p>
<p>GitLab does not support SSH access to a job to debug it like other CI tools, such as CircleCI or Travis CI. Instead, it provides developers with the ability to set up a <a href="https://docs.gitlab.com/runner/" target="_blank">GitLab Runner</a> to run jobs locally. This article explains how to run a job locally and halt it, so you can jump into the container and debug it.</p>
<h2>Background: a failing job at GitLab CI</h2>
<p>For the article, <a href="https://www.lullabot.com/articles/installer-drupal-8-and-gitlab-ci" target="_blank"><em>An Installer for Drupal 8 and GitLab CI</em></a>, a <a href="https://gitlab.com/juampynr/drupal8-gitlab" target="_blank">demo repository</a> was created to host a Drupal 8 site and a <a href="https://docs.gitlab.com/ee/ci/pipelines/" target="_blank">GitLab CI Pipeline</a> was written. This pipeline, among other things, ran end-to-end tests against the Drupal 8 site. These tests needed a browser, so <a href="https://developers.google.com/web/updates/2017/06/headless-karma-mocha-chai" target="_blank">Headless Chrome</a> was added.</p>
<p>While setting up the Existing Site Tests job, it appeared that the job would get stuck when running the Chrome browser in headless mode:</p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-06/existing-site-tests-job-stuck.png?itok=FyQUnbPR" width="900" height="344" alt="GitLab UI showing the job is stuck" /></figure></div>
<p> </p>
<p>If you look at the console output, the job gets stuck there and eventually times out. The <code>google-chrome-unstable</code> command is called by a <a href="https://robo.li" target="_blank">Robo task</a>. This was the job setup:</p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow image--captioned" style="max-width: 968px;"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-06/ci-job-setup.png?itok=ZCNkCeRr" width="900" height="456" alt="GitLab blob showing build and test jobs" /><figcaption class="image__caption">https://gitlab.com/juampynr/drupal8-gitlab/-/blob/master/.gitlab-ci.yml</figcaption></figure></div>
<p> </p>
<p>There had to be a way to make the job stop right before it runs <code>vendor/bin/robo run:chrome-headless </code>and somehow allow an interactive session inside the Docker container to debug that command manually. By looking at the GitLab documentation, it was clear that a GitLab Runner would allow it.</p>
<h2>Setting up a Docker runner</h2>
<p>After searching for a way to retry a failed job with SSH access, it turned out that <a href="https://gitlab.com/gitlab-org/gitlab-foss/-/issues/22319" target="_blank">this is not supported in GitLab</a>. Instead, the recommended approach is to install <code><a href="https://docs.gitlab.com/runner/install/linux-manually.html" target="_blank">gitlab-runner</a></code> and then <a href="https://docs.gitlab.com/runner/register/" target="_blank">register a Docker runner</a> so you can run a job locally.</p>
<p>First, start by installing the <code>gitlab-runner</code> command by following the steps listed at <a href="https://docs.gitlab.com/runner/install/linux-manually.html" target="_blank">Install GitLab Runner manually on GNU/Linux</a>:</p>
<pre>
<code class="language-bash">$ curl -LJO https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner_amd64.deb
$ sudo dpkg -i gitlab-runner_amd64.deb </code></pre>
<p>Next, follow the instructions at <a href="https://docs.gitlab.com/runner/register" target="_blank">https://docs.gitlab.com/runner/register</a> and register a runner, which asks a few questions along the way to configure it:</p>
<pre>
<code class="language-bash">$ sudo gitlab-runner register
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
https://gitlab.com
Please enter the gitlab-ci token for this runner:
[Find this token at your repository's Settings > CI / CD > Runners ]
Please enter the gitlab-ci description for this runner:
debugging
Please enter the gitlab-ci tags for this runner (comma separated):
debugging
Registering runner... succeeded runner=NZu1SRU5
Please enter the executor: virtualbox, docker, docker-ssh, shell, ssh, docker+machine, docker-ssh+machine, kubernetes, custom, parallels:
docker
Please enter the default Docker image (e.g. ruby:2.6):
juampynr/drupal8ci:latest
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded! </code></pre>
<p>Setup complete. Here is a screenshot of where to find the token to use above. It is not easy to find within the repository settings:</p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow image--captioned" style="max-width: 1452px;"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-06/gitlab-token-to-set-up-a-runner.png?itok=h7i6nCPq" width="900" height="626" alt="GitLab UI showing where to find the token to set up a runner" /><figcaption class="image__caption">Repository runner settings</figcaption></figure></div>
<p>Now you can run jobs locally.</p>
<h2>Adjusting jobs so they can run locally</h2>
<p> This is the command to run the job to debug:</p>
<p><code>$ gitlab-runner exec docker drupal8ci:existing_site_tests</code></p>
<p>When running this command, it failed with an error that stated that the <code>drupalci:build</code> dependency did not exist. It turns out, GitLab runners <a href="https://gitlab.com/gitlab-org/gitlab-runner/-/issues/2121" target="_blank">do not support job dependencies</a>. There are a few GitLab CI <a href="https://docs.gitlab.com/runner/commands/README.html#limitations-of-gitlab-runner-exec" target="_blank">features that do not work when running jobs locally</a>. It's a good idea to read them so you can adjust your pipeline accordingly.</p>
<p>While these limitations make running jobs locally trickier, you can adjust a job to overcome them. For example, in this case, to make <code>drupal8:existing_site_tests</code> not dependent on <code>drupal8ci:build</code>, you'd need to:</p>
<ol start="1"><li>Copy the <code>script</code> section from <code>drupal8ci:build</code> into <code>drupal8ci:existing_site_tests</code>.</li>
<li>Remove the dependencies section from <code>drupal8ci:existing_site_tests</code>.</li>
</ol><p>Here is the resulting job:</p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-06/adjusted-job.png?itok=w3cqMn0v" width="900" height="428" alt="GitLab blob showing the adjusted job" /></figure></div>
<p> </p>
<p>Add a <code>sleep 1h</code> statement where you want the job to halt so you can debug it.</p>
<p>With these changes complete, commit them so the <a href="https://gitlab.com/gitlab-org/gitlab-runner/-/issues/1359" target="_blank">GitLab Runner can pick them up</a>:</p>
<pre>
<code class="language-bash">$ git add .gitlab-ci.yml
$ git commit -m "Debug existing site tests job"</code></pre>
<p>There is no need to push the changes, as the GitLab runner will pick the code from your local Git configuration and not the remote. This is good because once you are done with debugging, you can delete the above commit with <code>git reset HEAD~1</code>.</p>
<p>Finally, you're ready to run jobs and debug them. Take a look at how it went in the following example.</p>
<h2>Running and debugging</h2>
<p>Here is the output of the command that runs a job locally:</p>
<pre>
<code class="language-bash">$ gitlab-runner exec docker drupal8ci:existing_site_tests
Running with gitlab-runner 12.2.0 (a987417a)
Using Docker executor with image juampynr/drupal8ci:latest ...
Pulling docker image registry.gitlab.com/juampynr/drupal8-gitlab:latest ...
Waiting for services to be up and running...
Pulling docker image juampynr/drupal8ci:latest ...
Fetching changes...
Initialized empty Git repository in /builds/project-0/.git/
Checking out 44d2e858 as master...
$ robo job:build
[Filesystem\FilesystemStack] _copy [".gitlab-ci/settings.local.php","web/sites/default/settings.local.php",true]
[Filesystem\FilesystemStack] _copy [".gitlab-ci/.env",".env",true]
[Composer\Validate] Validating composer.json: /usr/local/bin/composer validate --no-check-publish
[Composer\Validate] Running /usr/local/bin/composer validate --no-check-publish
Do not run Composer as root/super user! See https://getcomposer.org/root for details
./composer.json is valid
[Composer\Validate] Done in 0.174s
[Composer\Install] Installing Packages: /usr/local/bin/composer install --optimize-autoloader --no-interaction
[Composer\Install] Running /usr/local/bin/composer install --optimize-autoloader --no-interaction
> DrupalProject\composer\ScriptHandler::checkComposerVersion
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
1/142: https://codeload.github.com/webflo/drupal-core-require-dev/legacy.zip/d9e72ddd4b353727f6bf9293408bfaf429b7c69e
...
142/142: https://codeload.github.com/drupal/core/legacy.zip/39164616332832e1456199d32fc3ed11562f4721
Finished: success: 142, skipped: 0, failure: 0, total: 142
Package operations: 143 installs, 0 updates, 0 removals
- Installing cweagans/composer-patches (1.6.6): Loading from cache
...
Generating optimized autoload files
> DrupalProject\composer\ScriptHandler::createRequiredFiles
Created a sites/default/files directory with chmod 0777
[Composer\Install] Done in 29.56s
$ sleep 1h</code></pre>
<p>It works! Do you see that last line that says <code>sleep 1h</code>? This is the debugging statement that you added before so the job would halt. Now that the job is still running, all you need is the identifier of its Docker container so you can open an interactive session to do your debugging. Here is how to find that:</p>
<pre>
<code class="language-bash">$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2aa10ed01ed2 2e1af17c8319 "docker-php-entrypoi…" 8 seconds ago Up 7 seconds 80/tcp runner--project-0-concurrent-0-5229b3296d35403b-build-4
df8383d0a1f7 3843695a526c "mysqld" 20 seconds ago Up 19 seconds 3306/tcp runner--project-0-concurrent-0-5229b3296d35403b-registry.gitlab.com__juampynr__drupal8-gitlab-0</code></pre>
<p>There are two running containers in the above output. GitLab assigns them a name, such as <code>runner--project-0-concurrent…</code>. You can identify which is which by their command: the first one, <code>docker-php-entrypoi…</code> is the <a href="https://github.com/docker-library/php/blob/a3cf31c9020559777c731d980047450cba744122/7.3/stretch/apache/Dockerfile#L281" target="_blank">entrypoint of the PHP image</a>, the one that hosts PHP and Apache. The second one, <code>mysqld</code>, is the MySQL server. If you wanted to debug the former, the identifier that you'd use on your next command is <code>runner--project-0-concurrent-0-5229b3296d35403b-build-4</code>:</p>
<pre>
<code class="language-bash">$ docker exec -it runner--project-0-concurrent-0-5229b3296d35403b-build-4 bash
root@runner--project-0-concurrent-0:/var/www/html#
root@runner--project-0-concurrent-0:/var/www/html# cd /builds/
root@runner--project-0-concurrent-0:/builds# ls
project-0 project-0.tmp
root@runner--project-0-concurrent-0:/builds# cd project-0
root@runner--project-0-concurrent-0:/builds/project-0# ls
LICENSE README.md RoboFile.php composer.json composer.lock config console docs drush load.environment.php phpunit.xml.dist scripts web</code></pre>
<p>Once in the container, you'll need to locate the source code. GitLab Runner creates a directory called <code>builds</code> at the root of the container’s file system. Once there, you can see your project files and you're ready to debug the failing job.</p>
<h2>Conclusion</h2>
<p>The bug turned out to be a missing ampersand at the end of the command that started Headless Chrome. It may sound silly, but these are the kind of errors that may take you hours to figure out if you are not able to debug them properly.</p>
<p>Here are a couple of points for you to take with you the next time a GitLab CI job does not work as expected:</p>
<ol start="1"><li>Register a <a href="https://docs.gitlab.com/runner/register/" target="_blank">Docker runner</a> for the repository that you are working with. Check the screenshot above for where to find the token you'll need to enter.</li>
<li>Check out the <a href="https://docs.gitlab.com/runner/commands/README.html#limitations-of-gitlab-runner-exec" target="_blank">unsupported features</a> when running jobs via <code>gitlab-runner exec</code>. Adjust the job accordingly.</li>
<li>Add a <code>sleep 1h</code> statement where you want the job to halt when it runs. Commit that, but don’t push it.</li>
<li>Run the job with the command <code>gitlab-runner exec docker [job id goes here]</code>.</li>
</ol><p>Happy debugging!</p>
Wed, 03 Jun 2020 10:53:00 -0400Juampy NR62e6fe26-6e28-49a4-9583-c26fc2e6cb7eShould You Upgrade from Drupal 7 to Drupal 9?https://www.lullabot.com/articles/should-you-upgrade-drupal7-drupal9
<p>Drupal 7, our much-loved CMS that was released in 2011, is nearing the end of its life. No, that's not hyperbole; Drupal 7 is scheduled to reach end-of-life in November 2021. Drupal 8 has been out for a few years, but at the time of this writing, <a href="https://www.drupal.org/project/usage/drupal" target="_blank">Drupal core usage statistics</a> indicate that only about 350,000 of the more than 1.1 million reporting Drupal core sites are using Drupal 8.x. Over 730,000 of those sites are still using Drupal 7. If your site is one of those 730,000 still on Drupal 7, should you upgrade to Drupal 9? </p>
<h2>Drupal 7 is coming to an end</h2>
<p>Whether or not you choose to upgrade to Drupal 9, it's time to acknowledge one very important truth: Drupal 7 is coming to an end. After a decade in service, Drupal 7 will stop receiving official community support in November 2021, and the Drupal Association will stop supporting Drupal 7 on Drupal.org. Automated testing for Drupal 7 will stop being supported via Drupal.org, and Drupal 7 will no longer receive official security support.</p>
<p>Beyond the loss of support for Drupal 7 core, there is less focus on the Drupal 7 version of many contributed modules. Some of them are quite stable and may work well into the future, but others are more neglected. The reality is that once module maintainers have moved their own sites to Drupal 8 or Drupal 9, they may lose interest in spending the time it takes to keep a Drupal 7 version of their code up to date.</p>
<h2>Upgrading from Drupal 7 is harder than from Drupal 8</h2>
<p>Drupal 8 was released in November 2015. When the Drupal Association announced Drupal 9, they discussed a big change coming to the Drupal ecosystem: Major Drupal version changes would no longer be a substantial replatforming effort, but would instead be a continuation of an iterative development process. In practice, this means that Drupal 9 is built in Drupal 8, using deprecations and optional updated dependencies. The result is that upgrading from Drupal 8 to Drupal 9 is just an iterative change from the final Drupal 8 version. Drupal 9.0 involves the removal of some deprecated code, but introduces no new features; it's a continuation of the fully-tested, stable codebase that is Drupal 8. Basically, Drupal 9.0 is just another release of Drupal 8. </p>
<p>On the other hand, Drupal 7 has significant differences from Drupal 8 and 9. The jump from Drupal 7 to Drupal 9 can be an enormous undertaking. Third-party libraries replaced huge swaths of custom Drupal code. The procedural code was reworked into object-oriented code. The code changes were massive. Upgrading a Drupal 7 site to Drupal 9 will bring it into the new upgrade paradigm, but there's quite a bit of work to do to get there. So the question of whether, and how, to make the jump to Drupal 9 is more complicated.</p>
<p>That leaves Drupal 7 sites with a handful of options:</p>
<ul><li>Migrate to Drupal 9</li>
<li>Migrate to another CMS (like <a href="https://backdropcms.org/" target="_blank">Backdrop CMS</a>)</li>
<li>Convert the Drupal 7 website to a static site</li>
<li>Engage a <a href="https://www.drupal.org/project/d7es" target="_blank">Drupal 7 Extended Support</a><a href="https://www.drupal.org/project/d7es" target="_blank"> </a><a href="https://www.drupal.org/project/d7es" target="_blank">(ES)</a> vendor</li>
<li>Continue to use Drupal 7 unsupported</li>
</ul><p>We’ll focus on the first option in this article, and the others later.</p>
<h2>Benefits of Drupal 8 and 9</h2>
<p>While Drupal 8 is a big change from Drupal 7, it features many developmental and editorial improvements that pay dividends for users who are willing to take the time to learn how to use them.</p>
<h3>Lots of contributed module functionality now in core</h3>
<p>One of the biggest upsides of Drupal 8 and Drupal 9 versus Drupal 7 is the fact that many of the things that require contributed modules in Drupal 7 are just baked into core now. This includes things like:</p>
<ul><li>Layout Builder provides the ability to create customized page layouts that <a href="https://www.drupal.org/project/panels" target="_blank">Panels</a> or <a href="https://www.drupal.org/project/ds" target="_blank">Display Suite</a> provide in Drupal 7.</li>
<li>Blocks have been re-imagined to be fieldable and re-usable, things that require contributed modules like <a href="https://www.drupal.org/project/bean" target="_blank">Bean</a> in Drupal 7.</li>
<li>You don’t need to install a contributed module and third-party libraries to get a <a href="https://www.drupal.org/project/wysiwyg" target="_blank">WYSIWYG</a> editor; it’s built into core.</li>
<li><a href="https://www.drupal.org/project/views" target="_blank">Views</a> is in core, and most of the custom lists in core are now fully customizable views.</li>
<li>Media handling is not an add-on. It’s an integral feature. To get similar functionality in Drupal 7, you need a half dozen or more complicated contributed <a href="https://www.drupal.org/project/media" target="_blank">Media</a> framework modules, each of which might require quite a bit of additional configuration. You can get a pretty decent media handling experience in Drupal 9 by doing nothing more than enabling the Media and Media Library modules and using the default configuration.</li>
<li>Web services are built in, like <a href="https://www.drupal.org/project/jsonapi" target="_blank">JSON:API</a>.</li>
<li>Customized editorial workflows are now available in core, providing functionality that would have required contributed modules like <a href="https://www.drupal.org/project/workbench_moderation" target="_blank">Workbench Moderation</a> or <a href="https://www.drupal.org/project/workflow" target="_blank">Workflow</a>.</li>
</ul><p>That’s just to mention a few features; there are many things in core that would require contributed modules in Drupal 7.</p>
<p>Maintaining this functionality is simplified by having more of it in core. Managing fewer contributed modules simplifies the process of keeping them in sync as you update versions and dependencies, and trying to decide what to do when you get a conflict or something breaks. As Drupal 7 development falls by the wayside, this is even more important, as it could take months - or longer - to get updates to Drupal 7 contributed modules, until they’re no longer supported at all after end-of-life.</p>
<p>Having these solutions in core means everyone is using the same solution, instead of splintering developer focus in different directions. And having them in core means they’re well-tested and maintained.</p>
<h3>Composer gets us off the island</h3>
<p>One of the changes to Drupal since the Drupal 7 release is that Drupal 8 and 9 extensively use third party libraries like Symfony for important functionality, instead of relying on custom Drupal-specific code for everything. That move “off the island” has introduced a need to manage Drupal’s dependencies on those libraries. This is handled with yet another tool, a package called <a href="https://getcomposer.org" target="_blank">Composer</a>.</p>
<p>You need to manage the dependencies of these new top-level third-party libraries, but each of these libraries has dependencies on other libraries, which have dependencies on more libraries, creating a confusing spiderweb of dependencies and requirements and potential conflicts. Dependency management quickly becomes a maintenance nightmare. It’s a new tool to learn, but <a href="https://getcomposer.org" target="_blank">Composer</a> is a great dependency manager. Taking the time to learn Composer gives developers a powerful new tool to deal with dependency management.</p>
<p>Composer can do other things. If you add <a href="https://github.com/cweagans/composer-patches" target="_blank">cweagans/composer-patches</a>, it’s also a very useful tool for managing patches from Drupal.org. You can add a <strong>patches</strong> section to <strong>composer.json</strong> with links to the patches you want to watch. Composer will automatically apply the patches, and your <strong>composer.json</strong> file becomes a self-documenting record of the patches in use.</p>
<p>You can read more about Composer in another Lullabot article: <a href="https://www.lullabot.com/articles/drupal-8-composer-best-practices" target="_blank"><em>Drupal 8 Composer Best Practices</em></a>.</p>
<h3>No more Features for configuration management</h3>
<p>In Drupal 7, many sites deploy configuration using the <a href="https://www.drupal.org/project/features" target="_blank">Features</a> module. Depending on who you ask, using Features for configuration management could be regarded as a good thing or a bad thing. Many developers maintain that Drupal 8 (and therefore Drupal 9’s) <a href="https://www.lullabot.com/articles/configuration-management-in-drupal-8-the-key-concepts" target="_blank">Configuration Management</a> system, which allows database configuration to be exported to YML files, is much easier than the Drupal 7 Features system. As with Composer, it takes time to learn, but it enables developers who understand the system to accomplish more with less effort. </p>
<h3>Secure PHP support</h3>
<p>Drupal 7 sites could be running on deprecated versions of PHP, even as old as 5.3. <a href="https://www.drupal.org/docs/7/system-requirements/php-requirements" target="_blank">Drupal 7 sites should already have moved to PHP 7</a>, but could still be running on older, very outdated and insecure, versions of PHP. Drupal 7 currently works with PHP 7.3 but has <a href="https://www.drupal.org/project/drupal/issues/3081386" target="_blank">problems with PHP 7.4</a>. As PHP continues to <a href="https://www.php.net/supported-versions.php" target="_blank">progress and deprecate older versions</a>, you may find that you can no longer keep your Drupal 7 site running on a secure version of PHP. Drupal 8 runs on PHP 7.0+, and Drupal 9 runs on and requires a minimum of PHP 7.3, so both provide a better window of compatibility with secure PHP versions.</p>
<h2>Resistance to migrating to Drupal 8 and 9</h2>
<p>There are some reasons why sites delay making this move:</p>
<h3>Lack of Drupal 8 versions of Drupal 7 contributed modules</h3>
<p>Early in Drupal 8’s release cycle, one of the big complaints about Drupal 8 was that many Drupal 7 contributed modules no longer worked in D8. It did take time for some contributed modules to be updated to Drupal 8. However, many Drupal 7 contributed modules were no longer <em>needed</em> in Drupal 8, because the functionality they provided is now a part of Drupal 8 core.</p>
<p>If you haven’t checked the state of Drupal contributed modules in the last few years, take a look at what’s now available for Drupal 8. You can check the <a href="https://www.drupal.org/project/contrib_tracker" target="_blank">Drupal 8 Contrib Porting Tracker</a> to find the status of popular Drupal 7 modules and see whether they’ve gotten a Drupal 8 stable release. You may find that modules that were missing early on are now available, or that you no longer need some contributed modules because that functionality is now managed in another way.</p>
<p>More importantly, you don’t have to worry about lack of parity in Drupal 8 contributed modules when Drupal 9 is released; as long as the Drupal 8 module in question isn’t built on deprecated code, everything that works in 8.x should continue to work in Drupal 9. And if a D8 module <em>is</em> built on deprecated code, the maintainer should be aware of it. All the code that is being removed in Drupal 9 has already been deprecated in Drupal 8.8, so there won’t be any surprises for module or site maintainers.</p>
<h3>Maintenance overhead for small teams</h3>
<p>With the introduction of Drupal 8 and Drupal 9, the new paradigm in Drupal development is more frequent, smaller releases. This mirrors a larger trend in software development, where iterative development means frameworks make more frequent releases, and consequently, those releases aren’t supported as long. </p>
<p>This means you need to commit to keeping your site current with the latest releases. If you’re part of a small team managing a large Drupal site, you may simply not have the bandwidth or expertise to keep up with updates. </p>
<p>There are some tools to make it easier to keep a site current. There is an <a href="https://medium.com/@OPTASY.com/automatic-updates-in-drupal-from-mission-impossible-to-drupals-new-auto-update-feature-240ffc93a023" target="_blank">Automatic Updates</a> module that might be helpful for small sites. That module is a work in progress, and it does not yet support contributed module updates or composer based site installs. These are planned for Phase 2. But this is a project to keep an eye on. </p>
<p>You can <a href="https://www.drupal.org/docs/8/update/update-modules-and-themes-using-composerhttps://www.drupal.org/docs/8/update/update-modules-and-themes-using-composer" target="_blank">manage updates yourself</a> using Composer and Drush. Sites of any size can also use <a href="https://dependabot.com" target="_blank">DependaBot</a>, a service that creates automatic pull requests with updates. </p>
<p>And of course, some web hosts and most Drupal vendors will provide update services for a fee and just take care of this for you.</p>
<h3>The new way of doing things is harder</h3>
<p>The final complaint that has prevented many Drupal 7 sites from upgrading to Drupal 8 and Drupal 9 is that the new way of doing things is harder. Or, if not harder, different. There’s a lot to unpack here. In some cases, this reflects resistance to learning and using new tools. In other cases, it may be that long-time Drupal developers have a hard time learning new paradigms. Another option may be that developers are simply not interested in learning a new stack, and may no longer want to develop in new versions of Drupal. </p>
<p>Drupal 6 and 7 have a lot of “Drupalisms,” Drupal-specific, custom ways of doing things, so developers who have been deep into Drupal for a long time may feel the number of things to re-learn is overwhelming. Fortunately, the “new” things, such as Composer, Twig, and PHPUnit are used by other PHP projects, so there is a lot that Drupal 7 developers can learn that will be useful if they work on a Symfony or Laravel project, for example.</p>
<p>Developing for Drupal 8 and Drupal 9 is certainly different compared to Drupal 7 and older versions. Some developers may choose this as a turning point to shift gears into other career paths, developing for a different stack, or making a more substantial change. But with the Drupal 7 end-of-life approaching, developers who don’t want to move to Drupal 8 and Drupal 9 must make <em>some</em> move, just as Drupal 7 sites must move to a modern platform.</p>
<h3>Security considerations</h3>
<p>In today's world, enterprises have a responsibility to protect their website users' personal data - and they face costly liability considerations if they don't. For many organizations, this means website security is a looming and ongoing concern. It's common for enterprise security policies to require that organizations only use services with ongoing security support. Relative to the Drupal 9 upgrade, this means that many enterprises can't continue to maintain Drupal 7 websites after they stop receiving security support.</p>
<p>But what does “no more security support” actually mean?</p>
<p>When Drupal 7 reaches end-of-life, the Drupal community at large will no longer provide “official” security updates or bug fixes. The Drupal Security Team will no longer provide support or Security Advisories for Drupal 7 sites. Automated or manual processes that you currently use to update your sites may no longer work.</p>
<p>There is a bit of nuance to the lack of security support, however. The <a href="https://www.drupal.org/project/d7es" target="_blank">Drupal 7 ES </a><a href="https://www.drupal.org/project/d7es" target="_blank">program</a> involves partnering with a Drupal Association-vetted vendor and assuring that the vendor is coordinating responsible disclosure of security issues and fixes while publicly sharing the work toward those fixes.</p>
<p>Practically speaking, this means that even if you’re not partnered with an ES vendor, you can still get security patches for your site. However, websites using modules that aren’t actively supported by ES vendors won’t have the benefit of a partner to hunt down and fix issues with those modules, security, or otherwise. If you have modules or other dependencies that age out of security updates, such as the end-of-life of the PHP version you’re hosting, you may be left with a website with an increasing number of security holes.</p>
<p>Additionally, after November 2021, Drupal 7 core and Drupal 7 releases on all project pages will be flagged as not supported. As a result, third-party scans may flag sites using Drupal 7 as insecure since they’ll no longer get official security support.</p>
<h3>No more bug fixes or active development</h3>
<p>Alongside security considerations, a lesser concern of the Drupal 7 end-of-life timeline is an official end to community-at-large bug fixes and active development. Drupal 7 development has already shifted to focus on Drupal 8 over the past several years, with Drupal 7 bugs lingering. For example, take a look at the Drupal.org issue queue for <a href="https://www.drupal.org/project/issues/search/drupal?text=&assigned=&submitted=&project_issue_followers=&status%5B%5D=Open&categories%5B%5D=1&version%5B%5D=any_7.&issue_tags_op=%3D&issue_tags=" target="_blank">Drupal 7 core bugs</a>; you’ll see issues that haven’t been updated for weeks or months, versus hours or days for <a href="https://www.drupal.org/project/issues/search/drupal?text=&assigned=&submitted=&project_issue_followers=&status%5B%5D=Open&categories%5B%5D=1&version%5B%5D=any_9.&version%5B%5D=any_8.&issue_tags_op=%3D&issue_tags=" target="_blank">Drupal 8/9 development issues</a>.</p>
<h2>Questions to ask when migrating from Drupal 7</h2>
<p>So how do you decide which path is right for your organization? Here are some questions to ask.</p>
<h3>What are the skills and size of your development team?</h3>
<p>The shift from Drupal 7 to Drupal 8 and Drupal 9 involved a shift from Drupal-specific paradigms to incorporating more general object-oriented programming concepts. If your team consists of long-time Drupal developers who haven't done a lot of object-oriented programming, this paradigm shift involves a learning curve that does have an associated cost. For some budget-conscious organizations, this may mean it's more economical to remain on Drupal 7 while developers work on skilling up for Drupal 8/Drupal 9 paradigms.</p>
<p>Another consideration is the size of your development team. If your team is small, you may need to engage an agency for help or explore the other alternatives mentioned above.</p>
<h3>What are the plans for the site?</h3>
<p>How much active development is being done on the site? Are you planning to add new features, or is the site in maintenance mode? What is your budget and plan to maintain the site; do you have developers devoted to ongoing maintenance, or is it one small priority among many competing priorities? </p>
<p>If you're planning to add new features, the best option is to migrate to Drupal 8 and Drupal 9. Drupal 9 is under active development, and these modern systems may already include the new features you want to add. If not, working in an ecosystem that's under active development generally reduces development overhead. </p>
<h3>What is the life expectancy of the site?</h3>
<p>How many years do you expect the current iteration of the site to continue? Are you planning to use the site for three more years before a major redesign and upgrade? Eight more years? Sites with a shorter lifespan may be good candidates for Drupal 7 ES, while sites with longer life expectancies would benefit from upgrading to a modern platform with a longer lifespan.</p>
<h3>What code is the site using?</h3>
<p>Do an inventory of your site's code. What contributed modules are you using? What do you have that's custom? <a href="https://www.drupal.org/project/upgrade_check/" target="_blank">Drupal 8 upgrade evaluation</a> is a good place to start. </p>
<p>Some Drupal 7 contributed modules have Drupal 8 and Drupal 9 versions available, while others no longer apply in a world with different programming paradigms. Still, others may now be a part of Drupal 9 core. </p>
<p>If you're using a lot of custom modules and code, migrating to Drupal 8 and Drupal 9 is a bigger project. You might be able to mitigate some of that by altering the scope of your new site to take more advantage of the new capabilities of core and the available Drupal 8 contributed modules.</p>
<h3>What features do you want?</h3>
<p>Make a list of the features that are important to your organization. This should include features that your site currently has that you couldn't live without, and features you'd like to have but currently don't. Then, do a feature comparison between Drupal 8 and Drupal 9, and any other options you're considering. This may drive your decision to migrate, or you may decide that you can live without "must-have" features based on availability.</p>
<h2>Where to go from here</h2>
<p>Bottom line: with the Drupal 7 end-of-life date coming next year, now is the time to scope your site changes. But where do you go from here? The next few articles in this series explore how and when to upgrade from Drupal 7 to Drupal 9 and alternate solutions if upgrading isn’t a good choice for your organization. Stay tuned!</p>Wed, 27 May 2020 10:37:40 -0400Karen Stevenson684d73ef-1bf1-4c07-a5e6-54f690d396565 Steps to a More Accessible Websitehttps://www.lullabot.com/articles/5-steps-more-accessible-website
<p>Whether you're a developer or a designer, everyone has a role to play in making websites more accessible and usable for site visitors. Our design and development teams got together to create this checklist of high-priority tasks you can execute to make your site more accessible. These are some of the issues that affect users of assistive technologies the most, so they should be first on your list when you're forming a plan to improve website accessibility.</p>
<h2>Accessibility Checklist</h2>
<p><span>✓</span> <strong>Color Choice</strong> - Choose a well-balanced set of complementary colors that meet color contrast standards.</p>
<p><span>✓</span> <strong>Color Contrast</strong> - When combining colors, verify that they have <em>at least</em> the minimum required color contrast.</p>
<p><span>✓</span> <strong>Link Style</strong> - Add <em>at least</em> two differentiators to links for users with visual disabilities</p>
<p><span>✓</span> <strong>Buttons </strong>- Remember color contrast requirements, states (i.e., hover, focus, etc.), and readability when designing and developing buttons for your site.</p>
<p><span>✓</span> <strong>Forms</strong> - Set up your forms for success by including accessible labels, fields, instructions, errors, and alerts.</p>
<h2>Color Choice</h2>
<ul><li>Create a visual design for your site using a balance of colors that isn't too distracting, while also not being too timid. This helps organize the site for all users, especially for those with disabilities. The effective use of color combinations in your website can establish a visual hierarchy, organize content, and draw distinctions between things such as foreground and background, or static areas and areas that are interactive. </li>
<li>Use the primary color palette for things like calls to action (CTAs), icons, and any place where highlighting areas visually is important.</li>
<li>Save secondary colors to highlight less critical information, such as secondary CTAs or backgrounds.</li>
<li>Finally, apply neutral colors to things like text and backgrounds, or to tone things down when there are large areas of color.</li>
</ul><div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide image--captioned" style="max-width: 1600px;"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-05/gretathunberg.png?itok=NY3CrtQV 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-05/gretathunberg.png?itok=WDOuFRT6 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-05/gretathunberg.png?itok=Vl7qi77e 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-05/gretathunberg.png?itok=XfbC-9bK 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-05/gretathunberg.png?itok=BY-wUShS 1600w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-05/gretathunberg.png?itok=XfbC-9bK" alt="Tweet from Greta Thunberg" /><figcaption class="image__caption">Twitter's primary blue used for main CTAs & links.</figcaption></figure></div>
<h2>Color Contrast</h2>
<p>One of the designer's most important tasks is to check color contrasts. Once you learn how to do this, you can easily integrate this task into your design workflow. When you are checking color contrasts, you should:</p>
<h3><strong>Web-based</strong></h3>
<ul><li>Pull the hex value(s) and go to the <a href="https://webaim.org/resources/contrastchecker/" target="_blank"><strong>WebAIM Contrast Checker tool</strong></a></li>
<li>Enter your hex value(s) into the Foreground Color or Background Color field(s)</li>
<li>Using the sliders below the Foreground Color or Background Color, change the color values until the Contrast Ratio is at or above these minimum values:
<ul><li>For text that's at or over 18.66px and bold, look for a color contrast of at least 3:1</li>
<li>For text under 18.66px, look for a color contrast of at least 4.5:1</li>
</ul></li>
<li>Pull the new hex value(s) and place them into your page</li>
</ul><h3><strong>Desktop-based</strong></h3>
<ul><li>Download the <a href="https://developer.paciellogroup.com/resources/contrastanalyser/" target="_blank"><strong>Colour Contrast Analyser tool from The Paciello Group</strong></a> for Windows/macOS</li>
<li>Enter your hex value(s) into the Foreground Color or Background Color field(s)</li>
<li>Using the sliders below the Foreground Color or Background Color, change the color values until the Contrast Ratio is at or above these minimums:
<ul><li>For text that's at or over 18.66px and bold, look for a color contrast of at least 3:1</li>
<li>For text under 18.66px, look for a color contrast of at least 4.5:1</li>
</ul></li>
<li>Pull the new hex value(s) and place them into your design</li>
</ul><h3><strong>Other useful tools</strong></h3>
<ul><li><a href="https://chrome.google.com/webstore/detail/colorblinding/dgbgleaofjainknadoffbjkclicbbgaa?hl=en" target="_blank"><strong>Colorblinding</strong></a>: This extension simulates what your website looks like to a visitor with a color vision impairment.</li>
<li><a href="https://chrome.google.com/webstore/detail/colorzilla/bhlhnicpbhignbdhedgjhgdocnmhomnp?hl=en" target="_blank"><strong>ColorZilla</strong></a>: Advanced Eyedropper, Color Picker, Gradient Generator and other colorful goodies</li>
</ul><div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide image--captioned" style="max-width: 1600px;"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-05/webaim-contrast-checker.png?itok=C0g9FXZG 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-05/webaim-contrast-checker.png?itok=Gj-yeFP0 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-05/webaim-contrast-checker.png?itok=Q0dEhsRC 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-05/webaim-contrast-checker.png?itok=GORu-QX- 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-05/webaim-contrast-checker.png?itok=kTT3s83v 1600w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-05/webaim-contrast-checker.png?itok=GORu-QX-" alt="WebAIM Contrast Checker Tool" /><figcaption class="image__caption">WebAIM Contrast Checker Tool</figcaption></figure></div>
<h2>Link Style</h2>
<p>Make sure links can be distinguished from regular text. When you use links in body content, they should be visually distinct in two different ways. One of these should be color, but the other differentiator should be independent of color to provide a distinction for colorblind users. When links are placed in the header, footer, or sidebar navigation, this differentiation is not required, although it is recommended. Having links underlined by default and removed on hover/focus is the best option, because most users expect that behavior, and it is also the default behavior of browsers. Other options include highlighting, dots, dashes, an outline, or bolded text.</p>
<p>A focus state is required on all links, so be sure to include it when setting the hover state. This adds a solid border to a link when a user tabs to it using their keyboard, which helps keyboard-only users who use the keyboard to navigate instead of a mouse. </p>
<p>Make sure horizontal and vertical link groups have enough space to enable users to access them easily. Iconography can also help users, giving them another way to distinguish between links and plain text. Users understand content more quickly when paired with a visual cue, such as an icon. Finally, use descriptive text for links instead of general text like "More" or "Click here." Links should have some context to the content they're related to; however, make sure they are kept short and understandable.</p>
<p>When designing links, think about the following states:</p>
<ul><li>Default (unvisited)</li>
<li>Visited (already visited)</li>
<li>Hover (moused over)</li>
<li>Focus (focusable elements via the keyboard tab key, i.e., links, buttons, etc.)</li>
<li>Active (clicked on, i.e., tabs or navigation)</li>
<li>Disabled (not able to be activated by the user)</li>
</ul><h3> <strong>Further reading on link style:</strong></h3>
<ul><li><a href="https://www.smashingmagazine.com/2010/02/the-definitive-guide-to-styling-web-links/" target="_blank"><strong>The Definitive Guide To Styling Links With CSS</strong></a></li>
<li><a href="https://webaim.org/techniques/hypertext/" target="_blank"><strong>WebAIM: Links and Hypertext</strong></a></li>
</ul><div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide image--captioned" style="max-width: 1600px;"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-05/link-states.png?itok=AF8KFTUR 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-05/link-states.png?itok=PUFRl4QJ 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-05/link-states.png?itok=NAjVfTxC 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-05/link-states.png?itok=qPNf-O-L 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-05/link-states.png?itok=-DMSOqen 1600w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-05/link-states.png?itok=qPNf-O-L" alt="Link States" /><figcaption class="image__caption">Link States</figcaption></figure></div>
<h2>Buttons</h2>
<p>When we talk about buttons, we're referring to regular form buttons and links that are styled as buttons. When developing for accessibility, form buttons should always be used in forms, and links should be used when you need to redirect the user to another page, site, or an anchor within the page.</p>
<p>Buttons should have a clear and solid border color that meets color contrast against the background color and a background color that meets color contrast against the text. When you hover over a button, there should be a very noticeable differentiation in the background and text color. Inverting the colors is a good option; alternately, darken the background color, and invert the text color.</p>
<p>When designing buttons, think about button sizing for both desktop and touch screen. Minimum touch targets should be comfortable for an adult finger to navigate successfully. The Web Content Accessibility Guidelines (WCAG) specify a minimum size of 44x44 pixels, or larger for users such as children or people with motor difficulties. </p>
<p>Create button labels that are easy to read. Choose sentence case or title case over uppercase, and make sure the font is big enough for easy readability. Make labels action-oriented, i.e., "Next step," or "Save recipe." Including iconography within your buttons can help users understand actions more quickly. Include button states in all designs. These states provide users with feedback that an action is about to happen. When designing buttons, think about the following states:</p>
<ul><li>Default (what a button looks like without any interaction)</li>
<li>Hover (on desktop, when a user's cursor is hovering over a button)</li>
<li>Active (a user has clicked on, and it is selected)</li>
<li>Focus (when a user clicks on or uses a keyboard tab key)</li>
<li>Disabled (not active)</li>
</ul><p>Think about the overall button hierarchy within the system regarding primary, secondary, and tertiary buttons. This hierarchy lets users understand what the primary and secondary calls to action are on a page. </p>
<h3> <strong>Further reading on buttons:</strong></h3>
<ul><li><a href="http://web-accessibility.carnegiemuseums.org/code/buttons/" target="_blank"><strong>Carnegie Museums Web Accessibility Guidelines</strong></a></li>
<li><a href="https://medium.com/the-school-of-do/ui-cheat-sheets-buttons-7856ff90f544" target="_blank"><strong>UI cheat sheets: buttons</strong></a></li>
<li><a href="https://www.w3.org/WAI/WCAG21/Understanding/target-size.html" target="_blank"><strong>Web Content Accessibility Guidelines (WCAG) specify a minimum size of 44 by 44 CSS pixels</strong></a></li>
</ul><div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide image--captioned" style="max-width: 1600px;"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-05/button-states.png?itok=7GQuA_pD 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-05/button-states.png?itok=QmKI5OI9 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-05/button-states.png?itok=bWR_scu3 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-05/button-states.png?itok=-I7JE8vS 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-05/button-states.png?itok=xUrfo-9_ 1600w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-05/button-states.png?itok=-I7JE8vS" alt="Button States" /><figcaption class="image__caption">Button States</figcaption></figure></div>
<h2>Forms</h2>
<p>When designing forms, these tips can make them more readable and usable:</p>
<h3>Group related fields </h3>
<ul><li>Group logical sections with clear headings, i.e., Personal information, Contact information, Billing information. </li>
<li>Groups of fields (i.e. checkboxes, radio buttons, etc.) should be contained within a <fieldset> that includes a <legend> element. The <legend> element holds the title for the grouped fields, which will be displayed on the page.</li>
<li>Include ample white space between sections to provide a visual distinction across sections. </li>
</ul><h3><strong>Single column forms</strong></h3>
<ul><li>Forms are easiest to scan when form titles and fields are stacked in one column and aligned. This allows the eye to quickly scan down a single column instead of zig-zagging across multiple columns.</li>
</ul><h3><strong>Form labels</strong></h3>
<ul><li>For text fields, it is best practice to place labels above corresponding form fields in a form. Place checkboxes and radio buttons to the right of each field.</li>
<li>Use a bolded font to help form labels stand out. A flag on whether the form field is required should be placed right after the label as well. This can be a red asterisk, red "REQUIRED" text, or something similar. Form labels can also contain brief instructions for the particular field; for example, Date (mm/dd/yyyy)</li>
<li>In addition to a label, each form field should have descriptive helper text and placeholder text. Left-aligning and stacking form labels over their respective fields improve scannability. Keep the labels short and sweet.</li>
<li>Don't use placeholder text as a label, as this text isn't available to screen readers. Placeholder text disappears when the user interacts with the field. This can be annoying for users who forget what the placeholder text said. For sighted users, placeholder text offers an excellent opportunity to give users brief instructions, or show them how to format their information.</li>
</ul><h3><strong>Form fields</strong></h3>
<ul><li>Form fields should have a clear, solid border color that meets color contrast against the background color and a background color that meets color contrast against the text within the field.</li>
<li>The width of the form field should match the content it will contain. For instance, a date field would have a much shorter width than a name field that must accommodate long names.</li>
</ul><h3><strong>Form</strong> f<strong>ield states</strong></h3>
<ul><li>When designing your form fields, include the various field states. These field states give the user visual cues about where they are within the form, and where they're going next.</li>
<li>Field states to include are default, focus, feedback, and disabled.</li>
</ul><div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide image--captioned" style="max-width: 1600px;"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-05/field-states.png?itok=LtZuSKPN 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-05/field-states.png?itok=tNvp_yJa 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-05/field-states.png?itok=fNWOPvnL 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-05/field-states.png?itok=3l00izRy 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-05/field-states.png?itok=FQI76DnY 1600w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-05/field-states.png?itok=3l00izRy" alt="Field States" /><figcaption class="image__caption">Field States</figcaption></figure></div>
<h3><strong>Form instructions</strong></h3>
<ul><li>Provide a brief list of form instructions directly above all input forms. A note about required fields is recommended, as well as required formats (i.e., dates).</li>
</ul><h3><strong>Form alerts and errors</strong></h3>
<ul><li>Use form errors and alerts to concisely explain to users how to fix issues that prevent them from completing the form. Follow color contrast requirements with these alerts and errors.</li>
<li>Display alerts as a summary at the top of the form, including brief steps on how the user can fix the issues. You can also include links directly to the fields containing errors within the form. Display errors with each problematic form field to make it easier for a user to find specific error details. This may be inline, above, or under the field. </li>
<li>When a field has an error, change the form field's border to another color. Red is recommended because it's universally understandable as an error in a form. In addition to a color change, another differentiator should be added to form fields when they receive errors. This could include an error icon within the field or to the left of the error message.</li>
<li>Try to keep the form lengths short. If fields aren't required, consider whether those form fields are truly necessary on the form. If you don't need them, leave them out altogether. The shorter the form, the fewer opportunities for errors.</li>
</ul><h3><strong>Further reading on forms:</strong></h3>
<ul><li><a href="https://uxdesign.cc/the-ux-behind-designing-better-forms-d6ebe7a817d2" target="_blank"><strong>The UX behind designing better forms</strong></a></li>
<li><a href="https://uxdesign.cc/ui-cheat-sheet-text-fields-2152112615f8" target="_blank"><strong>UI cheat sheet: text fields</strong></a></li>
<li><a href="https://uxdesign.cc/forms-need-validation-2ecbccbacea1" target="_blank"><strong>Forms Need Validation</strong></a></li>
</ul><div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide image--captioned" style="max-width: 1600px;"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-05/default-form-vs-error-message.png?itok=W5tb_RcS 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-05/default-form-vs-error-message.png?itok=HvQyYs7h 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-05/default-form-vs-error-message.png?itok=rawn7QfR 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-05/default-form-vs-error-message.png?itok=HeV1KnML 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-05/default-form-vs-error-message.png?itok=JMpoQbli 1600w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-05/default-form-vs-error-message.png?itok=HeV1KnML" alt="Default form vs error messaging" /><figcaption class="image__caption">Default Form vs. Error Messaging</figcaption></figure></div>
<p>As designers and developers, we can help make the world more accessible to our users. Half the battle is knowing what needs you should be designing for, and the other half is applying the design during development with the best practices and requirements we've discussed. Let's start today by taking a closer look at our work and finding opportunities to make it more accessible, more usable, and more inclusive for everyone. </p>
<p><em>If you would like to ask us any questions about this article, <a href="https://join.slack.com/t/asklullabot/shared_invite/zt-eifb9kw5-s7WN~x5b5EYKWJ4oo9X9mA">please join us on Slack</a> in the #accessibility channel. We look forward to seeing you there!</em></p>Thu, 21 May 2020 11:09:39 -0400Kat Shaw, Ana Barcelona17fd39a6-84b7-4970-bfa4-145f1f50680fThe Olivero Theme Releases Its First Alphahttps://www.lullabot.com/articles/olivero-theme-releases-its-first-alpha
<p>This past Friday, the Olivero project reached a major milestone and tagged our first alpha release. You can download it on the <a href="https://www.drupal.org/project/olivero">Olivero project page.</a> This release marks a point in time where the code is stable(ish!), looks <strike>good</strike> amazing, and is ready for additional testing!</p>
<h2>What is Olivero?</h2>
<p>Olivero is a new theme that is slated to make it into Drupal core in version 9.1 as the new default theme (replacing Bartik). It’s named after <a href="https://www.drupal.org/forum/general/community-spotlight/2019-02-22/remembering-rachel-olivero">Rachel Olivero</a>, who was a valued community member and accessibility advocate. </p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-05/job_application_olivero_and_drupal_9_olivero_turning_conversation_into_a_core_initiative_olivero.jpg?itok=7KGEuFJ7 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-05/job_application_olivero_and_drupal_9_olivero_turning_conversation_into_a_core_initiative_olivero.jpg?itok=DzwU4xrW 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-05/job_application_olivero_and_drupal_9_olivero_turning_conversation_into_a_core_initiative_olivero.jpg?itok=T7Bo_nA5 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-05/job_application_olivero_and_drupal_9_olivero_turning_conversation_into_a_core_initiative_olivero.jpg?itok=tCcnxu4S 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-05/job_application_olivero_and_drupal_9_olivero_turning_conversation_into_a_core_initiative_olivero.jpg?itok=xkrTLOZO 1979w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-05/job_application_olivero_and_drupal_9_olivero_turning_conversation_into_a_core_initiative_olivero.jpg?itok=tCcnxu4S" alt="Olivero theme showing form styles" /></figure></div>
<h2>About this release</h2>
<p>This release has been a long time coming (<a href="https://www.lullabot.com/articles/drupal-9-olivero-turning-conversation-core-initiative">read about the origin here</a>) and has countless hours from dedicated volunteers poured into it with love. </p>
<p>This release is not perfect, in fact, we’ve stated that “<em>perfect is the enemy of good</em>” for our alpha releases! That being said, we’ve done extensive testing with various devices, and browsers (yes — this theme supports Internet Explorer 11), and have done lots of accessibility work, although more still needs to be done!</p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow image--captioned" style="max-width: 1752px;"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-05/msedge_-_win10_good_place_running_and_olivero_theme_demonstration_and_what_you_need_to_know_about_olivero_alpha_release_-_dropbox_paper_and_james_sansbury_lullabot.jpg?itok=UVcOaO4F" width="900" height="696" alt="Olivero theme being tested in Microsoft Edge using Windows High Contrast mode" /><figcaption class="image__caption">Olivero theme being tested in Microsoft Edge using Windows High Contrast mode</figcaption></figure></div>
<h2>You get a feature! You get a feature! Everyone gets a feature!</h2>
<div class="inline-entity inline-entity--align-right inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow image--captioned" style="max-width: 442px;"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-05/olivero_lullabot.jpg?itok=VMcR2YHg" width="442" height="244" alt="Olivero theme settings" /><figcaption class="image__caption">Olivero theme settings</figcaption></figure></div>
<p>Well… almost. Besides cross browser and accessibility work, we’ve included some common features in this initial release.</p>
<ul><li><strong>Dropdown menu support</strong> — this is self-explanatory, but until Drupal 9.1, core themes did not support multi-level menus. </li>
<li><strong>Option for “always-on” mobile navigation</strong> — This is intended for the use case where the page has more top-level menu items than can fit within the header.</li>
<li><strong>Background color options for site branding</strong> — You can change the site branding (logo) background to suit your needs. The current options are white, gray, and blue (which is the default). </li>
</ul><p>As of this release, we’re not including support for dark mode or changing the color of the theme (via color module, or anything else). However, <a href="https://www.drupal.org/project/olivero/issues/3086514">this is on the horizon</a> (likely after the initial core inclusion). </p>
<h2>How you can help</h2>
<p>We’re at the point now where we can use some real-world testing. Please find things that break, we know they’re there!</p>
<p><em>For example, I loaded up Lullabot.com with the new theme and discovered various default styling that can be tightened up (e.g., links within headers).</em></p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow image--captioned" style="max-width: 1478px;"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-05/lullabot_and_olivero_lullabot.jpg?itok=hHut1aj5" width="900" height="644" alt="The Olivero theme running on a local install of Lullabot.com" /><figcaption class="image__caption">The Olivero theme running on a local install of Lullabot.com</figcaption></figure></div>
<p><br />
We’re also at the point where we can use additional accessibility testing. Please fire up your screen readers, and decrease (or increase) your contrast and take a look!</p>
<p>As with all free open source projects, please take a look at the issue queue to make sure that the issue isn’t already created.</p>
<h2>Tugboat helps save the day!</h2>
<div class="inline-entity inline-entity--align-right inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow image--captioned" style="max-width: 992px;"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-05/tugboat.jpg?itok=DjGwwFAt" width="900" height="719" alt="Olivero project within the Tugboat dashboard" /><figcaption class="image__caption">Olivero project within the Tugboat dashboard</figcaption></figure></div>
<p>We also want to give out a huge shoutout to the Tugboat team. If you aren’t familiar with <a href="https://www.tugboat.qa/">Tugboat</a>, it’s a pull request builder that generates live, working websites from every PR, branch or tag.</p>
<p>Through the Tugboat service, we worked to set up a Drupal multisite install that has deploy previews with content, without content, and using the minimal profile (note this is currently broken). </p>
<p><a href="https://8-x-1-x-dev-2t4d1epwkj8tgwxduizmixhzevqwzi8w.tugboat.qa/">Check out the Tugboat preview here!</a> Note that the “working” status of this Tugboat preview will be in flux, as we commit code, rebuild the preview, etc.</p>
<h2>Next steps</h2>
<p>We’re off to the races! The next step is incremental alpha releases (either weekly or biweekly). We’re also going to make a list of features that are necessary to tag our first beta and work toward that. </p>
<p>We hope to create our first Drupal core path in a month or so. This will give core committers the opportunity to put an eye on the codebase, so it can be included in version 9.1. We’re aiming to get this committed into core in late July-ish.</p>
<h2>Thank you, thank you, thank you!</h2>
<p>We have so many people helping out, and we wouldn’t be here without them. Here is the full <a href="https://www.drupal.org/node/3083133/committers">list of committers</a> (16 as of today), and this does not even count the people who are doing testing, etc! <br />
</p>
Mon, 18 May 2020 15:20:57 -0400Mike Herchel, Putra Bonaccorsie65a5c09-2ebc-4a8b-8a12-6200717af6bdLullabot Podcast: Update on Olivero: The Making of Drupal 9's New Lookhttps://www.lullabot.com/podcasts/lullabot-podcast/update-olivero-making-drupal-9s-new-look
<p>Matt and Mike talk with Putra Bonaccorsi and host Mike Herchel about Drupal 9's new front-end theme, and its past, present, and future. </p>Thu, 14 May 2020 21:42:06 -0400Matt Kleve, Mike Herchel263937f4-0d6f-43e8-a13b-4d03f44e5c00Impact and Generosity in the Drupal Communityhttps://www.lullabot.com/articles/impact-and-generosity-drupal-community
<p>As the global pandemic continues to spread — causing widespread sickness and death, restricting in-person human contact, creating additional responsibilities at home or financial hardships, or any of the countless other changes to daily life that have resulted in feelings such as fear, anger, boredom, or uncertainty — this virus has forced some of us to reassess our values and our place in the world. While the majority of us who participate in the Drupal community remain focused squarely on technical issues, others might find now is an especially good time to take a closer look at Drupal's <a href="https://www.drupal.org/about/values-and-principles" target="_blank">Values and Principles</a>. For those of us privileged enough to have the time and space to consider more philosophical questions, we can ask if Drupal's stated values (still) align with our values, or even consider the role of Drupal in our lives when the pandemic subsides.</p>
<p>This article — the first in a series of articles exploring Drupal's values and principles — considers Drupal's <a href="https://www.drupal.org/about/values-and-principles#impact-gives-purpose" target="_blank">first principle</a>, "impact gives purpose," which is one aspect of the first value, "prioritize impact." On one level, the first principle is merely practical. It concludes by prioritizing the "stakeholders" we should consider: "When faced with trade-offs, prioritize the needs of the people who create content (our largest user base) before the people who build sites (our second largest user base) before the people who develop Drupal (our smallest user base)." In its simplest form, this principle tells us that Drupal ranks the needs of content creators before the needs of the developers.</p>
<p>However, the first principle offers considerably more depth. While acknowledging the practical nature of the Drupal software, it calls on us to aspire to a higher goal: "When contributing to Drupal, the goal is often to optimize the project for personal needs ('scratching our own itch'), but it has to be bigger than that." Thus, Drupal is presented as much more than simply a good product.</p>
<p>The phrase "scratching our own itch" has become a platitude. It's everywhere. The <a href="https://hbr.org/2014/05/when-scratch-your-own-itch-is-dangerous-advice-for-entrepreneurs" target="_blank">Harvard Business Review</a> called it "one of the most influential aphorisms in entrepreneurship." The phrase is well known among software developers in part because in his influential 1999 book, <em>The Cathedral and the Bazaar,</em> (the <a href="https://www.linux-magazine.com/Online/Blogs/Off-the-Beat-Bruce-Byfield-s-Blog/The-Decline-and-Fall-of-Eric-S.-Raymond" target="_blank">highly</a><a href="https://lunduke.com/posts/2020-03-9-b/" target="_blank"> controversial</a>) Eric S. Raymond wrote, "Every good work of software starts by scratching a developer's personal itch." In the Drupal community, however, we see ourselves as aspiring to much more. </p>
<p>As the first principle states, "Slowly, focus shifted from writing the perfect code to growing the project, and amplifying Drupal's impact through better marketing, user experience, and more." </p>
<p>Countless individuals and Drupal subgroups express their desire to impact people. For instance, the Drupal agency Palantir prioritizes <a href="https://www.palantir.net/about-us" target="_blank">impact</a> that is "positive," "lasting," "thoughtful," and "deliberate." Over at ThinkShout, a Drupal agency that works "with courageous organizations that put people and the planet first," the "impact" they aspire to in their first <a href="https://thinkshout.com/about/" target="_blank">core value</a> "is driven by our sense of connectedness and desire to deliver meaningful, measurable results." Countless individuals and organizations in the Drupal community feel motivated by a sincere desire to positively "impact" other human beings.</p>
<p>Drupal's first principle is especially ambitious in describing the impact of the Drupal community: "Prioritizing impact means that every community member acts in the best interest of the project." It seems unlikely that "every community member" can or should make the Drupal project their top priority. Though it may be idealized, it's a worthy goal. We also must reiterate that people will necessarily begin with their own needs.</p>
<p>Contributions to the Drupal project should not come at personal expense. Imagine telling a single parent, who recently lost their job and wants to build a career with Drupal, to consistently act "in the best interest of the project." Change should come from individuals who have the capacity to help others. Part of why some of us contribute to Drupal is because we imagine another human being finding value in our work. We do not require those who benefit to give back. In this idealized form, we encourage people to participate, but we give with an open hand and no expectation of reciprocation. We contribute because we believe our actions have meaning. As the first principle states, "We derive meaning from our contributions when our work creates more value for others than it does for us."</p>
<p>When we look inward to examine our value systems, we probably do not want to find a heap of clichés, and phrases like "prioritize impact" and "create value for others" might sound rather cliché to some ears. In fact, on various lists of "business buzzwords," the word "impact" takes <a href="https://www.inc.com/john-boitnott/you-still-need-to-use-these-20-smart-business-buzzwords.html" target="_blank">the top slot</a>. The noun "impact" comes from the Latin <em>impactus</em>, which means "collision" or "striking one thing against another." The cultural and historical context of "impact" doesn't negate its usefulness, but if the real goal is to "derive meaning," it might be helpful to reconsider this principle in more human terms.</p>
<p>As previously noted, much of Drupal's first principle points toward bigger goals that extend beyond the conference room to a human-centered skill that good people work to cultivate: generosity. We seek to help others, both at home and in our careers. The business-friendly language in the first principle like, "maximize the impact the project can have on others," could, for at least some of us, be read as "practice generosity toward others." We seek to use <a href="https://groups.drupal.org/drupal-for-good" target="_blank">Drupal for Good</a> or even live in (with) <a href="https://www.drutopia.org/" target="_blank">Drutopia</a>.</p>
<p>Thanks to Drupal and its community, some of us possess the fortunate capacity to help others. If that describes you, then consider in what ways you have the opportunity to be generous. Toni Morrison — the iconic writer, activist, and college professor who became the first African-American woman to win the Nobel Prize in Literature — used to <a href="http://www.oprah.com/omagazine/toni-morrison-talks-love/4" target="_blank">tell her students</a>:</p>
<p>"When you get these jobs that you have been so brilliantly trained for, remember that your real job is that if you are free, you need to free somebody else. If you have some power, then your job is to empower somebody else. This is not just a grab-bag candy game."</p>
<p>In this case, Morrison's inspirational words apply not just to students, but to countless people in the Drupal community. Many in our community have freedom and power. We have the opportunity to help others. Help <a href="https://www.drupal.org/project/issues" target="_blank">other Drupalers</a>. Help <a href="https://2015.drupalcampcolorado.org/session/teaching-kids-code-next-gen-drupalers" target="_blank">kids</a>. Help the <a href="https://events.drupal.org/nashville2018/sessions/homeless-addict-winning-drupal-how-community-saves-lives" target="_blank">homeless</a>. Help anyone in need. Maybe even help Drupal and give to <a href="https://www.drupal.org/association/blog/drupalcares-thanks-to-drupal-businesses-you-can-now-triple-your-impact" target="_blank">#DrupalCares</a>. If your actions produce positive results, keep going!</p>
<p>Ultimately, action matters more than language. Whether you feel motivated by the desire to make an impact, or you want to practice generosity, don't let up because the world has changed. Take another look at Drupal's <a href="https://www.drupal.org/about/values-and-principles" target="_blank">Values & Principles</a> and determine for yourself if they motivate you to action. This is not just a grab-bag candy game.</p>Wed, 13 May 2020 16:00:06 -0400Matthew Tift77be5435-9bbb-410b-ad44-9de43b6ada23Preparing Your Drupal 8 Site for Drupal 9https://www.lullabot.com/articles/preparing-your-drupal-8-site-drupal-9
<p>With Drupal 9 just around the corner, there's more and more buzz about preparing for the upgrade. From a project planning perspective, what do organizations need to consider when planning for the Drupal 9 upgrade? Developers may be wondering about the technical details; how to upgrade to Drupal 9. We’ve discussed <a href="https://www.lullabot.com/articles/who-should-upgrade-drupal-9"><strong>who</strong> should upgrade to Drupal 9</a> and <a href="https://www.lullabot.com/articles/when-upgrade-drupal-8-drupal-9"><strong>when</strong> to upgrade to Drupal 9</a>. Here’s <strong>how</strong> to do it.</p>
<h2>Drupal 9 Upgrade Project Planning</h2>
<h3>Plan a release window</h3>
<p><a data-target-href="https://www.drupal.org/core/release-cycle-overview" href="https://www.drupal.org/core/release-cycle-overview" rel="noreferrer nofollow noopener" target="_blank">Drupal 9 is currently slated for release in June 2020</a>. However, Drupal 8.9.x is scheduled to reach end-of-life in November 2021, with older versions, such as 8.7.x slated to stop receiving security support in June 2020. You first need to plan a release window to upgrade to Drupal 8.9.x during this timeframe to make sure your site is upgraded before the older Drupal versions are no longer supported. Once on Drupal 8.9, you can perform and release all the preparatory work described below. After that, you’ll be ready to plan a release window for the final upgrade to Drupal 9. </p>
<p>For more on planning a release window, check out <a data-target-href="https://www.lullabot.com/articles/drupal-8-release-planning-in-the-enterprise" href="https://www.lullabot.com/articles/drupal-8-release-planning-in-the-enterprise" rel="noreferrer nofollow noopener" target="_blank"><i>Drupal 8 Release Planning in the Enterprise</i></a>. Remember to factor in other development work, updates for the rest of your stack, and other ongoing development projects, and give yourself plenty of time to complete the work.</p>
<h3>Scope the upgrade project</h3>
<p>To scope the upgrade project, you'll need to consider a handful of factors:</p>
<ul><li>Deprecated code that must be updated</li>
<li>Other development work that you'll do as part of the upgrade project</li>
</ul><p>We'll dive deeper into how to check for and correct deprecated code and APIs shortly, but first, let's take a look at other development work you might do as part of the upgrade project.</p>
<h3>Solicit feature requests from stakeholders</h3>
<p>Does your website deliver stakeholder-required features using contributed modules that haven't yet been updated for Drupal 9? Do your key stakeholders want new features to better serve site users, or meet business objectives? </p>
<p>For many organizations, major Drupal replatforming efforts have provided a cadence for other website development work, including new feature development. If it's been a while since your organization checked in with stakeholders, now might be a good time to do that. </p>
<p>Regardless of whether or not you plan to deliver new feature development in the Drupal 9 upgrade project, it's a good idea to make sure you won't lose Drupal 8 contributed modules that provide the functionality your stakeholders can't live without - unless you've got a new way to deliver that functionality in Drupal 9.</p>
<h3>Architecture, content, accessibility audits and more</h3>
<p>For sites that are already on Drupal 8, the Drupal 9 upgrade is different than many previous major version updates; Drupal 8 to Drupal 9 does not require a content migration, so there's no real need to do a major information architecture audit and overhaul. In this new world, organizations should look at shifting the site redesign and content architecture cadence to an ongoing, iterative model.</p>
<h2>How to Prepare for Drupal 9</h2>
<h3>Upgrade to Drupal 8.8 or Drupal 8.9</h3>
<p>If you haven't already updated your Drupal 8 site to the most recent version of Drupal 8.8.x or 8.9.x, that's where you must start. Drupal 8.8 is a big milestone for API compatibility; it's the first release with an API that's fully compatible with Drupal 9. Practically speaking, this means that contributed modules released prior to 8.8 may not be compatible with Drupal 9.</p>
<p>Beyond API compatibility, Drupal 8.8 and 8.9 introduce further bugfixes, as well as database optimizations, to prepare for Drupal 9. If you upgrade your website and all contributed modules and themes to versions that are compatible with 8.9, those parts of your site should be ready for Drupal 9. </p>
<h3 data-usually-unique-id="654575349754489578799826">Platform requirements </h3>
<p>One change between Drupal 8 and Drupal 9 is that Drupal 9 requires a minimum of PHP 7.3. Drupal 8 <a data-target-href="https://www.drupal.org/node/3038583" href="https://www.drupal.org/node/3038583" rel="noreferrer nofollow noopener" target="_blank">recommends but does not require 7.3</a>. There are new minimum requirements for MYSQL and MariaDB and other databases. And your Drush version, if you use Drush, must be Drush 10. If you need to update any of these, you should be able to do it while still on Drupal 8, if you like. There may be other changes to the Drupal 9 requirements in the future, so double-check the <a data-target-href="https://www.drupal.org/docs/9/how-drupal-9-is-made-and-what-is-included/environment-requirements-of-drupal-9" href="https://www.drupal.org/docs/9/how-drupal-9-is-made-and-what-is-included/environment-requirements-of-drupal-9" rel="noreferrer nofollow noopener" target="_blank">environment requirements</a>.</p>
<h3>Audit for conflicting dependencies</h3>
<p>Composer manages third-party dependency updates and will update Drupal dependencies when you do Composer updates. However, if anything else in your stack, such as contributed modules or custom code, has conflicting dependencies, you could run into issues after you update. For this reason, you should check your code for any third-party dependency that conflicts with the core dependencies. </p>
<p>For example, Drupal 9 core requires Symfony 4.4, while Drupal 8 worked with Symfony 3.4. If you have contributed modules or custom code that depends on Symfony 3.4, you'll need to resolve those conflicts before you update to Drupal 9. If your code works with either version, you can update your<code>composer.json</code> to indicate that either version works. For instance, the following code in your module’s <code>composer.json</code> indicates that your code will work with either the 3.4 or 4.4 version of Symfony Console. This makes it compatible with both Drupal 8 and Drupal 9 and any other libraries that require either of these Symfony versions.</p>
<pre>
<code class="language-json">{
"require": {
"symfony/console": "~3.4.0 || ^4.4"
}
}</code></pre>
<p>If you have code or contributed modules that require incompatible versions of third party libraries and won’t work with the ones used in Drupal 9, you’ll have to find some way to remove those dependencies. That may mean rewriting custom code, helping your contributed modules rewrite their code, or finding alternative solutions that don’t have these problems.</p>
<h3>Check for deprecated code</h3>
<p>Sites that are already on Drupal 8 can see deprecated code using a few different tools:</p>
<ul><li>IDEs or code editors that understand `@deprecated` annotations;</li>
<li>Running <a data-target-href="https://github.com/mglaman/drupal-check" href="https://github.com/mglaman/drupal-check" rel="noreferrer nofollow noopener" target="_blank">Dr</a><a data-target-href="https://github.com/mglaman/drupal-check" href="https://github.com/mglaman/drupal-check" rel="noreferrer nofollow noopener" target="_blank">upal</a><a data-target-href="https://github.com/mglaman/drupal-check" href="https://github.com/mglaman/drupal-check" rel="noreferrer nofollow noopener" target="_blank"> C</a><a data-target-href="https://github.com/mglaman/drupal-check" href="https://github.com/mglaman/drupal-check" rel="noreferrer nofollow noopener" target="_blank">heck</a>, <a data-target-href="https://github.com/mglaman/phpstan-drupal" href="https://github.com/mglaman/phpstan-drupal" rel="noreferrer nofollow noopener" target="_blank">PhpStan Drupal,</a> or <a data-target-href="http://vijaycs85/drupal-quality-checker" href="http://vijaycs85/drupal-quality-checker" rel="noreferrer nofollow noopener" target="_blank">Drupal Quality Checker</a> from the command line or as part of a continuous integration system to check for deprecations and bugs;</li>
<li>Installing the Drupal 8 branch of the <a data-target-href="https://www.drupal.org/project/upgrade_status" href="https://www.drupal.org/project/upgrade_status" rel="noreferrer nofollow noopener" target="_blank">Upgrade Status</a> module to get Drupal Check functionality, plus additional scanning;</li>
<li><a data-target-href="https://www.drupal.org/docs/9/how-to-prepare-your-drupal-7-or-8-site-for-drupal-9/deprecation-checking-and-correction-tools" href="https://www.drupal.org/docs/9/how-to-prepare-your-drupal-7-or-8-site-for-drupal-9/deprecation-checking-and-correction-tools" rel="noreferrer nofollow noopener" target="_blank">Configuring your test suite</a> to fail when it tries to execute a method that calls a deprecated code path.</li>
</ul><p>See Hawkeye Tenderwolf’s article <a data-target-href="https://www.lullabot.com/articles/how-enforce-drupal-coding-standards-git" href="https://www.lullabot.com/articles/how-enforce-drupal-coding-standards-git" rel="noreferrer nofollow noopener" target="_blank">How to Enforce Drupal Coding Standards via Git</a> for more ideas. That article explains how Lullabot uses GrumPHP and Drupal Quality Checker to monitor code on some of our client and internal sites. </p>
<p>Many organizations already have solutions to check for deprecated code built into their workflow. Some organizations do this as part of testing, while others do it as part of a CI workflow. In the modern software development world, these tools are key components of developing and maintaining complex codebases.</p>
<p>While you can do this check in any version of Drupal 8, you’ll need to do a final pass once you upgrade any older Drupal 8 version to Drupal 8.8, because new deprecations have been identified in every release up to Drupal 8.8.</p>
<h3>Refactor, update and remove deprecated code</h3>
<p>If you find that your site contains deprecated code, there are a few avenues to fix it prior to upgrading to Drupal 9. Some of those tools include:</p>
<ul><li>Search-and-replacing simple deprecations.</li>
<li>Using <a data-target-href="https://github.com/palantirnet/drupal-rector" href="https://github.com/palantirnet/drupal-rector" rel="noreferrer nofollow noopener" target="_blank">Rector for Drupal 8</a> to automatically apply fixes to code, or <a data-target-href="https://www.drupal.org/project/upgrade_rector" href="https://www.drupal.org/project/upgrade_rector" rel="noreferrer nofollow noopener" target="_blank">Upgrade Rector</a> for a version that integrates directly into the <a data-target-href="https://www.drupal.org/project/upgrade_status" href="https://www.drupal.org/project/upgrade_status" rel="noreferrer nofollow noopener" target="_blank">Upgrade Status module</a>.</li>
<li>If your site provides third-party endpoints, check out the <a data-target-href="https://api.drupal.org/api/drupal/9.0.x" href="https://api.drupal.org/api/drupal/9.0.x" rel="noreferrer nofollow noopener" target="_blank">API documentation for reference</a>, particularly if you've been using API functionality or endpoints from Drupal versions older than 8.8.</li>
</ul><h3 data-usually-unique-id="161542588788972108930084">Flag modules as Drupal 9 compatible</h3>
<p>Once you’ve removed deprecated code from your custom modules, flag them as being compatible with both Drupal 8 and Drupal 9, by adding the following line to your module’s <b>info.yml</b> file.</p>
<pre>
<code class="language-yaml">core_version_requirement: ^8 || ^9</code></pre>
<h3 data-usually-unique-id="281298649237844914160323">What about contributed modules?</h3>
<p>If you're using contributed modules that are deprecated, work with module maintainers to offer help when possible to ensure that updates will happen. You can <a href="https://www.drupal.org/project/project_module?f%5B3%5D=sm_core_compatibility%3A9">find Drupal 9 compatible modules</a>, check <a data-target-href="https://www.drupal.org/project/issues/search?issue_tags_op=all+of&issue_tags=Drupal+9+compatibility" href="https://www.drupal.org/project/issues/search?issue_tags_op=all+of&issue_tags=Drupal+9+compatibility" rel="noreferrer nofollow noopener" target="_blank">reports in the drupal.org issue queue for Drupal 9 compatibility</a> or by checking <a data-target-href="https://dev.acquia.com/drupal9/deprecation_status" href="https://dev.acquia.com/drupal9/deprecation_status" rel="noreferrer nofollow noopener" target="_blank">Drupal 9 Deprecation Status</a>.</p>
<p><b>You should update all contributed modules to a Drupal 9-compatible version while your site is still on Drupal 8. Do this before attempting an upgrade to Drupal 9!</b></p>
<h2>Update to Drupal 9</h2>
<p>One interesting aspect of the Drupal 9 upgrade is that you should be able to do all the preparatory work while you’re still on Drupal 8.8+. Find and remove deprecated code, update all your contributed modules to D9-compatible versions, etc. Once that is done, updating to Drupal 9 is simple:</p>
<ol start="1"><li>Update the core codebase to Drupal 9.</li>
<li>Run <code>update.php</code>.</li>
</ol><h2>Drupal 9.x+</h2>
<p>The Drupal Association has announced its intent to provide minor release updates every six months. Assuming Drupal 9.0 releases successfully in June 2020, the Drupal 9.1 update is planned for December 2020, with 9.2 to follow in June 2021.</p>
<p>To make Drupal 9.0 as stable as possible, no new features are planned for Drupal 9.0. The minor updates every six months may introduce new features and code deprecations, similar to the Drupal 8 release cycle. With this planned release cycle, there is no benefit to waiting for Drupal 9.x releases to upgrade to Drupal 9; Drupal 9.0 should be as stable and mature as Drupal 8.9.</p>
<h2 data-usually-unique-id="233731813668054351954120">Other resources</h2>
<ul><li><a data-target-href="https://www.drupal.org/core/release-cycle-overview" href="https://www.drupal.org/core/release-cycle-overview" rel="noreferrer nofollow noopener" target="_blank">Drupal Release Cycles</a></li>
<li><a data-target-href="https://www.drupal.org/blog/how-to-prepare-for-drupal-9" href="https://www.drupal.org/blog/how-to-prepare-for-drupal-9" rel="noreferrer nofollow noopener" target="_blank">How to Prepare for Drupal 9</a></li>
</ul>Wed, 06 May 2020 14:47:10 -0400Karen Stevenson2702a8c3-06ce-4dbb-8aaa-35d159acf67dFrom Squiggles to Straight Lines: Sketching During Strategy and UX Workhttps://www.lullabot.com/articles/squiggles-straight-lines-sketching-during-strategy-and-ux-work
<p>Sketching is a great tool that you, too, can incorporate into your proverbial tool-belt for the web. Today, let's start with the basics of why sketching rocks, not only for web, UX, and strategy work but for working through many types of projects. In a future article, we'll dive further into the nitty-gritty: how to sketch, tools to use, and tips to sketch remotely.</p>
<h2>It's Easy, Accessible, and Cheap to Sketch = ROI </h2>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow image--captioned" style="max-width: 1600px;"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-04/valuable-visuals-sketch.jpeg?itok=AB2BGDjF" width="900" height="673" alt="Valuable visuals sketch" /><figcaption class="image__caption">From tools to time, it doesn’t take much to sketch out valuable visuals.</figcaption></figure></div>
<p>If you only take one point away from this article, it's this: sketching is an excellent tool because it's very fast, very light, and as a result, cheap. It's an artifact on the opposite end of the spectrum from labor-intensive polished digital comps, which often serve a very important, different purpose, but aren't the best tool when you need something more timely. </p>
<p>Anyone can sketch. Though people who identify as less" creative" folks may worry about their ability or feel hesitance toward sketching, the learning curve is actually quite low. We aren't creating fine art. Of course, this practice is especially relevant for designers; we have also found sketching beneficial when doing content strategy, managing projects and products, and understanding workflows.</p>
<p>Sketching doesn't require fancy, expensive tools. Of course, some folks have <em>their </em>pen and <em>their</em> whiteboard marker brand, and it's great that they found what works best for them, but don't let a lack of top-of-the-line equipment stop you. We've solved problems for clients using hotel room pens and printer paper. </p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-04/sketch-artifacts.png?itok=Onpnh6oa 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-04/sketch-artifacts.png?itok=ZrXCdJ8N 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-04/sketch-artifacts.png?itok=wkBWI4dI 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-04/sketch-artifacts.png?itok=kZnV5C3O 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-04/sketch-artifacts.png?itok=0eR2BT-_ 1440w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-04/sketch-artifacts.png?itok=kZnV5C3O" alt="sketch artifacts" /></figure></div>
<h2>Sketching Encourages Exploration and Iteration</h2>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow image--captioned" style="max-width: 1600px;"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-04/sketch-exploration.jpeg?itok=luw2F8XP" width="900" height="673" alt="Sketch exploration" /><figcaption class="image__caption">It’s an easy way to generate a lot of ideas until you find THE idea.</figcaption></figure></div>
<p>Sketching is a great way to iterate on ideas. We love how easy it is to spin up a completely new version of something, without worrying about getting the header design in place up top, for example. You can draw on top of them. You can stack, tear, and reorder them to "Frankenstein" a modified version. You can make changes as a group. When we collaborate, our design team encourages "design swaps," where we hand sketches or other work over to a fellow designer to run with.</p>
<p>Depending on where you find yourself in a project, it can be tricky as a designer to get into significant changes, especially if they require moving around perfectly-assembled comp files with lots of variations. It can be hard to throw out a file you've put a lot of love into. In this way, it's better for sketches to be quick and ugly, because you can generate more lower-fidelity ideas faster, and then "kill your darlings" without all the heartbreak. Put another way; messiness is fine as long as your core idea is clear.</p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-04/wireframe-sketches.png?itok=FFPDvQb8 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-04/wireframe-sketches.png?itok=7N8QJvQp 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-04/wireframe-sketches.png?itok=eazRpNSM 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-04/wireframe-sketches.png?itok=9M1XC1vk 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-04/wireframe-sketches.png?itok=FxeX9GdJ 1465w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-04/wireframe-sketches.png?itok=9M1XC1vk" alt="Wireframe Sketches" /></figure></div>
<p>When working through design challenges, it's better to find issues as soon as you can, ultimately improving the finished product. Some clients are hesitant to give much feedback once we get into later design stages. Meanwhile, sketching allows you to get (many) ideas in front of your stakeholders quickly and find a path you all feel comfortable investing in. Designers: imagine a world without "I'll know it when I see it!" Project stakeholders: imagine a world without the "big reveal!" </p>
<h2>Sketches Facilitate Quick Communication</h2>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow image--captioned" style="max-width: 1600px;"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-04/marker-doodle.jpeg?itok=fcv8mIvY" width="900" height="673" alt="Marker doodle" /><figcaption class="image__caption">The marker doodle is mightier than the long PDF.</figcaption></figure></div>
<p>Sketches, or any visual representations, are generally faster to parse and comprehend than long-form documentation. While it may be tempting to write a 70-page brief, a deliverable's value is ultimately determined by how much use a client gets out of it. Summaries of a few bullet points or quick diagrams are more likely to transfer knowledge than walls of text. </p>
<p>As a result, sketches can be both faster to create and faster to review, requiring less cognitive load and mental energy overall. Visuals are especially valuable over language in a multi-lingual team, or one without an established vocabulary, to bypass verbiage. They are also great across time, sharing ideas asynchronously, or simply reminding yourself of a previous conversation. </p>
<p>One excellent example of this is a three-day workshop that took place in a San Francisco conference room. On the first day, we covered the whiteboards in notes from discussions about goals, constraints, research insights, and all kinds of conversations. On the second day, we referenced these visuals like a cheat sheet, for example, checking personas while we card-sorted branding and tone aspirations. We did the same while we sketched wireframes, and so on, filling the empty wall spaces with each activity. On the third day, we stood in the middle of our squiggly chaos, sharing feelings of accomplishment and confidence in making intentional design decisions.</p>
<h2>Sketching is Collaborative (even if only one of you has a marker)</h2>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow image--captioned" style="max-width: 1600px;"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-04/verbal-conversation-doodle.jpeg?itok=7PzwaObH" width="900" height="673" alt="Verbal Conversation Sketch" /><figcaption class="image__caption">Relying on verbal conversation alone can obscure disagreement.</figcaption></figure></div>
<p>Last but certainly not least is the ability of sketch-noting to highlight differences in understanding. This is huge, especially during discovery or scope definition while planning; sometimes, everyone thinks they agree, but in reality, people are picturing something different in their heads. For example, as one design meeting began to wrap, we boxed out a chunky wireframe in marker, then asked the group to review the sketch of this "agreed-upon-layout." The quick feedback was enough to show us that we weren't all on the same page quite yet. </p>
<p>Sketching is a powerful collaborative exercise when you can empower everyone on the team; stakeholders, developers, et al., to draw out their ideas, too. Everyone's perspective is vital during blue-sky thinking, to create something with that perfect balance of wonderful and technically feasible. However, the reality is that you often have to match your activities to the comfort level of the group. Sometimes you can solve this, and cover more ground, by splitting up into groups to sketch, each with at least one participant that's confident with a Sharpie. Other times, it's okay to be the only one sketching. You can still empower the team by visualizing their intentions and facilitating progress by being the marker for the group. </p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-04/sketching-collaboratively_0.jpeg?itok=gaUOC2y4 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-04/sketching-collaboratively_0.jpeg?itok=ZGCcM-i- 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-04/sketching-collaboratively_0.jpeg?itok=WcCj6tVM 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-04/sketching-collaboratively_0.jpeg?itok=4n8bAAge 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-04/sketching-collaboratively_0.jpeg?itok=Cz7M3yBw 1440w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-04/sketching-collaboratively_0.jpeg?itok=4n8bAAge" alt="Sketching Collaboratively" /></figure></div>
<h2>Sketching Fits Into Many Project Phases </h2>
<p>Sketching is also versatile. We've found this process especially helpful during initial engagements, when we're getting to know a team and their problems, but have been pleasantly surprised to find value much further along. To protect us all from a long rant about design processes atop a soapbox, let's look instead at a shortlist of recent situations where our team found sketches beneficial: </p>
<ul><li><em>Taking notes (for yourself or others) </em></li>
<li><em>On-site client workshops (facilitating group conversations) </em></li>
<li><em>Goal setting and project planning </em></li>
<li><em>Getting a sense of scope </em></li>
<li><em>Generating ideas (brainstorming and sketch sessions)</em></li>
<li><em>Presenting ideas quickly for feedback (especially if you're choosing from multiple options, or want to show flexibility or variation examples)</em></li>
<li><em>Summarizing important decisions (reducing assumptions and sharing asynchronously) </em></li>
<li><em>Content modeling (illustrating or validating data presentation)</em></li>
<li><em>Wireframing or other layout examples </em></li>
<li><em>UX flows (such as personas, scenarios, interactions, or animations)</em></li>
<li><em>Visualizing quickly with a variety of maps and graphs (sitemaps, Venn diagrams, mind-mapping, user journies, etc.) </em></li>
<li><em>Communicating designs and functionality to development (grooming, ticketing, and flagging risks)</em></li>
</ul><p>Okay, maybe that wasn't particularly short, but it's not exhaustive either. </p>
<h2>Obligatory Disclaimers</h2>
<p>If you do start sketching more, keep in mind that sketching won't suffice for All The Things, so make sure you have support for such a lean artifact. For example, we've found it best to compliment sketched wireframes with a pattern library or other digital documentation for style, to inform design decisions absent from hand-made artifacts. </p>
<p>In other words, express your intentions to gauge the comfort levels of your production teams and clients before diving in. It's so important to plan together and set expectations so that no one is scratching their head during a handoff. Ask what everybody needs, or doodle an example and <em>then</em> ask.</p>
<h2>Live Long and Doodle</h2>
<p>This process is not just for sketching wireframes. We've leveraged this to create user flows, journey maps, data and governance workflows, content relationships, product maps, and more. Get creative about where you might apply this practice to your work or something else in your life. We'd love to see examples if any brave souls want to share!</p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow image--captioned" style="max-width: 1600px;"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-04/ugly-thumbnail-sketch.jpeg?itok=yVS07OU3" width="900" height="673" alt="Ugly Thumbnail Sketch" /><figcaption class="image__caption">An ugly thumbnail sketch is often all that’s needed. </figcaption></figure></div>
<p><em>I'll start. I completed this 14-second sketch on the back of a sheet of stamps while watching a YouTube </em><em>tutorial. It's ugly, my drawing teacher would lose it over my ellipses, but that's fine. Because this drawing was not what I was delivering, instead, it allowed me to quickly and clearly execute the actual deliverable. In this case, it was a Halloween costume I made, for a dog, as the accidental coffee cup from Game of Thrones. Don't worry; it was very cute.</em> </p>
<div> </div>
<div> </div>Wed, 29 Apr 2020 12:38:10 -0400Marissa Epsteinc60629f6-6675-4ff9-aa56-e8b97b59e937When to Upgrade from Drupal 8 to Drupal 9https://www.lullabot.com/articles/when-upgrade-drupal-8-drupal-9
<p>As the release of Drupal 9 approaches, organizations are starting to think about when to upgrade to Drupal 9. Quick Drupal adoption isn't automatic. Historically, it's taken years for some significant Drupal versions to gain traction. With a relatively short window between the Drupal 9 release and Drupal 8's end-of-life, however, organizations must move more quickly to adopt Drupal 9 or make other arrangements.</p>
<h2>No penalty for early Drupal 9 adopters</h2>
<p>A common strategy for many technology release cycles is to avoid the initial version of a major software release. Some organizations wait until one or more point releases after a new version, while others prefer to wait months or even years after a major version release for things like bug fixes, additional features, and helpful resources created by early adopters. In the Drupal world, this delay is often exacerbated by waiting for contributed modules to be compatible with the new version.</p>
<p>The nice thing about Drupal 9 is that there is no penalty for early adopters, so there's no reason to wait for a later version. The initial Drupal 9 version release introduces zero new features. Drupal 9.0 core code matches Drupal 8.9 core. The only differences between Drupal 9.0 and Drupal 8.8 or 8.9 are the removal of deprecated code and required upgrades to third-party dependencies.</p>
<p>The primary consideration is whether or not your favorite contributed modules have declared that they are Drupal 9 compatible. With past upgrades, waiting for contributed modules to be ready for the new Drupal version caused months or even years of delays. But the Drupal 9 upgrade path for contributed modules is relatively easy, so they should be able to adapt quickly. <a href="https://dev.acquia.com/drupal9/deprecation_status" target="_blank">Many modules are already c</a>ompatible, and others will need minimal changes.</p>
<h2>When your code is ready</h2>
<p>One of the core components of the Drupal 9 upgrade is the removal of deprecated code in Drupal 8. However, this means that when you're planning your release window, you'll need to schedule some time for the pre-work of auditing and refactoring deprecated code. If you've already been doing this, you may not need to pad your schedule to compensate for this work. We'll dive deeper into <strong>how</strong> to get your code ready in a future article.</p>
<p>In addition to scheduling time to address code deprecations, you'll also need to give yourself time to address any third-party dependencies that require newer versions in Drupal 9. When you're looking at when to upgrade to Drupal 9, you should do it after you've had a chance to resolve any third-party dependency updates that conflict with other things in your stack. If you've got a contrib module or custom code that requires an older version of a third-party dependency, but Drupal 9 calls for a newer version of that dependency, you'll need to make a plan and address this conflict before you upgrade to Drupal 9.</p>
<h2>Consider other website work</h2>
<p>Many organizations have traditionally used major Drupal version migrations as a time to plan overall website redesign projects, information architecture work, and other web development projects. Because the upgrade to Drupal 9 is more like a minor release than a major one, there's no need to deep dive into information architecture - there's no migration! That means your organization needs to establish a new strategy for these projects; we're working on an upcoming article to cover web development strategy for Drupal 9 for more insights around this.</p>
<p>If business logic dictates that your organization plan other web development projects for this year, make sure you give yourself time to complete the Drupal 9 upgrade before Drupal 8 reaches end-of-life in November 2021. </p>
<h2>Take the availability of preferred partners and development teams into account</h2>
<p>If you're planning to work with vendor partners, make sure you factor their availability into your project plan. With an upgrade window of slightly over a year between the release of Drupal 9 and the end-of-life of Drupal 8, some vendor partners may have limited availability, especially if yours is a larger project. Planning ahead helps to ensure you can work with your preferred partners; otherwise, you might add the stress of working with a new partner into the mix.</p>
<p>At the same time, don't forget about internal initiatives such as serving multiple stakeholders. For example, doing new feature development for content editors while simultaneously maintaining an up-to-date platform consistent with your organization's security policies can mean a dance to prioritize development resources to meet everyone's priorities and deadlines. While this complicates the release planning process, it's essential to consider these factors when determining the timing of upgrading to Drupal 9.</p>
<p>We dipped our toes into these considerations in <a href="https://www.lullabot.com/articles/drupal-8-release-planning-in-the-enterprise" target="_blank"><em>Drupal 8 Release Planning in the Enterprise</em></a>, and hope to release an updated version of this article soon for Drupal 9 release planning.</p>
<h2>Missing the Drupal 9 upgrade window</h2>
<p>To summarize, you should upgrade to Drupal 9 earlier rather than later. But what if your site can't upgrade to Drupal 9 before Drupal 8 reaches end-of-life? Unlike Drupal 7, Drupal 8 does not have an extended support program. The upgrade from Drupal 8 to Drupal 9 is such a minor replatforming effort compared to prior versions that the decision was made not to offer an extended support program for Drupal 8. </p>
<p>Support will continue through November 2021 for sites upgraded to 8.9.x, but support for that version ends when that Drupal 8 end-of-life date arrives. Older Drupal 8.x versions will cease getting support before that date; 8.7.x stops getting security support as of June 3, 2020, and security support ends for Drupal 8.8.x on December 2, 2020.</p>
<p>Long-term, your organization needs a plan to upgrade to Drupal 9, or to consider other options. A future article in this series offers more information about what that plan might look like.</p>
<p><em>Thanks to the Lullabot team for contributing to this article and to Dachary Carey for drafting it.</em></p>Wed, 22 Apr 2020 12:16:50 -0400Karen Stevenson23e8fbd1-5142-4051-ad7c-47add67d549fFostering Inclusion in Tech: Engaging with Staffhttps://www.lullabot.com/articles/fostering-inclusion-tech-engaging-staff
<p>Demographic trends point to the United States population fully transforming into a multi-racial majority by 2044 (<a href="https://www.census.gov/newsroom/press-releases/2015/cb15-tps16.html" target="_blank">United States Census Bureau</a>), and some states already have no majority. All companies, including ours, face the same challenges—evolving to meet the needs of new audiences, new clients, and new board and staff members, and we all seek improved ways of communicating and collaborating when implementing new technologies.</p>
<p>Benefits accrue as teams become more inclusive: </p>
<ul><li>When staff members belong to, contribute to, and take ownership of being a unified, dignified, modern team, the staff benefits.</li>
<li>When clients, supporters, and donors believe in the company's work, the clientele benefits.</li>
<li>When management commits to more inclusion, the business benefits.</li>
<li>When the end product cycles through more diverse viewpoints, the targeted audience benefits.</li>
</ul><p>Benefits arrive in the form of increased opportunities, more client work, and the ability to make a measurable difference.</p>
<h2>Provide mechanisms to address intentions clearly</h2>
<p>If channels do not yet exist, determine which communication methods best address concerns for the makeup of the team. For example, implement an internal newsletter or recurring e-mail from the executive team with regular open Q&A sessions. Lullabot offers a monthly Town Hall in an "ask me anything" style, with any question available to be asked of any executive manager. </p>
<p>Designate a Slack channel, use discussion boards, foster existing Microsoft Office Groups or Google Groups discussions, or put small group sessions into rotation on the calendar, and stick to a schedule.</p>
<h2>Listen and learn</h2>
<p>Do issues already exist? Are ad hoc whispers happening behind closed doors? Determine an appropriate way to listen and truly hear what teammates have to say. Schedule designated time: listening sessions, facilitated discussions, or open conversations. Ensure that executive leaders show up and remain present with respect to potential criticism, harsh opinions, and overwhelming feelings. </p>
<p>Remember to keep ears open and continually educate the team on how to be more empathetic and understanding. At the annual <a href="https://www.lullabot.com/articles/lullabot-team-retreat-2019" target="_blank">Lullabot retreat</a>, our team self-organized to share books and resources related to different technical topics. You can also foster informal or formal mechanisms to spark conversations.</p>
<h2>Lead with fortitude</h2>
<p>Discussions about inclusion often surface hidden assumptions and bring up strong emotions. Leadership teams signal commitment to the importance of keeping these discussions open when the CEO, President, or Executive Team messages all staff with an invitation to scheduled in-house meetings and also offers a follow-up plan — including clear opportunities to give feedback and get involved — for all employees. </p>
<p>Concerns always exist, and conversations can sometimes be fraught: what makes the difference is how leadership decides to deal with those completely understandable concerns.</p>
<h2>Identify a senior leader</h2>
<p>Creating a team or committee works; however, do not make this unpaid labor without a clear directive. Designating a senior leader within the organization as acting or titled head of inclusion efforts makes a difference in signaling the importance of the topic within the organization's culture. Provide this leader with a team and give them the authority to write and implement policies, allocate resources, and investigate issues. Cultivate participants from across the organization to advise the team. </p>
<h2>Formalize how individuals will reach out</h2>
<p>Reduce exposure and encourage greater transparency by determining —and clearly stating — how team members may best communicate their concerns. Let all parties know what to expect regarding any stated issue and its documentable response within a pre-arranged specified timeframe. Leadership teams demonstrate their awareness of the seriousness of potential problems when they clarify processes across the organization, address concerns in a responsible and orderly manner, and follow up continuously. No one wants to be blindsided: foster conversations and expected follow-ups as soon as possible.</p>
<h2>Assign an ombudsperson</h2>
<p>In cases of conflict, an ombudsperson approach may work for the organization. This neutral third-party investigates, collects documents, conducts interviews, and reports personnel issues to leadership. Cases of harassment, bullying, and intimidation require swift review. </p>
<h2>Clearly define and publish expectations of workplace conduct</h2>
<p>A published code of conduct makes expectations clear. When all parties may reference a code of conduct that expressly states the desired behavior, as well as clear consequences for behavior that deviates from the desired conduct, misunderstandings or obfuscations, can be avoided entirely.</p>
<p>Consider publishing the company code of conduct publicly (<a href="https://abc.xyz/investor/other/google-code-of-conduct.html" target="_blank">Alphabet aka Google code of conduct</a>) for greater accountability to staff, shareholders, and other investors, advisors, the media, and communities/clientele served.</p>
<h2>Clarify actions taken in any investigation of harassment</h2>
<p>Harassment requires special mention and guidance (<a href="https://www1.eeoc.gov/laws/types/harassment.cfm" target="_blank">EEOC</a>). Here are checkpoints in a reported case of harassment:</p>
<ul><li>Investigate immediately.</li>
<li>The inquiry must be prompt, thorough, and impartial. </li>
<li>Interview targeted individuals, offending individuals, and witnesses maintaining written documentation of all discussions. </li>
<li>Communicate with targeted individuals regarding steps taken. </li>
<li>Check in to ensure that negative behavior has ceased. </li>
</ul><p>Furthermore, if the investigation reveals that harassment has occurred, take steps reasonably calculated to end the harassment, eliminate any hostile environment, prevent harassment from recurring, and prevent retaliation against the targeted individual(s) or complainant(s). Legal counsel will assist with developing overarching policies for the organization.</p>
<h2>Offer training, support, and continuous education on DEI issues</h2>
<p>Every individual, at all levels of the organization, deserves an opportunity to learn continuously. Paid time off or in-service days to participate in help everyone within the organization get on the same page about presented materials. At Lullabot, we offer ongoing required paid training through Insperity workshops, with annual renewal.</p>
<p>Consider integrating unconscious bias training (here's an example of <a href="https://managingbias.fb.com/" target="_blank">Facebook's online training</a>), mentoring, or coaching into ongoing efforts.</p>
<h2>Support employee resource groups</h2>
<p>Employee-led resource groups formed around shared interests, issues, or background may be a positive way to create a healthy work environment and increase recruitment and retention. By supporting these groups with budget and assistance in setting a charter, determining the mission, vision, values, and infrastructure, and providing office space and time to participate, the company signifies its intent to support employee interests.</p>
<h2>Implement feedback loops</h2>
<p>Designate multiple ways to give feedback, both anonymously as well as named, and determine the team's stated timeframe and methods for responding. Give staff and clients a mechanism to surface complaints, suggestions, or issues, and ensure that the staff knows that raised points will be reviewed, addressed, responded to, and potentially incorporated back into daily practice. <a href="https://feedbacklabs.org/" target="_blank">Feedback Labs</a> offers mechanisms to work on all aspects of the feedback loop cycle.</p>
<p>Consider a mechanism such as a regular "Pulse Survey" to help determine issues by asking scale items such as: "On a scale of 1-10, how likely are you to continue working with us over the next two years?" Or, add the option to choose "Strongly Disagree, Disagree, Neutral, Agree, Strongly Agree" with a statement such as, "I feel like my current position offers work-life balance." Or, present multiple-choice answers to questions such as, "Which benefits do you most value?" Make space and time to process the inputs you receive, then discuss, make recommendations, implement incremental changes, and re-submit the survey in a continuous cycle.</p>
<h2>Conduct baseline surveys, collect and publish data</h2>
<p>A number improves when it's measured. Understand the baseline to better track progress over time and aim for goals that are SMART: specific, measurable, achievable, realistic, and timely. For example, a pulse survey helps determine employee satisfaction, approval of leadership, Net Promoter Score, or demographic data. Pulse surveys over time give insight into how policy changes affect scores.</p>
<p>How well does the team fulfill the needs of individuals living with disabilities, veterans, older adults, women, and minorities? Ask and find out. Consider circulating reports internally, or publishing the data widely (<a href="https://techcrunch.com/tag/diversity-report/" target="_blank">TechCrunch industry diversity report</a>) for additional accountability.</p>
<h2>Match the population</h2>
<p>Review the board and leadership makeup — does staff match the population served? If not, the company may already be "out of touch" with its audience. When any published list of board members, staff, or supporters appears homogenized, clients and supporters may take pause. Make sure public-facing staff represents the desired audience and speaks to, resonates with, and is sensitive to, client and customer needs. </p>
<p>This is an area in which we are continually improving at Lullabot, and where the rate of change is tied to the overall rate of growth for the company overall.</p>
<p>I'm grateful to my colleagues <a href="https://www.lullabot.com/about/james-sansbury" target="_blank">James Sansbury,</a> <a href="https://www.lullabot.com/about/marc-drummond" target="_blank">Marc Drummond</a>, and <a href="https://www.lullabot.com/about/andrew-berry" target="_blank">Andrew Berry</a>, for reviewing this article and for their thoughtful comments and feedback on these suggested tips.</p>Fri, 17 Apr 2020 16:27:19 -0400Monica S. Flores07a46f77-b23f-4a4b-8153-18b4dcd877c7Who Should Upgrade to Drupal 9?https://www.lullabot.com/articles/who-should-upgrade-drupal-9
<p>This article is the first in a series discussing <strong>Who</strong>, <strong>What</strong>, <strong>Why</strong>, and <strong>How</strong> Drupal 8 sites can upgrade to the upcoming Drupal 9 release. In a future series will discuss upgrading Drupal 7 sites.</p>
<p>With Drupal 9 scheduled for release in summer 2020, and with both Drupal 7 and Drupal 8 scheduled for end-of-life (EOL) in November 2021, it’s time to think about whether to upgrade Drupal sites to the new version. Upgrading to newer versions in the past was a significant replatforming effort that required a substantial investment and a non-trivial release window. The Drupal 8 to Drupal 9 upgrade is different, though; this is the first major version upgrade that’s reputed to be as simple as a minor point release. Can it really be that simple? Who should upgrade to Drupal 9?</p>
<h2>The Easy Path: Upgrading from Drupal 8</h2>
<p>Organizations that are already on Drupal 8 are several steps ahead in upgrading to Drupal 9. One of the biggest benefits of upgrading to Drupal 8 is that the platform and core code of Drupal 8 form the basis for Drupal 9. </p>
<p>Drupal 9.0 doesn’t introduce any new features or new code, so sites that are on the final Drupal 8 point release are essentially ready to upgrade to Drupal 9.0. No big lift; no major replatforming effort; no content migration; just a final audit to make sure the site doesn’t rely on any deprecated code or outdated Composer dependencies. </p>
<p>Sites that have kept up-to-date with Drupal 8’s incremental updates (see Andrew Berry’s article <a href="https://www.lullabot.com/articles/drupal-8-release-planning-in-the-enterprise" target="_blank">Drupal 8 </a><a href="https://www.lullabot.com/articles/drupal-8-release-planning-in-the-enterprise" target="_blank">R</a><a href="https://www.lullabot.com/articles/drupal-8-release-planning-in-the-enterprise" target="_blank">elease </a><a href="https://www.lullabot.com/articles/drupal-8-release-planning-in-the-enterprise" target="_blank">P</a><a href="https://www.lullabot.com/articles/drupal-8-release-planning-in-the-enterprise" target="_blank">lanning in the </a><a href="https://www.lullabot.com/articles/drupal-8-release-planning-in-the-enterprise" target="_blank">E</a><a href="https://www.lullabot.com/articles/drupal-8-release-planning-in-the-enterprise" target="_blank">nterprise</a>) should be ready to go when it comes to core code. Many sites are already using automated tools or workflows to keep them up-to-date on code deprecations for contributed and custom modules. Ideally, you have been continuously replacing older versions of contributed modules with versions that have removed deprecated code, removing deprecated code in your custom code, and dealing with any Composer dependency conflicts. If so, the upgrade effort for your site should be relatively simple. The same is true if you rely on widely-used and well-supported contributed modules and have little custom code.</p>
<p>If you have custom code and use less widely-used contributed modules, but you’ve been paying attention to code deprecations in your custom code and the readiness of your contributed modules, you’re probably in a good position to upgrade. If you have strong test coverage and aren’t relying on any deprecated third-party dependencies, you’re in even better shape. You shouldn’t see substantial changes from Drupal 8 to Drupal 9.0, so even custom code is likely to work without issue as long as it doesn’t rely on deprecated functions or methods that are removed. </p>
<p>The caveat is that if your custom code or contributed modules rely on older versions of Composer dependencies that are deprecated in Drupal 9 in favor of newer versions, you may need to do some refactoring to make sure that code works with the new third-party dependencies.</p>
<h2>Can you stay on Drupal 8 past its EOL?</h2>
<p>There should be no reason for anyone on Drupal 8 not to upgrade to Drupal 9. There will be a small window of time until November 2021, during which the last Drupal 8 release will be supported with security updates. That allows time to make the necessary changes to move to Drupal 9. But after that, you’ll need to make the switch.</p>
<p>When Drupal 6 reached its end of life, there was a <a href="https://www.drupal.org/project/d6lts" target="_blank">Long Term Support</a><a href="https://www.drupal.org/project/d6lts" target="_blank"> </a><a href="https://www.drupal.org/project/d6lts" target="_blank">(LTS)</a> program, which made it possible to stay on Drupal 6 past its EOL. There are plans to provide an LTS program for Drupal 7; however, <a href="https://www.drupal.org/core/release-cycle-overview" target="_blank">there will be no Long Term Support program for Drupal 8</a> because the upgrade path from Drupal 8 to Drupal 9 is much easier.</p>
<p>If you don’t make the move, you’ll be on your own to deal with security updates and other maintenance and bug fixes for your Drupal 8 code. And that would likely be more expensive and time-consuming than just doing the upgrade.</p>
<h2>Prepare to upgrade or start considering alternatives.</h2>
<p>With the Drupal 9 upgrade being relatively uncomplicated for sites that are already on Drupal 8, it's easy to recommend that those sites should upgrade. The main question is when, and what other options do you have? Later articles in this series will delve into more detail about how to prepare for the upgrade.</p>
<p><em>Thanks to the Lullabot team for contributing to this article and to Dachary Carey for drafting it.</em></p>Wed, 15 Apr 2020 14:27:27 -0400Karen Stevenson5ad8ffd1-c378-456e-864b-73f81ba370eeLullabot Podcast: #DrupalCares With Dries, Suzanne Dergacheva, and Matt Westgatehttps://www.lullabot.com/podcasts/lullabot-podcast/drupalcares-dries-suzanne-dergacheva-and-matt-westgate
<p>Matt and Mike sit down with Drupal Project Lead Dries Buytaert, Drupal Association Board Member Suzanne Dergacheva, and Lullabot CEO Matt Westgate to discuss the current state of the Drupal Association, the <a href="https://twitter.com/search?q=%23drupalcares&src=typed_query&f=live">#DrupalCares</a> initiative, and the future of DrupalCon.</p>Wed, 15 Apr 2020 01:52:23 -0400Matt Kleve, Mike Herchel9fe245c1-0954-45e7-8e7c-e62d76fd96faNaming Content Types Using a Ubiquitous Languagehttps://www.lullabot.com/articles/naming-content-types-using-ubiquitous-language
<p>The team of editors who populate content on Newspaper, Inc. has asked the development team to add another image field on the articles they write, one that caters to Pinterest sharing. The project manager writes up the ticket, and the development team gets to work. They add the field to the <em>Article </em>content type. They demo it later that week, and everyone seems happy.</p>
<p>Then some complaints start coming in. A few of the editors can't find the field on the articles they are writing. The development team checks to make sure the field is still there and that everyone has the right permissions, but everything checks out. They ask for a demo of the problem.</p>
<p>One editor attempts to create content of the type, <em>News Item</em>. The field, of course, is not there. "That's not an article," the development team explains. </p>
<p>"What? Of course, this is an article!" says the editor, a little frustrated. The development team can't detect the lack of an uppercase letter in the word "article." So, they go on to pedantically explain what Drupal content types are, what a real <em>Article</em> is in terms of development, and all the while, the editor just gets more and more frustrated. This organization could benefit from a shared, ubiquitous language.</p>
<h2>What is a ubiquitous language?</h2>
<p>The term "ubiquitous language" comes from the world of <a href="https://en.wikipedia.org/wiki/Domain-driven_design" target="_blank">Domain-Driven Design</a> (DDD), specifically from <a href="https://amzn.to/2uQmBsv" target="_blank">the book by Eric Evans</a>. Still, you don't have to fully commit to DDD to benefit from some of its recommendations.</p>
<p>A ubiquitous language is a vocabulary shared by everyone involved in a project, from domain experts to stakeholders, to project managers, to developers. This language should be used in all documentation, tickets, conversations, meeting invites, post-it notes on your computer, chalkboards that only appear in your dreams — everywhere. The goal is to reduce the ambiguity of communication.</p>
<p>Ideally, each term that surfaces corresponds to something concrete in the code. When people talk about an event, there is a content type named <em>Event</em> in your Drupal installation, for example. </p>
<p>But don't stop at just content types. The name of a View should also reflect how people actually talk about it. Or a block. If your marketing team consistently calls the email capture block a "lead generation form," think twice before naming it "newsletter sign up" in your code. Your module names should also reflect the vocabulary used by the organization and not just the whims of the development team.</p>
<h2>Driven by stakeholders or domain experts</h2>
<p>The development of a ubiquitous language is an ongoing conversation, but the terms used should be driven by stakeholders and domain experts, and not developers. If your editors think of everything they post on the website as an <em>Article</em>, then there should probably just be a single content type named <em>Article</em>. Taxonomy fields can handle permutations to handle the different contexts, the terms themselves controlled by a subset of the ubiquitous language, such a <em>News Item</em>, <em>Press Release</em>, or <em>Announcement</em>. (Or, leverage some of the techniques listed below to unify the concept in code). But this depends on the organization.</p>
<p>In some places, editors and teams might be used to referring to different types of content by different names, even though they might be similar. In that case, it might make sense to have different content types for <em>News Item and announcement</em>. It might not make the most sense from a technical perspective, but if it eases communication and reduces ambiguity, it makes sense.</p>
<p>When you have a true ubiquitous language, stakeholders and end-users can talk about things <em>in the code</em> with developers, and with some confidence. Conversations can delve deeper. Problems, needs, and solutions will surface with greater clarity.</p>
<h2>Benefits of having a ubiquitous language</h2>
<p>First, clearer communication. <strong>Less ambiguity</strong>. It makes it less likely to have misunderstanding and mismatched expectations.</p>
<p>Second, <strong>a useful shorthand can develop</strong>. Once the ubiquitous language becomes more ingrained, and good habits are established, an enormous amount of information and context is communicated when someone uses the word "article."</p>
<p>Third, people involved become more aware of potential issues when they come up, <strong>creating a beneficial feedback loop</strong>. Developers can push back against terms that are ambiguous or inconsistent. Stakeholders have an extra leg to stand on to insist that developers use terms that they can understand or ask questions about words that seem too awkward to use in everyday conversation. This leads to more back and forth communication that strengthens the shared language and makes it more relevant for everyone. </p>
<p>Naming things is one of the hardest problems in programming. It lets developers offload some of that cognitive effort.</p>
<p>If your editors are continually using the word <em>Lede</em> to refer to the initial summary of an article that appears in lists of articles, it surfaces an opportunity to have a specific field for this concept. In turn, this leads to greater clarity in code and is an opportunity to make the whole system better and more robust. </p>
<h2>We didn't name things right. Or the names evolved. What now?</h2>
<p>Things are rarely right the first time, and in today's development world, iteration is king. So what can you do if the code doesn't reflect the language you are using, and you're afraid the concrete has been drying for too long?</p>
<ol><li>Change the label. While machine names in Drupal can be harder to change, labels are easy. This isn't ideal, because machine names can be used throughout custom code, but at least there will be something concrete on the website, and in the code, that conforms to the ubiquitous language. </li>
<li>Use Decorator functions and classes. If you have several content types that should map to a single concept in your ubiquitous language, like in the example that opened up the Article, <a href="https://www.lullabot.com/articles/maintainable-code-drupal-wrapped-entities" target="_blank">consider using a wrapper class</a>. A wrapper class called <em>Article</em> can encapsulate various content types and add additional functions for specific business logic. It is this wrapper class that is used throughout your code and what maps to the vocabulary used in the ubiquitous language.</li>
<li>A mapping document in your documentation that everyone understands. This helps translate concepts from the ubiquitous language to the code. In practice, this could become just another document that seldom gets updated (the threat hanging over <em>all</em> documentation), and so should be used as a last resort. But it could be helpful in certain situations. Always try to map directly to the code, if possible.</li>
<li>Migration. In some cases, migrating several content types into a single type, so your Drupal installation better matches the ubiquitous language might be worth the time and effort. If you are preparing to make a big development push and need better foundations, or you're migrating from another CMS or to a newer Drupal version, the opportunity might present itself.</li>
</ol><h2>Conclusion</h2>
<p>A ubiquitous language used everywhere throughout your organization, with discipline and intention, has the power to help make your communication easier and your code more maintainable. By mapping the shared language directly to concepts in the code, and in this specific case, Drupal content types, requirements can be made more explicit, and discussions between stakeholders and developers can be more profitable.</p>
<p>Beyond <a href="https://amzn.to/2uQmBsv" target="_blank">Domain-Driven Design</a> by Eric Evans, referenced above, you can also checkout <a href="https://www.amazon.com/Domain-Driven-Design-Distilled-Vaughn-Vernon-ebook/dp/B01JJSGE5S" target="_blank">Domain-Driven Design Distilled</a> by Vaughn Vernon, which is exactly what its title suggests. It contains a good summary of ubiquitous languages.</p>
<p> </p>Wed, 08 Apr 2020 15:56:50 -0400Matt Robison3fcb3f6e-5851-4484-baef-eaefdb0491faClean Drupal Codebase Design with Application Serviceshttps://www.lullabot.com/articles/clean-drupal-codebase-design-application-services
<p>Drupal 8 has allowed and fostered the use of new design patterns through both Drupal core itself and through some well-known contributed modules. Those patterns are generally oriented to dealing with Drupal the framework, as expected. But, what about your business logic?</p>
<p>Sticking to using just core APIs and concepts is enough to get past the coding standards stage in a codebase. Still, you can take it a bit further and have cleaner business logic that better reflects what's going on in your application. That should also help to avoid what is known as the <em>Big Ball of Mud</em>.</p>
<p>With that goal in mind, a few artifacts from <a href="https://en.wikipedia.org/wiki/Domain-driven_design" target="_blank"><em>Domain-Driven Design (DDD)</em></a> were introduced to the Drupal projects. Those artifacts are Application Services and Request/Response Objects. </p>
<h2>Anatomy of an Application Service (AS)</h2>
<p>For the sake of simplicity, application services are defined as the classes responsible for controlling the execution flow of our application. They coordinate entities, repositories, other services of your domain, and other infrastructure services.</p>
<p>Another, more intuitive way, is to say that an AS is essentially a <strong><em>use case</em></strong><em> </em>of the application. If there's a piece of logic that takes some input that is not coming from your domain (e.g., a <strong><em>UUID</em></strong>), does some processing, and persists some data in the database, in one or more entities, that's a candidate to be an Application Service.</p>
<p><strong>DISCLAIMER:</strong> Note that in DDD, there's the concept of Domain Services, which are similar to but not quite the same as AS. To keep this post simple and actionable as a single, small step to simplify a Drupal codebase, know that they exist, and are worth reading about as well.</p>
<p>For example, Drupal's BanIpManager class has the following methods:</p>
<ul><li>isBanned</li>
<li>findAll</li>
<li>banIp</li>
<li>unbanIp</li>
<li>findById</li>
</ul><p>If this were to modeled as custom logic instead of as framework logic, you'd end up creating two different Application Services:</p>
<ul><li>BanIpService</li>
<li>UnbanIpService</li>
</ul><p>And they both would probably use, via Dependency Injection, a <strong><em>BannedIpRepository</em></strong>. </p>
<p>So how do you create an AS? Generally speaking, an AS is a normal class with only the constructor and one single public method. Other methods can be added for internal logic, but the public API will only be one method, like this:</p>
<pre class="lang-php">
<code>class CreateNewClientWithSubgroups {
public function __construct(Dep1Interface $dep1) {
$this->dep1 = $dep1;
}
public function execute(CreateNewClientWithSubgroupsRequest $request): CreateNewClientWithSubgroupsResponse {
// do necessary checks before attempting any db changes.
// throw exceptions as needed.
// Perform changes and return a response object.
return new CreateNewClientWithSubgroupsResponse($data1, $data2, $data3);
}
}
</code></pre>
<p>The constructor method doesn't need an explanation and is used to pass the dependencies of our service. The <strong><em>execute</em></strong> one gets a bit more interesting. Notice that it has only one argument, a <strong><em>CreateNewClientWithSubgroupsRequest</em></strong><em> </em>object, and it returns a <strong><em>CreateNewClientWithSubgroupsResponse</em> </strong>object. These will be covered shortly, but first, look at the benefits of this simple change:</p>
<ul><li>For starters, we're way closer to the "S"<strong> </strong>(Single responsibility)<strong> </strong>of <a href="https://en.wikipedia.org/wiki/SOLID" target="_blank">SOLID</a> than if we had placed this service as a method in a larger class.</li>
<li>As a consequence, our service is likely to have fewer dependencies, making it simpler to test.</li>
<li>With the logic encapsulated, it's easier to understand what's going on during that specific operation that takes place in our domain.</li>
<li>From a structural point of view, and specifically for maintaining codebases over a long period, this makes the codebase more expressive: A developer can open a Orders/ApplicationServices/ directory and see at a glance all the different use cases that can happen in the system for orders.</li>
<li>It's easier to reuse logic from other clients. Imagine a feature that comes up out of an editorial requirement. In most Drupal projects, you'll see the logic for the operation, mixed into the Form class, as part of the submit handler. Next week, a CI job happens to need that feature too, so another developer has to create a Drush command for it. Chances are that code will be copied and pasted in the command file, instead of reused. But if the logic is modeled as an AS in first place, the same service, already in place and tested, can be called from the command because it's no longer coupled with the web UI (Form API).</li>
</ul><h2>Request and Response Objects</h2>
<p>Back in the AS, as mentioned, there's only one public method called <strong>execute</strong>. One significant benefit of that is <em>consistency</em>. Every service or utility (or developer!) making use of AS knows it has the same entry point and is more aware of where to find details regarding what the AS is doing and how it's doing it. Such consistency is particularly useful to make the service work with a <a href="https://barryvanveen.nl/blog/49-what-is-a-command-bus-and-why-should-you-use-it" target="_blank">Command Bus</a>.*</p>
<p>A more important part of the <strong>execute</strong> method signature is its arguments. Generally speaking, you want this method only to accept a request object, and return a response object. That object is essentially an immutable <a href="https://en.wikipedia.org/wiki/Data_transfer_object" target="_blank">Data-Transfer Object</a> (DTO), which by definition, is only used to pass data around. They're generally named after the service itself, with a <em>Request</em> or <em>Response</em> suffix for each one, respectively. A request object would look like this (doc-blocks omitted):</p>
<pre class="lang-php">
<code>class CreateNewClientWithSubgroupsRequest
{
private $clientName;
private $groupName;
public function __construct(string $clientName, string $groupName)
{
$this->clientName = $clientName;
$this->groupName = $groupName;
}
public function getClientName(): string
{
return $this->clientName;
}
// Other getters...
}
</code></pre>
<p>Similarly, the response object would contain the data relevant to the service. Following the example, in this case, it could return the IDs of the subgroups created. Here are a few of the immediate benefits of this:</p>
<ul><li>No more array madness. If you've been around long enough in the Drupal world, you might fancy this. If you need to expand your service to use a new parameter, update the DTO signature.</li>
<li>For complex scenarios where the request parameters are many or the instantiation changes depending on the context, you get encapsulated logic for those scenarios. With objects, you can resort to having multiple static factory methods on the class, and even declare the constructor as <em>private</em> to make sure developers using it look for the appropriate method. For even more complex cases, a factory class to instantiate request objects can be a good choice as well.</li>
</ul><p>The most powerful property of using this approach comes not only with the clarity provided for purpose-specific artifacts but also with the impact it can have on the overall system. Since these DTOs are typed, they can be easily serialized and stored in a database. This makes for a consistent way to track requests for actions that need to happen within your application but are not required to run in the exact moment they're requested. The class <a href="https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Menu%21MenuTreeParameters.php/class/MenuTreeParameters/8.8.x" target="_blank">MenuTreeParameters</a> in Drupal 8 core, for example, makes for a good example of a DTO.</p>
<p>It's worth noting that, generally, it might not be a good idea to have static factory methods to instantiate your classes. However, in this scenario, that is perfectly fine because, as mentioned above, we're just dealing with a DTO, which is <em>purely for storage, retrieval, serialization, and deserialization.</em> In short, we'd be using a <a href="https://verraes.net/2014/06/named-constructors-in-php/" target="_blank">Named Constructor</a>, not to be confused with service instantiation via static factory methods.</p>
<h2>Closing and Trying Things Out</h2>
<p>Two of the main tactical artifacts of DDD, Application Services and Request/Response objects, have been covered. With these tools, you can start to simplify the code of our Drupal projects, and shape them in a way that will not only bring about a more expressive codebase but also scalability and performance improvements, if you choose to go that way. </p>
<p>Architecturally, modeling logic this way is one step forward to decouple it from the Drupal framework. While it might seem of little value if Drupal is the only framework you've worked with, it has a lot of potential benefits as you can eventually get parts of the application that could be placed better in a completely separate application that communicates with the main one via an API, message queues, etc. That allows you to experiment with new frameworks in low-risk areas of your business, or segregate certain logic into separate services that will be maintained by different teams without having to tear the whole thing apart or start from scratch.</p>
<p>If you're curious about how this would look like in your project, try it! Find some business logic that meets any of the following criteria (bonus points if it meets all four):</p>
<ul><li>Is in the submit handler of a Form API class</li>
<li>Is in one of the ever-present Entity API hooks</li>
<li>Is left alone in a Drush command file</li>
<li>Is in a .module file, as a standalone function</li>
</ul><p>After you find that piece of logic, create a separate Service class for it. Remember to make it with only one public method, which receives and returns a Request/Response object. That's it. You're done!</p>
<p>DDD consists of a broader range of concepts and artifacts, which can and should be combined following certain rules. Only a few of them are mentioned in this article, but hopefully, they have sparked some interest in you. If that's the case, you might enjoy some of the existing literature around this topic, with books like <a href="https://www.amazon.com/Domain-Driven-Design-PHP-Carlos-Buenosvinos-ebook/dp/B06ZYRPHMC/ref=sr_1_2?keywords=ddd+in+php&qid=1585552007&sr=8-2" target="_blank">Domain-Driven Design in PHP </a>(recommended if you're just starting)<a href="https://www.amazon.com/Domain-Driven-Design-PHP-Carlos-Buenosvinos-ebook/dp/B06ZYRPHMC/ref=sr_1_2?keywords=ddd+in+php&qid=1585552007&sr=8-2" target="_blank">,</a> <a href="https://www.amazon.com/Domain-Driven-Design-Distilled-Vaughn-Vernon-ebook/dp/B01JJSGE5S/ref=sr_1_1?keywords=distilled+ddd&qid=1585552035&sr=8-1" target="_blank">Domain-Driven Design Distilled</a>, <a href="https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software-ebook/dp/B00794TAUG/ref=sr_1_6?keywords=distilled+ddd&qid=1585552035&sr=8-6" target="_blank">Domain-Driven Design</a>, or <a href="https://www.amazon.com/Implementing-Domain-Driven-Design-Vaughn-Vernon/dp/0321834577" target="_blank">Implementing Domain-Driven Design</a>. </p>
<p>If you're unfamiliar with this pattern, a Command Bus is a way to separate infrastructure actions that are meant to happen alongside the changes the service is performing on the application but are not really part of the domain in which the service is interested. Such actions can be logging of certain events, shutdown processes, opening, and closing database transactions, etc.</p>
<h2>Acknowledgments</h2>
<ul><li><a href="https://www.lullabot.com/about/mateu-aguilo-bosch" target="_blank">Mateu Aguiló</a>, <a href="https://www.lullabot.com/about/juampy-nr" target="_blank">Juampy NR</a>, <a href="https://www.lullabot.com/about/hawkeye-tenderwolf" target="_blank">Hawkeye Tenderwolf</a> and <a href="https://www.lullabot.com/about/matt-robison" target="_blank">Matt Robison</a> for technical review.</li>
</ul><h2>Links</h2>
<ul><li><a href="https://blog.arkency.com/application-service-ruby-rails-ddd/" target="_blank">https://blog.arkency.com/application-service-ruby-rails-ddd/</a></li>
<li><a href="https://enterprisecraftsmanship.com/posts/domain-vs-application-services/" target="_blank">https://enterprisecraftsmanship.com/posts/domain-vs-application-services/</a></li>
</ul>Thu, 02 Apr 2020 09:18:05 -0400Salvador Molina Morenod83f125a-2369-40b9-beed-3449e0230658Working from Home: Going Beyond the Basicshttps://www.lullabot.com/articles/working-home-going-beyond-basics
<p>For those used to working in a physical office, it can be quite an adjustment working from home every day and feeling isolated from your team. Plenty of people have already covered the basics of working remotely, such as the importance of structuring your day, maintaining personal hygiene, and dissecting lists of tools that remote teams use. However, there's always room for bigger-picture questions of strategies to communicate effectively, build morale, and appreciate the humanity in one another. </p>
<p>We've been a distributed company from the beginning and have some well-established practices and strategies that we hope will help you get through the pandemic and improve your productivity while working from home.</p>
<h2>Meetings</h2>
<p>Just because you're not physically in the same room doesn't mean an end to meetings. Here are the types of meetings we have at Lullabot—some of which you'll relate to, and some you might want to start—and how we conduct them as a distributed team.</p>
<h3>Business Meetings</h3>
<h4><strong>Team syncs/scrums </strong></h4>
<p>Moderator-led, small group meeting, 15 to 60 minutes. Typically occurs weekly or multiple times per week. Semi-formal; a moderator goes around the team and asks for updates from each participant. Updates are usually brief; participants list current tasks and any blockers and sometimes recently completed or upcoming tasks when they're noteworthy, or participants need feedback. </p>
<p>After the meeting is over, the moderator releases attendees, except for people who want to participate in after-talks if time permits. The moderator may list specific people required for the after-talk, but other interested parties are welcome to listen and participate. After-talks are conversations that aren't directly related to current tasks but may be of interest to the team, such as strategic ideas or longer-term initiatives. After-talks are not time-sensitive; they can occur when a meeting has not consumed its allotted time but can be bumped to future meetings if time runs out.</p>
<p>For meetings with larger groups, we often start topic threads in the appropriate Slack channel where each meeting attendee adds their updates for all to review on their own time. The time on the call is then used primarily for after-talks, whether it's following up on a specific update or discussing other relevant topics that require attention.</p>
<h4><strong>All-Company Team Calls</strong></h4>
<p>Moderator-led all-company meeting, 60 minutes in length where ~20 members of the organization and the leadership team each give a 2-minute update. These are held weekly on Monday mornings to kick off the week. Updates typically follow the following format:</p>
<ul><li><em>How was your weekend?</em></li>
<li><em>What are you working on this week?</em></li>
<li><em>Optional: Personal updates that an individual speaker feels compelled to share with the team (i.e., life events, recent accomplishments, challenges, etc.)</em></li>
</ul><p>This meeting is part business, part morale, and team building. We use an automated program to select speakers for each call and assign them an order in which to speak. The speakers' names are generated on a rotation-basis to ensure everyone gets visibility, and the all-team format gives people insights into other parts of the organization beyond their immediate department and coworkers. Participants can ask to switch slots with someone if they need to miss part of the meeting, and there's also an alternate list should someone scheduled to speak not be able to make the meeting at all.</p>
<h4><strong>Town Halls</strong></h4>
<p>Moderator-led all-company meetings, 60 minutes long. Monthly. Formal; a moderator invites the leadership team to give updates on the state of the organization or specific strategic initiatives and then leads a Q&A period to surface questions that have been submitted by employees. The call takes place on Zoom, and the questions and conversation take place in Slack in our #townhall channel. In the channel, those with questions pin them so the moderator can easily find them. </p>
<h4><strong>Topic-Specific Meetings</strong></h4>
<p>Small group or all-company meetings, 30 to 60 minutes, depending on the topic. Formal; typically a presentation by one or more individuals on a specific topic. The topics may range from things like a new strategic initiative, an HR-mandated training, or even training on a particular tool that a team is adopting. </p>
<blockquote>
<p>For this kind of meeting, I like to have an agenda with questions and share them on my screen. This helps set the context and share the same language of concepts with everyone. Otherwise, two or three attendees may start discussing something complex, and the rest will lose track of what they are discussing because they don't understand the domain of knowledge well enough.</p>
<div class="author">Juampy NR, Senior Developer at Lullabot</div>
</blockquote>
<h3>Meetings for Morale</h3>
<h4><strong>Coffee Talk & Tea Times </strong></h4>
<p>Small, informal group meetings, typically 30 minutes, but sometimes without a set end time. Whoever is around and interested in a chat can join these meetings and talk about their day, pets, family, video games - whatever's on their mind. These meetings typically occur first thing in the morning or at the end of the day, but this may vary depending on the time zone of the participants; first thing in the morning in Texas is mid-morning for the East Coast. </p>
<h4><strong>Serendipity Calls </strong></h4>
<p>Small group calls, 30 minutes, weekly. We use an automated program to generate small groups of five or six people and a randomly selected moderator that rotates every Friday, and those people join a Zoom call and chat informally. The moderator kicks off the meeting with questions like "how has your week been?" and "what are you up to this weekend?" This call is excellent for building morale, and the rotating nature of the groups means that people routinely get to catch up with people they don't work with every day, which brings us closer together as an organization. It's also a great way to end the week and head into the weekend.</p>
<h4><strong>Co-working hangouts </strong></h4>
<p>Small group meetings with no set start or end time. Sometimes we just open a chat while they're each working on their tasks, only to have that line of communication open with the outside world. For us, this might be on someone's Zoom or Google Meet line, while other organizations maintain devoted virtual office space for these co-working hangouts. Sometimes you just want to hear another human's voice and know people are out there working while you're working on your own.</p>
<h4><strong>Show & Tell</strong></h4>
<p>Optional weekly meeting open to the entire company, consisting of two 30-minute slots devoted to specific topics. A moderator introduces the sessions and records the meetings. The recordings are later posted internally for people who are interested in the topics but unable to attend live. The sessions themselves are presented by individuals or small groups around the specific topic.</p>
<p>While these topics are sometimes business-related, such as a developer sharing a new tool, they can also be completely unrelated, like a team member sharing their knitting skills or how to do hydroponic gardening. It's an opportunity for people to share things they find interesting, and think other people will too. </p>
<h2>Asynchronous Communication</h2>
<p>You'll be doing a lot of asynchronous communication while working from home. While it can take some time to adjust to doing this remotely, there are steps you can take to make this a more seamless experience.</p>
<h3>Agree to a system for status updates.</h3>
<p>Whether you're starting a new project, moving a weekly scrum online, or onboarding a new employee, you need to agree to and communicate a system for status updates. Define asynchronous communication methods for the project or topic, and stick with those methods. This gives everyone clarity on where and how to add updates and locating the latest details of a project's status. </p>
<p>Some things you'll need to define include:</p>
<ul><li><em><strong>Where should people post their status updates?</strong> Slack? GitHub tickets? Jira? A Dropbox Paper document containing meeting notes? Here's <a href="https://www.dropbox.com/paper/customers/lullabot" target="_blank">an article on how we use Dropbox Paper</a> for collaboration.</em></li>
<li><em><strong>How frequently should people post updates?</strong> Daily? On the day of the scrum/meeting? Only when they have blockers? For some teams, daily updates are too frequent, while for others, visibility into current tasks is a must for project planning and allocating resources. Working remotely, more frequent communication is often more helpful than when you see your team in person every day.</em></li>
<li><em><strong>What should people do with questions/comments? </strong>If people are posting updates in a Jira ticket, but someone has a question or comment about an update, should they post it right in the ticket? Or should that conversation happen somewhere else, like in a team Slack channel or via voice chat? Some organizations may want all details and communications recorded directly in the ticket, while others prefer only to communicate relevant updates there, and other conversations happen elsewhere.</em> </li>
</ul><h3>When should you post updates?</h3>
<p>As a general rule of thumb, there are a few points where it's most relevant to share updates on in-progress work.</p>
<p>When the questions are not blockers, post them in the location you established when you set up your system for status updates. These are for people to consider when they have time, asynchronously. If nobody comments on them before the next scrum or team meeting, bring up the question during the meeting.</p>
<p>When questions are blockers, you should handle them via the separate process in place for managing those. Maybe that's a daily check-in meeting where you talk about what you're working on, or perhaps it's reaching out to the team via Slack to get help resolving the question more quickly. Make sure you communicate that the question is a blocker, so other people understand that you've paused your work until it gets addressed. Ideally, you'll have other work you can switch to while waiting for an answer.</p>
<p>If a question is a blocker and the work is time-sensitive, you'll need a system for whom to notify, and how. Maybe the answer is to call another team member first, and then loop in a project manager or other manager if schedules need to be adjusted. In this case, you probably can't wait for asynchronous communication and should move to a meeting or a call.</p>
<p>Finally, share the answer to your question wherever you originally shared the question. This lets people know that the question is no longer outstanding, and captures important details around how the issue was resolved.</p>
<p><span>When you've completed your work, post a status update, either via closing a ticket, requesting a peer review, or sharing an update in the team channel, basically wherever your team established when you agreed to the system for status updates. Sharing a status when you finish your work lets managers see that you're making progress and makes your work visible when your team is not together in person. Being visible while distributed is key to making the arrangement successful, and sharing status updates when you finish tasks goes a long way toward accomplishing that.</span></p>
<h3>When to Move from Asynchronous Communication to a Meeting or Call</h3>
<h4><strong>When you need to speed up or clarify communication</strong></h4>
<p>If a thread includes more than two or three replies and the replies are detailed, a verbal conversation is probably better and faster. It removes the chance of miscommunication, speeds up the resolution time, plus it's a good touchpoint.</p>
<h4>When to move from chat or email to collaborative documents</h4>
<p>When asynchronous communication isn't working, moving to a meeting or a call isn't the only solution; sometimes, it's moving to a different form of asynchronous communication. When discussing a complex topic, for example, it may be easier to write everything down in a Dropbox Paper or Google Doc and then share it with collaborators, asking for comments or updates. </p>
<p>Seeing everything spelled out in document form can be easier than trying to have an abstract conversation about a complex topic, and gives people a place to make changes or leave comments more directly. This gives you a place to iterate together toward consensus and is great for capturing complex concepts. Think of this as being similar to a virtual whiteboarding session, but it can occur asynchronously versus having everyone in the same place at the same time.</p>
<h3>Keeping Up Morale via Asynchronous Communication</h3>
<p>If you use Slack, you know it's a useful tool for business communications, but it's also part of the solution for keeping up morale and team-building in a distributed team. Beyond the morale-building meetings, we also structure Slack channels to provide an outlet and support for our employees, breaking things out into categories like #health, #parenting, #levity, #pets, and even #covid-19. </p>
<p>These channels give workers a chance to have non-work conversations, whether it's sharing a moment of levity during the day, trading tips on parenting challenges, or quarantining COVID-19 discussions to a specific channel, so it doesn't take over the entire Slack. By providing explicit channels for these topics, we also allow other workers to opt-out of conversations they don't want to participate in; simply don't join, or leave, those channels. This enables employees to avoid conversations they're not interested in while giving other people a venue to talk about these topics.</p>
<h2>Being "visible" While Distributed</h2>
<p>Being a distributed organization places more responsibility on the individuals to "stay visible" than when working at a co-located office. Each organization must define what "visibility" means to them. For us, the expectation is logging into Slack to be available to others and documenting your work (or non-work) for the day. </p>
<p>It's essential to encourage proactive and deliberate communication. Individual conversations can get lost, so we try to maintain collective knowledge so that other team members can jump in and continue someone else's work when necessary. This makes the team more agile, giving team members the ability to switch tasks if needed to where their expertise is most valuable. It also helps shore up against unexpected absences.</p>
<p>When communication issues are detected, ask these questions: </p>
<ul><li><em>Have we made expectations and methods of communication clear? </em></li>
<li><em>Does everyone have what they need to be supported and accomplish their work? </em></li>
<li><em>How can we increase visibility, and are additional tools needed?</em></li>
</ul><h2>Tips for Working with Kids at Home</h2>
<p>Even for us remote workers, it's not the norm to have kids running around the house during our workdays. While we have the remote working thing down, we, like many of you, are having to find ways to keep the kids occupied so we can stay productive. Here are some of the ways we're doing this.</p>
<h3>Take turns swapping "parent on duty."</h3>
<p>If you've got multiple adults in the household, take turns swapping "parent on duty." One person gets an uninterrupted block of time to work; maybe this is for focus work or scheduling meetings, while the other is staying with/looking after the kids. Swap every two hours, or establish a cadence that works for your family.</p>
<h3>Close the door.</h3>
<p>When you need to get stuff done, go into a room with a door that closes - and close it. Teach the family that a closed door means "don't interrupt unless there's an emergency," while an open door means "I'm working, but available for interruptions if there's something you need." If you're going to close the door, check with the family and make sure everyone has what they need before you close it, and let them know when you expect to open it again. i.e., "I'm in a meeting for the next hour, but I'll open the door again when it's over."</p>
<h3>Set a schedule for the family.</h3>
<p>Kids need schedules, too. Give the kids blocks of time. 30-minute blocks work well during which they'll do specific activities. A couple of examples of that include <a href="https://docs.google.com/document/d/e/2PACX-1vSZhOdEPAWjUQpqDkVAlJrFwxxZ9Sa6zGOq0CNRms6Z7DZNq-tQWS3OhuVCUbh_-P-WmksHAzbsrk9d/pub" target="_blank">the Khan Academy schedules for school closures</a> and the <a href="https://mommyhood101.com/daily-schedule-for-kids" target="_blank">Mommyhood101 Daily Schedule for Kids</a>. This gives your kids something to do while you're doing your work, and sets expectations for them about when they're expected to do certain things. </p>
<p>You can go one step further and add your schedule to a whiteboard, so kids can see when you're in meetings, and what types of things you might be doing while they're doing their activities. This can be helpful in a multiple-caregiver home; have everyone's schedule in one place, so people can see what the others are doing, when they need uninterrupted focus time, and can take turns tag-teaming the day.</p>
<h3>Establish a cadence for "checking in."</h3>
<p>For younger kids or kids unaccustomed to structuring their own time, this might be every half hour. As the family gets more practice, you might be able to expand that to every hour or every two hours. If you have some upcoming block of time during which you can't be interrupted, communicate that. Let the family know that you've got a 1-hour meeting at noon, but then you'll be available again. </p>
<p>Establishing a cadence for checking in, and communicating that, makes it easier for family members to defer interrupting you; not bothering working family members for an hour is a lot easier than not bothering working family members for an entire day, so family members are more likely to respect that.</p>
<h3>Don't be afraid to admit defeat.</h3>
<p>Sometimes things just aren't getting done. Maybe you're anxious about current events. Perhaps a kid has too much energy and needs an active outlet for a while. Be prepared to accept that some things just are not happening now, and take a break. The work will be waiting when you get back. Maybe you can go back to it after the kids are in bed, or sometime during the evening when everyone is relaxing. A traditional 9 to 5 schedule may not be realistic right now, and that's ok as long as you're communicating that with your team.</p>
<h3>Make fewer demands, but strive to improve.</h3>
<p>Reducing demands and expectations for both kids and adults will help. Maybe you won't show up to that meeting in a dress shirt with makeup fully applied. Maybe the kitchen will look like a bag of flour exploded for a few more hours. Maybe the kids won't be able to sit quietly for an hour if they've never had to do that before.</p>
<p>With practice, both you and your family will acclimate. After a week or so, increase expectations slightly. If the kids are old enough, add housework like dishes and laundry to their schedule. They'll need help and reminders, but they can learn to adapt.</p>
<p>Over time, with experience, communication, and setting and adjusting expectations, your family will get better at working when it's time to work, and enjoying the family time when it's not.</p>
<p><em>Thanks to the Lullabot team for contributing these tips and to Dachary Carey for drafting the article.</em></p>Wed, 25 Mar 2020 14:07:59 -0400Ellie Fanning821b141c-2886-4b9b-af05-4bb4dd2adffcFostering Inclusion in Tech: Hiring Inclusivelyhttps://www.lullabot.com/articles/hire-inclusively-technology
<p>In the <a href="https://www.lullabot.com/articles/fostering-inclusion-tech">previous article of this series</a>, we talked about how fostering diversity, equity, and inclusion (DEI) in an organization is no easy feat. However, there are steps you can take to help get you on your way. When it comes to the hiring process specifically, it's important to hire in the spirit of openness, transparency, accountability, and have a shared vision of what constitutes success for the new position. By offering a welcoming process for applying, organizations attract excellent candidates who participate and collaborate well with the existing mission, vision, and values. As we continue learning how to do this successfully, we've rounded up some tips that might be useful to your organization.</p>
<h2>Evaluate the Job Title</h2>
<p>Look at current job-seeking tools like <a href="https://www.indeed.com/" target="_blank">Indeed</a>, <a href="http://www.glassdoor.com/" target="_blank">Glassdoor</a>, <a href="https://www.idealist.org/" target="_blank">Idealist</a>, to make sure the current job title, description, and range of responsibilities are appropriate and reasonable for that role. Modifying the title or level of the position to match generally accepted standards (i.e., the difference between "Senior Product Manager" vs. "Product Manager" vs. "Project Manager" vs. "Product Associate") may make the difference in who applies. </p>
<h2>Be Thoughtful with Language</h2>
<p>Terms like "rock stars, ninjas, unicorns" do not suffice as descriptive language. Identify the bulleted list of actual skills required, as well as the desired background or experience, and consider the implications of the language. Hiring for a "unicorn" or a "collaborative team player," will receive different responses: the first from unicorns, the second from team players. Matthew Tift, James Sansbury, and Matt Westgate discuss "<a href="https://www.lullabot.com/podcasts/hacking-culture/imaginary-band-drupal-rock-stars-lullabot" target="_blank">The Imaginary Band of Rock Stars at Lullabot</a>" on the Hacking Culture podcast.</p>
<p>Cut out jargon to focus on the required skills and listed responsibilities of the job. If these are not yet clear, re-evaluate the role and its job description, and list out how a person will succeed in the role. </p>
<p>Ruby Sinreich (<a href="http://lotusmedia.org/" target="_blank">http://lotusmedia.org</a>), a web developer, technologist, and strategist who has worked in progressive advocacy organizations and online communities for over two decades, suggests the following tools for minimizing bias within the text (from the Drupal Diversity and Inclusion group):</p>
<ul><li><a href="https://joblint.org/" target="_blank">https://joblint.org</a></li>
<li><a href="http://gender-decoder.katmatfield.com/" target="_blank">http://gender-decoder.katmatfield.com</a></li>
<li><a href="https://textio.com/resources/language" target="_blank">https://textio.com/resources/language</a></li>
</ul><h2>Identify and Make Any Assumptions Explicit</h2>
<p>List all relevant aspects of the position to attract the correct type of applicants and make the implicit assumptions of who can work in this role transparent.</p>
<p>Sample questions to address in the description:</p>
<ul><li><em>Is travel included or required in this job?</em></li>
<li><em>Is there a need to lift heavy objects or crawl under desk spaces?</em></li>
<li><em>Is this a remote job or an on-site job?</em></li>
<li><em>Is the position salaried, contract, temp-to-hire?</em></li>
</ul><p>Include all non-negotiable aspects of the work up front, and be explicit about what constitutes success. For example, a recent job description for a <a href="tugboat.qa">Tugboat</a> Enterprise Account Executive position provided a coherent, attainable measure of success:</p>
<blockquote>
<p>Like anything, we understand it takes a bit of time to ramp up to a new gig. At the end of 6 months, Lullabot will have spent roughly $70,000 in wages for the position, and we'd be looking to come in a little above break-even with this investment. Our minimum expectation is to hit a Monthly Recurring Revenue goal of $20,000 of new business by the end of six months.</p>
</blockquote>
<p>Other questions to ask when measuring success include: Am I enjoying the work? Is the market opportunity substantial? Am I having fun? </p>
<h2>Publish the Pay Range</h2>
<p>Include a pay range and whether or not the role is salaried, temp-to-hire, short-term contract, or a long-term contract position. When you provide a salary range, studies show that this level of transparency <a href="https://www.payscale.com/compensation-today/2015/02/should-you-include-salary-in-job-advertisements-">increases job applications by 30%</a>. After all, no one wants to go through a lengthy hiring process only to find out the role isn't a financial fit.</p>
<p>Consider being clear about salary range, requirements, and perhaps, bands inside the role, and you'll come to a quicker agreement with the final candidate who has understood salary expectations from the beginning.</p>
<h2>Clearly List Benefits</h2>
<p>For many, health, vision, dental, retirement matching, flex-time, parental leave, paid time off, holidays and add-ons like fitness or technology budgets make a job significantly more attractive. At times, it might even be a determining factor. Display listed benefits in the "Work" or "Careers" section, e.g., our annual Events and Education budget is listed publicly on our website, among other benefits we offer.</p>
<h2>Encourage People from Marginalized and Underrepresented Groups to Apply</h2>
<p>Consider adding language that encourages applicants who identify as being from an underrepresented community to apply. It's important to go beyond the standard "equal opportunity" language and will make your job description appeal far more to diverse groups of people.</p>
<h2>Comply with Federal, State, and Local Guidelines</h2>
<p>Make sure the organization complies with any guidelines regarding harassment and discrimination. Here is some sample language about how hiring committees might consider candidates (from Green America's <a href="https://www.greenamerica.org/were-hiring" target="_blank">hiring statement</a>):</p>
<p>All qualified applicants will receive consideration for employment without discrimination regarding actual or perceived</p>
<ul><li><em>race, </em></li>
<li><em>color, </em></li>
<li><em>religion, </em></li>
<li><em>national origin, </em></li>
<li><em>sex (including pregnancy, childbirth, related medical conditions, breastfeeding, or reproductive health disorders),</em></li>
<li><em>age (18 years of age or older),</em></li>
<li><em>marital status (including domestic partnership and parenthood),</em></li>
<li><em>personal appearance,</em></li>
<li><em>sexual orientation,</em></li>
<li><em>gender identity or expression,</em></li>
<li><em>family responsibilities,</em></li>
<li><em>genetic information,</em></li>
<li><em>disability,</em></li>
<li><em>matriculation, </em></li>
<li><em>political affiliation,</em></li>
<li><em>citizenship status,</em></li>
<li><em>credit information, or</em></li>
<li><em>any other characteristic protected by federal, state, or local laws.</em></li>
<li><em>Harassment on the basis of a protected characteristic is included as a form of discrimination and is strictly prohibited.</em></li>
</ul><h2>Focus on the Organization's Culture: Mission, Vision, and Values</h2>
<p>Culture is one of the most significant determinants of whether or not the candidate will continue through with the process of applying. How do you attract high-quality teammates? The current organizational mission, vision, and publicly-stated values make a difference. What does the company, team, or project stand for? Say it loud and proud, and make sure the applicant understands organizational values. A blog post, "About" page, or video linked inside the job application will make values clear.</p>
<h2>Circulate the Listing to Diverse Audiences</h2>
<p>Change up and expand the networks where job listings get circulated. For example, Historically Black Colleges and Universities (HBCUs), remote job boards, or community groups that focused on a specific area, industry, or desired applicant pool, as well as many Slack channels, have job postings. Consider sharing the job post with the following networks first, and then expanding it to general job boards. Some examples include:</p>
<ul><li><em>Ability Links: <a href="https://abilitylinks.org/employers" target="_blank">https://abilitylinks.org/employers</a></em></li>
<li><em>Recruit Disability: <a href="http://recruitdisability.org/" target="_blank">http://recruitdisability.org</a></em></li>
<li><em>Diversity Jobs: <a href="https://www.diversityjobs.com/" target="_blank">https://www.diversityjobs.com/</a></em></li>
<li><em>IMDiversity: <a href="https://jobs.imdiversity.com/" target="_blank">https://jobs.imdiversity.com/</a></em></li>
<li><em>Hispanic/Latino Professional Association: <a href="https://jobs.hlpa.com/" target="_blank">https://jobs.hlpa.com/</a></em></li>
<li><em>Diversity in Tech: <a href="https://www.diversifytech.co/job-board/" target="_blank">https://www.diversifytech.co/job-board/</a></em></li>
<li><em>Native American jobs: <a href="https://employment.nativeamericanjobs.com/jobs/" target="_blank">https://employment.nativeamericanjobs.com/jobs/</a></em></li>
<li><em>Trans JobBank: <a href="http://tjobbank.com/" target="_blank">http://tjobbank.com/</a></em></li>
<li><em>LGBT Job Vacancies: <a href="https://pink-jobs.com/" target="_blank">https://pink-jobs.com/</a></em></li>
<li><em>Out Professional Network: <a href="https://www.outpronet.com/" target="_blank">https://www.outpronet.com/</a></em></li>
<li><em>Recruit Military: <a href="https://recruitmilitary.com/employers" target="_blank">https://recruitmilitary.com/employers</a></em></li>
<li><em>Veteran Jobs: <a href="http://vetjobs.com/" target="_blank">http://vetjobs.com/</a></em></li>
<li><em>Flex Jobs: <a href="https://www.flexjobs.com/" target="_blank">https://www.flexjobs.com/</a></em></li>
<li><em>Former Felons: <a href="https://www.70millionjobs.com/search" target="_blank">https://www.70millionjobs.com/search</a></em></li>
<li><em>Remote Workers: <a href="https://remote.co/remote-jobs/" target="_blank">https://remote.co/remote-jobs/</a></em></li>
<li><em>Remote Bliss: <a href="https://remotebliss.mysmartjobboard.com/jobs/" target="_blank">https://remotebliss.mysmartjobboard.com/jobs/</a></em></li>
<li><em>Women in Product: <a href="http://bit.ly/newjobform" target="_blank">http://bit.ly/newjobform</a></em></li>
<li><em>Women who Code: <a href="https://www.womenwhocode.com/" target="_blank">https://www.womenwhocode.com/</a></em></li>
</ul><h2>Identify Scoring in Advance</h2>
<p>Have a sheet that lists out the evaluation system used when evaluating applicants. If possible, include this in the job description to surface candidates who will be able to speak to the desired points and provide transparency in how they will be scored. In parallel, this procedure works when evaluating RFP respondents; for example, here's a sample <a href="https://docs.google.com/forms/d/e/1FAIpQLSf8a7I2ML-eD9DPTm4STQ9vFB9Wz0_qT6uOP8Fah3KUPf1jSg/viewform?c=0&w=1" target="_blank">questionnaire</a> (in the footer is the scoring mechanism) to evaluate a website redesign. </p>
<h2>Same Interviewers, Same Questions</h2>
<p>To make a fair assessment, have all interviewers ask the same questions of all the finalists. Use the predetermined points system when interviewers compare notes. Evaluate against the organization's stated responsibilities, and cross-check against mission, vision, and values.</p>
<h2>Consider Implementing the Rooney Rule</h2>
<p>The National Football League policy requires league teams to interview ethnic-minority candidates for head coaching and senior football operation jobs. Consider making an effort to interview at least one woman or other underrepresented minority, for the role, to mimic the NFL's results: at the start of the 2006 season, after instituting the Rooney Rule (<a href="http://www.nfl.com/news/story/0ap3000000999110/article/nfl-expands-rooney-rule-requirements-to-strengthen-diversity" target="_blank">definition from the NFL</a>) in 2002, <a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3092443/" target="_blank">the overall percentage of African-American coaches increased to 22%, up from 6%</a>.</p>
<h2>Offer Alternate Ways of Interviewing</h2>
<p>If being successful in a particular role requires a whiteboard walkthrough, 20-minute brainstorming exercise, video or written component, teleconference demonstration, or another method, it is appropriate and understandable to ask for this during the interview process. For example, if the role requires teleconferencing, allow for one of the interviews for the finalists to be held on the teleconferencing software needed. However, don't make these the only mechanisms for evaluation. </p>
<p>Consider offering multiple ways to answer questions to help the team make the best decision. It's also appropriate to ask for an existing portfolio or demonstration of existing products or tools that are relevant to the job. For example, if you're hiring for a designer, asking for a walkthrough of the three design projects for which the candidate is most proud of, is appropriate. </p>
<p>For further reading, there's another in-depth review of the hiring process on MoveOn CTO Ann Lewis's blog, "<a href="https://blog.usejournal.com/how-we-hire-tech-folks-7f36bfec594a" target="_blank">How We Hire Tech Folks</a>." Thanks to <a href="https://www.lullabot.com/about/james-sansbury" target="_blank">James Sansbury,</a> <a href="https://www.lullabot.com/about/marc-drummond" target="_blank">Marc Drummond</a>, and <a href="https://www.lullabot.com/about/andrew-berry" target="_blank">Andrew Berry</a>, for reviewing and providing thoughtful comments and feedback.</p>Fri, 20 Mar 2020 13:05:26 -0400Monica S. Flores064dd7dc-7b8b-471c-a643-1d1874381f8eLullabot Podcast: DrupalCon Europehttps://www.lullabot.com/podcasts/lullabot-podcast/drupalcon-europe
<p>Mike and Matt talk with organizers of DrupalCon Europe about the organization of the conference, COVID-19, and differences between it and DrupalCon North America.</p>Fri, 20 Mar 2020 08:07:54 -0400Matt Kleve, Mike Herchelff2ffd0c-64cc-4ecd-9f6f-cc4817056010Using Drupal in a Pandemichttps://www.lullabot.com/articles/using-drupal-pandemic
<p>One of the founders of Lullabot and former CEO, Jeff Robbins, used to joke that Lullabot has "built-in disaster recovery" because the employees are accustomed to working from just about anywhere. Lullabot, one of the first Drupal consulting companies, <a href="https://www.lullabot.com/articles/the-genesis-of-lullabot" target="_blank">started</a> in 2006 after Matt Westgate and Jeff Robbins met on Drupal.org. Drupal has been at the heart of Lullabot's work for more than 14 years, and the core of what Jeff suggested could apply similarly to the Drupal community.</p>
<p>As each of us negotiates a world where COVID-19 dominates the headlines and our everyday interactions, this article considers how some of the lessons that the Drupal community—perhaps an <em>idealized</em> Drupal community—has learned might shape our understanding of these times that feel so extraordinary. Drupal does not have a monopoly on any of these concepts, but in stressful times, similes and metaphors can help us interrogate our underlying assumptions and the communities that we have each <a href="https://www.lullabot.com/articles/the-cultural-construction-of-drupal" target="_blank">constructed</a>.</p>
<h2><strong>You Don't Have to Do Anything</strong></h2>
<p>Free software communities thrive when people contribute in the ways that feel comfortable to them rather than out of guilt. People <a href="https://dri.es/who-sponsors-drupal-development-2019" target="_blank">support</a> the Drupal community in a wide variety of ways, and we encourage people who choose to contribute to the project to <a href="https://www.drupal.org/about/values-and-principles#be-sure-to-have-fun" target="_blank">have fun</a> and enjoy the process of contributing. Sometimes this means the best choice is to take a step back and not contribute at all. The Drupal community is huge, with nearly <a href="http://www.drupalcores.com/" target="_blank">5,800</a> contributors to Drupal core alone, and it's okay for people to pause once in a while—or altogether—and let others step forward.</p>
<p>As COVID-19 spreads through the world, and the world works together to slow the progress, sometimes the best option is for us to stay home. This recommendation goes against the natural human urge to fix things, but we can bring to mind the fact that, with Drupal and viruses alike, we simply can't fix everything. No one of us can "fix" the more than 95,000 <a href="https://www.drupal.org/project/issues/drupal?status=All&categories=All" target="_blank">open issues</a> in Drupal core any more than we can "fix" the very real devastation caused by COVID-19. You can contribute, or you can do nothing, and the world will continue without you. There is no reason to feel guilty about taking a break and pausing to examine what is important to your life.</p>
<h2><strong>Honor Your Family</strong></h2>
<p>Historically, the Drupal community has supported people who have needed to take a break and focus on themselves or their families. From daily interactions to the highly-visible gestures of support, such as when <a href="https://www.drupal.org/node/2444367" target="_blank">Aaron Winborn</a> needed it, members of the Drupal community have offered countless acts of kindness.</p>
<p>In a recent example from just weeks ago, before most of us had ever heard of COVID-19, our friend, colleague, and long-time Drupal contributor, <a href="https://www.drupal.org/u/sirkitree" target="_blank">Jerad Bitner</a>, needed help after his wife received a <a href="http://jeradbitner.com/" target="_blank">diagnosis</a> of Stage 4 Brain Cancer. Jerad and his family have received assistance from people in all areas of their lives, and it was especially heartening to see so many people from the Drupal community among the impressive list of <a href="https://www.gofundme.com/f/jenney-and-jerad-bitner" target="_blank">supporters</a>.</p>
<p>While the Drupal community may seem like it exists and organizes itself primarily on the web, in a "socially distant" manner, it can present itself in very human and sincere ways when our members need assistance. Take the time to focus on yourself and your family during this period of uncertainty and take comfort in the fact that the Drupal community has a remarkable capacity to support its members in times of need.</p>
<h2><strong>Get Off the Drupal Island</strong></h2>
<p>Especially since Drupal 8, the Drupal community has learned about the benefits of drawing from other communities. When we partner with others "<a href="https://groups.drupal.org/node/140144">off the island</a>," we can save ourselves a lot of work.</p>
<p>Likewise, we can take what we have learned to help others. For those of us with the good fortune to have a job working for one of the many Drupal agencies with "built-in disaster recovery," we have a unique chance to help others. We can use our technical knowledge of online collaboration tools, microphones, cameras, and more to act as resources to those with less technical experience.</p>
<p>We don't have to go far to get off the "island." All around the world, meditation centers, yoga studios, churches, synagogues, and other places people seek during stressful times are scrambling to transform their services models and move them online. We in the Drupal community have an opportunity to volunteer our skills and knowledge to support organizations like these, both non-profits and for-profits. These are not business opportunities, but rather opportunities to help our neighbors. We can share our recommendations about open-source options for collaborating online, such as <a href="https://obsproject.com/" target="_blank">OBS Studio</a> and <a href="https://jitsi.org/jitsi-meet/" target="_blank">Jitsi Meet</a>. Perhaps our station in life allows us to donate money to organizations that need help, such as the food shelves that provide food and groceries to kids in areas where schools have closed.</p>
<p>Or we can put our Drupal skills directly to use. For instance, you might feel especially appreciative of your local public media organization for bringing you impartial news at this time. Many public radio and television <a href="https://groups.drupal.org/node/78273" target="_blank">stations use Drupal</a>. Without needing access to their entire codebase or infrastructure, you could ask them if any contributed modules need features they will use, bugs they need fixing, or other ways to help that match your skillset.</p>
<p>Because of the prevalence of Drupal among non-profits, non-governmental, and community organizations, there are many opportunities to contribute directly to local organizations <a href="https://groups.drupal.org/drupal-for-good" target="_blank">doing good</a>. Now that seemingly every in-person conference, vacation, user group, and other regular meetings on the schedule have been canceled, we might be looking for activities to fill those hours. The chances are high that organizations and businesses in our communities -- the ones important to our daily lives -- are struggling to find a way forward, and they might welcome unsolicited offers to help.</p>
<h2><strong>We don't need rock stars</strong></h2>
<p>In both the Drupal community and our local communities, our capacity to bring about change can feel limited. Anyone who has contributed to Drupal likely knows that the process of getting things done in the community can sometimes take a lot of time, effort, and discussion. We progress one <a href="https://www.drupal.org/patch" target="_blank">patch</a> at a time. Often it would be much simpler just to make a change to fit one specific use case, but in the Drupal community, we have learned that we need to work together and create consensus around ideas. We realize that we are stronger together and that sharing code feels so much better than hoarding code. Getting code into Drupal is rarely about maximizing revenue, but rather contributing to something bigger.</p>
<p>During a pandemic, the same mindset applies. Social distancing might feel challenging, but it's an act of compassion that benefits others. We help in the ways that feel genuine, not forced. We don't need <a href="https://events.drupal.org/seattle2019/sessions/imaginary-band-drupal-rock-stars" target="_blank">rock stars</a> and hoarders. We need just enough people to work toward more manageable, short-term goals. Thus, joining a group rather than going it alone can help make your otherwise small contributions feel more significant.</p>
<h2><strong>Do Your Homework</strong></h2>
<p>Through experience working on Drupal sites, we realize that many of the problems we face are already solved. We don't assume every problem is a bug in Drupal core. We don't assume that the problem with our Drupal site is unique. We encourage the person with a question to "<a href="https://www.drupal.org/forum-posting#homework" target="_blank">do your homework</a>." We look for others who have encountered similar problems and learn from those who are kind enough to share their solutions.</p>
<p>The argument that we live in exceptional times, while accurate in the short term, does not reflect a broader view of history. Everywhere in human history, people have been affected by violence, war, injustice, widespread fear, and, yes, disease. Our seemingly exceptional problems, which cause real suffering, are variations on similar historical problems. The <a href="https://www.cdc.gov/flu/pandemic-resources/1918-commemoration/1918-pandemic-history.htm" target="_blank">1918 flu pandemic</a>, for instance, killed 50 million people. Understanding and connecting to past events can help reduce the sense of exceptionalism that we all feel. In history, we find people who overcame fear and redirected their focus from helping themselves to helping others. As we become more socially distant in this current reality, we can connect to people online and in the past that have encountered problems like ours. We can also see how problems in the past always come to an end, even if they reappear again.</p>
<h2><strong>Your Code Won't Last Forever</strong></h2>
<p>The Drupal codebase and community, like everything else in the world, changes constantly. Our prized contributions get replaced. With software, it can be easier to accept the fact of change. We have learned that the point in time when we know everything about Drupal will never arrive. For as long as it can take to get a patch into Drupal core, it can simultaneously feel like Drupal moves at a breakneck speed. The list of completed and in-process <a href="https://www.drupal.org/about/strategic-initiatives" target="_blank">strategic initiatives</a> just for Drupal 8 is long, and Drupal 9 will arrive before we know it. We have learned to accept the fact that we need to learn continually, all of our contributions to Drupal will eventually be replaced, and change in the Drupal community is inevitable.</p>
<p>Similarly, the cozy worlds that some of us had grown to inhabit now feel threatened. We live in a society that rarely admits the inevitability of sickness and death, and yet both are guaranteed in life. The world, like Drupal, is always changing, and after our initial reactions begin to subside, we can choose how we respond to these ever-changing circumstances. We will each find our way to negotiate these always-changing realities. Some days will be sunny, and others will not.</p>
<h2><strong>Ask For Help</strong></h2>
<p>The current state of reality might feel overwhelming, but in the Drupal community, our response is to encourage people to ask for help. The Drupal software can feel like a complex, unknowable beast. We have learned to find others with more knowledge in a particular area than we do. We practice acts of kindness when we first look for answers by ourselves before asking others for help. Sometimes we work really hard on a problem and do everything we can before we "bother" another community member. In the Drupal community, we regularly practice a version of "social distancing" out of respect for the other people in our community. But at some point, we must ask for help, and significant relief can follow when the recipient of our question seems happy to offer assistance.</p>
<p>As we find our way through this new (and temporary) reality, we have many options: do nothing, offer help, connect with friends and family, connect our experiences with historical events, dig into the Drupal codebase, ask for help when necessary. None of these responses is incorrect. We can <a href="https://www.lullabot.com/articles/imagining-drupal" target="_blank">imagine</a> the ways that Drupal can help. Even better, we can stop merely imagining better worlds and embrace this reality by finding activities, words, and thoughts that reduce our struggles and the struggles of the people around us. When you notice that something you are doing is not helpful, consider shifting your efforts. The Drupal software will continue to evolve, and we can, too.</p>Wed, 18 Mar 2020 14:51:29 -0400Matthew Tift6333a26e-443d-41ba-a89e-aed1f5cd5eaaSocial Share Links Are (Probably) Spying On Youhttps://www.lullabot.com/articles/social-share-links-are-probably-spying-you
<p>We've worked on countless websites that have social media sharing functionality. You know, those little links that let you easily post to Facebook, Twitter, or some other social network?</p>
<p>These widgets work by requiring a developer to embed a script tag on their site. Like this:</p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-03/screen-shot-2020-03-11-at-11.15.47-am.png?itok=F3O4i6yk" width="900" height="111" alt="Twitter Embed Script" /></figure></div>
<p>By embedding JavaScript from a third-party source, you've allowed that provider to modify the content of your HTML page. Putting concerns aside about what could go wrong (XSS attacks, unexpected manipulation of the page, JavaScript execution errors), and just focusing on what <em>is</em> happening is a reason to avoid most approaches to share links.</p>
<p>When you embed a share widget on your site, you've added tracking by that social network. Now social networks can associate each visitor’s profile with the content that is on your page. Social networks, and Facebook, in particular, use that to build an advertising profile based on your content.</p>
<p>A hypothetical example: If your site provided medical or self-help advice, the share widget on the page loads JavaScript from Facebook. Like many, visitors to your site are always logged into Facebook even if they don't have it open. When the JavaScript is loaded, it knows the profile of the user—and how it shows when you've liked or retweeted something. The JavaScript can then check the URL of the page, which Facebook can then index. Facebook can associate the content of the page with the user's profile. And finally, Facebook can now show advertisements targeting the medical condition of visitors to your page. And, that's all just from the site visitor <em>looking</em> at your content. This requires <em>no interaction</em> with the Facebook share widget; the mere act of loading the widget is enough to associate the widget with the content of your site.</p>
<h2>Combined Share Widgets Can Be Even Worse</h2>
<p>An alternative to using the direct widgets provided by social networks are those created by other providers that <em>wrap around</em> social media links. Examples include AddThis, ShareThis, AddToAny, Shareaholic, and many others. However, this further compounds the problem. Not only are Facebook and Twitter tracking your visit, but so is the provider of the sharing widget.</p>
<p>For example, in the privacy policy of <a href="https://www.oracle.com/legal/privacy/addthis-privacy-policy.html" target="_blank">AddThis</a> (which is owned by Oracle) states:</p>
<blockquote>
<p>Publishers provide us with AddThis Data so that we can build Segments and Profiles to facilitate personalized interest-based advertising for you by Oracle and our Oracle Marketing & Data Cloud customers and partners. By installing the AddThis Toolbar, Toolbar Users provide consent for us to use their AddThis Data for interest-based advertising.</p>
</blockquote>
<p>Using a centralized share provider has only introduced another aggregator and broker of people's interests. Not all services are equally bad, but be sure to carefully read the terms of service when using any of these providers. Note in most cases, using one of these widgets will also load the SDKs for each enabled social network to count engagement such as likes, retweets, etc.</p>
<h2>Alternatives and Suggestions</h2>
<p>The absolute best thing an organization concerned with privacy can do is not include any share links at all. That would avoid any direct connection between your visitors and data aggregators. However, for many clients, designers, and visitors, having some share capabilities is expected. What can developers do to meet the requirements <em>and</em> be responsible for user data?</p>
<p>The answer is pretty simple. Use links. Each social network has a simple URL that you can use to prepopulate a sharing form with the URL of your content. At its simplest, these links look something like this:</p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-03/screen-shot-2020-03-11-at-11.19.37-am.png?itok=x2ihAu6M" width="900" height="87" alt="HTML Social Link for Social Sharing" /></figure></div>
<p>No JavaScript. Just HTML.</p>
<p>Be sure to include the <code>rel</code> attribute to <a href="https://developers.google.com/web/tools/lighthouse/audits/noopener" target="_blank">prevent the third-party site from being able to manipulate the browser history</a>. And using <code>target = "_blank"</code> opens a new window, so the user doesn't immediately leave your page.</p>
<p>This provides a happy middle ground where sharing is still available for users, but it makes it impossible for social networks to track users simply visiting the page. Once the user clicks/taps on the share link, then they're consenting to use those social networks (and thus be tracked and profiled).</p>
<p>Copy/paste example services that don’t include any JavaScript can help with generating these links, see the following sites for examples:</p>
<ul><li><em><a href="https://sharingbuttons.io/" target="_blank">https://sharingbuttons.io/</a></em></li>
<li><em><a href="https://stevenwestmoreland.com/2018/07/creating-social-sharing-links-without-javascript.html" target="_blank">https://stevenwestmoreland.com/2018/07/creating-social-sharing-links-without-javascript.html</a></em></li>
</ul><p>As of this writing, you can also check out the share links here on Lullabot.com, which uses a combination of these direct-share URLs with lightweight JavaScript to open in a sized new window.</p>
<p>Although privacy is starting to become a focus for the general public, many users still may not realize that their browser is logged into social networks <em>all the time</em>. Websites big and small, then facilitate the tracking of users by loading JavaScript from these social networks, resulting in extensive profiling based on the viewed content used to create targeted advertising. </p>
<p>Share links are often privacy trojan horses. As the builders of the web, we should take care to account for the privacy of our site visitors. So the next project you're on, advocate for a non-tracking solution.</p>
<p> </p>Wed, 11 Mar 2020 11:35:12 -0400Nate Lamptoncc10b1bc-7a8c-4c8f-8583-28413ffb032fFostering Inclusion in Techhttps://www.lullabot.com/articles/fostering-inclusion-tech
<p><span><span><span><span><span><span>Working with multifaceted and diverse teams to solve complex issues is a part of everyday life at Lullabot. Therefore, becoming stronger, more empathetic communicators who foster diversity, equity, and inclusion (DEI) across the organization is something we’re striving for continuously. That said, DEI is a tough nut to crack, and we’re a work in progress. Like many organizations, we’re constantly asking ourselves, “How do we better foster a sense of inclusion and allow for different types of people, with varied abilities and skills, to work together to solve problems for the future?”</span></span></span></span></span></span></p>
<p><span><span><span><span><span><span>A group that has the benefit of an inclusive environment will:</span></span></span></span></span></span></p>
<ul><li><em><span><span><span><span><span><span>be more agile and culturally competent, and</span></span></span></span></span></span></em></li>
<li><em><span><span><span><span><span><span>be able to work with a variety of viewpoints, carefully considered, toward building a more thoughtful, and hopefully better, end product or service.</span></span></span></span></span></span></em></li>
</ul><p><span><span><span><span><span><span>We're learning how to improve internal communication and hold space for each other as we dive into these types of conversations. This series is a compilation of some tips we’ve collected through our continuing work, and we encourage you to share your own. </span></span></span></span></span></span></p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide image--captioned" style="max-width: 3377px;"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-03/tech-womxn-event.jpg?itok=6-mMaNSy 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-03/tech-womxn-event.jpg?itok=QRO7WXWy 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-03/tech-womxn-event.jpg?itok=P7YwfIJl 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-03/tech-womxn-event.jpg?itok=iNwtL09D 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-03/tech-womxn-event.jpg?itok=rp-DIbVS 2600w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-03/tech-womxn-event.jpg?itok=iNwtL09D" alt="Tech Womxn event sponsored by Lullabot" /><figcaption class="image__caption">Lullabot-sponsored networking event kicking off TechWomxn.com.</figcaption></figure></div>
<h2><span><span><span><span><span><span>Make a Plan to Foster Inclusion</span></span></span></span></span></span></h2>
<p><span><span><span><span><span><span>While we consider ways to build up teammates and their sense of belonging in the job, we also desire each individual’s highest and best level of participation in a shared mission. </span></span></span></span></span></span><span><span><span><span><span><span>Our team, as well as other knowledge workers, are becoming increasingly aware of the ability to leverage the extreme potential and power of technology to expand ideas, increase access to opportunities, and level the playing field.</span></span></span></span></span></span></p>
<h3><span><span><span><span><span><span>Be Explicit</span></span></span></span></span></span></h3>
<p><span><span><span><span><span><span>If an organization does not have a policy publicly stated, proactively, in the marketplace, others may have already crafted a narrative about mission and values without management’s participation. Be explicit about where the company stands. Make clear what’s important to the company, both internally and externally. We share our</span></span></span></span></span></span><a href="https://www.lullabot.com/values"><span><span><span><span><span><span> </span></span></span></span></span></span><span><span><span><span><span><span><span><span>Mission and Core Values</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span> on our website and encourage clients and job applicants to review them, and are continuously finding ways of infusing these into our workflows and culture to ensure our team is living up to these ideas.</span></span></span></span></span></span></p>
<h3><span><span><span><span><span><span>Start Now and Build into the Schedule</span></span></span></span></span></span></h3>
<p><span><span><span><span><span><span>Who represents, guides, leads, and makes decisions for the company? Take a picture of the board or staff—who’s in the photo? Evaluate who’s who and identify whose voices the organization has chosen to elevate, increase, and honor.</span></span></span></span></span></span></p>
<ul><li><em><span><span><span><span><span><span>Are voices missing? If so, why?</span></span></span></span></span></span></em></li>
<li><em><span><span><span><span><span><span>What opportunities exist for all staff to increase their skills and advance?</span></span></span></span></span></span></em></li>
<li><em><span><span><span><span><span><span>What opportunities exist for people traditionally excluded from work?</span></span></span></span></span></span></em></li>
<li><em><span><span><span><span><span><span>In which ways may new people participate as staff, interns, apprentices, consultants, or vendors? </span></span></span></span></span></span></em></li>
<li><em><span><span><span><span><span><span>For any of the above, what’s the tracking mechanism? Free and low-cost tools exist to help you collect, analyze, report on, and share data to help with decision-making.</span></span></span></span></span></span></em></li>
<li><em><span><span><span><span><span><span>Is there a transparent way to demonstrate how staff may advance, either within their career track or if they'd like to switch to a different path such as management or sales?</span></span></span></span></span></span></em></li>
<li><em><span><span><span><span><span><span>Whose voice is missing? Who is not in the room? Representation matters.</span></span></span></span></span></span></em></li>
</ul><h3><span><span><span><span><span><span>Incorporate Inclusion as a Guiding Value, and Put It into Practice</span></span></span></span></span></span></h3>
<p><span><span><span><span><span><span>While it’s great to have ideas, implementing them is some of the most difficult and demanding work. It’s best to start where you are and incrementally improve.</span></span></span></span></span></span></p>
<p><span><span><span><span><span><span>Consider determining three tasks to implement in the next quarter to increase the types of voices you currently have represented, and evaluate every 8-12 weeks how you’re doing, what’s working, what’s not working, and where you want to invest strategically. The Drupal Diversity and Inclusion group offers </span></span></span></span></span></span><a href="https://www.drupaldiversity.com/get-involved"><span><span><span><span><span><span><span><span>weekly accountability, support, and connections</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span> on initiatives across the Drupal community. Internally, our newly-formed Inclusion and Equity working group is discussing governance, goals, and how to move forward with our efforts toward greater inclusion.</span></span></span></span></span></span></p>
<h3><span><span><span><span><span><span>Respect Autonomy</span></span></span></span></span></span></h3>
<p><span><span><span><span><span><span>The best work does not always happen in an assigned cubicle, hot desk, or office. An individual might be better suited to doing focus work in the morning, taking a siesta break, then picking up again after dinner and going to midnight. Some of our teammates need to make day-by-day, non-scheduled arrangements for childcare, eldercare, medical appointments, and other issues, and much unpaid labor falls on stay-at-home workers. When a new team member requests to be on-call for a specific block of time, how does a company make it work? A flexible schedule (learn about my colleague Sean Lange’s</span></span></span></span></span></span><a href="https://www.lullabot.com/articles/how-working-at-home-works-for-us"><span><span><span><span><span><span> </span></span></span></span></span></span><span><span><span><span><span><span><span><span>routine</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span> while working from home) allows our teammates to more equitably participate in the workforce and bring their best ideas to us when they’re ready to do so. </span></span></span></span></span></span></p>
<p><span><span><span><span><span><span>Respect staff’s autonomy and ability to choose and set their hours, and encourage a culture of high expectations as well as high performance. Clearly define deliverables and the practicalities of team needs (such as deadlines for when certain projects need to launch), but allow the team to determine the best way for them to deliver, rather than forcing people to adhere to an inflexible work arrangement where they clock in and immediately tune out.</span></span></span></span></span></span></p>
<p><span><span><span><span><span><span>Practically speaking, workforce trends for remote work are increasing: 26 million employed persons worked at home on an average day in 2018 (</span></span></span></span></span></span><a href="https://www.bls.gov/news.release/atus.t06.htm"><span><span><span><span><span><span><span><span>Bureau of Labor Statistics</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span>), and there are increasing numbers of positions that offer telework, telecommuting, and work-from-home options. Generation Z, which is predicted to</span></span></span></span></span></span><a href="https://weworkremotely.com/remote-work-in-2019-what-the-trends-insights-and-predictions-say"><span><span><span><span><span><span><span><span> become 36% of the global workforce in 2020</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span>, is comfortable with technology to make conferences, meetings, and training sessions seamless regardless of location. </span></span></span></span></span></span></p>
<p><span><span><span><span><span><span>We’re a 100% distributed company, where all workers are remote, and there is no centralized office. As everyone works across multiple time zones, we’ve continually experimented with practices that foster community and connection.</span></span></span></span></span></span><a href="https://www.lullabot.com/articles/what-is-a-distributed-company"><span><span><span><span><span><span> </span></span></span></span></span></span><span><span><span><span><span><span><span><span>Read more about being a distributed company</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span>. Based on client desires and existing systems, we use Zoom, Uberconference, Google Meet, Webex, and Slack, among others, for conferences.</span></span></span></span></span></span></p>
<h3><span><span><span><span><span><span>Support Mental Health and Wellness</span></span></span></span></span></span></h3>
<p><span><span><span><span><span><span>The bulk of staff time is spent in the workplace or doing work-related activities. Burnout, stress, depression, anxiety, and mental health disorders directly impact teammates, colleagues, and clients. The American Institute of Stress</span></span></span></span></span></span><a href="https://www.stress.org/workplace-stress"><span><span><span><span><span><span> </span></span></span></span></span></span><span><span><span><span><span><span><span><span>survey</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span> shows 40% of workers reporting that their job is “very or extremely stressful” and 80% of workers report feeling stress on the job (with almost half saying they “need help in learning how to manage stress”). Lost productivity resulting from depression and anxiety is estimated to cost the global economy</span></span></span></span></span></span><a href="https://www.who.int/mental_health/in_the_workplace/en/"><span><span><span><span><span><span> </span></span></span></span></span></span><span><span><span><span><span><span><span><span>US$ 1 trillion each year</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span>, according to the World Health Organization.</span></span></span></span></span></span></p>
<p><span><span><span><span><span><span>Over the last year, we have made mental health a priority: learn more about</span></span></span></span></span></span><a href="https://www.lullabot.com/articles/supporting-mental-health-lullabot"><span><span><span><span><span><span> </span></span></span></span></span></span><span><span><span><span><span><span><span><span>Supporting Mental Health at Lullabot</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span>. Three best practices to support reducing stress and increasing mental health at the workplace include:</span></span></span></span></span></span></p>
<p><span><span><span><span><span><span>1) </span></span></span></span></span></span><span><span><span><strong><span><span>Avoid overscheduling your team: offer flexible work arrangements.</span></span></strong></span></span></span><span><span><span><span><span><span> For example, we work a 30-hour billable week and have 10 additional hours for contributing back to the company and community.</span></span></span></span></span></span></p>
<p><span><span><span><span><span><span>2) </span></span></span></span></span></span><span><span><span><strong><span><span>Create an open and relaxed work environment with access to management and ongoing feedback loops.</span></span></strong></span></span></span><span><span><span><span><span><span> For example, we use the Know Your Team tool (</span></span></span></span></span></span><a href="https://knowyourteam.com/blog/podcast/episode-49-interview-with-matt-westgate-ceo-and-co-founder-of-lullabot/"><span><span><span><span><span><span><span><span>podcast</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span>) to organize constructive one-on-ones. Other activities, including scheduled coffee talks for drop-in support and advice, a weekly team call, small group calls on Fridays, a town hall with leadership every month, multiple Slack channels for conversations ranging from #being-human to #cats to #parenting, and a back-end “Daily Report” for internal news and reports.</span></span></span></span></span></span></p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide image--captioned" style="max-width: 1024px;"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-03/cat-collage.jpg?itok=hK5trLXf 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-03/cat-collage.jpg?itok=mAA3yExr 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-03/cat-collage.jpg?itok=eD13Tzdv 800w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-03/cat-collage.jpg?itok=u1SubqNL 1024w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-03/cat-collage.jpg?itok=lRdjtiQn" alt="Cats posted in Lullabot Slack channel, #cats." /><figcaption class="image__caption">Cats posted in Lullabot Slack channel, #cats.</figcaption></figure></div>
<p><span><span><span><span><span><span>3) </span></span></span></span></span></span><span><span><span><strong><span><span>Increase education. </span></span></strong></span></span></span><span><span><span><span><span><span>For example, offer access to mental health and general health and wellness topics, and provide training and development opportunities. With continuing education and professional skill-building, teams have documentable ways to increase productivity and overall experience.</span></span></span></span></span></span></p>
<p><span><span><span><span><span><span>Mental health awareness means implementing actions small and large across the organization that include:</span></span></span></span></span></span></p>
<ul><li><em><span><span><span><span><span><span>Scheduling breaks in long meetings.</span></span></span></span></span></span></em></li>
<li><em><span><span><span><span><span><span>Obtaining psychotherapy, counseling, grief support, and similar add-ons to the health package.</span></span></span></span></span></span></em></li>
<li><em><span><span><span><span><span><span>Scheduling monthly or quarterly gatherings to discuss or practice mental health wellness.</span></span></span></span></span></span></em></li>
<li><em><span><span><span><span><span><span>Offering paid time off.</span></span></span></span></span></span></em></li>
</ul><h3><span><span><span><span><span><span>Invest in Personal and Professional Development, Training, and Education</span></span></span></span></span></span></h3>
<p><span><span><span><span><span><span>“What happens if we train them, and they leave? What happens if we don’t, and they stay?”</span></span></span></span></span></span></p>
<p><span><span><span><span><span><span>While salary and benefits remain the base of any job, investment in an employee’s unique talents also pays off. Consider investing in professional development, training, certification, and other educational hours through an annual allocation or a pooled budget for staff-directed or individually-planned training. As part of our</span></span></span></span></span></span><a href="https://www.lullabot.com/jobs"><span><span><span><span><span><span> </span></span></span></span></span></span><span><span><span><span><span><span><span><span>benefits package</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span>, we each receive an education and event budget annually. This may take the form of an education budget used for conferences, seminars, training, and continuing education. Determine a process for staff to propose options and receive feedback or vetting, perhaps as part of their employee review process, as they build up a multi-year plan to improve their abilities. </span></span></span></span></span></span></p>
<h2><span><span><span><span><span><span>Required To-Do List, Start Here</span></span></span></span></span></span></h2>
<h3><span><span><span><span><span><span>Make software, website, and digital products accessible</span></span></span></span></span></span></h3>
<p><span><span><span><span><span><span>In web development, making digital properties as accessible as possible is required and best practice (</span></span></span></span></span></span><a href="https://a11yproject.com/"><span><span><span><span><span><span><span><span>check the a11y project</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span>). Technical Account Manager at Lullabot, Helena McCabe’s</span></span></span></span></span></span><a href="https://noti.st/helenasue"><span><span><span><span><span><span> </span></span></span></span></span></span><span><span><span><span><span><span><span><span>presentations</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span> give excellent tips on how to enable accessibility. By starting with an emphasis on accessibility for the digital property, additional issues around inclusion, culture, the role of technology, and overall trends in society may begin to surface. Our white paper on</span></span></span></span></span></span><a href="https://www.lullabot.com/articles/web-accessibility-how-inclusivity-protects-your-business-and-your-bottom-line"><span><span><span><span><span><span> </span></span></span></span></span></span><span><span><span><span><span><span><span><span>How Accessibility Protects your Business and your Bottom Line</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span> offers examples on how to make your products accessible and why it matters.</span></span></span></span></span></span></p>
<h3><span><span><span><span><span><span>Practice transparency</span></span></span></span></span></span></h3>
<p><span><span><span><span><span><span>Think of transparency as a way to build the team’s muscles, and to start working with fortitude, grace, and strength when grappling with heavier and more complex issues. For example, we practice </span></span></span></span></span></span><a href="https://en.wikipedia.org/wiki/Open-book_management"><span><span><span><span><span><span><span><span>open-book management</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span> (OBM), a financial practice that allows all employees to understand the current revenue, expenditures, and KPIs of the company (learn more at CEO Matt Westgate’s</span></span></span></span></span></span><a href="https://www.lullabot.com/articles/lullabot-team-retreat-2019"><span><span><span><span><span><span> </span></span></span></span></span></span><span><span><span><span><span><span><span><span>2019 Lullabot Team Retreat post</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span>). By creating the flexibility and capacity to have tough discussions, everyone may use a shared language and understanding of the company’s direction.</span></span></span></span></span></span></p>
<h3><span><span><span><span><span><span>Promote a sense of psychological safety</span></span></span></span></span></span></h3>
<p><span><span><span><span><span><span>The open-source movement continues to build on information freely shared, vetted, and evaluated across multiple use cases. The belief in sharing knowledge is in the DNA of companies like ours. In our case, Drupal is the community and platform on which </span></span></span></span></span></span><a href="https://www.drupal.org/lullabot"><span><span><span><span><span><span><span><span>many of the staff have built their careers</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span>.</span></span></span></span></span></span></p>
<p><span><span><span><span><span><span>Psychological safety is the bedrock for knowledge-sharing: it’s "a condition in which you feel (1) included, (2) safe to learn, (3) safe to contribute, and (4) safe to challenge the status quo—all without fear of being embarrassed, marginalized or punished in some way." (<a href="http://adigaskell.org/2019/11/17/the-4-stages-of-psychological-safety/">Timothy R Clark, 2019</a>). A</span></span></span></span></span></span><span><span><span><span><span><span>nd, it’s something to which Lullabot aspires: here’s a link to Matt Westgate’s </span></span></span></span></span></span><a href="https://www.youtube.com/watch?v=KPd4gTCwxps&feature=youtu.be"><span><span><span><span><span><span><span><span>lightning talk on psychological safety and DevOps</span></span></span></span></span></span></span></span></a><span><span><span><span><span><span>.</span></span></span></span></span></span></p>
<p><span><span><span><span><span><span>With accessibility, transparency, and safety in mind, we'll share more tips to begin or advance discussions around: hiring inclusively, engaging with staff, focusing on culture, and easier fixes. As we continue to work on this internally, we offer these ideas in the spirit of sharing and continuous improvement. Do you have ideas? Please drop a comment. We'd love to hear your thoughts and suggestions.</span></span></span></span></span></span></p>Fri, 06 Mar 2020 12:44:25 -0500Monica S. Flores33ffe9a2-afe2-48ab-a395-c5b602873587Lullabot Podcast: Drupal in Governmenthttps://www.lullabot.com/podcasts/lullabot-podcast/drupal-government
<p>Matt and Mike talk to two organizers of Drupal4Gov, as well as the project manager for Lullabot's Georgia.gov replatform about all things Drupal in the government.</p>Thu, 05 Mar 2020 13:55:11 -0500Matt Kleve, Mike Herchelcc6b0ec3-5e70-428b-8f31-7e2a8d4c69adSending a Drupal Site into Retirement Using the Static Generation Modulehttps://www.lullabot.com/articles/sending-drupal-site-retirement-using-static-generation-module
<p>The previous article in this series explained how to send a Drupal Site into retirement using HTTrack—one of the solutions to maintaining a Drupal site that isn't updated very often. While this solution works pretty well for any version of Drupal, another option is using the <a href="https://www.drupal.org/project/static" target="_blank">Static Generator</a> module to generate a static site instead. However, this module only works for Drupal 7 as it requires the installation of some modules on the site, and it uses Drupal to generate the results. </p>
<p>The Static Generator module relies on the <a href="https://www.drupal.org/project/xmlsitemap" target="_blank">XML</a><a href="https://www.drupal.org/project/xmlsitemap" target="_blank"> s</a><a href="https://www.drupal.org/project/xmlsitemap" target="_blank">ite</a><a href="https://www.drupal.org/project/xmlsitemap" target="_blank">m</a><a href="https://www.drupal.org/project/xmlsitemap" target="_blank">ap module</a> to create a manifest. The links in the XML sitemap serve as the list of pages that should be transformed into static pages. After generating the initial static pages, the <a href="https://www.drupal.org/project/expire" target="_blank">Cache Expiration</a> module keeps track of changed pages to be regenerated to keep the static site current. This combination of Static Generator, XML sitemap, and Cache Expiration is a good solution when the desire is to regenerate the static site again in the future, after making periodic updates.</p>
<p>There are many module dependencies, so quite a list of modules was downloaded and installed. Once installed, the high-level process is:</p>
<ul><li>Create and configure the XML sitemap and confirm it contains the right list of pages.</li>
<li>Configure Cache expiration to use the Static Generator and expire the right caches when content changes.</li>
<li>Go to <strong>/admin/config/system/static</strong> and queue all static items for regeneration.</li>
<li>Click a <strong>Publish</strong> button to generate the static site.</li>
</ul><h2>Install Static Generator</h2>
<p>The modules are downloaded and enabled using Drush. Enabling additional modules, like xmlsitemap_taxonomy, may be needed depending on the makeup of the site.</p>
<p><code>drush dl static expire xmlsitemap</code></p>
<p><code>drush en static_file, static_views, static_xmlsitemap, static_node, static</code></p>
<p><code>drush en expire</code></p>
<p><code>drush en xmlsitemap_menu, xmlsitemap_node, xmlsitemap</code></p>
<h2>Configure XMLSitemap</h2>
<p>On <strong>/admin/config/search/xmlsitemap</strong>, make sure the site map is accurately generated and represents all pages that should appear in the static site. Click on the link to the sitemap to see what it contains.</p>
<ul><li>Add all content types whose paths should be public.</li>
<li>Add menus and navigation needed to allow users to get to the appropriate parts of the site.</li>
<li>Make sure Views pages are available in the map.</li>
</ul><div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-02/image_preview-2.png?itok=bCUlSPPM" width="900" height="482" alt="View XML Sitemap screen" /></figure></div>
<p>A lot of custom XML sitemap paths may be required for dynamic views pages. If so, generate XML sitemap links in the code where the database is queried for all values that might exist as a path argument, then create a custom link for each path variation.</p>
<p>Code to add custom XML sitemap links look like this (this is Drupal 7 code):</p>
<pre class="lang-php">
<code>
/**
* Add a views path to xmlsitemap.
*
* @param string $path
* The path to add.
* @param float $priority
* The decimal priority of this link, defaults to 0.5.
*/
function MYMODULE_add_xmlsitemap_link($path, $priority = '0.5') {
drupal_load('module', 'xmlsitemap');
// Create a unique namespace for these links.
$namespace = 'MYMODULE';
$path = drupal_get_normal_path($path, LANGUAGE_NONE);
// See if link already exists.
$current = db_query("SELECT id FROM {xmlsitemap} WHERE type = :namespace AND loc = :loc", array(
':namespace' => $namespace,
':loc' => $path,
))->fetchField();
if ($current) {
return;
}
// Find the highest existing id for this namespace.
$id = db_query("SELECT max(id) FROM {xmlsitemap} WHERE type = :namespace", array(
':namespace' => $namespace,
))->fetchField();
// Create a new xmlsitemap link.
$link = array(
'type' => $namespace,
'id' => (int) $id + 1,
'loc' => $path,
'priority' => $priority,
'changefreq' => '86400', // 1 day = 24 h * 60 m * 60 s
'language' => LANGUAGE_NONE
);
xmlsitemap_link_save($link);
}
</code>
</pre>
<h2>Configure Cache Expiration</h2>
<p>On <strong>admin/config/system/expire</strong>, set up cache expiration options. Make sure that all the right caches will expire when content is added, edited, or deleted. For instance, the home page should expire any time nodes are added, changed, or deleted since the changed nodes change the results in the view of the latest content that appears there. </p>
<p> </p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-02/image_preview-3.png?itok=9rl45ihI" width="900" height="776" alt="Screenshot of Cache Expiration screen" /></figure></div>
<h2>Generate the Static Site</h2>
<p>Once configured, a <strong>Publish Site</strong> button appears on every page, which is a shortcut. But the first time through, it’s better to visit <strong>/admin/config/system/static</strong> to configure static site options and generate the static site. Some pages were created automatically, and others not during the initial setup. Once all other modules are configured, and the XML sitemap looks right, clear all the links and regenerate the static site.</p>
<p>The location where the static site is created can be controlled, but the default location is at the path, <strong>/static/normal</strong>, in the same repository as the original site. That location and other settings are configured on the Settings tab.</p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-02/image_preview-4.png?itok=WRPAr_nM" width="900" height="519" alt="Screenshot of Static Generator" /></figure></div>
<p>Generate the static site and ensure all the pages are accounted for and work correctly. This is an iterative process due to the discovery of missing links from the XML sitemap and elsewhere. Circle through the process of updating the sitemap and then regenerate the static site as many times as necessary.</p>
<p>The process of generating the static site runs in batches. It might also run only on cron depending on what options are chosen in settings. Uncheck the cron option when generating the initial static site and later use cron just to pick up changes. Otherwise, running cron multiple times to generate the initial collection of static pages is required.</p>
<p>For a 3,500 page site, it takes about seven minutes to generate the static pages. Later updates should be faster since only changed pages would have to be regenerated.</p>
<p>When making changes later, they need to be reflected in the XML sitemap before they will be picked up by Static Generator. If XML sitemap updates on cron, run cron first to update the sitemap, then update the static pages.</p>
<p>After generating the static site and sending it to GitHub, it was clear that the Static Generator module transforms a page like <strong>/about</strong> into the static file <strong>/about.html</strong>, then depends on an included <strong>.htaccess</strong> file that uses mod_rewrite to redirect requests to the right place. But, GitHub Pages won’t recognize mod_rewrite. That makes the Static Generator a poor solution for a site to be hosted on Github Pages, although it should work fine on sites where mod_rewrite will work. </p>
<h2>Comparing HTTrack and Static Generator Options</h2>
<p>Here’s a comparison of a couple of methods explored when creating a static site: </p>
<ul><li>HTTrack will work on any version of Drupal, Static Generator, only on Drupal 7.</li>
<li>HTTrack doesn’t require setup other than the standard preparation of any site, which is required for any static solution. Static Generator took some time to configure, especially since there wasn’t an existing XML sitemap and Cache Expiration installed and configured.</li>
<li>HTTrack can take quite a while to run, a half-hour to an hour, possibly longer. Static Generator is much faster—seven minutes for the initial pass over the whole site.</li>
<li>The Static Generator solution makes the most sense if there is a need to keep updating the site and regenerating the static pages. That situation justifies the up-front work required to configure it. HTTrack is easier to set up for a one-and-done situation.</li>
<li>The file pattern of <strong>/about/about.html</strong> created by our custom HTTrack arguments works fine for managing internal links on Github Pages. The file pattern of <strong>/about.html</strong> created by Static Generator will not correctly manage internal links on Github Pages. The second pattern will only work on a host that has mod_rewrite installed and the appropriate rules configured in .htaccess.</li>
</ul><p>Github Pages or the Static Generator module make excellent solutions. To view an example of a site generated with HTTrack, go to <a href="https://everbloom.us/" target="_blank">https://everbloom.us</a>.</p>Wed, 04 Mar 2020 08:22:10 -0500Karen Stevenson98799004-16a1-4145-b710-178f5998b4c8Update on the Status of Drupal’s New Olivero Themehttps://www.lullabot.com/articles/update-status-drupals-new-olivero-theme
<p>Olivero is a new theme that aims to be the new default front-end theme for Drupal 9. The theme's inception took place in a hotel lobby during DrupalCon Seattle and has now grown into a full Drupal core initiative (<a href="https://www.lullabot.com/articles/drupal-9-olivero-turning-conversation-core-initiative">read about its inception here</a>).</p>
<h2 id="from-design-to-proof-of-concept">From Design to Proof of Concept</h2>
<p>The <a href="https://www.drupal.org/about/strategic-initiatives#olivero">Drupal 9 theme initiative</a> started with stakeholder meetings and the design process (<a href="https://www.fldrupal.camp/sessions/design-theming-front-end-development/designing-chaos-design-process-behind-olivero">learn more here</a>). Once the designs were close to being final, I started working on translating the designs into markup, styles, and JavaScript within a <a href="https://olivero-poc.netlify.com/">static proof of concept</a>.</p>
<p>While working on the proof of concept, <a href="https://www.lullabot.com/about/putra-bonaccorsi">Putra Bonaccorsi</a> was laying the theming groundwork by creating boilerplate code for the theme and transpiling of the CSS and JavaScript.</p>
<p> </p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-03/olivero-screenshot_0.jpg?itok=6VXA9V7G 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-03/olivero-screenshot_0.jpg?itok=SXGfXNvK 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-03/olivero-screenshot_0.jpg?itok=ms6auPAs 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-03/olivero-screenshot_0.jpg?itok=ymtdQjrc 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-03/olivero-screenshot_0.jpg?itok=o-U2PTyD 1917w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-03/olivero-screenshot_0.jpg?itok=ymtdQjrc" alt="Olivero theme showing clean design and drop menu open" /></figure></div>
<h2 id="proof-of-concept">Proof of concept</h2>
<p>The process of creating a proof of concept has been invaluable. The overarching goals are to validate major DOM architectural decisions and get sign-off from the Drupal core accessibility team on major decisions before moving into templating. Additionally, the proof of concept has validated and influenced design decisions across multiple devices and viewports.</p>
<p>You can view the proof of concept at <a href="https://olivero-poc.netlify.com">https://olivero-poc.netlify.com</a>, but note that progress is rapid, and changing by the hour!</p>
<h2 id="bumps-in-the-road">Bumps in the Road</h2>
<p>When the Olivero team was creating the initial schedule, the plan was to get the theme into Drupal 9.1, because the first version of Drupal 9 (9.0) was going to be the same as the last version of Drupal 8 — but with deprecated code removed.</p>
<p>However, during the Driesnote at DrupalCon Amsterdam, Drupal project lead, Dries Buytaert, <a href="https://youtu.be/Apqd4ff0NRI?t=3974">stated that he wanted to get the theme</a> into the initial release of Drupal 9.0. This pushed up the timeline significantly!</p>
<p>Balancing between onboarding and mentoring new developers versus rapidly closing issues has proven to be delicate. Many contributors want to help with the initiative; however, because they are volunteers (as are the core team), they are not on a timetable for closing issues.</p>
<p>Because of the tight timeline, I’ve been leaning toward the latter (rapidly fixing issues).</p>
<h2 id="florida-drupalcamp-sprint">Florida DrupalCamp Sprint</h2>
<p>We decided that the initiative needs a shot in the arm, so we put on a mini code-sprint within the annual contribution sprint at <a href="https://www.fldrupal.camp">Florida DrupalCamp</a> in last month. Because Putra couldn’t make it down for the actual conference, Florida DrupalCamp sponsored her to fly in for the sprint.</p>
<p>During the 2 day sprint, we accomplished the following</p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow image--captioned" style="max-width: 4032px;"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-03/hawkeye-fldc.jpg?itok=L7Da_c_-" width="900" height="675" alt="Overlooking Hawkeye's shoulder looking at computer screen seeing code" /><figcaption class="image__caption">Lullabot Hawkeye Tenderwolf sprinting at Florida DrupalCamp</figcaption></figure></div>
<ul><li>Cleaned up the PostCSS build process</li>
<li>Integrated a default database export with indicative content into the Tugboat build process.</li>
<li>Copied some of the latest scripts/JS and CSS from the PoC repo into the Drupal theme.</li>
<li>We've exported block configs for the theme initial install for the following Core's blocks:
<ul><li>Primary Menu</li>
<li>User Account Menu</li>
<li>Powered By Drupal</li>
<li>Content</li>
</ul></li>
<li>The Primary Menu block is themed and configured to expose the drop-down menu by default.</li>
<li>The Secondary menu/User Account block is themed and configured.</li>
<li>The "Powered by" block is themed and configured.</li>
<li>The "Get Started" page has been created and will need to be revisited.</li>
<li>Latest preview is viewable on Tugboat: <a href="https://8-x-1-x-dev-2t4d1epwkj8tgwxduizmixhzevqwzi8w.tugboat.qa/">https://8-x-1-x-dev-2t4d1epwkj8tgwxduizmixhzevqwzi8w.tugboat.qa</a></li>
</ul><div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-video">
<figure class="video video--wide video--captioned"><video autoplay="" muted="" loop="" poster="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-03/olivero-focus-styles.jpg?itok=3Zu2vkon"><source src="https://www.lullabot.com/sites/default/files/2020-03/olivero-focus-styles-optimized.mp4" type="video/mp4"></source></video><figcaption class="video__caption">Olivero's focus states (above) were heavily worked on at the Florida DrupalCamp sprint.</figcaption></figure></div>
<p>If you were to install the theme initially, you'll be able to see the different regions and blocked configured, however, please note that there is still more theme development that needs to be done for the beta release.</p>
<h2 id="current-status">Current Status</h2>
<p>The work of the theme includes the proof of concept and the actual theming.</p>
<h3 id="proof-of-concept">Proof of Concept</h3>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow image--captioned" style="max-width: 1052px;"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-03/olivero-always-on-mobile-nav.jpg?itok=_JFmbvzN" width="900" height="695" alt="Screenshot from design showing the always on mobile navigation in an opened state" /><figcaption class="image__caption">Screenshot from Figma comp showing the always-on mobile navigation in an opened state.</figcaption></figure></div>
<p>We’re working on styling that will enable site owners to choose an “always-on” mobile theme in the event that the primary navigation has more items than the space can manage.</p>
<p>We’re also knocking out various accessibility issues—especially focus states in Windows high contrast mode, which are trickier than expected.</p>
<h3 id="drupal-theme">Drupal Theme</h3>
<p>The Drupal theme looks close to the designs! Work continues on the search integration into the header, in addition to standard theming.</p>
<h2 id="what-s-next-">What’s Next?</h2>
<p>We hope to pull in the final proof of concept markup, styling, and JavaScript into the theme toward the end of next week (around March 13th, 2020). At that point, work on the proof of concept will cease, and new styling fixes will go into the theme.</p>
<p>There’s still so much to do! We need:</p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow image--captioned" style="max-width: 1811px;"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-03/olivero-ie11-high-contrast.jpg?itok=GSW42QT9" width="900" height="606" alt="Olivero proof of concept viewed in Windows high contrast mode" /><figcaption class="image__caption">Accessibility is one of the primary concerns with Olivero. Above is the current state of the theme within Internet Explorer 11 when viewed in a Windows high contrast theme.</figcaption></figure></div>
<ul><li>Support for Drupal core features such as:
<ul><li>Book module</li>
<li>Forum module</li>
<li>Embedded media</li>
<li>Various Views rows styles (grid, etc.)</li>
</ul></li>
<li>More accessibility</li>
<li>Internationalization</li>
<li>Tests</li>
<li>Coding standards</li>
<li>And more!</li>
</ul><h2 id="standing-on-the-shoulders-of-giants">Standing on the Shoulders of Giants</h2>
<p>I also want to note that the rapid pace of development would not have been possible without the contributions of Claro (Drupal 9’s new administrative theme), and Umami (the theme for the out of the box initiative).</p>
<p>These themes blazed the way by including support for technologies in core such as web fonts, PostCSS, and the overall core theme architecture.</p>
<h2 id="completion">Completion</h2>
<p>Olivero was initially slated for inclusion in core in Drupal 9.1. That’s still the most likely scenario. That said, there’s a possibility that Drupal may shift the 9.0 beta deadline to the end of April. If that’s the case, there is a possibility to submit a core patch beforehand.</p>
<p>To commit by this time, we need to submit the patch a minimum of a few weeks ahead of time to give core committers time to review (and even that might not be enough time).</p>
<p>We’re currently working on <a href="https://www.drupal.org/project/drupal/issues/3111409">[META] Add new default Olivero frontend theme to Drupal 9 core</a> to define the minimum beta requirements to submit to core. Expect this issue to be more fleshed out within the coming days.</p>
<h2 id="after-completion">After Completion</h2>
<p>After the theme is in core, we still would love to add additional features such as support for accessible color schemes, dark mode, etc. However, the first step is finishing up the minimal viable product for inclusion in core.</p>
<h2 id="join-us-">Join Us!</h2>
<p>The Olivero team meets on Drupal Slack every Monday at 3 pm UTC (10 am ET) in the #d9-theme channel on drupal.slack.com‬. We <a href="https://www.drupal.org/project/issues/olivero?component=Meetings">post the agendas</a> in the <a href="https://www.drupal.org/project/issues/olivero?categories=All">Olivero issue queue</a> beforehand.</p>
<p>We need people to pick up issues and run with them, but keep in mind that for the next week or two, the primary styling is still in progress within the <a href="https://github.com/Lullabot/olivero-poc">proof of concept on Github</a>.</p>
<h2 id="upcoming-events-and-sprinting">Upcoming Events and Sprinting</h2>
<p>I will be attending <a href="https://drupalcamp.be/en/drupal-dev-days-2020">Drupal Dev Days</a> in Ghent Belgium, April 6-10th, 2020, and will be sprinting the entire time. We hope to work on getting the code ready for Drupal core inclusion by that time.</p>
<p>Putra and I (along with the majority of the Lullabot team) will be <a href="news/lullabot-drupalcon-minneapolis">attending DrupalCon Minneapolis</a> in May 2020. We will be heavily sprinting on Olivero during this time (especially on Friday).</p>Tue, 03 Mar 2020 12:06:18 -0500Mike Herchel6a6b89e0-a919-4f72-b5b7-753e072b7ef4Sending a Drupal Site Into Retirement Using HTTrackhttps://www.lullabot.com/articles/sending-drupal-site-retirement-using-httrack
<p>Maintaining a fully functional Drupal 7 site and keeping it updated with security updates year-round takes a lot of work and time. For example, some sites are only active during certain times of the year, so continuously upgrading to new Drupal versions doesn't always make the most sense. If a site is updated infrequently, it's often an ideal candidate for a static site. </p>
<p>To serve static pages, <a href="http://pages.github.com/" target="_blank">GitHub Pages</a> is a good, free option, especially when already using GitHub. GitHub Pages deploys Jekyll sites, but <a href="http://jekyllrb.com/" target="_blank">Jekyll</a> is perfectly happy to serve up static HTML, which doesn't require any actions other than creating functional HTML pages to get a solution working. Using <a href="https://everbloom.us/" target="_blank">this fishing tournament website</a> as the basis for this article, here’s how to retire a Drupal site using HTTrack. </p>
<h2>Inactivate the Site</h2>
<p>To get started, create a local copy of the original Drupal site and prepare it to go static using ideas from <a href="https://www.lullabot.com/articles/sending-a-drupal-site-into-retirement">Sending A Drupal Site into Retirement</a>.</p>
<h2>Create GitHub Page</h2>
<p>Next, create a <a href="http://github.com/karens/everbloom" target="_blank">project</a> on GitHub for the static site and set it up to use GitHub Pages. Just follow the instructions to create a simple Hello World repository to be sure it’s working. It’s a matter of choosing the option to use GitHub Pages in the settings and identifying the GitHub Pages branch to use. The GitHub pages options are way down at the bottom of the settings page. There's an option to select a GitHub theme, but if there's one provided in the static pages, it will override anything chosen. So, really, any theme will do.</p>
<p>A committed index.html file echoes back "Hello World" and the new page becomes viewable at the GitHub Pages URL. The URL pattern is <strong>http://REPO_OWNER.github.io/REPO_NAME</strong>; the GitHub Pages information block in the repository settings will display the actual URL for the project. </p>
<h2>Create Static Pages with HTTrack</h2>
<p>Now that there's a place for the static site, it's time to generate the static site pages into the new repository. Wget could spider the site, but a preferred solution is one that uses <a href="http://www.httrack.com/" target="_blank">HTTrack</a> to create static pages. This is a tool that starts on a given page, generally the home page, then follows every link to create a static HTML representation of each page that it finds. This will only be sufficient if every page on the site is accessible from another link on the site and the navigation or other links on the home page. HTTrack won't know anything about unlinked pages, although there are ways to customize the instructions to identify additional URLs to spider. </p>
<p>Since this solution doesn’t rely on Drupal at all, it's possible to use it for a site built with any version of Drupal, or even sites built with other CMSes. It self-discovers site pages, so there's no need to provide any manifest of pages to create. HTTrack has to touch every page and retrieve all the assets on each page, so it can be slow to run, especially when running it over the Internet. It's best to run it on a local copy of the site.</p>
<p>It's now time to review all the link elements in the head of the pages and make sure they are all intentional. Using the Pathauto module, the head elements added by Drupal 7, such as <strong><link rel="shortlink" href="</strong><a href="http://everbloom-7.lndo.site/node/9055" target="_blank"><strong>/node/</strong></a><strong>9999" /></strong>, should be removed. They point to URLs that don't require replication in the static site, and HTTrack will try to create all those additional pages when it encounters those links.</p>
<p>When using the Metatags module, configuring it to remove those tags is possible. Instead, a bit of code like the following is used in a custom module to strip tags out (borrowed from the Metatags module, code appropriate for a Drupal 7 site):</p>
<pre class="lang-php">
<code>
/**
* Implements hook_html_head_alter().
*
* Hide links added by core that we don't want in the static site.
*/
function MYMODULE_html_head_alter(&$elements) {
$core_tags = array(
'generator',
'shortlink',
'shortcut icon',
);
foreach ($elements as $name => &$element) {
foreach ($core_tags as $tag) {
if (!empty($element['#attributes']['rel']) && $element['#attributes']['rel'] == $tag) {
unset($elements[$name]);
}
elseif (!empty($element['#attributes']['name']) && strtolower($element['#attributes']['name']) == $tag) {
unset($elements[$name]);
}
}
}
}
</code>
</pre>
<p>The easiest way to install HTTrack on a Mac is with Homebrew:</p>
<p><code>brew install httrack</code></p>
<p>Based on the <a href="http://www.httrack.com/html/fcguide.html" target="_blank">documentation</a> and further thought, it became clear that the following command string is the ideal way to use HTTrack. After moving into the local GitHub Pages repo, the following command should be executed where <strong>LOCALSITE</strong> is the path to the local site copy that's being spidering, and <strong>DESTINATION</strong> is the path to the directory where the static pages should go:</p>
<p><code>httrack http://LOCALSITE -O DESTINATION -N "%h%p/%n/index%[page].%t" -WqQ%v --robots=0 --footer ''</code></p>
<p>The <strong>-N</strong> flag in the command will rewrite the pages of the site, including pager pages, into the pattern <strong>/results/index.html</strong>. Without the <strong>-N</strong> flag, the page at <strong>/results</strong> would have been transformed into a file called <strong>results.html</strong>. This will take advantage of the GitHub Pages server configuration, which will automatically redirect internal links that point to <strong>/results</strong> to the generated file <strong>/results/results.html</strong>.</p>
<p>The <strong>--footer </strong><strong>''</strong> option means omit comments that HTTrack automatically adds to each page and looks like the following. This gets rid of the first comment, but nothing appears to get rid of the second one. Getting rid of the first one, which has a date in it, eliminates having a Git repository in which every page appears to change every time HTTrack runs. It also obscures the URL of the original site, which may be confusing since it's a local environment.</p>
<p><code><!-- Mirrored from everbloom-7.lndo.site/fisherman/aaron-davitt by HTTrack Website Copier/3.x [XR&CO'2014], Sun, 05 Jan 2020 10:35:55 GMT --></code></p>
<p><code><!-- Added by HTTrack --><meta http-equiv="content-type" content="text/html;charset=utf-8" /><!-- /Added by HTTrack --></code></p>
<p>The pattern also deals with paged views results. It tells HTTrack to find a value in the query string called "page" and inserts that value, if it exists, into the URL pattern in the spot marked by <strong>[page]</strong>. Paged views create links like <strong>/about/index2.html</strong>, <strong>/about/index3.html</strong> for each page of the view. Without specifying this, the pager links would be created as meaningless hash values of the query string. This way, the pager links are user-friendly and similar (but not quite the same) as the original link URLs.</p>
<p>Shortly after the process starts, it will stop and ask a question about how far to go in the following links. '*' is the response to that question:</p>
<p> </p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-02/image_preview.png?itok=_VsEYrXK" width="900" height="353" alt="HTTrack continue screen" /></figure></div>
<p>The progress is viewable as it goes to see which sections of the site it is navigating into. The '%v' flag in the command tells it to use verbose output.</p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-02/everbloom7_-_httrack_http_everbloom-7_lndo_site_-o_repositories_gihub_everbloom_-n_h_p_n_index_page_t_-wqq_v_-robots_0_-_10030.png?itok=iqYcaq5J" width="900" height="272" alt="HTTrack progress screen" /></figure></div>
<p>HTTrack runs on a local version of the site to spider and creates about 3,500 files, including pages for every event and result and every page of the paged views. HTTrack is to slow to use across the network on the live site URL, so it makes sense to do this on a local copy. The first attempt took nearly two hours because so many unnecessary files were created, such as an extra <strong>/node/9999.html</strong> file for every node in addition to the desired file at the aliased path. After a while, it was apparent they came from the <strong>shortlink</strong> in the header pointing to the system URL. Removing the short links, cut the spidering time by more than half. Invalid links and images in the body of some older content that HTTrack attempted to follow (creating 404 pages at each of those destinations) also contributed to the slowness. Cleaning up all of those invalid links caused the time to spider the site to drop to less than a half-hour.</p>
<p>The files created by HTTrack are then committed to the appropriate branch of the repository, and in a few minutes, the results appear at <a href="http://karens.github.io/everbloom" target="_blank">http://karens.github.io/everbloom</a>.</p>
<p>Although incoming links to <strong>/results</strong> now work while internal links still look like this in the HTML:</p>
<p><code><strong>/results/index.html</strong></code></p>
<p>A quick command line fix to clean that up is to run this, from the top of the directory that contains the static files:</p>
<p><code>find . -name "*.html" -type f -print0 | xargs -0 perl -i -pe "s/\/index.html/\//g"</code></p>
<p>That will change all the internal links in those 3,500 pages from <strong>results/index.html</strong> to <strong>/results/</strong> resulting in a static site that pretty closely mirrors the original file structure and URL pattern of the original site.</p>
<p>One more change is to fix index.html at the root of the site. When HTTrack generates the site, it creates an index.html page that redirects to another page, <strong>/index/index.html</strong>. To clean things up a bit and remove the redirect, I copy <strong>/index/index.html</strong> to <strong>/index.html</strong>. The relative links in that file now need to be fixed to work in the new location, so I do a find and replace on the source of that file to remove <strong>../</strong> in the paths in that page to change URLs like <strong>../sites/default/files/image.jpg</strong> to <strong>sites/default/files/image.jpg</strong>.</p>
<p>Once this is working successfully, the final step was to have the old domain name redirect to the new GitHub Pages site. <a href="https://help.github.com/articles/setting-up-a-custom-domain-with-pages" target="_blank">GitHub</a> provides instructions about how to do that.</p>
<h2>Updating the Site</h2>
<p>Making future changes requires updating the local site and then regenerating the static pages using the method above. Since Drupal is not publicly available, there's no need to update or maintain it, nor worry about security updates, as long as it works well enough to regenerate the static site when necessary. When making changes locally, regenerate the static pages using HTTrack and push up the changes. </p>
<p>The next article in this series will investigate whether or not there is a faster way of creating a static site.</p>Wed, 26 Feb 2020 07:58:51 -0500Karen Stevensonda923178-c956-496a-aa76-27c79ab1a53aDrupal 9 Olivero: Turning Conversation into a Core Initiativehttps://www.lullabot.com/articles/drupal-9-olivero-turning-conversation-core-initiative
<p>One of the biggest benefits of an open-source community like Drupal is the ability to collaborate with fantastic people that you wouldn’t otherwise have the opportunity to work with. However, when you have an idea that you think would be a good initiative for a Drupal core release (such as Drupal 9) you might find yourself thinking: "How do I even begin? How can I advocate for my idea?” We all find ourselves asking these questions as we navigate the complex journey of turning an idea into a core initiative.</p>
<p>During DrupalCon Seattle, a handful of people had a casual conversation in a hotel lobby. This conversation turned into an official Drupal core strategic initiative to create a new default front-end theme for Drupal 9. Here's the story of how that happened, the steps we took, and the work we did before opening the project to the community. </p>
<h2>The Beginning: Is "Your Idea" Already in the Works?</h2>
<p>On the last day of DrupalCon Seattle, <a href="https://www.lullabot.com/about/mike-herchel" target="_blank">Mike Herchel</a> (Senior Developer at Lullabot), <a href="https://www.drupal.org/u/lauriii" target="_blank">Lauri Eskola</a> (Drupal core committer and front-end framework manager), <a href="https://www.drupal.org/u/webchick" target="_blank">Angie</a> <a href="https://www.drupal.org/u/webchick" target="_blank">Byron</a> (Drupal core committer and product manager), and I had a conversation about what exactly distinguishes a good CMS theme. Naturally, that led to the discussion of the current status of Drupal 9.</p>
<p>Mike and I were surprised to find out that there was no initiative in place for a Drupal 9 default theme. Having been in the community for quite a while, we knew that Bartik was created for Drupal 7 and has long served as the default theme, but it’s nearly ten years old. By 2019, the design had begun to look dated and no longer spoke to Drupal's strengths.</p>
<p>We began envisioning what kind of first impression a clean and modern default theme would have on users when Lauri mentioned something along the lines of, “Why don't you get involved since Drupal 9 is just around the corner and is expected to be released around mid-2020?” We were excited by the idea and that we already had buy-in from a key figure within the community. On our way to the airport the following morning, Mike and I continued chatting about ways this project could start.</p>
<h2>Setting Goals: Identify Why This Initiative Matters</h2>
<p>Before announcing to the world that you have an idea that can be shipped into Drupal core, stop and ask yourself what your goals are for the project. Mike and I started by writing down some of the pain points and challenges of the current status quo. As Dries pointed out in his keynote, experts love Drupal. However, Drupal as a CMS still has a negative connotation among beginners for its outdated interfaces and user experiences. Therefore, prioritizing the beginner experience through potential initiatives like the new default theme, guided tours (aka <a href="https://www.drupal.org/node/2847582" target="_blank">Out of the Box initiative</a>), and WYSIWYG page building would give Drupal a much-needed new look and feel that users expect from a modern CMS.</p>
<p>Here are the three goals that we identified:</p>
<ul><li><strong>Update to modern design:</strong> Design a theme that feels modern and ages well for the next 5 to 10 years.</li>
<li><strong>Add functionality that supports new features:</strong> Include support for commonly used Drupal functionality, such as second-level navigation, embedded media, layout builder, and more.</li>
<li><strong>Create a WCAG AA compliant theme:</strong> Work closely with accessibility experts and the Drupal Accessibility team to ensure that the theme passes <a href="https://www.drupal.org/node/464500" target="_blank">Drupal’s stringent accessibility standards</a> with flying colors.</li>
</ul><h2>Drupal Core Ideas Queue</h2>
<p>Setting these goals helped us stay focused on what we needed to do and got us prepared to open an “idea issue” for the redesign and development of a theme that could ship with the release of Drupal 9. The ideas queue section of Drupal.org let us propose ideas for Drupal core and got them through validation and planning phases.</p>
<p>Here’s a link to the issue that we submitted to the Drupal ideas project: <a href="https://www.drupal.org/project/ideas/issues/3064880" target="_blank">https://www.drupal.org/project/ideas/issues/3064880</a></p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide image--captioned" style="max-width: 1474px;"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-02/d9-issue-template.png?itok=I-5HF-Lw 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-02/d9-issue-template.png?itok=y5ujJyNN 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-02/d9-issue-template.png?itok=UeCFI6-J 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-02/d9-issue-template.png?itok=hvGZASzC 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-02/d9-issue-template.png?itok=6-MlhQEM 1474w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-02/d9-issue-template.png?itok=hvGZASzC" alt="D9 Issue Template" /><figcaption class="image__caption">A document we put together for Drupal’s ideas issue queue process.</figcaption></figure></div>
<h3>Forming the Band: Putting Together Your Initial Team</h3>
<p>With any big or small initiative, you can't do the work all by yourself. You need a team that can help bring in new perspectives and fill in the areas that are outside of your discipline. Once we knew our idea was valid and sought-after by the community, Mike engaged Lullabot designers <a href="https://www.drupal.org/u/jwitkowski79" target="_blank">Jen Witkowski</a> and <a href="https://www.drupal.org/u/jponch" target="_blank">Jared Ponchot</a> to lead the design effort for the new Drupal 9 default theme. <a href="https://www.drupal.org/u/katannshaw" target="_blank">Kat Shaw</a> and <a href="https://www.drupal.org/u/mtift" target="_blank">Matthew Tift</a> also joined for assistance with accessibility and project management.</p>
<h3>Identifying Stakeholders</h3>
<p>Part of this team's responsibility was to identify design stakeholders who could help us refine the design. We iterated on the design multiple times internally before presenting it to the community to avoid bikeshedding. Doing this helped speed up <a href="https://www.drupal.org/project/ideas/issues/3088378" target="_blank">the proposal</a> process, which was one of the key contributing points to us getting traction and building excitement for this core initiative.</p>
<p>The following people were chosen as stakeholders:</p>
<ul><li><a href="https://www.drupal.org/u/dries" target="_blank">Dries Buytaert</a>: Creator and project lead for Drupal</li>
<li><a href="https://www.drupal.org/u/webchick" target="_blank">Angie</a> <a href="https://www.drupal.org/u/webchick" target="_blank">Byron</a>: Drupal core committer and product manager</li>
<li><a href="https://www.drupal.org/u/lauriii" target="_blank">Lauri Eskola</a>: Drupal core committer and frontend framework manager</li>
<li><a href="https://www.drupal.org/u/ckrina" target="_blank">Cristina Chumillas</a>: Drupal usability maintainer</li>
<li><a href="https://www.drupal.org/u/g%C3%A1bor-hojtsy" target="_blank">Gábor Hojtsy</a>: Drupal core committer, product manager and initiative coordinator</li>
</ul><h2>Document and Design</h2>
<p>As the discovery process started to take shape, we continuously documented all of the discussions we had regarding the project. Documentation isn’t as fun or exciting as writing code, but it's one of the contributing factors to keeping us on track and getting to our goal of releasing a proposal to the community. </p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide image--captioned" style="max-width: 1474px;"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-02/d9-issue-queue-table-of-contents.png?itok=1IxUt8r5 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-02/d9-issue-queue-table-of-contents.png?itok=m4NyzaOM 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-02/d9-issue-queue-table-of-contents.png?itok=KXj_iR-u 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-02/d9-issue-queue-table-of-contents.png?itok=pTc-i_8I 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-02/d9-issue-queue-table-of-contents.png?itok=4rShFTZI 1474w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-02/d9-issue-queue-table-of-contents.png?itok=pTc-i_8I" alt="D9 Theme Discovery Phase table of contents" /><figcaption class="image__caption">An example of the table of contents document during our discovery phase.</figcaption></figure></div>
<p>Meanwhile, we worked with our stakeholders to identify adjectives that would help guide the visual design. We created a sliding-scale exercise where stakeholders could add points across several tone spectrums, a common practice that the design team at Lullabot likes to conduct on client projects. Some of these were one adjective versus another (“formal” not “casual”), while others highlight the importance of a balance (“approachable” and “official”).</p>
<h3>Voice and Tone of the Theme</h3>
<p>Below are keywords that were identified to serve as the <em>voice and tone</em> of the new theme:</p>
<ul><li>Formal</li>
<li>Light and bright (vs. dark & impactful)</li>
<li>Contemporary</li>
<li>Approachable and official</li>
<li>Novel (with some constraint)</li>
<li>Cool</li>
<li>High contrast with some restraint</li>
<li>Light (not heavy)</li>
</ul><div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide image--captioned" style="max-width: 1474px;"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-02/d9-theme-exercise.png?itok=0abw7FL0 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-02/d9-theme-exercise.png?itok=UicTIi0z 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-02/d9-theme-exercise.png?itok=ZgnM-Geg 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-02/d9-theme-exercise.png?itok=nmWO9g5q 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-02/d9-theme-exercise.png?itok=x_tcjJTy 1474w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-02/d9-theme-exercise.png?itok=nmWO9g5q" alt="D9 Theming Exercise" /><figcaption class="image__caption">A screenshot of the D9 theme’s branding and style exercise with stack-holders</figcaption></figure></div>
<h3>Design Principles</h3>
<p>The following principles were established through research and collaboration, and are useful for guiding future additions and feedback for changes.</p>
<ul><li><strong>Accessible:</strong> WCAG AA conformance is a top priority for the design. The layout, colors, and functionality should provide an accessible theme that can be enjoyed by everyone.</li>
<li><strong>Simple:</strong> Avoid unnecessary visual elements, colors, effects, and complexity.</li>
<li><strong>Modern:</strong> Take advantage of the capabilities and strengths of modern browsers and interaction patterns.</li>
<li><strong>Focused:</strong> Embrace high contrast, saturated color, and negative space to draw the eye to what’s most important.</li>
<li><strong>Flexible:</strong> Provide options and overrides to account for varied needs and preferences.</li>
</ul><h3>The Meeting / Feedback Loop</h3>
<p>Although this initiative is not a client project nor one that we work on daily, we established a routine of meeting every week to discuss what needed to be done to present a design to the stakeholders. Once we established the design principles and the voice and tone, we used <a href="https://www.lullabot.com/articles/zoom-mocks-bridging-the-divide-between-styles-and-page-design">zoom mocks</a> to explore style using the adjectives and design principles as our guide. We presented these to the stakeholders, who chose a design with which to move forward.</p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--narrow image--captioned" style="max-width: 1474px;"><img src="https://www.lullabot.com/sites/default/files/styles/max_900/public/2020-02/olivero-design-exploration.png?itok=Si4jDLta" width="900" height="650" alt="D9 theme design exploration" /><figcaption class="image__caption">Examples of D9 theme designs during exploration.</figcaption></figure></div>
<p>We continued to iterate on the chosen design direction based on the feedback from the stakeholders. The design process continued with the addition of internal accessibility testing, which highlighted several contrast deficiencies that we subsequently fixed.</p>
<h3>Proof of Concept</h3>
<p>Throughout the process, we built a prototype in static HTML, CSS, and JavaScript. The intention was to validate the new features and help answer potential UI/UX issues that might arise during the design process. We also used it as an opportunity to vet the use of the CSS grid and ensure the front-end architecture could be accessible, as well as work with Internet Explorer 11 (and other core supported browsers). This proof of concept is not yet fully accessible, although it will be eventually. The next step is to get full sign-off from the Drupal accessibility team, which will hopefully alleviate last-minute time crunches when submitting the patch to Drupal core.</p>
<p>The following are key activities we’re focusing on:</p>
<ul><li>Investigating the use of the header on scroll interaction on mobile and tablet devices.</li>
<li>Validating the use of the CSS grid in legacy browsers such as Internet Explorer 11 and identifying whether or not we’ll need to account for progressive enhancement features.</li>
<li>Verifying that the markup is semantic and meets the accessibility requirements.</li>
</ul><div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide image--captioned" style="max-width: 1474px;"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-02/olivero-proof-of-concept.png?itok=JXeSV3v9 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-02/olivero-proof-of-concept.png?itok=umIOp2qS 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-02/olivero-proof-of-concept.png?itok=SbYFSQsa 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-02/olivero-proof-of-concept.png?itok=QXizvDXM 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-02/olivero-proof-of-concept.png?itok=Q44M_GMg 1474w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-02/olivero-proof-of-concept.png?itok=QXizvDXM" alt="Olivero Proof of Concept" /><figcaption class="image__caption">A screenshot of the proof of concept HTML.</figcaption></figure></div>
<h2>Community Announcement: The Formal Processes on Submitting Your Idea to the Community</h2>
<p>Once the design was in a good place, we drafted a proposal to the community and sought feedback for the work that had been done (see link - <a href="https://www.drupal.org/project/ideas/issues/3088378" target="_blank">Designs for new front-end theme for Drupal 9</a>). The announcement issue included several processes that we took to get to where we are today. The response from the community was overwhelmingly positive, and we were thrilled to see the excitement.</p>
<div class="inline-entity inline-entity--medium inline-entity--type-media inline-entity--bundle-image">
<figure class="image image--wide image--captioned" style="max-width: 1474px;"><img srcset="https://www.lullabot.com/sites/default/files/styles/max_325x325/public/2020-02/d9-design-proposal.png?itok=e-cx2rRg 325w, https://www.lullabot.com/sites/default/files/styles/max_650x650/public/2020-02/d9-design-proposal.png?itok=lRopKIrL 650w, https://www.lullabot.com/sites/default/files/styles/max_800/public/2020-02/d9-design-proposal.png?itok=6SA_RUAp 800w, https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-02/d9-design-proposal.png?itok=S6eE6od8 1300w, https://www.lullabot.com/sites/default/files/styles/max_2600x2600/public/2020-02/d9-design-proposal.png?itok=5jtCJhEU 1474w" sizes="(min-width: 1290px) 1290px, 100vw" src="https://www.lullabot.com/sites/default/files/styles/max_1300x1300/public/2020-02/d9-design-proposal.png?itok=S6eE6od8" alt="D9 Design Proposal" /><figcaption class="image__caption">Our design proposal for the new Drupal 9 theme.</figcaption></figure></div>
<h2>What's next?</h2>
<p>The Drupal 9 theme initiative is currently underway. If you're interested in contributing to the new Olivero frontend theme effort, please check out "<a href="https://www.drupal.org/docs/8/themes/olivero/how-to-contribute-to-olivero" target="_blank">How</a><a href="https://www.drupal.org/docs/8/themes/olivero/how-to-contribute-to-olivero" target="_blank"> to contribute to Olivero</a>" and get involved with the team.</p>
<p>Building Olivero was the first time some of us have contributed to a Drupal core initiative, and admittedly, it can be scary and a little overwhelming. Sometimes you don't feel like you have enough years of experience or enough in-depth specific knowledge. But no matter what your background or experience level is, chances are there’s something you can do to help within the open-source community. In our case, we happened to be in the right place and know the right people. However, having a well-thought-out proposal, identifying key stakeholders, and having a phenomenal team involved can give legitimacy to your idea. I hope hearing the journey of how we got here provides some helpful takeaways and inspires you to jump-start your idea and advocate for getting your initiatives into Drupal core.</p>
<p>A huge special thank you to everyone who has contributed to the Olivero project so far! We wouldn’t be where we are without your support. </p>Wed, 19 Feb 2020 13:11:37 -0500Putra Bonaccorsic3481b7e-f7c2-423f-94ec-7a9b0096ebac