Planet Drupal

Syndicate content - aggregated feeds in category Planet Drupal
Updated: 16 min 41 sec ago

Nuvole: Optimal deployment workflow for Composer-based Drupal 8 projects

Fri, 08/19/2016 - 13:20
div class=field field-name-field-blog-subtitle field-type-text-long field-label-hiddendiv class=field-itemsdiv class=field-item evenConsiderations following our Drupal Dev Day Milan and Drupalaton presentations; and a preview of our DrupalCon training./div/div/divdiv class=field field-name-body field-type-text-with-summary field-label-hiddendiv class=field-itemsdiv class=field-item evenpemThis post is an excerpt from the topics covered by our a href= Dublin training: Drupal 8 Development - Workflows and Tools/a/em./p pDuring the recent Nuvole presentations at a href= Dev Days Milan 2016/a and a href= Hungary 2016/a we received a number of questions on how to properly setup a Drupal 8 project with Composer. An interesting case where we discovered that existing practices are completely different from each other is: What is the best way to deploy a Composer-based Drupal 8 project?./p pWe'll quickly discuss some options and describe what works best for us./p h3What to commit/h3 pYou should commit:/p ulliThe codecomposer.json/code file: this is obvious when using Composer./li liThe codecomposer.lock/code file: this is important since it will allow you to rebuild the entire codebase at the same status it was at a given point in the past./li /ulpThe fully built site is commonly left out of the repository. But this also means that you need to find a way for rebuilding and deploying the codebase safely./p h3Don't run Composer on the production server/h3 pYou would clearly never run codecomposer update/code on the production server, as you want to be sure that you will be deploying the same code you have been developing upon. For a while, we considered it to be enough to have Composer installed on the server and run codecomposer install/code to get predictable results from the (committed) codecomposer.lock/code file./p pThen we discovered that this approach has a few shortcomings:/p ullipThe process is not robust. A transient network error or timeout might result in a failed build, thus introducing uncertainty factors in the deploy scripts. Easy to handle, but still not desirable as part of a delicate step such as deployment./p/li lipThe process will inevitably take long. If you run codecomposer install/code in the webroot directly, your codebase will be unstable for a few minutes. This is orders of magnitude longer than a standard update process (i.e., running codedrush updb/code and codedrush cim/code) and it may affect your site availability. This can be circumvented by building in a separate directory and then symlinking or moving directories./p/li lipEven codecomposer install/code can be unpredictable, especially on servers with restrictions or running different versions of Composer or PHP; in rare circumstances, a build may succeed but yield a different codebase. This can be mitigated by enforcing (e.g., through Docker or virtualization) a dev/staging environment that matches the production environment, but you are still losing control on a relatively lengthy process./p/li lipYou have no way of properly testing the newly built codebase after building it and before making it live./p/li lipComposer simply does not belong in a production server. It is a tool with a different scope, unrelated to the main tasks of a production server./p/li /ulh3Where to build the codebase? CI to the rescue/h3 pAfter ruling out the production server, where should the codebase be built then?/p pBuilding it locally (i.e., using a developer's environment) can't work: besides the differences between the development and the production (code--no-dev/code) setup, there is the risk of missing possible small patches applied to the local codebase. And a totally clean build is always necessary anyway./p pWe ended up using Continuous Integration for this task. Besides the standard CI job, which operates after any push operation to the branches under active development, performs a clean installation and runs automated tests, another CI job builds the full codebase based on the codemaster/code branch and the codecomposer.lock/code file. This allows sharing it between developers, a fast deployment to production through a tarball or rsync, and opportunities for actually testing the upgrade (with a process like: automatically import the production database, run database updates, import the new configuration, run a subset of automated tests to ensure that basic site functionality has no regressions) for maximum safety./p pSlides from our recent presentations, mostly focused on Configuration Management but covering part of this discussion too, are below./p /div/div/divdiv class=field field-name-field-blog-tags field-type-taxonomy-term-reference field-label-inline clearfixdiv class=field-labelTags:nbsp;/divdiv class=field-itemsdiv class=field-item evenDrupal Planet/divdiv class=field-item oddDrupal 8/divdiv class=field-item evenDrupalCon/divdiv class=field-item oddTraining/div/div/divdiv class=field field-name-field-blog-attachments field-type-file field-label-abovediv class=field-labelAttachments:nbsp;/divdiv class=field-itemsdiv class=field-item evenspan class=fileimg class=file-icon alt= title=application/pdf src=/modules/file/icons/application-pdf.png / a href= type=application/pdf; length=1105449 title=Configuration Management in Drupal 8 - DDD2016.pdfSlides: Configuration Management in Drupal 8/a/span/div/div/div

Jim Birch: Styling Views Exposed Filters Selects in Drupal 8

Fri, 08/19/2016 - 11:20
a href=;utm_medium=drupal-planetamp;utm_campaign=node/62 title=Styling Views Exposed Filters Selects in Drupal 8img class=img-responsive src= width=620 height=465 alt=Lego Uncle Jim Votes! //abr pStyling the HTML lt;selectgt; tag to appear similar in all the different browsers is a task unto itself.  It seems on each new site , I find myself back visiting a href= post by Ivor Reić/a for a CSS only solution.  My task for today is to use this idea to theme an exposed filter on a view./p pThe first thing we need to do is add a div around the select.  We can do this by editing the select's twig template from Drupal 8 core's stable theme.  Copy the file from/p pcode class=language-markup/core/themes/stable/templates/form/select.html.twig/code to/p pcode class=language-markup/themes/yourtheme/templates/form/select.html.twig/code/p pThen add the extra lt;div class=select-stylegt; and closing lt;/divgt; as so./p script src= is the LESS file that I compile which includes Ivor's CSS, but also some adjustments I added to event the exposed filter out. Each rule is commented, explaining what they do./p script src= will compile this into my final CSS and we are good to go.  The display of the form, and the select list should be pretty accurate to what I want across all modern browsers.  Adjust as needed for your styles and design./p pa href=;utm_medium=drupal-planetamp;utm_campaign=node/62 title=Styling Views Exposed Filters Selects in Drupal 8Read more/a/p

Cocomore: The Central Data Hub of VDMA - Tango REST Interface (TRI)

Fri, 08/19/2016 - 00:00
pOn the VDMA website (Association of German Machinery and Plant Engineering) various professional associations are specifically listed with their individual information. To provide each page with information from the Tango Backend, a specific interface has been developed: The so-called Tango REST interface. In the seventh part of our series “The Central Data Hub of VDMA” we will introduce this interface, its technical realization and its functions. /p

Zivtech: Staff Augmentation and Outsourced Training: Do You Need It?

Thu, 08/18/2016 - 21:53
div class=field-image-photo img src=/sites/default/files/styles/large/public/25392653883_38957b36f7_k.jpg?itok=SwoAEVpJ width=645 height=431 alt= / /div div class=field-body The goal of any company is to reduce costs and increase profit, especially when it comes to online and IT projects. When an IT undertaking is a transitional effort, it makes sense to consider staff augmentation and /br / Consider the marketing efforts of one worldwide corporation. Until recently, each brand and global region built and hosted its own websites independently, often without a unified coding and branding standard. The result was a disparate collection of high maintenance, costly brand websites. h2A Thousand Sites: One Goals/h2 br / The organization has created nearly a thousand sites in total, but those sites were not developed at the same time or with the same goals. That’s a pain point. To solve this problem, the company decided to standardize all of its websites onto a single reference architecture, built on Drupal. br /br / The objective of the new proprietary platform includes universal standards, a single platform that can accommodate regional feature sets, automated testing, and sufficient features that work for 95% of use cases for the company’s websites /br / While building a custom platform is a great step forward, it must then be implemented, and staff needs to be brought up to speed. To train staff on technical skills and platforms, often the best solution is to outsource the training to experts who step in, take over training and propel the effort forward /br / As part of an embedded team, an outsourced trainer is an adjunct team member, attending all of the scrum meetings, with a hand in the future development of the training materials. h2Train Diverse Audiences/h2 br / A company may invest a lot of money into developing custom features, and trainers become a voice for the company, showing people how easy it is to implement, how much it is going to help, and how to achieve complex tasks such as activation processes. The goal is to get people to adopt the features and platform. Classroom style training allows for exercises on live sites and familiarity with specific features. h2The Training Workflow/h2 br / Trainers work closely with the business or feature owner to build a curriculum. It’s important to determine the business needs that inspired the change or addition. br /br / Starting with an initial outline, trainers and owners work together. Following feedback, more information gets added to flesh it out. This first phase can take four to five sessions to get the training exactly right for the business owner. For features that follow, the process becomes streamlined. It's more intuitive because the trainer has gotten through all the steps and heard the pain points, but it’s important to always consult the product owner. Once there is a plan, the trainers rehearse the curriculum to see what works, what doesn’t work, what’s too long, and where they need to cut things. h2Training Now amp; Future/h2 br / Training sessions may be onsite or remote. It is up to the business to decide if attendance is mandatory. Some staffers may wish to attend just to keep up with where the business is /br / Sessions are usually two hours with a lot of time for Qamp;A. With trainings that are hands-on, it’s important to factor in time for technical difficulties and different levels of digital competence. br /br / Remote trainings resemble webinars. Trainers also create videos to enable on demand trainings. They may be as simple as screencasts with a voiceover, but others have a little more work involved. Some include animations to demo tasks in a friendlier way before introducing a more static backend form. It is the job of the trainer to tease out what’s relevant to a wide net of audiences. br /br / The training becomes its own product that can live on. The recorded sessions are valuable to onboard and train up future employees. Trainers add more value to existing products and satisfy management goals. /div nav role=navigation class=links-inline/nav

Chromatic: Migrating (away) from the Body Field

Thu, 08/18/2016 - 19:46
pAs we move towards an ever more structured digital world of APIs, metatags, structured data, etc., and as the need for content to take on many forms across many platforms continues to grow, the humble “body” field is struggling to keep up. No longer can authors simply paste a word processing document into the body field, fix some formatting issues and call content complete. Unfortunately, that was the case for many years and consequently there is a lot of valuable data locked up in body fields all over the web. Finding tools to convert that content into useful structured data without requiring editors to manually rework countless pieces of content is essential if we are to move forward efficiently and accurately./p pHere at Chromatic, we recently tackled this very problem. We leveraged the Drupal a href= module to transform the content from unstructured body fields into re-usable entities. The following is a walkthrough./p h2Problem/h2 pOn this particular site, thousands of articles from multiple sources were all being migrated into Drupal. Each article had a title and body field with all of the images in each piece of content embedded into the body as codeimg/code tags. However, our new data model stored images as separate entities along with the associated metadata. Manually downloading all of the images, creating new image entities, and altering the image tags to point to the new image paths, clearly was not a viable or practical option. Additionally, we wanted to convert all of our images to lazy loaded images, so having programmatic control over the image markup during page rendering was going to be essential. We needed to automate these tasks during our migration./p h2Our Solution/h2 pSince we were already migrating content into Drupal, adapting a href= to both migrate the content in and fully transform it all in one repeatable step was going to be the best solution. The Migrate module offers many great a href= classes/a, but none can use codeimg/code elements within a string of HTML as a source. We quickly realized we would need to a href= a custom source class/a./p pA quick overview of the steps we’d be taking:/p ol liBuilding a new source class to find codeimg/code tags and provide them as a migration source./li liCreating a migration to import all of the images found by our new source class./li liConstructing a callback for our content migration to translate the codeimg/code tags into tokens that reference the newly created image entities./li /ol h3Building the source class/h3 pMigrate source classes work by finding all potential source elements and offering them to the migration class in an iterative fashion. So we need to find all of the potential image sources and put them into an array that can used a source for a migration. Source classes also need to have a unique key for each potential source element. During a migration the codegetNextRow()/code method is repeatedly called from the parent codeMigrateSource/code class until it returns codeFALSE/code. So let's start there and work our way back to the logic that will identify the potential image sources./p precode** * Fetch the next row of data, returning it as an object. * * @return object|bool * An object representing the image or FALSE when there is no more data available. */ public function getNextRow() { // Since our data source isn't iterative by nature, we need to trigger our // importContent method that builds a source data array and counts the number // of source records found during the first call to this method. $this-gt;importContent(); if ($this-gt;matchesCurrent lt; $this-gt;computeCount()) { $row = new stdClass(); // Add all of the values found in @see findMatches(). $match = array_shift(array_slice($this-gt;matches, $this-gt;matchesCurrent, 1)); foreach ($match as $key =gt; $value) { $row-gt;{$key} = $value; } // Increment the current match counter. $this-gt;matchesCurrent++; return $row; } else { return FALSE; } } /code/pre pNext let's explore our codeimportContent()/code method called above. First, it verifies that it hasn't already been executed, and if it has not, it calls an additional method called codebuildContent()/code./p precode/** * Find and parse the source data if it hasn't already been done. */ private function importContent() { if (!$this-gt;contentImported) { // Build the content string to parse for images. $this-gt;buildContent(); // Find the images in the string and populate the matches array. $this-gt;findImages(); // Note that the import has been completed and does not need to be // performed again. $this-gt;contentImported = TRUE; } } /code/pre pThe codebuildContent()/code method calls our codecontentQuery()/code method which allows us to define a custom database query object. This will supply us with the data to parse through. Then back in the codebuildContent()/code method we loop through the results and build the codecontent/code property that will be parsed for image tags./p precode/** * Get all of the HTML that needs to be filtered for image tags and tokens. */ private function buildContent() { $query = $this-gt;contentQuery(); $content = $query-gt;execute()-gt;fetchAll(); if (!empty($content)) { // This builds one long string for parsing that can done on long strings without // using too much memory. Here, we add fields ‘foo’ and ‘bar’ from the query. foreach ($content as $item) { $this-gt;content .= $item-gt;foo; $this-gt;content .= $item-gt;bar; } // This builds an array of content for parsing operations that need to be performed on // smaller chunks of the source data to avoid memory issues. This is is only required // if you run into parsing issues, otherwise it can be removed. $this-gt;contentArray[] = array( 'title' =gt; $item-gt;post_title, 'content' =gt; $item-gt;post_content, 'id' =gt; $item-gt;id, ); } } /code/pre pNow we have the the logic setup to iteratively return row data from our source. Great, but we still need to build an array of source data from a string of markup. To do that, we call our custom codefindImages()/code method from codeimportContent()/code, which does some basic checks and then calls all of the image locating methods./p pWe found it is best to create methods for each potential source variation, as image tags often store data in multiple formats. Some examples are pre-existing tokens, full paths to CDN assets, relative paths to images, etc. Each often requires unique logic to parse properly, so separate methods makes the most sense./p precode/** * Finds the desired elements in the markup. */ private function findImages() { // Verify that content was found. if (empty($this-gt;content)) { $message = 'No html content with image tags to download could be found.'; watchdog('example_migrate', $message, array(), WATCHDOG_NOTICE, 'link'); return FALSE; } // Find images where the entire source content string can be parsed at once. $this-gt;findImageMethodOne(); // Find images where the source content must be parsed in chunks. foreach ($this-gt;contentArray as $id =gt; $post) { $this-gt;findImageMethodTwo($post); } } /code/pre pThis example uses a regular expression to find the desired data, but you could also use a href= Simple HTML DOM Parser/a or the library of your choice. It should be noted that I opted for a regex example here to keep library-specific code out of my code sample. However, we would highly recommend using a DOM parsing library instead./p precode/** * This is an example of a image finding method. */ private function findImageMethodOne() { // Create a regex to look through the content. $matches = array(); $regex = '/regex/to/find/images/'; preg_match_all($regex, $this-gt;content, $matches, PREG_SET_ORDER); // Set a unique row identifier from some captured pattern of the regex- // this would likely be the full path to the image. You might need to // perform cleanup on this value to standardize it, as the path // to /foo/bar/image.jpg,, and // should not create three unique // source records. Standardizing the URL is key for not just avoiding // creating duplicate source records, but the URL is also the ID value you // will use in your destination class mapping callback that looks up the // resulting image entity ID from the data it finds in the body field. $id = ‘’; // Add to the list of matches after performing more custom logic to // find all of the correct chunks of data we need. Be sure to set // every value here that you will need when constructing your entity later. $this-gt;matches[$id] = array( 'url' =gt; $src, 'alt' =gt; $alttext, 'title' =gt; $description, 'credit' =gt; $credit, 'id' =gt; $id, 'filename' =gt; $filename, 'custom_thing' =gt; $custom_thing, ); } /code/pre h3Importing the images/h3 pNow that we have our a href= class/a complete, let's import all of the image files into image entities./p precode/** * Import images. */ class ExampleImageMigration extends ExampleMigration { /** * {@inheritdoc} */ public function __construct($arguments) { parent::__construct($arguments); $this-gt;description = t('Creates image entities.'); // Set the source. $this-gt;source = new ExampleMigrateSourceImage(); ... /code/pre pThe rest of the codeExampleImageMigration/code is available in a a href=, but it has been omitted here for brevity. It is just a standard migration class that maps the array keys we put into the codematches/code property of the source class to the fields of our image entity./p h3Transforming the image tags in the body/h3 pWith our image entities created and the associated migration added as a dependency, we can begin sorting out how we will convert all of the image tags to a href= This obviously assumes you are using tokens, but hopefully this will shed light on the general approach, which can then be adapted to your specific needs./p pInside our article migration (or whatever you happen to be migrating that has the image tags in the body field) we implement the codecallbacks()/code method on the body field mapping./p precode// Body. $this-gt;addFieldMapping('body', 'post_content') -gt;callbacks(array($this, 'replaceImageMarkup')); /code/pre pNow let's explore the logic that replaces the image tags with our new entity tokens. Each of the patterns references below will likely correspond to unique methods in the codeExampleMigrateSourceImage/code class that find images based upon unique patterns./p precode/** * Converts images into image tokens. * * @param string $body * The body HTML. * * @return string * The body HTML with converted image tokens. */ protected function replaceImageMarkup($body) { // Convert image tags that follow a given pattern. $body = preg_replace_callback(self::IMAGE_REGEX_FOO, `fooCallbackFunction`, $body); // Convert image tags that follow a different pattern. $body = preg_replace_callback(self::IMAGE_REGEX_BAR, `barCallbackFunction`, $body); return $body; /code/pre pIn the various callback functions we need to do several things:/p ol liAlter the source string following the same logic we used when we constructed our potential sources in our source class. This ensures that the value passed in the code$source_id/code variable below matches a value in the mapping table created by the image migration./li liNext we call the codehandleSourceMigration()/code method with the altered source value, which will find the destination id associated with the source id./li liWe then use the returned image entity id to construct the token and replace the image markup in the body data./li /ol precode$image_entity_id = self::handleSourceMigration('ExampleImageMigration', $source_id); /code/pre h2Implementation Details/h2 pAstute observers will notice that we called codeself::handleSourceMigration()/code, not code$this-gt;handleSourceMigration/code. This is due to the fact that the codehandleSourceMigration()/code method defined in the codeMigrate/code class is not static and uses code$this/code within the body of the method. Callback functions are called statically, so the reference to code$this/code is lost. Additionally, we can't instantiate a new codeMigration/code class object to get around this, as the codeMigrate/code class is an abstract class. You also cannot pass the current codeMigrate/code object into the callback function, due to the codeMigrate/code class not supporting additional arguments for the codecallbacks()/code method./p pThus, we are stuck trying to implement a singleton or global variable that stores the current codeMigrate/code object, or duplicating the codehandleSourceMigration()/code method and making it work statically. We weren’t a fan of either option, but we went with the latter. Other ideas or reasons to choose the alternate route are welcome!/p pIf you go the route we chose, these are the lines you should remove from the codehandleSourceMigration/code method in the codeMigrate/code class when you duplicate it into one of your custom classes./p precode- // If no destination ID was found, give each source migration a chance to - // create a stub. - if (!$destids) { - foreach ($source_migrations as $source_migration) { - // Is this a self reference? - if ($source_migration-gt;machineName == $this-gt;machineName) { - if (!array_diff($source_key, $this-gt;currentSourceKey())) { - $destids = array(); - $this-gt;needsUpdate = MigrateMap::STATUS_NEEDS_UPDATE; - break; - } - } - // Break out of the loop if a stub was successfully created. - if ($destids = $source_migration-gt;createStubWrapper($source_key, $migration)) { - break; - } - } - } /code/pre pBefore we continue, let's do a quick recap of the steps along the way./p ol liWe made an iterative source of all images from a source data string by creating the a href= class that extends the codeMigrateSource/code class./li liWe then used a href= as a migration source class the in the a href= class to import all of the images as new structured entities./li liFinally, we built our actual content migration and used the codecallbacks()/code method on the body field mapping in conjunction with the codehandleSourceMigration()/code method to convert the existing image markup to entity based tokens./li /ol h2The end result/h2 pWith all of this in place, you simply sit back and watch your migrations run! Of course before that, you get the joy of running it countless times and facing edge cases with malformed image paths, broken markup, new image sources you were never told about, etc. Then at the end of the day you are left with shiny new image entities full of metadata that are able to be searched, sorted, filtered, and re-used! Thanks to token rendering (if you go that route), you also gain full control over how your codeimg/code tags are rendered, which greatly simplifies the implementation of lazy-loading or responsive images. Most importantly, you have applied structure to your data, and you are now ready to transform and adapt your content as needed for any challenge that is thrown your way!/p

Jeff Geerling's Blog: Increase the Guzzle HTTP Client request timeout in Drupal 8

Thu, 08/18/2016 - 18:56
div class=field field-name-body field-type-text-with-summary field-label-hiddendiv class=field-itemsdiv class=field-item even property=content:encodedpDuring some migration operations on a Drupal 8 site, I needed to make an HTTP request that took gt; 30 seconds to return all the data... and when I ran the migration, I'd end up with exceptions like:/p div class=codeblockcodeMigration failed with source plugin exception: Error message: cURL error 28: Operation timed out after 29992 milliseconds with 2031262 out of 2262702 bytes received (see pThe solution, it turns out, is pretty simple! Drupal's a href=\Drupal\Core\Http\ClientFactory/code/a is the default way that plugins like Migrate's HTTP fetching plugin get a Guzzle client to make HTTP requests (though you could swap things out if you want via codeservices.yml/code), and in the code for that factory, there's a line after the defaults (where the code'timeout' =gt; 30/code is defined) like:/p/div/div/div

Valuebound: Get your Drupal8 Development platform ready with Drush8!

Thu, 08/18/2016 - 15:17
pAs we all know, we need Drush8 for our Drupal8 development platform. I have tried a href= target=_blankinstalling Drush 8 using composer/a, but sometimes it turns out to be a disaster, especially when you try to a href= target=_blankinstall Drush 8 on the Digital Ocean Droplet having Ubuntu 16.04/a./p pI have faced the same issue in the last few months to get the Drupal8 development platform ready with Drush8. So I have decided to find a solution to fix that forever. Well, finally found one which are the following lines of commands./p pre cd ~ php -r readfile(''); gt; drush chmod +x drush sudo mv drush /usr/bin drush init /pre pIf you are…/p

Drupal core announcements: We can add big new things to Drupal 8, but how do we decide what to add?

Thu, 08/18/2016 - 14:48
pDrupal 8 introduced the use of a href= Versioning/a, which practically means the use of three levels of version numbers. The current release is Drupal 8.1.8. While increments of the last number are done for bugfixes, the middle number is incremented when we add new features in a backwards compatible way. That allows us to add big new things to Drupal 8 while it is still compatible with all your themes and modules. We successfully added some new modules like BigPipe, Place Block, etc./p pBut how do we decide what will get in core? Should people come up with ideas, build them, and once they are done, they are either added in core or not? No. Looking for feedback at the end is a huge waste of time, because maybe the idea is not a good fit for core, or it clashes with another improvement in the works. But how would one go about getting feedback earlier?/p pWe held two well attended core conversations at the last DrupalCon in New Orleans titled a href= potential in Drupal 8.x and how to realize it/a and a href= for UX changes big and small/a both of which discussed a more agile approach to avoid wasting time./p pThe proposal is to separate the ideation and prototyping process from implementation. Within the implementation section the potential use of experimental modules helps with making the perfecting process more granular for modules. We are already actively using that approach for implementation. On the other hand the ideation process is still to be better defined. That is where we need your feedback now./p pSee a href= title= for the issue to discuss this. Looking forward to your feedback there. /p

Mediacurrent: How Drupal won an SEO game without really trying

Thu, 08/18/2016 - 14:23
img typeof=foaf:Image src= width=200 height=152 alt=Image of magnifying glass with amp;quot;SEOamp;quot; in the center title=Image of magnifying glass with amp;quot;SEOamp;quot; in the center / pAt Mediacurrent we architected and built a Drupal site for a department of a prominent U.S. university several years ago. As part of maintaining and supporting the site over the years, we have observed how well it has performed in search engine rankings, often out-performing other sites across campus built on other platforms./p

KnackForge: Drupal Commerce - PayPal payment was successful but order not completed

Thu, 08/18/2016 - 12:00
span data-quickedit-field-id=node/358/title/en/teaser class=field field--name-title field--type-string field--label-hiddenDrupal Commerce - PayPal payment was successful but order not completed /span div data-quickedit-field-id=node/358/body/en/teaser class=clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__itempMost of us use PayPal as a payment gateway for our eCommerce sites. Zero upfront, No maintenance fee, API availability and documentation makes anyone easy to get started. At times online references offer out-dated documentation or doesn't apply to us due to account type (Business / Individual), Country of the account holder, etc. We had this tough time when we wanted to set up Auto return to Drupal website./p/div span data-quickedit-field-id=node/358/uid/en/teaser class=field field--name-uid field--type-entity-reference field--label-hiddena title=View blogs by sivaji. href= lang= about=/sivaji typeof=schema:Person property=schema:name datatype= itemprop=url rel=author class=usernamesivaji/a/span span data-quickedit-field-id=node/358/created/en/teaser class=field field--name-created field--type-created field--label-hiddenThu, 08/18/2016 - 15:30/span div data-quickedit-field-id=node/358/field_blog_tag/en/teaser class=field field--name-field-blog-tag field--type-entity-reference field--label-above div class=field__labelTag(s)/div div class=field__items div class=field__itema href=/tags/drupal-planet hreflang=enDrupal planet/a/div div class=field__itema href=/tags/drupal-7 hreflang=enDrupal 7/a/div div class=field__itema href=/tags/dropthemes div class=field__itema href=/tags/drupal-commerce hreflang=endrupal-commerce/a/div /div /div div class=node__links ul class=links inlineli class=node-readmorea href=/blog/sivaji/drupal-commerce-paypal-payment-was-successful-order-not-completed rel=tag title=Drupal Commerce - PayPal payment was successful but order not completed hreflang=enRead morespan class=visually-hidden about Drupal Commerce - PayPal payment was successful but order not completed /span/a/lili class=comment-adda href=/blog/sivaji/drupal-commerce-paypal-payment-was-successful-order-not-completed#comment-form title=Share your thoughts and opinions. hreflang=enAdd new comment/a/li/ul /div

Unimity Solutions Drupal Blog: Video Annotations: A Powerful and Innovative Tool for Education

Thu, 08/18/2016 - 08:51
div class=field field-name-body field-type-text-with-summary field-label-hiddendiv class=field-itemsdiv class=field-item even property=content:encodedpAccording to a href= J Medina/a a famous molecular biologist “Vision trumps all other senses.” Human mind is attracted to remember dynamic pictures rather than listen to words or read long texts. Advancement in multimedia has enabled teachers to impart visual representation of content in the class room./p /div/div/div

Drupalize.Me: Learn by Mentoring at DrupalCon

Thu, 08/18/2016 - 08:37
div class=field field-name-body field-type-text-with-summary field-label-hidden text-content text-secondarydiv class=field-itemsdiv class=field-item evenpDrupalCon is a great opportunity to learn all kinds of new skills and grow professionally. For the 3 days of the main conference in Dublin (September 27–29) there will be sessions on just about everything related to Drupal that you could want. One amazing opportunity that you may not be aware of though is the Mentored Sprint on Friday, September 30th. This is a great place for new folks to learn the ropes of our community and how to contribute back. What may be less talked about is the chance to a href= a mentor/a. /p /div/div/divdiv id=comment-wrapper-nid-2681/div

Roy Scholten: Vetting Drupal product ideas

Wed, 08/17/2016 - 23:57
div class=field field-name-field-image field-type-image field-label-hiddendiv class=field-itemsdiv class=field-item evena href=/pieces/vetting-drupal-product-ideasimg src= width=640 height=627 alt=iterate on the idea before implementation //a/div/div/divdiv class=field field-name-body field-type-text-with-summary field-label-hiddendiv class=field-itemsdiv class=field-item evenpWe’ve made big strides since Drupalcon New Orleans in how we add new features to Drupal core. The concept of experimental modules has already helped us introduce features like a new way to add blocks to a page, content moderation and workflow tools and a whole new approach for editing all the things on a page while staying at that page./p pIn New Orleans we started to define the a href= for making these kinds of big changes/a. Probably the most important and defining aspect of it is that we’re (finally!) enabling a clearer separation between vetting ideas first, implementation second./p pTrue to form we a href= and detailed the latter part first/a :-)/p pSo, on to that first part, vetting Drupal product ideas. In a href= core conversation/a I outlined the need for making bigger UX changes, faster and suggested possible approaches for how to design and develop those, borrowing heavily from the a href= UX method/a/p pSince then, we’ve been reminded that we really do need a clearly defined space to discuss the strategic value of proposed new features. A place to decide if a given idea is desirable and viable as an addition to core./p pThe point being: core product manager with help from Drupal UX team members wrote up a href= proposal for how to propose core product ideas/a and what’s needed to turn a good idea into an actionable plan./p pIt needs your feedback. a href= read and share your thoughts/a./p /div/div/divdiv class=field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-inline clearfixdiv class=field-labelTags:nbsp;/divdiv class=field-itemsdiv class=field-item evena href=/tag/drupaldrupal/a/divdiv class=field-item odda href=/tag/uxux/a/divdiv class=field-item evena href=/tag/processprocess/a/divdiv class=field-item odda href=/drupaldrupalplanet/a/div/div/divdiv class=field field-name-field-sub-title field-type-text field-label-abovediv class=field-labelSub title:nbsp;/divdiv class=field-itemsdiv class=field-item evenAgree on why and what before figuring out the how/div/div/div

Mediacurrent: DrupalCon NOLA: The People Behind the Usernames

Wed, 08/17/2016 - 20:33
img typeof=foaf:Image src= width=200 height=152 / pAs we work every day on our own projects, with our own deadlines and priorities, it is often too easy to forget about the entire community of others using Drupal in much the same way. When we're working with Drupal in our various capacities, there is no shortage of methods to interact with the community and contribute back, but those aren't the focus of this post./p Drupal 6 security updates for Panels!

Wed, 08/17/2016 - 20:16
div class=field field-name-body field-type-text-with-summary field-label-hiddendiv class=field-itemsdiv class=field-item evenpemAs you may know, a href= 6 has reached End-of-Life (EOL)/a which means the Drupal Security Team is no longer doing Security Advisories or working on security patches for Drupal 6 core or contrib modules - but the a href= 6 LTS vendors/a are and we're one of them!/em/ppstrongToday, there is a Critical security releases for the a href= modules for multiple Access Bypass vulnerabilities./strong/ppThe first vulnerability allows anonymous users to use AJAX callbacks to pull content and configuration from Panels, which allow them to access private data. And the second, allows authenticated users with permission to use the Panels IPE to modify the Panels display for pages that they don't have permission to otherwise edit./ppSee the a href= advisory for Drupal 7/a for more information./ppHere you can download the a href= for 6.x-3.x/a!/ppstrong/strongIf you have a Drupal 6 site using the Panels module, we recommend you update immediately! We have already deployed the patch for all of our Drupal 6 Long-Term Support clients. :-)/ppstrongIf you'd like all your Drupal 6 modules to receive security updates and have the fixes deployed the same day they're released, please a href= out our D6LTS plans/a./strong/ppemspanNote/span: if you use the a href= module (totally free!), you'll be alerted to these and any future security updates, and will be able to use drush to install them (even though they won't necessarily have a release on

Rakesh's DSoC 16 blog: Last week - GSoC16 - Detected and Solved Merge Conflicts in Drupal8.

Wed, 08/17/2016 - 20:04
span property=schema:name class=field field--name-title field--type-string field--label-hiddenLast week - GSoC16 - Detected and Solved Merge Conflicts in Drupal8./span span rel=schema:author class=field field--name-uid field--type-entity-reference field--label-hiddenspan lang= about=/user/1 typeof=schema:Person property=schema:name datatype=rakesh/span/span span property=schema:dateCreated content=2016-08-17T18:04:06+00:00 class=field field--name-created field--type-created field--label-hiddenWed, 08/17/2016 - 23:34/span

Miloš Bovan: The final week of Google Summer of Code 2016

Wed, 08/17/2016 - 19:56
span property=schema:nameThe final week of Google Summer of Code 2016/span div property=schema:text class=field field--name-body field--type-text-with-summary field--label-hidden field--itemp dir=ltrIt’s been more than 13 warm weeks since I’ve started to work on mya href= Google Summer of Code 2016 - Mailhandler project/a. During the past few weeks, I finished a href= coding/a and worked on latest improvements related to documentation./p p dir=ltrIn the past week, the project documentation has been updated which was the last step before merging a href= repository/a and a href= project/a into a href= Drupal 8 release section was updated and it summarizes the features of D8 version. If you are wondering where you can use the code I developed this summer, feel free to read about a href= Mailhanlder use-cases/a./p p dir=ltrThis week, I am doing the latest cleanups and preparing the project for the final evaluation. It is the second evaluation after I submitted the a href= evaluation/a. In case you missed the previous posts from a href= Summer of Code series/a, I am providing the project retrospect /  /p h3 dir=ltrProject Retrospect/h3 p dir=ltrIn early February, in an ordinary conversation, a friend of mine told me about this year’s a href= Summer of Code/a program. I got interested in it since I took apart in a href= (The Google Highly Open Participation Contest; This program was replaced with a href= Code-In/a) during /br / In March, I’ve found out that Drupal will be one of the participating organizations and since I did a Drupal internship last year, this seemed to be a perfect opportunity to continue open-source contribution./p p dir=ltrAmong many interesting project ideas, I decided for “Port Mailhandler to Drupal 8”. Miro Dietiker proposed the project and during the proposal discussions, Primoz Hmeljak joined the mentoring team too./p p dir=ltrThe great news came in April. I was one of the 11 students chosen to contribute Drupal this summer! First Yay moment!/p p dir=ltrThe project progress could have been followed on this blog, so I’m will not go deeper into /br / 3 months of work passed quickly and at this point, I can confirm that I learned a ton of new things. I improved not only my coding skills but communication and project management skills too./p p dir=ltrIn my opinion, we reached all the goals we put on the paper in April. All the features defined in the proposal were developed, tested and documented./p p dir=ltrGoogle Summer of Code is a great program and I would sincerely recommend all the students to consider participating in it./p h3 dir=ltrFuture plans/h3 p dir=ltrThe future plans about the module are related to its maintenance. Inmail is still under development and it seems it will be ready for an alpha release very soon. In the following days, I am going to create an issue about nice-to-have features of Mailhandler. This issue could serve as a place for Drupal community to discuss the possible ways of the future Mailhandler development./p h3 dir=ltrbr / Thank you note/h3 p dir=ltrLast but not least, I would like to give huge thanks to my mentors (Miro Dietiker and Primoz Hmeljak) for being so supportive, helpful and flexible during this summer. Their mentoring helped me in many ways - from the proposal re-definition in April/May, endless code iterations, discussions of different ideas to blog post reviews, including this one :)./p p dir=ltrI would like to thank my company too for allowing me to contribute Drupal via this program and Google for giving me an opportunity to participate in this great program./p p dir=ltr /p p dir=ltr /p/div span rel=schema:authorspan lang= about=/user/4 typeof=schema:Person property=schema:name datatype=Milos/span/span span property=schema:dateCreated content=2016-08-17T17:56:30+00:00Wed, 08/17/2016 - 19:56/span div class=field field--name-field-tags field--type-entity-reference field--label-above div class=field--labelTags/div div class=field__items div class=field--itema href=/taxonomy/term/2 property=schema:about hreflang=enDrupal/a/div div class=field--itema href=/taxonomy/term/1 property=schema:about hreflang=enGoogle Summer of Code/a/div div class=field--itema href=/taxonomy/term/3 property=schema:about hreflang=enOpen source/a/div div class=field--itema href=/taxonomy/term/4 property=schema:about hreflang=enDrupal Planet/a/div /div /div section rel=schema:comment h2Add new comment/h2 drupal-render-placeholder callback=comment.lazy_builders:renderForm arguments=0=nodeamp;1=13amp;2=commentamp;3=comment token=9db1ea3d/drupal-render-placeholder /section

Acquia Developer Center Blog: Debugging Drupal 8 in PhpStorm. Part 2: Local Web-based Debugging in Mac OS X, Acquia Dev Desktop 2, and XDebug

Wed, 08/17/2016 - 17:10
div class=field field-name-field-blog-image field-type-image field-label-hiddendiv class=field-itemsdiv class=field-item evenimg typeof=foaf:Image src= width=140 height=85 alt=bug on keyboard //div/div/divdiv class=field field-name-body field-type-text-with-summary field-label-hiddendiv class=field-itemsdiv class=field-item even property=content:encodedpWelcome to Post #2 in my series about debugging in Drupal 8./p/div/div/divdiv class=field field-name-field-blog-tags field-type-taxonomy-term-reference field-label-inline clearfixdiv class=field-labelTags:nbsp;/divdiv class=field-itemsdiv class=field-item evena href=/tags/acquia-drupal-planet typeof=skos:Concept property=rdfs:label skos:prefLabel datatype=acquia drupal planet/a/div/div/div Tagged services in Drupal 8

Wed, 08/17/2016 - 16:07
div class=field-bodypWe saw how to write a a href= service container/a in Drupal earlier. We shall build a tagged service now. To illustrate a proper use case for tagged services, let's contrive a scenario where you add a pipeline custom filters to user text before rendering it on the page./p pFirst, clone the code which will be used in this post./p precode$ git clone /code/pre pCheckout the first version of the code where we take custom text from user, process and display it in a page without using services./p precode$ cd process_text $ git checkout -f just-filtering /code/pre pWe get custom text from a user using a config form./p precodeclass CustomTextSettingForm extends ConfigFormBase { /** * {@inheritdoc} */ protected function getEditableConfigNames() { return [ 'process_text.settings', ]; } /** * {@inheritdoc} */ public function getFormId() { return 'custom_text_setting_form'; } /** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { $config = $this-gt;config('process_text.settings'); $form['custom_text'] = [ '#type' =gt; 'textarea', '#title' =gt; $this-gt;t('Custom Text'), '#default_value' =gt; $config-gt;get('custom_text'), ]; return parent::buildForm($form, $form_state); } /** * {@inheritdoc} */ public function validateForm(array amp;$form, FormStateInterface $form_state) { parent::validateForm($form, $form_state); } /** * {@inheritdoc} */ public function submitForm(array amp;$form, FormStateInterface $form_state) { parent::submitForm($form, $form_state); $this-gt;config('process_text.settings') -gt;set('custom_text', $form_state-gt;getValue('custom_text')) -gt;save(); $form_state-gt;setRedirect(''); } } /code/pre pWe save it as a piece of configuration called codeprocess_text.settings.custom_text/code./p pBefore rendering this text, let's say you would want to:/p ul liRemove any codelt;divgt;/code tags./li liSubstitute a token code[greeting]/code with codelt;span classgreetinggt;hello worldlt;/spangt;/code throughout the text./li /ul pWe get the text and do all the above processing inside a custom controller./p precodeclass ProcessTextController extends ControllerBase { /** * Processtext. * * @return string * Return processed custom text. */ public function processText() { $custom_text = \Drupal::config('process_text.settings')-gt;get('custom_text'); // do processing // remove divs $custom_text = str_replace([lt;divgt;, lt;/divgt;], , $custom_text); // replace greeting tokens $custom_text = str_replace([greeting], 'lt;span class=greetinggt;hello worldlt;/spangt;', $custom_text); return [ '#type' =gt; 'markup', '#markup' =gt; $custom_text ]; } } /code/pre pThis is good, but we could do better. What if we change the filter applying mechanism? We have to change this code. Instead, let's convert it into a service./p precode$ cd process_text $ git checkout -f services-first-cut /code/pre pOur text filter service takes a set of filters and applies them to a given text when we call codeapplyFilters/code./p precodeclass TextFilterService { private $filters = []; /** * @param Filter $filter */ public function addFilter(Filter $filter) { $this-gt;filters[] = $filter; } /** * applies all filters to given text and returns * filtered text. * * @param string $txt * * @return string */ public function applyFilters($txt) { foreach ($this-gt;filters as $filter) { $txt = $filter-gt;apply_filter($txt); } return $txt; } } /code/pre pWe need to crate a services.yml file for the above service./p precodeservices: process_text.text_filter: class: Drupal\process_text\TextFilterService /code/pre pHere's how the codeprocessText/code function text looks now./p precodepublic function processText() { $custom_text = \Drupal::config('process_text.settings')-gt;get('custom_text'); // do processing using a service $filter_service = \Drupal::service('process_text.text_filter'); // remove divs $filter_service-gt;addFilter(new RemoveDivs()); // substitute greeting token $filter_service-gt;addFilter(new Greeting()); // apply all the above filters $custom_text = $filter_service-gt;applyFilters($custom_text); return [ '#type' =gt; 'markup', '#markup' =gt; $custom_text ]; } /code/pre pNow the filter applying mechanism is swappable. We can add write a different functionality and inject that implementation using service containers./p pNow, what if we want to add a new filter to this code, like, enclosing the whole text within a codelt;pgt;/code tag./p pSure. We could do that./p pLet's checkout the specific tag where we add a new filter./p precode$ cd process_text $ git checkout -f add-new-filter /code/pre pWe build that filter./p precodeclass EnclosePTags implements Filter { public function apply_filter($txt) { return 'lt;pgt;'. $txt . 'lt;/pgt;'; } } /code/pre p#x2026;and add it to the set of filters being applied./p precodepublic function processText() { $custom_text = \Drupal::config('process_text.settings')-gt;get('custom_text'); // do processing using a service $filter_service = \Drupal::service('process_text.text_filter'); // remove divs $filter_service-gt;addFilter(new RemoveDivs()); // substitute greeting token $filter_service-gt;addFilter(new Greeting()); // Enclose p tags $filter_service-gt;addFilter(new EnclosePTags()); // apply all the above filters $custom_text = $filter_service-gt;applyFilters($custom_text); return [ '#type' =gt; 'markup', '#markup' =gt; $custom_text ]; } /code/pre pHow about injecting the filter adding mechanism itself? Wouldn't it be cool if we are able to add new filters without changing this part of the code? Not to mention the fact that the code will be more testable than before if we follow this approach. This is exactly what tagged services help us accomplish./p pLet's write each filter as a tagged service./p precode$ cd process_text $ git checkout -f tagged-services /code/pre pHere's how our looks now./p precodeservices: process_text.text_filter: class: Drupal\process_text\TextFilterService tags: - { name: service_collector, tag: text_filter, call: addFilter } remove_divs_filter: class: Drupal\process_text\TextFilter\RemoveDivs tags: - { name: text_filter } greeting_filter: class: Drupal\process_text\TextFilter\Greeting tags: - { name: text_filter } enclose_p_filter: class: Drupal\process_text\TextFilter\EnclosePTags tags: - { name: text_filter } /code/pre pThere are many changes here. Firstly, all the filters have been converted to services themselves. The have a common tag called codetext_filter/code. The main service also has a few changes. It has a tag called codeservice_collector/code and a tag parameter codecall/code. This ritual of creating a service container and adding a set of tagged services is such a common pattern that Drupal 8 has a special tag to do this, called the codeservice_collector/code. This tag takes an additional parameter called codecall/code which indicates what function has to be called in the service to add all the tagged services./p pWhat happens is, Drupal's a href= picks up all services with service_collector tag, finds services which have the same tag as that of this service(codetext_filter/code in our case) and calls the method in codecall/code to consume the tagged service definition. If you're coming from Symfony world, this might seem familiar for you. In order to execute some custom code, like applying a set of filters, we implement codeCompilerPassInterface/code, which is run whenever the service cotainer(codeApplyFilter/code in our case) is being built. You can find more about CompilerPassInterface a href= pYour controller code looks a lot simpler now./p precodepublic function processText() { $custom_text = \Drupal::config('process_text.settings')-gt;get('custom_text'); // do processing using a service $filter_service = \Drupal::service('process_text.text_filter'); $custom_text = $filter_service-gt;applyFilters($custom_text); return [ '#type' =gt; 'markup', '#markup' =gt; $custom_text ]; } /code/pre pNow, all you need to add new filters is to update the service yaml file with the new filter service and tag it with text_filter tag./p h2Tagged services in the wild/h2 pDrupal allows developers to add a new authentication mechanism using tagged services. The codeauthentication_collector/code is defined in precodeauthentication_collector: class: Drupal\Core\Authentication\AuthenticationCollector tags: - { name: service_collector, tag: authentication_provider, call: addProvider } /code/pre pTo add a new authentication provider, one has to implement the a href=!lib!Drupal!Core!Authentication!AuthenticationProviderInterface.php/interface/AuthenticationProviderInterface/8.2.xAuthenticationProviderInterface/a and flesh out the codeapplies/code and codeauthenticate/code functions. This will be the subject of another post./p pHere's how the codeaddProvider/code function looks like:/p precodepublic function addProvider(AuthenticationProviderInterface $provider, $provider_id, $priority = 0, $global = FALSE) { $this-gt;providers[$provider_id] = $provider; $this-gt;providerOrders[$priority][$provider_id] = $provider; // Force the providers to be re-sorted. $this-gt;sortedProviders = NULL; if ($global) { $this-gt;globalProviders[$provider_id] = TRUE; } } /code/pre pAnd here's how we would register our hypothetical authentication provider service./p precodeservices: authentication.custom_auth: class: Drupal\custom_auth\Authentication\Provider\CustomAuth tags: - { name: authentication_provider } /code/pre pAnother example is the breadcrumb manager service./p precodebreadcrumb: class: Drupal\Core\Breadcrumb\BreadcrumbManager arguments: ['@module_handler'] tags: - { name: service_collector, tag: breadcrumb_builder, call: addBuilder } /code/pre pTo add breadcrumbs from your module, you would need to implement codeBreadcrumbBuilderInterface/code and add the following entry to your services.yml,/p precodeservices: foo.breadcrumb: class: Drupal\foo\MyBreadcrumbBuilder tags: - { name: breadcrumb_builder, priority: 100 } /code/pre pThe codeBreadcrumbManager::addBuilder/code collects all breadcrumb bilders and builds it using the codeBreadcrumbManager::build/code function./p precodepublic function addBuilder(BreadcrumbBuilderInterface $builder, $priority) { $this-gt;builders[$priority][] = $builder; // Force the builders to be re-sorted. $this-gt;sortedBuilders = NULL; } /code/pre /div span class=tag-link a href=/categories/drupalDrupal/a,/span span class=tag-link a href=/categories/drupal-8Drupal 8/a,/span span class=tag-link a href=/categories/drupal-planetDrupal Planet/a/span

Lullabot: Making Web Accessibility Great Again: Auditing the US Presidential Candidates Websites for Accessibility

Wed, 08/17/2016 - 15:00
div class=rich-textpImagine that you arrive at a website, but you cannot see the screen. How do you know what#x2019;s there? How do you navigate? This is normal for many people, and the accessibility of your site will make or break their experience. Accessibility is about including everyone. People with physical and cognitive disabilities have specific challenges online#x2014;and making your site accessible removes those barriers and opens the door to more users./p pSeverely disabled Americans constitute a population of 38.3 million people, and make up a huge swath of voters (see the a href=;vertical=defaultamp;q=%23CripTheVoteamp;src=typd#CripTheVote/a movement on Twitter). Some notable U.S. presidential elections have been decided by much less, and because of this, we#x2019;re auditing the US presidential election candidates#x2019; websites./p pDuring this audit, we#x2019;ll see what the candidates#x2019; websites are doing right and wrong, and where the low-hanging fruit lies. This article won#x2019;t be a full top-to-bottom audit, but we will show some of the important things to look for and explain why they#x2019;re important./p h2Our Methods/h2 h3Automated Testing/h3 pThe first step in our a11y audit is to do a quick automated check. If you#x2019;re new to accessibility, the a href= tool by WebAIM/a is a great place to start. It#x2019;ll check for standard accessibility features and errors in alt attributes, contrast, document outline, and form labels. For the features or errors it finds, it provides an info icon that you can click to learn what the issue is, why it#x2019;s important, and how to do it right. WAVE is free, and highlights both negative (errors, alerts, features, structural elements, ARIA attributes, contrast), and positive (features, structural elements, ARIA attributes)./p h3Keyboard Testing/h3 pAs great as WAVE is, an automated tool is never as good as a live person. This is because some accessibility requirements need human logic to apply them properly. For the next part, we#x2019;re going to navigate around each website using only the keyboard./p pThis is done by using the tab button to move to the next element, and shift-tab to move backwards. The spacebar (or return) is used to click or submit a form element. If everything is done right, a person will be able to navigate through your website without falling into tab rabbit-holes (tabbit-holes?). We should be able to tab through the whole page in a logical order without getting stuck or finding things that we can#x2019;t access or interact with./p pBeyond that, we need to be able to see where the focus lies as we tab across the page. Just as interactive elements give a visual cue on hover, we should get an indication when we land on an interactive element while tabbing, too. That state is referred to as #x2018;having focus#x2019;. You can extend your hover state to focus, or you can make a whole new interaction for focus. It#x2019;s up to you!/p div class=codepre code .link--cta-button:hover, .link--cta-button:focus { /* The :focus pseudo-class for a11y */ background: #2284c0; } /code/pre/div h3Screen Reader Audit/h3 pScreen readers are used by visually impaired and blind people to navigate websites. For this purpose we#x2019;ll use VoiceOver, which is the default pre-installed screen reader for OS X. We#x2019;re looking for things that read oddly (like an acronym), things that don#x2019;t get read that should get read (and vice-versa), and making sure all of the information is available./p h2Let#x2019;s start with Donald Trump#x2019;s website/h2 pThe first thing that we did while looking at Donald Trump#x2019;s website was audit it using the WAVE tool. Here#x2019;s what we found:/p ulli8 Errors/li li14 Alerts/li li15 Features/li li35 Structural Elements/li li5 HTML5 and ARIA/li li5 Contrast Errors (2 were false flags)/li /ul img src= alt=WAVE tool open on Trump#039;s website/ h2The Good/h2 h3Bold Colors for a Bold Candidate/h3 pThe color scheme is very accessible. On the front page, there were only two places where the contrast wasn#x2019;t sufficient. Outside of that, his color scheme provides text that stands out well from the background and is easy to read for low-vision users./p img src= alt=Donald Trump#039;s High Contrast Navigation/ h2The Bad/h2 h3Lacking Focus/h3 pRemember how we talked about focus states? This site has almost none. Tabbing through the page is confusing because there are no visual indications of where you are./p img src= alt=Animated Gif tabbing through Trump#039;s websites/ pThis is especially egregious because the browser automatically applies focus states to focusable elements for you. In order for there not to be focus elements at all, the developer has to actively go out of their way to break them by applying codeoutline: 0;/code to the element#x2019;s focus state. This is okay if you#x2019;re doing it to replace the focus state with something more decorative, but taking it off and not replacing it is a big accessibility no-no./p img src= alt=Disabled outline on elements on Trump#039;s website/ h3Skipped Skip Link/h3 pWhen tabbing through The Donald#x2019;s website, the first thing we notice is the absence of a a href= link/a. Without a skip link, a keyboard user is forced to tab through each link in the navigation when arriving at each page before they can access the rest of the content. This repetitive task can become aggravating quickly, especially on sites with a lot of navigation links./p h3Unclear Link Text/h3 pLinks should always have text that clearly explains where the user is going. The link#x2019;s text is what a person using a screen reader will hear, so text like #x2018;Read More#x2019; or text and icons that require visual context to understand their destination aren#x2019;t ideal./p img src= alt=Screenshot of Tweets off of Trump#039;s website/ pIn this area, the link text that goes out to the linked Twitter posts read #x2018;12h#x2019; and #x2018;13h#x2019;. Without the visual context of a Twitter icon (that#x2019;s a background image, so there#x2019;s no alternative text to provide that), the user probably has no idea what #x2018;12h#x2019; is referring to or where that link will lead./p h2The Ugly/h2 h3Navigation Nightmares/h3 pThe most important part of any website, in terms of access, is the navigation. An inaccessible navigation menu can block access to large portions of the website. Unfortunately, the navigation system of Trump#x2019;s website does just that, and prevents users with disabilities from directly accessing the sub-navigation items under the issues and media sections./p pA more accessible way to do this is to use a click dropdown instead of a code:hover/code. If that doesn#x2019;t work for the design, make sure that the code:hover/code state of the menu applies to code:focus/code as well, so that the menu will open the nested links when the parent menu item is tabbed to./p h3Disorganized Structure/h3 pStructural elements (h1, h2, h3, etc tags) are very helpful when used properly. In this case, they#x2019;re definitely not. Heading levels aren#x2019;t sequential, and nested information isn#x2019;t always relevant to its parent./p img src= alt=Trump#039;s website document outline/ h2Audit of Hillary Clinton#x2019;s website/h2 h2The Good/h2 pOverall, Clinton#x2019;s website is better than most when it comes to accessibility. It#x2019;s that clear that her development team made it a purposeful consideration during the development process. While it#x2019;s not perfect, there was a lot of good done here. Let#x2019;s explore some examples of things done right./p img src= alt=WAVE Tool opened to Hillary Clinton#039;s website/ h3Keyboard Accessibility/h3 pThe keyboard accessibility on this site is very good. We found that we could access the elements and navigate to other pages easily without a mouse. It was easy to open and shut the drop-down #x2018;More#x2019; area in the navigation, and access its nested links, which is a good example of how to implement what we were talking about when we covered the shortfalls of the navigation system on Trump#x2019;s website./p img src= alt=Tabbing through Hillary Clinton#039;s website/ h3Skip Link/h3 pHillary Clinton#x2019;s website includes a proper skip link, which allows users to skip the navigation and go directly to the content./p img src= alt=Skip link on Hillary Clinton#039;s website/ h3Great Focus States/h3 pThe other thing we found when checking the keyboard accessibility was that everything has a focus state that makes it visually obvious where you are on the page. The light dotted border focus state is a bit subtle for low-vision users, but the fact that the focus state of the elements was styled independently from the hover state shows that the developer was aware of the need for focus indicators and made a conscious effort to implement them./p h3Translation/h3 pWe usually think of accessibility in terms of people with disabilities because they often benefit from it the most, but accessibility is really just about including as many people as possible. A nice touch we found on Clinton#x2019;s site was a button at the top to translate the site into Spanish. With 41 million native Spanish speakers in the US, providing the option to experience the content in the user#x2019;s first language is a great accessibility move./p img src= alt=Navigation on Hillary#039;s website showing link to Spanish version/ h3Video Captioning/h3 pDeaf people rely on captions to get the dialogue from videos, since it#x2019;s very difficult to lip-read in film. The videos on Hillary#x2019;s site are furnished with open captions, which means that they#x2019;re always on. Open captions are great for people with disabilities, but they#x2019;re also a smart move to capture your non-disabled audience as well. Often autoplay videos won#x2019;t play any sound unless they#x2019;re interacted with, but providing open captions on the video gives you another chance to capture the audience#x2019;s interest by displaying the words on the screen./p h2The Bad/h2 h3No Transcripts for Video/h3 pWhile it was great that the videos were captioned, we couldn#x2019;t find a transcript provided. Many people erroneously believe that you only need one or the other, but captions and transcripts actually serve different purposes, so it#x2019;s ideal to provide both. Captions are great for the Deaf, who want to read the words in real-time with the video. Transcripts are more useful for the blind and the Deaf-blind, who benefit from a written summary of what#x2019;s visually happening onscreen in each scene before the written dialogue begins. A braille terminal, used by the Deaf-blind, can#x2019;t convert open captions inlaid into the video#x2019;s frames into braille for its users, so these users won#x2019;t benefit from that./p h3Low Contrast/h3 pContrast is important for low-vision users. We know that subtlety is all the rage, but design choices like putting blue text on a blue background makes it really difficult for some people to read. There are some great free tools like a href= Plankton/a that will let you see if your contrast level is high enough to meet accessibility standards./p img src= alt=Hillary#039;s website#039;s low-contrast navigation/ pSchemes like this fail contrast testing on every level, so it#x2019;s best to avoid them. A better choice probably would have been white over a slightly darker blue./p h2The Ugly/h2 h3The Horrible Modal/h3 pDespite the obvious hard work that went into making Hillary#x2019;s website accessible, much of the effort is lost due to a modal that appears when visiting the website for the first time (or in incognito mode). The problem is that the modal doesn#x2019;t receive focus when it pops up, and its close button has no focus indicator. While it technically can be closed via keyboard by navigating backwards (or tabbing through every single link on the page) once it pops up, it#x2019;s not obvious visually when that close button has focus, and navigating backwards isn#x2019;t exactly intuitive./p img src= alt=The inaccessible modal on Hillary#039;s website/ h2Conclusion/h2 pWith one glaring exception, it#x2019;s obvious that lots of thought and work had been put into making Hillary Clinton#x2019;s website accessible to voters with disabilities. There is definitely room for improvement with small things like somewhat irrelevant alternative attributes on photos, but on the whole the site is better on accessibility than the vast majority of the sites that we see./p pUnfortunately, it is also obvious that accessibility is deeply neglected within Donald Trump#x2019;s website, which leaves a large swath of potential voters unable to browse to his stance on issues and other content. Hopefully, this will be attended to shortly./p pHopefully these auditing case studies lead you to think about your own website from the point of view of a person with a disability. There are plenty of challenges online for the disability community, but lots of those can be fixed with a few easy tweaks like the ones we covered here. We hope youapos;ll use what youapos;ve learned to make your website accessible, too./p /div