diff --git a/core/core.services.yml b/core/core.services.yml
index 12f3a3c..bdc9516 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -165,6 +165,21 @@ services:
     calls:
       - [addSubscriber, ['@http_client_simpletest_subscriber']]
       - [setUserAgent, ['Drupal (+http://drupal.org/)']]
+  theme.negotiator:
+    class: Drupal\Core\Theme\ThemeNegotiator
+    arguments: ['@access_check.theme']
+    calls:
+      - [setRequest, ['@request']]
+  theme.negotiator.default:
+    class: Drupal\Core\Theme\DefaultNegotiator
+    arguments: ['@config.factory']
+    tags:
+      - { name: theme_negotiator, priority: -100 }
+  theme.negotiator.ajax_base_page:
+    class: Drupal\Core\Theme\AjaxBasePageNegotiator
+    arguments: ['@csrf_token']
+    tags:
+      - { name: theme_negotiator, priority: 1000 }
   container.namespaces:
     class: ArrayObject
     arguments: [ '%container.namespaces%' ]
diff --git a/core/includes/ajax.inc b/core/includes/ajax.inc
index 3f2389f..3ff6c17 100644
--- a/core/includes/ajax.inc
+++ b/core/includes/ajax.inc
@@ -303,42 +303,6 @@ function ajax_render($commands = array()) {
 }
 
 /**
- * Theme callback: Returns the correct theme for an Ajax request.
- *
- * Many different pages can invoke an Ajax request to system/ajax or another
- * generic Ajax path. It is almost always desired for an Ajax response to be
- * rendered using the same theme as the base page, because most themes are built
- * with the assumption that they control the entire page, so if the CSS for two
- * themes are both loaded for a given page, they may conflict with each other.
- * For example, Bartik is Drupal's default theme, and Seven is Drupal's default
- * administration theme. Depending on whether the "Use the administration theme
- * when editing or creating content" checkbox is checked, the node edit form may
- * be displayed in either theme, but the Ajax response to the Field module's
- * "Add another item" button should be rendered using the same theme as the rest
- * of the page. Therefore, system_menu() sets the 'theme callback' for
- * 'system/ajax' to this function, and it is recommended that modules
- * implementing other generic Ajax paths do the same.
- *
- * @see system_menu()
- * @see file_menu()
- */
-function ajax_base_page_theme() {
-  if (!empty($_POST['ajax_page_state']['theme']) && !empty($_POST['ajax_page_state']['theme_token'])) {
-    $theme = $_POST['ajax_page_state']['theme'];
-    $token = $_POST['ajax_page_state']['theme_token'];
-
-    // Prevent a request forgery from giving a person access to a theme they
-    // shouldn't be otherwise allowed to see. However, since everyone is allowed
-    // to see the default theme, token validation isn't required for that, and
-    // bypassing it allows most use-cases to work even when accessed from the
-    // page cache.
-    if ($theme === \Drupal::config('system.theme')->get('default') || drupal_valid_token($token, $theme)) {
-      return $theme;
-    }
-  }
-}
-
-/**
  * Converts the return value of a page callback into an Ajax commands array.
  *
  * @param $page_callback_result
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 07b5db6..4b1fb6d 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -2330,7 +2330,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
       global $theme_key;
       // Provide the page with information about the theme that's used, so that
       // a later AJAX request can be rendered using the same theme.
-      // @see ajax_base_page_theme()
+      // @see \Drupal\Core\Theme\AjaxBasePageNegotiator
       $setting['ajaxPageState']['theme'] = $theme_key;
       // Checks that the DB is available before filling theme_token.
       if (!defined('MAINTENANCE_MODE')) {
@@ -3142,7 +3142,6 @@ function _drupal_bootstrap_full($skip = FALSE) {
   // Let all modules take action before the menu system handles the request.
   // We do not want this while running update.php.
   if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
-    menu_set_custom_theme();
     drupal_theme_initialize();
   }
 }
diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index 78d6367..aaab0f1 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -481,8 +481,8 @@ function menu_set_item($path, $router_item) {
  *   menu_router table. The value corresponding to the key 'map' holds the
  *   loaded objects. The value corresponding to the key 'access' is TRUE if the
  *   current user can access this page. The values corresponding to the keys
- *   'title', 'page_arguments', 'access_arguments', and 'theme_arguments' will
- *   be filled in based on the database values and the objects loaded.
+ *   'title', 'page_arguments', and 'access_arguments', will be filled in based
+ *   on the database values and the objects loaded.
  */
 function menu_get_item($path = NULL, $router_item = NULL) {
   $router_items = &drupal_static(__FUNCTION__);
@@ -519,7 +519,6 @@ function menu_get_item($path = NULL, $router_item = NULL) {
       if ($router_item['access']) {
         $router_item['map'] = $map;
         $router_item['page_arguments'] = array_merge(menu_unserialize($router_item['page_arguments'], $map), array_slice($map, $router_item['number_parts']));
-        $router_item['theme_arguments'] = array_merge(menu_unserialize($router_item['theme_arguments'], $map), array_slice($map, $router_item['number_parts']));
       }
     }
     $router_items[$path] = $router_item;
@@ -1814,51 +1813,6 @@ function drupal_help_arg($arg = array()) {
 }
 
 /**
- * Gets the custom theme for the current page, if there is one.
- *
- * @param $initialize
- *   This parameter should only be used internally; it is set to TRUE in order
- *   to force the custom theme to be initialized for the current page request.
- *
- * @return
- *   The machine-readable name of the custom theme, if there is one.
- *
- * @see menu_set_custom_theme()
- */
-function menu_get_custom_theme($initialize = FALSE) {
-  $custom_theme = &drupal_static(__FUNCTION__);
-  // Skip this if the site is offline or being installed or updated, since the
-  // menu system may not be correctly initialized then.
-  if ($initialize && !_menu_site_is_offline(TRUE) && (!defined('MAINTENANCE_MODE') || (MAINTENANCE_MODE != 'update' && MAINTENANCE_MODE != 'install'))) {
-    // First allow modules to dynamically set a custom theme for the current
-    // page. Since we can only have one, the last module to return a valid
-    // theme takes precedence.
-    $custom_themes = array_filter(\Drupal::moduleHandler()->invokeAll('custom_theme'), 'drupal_theme_access');
-    if (!empty($custom_themes)) {
-      $custom_theme = array_pop($custom_themes);
-    }
-    // If there is a theme callback function for the current page, execute it.
-    // If this returns a valid theme, it will override any theme that was set
-    // by a hook_custom_theme() implementation above.
-    $router_item = menu_get_item();
-    if (!empty($router_item['access']) && !empty($router_item['theme_callback'])) {
-      $theme_name = call_user_func_array($router_item['theme_callback'], $router_item['theme_arguments']);
-      if (drupal_theme_access($theme_name)) {
-        $custom_theme = $theme_name;
-      }
-    }
-  }
-  return $custom_theme;
-}
-
-/**
- * Sets a custom theme for the current page, if there is one.
- */
-function menu_set_custom_theme() {
-  menu_get_custom_theme(TRUE);
-}
-
-/**
  * Returns an array containing the names of system-defined (default) menus.
  */
 function menu_list_system_menus() {
@@ -3212,13 +3166,6 @@ function _menu_router_build($callbacks, $save = FALSE) {
             }
           }
         }
-        // Same for theme callbacks.
-        if (!isset($item['theme callback']) && isset($parent['theme callback'])) {
-          $item['theme callback'] = $parent['theme callback'];
-          if (!isset($item['theme arguments']) && isset($parent['theme arguments'])) {
-            $item['theme arguments'] = $parent['theme arguments'];
-          }
-        }
         // Same for load arguments: if a loader doesn't have any explict
         // arguments, try to find arguments in the parent.
         if (!isset($item['load arguments'])) {
@@ -3255,8 +3202,6 @@ function _menu_router_build($callbacks, $save = FALSE) {
       'page callback' => '',
       'title arguments' => array(),
       'title callback' => 't',
-      'theme arguments' => array(),
-      'theme callback' => '',
       'description' => '',
       'description arguments' => array(),
       'description callback' => 't',
@@ -3321,8 +3266,6 @@ function _menu_router_save($menu, $masks) {
       'title',
       'title_callback',
       'title_arguments',
-      'theme_callback',
-      'theme_arguments',
       'type',
       'description',
       'description_callback',
@@ -3353,8 +3296,6 @@ function _menu_router_save($menu, $masks) {
       'title' => $item['title'],
       'title_callback' => $item['title callback'],
       'title_arguments' => ($item['title arguments'] ? serialize($item['title arguments']) : ''),
-      'theme_callback' => $item['theme callback'],
-      'theme_arguments' => serialize($item['theme arguments']),
       'type' => $item['type'],
       'description' => $item['description'],
       'description_callback' => $item['description callback'],
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 9a0d713..d2e152a 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -93,16 +93,14 @@ function drupal_theme_initialize() {
   }
 
   drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
-  $themes = list_themes();
 
-  // Only select the user selected theme if it is available in the
-  // list of themes that can be accessed.
-  $theme = !empty($user->theme) && drupal_theme_access($user->theme) ? $user->theme : \Drupal::config('system.theme')->get('default');
+  $themes = list_themes();
 
-  // Allow modules to override the theme. Validation has already been performed
-  // inside menu_get_custom_theme(), so we do not need to check it again here.
-  $custom_theme = menu_get_custom_theme();
-  $theme = !empty($custom_theme) ? $custom_theme : $theme;
+  // @todo Let the theme.negotiator listen to the kernel request event.
+  // Determine the active theme for the theme negotiator service. This includes
+  // the default theme as well as really specific ones like the ajax base theme.
+  $request = \Drupal::request();
+  $theme = \Drupal::service('theme.negotiator')->determineActiveTheme($request) ?: 'stark';
 
   // Store the identifier for retrieving theme settings with.
   $theme_key = $theme;
@@ -117,7 +115,7 @@ function drupal_theme_initialize() {
   _drupal_theme_initialize($themes[$theme], array_reverse($base_theme));
 
   // Themes can have alter functions, so reset the drupal_alter() cache.
-  drupal_static_reset('drupal_alter');
+  \Drupal::moduleHandler()->resetImplementations();
 }
 
 /**
diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php
index 9cd8f5a..4bd88ee 100644
--- a/core/lib/Drupal/Core/CoreServiceProvider.php
+++ b/core/lib/Drupal/Core/CoreServiceProvider.php
@@ -22,6 +22,7 @@
 use Drupal\Core\DependencyInjection\Compiler\RegisterBreadcrumbBuilderPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterAuthenticationPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterTwigExtensionsPass;
+use Drupal\Core\Theme\ThemeNegotiatorPass;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\DependencyInjection\Definition;
@@ -69,6 +70,9 @@ public function register(ContainerBuilder $container) {
     // Add the compiler pass that will process the tagged breadcrumb builder
     // services.
     $container->addCompilerPass(new RegisterBreadcrumbBuilderPass());
+    // Add the compiler pass that will process the tagged theme negotiator
+    // service.
+    $container->addCompilerPass(new ThemeNegotiatorPass());
     // Add the compiler pass that lets service providers modify existing
     // service definitions.
     $container->addCompilerPass(new ModifyServiceDefinitionsPass());
diff --git a/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php
index 39828ef..2a495f4 100644
--- a/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php
@@ -27,9 +27,6 @@ class LegacyRequestSubscriber implements EventSubscriberInterface {
    */
   public function onKernelRequestLegacy(GetResponseEvent $event) {
     if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) {
-      menu_set_custom_theme();
-      drupal_theme_initialize();
-
       // Tell Drupal it is now fully bootstrapped (for the benefit of code that
       // calls drupal_get_bootstrap_phase()), but without having
       // _drupal_bootstrap_full() do anything, since we've already done the
@@ -40,6 +37,16 @@ public function onKernelRequestLegacy(GetResponseEvent $event) {
   }
 
   /**
+   * Initializes the theme system after the routing system.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onKernelRequestLegacyAfterRouting(GetResponseEvent $event) {
+    drupal_theme_initialize();
+  }
+
+  /**
    * Registers the methods in this class that should be listeners.
    *
    * @return array
@@ -47,6 +54,8 @@ public function onKernelRequestLegacy(GetResponseEvent $event) {
    */
   static function getSubscribedEvents() {
     $events[KernelEvents::REQUEST][] = array('onKernelRequestLegacy', 90);
+    // Initialize the theme system after the routing system.
+    $events[KernelEvents::REQUEST][] = array('onKernelRequestLegacyAfterRouting', 30);
 
     return $events;
   }
diff --git a/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php b/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php
new file mode 100644
index 0000000..8a55235
--- /dev/null
+++ b/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Theme\AjaxBasePageNegotiator.
+ */
+
+namespace Drupal\Core\Theme;
+
+use Drupal\Core\Access\CsrfTokenGenerator;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Defines a theme negotiator that deals with the active theme on ajax requests.
+ *
+ * Many different pages can invoke an Ajax request to system/ajax or another
+ * generic Ajax path. It is almost always desired for an Ajax response to be
+ * rendered using the same theme as the base page, because most themes are built
+ * with the assumption that they control the entire page, so if the CSS for two
+ * themes are both loaded for a given page, they may conflict with each other.
+ * For example, Bartik is Drupal's default theme, and Seven is Drupal's default
+ * administration theme. Depending on whether the "Use the administration theme
+ * when editing or creating content" checkbox is checked, the node edit form may
+ * be displayed in either theme, but the Ajax response to the Field module's
+ * "Add another item" button should be rendered using the same theme as the rest
+ * of the page.
+ *
+ * Therefore specify '_theme: ajax_base_page' as part of the router options.
+ */
+class AjaxBasePageNegotiator implements ThemeNegotiatorInterface {
+
+  /**
+   * The CSRF token generator.
+   *
+   * @var \Drupal\Core\Access\CsrfTokenGenerator
+   */
+  protected $csrfGenerator;
+
+  /**
+   * Constructs a new AjaxBasePageNegotiator.
+   *
+   * @param \Drupal\Core\Access\CsrfTokenGenerator $token_generator
+   *   The CSRF token generator.
+   */
+  public function __construct(CsrfTokenGenerator $token_generator) {
+    $this->csrfGenerator = $token_generator;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function determineActiveTheme(Request $request) {
+    // Check whether the route was configured to use the base page theme.
+    if (!(($route = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)) && $route->hasOption('_theme') && $route->getOption('_theme') == 'ajax_base_page')) {
+      return NULL;
+    }
+    if (($ajax_page_state = $request->request->get('ajax_page_state'))  && !empty($ajax_page_state['theme']) && !empty($ajax_page_state['theme_token'])) {
+      $theme = $ajax_page_state['theme'];
+      $token = $ajax_page_state['theme_token'];
+
+      // Ensure that the user only access a theme they are allowed to see.
+      if ($this->csrfGenerator->validate($token, $theme)) {
+        return $theme;
+      }
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Theme/DefaultNegotiator.php b/core/lib/Drupal/Core/Theme/DefaultNegotiator.php
new file mode 100644
index 0000000..d596678
--- /dev/null
+++ b/core/lib/Drupal/Core/Theme/DefaultNegotiator.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Theme\DefaultNegotiator.
+ */
+
+namespace Drupal\Core\Theme;
+
+use Drupal\Core\Config\ConfigFactory;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Determines the default theme of the site.
+ */
+class DefaultNegotiator implements ThemeNegotiatorInterface {
+
+  /**
+   * The system theme config object.
+   *
+   * @var \Drupal\Core\Config\Config
+   */
+  protected $config;
+
+  /**
+   * Constructs a DefaultNegotiator object.
+   *
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   *   The config factory.
+   */
+  public function __construct(ConfigFactory $config_factory) {
+    $this->config = $config_factory->get('system.theme');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function determineActiveTheme(Request $request) {
+    return $this->config->get('default');
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Theme/ThemeNegotiator.php b/core/lib/Drupal/Core/Theme/ThemeNegotiator.php
new file mode 100644
index 0000000..96216ca
--- /dev/null
+++ b/core/lib/Drupal/Core/Theme/ThemeNegotiator.php
@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Theme\ThemeNegotiator.
+ */
+
+namespace Drupal\Core\Theme;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Provides a class which determines the active theme of the page.
+ *
+ * It therefore uses ThemeNegotiatorInterface objects which are passed in
+ * using the 'theme_negotiator' tag.
+ *
+ * @see \Drupal\Core\Theme\ThemeNegotiatorPass
+ * @see \Drupal\Core\Theme\ThemeNegotiatorInterface
+ */
+class ThemeNegotiator implements ThemeNegotiatorInterface {
+
+  /**
+   * Holds arrays of theme negotiators, keyed by priority.
+   *
+   * @var array
+   */
+  protected $negotiators = array();
+
+  /**
+   * Holds the array of theme negotiators sorted by priority.
+   *
+   * Set to NULL if the array needs to be re-calculated.
+   *
+   * @var array|NULL
+   */
+  protected $sortedNegotiators;
+
+  /**
+   * The current request.
+   *
+   * @var \Symfony\Component\HttpFoundation\Request
+   */
+  protected $request;
+
+  /**
+   * The access checker for themes.
+   *
+   * @var \Drupal\Core\Theme\ThemeAccessCheck
+   */
+  protected $themeAccess;
+
+  /**
+   * Constructs a new ThemeNegotiator.
+   *
+   * @param \Drupal\Core\Theme\ThemeAccessCheck $theme_access
+   *   The access checker for themes.
+   */
+  public function __construct(ThemeAccessCheck $theme_access) {
+    $this->themeAccess = $theme_access;
+  }
+
+  /**
+   * Sets the request object to use.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request object.
+   */
+  public function setRequest(Request $request) {
+    $this->request = $request;
+  }
+
+  /**
+   * Adds a active theme negotiation service.
+   *
+   * @param \Drupal\Core\Theme\ThemeNegotiatorInterface $negotiator
+   *   The theme negotiator to add.
+   * @param int $priority
+   *   Priority of the breadcrumb builder.
+   */
+  public function addNegotiator(ThemeNegotiatorInterface $negotiator, $priority) {
+    $this->negotiators[$priority][] = $negotiator;
+    // Force the negotiators to be re-sorted.
+    $this->sortedNegotiators = NULL;
+  }
+
+  /**
+   * Returns the sorted array of theme negotiators.
+   *
+   * @return array|\Drupal\Core\Theme\ThemeNegotiatorInterface[]
+   *   An array of breadcrumb builder objects.
+   */
+  protected function getSortedNegotiators() {
+    if (!isset($this->sortedNegotiators)) {
+      // Sort the negotiators according to priority.
+      krsort($this->negotiators);
+      // Merge nested negotiators from $this->negotiators into
+      // $this->sortedNegotiators.
+      $this->sortedNegotiators = array();
+      foreach ($this->negotiators as $builders) {
+        $this->sortedNegotiators = array_merge($this->sortedNegotiators, $builders);
+      }
+    }
+    return $this->sortedNegotiators;
+  }
+
+  /**
+   * Get the current active theme.
+   *
+   * @return string
+   *   The current active string.
+   */
+  public function getActiveTheme() {
+    if (!$this->request->attributes->has('_theme_active')) {
+      $this->determineActiveTheme($this->request);
+    }
+    return $this->request->attributes->get('_theme_active');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function determineActiveTheme(Request $request) {
+    foreach ($this->getSortedNegotiators() as $negotiator) {
+      $theme = $negotiator->determineActiveTheme($request);
+      if ($theme !== NULL && $this->themeAccess->checkAccess($theme)) {
+        $request->attributes->set('_theme_active', $theme);
+        return $request->attributes->get('_theme_active');
+      }
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php b/core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php
new file mode 100644
index 0000000..9bb80b1
--- /dev/null
+++ b/core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Theme\ThemeNegotiatorInterface.
+ */
+
+namespace Drupal\Core\Theme;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Defines an interface for classes which determine the active theme.
+ */
+interface ThemeNegotiatorInterface {
+
+  /**
+   * Determine the active theme for the request.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The active request of the site.
+   *
+   * @return string|null
+   *   Returns the active theme name, else return NULL.
+   */
+  public function determineActiveTheme(Request $request);
+
+}
diff --git a/core/lib/Drupal/Core/Theme/ThemeNegotiatorPass.php b/core/lib/Drupal/Core/Theme/ThemeNegotiatorPass.php
new file mode 100644
index 0000000..7f14692
--- /dev/null
+++ b/core/lib/Drupal/Core/Theme/ThemeNegotiatorPass.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Theme\ThemeNegotiatorPass.
+ */
+
+namespace Drupal\Core\Theme;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Adds services to the theme negotiator service.
+ *
+ * @see \Drupal\Core\Theme\ThemeNegotiator
+ * @see \Drupal\Core\Theme\ThemeNegotiatorInterfa
+ */
+class ThemeNegotiatorPass implements CompilerPassInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function process(ContainerBuilder $container) {
+    if (!$container->hasDefinition('theme.negotiator')) {
+      return;
+    }
+    $manager = $container->getDefinition('theme.negotiator');
+    foreach ($container->findTaggedServiceIds('theme_negotiator') as $id => $attributes) {
+      $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
+      $manager->addMethodCall('addNegotiator', array(new Reference($id), $priority));
+    }
+  }
+
+}
diff --git a/core/misc/ajax.js b/core/misc/ajax.js
index a586945..e9baf94 100644
--- a/core/misc/ajax.js
+++ b/core/misc/ajax.js
@@ -382,7 +382,7 @@ Drupal.ajax.prototype.beforeSerialize = function (element, options) {
 
   // Allow Drupal to return new JavaScript and CSS files to load without
   // returning the ones already loaded.
-  // @see ajax_base_page_theme()
+  // @see \Drupal\Core\Theme\AjaxBasePageNegotiator
   // @see drupal_get_css()
   // @see drupal_get_js()
   var pageState = drupalSettings.ajaxPageState;
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index e27af49..2cdce80 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -114,42 +114,10 @@ function block_menu() {
     'type' => MENU_VISIBLE_IN_BREADCRUMB,
     'route_name' => 'block.admin_add',
   );
-  // Block administration is tied to the theme and plugin definition so
-  // that the plugin can appropriately attach to this URL structure.
-  // @todo D8: Use dynamic % arguments instead of static, hard-coded theme names
-  //   and plugin IDs to decouple the routes from these dependencies.
-  // @see http://drupal.org/node/1067408
-  foreach (list_themes() as $key => $theme) {
-    $items["admin/structure/block/demo/$key"] = array(
-      'route_name' => 'block.admin_demo',
-      'type' => MENU_CALLBACK,
-      'theme callback' => '_block_custom_theme',
-      'theme arguments' => array($key),
-    );
-  }
   return $items;
 }
 
 /**
- * Theme callback: Uses the theme specified in the parameter.
- *
- * @param $theme
- *   The theme whose blocks are being configured. If not set, the default theme
- *   is assumed.
- *
- * @return
- *   The theme that should be used for the block configuration page, or NULL
- *   to indicate that the default theme should be used.
- *
- * @see block_menu()
- */
-function _block_custom_theme($theme = NULL) {
-  // We return exactly what was passed in, to guarantee that the page will
-  // always be displayed using the theme whose blocks are being configured.
-  return $theme;
-}
-
-/**
  * Implements hook_page_build().
  *
  * Renders blocks into their regions.
diff --git a/core/modules/block/block.services.yml b/core/modules/block/block.services.yml
index b6bf1f4..29a31c2 100644
--- a/core/modules/block/block.services.yml
+++ b/core/modules/block/block.services.yml
@@ -9,3 +9,8 @@ services:
     factory_method: get
     factory_service: cache_factory
     arguments: [block]
+  theme.negotiator.block.admin_demo:
+    class: Drupal\block\Theme\AdminDemoNegotiator
+    tags:
+      - { name: theme_negotiator, priority: 1000 }
+
diff --git a/core/modules/block/lib/Drupal/block/Theme/AdminDemoNegotiator.php b/core/modules/block/lib/Drupal/block/Theme/AdminDemoNegotiator.php
new file mode 100644
index 0000000..d3dcb33
--- /dev/null
+++ b/core/modules/block/lib/Drupal/block/Theme/AdminDemoNegotiator.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block\Theme\AdminDemoNegotiator.
+ */
+
+namespace Drupal\block\Theme;
+
+use Drupal\Core\Theme\ThemeNegotiatorInterface;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Negotiates the theme for the block admin demo page via the URL.
+ */
+class AdminDemoNegotiator implements ThemeNegotiatorInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function determineActiveTheme(Request $request) {
+    // We return exactly what was passed in, to guarantee that the page will
+    // always be displayed using the theme whose blocks are being configured.
+    if ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'block.admin_demo') {
+      return $request->attributes->get('theme');
+    }
+  }
+
+}
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index b31b8d8..bd9a1fc 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -166,7 +166,7 @@ function content_translation_menu() {
     if (content_translation_enabled($entity_type)) {
       $path = $info['menu_base_path'];
       $entity_position = count(explode('/', $path)) - 1;
-      $keys = array_flip(array('theme_callback', 'theme_arguments', 'access_callback', 'access_arguments', 'load_arguments'));
+      $keys = array_flip(array('access_callback', 'access_arguments', 'load_arguments'));
       $menu_info = array_intersect_key($info['translation']['content_translation'], $keys) + array('file' => 'content_translation.pages.inc');
       $item = array();
 
diff --git a/core/modules/contextual/contextual.module b/core/modules/contextual/contextual.module
index 529fa5c..508b9ae 100644
--- a/core/modules/contextual/contextual.module
+++ b/core/modules/contextual/contextual.module
@@ -6,21 +6,6 @@
  */
 
 /**
- * Implements hook_menu().
- */
-function contextual_menu() {
-  // @todo Remove this menu item in http://drupal.org/node/1954892 when theme
-  //   callbacks are replaced with something else.
-  $items['contextual/render'] = array(
-    'route_name' => 'contextual.render',
-    'theme callback' => 'ajax_base_page_theme',
-    'type' => MENU_CALLBACK,
-  );
-
-  return $items;
-}
-
-/**
  * Implements hook_toolbar().
  */
 function contextual_toolbar() {
diff --git a/core/modules/contextual/contextual.routing.yml b/core/modules/contextual/contextual.routing.yml
index 8ab2f28..b44d619 100644
--- a/core/modules/contextual/contextual.routing.yml
+++ b/core/modules/contextual/contextual.routing.yml
@@ -2,5 +2,7 @@ contextual.render:
   path: '/contextual/render'
   defaults:
     _controller: '\Drupal\contextual\ContextualController::render'
+  options:
+    _theme: ajax_base_page
   requirements:
     _permission: 'access contextual links'
diff --git a/core/modules/edit/edit.module b/core/modules/edit/edit.module
index 339e255..3a0b167 100644
--- a/core/modules/edit/edit.module
+++ b/core/modules/edit/edit.module
@@ -18,26 +18,6 @@
 use Drupal\user\TempStoreFactory;
 
 /**
- * Implements hook_menu().
- */
-function edit_menu() {
-  // @todo Remove these menu items in http://drupal.org/node/1954892 when theme
-  //   callbacks are replaced with something else.
-  $items['edit/metadata'] = array(
-    'route_name' => 'edit.metadata',
-    'theme callback' => 'ajax_base_page_theme',
-    'type' => MENU_CALLBACK,
-  );
-  $items['edit/form/%/%/%/%/%'] = array(
-    'route_name' => 'edit.field_form',
-    'theme callback' => 'ajax_base_page_theme',
-    'type' => MENU_CALLBACK,
-  );
-
-  return $items;
-}
-
-/**
  * Implements hook_permission().
  */
 function edit_permission() {
diff --git a/core/modules/edit/edit.routing.yml b/core/modules/edit/edit.routing.yml
index 41acaac..80fbbf8 100644
--- a/core/modules/edit/edit.routing.yml
+++ b/core/modules/edit/edit.routing.yml
@@ -2,9 +2,10 @@ edit.metadata:
   path: '/edit/metadata'
   defaults:
     _controller: '\Drupal\edit\EditController::metadata'
+  options:
+    _theme: ajax_base_page
   requirements:
     _permission: 'access in-place editing'
-
 edit.attachments:
   path: '/edit/attachments'
   defaults:
@@ -16,6 +17,9 @@ edit.field_form:
   path: '/edit/form/{entity_type}/{entity}/{field_name}/{langcode}/{view_mode_id}'
   defaults:
     _controller: '\Drupal\edit\EditController::fieldForm'
+  options:
+    _access_mode: 'ALL'
+    _theme: ajax_base_page
   requirements:
     _permission: 'access in-place editing'
     _access_edit_entity_field: 'TRUE'
diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module
index 6364102..cdf0a79 100644
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -140,21 +140,6 @@ function editor_library_info() {
 }
 
 /**
- * Implements hook_menu().
- */
-function editor_menu() {
-  // @todo Remove this menu item in http://drupal.org/node/1954892 when theme
-  //   callbacks are replaced with something else.
-  $items['editor/%/%/%/%/%'] = array(
-    'route_name' => 'editor.field_untransformed_text',
-    'theme callback' => 'ajax_base_page_theme',
-    'type' => MENU_CALLBACK,
-  );
-
-  return $items;
-}
-
-/**
  * Implements hook_form_FORM_ID_alter().
  */
 function editor_form_filter_admin_overview_alter(&$form, $form_state) {
diff --git a/core/modules/editor/editor.routing.yml b/core/modules/editor/editor.routing.yml
index 3308dd0..bf9d360 100644
--- a/core/modules/editor/editor.routing.yml
+++ b/core/modules/editor/editor.routing.yml
@@ -2,6 +2,8 @@ editor.field_untransformed_text:
   path: '/editor/{entity_type}/{entity}/{field_name}/{langcode}/{view_mode_id}'
   defaults:
     _controller: '\Drupal\editor\EditorController::getUntransformedText'
+  options:
+    _theme: ajax_base_page
   requirements:
     _permission: 'access in-place editing'
     _access_edit_entity_field: 'TRUE'
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index bf67993..1a48d2c 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -38,21 +38,6 @@ function file_help($path, $arg) {
 }
 
 /**
- * Implements hook_menu().
- */
-function file_menu() {
-  $items = array();
-
-  $items['file/ajax'] = array(
-    'route_name' => 'file.ajax_upload',
-    'theme callback' => 'ajax_base_page_theme',
-    'type' => MENU_CALLBACK,
-  );
-
-  return $items;
-}
-
-/**
  * Implements hook_element_info().
  *
  * The managed file element may be used anywhere in Drupal.
diff --git a/core/modules/file/file.routing.yml b/core/modules/file/file.routing.yml
index 8bf971c..d9d4efa 100644
--- a/core/modules/file/file.routing.yml
+++ b/core/modules/file/file.routing.yml
@@ -2,6 +2,8 @@ file.ajax_upload:
   path: '/file/ajax'
   defaults:
     _controller: '\Drupal\file\Controller\FileWidgetAjaxController::upload'
+  options:
+    _theme: ajax_base_page
   requirements:
     _permission: 'access content'
 
diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchPageOverrideTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchPageOverrideTest.php
index ab3f913..101b745 100644
--- a/core/modules/search/lib/Drupal/search/Tests/SearchPageOverrideTest.php
+++ b/core/modules/search/lib/Drupal/search/Tests/SearchPageOverrideTest.php
@@ -38,7 +38,7 @@ function setUp() {
 
     // Enable the extra type module for searching.
     \Drupal::config('search.settings')->set('active_plugins', array('node_search', 'user_search', 'search_extra_type_search'))->save();
-    \Drupal::state()->set('menu_rebuild_needed', TRUE);
+    \Drupal::service('router.builder')->rebuild();
   }
 
   function testSearchPageHook() {
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutLinksTest.php b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutLinksTest.php
index bd2d194..b747d79 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutLinksTest.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutLinksTest.php
@@ -145,6 +145,7 @@ function testShortcutLinkDelete() {
    */
   function testNoShortcutLink() {
     // Change to a theme that displays shortcuts.
+    theme_enable(array('seven'));
     \Drupal::config('system.theme')
       ->set('default', 'seven')
       ->save();
@@ -157,8 +158,9 @@ function testNoShortcutLink() {
     $this->assertNoRaw('add-shortcut', 'Add to shortcuts link was not shown on a page the user does not have access to.');
 
     // Verify that the testing mechanism works by verifying the shortcut
-    // link appears on admin/content/node.
-    $this->drupalGet('admin/content/node');
+    // link appears on admin/people.
+    $this->drupalGet('admin/people');
     $this->assertRaw('add-shortcut', 'Add to shortcuts link was shown on a page the user does have access to.');
   }
+
 }
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutTestBase.php b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutTestBase.php
index 06088ef..e0bdb31 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutTestBase.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutTestBase.php
@@ -69,7 +69,7 @@ function setUp() {
     }
 
     // Create users.
-    $this->admin_user = $this->drupalCreateUser(array('access toolbar', 'administer shortcuts', 'view the administration theme', 'create article content', 'create page content', 'access content overview'));
+    $this->admin_user = $this->drupalCreateUser(array('access toolbar', 'administer shortcuts', 'view the administration theme', 'create article content', 'create page content', 'access content overview', 'administer users'));
     $this->shortcut_user = $this->drupalCreateUser(array('customize shortcut links', 'switch shortcut sets'));
 
     // Create a node.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php
index f341681..28e3694 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php
@@ -498,18 +498,9 @@ public function testThemeIntegration() {
     $this->doTestThemeCallbackFakeTheme();
 
     $this->initializeTestThemeConfiguration();
-    $this->doTestHookCustomTheme();
-
-    $this->initializeTestThemeConfiguration();
-    $this->doTestThemeCallbackHookCustomTheme();
-
-    $this->initializeTestThemeConfiguration();
     $this->doTestThemeCallbackAdministrative();
 
     $this->initializeTestThemeConfiguration();
-    $this->doTestThemeCallbackInheritance();
-
-    $this->initializeTestThemeConfiguration();
     $this->doTestThemeCallbackNoThemeRequested();
 
     $this->initializeTestThemeConfiguration();
@@ -532,27 +523,17 @@ protected function initializeTestThemeConfiguration() {
   }
 
   /**
-   * Test the theme callback when it is set to use an administrative theme.
+   * Test the theme negotiation when it is set to use an administrative theme.
    */
   protected function doTestThemeCallbackAdministrative() {
     theme_enable(array($this->admin_theme));
     $this->drupalGet('menu-test/theme-callback/use-admin-theme');
-    $this->assertText('Custom theme: seven. Actual theme: seven.', 'The administrative theme can be correctly set in a theme callback.');
-    $this->assertRaw('seven/style.css', "The administrative theme's CSS appears on the page.");
-  }
-
-  /**
-   * Test that the theme callback is properly inherited.
-   */
-  protected function doTestThemeCallbackInheritance() {
-    theme_enable(array($this->admin_theme));
-    $this->drupalGet('menu-test/theme-callback/use-admin-theme/inheritance');
-    $this->assertText('Custom theme: seven. Actual theme: seven. Theme callback inheritance is being tested.', 'Theme callback inheritance correctly uses the administrative theme.');
+    $this->assertText('Active theme: seven. Actual theme: seven.', 'The administrative theme can be correctly set in a theme negotiation.');
     $this->assertRaw('seven/style.css', "The administrative theme's CSS appears on the page.");
   }
 
   /**
-   * Test the theme callback when the site is in maintenance mode.
+   * Test the theme negotiation when the site is in maintenance mode.
    */
   protected function doTestThemeCallbackMaintenanceMode() {
     $this->container->get('state')->set('system.maintenance_mode', TRUE);
@@ -567,76 +548,44 @@ protected function doTestThemeCallbackMaintenanceMode() {
     $admin_user = $this->drupalCreateUser(array('access site in maintenance mode'));
     $this->drupalLogin($admin_user);
     $this->drupalGet('menu-test/theme-callback/use-admin-theme');
-    $this->assertText('Custom theme: seven. Actual theme: seven.', 'The theme callback system is correctly triggered for an administrator when the site is in maintenance mode.');
+    $this->assertText('Active theme: seven. Actual theme: seven.', 'The theme negotiation system is correctly triggered for an administrator when the site is in maintenance mode.');
     $this->assertRaw('seven/style.css', "The administrative theme's CSS appears on the page.");
 
     $this->container->get('state')->set('system.maintenance_mode', FALSE);
   }
 
   /**
-   * Test the theme callback when it is set to use an optional theme.
+   * Test the theme negotiation when it is set to use an optional theme.
    */
   protected function doTestThemeCallbackOptionalTheme() {
     // Request a theme that is not enabled.
     $this->drupalGet('menu-test/theme-callback/use-stark-theme');
-    $this->assertText('Custom theme: NONE. Actual theme: bartik.', 'The theme callback system falls back on the default theme when a theme that is not enabled is requested.');
+    $this->assertText('Active theme: bartik. Actual theme: bartik.', 'The theme negotiation system falls back on the default theme when a theme that is not enabled is requested.');
     $this->assertRaw('bartik/css/style.css', "The default theme's CSS appears on the page.");
 
     // Now enable the theme and request it again.
     theme_enable(array($this->alternate_theme));
     $this->drupalGet('menu-test/theme-callback/use-stark-theme');
-    $this->assertText('Custom theme: stark. Actual theme: stark.', 'The theme callback system uses an optional theme once it has been enabled.');
+    $this->assertText('Active theme: stark. Actual theme: stark.', 'The theme negotiation system uses an optional theme once it has been enabled.');
     $this->assertRaw('stark/css/layout.css', "The optional theme's CSS appears on the page.");
   }
 
   /**
-   * Test the theme callback when it is set to use a theme that does not exist.
+   * Test the theme negotiation when it is set to use a theme that does not exist.
    */
   protected function doTestThemeCallbackFakeTheme() {
     $this->drupalGet('menu-test/theme-callback/use-fake-theme');
-    $this->assertText('Custom theme: NONE. Actual theme: bartik.', 'The theme callback system falls back on the default theme when a theme that does not exist is requested.');
+    $this->assertText('Active theme: bartik. Actual theme: bartik.', 'The theme negotiation system falls back on the default theme when a theme that does not exist is requested.');
     $this->assertRaw('bartik/css/style.css', "The default theme's CSS appears on the page.");
   }
 
   /**
-   * Test the theme callback when no theme is requested.
+   * Test the theme negotiation when no theme is requested.
    */
   protected function doTestThemeCallbackNoThemeRequested() {
     $this->drupalGet('menu-test/theme-callback/no-theme-requested');
-    $this->assertText('Custom theme: NONE. Actual theme: bartik.', 'The theme callback system falls back on the default theme when no theme is requested.');
+    $this->assertText('Active theme: bartik. Actual theme: bartik.', 'The theme negotiation system falls back on the default theme when no theme is requested.');
     $this->assertRaw('bartik/css/style.css', "The default theme's CSS appears on the page.");
   }
 
-  /**
-   * Test that hook_custom_theme() can control the theme of a page.
-   */
-  protected function doTestHookCustomTheme() {
-    // Trigger hook_custom_theme() to dynamically request the Stark theme for
-    // the requested page.
-    \Drupal::state()->set('menu_test.hook_custom_theme_name', $this->alternate_theme);
-    theme_enable(array($this->alternate_theme, $this->admin_theme));
-
-    // Visit a page that does not implement a theme callback. The above request
-    // should be honored.
-    $this->drupalGet('menu-test/no-theme-callback');
-    $this->assertText('Custom theme: stark. Actual theme: stark.', 'The result of hook_custom_theme() is used as the theme for the current page.');
-    $this->assertRaw('stark/css/layout.css', "The Stark theme's CSS appears on the page.");
-  }
-
-  /**
-   * Test that the theme callback wins out over hook_custom_theme().
-   */
-  protected function doTestThemeCallbackHookCustomTheme() {
-    // Trigger hook_custom_theme() to dynamically request the Stark theme for
-    // the requested page.
-    \Drupal::state()->set('menu_test.hook_custom_theme_name', $this->alternate_theme);
-    theme_enable(array($this->alternate_theme, $this->admin_theme));
-
-    // The menu "theme callback" should take precedence over a value set in
-    // hook_custom_theme().
-    $this->drupalGet('menu-test/theme-callback/use-admin-theme');
-    $this->assertText('Custom theme: seven. Actual theme: seven.', 'The result of hook_custom_theme() does not override what was set in a theme callback.');
-    $this->assertRaw('seven/style.css', "The Seven theme's CSS appears on the page.");
-  }
-
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php
index 9a13acb..554450a 100644
--- a/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php
@@ -178,7 +178,7 @@ function testThemeSettings() {
    * Test the administration theme functionality.
    */
   function testAdministrationTheme() {
-    theme_enable(array('seven'));
+    theme_enable(array('bartik', 'seven'));
 
     // Enable an administration theme and show it on the node admin pages.
     $edit = array(
diff --git a/core/modules/system/lib/Drupal/system/Theme/AdminNegotiator.php b/core/modules/system/lib/Drupal/system/Theme/AdminNegotiator.php
new file mode 100644
index 0000000..72359ea
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Theme/AdminNegotiator.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Theme\AdminNegotiator.
+ */
+
+namespace Drupal\system\Theme;
+
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Theme\ThemeNegotiatorInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Sets the active theme on admin pages.
+ */
+class AdminNegotiator implements ThemeNegotiatorInterface {
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $user;
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactory
+   */
+  protected $configFactory;
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManager
+   */
+  protected $entityManager;
+
+  /**
+   * Creates a new AdminNegotiator instance.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $user
+   *   The current user.
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   *   The config factory.
+   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   *   The entity manager.
+   */
+  public function __construct(AccountInterface $user, ConfigFactory $config_factory, EntityManager $entity_manager) {
+    $this->user = $user;
+    $this->configFactory = $config_factory;
+    $this->entityManager = $entity_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function determineActiveTheme(Request $request) {
+    $path = $request->attributes->get('_system_path');
+
+    // Don't break if the user_role entity is not available in order to decouple
+    // system and user module.
+    if ($this->entityManager->hasController('user_role', 'storage') && $this->user->hasPermission('view the administration theme') && path_is_admin($path)) {
+      return $this->configFactory->get('system.theme')->get('admin');
+    }
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Theme/BatchNegotiator.php b/core/modules/system/lib/Drupal/system/Theme/BatchNegotiator.php
new file mode 100644
index 0000000..a10374d
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Theme/BatchNegotiator.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Theme\BatchNegotiator.
+ */
+
+namespace Drupal\system\Theme;
+
+use Drupal\Core\Batch\BatchStorageInterface;
+use Drupal\Core\Theme\ThemeNegotiatorInterface;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Sets the active theme for the batch page.
+ */
+class BatchNegotiator implements ThemeNegotiatorInterface {
+
+  /**
+   * The batch storage.
+   *
+   * @var \Drupal\Core\Batch\BatchStorageInterface
+   */
+  protected $batchStorage;
+
+  /**
+   * Constructs a BatchNegotiator.
+   *
+   * @param \Drupal\Core\Batch\BatchStorageInterface $batch_storage
+   *   The batch storage.
+   */
+  public function __construct(BatchStorageInterface $batch_storage) {
+    $this->batchStorage = $batch_storage;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function determineActiveTheme(Request $request) {
+    if ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'system.batch_page') {
+      // Retrieve the current state of the batch.
+      $batch = &batch_get();
+      if (!$batch && $request->request->has('id')) {
+        $batch = $this->batchStorage->load($request->request->get('id'));
+      }
+      // Use the same theme as the page that started the batch.
+      if (!empty($batch['theme'])) {
+        return $batch['theme'];
+      }
+    }
+  }
+
+}
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 47665c5..ccbf7ac 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -1412,36 +1412,6 @@ function hook_template_preprocess_default_variables_alter(&$variables) {
 }
 
 /**
- * Return the machine-readable name of the theme to use for the current page.
- *
- * This hook can be used to dynamically set the theme for the current page
- * request. It should be used by modules which need to override the theme
- * based on dynamic conditions (for example, a module which allows the theme to
- * be set based on the current user's role). The return value of this hook will
- * be used on all pages except those which have a valid per-page or per-section
- * theme set via a theme callback function in hook_menu(); the themes on those
- * pages can only be overridden using hook_menu_alter().
- *
- * Note that returning different themes for the same path may not work with page
- * caching. This is most likely to be a problem if an anonymous user on a given
- * path could have different themes returned under different conditions.
- *
- * Since only one theme can be used at a time, the last (i.e., highest
- * weighted) module which returns a valid theme name from this hook will
- * prevail.
- *
- * @return
- *   The machine-readable name of the theme that should be used for the current
- *   page request. The value returned from this function will only have an
- *   effect if it corresponds to a currently-active theme on the site. Do not
- *   return a value if you do not wish to set a custom theme.
- */
-function hook_custom_theme() {
-  // Allow the user to request a particular theme via a query parameter.
-  return \Drupal::request()->query->get('theme');
-}
-
-/**
  * Log an event message.
  *
  * This hook allows modules to route log events to custom destinations, such as
diff --git a/core/modules/system/system.api.php.orig b/core/modules/system/system.api.php.orig
new file mode 100644
index 0000000..47665c5
--- /dev/null
+++ b/core/modules/system/system.api.php.orig
@@ -0,0 +1,3317 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by Drupal core and the System module.
+ */
+
+use Drupal\Core\Utility\UpdateException;
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Defines one or more hooks that are exposed by a module.
+ *
+ * Normally hooks do not need to be explicitly defined. However, by declaring a
+ * hook explicitly, a module may define a "group" for it. Modules that implement
+ * a hook may then place their implementation in either $module.module or in
+ * $module.$group.inc. If the hook is located in $module.$group.inc, then that
+ * file will be automatically loaded when needed.
+ * In general, hooks that are rarely invoked and/or are very large should be
+ * placed in a separate include file, while hooks that are very short or very
+ * frequently called should be left in the main module file so that they are
+ * always available.
+ *
+ * @return
+ *   An associative array whose keys are hook names and whose values are an
+ *   associative array containing:
+ *   - group: A string defining the group to which the hook belongs. The module
+ *     system will determine whether a file with the name $module.$group.inc
+ *     exists, and automatically load it when required.
+ *
+ * See system_hook_info() for all hook groups defined by Drupal core.
+ *
+ * @see hook_hook_info_alter().
+ */
+function hook_hook_info() {
+  $hooks['token_info'] = array(
+    'group' => 'tokens',
+  );
+  $hooks['tokens'] = array(
+    'group' => 'tokens',
+  );
+  return $hooks;
+}
+
+/**
+ * Define administrative paths.
+ *
+ * Modules may specify whether or not the routing paths they define are
+ * to be considered administrative. Other modules may use this information to
+ * display those pages differently (e.g. in a modal overlay, or in a different
+ * theme).
+ *
+ * To change the administrative status of menu items defined in another module's
+ * routing paths, modules should implement hook_admin_paths_alter().
+ *
+ * @return
+ *   An associative array. For each item, the key is the path in question, in
+ *   a format acceptable to drupal_match_path(). The value for each item should
+ *   be TRUE (for paths considered administrative) or FALSE (for non-
+ *   administrative paths).
+ *
+ * @see drupal_match_path()
+ * @see hook_admin_paths_alter()
+ */
+function hook_admin_paths() {
+  $paths = array(
+    'mymodule/*/add' => TRUE,
+    'mymodule/*/edit' => TRUE,
+  );
+  return $paths;
+}
+
+/**
+ * Redefine administrative paths defined by other modules.
+ *
+ * @param $paths
+ *   An associative array of administrative paths, as defined by implementations
+ *   of hook_admin_paths().
+ *
+ * @see hook_admin_paths()
+ */
+function hook_admin_paths_alter(&$paths) {
+  // Treat all user pages as administrative.
+  $paths['user'] = TRUE;
+  $paths['user/*'] = TRUE;
+  // Treat the forum topic node form as a non-administrative page.
+  $paths['node/add/forum'] = FALSE;
+}
+
+/**
+ * Perform periodic actions.
+ *
+ * Modules that require some commands to be executed periodically can
+ * implement hook_cron(). The engine will then call the hook whenever a cron
+ * run happens, as defined by the administrator. Typical tasks managed by
+ * hook_cron() are database maintenance, backups, recalculation of settings
+ * or parameters, automated mailing, and retrieving remote data.
+ *
+ * Short-running or non-resource-intensive tasks can be executed directly in
+ * the hook_cron() implementation.
+ *
+ * Long-running tasks and tasks that could time out, such as retrieving remote
+ * data, sending email, and intensive file tasks, should use the queue API
+ * instead of executing the tasks directly. To do this, first define one or
+ * more queues via hook_queue_info(). Then, add items that need to be
+ * processed to the defined queues.
+ */
+function hook_cron() {
+  // Short-running operation example, not using a queue:
+  // Delete all expired records since the last cron run.
+  $expires = \Drupal::state()->get('mymodule.cron_last_run', REQUEST_TIME);
+  db_delete('mymodule_table')
+    ->condition('expires', $expires, '>=')
+    ->execute();
+  \Drupal::state()->set('mymodule.cron_last_run', REQUEST_TIME);
+
+  // Long-running operation example, leveraging a queue:
+  // Fetch feeds from other sites.
+  $result = db_query('SELECT * FROM {aggregator_feed} WHERE checked + refresh < :time AND refresh <> :never', array(
+    ':time' => REQUEST_TIME,
+    ':never' => AGGREGATOR_CLEAR_NEVER,
+  ));
+  $queue = \Drupal::queue('aggregator_feeds');
+  foreach ($result as $feed) {
+    $queue->createItem($feed);
+  }
+}
+
+/**
+ * Alter available data types for typed data wrappers.
+ *
+ * @param array $data_types
+ *   An array of data type information.
+ *
+ * @see hook_data_type_info()
+ */
+function hook_data_type_info_alter(&$data_types) {
+  $data_types['email']['class'] = '\Drupal\mymodule\Type\Email';
+}
+
+/**
+ * Declare queues holding items that need to be run periodically.
+ *
+ * While there can be only one hook_cron() process running at the same time,
+ * there can be any number of processes defined here running. Because of
+ * this, long running tasks are much better suited for this API. Items queued
+ * in hook_cron() might be processed in the same cron run if there are not many
+ * items in the queue, otherwise it might take several requests, which can be
+ * run in parallel.
+ *
+ * You can create queues, add items to them, claim them, etc without declaring
+ * the queue in this hook if you want, however, you need to take care of
+ * processing the items in the queue in that case.
+ *
+ * @return
+ *   An associative array where the key is the queue name and the value is
+ *   again an associative array. Possible keys are:
+ *   - 'worker callback': A PHP callable to call. It will be called
+ *     with one argument, the item created via
+ *     \Drupal\Core\Queue\QueueInterface::createItem().
+ *   - 'cron': (optional) An associative array containing the optional key:
+ *     - 'time': (optional) How much time Drupal cron should spend on calling
+ *       this worker in seconds. Defaults to 15.
+ *     If the cron key is not defined, the queue will not be processed by cron,
+ *     and must be processed by other means.
+ *
+ * @see hook_cron()
+ * @see hook_queue_info_alter()
+ */
+function hook_queue_info() {
+  $queues['aggregator_feeds'] = array(
+    'title' => t('Aggregator refresh'),
+    'worker callback' => array('Drupal\my_module\MyClass', 'aggregatorRefresh'),
+    // Only needed if this queue should be processed by cron.
+    'cron' => array(
+      'time' => 60,
+    ),
+  );
+  return $queues;
+}
+
+/**
+ * Alter cron queue information before cron runs.
+ *
+ * Called by drupal_cron_run() to allow modules to alter cron queue settings
+ * before any jobs are processesed.
+ *
+ * @param array $queues
+ *   An array of cron queue information.
+ *
+ * @see hook_queue_info()
+ * @see drupal_cron_run()
+ */
+function hook_queue_info_alter(&$queues) {
+  // This site has many feeds so let's spend 90 seconds on each cron run
+  // updating feeds instead of the default 60.
+  $queues['aggregator_feeds']['cron']['time'] = 90;
+}
+
+/**
+ * Allows modules to declare their own Form API element types and specify their
+ * default values.
+ *
+ * This hook allows modules to declare their own form element types and to
+ * specify their default values. The values returned by this hook will be
+ * merged with the elements returned by form constructor implementations and so
+ * can return defaults for any Form APIs keys in addition to those explicitly
+ * mentioned below.
+ *
+ * Each of the form element types defined by this hook is assumed to have
+ * a matching theme function, e.g. theme_elementtype(), which should be
+ * registered with hook_theme() as normal.
+ *
+ * For more information about custom element types see the explanation at
+ * http://drupal.org/node/169815.
+ *
+ * @return
+ *  An associative array describing the element types being defined. The array
+ *  contains a sub-array for each element type, with the machine-readable type
+ *  name as the key. Each sub-array has a number of possible attributes:
+ *  - "#input": boolean indicating whether or not this element carries a value
+ *    (even if it's hidden).
+ *  - "#process": array of callback functions taking $element, $form_state,
+ *    and $complete_form.
+ *  - "#after_build": array of callables taking $element and $form_state.
+ *  - "#validate": array of callback functions taking $form and $form_state.
+ *  - "#element_validate": array of callback functions taking $element and
+ *    $form_state.
+ *  - "#pre_render": array of callables taking $element.
+ *  - "#post_render": array of callables taking $children and $element.
+ *  - "#submit": array of callback functions taking $form and $form_state.
+ *  - "#title_display": optional string indicating if and how #title should be
+ *    displayed, see theme_form_element() and theme_form_element_label().
+ *
+ * @see hook_element_info_alter()
+ * @see system_element_info()
+ */
+function hook_element_info() {
+  $types['filter_format'] = array(
+    '#input' => TRUE,
+  );
+  return $types;
+}
+
+/**
+ * Alter the element type information returned from modules.
+ *
+ * A module may implement this hook in order to alter the element type defaults
+ * defined by a module.
+ *
+ * @param $type
+ *   All element type defaults as collected by hook_element_info().
+ *
+ * @see hook_element_info()
+ */
+function hook_element_info_alter(&$type) {
+  // Decrease the default size of textfields.
+  if (isset($type['textfield']['#size'])) {
+    $type['textfield']['#size'] = 40;
+  }
+}
+
+/**
+ * Perform necessary alterations to the JavaScript before it is presented on
+ * the page.
+ *
+ * @param $javascript
+ *   An array of all JavaScript being presented on the page.
+ *
+ * @see drupal_add_js()
+ * @see drupal_get_js()
+ * @see drupal_js_defaults()
+ */
+function hook_js_alter(&$javascript) {
+  // Swap out jQuery to use an updated version of the library.
+  $javascript['core/assets/vendor/jquery/jquery.js']['data'] = drupal_get_path('module', 'jquery_update') . '/jquery.js';
+}
+
+/**
+ * Registers JavaScript/CSS libraries associated with a module.
+ *
+ * Modules implementing this return an array of arrays. The key to each
+ * sub-array is the machine readable name of the library. Each library may
+ * contain the following items:
+ *
+ * - 'title': The human readable name of the library.
+ * - 'website': The URL of the library's web site.
+ * - 'version': A string specifying the version of the library; intentionally
+ *   not a float because a version like "1.2.3" is not a valid float. Use PHP's
+ *   version_compare() to compare different versions.
+ * - 'js': An array of JavaScript elements; each element's key is used as $data
+ *   argument, each element's value is used as $options array for
+ *   drupal_add_js(). To add library-specific (not module-specific) JavaScript
+ *   settings, the key may be skipped, the value must specify
+ *   'type' => 'setting', and the actual settings must be contained in a 'data'
+ *   element of the value.
+ * - 'css': Like 'js', an array of CSS elements passed to drupal_add_css().
+ * - 'dependencies': An array of libraries that are required for a library. Each
+ *   element is an array listing the module and name of another library. Note
+ *   that all dependencies for each dependent library will also be added when
+ *   this library is added.
+ *
+ * Registered information for a library should contain re-usable data only.
+ * Module- or implementation-specific data and integration logic should be added
+ * separately.
+ *
+ * @return
+ *   An array defining libraries associated with a module.
+ *
+ * @see system_library_info()
+ * @see drupal_add_library()
+ * @see drupal_get_library()
+ */
+function hook_library_info() {
+  // Library One.
+  $libraries['library-1'] = array(
+    'title' => 'Library One',
+    'website' => 'http://example.com/library-1',
+    'version' => '1.2',
+    'js' => array(
+      drupal_get_path('module', 'my_module') . '/library-1.js' => array(),
+    ),
+    'css' => array(
+      drupal_get_path('module', 'my_module') . '/library-2.css' => array(
+        'type' => 'file',
+        'media' => 'screen',
+      ),
+    ),
+  );
+  // Library Two.
+  $libraries['library-2'] = array(
+    'title' => 'Library Two',
+    'website' => 'http://example.com/library-2',
+    'version' => '3.1-beta1',
+    'js' => array(
+      // JavaScript settings may use the 'data' key.
+      array(
+        'type' => 'setting',
+        'data' => array('library2' => TRUE),
+      ),
+    ),
+    'dependencies' => array(
+      // Require jQuery UI core by System module.
+      array('system', 'jquery.ui.core'),
+      // Require our other library.
+      array('my_module', 'library-1'),
+      // Require another library.
+      array('other_module', 'library-3'),
+    ),
+  );
+  return $libraries;
+}
+
+/**
+ * Alters the JavaScript/CSS library registry.
+ *
+ * Allows certain, contributed modules to update libraries to newer versions
+ * while ensuring backwards compatibility. In general, such manipulations should
+ * only be done by designated modules, since most modules that integrate with a
+ * certain library also depend on the API of a certain library version.
+ *
+ * @param $libraries
+ *   The JavaScript/CSS libraries provided by $module. Keyed by internal library
+ *   name and passed by reference.
+ * @param $module
+ *   The name of the module that registered the libraries.
+ *
+ * @see hook_library_info()
+ */
+function hook_library_info_alter(&$libraries, $module) {
+  // Update Farbtastic to version 2.0.
+  if ($module == 'system' && isset($libraries['farbtastic'])) {
+    // Verify existing version is older than the one we are updating to.
+    if (version_compare($libraries['farbtastic']['version'], '2.0', '<')) {
+      // Update the existing Farbtastic to version 2.0.
+      $libraries['farbtastic']['version'] = '2.0';
+      $libraries['farbtastic']['js'] = array(
+        drupal_get_path('module', 'farbtastic_update') . '/farbtastic-2.0.js' => array(),
+      );
+    }
+  }
+}
+
+/**
+ * Alter CSS files before they are output on the page.
+ *
+ * @param $css
+ *   An array of all CSS items (files and inline CSS) being requested on the page.
+ *
+ * @see drupal_add_css()
+ * @see drupal_get_css()
+ */
+function hook_css_alter(&$css) {
+  // Remove defaults.css file.
+  unset($css[drupal_get_path('module', 'system') . '/defaults.css']);
+}
+
+/**
+ * Alter the commands that are sent to the user through the Ajax framework.
+ *
+ * @param $commands
+ *   An array of all commands that will be sent to the user.
+ *
+ * @see ajax_render()
+ */
+function hook_ajax_render_alter($commands) {
+  // Inject any new status messages into the content area.
+  $status_messages = array('#theme' => 'status_messages');
+  $commands[] = ajax_command_prepend('#block-system-main .content', drupal_render($status_messages));
+}
+
+/**
+ * Add elements to a page before it is rendered.
+ *
+ * Use this hook when you want to add elements at the page level. For your
+ * additions to be printed, they have to be placed below a top level array key
+ * of the $page array that has the name of a region of the active theme.
+ *
+ * By default, valid region keys are 'page_top', 'header', 'sidebar_first',
+ * 'content', 'sidebar_second' and 'page_bottom'. To get a list of all regions
+ * of the active theme, use system_region_list($theme). Note that $theme is a
+ * global variable.
+ *
+ * If you want to alter the elements added by other modules or if your module
+ * depends on the elements of other modules, use hook_page_alter() instead which
+ * runs after this hook.
+ *
+ * @param $page
+ *   Nested array of renderable elements that make up the page.
+ *
+ * @see hook_page_alter()
+ * @see drupal_render_page()
+ */
+function hook_page_build(&$page) {
+  $path = drupal_get_path('module', 'foo');
+  // Add JavaScript/CSS assets to all pages.
+  // @see drupal_process_attached()
+  $page['#attached']['js'][$path . '/foo.css'] = array('every_page' => TRUE);
+  $page['#attached']['css'][$path . '/foo.base.css'] = array('every_page' => TRUE);
+  $page['#attached']['css'][$path . '/foo.theme.css'] = array('every_page' => TRUE);
+
+  // Add a special CSS file to a certain page only.
+  if (drupal_is_front_page()) {
+    $page['#attached']['css'][] = $path . '/foo.front.css';
+  }
+
+  // Append a standard disclaimer to the content region on a node detail page.
+  if (menu_get_object('node', 1)) {
+    $page['content']['disclaimer'] = array(
+      '#markup' => t('Acme, Inc. is not responsible for the contents of this sample code.'),
+      '#weight' => 25,
+    );
+  }
+}
+
+/**
+ * Alter a menu router item right after it has been retrieved from the database or cache.
+ *
+ * This hook is invoked by menu_get_item() and allows for run-time alteration of router
+ * information (page_callback, title, and so on) before it is translated and checked for
+ * access. The passed-in $router_item is statically cached for the current request, so this
+ * hook is only invoked once for any router item that is retrieved via menu_get_item().
+ *
+ * Usually, modules will only want to inspect the router item and conditionally
+ * perform other actions (such as preparing a state for the current request).
+ * Note that this hook is invoked for any router item that is retrieved by
+ * menu_get_item(), which may or may not be called on the path itself, so implementations
+ * should check the $path parameter if the alteration should fire for the current request
+ * only.
+ *
+ * @param $router_item
+ *   The menu router item for $path.
+ * @param $path
+ *   The originally passed path, for which $router_item is responsible.
+ * @param $original_map
+ *   The path argument map, as contained in $path.
+ *
+ * @see menu_get_item()
+ */
+function hook_menu_get_item_alter(&$router_item, $path, $original_map) {
+  // When retrieving the router item for the current path...
+  if ($path == current_path()) {
+    // ...call a function that prepares something for this request.
+    mymodule_prepare_something();
+  }
+}
+
+/**
+ * Define links for menus.
+ *
+ * @section sec_menu_link Creating Menu Items
+ * Menu item example of type MENU_NORMAL_ITEM:
+ * @code
+ * // Make "Foo settings" appear on the admin Config page
+ * $items['admin/config/system/foo'] = array(
+ *   'title' => 'Foo settings',
+ *   'type' => MENU_NORMAL_ITEM,
+ *   'route_name' => 'foo.settings'
+ * );
+ * @endcode
+ *
+ * @todo The section that used to be here about path argument substitution has
+ *   been removed, but is still referred to in the return section. It needs to
+ *   be added back in, or a corrected version of it.
+ *
+ * @return
+ *   An array of menu items. Each menu item has a key corresponding to the
+ *   Drupal path being registered. The corresponding array value is an
+ *   associative array that may contain the following key-value pairs:
+ *   - "title": Required. The untranslated title of the menu item.
+ *   - "title callback": Function to generate the title; defaults to t().
+ *     If you require only the raw string to be output, set this to FALSE.
+ *   - "title arguments": Arguments to send to t() or your custom callback,
+ *     with path component substitution as described above.
+ *   - "description": The untranslated description of the menu item.
+ *   - description callback: Function to generate the description; defaults to
+ *     t(). If you require only the raw string to be output, set this to FALSE.
+ *   - description arguments: Arguments to send to t() or your custom callback,
+ *     with path component substitution as described above.
+ *   - "weight": An integer that determines the relative position of items in
+ *     the menu; higher-weighted items sink. Defaults to 0. Menu items with the
+ *     same weight are ordered alphabetically.
+ *   - "menu_name": Optional. Set this to a custom menu if you don't want your
+ *     item to be placed in the default Tools menu.
+ *   - "expanded": Optional. If set to TRUE, and if a menu link is provided for
+ *     this menu item (as a result of other properties), then the menu link is
+ *     always expanded, equivalent to its 'always expanded' checkbox being set
+ *     in the UI.
+ *   - "position": Position of the block ('left' or 'right') on the system
+ *     administration page for this item.
+ *   - "type": A bitmask of flags describing properties of the menu item.
+ *     Many shortcut bitmasks are provided as constants in menu.inc:
+ *     - MENU_NORMAL_ITEM: Normal menu items show up in the menu tree and can be
+ *       moved/hidden by the administrator.
+ *     - MENU_SUGGESTED_ITEM: Modules may "suggest" menu items that the
+ *       administrator may enable.
+ *     If the "type" element is omitted, MENU_NORMAL_ITEM is assumed.
+ *   - "options": An array of options to be passed to l() when generating a link
+ *     from this menu item.
+ *
+ * For a detailed usage example, see page_example.module.
+ * For comprehensive documentation on the menu system, see
+ * http://drupal.org/node/102338.
+ *
+ * @see menu
+ */
+function hook_menu() {
+  $items['example'] = array(
+    'title' => 'Example Page',
+    'route_name' => 'example.page',
+  );
+  $items['example/feed'] = array(
+    'title' => 'Example RSS feed',
+    'route_name' => 'example.feed',
+  );
+
+  return $items;
+}
+
+/**
+ * Alter the data being saved to the {menu_router} table after hook_menu is invoked.
+ *
+ * This hook is invoked by menu_router_build(). The menu definitions are passed
+ * in by reference. Each element of the $items array is one item returned
+ * by a module from hook_menu. Additional items may be added, or existing items
+ * altered.
+ *
+ * @param $items
+ *   Associative array of menu router definitions returned from hook_menu().
+ */
+function hook_menu_alter(&$items) {
+  // Example - disable the page at node/add
+  $items['node/add']['access callback'] = FALSE;
+}
+
+/**
+ * Alter tabs and actions displayed on the page before they are rendered.
+ *
+ * This hook is invoked by menu_local_tasks(). The system-determined tabs and
+ * actions are passed in by reference. Additional tabs or actions may be added.
+ *
+ * Each tab or action is an associative array containing:
+ * - #theme: The theme function to use to render.
+ * - #link: An associative array containing:
+ *   - title: The localized title of the link.
+ *   - href: The system path to link to.
+ *   - localized_options: An array of options to pass to l().
+ * - #weight: The link's weight compared to other links.
+ * - #active: Whether the link should be marked as 'active'.
+ *
+ * @param array $data
+ *   An associative array containing:
+ *   - actions: A list of of actions keyed by their href, each one being an
+ *     associative array as described above.
+ *   - tabs: A list of (up to 2) tab levels that contain a list of of tabs keyed
+ *     by their href, each one being an associative array as described above.
+ * @param string $route_name
+ *   The route name of the page.
+ */
+function hook_menu_local_tasks(&$data, $route_name) {
+  // Add an action linking to node/add to all pages.
+  $data['actions']['node/add'] = array(
+    '#theme' => 'menu_local_action',
+    '#link' => array(
+      'title' => t('Add new content'),
+      'href' => 'node/add',
+      'localized_options' => array(
+        'attributes' => array(
+          'title' => t('Add new content'),
+        ),
+      ),
+    ),
+  );
+
+  // Add a tab linking to node/add to all pages.
+  $data['tabs'][0]['node/add'] = array(
+    '#theme' => 'menu_local_task',
+    '#link' => array(
+      'title' => t('Example tab'),
+      'href' => 'node/add',
+      'localized_options' => array(
+        'attributes' => array(
+          'title' => t('Add new content'),
+        ),
+      ),
+    ),
+  );
+}
+
+/**
+ * Alter tabs and actions displayed on the page before they are rendered.
+ *
+ * This hook is invoked by menu_local_tasks(). The system-determined tabs and
+ * actions are passed in by reference. Existing tabs or actions may be altered.
+ *
+ * @param array $data
+ *   An associative array containing tabs and actions. See
+ *   hook_menu_local_tasks() for details.
+ * @param string $route_name
+ *   The route name of the page.
+ *
+ * @see hook_menu_local_tasks()
+ */
+function hook_menu_local_tasks_alter(&$data, $route_name) {
+}
+
+/**
+ * Alter local actions plugins.
+ *
+ * @param array $local_actions
+ *   The array of local action plugin definitions, keyed by plugin ID.
+ *
+ * @see \Drupal\Core\Menu\LocalActionInterface
+ * @see \Drupal\Core\Menu\LocalActionManager
+ */
+function hook_menu_local_actions_alter(&$local_actions) {
+}
+
+/**
+ * Alter local tasks plugins.
+ *
+ * @param array $local_tasks
+ *   The array of local tasks plugin definitions, keyed by plugin ID.
+ *
+ * @see \Drupal\Core\Menu\LocalTaskInterface
+ * @see \Drupal\Core\Menu\LocalTaskManager
+ */
+function hook_local_task_alter(&$local_tasks) {
+  // Remove a specified local task plugin.
+  unset($local_tasks['example_plugin_id']);
+}
+
+/**
+ * Alter contextual links before they are rendered.
+ *
+ * This hook is invoked by menu_contextual_links(). The system-determined
+ * contextual links are passed in by reference. Additional links may be added
+ * or existing links can be altered.
+ *
+ * Each contextual link must at least contain:
+ * - title: The localized title of the link.
+ * - href: The system path to link to.
+ * - localized_options: An array of options to pass to url().
+ *
+ * @param $links
+ *   An associative array containing contextual links for the given $root_path,
+ *   as described above. The array keys are used to build CSS class names for
+ *   contextual links and must therefore be unique for each set of contextual
+ *   links.
+ * @param $router_item
+ *   The menu router item belonging to the $root_path being requested.
+ * @param $root_path
+ *   The (parent) path that has been requested to build contextual links for.
+ *   This is a normalized path, which means that an originally passed path of
+ *   'node/123' became 'node/%'.
+ *
+ * @see hook_contextual_links_view_alter()
+ * @see menu_contextual_links()
+ * @see hook_menu()
+ * @see contextual_preprocess()
+ */
+function hook_menu_contextual_links_alter(&$links, $router_item, $root_path) {
+  // Add a link to all contextual links for nodes.
+  if ($root_path == 'node/%') {
+    $links['foo'] = array(
+      'title' => t('Do fu'),
+      'href' => 'foo/do',
+      'localized_options' => array(
+        'query' => array(
+          'foo' => 'bar',
+        ),
+      ),
+    );
+  }
+}
+
+/**
+ * Perform alterations before a page is rendered.
+ *
+ * Use this hook when you want to remove or alter elements at the page
+ * level, or add elements at the page level that depend on an other module's
+ * elements (this hook runs after hook_page_build().
+ *
+ * If you are making changes to entities such as forms, menus, or user
+ * profiles, use those objects' native alter hooks instead (hook_form_alter(),
+ * for example).
+ *
+ * The $page array contains top level elements for each block region:
+ * @code
+ *   $page['page_top']
+ *   $page['header']
+ *   $page['sidebar_first']
+ *   $page['content']
+ *   $page['sidebar_second']
+ *   $page['page_bottom']
+ * @endcode
+ *
+ * The 'content' element contains the main content of the current page, and its
+ * structure will vary depending on what module is responsible for building the
+ * page. Some legacy modules may not return structured content at all: their
+ * pre-rendered markup will be located in $page['content']['main']['#markup'].
+ *
+ * Pages built by Drupal's core Node module use a standard structure:
+ *
+ * @code
+ *   // Node body.
+ *   $page['content']['system_main']['nodes'][$nid]['body']
+ *   // Array of links attached to the node (add comments, read more).
+ *   $page['content']['system_main']['nodes'][$nid]['links']
+ *   // The node entity itself.
+ *   $page['content']['system_main']['nodes'][$nid]['#node']
+ *   // The results pager.
+ *   $page['content']['system_main']['pager']
+ * @endcode
+ *
+ * Blocks may be referenced by their module/delta pair within a region:
+ * @code
+ *   // The login block in the first sidebar region.
+ *   $page['sidebar_first']['user_login']['#block'];
+ * @endcode
+ *
+ * @param $page
+ *   Nested array of renderable elements that make up the page.
+ *
+ * @see hook_page_build()
+ * @see drupal_render_page()
+ */
+function hook_page_alter(&$page) {
+  // Add help text to the user login block.
+  $page['sidebar_first']['user_login']['help'] = array(
+    '#weight' => -10,
+    '#markup' => t('To post comments or add new content, you first have to log in.'),
+  );
+}
+
+/**
+ * Perform alterations before a form is rendered.
+ *
+ * One popular use of this hook is to add form elements to the node form. When
+ * altering a node form, the node entity can be retrieved by invoking
+ * $form_state['controller']->getEntity().
+ *
+ * In addition to hook_form_alter(), which is called for all forms, there are
+ * two more specific form hooks available. The first,
+ * hook_form_BASE_FORM_ID_alter(), allows targeting of a form/forms via a base
+ * form (if one exists). The second, hook_form_FORM_ID_alter(), can be used to
+ * target a specific form directly.
+ *
+ * The call order is as follows: all existing form alter functions are called
+ * for module A, then all for module B, etc., followed by all for any base
+ * theme(s), and finally for the theme itself. The module order is determined
+ * by system weight, then by module name.
+ *
+ * Within each module, form alter hooks are called in the following order:
+ * first, hook_form_alter(); second, hook_form_BASE_FORM_ID_alter(); third,
+ * hook_form_FORM_ID_alter(). So, for each module, the more general hooks are
+ * called first followed by the more specific.
+ *
+ * @param $form
+ *   Nested array of form elements that comprise the form.
+ * @param $form_state
+ *   A keyed array containing the current state of the form. The arguments
+ *   that drupal_get_form() was originally called with are available in the
+ *   array $form_state['build_info']['args'].
+ * @param $form_id
+ *   String representing the name of the form itself. Typically this is the
+ *   name of the function that generated the form.
+ *
+ * @see hook_form_BASE_FORM_ID_alter()
+ * @see hook_form_FORM_ID_alter()
+ * @see forms_api_reference.html
+ */
+function hook_form_alter(&$form, &$form_state, $form_id) {
+  if (isset($form['type']) && $form['type']['#value'] . '_node_settings' == $form_id) {
+    $upload_enabled_types = \Drupal::config('mymodule.settings')->get('upload_enabled_types');
+    $form['workflow']['upload_' . $form['type']['#value']] = array(
+      '#type' => 'radios',
+      '#title' => t('Attachments'),
+      '#default_value' => in_array($form['type']['#value'], $upload_enabled_types) ? 1 : 0,
+      '#options' => array(t('Disabled'), t('Enabled')),
+    );
+    // Add a custom submit handler to save the array of types back to the config file.
+    $form['actions']['submit']['#submit'][] = 'mymodule_upload_enabled_types_submit';
+  }
+}
+
+/**
+ * Provide a form-specific alteration instead of the global hook_form_alter().
+ *
+ * Modules can implement hook_form_FORM_ID_alter() to modify a specific form,
+ * rather than implementing hook_form_alter() and checking the form ID, or
+ * using long switch statements to alter multiple forms.
+ *
+ * Form alter hooks are called in the following order: hook_form_alter(),
+ * hook_form_BASE_FORM_ID_alter(), hook_form_FORM_ID_alter(). See
+ * hook_form_alter() for more details.
+ *
+ * @param $form
+ *   Nested array of form elements that comprise the form.
+ * @param $form_state
+ *   A keyed array containing the current state of the form. The arguments
+ *   that drupal_get_form() was originally called with are available in the
+ *   array $form_state['build_info']['args'].
+ * @param $form_id
+ *   String representing the name of the form itself. Typically this is the
+ *   name of the function that generated the form.
+ *
+ * @see hook_form_alter()
+ * @see hook_form_BASE_FORM_ID_alter()
+ * @see drupal_prepare_form()
+ * @see forms_api_reference.html
+ */
+function hook_form_FORM_ID_alter(&$form, &$form_state, $form_id) {
+  // Modification for the form with the given form ID goes here. For example, if
+  // FORM_ID is "user_register_form" this code would run only on the user
+  // registration form.
+
+  // Add a checkbox to registration form about agreeing to terms of use.
+  $form['terms_of_use'] = array(
+    '#type' => 'checkbox',
+    '#title' => t("I agree with the website's terms and conditions."),
+    '#required' => TRUE,
+  );
+}
+
+/**
+ * Provide a form-specific alteration for shared ('base') forms.
+ *
+ * By default, when drupal_get_form() is called, Drupal looks for a function
+ * with the same name as the form ID, and uses that function to build the form.
+ * In contrast, base forms allow multiple form IDs to be mapped to a single base
+ * (also called 'factory') form function.
+ *
+ * Modules can implement hook_form_BASE_FORM_ID_alter() to modify a specific
+ * base form, rather than implementing hook_form_alter() and checking for
+ * conditions that would identify the shared form constructor.
+ *
+ * To identify the base form ID for a particular form (or to determine whether
+ * one exists) check the $form_state. The base form ID is stored under
+ * $form_state['build_info']['base_form_id'].
+ *
+ * See hook_forms() for more information on how to implement base forms in
+ * Drupal.
+ *
+ * Form alter hooks are called in the following order: hook_form_alter(),
+ * hook_form_BASE_FORM_ID_alter(), hook_form_FORM_ID_alter(). See
+ * hook_form_alter() for more details.
+ *
+ * @param $form
+ *   Nested array of form elements that comprise the form.
+ * @param $form_state
+ *   A keyed array containing the current state of the form.
+ * @param $form_id
+ *   String representing the name of the form itself. Typically this is the
+ *   name of the function that generated the form.
+ *
+ * @see hook_form_alter()
+ * @see hook_form_FORM_ID_alter()
+ * @see drupal_prepare_form()
+ * @see hook_forms()
+ */
+function hook_form_BASE_FORM_ID_alter(&$form, &$form_state, $form_id) {
+  // Modification for the form with the given BASE_FORM_ID goes here. For
+  // example, if BASE_FORM_ID is "node_form", this code would run on every
+  // node form, regardless of node type.
+
+  // Add a checkbox to the node form about agreeing to terms of use.
+  $form['terms_of_use'] = array(
+    '#type' => 'checkbox',
+    '#title' => t("I agree with the website's terms and conditions."),
+    '#required' => TRUE,
+  );
+}
+
+/**
+ * Map form_ids to form builder functions.
+ *
+ * By default, when drupal_get_form() is called, the system will look for a
+ * function with the same name as the form ID, and use that function to build
+ * the form. If no such function is found, Drupal calls this hook. Modules
+ * implementing this hook can then provide their own instructions for mapping
+ * form IDs to constructor functions. As a result, you can easily map multiple
+ * form IDs to a single form constructor (referred to as a 'base' form).
+ *
+ * Using a base form can help to avoid code duplication, by allowing many
+ * similar forms to use the same code base. Another benefit is that it becomes
+ * much easier for other modules to apply a general change to the group of
+ * forms; hook_form_BASE_FORM_ID_alter() can be used to easily alter multiple
+ * forms at once by directly targeting the shared base form.
+ *
+ * Two example use cases where base forms may be useful are given below.
+ *
+ * First, you can use this hook to tell the form system to use a different
+ * function to build certain forms in your module; this is often used to define
+ * a form "factory" function that is used to build several similar forms. In
+ * this case, your hook implementation will likely ignore all of the input
+ * arguments. See node_forms() for an example of this. Note, node_forms() is the
+ * hook_forms() implementation; the base form itself is defined in node_form().
+ *
+ * Second, you could use this hook to define how to build a form with a
+ * dynamically-generated form ID. In this case, you would need to verify that
+ * the $form_id input matched your module's format for dynamically-generated
+ * form IDs, and if so, act appropriately.
+ *
+ * @param $form_id
+ *   The unique string identifying the desired form.
+ * @param $args
+ *   An array containing the original arguments provided to drupal_get_form()
+ *   or drupal_form_submit(). These are always passed to the form builder and
+ *   do not have to be specified manually in 'callback arguments'.
+ *
+ * @return
+ *   An associative array whose keys define form_ids and whose values are an
+ *   associative array defining the following keys:
+ *   - callback: The name of the form builder function to invoke. This will be
+ *     used for the base form ID, for example, to target a base form using
+ *     hook_form_BASE_FORM_ID_alter().
+ *   - callback arguments: (optional) Additional arguments to pass to the
+ *     function defined in 'callback', which are prepended to $args.
+ *   - wrapper_callback: (optional) The name of a form builder function to
+ *     invoke before the form builder defined in 'callback' is invoked. This
+ *     wrapper callback may prepopulate the $form array with form elements,
+ *     which will then be already contained in the $form that is passed on to
+ *     the form builder defined in 'callback'. For example, a wrapper callback
+ *     could setup wizard-alike form buttons that are the same for a variety of
+ *     forms that belong to the wizard, which all share the same wrapper
+ *     callback.
+ */
+function hook_forms($form_id, $args) {
+  // Simply reroute the (non-existing) $form_id 'mymodule_first_form' to
+  // 'mymodule_main_form'.
+  $forms['mymodule_first_form'] = array(
+    'callback' => 'mymodule_main_form',
+  );
+
+  // Reroute the $form_id and prepend an additional argument that gets passed to
+  // the 'mymodule_main_form' form builder function.
+  $forms['mymodule_second_form'] = array(
+    'callback' => 'mymodule_main_form',
+    'callback arguments' => array('some parameter'),
+  );
+
+  // Reroute the $form_id, but invoke the form builder function
+  // 'mymodule_main_form_wrapper' first, so we can prepopulate the $form array
+  // that is passed to the actual form builder 'mymodule_main_form'.
+  $forms['mymodule_wrapped_form'] = array(
+    'callback' => 'mymodule_main_form',
+    'wrapper_callback' => 'mymodule_main_form_wrapper',
+  );
+
+  return $forms;
+}
+
+/**
+ * Alter an email message created with the drupal_mail() function.
+ *
+ * hook_mail_alter() allows modification of email messages created and sent
+ * with drupal_mail(). Usage examples include adding and/or changing message
+ * text, message fields, and message headers.
+ *
+ * Email messages sent using functions other than drupal_mail() will not
+ * invoke hook_mail_alter(). For example, a contributed module directly
+ * calling the drupal_mail_system()->mail() or PHP mail() function
+ * will not invoke this hook. All core modules use drupal_mail() for
+ * messaging, it is best practice but not mandatory in contributed modules.
+ *
+ * @param $message
+ *   An array containing the message data. Keys in this array include:
+ *  - 'id':
+ *     The drupal_mail() id of the message. Look at module source code or
+ *     drupal_mail() for possible id values.
+ *  - 'to':
+ *     The address or addresses the message will be sent to. The
+ *     formatting of this string must comply with RFC 2822.
+ *  - 'from':
+ *     The address the message will be marked as being from, which is
+ *     either a custom address or the site-wide default email address.
+ *  - 'subject':
+ *     Subject of the email to be sent. This must not contain any newline
+ *     characters, or the email may not be sent properly.
+ *  - 'body':
+ *     An array of strings containing the message text. The message body is
+ *     created by concatenating the individual array strings into a single text
+ *     string using "\n\n" as a separator.
+ *  - 'headers':
+ *     Associative array containing mail headers, such as From, Sender,
+ *     MIME-Version, Content-Type, etc.
+ *  - 'params':
+ *     An array of optional parameters supplied by the caller of drupal_mail()
+ *     that is used to build the message before hook_mail_alter() is invoked.
+ *  - 'language':
+ *     The language object used to build the message before hook_mail_alter()
+ *     is invoked.
+ *  - 'send':
+ *     Set to FALSE to abort sending this email message.
+ *
+ * @see drupal_mail()
+ */
+function hook_mail_alter(&$message) {
+  if ($message['id'] == 'modulename_messagekey') {
+    if (!example_notifications_optin($message['to'], $message['id'])) {
+      // If the recipient has opted to not receive such messages, cancel
+      // sending.
+      $message['send'] = FALSE;
+      return;
+    }
+    $message['body'][] = "--\nMail sent out from " . \Drupal::config('system.site')->get('name');
+  }
+}
+
+/**
+ * Alter the registry of modules implementing a hook.
+ *
+ * This hook is invoked during \Drupal::moduleHandler()->getImplementations().
+ * A module may implement this hook in order to reorder the implementing
+ * modules, which are otherwise ordered by the module's system weight.
+ *
+ * Note that hooks invoked using drupal_alter() can have multiple variations
+ * (such as hook_form_alter() and hook_form_FORM_ID_alter()). drupal_alter()
+ * will call all such variants defined by a single module in turn. For the
+ * purposes of hook_module_implements_alter(), these variants are treated as
+ * a single hook. Thus, to ensure that your implementation of
+ * hook_form_FORM_ID_alter() is called at the right time, you will have to
+ * change the order of hook_form_alter() implementation in
+ * hook_module_implements_alter().
+ *
+ * @param $implementations
+ *   An array keyed by the module's name. The value of each item corresponds
+ *   to a $group, which is usually FALSE, unless the implementation is in a
+ *   file named $module.$group.inc.
+ * @param $hook
+ *   The name of the module hook being implemented.
+ */
+function hook_module_implements_alter(&$implementations, $hook) {
+  if ($hook == 'rdf_mapping') {
+    // Move my_module_rdf_mapping() to the end of the list.
+    // \Drupal::moduleHandler()->getImplementations()
+    // iterates through $implementations with a foreach loop which PHP iterates
+    // in the order that the items were added, so to move an item to the end of
+    // the array, we remove it and then add it.
+    $group = $implementations['my_module'];
+    unset($implementations['my_module']);
+    $implementations['my_module'] = $group;
+  }
+}
+
+/**
+ * Perform alterations to the breadcrumb built by the BreadcrumbManager.
+ *
+ * @param array $breadcrumb
+ *   An array of breadcrumb link a tags, returned by the breadcrumb manager
+ *   build method, for example
+ *   @code
+ *     array('<a href="/">Home</a>');
+ *   @endcode
+ * @param array $attributes
+ *   Attributes representing the current page, coming from
+ *   \Drupal::request()->attributes.
+ * @param array $context
+ *   May include the following key:
+ *   - builder: the instance of
+ *     \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface that constructed this
+ *     breadcrumb, or NULL if no builder acted based on the current attributes.
+ */
+function hook_system_breadcrumb_alter(array &$breadcrumb, array $attributes, array $context) {
+  // Add an item to the end of the breadcrumb.
+  $breadcrumb[] = Drupal::l(t('Text'), 'example_route_name');
+}
+
+/**
+ * Return additional themes provided by modules.
+ *
+ * Only use this hook for testing purposes. Use a hidden MYMODULE_test.module
+ * to implement this hook. Testing themes should be hidden, too.
+ *
+ * This hook is invoked from _system_rebuild_theme_data() and allows modules to
+ * register additional themes outside of the regular 'themes' directories of a
+ * Drupal installation.
+ *
+ * @return
+ *   An associative array. Each key is the system name of a theme and each value
+ *   is the corresponding path to the theme's .info.yml file.
+ */
+function hook_system_theme_info() {
+  $themes['mymodule_test_theme'] = drupal_get_path('module', 'mymodule') . '/mymodule_test_theme/mymodule_test_theme.info.yml';
+  return $themes;
+}
+
+/**
+ * Alter the information parsed from module and theme .info.yml files
+ *
+ * This hook is invoked in _system_rebuild_module_data() and in
+ * _system_rebuild_theme_data(). A module may implement this hook in order to
+ * add to or alter the data generated by reading the .info.yml file with
+ * \Drupal\Core\Extension\InfoParser.
+ *
+ * @param $info
+ *   The .info.yml file contents, passed by reference so that it can be altered.
+ * @param $file
+ *   Full information about the module or theme, including $file->name, and
+ *   $file->filename
+ * @param $type
+ *   Either 'module' or 'theme', depending on the type of .info.yml file that
+ *   was passed.
+ */
+function hook_system_info_alter(&$info, $file, $type) {
+  // Only fill this in if the .info.yml file does not define a 'datestamp'.
+  if (empty($info['datestamp'])) {
+    $info['datestamp'] = filemtime($file->filename);
+  }
+}
+
+/**
+ * Define user permissions.
+ *
+ * This hook can supply permissions that the module defines, so that they
+ * can be selected on the user permissions page and used to grant or restrict
+ * access to actions the module performs.
+ *
+ * Permissions are checked using user_access().
+ *
+ * For a detailed usage example, see page_example.module.
+ *
+ * @return
+ *   An array whose keys are permission names and whose corresponding values
+ *   are arrays containing the following key-value pairs:
+ *   - title: The human-readable name of the permission, to be shown on the
+ *     permission administration page. This should be wrapped in the t()
+ *     function so it can be translated.
+ *   - description: (optional) A description of what the permission does. This
+ *     should be wrapped in the t() function so it can be translated.
+ *   - restrict access: (optional) A boolean which can be set to TRUE to
+ *     indicate that site administrators should restrict access to this
+ *     permission to trusted users. This should be used for permissions that
+ *     have inherent security risks across a variety of potential use cases
+ *     (for example, the "administer filters" and "bypass node access"
+ *     permissions provided by Drupal core). When set to TRUE, a standard
+ *     warning message defined in user_admin_permissions() and output via
+ *     theme_user_permission_description() will be associated with the
+ *     permission and displayed with it on the permission administration page.
+ *     Defaults to FALSE.
+ *   - warning: (optional) A translated warning message to display for this
+ *     permission on the permission administration page. This warning overrides
+ *     the automatic warning generated by 'restrict access' being set to TRUE.
+ *     This should rarely be used, since it is important for all permissions to
+ *     have a clear, consistent security warning that is the same across the
+ *     site. Use the 'description' key instead to provide any information that
+ *     is specific to the permission you are defining.
+ *
+ * @see theme_user_permission_description()
+ */
+function hook_permission() {
+  return array(
+    'administer my module' =>  array(
+      'title' => t('Administer my module'),
+      'description' => t('Perform administration tasks for my module.'),
+    ),
+  );
+}
+
+/**
+ * Register a module or theme's theme implementations.
+ *
+ * The implementations declared by this hook have several purposes:
+ * - They can specify how a particular render array is to be rendered as HTML.
+ *   This is usually the case if the theme function is assigned to the render
+ *   array's #theme property.
+ * - They can return HTML for default calls to theme().
+ * - They can return HTML for calls to theme() for a theme suggestion.
+ *
+ * @param array $existing
+ *   An array of existing implementations that may be used for override
+ *   purposes. This is primarily useful for themes that may wish to examine
+ *   existing implementations to extract data (such as arguments) so that
+ *   it may properly register its own, higher priority implementations.
+ * @param $type
+ *   Whether a theme, module, etc. is being processed. This is primarily useful
+ *   so that themes tell if they are the actual theme being called or a parent
+ *   theme. May be one of:
+ *   - 'module': A module is being checked for theme implementations.
+ *   - 'base_theme_engine': A theme engine is being checked for a theme that is
+ *     a parent of the actual theme being used.
+ *   - 'theme_engine': A theme engine is being checked for the actual theme
+ *     being used.
+ *   - 'base_theme': A base theme is being checked for theme implementations.
+ *   - 'theme': The actual theme in use is being checked.
+ * @param $theme
+ *   The actual name of theme, module, etc. that is being being processed.
+ * @param $path
+ *   The directory path of the theme or module, so that it doesn't need to be
+ *   looked up.
+ *
+ * @return array
+ *   An associative array of information about theme implementations. The keys
+ *   on the outer array are known as "theme hooks". For simple theme
+ *   implementations for regular calls to theme(), the theme hook is the first
+ *   argument. For theme suggestions, instead of the array key being the base
+ *   theme hook, the key is a theme suggestion name with the format
+ *   'base_hook_name__sub_hook_name'. For render elements, the key is the
+ *   machine name of the render element. The array values are themselves arrays
+ *   containing information about the theme hook and its implementation. Each
+ *   information array must contain either a 'variables' element (for theme()
+ *   calls) or a 'render element' element (for render elements), but not both.
+ *   The following elements may be part of each information array:
+ *   - variables: Used for theme() call items only: an array of variables,
+ *     where the array keys are the names of the variables, and the array
+ *     values are the default values if they are not passed into theme().
+ *     Template implementations receive each array key as a variable in the
+ *     template file (so they must be legal PHP/Twig variable names). Function
+ *     implementations are passed the variables in a single $variables function
+ *     argument.
+ *   - render element: Used for render element items only: the name of the
+ *     renderable element or element tree to pass to the theme function. This
+ *     name is used as the name of the variable that holds the renderable
+ *     element or tree in preprocess and process functions.
+ *   - file: The file the implementation resides in. This file will be included
+ *     prior to the theme being rendered, to make sure that the function or
+ *     preprocess function (as needed) is actually loaded; this makes it
+ *     possible to split theme functions out into separate files quite easily.
+ *   - path: Override the path of the file to be used. Ordinarily the module or
+ *     theme path will be used, but if the file will not be in the default
+ *     path, include it here. This path should be relative to the Drupal root
+ *     directory.
+ *   - template: If specified, this theme implementation is a template, and
+ *     this is the template file without an extension. Do not put .html.twig on
+ *     this file; that extension will be added automatically by the default
+ *     rendering engine (which is Twig). If 'path' above is specified, the
+ *     template should also be in this path.
+ *   - function: If specified, this will be the function name to invoke for
+ *     this implementation. If neither 'template' nor 'function' is specified,
+ *     a default function name will be assumed. For example, if a module
+ *     registers the 'node' theme hook, 'theme_node' will be assigned to its
+ *     function. If the chameleon theme registers the node hook, it will be
+ *     assigned 'chameleon_node' as its function.
+ *   - base hook: Used for theme() suggestions only: the base theme hook name.
+ *     Instead of this suggestion's implementation being used directly, the base
+ *     hook will be invoked with this implementation as its first suggestion.
+ *     The base hook's files will be included and the base hook's preprocess
+ *     functions will be called in place of any suggestion's preprocess
+ *     functions. If an implementation of hook_theme_suggestions_HOOK() (where
+ *     HOOK is the base hook) changes the suggestion order, a different
+ *     suggestion may be used in place of this suggestion. If after
+ *     hook_theme_suggestions_HOOK() this suggestion remains the first
+ *     suggestion, then this suggestion's function or template will be used to
+ *     generate the output for theme().
+ *   - pattern: A regular expression pattern to be used to allow this theme
+ *     implementation to have a dynamic name. The convention is to use __ to
+ *     differentiate the dynamic portion of the theme. For example, to allow
+ *     forums to be themed individually, the pattern might be: 'forum__'. Then,
+ *     when the forum is themed, call:
+ *     @code
+ *     theme(array('forum__' . $tid, 'forum'), $forum)
+ *     @endcode
+ *   - preprocess functions: A list of functions used to preprocess this data.
+ *     Ordinarily this won't be used; it's automatically filled in. By default,
+ *     for a module this will be filled in as template_preprocess_HOOK. For
+ *     a theme this will be filled in as twig_preprocess and
+ *     twig_preprocess_HOOK as well as themename_preprocess and
+ *     themename_preprocess_HOOK.
+ *   - override preprocess functions: Set to TRUE when a theme does NOT want
+ *     the standard preprocess functions to run. This can be used to give a
+ *     theme FULL control over how variables are set. For example, if a theme
+ *     wants total control over how certain variables in the page.html.twig are
+ *     set, this can be set to true. Please keep in mind that when this is used
+ *     by a theme, that theme becomes responsible for making sure necessary
+ *     variables are set.
+ *   - type: (automatically derived) Where the theme hook is defined:
+ *     'module', 'theme_engine', or 'theme'.
+ *   - theme path: (automatically derived) The directory path of the theme or
+ *     module, so that it doesn't need to be looked up.
+ *
+ * @see hook_theme_registry_alter()
+ */
+function hook_theme($existing, $type, $theme, $path) {
+  return array(
+    'forum_display' => array(
+      'variables' => array('forums' => NULL, 'topics' => NULL, 'parents' => NULL, 'tid' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
+    ),
+    'forum_list' => array(
+      'variables' => array('forums' => NULL, 'parents' => NULL, 'tid' => NULL),
+    ),
+    'forum_topic_list' => array(
+      'variables' => array('tid' => NULL, 'topics' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
+    ),
+    'forum_icon' => array(
+      'variables' => array('new_posts' => NULL, 'num_posts' => 0, 'comment_mode' => 0, 'sticky' => 0),
+    ),
+    'status_report' => array(
+      'render element' => 'requirements',
+      'file' => 'system.admin.inc',
+    ),
+  );
+}
+
+/**
+ * Alter the theme registry information returned from hook_theme().
+ *
+ * The theme registry stores information about all available theme hooks,
+ * including which callback functions those hooks will call when triggered,
+ * what template files are exposed by these hooks, and so on.
+ *
+ * Note that this hook is only executed as the theme cache is re-built.
+ * Changes here will not be visible until the next cache clear.
+ *
+ * The $theme_registry array is keyed by theme hook name, and contains the
+ * information returned from hook_theme(), as well as additional properties
+ * added by _theme_process_registry().
+ *
+ * For example:
+ * @code
+ * $theme_registry['user'] = array(
+ *   'variables' => array(
+ *     'account' => NULL,
+ *   ),
+ *   'template' => 'core/modules/user/user',
+ *   'file' => 'core/modules/user/user.pages.inc',
+ *   'type' => 'module',
+ *   'theme path' => 'core/modules/user',
+ *   'preprocess functions' => array(
+ *     0 => 'template_preprocess',
+ *     1 => 'template_preprocess_user_profile',
+ *   ),
+ * );
+ * @endcode
+ *
+ * @param $theme_registry
+ *   The entire cache of theme registry information, post-processing.
+ *
+ * @see hook_theme()
+ * @see _theme_process_registry()
+ */
+function hook_theme_registry_alter(&$theme_registry) {
+  // Kill the next/previous forum topic navigation links.
+  foreach ($theme_registry['forum_topic_navigation']['preprocess functions'] as $key => $value) {
+    if ($value == 'template_preprocess_forum_topic_navigation') {
+      unset($theme_registry['forum_topic_navigation']['preprocess functions'][$key]);
+    }
+  }
+}
+
+/**
+ * Alter the default, hook-independent variables for all templates.
+ *
+ * Allows modules to provide additional default template variables or manipulate
+ * existing. This hook is invoked from template_preprocess() after basic default
+ * template variables have been set up and before the next template preprocess
+ * function is invoked.
+ *
+ * Note that the default template variables are statically cached within a
+ * request. When adding a template variable that depends on other context, it is
+ * your responsibility to appropriately reset the static cache in
+ * template_preprocess() when needed:
+ * @code
+ * drupal_static_reset('template_preprocess');
+ * @endcode
+ *
+ * See user_template_preprocess_default_variables_alter() for an example.
+ *
+ * @param array $variables
+ *   An associative array of default template variables, as set up by
+ *   _template_preprocess_default_variables(). Passed by reference.
+ *
+ * @see template_preprocess()
+ * @see _template_preprocess_default_variables()
+ */
+function hook_template_preprocess_default_variables_alter(&$variables) {
+  $variables['is_admin'] = user_access('access administration pages');
+}
+
+/**
+ * Return the machine-readable name of the theme to use for the current page.
+ *
+ * This hook can be used to dynamically set the theme for the current page
+ * request. It should be used by modules which need to override the theme
+ * based on dynamic conditions (for example, a module which allows the theme to
+ * be set based on the current user's role). The return value of this hook will
+ * be used on all pages except those which have a valid per-page or per-section
+ * theme set via a theme callback function in hook_menu(); the themes on those
+ * pages can only be overridden using hook_menu_alter().
+ *
+ * Note that returning different themes for the same path may not work with page
+ * caching. This is most likely to be a problem if an anonymous user on a given
+ * path could have different themes returned under different conditions.
+ *
+ * Since only one theme can be used at a time, the last (i.e., highest
+ * weighted) module which returns a valid theme name from this hook will
+ * prevail.
+ *
+ * @return
+ *   The machine-readable name of the theme that should be used for the current
+ *   page request. The value returned from this function will only have an
+ *   effect if it corresponds to a currently-active theme on the site. Do not
+ *   return a value if you do not wish to set a custom theme.
+ */
+function hook_custom_theme() {
+  // Allow the user to request a particular theme via a query parameter.
+  return \Drupal::request()->query->get('theme');
+}
+
+/**
+ * Log an event message.
+ *
+ * This hook allows modules to route log events to custom destinations, such as
+ * SMS, Email, pager, syslog, ...etc.
+ *
+ * @param array $log_entry
+ *   An associative array containing the following keys:
+ *   - type: The type of message for this entry.
+ *   - user: The user object for the user who was logged in when the event
+ *     happened.
+ *   - uid: The user ID for the user who was logged in when the event happened.
+ *   - request_uri: The request URI for the page the event happened in.
+ *   - referer: The page that referred the user to the page where the event
+ *     occurred.
+ *   - ip: The IP address where the request for the page came from.
+ *   - timestamp: The UNIX timestamp of the date/time the event occurred.
+ *   - severity: The severity of the message; one of the following values as
+ *     defined in @link http://www.faqs.org/rfcs/rfc3164.html RFC 3164: @endlink
+ *     - WATCHDOG_EMERGENCY: Emergency, system is unusable.
+ *     - WATCHDOG_ALERT: Alert, action must be taken immediately.
+ *     - WATCHDOG_CRITICAL: Critical conditions.
+ *     - WATCHDOG_ERROR: Error conditions.
+ *     - WATCHDOG_WARNING: Warning conditions.
+ *     - WATCHDOG_NOTICE: Normal but significant conditions.
+ *     - WATCHDOG_INFO: Informational messages.
+ *     - WATCHDOG_DEBUG: Debug-level messages.
+ *   - link: An optional link provided by the module that called the watchdog()
+ *     function.
+ *   - message: The text of the message to be logged. Variables in the message
+ *     are indicated by using placeholder strings alongside the variables
+ *     argument to declare the value of the placeholders. See t() for
+ *     documentation on how the message and variable parameters interact.
+ *   - variables: An array of variables to be inserted into the message on
+ *     display. Will be NULL or missing if a message is already translated or if
+ *     the message is not possible to translate.
+ */
+function hook_watchdog(array $log_entry) {
+  global $base_url;
+  $language_interface = language(\Drupal\Core\Language\Language::TYPE_INTERFACE);
+
+  $severity_list = array(
+    WATCHDOG_EMERGENCY     => t('Emergency'),
+    WATCHDOG_ALERT     => t('Alert'),
+    WATCHDOG_CRITICAL     => t('Critical'),
+    WATCHDOG_ERROR       => t('Error'),
+    WATCHDOG_WARNING   => t('Warning'),
+    WATCHDOG_NOTICE    => t('Notice'),
+    WATCHDOG_INFO      => t('Info'),
+    WATCHDOG_DEBUG     => t('Debug'),
+  );
+
+  $to = 'someone@example.com';
+  $params = array();
+  $params['subject'] = t('[@site_name] @severity_desc: Alert from your web site', array(
+    '@site_name' => \Drupal::config('system.site')->get('name'),
+    '@severity_desc' => $severity_list[$log_entry['severity']],
+  ));
+
+  $params['message']  = "\nSite:         @base_url";
+  $params['message'] .= "\nSeverity:     (@severity) @severity_desc";
+  $params['message'] .= "\nTimestamp:    @timestamp";
+  $params['message'] .= "\nType:         @type";
+  $params['message'] .= "\nIP Address:   @ip";
+  $params['message'] .= "\nRequest URI:  @request_uri";
+  $params['message'] .= "\nReferrer URI: @referer_uri";
+  $params['message'] .= "\nUser:         (@uid) @name";
+  $params['message'] .= "\nLink:         @link";
+  $params['message'] .= "\nMessage:      \n\n@message";
+
+  $params['message'] = t($params['message'], array(
+    '@base_url'      => $base_url,
+    '@severity'      => $log_entry['severity'],
+    '@severity_desc' => $severity_list[$log_entry['severity']],
+    '@timestamp'     => format_date($log_entry['timestamp']),
+    '@type'          => $log_entry['type'],
+    '@ip'            => $log_entry['ip'],
+    '@request_uri'   => $log_entry['request_uri'],
+    '@referer_uri'   => $log_entry['referer'],
+    '@uid'           => $log_entry['uid'],
+    '@name'          => $log_entry['user']->name,
+    '@link'          => strip_tags($log_entry['link']),
+    '@message'       => strip_tags($log_entry['message']),
+  ));
+
+  drupal_mail('emaillog', 'entry', $to, $language_interface->id, $params);
+}
+
+/**
+ * Prepare a message based on parameters; called from drupal_mail().
+ *
+ * Note that hook_mail(), unlike hook_mail_alter(), is only called on the
+ * $module argument to drupal_mail(), not all modules.
+ *
+ * @param $key
+ *   An identifier of the mail.
+ * @param $message
+ *   An array to be filled in. Elements in this array include:
+ *   - id: An ID to identify the mail sent. Look at module source code
+ *     or drupal_mail() for possible id values.
+ *   - to: The address or addresses the message will be sent to. The
+ *     formatting of this string must comply with RFC 2822.
+ *   - subject: Subject of the e-mail to be sent. This must not contain any
+ *     newline characters, or the mail may not be sent properly. drupal_mail()
+ *     sets this to an empty string when the hook is invoked.
+ *   - body: An array of lines containing the message to be sent. Drupal will
+ *     format the correct line endings for you. drupal_mail() sets this to an
+ *     empty array when the hook is invoked.
+ *   - from: The address the message will be marked as being from, which is
+ *     set by drupal_mail() to either a custom address or the site-wide
+ *     default email address when the hook is invoked.
+ *   - headers: Associative array containing mail headers, such as From,
+ *     Sender, MIME-Version, Content-Type, etc. drupal_mail() pre-fills
+ *     several headers in this array.
+ * @param $params
+ *   An array of parameters supplied by the caller of drupal_mail().
+ */
+function hook_mail($key, &$message, $params) {
+  $account = $params['account'];
+  $context = $params['context'];
+  $variables = array(
+    '%site_name' => \Drupal::config('system.site')->get('name'),
+    '%username' => user_format_name($account),
+  );
+  if ($context['hook'] == 'taxonomy') {
+    $entity = $params['entity'];
+    $vocabulary = entity_load('taxonomy_vocabulary', $entity->id());
+    $variables += array(
+      '%term_name' => $entity->name,
+      '%term_description' => $entity->description,
+      '%term_id' => $entity->id(),
+      '%vocabulary_name' => $vocabulary->name,
+      '%vocabulary_description' => $vocabulary->description,
+      '%vocabulary_id' => $vocabulary->id(),
+    );
+  }
+
+  // Node-based variable translation is only available if we have a node.
+  if (isset($params['node'])) {
+    $node = $params['node'];
+    $variables += array(
+      '%uid' => $node->getAuthorId(),
+      '%node_url' => url('node/' . $node->id(), array('absolute' => TRUE)),
+      '%node_type' => node_get_type_label($node),
+      '%title' => $node->getTitle(),
+      '%teaser' => $node->teaser,
+      '%body' => $node->body,
+    );
+  }
+  $subject = strtr($context['subject'], $variables);
+  $body = strtr($context['message'], $variables);
+  $message['subject'] .= str_replace(array("\r", "\n"), '', $subject);
+  $message['body'][] = drupal_html_to_text($body);
+}
+
+/**
+ * Flush all persistent and static caches.
+ *
+ * This hook asks your module to clear all of its static caches,
+ * in order to ensure a clean environment for subsequently
+ * invoked data rebuilds.
+ *
+ * Do NOT use this hook for rebuilding information. Only use it to flush custom
+ * caches.
+ *
+ * Static caches using drupal_static() do not need to be reset manually.
+ * However, all other static variables that do not use drupal_static() must be
+ * manually reset.
+ *
+ * This hook is invoked by drupal_flush_all_caches(). It runs before module data
+ * is updated and before hook_rebuild().
+ *
+ * @see drupal_flush_all_caches()
+ * @see hook_rebuild()
+ */
+function hook_cache_flush() {
+  if (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update') {
+    _update_cache_clear();
+  }
+}
+
+/**
+ * Rebuild data based upon refreshed caches.
+ *
+ * This hook allows your module to rebuild its data based on the latest/current
+ * module data. It runs after hook_cache_flush() and after all module data has
+ * been updated.
+ *
+ * This hook is only invoked after the system has been completely cleared;
+ * i.e., all previously cached data is known to be gone and every API in the
+ * system is known to return current information, so your module can safely rely
+ * on all available data to rebuild its own.
+ *
+ * The menu router is the only exception regarding rebuilt data; it is only
+ * rebuilt after all hook_rebuild() implementations have been invoked. That
+ * ensures that hook_menu() implementations and the final router rebuild can
+ * rely on all data being returned by all modules.
+ *
+ * @see hook_cache_flush()
+ * @see drupal_flush_all_caches()
+ */
+function hook_rebuild() {
+  $themes = list_themes();
+  foreach ($themes as $theme) {
+    _block_rehash($theme->name);
+  }
+}
+
+/**
+ * Perform necessary actions before a module is installed.
+ *
+ * @param string $module
+ *   The name of the module about to be installed.
+ */
+function hook_module_preinstall($module) {
+  mymodule_cache_clear();
+}
+
+/**
+ * Perform necessary actions after modules are installed.
+ *
+ * This function differs from hook_install() in that it gives all other modules
+ * a chance to perform actions when a module is installed, whereas
+ * hook_install() is only called on the module actually being installed. See
+ * \Drupal\Core\Extension\ModuleHandler::install() for a detailed description of
+ * the order in which install hooks are invoked.
+ *
+ * @param $modules
+ *   An array of the modules that were installed.
+ *
+ * @see \Drupal\Core\Extension\ModuleHandler::install()
+ * @see hook_install()
+ */
+function hook_modules_installed($modules) {
+  if (in_array('lousy_module', $modules)) {
+    \Drupal::state()->set('mymodule.lousy_module_compatibility', TRUE);
+  }
+}
+
+/**
+ * Perform necessary actions before a module is uninstalled.
+ *
+ * @param string $module
+ *   The name of the module about to be uninstalled.
+ */
+function hook_module_preuninstall($module) {
+  mymodule_cache_clear();
+}
+
+/**
+ * Perform necessary actions after modules are uninstalled.
+ *
+ * This function differs from hook_uninstall() in that it gives all other
+ * modules a chance to perform actions when a module is uninstalled, whereas
+ * hook_uninstall() is only called on the module actually being uninstalled.
+ *
+ * It is recommended that you implement this hook if your module stores
+ * data that may have been set by other modules.
+ *
+ * @param $modules
+ *   An array of the modules that were uninstalled.
+ *
+ * @see hook_uninstall()
+ * @see hook_modules_disabled()
+ */
+function hook_modules_uninstalled($modules) {
+  if (in_array('lousy_module', $modules)) {
+    \Drupal::state()->delete('mymodule.lousy_module_compatibility');
+  }
+  mymodule_cache_rebuild();
+}
+
+/**
+ * Registers PHP stream wrapper implementations associated with a module.
+ *
+ * Provide a facility for managing and querying user-defined stream wrappers
+ * in PHP. PHP's internal stream_get_wrappers() doesn't return the class
+ * registered to handle a stream, which we need to be able to find the handler
+ * for class instantiation.
+ *
+ * If a module registers a scheme that is already registered with PHP, it will
+ * be unregistered and replaced with the specified class.
+ *
+ * @return
+ *   A nested array, keyed first by scheme name ("public" for "public://"),
+ *   then keyed by the following values:
+ *   - 'name' A short string to name the wrapper.
+ *   - 'class' A string specifying the PHP class that implements the
+ *     Drupal\Core\StreamWrapper\StreamWrapperInterface interface.
+ *   - 'description' A string with a short description of what the wrapper does.
+ *   - 'type' (Optional) A bitmask of flags indicating what type of streams this
+ *     wrapper will access - local or remote, readable and/or writeable, etc.
+ *     Many shortcut constants are defined in file.inc. Defaults to
+ *     STREAM_WRAPPERS_NORMAL which includes all of these bit flags:
+ *     - STREAM_WRAPPERS_READ
+ *     - STREAM_WRAPPERS_WRITE
+ *     - STREAM_WRAPPERS_VISIBLE
+ *
+ * @see file_get_stream_wrappers()
+ * @see hook_stream_wrappers_alter()
+ * @see system_stream_wrappers()
+ */
+function hook_stream_wrappers() {
+  return array(
+    'public' => array(
+      'name' => t('Public files'),
+      'class' => 'Drupal\Core\StreamWrapper\PublicStream',
+      'description' => t('Public local files served by the webserver.'),
+      'type' => STREAM_WRAPPERS_LOCAL_NORMAL,
+    ),
+    'private' => array(
+      'name' => t('Private files'),
+      'class' => 'Drupal\Core\StreamWrapper\PrivateStream',
+      'description' => t('Private local files served by Drupal.'),
+      'type' => STREAM_WRAPPERS_LOCAL_NORMAL,
+    ),
+    'temp' => array(
+      'name' => t('Temporary files'),
+      'class' => 'Drupal\Core\StreamWrapper\TemporaryStream',
+      'description' => t('Temporary local files for upload and previews.'),
+      'type' => STREAM_WRAPPERS_LOCAL_HIDDEN,
+    ),
+    'cdn' => array(
+      'name' => t('Content delivery network files'),
+      // @todo: Fix the name of this class when we decide on module PSR-0 usage.
+      'class' => 'MyModuleCDNStream',
+      'description' => t('Files served by a content delivery network.'),
+      // 'type' can be omitted to use the default of STREAM_WRAPPERS_NORMAL
+    ),
+    'youtube' => array(
+      'name' => t('YouTube video'),
+      // @todo: Fix the name of this class when we decide on module PSR-0 usage.
+      'class' => 'MyModuleYouTubeStream',
+      'description' => t('Video streamed from YouTube.'),
+      // A module implementing YouTube integration may decide to support using
+      // the YouTube API for uploading video, but here, we assume that this
+      // particular module only supports playing YouTube video.
+      'type' => STREAM_WRAPPERS_READ_VISIBLE,
+    ),
+  );
+}
+
+/**
+ * Alters the list of PHP stream wrapper implementations.
+ *
+ * @see file_get_stream_wrappers()
+ * @see hook_stream_wrappers()
+ */
+function hook_stream_wrappers_alter(&$wrappers) {
+  // Change the name of private files to reflect the performance.
+  $wrappers['private']['name'] = t('Slow files');
+}
+
+/**
+ * Control access to private file downloads and specify HTTP headers.
+ *
+ * This hook allows modules enforce permissions on file downloads when the
+ * private file download method is selected. Modules can also provide headers
+ * to specify information like the file's name or MIME type.
+ *
+ * @param $uri
+ *   The URI of the file.
+ * @return
+ *   If the user does not have permission to access the file, return -1. If the
+ *   user has permission, return an array with the appropriate headers. If the
+ *   file is not controlled by the current module, the return value should be
+ *   NULL.
+ *
+ * @see file_download()
+ */
+function hook_file_download($uri) {
+  // Check to see if this is a config download.
+  $scheme = file_uri_scheme($uri);
+  $target = file_uri_target($uri);
+  if ($scheme == 'temporary' && $target == 'config.tar.gz') {
+    return array(
+      'Content-disposition' => 'attachment; filename="config.tar.gz"',
+    );
+  }
+}
+
+/**
+ * Alter the URL to a file.
+ *
+ * This hook is called from file_create_url(), and  is called fairly
+ * frequently (10+ times per page), depending on how many files there are in a
+ * given page.
+ * If CSS and JS aggregation are disabled, this can become very frequently
+ * (50+ times per page) so performance is critical.
+ *
+ * This function should alter the URI, if it wants to rewrite the file URL.
+ *
+ * @param $uri
+ *   The URI to a file for which we need an external URL, or the path to a
+ *   shipped file.
+ */
+function hook_file_url_alter(&$uri) {
+  global $user;
+
+  // User 1 will always see the local file in this example.
+  if ($user->id() == 1) {
+    return;
+  }
+
+  $cdn1 = 'http://cdn1.example.com';
+  $cdn2 = 'http://cdn2.example.com';
+  $cdn_extensions = array('css', 'js', 'gif', 'jpg', 'jpeg', 'png');
+
+  // Most CDNs don't support private file transfers without a lot of hassle,
+  // so don't support this in the common case.
+  $schemes = array('public');
+
+  $scheme = file_uri_scheme($uri);
+
+  // Only serve shipped files and public created files from the CDN.
+  if (!$scheme || in_array($scheme, $schemes)) {
+    // Shipped files.
+    if (!$scheme) {
+      $path = $uri;
+    }
+    // Public created files.
+    else {
+      $wrapper = file_stream_wrapper_get_instance_by_scheme($scheme);
+      $path = $wrapper->getDirectoryPath() . '/' . file_uri_target($uri);
+    }
+
+    // Clean up Windows paths.
+    $path = str_replace('\\', '/', $path);
+
+    // Serve files with one of the CDN extensions from CDN 1, all others from
+    // CDN 2.
+    $pathinfo = pathinfo($path);
+    if (isset($pathinfo['extension']) && in_array($pathinfo['extension'], $cdn_extensions)) {
+      $uri = $cdn1 . '/' . $path;
+    }
+    else {
+      $uri = $cdn2 . '/' . $path;
+    }
+  }
+}
+
+/**
+ * Check installation requirements and do status reporting.
+ *
+ * This hook has three closely related uses, determined by the $phase argument:
+ * - Checking installation requirements ($phase == 'install').
+ * - Checking update requirements ($phase == 'update').
+ * - Status reporting ($phase == 'runtime').
+ *
+ * Note that this hook, like all others dealing with installation and updates,
+ * must reside in a module_name.install file, or it will not properly abort
+ * the installation of the module if a critical requirement is missing.
+ *
+ * During the 'install' phase, modules can for example assert that
+ * library or server versions are available or sufficient.
+ * Note that the installation of a module can happen during installation of
+ * Drupal itself (by install.php) with an installation profile or later by hand.
+ * As a consequence, install-time requirements must be checked without access
+ * to the full Drupal API, because it is not available during install.php.
+ * If a requirement has a severity of REQUIREMENT_ERROR, install.php will abort
+ * or at least the module will not install.
+ * Other severity levels have no effect on the installation.
+ * Module dependencies do not belong to these installation requirements,
+ * but should be defined in the module's .info.yml file.
+ *
+ * The 'runtime' phase is not limited to pure installation requirements
+ * but can also be used for more general status information like maintenance
+ * tasks and security issues.
+ * The returned 'requirements' will be listed on the status report in the
+ * administration section, with indication of the severity level.
+ * Moreover, any requirement with a severity of REQUIREMENT_ERROR severity will
+ * result in a notice on the administration configuration page.
+ *
+ * @param $phase
+ *   The phase in which requirements are checked:
+ *   - install: The module is being installed.
+ *   - update: The module is enabled and update.php is run.
+ *   - runtime: The runtime requirements are being checked and shown on the
+ *     status report page.
+ *
+ * @return
+ *   An associative array where the keys are arbitrary but must be unique (it
+ *   is suggested to use the module short name as a prefix) and the values are
+ *   themselves associative arrays with the following elements:
+ *   - title: The name of the requirement.
+ *   - value: The current value (e.g., version, time, level, etc). During
+ *     install phase, this should only be used for version numbers, do not set
+ *     it if not applicable.
+ *   - description: The description of the requirement/status.
+ *   - severity: The requirement's result/severity level, one of:
+ *     - REQUIREMENT_INFO: For info only.
+ *     - REQUIREMENT_OK: The requirement is satisfied.
+ *     - REQUIREMENT_WARNING: The requirement failed with a warning.
+ *     - REQUIREMENT_ERROR: The requirement failed with an error.
+ */
+function hook_requirements($phase) {
+  $requirements = array();
+
+  // Report Drupal version
+  if ($phase == 'runtime') {
+    $requirements['drupal'] = array(
+      'title' => t('Drupal'),
+      'value' => \Drupal::VERSION,
+      'severity' => REQUIREMENT_INFO
+    );
+  }
+
+  // Test PHP version
+  $requirements['php'] = array(
+    'title' => t('PHP'),
+    'value' => ($phase == 'runtime') ? l(phpversion(), 'admin/reports/status/php') : phpversion(),
+  );
+  if (version_compare(phpversion(), DRUPAL_MINIMUM_PHP) < 0) {
+    $requirements['php']['description'] = t('Your PHP installation is too old. Drupal requires at least PHP %version.', array('%version' => DRUPAL_MINIMUM_PHP));
+    $requirements['php']['severity'] = REQUIREMENT_ERROR;
+  }
+
+  // Report cron status
+  if ($phase == 'runtime') {
+    $cron_last = \Drupal::state()->get('system.cron_last');
+
+    if (is_numeric($cron_last)) {
+      $requirements['cron']['value'] = t('Last run !time ago', array('!time' => format_interval(REQUEST_TIME - $cron_last)));
+    }
+    else {
+      $requirements['cron'] = array(
+        'description' => t('Cron has not run. It appears cron jobs have not been setup on your system. Check the help pages for <a href="@url">configuring cron jobs</a>.', array('@url' => 'http://drupal.org/cron')),
+        'severity' => REQUIREMENT_ERROR,
+        'value' => t('Never run'),
+      );
+    }
+
+    $requirements['cron']['description'] .= ' ' . t('You can <a href="@cron">run cron manually</a>.', array('@cron' => url('admin/reports/status/run-cron')));
+
+    $requirements['cron']['title'] = t('Cron maintenance tasks');
+  }
+
+  return $requirements;
+}
+
+/**
+ * Define the current version of the database schema.
+ *
+ * A Drupal schema definition is an array structure representing one or more
+ * tables and their related keys and indexes. A schema is defined by
+ * hook_schema() which must live in your module's .install file.
+ *
+ * This hook is called at install and uninstall time, and in the latter case, it
+ * cannot rely on the .module file being loaded or hooks being known. If the
+ * .module file is needed, it may be loaded with drupal_load().
+ *
+ * The tables declared by this hook will be automatically created when the
+ * module is installed, and removed when the module is uninstalled. This happens
+ * before hook_install() is invoked, and after hook_uninstall() is invoked,
+ * respectively.
+ *
+ * By declaring the tables used by your module via an implementation of
+ * hook_schema(), these tables will be available on all supported database
+ * engines. You don't have to deal with the different SQL dialects for table
+ * creation and alteration of the supported database engines.
+ *
+ * See the Schema API Handbook at http://drupal.org/node/146843 for details on
+ * schema definition structures.
+ *
+ * @return array
+ *   A schema definition structure array. For each element of the
+ *   array, the key is a table name and the value is a table structure
+ *   definition.
+ *
+ * @see hook_schema_alter()
+ *
+ * @ingroup schemaapi
+ */
+function hook_schema() {
+  $schema['node'] = array(
+    // Example (partial) specification for table "node".
+    'description' => 'The base table for nodes.',
+    'fields' => array(
+      'nid' => array(
+        'description' => 'The primary identifier for a node.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'vid' => array(
+        'description' => 'The current {node_field_revision}.vid version identifier.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'type' => array(
+        'description' => 'The type of this node.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'title' => array(
+        'description' => 'The title of this node, always treated as non-markup plain text.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'indexes' => array(
+      'node_changed'        => array('changed'),
+      'node_created'        => array('created'),
+    ),
+    'unique keys' => array(
+      'nid_vid' => array('nid', 'vid'),
+      'vid'     => array('vid'),
+    ),
+    'foreign keys' => array(
+      'node_revision' => array(
+        'table' => 'node_field_revision',
+        'columns' => array('vid' => 'vid'),
+      ),
+      'node_author' => array(
+        'table' => 'users',
+        'columns' => array('uid' => 'uid'),
+      ),
+    ),
+    'primary key' => array('nid'),
+  );
+  return $schema;
+}
+
+/**
+ * Perform alterations to existing database schemas.
+ *
+ * When a module modifies the database structure of another module (by
+ * changing, adding or removing fields, keys or indexes), it should
+ * implement hook_schema_alter() to update the default $schema to take its
+ * changes into account.
+ *
+ * See hook_schema() for details on the schema definition structure.
+ *
+ * @param $schema
+ *   Nested array describing the schemas for all modules.
+ *
+ * @ingroup schemaapi
+ */
+function hook_schema_alter(&$schema) {
+  // Add field to existing schema.
+  $schema['users']['fields']['timezone_id'] = array(
+    'type' => 'int',
+    'not null' => TRUE,
+    'default' => 0,
+    'description' => 'Per-user timezone configuration.',
+  );
+}
+
+/**
+ * Perform alterations to a structured query.
+ *
+ * Structured (aka dynamic) queries that have tags associated may be altered by any module
+ * before the query is executed.
+ *
+ * @param $query
+ *   A Query object describing the composite parts of a SQL query.
+ *
+ * @see hook_query_TAG_alter()
+ * @see node_query_node_access_alter()
+ * @see AlterableInterface
+ * @see SelectInterface
+ */
+function hook_query_alter(Drupal\Core\Database\Query\AlterableInterface $query) {
+  if ($query->hasTag('micro_limit')) {
+    $query->range(0, 2);
+  }
+}
+
+/**
+ * Perform alterations to a structured query for a given tag.
+ *
+ * @param $query
+ *   An Query object describing the composite parts of a SQL query.
+ *
+ * @see hook_query_alter()
+ * @see node_query_node_access_alter()
+ * @see AlterableInterface
+ * @see SelectInterface
+ */
+function hook_query_TAG_alter(Drupal\Core\Database\Query\AlterableInterface $query) {
+  // Skip the extra expensive alterations if site has no node access control modules.
+  if (!node_access_view_all_nodes()) {
+    // Prevent duplicates records.
+    $query->distinct();
+    // The recognized operations are 'view', 'update', 'delete'.
+    if (!$op = $query->getMetaData('op')) {
+      $op = 'view';
+    }
+    // Skip the extra joins and conditions for node admins.
+    if (!user_access('bypass node access')) {
+      // The node_access table has the access grants for any given node.
+      $access_alias = $query->join('node_access', 'na', '%alias.nid = n.nid');
+      $or = db_or();
+      // If any grant exists for the specified user, then user has access to the node for the specified operation.
+      foreach (node_access_grants($op, $query->getMetaData('account')) as $realm => $gids) {
+        foreach ($gids as $gid) {
+          $or->condition(db_and()
+            ->condition($access_alias . '.gid', $gid)
+            ->condition($access_alias . '.realm', $realm)
+          );
+        }
+      }
+
+      if (count($or->conditions())) {
+        $query->condition($or);
+      }
+
+      $query->condition($access_alias . 'grant_' . $op, 1, '>=');
+    }
+  }
+}
+
+/**
+ * Perform setup tasks when the module is installed.
+ *
+ * If the module implements hook_schema(), the database tables will
+ * be created before this hook is fired.
+ *
+ * Implementations of this hook are by convention declared in the module's
+ * .install file. The implementation can rely on the .module file being loaded.
+ * The hook will only be called when a module is installed. The module's schema
+ * version will be set to the module's greatest numbered update hook. Because of
+ * this, any time a hook_update_N() is added to the module, this function needs
+ * to be updated to reflect the current version of the database schema.
+ *
+ * See the @link http://drupal.org/node/146843 Schema API documentation @endlink
+ * for details on hook_schema and how database tables are defined.
+ *
+ * Note that since this function is called from a full bootstrap, all functions
+ * (including those in modules enabled by the current page request) are
+ * available when this hook is called. Use cases could be displaying a user
+ * message, or calling a module function necessary for initial setup, etc.
+ *
+ * Please be sure that anything added or modified in this function that can
+ * be removed during uninstall should be removed with hook_uninstall().
+ *
+ * @see hook_schema()
+ * @see \Drupal\Core\Extension\ModuleHandler::install()
+ * @see hook_uninstall()
+ * @see hook_modules_installed()
+ */
+function hook_install() {
+  // Create the styles directory and ensure it's writable.
+  $directory = file_default_scheme() . '://styles';
+  $mode = isset($GLOBALS['install_state']['mode']) ? $GLOBALS['install_state']['mode'] : NULL;
+  file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS, $mode);
+}
+
+/**
+ * Perform a single update.
+ *
+ * For each change that requires one or more actions to be performed when
+ * updating a site, add a new hook_update_N(), which will be called by
+ * update.php. The documentation block preceding this function is stripped of
+ * newlines and used as the description for the update on the pending updates
+ * task list. Schema updates should adhere to the
+ * @link http://drupal.org/node/150215 Schema API. @endlink
+ *
+ * Implementations of hook_update_N() are named (module name)_update_(number).
+ * The numbers are composed of three parts:
+ * - 1 digit for Drupal core compatibility.
+ * - 1 digit for your module's major release version (e.g., is this the 8.x-1.*
+ *   (1) or 8.x-2.* (2) series of your module?). This digit should be 0 for
+ *   initial porting of your module to a new Drupal core API.
+ * - 2 digits for sequential counting, starting with 00.
+ *
+ * Examples:
+ * - mymodule_update_8000(): This is the required update for mymodule to run
+ *   with Drupal core API 8.x when upgrading from Drupal core API 7.x.
+ * - mymodule_update_8100(): This is the first update to get the database ready
+ *   to run mymodule 8.x-1.*.
+ * - mymodule_update_8200(): This is the first update to get the database ready
+ *   to run mymodule 8.x-2.*. Users can directly update from 7.x-2.* to 8.x-2.*
+ *   and they get all 80xx and 82xx updates, but not 81xx updates, because
+ *   those reside in the 8.x-1.x branch only.
+ *
+ * A good rule of thumb is to remove updates older than two major releases of
+ * Drupal. See hook_update_last_removed() to notify Drupal about the removals.
+ * For further information about releases and release numbers see:
+ * @link http://drupal.org/node/711070 Maintaining a drupal.org project with Git @endlink
+ *
+ * Never renumber update functions.
+ *
+ * Implementations of this hook should be placed in a mymodule.install file in
+ * the same directory as mymodule.module. Drupal core's updates are implemented
+ * using the system module as a name and stored in database/updates.inc.
+ *
+ * Not all module functions are available from within a hook_update_N() function.
+ * In order to call a function from your mymodule.module or an include file,
+ * you need to explicitly load that file first.
+ *
+ * During database updates the schema of any module could be out of date. For
+ * this reason, caution is needed when using any API function within an update
+ * function - particularly CRUD functions, functions that depend on the schema
+ * (for example by using drupal_write_record()), and any functions that invoke
+ * hooks. See @link update_api Update versions of API functions @endlink for
+ * details.
+ *
+ * If your update task is potentially time-consuming, you'll need to implement a
+ * multipass update to avoid PHP timeouts. Multipass updates use the $sandbox
+ * parameter provided by the batch API (normally, $context['sandbox']) to store
+ * information between successive calls, and the $sandbox['#finished'] value
+ * to provide feedback regarding completion level.
+ *
+ * See the batch operations page for more information on how to use the
+ * @link http://drupal.org/node/180528 Batch API. @endlink
+ *
+ * @param $sandbox
+ *   Stores information for multipass updates. See above for more information.
+ *
+ * @throws \Drupal\Core\Utility\UpdateException, PDOException
+ *   In case of error, update hooks should throw an instance of
+ *   Drupal\Core\Utility\UpdateException with a meaningful message for the user.
+ *   If a database query fails for whatever reason, it will throw a
+ *   PDOException.
+ *
+ * @return
+ *   Optionally, update hooks may return a translated string that will be
+ *   displayed to the user after the update has completed. If no message is
+ *   returned, no message will be presented to the user.
+ *
+ * @see batch
+ * @see schemaapi
+ * @see update_api
+ * @see hook_update_last_removed()
+ * @see update_get_update_list()
+ */
+function hook_update_N(&$sandbox) {
+  // For non-multipass updates, the signature can simply be;
+  // function hook_update_N() {
+
+  // For most updates, the following is sufficient.
+  db_add_field('mytable1', 'newcol', array('type' => 'int', 'not null' => TRUE, 'description' => 'My new integer column.'));
+
+  // However, for more complex operations that may take a long time,
+  // you may hook into Batch API as in the following example.
+
+  // Update 3 users at a time to have an exclamation point after their names.
+  // (They're really happy that we can do batch API in this hook!)
+  if (!isset($sandbox['progress'])) {
+    $sandbox['progress'] = 0;
+    $sandbox['current_uid'] = 0;
+    // We'll -1 to disregard the uid 0...
+    $sandbox['max'] = db_query('SELECT COUNT(DISTINCT uid) FROM {users}')->fetchField() - 1;
+  }
+
+  $users = db_select('users', 'u')
+    ->fields('u', array('uid', 'name'))
+    ->condition('uid', $sandbox['current_uid'], '>')
+    ->range(0, 3)
+    ->orderBy('uid', 'ASC')
+    ->execute();
+
+  foreach ($users as $user) {
+    $user->setUsername($user->getUsername() . '!');
+    db_update('users')
+      ->fields(array('name' => $user->getUsername()))
+      ->condition('uid', $user->id())
+      ->execute();
+
+    $sandbox['progress']++;
+    $sandbox['current_uid'] = $user->id();
+  }
+
+  $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
+
+  if ($some_error_condition_met) {
+    // In case of an error, simply throw an exception with an error message.
+    throw new UpdateException('Something went wrong; here is what you should do.');
+  }
+
+  // To display a message to the user when the update is completed, return it.
+  // If you do not want to display a completion message, simply return nothing.
+  return t('The update did what it was supposed to do.');
+}
+
+/**
+ * Return an array of information about module update dependencies.
+ *
+ * This can be used to indicate update functions from other modules that your
+ * module's update functions depend on, or vice versa. It is used by the update
+ * system to determine the appropriate order in which updates should be run, as
+ * well as to search for missing dependencies.
+ *
+ * Implementations of this hook should be placed in a mymodule.install file in
+ * the same directory as mymodule.module.
+ *
+ * @return
+ *   A multidimensional array containing information about the module update
+ *   dependencies. The first two levels of keys represent the module and update
+ *   number (respectively) for which information is being returned, and the
+ *   value is an array of information about that update's dependencies. Within
+ *   this array, each key represents a module, and each value represents the
+ *   number of an update function within that module. In the event that your
+ *   update function depends on more than one update from a particular module,
+ *   you should always list the highest numbered one here (since updates within
+ *   a given module always run in numerical order).
+ *
+ * @see update_resolve_dependencies()
+ * @see hook_update_N()
+ */
+function hook_update_dependencies() {
+  // Indicate that the mymodule_update_8000() function provided by this module
+  // must run after the another_module_update_8002() function provided by the
+  // 'another_module' module.
+  $dependencies['mymodule'][8000] = array(
+    'another_module' => 8002,
+  );
+  // Indicate that the mymodule_update_8001() function provided by this module
+  // must run before the yet_another_module_update_8004() function provided by
+  // the 'yet_another_module' module. (Note that declaring dependencies in this
+  // direction should be done only in rare situations, since it can lead to the
+  // following problem: If a site has already run the yet_another_module
+  // module's database updates before it updates its codebase to pick up the
+  // newest mymodule code, then the dependency declared here will be ignored.)
+  $dependencies['yet_another_module'][8004] = array(
+    'mymodule' => 8001,
+  );
+  return $dependencies;
+}
+
+/**
+ * Return a number which is no longer available as hook_update_N().
+ *
+ * If you remove some update functions from your mymodule.install file, you
+ * should notify Drupal of those missing functions. This way, Drupal can
+ * ensure that no update is accidentally skipped.
+ *
+ * Implementations of this hook should be placed in a mymodule.install file in
+ * the same directory as mymodule.module.
+ *
+ * @return
+ *   An integer, corresponding to hook_update_N() which has been removed from
+ *   mymodule.install.
+ *
+ * @see hook_update_N()
+ */
+function hook_update_last_removed() {
+  // We've removed the 5.x-1.x version of mymodule, including database updates.
+  // The next update function is mymodule_update_5200().
+  return 5103;
+}
+
+/**
+ * Remove any information that the module sets.
+ *
+ * The information that the module should remove includes:
+ * - state that the module has set using \Drupal::state()
+ * - modifications to existing tables
+ *
+ * The module should not remove its entry from the module configuration.
+ * Database tables defined by hook_schema() will be removed automatically.
+ *
+ * The uninstall hook must be implemented in the module's .install file. It
+ * will fire when the module gets uninstalled but before the module's database
+ * tables are removed, allowing your module to query its own tables during
+ * this routine.
+ *
+ * When hook_uninstall() is called, your module will already be disabled, so
+ * its .module file will not be automatically included. If you need to call API
+ * functions from your .module file in this hook, use drupal_load() to make
+ * them available. (Keep this usage to a minimum, though, especially when
+ * calling API functions that invoke hooks, or API functions from modules
+ * listed as dependencies, since these may not be available or work as expected
+ * when the module is disabled.)
+ *
+ * @see hook_install()
+ * @see hook_schema()
+ * @see hook_disable()
+ * @see hook_modules_uninstalled()
+ */
+function hook_uninstall() {
+  // Remove the styles directory and generated images.
+  file_unmanaged_delete_recursive(file_default_scheme() . '://styles');
+}
+
+/**
+ * Return an array of tasks to be performed by an installation profile.
+ *
+ * Any tasks you define here will be run, in order, after the installer has
+ * finished the site configuration step but before it has moved on to the
+ * final import of languages and the end of the installation. You can have any
+ * number of custom tasks to perform during this phase.
+ *
+ * Each task you define here corresponds to a callback function which you must
+ * separately define and which is called when your task is run. This function
+ * will receive the global installation state variable, $install_state, as
+ * input, and has the opportunity to access or modify any of its settings. See
+ * the install_state_defaults() function in the installer for the list of
+ * $install_state settings used by Drupal core.
+ *
+ * At the end of your task function, you can indicate that you want the
+ * installer to pause and display a page to the user by returning any themed
+ * output that should be displayed on that page (but see below for tasks that
+ * use the form API or batch API; the return values of these task functions are
+ * handled differently). You should also use drupal_set_title() within the task
+ * callback function to set a custom page title. For some tasks, however, you
+ * may want to simply do some processing and pass control to the next task
+ * without ending the page request; to indicate this, simply do not send back
+ * a return value from your task function at all. This can be used, for
+ * example, by installation profiles that need to configure certain site
+ * settings in the database without obtaining any input from the user.
+ *
+ * The task function is treated specially if it defines a form or requires
+ * batch processing; in that case, you should return either the form API
+ * definition or batch API array, as appropriate. See below for more
+ * information on the 'type' key that you must define in the task definition
+ * to inform the installer that your task falls into one of those two
+ * categories. It is important to use these APIs directly, since the installer
+ * may be run non-interactively (for example, via a command line script), all
+ * in one page request; in that case, the installer will automatically take
+ * care of submitting forms and processing batches correctly for both types of
+ * installations. You can inspect the $install_state['interactive'] boolean to
+ * see whether or not the current installation is interactive, if you need
+ * access to this information.
+ *
+ * Remember that a user installing Drupal interactively will be able to reload
+ * an installation page multiple times, so you should use \Drupal::state() to
+ * store any data that you may need later in the installation process. Any
+ * temporary state must be removed using \Drupal::state()->delete() before
+ * your last task has completed and control is handed back to the installer.
+ *
+ * @param array $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return array
+ *   A keyed array of tasks the profile will perform during the final stage of
+ *   the installation. Each key represents the name of a function (usually a
+ *   function defined by this profile, although that is not strictly required)
+ *   that is called when that task is run. The values are associative arrays
+ *   containing the following key-value pairs (all of which are optional):
+ *   - display_name: The human-readable name of the task. This will be
+ *     displayed to the user while the installer is running, along with a list
+ *     of other tasks that are being run. Leave this unset to prevent the task
+ *     from appearing in the list.
+ *   - display: This is a boolean which can be used to provide finer-grained
+ *     control over whether or not the task will display. This is mostly useful
+ *     for tasks that are intended to display only under certain conditions;
+ *     for these tasks, you can set 'display_name' to the name that you want to
+ *     display, but then use this boolean to hide the task only when certain
+ *     conditions apply.
+ *   - type: A string representing the type of task. This parameter has three
+ *     possible values:
+ *     - normal: (default) This indicates that the task will be treated as a
+ *       regular callback function, which does its processing and optionally
+ *       returns HTML output.
+ *     - batch: This indicates that the task function will return a batch API
+ *       definition suitable for batch_set(). The installer will then take care
+ *       of automatically running the task via batch processing.
+ *     - form: This indicates that the task function will return a standard
+ *       form API definition (and separately define validation and submit
+ *       handlers, as appropriate). The installer will then take care of
+ *       automatically directing the user through the form submission process.
+ *   - run: A constant representing the manner in which the task will be run.
+ *     This parameter has three possible values:
+ *     - INSTALL_TASK_RUN_IF_NOT_COMPLETED: (default) This indicates that the
+ *       task will run once during the installation of the profile.
+ *     - INSTALL_TASK_SKIP: This indicates that the task will not run during
+ *       the current installation page request. It can be used to skip running
+ *       an installation task when certain conditions are met, even though the
+ *       task may still show on the list of installation tasks presented to the
+ *       user.
+ *     - INSTALL_TASK_RUN_IF_REACHED: This indicates that the task will run on
+ *       each installation page request that reaches it. This is rarely
+ *       necessary for an installation profile to use; it is primarily used by
+ *       the Drupal installer for bootstrap-related tasks.
+ *   - function: Normally this does not need to be set, but it can be used to
+ *     force the installer to call a different function when the task is run
+ *     (rather than the function whose name is given by the array key). This
+ *     could be used, for example, to allow the same function to be called by
+ *     two different tasks.
+ *
+ * @see install_state_defaults()
+ * @see batch_set()
+ */
+function hook_install_tasks(&$install_state) {
+  // Here, we define a variable to allow tasks to indicate that a particular,
+  // processor-intensive batch process needs to be triggered later on in the
+  // installation.
+  $myprofile_needs_batch_processing = \Drupal::state()->get('myprofile.needs_batch_processing', FALSE);
+  $tasks = array(
+    // This is an example of a task that defines a form which the user who is
+    // installing the site will be asked to fill out. To implement this task,
+    // your profile would define a function named myprofile_data_import_form()
+    // as a normal form API callback function, with associated validation and
+    // submit handlers. In the submit handler, in addition to saving whatever
+    // other data you have collected from the user, you might also call
+    // \Drupal::state()->set('myprofile.needs_batch_processing', TRUE) if the
+    // user has entered data which requires that batch processing will need to
+    // occur later on.
+    'myprofile_data_import_form' => array(
+      'display_name' => t('Data import options'),
+      'type' => 'form',
+    ),
+    // Similarly, to implement this task, your profile would define a function
+    // named myprofile_settings_form() with associated validation and submit
+    // handlers. This form might be used to collect and save additional
+    // information from the user that your profile needs. There are no extra
+    // steps required for your profile to act as an "installation wizard"; you
+    // can simply define as many tasks of type 'form' as you wish to execute,
+    // and the forms will be presented to the user, one after another.
+    'myprofile_settings_form' => array(
+      'display_name' => t('Additional options'),
+      'type' => 'form',
+    ),
+    // This is an example of a task that performs batch operations. To
+    // implement this task, your profile would define a function named
+    // myprofile_batch_processing() which returns a batch API array definition
+    // that the installer will use to execute your batch operations. Due to the
+    // 'myprofile.needs_batch_processing' variable used here, this task will be
+    // hidden and skipped unless your profile set it to TRUE in one of the
+    // previous tasks.
+    'myprofile_batch_processing' => array(
+      'display_name' => t('Import additional data'),
+      'display' => $myprofile_needs_batch_processing,
+      'type' => 'batch',
+      'run' => $myprofile_needs_batch_processing ? INSTALL_TASK_RUN_IF_NOT_COMPLETED : INSTALL_TASK_SKIP,
+    ),
+    // This is an example of a task that will not be displayed in the list that
+    // the user sees. To implement this task, your profile would define a
+    // function named myprofile_final_site_setup(), in which additional,
+    // automated site setup operations would be performed. Since this is the
+    // last task defined by your profile, you should also use this function to
+    // call \Drupal::state()->delete('myprofile.needs_batch_processing') and
+    // clean up the state that was used above. If you want the user to pass
+    // to the final Drupal installation tasks uninterrupted, return no output
+    // from this function. Otherwise, return themed output that the user will
+    // see (for example, a confirmation page explaining that your profile's
+    // tasks are complete, with a link to reload the current page and therefore
+    // pass on to the final Drupal installation tasks when the user is ready to
+    // do so).
+    'myprofile_final_site_setup' => array(
+    ),
+  );
+  return $tasks;
+}
+
+/**
+ * Alter XHTML HEAD tags before they are rendered by drupal_get_html_head().
+ *
+ * Elements available to be altered are only those added using
+ * drupal_add_html_head_link() or drupal_add_html_head(). CSS and JS files
+ * are handled using drupal_add_css() and drupal_add_js(), so the head links
+ * for those files will not appear in the $head_elements array.
+ *
+ * @param $head_elements
+ *   An array of renderable elements. Generally the values of the #attributes
+ *   array will be the most likely target for changes.
+ */
+function hook_html_head_alter(&$head_elements) {
+  foreach ($head_elements as $key => $element) {
+    if (isset($element['#attributes']['rel']) && $element['#attributes']['rel'] == 'canonical') {
+      // I want a custom canonical URL.
+      $head_elements[$key]['#attributes']['href'] = mymodule_canonical_url();
+    }
+  }
+}
+
+/**
+ * Alter the full list of installation tasks.
+ *
+ * You can use this hook to change or replace any part of the Drupal
+ * installation process that occurs after the installation profile is selected.
+ *
+ * @param $tasks
+ *   An array of all available installation tasks, including those provided by
+ *   Drupal core. You can modify this array to change or replace individual
+ *   steps within the installation process.
+ * @param $install_state
+ *   An array of information about the current installation state.
+ */
+function hook_install_tasks_alter(&$tasks, $install_state) {
+  // Replace the entire site configuration form provided by Drupal core
+  // with a custom callback function defined by this installation profile.
+  $tasks['install_configure_form']['function'] = 'myprofile_install_configure_form';
+}
+
+/**
+ * Alter MIME type mappings used to determine MIME type from a file extension.
+ *
+ * This hook is run when file_mimetype_mapping() is called. It is used to
+ * allow modules to add to or modify the default mapping from
+ * file_default_mimetype_mapping().
+ *
+ * @param $mapping
+ *   An array of mimetypes correlated to the extensions that relate to them.
+ *   The array has 'mimetypes' and 'extensions' elements, each of which is an
+ *   array.
+ *
+ * @see file_default_mimetype_mapping()
+ */
+function hook_file_mimetype_mapping_alter(&$mapping) {
+  // Add new MIME type 'drupal/info'.
+  $mapping['mimetypes']['example_info'] = 'drupal/info';
+  // Add new extension '.info.yml' and map it to the 'drupal/info' MIME type.
+  $mapping['extensions']['info'] = 'example_info';
+  // Override existing extension mapping for '.ogg' files.
+  $mapping['extensions']['ogg'] = 189;
+}
+
+/**
+ * Alter archiver information declared by other modules.
+ *
+ * See hook_archiver_info() for a description of archivers and the archiver
+ * information structure.
+ *
+ * @param $info
+ *   Archiver information to alter (return values from hook_archiver_info()).
+ */
+function hook_archiver_info_alter(&$info) {
+  $info['tar']['extensions'][] = 'tgz';
+}
+
+/**
+ * Alters theme operation links.
+ *
+ * @param $theme_groups
+ *   An associative array containing groups of themes.
+ *
+ * @see system_themes_page()
+ */
+function hook_system_themes_page_alter(&$theme_groups) {
+  foreach ($theme_groups as $state => &$group) {
+    foreach ($theme_groups[$state] as &$theme) {
+      // Add a foo link to each list of theme operations.
+      $theme->operations[] = array(
+        'title' => t('Foo'),
+        'href' => 'admin/appearance/foo',
+        'query' => array('theme' => $theme->name)
+      );
+    }
+  }
+}
+
+/**
+ * Alters outbound URLs.
+ *
+ * @param $path
+ *   The outbound path to alter, not adjusted for path aliases yet. It won't be
+ *   adjusted for path aliases until all modules are finished altering it. This
+ *   may have been altered by other modules before this one.
+ * @param $options
+ *   A set of URL options for the URL so elements such as a fragment or a query
+ *   string can be added to the URL.
+ * @param $original_path
+ *   The original path, before being altered by any modules.
+ *
+ * @see url()
+ */
+function hook_url_outbound_alter(&$path, &$options, $original_path) {
+  // Use an external RSS feed rather than the Drupal one.
+  if ($path == 'rss.xml') {
+    $path = 'http://example.com/rss.xml';
+    $options['external'] = TRUE;
+  }
+
+  // Instead of pointing to user/[uid]/edit, point to user/me/edit.
+  if (preg_match('|^user/([0-9]*)/edit(/.*)?|', $path, $matches)) {
+    global $user;
+    if ($user->id() == $matches[1]) {
+      $path = 'user/me/edit' . $matches[2];
+    }
+  }
+}
+
+/**
+ * Provide replacement values for placeholder tokens.
+ *
+ * This hook is invoked when someone calls
+ * \Drupal\Core\Utility\Token::replace(). That function first scans the text for
+ * [type:token] patterns, and splits the needed tokens into groups by type.
+ * Then hook_tokens() is invoked on each token-type group, allowing your module
+ * to respond by providing replacement text for any of the tokens in the group
+ * that your module knows how to process.
+ *
+ * A module implementing this hook should also implement hook_token_info() in
+ * order to list its available tokens on editing screens.
+ *
+ * @param $type
+ *   The machine-readable name of the type (group) of token being replaced, such
+ *   as 'node', 'user', or another type defined by a hook_token_info()
+ *   implementation.
+ * @param $tokens
+ *   An array of tokens to be replaced. The keys are the machine-readable token
+ *   names, and the values are the raw [type:token] strings that appeared in the
+ *   original text.
+ * @param $data
+ *   (optional) An associative array of data objects to be used when generating
+ *   replacement values, as supplied in the $data parameter to
+ *   \Drupal\Core\Utility\Token::replace().
+ * @param $options
+ *   (optional) An associative array of options for token replacement; see
+ *   \Drupal\Core\Utility\Token::replace() for possible values.
+ *
+ * @return
+ *   An associative array of replacement values, keyed by the raw [type:token]
+ *   strings from the original text.
+ *
+ * @see hook_token_info()
+ * @see hook_tokens_alter()
+ */
+function hook_tokens($type, $tokens, array $data = array(), array $options = array()) {
+  $token_service = \Drupal::token();
+
+  $url_options = array('absolute' => TRUE);
+  if (isset($options['langcode'])) {
+    $url_options['language'] = language_load($options['langcode']);
+    $langcode = $options['langcode'];
+  }
+  else {
+    $langcode = NULL;
+  }
+  $sanitize = !empty($options['sanitize']);
+
+  $replacements = array();
+
+  if ($type == 'node' && !empty($data['node'])) {
+    $node = $data['node'];
+
+    foreach ($tokens as $name => $original) {
+      switch ($name) {
+        // Simple key values on the node.
+        case 'nid':
+          $replacements[$original] = $node->nid;
+          break;
+
+        case 'title':
+          $replacements[$original] = $sanitize ? check_plain($node->getTitle()) : $node->getTitle();
+          break;
+
+        case 'edit-url':
+          $replacements[$original] = url('node/' . $node->id() . '/edit', $url_options);
+          break;
+
+        // Default values for the chained tokens handled below.
+        case 'author':
+          $account = $node->getAuthor() ? $node->getAuthor() : user_load(0);
+          $replacements[$original] = $sanitize ? check_plain($account->label()) : $account->label();
+          break;
+
+        case 'created':
+          $replacements[$original] = format_date($node->getCreatedTime(), 'medium', '', NULL, $langcode);
+          break;
+      }
+    }
+
+    if ($author_tokens = $token_service->findWithPrefix($tokens, 'author')) {
+      $replacements += $token_service->generate('user', $author_tokens, array('user' => $node->getAuthor()), $options);
+    }
+
+    if ($created_tokens = $token_service->findWithPrefix($tokens, 'created')) {
+      $replacements += $token_service->generate('date', $created_tokens, array('date' => $node->getCreatedTime()), $options);
+    }
+  }
+
+  return $replacements;
+}
+
+/**
+ * Alter replacement values for placeholder tokens.
+ *
+ * @param $replacements
+ *   An associative array of replacements returned by hook_tokens().
+ * @param $context
+ *   The context in which hook_tokens() was called. An associative array with
+ *   the following keys, which have the same meaning as the corresponding
+ *   parameters of hook_tokens():
+ *   - 'type'
+ *   - 'tokens'
+ *   - 'data'
+ *   - 'options'
+ *
+ * @see hook_tokens()
+ */
+function hook_tokens_alter(array &$replacements, array $context) {
+  $options = $context['options'];
+
+  if (isset($options['langcode'])) {
+    $url_options['language'] = language_load($options['langcode']);
+    $langcode = $options['langcode'];
+  }
+  else {
+    $langcode = NULL;
+  }
+
+  if ($context['type'] == 'node' && !empty($context['data']['node'])) {
+    $node = $context['data']['node'];
+
+    // Alter the [node:title] token, and replace it with the rendered content
+    // of a field (field_title).
+    if (isset($context['tokens']['title'])) {
+      $title = field_view_field($node, 'field_title', 'default', $langcode);
+      $replacements[$context['tokens']['title']] = drupal_render($title);
+    }
+  }
+}
+
+/**
+ * Provide information about available placeholder tokens and token types.
+ *
+ * Tokens are placeholders that can be put into text by using the syntax
+ * [type:token], where type is the machine-readable name of a token type, and
+ * token is the machine-readable name of a token within this group. This hook
+ * provides a list of types and tokens to be displayed on text editing screens,
+ * so that people editing text can see what their token options are.
+ *
+ * The actual token replacement is done by
+ * \Drupal\Core\Utility\Token::replace(), which invokes hook_tokens(). Your
+ * module will need to implement that hook in order to generate token
+ * replacements from the tokens defined here.
+ *
+ * @return
+ *   An associative array of available tokens and token types. The outer array
+ *   has two components:
+ *   - types: An associative array of token types (groups). Each token type is
+ *     an associative array with the following components:
+ *     - name: The translated human-readable short name of the token type.
+ *     - description: A translated longer description of the token type.
+ *     - needs-data: The type of data that must be provided to
+ *       \Drupal\Core\Utility\Token::replace() in the $data argument (i.e., the
+ *       key name in $data) in order for tokens of this type to be used in the
+ *       $text being processed. For instance, if the token needs a node object,
+ *       'needs-data' should be 'node', and to use this token in
+ *       \Drupal\Core\Utility\Token::replace(), the caller needs to supply a
+ *       node object as $data['node']. Some token data can also be supplied
+ *       indirectly; for instance, a node object in $data supplies a user object
+ *       (the author of the node), allowing user tokens to be used when only
+ *       a node data object is supplied.
+ *   - tokens: An associative array of tokens. The outer array is keyed by the
+ *     group name (the same key as in the types array). Within each group of
+ *     tokens, each token item is keyed by the machine name of the token, and
+ *     each token item has the following components:
+ *     - name: The translated human-readable short name of the token.
+ *     - description: A translated longer description of the token.
+ *     - type (optional): A 'needs-data' data type supplied by this token, which
+ *       should match a 'needs-data' value from another token type. For example,
+ *       the node author token provides a user object, which can then be used
+ *       for token replacement data in \Drupal\Core\Utility\Token::replace()
+ *       without having to supply a separate user object.
+ *
+ * @see hook_token_info_alter()
+ * @see hook_tokens()
+ */
+function hook_token_info() {
+  $type = array(
+    'name' => t('Nodes'),
+    'description' => t('Tokens related to individual nodes.'),
+    'needs-data' => 'node',
+  );
+
+  // Core tokens for nodes.
+  $node['nid'] = array(
+    'name' => t("Node ID"),
+    'description' => t("The unique ID of the node."),
+  );
+  $node['title'] = array(
+    'name' => t("Title"),
+    'description' => t("The title of the node."),
+  );
+  $node['edit-url'] = array(
+    'name' => t("Edit URL"),
+    'description' => t("The URL of the node's edit page."),
+  );
+
+  // Chained tokens for nodes.
+  $node['created'] = array(
+    'name' => t("Date created"),
+    'description' => t("The date the node was posted."),
+    'type' => 'date',
+  );
+  $node['author'] = array(
+    'name' => t("Author"),
+    'description' => t("The author of the node."),
+    'type' => 'user',
+  );
+
+  return array(
+    'types' => array('node' => $type),
+    'tokens' => array('node' => $node),
+  );
+}
+
+/**
+ * Alter the metadata about available placeholder tokens and token types.
+ *
+ * @param $data
+ *   The associative array of token definitions from hook_token_info().
+ *
+ * @see hook_token_info()
+ */
+function hook_token_info_alter(&$data) {
+  // Modify description of node tokens for our site.
+  $data['tokens']['node']['nid'] = array(
+    'name' => t("Node ID"),
+    'description' => t("The unique ID of the article."),
+  );
+  $data['tokens']['node']['title'] = array(
+    'name' => t("Title"),
+    'description' => t("The title of the article."),
+  );
+
+  // Chained tokens for nodes.
+  $data['tokens']['node']['created'] = array(
+    'name' => t("Date created"),
+    'description' => t("The date the article was posted."),
+    'type' => 'date',
+  );
+}
+
+/**
+ * Alter batch information before a batch is processed.
+ *
+ * Called by batch_process() to allow modules to alter a batch before it is
+ * processed.
+ *
+ * @param $batch
+ *   The associative array of batch information. See batch_set() for details on
+ *   what this could contain.
+ *
+ * @see batch_set()
+ * @see batch_process()
+ *
+ * @ingroup batch
+ */
+function hook_batch_alter(&$batch) {
+  // If the current page request is inside the overlay, add ?render=overlay to
+  // the success callback URL, so that it appears correctly within the overlay.
+  if (overlay_get_mode() == 'child') {
+    if (isset($batch['url_options']['query'])) {
+      $batch['url_options']['query']['render'] = 'overlay';
+    }
+    else {
+      $batch['url_options']['query'] = array('render' => 'overlay');
+    }
+  }
+}
+
+/**
+ * Provide information on Updaters (classes that can update Drupal).
+ *
+ * Drupal\Core\Updater\Updater is a class that knows how to update various parts
+ * of the Drupal file system, for example to update modules that have newer
+ * releases, or to install a new theme.
+ *
+ * @return
+ *   An associative array of information about the updater(s) being provided.
+ *   This array is keyed by a unique identifier for each updater, and the
+ *   values are subarrays that can contain the following keys:
+ *   - class: The name of the PHP class which implements this updater.
+ *   - name: Human-readable name of this updater.
+ *   - weight: Controls what order the Updater classes are consulted to decide
+ *     which one should handle a given task. When an update task is being run,
+ *     the system will loop through all the Updater classes defined in this
+ *     registry in weight order and let each class respond to the task and
+ *     decide if each Updater wants to handle the task. In general, this
+ *     doesn't matter, but if you need to override an existing Updater, make
+ *     sure your Updater has a lighter weight so that it comes first.
+ *
+ * @see drupal_get_updaters()
+ * @see hook_updater_info_alter()
+ */
+function hook_updater_info() {
+  return array(
+    'module' => array(
+      'class' => 'Drupal\Core\Updater\Module',
+      'name' => t('Update modules'),
+      'weight' => 0,
+    ),
+    'theme' => array(
+      'class' => 'Drupal\Core\Updater\Theme',
+      'name' => t('Update themes'),
+      'weight' => 0,
+    ),
+  );
+}
+
+/**
+ * Alter the Updater information array.
+ *
+ * An Updater is a class that knows how to update various parts of the Drupal
+ * file system, for example to update modules that have newer releases, or to
+ * install a new theme.
+ *
+ * @param array $updaters
+ *   Associative array of updaters as defined through hook_updater_info().
+ *   Alter this array directly.
+ *
+ * @see drupal_get_updaters()
+ * @see hook_updater_info()
+ */
+function hook_updater_info_alter(&$updaters) {
+  // Adjust weight so that the theme Updater gets a chance to handle a given
+  // update task before module updaters.
+  $updaters['theme']['weight'] = -1;
+}
+
+/**
+ * Alter the default country list.
+ *
+ * @param $countries
+ *   The associative array of countries keyed by two-letter country code.
+ *
+ * @see \Drupal\Core\Locale\CountryManager::getList().
+ */
+function hook_countries_alter(&$countries) {
+  // Elbonia is now independent, so add it to the country list.
+  $countries['EB'] = 'Elbonia';
+}
+
+/**
+ * Register information about FileTransfer classes provided by a module.
+ *
+ * The FileTransfer class allows transferring files over a specific type of
+ * connection. Core provides classes for FTP and SSH. Contributed modules are
+ * free to extend the FileTransfer base class to add other connection types,
+ * and if these classes are registered via hook_filetransfer_info(), those
+ * connection types will be available to site administrators using the Update
+ * manager when they are redirected to the authorize.php script to authorize
+ * the file operations.
+ *
+ * @return array
+ *   Nested array of information about FileTransfer classes. Each key is a
+ *   FileTransfer type (not human readable, used for form elements and
+ *   variable names, etc), and the values are subarrays that define properties
+ *   of that type. The keys in each subarray are:
+ *   - 'title': Required. The human-readable name of the connection type.
+ *   - 'class': Required. The name of the FileTransfer class. The constructor
+ *     will always be passed the full path to the root of the site that should
+ *     be used to restrict where file transfer operations can occur (the $jail)
+ *     and an array of settings values returned by the settings form.
+ *   - 'file': Required. The include file containing the FileTransfer class.
+ *     This should be a separate .inc file, not just the .module file, so that
+ *     the minimum possible code is loaded when authorize.php is running.
+ *   - 'file path': Optional. The directory (relative to the Drupal root)
+ *     where the include file lives. If not defined, defaults to the base
+ *     directory of the module implementing the hook.
+ *   - 'weight': Optional. Integer weight used for sorting connection types on
+ *     the authorize.php form.
+ *
+ * @see \Drupal\Core\FileTransfer\FileTransfer
+ * @see authorize.php
+ * @see hook_filetransfer_info_alter()
+ * @see drupal_get_filetransfer_info()
+ */
+function hook_filetransfer_info() {
+  $info['sftp'] = array(
+    'title' => t('SFTP (Secure FTP)'),
+    'class' => 'Drupal\Core\FileTransfer\SFTP',
+    'weight' => 10,
+  );
+  return $info;
+}
+
+/**
+ * Alter the FileTransfer class registry.
+ *
+ * @param array $filetransfer_info
+ *   Reference to a nested array containing information about the FileTransfer
+ *   class registry.
+ *
+ * @see hook_filetransfer_info()
+ */
+function hook_filetransfer_info_alter(&$filetransfer_info) {
+  // Remove the FTP option entirely.
+  unset($filetransfer_info['ftp']);
+  // Make sure the SSH option is listed first.
+  $filetransfer_info['ssh']['weight'] = -10;
+}
+
+/**
+ * Alter the parameters for links.
+ *
+ * @param array $variables
+ *   An associative array of variables defining a link. The link may be either a
+ *   "route link" using \Drupal\Core\Utility\LinkGenerator::link(), which is
+ *   exposed as the 'link_generator' service or a link generated by l(). If the
+ *   link is a "route link", 'route_name' will be set, otherwise 'path' will be
+ *   set. The following keys can be altered:
+ *   - text: The link text for the anchor tag as a translated string.
+ *   - url_is_active: Whether or not the link points to the currently active
+ *     URL.
+ *   - path: If this link is being generated by l(), this system path, relative
+ *     path, or external URL will be passed to url() to generate the href
+ *     attribute for this link.
+ *   - route_name: The name of the route to use to generate the link, if
+ *     this is a "route link".
+ *   - parameters: Any parameters needed to render the route path pattern, if
+ *     this is a "route link".
+ *   - options: An associative array of additional options that will be passed
+ *     to either \Drupal\Core\Routing\UrlGenerator::generateFromPath() or
+ *     \Drupal\Core\Routing\UrlGenerator::generateFromRoute() to generate the
+ *     href attribute for this link, and also used when generating the link.
+ *     Defaults to an empty array. It may contain the following elements:
+ *     - 'query': An array of query key/value-pairs (without any URL-encoding) to
+ *       append to the URL.
+ *     - absolute: Whether to force the output to be an absolute link (beginning
+ *       with http:). Useful for links that will be displayed outside the site,
+ *       such as in an RSS feed. Defaults to FALSE.
+ *     - language: An optional language object. May affect the rendering of
+ *       the anchor tag, such as by adding a language prefix to the path.
+ *     - attributes: An associative array of HTML attributes to apply to the
+ *       anchor tag. If element 'class' is included, it must be an array; 'title'
+ *       must be a string; other elements are more flexible, as they just need
+ *       to work as an argument for the constructor of the class
+ *       Drupal\Core\Template\Attribute($options['attributes']).
+ *     - html: Whether or not HTML should be allowed as the link text. If FALSE,
+ *       the text will be run through
+ *       \Drupal\Component\Utility\String::checkPlain() before being output.
+ *
+ * @see \Drupal\Core\Routing\UrlGenerator::generateFromPath()
+ * @see \Drupal\Core\Routing\UrlGenerator::generateFromRoute()
+ */
+function hook_link_alter(&$variables) {
+  // Add a warning to the end of route links to the admin section.
+  if (isset($variables['route_name']) && strpos($variables['route_name'], 'admin') !== FALSE) {
+    $variables['text'] .= ' (Warning!)';
+  }
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
+
+/**
+ * @defgroup update_api Update versions of API functions
+ * @{
+ * Functions that are similar to normal API functions, but do not invoke hooks.
+ *
+ * These simplified versions of core API functions are provided for use by
+ * update functions (hook_update_N() implementations).
+ *
+ * During database updates the schema of any module could be out of date. For
+ * this reason, caution is needed when using any API function within an update
+ * function - particularly CRUD functions, functions that depend on the schema
+ * (for example by using drupal_write_record()), and any functions that invoke
+ * hooks.
+ *
+ * Instead, a simplified utility function should be used. If a utility version
+ * of the API function you require does not already exist, then you should
+ * create a new function. The new utility function should be named
+ * _update_N_mymodule_my_function(). N is the schema version the function acts
+ * on (the schema version is the number N from the hook_update_N()
+ * implementation where this schema was introduced, or a number following the
+ * same numbering scheme), and mymodule_my_function is the name of the original
+ * API function including the module's name.
+ *
+ * Examples:
+ * - _update_7000_mymodule_save(): This function performs a save operation
+ *   without invoking any hooks using the 7.x schema.
+ * - _update_8000_mymodule_save(): This function performs the same save
+ *   operation using the 8.x schema.
+ *
+ * The utility function should not invoke any hooks, and should perform database
+ * operations using functions from the
+ * @link database Database abstraction layer, @endlink
+ * like db_insert(), db_update(), db_delete(), db_query(), and so on.
+ *
+ * If a change to the schema necessitates a change to the utility function, a
+ * new function should be created with a name based on the version of the schema
+ * it acts on. See _update_8000_bar_get_types() and _update_8001_bar_get_types()
+ * in the code examples that follow.
+ *
+ * For example, foo.install could contain:
+ * @code
+ * function foo_update_dependencies() {
+ *   // foo_update_8010() needs to run after bar_update_8000().
+ *   $dependencies['foo'][8010] = array(
+ *     'bar' => 8000,
+ *   );
+ *
+ *   // foo_update_8036() needs to run after bar_update_8001().
+ *   $dependencies['foo'][8036] = array(
+ *     'bar' => 8001,
+ *   );
+ *
+ *   return $dependencies;
+ * }
+ *
+ * function foo_update_8000() {
+ *   // No updates have been run on the {bar_types} table yet, so this needs
+ *   // to work with the 7.x schema.
+ *   foreach (_update_7000_bar_get_types() as $type) {
+ *     // Rename a variable.
+ *   }
+ * }
+ *
+ * function foo_update_8010() {
+ *    // Since foo_update_8010() is going to run after bar_update_8000(), it
+ *    // needs to operate on the new schema, not the old one.
+ *    foreach (_update_8000_bar_get_types() as $type) {
+ *      // Rename a different variable.
+ *    }
+ * }
+ *
+ * function foo_update_8036() {
+ *   // This update will run after bar_update_8001().
+ *   foreach (_update_8001_bar_get_types() as $type) {
+ *   }
+ * }
+ * @endcode
+ *
+ * And bar.install could contain:
+ * @code
+ * function bar_update_8000() {
+ *   // Type and bundle are confusing, so we renamed the table.
+ *   db_rename_table('bar_types', 'bar_bundles');
+ * }
+ *
+ * function bar_update_8001() {
+ *   // Database table names should be singular when possible.
+ *   db_rename_table('bar_bundles', 'bar_bundle');
+ * }
+ *
+ * function _update_7000_bar_get_types() {
+ *   db_query('SELECT * FROM {bar_types}')->fetchAll();
+ * }
+ *
+ * function _update_8000_bar_get_types() {
+ *   db_query('SELECT * FROM {bar_bundles'})->fetchAll();
+ * }
+ *
+ * function _update_8001_bar_get_types() {
+ *   db_query('SELECT * FROM {bar_bundle}')->fetchAll();
+ * }
+ * @endcode
+ *
+ * @see hook_update_N()
+ * @see hook_update_dependencies()
+ */
+
+/**
+ * @} End of "defgroup update_api".
+ */
+
+/**
+ * @defgroup annotation Annotations
+ * @{
+ * Annotations for class discovery and metadata description.
+ *
+ * The Drupal plugin system has a set of reusable components that developers
+ * can use, override, and extend in their modules. Most of the plugins use
+ * annotations, which let classes register themselves as plugins and describe
+ * their metadata. (Annotations can also be used for other purposes, though
+ * at the moment, Drupal only uses them for the plugin system.)
+ *
+ * To annotate a class as a plugin, add code similar to the following to the
+ * end of the documentation block immediately preceding the class declaration:
+ * @code
+ * * @EntityType(
+ * *   id = "comment",
+ * *   label = @Translation("Comment"),
+ * *   ...
+ * *   base_table = "comment"
+ * * )
+ * @endcode
+ *
+ * Note that you must use double quotes; single quotes will not work in
+ * annotations.
+ *
+ * The available annotation classes are listed in this topic, and can be
+ * identified when you are looking at the Drupal source code by having
+ * "@ Annotation" in their documentation blocks (without the space after @). To
+ * find examples of annotation for a particular annotation class, such as
+ * EntityType, look for class files that contain a PHP "use" declaration of the
+ * annotation class, or files that have an @ annotation section using the
+ * annotation class.
+ * @}
+ */
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 0d2dbeb..559ece1 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -824,20 +824,6 @@ function system_schema() {
         'not null' => TRUE,
         'default' => '',
       ),
-      'theme_callback' => array(
-        'description' => 'A function which returns the name of the theme that will be used to render this page. If left empty, the default theme will be used.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'theme_arguments' => array(
-        'description' => 'A serialized array of arguments for the theme callback.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-      ),
       'type' => array(
         'description' => 'Numeric representation of the type of the menu item, like MENU_LOCAL_TASK.',
         'type' => 'int',
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 66e5a6f..fea6443 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -609,12 +609,6 @@ function system_element_info() {
  * Implements hook_menu().
  */
 function system_menu() {
-  $items['system/ajax'] = array(
-    'title' => 'AHAH callback',
-    'route_name' => 'system.ajax',
-    'theme callback' => 'ajax_base_page_theme',
-    'type' => MENU_CALLBACK,
-  );
   $items['admin'] = array(
     'title' => 'Administration',
     'route_name' => 'system.admin',
@@ -815,13 +809,6 @@ function system_menu() {
     'route_name' => 'system.status',
   );
 
-  // Default page for batch operations.
-  $items['batch'] = array(
-    'route_name' => 'system.batch_page',
-    'theme callback' => '_system_batch_theme',
-    'type' => MENU_CALLBACK,
-  );
-
   // Localize date formats.
   if (\Drupal::moduleHandler()->moduleExists('language')) {
     $items['admin/config/regional/date-time/locale'] = array(
@@ -894,21 +881,6 @@ function system_theme_suggestions_region(array $variables) {
 }
 
 /**
- * Theme callback for the default batch page.
- */
-function _system_batch_theme() {
-  // Retrieve the current state of the batch.
-  $batch = &batch_get();
-  if (!$batch && isset($_REQUEST['id'])) {
-    $batch = \Drupal::service('batch.storage')->load($_REQUEST['id']);
-  }
-  // Use the same theme as the page that started the batch.
-  if (!empty($batch['theme'])) {
-    return $batch['theme'];
-  }
-}
-
-/**
  * Implements hook_library_info().
  */
 function system_library_info() {
@@ -2155,19 +2127,6 @@ function system_page_build(&$page) {
 }
 
 /**
- * Implements hook_custom_theme().
- */
-function system_custom_theme() {
-  if (drupal_container()->isScopeActive('request')) {
-    $request = \Drupal::request();
-    $path = $request->attributes->get('_system_path');
-    if (user_access('view the administration theme') && path_is_admin($path)) {
-      return \Drupal::config('system.theme')->get('admin');
-    }
-  }
-}
-
-/**
  * Implements hook_form_FORM_ID_alter().
  */
 function system_form_user_form_alter(&$form, &$form_state) {
diff --git a/core/modules/system/system.module.orig b/core/modules/system/system.module.orig
new file mode 100644
index 0000000..66e5a6f
--- /dev/null
+++ b/core/modules/system/system.module.orig
@@ -0,0 +1,3189 @@
+<?php
+
+/**
+ * @file
+ * Configuration system that lets administrators modify the workings of the site.
+ */
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Utility\ModuleInfo;
+use Drupal\user\UserInterface;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpFoundation\Response;
+use Guzzle\Http\Exception\BadResponseException;
+use Guzzle\Http\Exception\RequestException;
+
+/**
+ * Maximum age of temporary files in seconds.
+ */
+const DRUPAL_MAXIMUM_TEMP_FILE_AGE = 21600;
+
+/**
+ * New users will be set to the default time zone at registration.
+ */
+const DRUPAL_USER_TIMEZONE_DEFAULT = 0;
+
+/**
+ * New users will get an empty time zone at registration.
+ */
+const DRUPAL_USER_TIMEZONE_EMPTY = 1;
+
+/**
+ * New users will select their own timezone at registration.
+ */
+const DRUPAL_USER_TIMEZONE_SELECT = 2;
+
+ /**
+ * Disabled option on forms and settings
+ */
+const DRUPAL_DISABLED = 0;
+
+/**
+ * Optional option on forms and settings
+ */
+const DRUPAL_OPTIONAL = 1;
+
+/**
+ * Required option on forms and settings
+ */
+const DRUPAL_REQUIRED = 2;
+
+/**
+ * Return only visible regions.
+ *
+ * @see system_region_list()
+ */
+const REGIONS_VISIBLE = 'visible';
+
+/**
+ * Return all regions.
+ *
+ * @see system_region_list()
+ */
+const REGIONS_ALL = 'all';
+
+/**
+ * Implements hook_help().
+ */
+function system_help($path, $arg) {
+  global $base_url;
+
+  switch ($path) {
+    case 'admin/help#system':
+      $output = '';
+      $output .= '<h3>' . t('About') . '</h3>';
+      $output .= '<p>' . t('The System module is integral to the site, and provides basic but extensible functionality for use by other modules and themes. Some integral elements of Drupal are contained in and managed by the System module, including caching, enabling and disabling modules and themes, preparing and displaying the administrative page, and configuring fundamental site settings. A number of key system maintenance operations are also part of the System module. For more information, see the online handbook entry for <a href="@system">System module</a>.', array('@system' => 'http://drupal.org/documentation/modules/system')) . '</p>';
+      $output .= '<h3>' . t('Uses') . '</h3>';
+      $output .= '<dl>';
+      $output .= '<dt>' . t('Managing modules') . '</dt>';
+      $output .= '<dd>' . t('The System module allows users with the appropriate permissions to enable and disable modules on the <a href="@modules">Modules administration page</a>. Drupal comes with a number of core modules, and each module provides a discrete set of features and may be enabled or disabled depending on the needs of the site. Many additional modules contributed by members of the Drupal community are available for download at the <a href="@drupal-modules">Drupal.org module page</a>.', array('@modules' => url('admin/modules'), '@drupal-modules' => 'http://drupal.org/project/modules')) . '</dd>';
+      $output .= '<dt>' . t('Managing themes') . '</dt>';
+      $output .= '<dd>' . t('The System module allows users with the appropriate permissions to enable and disable themes on the <a href="@themes">Appearance administration page</a>. Themes determine the design and presentation of your site. Drupal comes packaged with several core themes, and additional contributed themes are available at the <a href="@drupal-themes">Drupal.org theme page</a>.', array('@themes' => url('admin/appearance'), '@drupal-themes' => 'http://drupal.org/project/themes')) . '</dd>';
+      $output .= '<dt>' . t('Managing caching') . '</dt>';
+      $output .= '<dd>' . t("The System module allows users with the appropriate permissions to manage caching on the <a href='@cache-settings'>Performance settings page</a>. Drupal has a robust caching system that allows the efficient re-use of previously-constructed web pages and web page components. Pages requested by anonymous users are stored in a compressed format; depending on your site configuration and the amount of your web traffic tied to anonymous visitors, the caching system may significantly increase the speed of your site.", array('@cache-settings' => url('admin/config/development/performance'))) . '</dd>';
+      $output .= '<dt>' . t('Performing system maintenance') . '</dt>';
+      $output .= '<dd>' . t('In order for the site and its modules to continue to operate well, a set of routine administrative operations must run on a regular basis. The System module manages this task by making use of a system cron job. You can verify the status of cron tasks by visiting the <a href="@status">Status report page</a>. For more information, see the online handbook entry for <a href="@handbook">configuring cron jobs</a>. You can set up cron job by visiting <a href="@cron">Cron configuration</a> page', array('@status' => url('admin/reports/status'), '@handbook' => 'http://drupal.org/cron', '@cron' => url('admin/config/system/cron'))) . '</dd>';
+      $output .= '<dt>' . t('Configuring basic site settings') . '</dt>';
+      $output .= '<dd>' . t('The System module also handles basic configuration options for your site, including <a href="@date-time-settings">Date and time settings</a>, <a href="@file-system">File system settings</a>, <a href="@site-info">Site name and other information</a>, and a <a href="@maintenance-mode">Maintenance mode</a> for taking your site temporarily offline.', array('@date-time-settings' => url('admin/config/regional/date-time'), '@file-system' => url('admin/config/media/file-system'), '@site-info' => url('admin/config/system/site-information'), '@maintenance-mode' => url('admin/config/development/maintenance'))) . '</dd>';
+      $output .= '</dl>';
+      return $output;
+    case 'admin/index':
+      return '<p>' . t('This page shows you all available administration tasks for each module.') . '</p>';
+    case 'admin/appearance':
+      $output = '<p>' . t('Set and configure the default theme for your website.  Alternative <a href="@themes">themes</a> are available.', array('@themes' => 'http://drupal.org/project/themes')) . '</p>';
+      return $output;
+    case 'admin/appearance/settings/' . $arg[3]:
+      $theme_list = list_themes();
+      $theme = $theme_list[$arg[3]];
+      return '<p>' . t('These options control the display settings for the %name theme. When your site is displayed using this theme, these settings will be used.', array('%name' => $theme->info['name'])) . '</p>';
+    case 'admin/appearance/settings':
+      return '<p>' . t('These options control the default display settings for your entire site, across all themes. Unless they have been overridden by a specific theme, these settings will be used.') . '</p>';
+    case 'admin/modules':
+      $output = '<p>' . t('Download additional <a href="@modules">contributed modules</a> to extend Drupal\'s functionality.', array('@modules' => 'http://drupal.org/project/modules')) . '</p>';
+      if (\Drupal::moduleHandler()->moduleExists('update')) {
+        if (update_manager_access()) {
+          $output .= '<p>' . t('Regularly review and install <a href="@updates">available updates</a> to maintain a secure and current site. Always run the <a href="@update-php">update script</a> each time a module is updated.', array('@update-php' => $base_url . '/core/update.php', '@updates' => url('admin/reports/updates'))) . '</p>';
+        }
+        else {
+          $output .= '<p>' . t('Regularly review <a href="@updates">available updates</a> to maintain a secure and current site. Always run the <a href="@update-php">update script</a> each time a module is updated.', array('@update-php' => $base_url . '/core/update.php', '@updates' => url('admin/reports/updates'))) . '</p>';
+        }
+      }
+      else {
+        $output .= '<p>' . t('Regularly review available updates to maintain a secure and current site. Always run the <a href="@update-php">update script</a> each time a module is updated. Enable the Update Manager module to update and install modules and themes.', array('@update-php' => $base_url . '/core/update.php')) . '</p>';
+      }
+      return $output;
+    case 'admin/modules/uninstall':
+      return '<p>' . t('The uninstall process removes all data related to a module.') . '</p>';
+    case 'admin/structure/block/manage':
+      if ($arg[4] == 'system' && $arg[5] == 'powered-by') {
+        return '<p>' . t('The <em>Powered by Drupal</em> block is an optional link to the home page of the Drupal project. While there is absolutely no requirement that sites feature this link, it may be used to show support for Drupal.') . '</p>';
+      }
+      break;
+    case 'admin/config/development/maintenance':
+      global $user;
+      if ($user->id() == 1) {
+        return '<p>' . t('Use maintenance mode when making major updates, particularly if the updates could disrupt visitors or the update process. Examples include upgrading, importing or exporting content, modifying a theme, modifying content types, and making backups.') . '</p>';
+      }
+      break;
+    case 'admin/reports/status':
+      return '<p>' . t("Here you can find a short overview of your site's parameters as well as any problems detected with your installation. It may be useful to copy and paste this information into support requests filed on drupal.org's support forums and project issue queues. Before filing a support request, ensure that your web server meets the <a href=\"@system-requirements\">system requirements.</a>", array('@system-requirements' => 'http://drupal.org/requirements')) . '</p>';
+  }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function system_theme() {
+  return array_merge(drupal_common_theme(), array(
+    'system_themes_page' => array(
+      'variables' => array(
+        'theme_groups' => NULL,
+        'theme_group_titles' => NULL,
+      ),
+      'file' => 'system.admin.inc',
+    ),
+    'system_config_form' => array(
+      'render element' => 'form',
+    ),
+    'confirm_form' => array(
+      'render element' => 'form',
+    ),
+    'system_modules_details' => array(
+      'render element' => 'form',
+      'file' => 'system.admin.inc',
+    ),
+    'system_modules_incompatible' => array(
+      'variables' => array('message' => NULL),
+      'file' => 'system.admin.inc',
+    ),
+    'system_modules_uninstall' => array(
+      'render element' => 'form',
+      'file' => 'system.admin.inc',
+    ),
+    'status_report' => array(
+      'variables' => array('requirements' => NULL),
+      'file' => 'system.admin.inc',
+    ),
+    'admin_page' => array(
+      'variables' => array('blocks' => NULL),
+      'file' => 'system.admin.inc',
+    ),
+    'admin_block' => array(
+      'variables' => array('block' => NULL),
+      'file' => 'system.admin.inc',
+    ),
+    'admin_block_content' => array(
+      'variables' => array('content' => NULL),
+      'file' => 'system.admin.inc',
+    ),
+    'system_admin_index' => array(
+      'variables' => array('menu_items' => NULL),
+      'file' => 'system.admin.inc',
+    ),
+    'system_powered_by' => array(
+      'variables' => array(),
+    ),
+    'system_compact_link' => array(
+      'variables' => array(),
+    ),
+    'system_date_format_localize_form' => array(
+      'render element' => 'form',
+    ),
+  ));
+}
+
+/**
+ * Implements hook_permission().
+ */
+function system_permission() {
+  return array(
+    'administer modules' => array(
+      'title' => t('Administer modules'),
+    ),
+    'administer site configuration' => array(
+      'title' => t('Administer site configuration'),
+      'restrict access' => TRUE,
+    ),
+    'administer themes' => array(
+      'title' => t('Administer themes'),
+    ),
+    'administer software updates' => array(
+      'title' => t('Administer software updates'),
+      'restrict access' => TRUE,
+    ),
+    'access administration pages' => array(
+      'title' => t('Use the administration pages and help'),
+    ),
+    'access site in maintenance mode' => array(
+      'title' => t('Use the site in maintenance mode'),
+    ),
+    'view the administration theme' => array(
+      'title' => t('View the administration theme'),
+      'description' => t('This is only used when the site is configured to use a separate administration theme on the <a href="@appearance-url">Appearance</a> page.', array('@appearance-url' => url('admin/appearance'))),
+    ),
+    'access site reports' => array(
+      'title' => t('View site reports'),
+    ),
+  );
+}
+
+/**
+ * Implements hook_hook_info().
+ */
+function system_hook_info() {
+  $hooks['token_info'] = array(
+    'group' => 'tokens',
+  );
+  $hooks['token_info_alter'] = array(
+    'group' => 'tokens',
+  );
+  $hooks['tokens'] = array(
+    'group' => 'tokens',
+  );
+  $hooks['tokens_alter'] = array(
+    'group' => 'tokens',
+  );
+
+  return $hooks;
+}
+
+/**
+ * Implements hook_element_info().
+ */
+function system_element_info() {
+  // Top level elements.
+  $types['form'] = array(
+    '#method' => 'post',
+    '#action' => request_uri(),
+    '#theme_wrappers' => array('form'),
+  );
+  $types['page'] = array(
+    '#post_render' => array('drupal_post_render_cache_tags_page_set'),
+    '#show_messages' => TRUE,
+    '#theme' => 'page',
+    '#theme_wrappers' => array('html'),
+  );
+  // By default, we don't want Ajax commands being rendered in the context of an
+  // HTML page, so we don't provide defaults for #theme or #theme_wrappers.
+  // However, modules can set these properties (for example, to provide an HTML
+  // debugging page that displays rather than executes Ajax commands).
+  $types['ajax'] = array(
+    '#header' => TRUE,
+    '#commands' => array(),
+    '#error' => NULL,
+  );
+  $types['html_tag'] = array(
+    '#pre_render' => array('drupal_pre_render_conditional_comments', 'drupal_pre_render_html_tag'),
+    '#attributes' => array(),
+    '#value' => NULL,
+  );
+  $types['styles'] = array(
+    '#items' => array(),
+    '#pre_render' => array('drupal_pre_render_styles'),
+  );
+  $types['scripts'] = array(
+    '#items' => array(),
+    '#pre_render' => array('drupal_pre_render_scripts'),
+  );
+
+  // Input elements.
+  $types['submit'] = array(
+    '#input' => TRUE,
+    '#name' => 'op',
+    '#is_button' => TRUE,
+    '#executes_submit_callback' => TRUE,
+    '#limit_validation_errors' => FALSE,
+    '#process' => array('form_process_button', 'ajax_process_form'),
+    '#pre_render' => array('form_pre_render_button'),
+    '#theme_wrappers' => array('input__submit'),
+  );
+  $types['button'] = array(
+    '#input' => TRUE,
+    '#name' => 'op',
+    '#is_button' => TRUE,
+    '#executes_submit_callback' => FALSE,
+    '#limit_validation_errors' => FALSE,
+    '#process' => array('form_process_button', 'ajax_process_form'),
+    '#pre_render' => array('form_pre_render_button'),
+    '#theme_wrappers' => array('input__button'),
+  );
+  $types['image_button'] = array(
+    '#input' => TRUE,
+    '#is_button' => TRUE,
+    '#executes_submit_callback' => TRUE,
+    '#limit_validation_errors' => FALSE,
+    '#process' => array('form_process_button', 'ajax_process_form'),
+    '#return_value' => TRUE,
+    '#has_garbage_value' => TRUE,
+    '#src' => NULL,
+    '#pre_render' => array('form_pre_render_image_button'),
+    '#theme_wrappers' => array('input__image_button'),
+  );
+  $types['textfield'] = array(
+    '#input' => TRUE,
+    '#size' => 60,
+    '#maxlength' => 128,
+    '#autocomplete_route_name' => FALSE,
+    '#process' => array('form_process_autocomplete', 'ajax_process_form', 'form_process_pattern'),
+    '#pre_render' => array('form_pre_render_textfield'),
+    '#theme' => 'input__textfield',
+    '#theme_wrappers' => array('form_element'),
+  );
+  $types['tel'] = array(
+    '#input' => TRUE,
+    '#size' => 30,
+    '#maxlength' => 128,
+    '#autocomplete_route_name' => FALSE,
+    '#process' => array('form_process_autocomplete', 'ajax_process_form', 'form_process_pattern'),
+    '#pre_render' => array('form_pre_render_tel'),
+    '#theme' => 'input__tel',
+    '#theme_wrappers' => array('form_element'),
+  );
+  $types['email'] = array(
+    '#input' => TRUE,
+    '#size' => 60,
+    // user.module is not loaded in case of early bootstrap errors.
+    '#maxlength' => defined('EMAIL_MAX_LENGTH') ? EMAIL_MAX_LENGTH : 255,
+    '#autocomplete_route_name' => FALSE,
+    '#process' => array('form_process_autocomplete', 'ajax_process_form', 'form_process_pattern'),
+    '#element_validate' => array('form_validate_email'),
+    '#pre_render' => array('form_pre_render_email'),
+    '#theme' => 'input__email',
+    '#theme_wrappers' => array('form_element'),
+  );
+  $types['url'] = array(
+    '#input' => TRUE,
+    '#size' => 60,
+    '#maxlength' => 255,
+    '#autocomplete_route_name' => FALSE,
+    '#process' => array('form_process_autocomplete', 'ajax_process_form', 'form_process_pattern'),
+    '#element_validate' => array('form_validate_url'),
+    '#pre_render' => array('form_pre_render_url'),
+    '#theme' => 'input__url',
+    '#theme_wrappers' => array('form_element'),
+  );
+  $types['search'] = array(
+    '#input' => TRUE,
+    '#size' => 60,
+    '#maxlength' => 128,
+    '#autocomplete_route_name' => FALSE,
+    '#process' => array('form_process_autocomplete', 'ajax_process_form'),
+    '#pre_render' => array('form_pre_render_search'),
+    '#theme' => 'input__search',
+    '#theme_wrappers' => array('form_element'),
+  );
+  $types['number'] = array(
+    '#input' => TRUE,
+    '#step' => 1,
+    '#process' => array('ajax_process_form'),
+    '#element_validate' => array('form_validate_number'),
+    '#pre_render' => array('form_pre_render_number'),
+    '#theme' => 'input__number',
+    '#theme_wrappers' => array('form_element'),
+  );
+  $types['range'] = array(
+    '#input' => TRUE,
+    '#step' => 1,
+    '#min' => 0,
+    '#max' => 100,
+    '#process' => array('ajax_process_form'),
+    '#element_validate' => array('form_validate_number'),
+    '#pre_render' => array('form_pre_render_range'),
+    '#theme' => 'input__range',
+    '#theme_wrappers' => array('form_element'),
+  );
+  $types['color'] = array(
+    '#input' => TRUE,
+    '#process' => array('ajax_process_form'),
+    '#element_validate' => array('form_validate_color'),
+    '#pre_render' => array('form_pre_render_color'),
+    '#theme' => 'input__color',
+    '#theme_wrappers' => array('form_element'),
+  );
+  $types['machine_name'] = array(
+    '#input' => TRUE,
+    '#default_value' => NULL,
+    '#required' => TRUE,
+    '#maxlength' => 64,
+    '#size' => 60,
+    '#autocomplete_route_name' => FALSE,
+    '#process' => array('form_process_machine_name', 'form_process_autocomplete', 'ajax_process_form'),
+    '#element_validate' => array('form_validate_machine_name'),
+    '#pre_render' => array('form_pre_render_textfield'),
+    '#theme' => 'input__textfield',
+    '#theme_wrappers' => array('form_element'),
+  );
+  $types['password'] = array(
+    '#input' => TRUE,
+    '#size' => 60,
+    '#maxlength' => 128,
+    '#process' => array('ajax_process_form', 'form_process_pattern'),
+    '#pre_render' => array('form_pre_render_password'),
+    '#theme' => 'input__password',
+    '#theme_wrappers' => array('form_element'),
+  );
+  $types['password_confirm'] = array(
+    '#input' => TRUE,
+    '#process' => array('form_process_password_confirm', 'user_form_process_password_confirm'),
+    '#theme_wrappers' => array('form_element'),
+  );
+  $types['textarea'] = array(
+    '#input' => TRUE,
+    '#cols' => 60,
+    '#rows' => 5,
+    '#resizable' => 'vertical',
+    '#process' => array('ajax_process_form'),
+    '#theme' => 'textarea',
+    '#theme_wrappers' => array('form_element'),
+  );
+  $types['radios'] = array(
+    '#input' => TRUE,
+    '#process' => array('form_process_radios'),
+    '#theme_wrappers' => array('radios'),
+    '#pre_render' => array('form_pre_render_conditional_form_element'),
+  );
+  $types['radio'] = array(
+    '#input' => TRUE,
+    '#default_value' => NULL,
+    '#process' => array('ajax_process_form'),
+    '#pre_render' => array('form_pre_render_radio'),
+    '#theme' => 'input__radio',
+    '#theme_wrappers' => array('form_element'),
+    '#title_display' => 'after',
+  );
+  $types['checkboxes'] = array(
+    '#input' => TRUE,
+    '#process' => array('form_process_checkboxes'),
+    '#pre_render' => array('form_pre_render_conditional_form_element'),
+    '#theme_wrappers' => array('checkboxes'),
+  );
+  $types['checkbox'] = array(
+    '#input' => TRUE,
+    '#return_value' => 1,
+    '#process' => array('form_process_checkbox', 'ajax_process_form'),
+    '#pre_render' => array('form_pre_render_checkbox'),
+    '#theme' => 'input__checkbox',
+    '#theme_wrappers' => array('form_element'),
+    '#title_display' => 'after',
+  );
+  $types['select'] = array(
+    '#input' => TRUE,
+    '#multiple' => FALSE,
+    '#process' => array('form_process_select', 'ajax_process_form'),
+    '#theme' => 'select',
+    '#theme_wrappers' => array('form_element'),
+    '#options' => array(),
+  );
+  $types['language_select'] = array(
+    '#input' => TRUE,
+    '#default_value' => Language::LANGCODE_NOT_SPECIFIED,
+  );
+  $types['weight'] = array(
+    '#input' => TRUE,
+    '#delta' => 10,
+    '#default_value' => 0,
+    '#process' => array('form_process_weight', 'ajax_process_form'),
+  );
+  $types['date'] = array(
+    '#input' => TRUE,
+    '#theme' => 'date',
+    '#theme_wrappers' => array('form_element'),
+  );
+  $types['file'] = array(
+    '#input' => TRUE,
+    '#multiple' => FALSE,
+    '#process' => array('form_process_file'),
+    '#size' => 60,
+    '#pre_render' => array('form_pre_render_file'),
+    '#theme' => 'input__file',
+    '#theme_wrappers' => array('form_element'),
+  );
+  $types['tableselect'] = array(
+    '#input' => TRUE,
+    '#js_select' => TRUE,
+    '#multiple' => TRUE,
+    '#process' => array('form_process_tableselect'),
+    '#options' => array(),
+    '#empty' => '',
+    '#theme' => 'tableselect',
+  );
+
+  // Form structure.
+  $types['item'] = array(
+    // Forms that show author fields to both anonymous and authenticated users
+    // need to dynamically switch between #type 'textfield' and #type 'item' to
+    // automatically take over the authenticated user's information. Therefore,
+    // we allow #type 'item' to receive input, which is internally assigned by
+    // Form API based on the #default_value or #value properties.
+    '#input' => TRUE,
+    '#markup' => '',
+    '#theme_wrappers' => array('form_element'),
+  );
+  $types['hidden'] = array(
+    '#input' => TRUE,
+    '#process' => array('ajax_process_form'),
+    '#pre_render' => array('form_pre_render_hidden'),
+    '#theme' => 'input__hidden',
+  );
+  $types['token'] = array(
+    '#input' => TRUE,
+    '#pre_render' => array('form_pre_render_hidden'),
+    '#theme' => 'input__hidden',
+  );
+  $types['value'] = array(
+    '#input' => TRUE,
+  );
+  $types['link'] = array(
+    '#pre_render' => array('drupal_pre_render_link'),
+  );
+  $types['fieldset'] = array(
+    '#value' => NULL,
+    '#process' => array('form_process_group', 'ajax_process_form'),
+    '#pre_render' => array('form_pre_render_group'),
+    '#theme_wrappers' => array('fieldset'),
+  );
+  $types['details'] = array(
+    '#collapsed' => FALSE,
+    '#value' => NULL,
+    '#process' => array('form_process_group', 'ajax_process_form'),
+    '#pre_render' => array('form_pre_render_details', 'form_pre_render_group'),
+    '#theme_wrappers' => array('details'),
+  );
+  $types['vertical_tabs'] = array(
+    '#default_tab' => '',
+    '#process' => array('form_process_vertical_tabs'),
+    '#pre_render' => array('form_pre_render_vertical_tabs'),
+    '#theme_wrappers' => array('vertical_tabs', 'form_element'),
+  );
+  $types['dropbutton'] = array(
+    '#pre_render' => array('drupal_pre_render_dropbutton'),
+    '#theme' => 'links__dropbutton',
+  );
+  $types['operations'] = array(
+    '#pre_render' => array('drupal_pre_render_dropbutton'),
+    '#theme' => 'links__dropbutton__operations',
+  );
+
+  $types['container'] = array(
+    '#process' => array('form_process_group', 'form_process_container'),
+    '#pre_render' => array('form_pre_render_group'),
+    '#theme_wrappers' => array('container'),
+  );
+  $types['actions'] = array(
+    '#process' => array('form_pre_render_actions_dropbutton', 'form_process_actions', 'form_process_container'),
+    '#weight' => 100,
+    '#theme_wrappers' => array('container'),
+  );
+
+  $types['table'] = array(
+    '#header' => array(),
+    '#rows' => array(),
+    '#empty' => '',
+    // Properties for tableselect support.
+    '#input' => TRUE,
+    '#tree' => TRUE,
+    '#tableselect' => FALSE,
+    '#multiple' => TRUE,
+    '#js_select' => TRUE,
+    '#value_callback' => 'form_type_table_value',
+    '#process' => array('form_process_table'),
+    '#element_validate' => array('form_validate_table'),
+    // Properties for tabledrag support.
+    // The value is a list of arrays that are passed to drupal_add_tabledrag().
+    // drupal_pre_render_table() prepends the HTML ID of the table to each set
+    // of arguments.
+    // @see drupal_add_tabledrag()
+    '#tabledrag' => array(),
+    // Render properties.
+    '#pre_render' => array('drupal_pre_render_table'),
+    '#theme' => 'table',
+  );
+
+  return $types;
+}
+
+/**
+ * Implements hook_menu().
+ */
+function system_menu() {
+  $items['system/ajax'] = array(
+    'title' => 'AHAH callback',
+    'route_name' => 'system.ajax',
+    'theme callback' => 'ajax_base_page_theme',
+    'type' => MENU_CALLBACK,
+  );
+  $items['admin'] = array(
+    'title' => 'Administration',
+    'route_name' => 'system.admin',
+    'weight' => 9,
+    'menu_name' => 'admin',
+  );
+
+  // Menu items that are basically just menu blocks.
+  $items['admin/structure'] = array(
+    'title' => 'Structure',
+    'description' => 'Administer blocks, content types, menus, etc.',
+    'position' => 'right',
+    'weight' => -8,
+    'route_name' => 'system.admin_structure',
+  );
+  // Appearance.
+  $items['admin/appearance'] = array(
+    'title' => 'Appearance',
+    'description' => 'Select and configure your themes.',
+    'route_name' => 'system.themes_page',
+    'position' => 'left',
+    'weight' => -6,
+  );
+
+  // Modules.
+  $items['admin/modules'] = array(
+    'title' => 'Extend',
+    'description' => 'Add and enable modules to extend site functionality.',
+    'route_name' => 'system.modules_list',
+    'weight' => -2,
+  );
+
+  // Configuration.
+  $items['admin/config'] = array(
+    'title' => 'Configuration',
+    'description' => 'Administer settings.',
+    'route_name' => 'system.admin_config',
+  );
+
+  // Media settings.
+  $items['admin/config/media'] = array(
+    'title' => 'Media',
+    'description' => 'Media tools.',
+    'position' => 'left',
+    'weight' => -10,
+    'route_name' => 'system.admin_config_media',
+  );
+  $items['admin/config/media/file-system'] = array(
+    'title' => 'File system',
+    'description' => 'Tell Drupal where to store uploaded files and how they are accessed.',
+    'route_name' => 'system.file_system_settings',
+    'weight' => -10,
+  );
+  $items['admin/config/media/image-toolkit'] = array(
+    'title' => 'Image toolkit',
+    'description' => 'Choose which image toolkit to use if you have installed optional toolkits.',
+    'route_name' => 'system.image_toolkit_settings',
+    'weight' => 20,
+  );
+
+  // Service settings.
+  $items['admin/config/services'] = array(
+    'title' => 'Web services',
+    'description' => 'Tools related to web services.',
+    'position' => 'right',
+    'weight' => 0,
+    'route_name' => 'system.admin_config_services',
+  );
+  $items['admin/config/services/rss-publishing'] = array(
+    'title' => 'RSS publishing',
+    'description' => 'Configure the site description, the number of items per feed and whether feeds should be titles/teasers/full-text.',
+    'route_name' => 'system.rss_feeds_settings',
+  );
+
+  // Development settings.
+  $items['admin/config/development'] = array(
+    'title' => 'Development',
+    'description' => 'Development tools.',
+    'position' => 'right',
+    'weight' => -10,
+    'route_name' => 'system.admin_config_development',
+  );
+  $items['admin/config/development/maintenance'] = array(
+    'title' => 'Maintenance mode',
+    'description' => 'Take the site offline for maintenance or bring it back online.',
+    'route_name' => 'system.site_maintenance_mode',
+    'weight' => -10,
+  );
+  $items['admin/config/development/performance'] = array(
+    'title' => 'Performance',
+    'description' => 'Enable or disable page caching for anonymous users and set CSS and JS bandwidth optimization options.',
+    'route_name' => 'system.performance_settings',
+    'weight' => -20,
+  );
+  $items['admin/config/development/logging'] = array(
+    'title' => 'Logging and errors',
+    'description' => "Settings for logging and alerts modules. Various modules can route Drupal's system events to different destinations, such as syslog, database, email, etc.",
+    'route_name' => 'system.logging_settings',
+    'weight' => -15,
+  );
+
+  // Regional and date settings.
+  $items['admin/config/regional'] = array(
+    'title' => 'Regional and language',
+    'description' => 'Regional settings, localization and translation.',
+    'position' => 'left',
+    'weight' => -5,
+    'route_name' => 'system.admin_config_regional',
+  );
+  $items['admin/config/regional/settings'] = array(
+    'title' => 'Regional settings',
+    'description' => "Settings for the site's default time zone and country.",
+    'route_name' => 'system.regional_settings',
+    'weight' => -20,
+  );
+  $items['admin/config/regional/date-time'] = array(
+    'title' => 'Date and time formats',
+    'description' => 'Configure display format strings for date and time.',
+    'route_name' => 'system.date_format_list',
+    'weight' => -9,
+  );
+  $items['admin/config/regional/date-time/formats/add'] = array(
+    'title' => 'Add format',
+    'description' => 'Allow users to add additional date formats.',
+    'type' => MENU_LOCAL_ACTION,
+    'route_name' => 'system.date_format_add',
+    'weight' => -10,
+  );
+  $items['admin/config/regional/date-time/formats/manage/%'] = array(
+    'title' => 'Edit date format',
+    'description' => 'Allow users to edit a configured date format.',
+    'route_name' => 'system.date_format_edit',
+  );
+
+  // Search settings.
+  $items['admin/config/search'] = array(
+    'title' => 'Search and metadata',
+    'description' => 'Local site search, metadata and SEO.',
+    'position' => 'left',
+    'weight' => -10,
+    'route_name' => 'system.admin_config_search',
+  );
+
+  // System settings.
+  $items['admin/config/system'] = array(
+    'title' => 'System',
+    'description' => 'General system related configuration.',
+    'position' => 'right',
+    'weight' => -20,
+    'route_name' => 'system.admin_config_system',
+  );
+  $items['admin/config/system/site-information'] = array(
+    'title' => 'Site information',
+    'description' => 'Change site name, e-mail address, slogan, default front page and error pages.',
+    'route_name' => 'system.site_information_settings',
+    'weight' => -20,
+  );
+  $items['admin/config/system/cron'] = array(
+    'title' => 'Cron',
+    'description' => 'Manage automatic site maintenance tasks.',
+    'route_name' => 'system.cron_settings',
+    'weight' => 20,
+  );
+  // Additional categories
+  $items['admin/config/user-interface'] = array(
+    'title' => 'User interface',
+    'description' => 'Tools that enhance the user interface.',
+    'position' => 'right',
+    'route_name' => 'system.admin_config_ui',
+    'weight' => -15,
+  );
+  $items['admin/config/workflow'] = array(
+    'title' => 'Workflow',
+    'description' => 'Content workflow, editorial workflow tools.',
+    'position' => 'right',
+    'weight' => 5,
+    'route_name' => 'system.admin_config_workflow',
+  );
+  $items['admin/config/content'] = array(
+    'title' => 'Content authoring',
+    'description' => 'Settings related to formatting and authoring content.',
+    'position' => 'left',
+    'weight' => -15,
+    'route_name' => 'system.admin_config_content',
+  );
+
+  // Reports.
+  $items['admin/reports'] = array(
+    'title' => 'Reports',
+    'description' => 'View reports, updates, and errors.',
+    'route_name' => 'system.admin_reports',
+    'weight' => 5,
+    'position' => 'left',
+  );
+  $items['admin/reports/status'] = array(
+    'title' => 'Status report',
+    'description' => "Get a status report about your site's operation and any detected problems.",
+    'route_name' => 'system.status',
+  );
+
+  // Default page for batch operations.
+  $items['batch'] = array(
+    'route_name' => 'system.batch_page',
+    'theme callback' => '_system_batch_theme',
+    'type' => MENU_CALLBACK,
+  );
+
+  // Localize date formats.
+  if (\Drupal::moduleHandler()->moduleExists('language')) {
+    $items['admin/config/regional/date-time/locale'] = array(
+      'title' => 'Localize',
+      'description' => 'Configure date formats for each locale',
+      'weight' => -8,
+      'route_name' => 'system.date_format_language_overview',
+    );
+    $items['admin/config/regional/date-time/locale/%/edit'] = array(
+      'title' => 'Localize date formats',
+      'route_name' => 'system.localize_date_format',
+      'description' => 'Configure date formats for each locale',
+    );
+    $items['admin/config/regional/date-time/locale/%/reset'] = array(
+      'title' => 'Reset date formats',
+      'description' => 'Reset localized date formats to global defaults',
+      'route_name' => 'system.date_format_localize_reset',
+    );
+  }
+
+  return $items;
+}
+
+/**
+ * Implements hook_theme_suggestions_HOOK().
+ */
+function system_theme_suggestions_html(array $variables) {
+  return theme_get_suggestions(arg(), 'html');
+}
+
+/**
+ * Implements hook_theme_suggestions_HOOK().
+ */
+function system_theme_suggestions_page(array $variables) {
+  return theme_get_suggestions(arg(), 'page');
+}
+
+/**
+ * Implements hook_theme_suggestions_HOOK().
+ */
+function system_theme_suggestions_maintenance_page(array $variables) {
+  $suggestions = array();
+
+  // Dead databases will show error messages so supplying this template will
+  // allow themers to override the page and the content completely.
+  $offline = defined('MAINTENANCE_MODE');
+  try {
+    drupal_is_front_page();
+  }
+  catch (Exception $e) {
+    // The database is not yet available.
+    $offline = TRUE;
+  }
+  if ($offline) {
+    $suggestions[] = 'maintenance_page__offline';
+  }
+
+  return $suggestions;
+}
+
+/**
+ * Implements hook_theme_suggestions_HOOK().
+ */
+function system_theme_suggestions_region(array $variables) {
+  $suggestions = array();
+  if (!empty($variables['elements']['#region'])) {
+    $suggestions[] = 'region__' . $variables['elements']['#region'];
+  }
+  return $suggestions;
+}
+
+/**
+ * Theme callback for the default batch page.
+ */
+function _system_batch_theme() {
+  // Retrieve the current state of the batch.
+  $batch = &batch_get();
+  if (!$batch && isset($_REQUEST['id'])) {
+    $batch = \Drupal::service('batch.storage')->load($_REQUEST['id']);
+  }
+  // Use the same theme as the page that started the batch.
+  if (!empty($batch['theme'])) {
+    return $batch['theme'];
+  }
+}
+
+/**
+ * Implements hook_library_info().
+ */
+function system_library_info() {
+  // Drupal-specific JavaScript.
+  $libraries['drupal'] = array(
+    'title' => 'Drupal',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/drupal.js' => array('group' => JS_LIBRARY, 'weight' => -18),
+    ),
+    'dependencies' => array(
+      array('system', 'domready'),
+    ),
+  );
+
+  // Drupal settings.
+  $libraries['drupalSettings'] = array(
+    'title' => 'Drupal Settings',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      array('type' => 'setting', 'data' => array()),
+    ),
+  );
+
+  // Drupal's Ajax framework.
+  $libraries['drupal.ajax'] = array(
+    'title' => 'Drupal AJAX',
+    'website' => 'http://api.drupal.org/api/group/ajax/8',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/ajax.js' => array('group' => JS_LIBRARY, 'weight' => 2),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+      array('system', 'drupalSettings'),
+      array('system', 'drupal.progress'),
+      array('system', 'jquery.once'),
+    ),
+  );
+
+  // Drupal's Screen Reader change announcement utility.
+  $libraries['drupal.announce'] = array(
+    'title' => 'Drupal announce',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/announce.js' => array('group' => JS_LIBRARY),
+    ),
+    'dependencies' => array(
+      array('system', 'drupal'),
+      array('system', 'drupal.debounce'),
+    ),
+  );
+
+  // Drupal's batch API.
+  $libraries['drupal.batch'] = array(
+    'title' => 'Drupal batch API',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/batch.js' => array('group' => JS_DEFAULT, 'cache' => FALSE),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+      array('system', 'drupalSettings'),
+      array('system', 'drupal.ajax'),
+      array('system', 'drupal.progress'),
+      array('system', 'jquery.once'),
+    ),
+  );
+
+  // Drupal's progress indicator.
+  $libraries['drupal.progress'] = array(
+    'title' => 'Drupal progress indicator',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/progress.js' => array('group' => JS_DEFAULT),
+    ),
+    'dependencies' => array(
+      array('system', 'drupal'),
+      array('system', 'jquery'),
+      array('system', 'drupalSettings'),
+    ),
+  );
+
+  // Drupal's form library.
+  $libraries['drupal.form'] = array(
+    'title' => 'Drupal form library',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/form.js' => array('group' => JS_LIBRARY, 'weight' => 1),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+      array('system', 'jquery.cookie'),
+      array('system', 'jquery.once'),
+    ),
+  );
+
+  // Drupal's dialog component.
+  $libraries['drupal.dialog'] = array(
+    'title' => 'Drupal Dialog',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/dialog.js' => array('group' => JS_LIBRARY),
+      'core/misc/dialog.position.js' => array('group' => JS_LIBRARY),
+    ),
+    'css' => array(
+      'core/misc/dialog.theme.css' => array('weight' => 1),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+      array('system', 'drupalSettings'),
+      array('system', 'drupal.debounce'),
+      array('system', 'drupal.displace'),
+      array('system', 'jquery.ui.dialog'),
+    ),
+  );
+
+  // Drupal's integration between AJAX and dialogs.
+  $libraries['drupal.dialog.ajax'] = array(
+    'title' => 'Drupal Dialog AJAX',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/dialog.ajax.js' => array('group' => JS_LIBRARY, 'weight' => 3),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+      array('system', 'drupalSettings'),
+      array('system', 'drupal.ajax'),
+      array('system', 'drupal.dialog'),
+    ),
+  );
+
+  // Drupal's states library.
+  $libraries['drupal.states'] = array(
+    'title' => 'Drupal states',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/states.js' => array('group' => JS_LIBRARY, 'weight' => 1),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+      array('system', 'drupalSettings'),
+      array('system', 'jquery.once'),
+    ),
+  );
+
+  // Drupal's tabledrag library.
+  $libraries['drupal.tabledrag'] = array(
+    'title' => 'Drupal tabledrag',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/tabledrag.js' => array('group' => JS_LIBRARY, 'weight' => -1),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'modernizr'),
+      array('system', 'drupal'),
+      array('system', 'drupalSettings'),
+      array('system', 'jquery.once'),
+      array('system', 'jquery.cookie'),
+    ),
+  );
+
+  // Drupal's responsive table API.
+  $libraries['drupal.tableresponsive'] = array(
+    'title' => 'Drupal responsive table API',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/tableresponsive.js' => array('group' => JS_LIBRARY),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+      array('system', 'jquery.once'),
+    ),
+  );
+
+  // Collapsible details.
+  $libraries['drupal.collapse'] = array(
+    'title' => 'Collapsible details',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/collapse.js' => array('group' => JS_DEFAULT),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'modernizr'),
+      array('system', 'drupal'),
+      // collapse.js relies on drupalGetSummary in form.js
+      array('system', 'drupal.form'),
+      array('system', 'jquery.once'),
+    ),
+  );
+
+  // Drupal's autocomplete widget.
+  $libraries['drupal.autocomplete'] = array(
+    'title' => 'Drupal autocomplete',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/autocomplete.js' => array('group' => JS_DEFAULT),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+      array('system', 'drupal.ajax'),
+    ),
+  );
+
+  // A utility that measures and reports viewport offset dimensions from
+  // elements like the toolbar that can potentially displace the positioning of
+  // elements like the overlay.
+  $libraries['drupal.displace'] = array(
+    'title' => 'Drupal displace',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/displace.js' => array('group' => JS_LIBRARY),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+      array('system', 'drupal.debounce'),
+    ),
+  );
+
+  // Manages tab orders in the document.
+  $libraries['drupal.tabbingmanager'] = array(
+    'title' => 'Drupal tabbing manager',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/tabbingmanager.js' => array('group' => JS_LIBRARY),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      // Depends on jQuery UI Core to use the ":tabbable" pseudo selector.
+      array('system', 'jquery.ui.core'),
+      array('system', 'drupal'),
+    ),
+  );
+
+  // A utility function to limit calls to a function with a given time.
+  $libraries['drupal.debounce'] = array(
+    'title' => 'Drupal debounce',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/debounce.js' => array('group' => JS_LIBRARY),
+    ),
+    'dependencies' => array(
+      // @todo remove drupal dependency.
+      array('system', 'drupal'),
+    ),
+  );
+
+  // domReady.
+  $libraries['domready'] = array(
+    'title' => 'domReady',
+    'website' => 'https://github.com/ded/domready',
+    'version' => 'master',
+    'js' => array(
+      'core/assets/vendor/domready/ready.min.js' => array('group' => JS_LIBRARY, 'weight' => -21),
+    ),
+  );
+
+  // jQuery.
+  $libraries['jquery'] = array(
+    'title' => 'jQuery',
+    'website' => 'http://jquery.com',
+    'version' => '2.0.3',
+    'js' => array(
+      'core/assets/vendor/jquery/jquery.js' => array('group' => JS_LIBRARY, 'weight' => -20),
+    ),
+  );
+
+  // jQuery Once.
+  $libraries['jquery.once'] = array(
+    'title' => 'jQuery Once',
+    'website' => 'http://plugins.jquery.com/once/',
+    'version' => '1.2.3',
+    'js' => array(
+      'core/assets/vendor/jquery-once/jquery.once.js' => array('group' => JS_LIBRARY, 'weight' => -19),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+    ),
+  );
+
+  // jQuery Form Plugin.
+  $libraries['jquery.form'] = array(
+    'title' => 'jQuery Form Plugin',
+    'website' => 'http://malsup.com/jquery/form/',
+    'version' => '3.39',
+    'js' => array(
+      'core/assets/vendor/jquery-form/jquery.form.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'jquery.cookie'),
+    ),
+  );
+
+  // jQuery BBQ plugin.
+  $libraries['jquery.bbq'] = array(
+    'title' => 'jQuery BBQ',
+    'website' => 'http://benalman.com/projects/jquery-bbq-plugin/',
+    'version' => '1.3pre',
+    'js' => array(
+      'core/assets/vendor/jquery-bbq/jquery.ba-bbq.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+    ),
+  );
+
+  // Dropbutton.
+  $libraries['drupal.dropbutton'] = array(
+    'title' => 'Dropbutton',
+    'website' => 'http://drupal.org/node/1608878',
+    'version' => '1.0',
+    'js' => array(
+      'core/misc/dropbutton/dropbutton.js' => array(),
+    ),
+    'css' => array(
+      'core/misc/dropbutton/dropbutton.css' => array(),
+      'core/misc/dropbutton/dropbutton.theme.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+      array('system', 'drupalSettings'),
+      array('system', 'jquery.once'),
+    ),
+  );
+
+  // Vertical Tabs.
+  $libraries['drupal.vertical-tabs'] = array(
+    'title' => 'Vertical Tabs',
+    'website' => 'http://drupal.org/node/323112',
+    'version' => '1.0',
+    'js' => array(
+      'core/misc/vertical-tabs.js' => array(),
+    ),
+    'css' => array(
+      'core/misc/vertical-tabs.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+      array('system', 'drupalSettings'),
+      // Vertical tabs relies on drupalGetSummary in form.js
+      array('system', 'drupal.form'),
+    ),
+  );
+
+  // matchMedia polyfill.
+  $libraries['matchmedia'] = array(
+    'title' => 'window.matchMedia polyfill',
+    'website' => 'http://drupal.org/node/1815602',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/matchmedia.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'drupal.debounce'),
+    ),
+  );
+
+  // Farbtastic.
+  $libraries['jquery.farbtastic'] = array(
+    'title' => 'Farbtastic',
+    'website' => 'http://code.google.com/p/farbtastic/',
+    'version' => '1.2',
+    'js' => array(
+      'core/assets/vendor/farbtastic/farbtastic.js' => array(),
+    ),
+    'css' => array(
+      'core/assets/vendor/farbtastic/farbtastic.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+    ),
+  );
+
+  // HTML5 Shiv.
+  $libraries['html5shiv'] = array(
+    'title' => 'html5shiv',
+    'website' => 'https://github.com/aFarkas/html5shiv/',
+    'version' => '3.6.2',
+    'js' => array(
+      'core/assets/vendor/html5shiv/html5.js' => array(
+        'group' => JS_LIBRARY,
+        'weight' => -22,
+        'browsers' => array('IE' => 'lte IE 8', '!IE' => FALSE),
+      ),
+    ),
+  );
+
+  // Modernizr.
+  $libraries['modernizr'] = array(
+    'title' => 'Modernizr',
+    'website' => 'http://modernizr.com/',
+    'version' => '2.6.2',
+    'js' => array(
+      'core/assets/vendor/modernizr/modernizr.min.js' => array(
+        'every_page' => TRUE,
+        'group' => JS_LIBRARY,
+        'preprocess' => 0,
+        'scope' => 'header',
+        'weight' => -21,
+      ),
+    ),
+  );
+
+  // Normalize.
+  $libraries['normalize'] = array(
+    'title' => 'normalize.css',
+    'website' => 'http://git.io/normalize',
+    'version' => '2.1.2',
+    'css' => array(
+      'core/assets/vendor/normalize-css/normalize.css' => array(
+        'every_page' => TRUE,
+        'weight' => CSS_BASE - 20,
+      ),
+    ),
+  );
+
+  // jQuery UI.
+  $libraries['jquery.ui.core'] = array(
+    'title' => 'jQuery UI: Core',
+    'website' => 'http://jqueryui.com',
+    'version' => '1.10.2',
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.core.js' => array('group' => JS_LIBRARY, 'weight' => -11),
+    ),
+    'css' => array(
+      'core/assets/vendor/jquery.ui/themes/base/jquery.ui.core.css' => array(),
+      'core/assets/vendor/jquery.ui/themes/base/jquery.ui.theme.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+    ),
+  );
+  $libraries['jquery.ui.accordion'] = array(
+    'title' => 'jQuery UI: Accordion',
+    'website' => 'http://jqueryui.com/demos/accordion/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.accordion.js' => array(),
+    ),
+    'css' => array(
+      'core/assets/vendor/jquery.ui/themes/base/jquery.ui.accordion.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+      array('system', 'jquery.ui.widget'),
+    ),
+  );
+  $libraries['jquery.ui.autocomplete'] = array(
+    'title' => 'jQuery UI: Autocomplete',
+    'website' => 'http://jqueryui.com/demos/autocomplete/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.autocomplete.js' => array(),
+    ),
+    'css' => array(
+      'core/assets/vendor/jquery.ui/themes/base/jquery.ui.autocomplete.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+      array('system', 'jquery.ui.widget'),
+      array('system', 'jquery.ui.position'),
+      array('system', 'jquery.ui.menu'),
+    ),
+  );
+  $libraries['jquery.ui.button'] = array(
+    'title' => 'jQuery UI: Button',
+    'website' => 'http://jqueryui.com/demos/button/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.button.js' => array(),
+    ),
+    'css' => array(
+      'core/assets/vendor/jquery.ui/themes/base/jquery.ui.button.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+      array('system', 'jquery.ui.widget'),
+    ),
+  );
+  $libraries['jquery.ui.datepicker'] = array(
+    'title' => 'jQuery UI: Date Picker',
+    'website' => 'http://jqueryui.com/demos/datepicker/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.datepicker.js' => array(),
+    ),
+    'css' => array(
+      'core/assets/vendor/jquery.ui/themes/base/jquery.ui.datepicker.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+    ),
+  );
+  $libraries['jquery.ui.dialog'] = array(
+    'title' => 'jQuery UI: Dialog',
+    'website' => 'http://jqueryui.com/demos/dialog/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.dialog.js' => array(),
+    ),
+    'css' => array(
+      'core/assets/vendor/jquery.ui/themes/base/jquery.ui.dialog.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+      array('system', 'jquery.ui.widget'),
+      array('system', 'jquery.ui.button'),
+      array('system', 'jquery.ui.draggable'),
+      array('system', 'jquery.ui.mouse'),
+      array('system', 'jquery.ui.position'),
+      array('system', 'jquery.ui.resizable'),
+    ),
+  );
+  $libraries['jquery.ui.draggable'] = array(
+    'title' => 'jQuery UI: Draggable',
+    'website' => 'http://jqueryui.com/demos/draggable/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.draggable.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+      array('system', 'jquery.ui.mouse'),
+      array('system', 'jquery.ui.widget'),
+    ),
+  );
+  $libraries['jquery.ui.droppable'] = array(
+    'title' => 'jQuery UI: Droppable',
+    'website' => 'http://jqueryui.com/demos/droppable/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.droppable.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+      array('system', 'jquery.ui.widget'),
+      array('system', 'jquery.ui.mouse'),
+      array('system', 'jquery.ui.draggable'),
+    ),
+  );
+  $libraries['jquery.ui.menu'] = array(
+    'title' => 'jQuery UI: Mouse',
+    'website' => 'http://docs.jquery.com/UI/Menu',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.menu.js' => array(),
+    ),
+    'css' => array(
+      'core/assets/vendor/jquery.ui/themes/base/jquery.ui.menu.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+      array('system', 'jquery.ui.widget'),
+    ),
+  );
+  $libraries['jquery.ui.mouse'] = array(
+    'title' => 'jQuery UI: Mouse',
+    'website' => 'http://docs.jquery.com/UI/Mouse',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.mouse.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.widget'),
+    ),
+  );
+  $libraries['jquery.ui.position'] = array(
+    'title' => 'jQuery UI: Position',
+    'website' => 'http://jqueryui.com/demos/position/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.position.js' => array(),
+    ),
+  );
+  $libraries['jquery.ui.progressbar'] = array(
+    'title' => 'jQuery UI: Progress Bar',
+    'website' => 'http://jqueryui.com/demos/progressbar/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.progressbar.js' => array(),
+    ),
+    'css' => array(
+      'core/assets/vendor/jquery.ui/themes/base/jquery.ui.progressbar.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+      array('system', 'jquery.ui.widget'),
+    ),
+  );
+  $libraries['jquery.ui.resizable'] = array(
+    'title' => 'jQuery UI: Resizable',
+    'website' => 'http://jqueryui.com/demos/resizable/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.resizable.js' => array(),
+    ),
+    'css' => array(
+      'core/assets/vendor/jquery.ui/themes/base/jquery.ui.resizable.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+      array('system', 'jquery.ui.widget'),
+      array('system', 'jquery.ui.mouse'),
+    ),
+  );
+  $libraries['jquery.ui.selectable'] = array(
+    'title' => 'jQuery UI: Selectable',
+    'website' => 'http://jqueryui.com/demos/selectable/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.selectable.js' => array(),
+    ),
+    'css' => array(
+      'core/assets/vendor/jquery.ui/themes/base/jquery.ui.selectable.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+      array('system', 'jquery.ui.mouse'),
+      array('system', 'jquery.ui.widget'),
+    ),
+  );
+  $libraries['jquery.ui.slider'] = array(
+    'title' => 'jQuery UI: Slider',
+    'website' => 'http://jqueryui.com/demos/slider/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.slider.js' => array(),
+    ),
+    'css' => array(
+      'core/assets/vendor/jquery.ui/themes/base/jquery.ui.slider.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+      array('system', 'jquery.ui.mouse'),
+      array('system', 'jquery.ui.widget'),
+    ),
+  );
+  $libraries['jquery.ui.sortable'] = array(
+    'title' => 'jQuery UI: Sortable',
+    'website' => 'http://jqueryui.com/demos/sortable/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.sortable.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+      array('system', 'jquery.ui.mouse'),
+      array('system', 'jquery.ui.widget'),
+    ),
+  );
+  $libraries['jquery.ui.spinner'] = array(
+    'title' => 'jQuery UI: Spinner',
+    'website' => 'http://jqueryui.com/demos/spinner/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.spinner.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+      array('system', 'jquery.ui.widget'),
+      array('system', 'jquery.ui.button'),
+    ),
+  );
+  $libraries['jquery.ui.tabs'] = array(
+    'title' => 'jQuery UI: Tabs',
+    'website' => 'http://jqueryui.com/demos/tabs/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.tabs.js' => array(),
+    ),
+    'css' => array(
+      'core/assets/vendor/jquery.ui/themes/base/jquery.ui.tabs.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+      array('system', 'jquery.ui.widget'),
+    ),
+  );
+  $libraries['jquery.ui.tooltip'] = array(
+    'title' => 'jQuery UI: Tooltip',
+    'website' => 'http://jqueryui.com/demos/tooltip/',
+    'version' => $libraries['jquery.ui.core']['version'],
+      'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.tooltip.js' => array(),
+    ),
+    'css' => array(
+      'core/assets/vendor/jquery.ui/themes/base/jquery.ui.tooltip.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+      array('system', 'jquery.ui.widget'),
+      array('system', 'jquery.ui.position'),
+    ),
+  );
+  $libraries['jquery.ui.widget'] = array(
+    'title' => 'jQuery UI: Widget',
+    'website' => 'http://docs.jquery.com/UI/Widget',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.ui.widget.js' => array('group' => JS_LIBRARY, 'weight' => -10),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+    ),
+  );
+  $libraries['jquery.effects.core'] = array(
+    'title' => 'jQuery UI: Effects',
+    'website' => 'http://jqueryui.com/demos/effect/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.effects.core.js' => array('group' => JS_LIBRARY, 'weight' => -9),
+    ),
+  );
+  $libraries['jquery.effects.blind'] = array(
+    'title' => 'jQuery UI: Effects Blind',
+    'website' => 'http://jqueryui.com/demos/effect/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.effects.blind.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.effects.core'),
+    ),
+  );
+  $libraries['jquery.effects.bounce'] = array(
+    'title' => 'jQuery UI: Effects Bounce',
+    'website' => 'http://jqueryui.com/demos/effect/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.effects.bounce.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.effects.core'),
+    ),
+  );
+  $libraries['jquery.effects.clip'] = array(
+    'title' => 'jQuery UI: Effects Clip',
+    'website' => 'http://jqueryui.com/demos/effect/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.effects.clip.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.effects.core'),
+    ),
+  );
+  $libraries['jquery.effects.drop'] = array(
+    'title' => 'jQuery UI: Effects Drop',
+    'website' => 'http://jqueryui.com/demos/effect/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.effects.drop.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.effects.core'),
+    ),
+  );
+  $libraries['jquery.effects.explode'] = array(
+    'title' => 'jQuery UI: Effects Explode',
+    'website' => 'http://jqueryui.com/demos/effect/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.effects.explode.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.effects.core'),
+    ),
+  );
+  $libraries['jquery.effects.fade'] = array(
+    'title' => 'jQuery UI: Effects Fade',
+    'website' => 'http://jqueryui.com/demos/effect/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.effects.fade.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.effects.core'),
+    ),
+  );
+  $libraries['jquery.effects.fold'] = array(
+    'title' => 'jQuery UI: Effects Fold',
+    'website' => 'http://jqueryui.com/demos/effect/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.effects.fold.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.effects.core'),
+    ),
+  );
+  $libraries['jquery.effects.highlight'] = array(
+    'title' => 'jQuery UI: Effects Highlight',
+    'website' => 'http://jqueryui.com/demos/effect/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.effects.highlight.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.effects.core'),
+    ),
+  );
+  $libraries['jquery.effects.pulsate'] = array(
+    'title' => 'jQuery UI: Effects Pulsate',
+    'website' => 'http://jqueryui.com/demos/effect/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.effects.pulsate.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.effects.core'),
+    ),
+  );
+  $libraries['jquery.effects.scale'] = array(
+    'title' => 'jQuery UI: Effects Scale',
+    'website' => 'http://jqueryui.com/demos/effect/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.effects.scale.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.effects.core'),
+    ),
+  );
+  $libraries['jquery.effects.shake'] = array(
+    'title' => 'jQuery UI: Effects Shake',
+    'website' => 'http://jqueryui.com/demos/effect/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.effects.shake.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.effects.core'),
+    ),
+  );
+  $libraries['jquery.effects.slide'] = array(
+    'title' => 'jQuery UI: Effects Slide',
+    'website' => 'http://jqueryui.com/demos/effect/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.effects.slide.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.effects.core'),
+    ),
+  );
+  $libraries['jquery.effects.transfer'] = array(
+    'title' => 'jQuery UI: Effects Transfer',
+    'website' => 'http://jqueryui.com/demos/effect/',
+    'version' => $libraries['jquery.ui.core']['version'],
+    'js' => array(
+      'core/assets/vendor/jquery.ui/ui/jquery.effects.transfer.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.effects.core'),
+    ),
+  );
+
+  // Touch Punch for jQuery UI touch support.
+  $libraries['jquery.ui.touch-punch'] = array(
+    'title' => 'jQuery UI Touch Punch',
+    'website' => 'http://jqueryui.com/demos/effect/',
+    'version' => '0.2.2',
+    'js' => array(
+      'core/assets/vendor/jquery-ui-touch-punch/jquery.ui.touch-punch.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.core'),
+    ),
+  );
+
+  // Underscore.
+  $libraries['underscore'] = array(
+    'title' => 'Underscore.js',
+    'website' => 'http://underscorejs.org/',
+    'version' => '1.4.0',
+    'js' => array(
+      'core/assets/vendor/underscore/underscore.js' => array('group' => JS_LIBRARY, 'weight' => -20),
+    ),
+  );
+
+  // Backbone.
+  $libraries['backbone'] = array(
+    'title' => 'Backbone.js',
+    'website' => 'http://backbonejs.org/',
+    'version' => '0.9.2',
+    'js' => array(
+      'core/assets/vendor/backbone/backbone.js' => array('group' => JS_LIBRARY, 'weight' => -19),
+    ),
+    'dependencies' => array(
+      array('system', 'underscore'),
+    ),
+  );
+
+  // Cookie.
+  $libraries['jquery.cookie'] = array(
+    'title' => 'Cookie',
+    'website' => 'http://plugins.jquery.com/project/cookie',
+    'version' => $libraries['jquery.ui.core']['version'], // Shipped with jQuery UI.
+    'js' => array(
+      'core/assets/vendor/jquery.ui/external/jquery.cookie.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+    ),
+  );
+  $libraries['drupal.tableselect'] = array(
+    'title' => 'Tableselect',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/tableselect.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'drupal'),
+      array('system', 'jquery'),
+    ),
+  );
+  $libraries['drupal.tableheader'] = array(
+    'title' => 'Table header',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/tableheader.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+      array('system', 'drupalSettings'),
+      array('system', 'jquery.once'),
+      array('system', 'drupal.displace'),
+    ),
+  );
+  $libraries['drupal.timezone'] = array(
+    'title' => 'Timezone',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/timezone.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+    ),
+  );
+  $libraries['drupal.machine-name'] = array(
+    'title' => 'Machine name',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      'core/misc/machine-name.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'jquery.once'),
+      array('system', 'drupal'),
+      array('system', 'drupalSettings'),
+    ),
+  );
+
+  $libraries['drupal.system'] = array(
+    'title' => 'System',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      drupal_get_path('module', 'system') . '/system.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+      array('system', 'drupalSettings'),
+      array('system', 'jquery.once'),
+    ),
+  );
+  $libraries['drupal.system.modules'] = array(
+    'title' => 'System modules',
+    'version' => \Drupal::VERSION,
+    'js' => array(
+      drupal_get_path('module', 'system') . '/system.modules.js' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+      array('system', 'jquery.once'),
+    ),
+  );
+
+  return $libraries;
+}
+
+/**
+ * Implements hook_stream_wrappers().
+ */
+function system_stream_wrappers() {
+  $wrappers = array(
+    'public' => array(
+      'name' => t('Public files'),
+      'class' => 'Drupal\Core\StreamWrapper\PublicStream',
+      'description' => t('Public local files served by the webserver.'),
+      'type' => STREAM_WRAPPERS_LOCAL_NORMAL,
+    ),
+    'temporary' => array(
+      'name' => t('Temporary files'),
+      'class' => 'Drupal\Core\StreamWrapper\TemporaryStream',
+      'description' => t('Temporary local files for upload and previews.'),
+      'type' => STREAM_WRAPPERS_LOCAL_HIDDEN,
+    ),
+  );
+
+  // Only register the private file stream wrapper if a file path has been set.
+  if (\Drupal::config('system.file')->get('path.private')) {
+    $wrappers['private'] = array(
+      'name' => t('Private files'),
+      'class' => 'Drupal\Core\StreamWrapper\PrivateStream',
+      'description' => t('Private local files served by Drupal.'),
+      'type' => STREAM_WRAPPERS_LOCAL_NORMAL,
+    );
+  }
+
+  return $wrappers;
+}
+
+/**
+ * Menu item access callback - only enabled themes can be accessed.
+ */
+function _system_themes_access($theme) {
+  return user_access('administer themes') && drupal_theme_access($theme);
+}
+
+/**
+ * @defgroup authorize Authorized operations
+ * @{
+ * Functions to run operations with elevated privileges via authorize.php.
+ *
+ * Because of the Update manager functionality included in Drupal core, there
+ * is a mechanism for running operations with elevated file system privileges,
+ * the top-level authorize.php script. This script runs at a reduced Drupal
+ * bootstrap level so that it is not reliant on the entire site being
+ * functional. The operations use a FileTransfer class to manipulate code
+ * installed on the system as the user that owns the files, not the user that
+ * the httpd is running as.
+ *
+ * The first setup is to define a callback function that should be authorized
+ * to run with the elevated privileges. This callback should take a
+ * FileTransfer as its first argument, although you can define an array of
+ * other arguments it should be invoked with. The callback should be placed in
+ * a separate .inc file that will be included by authorize.php.
+ *
+ * To run the operation, certain data must be saved into the SESSION, and then
+ * the flow of control should be redirected to the authorize.php script. There
+ * are two ways to do this, either to call system_authorized_run() directly,
+ * or to call system_authorized_init() and then redirect to authorize.php,
+ * using the URL from system_authorized_get_url(). Redirecting yourself is
+ * necessary when your authorized operation is being triggered by a form
+ * submit handler, since calling redirecting in a submit handler is a bad
+ * idea, and you should instead set $form_state['redirect'].
+ *
+ * Once the SESSION is setup for the operation and the user is redirected to
+ * authorize.php, they will be prompted for their connection credentials (core
+ * provides FTP and SSH by default, although other connection classes can be
+ * added via contributed modules). With valid credentials, authorize.php will
+ * instantiate the appropriate FileTransfer object, and then invoke the
+ * desired operation passing in that object. The authorize.php script can act
+ * as a Batch API processing page, if the operation requires a batch.
+ *
+ * @see authorize.php
+ * @see \Drupal\Core\FileTransfer\FileTransfer
+ * @see hook_filetransfer_info()
+ */
+
+/**
+ * Setup a given callback to run via authorize.php with elevated privileges.
+ *
+ * To use authorize.php, certain variables must be stashed into $_SESSION. This
+ * function sets up all the necessary $_SESSION variables. The calling function
+ * should then redirect to authorize.php, using the full path returned by
+ * system_authorized_get_url(). That initiates the workflow that will eventually
+ * lead to the callback being invoked. The callback will be invoked at a low
+ * bootstrap level, without all modules being invoked, so it needs to be careful
+ * not to assume any code exists. Example (system_authorized_run()):
+ * @code
+ *   system_authorized_init($callback, $file, $arguments, $page_title);
+ *   return new RedirectResponse(system_authorized_get_url());
+ * @endcode
+ * Example (update_manager_install_form_submit()):
+ * @code
+ *  system_authorized_init('update_authorize_run_install',
+ *    drupal_get_path('module', 'update') . '/update.authorize.inc',
+ *    $arguments, t('Update manager'));
+ *  $form_state['redirect'] = system_authorized_get_url();
+ * @endcode
+ *
+ * @param $callback
+ *   The name of the function to invoke once the user authorizes the operation.
+ * @param $file
+ *   The full path to the file where the callback function is implemented.
+ * @param $arguments
+ *   Optional array of arguments to pass into the callback when it is invoked.
+ *   Note that the first argument to the callback is always the FileTransfer
+ *   object created by authorize.php when the user authorizes the operation.
+ * @param $page_title
+ *   Optional string to use as the page title once redirected to authorize.php.
+ * @return
+ *   Nothing, this function just initializes variables in the user's session.
+ */
+function system_authorized_init($callback, $file, $arguments = array(), $page_title = NULL) {
+  // First, figure out what file transfer backends the site supports, and put
+  // all of those in the SESSION so that authorize.php has access to all of
+  // them via the class autoloader, even without a full bootstrap.
+  $_SESSION['authorize_filetransfer_info'] = drupal_get_filetransfer_info();
+
+  // Now, define the callback to invoke.
+  $_SESSION['authorize_operation'] = array(
+    'callback' => $callback,
+    'file' => $file,
+    'arguments' => $arguments,
+  );
+
+  if (isset($page_title)) {
+    $_SESSION['authorize_operation']['page_title'] = $page_title;
+  }
+}
+
+/**
+ * Return the URL for the authorize.php script.
+ *
+ * @param array $options
+ *   Optional array of options to pass to url().
+ * @return
+ *   The full URL to authorize.php, using HTTPS if available.
+ *
+ * @see system_authorized_init()
+ */
+function system_authorized_get_url(array $options = array()) {
+  global $base_url;
+  // Force HTTPS if available, regardless of what the caller specifies.
+  $options['https'] = TRUE;
+  // Prefix with $base_url so url() treats it as an external link.
+  return url($base_url . '/core/authorize.php', $options);
+}
+
+/**
+ * Returns the URL for the authorize.php script when it is processing a batch.
+ *
+ * @param array $options
+ *   Optional array of options to pass to url().
+ */
+function system_authorized_batch_processing_url(array $options = array()) {
+  $options['query'] = array('batch' => '1');
+  return system_authorized_get_url($options);
+}
+
+/**
+ * Setup and invoke an operation using authorize.php.
+ *
+ * @see system_authorized_init()
+ */
+function system_authorized_run($callback, $file, $arguments = array(), $page_title = NULL) {
+  system_authorized_init($callback, $file, $arguments, $page_title);
+  return new RedirectResponse(system_authorized_get_url());
+}
+
+/**
+ * Use authorize.php to run batch_process().
+ *
+ * @see batch_process()
+ */
+function system_authorized_batch_process() {
+  $finish_url = system_authorized_get_url();
+  $process_url = system_authorized_batch_processing_url();
+  return batch_process($finish_url, $process_url);
+}
+
+/**
+ * @} End of "defgroup authorize".
+ */
+
+/**
+ * Implements hook_updater_info().
+ */
+function system_updater_info() {
+  return array(
+    'module' => array(
+      'class' => 'Drupal\Core\Updater\Module',
+      'name' => t('Update modules'),
+      'weight' => 0,
+    ),
+    'theme' => array(
+      'class' => 'Drupal\Core\Updater\Theme',
+      'name' => t('Update themes'),
+      'weight' => 0,
+    ),
+  );
+}
+
+/**
+ * Implements hook_filetransfer_info().
+ */
+function system_filetransfer_info() {
+  $backends = array();
+
+  // This is the default, will be available on most systems.
+  if (function_exists('ftp_connect')) {
+    $backends['ftp'] = array(
+      'title' => t('FTP'),
+      'class' => 'Drupal\Core\FileTransfer\FTP',
+      'weight' => 0,
+    );
+  }
+
+  // SSH2 lib connection is only available if the proper PHP extension is
+  // installed.
+  if (function_exists('ssh2_connect')) {
+    $backends['ssh'] = array(
+      'title' => t('SSH'),
+      'class' => 'Drupal\Core\FileTransfer\SSH',
+      'weight' => 20,
+    );
+  }
+  return $backends;
+}
+
+/**
+ * Implements hook_page_build().
+ */
+function system_page_build(&$page) {
+  // Note: ensure the same CSS is loaded in _drupal_maintenance_theme().
+  $page['#attached']['library'][] = array('system', 'normalize');
+  $path = drupal_get_path('module', 'system');
+  // Adjust the weights to load these early.
+  $page['#attached']['css'][$path . '/css/system.module.css'] = array('weight' => CSS_COMPONENT - 10, 'every_page' => TRUE);
+  $page['#attached']['css'][$path . '/css/system.theme.css'] = array('weight' => CSS_SKIN - 10, 'every_page' => TRUE);
+  if (path_is_admin(current_path())) {
+    $page['#attached']['css'][$path . '/css/system.admin.css'] = array('weight' => CSS_COMPONENT - 10);
+  }
+}
+
+/**
+ * Implements hook_custom_theme().
+ */
+function system_custom_theme() {
+  if (drupal_container()->isScopeActive('request')) {
+    $request = \Drupal::request();
+    $path = $request->attributes->get('_system_path');
+    if (user_access('view the administration theme') && path_is_admin($path)) {
+      return \Drupal::config('system.theme')->get('admin');
+    }
+  }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function system_form_user_form_alter(&$form, &$form_state) {
+  if (\Drupal::config('system.date')->get('timezone.user.configurable')) {
+    system_user_timezone($form, $form_state);
+  }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function system_form_user_register_form_alter(&$form, &$form_state) {
+  $config = \Drupal::config('system.date');
+  if ($config->get('timezone.user.configurable') && $config->get('timezone.user.default') == DRUPAL_USER_TIMEZONE_SELECT) {
+    system_user_timezone($form, $form_state);
+  }
+}
+
+/**
+ * Implements hook_user_presave().
+ */
+function system_user_presave(UserInterface $account) {
+  $config = \Drupal::config('system.date');
+  if ($config->get('timezone.user.configurable') && !$account->getTimeZone() && !$config->get('timezone.user.default')) {
+    $account->timezone = $config->get('timezone.default');
+  }
+}
+
+/**
+ * Implements hook_user_login().
+ */
+function system_user_login($account) {
+  $config = \Drupal::config('system.date');
+  // If the user has a NULL time zone, notify them to set a time zone.
+  if (!$account->getTimezone() && $config->get('timezone.user.configurable') && $config->get('timezone.user.warn')) {
+    drupal_set_message(t('Configure your <a href="@user-edit">account time zone setting</a>.', array('@user-edit' => url("user/$account->id()/edit", array('query' => drupal_get_destination(), 'fragment' => 'edit-timezone')))));
+  }
+}
+
+/**
+ * Add the time zone field to the user edit and register forms.
+ */
+function system_user_timezone(&$form, &$form_state) {
+  global $user;
+
+  $account = $form_state['controller']->getEntity();
+  $form['timezone'] = array(
+    '#type' => 'details',
+    '#title' => t('Locale settings'),
+    '#weight' => 6,
+  );
+  $form['timezone']['timezone'] = array(
+    '#type' => 'select',
+    '#title' => t('Time zone'),
+    '#default_value' => $account->getTimezone() ? $account->getTimezone() : \Drupal::config('system.date')->get('timezone.default'),
+    '#options' => system_time_zones($account->id() != $user->id()),
+    '#description' => t('Select the desired local time and time zone. Dates and times throughout this site will be displayed using this time zone.'),
+  );
+  if (!$account->getTimezone() && $account->id() == $user->id() && empty($form_state['input']['timezone'])) {
+    $form['timezone']['#description'] = t('Your time zone setting will be automatically detected if possible. Confirm the selection and click save.');
+    $form['timezone']['timezone']['#attributes'] = array('class' => array('timezone-detect'));
+    drupal_add_library('system', 'drupal.timezone');
+  }
+}
+
+/**
+ * Implements hook_preprocess_HOOK() for block templates.
+ */
+function system_preprocess_block(&$variables) {
+  // Derive the base plugin ID.
+  // @todo Clean up when http://drupal.org/node/1874498 lands.
+  list($plugin_id, $derivative) = explode(':', $variables['plugin_id'] . ':');
+  switch ($plugin_id) {
+    case 'system_powered_by_block':
+      $variables['attributes']['role'] = 'complementary';
+      break;
+
+    case 'system_help_block':
+      $variables['attributes']['role'] = 'complementary';
+      break;
+
+    case 'system_menu_block':
+      $menus = menu_list_system_menus();
+      if (isset($menus[$derivative])) {
+        $variables['attributes']['role'] = 'navigation';
+        $variables['attributes']['class'][] = 'block-menu';
+      }
+      break;
+  }
+}
+
+/**
+ * Checks the existence of the directory specified in $form_element.
+ *
+ * This function is called from the system_settings form to check all core
+ * file directories (file_public_path, file_private_path, file_temporary_path).
+ *
+ * @param $form_element
+ *   The form element containing the name of the directory to check.
+ */
+function system_check_directory($form_element) {
+  $directory = $form_element['#value'];
+  if (strlen($directory) == 0) {
+    return $form_element;
+  }
+
+  if (!is_dir($directory) && !drupal_mkdir($directory, NULL, TRUE)) {
+    // If the directory does not exists and cannot be created.
+    form_set_error($form_element['#parents'][0], t('The directory %directory does not exist and could not be created.', array('%directory' => $directory)));
+    watchdog('file system', 'The directory %directory does not exist and could not be created.', array('%directory' => $directory), WATCHDOG_ERROR);
+  }
+
+  if (is_dir($directory) && !is_writable($directory) && !drupal_chmod($directory)) {
+    // If the directory is not writable and cannot be made so.
+    form_set_error($form_element['#parents'][0], t('The directory %directory exists but is not writable and could not be made writable.', array('%directory' => $directory)));
+    watchdog('file system', 'The directory %directory exists but is not writable and could not be made writable.', array('%directory' => $directory), WATCHDOG_ERROR);
+  }
+  elseif (is_dir($directory)) {
+    if ($form_element['#name'] == 'file_public_path') {
+      // Create public .htaccess file.
+      file_save_htaccess($directory, FALSE);
+    }
+    else {
+      // Create private .htaccess file.
+      file_save_htaccess($directory);
+    }
+  }
+
+  return $form_element;
+}
+
+/**
+ * Returns an array of information about enabled modules or themes.
+ *
+ * This function returns the contents of the .info.yml file for each installed
+ * module or theme.
+ *
+ * @param $type
+ *   Either 'module' or 'theme'.
+ * @param $name
+ *   (optional) The name of a module or theme whose information shall be
+ *   returned. If omitted, all records for the provided $type will be returned.
+ *   If $name does not exist in the provided $type or is not enabled, an empty
+ *   array will be returned.
+ *
+ * @return
+ *   An associative array of module or theme information keyed by name, or only
+ *   information for $name, if given. If no records are available, an empty
+ *   array is returned.
+ *
+ * @see system_rebuild_module_data()
+ * @see system_rebuild_theme_data()
+ */
+function system_get_info($type, $name = NULL) {
+  $info = array();
+  if ($type == 'module') {
+    $data = system_rebuild_module_data();
+    foreach (\Drupal::moduleHandler()->getModuleList() as $module => $filename) {
+      if (isset($data[$module])) {
+        $info[$module] = $data[$module]->info;
+      }
+    }
+  }
+  else {
+    $list = system_list($type);
+    foreach ($list as $shortname => $item) {
+      if (!empty($item->status)) {
+        $info[$shortname] = $item->info;
+      }
+    }
+  }
+  if (isset($name)) {
+    return isset($info[$name]) ? $info[$name] : array();
+  }
+  return $info;
+}
+
+/**
+ * Return .info.yml data for modules.
+ *
+ * @param string $property
+ *   The .info.yml property to retrieve.
+ *
+ * @return array
+ *   An array keyed by module name, with the .info.yml file property as values.
+ *   Only modules with the property specified in their .info.yml file will be
+ *   returned.
+ *
+ * @see \Drupal\Core\Utility\ModuleInfo
+ */
+function system_get_module_info($property) {
+  static $info;
+  if (!isset($info)) {
+    $info = new ModuleInfo('system_info', 'cache');
+  }
+  return $info[$property];
+}
+
+/**
+ * Helper function to scan and collect module .info.yml data.
+ *
+ * @return
+ *   An associative array of module information.
+ */
+function _system_rebuild_module_data() {
+  // Find modules
+  $modules = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0);
+
+  // Find installation profiles.
+  $profiles = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.profile$/', 'profiles', 'name', 0);
+
+  // Include the installation profile in modules that are loaded.
+  $profile = drupal_get_profile();
+  $modules[$profile] = $profiles[$profile];
+
+  // Installation profile hooks are always executed last.
+  $modules[$profile]->weight = 1000;
+
+  // Set defaults for module info.
+  $defaults = array(
+    'dependencies' => array(),
+    'description' => '',
+    'package' => 'Other',
+    'version' => NULL,
+    'php' => DRUPAL_MINIMUM_PHP,
+    'files' => array(),
+  );
+
+  // Read info files for each module.
+  foreach ($modules as $key => $module) {
+    // The module system uses the key 'filename' instead of 'uri' so copy the
+    // value so it will be used by the modules system.
+    $modules[$key]->filename = $module->uri;
+
+    // Look for the info file.
+    $module->info = \Drupal::service('info_parser')->parse(dirname($module->uri) . '/' . $module->name . '.info.yml');
+
+    // Skip modules/profiles that don't provide info or have the wrong type.
+    if (empty($module->info) || !isset($module->info['type']) || !in_array($module->info['type'], array('module', 'profile'))) {
+      unset($modules[$key]);
+      continue;
+    }
+
+    // Add the info file modification time, so it becomes available for
+    // contributed modules to use for ordering module lists.
+    $module->info['mtime'] = filemtime(dirname($module->uri) . '/' . $module->name . '.info.yml');
+
+    // Merge in defaults and save.
+    $modules[$key]->info = $module->info + $defaults;
+
+    // Installation profiles are hidden by default, unless explicitly specified
+    // otherwise in the .info.yml file.
+    if ($key == $profile && !isset($modules[$key]->info['hidden'])) {
+      $modules[$key]->info['hidden'] = TRUE;
+    }
+
+    // Invoke hook_system_info_alter() to give installed modules a chance to
+    // modify the data in the .info.yml files if necessary.
+    $type = 'module';
+    drupal_alter('system_info', $modules[$key]->info, $modules[$key], $type);
+  }
+
+  if (isset($modules[$profile])) {
+    // The installation profile is required, if it's a valid module.
+    $modules[$profile]->info['required'] = TRUE;
+    // Add a default distribution name if the profile did not provide one. This
+    // matches the default value used in install_profile_info().
+    if (!isset($modules[$profile]->info['distribution_name'])) {
+      $modules[$profile]->info['distribution_name'] = 'Drupal';
+    }
+  }
+
+  return $modules;
+}
+
+/**
+ * Rebuild, save, and return data about all currently available modules.
+ *
+ * @return
+ *   Array of all available modules and their data.
+ */
+function system_rebuild_module_data() {
+  $modules_cache = &drupal_static(__FUNCTION__);
+  // Only rebuild once per request. $modules and $modules_cache cannot be
+  // combined into one variable, because the $modules_cache variable is reset by
+  // reference from system_list_reset() during the rebuild.
+  if (!isset($modules_cache)) {
+    $modules = _system_rebuild_module_data();
+    $files = array();
+    ksort($modules);
+    // Add name, status, weight, and schema version.
+    $installed_modules = (array) \Drupal::config('system.module')->get('enabled');
+    foreach ($modules as $module => $record) {
+      $record->name = $module;
+      $record->weight = isset($installed_modules[$module]) ? $installed_modules[$module] : 0;
+      $record->status = (int) isset($installed_modules[$module]);
+      $record->schema_version = SCHEMA_UNINSTALLED;
+      $files[$module] = $record->filename;
+    }
+    $modules = \Drupal::moduleHandler()->buildModuleDependencies($modules);
+    $modules_cache = $modules;
+
+    // Store filenames to allow system_list() and drupal_get_filename() to
+    // retrieve them without having to rebuild or scan the filesystem.
+    \Drupal::state()->set('system.module.files', $files);
+  }
+  return $modules_cache;
+}
+
+/**
+ * Helper function to scan and collect theme .info.yml data and their engines.
+ *
+ * @return
+ *   An associative array of themes information.
+ */
+function _system_rebuild_theme_data() {
+  // Find themes
+  $themes = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info.yml$/', 'themes');
+  // Allow modules to add further themes.
+  if ($module_themes = \Drupal::moduleHandler()->invokeAll('system_theme_info')) {
+    foreach ($module_themes as $name => $uri) {
+      // @see file_scan_directory()
+      $themes[$name] = (object) array(
+        'uri' => $uri,
+        'filename' => pathinfo($uri, PATHINFO_FILENAME),
+        'name' => $name,
+      );
+    }
+  }
+
+  // Find theme engines
+  $engines = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.engine$/', 'themes/engines');
+
+  // Set defaults for theme info.
+  $defaults = array(
+    'engine' => 'twig',
+    'regions' => array(
+      'sidebar_first' => 'Left sidebar',
+      'sidebar_second' => 'Right sidebar',
+      'content' => 'Content',
+      'header' => 'Header',
+      'footer' => 'Footer',
+      'highlighted' => 'Highlighted',
+      'help' => 'Help',
+      'page_top' => 'Page top',
+      'page_bottom' => 'Page bottom',
+    ),
+    'description' => '',
+    'features' => _system_default_theme_features(),
+    'screenshot' => 'screenshot.png',
+    'php' => DRUPAL_MINIMUM_PHP,
+    'stylesheets' => array(),
+    'scripts' => array(),
+  );
+
+  $sub_themes = array();
+  // Read info files for each theme
+  foreach ($themes as $key => $theme) {
+    $themes[$key]->filename = $theme->uri;
+    $themes[$key]->info = \Drupal::service('info_parser')->parse($theme->uri) + $defaults;
+
+    // Skip this extension if its type is not theme.
+    if (!isset($themes[$key]->info['type']) || $themes[$key]->info['type'] != 'theme') {
+      unset($themes[$key]);
+      continue;
+    }
+
+    // Add the info file modification time, so it becomes available for
+    // contributed modules to use for ordering theme lists.
+    $themes[$key]->info['mtime'] = filemtime($theme->uri);
+
+    // Invoke hook_system_info_alter() to give installed modules a chance to
+    // modify the data in the .info.yml files if necessary.
+    $type = 'theme';
+    drupal_alter('system_info', $themes[$key]->info, $themes[$key], $type);
+
+    if (!empty($themes[$key]->info['base theme'])) {
+      $sub_themes[] = $key;
+    }
+
+    $engine = $themes[$key]->info['engine'];
+    if (isset($engines[$engine])) {
+      $themes[$key]->owner = $engines[$engine]->uri;
+      $themes[$key]->prefix = $engines[$engine]->name;
+      $themes[$key]->template = TRUE;
+    }
+
+    // Prefix stylesheets and scripts with module path.
+    $path = dirname($theme->uri);
+    $theme->info['stylesheets'] = _system_info_add_path($theme->info['stylesheets'], $path);
+    $theme->info['scripts'] = _system_info_add_path($theme->info['scripts'], $path);
+
+    // Give the screenshot proper path information.
+    if (!empty($themes[$key]->info['screenshot'])) {
+      $themes[$key]->info['screenshot'] = $path . '/' . $themes[$key]->info['screenshot'];
+    }
+  }
+
+  // Now that we've established all our master themes, go back and fill in data
+  // for subthemes.
+  foreach ($sub_themes as $key) {
+    $themes[$key]->base_themes = drupal_find_base_themes($themes, $key);
+    // Don't proceed if there was a problem with the root base theme.
+    if (!current($themes[$key]->base_themes)) {
+      continue;
+    }
+    $base_key = key($themes[$key]->base_themes);
+    foreach (array_keys($themes[$key]->base_themes) as $base_theme) {
+      $themes[$base_theme]->sub_themes[$key] = $themes[$key]->info['name'];
+    }
+    // Copy the 'owner' and 'engine' over if the top level theme uses a theme
+    // engine.
+    if (isset($themes[$base_key]->owner)) {
+      if (isset($themes[$base_key]->info['engine'])) {
+        $themes[$key]->info['engine'] = $themes[$base_key]->info['engine'];
+        $themes[$key]->owner = $themes[$base_key]->owner;
+        $themes[$key]->prefix = $themes[$base_key]->prefix;
+      }
+      else {
+        $themes[$key]->prefix = $key;
+      }
+    }
+  }
+
+  return $themes;
+}
+
+/**
+ * Rebuild, save, and return data about all currently available themes.
+ *
+ * @return
+ *   Array of all available themes and their data.
+ */
+function system_rebuild_theme_data() {
+  $themes = _system_rebuild_theme_data();
+  ksort($themes);
+  // @todo This function has no business in determining/setting the status of
+  //   a theme, but various other functions expect it to return themes with a
+  //   $status property. system_list() stores the return value of this function
+  //   in state, and ensures to set/override the $status property for each theme
+  //   based on the current config. Remove this code when themes have a proper
+  //   installation status.
+  // @see http://drupal.org/node/1067408
+  $enabled_themes = (array) \Drupal::config('system.theme')->get('enabled');
+  $files = array();
+  foreach ($themes as $name => $theme) {
+    $theme->status = (int) isset($enabled_themes[$name]);
+    $files[$name] = $theme->filename;
+  }
+  // Replace last known theme data state.
+  // @todo Obsolete with proper installation status for themes.
+  \Drupal::state()->set('system.theme.data', $themes);
+
+  // Store filenames to allow system_list() and drupal_get_filename() to
+  // retrieve them without having to rebuild or scan the filesystem.
+  \Drupal::state()->set('system.theme.files', $files);
+
+  return $themes;
+}
+
+/**
+ * Prefixes all values in an .info.yml file array with a given path.
+ *
+ * This helper function is mainly used to prefix all array values of an
+ * .info.yml file property with a single given path (to the module or theme);
+ * e.g., to prefix all values of the 'stylesheets' or 'scripts' properties with
+ * the file path to the defining module/theme.
+ *
+ * @param $info
+ *   A nested array of data of an .info.yml file to be processed.
+ * @param $path
+ *   A file path to prepend to each value in $info.
+ *
+ * @return
+ *   The $info array with prefixed values.
+ *
+ * @see _system_rebuild_module_data()
+ * @see _system_rebuild_theme_data()
+ */
+function _system_info_add_path($info, $path) {
+  foreach ($info as $key => $value) {
+    // Recurse into nested values until we reach the deepest level.
+    if (is_array($value)) {
+      $info[$key] = _system_info_add_path($info[$key], $path);
+    }
+    // Unset the original value's key and set the new value with prefix, using
+    // the original value as key, so original values can still be looked up.
+    else {
+      unset($info[$key]);
+      $info[$value] = $path . '/' . $value;
+    }
+  }
+  return $info;
+}
+
+/**
+ * Returns an array of default theme features.
+ */
+function _system_default_theme_features() {
+  return array(
+    'logo',
+    'favicon',
+    'name',
+    'slogan',
+    'node_user_picture',
+    'comment_user_picture',
+    'comment_user_verification',
+    'main_menu',
+    'secondary_menu',
+  );
+}
+
+/**
+ * Get a list of available regions from a specified theme.
+ *
+ * @param $theme_key
+ *   The name of a theme.
+ * @param $show
+ *   Possible values: REGIONS_ALL or REGIONS_VISIBLE. Visible excludes hidden
+ *   regions.
+ * @return
+ *   An array of regions in the form $region['name'] = 'description'.
+ */
+function system_region_list($theme_key, $show = REGIONS_ALL) {
+  $themes = list_themes();
+  if (!isset($themes[$theme_key])) {
+    return array();
+  }
+
+  $list = array();
+  $info = $themes[$theme_key]->info;
+  // If requested, suppress hidden regions. See block_admin_display_form().
+  foreach ($info['regions'] as $name => $label) {
+    if ($show == REGIONS_ALL || !isset($info['regions_hidden']) || !in_array($name, $info['regions_hidden'])) {
+      $list[$name] = t($label);
+    }
+  }
+
+  return $list;
+}
+
+/**
+ * Implements hook_system_info_alter().
+ */
+function system_system_info_alter(&$info, $file, $type) {
+  // Remove page-top and page-bottom from the blocks UI since they are reserved for
+  // modules to populate from outside the blocks system.
+  if ($type == 'theme') {
+    $info['regions_hidden'][] = 'page_top';
+    $info['regions_hidden'][] = 'page_bottom';
+  }
+}
+
+/**
+ * Gets the name of the default region for a given theme.
+ *
+ * @param $theme
+ *   The name of a theme.
+ * @return
+ *   A string that is the region name.
+ */
+function system_default_region($theme) {
+  $regions = array_keys(system_region_list($theme, REGIONS_VISIBLE));
+  return isset($regions[0]) ? $regions[0] : '';
+}
+
+/**
+ * Generates a form array for a confirmation form.
+ *
+ * This function returns a complete form array for confirming an action. The
+ * form contains a confirm button as well as a cancellation link that allows a
+ * user to abort the action.
+ *
+ * If the submit handler for a form that implements confirm_form() is invoked,
+ * the user successfully confirmed the action. You should never directly
+ * inspect $_POST to see if an action was confirmed.
+ *
+ * Note - if the parameters $question, $description, $yes, or $no could contain
+ * any user input (such as node titles or taxonomy terms), it is the
+ * responsibility of the code calling confirm_form() to sanitize them first with
+ * a function like check_plain() or filter_xss().
+ *
+ * @param $form
+ *   Additional elements to add to the form. These can be regular form elements,
+ *   #value elements, etc., and their values will be available to the submit
+ *   handler.
+ * @param $question
+ *   The question to ask the user (e.g. "Are you sure you want to delete the
+ *   block <em>foo</em>?"). The page title will be set to this value.
+ * @param $path
+ *   The page to go to if the user cancels the action. This can be either:
+ *   - A string containing a Drupal path.
+ *   - An associative array with a 'path' key. Additional array values are
+ *     passed as the $options parameter to l().
+ *   If the 'destination' query parameter is set in the URL when viewing a
+ *   confirmation form, that value will be used instead of $path.
+ * @param $description
+ *   Additional text to display. Defaults to t('This action cannot be undone.').
+ * @param $yes
+ *   A caption for the button that confirms the action (e.g. "Delete",
+ *   "Replace", ...). Defaults to t('Confirm').
+ * @param $no
+ *   A caption for the link which cancels the action (e.g. "Cancel"). Defaults
+ *   to t('Cancel').
+ * @param $name
+ *   The internal name used to refer to the confirmation item.
+ *
+ * @return
+ *   The form array.
+ *
+ * @deprecated Use \Drupal\Core\Form\ConfirmFormBase instead.
+ */
+function confirm_form($form, $question, $path, $description = NULL, $yes = NULL, $no = NULL, $name = 'confirm') {
+  $description = isset($description) ? $description : t('This action cannot be undone.');
+
+  // Prepare cancel link.
+  if (isset($_GET['destination'])) {
+    $options = drupal_parse_url($_GET['destination']);
+  }
+  elseif (is_array($path)) {
+    $options = $path;
+  }
+  else {
+    $options = array('path' => $path);
+  }
+
+  drupal_set_title($question, PASS_THROUGH);
+
+  $form['#attributes']['class'][] = 'confirmation';
+  $form['description'] = array('#markup' => $description);
+  $form[$name] = array('#type' => 'hidden', '#value' => 1);
+
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => $yes ? $yes : t('Confirm'),
+  );
+  $form['actions']['cancel'] = array(
+    '#type' => 'link',
+    '#title' => $no ? $no : t('Cancel'),
+    '#href' => $options['path'],
+    '#options' => $options,
+  );
+  // By default, render the form using theme_confirm_form().
+  if (!isset($form['#theme'])) {
+    $form['#theme'] = 'confirm_form';
+  }
+  return $form;
+}
+
+/**
+ * Determines whether the current user is in compact mode.
+ *
+ * Compact mode shows certain administration pages with less description text,
+ * such as the configuration page and the permissions page.
+ *
+ * Whether the user is in compact mode is determined by a cookie, which is set
+ * for the user by \Drupal\system\Controller\SystemController::compactPage().
+ *
+ * If the user does not have the cookie, the default value is given by the
+ * system variable 'admin_compact_mode', which itself defaults to FALSE. This
+ * does not have a user interface to set it: it is a hidden variable which can
+ * be set in the settings.php file.
+ *
+ * @return bool
+ *   TRUE when in compact mode, FALSE when in expanded mode.
+ */
+function system_admin_compact_mode() {
+  // PHP converts dots into underscores in cookie names to avoid problems with
+  // its parser, so we use a converted cookie name.
+  return isset($_COOKIE['Drupal_visitor_admin_compact_mode']) ? $_COOKIE['Drupal_visitor_admin_compact_mode'] : \Drupal::config('system.site')->get('admin_compact_mode');
+}
+
+/**
+ * Generate a list of tasks offered by a specified module.
+ *
+ * @param $module
+ *   Module name.
+ * @param $info
+ *   The module's information, as provided by system_get_info().
+ *
+ * @return
+ *   An array of task links.
+ */
+function system_get_module_admin_tasks($module, $info) {
+  $links = &drupal_static(__FUNCTION__);
+
+  if (!isset($links)) {
+    $links = array();
+    $menu_links = entity_get_controller('menu_link')->loadModuleAdminTasks();
+    foreach ($menu_links as $link) {
+      _menu_link_translate($link);
+      if ($link['access']) {
+        $links[$link['router_path']] = $link;
+      }
+    }
+  }
+
+  $admin_tasks = array();
+  $titles = array();
+  if ($menu = module_invoke($module, 'menu')) {
+    foreach ($menu as $path => $item) {
+      if (isset($links[$path])) {
+        $task = $links[$path];
+        // The link description, either derived from 'description' in
+        // hook_menu() or customized via menu module is used as title attribute.
+        if (!empty($task['localized_options']['attributes']['title'])) {
+          $task['description'] = $task['localized_options']['attributes']['title'];
+          unset($task['localized_options']['attributes']['title']);
+        }
+
+        // Check the admin tasks for duplicate names. If one is found,
+        // append the parent menu item's title to differentiate.
+        $duplicate_path = array_search($task['title'], $titles);
+        if ($duplicate_path !== FALSE) {
+          if ($parent = menu_link_load($task['plid'])) {
+            // Append the parent item's title to this task's title.
+            $task['title'] = t('@original_title (@parent_title)', array('@original_title' => $task['title'], '@parent_title' => $parent['title']));
+          }
+          if ($parent = menu_link_load($admin_tasks[$duplicate_path]['plid'])) {
+            // Append the parent item's title to the duplicated task's title.
+            // We use $links[$duplicate_path] in case there are triplicates.
+            $admin_tasks[$duplicate_path]['title'] = t('@original_title (@parent_title)', array('@original_title' => $links[$duplicate_path]['title'], '@parent_title' => $parent['title']));
+          }
+        }
+        else {
+          $titles[$path] = $task['title'];
+        }
+
+        $admin_tasks[$path] = $task;
+      }
+    }
+  }
+
+  // Append link for permissions.
+  if (\Drupal::moduleHandler()->implementsHook($module, 'permission')) {
+    $item = menu_get_item('admin/people/permissions');
+    if (!empty($item['access'])) {
+      $item['link_path'] = $item['href'];
+      $item['title'] = t('Configure @module permissions', array('@module' => $info['name']));
+      unset($item['description']);
+      $item['localized_options']['fragment'] = 'module-' . $module;
+      $item = entity_create('menu_link', $item);
+      $admin_tasks["admin/people/permissions#module-$module"] = $item;
+    }
+  }
+
+  return $admin_tasks;
+}
+
+/**
+ * Implements hook_cron().
+ *
+ * Remove older rows from flood and batch table. Remove old temporary files.
+ */
+function system_cron() {
+  // Cleanup the flood.
+  \Drupal::service('flood')->garbageCollection();
+
+  foreach (Cache::getBins() as $cache_backend) {
+    $cache_backend->garbageCollection();
+  }
+
+  // Cleanup the queue for failed batches.
+  db_delete('queue')
+    ->condition('created', REQUEST_TIME - 864000, '<')
+    ->condition('name', 'drupal_batch:%', 'LIKE')
+    ->execute();
+
+  // Reset expired items in the default queue implementation table. If that's
+  // not used, this will simply be a no-op.
+  db_update('queue')
+    ->fields(array(
+      'expire' => 0,
+    ))
+    ->condition('expire', 0, '<>')
+    ->condition('expire', REQUEST_TIME, '<')
+    ->execute();
+}
+
+/**
+ * Implements hook_mail().
+ */
+function system_mail($key, &$message, $params) {
+  $token_service = \Drupal::token();
+
+  $context = $params['context'];
+
+  $subject = $token_service->replace($context['subject'], $context);
+  $body = $token_service->replace($context['message'], $context);
+
+  $message['subject'] .= str_replace(array("\r", "\n"), '', $subject);
+  $message['body'][] = $body;
+}
+
+/**
+ * Generate an array of time zones and their local time&date.
+ *
+ * @param $blank
+ *   If evaluates true, prepend an empty time zone option to the array.
+ */
+function system_time_zones($blank = NULL) {
+  $zonelist = timezone_identifiers_list();
+  $zones = $blank ? array('' => t('- None selected -')) : array();
+  foreach ($zonelist as $zone) {
+    // Because many time zones exist in PHP only for backward compatibility
+    // reasons and should not be used, the list is filtered by a regular
+    // expression.
+    if (preg_match('!^((Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Europe|Indian|Pacific)/|UTC$)!', $zone)) {
+      $zones[$zone] = t('@zone', array('@zone' => t(str_replace('_', ' ', $zone))));
+    }
+  }
+  // Sort the translated time zones alphabetically.
+  asort($zones);
+  return $zones;
+}
+
+/**
+ * Returns HTML for the Powered by Drupal text.
+ *
+ * @ingroup themeable
+ */
+function theme_system_powered_by() {
+  return '<span>' . t('Powered by <a href="@poweredby">Drupal</a>', array('@poweredby' => 'http://drupal.org')) . '</span>';
+}
+
+/**
+ * Returns HTML for a link to show or hide inline help descriptions.
+ *
+ * @ingroup themeable
+ */
+function theme_system_compact_link() {
+  $output = '<div class="compact-link">';
+  if (system_admin_compact_mode()) {
+    $output .= l(t('Show descriptions'), 'admin/compact/off', array('attributes' => array('title' => t('Expand layout to include descriptions.')), 'query' => drupal_get_destination()));
+  }
+  else {
+    $output .= l(t('Hide descriptions'), 'admin/compact/on', array('attributes' => array('title' => t('Compress layout by hiding descriptions.')), 'query' => drupal_get_destination()));
+  }
+  $output .= '</div>';
+
+  return $output;
+}
+
+/**
+ * Attempts to get a file using Guzzle HTTP client and to store it locally.
+ *
+ * @param $url
+ *   The URL of the file to grab.
+ *
+ * @param $destination
+ *   Stream wrapper URI specifying where the file should be placed. If a
+ *   directory path is provided, the file is saved into that directory under
+ *   its original name. If the path contains a filename as well, that one will
+ *   be used instead.
+ *   If this value is omitted, the site's default files scheme will be used,
+ *   usually "public://".
+ *
+ * @param $managed boolean
+ *   If this is set to TRUE, the file API hooks will be invoked and the file is
+ *   registered in the database.
+ *
+ * @param $replace boolean
+ *   Replace behavior when the destination file already exists:
+ *   - FILE_EXISTS_REPLACE: Replace the existing file.
+ *   - FILE_EXISTS_RENAME: Append _{incrementing number} until the filename is
+ *     unique.
+ *   - FILE_EXISTS_ERROR: Do nothing and return FALSE.
+ *
+ * @return
+ *   On success the location the file was saved to, FALSE on failure.
+ */
+function system_retrieve_file($url, $destination = NULL, $managed = FALSE, $replace = FILE_EXISTS_RENAME) {
+  $parsed_url = parse_url($url);
+  if (!isset($destination)) {
+    $path = file_build_uri(drupal_basename($parsed_url['path']));
+  }
+  else {
+    if (is_dir(drupal_realpath($destination))) {
+      // Prevent URIs with triple slashes when glueing parts together.
+      $path = str_replace('///', '//', "$destination/") . drupal_basename($parsed_url['path']);
+    }
+    else {
+      $path = $destination;
+    }
+  }
+  try {
+    $data = \Drupal::httpClient()
+      ->get($url)
+      ->send()
+      ->getBody(TRUE);
+    $local = $managed ? file_save_data($data, $path, $replace) : file_unmanaged_save_data($data, $path, $replace);
+  }
+  catch (BadResponseException $exception) {
+    $response = $exception->getResponse();
+    drupal_set_message(t('Failed to fetch file due to HTTP error "%error"', array('%error' => $response->getStatusCode() . ' ' . $response->getReasonPhrase())), 'error');
+    return FALSE;
+  }
+  catch (RequestException $exception) {
+    drupal_set_message(t('Failed to fetch file due to error "%error"', array('%error' => $exception->getMessage())), 'error');
+    return FALSE;
+  }
+  if (!$local) {
+    drupal_set_message(t('@remote could not be saved to @path.', array('@remote' => $url, '@path' => $path)), 'error');
+  }
+
+  return $local;
+}
+
+/**
+ * Implements hook_page_alter().
+ */
+function system_page_alter(&$page) {
+  // Find all non-empty page regions, and add a theme wrapper function that
+  // allows them to be consistently themed.
+  $regions = system_region_list($GLOBALS['theme']);
+  foreach (array_keys($regions) as $region) {
+    if (!empty($page[$region])) {
+      $page[$region]['#theme_wrappers'][] = 'region';
+      $page[$region]['#region'] = $region;
+    }
+  }
+}
+
+/**
+ * Run the automated cron if enabled.
+ */
+function system_run_automated_cron() {
+  // If the site is not fully installed, suppress the automated cron run.
+  // Otherwise it could be triggered prematurely by Ajax requests during
+  // installation.
+  if (($threshold = \Drupal::config('system.cron')->get('threshold.autorun')) > 0 && \Drupal::state()->get('install_task') == 'done') {
+    $cron_last = \Drupal::state()->get('system.cron_last') ?: NULL;
+    if (!isset($cron_last) || (REQUEST_TIME - $cron_last > $threshold)) {
+      drupal_cron_run();
+    }
+  }
+}
+
+/**
+ * Returns HTML for a confirmation form.
+ *
+ * By default this does not alter the appearance of a form at all,
+ * but is provided as a convenience for themers.
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - form: A render element representing the form.
+ *
+ * @ingroup themeable
+ */
+function theme_confirm_form($variables) {
+  return drupal_render_children($variables['form']);
+}
+
+/**
+ * Returns HTML for a system settings form.
+ *
+ * By default this does not alter the appearance of a form at all,
+ * but is provided as a convenience for themers.
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - form: A render element representing the form.
+ *
+ * @ingroup themeable
+ */
+function theme_system_config_form($variables) {
+  return drupal_render_children($variables['form']);
+}
+
+/**
+ * Returns HTML for an exposed filter form.
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - form: An associative array containing the structure of the form.
+ *
+ * @return
+ *   A string containing an HTML-formatted form.
+ *
+ * @ingroup themeable
+ */
+function theme_exposed_filters($variables) {
+  $form = $variables['form'];
+  $output = '';
+
+  if (isset($form['current'])) {
+    $items = array();
+    foreach (element_children($form['current']) as $key) {
+      $items[] = $form['current'][$key];
+    }
+    $item_list = array(
+      '#theme' => 'item_list',
+      '#items' => $items,
+      '#attributes' => array('class' => array('clearfix', 'current-filters')),
+    );
+    $output .= drupal_render($item_list);
+  }
+
+  $output .= drupal_render_children($form);
+
+  return '<div class="exposed-filters">' . $output . '</div>';
+}
+
+/**
+ * Implements hook_admin_paths().
+ */
+function system_admin_paths() {
+  $paths = array(
+    'admin' => TRUE,
+    'admin/*' => TRUE,
+    'batch' => TRUE,
+    // This page should not be treated as administrative since it outputs its
+    // own content (outside of any administration theme).
+    'admin/reports/status/php' => FALSE,
+  );
+  return $paths;
+}
diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml
index 4281cb4..01e0ebb 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -2,6 +2,8 @@ system.ajax:
   path: '/system/ajax'
   defaults:
     _controller: '\Drupal\system\Controller\FormAjaxController::content'
+  options:
+    _theme: ajax_base_page
   requirements:
     _access: 'TRUE'
 
diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml
index 3371dad..99faa25 100644
--- a/core/modules/system/system.services.yml
+++ b/core/modules/system/system.services.yml
@@ -19,3 +19,13 @@ services:
     class: Drupal\system\PathProcessor\PathProcessorFiles
     tags:
       - { name: path_processor_inbound, priority: 200 }
+  theme.negotiator.system.batch:
+    class: Drupal\system\Theme\BatchNegotiator
+    arguments: ['@batch.storage']
+    tags:
+      - { name: theme_negotiator, priority: 1000 }
+  theme.negotiator.admin_theme:
+    class: Drupal\system\Theme\AdminNegotiator
+    arguments: ['@current_user', '@config.factory', '@entity.manager']
+    tags:
+      - { name: theme_negotiator, priority: -40 }
diff --git a/core/modules/system/tests/modules/ajax_test/ajax_test.module b/core/modules/system/tests/modules/ajax_test/ajax_test.module
index 802b34e..0d0eb28 100644
--- a/core/modules/system/tests/modules/ajax_test/ajax_test.module
+++ b/core/modules/system/tests/modules/ajax_test/ajax_test.module
@@ -13,19 +13,6 @@
 use Drupal\Core\Ajax\HtmlCommand;
 
 /**
- * Implements hook_menu().
- */
-function ajax_test_menu() {
-  $items['ajax-test/order'] = array(
-    'title' => 'AJAX commands order',
-    'route_name' => 'ajax_test.order',
-    'theme callback' => 'ajax_base_page_theme',
-    'type' => MENU_CALLBACK,
-  );
-  return $items;
-}
-
-/**
  * Implements hook_system_theme_info().
  */
 function ajax_test_system_theme_info() {
diff --git a/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml b/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml
index 9453bef..270ef51 100644
--- a/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml
+++ b/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml
@@ -37,6 +37,8 @@ ajax_test.order:
   path: '/ajax-test/order'
   defaults:
     _controller: '\Drupal\ajax_test\Controller\AjaxTestController::order'
+  options:
+    _theme: ajax_base_page
   requirements:
     _access: 'TRUE'
 
diff --git a/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/EventSubscriber/ActiveTrailSubscriber.php b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/EventSubscriber/ActiveTrailSubscriber.php
new file mode 100644
index 0000000..4bb3595
--- /dev/null
+++ b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/EventSubscriber/ActiveTrailSubscriber.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\menu_test\EventSubscriber\ActiveTrailSubscriber.
+ */
+
+namespace Drupal\menu_test\EventSubscriber;
+
+use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\KernelEvents;
+
+/**
+ * Tracks the active trail.
+ */
+class ActiveTrailSubscriber implements EventSubscriberInterface {
+
+  /**
+   * The active trail before redirect.
+   *
+   * @var array
+   */
+  protected $trail = array();
+
+  /**
+   * The state service.
+   *
+   * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
+   */
+  protected $state;
+
+  /**
+   * Constructs a new ActiveTrailSubscriber.
+   *
+   * @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $state
+   *   The state service.
+   */
+  public function __construct(KeyValueStoreInterface $state) {
+    $this->state = $state;
+  }
+
+  /**
+   * Tracks the active trail.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
+   *   The event to process.
+   */
+  public function onKernelRequest(GetResponseEvent $event) {
+    // When requested by one of the MenuTrailTestCase tests, record the initial
+    // active trail during Drupal's bootstrap (before the user is redirected to
+    // a custom 403 or 404 page).
+    if (!$this->trail && $this->state->get('menu_test.record_active_trail') ?: FALSE) {
+      $this->trail = menu_get_active_trail();
+      $this->state->set('menu_test.active_trail_initial', $this->trail);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[KernelEvents::REQUEST][] = array('onKernelRequest');
+    return $events;
+  }
+
+}
diff --git a/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Theme/TestThemeNegotiator.php b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Theme/TestThemeNegotiator.php
new file mode 100644
index 0000000..b324d8d
--- /dev/null
+++ b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Theme/TestThemeNegotiator.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\menu_test\Theme\TestThemeNegotiator.
+ */
+
+namespace Drupal\menu_test\Theme;
+
+use Drupal\Core\Theme\ThemeNegotiatorInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Tests the theme negotiation functionality.
+ *
+ * Retrieves the theme key of the theme to use for the current request based on
+ * the theme name provided in the URL.
+ */
+class TestThemeNegotiator implements ThemeNegotiatorInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function determineActiveTheme(Request $request) {
+    $argument = $request->attributes->get('inherited');
+    // Test using the variable administrative theme.
+    if ($argument == 'use-admin-theme') {
+      return \Drupal::config('system.theme')->get('admin');
+    }
+    // Test using a theme that exists, but may or may not be enabled.
+    elseif ($argument == 'use-stark-theme') {
+      return 'stark';
+    }
+    // Test using a theme that does not exist.
+    elseif ($argument == 'use-fake-theme') {
+      return 'fake_theme';
+    }
+    // For any other value of the URL argument, do not return anything. This
+    // allows us to test that returning nothing from a theme negotiation
+    // causes the page to correctly fall back on using the main site theme.
+  }
+
+}
diff --git a/core/modules/system/tests/modules/menu_test/menu_test.module b/core/modules/system/tests/modules/menu_test/menu_test.module
index 566a620..54854c5 100644
--- a/core/modules/system/tests/modules/menu_test/menu_test.module
+++ b/core/modules/system/tests/modules/menu_test/menu_test.module
@@ -64,18 +64,12 @@ function menu_test_menu() {
     'route_name' => 'menu_test.hierarchy_parent_child2',
   );
   // Theme callback tests.
-  $items['menu-test/theme-callback/%'] = array(
-    'title' => 'Page that displays different themes',
-    'route_name' => 'menu_test.theme_callback',
-    'theme callback' => 'menu_test_theme_callback',
-    'theme arguments' => array(2),
-  );
   $items['menu-test/theme-callback/%/inheritance'] = array(
-    'title' => 'Page that tests theme callback inheritance.',
+    'title' => 'Page that tests theme negotiation inheritance.',
     'route_name' => 'menu_test.theme_callback_inheritance',
   );
   $items['menu-test/no-theme-callback'] = array(
-    'title' => 'Page that displays different themes without using a theme callback.',
+    'title' => 'Page that displays different themes without using a theme negotiation.',
     'route_name' => 'menu_test.no_theme_callback',
   );
   // Path containing "exotic" characters.
@@ -482,7 +476,7 @@ function menu_test_custom_403_404_callback() {
 }
 
 /**
- * Page callback: Tests the theme callback functionality.
+ * Page callback: Tests the theme negotiation functionality.
  *
  * @param bool $inherited
  *   (optional) TRUE when the requested page is intended to inherit
@@ -500,67 +494,16 @@ function menu_test_theme_page_callback($inherited = FALSE) {
   global $theme_key;
   // Initialize the theme system so that $theme_key will be populated.
   drupal_theme_initialize();
-  // Now check both the requested custom theme and the actual theme being used.
-  $custom_theme = menu_get_custom_theme();
-  $requested_theme = empty($custom_theme) ? 'NONE' : $custom_theme;
-  $output = "Custom theme: $requested_theme. Actual theme: $theme_key.";
+  // Now we check what the theme negotiator service returns.
+  $active_theme = \Drupal::service('theme.negotiator')->getActiveTheme('getActiveTheme');
+  $output = "Active theme: $active_theme. Actual theme: $theme_key.";
   if ($inherited) {
-    $output .= ' Theme callback inheritance is being tested.';
+    $output .= ' Theme negotiation inheritance is being tested.';
   }
   return $output;
 }
 
 /**
- * Theme callback: Tests the theme callback functionality.
- *
- * Retrieves the theme key of the theme to use for the current request based on
- * the theme name provided in the URL.
- *
- * @param string $argument
- *   The argument passed in from the URL.
- *
- * @return string
- *   The name of the custom theme to request for the current page.
- *
- * @see menu_test_menu().
- */
-function menu_test_theme_callback($argument) {
-  // Test using the variable administrative theme.
-  if ($argument == 'use-admin-theme') {
-    return \Drupal::config('system.theme')->get('admin');
-  }
-  // Test using a theme that exists, but may or may not be enabled.
-  elseif ($argument == 'use-stark-theme') {
-    return 'stark';
-  }
-  // Test using a theme that does not exist.
-  elseif ($argument == 'use-fake-theme') {
-    return 'fake_theme';
-  }
-  // For any other value of the URL argument, do not return anything. This
-  // allows us to test that returning nothing from a theme callback function
-  // causes the page to correctly fall back on using the main site theme.
-}
-
-/**
- * Implements hook_custom_theme().
- *
- * If an appropriate variable has been set in the database, request the theme
- * that is stored there. Otherwise, do not attempt to dynamically set the theme.
- */
-function menu_test_custom_theme() {
-  // When requested by one of the MenuTrailTestCase tests, record the initial
-  // active trail during Drupal's bootstrap (before the user is redirected to a
-  // custom 403 or 404 page). See menu_test_custom_403_404_callback().
-  if (\Drupal::state()->get('menu_test.record_active_trail') ?: FALSE) {
-    \Drupal::state()->set('menu_test.active_trail_initial', menu_get_active_trail());
-  }
-  if ($theme = \Drupal::state()->get('menu_test.hook_custom_theme_name') ?: FALSE) {
-    return $theme;
-  }
-}
-
-/**
  * Sets a static variable for the testMenuName() test.
  *
  * Used to change the menu_name parameter of a menu.
diff --git a/core/modules/system/tests/modules/menu_test/menu_test.routing.yml b/core/modules/system/tests/modules/menu_test/menu_test.routing.yml
index 5ab1a94..d37f47b 100644
--- a/core/modules/system/tests/modules/menu_test/menu_test.routing.yml
+++ b/core/modules/system/tests/modules/menu_test/menu_test.routing.yml
@@ -490,7 +490,7 @@ menu_test.theme_callback:
 menu_test.no_theme_callback:
   path: '/menu-test/no-theme-callback'
   defaults:
-    _title: 'Page that displays different themes without using a theme callback.'
+    _title: 'Page that displays different themes without using a theme negotiation.'
     _content: '\Drupal\menu_test\Controller\MenuTestController::themePage'
     inherited: false
   requirements:
@@ -511,7 +511,7 @@ menu_test.exotic_path:
 menu_test.theme_callback_inheritance:
   path: '/menu-test/theme-callback/{inherited}/inheritance'
   defaults:
-    _title: 'Page that tests theme callback inheritance.'
+    _title: 'Page that tests theme negotiation inheritance.'
     _content: '\Drupal\menu_test\Controller\MenuTestController::themePage'
   requirements:
     _permission: 'access content'
diff --git a/core/modules/system/tests/modules/menu_test/menu_test.services.yml b/core/modules/system/tests/modules/menu_test/menu_test.services.yml
index 097ddf2..3de0169 100644
--- a/core/modules/system/tests/modules/menu_test/menu_test.services.yml
+++ b/core/modules/system/tests/modules/menu_test/menu_test.services.yml
@@ -3,3 +3,14 @@ services:
     class: Drupal\menu_test\EventSubscriber\MaintenanceModeSubscriber
     tags:
       - { name: event_subscriber }
+
+  menu_test.active_trail_subscriber:
+    class: Drupal\menu_test\EventSubscriber\ActiveTrailSubscriber
+    arguments: ['@state']
+    tags:
+      - { name: event_subscriber }
+
+  theme.negotiator.test_theme:
+    class: Drupal\menu_test\Theme\TestThemeNegotiator
+    tags:
+      - { name: theme_negotiator }
diff --git a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/CustomThemeNegotiator.php b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/CustomThemeNegotiator.php
new file mode 100644
index 0000000..9c0396e
--- /dev/null
+++ b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/CustomThemeNegotiator.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\theme_test\Theme\CustomThemeNegotiator.
+ */
+
+namespace Drupal\theme_test\Theme;
+
+use Drupal\Core\Theme\ThemeNegotiatorInterface;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Just forces the 'test_theme' theme.
+ */
+class CustomThemeNegotiator implements ThemeNegotiatorInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function determineActiveTheme(Request $request) {
+    if (($route_object = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)) && $route_object instanceof Route && $route_object->hasOption('_custom_theme')) {
+      return $route_object->getOption('_custom_theme');
+    }
+  }
+
+}
diff --git a/core/modules/system/tests/modules/theme_test/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module
index badea81..468fc9c 100644
--- a/core/modules/system/tests/modules/theme_test/theme_test.module
+++ b/core/modules/system/tests/modules/theme_test/theme_test.module
@@ -57,26 +57,6 @@ function theme_test_system_theme_info() {
 }
 
 /**
- * Implements hook_menu().
- */
-function theme_test_menu() {
-  $items['theme-test/suggestion'] = array(
-    'route_name' => 'theme_test.suggestion',
-    'theme callback' => '_theme_custom_theme',
-    'type' => MENU_CALLBACK,
-  );
-  $items['theme-test/alter'] = array(
-    'theme callback' => '_theme_custom_theme',
-    'route_name' => 'theme_test.alter',
-    'type' => MENU_CALLBACK,
-  );
-  $items['theme-test/function-template-overridden'] = array(
-    'theme callback' => '_theme_custom_theme',
-    'route_name' => 'theme_test.function_template_override',
-  );
-  return $items;
-}
-/**
  * Fake registry loading callback.
  */
 function _theme_test_load_registry() {
@@ -85,13 +65,6 @@ function _theme_test_load_registry() {
 }
 
 /**
- * Custom theme callback.
- */
-function _theme_custom_theme() {
-  return 'test_theme';
-}
-
-/**
  * Implements hook_preprocess_HOOK() for HTML document templates.
  */
 function theme_test_preprocess_html(&$variables) {
diff --git a/core/modules/system/tests/modules/theme_test/theme_test.routing.yml b/core/modules/system/tests/modules/theme_test/theme_test.routing.yml
index b4a7fd6..8a750f3 100644
--- a/core/modules/system/tests/modules/theme_test/theme_test.routing.yml
+++ b/core/modules/system/tests/modules/theme_test/theme_test.routing.yml
@@ -1,5 +1,7 @@
 theme_test.function_template_override:
   path: '/theme-test/function-template-overridden'
+  options:
+    _custom_theme: 'test_theme'
   defaults:
     _content: '\Drupal\theme_test\ThemeTestController::functionTemplateOverridden'
   requirements:
@@ -21,6 +23,8 @@ theme_test.template_test:
 
 theme_test.suggestion:
   path: '/theme-test/suggestion'
+  options:
+    _custom_theme: 'test_theme'
   defaults:
     _content: '\Drupal\theme_test\ThemeTestController::testSuggestion'
     _title: 'Suggestion'
@@ -29,6 +33,8 @@ theme_test.suggestion:
 
 theme_test.alter:
   path: '/theme-test/alter'
+  options:
+    _custom_theme: 'test_theme'
   defaults:
     _content: '\Drupal\theme_test\ThemeTestController::testAlter'
     _title: 'Suggestion'
diff --git a/core/modules/system/tests/modules/theme_test/theme_test.services.yml b/core/modules/system/tests/modules/theme_test/theme_test.services.yml
index 8e442df..29c419d 100644
--- a/core/modules/system/tests/modules/theme_test/theme_test.services.yml
+++ b/core/modules/system/tests/modules/theme_test/theme_test.services.yml
@@ -3,3 +3,8 @@ services:
     class: Drupal\theme_test\EventSubscriber\ThemeTestSubscriber
     tags:
       - { name: event_subscriber }
+
+  theme.negotiator.test_custom_theme:
+    class: Drupal\theme_test\Theme\CustomThemeNegotiator
+    tags:
+      - { name: theme_negotiator }
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/ThemeTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/ThemeTest.php
index 452e33f..ee8cc38 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/ThemeTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/ThemeTest.php
@@ -25,11 +25,11 @@ function setUp() {
 
     // Make sure we are using distinct default and administrative themes for
     // the duration of these tests.
+    theme_enable(array('bartik', 'seven'));
     \Drupal::config('system.theme')
       ->set('default', 'bartik')
+      ->set('admin', 'seven')
       ->save();
-    theme_enable(array('seven'));
-    \Drupal::config('system.theme')->set('admin', 'seven')->save();
 
     // Create and log in as a user who has permission to add and edit taxonomy
     // terms and view the administrative theme.
diff --git a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
index 72c0fe9..3cd5dca 100644
--- a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
+++ b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
@@ -49,6 +49,7 @@ function setUp() {
     // Configure the theme system.
     $this->installConfig(array('system', 'field'));
     $this->installSchema('entity_test', 'entity_test');
+    $this->installSchema('user', 'users');
 
     // @todo Add helper methods for all of the following.
 
diff --git a/core/modules/user/lib/Drupal/user/Theme/UserNegotiator.php b/core/modules/user/lib/Drupal/user/Theme/UserNegotiator.php
new file mode 100644
index 0000000..17c1e33
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Theme/UserNegotiator.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\Theme\UserNegotiator.
+ */
+
+namespace Drupal\user\Theme;
+
+use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Theme\ActiveTheme;
+use Drupal\Core\Theme\ThemeNegotiatorInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Defines the theme negotiator service for theme configured per user.
+ */
+class UserNegotiator implements  ThemeNegotiatorInterface {
+
+  /**
+   * The user storage controller.
+   *
+   * @var \Drupal\user\UserStorageControllerInterface
+   */
+  protected $userStorageController;
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * Constructs a UserNegotiator object.
+   *
+   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   *   The entity manager
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current user.
+   */
+  public function __construct(EntityManager $entity_manager, AccountInterface $current_user) {
+    $this->userStorageController = $entity_manager->getStorageController('user');
+    $this->currentUser = $current_user;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function determineActiveTheme(Request $request) {
+    if ($user = $this->userStorageController->load($this->currentUser->id())) {;
+      // Only select the user selected theme if it is available in the
+      // list of themes that can be accessed.
+      if (!empty($user->theme) && drupal_theme_access($user->theme)) {
+        return $user->theme;
+      }
+    }
+  }
+
+}
diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml
index 6fb7d47..e44a6f8 100644
--- a/core/modules/user/user.services.yml
+++ b/core/modules/user/user.services.yml
@@ -25,3 +25,9 @@ services:
     class: Drupal\user\EventSubscriber\MaintenanceModeSubscriber
     tags:
       - { name: event_subscriber }
+  theme.negotiator.user:
+    class: Drupal\user\Theme\UserNegotiator
+    arguments: ['@plugin.manager.entity', '@current_user']
+    tags:
+      - { name: theme_negotiator, priority: -50 }
+
diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldCounterTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldCounterTest.php
index 8773248..897bfd9 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldCounterTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldCounterTest.php
@@ -19,7 +19,7 @@ class FieldCounterTest extends ViewUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('user');
+  public static $modules = array('user', 'field');
 
   /**
    * Views used by this test.
@@ -36,6 +36,12 @@ public static function getInfo() {
     );
   }
 
+  protected function setUp() {
+    parent::setUp();
+
+    $this->installSchema('user', 'users');
+  }
+
   function testSimple() {
     $view = views_get_view('test_view');
     $view->setDisplay();
diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUnitTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUnitTest.php
index eff8929..9c50002 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUnitTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUnitTest.php
@@ -17,7 +17,7 @@
  */
 class FieldUnitTest extends ViewUnitTestBase {
 
-  public static $modules = array('user');
+  public static $modules = array('user', 'field');
 
   /**
    * Views used by this test.
@@ -38,6 +38,12 @@ public static function getInfo() {
     );
   }
 
+  protected function setUp() {
+    parent::setUp();
+
+    $this->installSchema('user', 'users');
+  }
+
   /**
    * Overrides Drupal\views\Tests\ViewTestBase::viewsData().
    */
diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php
index 3e5b590..dc51709 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php
@@ -59,6 +59,7 @@ protected function setUp() {
     // Setup the needed tables in order to make the drupal router working.
     $this->installSchema('system', array('router', 'menu_router', 'url_alias'));
     $this->installSchema('menu_link', 'menu_links');
+    $this->installSchema('user', 'users');
   }
 
   /**
diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php
index d4806f0..55767e1 100644
--- a/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/ViewPageControllerTest.php
@@ -25,7 +25,7 @@ class ViewPageControllerTest extends ViewUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('user');
+  public static $modules = array('user', 'field');
 
   /**
    * Views used by this test.
@@ -55,6 +55,7 @@ public static function getInfo() {
   protected function setUp() {
     parent::setUp();
 
+    $this->installSchema('user', 'users');
     $this->installSchema('system', array('router', 'menu_router'));
 
     $this->pageController = new ViewPageController($this->container->get('entity.manager')->getStorageController('view'), new ViewExecutableFactory());
diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php
index 41050d6..7685810 100644
--- a/core/modules/views/lib/Drupal/views/ViewExecutable.php
+++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php
@@ -1302,8 +1302,6 @@ public function render($display_id = NULL) {
       return;
     }
 
-    drupal_theme_initialize();
-
     $exposed_form = $this->display_handler->getPlugin('exposed_form');
     $exposed_form->preRender($this->result);
 
@@ -1365,11 +1363,13 @@ public function render($display_id = NULL) {
       $module_handler->invokeAll('views_pre_render', array($this));
 
       // Let the themes play too, because pre render is a very themey thing.
-      foreach ($GLOBALS['base_theme_info'] as $base) {
-        $module_handler->invoke($base, 'views_pre_render', array($this));
-      }
+      if (isset($GLOBALS['base_theme_info']) && isset($GLOBALS['theme'])) {
+        foreach ($GLOBALS['base_theme_info'] as $base) {
+          $module_handler->invoke($base, 'views_pre_render', array($this));
+        }
 
-      $module_handler->invoke($GLOBALS['theme'], 'views_pre_render', array($this));
+        $module_handler->invoke($GLOBALS['theme'], 'views_pre_render', array($this));
+      }
 
       $this->display_handler->output = $this->display_handler->render();
       if ($cache) {
@@ -1387,11 +1387,13 @@ public function render($display_id = NULL) {
     $module_handler->invokeAll('views_post_render', array($this, &$this->display_handler->output, $cache));
 
     // Let the themes play too, because post render is a very themey thing.
-    foreach ($GLOBALS['base_theme_info'] as $base) {
-      $module_handler->invoke($base, 'views_post_render', array($this));
-    }
+    if (isset($GLOBALS['base_theme_info']) && isset($GLOBALS['theme'])) {
+      foreach ($GLOBALS['base_theme_info'] as $base) {
+        $module_handler->invoke($base, 'views_post_render', array($this));
+      }
 
-    $module_handler->invoke($GLOBALS['theme'], 'views_post_render', array($this));
+      $module_handler->invoke($GLOBALS['theme'], 'views_post_render', array($this));
+    }
 
     return $this->display_handler->output;
   }
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 279f14c..9838099 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -278,20 +278,6 @@ function views_permission() {
 }
 
 /**
- * Implement hook_menu().
- */
-function views_menu() {
-  $items = array();
-  $items['views/ajax'] = array(
-    'title' => 'Views',
-    'theme callback' => 'ajax_base_page_theme',
-    'route_name' => 'views.ajax',
-    'type' => MENU_CALLBACK,
-  );
-  return $items;
-}
-
-/**
  * Implement hook_menu_alter().
  */
 function views_menu_alter(&$callbacks) {
diff --git a/core/modules/views/views.routing.yml b/core/modules/views/views.routing.yml
index 9abe5d5..691640d 100644
--- a/core/modules/views/views.routing.yml
+++ b/core/modules/views/views.routing.yml
@@ -2,5 +2,7 @@ views.ajax:
   path: '/views/ajax'
   defaults:
     _controller: '\Drupal\views\Controller\ViewAjaxController::ajaxView'
+  options:
+    _theme: ajax_base_page
   requirements:
     _access: 'TRUE'
diff --git a/core/tests/Drupal/Tests/Core/Theme/ThemeNegotiatorTest.php b/core/tests/Drupal/Tests/Core/Theme/ThemeNegotiatorTest.php
new file mode 100644
index 0000000..9779e63
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Theme/ThemeNegotiatorTest.php
@@ -0,0 +1,141 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Theme\ThemeNegotiatorTest.
+ */
+
+namespace Drupal\Tests\Core\Theme;
+
+use Drupal\Core\Theme\ThemeNegotiator;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Tests the theme negotiator.
+ *
+ * @see \Drupal\Core\Theme\ThemeNegotiator
+ */
+class ThemeNegotiatorTest extends UnitTestCase {
+
+  /**
+   * The mocked theme access checker.
+   *
+   * @var \Drupal\Core\Theme\ThemeAccessCheck|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $themeAccessCheck;
+
+  /**
+   * The actual tested theme negotiator.
+   *
+   * @var \Drupal\Core\Theme\ThemeNegotiator
+   */
+  protected $themeNegotiator;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Theme negotiator',
+      'description' => 'Tests the theme negotiator.',
+      'group' => 'Theme',
+    );
+  }
+
+  protected function setUp() {
+    $this->themeAccessCheck = $this->getMockBuilder('\Drupal\Core\Theme\ThemeAccessCheck')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $this->themeNegotiator = new ThemeNegotiator($this->themeAccessCheck);
+  }
+
+  /**
+   * Tests determining the theme.
+   *
+   * @see \Drupal\Core\Theme\ThemeNegotiator::determineActiveTheme()
+   */
+  public function testDetermineActiveTheme() {
+    $negotiator = $this->getMock('Drupal\Core\Theme\ThemeNegotiatorInterface');
+    $negotiator->expects($this->once())
+      ->method('determineActiveTheme')
+      ->will($this->returnValue('example_test'));
+
+    $this->themeNegotiator->addNegotiator($negotiator, 0);
+
+    $this->themeAccessCheck->expects($this->any())
+      ->method('checkAccess')
+      ->will($this->returnValue(TRUE));
+
+    $request = Request::create('/test-route');
+    $theme = $this->themeNegotiator->determineActiveTheme($request);
+
+    $this->assertEquals('example_test', $theme);
+    $this->assertEquals('example_test', $request->attributes->get('_theme_active'));
+  }
+
+  /**
+   * Tests determining with two negotiators checking the priority.
+   *
+   * @see \Drupal\Core\Theme\ThemeNegotiator::determineActiveTheme()
+   */
+  public function testDetermineActiveThemeWithPriority() {
+    $negotiator = $this->getMock('Drupal\Core\Theme\ThemeNegotiatorInterface');
+    $negotiator->expects($this->once())
+      ->method('determineActiveTheme')
+      ->will($this->returnValue('example_test'));
+
+    $this->themeNegotiator->addNegotiator($negotiator, 10);
+
+    $negotiator = $this->getMock('Drupal\Core\Theme\ThemeNegotiatorInterface');
+    $negotiator->expects($this->never())
+      ->method('determineActiveTheme');
+
+    $this->themeNegotiator->addNegotiator($negotiator, 0);
+
+    $this->themeAccessCheck->expects($this->any())
+      ->method('checkAccess')
+      ->will($this->returnValue(TRUE));
+
+    $request = Request::create('/test-route');
+    $theme = $this->themeNegotiator->determineActiveTheme($request);
+
+    $this->assertEquals('example_test', $theme);
+    $this->assertEquals('example_test', $request->attributes->get('_theme_active'));
+  }
+
+  /**
+   * Tests determining with two negotiators of which just one returns access.
+   *
+   * @see \Drupal\Core\Theme\ThemeNegotiator::determineActiveTheme()
+   */
+  public function testDetermineActiveThemeWithAccessCheck() {
+    $negotiator = $this->getMock('Drupal\Core\Theme\ThemeNegotiatorInterface');
+    $negotiator->expects($this->once())
+      ->method('determineActiveTheme')
+      ->will($this->returnValue('example_test'));
+
+    $this->themeNegotiator->addNegotiator($negotiator, 10);
+
+    $negotiator = $this->getMock('Drupal\Core\Theme\ThemeNegotiatorInterface');
+    $negotiator->expects($this->once())
+      ->method('determineActiveTheme')
+      ->will($this->returnValue('example_test2'));
+
+    $this->themeNegotiator->addNegotiator($negotiator, 0);
+
+    $this->themeAccessCheck->expects($this->at(0))
+      ->method('checkAccess')
+      ->with('example_test')
+      ->will($this->returnValue(FALSE));
+
+    $this->themeAccessCheck->expects($this->at(1))
+      ->method('checkAccess')
+      ->with('example_test2')
+      ->will($this->returnValue(TRUE));
+
+    $request = Request::create('/test-route');
+    $theme = $this->themeNegotiator->determineActiveTheme($request);
+
+    $this->assertEquals('example_test2', $theme);
+    $this->assertEquals('example_test2', $request->attributes->get('_theme_active'));
+  }
+
+}
