You are here

seo_checklist.module in SEO Checklist 8.4

Same filename and directory in other branches
  1. 7.4 seo_checklist.module
  2. 5.0.x seo_checklist.module

Uses best practices to check for proper search engine optimization.

File

seo_checklist.module
View source
<?php

/**
 * @file
 * Uses best practices to check for proper search engine optimization.
 */
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Symfony\Component\Routing\Exception\RouteNotFoundException;

// @codingStandardsIgnoreLine
define('SEO_CHECKLIST_GA_QUERY_STRING', 'utm_source=seo_checklist&utm_medium=backend&utm_content=logo&utm_campaign=volacci_seo');

/**
 * Implements hook_checklistapi_checklist_info().
 *
 * @see _seo_checklist_preprocess_checklist_items()
 */
function seo_checklist_checklistapi_checklist_info() {
  $definitions = [];
  $definitions['seo_checklist'] = [
    '#title' => t('SEO checklist'),
    '#description' => t('Keep track of your Drupal Search Engine Optimization tasks.'),
    '#path' => '/admin/config/search/seo-checklist',
    '#callback' => 'seo_checklist_checklistapi_checklist_items',
    '#help' => t("<p>Check off each SEO-related task as you complete it. Don't forget to click the <em>Save</em> button!</p>"),
  ];
  return $definitions;
}

/**
 * Implements callback_checklistapi_checklist_items() for seo_checklist.
 */
function seo_checklist_checklistapi_checklist_items() {

  /** @var \Drupal\Core\Render\RendererInterface $renderer */
  $renderer = \Drupal::service('renderer');
  $getting_started = [
    '#theme' => 'seo_checklist_getting_started',
  ];
  $items = [
    // Getting started.
    'getting_started' => [
      '#title' => t('Getting started'),
      '#description' => $renderer
        ->renderPlain($getting_started),
    ],
    // Be efficient.
    'be_efficient' => [
      '#title' => t('Be efficient'),
      '#description' => t('While not strictly necessary for SEO, these tasks will set you up to get things done much quicker.'),
      'install_and_enable_seo_checklist_module' => [
        '#title' => t('Install and Enable SEO Checklist module'),
        '#description' => t('The Drupal SEO Checklist uses Drupal SEO best practices to check your website for proper search engine optimization.'),
        '#book_ref' => [
          'chapter' => 2,
          'page' => 19,
        ],
        '#module' => 'seo_checklist',
      ],
      'install_and_enable_coffee_module' => [
        '#title' => t('[Optional] Install and Enable Coffee module'),
        '#description' => t('The Coffee module helps you to navigate through the Drupal admin faster, inspired by Mac apps Alfred and Spotlight.'),
        '#book_ref' => [
          'chapter' => 2,
          'page' => 27,
        ],
        '#module' => 'coffee',
        '#seo_training_camp' => Url::fromUri('https://dev.acquia.com/blog/drupal-8-module-of-the-week/drupal-8-module-of-the-week-coffee/12/04/2016/10291'),
        '#configure' => 'coffee.configuration',
      ],
      'configure_coffee_module' => [
        '#title' => t('[Optional] Configure Coffee module'),
        '#description' => t('Set up which menus Coffee will display.'),
        '#book_ref' => [
          'chapter' => 2,
          'page' => 31,
        ],
        '#configure' => 'coffee.configuration',
      ],
      'install_and_enable_admin_toolbar_module' => [
        '#title' => t('[Optional] Install and Enable Admin Toolbar module'),
        '#description' => t('Provides quicker access to administration pages by adding drop-down and pull-out menus to the Drupal admin toolbar.'),
        '#book_ref' => [
          'chapter' => 2,
          'page' => 35,
        ],
        '#module' => 'admin_toolbar',
        '#seo_training_camp' => Url::fromUri('https://dev.acquia.com/blog/drupal-8-module-of-the-week/drupal-8-module-of-the-week-admin-toolbar/04/02/2016/9661'),
      ],
      'install_composer_command_line_tool' => [
        '#title' => t('[Optional] Install Composer command line tool'),
        '#description' => t('Composer is the package manager for PHP. It installs modules and keeps a configuration file for faster deployment.'),
        '#book_ref' => [
          'chapter' => 1,
        ],
        'project_page' => [
          '#text' => t('Download'),
          '#url' => Url::fromUri('https://getcomposer.org/'),
        ],
      ],
      'install_console_command_line_tool' => [
        '#title' => t('[Optional] Install Drupal Console command line tool'),
        '#description' => t('Drupal Console is a newer command line tool that also helps you to install modules faster.'),
        '#book_ref' => [
          'chapter' => 1,
        ],
        '#seo_training_camp' => Url::fromUri('https://drupalconsole.com/docs'),
        'project_page' => [
          '#text' => t('Download'),
          '#url' => Url::fromUri('https://drupalconsole.com/'),
        ],
      ],
      'install_drush_command_line_tool' => [
        '#title' => t('[Optional] Install Drush command line tool'),
        '#description' => t('Drush is a command line tool for Drupal that you can use, among other things, to install modules faster.'),
        '#book_ref' => [
          'chapter' => 1,
        ],
        '#seo_training_camp' => Url::fromUri('http://docs.drush.org/en/master/'),
        'project_page' => [
          '#text' => t('Download'),
          '#url' => Url::fromUri('http://docs.drush.org/en/master/install/'),
        ],
      ],
      'show_cli_commands' => [
        '#title' => t('[Optional] Check here to show suggested Composer, Drupal Console, and Drush commands in SEO Checklist item descriptions.'),
      ],
    ],
    // Basic SEO part 1 - Clean URLs.
    'basic_seo_part_1' => [
      '#title' => t('Basic SEO part 1 - Clean URLs'),
      '#description' => t('Clean, well-formed URLs are the foundation of search engine optimization.'),
      'enable_clean_urls' => [
        '#title' => t('Enable clean URLs.'),
        '#description' => t('Clean URLs remove query strings from Drupal paths which improves SEO.'),
        '#book_ref' => [
          'chapter' => 3,
          'page' => 37,
        ],
      ],
      'install_and_enable_redirect_module' => [
        '#title' => t('Install and Enable Redirect module'),
        '#description' => t('Redirects visitors from old URLs to new URLs.'),
        '#book_ref' => [
          'chapter' => 3,
          'page' => 41,
        ],
        '#module' => 'redirect',
      ],
      'configure_the_redirect_module' => [
        '#title' => t('Configure the Redirect module'),
        '#description' => t('Tweak redirect settings for maximum benefit.'),
        '#book_ref' => [
          'chapter' => 3,
          'page' => 43,
        ],
        '#configure' => 'redirect.settings',
      ],
      'install_and_enable_pathauto_module' => [
        '#title' => t('Install and Enable Pathauto module'),
        '#description' => t('Automatically generates URL/path aliases for various kinds of content without requiring the user to manually specify the path alias.'),
        '#book_ref' => [
          'chapter' => 3,
          'page' => 48,
        ],
        '#module' => 'pathauto',
      ],
      'configure_pathauto_module' => [
        '#title' => t('Configure Pathauto module'),
        '#description' => t('Defaults are usually OK for most sites.'),
        '#book_ref' => [
          'chapter' => 3,
          'page' => 50,
        ],
        '#configure' => 'pathauto.settings.form',
      ],
      'create_pathauto_patterns' => [
        '#title' => t('Create Pathauto patterns'),
        '#description' => t('Specify a pattern that Drupal will use to create the path name for a new piece of content.'),
        '#book_ref' => [
          'chapter' => 3,
        ],
        '#configure' => 'entity.pathauto_pattern.collection',
      ],
    ],
    // Basic SEO part 2 - Meta tags.
    'basic_seo_part_2' => [
      '#title' => t('Basic SEO part 2 - Meta tags'),
      '#description' => t('Search engines look at your <code>TITLE</code> tags and certain meta data to determine what your site is about. These modules give you control over this important information.'),
      'install_and_enable_metatag_module' => [
        '#title' => t('Install and Enable Metatag module'),
        '#description' => t('Provides structured metadata, aka "meta tags", about a website.'),
        '#book_ref' => [
          'chapter' => 4,
          'page' => 64,
        ],
        '#module' => 'metatag',
        '#seo_training_camp' => Url::fromUri('https://dev.acquia.com/blog/drupal-8-module-of-the-week/drupal-8-module-of-the-week-metatag/17/02/2016/9716'),
      ],
      'set_metatags_for_your_site' => [
        '#title' => t('Set Metatags for your site'),
        '#description' => t('Set up the metatags for your content.'),
        '#book_ref' => [
          'chapter' => 4,
          'page' => 67,
        ],
        '#configure' => 'entity.metatag_defaults.collection',
      ],
      'install_and_enable_alternate_hreflang_module' => [
        '#title' => t('Install and Enable Alternate hreflang module'),
        '#description' => t('Automatically adds hreflang tags to your pages.'),
        '#book_ref' => [
          'chapter' => 4,
          'page' => 88,
        ],
        '#module' => 'hreflang',
        '#seo_training_camp' => Url::fromUri('https://support.google.com/webmasters/answer/189077'),
      ],
    ],
    // Search engines.
    'search_engines' => [
      '#title' => t('Search engines'),
      '#description' => t('Set your site up with the search engines and submit your XML sitemap.'),
      'install_and_enable_xml_sitemap_module' => [
        '#title' => t('Install and Enable XML Sitemap module'),
        '#description' => t('Creates an XML Sitemap of your content that you can submit to the search engines.'),
        '#module' => 'xmlsitemap',
        '#book_ref' => [
          'chapter' => 5,
          'page' => 96,
        ],
      ],
      'configure_xml_sitemap_module' => [
        '#title' => t('Configure XML Sitemap module'),
        '#description' => t('Set up the entities that will appear in the XML Sitemap.'),
        '#book_ref' => [
          'chapter' => 5,
          'page' => 98,
        ],
        '#configure' => 'xmlsitemap.admin_settings',
      ],
      'set_up_cron' => [
        '#title' => t('Set up Cron'),
        '#description' => t('Set up cron to run periodically so that your XML sitemap will be updated.'),
        '#book_ref' => [
          'chapter' => 5,
          'page' => 104,
        ],
      ],
      'get_a_google_account' => [
        '#title' => t('Get a Google account.'),
        '#description' => t('A Google account gives you access to Google Search Console.'),
        '#book_ref' => [
          'chapter' => 5,
          'page' => 106,
        ],
        'create_a_google_account' => [
          '#text' => t('Create Google account'),
          '#url' => Url::fromUri('https://accounts.google.com/SignUp'),
        ],
      ],
      'verify_with_google_search_console' => [
        '#title' => t('Verify with Google Search Console.'),
        '#description' => t('Verify your ownership of your website with Google.'),
        '#book_ref' => [
          'chapter' => 5,
          'page' => 107,
        ],
        'google_search_console' => [
          '#text' => t('Google Search Console'),
          '#url' => Url::fromUri('https://www.google.com/webmasters/tools/'),
        ],
      ],
      'submit_xml_sitemap_to_google' => [
        '#title' => t('Submit XML sitemap to Google.'),
        '#description' => t('Gives Google a list of all the content on your website.'),
        '#book_ref' => [
          'chapter' => 5,
          'page' => 111,
        ],
        'submit_xml_sitemap' => [
          '#text' => t('Submit XML sitemap'),
          '#url' => Url::fromUri('https://www.google.com/webmasters/tools/sitemap-list'),
        ],
      ],
      'get_a_microsoft_account' => [
        '#title' => t('Get a Microsoft account.'),
        '#description' => t('A Microsoft account gives you access to Bing Webmaster Tools.'),
        '#book_ref' => [
          'chapter' => 5,
          'page' => 113,
        ],
        'create_microsoft_account' => [
          '#text' => t('Create Microsoft account'),
          '#url' => Url::fromUri('https://signup.live.com/'),
        ],
      ],
      'authenticate_with_bing' => [
        '#title' => t('Authenticate with Bing.'),
        '#description' => t('Authenticate your ownership of your website with Bing.'),
        '#book_ref' => [
          'chapter' => 5,
          'page' => 114,
        ],
        'bing_webmaster_tools' => [
          '#text' => t('Bing Webmaster Tools'),
          '#url' => Url::fromUri('https://www.bing.com/webmaster/home/'),
        ],
      ],
      'submit_xml_sitemap_to_bing' => [
        '#title' => t('Submit XML sitemap to Bing.'),
        '#description' => t('Gives Bing a list of all the content on your website.'),
        '#book_ref' => [
          'chapter' => 5,
          'page' => 114,
        ],
        'bing_webmaster_tools' => [
          '#text' => t('Bing Webmaster Tools'),
          '#url' => Url::fromUri('https://www.bing.com/webmaster/home/'),
        ],
      ],
      'add_xml_sitemap_to_your_robotstxt_file' => [
        '#title' => t('Add XML Sitemap to your robots.txt file.'),
        '#description' => t('Makes your XML sitemap findable by other search engines.'),
        '#book_ref' => [
          'chapter' => 5,
          'page' => 119,
        ],
      ],
    ],
    // Analytics.
    'analytics' => [
      '#title' => t('Analytics'),
      '#description' => t('See where your visitors are coming from and what they do while visiting your site.'),
      'install_and_enable_google_analytics_module' => [
        '#title' => t('Install and Enable Google Analytics module'),
        '#description' => t('Adds the Google Analytics code snippet to your website and allows you to control how and when it is used.'),
        '#book_ref' => [
          'chapter' => 5,
          'page' => 123,
        ],
        '#module' => 'google_analytics',
      ],
      'configure_google_analytics_module' => [
        '#title' => t('Configure Google Analytics module'),
        '#description' => t("Set the tracking property in your site and make sure you're tracking the right roles."),
        '#book_ref' => [
          'chapter' => 5,
          'page' => 125,
        ],
        '#configure' => 'google_analytics.admin_settings_form',
      ],
      'sign_in_to_google_analytics_and_create_an_analytics_property_for_your_website' => [
        '#title' => t('Sign in to Google Analytics and create an Analytics property for your website.'),
        '#description' => t('Create a tracking property for your website.'),
        '#book_ref' => [
          'chapter' => 5,
          'page' => 126,
        ],
      ],
      'verify_your_site_is_sending_data_to_google_analytics' => [
        '#title' => t('Verify your site is sending data to Google Analytics.'),
        '#description' => t('Look for data in Google Analytics from your website.'),
        '#book_ref' => [
          'chapter' => 5,
        ],
      ],
      'cache_the_google_analytics_code' => [
        '#title' => t('Cache the Google Analytics code'),
        '#description' => t('If data is showing in Google Analytics, set caching to increase performance.'),
        '#book_ref' => [
          'chapter' => 5,
          'page' => 130,
        ],
        '#configure' => 'google_analytics.admin_settings_form',
      ],
      'alt_install_and_enable_piwik_web_analytics' => [
        '#title' => t('ALT: Install and Enable Piwik Web Analytics'),
        '#description' => t('As an alternative to Google Analytics, consider the Piwik module.'),
        '#book_ref' => [
          'chapter' => 5,
        ],
        '#module' => 'piwik',
        '#seo_training_camp' => Url::fromUri('https://piwik.org/'),
      ],
      'alt_configure_piwik_web_analytics' => [
        '#title' => t('ALT: Configure Piwik Web Analytics'),
        '#configure' => 'piwik.admin_settings_form',
      ],
    ],
    // Optimizing content.
    'optimizing_content' => [
      '#title' => t('Optimizing content'),
      '#description' => t('Well-written and optimized content is important to the search engines and your visitors.'),
      'install_and_enable_easy_breadcrumbs_module' => [
        '#title' => t('Install and Enable Easy Breadcrumbs module'),
        '#description' => t('Easy Breadcrumb uses the current URL (path alias) and the current page title to automatically create breadcrumbs.'),
        '#book_ref' => [
          'chapter' => 6,
          'page' => 136,
        ],
        '#module' => 'easy_breadcrumb',
      ],
      'configure_easy_breadcrumbs_module' => [
        '#title' => t('Configure Easy Breadcrumbs module'),
        '#description' => t('Set front page breadcrumb and other options.'),
        '#book_ref' => [
          'chapter' => 6,
          'page' => 137,
        ],
        '#configure' => 'easy_breadcrumb.general_settings_form',
      ],
      'install_and_enable_rdf_ui_module' => [
        '#title' => t('Install and Enable RDF UI module (use the dev version until beta2 is released)'),
        '#description' => t('Allows site builders to integrate schema.org seamlessly during or after the site building process on Drupal 8.'),
        '#book_ref' => [
          'chapter' => 6,
          'page' => 143,
        ],
        '#module' => 'rdfui',
      ],
      'set_schemaorg_schemas_for_your_content' => [
        '#title' => t('Set Schema.org schemas for your content'),
        '#description' => t('Select the schemas that best match your content.'),
        '#book_ref' => [
          'chapter' => 6,
          'page' => 144,
        ],
        '#configure' => 'entity.node_type.collection',
      ],
      'install_and_enable_linkit_module' => [
        '#title' => t('Install and Enable Linkit module'),
        '#description' => t('Provides an easy interface for internal and external linking with wysiwyg editors by using an autocomplete field.'),
        '#book_ref' => [
          'chapter' => 6,
          'page' => 149,
        ],
        '#module' => 'linkit',
        '#configure' => 'entity.linkit_profile',
      ],
      'configure_linkit_module' => [
        '#title' => t('Configure Linkit module'),
        '#description' => t('Set up CKEditor to use the Linkit module'),
        '#book_ref' => [
          'chapter' => 6,
          'page' => 150,
        ],
      ],
      'configure_text_formats_to_use_linkit_module' => [
        '#title' => t('Configure Text formats to use Linkit module'),
        '#configure' => 'filter.admin_overview',
        '#book_ref' => [
          'chapter' => 6,
          'page' => 153,
        ],
      ],
      'install_and_enable_d8_editor_advanced_link_module' => [
        '#title' => t('Install and Enable D8 Editor Advanced link module'),
        '#description' => t('Allows you to define title, class, id, target, and rel for links in CKEditor.'),
        '#book_ref' => [
          'chapter' => 6,
          'page' => 161,
        ],
        '#module' => 'editor_advanced_link',
      ],
      'configure_text_formats_to_use_d8_editor_advanced_link_module' => [
        '#title' => t('Configure Text formats to use D8 Editor Advanced link module'),
        '#description' => t('Set up CKEditor to use the D8 Editor Advanced link module'),
        '#book_ref' => [
          'chapter' => 6,
          'page' => 162,
        ],
        '#configure' => 'filter.admin_overview',
      ],
      'install_and_enable_w3c_validator_module' => [
        '#title' => t('Install and Enable W3C Validator module'),
        '#description' => t('W3C Validator provides a Drupal interface to use the W3C Markup Validator.'),
        '#book_ref' => [
          'chapter' => 7,
          'page' => 171,
        ],
        '#module' => 'w3c_validator',
      ],
      'configure_w3c_validator_module' => [
        '#title' => t('Configure W3C Validator module'),
        '#description' => t('Set up W3C Validator and check your pages.'),
        '#book_ref' => [
          'chapter' => 7,
          'page' => 172,
        ],
        '#configure' => 'w3c_validator.overview',
      ],
      'install_and_enable_sitemap_module' => [
        '#title' => t('Install and Enable Sitemap module'),
        '#description' => t('This module provides a site map that gives visitors an overview of your site. It can also display the RSS feeds for all blogs and categories.'),
        '#book_ref' => [
          'chapter' => 7,
          'page' => 175,
        ],
      ],
      'configure_sitemap_module' => [
        '#title' => t('Configure Sitemap module'),
        '#description' => t('Name and enable content of your sitemap.'),
        '#book_ref' => [
          'chapter' => 7,
          'page' => 177,
        ],
        '#module' => 'sitemap',
        '#configure' => 'sitemap.settings',
      ],
      'install_and_enable_search_404_module' => [
        '#title' => t('Install and Enable Search 404 module'),
        '#description' => t('Performs a search on the keywords in the URL instead of showing a standard "404 Page not found".'),
        '#book_ref' => [
          'chapter' => 7,
          'page' => 181,
        ],
        '#module' => 'search404',
      ],
      'configure_search_404_module' => [
        '#title' => t('Configure Search 404 module'),
        '#description' => t('Set up how Search 404 responds to visitors.'),
        '#book_ref' => [
          'chapter' => 7,
          'page' => 182,
        ],
        '#configure' => 'search404.settings',
      ],
    ],
    // Page optimization.
    'page_optimization' => [
      '#title' => t('Page optimization'),
      '#description' => t('Optimize each page of your website for optimal results.'),
      'install_and_enable_yoast_seo_module' => [
        '#title' => t('Install and Enable Yoast SEO module'),
        '#description' => t('Helps you optimize content around keywords in a natural, non-spam way.'),
        '#book_ref' => [
          'chapter' => 8,
          'page' => 189,
        ],
        '#module' => 'yoast_seo',
        '#configure' => 'yoast_seo.settings',
      ],
      'configure_yoast_seo_module' => [
        '#title' => t('Configure Yoast SEO Module'),
        '#description' => t('Set up which content types the Yoast module will evaluate.'),
        '#book_ref' => [
          'chapter' => 8,
          'page' => 190,
        ],
      ],
      'turn_on_create_new_revisions_for_all_content' => [
        '#title' => t('Turn on Create New Revisions for all content types'),
        '#description' => t('Helps you figure out what changes increased or decreased rankings.'),
        '#book_ref' => [
          'chapter' => 8,
          'page' => 196,
        ],
        '#configure' => 'filter.admin_overview',
      ],
      'install_and_enable_diff_module' => [
        '#title' => t('Install and Enable Diff module'),
        '#description' => t('Shows you differences between revisions to nodes.'),
        '#book_ref' => [
          'chapter' => 8,
          'page' => 198,
        ],
        '#module' => 'diff',
      ],
      'configure_the_diff_module' => [
        '#title' => t('Configure the Diff module'),
        '#description' => t('Configure how the diff module displays differences in your content.'),
        '#book_ref' => [
          'chapter' => 8,
          'page' => 198,
        ],
        '#configure' => 'diff.general_settings',
      ],
      'install_and_enable_scheduler_module' => [
        '#title' => t('Install and Enable Scheduler module'),
        '#description' => t('Scheduler gives content editors the ability to schedule nodes to be published and unpublished at specified dates and times in the future.'),
        '#book_ref' => [
          'chapter' => 8,
          'page' => 204,
        ],
        '#module' => 'scheduler',
      ],
      'configure_the_scheduler_module' => [
        '#title' => t('Configure the Scheduler module.'),
        '#description' => t('Set up how the Scheduler module displays dates and publishes content.'),
        '#book_ref' => [
          'chapter' => 8,
          'page' => 207,
        ],
        '#configure' => 'scheduler.admin_form',
      ],
    ],
    // Security and performance.
    'security_and_performance' => [
      '#title' => t('Security and performance'),
      '#description' => t("The best SEO'd website will not perform well if it is slow, gets hacked, or is spammed. Take these steps to lock things down and speed them up."),
      'install_and_enable_security_review_module' => [
        '#title' => t('Install and Enable Security Review module'),
        '#description' => t('The Security Review module automates testing for many of the easy-to-make mistakes that render your site insecure.'),
        '#book_ref' => [
          'chapter' => 9,
          'page' => 215,
        ],
        '#module' => 'security_review',
      ],
      'configure_security_review_module' => [
        '#title' => t('Configure Security Review module'),
        '#description' => t('Select the checks that the Security Review module makes on your site.'),
        '#book_ref' => [
          'chapter' => 9,
          'page' => 217,
        ],
      ],
      'secure_your_website_with_https' => [
        '#title' => t('Secure Your Website with HTTPS'),
        '#description' => t('Google rewards secure websites with higher rankings.'),
        '#book_ref' => [
          'chapter' => 9,
          'page' => 227,
        ],
      ],
      'adjust_performance_admin_settings' => [
        '#title' => t('Adjust Performance Admin Settings'),
        '#description' => t('Increase performance of your website with proper caching.'),
        '#book_ref' => [
          'chapter' => 9,
          'page' => 230,
        ],
        '#configure' => 'system.performance_settings',
      ],
      'install_and_enable_advanced_css_js_aggregation_module' => [
        '#title' => t('Install and Enable Advanced CSS/JS Aggregation module'),
        '#description' => t('Makes your site run faster by aggregating and compressing CSS and Javascript files.'),
        '#book_ref' => [
          'chapter' => 9,
          'page' => 233,
        ],
        '#module' => 'advagg',
      ],
      'configure_advanced_cssjs_aggregation_module' => [
        '#title' => t('Configure Advanced CSS/JS Aggregation module'),
        '#description' => t('Set up how the Advanced CSS/JS Aggregation module works.'),
        '#book_ref' => [
          'chapter' => 9,
          'page' => 235,
        ],
        '#configure' => 'advagg.settings',
      ],
      'configure_image_styles' => [
        '#title' => t('Configure Image Styles'),
        '#description' => t('Use smaller, perfectly-sized images to decrease bandwidth usage and increase site speed.'),
        '#book_ref' => [
          'chapter' => 9,
          'page' => 239,
        ],
        '#configure' => 'entity.image_style.collection',
      ],
      'set_up_a_cdn_cloudflare_etc' => [
        '#title' => t('Set up a CDN (Cloudflare, etc.)'),
        '#description' => t('CloudFlare is a free reverse proxy, firewall, and global content delivery network and can be implemented without installing any server software or hardware.'),
        '#book_ref' => [
          'chapter' => 9,
          'page' => 247,
        ],
        '#module' => 'cloudflare',
        '#seo_training_camp' => Url::fromUri('https://www.cloudflare.com/'),
      ],
      'move_to_fast_hosting' => [
        '#title' => t('Move to Fast Hosting'),
        '#description' => t('Faster hosting increases website response times which increase search rankings.'),
        '#book_ref' => [
          'chapter' => 9,
          'page' => 247,
        ],
      ],
    ],
    // Mobile and social.
    'mobile_and_social' => [
      '#title' => t('Mobile and social'),
      '#description' => t('A mobile-friendly website ranks better in many search engines including Google. Social will help you build links.'),
      'make_sure_website_is_responsive' => [
        '#title' => t('Make sure website is responsive.'),
        '#description' => t('Google recommends responsive websites for non-desktop devices.'),
        '#book_ref' => [
          'chapter' => 10,
          'page' => 252,
        ],
      ],
      'install_and_enable_amp_module_theme_and_php_library_using_composer' => [
        '#title' => t('Install and Enable AMP Module, Theme, and PHP Library using Composer'),
        '#description' => t('The AMP module is designed to convert Drupal pages into pages that comply with the AMP standard.'),
        '#book_ref' => [
          'chapter' => 10,
          'page' => 257,
        ],
        '#module' => 'amp',
        '#seo_training_camp' => Url::fromUri('https://www.ampproject.org/'),
      ],
      'configure_amp' => [
        '#title' => t('Configure AMP'),
        '#description' => t('Configure the AMP module to show AMP pages when requested.'),
        '#book_ref' => [
          'chapter' => 10,
          'page' => 259,
        ],
        '#configure' => 'amp.settings',
      ],
      'install_and_enable_share_buttons_by_addtoany' => [
        '#title' => t('Install and Enable Share Buttons by AddToAny'),
        '#description' => t('Adds SVG sharing icons for Drupal including a universal share button, Facebook, Twitter, Google+, Pinterest, WhatsApp, and many more.'),
        '#book_ref' => [
          'chapter' => 10,
          'page' => 266,
        ],
        '#module' => 'addtoany',
        '#seo_training_camp' => Url::fromUri('https://www.addtoany.com/'),
      ],
      'configure_share_buttons_by_addtoany' => [
        '#title' => t('Configure Share Buttons by AddToAny'),
        '#description' => t('Set up how sharing buttons show up on your site.'),
        '#book_ref' => [
          'chapter' => 10,
          'page' => 267,
        ],
        '#configure' => 'addtoany.admin_settings',
      ],
    ],
    // Learning and giving back.
    'learning_giving_back' => [
      '#title' => t('Learning and giving back'),
      '#description' => t('Learn more about Drupal 8 SEO, give back by saying thanks, or report a bug or suggest a new feature.'),
      'read_drupal_8_seo_by_ben_finklea' => [
        '#title' => '<strong>' . t('[Optional] Read <a href=":url">Drupal 8 SEO</a> by Ben Finklea', [
          ':url' => 'https://www.drupal8seo.com/',
        ]) . '</strong>',
        '#description' => t('This module was built together with the book Drupal 8 SEO. The book contains even more helpful tips along with step-by-step how-tos to get your site optimized for Google.'),
      ],
      'download_free_internet_marketing_resources_from_volacci' => [
        '#title' => t('[Optional] Download <a href=":url">free Internet marketing resources</a> from Volacci.', [
          ':url' => 'https://www.volacci.com/resources/whitepapers?' . SEO_CHECKLIST_GA_QUERY_STRING,
        ]),
        '#description' => t('Free resources from the Drupal SEO agency.'),
      ],
      'if_the_seo_checklist_module_helped_you_please_help_get_the_word_out' => [
        '#title' => t('[Optional] If the SEO Checklist module helped you, please help get the word out!'),
        '#description' => t('Let\'s tell the world that Drupal 8 is excellent for SEO! Please link to <a href=":url">the module page</a>.', [
          ':url' => 'https://www.drupal.org/project/seo_checklist',
        ]),
        'tweet' => [
          '#text' => 'Tweet',
          '#url' => Url::fromUri('http://ctt.ec/IR0k4'),
        ],
        'post_to_facebook' => [
          '#text' => 'Post to Facebook',
          '#url' => Url::fromUri('http://www.facebook.com/share.php?u=https://www.drupal.org/project/seo_checklist'),
        ],
      ],
      'report_a_bug_or_recommend_changes_to_the_seo_checklist_module' => [
        '#title' => t('[Optional] Report a bug or recommend changes to the SEO Checklist module.'),
        '#description' => t('Submit a ticket on Drupal.org if you encounter a bug or want to suggest changes to the checklist.'),
        'issue_queue' => [
          '#text' => t('Issue queue'),
          '#url' => Url::fromUri('https://www.drupal.org/project/issues/search/seo_checklist?status[]=Open'),
        ],
      ],
    ],
  ];
  return _seo_checklist_preprocess_checklist_items($items);
}

/**
 * Adds support for SEO Checklist's augmented checklist definitions schema.
 *
 * @param array $items
 *   An array of checklist definitions as defined by
 *   callback_checklistapi_checklist_items(), optionally with any of the
 *   following key-pair additions to the checklist items:
 *   - #book_ref: (optional) A reference to where more information may be found
 *     in the Drupal 8 SEO book, with the following key-value pairs:
 *     - #chapter: The chapter number as an integer.
 *   - #module: (optional) The project-and-machine name of a module. The item's
 *     checkbox will be pre-checked if the module is installed, and relevant
 *     links will be conditionally added to the item.
 *   - #seo_training_camp: (optional) A URL to an additional training resource
 *     to be displayed as a link.
 *   - #configure: (optional) The route name to a configuration page to be
 *     displayed as a link.
 *
 * @return array
 *   The preprocessed checklist definitions.
 *
 * @see hook_checklistapi_checklist_info()
 *
 * @internal
 */
function _seo_checklist_preprocess_checklist_items(array $items) {
  $module_handler = \Drupal::moduleHandler();
  $access_manager = \Drupal::accessManager();
  $current_user = \Drupal::currentUser();

  /** @var \Drupal\Core\Routing\RouteProviderInterface $route_provider */
  $route_provider = \Drupal::service('router.route_provider');
  foreach ($items as &$group) {
    if (is_array($group)) {
      foreach ($group as &$item) {
        if (is_array($item)) {

          // Add book reference.
          if (isset($item['#book_ref'])) {
            $page = isset($item['#book_ref']['page']) ? $item['#book_ref']['page'] : FALSE;
            $args = [
              ':url' => 'https://www.drupal8seo.com/',
              '@chapter' => $item['#book_ref']['chapter'],
              '@page' => $page,
            ];
            if ($page) {
              $bookref = '<em class="book-link">' . t('See <a href=":url">Drupal 8 SEO</a> Chapter @chapter, p. @page.', $args) . '</em>';
            }
            else {
              $bookref = '<em class="book-link">' . t('See <a href=":url">Drupal 8 SEO</a> Chapter @chapter.', $args) . '</em>';
            }
            $description = isset($item['#description']) ? "{$item['#description']} " : '';
            $item['#description'] = $description . $bookref;
            unset($item['#book_ref']);
          }

          // Made module-related additions.
          if (isset($item['#module'])) {
            $module_name = $item['#module'];
            $module_url_fragment_safe_name = str_replace('_', '-', $module_name);
            $enabled = $module_handler
              ->moduleExists($module_name);

            // Pre-check installed modules.
            $item['#default_value'] = $enabled;

            // Add CLI commands to description.
            $item['#description'] .= '</p><p class="cli-commands">' . "Composer: <code>composer require drupal/{$module_name}</code><br />" . "Drupal Console: <code>drupal module:install {$module_name} --latest</code><br />" . "Drush: <code>drush dl {$module_name} && drush en {$module_name}</code>";

            // Add module links.
            $item['project_page'] = [
              '#text' => t('Download'),
              '#url' => Url::fromUri("https://www.drupal.org/project/{$module_name}"),
            ];
            if ($access_manager
              ->checkNamedRoute('system.modules_list', [], $current_user)) {
              $item['modules_page'] = [
                '#text' => t('Install'),
                '#url' => Url::fromRoute('system.modules_list', [], [
                  'fragment' => "module-{$module_url_fragment_safe_name}",
                ]),
              ];
            }
            if ($enabled && $access_manager
              ->checkNamedRoute('user.admin_permissions', [], $current_user)) {
              $item['permissions_page'] = [
                '#text' => t('Configure permissions'),
                '#url' => Url::fromRoute('user.admin_permissions', [], [
                  'fragment' => "module-{$module_url_fragment_safe_name}",
                ]),
              ];
            }
            unset($item['#module']);
          }

          // Add SEO training camp links.
          if (isset($item['#seo_training_camp'])) {
            $item['seo_training_camp'] = [
              '#text' => t('SEO training camp'),
              '#url' => $item['#seo_training_camp'],
            ];
            unset($item['#seo_training_camp']);
          }

          // Add Configure link.
          if (isset($item['#configure'])) {
            $route_name = $item['#configure'];
            try {
              $route_exists = (bool) $route_provider
                ->getRouteByName($route_name);
            } catch (RouteNotFoundException $e) {
              $route_exists = FALSE;
            }
            if ($route_exists && $access_manager
              ->checkNamedRoute($route_name, [], $current_user)) {
              $item['configure'] = [
                '#text' => t('Configure'),
                '#url' => Url::fromRoute($route_name),
              ];
            }
            unset($item['#configure']);
          }
        }
      }
    }
  }
  return $items;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function seo_checklist_form_checklistapi_checklist_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  /** @var \Drupal\checklistapi\ChecklistapiChecklist $checklist */
  $checklist = $form['#checklist'];
  if ($checklist->id == 'seo_checklist') {
    $form['#attached']['library'][] = 'seo_checklist/seo_checklist';

    // Add a non-link item to the "get the word out" links.
    $get_the_word_out_links =& $form['learning_giving_back']['if_the_seo_checklist_module_helped_you_please_help_get_the_word_out']['#description'];
    $get_the_word_out_links = str_replace('</div>', ' | ' . t('Blog about it') . '</div>', $get_the_word_out_links);
  }
}

/**
 * Implements hook_theme().
 */
function seo_checklist_theme($existing, $type, $theme, $path) {
  return [
    'seo_checklist_getting_started' => [
      'variables' => [
        'ga_query_string' => SEO_CHECKLIST_GA_QUERY_STRING,
      ],
    ],
  ];
}