Browse Source

updated contrib modules

Bachir Soussi Chiadmi 7 years ago
parent
commit
b32fe9ef14
100 changed files with 3463 additions and 689 deletions
  1. 3 3
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar.info.yml
  2. 1 0
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar.libraries.yml
  3. 8 8
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar.module
  4. 3 3
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/admin_toolbar_tools.info.yml
  5. 161 144
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/admin_toolbar_tools.module
  6. 8 3
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/src/Controller/ToolbarController.php
  7. 1 0
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/src/Tests/AdminToolbarToolsAlterTest.php
  8. 15 12
      sites/all/modules/contrib/admin/admin_toolbar/css/admin.toolbar.css
  9. 15 2
      sites/all/modules/contrib/admin/admin_toolbar/js/admin_toolbar.js
  10. 158 0
      sites/all/modules/contrib/admin/admin_toolbar/js/jquery.hoverIntent.js
  11. 4 4
      sites/all/modules/contrib/admin/config_update/README.txt
  12. 3 3
      sites/all/modules/contrib/admin/config_update/config_update.info.yml
  13. 17 2
      sites/all/modules/contrib/admin/config_update/config_update_ui/config_update_ui.drush_testing.inc
  14. 3 3
      sites/all/modules/contrib/admin/config_update/config_update_ui/config_update_ui.info.yml
  15. 1 1
      sites/all/modules/contrib/admin/config_update/config_update_ui/config_update_ui.module
  16. 62 16
      sites/all/modules/contrib/admin/config_update/config_update_ui/src/Controller/ConfigUpdateController.php
  17. 97 0
      sites/all/modules/contrib/admin/config_update/config_update_ui/src/Tests/ConfigProfileOverridesTest.php
  18. 40 11
      sites/all/modules/contrib/admin/config_update/config_update_ui/src/Tests/ConfigUpdateTest.php
  19. 4 4
      sites/all/modules/contrib/admin/config_update/src/ConfigDiffInterface.php
  20. 8 8
      sites/all/modules/contrib/admin/config_update/src/ConfigDiffer.php
  21. 23 1
      sites/all/modules/contrib/admin/config_update/src/ConfigLister.php
  22. 3 7
      sites/all/modules/contrib/admin/config_update/src/ConfigListerWithProviders.php
  23. 308 0
      sites/all/modules/contrib/admin/config_update/tests/src/Unit/ConfigDifferTest.php
  24. 0 0
      sites/all/modules/contrib/dev/config_devel/LICENSE.txt
  25. 8 0
      sites/all/modules/contrib/dev/config_devel/composer.json
  26. 2 0
      sites/all/modules/contrib/dev/config_devel/config/install/config_devel.settings.yml
  27. 23 0
      sites/all/modules/contrib/dev/config_devel/config/schema/config_devel.schema.yml
  28. 13 0
      sites/all/modules/contrib/dev/config_devel/config_devel.info.yml
  29. 5 0
      sites/all/modules/contrib/dev/config_devel/config_devel.links.menu.yml
  30. 7 0
      sites/all/modules/contrib/dev/config_devel/config_devel.routing.yml
  31. 11 0
      sites/all/modules/contrib/dev/config_devel/config_devel.services.yml
  32. 246 0
      sites/all/modules/contrib/dev/config_devel/drush/config_devel.drush.inc
  33. 26 0
      sites/all/modules/contrib/dev/config_devel/src/Event/ConfigDevelEvents.php
  34. 79 0
      sites/all/modules/contrib/dev/config_devel/src/Event/ConfigDevelSaveEvent.php
  35. 136 0
      sites/all/modules/contrib/dev/config_devel/src/EventSubscriber/ConfigDevelAutoExportSubscriber.php
  36. 83 0
      sites/all/modules/contrib/dev/config_devel/src/EventSubscriber/ConfigDevelAutoImportSubscriber.php
  37. 66 0
      sites/all/modules/contrib/dev/config_devel/src/EventSubscriber/ConfigDevelSubscriberBase.php
  38. 100 0
      sites/all/modules/contrib/dev/config_devel/src/Form/ConfigDevelSettingsForm.php
  39. 32 0
      sites/all/modules/contrib/dev/config_devel/tests/src/Kernel/ConfigDevelSubscriberEntityTest.php
  40. 25 0
      sites/all/modules/contrib/dev/config_devel/tests/src/Kernel/ConfigDevelSubscriberRawTest.php
  41. 65 0
      sites/all/modules/contrib/dev/config_devel/tests/src/Kernel/ConfigDevelSubscriberTestBase.php
  42. 53 0
      sites/all/modules/contrib/dev/config_devel/tests/src/Unit/ConfigDevelAutoExportSubscriberTest.php
  43. 44 0
      sites/all/modules/contrib/dev/config_devel/tests/src/Unit/ConfigDevelTestBase.php
  44. 3 3
      sites/all/modules/contrib/dev/devel/devel.info.yml
  45. 3 3
      sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.info.yml
  46. 2 1
      sites/all/modules/contrib/dev/devel/devel_generate/src/Commands/DevelGenerateCommands.php
  47. 4 0
      sites/all/modules/contrib/dev/devel/devel_generate/src/DevelGenerateBase.php
  48. 9 9
      sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/ContentDevelGenerate.php
  49. 16 18
      sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/MenuDevelGenerate.php
  50. 5 14
      sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php
  51. 17 4
      sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/UserDevelGenerate.php
  52. 3 3
      sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/VocabularyDevelGenerate.php
  53. 3 3
      sites/all/modules/contrib/dev/devel/devel_generate/tests/modules/devel_generate_example/devel_generate_example.info.yml
  54. 1 1
      sites/all/modules/contrib/dev/devel/drush.services.yml
  55. 3 3
      sites/all/modules/contrib/dev/devel/kint/kint.info.yml
  56. 71 41
      sites/all/modules/contrib/dev/devel/src/Commands/DevelCommands.php
  57. 3 3
      sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/devel_dumper_test.info.yml
  58. 3 3
      sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/devel_entity_test.info.yml
  59. 3 3
      sites/all/modules/contrib/dev/devel/tests/modules/devel_test/devel_test.info.yml
  60. 10 10
      sites/all/modules/contrib/dev/devel/tests/src/Functional/DevelControllerTest.php
  61. 33 27
      sites/all/modules/contrib/dev/devel/tests/src/Functional/DevelDumperTest.php
  62. 3 3
      sites/all/modules/contrib/dev/devel/tests/src/Functional/DevelMenuLinksTest.php
  63. 8 8
      sites/all/modules/contrib/dev/devel/tests/src/Functional/DevelModulesReinstallTest.php
  64. 6 6
      sites/all/modules/contrib/dev/devel/tests/src/Functional/DevelSwitchUserTest.php
  65. 6 3
      sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DatabaseDataCollector.php
  66. 7 0
      sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/Decorators/Config/ConfigEntityStorageDecorator.php
  67. 28 30
      sites/all/modules/contrib/dev/devel/webprofiler/src/EventDispatcher/TraceableEventDispatcher.php
  68. 1 1
      sites/all/modules/contrib/dev/devel/webprofiler/src/Form/ConfigForm.php
  69. 1 1
      sites/all/modules/contrib/dev/devel/webprofiler/src/Views/TraceableViewExecutable.php
  70. 3 3
      sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.info.yml
  71. 1 1
      sites/all/modules/contrib/dev/entity/.travis.yml
  72. 1 1
      sites/all/modules/contrib/dev/entity/composer.json
  73. 4 4
      sites/all/modules/contrib/dev/entity/entity.info.yml
  74. 82 0
      sites/all/modules/contrib/dev/entity/entity.module
  75. 11 1
      sites/all/modules/contrib/dev/entity/entity.services.yml
  76. 1 1
      sites/all/modules/contrib/dev/entity/entity.views.inc
  77. 19 16
      sites/all/modules/contrib/dev/entity/src/Access/EntityRevisionRouteAccessChecker.php
  78. 27 0
      sites/all/modules/contrib/dev/entity/src/BundleFieldDefinition.php
  79. 102 0
      sites/all/modules/contrib/dev/entity/src/BundlePlugin/BundlePluginHandler.php
  80. 37 0
      sites/all/modules/contrib/dev/entity/src/BundlePlugin/BundlePluginHandlerInterface.php
  81. 94 0
      sites/all/modules/contrib/dev/entity/src/BundlePlugin/BundlePluginInstaller.php
  82. 34 0
      sites/all/modules/contrib/dev/entity/src/BundlePlugin/BundlePluginInstallerInterface.php
  83. 24 0
      sites/all/modules/contrib/dev/entity/src/BundlePlugin/BundlePluginInterface.php
  84. 76 0
      sites/all/modules/contrib/dev/entity/src/BundlePlugin/BundlePluginUninstallValidator.php
  85. 0 1
      sites/all/modules/contrib/dev/entity/src/Controller/RevisionOverviewController.php
  86. 57 19
      sites/all/modules/contrib/dev/entity/src/EntityAccessControlHandler.php
  87. 21 202
      sites/all/modules/contrib/dev/entity/src/EntityPermissionProvider.php
  88. 231 0
      sites/all/modules/contrib/dev/entity/src/EntityPermissionProviderBase.php
  89. 1 0
      sites/all/modules/contrib/dev/entity/src/EntityPermissions.php
  90. 1 1
      sites/all/modules/contrib/dev/entity/src/Plugin/Action/Derivative/DeleteActionDeriver.php
  91. 3 2
      sites/all/modules/contrib/dev/entity/src/Plugin/Derivative/RevisionsOverviewDeriver.php
  92. 0 1
      sites/all/modules/contrib/dev/entity/src/Revision/RevisionableContentEntityBase.php
  93. 138 0
      sites/all/modules/contrib/dev/entity/src/UncacheableEntityAccessControlHandler.php
  94. 143 0
      sites/all/modules/contrib/dev/entity/src/UncacheableEntityPermissionProvider.php
  95. 13 0
      sites/all/modules/contrib/dev/entity/tests/modules/entity_module_bundle_plugin_examples_test/entity_module_bundle_plugin_examples_test.info.yml
  96. 31 0
      sites/all/modules/contrib/dev/entity/tests/modules/entity_module_bundle_plugin_examples_test/src/Plugin/BundlePluginTest/Second.php
  97. 13 0
      sites/all/modules/contrib/dev/entity/tests/modules/entity_module_bundle_plugin_test/entity_module_bundle_plugin_test.info.yml
  98. 4 0
      sites/all/modules/contrib/dev/entity/tests/modules/entity_module_bundle_plugin_test/entity_module_bundle_plugin_test.services.yml
  99. 34 0
      sites/all/modules/contrib/dev/entity/tests/modules/entity_module_bundle_plugin_test/src/Annotation/BundlePluginTest.php
  100. 35 0
      sites/all/modules/contrib/dev/entity/tests/modules/entity_module_bundle_plugin_test/src/BundlePluginTestManager.php

+ 3 - 3
sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar.info.yml

@@ -6,8 +6,8 @@ type: module
 dependencies:
   - toolbar
 
-# Information added by Drupal.org packaging script on 2017-04-06
-version: '8.x-1.19'
+# Information added by Drupal.org packaging script on 2017-09-12
+version: '8.x-1.20'
 core: '8.x'
 project: 'admin_toolbar'
-datestamp: 1491487686
+datestamp: 1505199847

+ 1 - 0
sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar.libraries.yml

@@ -3,6 +3,7 @@ toolbar.tree:
     theme:
       css/admin.toolbar.css: {}
   js:
+    js/jquery.hoverIntent.js: {}
     js/admin_toolbar.js: {}
   dependencies:
     - core/jquery

+ 8 - 8
sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar.module

@@ -13,7 +13,7 @@ use Drupal\Core\Url;
  * Implements hook_toolbar_alter().
  */
 function admin_toolbar_toolbar_alter(&$items) {
-  $items['administration']['tray']['toolbar_administration']['#pre_render'] = array('admin_toolbar_prerender_toolbar_administration_tray');
+  $items['administration']['tray']['toolbar_administration']['#pre_render'] = ['admin_toolbar_prerender_toolbar_administration_tray'];
   $items['administration']['#attached']['library'][] = 'admin_toolbar/toolbar.tree';
 }
 
@@ -25,7 +25,7 @@ function admin_toolbar_help($route_name, RouteMatchInterface $route_match) {
     case 'help.page.admin_toolbar':
       $output = '';
       $output .= '<h3>' . t('About') . '</h3>';
-      $output .= '<p>' . t('The Admin Toolbar module enhances the <a href=":toolbar">Toolbar</a> module by providing fast access to all the administrative links at the top of your site. Admin Toolbar remains a very "lightweight" module by closely integrating with all Toolbar functionality. It can be used in conjunction with all the sub or complimentary modules, listed on <a href="https://www.drupal.org/project/admin_toolbar">Admin Toolbar</a>, for quick access to system commands such as Flush all caches, <a href=":automated_cron">Run cron</a>, Run Updates, etc... For more information, see <a href=":admin_toolbar_documentation">the online documentation for the Admin Toolbar module</a>.', array(':toolbar' => Url::fromRoute('help.page', array('name' => 'toolbar'))->toString(), ':automated_cron' => (\Drupal::moduleHandler()->moduleExists('automated_cron')) ? Url::fromRoute('help.page', array('name' => 'automated_cron'))->toString() : '#', ':admin_toolbar_documentation' => 'https://www.drupal.org/node/2713693')) . '</p>';
+      $output .= '<p>' . t('The Admin Toolbar module enhances the <a href=":toolbar">Toolbar</a> module by providing fast access to all the administrative links at the top of your site. Admin Toolbar remains a very "lightweight" module by closely integrating with all Toolbar functionality. It can be used in conjunction with all the sub or complimentary modules, listed on <a href="https://www.drupal.org/project/admin_toolbar">Admin Toolbar</a>, for quick access to system commands such as Flush all caches, <a href=":automated_cron">Run cron</a>, Run Updates, etc... For more information, see <a href=":admin_toolbar_documentation">the online documentation for the Admin Toolbar module</a>.', [':toolbar' => Url::fromRoute('help.page', ['name' => 'toolbar'])->toString(), ':automated_cron' => (\Drupal::moduleHandler()->moduleExists('automated_cron')) ? Url::fromRoute('help.page', ['name' => 'automated_cron'])->toString() : '#', ':admin_toolbar_documentation' => 'https://www.drupal.org/node/2713693']) . '</p>';
       $output .= '<h3>' . t('Uses') . '</h3>';
       $output .= '<p>' . t('The Admin Toolbar greatly improves the user experience for those who regularly interact with the Drupal Toolbar by providing fast, full access to all links in the Drupal Toolbar without having to click to get there.') . '</p>';
 
@@ -52,11 +52,11 @@ function admin_toolbar_prerender_toolbar_administration_tray(array $element) {
   $parameters = new MenuTreeParameters();
   $parameters->setRoot('system.admin')->excludeRoot()->setMaxDepth(4)->onlyEnabledLinks();
   $tree = $menu_tree->load(NULL, $parameters);
-  $manipulators = array(
-    array('callable' => 'menu.default_tree_manipulators:checkAccess'),
-    array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
-    array('callable' => 'toolbar_tools_menu_navigation_links'),
-  );
+  $manipulators = [
+    ['callable' => 'menu.default_tree_manipulators:checkAccess'],
+    ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
+    ['callable' => 'toolbar_tools_menu_navigation_links'],
+  ];
   $tree = $menu_tree->transform($tree, $manipulators);
   $element['administration_menu'] = $menu_tree->build($tree);
 
@@ -83,7 +83,7 @@ function toolbar_tools_menu_navigation_links(array $tree) {
     $definition = $link->getPluginDefinition();
 
     $element->options['attributes']['class'][] = 'toolbar-icon';
-    $element->options['attributes']['class'][] = 'toolbar-icon-' . strtolower(str_replace(array('.', ' ', '_'), array('-', '-', '-'), $definition['id']));
+    $element->options['attributes']['class'][] = 'toolbar-icon-' . strtolower(str_replace(['.', ' ', '_'], ['-', '-', '-'], $definition['id']));
     $element->options['attributes']['title'] = $link->getDescription();
   }
   return $tree;

+ 3 - 3
sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/admin_toolbar_tools.info.yml

@@ -6,8 +6,8 @@ type: module
 dependencies:
   - admin_toolbar
 
-# Information added by Drupal.org packaging script on 2017-04-06
-version: '8.x-1.19'
+# Information added by Drupal.org packaging script on 2017-09-12
+version: '8.x-1.20'
 core: '8.x'
 project: 'admin_toolbar'
-datestamp: 1491487686
+datestamp: 1505199847

+ 161 - 144
sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/admin_toolbar_tools.module

@@ -11,18 +11,17 @@ use Drupal\Core\Routing\RouteMatchInterface;
  * Implements hook_toolbar().
  */
 function admin_toolbar_tools_toolbar() {
-  $items = array();
-  $items['admin_toolbar_tools'] = array(
+  $items = [];
+  $items['admin_toolbar_tools'] = [
     '#type' => 'toolbar_item',
-    'tab' => array(
+    'tab' => [
       '#type' => 'link',
-      '#attributes' => array(
-        'class' => array('toolbar-icon', 'toolbar-icon-admin-toolbar-tools-help'),
-      ),
-    ),
-    '#attached' => array('library' => array('admin_toolbar_tools/toolbar.icon'),
-    ),
-  );
+      '#attributes' => [
+        'class' => ['toolbar-icon', 'toolbar-icon-admin-toolbar-tools-help'],
+      ],
+    ],
+    '#attached' => ['library' => ['admin_toolbar_tools/toolbar.icon']],
+  ];
 
   return $items;
 }
@@ -49,19 +48,19 @@ function admin_toolbar_tools_menu_links_discovered_alter(&$links) {
   $moduleHandler = \Drupal::moduleHandler();
   $entityTypeManager = \Drupal::entityTypeManager();
   $routeProvider = \Drupal::service('router.route_provider');
-  $routes = array();
+  $routes = [];
   foreach ($routeProvider->getAllRoutes() as $route_name => $route) {
     $routes[] = $route_name;
   }
 
   $entityTypes = $entityTypeManager->getDefinitions();
-  $content_entities = array();
+  $content_entities = [];
   foreach ($entityTypes as $key => $entityType) {
     if ($entityType->getBundleEntityType() && ($entityType->get('field_ui_base_route') != '')) {
-      $content_entities[$key] = array(
+      $content_entities[$key] = [
         'content_entity' => $key,
         'content_entity_bundle' => $entityType->getBundleEntityType(),
-      );
+      ];
     }
   }
 
@@ -75,23 +74,23 @@ function admin_toolbar_tools_menu_links_discovered_alter(&$links) {
       if (in_array('entity.' . $content_entity_bundle . '.overview_form', $routes)) {
         // Some bundles have an overview/list form that make a better root link.
         $content_entity_bundle_root = 'entity.' . $content_entity_bundle . '.overview_form.' . $machine_name;
-        $links[$content_entity_bundle_root] = array(
-          'title' => $bundle->label(),
+        $links[$content_entity_bundle_root] = [
+          'title' => t($bundle->label()),
           'route_name' => 'entity.' . $content_entity_bundle . '.overview_form',
           'menu_name' => 'admin',
           'parent' => 'entity.' . $content_entity_bundle . '.collection',
-          'route_parameters' => array($content_entity_bundle => $machine_name),
-        );
+          'route_parameters' => [$content_entity_bundle => $machine_name],
+        ];
       }
       if (in_array('entity.' . $content_entity_bundle . '.edit_form', $routes)) {
         $key = 'entity.' . $content_entity_bundle . '.edit_form.' . $machine_name;
-        $links[$key] = array(
-          'title' => $bundle->label(),
+        $links[$key] = [
+          'title' => t($bundle->label()),
           'route_name' => 'entity.' . $content_entity_bundle . '.edit_form',
           'menu_name' => 'admin',
           'parent' => 'entity.' . $content_entity_bundle . '.collection',
-          'route_parameters' => array($content_entity_bundle => $machine_name),
-        );
+          'route_parameters' => [$content_entity_bundle => $machine_name],
+        ];
         if (empty($content_entity_bundle_root)) {
           $content_entity_bundle_root = $key;
         }
@@ -102,396 +101,413 @@ function admin_toolbar_tools_menu_links_discovered_alter(&$links) {
       }
       if ($moduleHandler->moduleExists('field_ui')) {
         if (in_array('entity.' . $content_entity . '.field_ui_fields', $routes)) {
-          $links['entity.' . $content_entity . '.field_ui_fields' . $machine_name] = array(
+          $links['entity.' . $content_entity . '.field_ui_fields' . $machine_name] = [
             'title' => t('Manage fields'),
             'route_name' => 'entity.' . $content_entity . '.field_ui_fields',
             'menu_name' => 'admin',
             'parent' => $content_entity_bundle_root,
-            'route_parameters' => array($content_entity_bundle => $machine_name),
+            'route_parameters' => [$content_entity_bundle => $machine_name],
             'weight' => 1,
-          );
+          ];
         }
         if (in_array('entity.entity_form_display.' . $content_entity . '.default', $routes)) {
-          $links['entity.entity_form_display.' . $content_entity . '.default' . $machine_name] = array(
+          $links['entity.entity_form_display.' . $content_entity . '.default' . $machine_name] = [
             'title' => t('Manage form display'),
             'route_name' => 'entity.entity_form_display.' . $content_entity . '.default',
             'menu_name' => 'admin',
             'parent' => $content_entity_bundle_root,
-            'route_parameters' => array($content_entity_bundle => $machine_name),
+            'route_parameters' => [$content_entity_bundle => $machine_name],
             'weight' => 2,
-          );
+          ];
         }
         if (in_array('entity.entity_view_display.' . $content_entity . '.default', $routes)) {
-          $links['entity.entity_view_display.' . $content_entity . '.default.' . $machine_name] = array(
+          $links['entity.entity_view_display.' . $content_entity . '.default.' . $machine_name] = [
             'title' => t('Manage display'),
             'route_name' => 'entity.entity_view_display.' . $content_entity . '.default',
             'menu_name' => 'admin',
             'parent' => $content_entity_bundle_root,
-            'route_parameters' => array($content_entity_bundle => $machine_name),
+            'route_parameters' => [$content_entity_bundle => $machine_name],
             'weight' => 3,
-          );
+          ];
         }
       }
       if ($moduleHandler->moduleExists('devel') && in_array('entity.' . $content_entity_bundle . '.devel_load', $routes)) {
-        $links['entity.' . $content_entity_bundle . '.devel_load.' . $machine_name] = array(
+        $links['entity.' . $content_entity_bundle . '.devel_load.' . $machine_name] = [
           'title' => t('Devel'),
           'route_name' => 'entity.' . $content_entity_bundle . '.devel_load',
           'menu_name' => 'admin',
           'parent' => $content_entity_bundle_root,
-          'route_parameters' => array($content_entity_bundle => $machine_name),
+          'route_parameters' => [$content_entity_bundle => $machine_name],
           'weight' => 4,
-        );
+        ];
       }
       if (in_array('entity.' . $content_entity_bundle . '.delete_form', $routes)) {
-        $links['entity.' . $content_entity_bundle . '.delete_form.' . $machine_name] = array(
+        $links['entity.' . $content_entity_bundle . '.delete_form.' . $machine_name] = [
           'title' => t('Delete'),
           'route_name' => 'entity.' . $content_entity_bundle . '.delete_form',
           'menu_name' => 'admin',
           'parent' => $content_entity_bundle_root,
-          'route_parameters' => array($content_entity_bundle => $machine_name),
+          'route_parameters' => [$content_entity_bundle => $machine_name],
           'weight' => 5,
-        );
+        ];
       }
     }
   }
 
   // Add user links.
-  $links['user.admin_create'] = array(
+  $links['user.admin_create'] = [
     'title' => t('Add a new user'),
     'route_name' => 'user.admin_create',
     'menu_name' => 'admin',
     'parent' => 'entity.user.collection',
-  );
-  $links['user.admin_permissions'] = array(
+  ];
+  $links['user.admin_permissions'] = [
     'title' => t('Permissions'),
     'route_name' => 'user.admin_permissions',
     'menu_name' => 'admin',
     'parent' => 'entity.user.collection',
-  );
-  $links['entity.user_role.collection'] = array(
+  ];
+  $links['entity.user_role.collection'] = [
     'title' => t('Roles'),
     'route_name' => 'entity.user_role.collection',
     'menu_name' => 'admin',
     'parent' => 'entity.user.collection',
-  );
-  $links['admin_toolbar_tools.user.logout'] = array(
+  ];
+  $links['admin_toolbar_tools.user.logout'] = [
     'title' => t('Logout'),
     'route_name' => 'user.logout',
     'parent' => 'admin_toolbar_tools.help',
     'weight' => 10,
-  );
-  $links['user.role_add'] = array(
+  ];
+  $links['user.role_add'] = [
     'title' => t('Add a new role'),
     'route_name' => 'user.role_add',
     'menu_name' => 'admin',
     'parent' => 'entity.user_role.collection',
     'weight' => -5,
-  );
+  ];
   if ($moduleHandler->moduleExists('field_ui')) {
-    $links['entity.user.field_ui_fields_'] = array(
+    $links['entity.user.field_ui_fields_'] = [
       'title' => t('Manage fields'),
       'route_name' => 'entity.user.field_ui_fields',
       'menu_name' => 'admin',
       'parent' => 'entity.user.admin_form',
-    );
-    $links['entity.entity_form_display.user.default_'] = array(
+    ];
+    $links['entity.entity_form_display.user.default_'] = [
       'title' => t('Manage form display'),
       'route_name' => 'entity.entity_form_display.user.default',
       'menu_name' => 'admin',
       'parent' => 'entity.user.admin_form',
-    );
-    $links['entity.entity_view_display.user.default_'] = array(
+    ];
+    $links['entity.entity_view_display.user.default_'] = [
       'title' => t('Manage display'),
       'route_name' => 'entity.entity_view_display.user.default',
       'menu_name' => 'admin',
       'parent' => 'entity.user.admin_form',
-    );
+    ];
   }
   foreach (user_roles() as $role) {
-    $links['entity.user_role.edit_form.' . $role->id()] = array(
-      'title' => $role->label(),
+    $links['entity.user_role.edit_form.' . $role->id()] = [
+      'title' => t($role->label()),
       'route_name' => 'entity.user_role.edit_form',
       'menu_name' => 'admin',
       'parent' => 'entity.user_role.collection',
-      'route_parameters' => array('user_role' => $role->id()),
-    );
-    $links['entity.user_role.edit_permissions_form.' . $role->id()] = array(
+      'route_parameters' => ['user_role' => $role->id()],
+    ];
+    $links['entity.user_role.edit_permissions_form.' . $role->id()] = [
       'title' => t('Edit permissions'),
       'route_name' => 'entity.user_role.edit_permissions_form',
       'menu_name' => 'admin',
       'parent' => 'entity.user_role.edit_form.' . $role->id(),
-      'route_parameters' => array('user_role' => $role->id()),
-    );
-    $links['entity.user_role.delete_form.' . $role->id()] = array(
+      'route_parameters' => ['user_role' => $role->id()],
+    ];
+    $links['entity.user_role.delete_form.' . $role->id()] = [
       'title' => t('Delete'),
       'route_name' => 'entity.user_role.delete_form',
       'menu_name' => 'admin',
       'parent' => 'entity.user_role.edit_form.' . $role->id(),
-      'route_parameters' => array('user_role' => $role->id()),
-    );
+      'route_parameters' => ['user_role' => $role->id()],
+    ];
     if ($moduleHandler->moduleExists('devel')) {
-      $links['entity.user_role.devel_load.' . $role->id()] = array(
+      $links['entity.user_role.devel_load.' . $role->id()] = [
         'title' => t('Devel'),
         'route_name' => 'entity.user_role.devel_load',
         'menu_name' => 'admin',
         'parent' => 'entity.user_role.edit_form.' . $role->id(),
-        'route_parameters' => array('user_role' => $role->id()),
-      );
+        'route_parameters' => ['user_role' => $role->id()],
+      ];
     }
   }
 
   if ($moduleHandler->moduleExists('node')) {
-    $links['node.add_page']['parent'] = 'system.admin_content';
-    $links['node.type_add'] = array(
+    $links['admin_toolbar_tools.add_content'] = $links['node.add_page'];
+    $links['admin_toolbar_tools.add_content']['parent'] = 'system.admin_content';
+    $links['node.type_add'] = [
       'title' => t('Add content type'),
       'route_name' => 'node.type_add',
       'menu_name' => 'admin',
       'parent' => 'entity.node_type.collection',
       'weight' => -5,
-    );
+    ];
     // Add node links for each content type.
     foreach (\Drupal::entityTypeManager()->getStorage('node_type')->loadMultiple() as $type) {
-      $links['node.add.' . $type->id()] = array(
+      $links['node.add.' . $type->id()] = [
         'title' => t($type->label()),
         'route_name' => 'node.add',
-        'parent' => 'node.add_page',
-        'route_parameters' => array('node_type' => $type->id()),
-      );
+        'parent' => 'admin_toolbar_tools.add_content',
+        'route_parameters' => ['node_type' => $type->id()],
+      ];
     }
   }
 
   if ($moduleHandler->moduleExists('field_ui')) {
-    $links['field_ui.entity_form_mode_add'] = array(
+    $links['field_ui.entity_form_mode_add'] = [
       'title' => t('Add new form mode'),
       'route_name' => 'field_ui.entity_form_mode_add',
       'menu_name' => 'admin',
       'parent' => 'entity.entity_form_mode.collection',
-    );
-    $links['field_ui.entity_view_mode_add'] = array(
+    ];
+    $links['field_ui.entity_view_mode_add'] = [
       'title' => t('Add new view mode'),
       'route_name' => 'field_ui.entity_view_mode_add',
       'menu_name' => 'admin',
       'parent' => 'entity.entity_view_mode.collection',
-    );
+    ];
   }
 
   if ($moduleHandler->moduleExists('taxonomy')) {
-    $links['entity.taxonomy_vocabulary.add_form'] = array(
+    $links['entity.taxonomy_vocabulary.add_form'] = [
       'title' => t('Add vocabulary'),
       'route_name' => 'entity.taxonomy_vocabulary.add_form',
       'menu_name' => 'admin',
       'parent' => 'entity.taxonomy_vocabulary.collection',
       'weight' => -5,
-    );
+    ];
   }
 
   if ($moduleHandler->moduleExists('menu_ui')) {
-    $links['entity.menu.add_form'] = array(
+    $links['entity.menu.add_form'] = [
       'title' => t('Add menu'),
       'route_name' => 'entity.menu.add_form',
       'menu_name' => 'admin',
       'parent' => 'entity.menu.collection',
       'weight' => -50,
-    );
+    ];
     // Adds links to /admin/structure/menu.
     foreach (menu_ui_get_menus() as $machine_name => $label) {
-      $links['entity.menu.edit_form.' . $machine_name] = array(
-        'title' => $label,
+      $links['entity.menu.edit_form.' . $machine_name] = [
+        'title' => t($label),
         'route_name' => 'entity.menu.edit_form',
         'menu_name' => 'admin',
         'parent' => 'entity.menu.collection',
-        'route_parameters' => array('menu' => $machine_name),
-      );
-      $links['entity.menu.delete_form.' . $machine_name] = array(
+        'route_parameters' => ['menu' => $machine_name],
+      ];
+      $links['entity.menu.delete_form.' . $machine_name] = [
         'title' => t('Delete'),
         'route_name' => 'entity.menu.delete_form',
         'menu_name' => 'admin',
         'parent' => 'entity.menu.edit_form.' . $machine_name,
-        'route_parameters' => array('menu' => $machine_name),
-      );
+        'route_parameters' => ['menu' => $machine_name],
+      ];
       if ($moduleHandler->moduleExists('devel')) {
-        $links['entity.menu.devel_load.' . $machine_name] = array(
+        $links['entity.menu.devel_load.' . $machine_name] = [
           'title' => t('Devel'),
           'route_name' => 'entity.menu.devel_load',
           'menu_name' => 'admin',
           'parent' => 'entity.menu.edit_form.' . $machine_name,
-          'route_parameters' => array('menu' => $machine_name),
-        );
+          'route_parameters' => ['menu' => $machine_name],
+        ];
       }
-      $links['entity.menu.add_link_form.' . $machine_name] = array(
+      $links['entity.menu.add_link_form.' . $machine_name] = [
         'title' => t('Add link'),
         'route_name' => 'entity.menu.add_link_form',
         'menu_name' => 'admin',
         'parent' => 'entity.menu.edit_form.' . $machine_name,
-        'route_parameters' => array('menu' => $machine_name),
-      );
+        'route_parameters' => ['menu' => $machine_name],
+      ];
     }
   }
 
   // If module block_content is enabled.
   if ($moduleHandler->moduleExists('block_content')) {
-    $links['block_content.add_page'] = array(
+    $links['block_content.add_page'] = [
       'title' => t('Add custom block'),
       'route_name' => 'block_content.add_page',
       'menu_name' => 'admin',
       'parent' => 'block.admin_display',
       'weight' => -100,
-    );
-    $links['entity.block_content.collection'] = array(
+    ];
+    $links['entity.block_content.collection'] = [
       'title' => t('Custom block library'),
       'route_name' => 'entity.block_content.collection',
       'menu_name' => 'admin',
       'parent' => 'block.admin_display',
-    );
-    $links['entity.block_content_type.collection'] = array(
+    ];
+    $links['entity.block_content_type.collection'] = [
       'title' => t('Types'),
       'route_name' => 'entity.block_content_type.collection',
       'menu_name' => 'admin',
       'parent' => 'block.admin_display',
-    );
+    ];
   }
 
   // If module Contact is enabled.
   if ($moduleHandler->moduleExists('contact')) {
-    $links['contact.form_add'] = array(
+    $links['contact.form_add'] = [
       'title' => t('Add contact form'),
       'route_name' => 'contact.form_add',
       'menu_name' => 'admin',
       'parent' => 'entity.contact_form.collection',
       'weight' => -5,
-    );
+    ];
   }
 
   // If module Update Manager is enabled.
   if ($moduleHandler->moduleExists('update')) {
-    $links['update.module_update'] = array(
+    $links['update.module_update'] = [
       'title' => t('Update'),
       'route_name' => 'update.module_update',
       'menu_name' => 'admin',
       'parent' => 'system.modules_list',
-    );
-    $links['update.module_install'] = array(
+    ];
+    $links['update.module_install'] = [
       'title' => t('Install new module'),
       'route_name' => 'update.module_install',
       'menu_name' => 'admin',
       'parent' => 'system.modules_list',
-    );
+    ];
   }
 
   // If module Devel is enabled.
   if ($moduleHandler->moduleExists('devel')) {
-    $links['admin_development'] = array(
+    $links['admin_development'] = [
       'title' => t('Development'),
       'route_name' => 'system.admin_config_development',
       'menu_name' => 'admin',
       'parent' => 'admin_toolbar_tools.help',
       'weight' => '-8',
-    );
-    $links['admin_toolbar_tools.devel.admin_settings'] = array(
+    ];
+    $links['admin_toolbar_tools.devel.admin_settings'] = [
       'title' => t('Devel settings'),
       'route_name' => 'devel.admin_settings',
       'menu_name' => 'admin',
       'parent' => 'admin_development',
       'weight' => '-1',
-    );
-    $links['admin_toolbar_tools.devel.configs_list'] = array(
+    ];
+    if ($moduleHandler->moduleExists('webprofiler')) {
+      $links['admin_menu_tools.devel.webprofiler'] = [
+        'title' => t('Web Profiler settings'),
+        'route_name' => 'webprofiler.settings',
+        'menu_name' => 'admin',
+        'parent' => 'admin_development',
+      ];
+    }
+    $links['admin_toolbar_tools.devel.configs_list'] = [
       'title' => t('Config editor'),
       'route_name' => 'devel.configs_list',
       'menu_name' => 'admin',
       'parent' => 'admin_development',
-    );
-    $links['admin_toolbar_tools.devel.reinstall'] = array(
+    ];
+    $links['admin_toolbar_tools.devel.reinstall'] = [
       'title' => t('Reinstall modules'),
       'route_name' => 'devel.reinstall',
       'parent' => 'admin_development',
-    );
-    $links['admin_toolbar_tools.devel.menu_rebuild'] = array(
+    ];
+    $links['admin_toolbar_tools.devel.menu_rebuild'] = [
       'title' => t('Rebuild menu'),
       'route_name' => 'devel.menu_rebuild',
       'menu_name' => 'admin',
       'parent' => 'admin_development',
-    );
-    $links['admin_toolbar_tools.devel.state_system_page'] = array(
+    ];
+    $links['admin_toolbar_tools.devel.state_system_page'] = [
       'title' => t('State editor'),
       'route_name' => 'devel.state_system_page',
       'menu_name' => 'admin',
       'parent' => 'admin_development',
-    );
-    $links['admin_toolbar_tools.devel.theme_registry'] = array(
+    ];
+    $links['admin_toolbar_tools.devel.theme_registry'] = [
       'title' => t('Theme registry'),
       'route_name' => 'devel.theme_registry',
       'menu_name' => 'admin',
       'parent' => 'admin_development',
-    );
-    $links['admin_toolbar_tools.devel.entity_info_page'] = array(
+    ];
+    $links['admin_toolbar_tools.devel.entity_info_page'] = [
       'title' => t('Entity Info'),
       'route_name' => 'devel.entity_info_page',
       'menu_name' => 'admin',
       'parent' => 'admin_development',
-    );
-    $links['admin_toolbar_tools.devel.execute_php'] = array(
+    ];
+    $links['admin_toolbar_tools.devel.execute_php'] = [
       'title' => t('Execute PHP Code'),
       'route_name' => 'devel.execute_php',
       'menu_name' => 'admin',
       'parent' => 'admin_development',
-    );
-    $links['admin_toolbar_tools.devel.session'] = array(
+    ];
+    $links['admin_toolbar_tools.devel.session'] = [
       'title' => t('Session viewer'),
       'route_name' => 'devel.session',
       'menu_name' => 'admin',
       'parent' => 'admin_development',
-    );
-    $links['admin_toolbar_tools.devel.elements_page'] = array(
+    ];
+    $links['admin_toolbar_tools.devel.elements_page'] = [
       'title' => t('Form API field types'),
       'route_name' => 'devel.elements_page',
       'menu_name' => 'admin',
       'parent' => 'admin_development',
-    );
+    ];
   }
 
   // If module Views Ui enabled.
   if ($moduleHandler->moduleExists('views_ui')) {
-    $links['views_ui.add'] = array(
+    $links['views_ui.add'] = [
       'title' => t('Add new view'),
       'route_name' => 'views_ui.add',
       'menu_name' => 'admin',
       'parent' => 'entity.view.collection',
       'weight' => -5,
-    );
+    ];
   }
 
-  $links['system.theme_settings_'] = array(
+  $links['system.theme_settings_'] = [
     'title' => t('Settings'),
     'route_name' => 'system.theme_settings',
     'menu_name' => 'admin',
     'parent' => 'system.themes_page',
-  );
+  ];
 
+  if ($moduleHandler->moduleExists('webprofiler')) {
+    $links['admin_menu_tools.devel.webprofiler'] = [
+      'title' => t('Webprofiler settings'),
+      'route_name' => 'webprofiler.settings',
+      'menu_name' => 'admin',
+      'parent' => 'admin_development',
+    ];
+  }
   if ($moduleHandler->moduleExists('update')) {
-    $links['update.theme_install_'] = array(
+    $links['update.theme_install_'] = [
       'title' => t('Install new theme'),
       'route_name' => 'update.theme_install',
       'menu_name' => 'admin',
       'parent' => 'system.themes_page',
-    );
-    $links['update.theme_update_'] = array(
+    ];
+    $links['update.theme_update_'] = [
       'title' => t('Update'),
       'route_name' => 'update.theme_update',
       'menu_name' => 'admin',
       'parent' => 'system.themes_page',
-    );
+    ];
     // Lists installed themes.
     $installed_themes = admin_toolbar_tools_installed_themes();
     foreach ($installed_themes as $key_theme => $label_theme) {
-      $links['system.theme_settings_theme.' . $key_theme] = array(
+      $links['system.theme_settings_theme.' . $key_theme] = [
         'title' => t($label_theme),
         'route_name' => 'system.theme_settings_theme',
         'menu_name' => 'admin',
         'parent' => 'system.theme_settings_',
-        'route_parameters' => array(
+        'route_parameters' => [
           'theme' => $key_theme,
-        ),
-      );
+        ],
+      ];
     }
   }
 }
@@ -503,11 +519,12 @@ function admin_toolbar_tools_menu_links_discovered_alter(&$links) {
  *   An array of friendly theme names, keyed by the machine name.
  */
 function admin_toolbar_tools_installed_themes() {
-  $all_themes = \Drupal::service('theme_handler')->listInfo();
-  $themes_installed = array();
+  $themeHandler = \Drupal::service('theme_handler');
+  $all_themes = $themeHandler->listInfo();
+  $themes_installed = [];
   foreach ($all_themes as $key_theme => $theme) {
-    if (\Drupal::service('theme_handler')->hasUi($key_theme)) {
-      $themes_installed[$key_theme] = $theme->getName();
+    if ($themeHandler->hasUi($key_theme)) {
+      $themes_installed[$key_theme] = $themeHandler->getName($key_theme);
     }
   }
 

+ 8 - 3
sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/src/Controller/ToolbarController.php

@@ -6,6 +6,7 @@ use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\CronInterface;
 use Drupal\Core\Routing\TrustedRedirectResponse;
+use Drupal\Component\Datetime\Time;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use Drupal\Core\Menu\ContextualLinkManager;
@@ -31,6 +32,7 @@ class ToolbarController extends ControllerBase {
   protected $localTaskLinkManager;
   protected $localActionLinkManager;
   protected $cacheRender;
+  protected $time;
 
   /**
    * Constructs a CronController object.
@@ -43,13 +45,15 @@ class ToolbarController extends ControllerBase {
                               ContextualLinkManager $contextualLinkManager,
                               LocalTaskManager $localTaskLinkManager,
                               LocalActionManager $localActionLinkManager,
-                              CacheBackendInterface $cacheRender) {
+                              CacheBackendInterface $cacheRender,
+                              Time $time) {
     $this->cron = $cron;
     $this->menuLinkManager = $menuLinkManager;
     $this->contextualLinkManager = $contextualLinkManager;
     $this->localTaskLinkManager = $localTaskLinkManager;
     $this->localActionLinkManager = $localActionLinkManager;
     $this->cacheRender = $cacheRender;
+    $this->time = $time;
   }
 
   /**
@@ -62,7 +66,8 @@ class ToolbarController extends ControllerBase {
       $container->get('plugin.manager.menu.contextual_link'),
       $container->get('plugin.manager.menu.local_task'),
       $container->get('plugin.manager.menu.local_action'),
-      $container->get('cache.render')
+      $container->get('cache.render'),
+      $container->get('datetime.time')
     );
   }
 
@@ -93,7 +98,7 @@ class ToolbarController extends ControllerBase {
    */
   public function flush_js_css() {
     \Drupal::state()
-      ->set('system.css_js_query_string', base_convert(REQUEST_TIME, 10, 36));
+      ->set('system.css_js_query_string', base_convert($this->time->getCurrentTime(), 10, 36));
     drupal_set_message($this->t('CSS and JavaScript cache cleared.'));
     return new RedirectResponse($this->reload_page());
   }

+ 1 - 0
sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/src/Tests/AdminToolbarToolsAlterTest.php

@@ -34,6 +34,7 @@ class AdminToolbarToolsAlterTest extends WebTestBase {
     $this->adminUser = $this->drupalCreateUser([
       'access toolbar',
       'access administration pages',
+      'administer site configuration',
     ]);
     $this->drupalLogin($this->adminUser);
   }

+ 15 - 12
sites/all/modules/contrib/admin/admin_toolbar/css/admin.toolbar.css

@@ -1,4 +1,7 @@
 /*---------------------- menu horizontal hover---- Krout Fethi FrontEnd Developer-----*/
+#toolbar-bar {
+  z-index: 9999;
+}
 .toolbar-tray-horizontal .menu-item:hover {
   background: #fff;
 }
@@ -33,20 +36,20 @@
   border-top: 1px solid #dddddd;
 }
 
-.toolbar-tray-horizontal li.menu-item--expanded:hover ul ul,
-.toolbar-tray-horizontal li.menu-item--expanded:hover ul ul ul,
-.toolbar-tray-horizontal li.menu-item--expanded:hover ul ul ul ul,
-.toolbar-tray-horizontal li.menu-item--expanded:hover ul ul ul ul ul {
+.toolbar-tray-horizontal li.menu-item--expanded.hover-intent ul ul,
+.toolbar-tray-horizontal li.menu-item--expanded.hover-intent ul ul ul,
+.toolbar-tray-horizontal li.menu-item--expanded.hover-intent ul ul ul ul,
+.toolbar-tray-horizontal li.menu-item--expanded.hover-intent ul ul ul ul ul {
   display: none;
   left: -999em; /* LTR */
 }
 
 /* Lists nested under hovered list items */
-.toolbar-tray-horizontal li.menu-item--expanded:hover ul,
-.toolbar-tray-horizontal li li.menu-item--expanded:hover ul,
-.toolbar-tray-horizontal li li li.menu-item--expanded:hover ul,
-.toolbar-tray-horizontal li li li li.menu-item--expanded:hover ul,
-.toolbar-tray-horizontal li li li li li.menu-item--expanded:hover ul {
+.toolbar-tray-horizontal li.menu-item--expanded.hover-intent ul,
+.toolbar-tray-horizontal li li.menu-item--expanded.hover-intent ul,
+.toolbar-tray-horizontal li li li.menu-item--expanded.hover-intent ul,
+.toolbar-tray-horizontal li li li li.menu-item--expanded.hover-intent ul,
+.toolbar-tray-horizontal li li li li li.menu-item--expanded.hover-intent ul {
   display: block;
   left: auto; /* LTR */
 }
@@ -56,7 +59,7 @@
   padding: 12px 15px 12px 12px;
 }
 
-.toolbar-tray-horizontal ul li.menu-item--expanded:hover ul {
+.toolbar-tray-horizontal ul li.menu-item--expanded.hover-intent ul {
   display: block;
   position: absolute;
   width: 200px;
@@ -74,12 +77,12 @@
   background-repeat: no-repeat;
 }
 
-.toolbar-tray-horizontal ul li.menu-item--expanded .menu-item:hover ul {
+.toolbar-tray-horizontal ul li.menu-item--expanded .menu-item.hover-intent ul {
   display: block;
   margin: -40px 0 0 197px;
 }
 
-.toolbar-tray-horizontal li:hover ul li {
+.toolbar-tray-horizontal li.hover-intent ul li {
   float: none;
 }
 

+ 15 - 2
sites/all/modules/contrib/admin/admin_toolbar/js/admin_toolbar.js

@@ -1,5 +1,18 @@
-(function($) {
-  $(document).ready(function() {
+(function ($) {
+  $(document).ready(function () {
     $('a.toolbar-icon').removeAttr('title');
+
+    $('.toolbar-tray-horizontal li.menu-item--expanded, .toolbar-tray-horizontal ul li.menu-item--expanded .menu-item').hoverIntent({
+      over: function () {
+        // At the current depth, we should delete all "hover-intent" classes.
+        // Other wise we get unwanted behaviour where menu items are expanded while already in hovering other ones.
+        $(this).parent().find('li').removeClass('hover-intent');
+        $(this).addClass('hover-intent');
+      },
+      out: function () {
+        $(this).removeClass('hover-intent');
+      },
+      timeout: 500
+    });
   });
 })(jQuery);

+ 158 - 0
sites/all/modules/contrib/admin/admin_toolbar/js/jquery.hoverIntent.js

@@ -0,0 +1,158 @@
+/*!
+ * hoverIntent v1.8.1 // 2014.08.11 // jQuery v1.9.1+
+ * http://briancherne.github.io/jquery-hoverIntent/
+ *
+ * You may use hoverIntent under the terms of the MIT license. Basically that
+ * means you are free to use hoverIntent as long as this header is left intact.
+ * Copyright 2007, 2014 Brian Cherne
+ */
+
+/* hoverIntent is similar to jQuery's built-in "hover" method except that
+ * instead of firing the handlerIn function immediately, hoverIntent checks
+ * to see if the user's mouse has slowed down (beneath the sensitivity
+ * threshold) before firing the event. The handlerOut function is only
+ * called after a matching handlerIn.
+ *
+ * // basic usage ... just like .hover()
+ * .hoverIntent( handlerIn, handlerOut )
+ * .hoverIntent( handlerInOut )
+ *
+ * // basic usage ... with event delegation!
+ * .hoverIntent( handlerIn, handlerOut, selector )
+ * .hoverIntent( handlerInOut, selector )
+ *
+ * // using a basic configuration object
+ * .hoverIntent( config )
+ *
+ * @param  handlerIn   function OR configuration object
+ * @param  handlerOut  function OR selector for delegation OR undefined
+ * @param  selector    selector OR undefined
+ * @author Brian Cherne <brian(at)cherne(dot)net>
+ */
+
+;(function(factory) {
+  'use strict';
+  if (typeof define === 'function' && define.amd) {
+    define(['jquery'], factory);
+  } else if (jQuery && !jQuery.fn.hoverIntent) {
+    factory(jQuery);
+  }
+})(function($) {
+  'use strict';
+
+  // default configuration values
+  var _cfg = {
+    interval: 100,
+    sensitivity: 6,
+    timeout: 0
+  };
+
+  // counter used to generate an ID for each instance
+  var INSTANCE_COUNT = 0;
+
+  // current X and Y position of mouse, updated during mousemove tracking (shared across instances)
+  var cX, cY;
+
+  // saves the current pointer position coordinates based on the given mousemove event
+  var track = function(ev) {
+    cX = ev.pageX;
+    cY = ev.pageY;
+  };
+
+  // compares current and previous mouse positions
+  var compare = function(ev,$el,s,cfg) {
+    // compare mouse positions to see if pointer has slowed enough to trigger `over` function
+    if ( Math.sqrt( (s.pX-cX)*(s.pX-cX) + (s.pY-cY)*(s.pY-cY) ) < cfg.sensitivity ) {
+      $el.off(s.event,track);
+      delete s.timeoutId;
+      // set hoverIntent state as active for this element (permits `out` handler to trigger)
+      s.isActive = true;
+      // overwrite old mouseenter event coordinates with most recent pointer position
+      ev.pageX = cX; ev.pageY = cY;
+      // clear coordinate data from state object
+      delete s.pX; delete s.pY;
+      return cfg.over.apply($el[0],[ev]);
+    } else {
+      // set previous coordinates for next comparison
+      s.pX = cX; s.pY = cY;
+      // use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
+      s.timeoutId = setTimeout( function(){compare(ev, $el, s, cfg);} , cfg.interval );
+    }
+  };
+
+  // triggers given `out` function at configured `timeout` after a mouseleave and clears state
+  var delay = function(ev,$el,s,out) {
+    delete $el.data('hoverIntent')[s.id];
+    return out.apply($el[0],[ev]);
+  };
+
+  $.fn.hoverIntent = function(handlerIn,handlerOut,selector) {
+    // instance ID, used as a key to store and retrieve state information on an element
+    var instanceId = INSTANCE_COUNT++;
+
+    // extend the default configuration and parse parameters
+    var cfg = $.extend({}, _cfg);
+    if ( $.isPlainObject(handlerIn) ) {
+      cfg = $.extend(cfg, handlerIn);
+      if ( !$.isFunction(cfg.out) ) {
+        cfg.out = cfg.over;
+      }
+    } else if ( $.isFunction(handlerOut) ) {
+      cfg = $.extend(cfg, { over: handlerIn, out: handlerOut, selector: selector } );
+    } else {
+      cfg = $.extend(cfg, { over: handlerIn, out: handlerIn, selector: handlerOut } );
+    }
+
+    // A private function for handling mouse 'hovering'
+    var handleHover = function(e) {
+      // cloned event to pass to handlers (copy required for event object to be passed in IE)
+      var ev = $.extend({},e);
+
+      // the current target of the mouse event, wrapped in a jQuery object
+      var $el = $(this);
+
+      // read hoverIntent data from element (or initialize if not present)
+      var hoverIntentData = $el.data('hoverIntent');
+      if (!hoverIntentData) { $el.data('hoverIntent', (hoverIntentData = {})); }
+
+      // read per-instance state from element (or initialize if not present)
+      var state = hoverIntentData[instanceId];
+      if (!state) { hoverIntentData[instanceId] = state = { id: instanceId }; }
+
+      // state properties:
+      // id = instance ID, used to clean up data
+      // timeoutId = timeout ID, reused for tracking mouse position and delaying "out" handler
+      // isActive = plugin state, true after `over` is called just until `out` is called
+      // pX, pY = previously-measured pointer coordinates, updated at each polling interval
+      // event = string representing the namespaced event used for mouse tracking
+
+      // clear any existing timeout
+      if (state.timeoutId) { state.timeoutId = clearTimeout(state.timeoutId); }
+
+      // namespaced event used to register and unregister mousemove tracking
+      var mousemove = state.event = 'mousemove.hoverIntent.hoverIntent'+instanceId;
+
+      // handle the event, based on its type
+      if (e.type === 'mouseenter') {
+        // do nothing if already active
+        if (state.isActive) { return; }
+        // set "previous" X and Y position based on initial entry point
+        state.pX = ev.pageX; state.pY = ev.pageY;
+        // update "current" X and Y position based on mousemove
+        $el.off(mousemove,track).on(mousemove,track);
+        // start polling interval (self-calling timeout) to compare mouse coordinates over time
+        state.timeoutId = setTimeout( function(){compare(ev,$el,state,cfg);} , cfg.interval );
+      } else { // "mouseleave"
+        // do nothing if not already active
+        if (!state.isActive) { return; }
+        // unbind expensive mousemove event
+        $el.off(mousemove,track);
+        // if hoverIntent state is true, then call the mouseOut function after the specified delay
+        state.timeoutId = setTimeout( function(){delay(ev,$el,state,cfg.out);} , cfg.timeout );
+      }
+    };
+
+    // listen for mouseenter and mouseleave
+    return this.on({'mouseenter.hoverIntent':handleHover,'mouseleave.hoverIntent':handleHover}, cfg.selector);
+  };
+});

+ 4 - 4
sites/all/modules/contrib/admin/config_update/README.txt

@@ -7,7 +7,7 @@ Configuration Update Base: A base module providing functionality related to
   updating and computing differences between configuration versions. No
   user interface; used by other modules such as Features.
 
-Configuration Update Reports (in the config_ui sub-directory of this project):
-  Adds an updates report and revert functionality to configuration management.
-  Depends on Configuration Update Base. For more information, see the
-  README.txt file in the subdirectory.
+Configuration Update Reports (in the config_update_ui sub-directory of this
+  project): Adds an updates report and revert functionality to configuration
+  management.  Depends on Configuration Update Base. For more information, see
+  the README.txt file in the subdirectory.

+ 3 - 3
sites/all/modules/contrib/admin/config_update/config_update.info.yml

@@ -5,8 +5,8 @@ description: 'Provides basic revert and update functionality for other modules'
 dependencies:
   - drupal:config
 
-# Information added by Drupal.org packaging script on 2017-01-18
-version: '8.x-1.3'
+# Information added by Drupal.org packaging script on 2017-09-18
+version: '8.x-1.4'
 core: '8.x'
 project: 'config_update'
-datestamp: 1484770686
+datestamp: 1505755746

+ 17 - 2
sites/all/modules/contrib/admin/config_update/config_update_ui/config_update_ui.drush_testing.inc

@@ -8,9 +8,24 @@
  */
 
 if (!function_exists('drush_print')) {
-  function drush_print() {}
+
+  /**
+   * Stub for drush_print() function for testing.
+   */
+  function drush_print() {
+  }
+
 }
+
 if (!function_exists('dt')) {
-  function dt($text) { return $text; }
+
+  /**
+   * Stub for dt() function for testing.
+   */
+  function dt($text) {
+    return $text;
+  }
+
 }
+
 defined('STDERR') or define('STDERR', 25);

+ 3 - 3
sites/all/modules/contrib/admin/config_update/config_update_ui/config_update_ui.info.yml

@@ -7,8 +7,8 @@ dependencies:
   - config_update:config_update
   - drupal:config
 
-# Information added by Drupal.org packaging script on 2017-01-18
-version: '8.x-1.3'
+# Information added by Drupal.org packaging script on 2017-09-18
+version: '8.x-1.4'
 core: '8.x'
 project: 'config_update'
-datestamp: 1484770686
+datestamp: 1505755746

+ 1 - 1
sites/all/modules/contrib/admin/config_update/config_update_ui/config_update_ui.module

@@ -26,7 +26,7 @@ function config_update_ui_help($route_name, RouteMatchInterface $route_match) {
 
     case 'config_update_ui.report':
       $output = '';
-      $output .= '<p>' . t('This report shows which default configuration items provided by the current versions of your installed modules, themes, and install profile differ from the active configuration of your site. You can generate the report for all configuration of a certain type, or for a particular installed module, theme, or your install profile (only modules, themes, and install profiles that actually provide configuration are listed).') . '</p>';
+      $output .= '<p>' . t('This report shows which default configuration items provided by the current versions of your installed modules, themes, and install profile differ from the active configuration of your site. You can generate the report for all configuration of a certain type; for a particular installed module, theme, or your install profile (only modules, themes, and install profiles that actually provide configuration are listed); or for all configuration.') . '</p>';
       $output .= '<p>' . t('Some configuration provided by modules has dependencies, and is only added to your system when all the dependencies are satisfied. This is known as <em>optional</em> configuration.') . '</p>';
       $output .= '<p>' . t('Differences, including missing and added configuration, can be due to either an updated module or theme providing different configuration from when you installed it, or to changes made by site administrators. This report does not differentiate between these two sources for differences, and note that an install profile can also silently override module/theme configuration. Overrides (such as those in your settings.php file) and translations are not considered when looking at differences.') . '</p>';
       return $output;

+ 62 - 16
sites/all/modules/contrib/admin/config_update/config_update_ui/src/Controller/ConfigUpdateController.php

@@ -3,6 +3,7 @@
 namespace Drupal\config_update_ui\Controller;
 
 use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Diff\DiffFormatter;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Extension\ThemeHandlerInterface;
@@ -60,6 +61,13 @@ class ConfigUpdateController extends ControllerBase {
    */
   protected $themeHandler;
 
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
   /**
    * Constructs a ConfigUpdateController object.
    *
@@ -75,8 +83,10 @@ class ConfigUpdateController extends ControllerBase {
    *   The module handler.
    * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
    *   The theme handler.
+   * @param \drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory.
    */
-  public function __construct(ConfigDiffInterface $config_diff, ConfigListByProviderInterface $config_list, ConfigRevertInterface $config_update, DiffFormatter $diff_formatter, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) {
+  public function __construct(ConfigDiffInterface $config_diff, ConfigListByProviderInterface $config_list, ConfigRevertInterface $config_update, DiffFormatter $diff_formatter, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, ConfigFactoryInterface $config_factory) {
     $this->configDiff = $config_diff;
     $this->configList = $config_list;
     $this->configRevert = $config_update;
@@ -84,6 +94,7 @@ class ConfigUpdateController extends ControllerBase {
     $this->diffFormatter->show_header = FALSE;
     $this->moduleHandler = $module_handler;
     $this->themeHandler = $theme_handler;
+    $this->configFactory = $config_factory;
   }
 
   /**
@@ -96,7 +107,8 @@ class ConfigUpdateController extends ControllerBase {
       $container->get('config_update.config_update'),
       $container->get('diff.formatter'),
       $container->get('module_handler'),
-      $container->get('theme_handler')
+      $container->get('theme_handler'),
+      $container->get('config.factory')
     );
   }
 
@@ -238,6 +250,22 @@ class ConfigUpdateController extends ControllerBase {
       '#rows' => [],
     ];
 
+    // Full report of all configuration.
+    $links['report_full'] = [
+      'title' => $this->t('Everything'),
+      'url' => Url::fromRoute('config_update_ui.report', ['report_type' => 'type', 'name' => 'system.all']),
+    ];
+    $build['links']['#rows'][] = [
+      $this->t('Full report'),
+      [
+        'data' => [
+          '#type' => 'operations',
+          '#links' => $links,
+        ],
+      ],
+    ];
+
+    // Reports by configuration type.
     $definitions = $this->configList->listTypes();
     $links = [];
     foreach ($definitions as $entity_type => $definition) {
@@ -250,10 +278,6 @@ class ConfigUpdateController extends ControllerBase {
     uasort($links, [$this, 'sortLinks']);
 
     $links = [
-      'report_type_all' => [
-        'title' => $this->t('All types'),
-        'url' => Url::fromRoute('config_update_ui.report', ['report_type' => 'type', 'name' => 'system.all']),
-      ],
       'report_type_system.simple' => [
         'title' => $this->t('Simple configuration'),
         'url' => Url::fromRoute('config_update_ui.report', ['report_type' => 'type', 'name' => 'system.simple']),
@@ -261,7 +285,7 @@ class ConfigUpdateController extends ControllerBase {
     ] + $links;
 
     $build['links']['#rows'][] = [
-      $this->t('Configuration type'),
+      $this->t('Single configuration type'),
       [
         'data' => [
           '#type' => 'operations',
@@ -271,7 +295,7 @@ class ConfigUpdateController extends ControllerBase {
     ];
 
     // Make a list of installed modules.
-    $profile = Settings::get('install_profile');
+    $profile = $this->getProfileName();
     $modules = $this->moduleHandler->getModuleList();
     $links = [];
     foreach ($modules as $machine_name => $module) {
@@ -285,7 +309,7 @@ class ConfigUpdateController extends ControllerBase {
     uasort($links, [$this, 'sortLinks']);
 
     $build['links']['#rows'][] = [
-      $this->t('Module'),
+      $this->t('Single module'),
       [
         'data' => [
           '#type' => 'operations',
@@ -308,7 +332,7 @@ class ConfigUpdateController extends ControllerBase {
     uasort($links, [$this, 'sortLinks']);
 
     $build['links']['#rows'][] = [
-      $this->t('Theme'),
+      $this->t('Single theme'),
       [
         'data' => [
           '#type' => 'operations',
@@ -393,7 +417,7 @@ class ConfigUpdateController extends ControllerBase {
         break;
 
       case 'profile':
-        $profile = Settings::get('install_profile');
+        $profile = $this->getProfileName();
         $label = $this->t('@name profile', ['@name' => $this->moduleHandler->getName($profile)]);
         break;
 
@@ -449,7 +473,8 @@ class ConfigUpdateController extends ControllerBase {
     $build['different'] = [
       '#caption' => $this->t('Changed configuration items'),
       '#empty' => $this->t('None: no active configuration items differ from their current provided versions.'),
-    ] + $this->makeReportTable($different, 'active', ['diff', 'export', 'revert']);
+    ] + $this->makeReportTable($different, 'active',
+      ['diff', 'export', 'revert']);
 
     return $build;
   }
@@ -468,13 +493,13 @@ class ConfigUpdateController extends ControllerBase {
    *   - revert
    *   - export
    *   - import
-   *   - delete
+   *   - delete.
    *
    * @return array
    *   Render array for the table, not including the #empty and #prefix
    *   properties.
    */
-  protected function makeReportTable($names, $storage, $actions) {
+  protected function makeReportTable(array $names, $storage, array $actions) {
     $build = [];
 
     $build['#type'] = 'table';
@@ -544,7 +569,7 @@ class ConfigUpdateController extends ControllerBase {
       $provider = $this->configList->getConfigProvider($name);
       $provider_name = '';
       if (!empty($provider)) {
-        switch($provider[0]) {
+        switch ($provider[0]) {
           case 'profile':
             $provider_name = $this->moduleHandler->getName($provider[1]);
             if ($provider_name) {
@@ -574,7 +599,6 @@ class ConfigUpdateController extends ControllerBase {
               $provider_name = '';
             }
             break;
-            break;
         }
       }
       $row[] = $provider_name;
@@ -627,4 +651,26 @@ class ConfigUpdateController extends ControllerBase {
     return ($title1 < $title2) ? -1 : 1;
   }
 
+  /**
+   * Returns the name of the install profile.
+   *
+   * For backwards compatibility with pre/post 8.3.x, tries to get it from
+   * either configuration or settings.
+   *
+   * @return string
+   *   The name of the install profile.
+   */
+  protected function getProfileName() {
+    // Code adapted from DrupalKernel::getInstalProfile() in Core.
+    // In Core 8.3.x or later, read from config.
+    $profile = $this->configFactory->get('core.extension')->get('profile');
+    if (!empty($profile)) {
+      return $profile;
+    }
+    else {
+      // If system_update_8300() has not yet run, use settings.
+      return Settings::get('install_profile');
+    }
+  }
+
 }

+ 97 - 0
sites/all/modules/contrib/admin/config_update/config_update_ui/src/Tests/ConfigProfileOverridesTest.php

@@ -0,0 +1,97 @@
+<?php
+
+namespace Drupal\config_update_ui\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Verify config reports, reverts, and diffs with profile overrides.
+ *
+ * @group config
+ */
+class ConfigProfileOverridesTest extends WebTestBase {
+
+  /**
+   * Use the Standard profile, so that there are profile config overrides.
+   *
+   * @var string
+   */
+  protected $profile = 'standard';
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = [
+    'config',
+    'config_update',
+    'config_update_ui',
+  ];
+
+  /**
+   * The admin user that will be created.
+   *
+   * @var object
+   */
+  protected $adminUser;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Create user and log in.
+    $this->adminUser = $this->drupalCreateUser([
+      'access administration pages',
+      'administer themes',
+      'view config updates report',
+      'synchronize configuration',
+      'export configuration',
+      'import configuration',
+      'revert configuration',
+      'delete configuration',
+    ]);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  /**
+   * Tests that config overrides work as expected.
+   */
+  public function testConfigOverrides() {
+
+    // The Standard install profile contains a system.theme.yml file that
+    // sets up bartik/seven as the default/admin theme. The default config
+    // from the System module has no admin theme and stark as the default
+    // theme. So first, run the report on simple configuration and verify
+    // that system.theme is not shown (it should not be missing, or added,
+    // or overridden).
+    $this->drupalGet('admin/config/development/configuration/report/type/system.simple');
+    $this->assertNoRaw('system.theme');
+
+    // Go to the Appearance page and change the theme to whatever is currently
+    // disabled. Return to the report and verify that system.theme is there,
+    // since it is now overridden.
+    $this->drupalGet('admin/appearance');
+    $this->clickLink('Install and set as default');
+    $this->drupalGet('admin/config/development/configuration/report/type/system.simple');
+    $this->assertText('system.theme');
+
+    // Look at the differences for system.theme and verify it's against
+    // the standard profile version, not default version. The line for
+    // default should show bartik as the source; if it's against the system
+    // version, the word bartik would not be there.
+    $this->drupalGet('admin/config/development/configuration/report/diff/system.simple/system.theme');
+    $this->assertText('bartik');
+
+    // Revert and verify that it reverted to the profile version, not the
+    // system module version.
+    $this->drupalGet('admin/config/development/configuration/report/revert/system.simple/system.theme');
+    $this->drupalPostForm(NULL, [], 'Revert');
+    $this->drupalGet('admin/config/development/configuration/single/export/system.simple/system.theme');
+    $this->assertText('admin: seven');
+    $this->assertText('default: bartik');
+  }
+
+}

+ 40 - 11
sites/all/modules/contrib/admin/config_update/config_update_ui/src/Tests/ConfigUpdateTest.php

@@ -17,12 +17,25 @@ class ConfigUpdateTest extends WebTestBase {
    * Use the Search module because it has two included config items in its
    * config/install, assuming node and user are also enabled.
    *
-   * @var array.
+   * @var array
    */
-  public static $modules = ['config', 'config_update', 'config_update_ui', 'search', 'node', 'user', 'block', 'text', 'field', 'filter'];
+  public static $modules = [
+    'config',
+    'config_update',
+    'config_update_ui',
+    'search',
+    'node',
+    'user',
+    'block',
+    'text',
+    'field',
+    'filter',
+  ];
 
   /**
    * The admin user that will be created.
+   *
+   * @var object
    */
   protected $adminUser;
 
@@ -33,7 +46,17 @@ class ConfigUpdateTest extends WebTestBase {
     parent::setUp();
 
     // Create user and log in.
-    $this->adminUser = $this->drupalCreateUser(['access administration pages', 'administer search', 'view config updates report', 'synchronize configuration', 'export configuration', 'import configuration', 'revert configuration', 'delete configuration', 'administer filters']);
+    $this->adminUser = $this->drupalCreateUser([
+      'access administration pages',
+      'administer search',
+      'view config updates report',
+      'synchronize configuration',
+      'export configuration',
+      'import configuration',
+      'revert configuration',
+      'delete configuration',
+      'administer filters',
+    ]);
     $this->drupalLogin($this->adminUser);
 
     // Make sure local tasks and page title are showing.
@@ -99,7 +122,13 @@ class ConfigUpdateTest extends WebTestBase {
     $this->drupalGet('admin/config/development/configuration/report/module/user');
     $this->assertReport('User module', [], [], [], $inactive, ['added', 'changed']);
     $this->assertDrushReports('module', 'user', [], [], [],
-      ['rdf.mapping.user.user', 'search.page.user_search', 'views.view.user_admin_people', 'views.view.who_s_new', 'views.view.who_s_online' ], ['changed']);
+      [
+        'rdf.mapping.user.user',
+        'search.page.user_search',
+        'views.view.user_admin_people',
+        'views.view.who_s_new',
+        'views.view.who_s_online',
+      ], ['changed']);
 
     // Use the import link to get it back. Do this from the search page
     // report to make sure we are importing the right config.
@@ -314,7 +343,7 @@ class ConfigUpdateTest extends WebTestBase {
    * @param string[] $skip
    *   Array of report sections to skip checking.
    */
-  protected function assertReport($title, $missing, $added, $changed, $inactive, $skip = []) {
+  protected function assertReport($title, array $missing, array $added, array $changed, array $inactive, array $skip = []) {
     $this->assertText('Configuration updates report for ' . $title);
     $this->assertText('Generate new report');
 
@@ -393,7 +422,7 @@ class ConfigUpdateTest extends WebTestBase {
    * @param string[] $skip
    *   Array of report sections to skip checking.
    */
-  protected function assertDrushReports($type, $name, $missing, $added, $changed, $inactive, $skip = []) {
+  protected function assertDrushReports($type, $name, array $missing, array $added, array $changed, array $inactive, array $skip = []) {
     if (!in_array('missing', $skip)) {
       $output = drush_config_update_ui_config_missing_report($type, $name);
       $this->assertEqual(count($output), count($missing), 'Drush missing report has correct number of items');
@@ -442,9 +471,10 @@ class ConfigUpdateTest extends WebTestBase {
    */
   protected function assertNoReport() {
     $this->assertText('Report type');
-    $this->assertText('Configuration type');
-    $this->assertText('Module');
-    $this->assertText('Theme');
+    $this->assertText('Full report');
+    $this->assertText('Single configuration type');
+    $this->assertText('Single module');
+    $this->assertText('Single theme');
     $this->assertText('Installation profile');
     $this->assertText('Updates report');
     $this->assertNoText('Missing configuration items');
@@ -454,7 +484,6 @@ class ConfigUpdateTest extends WebTestBase {
 
     // Verify that certain report links are shown or not shown. For extensions,
     // only extensions that have configuration should be shown.
-
     // Modules.
     $this->assertLink('Search');
     $this->assertLink('Field');
@@ -469,7 +498,7 @@ class ConfigUpdateTest extends WebTestBase {
     $this->assertLink('Testing');
 
     // Configuration types.
-    $this->assertLink('All types');
+    $this->assertLink('Everything');
     $this->assertLink('Simple configuration');
     $this->assertLink('Search page');
   }

+ 4 - 4
sites/all/modules/contrib/admin/config_update/src/ConfigDiffInterface.php

@@ -14,9 +14,9 @@ interface ConfigDiffInterface {
    * have the same keys and values. It is up to the particular implementing
    * class to decide what normalizing means.
    *
-   * @param array $source
+   * @param array|null $source
    *   Source config.
-   * @param array $target
+   * @param array|null $target
    *   Target config.
    *
    * @return bool
@@ -32,9 +32,9 @@ interface ConfigDiffInterface {
    * differences. It is up to the particular implementing class to decide what
    * normalizing means.
    *
-   * @param array $source
+   * @param array|null $source
    *   Source config.
-   * @param array $target
+   * @param array|null $target
    *   Target config.
    *
    * @return \Drupal\Component\Diff\Diff

+ 8 - 8
sites/all/modules/contrib/admin/config_update/src/ConfigDiffer.php

@@ -43,7 +43,7 @@ class ConfigDiffer implements ConfigDiffInterface {
   /**
    * Constructs a ConfigDiffer.
    *
-   * @param TranslationInterface $translation
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $translation
    *   String translation service.
    * @param string[] $ignore
    *   Config components to ignore.
@@ -52,7 +52,7 @@ class ConfigDiffer implements ConfigDiffInterface {
    * @param string $value_prefix
    *   Prefix to use in diffs for array value.
    */
-  public function __construct(TranslationInterface $translation, $ignore = ['uuid', '_core'], $hierarchy_prefix = '::', $value_prefix = ' : ') {
+  public function __construct(TranslationInterface $translation, array $ignore = ['uuid', '_core'], $hierarchy_prefix = '::', $value_prefix = ' : ') {
     $this->stringTranslation = $translation;
     $this->hierarchyPrefix = $hierarchy_prefix;
     $this->valuePrefix = $value_prefix;
@@ -66,7 +66,7 @@ class ConfigDiffer implements ConfigDiffInterface {
    * as well as empty array values, and sorts at each level by array key, so
    * that config from different storage can be compared meaningfully.
    *
-   * @param array $config
+   * @param array|null $config
    *   Configuration array to normalize.
    *
    * @return array
@@ -109,7 +109,7 @@ class ConfigDiffer implements ConfigDiffInterface {
   public function same($source, $target) {
     $source = $this->normalize($source);
     $target = $this->normalize($target);
-    return $source == $target;
+    return $source === $target;
   }
 
   /**
@@ -136,15 +136,15 @@ class ConfigDiffer implements ConfigDiffInterface {
    *   (optional) When called recursively, the prefix to put on each line. Omit
    *   when initially calling this function.
    *
-   * @return string[] Array of config lines formatted so that a line-by-line
-   *   diff will show the context in each line, and meaningful differences will
-   *   be computed.
+   * @return string[]
+   *   Array of config lines formatted so that a line-by-line diff will show the
+   *   context in each line, and meaningful differences will be computed.
    *
    * @see ConfigDiffer::normalize()
    * @see ConfigDiffer::$hierarchyPrefix
    * @see ConfigDiffer::$valuePrefix
    */
-  protected function format($config, $prefix = '') {
+  protected function format(array $config, $prefix = '') {
     $lines = [];
 
     foreach ($config as $key => $value) {

+ 23 - 1
sites/all/modules/contrib/admin/config_update/src/ConfigLister.php

@@ -164,7 +164,7 @@ class ConfigLister implements ConfigListInterface {
         break;
 
       case 'profile':
-        $name = Settings::get('install_profile');
+        $name = $this->getProfileName();
         // Intentional fall-through here to the 'module' or 'theme' case.
       case 'module':
       case 'theme':
@@ -220,4 +220,26 @@ class ConfigLister implements ConfigListInterface {
     return array_values($list);
   }
 
+  /**
+   * Returns the name of the install profile.
+   *
+   * For backwards compatibility with pre/post 8.3.x, tries to get it from
+   * either configuration or settings.
+   *
+   * @return string
+   *   The name of the install profile.
+   */
+  protected function getProfileName() {
+    // Code adapted from DrupalKernel::getInstalProfile() in Core.
+    // In Core 8.3.x or later, read from config.
+    $config = $this->activeConfigStorage->read('core.extension');
+    if (!empty($config['profile'])) {
+      return $config['profile'];
+    }
+    else {
+      // If system_update_8300() has not yet run, use settings.
+      return Settings::get('install_profile');
+    }
+  }
+
 }

+ 3 - 7
sites/all/modules/contrib/admin/config_update/src/ConfigListerWithProviders.php

@@ -7,8 +7,6 @@ use Drupal\Core\Config\StorageInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Extension\ThemeHandlerInterface;
-use Drupal\Core\Site\Settings;
-use Drupal\Core\Extension\Extension;
 
 /**
  * Provides methods related to config listing, including provider calculation.
@@ -88,12 +86,10 @@ class ConfigListerWithProviders extends ConfigLister implements ConfigListByProv
     }
 
     // Calculate if it hasn't been set up yet.
-
     // List all of the profile, modules, and themes.
     $extensionsToDo = [];
-    $profile = Settings::get('install_profile');
+    $profile = $this->getProfileName();
     $extensionsToDo[] = ['profile', $profile];
-
     $modules = $this->moduleHandler->getModuleList();
     foreach ($modules as $machine_name => $module) {
       if ($machine_name != $profile) {
@@ -114,14 +110,14 @@ class ConfigListerWithProviders extends ConfigLister implements ConfigListByProv
       'theme' => [],
     ];
 
-    foreach($extensionsToDo as $item) {
+    foreach ($extensionsToDo as $item) {
       $type = $item[0];
       $name = $item[1];
       $configs = array_merge($this->listProvidedItems($type, $name),
         $this->listProvidedItems($type, $name, TRUE));
       if (!empty($configs)) {
         $this->extensionsWithConfig[$type][$name] = $name;
-        foreach($configs as $config) {
+        foreach ($configs as $config) {
           $this->providers[$config] = [$type, $name];
         }
       }

+ 308 - 0
sites/all/modules/contrib/admin/config_update/tests/src/Unit/ConfigDifferTest.php

@@ -0,0 +1,308 @@
+<?php
+
+namespace Drupal\Tests\config_update\Unit;
+
+use Drupal\config_update\ConfigDiffer;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the \Drupal\config_update\ConfigDiffer class.
+ *
+ * @group config_update
+ *
+ * @coversDefaultClass \Drupal\config_update\ConfigDiffer
+ */
+class ConfigDifferTest extends UnitTestCase {
+
+  /**
+   * The mock translation object.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationInterface
+   */
+  protected $stringTranslation;
+
+  /**
+   * The config differ to test.
+   *
+   * @var \Drupal\config_update\ConfigDiffer
+   */
+  protected $configDiffer;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->stringTranslation = $this->getMockBuilder('Drupal\Core\StringTranslation\TranslationInterface')->getMock();
+    $this->stringTranslation
+      ->method('t')
+      ->will($this->returnArgument(0));
+
+    $this->configDiffer = new ConfigDiffer($this->stringTranslation);
+  }
+
+  /**
+   * @covers \Drupal\config_update\ConfigDiffer::same
+   * @dataProvider sameProvider
+   */
+  public function testSame($a, $b, $expected) {
+    $this->assertEquals($expected, $this->configDiffer->same($a, $b));
+  }
+
+  /**
+   * Data provider for self:testSame().
+   */
+  public function sameProvider() {
+    $base = [
+      'uuid' => 'bar',
+      'a' => 'a',
+      'b' => 0,
+      'c' => [
+        'd' => TRUE,
+        'e' => FALSE,
+      ],
+    ];
+
+    return [
+      [$base, $base, TRUE],
+
+      // Add _core, omit uuid. Should match.
+      [
+        $base,
+        [
+          '_core' => 'foo',
+          'a' => 'a',
+          'b' => 0,
+          'c' => [
+            'd' => TRUE,
+            'e' => FALSE,
+          ],
+        ],
+        TRUE,
+      ],
+
+      // Change order. Should match.
+      [
+        $base,
+        [
+          'a' => 'a',
+          'uuid' => 'bar',
+          'b' => 0,
+          'c' => [
+            'd' => TRUE,
+            'e' => FALSE,
+          ],
+        ],
+        TRUE,
+      ],
+
+      // Change order and add _core in deeper level. Should match.
+      [
+        $base,
+        [
+          'uuid' => 'bar',
+          'a' => 'a',
+          'b' => 0,
+          'c' => [
+            'e' => FALSE,
+            '_core' => 'foo',
+            'd' => TRUE,
+          ],
+        ],
+        TRUE,
+      ],
+
+      // Omit a component. Should not match.
+      [
+        $base,
+        [
+          'uuid' => 'bar',
+          'a' => 'a',
+          'c' => [
+            'd' => TRUE,
+            'e' => FALSE,
+          ],
+        ],
+        FALSE,
+      ],
+
+      // Add a component. Should not match.
+      [
+        $base,
+        [
+          'uuid' => 'bar',
+          'a' => 'a',
+          'b' => 0,
+          'c' => [
+            'd' => TRUE,
+            'e' => FALSE,
+          ],
+          'f' => 'f',
+        ],
+        FALSE,
+      ],
+
+      // 0 should not match a string.
+      [
+        $base,
+        [
+          '_core' => 'foo',
+          'uuid' => 'bar',
+          'a' => 'a',
+          'b' => 'b',
+          'c' => [
+            'd' => TRUE,
+            'e' => FALSE,
+          ],
+        ],
+        FALSE,
+      ],
+
+      // FALSE should not match a string.
+      [
+        $base,
+        [
+          '_core' => 'foo',
+          'uuid' => 'bar',
+          'a' => 'a',
+          'b' => 0,
+          'c' => [
+            'd' => TRUE,
+            'e' => 'e',
+          ],
+        ],
+        FALSE,
+      ],
+
+      // TRUE should not match a string.
+      [
+        $base,
+        [
+          '_core' => 'foo',
+          'uuid' => 'bar',
+          'a' => 'a',
+          'b' => 0,
+          'c' => [
+            'd' => 'd',
+            'e' => FALSE,
+          ],
+        ],
+        FALSE,
+      ],
+    ];
+  }
+
+  /**
+   * @covers \Drupal\config_update\ConfigDiffer::diff
+   */
+  public function testDiff() {
+    $configOne = [
+      'uuid' => '1234-5678-90',
+      'id' => 'test.config.id',
+      'id_to_remove' => 'test.remove.id',
+      'type' => 'old_type',
+      'true_value' => TRUE,
+      'nested_array' => [
+        'flat_array' => [
+          'value2',
+          'value1',
+          'value3',
+        ],
+        'custom_key' => 'value',
+      ],
+    ];
+
+    $configTwo = [
+      'uuid' => '09-8765-4321',
+      'id' => 'test.config.id',
+      'type' => 'new_type',
+      'true_value' => FALSE,
+      'nested_array' => [
+        'flat_array' => [
+          'value2',
+          'value3',
+        ],
+        'custom_key' => 'value',
+        'custom_key_2' => 'value2',
+      ],
+    ];
+
+    $edits = $this->configDiffer->diff($configOne, $configTwo)->getEdits();
+
+    $expectedEdits = [
+      [
+        'copy' => [
+          'orig' => [
+            'id : test.config.id',
+          ],
+          'closing' => [
+            'id : test.config.id',
+          ],
+        ],
+      ],
+      [
+        'delete' => [
+          'orig' => [
+            'id_to_remove : test.remove.id',
+          ],
+          'closing' => FALSE,
+        ],
+      ],
+      [
+        'copy' => [
+          'orig' => [
+            'nested_array',
+            'nested_array::custom_key : value',
+          ],
+          'closing' => [
+            'nested_array',
+            'nested_array::custom_key : value',
+          ],
+        ],
+      ],
+      [
+        'add' => [
+          'orig' => FALSE,
+          'closing' => [
+            'nested_array::custom_key_2 : value2',
+          ],
+        ],
+      ],
+      [
+        'copy' => [
+          'orig' => [
+            'nested_array::flat_array',
+            'nested_array::flat_array::0 : value2',
+          ],
+          'closing' => [
+            'nested_array::flat_array',
+            'nested_array::flat_array::0 : value2',
+          ],
+        ],
+      ],
+      [
+        'change' => [
+          'orig' => [
+            'nested_array::flat_array::1 : value1',
+            'nested_array::flat_array::2 : value3',
+            'true_value : 1',
+            'type : old_type',
+          ],
+          'closing' => [
+            'nested_array::flat_array::1 : value3',
+            'true_value : ',
+            'type : new_type',
+          ],
+        ],
+      ],
+    ];
+
+    $this->assertEquals(count($expectedEdits), count($edits));
+
+    /** @var \Drupal\Component\Diff\Engine\DiffOp $diffOp */
+    foreach ($edits as $index => $diffOp) {
+      $this->assertEquals($expectedEdits[$index][$diffOp->type]['orig'], $diffOp->orig);
+      $this->assertEquals($expectedEdits[$index][$diffOp->type]['closing'], $diffOp->closing);
+    }
+  }
+
+}

+ 0 - 0
sites/all/modules/contrib/migrate/migrate_plus/LICENSE.txt → sites/all/modules/contrib/dev/config_devel/LICENSE.txt


+ 8 - 0
sites/all/modules/contrib/dev/config_devel/composer.json

@@ -0,0 +1,8 @@
+{
+  "name": "drupal/config_devel",
+  "description": "Helps developers work with configuration.",
+  "type": "drupal-module",
+  "license": "GPL-2.0+",
+  "minimum-stability": "dev",
+  "require": { }
+}

+ 2 - 0
sites/all/modules/contrib/dev/config_devel/config/install/config_devel.settings.yml

@@ -0,0 +1,2 @@
+auto_import: []
+auto_export: []

+ 23 - 0
sites/all/modules/contrib/dev/config_devel/config/schema/config_devel.schema.yml

@@ -0,0 +1,23 @@
+config_devel.settings:
+  type: config_object
+  label: config devel settings
+  mapping:
+    auto_import:
+      type: sequence
+      sequence:
+        - type: mapping
+          mapping:
+            filename:
+              type: string
+            hash:
+              type: string
+    auto_export:
+      type: sequence
+      sequence:
+        - type: string
+config_devel.test:
+  type: config_object
+  label: config devel test
+  mapping:
+    label:
+      type: label

+ 13 - 0
sites/all/modules/contrib/dev/config_devel/config_devel.info.yml

@@ -0,0 +1,13 @@
+name: Configuration development module
+type: module
+description: 'Helps developers work with configuration.'
+package: Configuration
+# version: VERSION
+# core: 8.x
+configure: config_devel.settings
+
+# Information added by Drupal.org packaging script on 2017-09-11
+version: '8.x-1.1'
+core: '8.x'
+project: 'config_devel'
+datestamp: 1505141047

+ 5 - 0
sites/all/modules/contrib/dev/config_devel/config_devel.links.menu.yml

@@ -0,0 +1,5 @@
+config_devel.settings:
+  title: Config devel
+  description: 'Specify which configuration objects to automatically import/export.'
+  route_name: config_devel.settings
+  parent: system.admin_config_development

+ 7 - 0
sites/all/modules/contrib/dev/config_devel/config_devel.routing.yml

@@ -0,0 +1,7 @@
+config_devel.settings:
+  path: '/admin/config/development/config_devel'
+  defaults:
+    _form: 'Drupal\config_devel\Form\ConfigDevelSettingsForm'
+    _title: 'Config Devel'
+  requirements:
+    _permission: 'import configuration'

+ 11 - 0
sites/all/modules/contrib/dev/config_devel/config_devel.services.yml

@@ -0,0 +1,11 @@
+services:
+  config_devel.writeback_subscriber:
+    class: Drupal\config_devel\EventSubscriber\ConfigDevelAutoExportSubscriber
+    arguments: ['@config.factory', '@config.manager', '@event_dispatcher']
+    tags:
+      - { name: event_subscriber }
+  config_devel.auto_import_subscriber:
+    class: Drupal\config_devel\EventSubscriber\ConfigDevelAutoImportSubscriber
+    arguments: ['@config.factory', '@config.manager']
+    tags:
+      - { name: event_subscriber }

+ 246 - 0
sites/all/modules/contrib/dev/config_devel/drush/config_devel.drush.inc

@@ -0,0 +1,246 @@
+<?php
+
+/**
+ * @file
+ * Configuration development module drush integration.
+ */
+
+use Drupal\Core\Config\InstallStorage;
+
+/**
+ * Implements hook_drush_command().
+ */
+function config_devel_drush_command() {
+  $items = array();
+
+  $description = '';
+  $description .= "List which configuration settings you want to export in the\n";
+  $description .= "module's info file by listing them under 'config_devel', as shown below:\n";
+  $description .= "\n";
+  $description .= "config_devel:\n";
+  $description .= "  install:\n";
+  $description .= "    - entity.view_display.node.article.default\n";
+  $description .= "    - entity.view_display.node.article.teaser\n";
+  $description .= "    - field.instance.node.article.body\n";
+  $description .= "  optional:\n";
+  $description .= "    - field.instance.node.article.tags\n";
+
+  $items['config-devel-export'] = array(
+    'description' => "Write back configuration to module's config directory.\n" . $description,
+    'arguments' => array(
+      'module' => 'Module machine name.',
+    ),
+    'options' => array(
+    ),
+    'required-arguments' => TRUE,
+    'examples' => array(
+      'drush config-devel-export MODULE_NAME' => 'Write back configuration to the specified module, based on .info file.',
+    ),
+    'aliases' => array('cde', 'cd-em'),
+  );
+
+  $items['config-devel-import'] = array(
+    'description' => "Import configuration from module's config directory to active storage.\n" . $description,
+    'arguments' => array(
+      'module' => 'Module machine name.',
+    ),
+    'options' => array(
+    ),
+    'required-arguments' => TRUE,
+    'examples' => array(
+      'drush config-devel-import MODULE_NAME' => 'Import configuration from the specified module into the active storage, based on .info file.',
+    ),
+    'aliases' => array('cdi', 'cd-im'),
+  );
+
+  $items['config-devel-import-one'] = array(
+    'description' => "Import a single configuration item from module's config directory to active storage.\n" . $description,
+    'arguments' => array(
+      'path' => 'Config file name.',
+    ),
+    'options' => array(
+    ),
+    'required-arguments' => TRUE,
+    'examples' => array(
+      'drush config-devel-import-one system.site.yml' => 'Import the contents of system.site.yml into the config object system.site.',
+      'drush config-devel-import-one system.site' => 'Import the standard input into the config object system.site. Helpful for scripting copying to remote',
+    ),
+    'aliases' => array('cdi1', 'cd-i1'),
+  );
+
+  return $items;
+}
+
+/**
+ * Drush command callback.
+ */
+function drush_config_devel_export($extension) {
+  // Determine the type of extension we're dealing with.
+  $type = drush_config_devel_get_type($extension);
+
+  if ($type) {
+    // Get the config
+    $config = drush_config_devel_get_config($type, $extension);
+
+    // Process config
+    if (isset($config['install'])) {
+      drush_config_devel_process_config($config['install'], $type, $extension, InstallStorage::CONFIG_INSTALL_DIRECTORY);
+    }
+
+    // If we have any optional configuration, process that as well.
+    if (isset($config['optional'])) {
+      drush_config_devel_process_config($config['optional'], $type, $extension, InstallStorage::CONFIG_OPTIONAL_DIRECTORY);
+    }
+  }
+  else {
+    drush_set_error("Couldn't export configuration. The '$extension' extension is not enabled.");
+  }
+}
+
+/**
+ * Drush command callback.
+ */
+function drush_config_devel_import($extension) {
+  // Determine the type of extension we're dealing with.
+  $type = drush_config_devel_get_type($extension);
+
+  if ($type) {
+    // Get the config
+    $config = drush_config_devel_get_config($type, $extension);
+
+    // Import config
+    if (isset($config['install'])) {
+      drush_config_devel_import_config($config['install'], $type, $extension, InstallStorage::CONFIG_INSTALL_DIRECTORY);
+    }
+
+    // Import optional config
+    if (isset($config['optional'])) {
+      drush_config_devel_import_config($config['optional'], $type, $extension, InstallStorage::CONFIG_OPTIONAL_DIRECTORY);
+    }
+  }
+  else {
+    drush_set_error("Couldn't import configuration. The '$extension' extension is not enabled.");
+  }
+}
+
+/**
+ * Drush command callback.
+ */
+function drush_config_devel_import_one($path) {
+  $contents = '';
+  if (!file_exists($path)) {
+    if (substr($path, -4) != '.yml') {
+      $contents = file_get_contents('php://stdin');
+    }
+    elseif (!empty($_SERVER['PWD'])) {
+      $path = $_SERVER['PWD'] . '/' . trim($path, '/');
+    }
+  }
+  if ($contents || file_exists($path)) {
+    \Drupal::service('config_devel.auto_import_subscriber')->importOne($path, '', $contents);
+  }
+  else {
+    drush_log("file '$path' not found", 'error');
+    exit;
+  }
+}
+
+/**
+ * Exports a list of configuration entities.
+ *
+ * @param array $config_list
+ *   An array of configuration entities.
+ * @param string $type
+ *   The type of extension we're exporting, one of module or theme.
+ * @param string $extension
+ *   The module, theme or install profile we're exporting.
+ * @param string $directory
+ *   The directory we're exporting to.
+ */
+function drush_config_devel_process_config($config_list, $type, $extension, $directory) {
+  $config_path = drupal_get_path($type, $extension) . "/$directory";
+  foreach ($config_list as $name) {
+    $config = \Drupal::config($name);
+    $file_names = array($config_path . '/' . $name . '.yml');
+
+    \Drupal::service('config_devel.writeback_subscriber')->writeBackConfig($config, $file_names);
+  }
+}
+
+/**
+ * Imports a list of configuration entities
+ *
+ * @param array $config_list
+ *   An array of configuration entities.
+ * @param string $type
+ *   The type of extension we're exporting, one of module or theme.
+ * @param string $extension
+ *   The module, theme or install profile we're exporting.
+ * @param string $directory
+ *   The directory we're exporting to.
+ */
+function drush_config_devel_import_config($config_list, $type, $extension, $directory) {
+  $config_path = drupal_get_path($type, $extension) . "/$directory";
+  foreach ($config_list as $name) {
+    $file_name = $config_path . '/' . $name . '.yml';
+    drush_config_devel_import_one($file_name);
+  }
+}
+
+/**
+ * Gets the config.
+ *
+ * @param  string $type
+ *   module, theme or profile
+ * @param  string $extension
+ *   extension name
+ * @return array
+ *   An array containing install and optional config
+ */
+function drush_config_devel_get_config($type, $extension) {
+  $filename = drupal_get_path($type, $extension) . '/' . $extension .'.info.yml';
+  $info = \Drupal::service('info_parser')->parse($filename);
+
+  $config = array();
+  if (isset($info['config_devel'])) {
+    // Keep backwards compatibility for the old format. This has config names
+    // listed directly beneath 'config_devel', rather than an intermediate level
+    // for 'install' and 'optional'.
+    // Detect the old format based on whether there's neither of these two keys.
+    if (!isset($info['config_devel']['install']) && !isset($info['config_devel']['optional'])) {
+      $info['config_devel']['install'] = $info['config_devel'];
+    }
+
+    $config['install'] = $info['config_devel']['install'];
+
+    // If we have any optional configuration, fetch that as well.
+    if (isset($info['config_devel']['optional'])) {
+      $config['optional'] = $info['config_devel']['optional'];
+    }
+  }
+
+  return $config;
+}
+
+/**
+ * Gets the type for the given extension.
+ *
+ * @param  string $extension
+ *   extension name
+ * @return string
+ *   module, theme, profile, or FALSE if no valid extension provided.
+ */
+function drush_config_devel_get_type($extension) {
+  $type = NULL;
+  if (\Drupal::moduleHandler()->moduleExists($extension)) {
+    $type = 'module';
+  }
+  elseif (\Drupal::service('theme_handler')->themeExists($extension)) {
+    $type = 'theme';
+  }
+  elseif (drupal_get_profile() === $extension) {
+    $type = 'profile';
+  }
+
+  return $type;
+}

+ 26 - 0
sites/all/modules/contrib/dev/config_devel/src/Event/ConfigDevelEvents.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace Drupal\config_devel\Event;
+
+/**
+ * Defines events for config devel.
+ *
+ * @see \Drupal\config_devel\Event\ConfigDevelSaveEvent
+ */
+final class ConfigDevelEvents {
+
+  /**
+   * Name of the event fired when saving a config entity to disk.
+   *
+   * This event allows other modules to impact the configuration that is being
+   * written to disk
+   *
+   * @Event
+   *
+   * @see \Drupal\config_devel\Event\ConfigDevelSaveEvent
+   *
+   * @var string
+   */
+  const SAVE = 'config_devel.save';
+
+}

+ 79 - 0
sites/all/modules/contrib/dev/config_devel/src/Event/ConfigDevelSaveEvent.php

@@ -0,0 +1,79 @@
+<?php
+
+namespace Drupal\config_devel\Event;
+
+use Symfony\Component\EventDispatcher\Event;
+
+/**
+ * The ConfigDevelSaveEvent class.
+ */
+class ConfigDevelSaveEvent extends Event {
+
+  /**
+   * An array of files names that will  be written.
+   *
+   * @var array
+   */
+  protected $fileNames = [];
+
+  /**
+   * An array representing the config object to be written.
+   *
+   * @var array
+   */
+  protected $data;
+
+  /**
+   * ConfigDevelSaveEvent constructor.
+   *
+   * @param array $file_names
+   *   The config file names.
+   * @param array $data
+   *   The config data.
+   */
+  public function __construct(array $file_names, array $data) {
+    $this->fileNames = $file_names;
+    $this->data = $data;
+  }
+
+  /**
+   * Gets the filenames that will be saved.
+   *
+   * @return array
+   *   An array of file names.
+   */
+  public function getFileNames() {
+    return $this->fileNames;
+  }
+
+  /**
+   * Sets the file names.
+   *
+   * @param array $file_names
+   *   An array of file names to be written out
+   */
+  public function setFileNames(array $file_names) {
+    $this->fileNames = $file_names;
+  }
+
+  /**
+   * Gets the data representing the config object.
+   *
+   * @return array
+   *   An array of config data.
+   */
+  public function getData() {
+    return $this->data;
+  }
+
+  /**
+   * Sets the data for the config object.
+   *
+   * @param array $data
+   *   The config data.
+   */
+  public function setData(array $data) {
+    $this->data = $data;
+  }
+
+}

+ 136 - 0
sites/all/modules/contrib/dev/config_devel/src/EventSubscriber/ConfigDevelAutoExportSubscriber.php

@@ -0,0 +1,136 @@
+<?php
+
+namespace Drupal\config_devel\EventSubscriber;
+
+use Drupal\config_devel\Event\ConfigDevelEvents;
+use Drupal\config_devel\Event\ConfigDevelSaveEvent;
+use Drupal\Core\Config\Config;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Config\ConfigManagerInterface;
+use Drupal\Core\Config\FileStorage;
+use Drupal\Core\Config\InstallStorage;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\Yaml\Exception\DumpException;
+use Drupal\Core\Config\ConfigCrudEvent;
+use Drupal\Core\Config\ConfigRenameEvent;
+use Drupal\Core\Config\ConfigEvents;
+
+/**
+ * ConfigDevelAutoExportSubscriber subscriber for configuration CRUD events.
+ */
+class ConfigDevelAutoExportSubscriber extends ConfigDevelSubscriberBase implements EventSubscriberInterface {
+
+  /**
+   * The event dispatcher.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $eventDispatcher;
+
+  /**
+   * Constructs the ConfigDevelAutoExportSubscriber object.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The configuration factory.
+   * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager
+   *   The configuration manager.
+   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
+   *   The event dispatcher.
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, ConfigManagerInterface $config_manager, EventDispatcherInterface $event_dispatcher) {
+    parent::__construct($config_factory, $config_manager);
+    $this->configFactory = $config_factory;
+    $this->configManager = $config_manager;
+    $this->eventDispatcher = $event_dispatcher;
+  }
+
+  /**
+   * The files to automatically export.
+   *
+   * @var array
+   */
+  protected $autoExportFiles;
+
+  /**
+   * React to configuration ConfigEvent::SAVE events.
+   *
+   * @param \Drupal\Core\Config\ConfigCrudEvent $event
+   *   The event to process.
+   */
+  public function onConfigSave(ConfigCrudEvent $event) {
+    $this->autoExportConfig($event->getConfig());
+  }
+
+  /**
+   * React to configuration ConfigEvent::RENAME events.
+   *
+   * @param \Drupal\Core\Config\ConfigRenameEvent $event
+   *   The event to process.
+   */
+  public function onConfigRename(ConfigRenameEvent $event) {
+    $this->autoExportConfig($event->getConfig());
+  }
+
+  /**
+   * Automatically export configuration.
+   *
+   * @param Config $config
+   *   The config object.
+   */
+  protected function autoExportConfig(Config $config) {
+    $config_name = $config->getName();
+    $file_names = array_filter($this->getSettings()->get('auto_export') ?: [], function ($file_name) use ($config_name) {
+      return basename($file_name, '.' . FileStorage::getFileExtension()) == $config_name;
+    });
+    $this->writeBackConfig($config, $file_names);
+  }
+
+  /**
+   * write configuration back to files.
+   *
+   * @param \Drupal\Core\Config\Config $config
+   *   The config object.
+   * @param array $file_names
+   *   The file names to which the configuration should be written.
+   */
+  public function writeBackConfig(Config $config, array $file_names) {
+    if ($file_names) {
+      $data = $config->get();
+      $config_name = $config->getName();
+      unset($data['_core']);
+      if ($entity_type_id = $this->configManager->getEntityTypeIdByName($config_name)) {
+        unset($data['uuid']);
+      }
+
+      // Let everyone else have a change to update the exported data.
+      $event = new ConfigDevelSaveEvent($file_names, $data);
+      $this->eventDispatcher->dispatch(ConfigDevelEvents::SAVE, $event);
+      $data = $event->getData();
+      $file_names = $event->getFileNames();
+
+      foreach ($file_names as $file_name) {
+        try {
+          file_put_contents($file_name, (new InstallStorage())->encode($data));
+        }
+        catch (DumpException $e) {
+          // Do nothing. What could we do?
+        }
+      }
+    }
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[ConfigEvents::SAVE][] = array('onConfigSave', 10);
+    $events[ConfigEvents::RENAME][] = array('onConfigRename', 10);
+    return $events;
+  }
+
+}

+ 83 - 0
sites/all/modules/contrib/dev/config_devel/src/EventSubscriber/ConfigDevelAutoImportSubscriber.php

@@ -0,0 +1,83 @@
+<?php
+
+namespace Drupal\config_devel\EventSubscriber;
+
+use Drupal\Component\Utility\Crypt;
+use Drupal\Core\Config\InstallStorage;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpKernel\KernelEvents;
+
+class ConfigDevelAutoImportSubscriber extends ConfigDevelSubscriberBase implements EventSubscriberInterface {
+
+  /**
+   * Reinstall changed config files.
+   */
+  public function autoImportConfig() {
+    $config = $this->getSettings();
+    $changed = FALSE;
+    foreach ($config->get('auto_import') as $key => $file) {
+      if ($new_hash = $this->importOne($file['filename'], $file['hash'])) {
+        $config->set("auto_import.$key.hash", $new_hash);
+        $changed = TRUE;
+      }
+    }
+    if ($changed) {
+      $config->save();
+    }
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::REQUEST][] = array('autoImportConfig', 20);
+    return $events;
+  }
+
+  /**
+   * @param string $filename
+   * @param string $original_hash
+   * @return bool
+   */
+  public function importOne($filename, $original_hash = '', $contents = '') {
+    $hash = '';
+    if (!$contents && (!$contents = @file_get_contents($filename))) {
+      return $hash;
+    }
+    $needs_import = TRUE;
+    if ($original_hash) {
+      $hash = Crypt::hashBase64($contents);
+      if ($hash == $original_hash) {
+        $needs_import = FALSE;
+      }
+    }
+    if ($needs_import) {
+      $data = (new InstallStorage())->decode($contents);
+      $config_name = basename($filename, '.yml');
+      $entity_type_id = $this->configManager->getEntityTypeIdByName($config_name);
+      if ($entity_type_id) {
+        $entity_storage = $this->getStorage($entity_type_id);
+        $entity_id = $this->getEntityId($entity_storage, $config_name);
+        $entity_type = $entity_storage->getEntityType();
+        $id_key = $entity_type->getKey('id');
+        $data[$id_key] = $entity_id;
+        /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $entity */
+        $entity = $entity_storage->create($data);
+        if ($existing_entity = $entity_storage->load($entity_id)) {
+          $entity
+            ->set('uuid', $existing_entity->uuid())
+            ->enforceIsNew(FALSE);
+        }
+        $entity_storage->save($entity);
+      }
+      else {
+        $this->configFactory->getEditable($config_name)->setData($data)->save();
+      }
+    }
+    return $hash;
+  }
+
+}

+ 66 - 0
sites/all/modules/contrib/dev/config_devel/src/EventSubscriber/ConfigDevelSubscriberBase.php

@@ -0,0 +1,66 @@
+<?php
+
+namespace Drupal\config_devel\EventSubscriber;
+
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Config\ConfigManagerInterface;
+use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
+
+class ConfigDevelSubscriberBase {
+
+  /**
+   * The configuration factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The configuration manager.
+   *
+   * @var \Drupal\Core\Config\ConfigManagerInterface
+   */
+  protected $configManager;
+
+  /**
+   * Constructs the ConfigDevelAutoExportSubscriber object.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The configuration factory.
+   * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager
+   *   The configuration manager.
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, ConfigManagerInterface $config_manager) {
+    $this->configFactory = $config_factory;
+    $this->configManager = $config_manager;
+  }
+
+  /**
+   * @param string $entity_type_id
+   *
+   * @return \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
+   */
+  protected function getStorage($entity_type_id) {
+    return $this->configManager->getEntityManager()->getStorage($entity_type_id);
+  }
+
+  /**
+   * @param \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $entity_storage
+   * @param string $config_name
+   *
+   * @return string
+   */
+  protected function getEntityId(ConfigEntityStorageInterface $entity_storage, $config_name) {
+    // getIDFromConfigName adds a dot but getConfigPrefix has a dot already.
+    return $entity_storage::getIDFromConfigName($config_name, $entity_storage->getEntityType()->getConfigPrefix());
+  }
+
+  /**
+   * @return \Drupal\Core\Config\Config
+   */
+  protected function getSettings() {
+    return $this->configFactory->getEditable('config_devel.settings');
+  }
+
+}

+ 100 - 0
sites/all/modules/contrib/dev/config_devel/src/Form/ConfigDevelSettingsForm.php

@@ -0,0 +1,100 @@
+<?php
+
+namespace Drupal\config_devel\Form;
+
+use Drupal\Core\Config\FileStorage;
+use Drupal\Core\Form\ConfigFormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Settings form for config devel.
+ */
+class ConfigDevelSettingsForm extends ConfigFormBase {
+
+  /**
+   * Name of the config being edited.
+   */
+  const CONFIGNAME = 'config_devel.settings';
+
+  /**
+   * @var array
+   */
+  protected $keys;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $default_value = '';
+    foreach ($this->config(static::CONFIGNAME)->get('auto_import') as $file) {
+      $default_value .= $file['filename'] . "\n";
+    }
+    $form['auto_import'] = array(
+      '#type' => 'textarea',
+      '#title' => $this->t('Auto import'),
+      '#default_value' => $default_value,
+      '#description' => $this->t('When these files change, they will be automatically imported at the beginning of the next request.') . '<br>' .
+        $this->t('Enter one item per line. Each item should consist of a path relative to the Drupal root and a filename in the same form as a config YML file.') . '<br>' .
+        $this->t('For example: "modules/mymodule/config/install/node.type.mytype.yml".'),
+    );
+    $form['auto_export'] = array(
+      '#type' => 'textarea',
+      '#title' => $this->t('Auto export'),
+      '#default_value' => implode("\n", $this->config(static::CONFIGNAME)->get('auto_export')),
+      '#description' => $this->t('Automatically export to the files specified when the corresponding config item is changed in the admin UI.') . '<br>' .
+        $this->t('Enter one item per line. Each item should consist of a path relative to the Drupal root and a filename in the same form as a config YML file.') . '<br>' .
+        $this->t('For example: "modules/mymodule/config/install/node.type.mytype.yml".'),
+    );
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    foreach (array('auto_import', 'auto_export') as $key) {
+      $form_state->setValue($key, array_filter(preg_split("/\r\n/", $form_state->getValues()[$key])));
+    }
+    foreach ($form_state->getValues()['auto_import'] as $file) {
+      $name = basename($file, '.' . FileStorage::getFileExtension());
+      if (in_array($name, array('system.site', 'core.extension', 'simpletest.settings'))) {
+        $form_state->setErrorByName('auto_import', $this->t('@name is not compatible with this module', array('@name' => $name)));
+      }
+    }
+    parent::validateForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $auto_import = array();
+    foreach ($form_state->getValues()['auto_import'] as $file) {
+      $auto_import[] = array(
+        'filename' => $file,
+        'hash' => '',
+      );
+    }
+    $this->config(static::CONFIGNAME)
+      ->set('auto_import', $auto_import)
+      ->set('auto_export', $form_state->getValues()['auto_export'])
+      ->save();
+    parent::submitForm($form, $form_state);
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'config_devel_settings';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEditableConfigNames() {
+    return ['config_devel.settings'];
+  }
+
+}

+ 32 - 0
sites/all/modules/contrib/dev/config_devel/tests/src/Kernel/ConfigDevelSubscriberEntityTest.php

@@ -0,0 +1,32 @@
+<?php
+
+namespace Drupal\Tests\config_devel\Kernel;
+
+/**
+ * Tests the automated importer for config entities.
+ *
+ * @group config_devel
+ */
+class ConfigDevelSubscriberEntityTest extends ConfigDevelSubscriberTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = array('config_test');
+
+  /**
+   * {@inheritdoc}
+   */
+  const CONFIGNAME = 'config_test.dynamic.test';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doAssert(array $data, array $exported_data) {
+    $entity = entity_load('config_test', 'test', TRUE);
+    $this->assertIdentical($data['label'], $entity->get('label'));
+    $this->assertIdentical($exported_data['label'], $data['label']);
+    $this->assertIdentical($exported_data['id'], 'test');
+    $this->assertFalse(isset($exported_data['uuid']));
+  }
+}

+ 25 - 0
sites/all/modules/contrib/dev/config_devel/tests/src/Kernel/ConfigDevelSubscriberRawTest.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace Drupal\Tests\config_devel\Kernel;
+
+/**
+ * Tests the automated importer for raw config objects.
+ *
+ * @group config_devel
+ */
+class ConfigDevelSubscriberRawTest extends ConfigDevelSubscriberTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  const CONFIGNAME = 'config_devel.test';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doAssert(array $data, array $exported_data) {
+    $this->assertIdentical($data, $this->storage->read(static::CONFIGNAME));
+    $this->assertIdentical($data, $exported_data);
+  }
+
+}

+ 65 - 0
sites/all/modules/contrib/dev/config_devel/tests/src/Kernel/ConfigDevelSubscriberTestBase.php

@@ -0,0 +1,65 @@
+<?php
+
+namespace Drupal\Tests\config_devel\Kernel;
+
+use Drupal\Component\Serialization\Yaml;
+use Drupal\KernelTests\KernelTestBase;
+
+abstract class ConfigDevelSubscriberTestBase extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = array('config_devel');
+
+  /**
+   * Name of the config object.
+   */
+  const CONFIGNAME = '';
+
+  /**
+   * @var \Drupal\Core\Config\StorageInterface
+   */
+  protected $storage;
+
+  /**
+   * Test the import subscriber.
+   */
+  public function testSubscribers() {
+    // Without this the config exporter breaks.
+    \Drupal::service('config.installer')->installDefaultConfig('module', 'config_devel');
+    $filename = 'public://'. static::CONFIGNAME . '.yml';
+    drupal_mkdir('public://exported');
+    $exported_filename = 'public://exported/' . static::CONFIGNAME . '.yml';
+    \Drupal::configFactory()->getEditable('config_devel.settings')
+      ->set('auto_import', array(array(
+        'filename' => $filename,
+        'hash' => '',
+      )))
+      ->set('auto_export', array(
+        $exported_filename,
+      ))
+      ->save();
+    $this->storage = \Drupal::service('config.storage');
+    $this->assertFalse($this->storage->exists(static::CONFIGNAME));
+    $subscriber = \Drupal::service('config_devel.auto_import_subscriber');
+    for ($i = 2; $i; $i--) {
+      $data['label'] = $this->randomString();
+      file_put_contents($filename, Yaml::encode($data));
+      // The import fires an export too.
+      $subscriber->autoImportConfig();
+      $this->doAssert($data, Yaml::decode(file_get_contents($exported_filename)));
+    }
+  }
+
+  /**
+   * Assert that the config import succeeded.
+   *
+   * @param array $writen_data
+   *   The config data as written.
+   * @param array $exported_data
+   *   The config data exported.
+   */
+  abstract protected function doAssert(array $data, array $exported_data);
+
+}

+ 53 - 0
sites/all/modules/contrib/dev/config_devel/tests/src/Unit/ConfigDevelAutoExportSubscriberTest.php

@@ -0,0 +1,53 @@
+<?php
+
+namespace Drupal\Tests\config_devel\Unit;
+
+use org\bovigo\vfs\vfsStream;
+use Drupal\Component\Serialization\Yaml;
+
+use Drupal\config_devel\EventSubscriber\ConfigDevelAutoExportSubscriber;
+
+/**
+ * @coversDefaultClass \Drupal\config_devel\EventSubscriber\ConfigDevelAutoExportSubscriber
+ * @group config_devel
+ */
+class ConfigDevelAutoExportSubscriberTest extends ConfigDevelTestBase {
+
+  /**
+   * Test ConfigDevelAutoExportSubscriber::writeBackConfig().
+   */
+  public function testWriteBackConfig() {
+    $config_data = array(
+      'id' => $this->randomMachineName(),
+      'langcode' => 'en',
+      'uuid' => '836769f4-6791-402d-9046-cc06e20be87f',
+    );
+
+    $config = $this->getMockBuilder('\Drupal\Core\Config\Config')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $config->expects($this->any())
+      ->method('getName')
+      ->will($this->returnValue($this->randomMachineName()));
+    $config->expects($this->any())
+      ->method('get')
+      ->will($this->returnValue($config_data));
+
+    $file_names = array(
+      vfsStream::url('public://' . $this->randomMachineName() . '.yml'),
+      vfsStream::url('public://' . $this->randomMachineName() . '.yml'),
+    );
+
+    $configDevelSubscriber = new ConfigDevelAutoExportSubscriber($this->configFactory, $this->configManager, $this->eventDispatcher);
+    $configDevelSubscriber->writeBackConfig($config, $file_names);
+
+    $data = $config_data;
+    unset($data['uuid']);
+    unset($data['_core']);
+
+    foreach ($file_names as $file_name) {
+      $this->assertEquals($data, Yaml::decode(file_get_contents($file_name)));
+    }
+  }
+
+}

+ 44 - 0
sites/all/modules/contrib/dev/config_devel/tests/src/Unit/ConfigDevelTestBase.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace Drupal\Tests\config_devel\Unit;
+
+use org\bovigo\vfs\vfsStream;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Helper class with mock objects.
+ */
+abstract class ConfigDevelTestBase extends UnitTestCase {
+
+  /**
+   * @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $configFactory;
+
+  /**
+   * @var \Drupal\Core\Config\ConfigManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $configManager;
+
+  /**
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $eventDispatcher;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+    $this->configFactory = $this->getMock('Drupal\Core\Config\ConfigFactoryInterface');
+
+    $this->eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
+
+    $this->configManager = $this->getMock('Drupal\Core\Config\ConfigManagerInterface');
+    $this->configManager->expects($this->any())
+      ->method('getEntityTypeIdByName')
+      ->will($this->returnArgument(0));
+
+    vfsStream::setup('public://');
+  }
+}

+ 3 - 3
sites/all/modules/contrib/dev/devel/devel.info.yml

@@ -7,8 +7,8 @@ configure: devel.admin_settings
 tags:
  - developer
 
-# Information added by Drupal.org packaging script on 2017-08-14
-version: '8.x-1.0'
+# Information added by Drupal.org packaging script on 2017-10-05
+version: '8.x-1.2'
 core: '8.x'
 project: 'devel'
-datestamp: 1502732047
+datestamp: 1507197848

+ 3 - 3
sites/all/modules/contrib/dev/devel/devel_generate/devel_generate.info.yml

@@ -6,8 +6,8 @@ package: Development
 tags:
  - developer
 
-# Information added by Drupal.org packaging script on 2017-08-14
-version: '8.x-1.0'
+# Information added by Drupal.org packaging script on 2017-10-05
+version: '8.x-1.2'
 core: '8.x'
 project: 'devel'
-datestamp: 1502732047
+datestamp: 1507197848

+ 2 - 1
sites/all/modules/contrib/dev/devel/devel_generate/src/Commands/DevelGenerateCommands.php

@@ -141,6 +141,7 @@ class DevelGenerateCommands extends DrushCommands {
    * @param $max_width Max width of first level of links.
    * @option kill Delete all content before generating new content.
    * @aliases genm
+   * @validate-module-enabled menu_link_content
    */
   public function menus($number_menus = 2, $number_links = 50, $max_depth = 3, $max_width = 8, $options = ['kill' => FALSE]) {
     $this->generate();
@@ -179,7 +180,7 @@ class DevelGenerateCommands extends DrushCommands {
     /** @var DevelGenerateBaseInterface $instance */
     $instance = $manager->createInstance($commandData->annotationData()->get('pluginId'), array());
     $this->setPluginInstance($instance);
-    $parameters = $instance->validateDrushParams($args);
+    $parameters = $instance->validateDrushParams($args, $commandData->input()->getOptions());
     $this->setParameters($parameters);
   }
 

+ 4 - 0
sites/all/modules/contrib/dev/devel/devel_generate/src/DevelGenerateBase.php

@@ -165,4 +165,8 @@ abstract class DevelGenerateBase extends PluginBase implements DevelGenerateBase
     }
     return $this->random;
   }
+
+  protected function isDrush8() {
+    return function_exists('drush_drupal_load_autoloader');
+  }
 }

+ 9 - 9
sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/ContentDevelGenerate.php

@@ -316,7 +316,7 @@ class ContentDevelGenerate extends DevelGenerateBase implements ContainerFactory
       $start = time();
       for ($i = 1; $i <= $values['num']; $i++) {
         $this->develGenerateContentAddNode($values);
-        if (function_exists('drush_log') && $i % drush_get_option('feedback', 1000) == 0) {
+        if ($this->isDrush8() && function_exists('drush_log') && $i % drush_get_option('feedback', 1000) == 0) {
           $now = time();
           drush_log(dt('Completed @feedback nodes (@rate nodes/min)', array('@feedback' => drush_get_option('feedback', 1000), '@rate' => (drush_get_option('feedback', 1000) * 60) / ($now - $start))), 'ok');
           $start = $now;
@@ -372,8 +372,8 @@ class ContentDevelGenerate extends DevelGenerateBase implements ContainerFactory
   /**
    * {@inheritdoc}
    */
-  public function validateDrushParams($args) {
-    $add_language = drush_get_option('languages');
+  public function validateDrushParams($args, $options = []) {
+    $add_language = $this->isDrush8() ? drush_get_option('languages') : $options['languages'];
     if (!empty($add_language)) {
       $add_language = explode(',', str_replace(' ', '', $add_language));
       // Intersect with the enabled languages to make sure the language args
@@ -381,17 +381,17 @@ class ContentDevelGenerate extends DevelGenerateBase implements ContainerFactory
       $values['values']['add_language'] = array_intersect($add_language, array_keys($this->languageManager->getLanguages(LanguageInterface::STATE_ALL)));
     }
 
-    $values['kill'] = drush_get_option('kill');
+    $values['kill'] = $this->isDrush8() ? drush_get_option('kill') : $options['kill'];
     $values['title_length'] = 6;
     $values['num'] = array_shift($args);
     $values['max_comments'] = array_shift($args);
     $all_types = array_keys(node_type_get_names());
     $default_types = array_intersect(array('page', 'article'), $all_types);
-    $selected_types = StringUtils::csvToArray(drush_get_option('types', $default_types));
-
-    // Validates the input format for content types option.
-    if (drush_get_option('types', $default_types) === TRUE) {
-      throw new \Exception(dt('Wrong syntax or no content type selected. The correct syntax uses "=", eg.: --types=page,article'));
+    if ($this->isDrush8()) {
+      $selected_types = _convert_csv_to_array(drush_get_option('types', $default_types));
+    }
+    else {
+      $selected_types = StringUtils::csvToArray($options['types'] ?: $default_types);
     }
 
     if (empty($selected_types)) {

+ 16 - 18
sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/MenuDevelGenerate.php

@@ -221,14 +221,14 @@ class MenuDevelGenerate extends DevelGenerateBase implements ContainerFactoryPlu
   /**
    * {@inheritdoc}
    */
-  public function validateDrushParams($args) {
+  public function validateDrushParams($args, $options = []) {
 
     $link_types = array('node', 'front', 'external');
     $values = array(
       'num_menus' => array_shift($args),
       'num_links' => array_shift($args),
-      'kill' => drush_get_option('kill'),
-      'pipe' => drush_get_option('pipe'),
+      'kill' => $this->isDrush8() ? drush_get_option('kill') : $options['kill'],
+      'pipe' => $this->isDrush8() ? drush_get_option('pipe') : $options['pipe'],
       'link_types' => array_combine($link_types, $link_types),
     );
 
@@ -240,16 +240,16 @@ class MenuDevelGenerate extends DevelGenerateBase implements ContainerFactoryPlu
     $values['existing_menus']['__new-menu__'] = TRUE;
 
     if ($this->isNumber($values['num_menus']) == FALSE) {
-      return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of menus'));
+      throw new \Exception(dt('Invalid number of menus'));
     }
     if ($this->isNumber($values['num_links']) == FALSE) {
-      return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of links'));
+      throw new \Exception(dt('Invalid number of links'));
     }
     if ($this->isNumber($values['max_depth']) == FALSE || $values['max_depth'] > 9 || $values['max_depth'] < 1) {
-      return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid maximum link depth. Use a value between 1 and 9'));
+      throw new \Exception(dt('Invalid maximum link depth. Use a value between 1 and 9'));
     }
     if ($this->isNumber($values['max_width']) == FALSE || $values['max_width'] < 1) {
-      return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid maximum menu width. Use a positive numeric value.'));
+      throw new \Exception(dt('Invalid maximum menu width. Use a positive numeric value.'));
     }
 
     return $values;
@@ -300,19 +300,17 @@ class MenuDevelGenerate extends DevelGenerateBase implements ContainerFactoryPlu
   protected function generateMenus($num_menus, $title_length = 12) {
     $menus = array();
 
-    if ($this->moduleHandler->moduleExists('menu_ui')) {
-      for ($i = 1; $i <= $num_menus; $i++) {
-        $name = $this->getRandom()->word(mt_rand(2, max(2, $title_length)));
+    for ($i = 1; $i <= $num_menus; $i++) {
+      $name = $this->getRandom()->word(mt_rand(2, max(2, $title_length)));
 
-        $menu = $this->menuStorage->create(array(
-          'label' => $name,
-          'id' => 'devel-' . Unicode::strtolower($name),
-          'description' => $this->t('Description of @name', array('@name' => $name)),
-        ));
+      $menu = $this->menuStorage->create(array(
+        'label' => $name,
+        'id' => 'devel-' . Unicode::strtolower($name),
+        'description' => $this->t('Description of @name', array('@name' => $name)),
+      ));
 
-        $menu->save();
-        $menus[$menu->id()] = $menu->label();
-      }
+      $menu->save();
+      $menus[$menu->id()] = $menu->label();
     }
 
     return $menus;

+ 5 - 14
sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php

@@ -210,15 +210,6 @@ class TermDevelGenerate extends DevelGenerateBase implements ContainerFactoryPlu
 
       $max++;
 
-      if (function_exists('drush_log')) {
-        $feedback = drush_get_option('feedback', 1000);
-        if ($i % $feedback == 0) {
-          $now = time();
-          drush_log(dt('Completed @feedback terms (@rate terms/min)', array('@feedback' => $feedback, '@rate' => $feedback * 60 / ($now - $start))), 'ok');
-          $start = $now;
-        }
-      }
-
       // Limit memory usage. Only report first 20 created terms.
       if ($i < 20) {
         $terms[] = $term->label();
@@ -233,7 +224,7 @@ class TermDevelGenerate extends DevelGenerateBase implements ContainerFactoryPlu
   /**
    * {@inheritdoc}
    */
-  public function validateDrushParams($args) {
+  public function validateDrushParams($args, $options = []) {
     $vocabulary_name = array_shift($args);
     $number = array_shift($args);
 
@@ -242,21 +233,21 @@ class TermDevelGenerate extends DevelGenerateBase implements ContainerFactoryPlu
     }
 
     if (!$vocabulary_name) {
-      return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Please provide a vocabulary machine name.'));
+      throw new \Exception(dt('Please provide a vocabulary machine name.'));
     }
 
     if (!$this->isNumber($number)) {
-      return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of terms: @num', array('@num' => $number)));
+      throw new \Exception(dt('Invalid number of terms: @num', array('@num' => $number)));
     }
 
     // Try to convert machine name to a vocabulary id.
     if (!$vocabulary = $this->vocabularyStorage->load($vocabulary_name)) {
-      return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid vocabulary name: @name', array('@name' => $vocabulary_name)));
+      throw new \Exception(dt('Invalid vocabulary name: @name', array('@name' => $vocabulary_name)));
     }
 
     $values = [
       'num' => $number,
-      'kill' => drush_get_option('kill'),
+      'kill' => $this->isDrush8() ? drush_get_option('kill') : $options['kill'],
       'title_length' => 12,
       'vids' => [$vocabulary->id()],
     ];

+ 17 - 4
sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/UserDevelGenerate.php

@@ -7,6 +7,7 @@ use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\devel_generate\DevelGenerateBase;
+use Drush\Utils\StringUtils;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -177,14 +178,26 @@ class UserDevelGenerate extends DevelGenerateBase implements ContainerFactoryPlu
   /**
    * {@inheritdoc}
    */
-  public function validateDrushParams($args) {
+  public function validateDrushParams($args, $options = []) {
     $values = array(
       'num' => array_shift($args),
-      'roles' => drush_get_option('roles') ? explode(',', drush_get_option('roles')) : array(),
-      'kill' => drush_get_option('kill'),
-      'pass' => drush_get_option('pass', NULL),
       'time_range' => 0,
     );
+
+    if ($this->isDrush8()) {
+      $values += [
+        'roles' => explode(',', drush_get_option('roles', '')),
+        'kill' => drush_get_option('kill'),
+        'pass' => drush_get_option('pass', NULL),
+      ];
+    }
+    else {
+      $values += [
+        'roles' => StringUtils::csvToArray($options['roles']),
+        'kill' => $options['kill'],
+        'pass' => $options['pass'],
+      ];
+    }
     return $values;
   }
 

+ 3 - 3
sites/all/modules/contrib/dev/devel/devel_generate/src/Plugin/DevelGenerate/VocabularyDevelGenerate.php

@@ -158,15 +158,15 @@ class VocabularyDevelGenerate extends DevelGenerateBase implements ContainerFact
   /**
    * {@inheritdoc}
    */
-  public function validateDrushParams($args) {
+  public function validateDrushParams($args, $options = []) {
     $values = array(
       'num' => array_shift($args),
-      'kill' => drush_get_option('kill'),
+      'kill' => $this->isDrush8() ? drush_get_option('kill') : $options['kill'],
       'title_length' => 12,
     );
 
     if ($this->isNumber($values['num']) == FALSE) {
-      return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of vocabularies: @num.', array('@num' => $values['num'])));
+      throw new \Exception(dt('Invalid number of vocabularies: @num.', array('@num' => $values['num'])));
     }
 
     return $values;

+ 3 - 3
sites/all/modules/contrib/dev/devel/devel_generate/tests/modules/devel_generate_example/devel_generate_example.info.yml

@@ -7,8 +7,8 @@ configure: admin/config/development/generate
 tags:
  - developer
 
-# Information added by Drupal.org packaging script on 2017-08-14
-version: '8.x-1.0'
+# Information added by Drupal.org packaging script on 2017-10-05
+version: '8.x-1.2'
 core: '8.x'
 project: 'devel'
-datestamp: 1502732047
+datestamp: 1507197848

+ 1 - 1
sites/all/modules/contrib/dev/devel/drush.services.yml

@@ -1,6 +1,6 @@
 services:
   devel.command:
     class: Drupal\devel\Commands\DevelCommands
-    arguments: ['@token', '@service_container', '@event_dispatcher']
+    arguments: ['@token', '@service_container', '@event_dispatcher', '@module_handler']
     tags:
       -  { name: drush.command }

+ 3 - 3
sites/all/modules/contrib/dev/devel/kint/kint.info.yml

@@ -6,8 +6,8 @@ package: Development
 tags:
  - developer
 
-# Information added by Drupal.org packaging script on 2017-08-14
-version: '8.x-1.0'
+# Information added by Drupal.org packaging script on 2017-10-05
+version: '8.x-1.2'
 core: '8.x'
 project: 'devel'
-datestamp: 1502732047
+datestamp: 1507197848

+ 71 - 41
sites/all/modules/contrib/dev/devel/src/Commands/DevelCommands.php

@@ -5,6 +5,9 @@ use Drupal\Component\Uuid\Php;
 use Drupal\Core\Utility\Token;
 use Drush\Commands\DrushCommands;
 use Drush\Exceptions\UserAbortException;
+use Drush\Utils\StringUtils;
+use Symfony\Component\Console\Input\Input;
+use Symfony\Component\Console\Output\Output;
 
 /**
  * For commands that are parts of modules, Drush expects to find commandfiles in
@@ -21,11 +24,21 @@ class DevelCommands extends DrushCommands {
 
   protected $eventDispatcher;
 
-  public function __construct(Token $token, $container, $eventDispatcher) {
+  protected $moduleHandler;
+
+  public function __construct(Token $token, $container, $eventDispatcher, $moduleHandler) {
     parent::__construct();
     $this->token = $token;
     $this->container = $container;
     $this->eventDispatcher = $eventDispatcher;
+    $this->moduleHandler = $moduleHandler;
+  }
+
+  /**
+   * @return \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  public function getModuleHandler() {
+    return $this->moduleHandler;
   }
 
   /**
@@ -50,93 +63,110 @@ class DevelCommands extends DrushCommands {
   }
 
   /**
-   * Uninstall, and Install a list of modules.
+   * Uninstall, and Install modules.
 
-   * @command devel-reinstall
+   * @command devel:reinstall
    * @param $modules A comma-separated list of module names.
-   * @aliases dre
+   * @aliases dre,devel-reinstall
    * @allow-additional-options pm-uninstall,pm-enable
    */
-  public function reinstall($projects) {
-    $projects = _convert_csv_to_array($projects);
+  public function reinstall($modules) {
+    $modules = StringUtils::csvToArray($modules);
 
-    // This is faster than 3 separate bootstraps.
-    $args = array_merge(array('pm-uninstall'), $projects);
-    // @todo. Use $application dispatch instead of drush_invoke().
-    call_user_func_array('drush_invoke', $args);
-
-    $args = array_merge(array('pm-enable'), $projects);
-    call_user_func_array('drush_invoke', $args);
+    $modules_str = implode(',', $modules);
+    drush_invoke_process('@self', 'pm:uninstall', [$modules_str], []);
+    drush_invoke_process('@self', 'pm:enable', [$modules_str], []);
   }
 
   /**
    * List implementations of a given hook and optionally edit one.
    *
-   * @command devel-hook
+   * @command devel:hook
    * @param $hook The name of the hook to explore.
+   * @param $implementation The name of the implementation to edit. Usually omitted.
    * @usage devel-hook cron
    *   List implementations of hook_cron().
-   * @aliases fnh,fn-hook,hook
+   * @aliases fnh,fn-hook,hook,devel-hook
+   * @optionset_get_editor
    */
-  function hook($hook) {
+  function hook($hook, $implementation) {
     // Get implementations in the .install files as well.
     include_once './core/includes/install.inc';
     drupal_load_updates();
+    $info = $this->codeLocate($implementation . "_$hook");
+    $exec = drush_get_editor();
+    drush_shell_exec_interactive($exec, $info['file']);
+  }
 
-    if ($hook_implementations = \Drupal::moduleHandler()->getImplementations($hook)) {
-      if ($choice = drush_choice(array_combine($hook_implementations, $hook_implementations), 'Enter the number of the hook implementation you wish to view.')) {
-        $info= $this->codeLocate($choice . "_$hook");
-        $exec = drush_get_editor();
-        drush_shell_exec_interactive($exec, $info['file']);
+  /**
+   * @hook interact hook
+   */
+  public function hookInteract(Input $input, Output $output) {
+    if (!$input->getArgument('implementation')) {
+      if ($hook_implementations = $this->getModuleHandler()->getImplementations($input->getArgument('hook'))) {
+        if (!$choice = $this->io()->choice('Enter the number of the hook implementation you wish to view.', array_combine($hook_implementations, $hook_implementations))) {
+          throw new UserAbortException();
+        }
+        $input->setArgument('implementation', $choice);
+      }
+      else {
+        throw new \Exception(dt('No implementations'));
       }
-    }
-    else {
-      $this->logger()->success(dt('No implementations.'));
     }
   }
 
   /**
    * List implementations of a given event and optionally edit one.
    *
-   * @command devel-event
+   * @command devel:event
    * @param $event The name of the event to explore. If omitted, a list of events is shown.
+   * @param $implementation The name of the implementation to show. Usually omitted.
    * @usage devel-event
    *   Pick a Kernel event, then pick an implementation, and then view its source code.
    * @usage devel-event kernel.terminate
-   *   Pick a terminate subscribers and view its source code.
+   *   Pick a terminate subscribers implementation and view its source code.
    * @aliases fne,fn-event,event
    */
-  function event($event) {
+  function event($event, $implementation) {
+    $info= $this->codeLocate($implementation);
+    $exec = drush_get_editor();
+    drush_shell_exec_interactive($exec, $info['file']);
+  }
+
+  /**
+   * @hook interact devel:event
+   */
+  public function interactEvent(Input $input, Output $output) {
     $dispatcher = $this->getEventDispatcher();
-    if (empty($event)) {
-      // @todo Expand this list and move to interact().
+    if (!$input->getArgument('event')) {
+      // @todo Expand this list.
       $events = array('kernel.controller', 'kernel.exception', 'kernel.request', 'kernel.response', 'kernel.terminate', 'kernel.view');
       $events = array_combine($events, $events);
-      if (!$event = drush_choice($events, 'Enter the event you wish to explore.')) {
+      if (!$event = $this->io()->choice('Enter the event you wish to explore.', $events)) {
         throw new UserAbortException();
       }
+      $input->setArgument('event', $event);
     }
     if ($implementations = $dispatcher->getListeners($event)) {
       foreach ($implementations as $implementation) {
         $callable = get_class($implementation[0]) . '::' . $implementation[1];
         $choices[$callable] = $callable;
       }
-      if ($choice = drush_choice($choices, 'Enter the number of the implementation you wish to view.')) {
-        $info= $this->codeLocate($choice);
-        $exec = drush_get_editor();
-        drush_shell_exec_interactive($exec, $info['file']);
+      if (!$choice = $this->io()->choice('Enter the number of the implementation you wish to view.', $choices)) {
+        throw new UserAbortException();
       }
+      $input->setArgument('implementation', $choice);
     }
     else {
-      $this->logger()->success(dt('No implementations.'));
+      throw new \Exception(dt('No implementations.'));
     }
   }
 
   /**
    * List available tokens.
    *
-   * @command devel-token
-   * @aliases token
+   * @command devel:token
+   * @aliases token,devel-token
    * @field-labels
    *   group: Group
    *   token: Token
@@ -162,8 +192,8 @@ class DevelCommands extends DrushCommands {
   /**
    * Generate a UUID.
    *
-   * @command devel-uuid
-   * @aliases uuid
+   * @command devel:uuid
+   * @aliases uuid,devel-uuid
    * @usage drush devel-uuid
    *   Outputs a Universally Unique Identifier.
    *
@@ -203,9 +233,9 @@ class DevelCommands extends DrushCommands {
   /**
    * Get a list of available container services.
    *
-   * @command devel-services
+   * @command devel:services
    * @param $prefix A prefix to filter the service list by.
-   * @aliases devel-container-services,dcs
+   * @aliases devel-container-services,dcs,devel-services
    * @usage drush devel-services
    *   Gets a list of all available container services
    * @usage drush dcs plugin.manager

+ 3 - 3
sites/all/modules/contrib/dev/devel/tests/modules/devel_dumper_test/devel_dumper_test.info.yml

@@ -5,8 +5,8 @@ package: Testing
 # version: VERSION
 # core: 8.x
 
-# Information added by Drupal.org packaging script on 2017-08-14
-version: '8.x-1.0'
+# Information added by Drupal.org packaging script on 2017-10-05
+version: '8.x-1.2'
 core: '8.x'
 project: 'devel'
-datestamp: 1502732047
+datestamp: 1507197848

+ 3 - 3
sites/all/modules/contrib/dev/devel/tests/modules/devel_entity_test/devel_entity_test.info.yml

@@ -9,8 +9,8 @@ dependencies:
   - text
   - entity_test
 
-# Information added by Drupal.org packaging script on 2017-08-14
-version: '8.x-1.0'
+# Information added by Drupal.org packaging script on 2017-10-05
+version: '8.x-1.2'
 core: '8.x'
 project: 'devel'
-datestamp: 1502732047
+datestamp: 1507197848

+ 3 - 3
sites/all/modules/contrib/dev/devel/tests/modules/devel_test/devel_test.info.yml

@@ -5,8 +5,8 @@ package: Testing
 # version: VERSION
 # core: 8.x
 
-# Information added by Drupal.org packaging script on 2017-08-14
-version: '8.x-1.0'
+# Information added by Drupal.org packaging script on 2017-10-05
+version: '8.x-1.2'
 core: '8.x'
 project: 'devel'
-datestamp: 1502732047
+datestamp: 1507197848

+ 10 - 10
sites/all/modules/contrib/dev/devel/src/Tests/DevelControllerTest.php → sites/all/modules/contrib/dev/devel/tests/src/Functional/DevelControllerTest.php

@@ -1,22 +1,22 @@
 <?php
 
-namespace Drupal\devel\Tests;
+namespace Drupal\Tests\devel\Functional;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Tests Devel controller.
  *
  * @group devel
  */
-class DevelControllerTest extends WebTestBase {
+class DevelControllerTest extends BrowserTestBase {
 
   /**
    * Modules to enable.
    *
    * @var array
    */
-  public static $modules = array('devel', 'node', 'entity_test', 'devel_entity_test', 'block');
+  public static $modules = ['devel', 'node', 'entity_test', 'devel_entity_test', 'block'];
 
   /**
    * {@inheritdoc}
@@ -26,35 +26,35 @@ class DevelControllerTest extends WebTestBase {
 
     // Create a test entity.
     $random_label = $this->randomMachineName();
-    $data = array('type' => 'entity_test', 'name' => $random_label);
+    $data = ['type' => 'entity_test', 'name' => $random_label];
     $this->entity = entity_create('entity_test', $data);
     $this->entity->save();
 
     // Create a test entity with only canonical route.
     $random_label = $this->randomMachineName();
-    $data = array('type' => 'devel_entity_test_canonical', 'name' => $random_label);
+    $data = ['type' => 'devel_entity_test_canonical', 'name' => $random_label];
     $this->entity_canonical = entity_create('devel_entity_test_canonical', $data);
     $this->entity_canonical->save();
 
     // Create a test entity with only edit route.
     $random_label = $this->randomMachineName();
-    $data = array('type' => 'devel_entity_test_edit', 'name' => $random_label);
+    $data = ['type' => 'devel_entity_test_edit', 'name' => $random_label];
     $this->entity_edit = entity_create('devel_entity_test_edit', $data);
     $this->entity_edit->save();
 
     // Create a test entity with no routes.
     $random_label = $this->randomMachineName();
-    $data = array('type' => 'devel_entity_test_no_links', 'name' => $random_label);
+    $data = ['type' => 'devel_entity_test_no_links', 'name' => $random_label];
     $this->entity_no_links = entity_create('devel_entity_test_no_links', $data);
     $this->entity_no_links->save();
 
     $this->drupalPlaceBlock('local_tasks_block');
 
-    $web_user = $this->drupalCreateUser(array(
+    $web_user = $this->drupalCreateUser([
       'view test entity',
       'administer entity_test content',
       'access devel information',
-    ));
+    ]);
     $this->drupalLogin($web_user);
   }
 

+ 33 - 27
sites/all/modules/contrib/dev/devel/src/Tests/DevelDumperTest.php → sites/all/modules/contrib/dev/devel/tests/src/Functional/DevelDumperTest.php

@@ -1,16 +1,16 @@
 <?php
 
-namespace Drupal\devel\Tests;
+namespace Drupal\Tests\devel\Functional;
 
 use Drupal\Component\Render\FormattableMarkup;
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Tests pluggable dumper feature.
  *
  * @group devel
  */
-class DevelDumperTest extends WebTestBase {
+class DevelDumperTest extends BrowserTestBase {
 
   /**
    * Modules to enable.
@@ -36,36 +36,42 @@ class DevelDumperTest extends WebTestBase {
     $this->drupalGet('admin/config/development/devel');
 
     // Ensures that the dumper input is present on the config page.
-    $this->assertFieldByName('dumper');
+    $this->assertSession()->fieldExists('dumper');
 
     // Ensures that the 'default' dumper is enabled by default.
-    $this->assertFieldChecked('edit-dumper-default');
+    $this->assertSession()->checkboxChecked('edit-dumper-default');
 
     // Ensures that all dumpers declared by devel are present on the config page
     // and that only the available dumpers are selectable.
-    $dumpers = ['default', 'drupal_variable', 'firephp', 'chromephp', 'var_dumper'];
+    $dumpers = [
+      'default',
+      'drupal_variable',
+      'firephp',
+      'chromephp',
+      'var_dumper',
+    ];
     $available_dumpers = ['default', 'drupal_variable'];
 
     foreach ($dumpers as $dumper) {
-      $this->assertFieldByXPath('//input[@type="radio" and @name="dumper"]', $dumper, new FormattableMarkup('Radio button for @dumper found.', ['@dumper' => $dumper]));
+      $this->assertFieldByXPath('//input[@type="radio" and @name="dumper"]', $dumper);
       if (in_array($dumper, $available_dumpers)) {
-        $this->assertFieldByXPath('//input[@name="dumper" and not(@disabled="disabled")]', $dumper, new FormattableMarkup('Dumper @dumper is available.', ['@dumper' => $dumper]));
+        $this->assertFieldByXPath('//input[@name="dumper" and not(@disabled="disabled")]', $dumper);
       }
       else {
-        $this->assertFieldByXPath('//input[@name="dumper" and @disabled="disabled"]', $dumper, new FormattableMarkup('Dumper @dumper is disabled.', ['@dumper' => $dumper]));
+        $this->assertFieldByXPath('//input[@name="dumper" and @disabled="disabled"]', $dumper);
       }
     }
 
     // Ensures that dumper plugins declared by other modules are present on the
     // config page and that only the available dumpers are selectable.
-    $this->assertFieldByName('dumper', 'available_test_dumper');
-    $this->assertText('Available test dumper.', 'Available dumper label is present');
-    $this->assertText('Drupal dumper for testing purposes (available).', 'Available dumper description is present');
+    $this->assertFieldByXPath('//input[@name="dumper"]', 'available_test_dumper');
+    $this->assertSession()->pageTextContains('Available test dumper.');
+    $this->assertSession()->pageTextContains('Drupal dumper for testing purposes (available).');
     $this->assertFieldByXPath('//input[@name="dumper" and not(@disabled="disabled")]', 'available_test_dumper', 'Available dumper input not is disabled.');
 
-    $this->assertFieldByName('dumper', 'not_available_test_dumper');
-    $this->assertText('Not available test dumper.', 'Non available dumper label is present');
-    $this->assertText('Drupal dumper for testing purposes (not available).Not available. You may need to install external dependencies for use this plugin.', 'Non available dumper description is present');
+    $this->assertFieldByXPath('//input[@name="dumper"]', 'not_available_test_dumper');
+    $this->assertSession()->pageTextContains('Not available test dumper.');
+    $this->assertSession()->pageTextContains('Drupal dumper for testing purposes (not available).Not available. You may need to install external dependencies for use this plugin.');
     $this->assertFieldByXPath('//input[@name="dumper" and @disabled="disabled"]', 'not_available_test_dumper', 'Non available dumper input is disabled.');
 
     // Ensures that saving of the dumpers configuration works as expected.
@@ -73,10 +79,10 @@ class DevelDumperTest extends WebTestBase {
       'dumper' => 'drupal_variable',
     ];
     $this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration'));
-    $this->assertText(t('The configuration options have been saved.'));
+    $this->assertSession()->pageTextContains(t('The configuration options have been saved.'));
 
     $config = \Drupal::config('devel.settings')->get('devel_dumper');
-    $this->assertEqual('drupal_variable', $config, 'The configuration options have been properly saved');
+    $this->assertEquals('drupal_variable', $config, 'The configuration options have been properly saved');
 
     // Ensure that if the chosen dumper is not available (e.g. the module that
     // provide it is uninstalled) the 'default' dumper appears selected in the
@@ -84,22 +90,22 @@ class DevelDumperTest extends WebTestBase {
     \Drupal::service('module_installer')->install(['kint']);
 
     $this->drupalGet('admin/config/development/devel');
-    $this->assertFieldByName('dumper', 'kint');
+    $this->assertFieldByXPath('//input[@name="dumper"]', 'kint');
 
     $edit = [
       'dumper' => 'kint',
     ];
     $this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration'));
-    $this->assertText(t('The configuration options have been saved.'));
+    $this->assertSession()->pageTextContains(t('The configuration options have been saved.'));
 
     $config = \Drupal::config('devel.settings')->get('devel_dumper');
-    $this->assertEqual('kint', $config, 'The configuration options have been properly saved');
+    $this->assertEquals('kint', $config, 'The configuration options have been properly saved');
 
     \Drupal::service('module_installer')->uninstall(['kint']);
 
     $this->drupalGet('admin/config/development/devel');
-    $this->assertNoFieldByName('dumper', 'kint');
-    $this->assertFieldChecked('edit-dumper-default');
+    $this->assertNoFieldByXPath('//input[@name="dumper"]', 'kint');
+    $this->assertSession()->checkboxChecked('edit-dumper-default');
   }
 
   /**
@@ -110,7 +116,7 @@ class DevelDumperTest extends WebTestBase {
       'dumper' => 'available_test_dumper',
     ];
     $this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration'));
-    $this->assertText(t('The configuration options have been saved.'));
+    $this->assertSession()->pageTextContains(t('The configuration options have been saved.'));
 
     $this->drupalGet('devel_dumper_test/dump');
     $elements = $this->xpath('//body/pre[contains(text(), :message)]', [':message' => 'AvailableTestDumper::dump() Test output']);
@@ -129,8 +135,8 @@ class DevelDumperTest extends WebTestBase {
     $this->assertTrue(!empty($elements), 'Dumped message is present.');
     // Ensures that plugins can add libraries to the page when the
     // ::exportAsRenderable() method is used.
-    $this->assertRaw('devel_dumper_test/css/devel_dumper_test.css');
-    $this->assertRaw('devel_dumper_test/js/devel_dumper_test.js');
+    $this->assertSession()->responseContains('devel_dumper_test/css/devel_dumper_test.css');
+    $this->assertSession()->responseContains('devel_dumper_test/js/devel_dumper_test.js');
 
     $debug_filename = file_directory_temp() . '/drupal_debug.txt';
 
@@ -140,7 +146,7 @@ class DevelDumperTest extends WebTestBase {
 <pre>AvailableTestDumper::export() Test output</pre>
 
 EOF;
-    $this->assertEqual($file_content, $expected, 'Dumped message is present.');
+    $this->assertEquals($file_content, $expected, 'Dumped message is present.');
 
     // Ensures that the DevelDumperManager::debug() is not access checked and
     // that the dump is written in the debug file even if the user has not the
@@ -153,7 +159,7 @@ EOF;
 <pre>AvailableTestDumper::export() Test output</pre>
 
 EOF;
-    $this->assertEqual($file_content, $expected, 'Dumped message is present.');
+    $this->assertEquals($file_content, $expected, 'Dumped message is present.');
   }
 
 }

+ 3 - 3
sites/all/modules/contrib/dev/devel/src/Tests/DevelMenuLinksTest.php → sites/all/modules/contrib/dev/devel/tests/src/Functional/DevelMenuLinksTest.php

@@ -1,16 +1,16 @@
 <?php
 
-namespace Drupal\devel\Tests;
+namespace Drupal\Tests\devel\Functional;
 
 use Drupal\Core\Url;
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Tests devel menu links.
  *
  * @group devel
  */
-class DevelMenuLinksTest extends WebTestBase {
+class DevelMenuLinksTest extends BrowserTestBase {
 
   /**
    * Modules to enable.

+ 8 - 8
sites/all/modules/contrib/dev/devel/src/Tests/DevelReinstallTest.php → sites/all/modules/contrib/dev/devel/tests/src/Functional/DevelModulesReinstallTest.php

@@ -1,22 +1,22 @@
 <?php
 
-namespace Drupal\devel\Tests;
+namespace Drupal\Tests\devel\Functional;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Tests reinstall modules.
  *
  * @group devel
  */
-class DevelReinstallTest extends WebTestBase {
+class DevelModulesReinstallTest extends BrowserTestBase {
 
   /**
    * Modules to enable.
    *
    * @var array
    */
-  public static $modules = array('devel');
+  public static $modules = ['devel'];
 
   /**
    * The profile to install as a basis for testing.
@@ -31,7 +31,7 @@ class DevelReinstallTest extends WebTestBase {
   protected function setUp() {
     parent::setUp();
 
-    $web_user = $this->drupalCreateUser(array('administer site configuration'));
+    $web_user = $this->drupalCreateUser(['administer site configuration']);
     $this->drupalLogin($web_user);
   }
 
@@ -40,7 +40,7 @@ class DevelReinstallTest extends WebTestBase {
    */
   public function testDevelReinstallModules() {
     // Minimal profile enables only dblog, block and node.
-    $modules = array('dblog', 'block');
+    $modules = ['dblog', 'block'];
 
     // Needed for compare correctly the message.
     sort($modules);
@@ -48,13 +48,13 @@ class DevelReinstallTest extends WebTestBase {
     $this->drupalGet('devel/reinstall');
 
     // Prepare field data in an associative array
-    $edit = array();
+    $edit = [];
     foreach ($modules as $module) {
       $edit["reinstall[$module]"] = TRUE;
     }
 
     $this->drupalPostForm('devel/reinstall', $edit, t('Reinstall'));
-    $this->assertText(t('Uninstalled and installed: @names.', array('@names' => implode(', ', $modules))));
+    $this->assertText(t('Uninstalled and installed: @names.', ['@names' => implode(', ', $modules)]));
   }
 
 }

+ 6 - 6
sites/all/modules/contrib/dev/devel/src/Tests/DevelSwitchUserTest.php → sites/all/modules/contrib/dev/devel/tests/src/Functional/DevelSwitchUserTest.php

@@ -1,16 +1,16 @@
 <?php
 
-namespace Drupal\devel\Tests;
+namespace Drupal\Tests\devel\Functional;
 
 use Drupal\Component\Render\FormattableMarkup;
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Tests switch user.
  *
  * @group devel
  */
-class DevelSwitchUserTest extends WebTestBase {
+class DevelSwitchUserTest extends BrowserTestBase {
 
   /**
    * Modules to enable.
@@ -276,12 +276,12 @@ class DevelSwitchUserTest extends WebTestBase {
    */
   protected function assertSessionByUid($uid) {
     $query = \Drupal::database()->select('sessions');
-    $query->fields('sessions', array('uid'));
+    $query->fields('sessions', ['uid']);
     $query->condition('uid', $uid);
     $result = $query->execute()->fetchAll();
 
     if (empty($result)) {
-      $this->fail(new FormattableMarkup('No session found for uid @uid', array('@uid' => $uid)));
+      $this->fail(new FormattableMarkup('No session found for uid @uid', ['@uid' => $uid]));
     }
     elseif (count($result) > 1) {
       // If there is more than one session, then that must be unexpected.
@@ -304,7 +304,7 @@ class DevelSwitchUserTest extends WebTestBase {
    */
   protected function assertNoSessionByUid($uid) {
     $query = \Drupal::database()->select('sessions');
-    $query->fields('sessions', array('uid'));
+    $query->fields('sessions', ['uid']);
     $query->condition('uid', $uid);
     $result = $query->execute()->fetchAll();
     $this->assert(empty($result), "No session for uid $uid found.");

+ 6 - 3
sites/all/modules/contrib/dev/devel/webprofiler/src/DataCollector/DatabaseDataCollector.php

@@ -56,7 +56,7 @@ class DatabaseDataCollector extends DataCollector implements DrupalDataCollector
         unset($query['caller']['args']);
 
         // Remove query args element if empty.
-        if (empty($query['args'])) {
+        if (isset($query['args']) && empty($query['args'])) {
           unset($query['args']);
         }
 
@@ -223,8 +223,11 @@ class DatabaseDataCollector extends DataCollector implements DrupalDataCollector
       $query['type'] = $type;
 
       $quoted = [];
-      foreach ((array) $query['args'] as $key => $val) {
-        $quoted[$key] = is_null($val) ? 'NULL' : $conn->quote($val);
+
+      if (isset($query['args'])) {
+        foreach ((array) $query['args'] as $key => $val) {
+          $quoted[$key] = is_null($val) ? 'NULL' : $conn->quote($val);
+        }
       }
 
       $query['query_args'] = strtr($query['query'], $quoted);

+ 7 - 0
sites/all/modules/contrib/dev/devel/webprofiler/src/Entity/Decorators/Config/ConfigEntityStorageDecorator.php

@@ -108,6 +108,13 @@ class ConfigEntityStorageDecorator extends EntityDecorator implements ConfigEnti
     return $this->getOriginalObject()->save($entity);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function hasData() {
+    return $this->getOriginalObject()->hasData();
+  }
+
   /**
    * {@inheritdoc}
    */

+ 28 - 30
sites/all/modules/contrib/dev/devel/webprofiler/src/EventDispatcher/TraceableEventDispatcher.php

@@ -4,6 +4,7 @@ namespace Drupal\webprofiler\EventDispatcher;
 
 use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher;
 use Drupal\webprofiler\Stopwatch;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\EventDispatcher\Event;
 use Symfony\Component\HttpKernel\KernelEvents;
 
@@ -29,10 +30,19 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
   protected $notCalledListeners;
 
   /**
-   * @param \Drupal\webprofiler\Stopwatch $stopwatch
+   * {@inheritdoc}
    */
-  public function setStopwatch(Stopwatch $stopwatch) {
-    $this->stopwatch = $stopwatch;
+  public function __construct(ContainerInterface $container, array $listeners = []) {
+    parent::__construct($container, $listeners);
+    $this->notCalledListeners = $listeners;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function addListener($event_name, $listener, $priority = 0) {
+    parent::addListener($event_name, $listener, $priority);
+    $this->notCalledListeners[$event_name][$priority][] = ['callable' => $listener];
   }
 
   /**
@@ -97,6 +107,13 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
     return $this->notCalledListeners;
   }
 
+  /**
+   * @param \Drupal\webprofiler\Stopwatch $stopwatch
+   */
+  public function setStopwatch(Stopwatch $stopwatch) {
+    $this->stopwatch = $stopwatch;
+  }
+
   /**
    * Called before dispatching the event.
    *
@@ -158,38 +175,19 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
       'method' => $definition['callable'][1],
     ];
 
-    // Remove this listener from the $notCalledListeners array.
-    if (!$this->notCalledListeners) {
-      $this->notCalledListeners = $this->cloneListeners($this->listeners);
-    }
-
     foreach ($this->notCalledListeners[$event_name][$priority] as $key => $listener) {
-      if ($listener['service'][0] == $definition['service'][0] && $listener['service'][1] == $definition['service'][1]) {
-        unset($this->notCalledListeners[$event_name][$priority][$key]);
+      if (isset($listener['service'])) {
+        if ($listener['service'][0] == $definition['service'][0] && $listener['service'][1] == $definition['service'][1]) {
+          unset($this->notCalledListeners[$event_name][$priority][$key]);
+        }
       }
-    }
-  }
-
-  /**
-   * @param $listeners
-   *
-   * @return array
-   */
-  private function cloneListeners($listeners) {
-    $clone = [];
-
-    foreach ($listeners as $eventName => $events) {
-      foreach ($events as $priorityValue => $priorities) {
-        foreach ($priorities as $key => $listener) {
-          $clone[$eventName][$priorityValue][$key]['service'] = [
-            $listener['service'][0],
-            $listener['service'][1]
-          ];
+      else {
+        if (get_class($listener['callable'][0]) == get_class($definition['callable'][0]) && $listener['callable'][1] == $definition['callable'][1]) {
+          unset($this->notCalledListeners[$event_name][$priority][$key]);
         }
       }
-    }
 
-    return $clone;
+    }
   }
 
 }

+ 1 - 1
sites/all/modules/contrib/dev/devel/webprofiler/src/Form/ConfigForm.php

@@ -134,7 +134,7 @@ class ConfigForm extends ConfigFormBase {
       '#states' => array(
         'visible' => array(
           array(
-            ':input[name="active_toolbar_items[database]' => array('checked' => TRUE),
+            'input[name="active_toolbar_items[database]"]' => array('checked' => TRUE),
           ),
         ),
       ),

+ 1 - 1
sites/all/modules/contrib/dev/devel/webprofiler/src/Views/TraceableViewExecutable.php

@@ -31,7 +31,7 @@ class TraceableViewExecutable extends ViewExecutable {
    * @return float
    */
   public function getExecuteTime() {
-    return $this->execute_time;
+    return property_exists($this, 'execute_time') ? $this->execute_time : 0.0;
   }
 
   /**

+ 3 - 3
sites/all/modules/contrib/dev/devel/webprofiler/webprofiler.info.yml

@@ -9,8 +9,8 @@ tags:
 dependencies:
  - devel
 
-# Information added by Drupal.org packaging script on 2017-08-14
-version: '8.x-1.0'
+# Information added by Drupal.org packaging script on 2017-10-05
+version: '8.x-1.2'
 core: '8.x'
 project: 'devel'
-datestamp: 1502732047
+datestamp: 1507197848

+ 1 - 1
sites/all/modules/contrib/dev/entity/.travis.yml

@@ -38,7 +38,7 @@ before_script:
   # Export database variable for kernel tests.
   - export SIMPLETEST_DB=mysql://root:@127.0.0.1/entity
   # Download Drupal 8 core.
-  - travis_retry git clone --branch 8.1.x --depth 1 http://git.drupal.org/project/drupal.git
+  - travis_retry git clone --branch 8.3.x --depth 1 http://git.drupal.org/project/drupal.git
   - cd drupal
   - composer self-update
   - composer install -n

+ 1 - 1
sites/all/modules/contrib/dev/entity/composer.json

@@ -5,6 +5,6 @@
   "homepage": "http://drupal.org/project/entity",
   "license": "GPL-2.0+",
   "require": {
-    "drupal/core": "~8.1"
+    "drupal/core": "~8.3"
   }
 }

+ 4 - 4
sites/all/modules/contrib/dev/entity/entity.info.yml

@@ -3,10 +3,10 @@ description: Provides expanded entity APIs, which will be moved to Drupal core o
 type: module
 # core: 8.x
 dependencies:
-  - system (>=8.1.0)
+  - drupal:system (>=8.3.0)
 
-# Information added by Drupal.org packaging script on 2016-12-08
-version: '8.x-1.0-alpha4'
+# Information added by Drupal.org packaging script on 2017-09-20
+version: '8.x-1.0-beta1'
 core: '8.x'
 project: 'entity'
-datestamp: 1481194986
+datestamp: 1505895847

+ 82 - 0
sites/all/modules/contrib/dev/entity/entity.module

@@ -4,3 +4,85 @@
  * @file
  * Provides expanded entity APIs.
  */
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\entity\BundlePlugin\BundlePluginHandler;
+
+/**
+ * Gets the entity types which use bundle plugins.
+ *
+ * @return \Drupal\Core\Entity\EntityTypeInterface[]
+ *   The entity types.
+ */
+function entity_get_bundle_plugin_entity_types() {
+  $entity_types = \Drupal::entityTypeManager()->getDefinitions();
+  $entity_types = array_filter($entity_types, function (EntityTypeInterface $entity_type) {
+    return $entity_type->hasHandlerClass('bundle_plugin');
+  });
+
+  return $entity_types;
+}
+
+/**
+ * Implements hook_entity_type_build().
+ */
+function entity_entity_type_build(array &$entity_types) {
+  foreach ($entity_types as $entity_type) {
+    if ($entity_type->get('bundle_plugin_type')) {
+      $entity_type->setHandlerClass('bundle_plugin', BundlePluginHandler::class);
+    }
+  }
+}
+
+/**
+ * Implements hook_entity_bundle_info().
+ */
+function entity_entity_bundle_info() {
+  $bundles = [];
+  foreach (entity_get_bundle_plugin_entity_types() as $entity_type) {
+    /** @var \Drupal\entity\BundlePlugin\BundlePluginHandler $bundle_handler */
+    $bundle_handler = \Drupal::entityTypeManager()->getHandler($entity_type->id(), 'bundle_plugin');
+    $bundles[$entity_type->id()] = $bundle_handler->getBundleInfo();
+  }
+  return $bundles;
+}
+
+/**
+ * Implements hook_entity_field_storage_info().
+ */
+function entity_entity_field_storage_info(EntityTypeInterface $entity_type) {
+  if ($entity_type->hasHandlerClass('bundle_plugin')) {
+    /** @var \Drupal\entity\BundlePlugin\BundlePluginHandler $bundle_handler */
+    $bundle_handler = \Drupal::entityTypeManager()->getHandler($entity_type->id(), 'bundle_plugin');
+    return $bundle_handler->getFieldStorageDefinitions();
+  }
+}
+
+/**
+ * Implements hook_entity_bundle_field_info().
+ */
+function entity_entity_bundle_field_info(EntityTypeInterface $entity_type, $bundle) {
+  if ($entity_type->hasHandlerClass('bundle_plugin')) {
+    /** @var \Drupal\entity\BundlePlugin\BundlePluginHandler $bundle_handler */
+    $bundle_handler = \Drupal::entityTypeManager()->getHandler($entity_type->id(), 'bundle_plugin');
+    return $bundle_handler->getFieldDefinitions($bundle);
+  }
+}
+
+/**
+ * Implements hook_modules_installed().
+ */
+function entity_modules_installed($modules) {
+  foreach (entity_get_bundle_plugin_entity_types() as $entity_type) {
+    \Drupal::service('entity.bundle_plugin_installer')->installBundles($entity_type, $modules);
+  }
+}
+
+/**
+ * Implements hook_module_preuninstall().
+ */
+function entity_module_preuninstall($module) {
+  foreach (entity_get_bundle_plugin_entity_types() as $entity_type) {
+    \Drupal::service('entity.bundle_plugin_installer')->uninstallBundles($entity_type, [$module]);
+  }
+}

+ 11 - 1
sites/all/modules/contrib/dev/entity/entity.services.yml

@@ -1,6 +1,16 @@
 services:
   access_checker.entity_revision:
     class: \Drupal\entity\Access\EntityRevisionRouteAccessChecker
-    arguments: ['@entity_type.manager', '@request_stack']
+    arguments: ['@entity_type.manager', '@current_route_match']
     tags:
       - { name: access_check, applies_to: _entity_access_revision }
+
+  entity.bundle_plugin_installer:
+    class: Drupal\entity\BundlePlugin\BundlePluginInstaller
+    arguments: ['@entity_type.manager', '@entity_bundle.listener', '@field_storage_definition.listener', '@field_definition.listener']
+
+  entity.bundle_plugin.uninstall_validator:
+    class: \Drupal\entity\BundlePlugin\BundlePluginUninstallValidator
+    tags:
+      - { name: module_install.uninstall_validator }
+    arguments: ['@entity_type.manager', '@string_translation']

+ 1 - 1
sites/all/modules/contrib/dev/entity/entity.views.inc

@@ -9,7 +9,7 @@ use Drupal\Core\Entity\EntityTypeInterface;
 function entity_views_data() {
   $entity_types = \Drupal::entityTypeManager()->getDefinitions();
   $entity_types = array_filter($entity_types, function (EntityTypeInterface $entity_type) {
-    return $entity_type->isSubclassOf(ContentEntityInterface::class);
+    return $entity_type->entityClassImplements(ContentEntityInterface::class);
   });
 
   $data = [];

+ 19 - 16
sites/all/modules/contrib/dev/entity/src/Access/EntityRevisionRouteAccessChecker.php

@@ -7,9 +7,8 @@ use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Routing\Access\AccessInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Session\AccountInterface;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\RequestStack;
 use Symfony\Component\Routing\Route;
 
 /**
@@ -30,42 +29,42 @@ class EntityRevisionRouteAccessChecker implements AccessInterface {
   protected $accessCache = array();
 
   /**
-   * The request stack.
+   * The currently active route match object.
    *
-   * @var \Symfony\Component\HttpFoundation\RequestStack
+   * @var \Drupal\Core\Routing\RouteMatchInterface
    */
-  protected $requestStack;
+  protected $routeMatch;
 
   /**
    * Creates a new EntityRevisionRouteAccessChecker instance.
    *
    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
    *   The entity manager.
-   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
-   *   The request stack.
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The currently active route match object.
    */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager, RequestStack $request_stack) {
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, RouteMatchInterface $route_match) {
     $this->entityTypeManager = $entity_type_manager;
-    $this->requestStack = $request_stack;
+    $this->routeMatch = $route_match;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function access(Route $route, AccountInterface $account, Request $request = NULL) {
-    if (empty($request)) {
-      $request = $this->requestStack->getCurrentRequest();
+  public function access(Route $route, AccountInterface $account, RouteMatchInterface $route_match = NULL) {
+    if (empty($route_match)) {
+      $route_match = $this->routeMatch;
     }
 
     $operation = $route->getRequirement('_entity_access_revision');
-    list(, $operation) = explode('.', $operation, 2);
+    list($entity_type_id, $operation) = explode('.', $operation, 2);
 
     if ($operation === 'list') {
-      $_entity = $request->attributes->get('_entity', $request->attributes->get($route->getOption('entity_type_id')));
+      $_entity = $route_match->getParameter($entity_type_id);
       return AccessResult::allowedIf($this->checkAccess($_entity, $account, $operation))->cachePerPermissions();
     }
     else {
-      $_entity_revision = $request->attributes->get('_entity_revision');
+      $_entity_revision = $route_match->getParameter($entity_type_id . '_revision');
       return AccessResult::allowedIf($_entity_revision && $this->checkAccess($_entity_revision, $account, $operation))->cachePerPermissions();
     }
   }
@@ -104,8 +103,10 @@ class EntityRevisionRouteAccessChecker implements AccessInterface {
     $cid = $entity->getRevisionId() . ':' . $langcode . ':' . $account->id() . ':' . $operation;
 
     if (!isset($this->accessCache[$cid])) {
+      $admin_permission = $entity_type->getAdminPermission();
+
       // Perform basic permission checks first.
-      if (!$account->hasPermission($map[$operation]) && !$account->hasPermission($type_map[$operation]) && !$account->hasPermission('administer nodes')) {
+      if (!$account->hasPermission($map[$operation]) && !$account->hasPermission($type_map[$operation]) && ($admin_permission && !$account->hasPermission($admin_permission))) {
         $this->accessCache[$cid] = FALSE;
         return FALSE;
       }
@@ -114,6 +115,8 @@ class EntityRevisionRouteAccessChecker implements AccessInterface {
         $this->accessCache[$cid] = TRUE;
       }
       else {
+        // Entity access handlers are generally not aware of the "list" operation.
+        $operation = $operation == 'list' ? 'view' : $operation;
         // First check the access to the default revision and finally, if the
         // node passed in is not the default revision then access to that, too.
         $this->accessCache[$cid] = $entity_access->access($entity_storage->load($entity->id()), $operation, $account) && ($entity->isDefaultRevision() || $entity_access->access($entity, $operation, $account));

+ 27 - 0
sites/all/modules/contrib/dev/entity/src/BundleFieldDefinition.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace Drupal\entity;
+
+use Drupal\Core\Field\BaseFieldDefinition;
+
+/**
+ * Provides a field definition class for bundle fields.
+ *
+ * Core currently doesn't provide one, the hook_entity_bundle_field_info()
+ * example uses BaseFieldDefinition, which is wrong. Tracked in #2346347.
+ *
+ * Note that this class implements both FieldStorageDefinitionInterface and
+ * FieldDefinitionInterface. This is a simplification for DX reasons,
+ * allowing code to return just the bundle definitions instead of having to
+ * return both storage definitions and bundle definitions.
+ */
+class BundleFieldDefinition extends BaseFieldDefinition {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isBaseField() {
+    return FALSE;
+  }
+
+}

+ 102 - 0
sites/all/modules/contrib/dev/entity/src/BundlePlugin/BundlePluginHandler.php

@@ -0,0 +1,102 @@
+<?php
+
+namespace Drupal\entity\BundlePlugin;
+
+use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class BundlePluginHandler implements BundlePluginHandlerInterface {
+
+  /**
+   * The entity type.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeInterface
+   */
+  protected $entityType;
+
+  /**
+   * The bundle plugin manager.
+   *
+   * @var \Drupal\Component\Plugin\PluginManagerInterface
+   */
+  protected $pluginManager;
+
+  /**
+   * Constructs a new BundlePluginHandler object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   * @param \Drupal\Component\Plugin\PluginManagerInterface $plugin_manager
+   *   The bundle plugin manager.
+   */
+  public function __construct(EntityTypeInterface $entity_type, PluginManagerInterface $plugin_manager) {
+    $this->entityType = $entity_type;
+    $this->pluginManager = $plugin_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static(
+      $entity_type,
+      $container->get('plugin.manager.' . $entity_type->get('bundle_plugin_type'))
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBundleInfo() {
+    $bundles = [];
+    foreach ($this->pluginManager->getDefinitions() as $plugin_id => $definition) {
+      $bundles[$plugin_id] = [
+        'label' => $definition['label'],
+        'description' => isset($definition['description']) ? $definition['description'] : '',
+        'translatable' => $this->entityType->isTranslatable(),
+        'provider' => $definition['provider'],
+      ];
+    }
+    return $bundles;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldStorageDefinitions() {
+    $definitions = [];
+    foreach (array_keys($this->pluginManager->getDefinitions()) as $plugin_id) {
+      /** @var \Drupal\entity\BundlePlugin\BundlePluginInterface $plugin */
+      $plugin = $this->pluginManager->createInstance($plugin_id);
+      $definitions += $plugin->buildFieldDefinitions();
+    }
+    // Ensure the presence of required keys which aren't set by the plugin.
+    foreach ($definitions as $field_name => $definition) {
+      $definition->setName($field_name);
+      $definition->setTargetEntityTypeId($this->entityType->id());
+      $definitions[$field_name] = $definition;
+    }
+
+    return $definitions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldDefinitions($bundle) {
+    /** @var \Drupal\entity\BundlePlugin\BundlePluginInterface $plugin */
+    $plugin = $this->pluginManager->createInstance($bundle);
+    $definitions = $plugin->buildFieldDefinitions();
+    // Ensure the presence of required keys which aren't set by the plugin.
+    foreach ($definitions as $field_name => $definition) {
+      $definition->setName($field_name);
+      $definition->setTargetEntityTypeId($this->entityType->id());
+      $definition->setTargetBundle($bundle);
+      $definitions[$field_name] = $definition;
+    }
+
+    return $definitions;
+  }
+
+}

+ 37 - 0
sites/all/modules/contrib/dev/entity/src/BundlePlugin/BundlePluginHandlerInterface.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace Drupal\entity\BundlePlugin;
+
+use Drupal\Core\Entity\EntityHandlerInterface;
+
+/**
+ * Handles plugin-provided bundles.
+ */
+interface BundlePluginHandlerInterface extends EntityHandlerInterface {
+
+  /**
+   * Gets the bundle info.
+   *
+   * @return array
+   *   An array of bundle information keyed by the bundle name.
+   *   The format expected by hook_entity_bundle_info().
+   */
+  public function getBundleInfo();
+
+  /**
+   * Gets the field storage definitions.
+   */
+  public function getFieldStorageDefinitions();
+
+  /**
+   * Gets the field definitions for a specific bundle.
+   *
+   * @param string $bundle
+   *   The bundle name.
+   *
+   * @return \Drupal\entity\BundleFieldDefinition[]
+   *   An array of bundle field definitions, keyed by field name.
+   */
+  public function getFieldDefinitions($bundle);
+
+}

+ 94 - 0
sites/all/modules/contrib/dev/entity/src/BundlePlugin/BundlePluginInstaller.php

@@ -0,0 +1,94 @@
+<?php
+
+namespace Drupal\entity\BundlePlugin;
+
+use Drupal\Core\Entity\EntityBundleListenerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Field\FieldDefinitionListenerInterface;
+use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
+
+class BundlePluginInstaller implements BundlePluginInstallerInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity bundle listener.
+   *
+   * @var \Drupal\Core\Entity\EntityBundleListenerInterface
+   */
+  protected $entityBundleListener;
+
+  /**
+   * The field storage definition listener.
+   *
+   * @var \Drupal\Core\Field\FieldStorageDefinitionListenerInterface
+   */
+  protected $fieldStorageDefinitionListener;
+
+  /**
+   * The field definition listener.
+   *
+   * @var \Drupal\Core\Field\FieldDefinitionListenerInterface
+   */
+  protected $fieldDefinitionListener;
+
+  /**
+   * Constructs a new BundlePluginInstaller object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Entity\EntityBundleListenerInterface $entity_bundle_listener
+   *   The entity bundle listener.
+   * @param \Drupal\Core\Field\FieldStorageDefinitionListenerInterface $field_storage_definition_listener
+   *   The field storage definition listener.
+   * @param \Drupal\Core\Field\FieldDefinitionListenerInterface $field_definition_listener
+   *   The field definition listener.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityBundleListenerInterface $entity_bundle_listener, FieldStorageDefinitionListenerInterface $field_storage_definition_listener, FieldDefinitionListenerInterface $field_definition_listener) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->entityBundleListener = $entity_bundle_listener;
+    $this->fieldStorageDefinitionListener = $field_storage_definition_listener;
+    $this->fieldDefinitionListener = $field_definition_listener;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function installBundles(EntityTypeInterface $entity_type, array $modules) {
+    $bundle_handler = $this->entityTypeManager->getHandler($entity_type->id(), 'bundle_plugin');
+    $bundles = array_filter($bundle_handler->getBundleInfo(), function ($bundle_info) use ($modules) {
+      return in_array($bundle_info['provider'], $modules, TRUE);
+    });
+    foreach (array_keys($bundles) as $bundle) {
+      $this->entityBundleListener->onBundleCreate($bundle, $entity_type->id());
+      foreach ($bundle_handler->getFieldDefinitions($bundle) as $definition) {
+        $this->fieldStorageDefinitionListener->onFieldStorageDefinitionCreate($definition);
+        $this->fieldDefinitionListener->onFieldDefinitionCreate($definition);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function uninstallBundles(EntityTypeInterface $entity_type, array $modules) {
+    $bundle_handler = $this->entityTypeManager->getHandler($entity_type->id(), 'bundle_plugin');
+    $bundles = array_filter($bundle_handler->getBundleInfo(), function ($bundle_info) use ($modules) {
+      return in_array($bundle_info['provider'], $modules, TRUE);
+    });
+    foreach (array_keys($bundles) as $bundle) {
+      $this->entityBundleListener->onBundleDelete($bundle, $entity_type->id());
+      foreach ($bundle_handler->getFieldDefinitions($bundle) as $definition) {
+        $this->fieldDefinitionListener->onFieldDefinitionDelete($definition);
+        $this->fieldStorageDefinitionListener->onFieldStorageDefinitionDelete($definition);
+      }
+    }
+  }
+
+}

+ 34 - 0
sites/all/modules/contrib/dev/entity/src/BundlePlugin/BundlePluginInstallerInterface.php

@@ -0,0 +1,34 @@
+<?php
+
+namespace Drupal\entity\BundlePlugin;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+
+/**
+ * Installs and uninstalls bundle plugins.
+ *
+ * Ensures that the fields provided by the bundle plugins are created/deleted.
+ */
+interface BundlePluginInstallerInterface {
+
+  /**
+   * Installs the bundle plugins provided by the specified modules.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   * @param array $modules
+   *   The modules.
+   */
+  public function installBundles(EntityTypeInterface $entity_type, array $modules);
+
+  /**
+   * Uninstalls the bundle plugins provided by the specified modules.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   * @param array $modules
+   *   The modules.
+   */
+  public function uninstallBundles(EntityTypeInterface $entity_type, array $modules);
+
+}

+ 24 - 0
sites/all/modules/contrib/dev/entity/src/BundlePlugin/BundlePluginInterface.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace Drupal\entity\BundlePlugin;
+
+use Drupal\Component\Plugin\PluginInspectionInterface;
+
+/**
+ * Interface for plugins which act as entity bundles.
+ */
+interface BundlePluginInterface extends PluginInspectionInterface {
+
+  /**
+   * Builds the field definitions for entities of this bundle.
+   *
+   * Important:
+   * Field names must be unique across all bundles.
+   * It is recommended to prefix them with the bundle name (plugin ID).
+   *
+   * @return \Drupal\entity\BundleFieldDefinition[]
+   *   An array of bundle field definitions, keyed by field name.
+   */
+  public function buildFieldDefinitions();
+
+}

+ 76 - 0
sites/all/modules/contrib/dev/entity/src/BundlePlugin/BundlePluginUninstallValidator.php

@@ -0,0 +1,76 @@
+<?php
+
+namespace Drupal\entity\BundlePlugin;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Extension\ModuleUninstallValidatorInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+
+/**
+ * Prevents uninstalling modules with bundle plugins in case of found data.
+ */
+class BundlePluginUninstallValidator implements ModuleUninstallValidatorInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Constructs the object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   *   The string translation service.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->stringTranslation = $string_translation;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validate($module) {
+    $reasons = [];
+
+    foreach (entity_get_bundle_plugin_entity_types() as $entity_type) {
+      /** @var \Drupal\entity\BundlePlugin\BundlePluginHandler $bundle_handler */
+      $bundle_handler = $this->entityTypeManager->getHandler($entity_type->id(), 'bundle_plugin');
+      $bundles = $bundle_handler->getBundleInfo();
+
+      // We find all bundles which have to be removed due to the uninstallation.
+      $bundles_filtered_by_module = array_filter($bundles, function ($bundle_info) use ($module) {
+        return $module === $bundle_info['provider'];
+      });
+
+      if (!empty($bundles_filtered_by_module)) {
+        $bundle_keys_with_content = array_filter(array_keys($bundles_filtered_by_module), function ($bundle) use ($entity_type) {
+          $result = $this->entityTypeManager->getStorage($entity_type->id())->getQuery()
+            ->condition($entity_type->getKey('bundle'), $bundle)
+            ->range(0, 1)
+            ->execute();
+          return !empty($result);
+        });
+
+        $bundles_with_content = array_intersect_key($bundles_filtered_by_module, array_flip($bundle_keys_with_content));
+  
+        foreach ($bundles_with_content as $bundle) {
+          $reasons[] = $this->t('There is data for the bundle @bundle on the entity type @entity_type. Please remove all content before uninstalling the module.', [
+            '@bundle' => $bundle['label'],
+            '@entity_type' => $entity_type->getLabel(),
+          ]);
+        }
+      }
+    }
+
+    return $reasons;
+  }
+
+}

+ 0 - 1
sites/all/modules/contrib/dev/entity/src/Controller/RevisionOverviewController.php

@@ -11,7 +11,6 @@ use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Entity\RevisionLogInterface;
-use Drupal\user\EntityOwnerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**

+ 57 - 19
sites/all/modules/contrib/dev/entity/src/EntityAccessControlHandler.php

@@ -5,16 +5,30 @@ namespace Drupal\entity;
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Entity\EntityAccessControlHandler as CoreEntityAccessControlHandler;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityPublishedInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\user\EntityOwnerInterface;
 
 /**
  * Controls access based on the generic entity permissions.
  *
- * @see \Drupal\entity\EntityPermissionProvider
+ * @see \Drupal\entity\UncacheableEntityPermissionProvider
  */
 class EntityAccessControlHandler extends CoreEntityAccessControlHandler {
 
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(EntityTypeInterface $entity_type) {
+    parent::__construct($entity_type);
+
+    if (!$entity_type->hasHandlerClass('permission_provider') || !is_a($entity_type->getHandlerClass('permission_provider'), EntityPermissionProvider::class, TRUE)) {
+      throw new \Exception("This entity access control handler requires the entity permissions provider: {EntityPermissionProvider::class}");
+    }
+  }
+
+
   /**
    * {@inheritdoc}
    */
@@ -51,10 +65,18 @@ class EntityAccessControlHandler extends CoreEntityAccessControlHandler {
    *   The access result.
    */
   protected function checkEntityPermissions(EntityInterface $entity, $operation, AccountInterface $account) {
-    return AccessResult::allowedIfHasPermissions($account, [
-      "$operation {$entity->getEntityTypeId()}",
-      "$operation {$entity->bundle()} {$entity->getEntityTypeId()}",
-    ], 'OR');
+    if ($operation === 'view') {
+      $permissions = [
+        "view {$entity->getEntityTypeId()}"
+      ];
+    }
+    else {
+      $permissions = [
+        "$operation {$entity->getEntityTypeId()}",
+        "$operation {$entity->bundle()} {$entity->getEntityTypeId()}",
+      ];
+    }
+    return AccessResult::allowedIfHasPermissions($account, $permissions, 'OR');
   }
 
   /**
@@ -72,23 +94,39 @@ class EntityAccessControlHandler extends CoreEntityAccessControlHandler {
    *   The access result.
    */
   protected function checkEntityOwnerPermissions(EntityInterface $entity, $operation, AccountInterface $account) {
-    /** @var \Drupal\Core\Entity\EntityInterface|\Drupal\user\EntityOwnerInterface $entity */
-    if (($account->id() == $entity->getOwnerId())) {
-      $result = AccessResult::allowedIfHasPermissions($account, [
-        "$operation own {$entity->getEntityTypeId()}",
-        "$operation any {$entity->getEntityTypeId()}",
-        "$operation own {$entity->bundle()} {$entity->getEntityTypeId()}",
-        "$operation any {$entity->bundle()} {$entity->getEntityTypeId()}",
-      ], 'OR');
+    if ($operation === 'view') {
+      if ($entity instanceof EntityPublishedInterface && !$entity->isPublished()) {
+        if (($account->id() == $entity->getOwnerId())) {
+          $permissions = [
+            "view own unpublished {$entity->getEntityTypeId()}",
+          ];
+          return AccessResult::allowedIfHasPermissions($account, $permissions)->cachePerUser();
+        }
+        return AccessResult::neutral()->cachePerUser();
+      }
+      else {
+        return AccessResult::allowedIfHasPermissions($account, [
+          "view {$entity->getEntityTypeId()}",
+        ]);
+      }
     }
     else {
-      $result = AccessResult::allowedIfHasPermissions($account, [
-        "$operation any {$entity->getEntityTypeId()}",
-        "$operation any {$entity->bundle()} {$entity->getEntityTypeId()}",
-      ], 'OR');
+     if (($account->id() == $entity->getOwnerId())) {
+        $result = AccessResult::allowedIfHasPermissions($account, [
+          "$operation own {$entity->getEntityTypeId()}",
+          "$operation any {$entity->getEntityTypeId()}",
+          "$operation own {$entity->bundle()} {$entity->getEntityTypeId()}",
+          "$operation any {$entity->bundle()} {$entity->getEntityTypeId()}",
+        ], 'OR');
+      }
+      else {
+        $result = AccessResult::allowedIfHasPermissions($account, [
+          "$operation any {$entity->getEntityTypeId()}",
+          "$operation any {$entity->bundle()} {$entity->getEntityTypeId()}",
+        ], 'OR');
+      }
+      return $result;
     }
-
-    return $result->cachePerUser();
   }
 
   /**

+ 21 - 202
sites/all/modules/contrib/dev/entity/src/EntityPermissionProvider.php

@@ -2,8 +2,8 @@
 
 namespace Drupal\entity;
 
-use Drupal\Core\Entity\ContentEntityTypeInterface;
 use Drupal\Core\Entity\EntityHandlerInterface;
+use Drupal\Core\Entity\EntityPublishedInterface;
 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
@@ -11,10 +11,21 @@ use Drupal\user\EntityOwnerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
- * Provides generic entity permissions.
+ * Provides generic entity permissions which are still cacheable.
  *
- * Supports both entity_type and bundle granularities.
- * Supports entity ownership (own/any permissions).
+ * This includes:
+ *
+ * - administer $entity_type
+ * - access $entity_type overview
+ * - view $entity_type
+ * - view own unpublished $entity_type
+ * - update (own|any) ($bundle) $entity_type
+ * - delete (own|any) ($bundle) $entity_type
+ * - create $bundle $entity_type
+ *
+ * This class does not support "view own ($bundle) $entity_type", because this
+ * results in caching per user. If you need this use case, please use
+ * \Drupal\entity\UncacheableEntityPermissionProvider instead.
  *
  * Intended for content entity types, since config entity types usually rely
  * on a single "administer" permission.
@@ -29,218 +40,26 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  * @see \Drupal\entity\EntityAccessControlHandler
  * @see \Drupal\entity\EntityPermissions
  */
-class EntityPermissionProvider implements EntityPermissionProviderInterface, EntityHandlerInterface {
-
-  use StringTranslationTrait;
-
-  /**
-   * The entity type bundle info.
-   *
-   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
-   */
-  protected $entityTypeBundleInfo;
-
-  /**
-   * Constructs a new EntityPermissionProvider object.
-   *
-   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
-   *   The entity type bundle info.
-   */
-  public function __construct(EntityTypeBundleInfoInterface $entity_type_bundle_info) {
-    $this->entityTypeBundleInfo = $entity_type_bundle_info;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
-    return new static(
-      $container->get('entity_type.bundle.info')
-    );
-  }
+class EntityPermissionProvider extends EntityPermissionProviderBase {
 
   /**
    * {@inheritdoc}
    */
   public function buildPermissions(EntityTypeInterface $entity_type) {
     $entity_type_id = $entity_type->id();
-    $has_owner = $entity_type->isSubclassOf(EntityOwnerInterface::class);
-    $singular_label = $entity_type->getSingularLabel();
     $plural_label = $entity_type->getPluralLabel();
 
-    $permissions = [];
-    $permissions["administer {$entity_type_id}"] = [
-      'title' => $this->t('Administer @type', ['@type' => $plural_label]),
-      'restrict access' => TRUE,
-    ];
-    $permissions["access {$entity_type_id} overview"] = [
-      'title' => $this->t('Access the @type overview page', ['@type' => $plural_label]),
-    ];
-    // View permissions are the same for both granularities.
-    if ($has_owner) {
-      $permissions["view any {$entity_type_id}"] = [
-        'title' => $this->t('View any @type', [
-          '@type' => $singular_label,
-        ]),
-      ];
-      $permissions["view own {$entity_type_id}"] = [
-        'title' => $this->t('View own @type', [
-          '@type' => $plural_label,
-        ]),
-      ];
-    }
-    else {
-      $permissions["view {$entity_type_id}"] = [
-        'title' => $this->t('View @type', [
-          '@type' => $plural_label,
-        ]),
-      ];
-    }
-    // Generate the other permissions based on granularity.
-    if ($entity_type->getPermissionGranularity() == 'entity_type') {
-      $permissions += $this->buildEntityTypePermissions($entity_type);
-    }
-    else {
-      $permissions += $this->buildBundlePermissions($entity_type);
-    }
+    $permissions = parent::buildPermissions($entity_type);
 
-    foreach ($permissions as $name => $permission) {
-      // Permissions are grouped by provider on admin/people/permissions.
-      $permissions[$name]['provider'] = $entity_type->getProvider();
-      // TranslatableMarkup objects don't sort properly.
-      $permissions[$name]['title'] = (string) $permission['title'];
-    }
-
-    return $permissions;
-  }
-
-  /**
-   * Builds permissions for the entity_type granularity.
-   *
-   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
-   *   The entity type.
-   *
-   * @return array
-   *   The permissions.
-   */
-  protected function buildEntityTypePermissions(EntityTypeInterface $entity_type) {
-    $entity_type_id = $entity_type->id();
-    $has_owner = $entity_type->isSubclassOf(EntityOwnerInterface::class);
-    $singular_label = $entity_type->getSingularLabel();
-    $plural_label = $entity_type->getPluralLabel();
-
-    $permissions = [];
-    $permissions["create {$entity_type_id}"] = [
-      'title' => $this->t('Create @type', [
+    // View permissions are the same for both granularities.
+    $permissions["view {$entity_type_id}"] = [
+      'title' => $this->t('View @type', [
         '@type' => $plural_label,
       ]),
     ];
-    if ($has_owner) {
-      $permissions["update any {$entity_type_id}"] = [
-        'title' => $this->t('Update any @type', [
-          '@type' => $singular_label,
-        ]),
-      ];
-      $permissions["update own {$entity_type_id}"] = [
-        'title' => $this->t('Update own @type', [
-          '@type' => $plural_label,
-        ]),
-      ];
-      $permissions["delete any {$entity_type_id}"] = [
-        'title' => $this->t('Delete any @type', [
-          '@type' => $singular_label,
-        ]),
-      ];
-      $permissions["delete own {$entity_type_id}"] = [
-        'title' => $this->t('Delete own @type', [
-          '@type' => $plural_label,
-        ]),
-      ];
-    }
-    else {
-      $permissions["update {$entity_type_id}"] = [
-        'title' => $this->t('Update @type', [
-          '@type' => $plural_label,
-        ]),
-      ];
-      $permissions["delete {$entity_type_id}"] = [
-        'title' => $this->t('Delete @type', [
-          '@type' => $plural_label,
-        ]),
-      ];
-    }
 
-    return $permissions;
+    return $this->processPermissions($permissions, $entity_type);
   }
 
-  /**
-   * Builds permissions for the bundle granularity.
-   *
-   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
-   *   The entity type.
-   *
-   * @return array
-   *   The permissions.
-   */
-  protected function buildBundlePermissions(EntityTypeInterface $entity_type) {
-    $entity_type_id = $entity_type->id();
-    $bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id);
-    $has_owner = $entity_type->isSubclassOf(EntityOwnerInterface::class);
-    $singular_label = $entity_type->getSingularLabel();
-    $plural_label = $entity_type->getPluralLabel();
-
-    $permissions = [];
-    foreach ($bundles as $bundle_name => $bundle_info) {
-      $permissions["create {$bundle_name} {$entity_type_id}"] = [
-        'title' => $this->t('@bundle: Create @type', [
-          '@bundle' => $bundle_info['label'],
-          '@type' => $plural_label,
-        ]),
-      ];
-
-      if ($has_owner) {
-        $permissions["update any {$bundle_name} {$entity_type_id}"] = [
-          'title' => $this->t('@bundle: Update any @type', [
-            '@bundle' => $bundle_info['label'],
-            '@type' => $singular_label,
-          ]),
-        ];
-        $permissions["update own {$bundle_name} {$entity_type_id}"] = [
-          'title' => $this->t('@bundle: Update own @type', [
-            '@bundle' => $bundle_info['label'],
-            '@type' => $plural_label,
-          ]),
-        ];
-        $permissions["delete any {$bundle_name} {$entity_type_id}"] = [
-          'title' => $this->t('@bundle: Delete any @type', [
-            '@bundle' => $bundle_info['label'],
-            '@type' => $singular_label,
-          ]),
-        ];
-        $permissions["delete own {$bundle_name} {$entity_type_id}"] = [
-          'title' => $this->t('@bundle: Delete own @type', [
-            '@bundle' => $bundle_info['label'],
-            '@type' => $plural_label,
-          ]),
-        ];
-      }
-      else {
-        $permissions["update {$bundle_name} {$entity_type_id}"] = [
-          'title' => $this->t('@bundle: Update @type', [
-            '@bundle' => $bundle_info['label'],
-            '@type' => $plural_label,
-          ]),
-        ];
-        $permissions["delete {$bundle_name} {$entity_type_id}"] = [
-          'title' => $this->t('@bundle: Delete @type', [
-            '@bundle' => $bundle_info['label'],
-            '@type' => $plural_label,
-          ]),
-        ];
-      }
-    }
-
-    return $permissions;
-  }
 
 }

+ 231 - 0
sites/all/modules/contrib/dev/entity/src/EntityPermissionProviderBase.php

@@ -0,0 +1,231 @@
+<?php
+
+namespace Drupal\entity;
+
+use Drupal\Core\Entity\EntityHandlerInterface;
+use Drupal\Core\Entity\EntityPublishedInterface;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\user\EntityOwnerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @internal
+ */
+class EntityPermissionProviderBase implements EntityPermissionProviderInterface, EntityHandlerInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * The entity type bundle info.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
+   */
+  protected $entityTypeBundleInfo;
+
+  /**
+   * Constructs a new EntityPermissionProvider object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
+   *   The entity type bundle info.
+   */
+  public function __construct(EntityTypeBundleInfoInterface $entity_type_bundle_info) {
+    $this->entityTypeBundleInfo = $entity_type_bundle_info;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static(
+      $container->get('entity_type.bundle.info')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildPermissions(EntityTypeInterface $entity_type) {
+    $entity_type_id = $entity_type->id();
+    $has_owner = $entity_type->entityClassImplements(EntityOwnerInterface::class);
+    $plural_label = $entity_type->getPluralLabel();
+
+    $permissions = [];
+    $permissions["administer {$entity_type_id}"] = [
+      'title' => $this->t('Administer @type', ['@type' => $plural_label]),
+      'restrict access' => TRUE,
+    ];
+    $permissions["access {$entity_type_id} overview"] = [
+      'title' => $this->t('Access the @type overview page', ['@type' => $plural_label]),
+    ];
+    if ($has_owner && $entity_type->entityClassImplements(EntityPublishedInterface::class)) {
+      $permissions["view own unpublished {$entity_type_id}"] = [
+        'title' => $this->t('View own unpublished @type', [
+          '@type' => $plural_label,
+        ]),
+      ];
+    }
+
+    // Generate the other permissions based on granularity.
+    if ($entity_type->getPermissionGranularity() === 'entity_type') {
+      $permissions += $this->buildEntityTypePermissions($entity_type);
+    }
+    else {
+      $permissions += $this->buildBundlePermissions($entity_type);
+    }
+
+    return $this->processPermissions($permissions, $entity_type);
+  }
+
+  /**
+   * Adds the provider and converts the titles to strings to allow sorting.
+   *
+   * @param array $permissions
+   *   The array of permissions
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   *
+   * @return array
+   *   An array of processed permissions.
+   */
+  protected function processPermissions(array $permissions, EntityTypeInterface $entity_type) {
+    foreach ($permissions as $name => $permission) {
+      // Permissions are grouped by provider on admin/people/permissions.
+      $permissions[$name]['provider'] = $entity_type->getProvider();
+      // TranslatableMarkup objects don't sort properly.
+      $permissions[$name]['title'] = (string) $permission['title'];
+    }
+    return $permissions;
+  }
+
+  /**
+   * Builds permissions for the entity_type granularity.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   *
+   * @return array
+   *   The permissions.
+   */
+  protected function buildEntityTypePermissions(EntityTypeInterface $entity_type) {
+    $entity_type_id = $entity_type->id();
+    $has_owner = $entity_type->entityClassImplements(EntityOwnerInterface::class);
+    $singular_label = $entity_type->getSingularLabel();
+    $plural_label = $entity_type->getPluralLabel();
+
+    $permissions = [];
+    $permissions["create {$entity_type_id}"] = [
+      'title' => $this->t('Create @type', [
+        '@type' => $plural_label,
+      ]),
+    ];
+    if ($has_owner) {
+      $permissions["update any {$entity_type_id}"] = [
+        'title' => $this->t('Update any @type', [
+          '@type' => $singular_label,
+        ]),
+      ];
+      $permissions["update own {$entity_type_id}"] = [
+        'title' => $this->t('Update own @type', [
+          '@type' => $plural_label,
+        ]),
+      ];
+      $permissions["delete any {$entity_type_id}"] = [
+        'title' => $this->t('Delete any @type', [
+          '@type' => $singular_label,
+        ]),
+      ];
+      $permissions["delete own {$entity_type_id}"] = [
+        'title' => $this->t('Delete own @type', [
+          '@type' => $plural_label,
+        ]),
+      ];
+    }
+    else {
+      $permissions["update {$entity_type_id}"] = [
+        'title' => $this->t('Update @type', [
+          '@type' => $plural_label,
+        ]),
+      ];
+      $permissions["delete {$entity_type_id}"] = [
+        'title' => $this->t('Delete @type', [
+          '@type' => $plural_label,
+        ]),
+      ];
+    }
+
+    return $permissions;
+  }
+
+  /**
+   * Builds permissions for the bundle granularity.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   *
+   * @return array
+   *   The permissions.
+   */
+  protected function buildBundlePermissions(EntityTypeInterface $entity_type) {
+    $entity_type_id = $entity_type->id();
+    $bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id);
+    $has_owner = $entity_type->entityClassImplements(EntityOwnerInterface::class);
+    $singular_label = $entity_type->getSingularLabel();
+    $plural_label = $entity_type->getPluralLabel();
+
+    $permissions = [];
+    foreach ($bundles as $bundle_name => $bundle_info) {
+      $permissions["create {$bundle_name} {$entity_type_id}"] = [
+        'title' => $this->t('@bundle: Create @type', [
+          '@bundle' => $bundle_info['label'],
+          '@type' => $plural_label,
+        ]),
+      ];
+
+      if ($has_owner) {
+        $permissions["update any {$bundle_name} {$entity_type_id}"] = [
+          'title' => $this->t('@bundle: Update any @type', [
+            '@bundle' => $bundle_info['label'],
+            '@type' => $singular_label,
+          ]),
+        ];
+        $permissions["update own {$bundle_name} {$entity_type_id}"] = [
+          'title' => $this->t('@bundle: Update own @type', [
+            '@bundle' => $bundle_info['label'],
+            '@type' => $plural_label,
+          ]),
+        ];
+        $permissions["delete any {$bundle_name} {$entity_type_id}"] = [
+          'title' => $this->t('@bundle: Delete any @type', [
+            '@bundle' => $bundle_info['label'],
+            '@type' => $singular_label,
+          ]),
+        ];
+        $permissions["delete own {$bundle_name} {$entity_type_id}"] = [
+          'title' => $this->t('@bundle: Delete own @type', [
+            '@bundle' => $bundle_info['label'],
+            '@type' => $plural_label,
+          ]),
+        ];
+      }
+      else {
+        $permissions["update {$bundle_name} {$entity_type_id}"] = [
+          'title' => $this->t('@bundle: Update @type', [
+            '@bundle' => $bundle_info['label'],
+            '@type' => $plural_label,
+          ]),
+        ];
+        $permissions["delete {$bundle_name} {$entity_type_id}"] = [
+          'title' => $this->t('@bundle: Delete @type', [
+            '@bundle' => $bundle_info['label'],
+            '@type' => $plural_label,
+          ]),
+        ];
+      }
+    }
+
+    return $permissions;
+  }
+
+}

+ 1 - 0
sites/all/modules/contrib/dev/entity/src/EntityPermissions.php

@@ -10,6 +10,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  * Generates entity permissions via their permission providers.
  *
  * @see \Drupal\entity\EntityPermissionProvider
+ * @see \Drupal\entity\UncacheableEntityPermissionProvider
  */
 class EntityPermissions implements ContainerInjectionInterface {
 

+ 1 - 1
sites/all/modules/contrib/dev/entity/src/Plugin/Action/Derivative/DeleteActionDeriver.php

@@ -69,7 +69,7 @@ class DeleteActionDeriver extends DeriverBase implements ContainerDeriverInterfa
   protected function getParticipatingEntityTypes() {
     $entity_types = $this->entityTypeManager->getDefinitions();
     $entity_types = array_filter($entity_types, function (EntityTypeInterface $entity_type) {
-      return $entity_type->isSubclassOf(ContentEntityInterface::class) && $entity_type->hasLinkTemplate('delete-multiple-form');
+      return $entity_type->entityClassImplements(ContentEntityInterface::class) && $entity_type->hasLinkTemplate('delete-multiple-form');
     });
 
     return $entity_types;

+ 3 - 2
sites/all/modules/contrib/dev/entity/src/Plugin/Derivative/RevisionsOverviewDeriver.php

@@ -3,6 +3,7 @@
 namespace Drupal\entity\Plugin\Derivative;
 
 use Drupal\Component\Plugin\Derivative\DeriverBase;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -24,7 +25,7 @@ class RevisionsOverviewDeriver extends DeriverBase implements ContainerDeriverIn
    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
    *   The entity type manager.
    */
-  public function __construct(\Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager) {
+  public function __construct(EntityTypeManagerInterface $entityTypeManager) {
     $this->entityTypeManager = $entityTypeManager;
   }
 
@@ -55,7 +56,7 @@ class RevisionsOverviewDeriver extends DeriverBase implements ContainerDeriverIn
 
       $this->derivatives[$entity_type_id] = [
         'route_name' => "entity.$entity_type_id.version_history",
-        'title' => 'Revisions',
+        'title' => t('Revisions'),
         'base_route' => "entity.$entity_type_id.canonical",
         'weight' => 20,
       ] + $base_plugin_definition;

+ 0 - 1
sites/all/modules/contrib/dev/entity/src/Revision/RevisionableContentEntityBase.php

@@ -3,7 +3,6 @@
 namespace Drupal\entity\Revision;
 
 use Drupal\Core\Entity\RevisionableContentEntityBase as BaseRevisionableContentEntityBase;
-use Drupal\Core\Entity\ContentEntityBase;
 
 /**
  * Improves the url route handling of core's revisionable content entity base.

+ 138 - 0
sites/all/modules/contrib/dev/entity/src/UncacheableEntityAccessControlHandler.php

@@ -0,0 +1,138 @@
+<?php
+
+namespace Drupal\entity;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Entity\EntityAccessControlHandler as CoreEntityAccessControlHandler;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityPublishedInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\user\EntityOwnerInterface;
+
+/**
+ * Controls access based on the uncacheable entity permissions.
+ *
+ * @see \Drupal\entity\UncacheableEntityPermissionProvider
+ *
+ * Note: this access control handler will cause pages to be cached per user.
+ */
+class UncacheableEntityAccessControlHandler extends CoreEntityAccessControlHandler {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(EntityTypeInterface $entity_type) {
+    parent::__construct($entity_type);
+
+    if (!$entity_type->hasHandlerClass('permission_provider') || !is_a($entity_type->getHandlerClass('permission_provider'), UncacheableEntityPermissionProvider::class, TRUE)) {
+      throw new \Exception("This entity access control handler requires the entity permissions provider: {EntityPermissionProvider::class}");
+    }
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
+    $account = $this->prepareUser($account);
+    /** @var \Drupal\Core\Access\AccessResult $result */
+    $result = parent::checkAccess($entity, $operation, $account);
+
+    if ($result->isNeutral()) {
+      if ($entity instanceof EntityOwnerInterface) {
+        $result = $this->checkEntityOwnerPermissions($entity, $operation, $account);
+      }
+      else {
+        $result = $this->checkEntityPermissions($entity, $operation, $account);
+      }
+    }
+
+    // Ensure that access is evaluated again when the entity changes.
+    return $result->addCacheableDependency($entity);
+  }
+
+  /**
+   * Checks the entity operation and bundle permissions.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity for which to check access.
+   * @param string $operation
+   *   The entity operation. Usually one of 'view', 'view label', 'update' or
+   *   'delete'.
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The user for which to check access.
+   *
+   * @return \Drupal\Core\Access\AccessResultInterface
+   *   The access result.
+   */
+  protected function checkEntityPermissions(EntityInterface $entity, $operation, AccountInterface $account) {
+    return AccessResult::allowedIfHasPermissions($account, [
+      "$operation {$entity->getEntityTypeId()}",
+      "$operation {$entity->bundle()} {$entity->getEntityTypeId()}",
+    ], 'OR');
+  }
+
+  /**
+   * Checks the entity operation and bundle permissions, with owners.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity for which to check access.
+   * @param string $operation
+   *   The entity operation. Usually one of 'view', 'view label', 'update' or
+   *   'delete'.
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The user for which to check access.
+   *
+   * @return \Drupal\Core\Access\AccessResultInterface
+   *   The access result.
+   */
+  protected function checkEntityOwnerPermissions(EntityInterface $entity, $operation, AccountInterface $account) {
+    /** @var \Drupal\Core\Entity\EntityInterface|\Drupal\user\EntityOwnerInterface $entity */
+    if (($account->id() == $entity->getOwnerId())) {
+      if ($operation === 'view' && $entity instanceof EntityPublishedInterface && !$entity->isPublished()) {
+        $permissions = [
+          "view own unpublished {$entity->getEntityTypeId()}",
+        ];
+      }
+      else {
+        $permissions = [
+          "$operation own {$entity->getEntityTypeId()}",
+          "$operation any {$entity->getEntityTypeId()}",
+          "$operation own {$entity->bundle()} {$entity->getEntityTypeId()}",
+          "$operation any {$entity->bundle()} {$entity->getEntityTypeId()}",
+        ];
+      }
+      $result = AccessResult::allowedIfHasPermissions($account, $permissions, 'OR');
+    }
+    else {
+      $result = AccessResult::allowedIfHasPermissions($account, [
+        "$operation any {$entity->getEntityTypeId()}",
+        "$operation any {$entity->bundle()} {$entity->getEntityTypeId()}",
+      ], 'OR');
+    }
+
+    return $result->cachePerUser();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
+    $result = parent::checkCreateAccess($account, $context, $entity_bundle);
+    if ($result->isNeutral()) {
+      $permissions = [
+        'administer ' . $this->entityTypeId,
+        'create ' . $this->entityTypeId,
+      ];
+      if ($entity_bundle) {
+        $permissions[] = 'create ' . $entity_bundle . ' ' . $this->entityTypeId;
+      }
+
+      $result = AccessResult::allowedIfHasPermissions($account, $permissions, 'OR');
+    }
+
+    return $result;
+  }
+
+}

+ 143 - 0
sites/all/modules/contrib/dev/entity/src/UncacheableEntityPermissionProvider.php

@@ -0,0 +1,143 @@
+<?php
+
+namespace Drupal\entity;
+
+use Drupal\Core\Entity\EntityHandlerInterface;
+use Drupal\Core\Entity\EntityPublishedInterface;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\user\EntityOwnerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides generic entity permissions which are cached per user.
+ *
+ * This includes:
+ *
+ * - administer $entity_type
+ * - access $entity_type overview
+ * - view an ($bundle) $entity_type
+ * - view own ($bundle) $entity_type
+ * - view own unpublished $entity_type
+ * - update (own|any) ($bundle) $entity_type
+ * - delete (own|any) ($bundle) $entity_type
+ * - create $bundle $entity_type
+ *
+ * As this class supports "view own ($bundle) $entity_type" it is just cacheable
+ * per user, which might harm performance of sites. Given that please use 
+ * \Drupal\entity\EntityPermissionProvider unless you need the feature, or your
+ * entity type is not really user facing (commerce orders for example).
+ *
+ * Intended for content entity types, since config entity types usually rely
+ * on a single "administer" permission.
+ * Example annotation:
+ * @code
+ *  handlers = {
+ *    "access" = "Drupal\entity\UncacheableEntityAccessControlHandler",
+ *    "permission_provider" = "Drupal\entity\UncacheableEntityPermissionProvider",
+ *  }
+ * @endcode
+ *
+ * @see \Drupal\entity\EntityAccessControlHandler
+ * @see \Drupal\entity\EntityPermissions
+ */
+class UncacheableEntityPermissionProvider extends EntityPermissionProviderBase {
+
+  /**
+   * Builds permissions for the entity_type granularity.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   *
+   * @return array
+   *   The permissions.
+   */
+  protected function buildEntityTypePermissions(EntityTypeInterface $entity_type) {
+    $permissions = parent::buildEntityTypePermissions($entity_type);
+
+    $entity_type_id = $entity_type->id();
+    $has_owner = $entity_type->entityClassImplements(EntityOwnerInterface::class);
+    $plural_label = $entity_type->getPluralLabel();
+
+    if ($has_owner) {
+      $permissions["view any {$entity_type_id}"] = [
+        'title' => $this->t('View any @type', [
+          '@type' => $plural_label,
+        ]),
+      ];
+      $permissions["view own {$entity_type_id}"] = [
+        'title' => $this->t('View own @type', [
+          '@type' => $plural_label,
+        ]),
+      ];
+    }
+    else {
+      $permissions["view any {$entity_type_id}"] = [
+        'title' => $this->t('View any @type', [
+          '@type' => $plural_label,
+        ]),
+      ];
+    }
+
+    return $permissions;
+  }
+
+  /**
+   * Builds permissions for the bundle granularity.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   *
+   * @return array
+   *   The permissions.
+   */
+  protected function buildBundlePermissions(EntityTypeInterface $entity_type) {
+    $permissions = parent::buildBundlePermissions($entity_type);
+    $entity_type_id = $entity_type->id();
+    $bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id);
+    $has_owner = $entity_type->entityClassImplements(EntityOwnerInterface::class);
+    $plural_label = $entity_type->getPluralLabel();
+
+    $permissions["view any {$entity_type_id}"] = [
+      'title' => $this->t('View any @type', [
+        '@type' => $plural_label,
+      ]),
+    ];
+    if ($has_owner) {
+      $permissions["view own {$entity_type_id}"] = [
+        'title' => $this->t('View own @type', [
+          '@type' => $plural_label,
+        ]),
+      ];
+    }
+
+    foreach ($bundles as $bundle_name => $bundle_info) {
+      if ($has_owner) {
+        $permissions["view any {$bundle_name} {$entity_type_id}"] = [
+          'title' => $this->t('@bundle: View any @type', [
+            '@bundle' => $bundle_info['label'],
+            '@type' => $plural_label,
+          ]),
+        ];
+        $permissions["view own {$bundle_name} {$entity_type_id}"] = [
+          'title' => $this->t('@bundle: View own @type', [
+            '@bundle' => $bundle_info['label'],
+            '@type' => $plural_label,
+          ]),
+        ];
+      }
+      else {
+        $permissions["view any {$bundle_name} {$entity_type_id}"] = [
+          'title' => $this->t('@bundle: View any @type', [
+            '@bundle' => $bundle_info['label'],
+            '@type' => $plural_label,
+          ]),
+        ];
+      }
+    }
+
+    return $permissions;
+  }
+
+}

+ 13 - 0
sites/all/modules/contrib/dev/entity/tests/modules/entity_module_bundle_plugin_examples_test/entity_module_bundle_plugin_examples_test.info.yml

@@ -0,0 +1,13 @@
+name: 'Entity bundle plugin examples test'
+type: module
+description: 'Module for testing bundle plugins.'
+package: Testing
+# core: 8.x
+dependencies:
+  - entity
+
+# Information added by Drupal.org packaging script on 2017-09-20
+version: '8.x-1.0-beta1'
+core: '8.x'
+project: 'entity'
+datestamp: 1505895847

+ 31 - 0
sites/all/modules/contrib/dev/entity/tests/modules/entity_module_bundle_plugin_examples_test/src/Plugin/BundlePluginTest/Second.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace Drupal\entity_module_bundle_plugin_examples_test\Plugin\BundlePluginTest;
+
+use Drupal\entity\BundleFieldDefinition;
+use Drupal\Core\Plugin\PluginBase;
+use Drupal\entity_module_bundle_plugin_test\Plugin\BundlePluginTest\BundlePluginTestInterface;
+
+/**
+ * Provides the second bundle plugin.
+ *
+ * @BundlePluginTest(
+ *   id = "second",
+ *   label = @Translation("Second"),
+ * )
+ */
+class Second extends PluginBase implements BundlePluginTestInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildFieldDefinitions() {
+    $fields = [];
+    $fields['second_mail'] = BundleFieldDefinition::create('email')
+      ->setLabel(t('Email'))
+      ->setRequired(TRUE);
+
+    return $fields;
+  }
+
+}

+ 13 - 0
sites/all/modules/contrib/dev/entity/tests/modules/entity_module_bundle_plugin_test/entity_module_bundle_plugin_test.info.yml

@@ -0,0 +1,13 @@
+name: 'Entity bundle plugin test'
+type: module
+description: 'Module for testing bundle plugins.'
+package: Testing
+# core: 8.x
+dependencies:
+  - entity
+
+# Information added by Drupal.org packaging script on 2017-09-20
+version: '8.x-1.0-beta1'
+core: '8.x'
+project: 'entity'
+datestamp: 1505895847

+ 4 - 0
sites/all/modules/contrib/dev/entity/tests/modules/entity_module_bundle_plugin_test/entity_module_bundle_plugin_test.services.yml

@@ -0,0 +1,4 @@
+services:
+  plugin.manager.bundle_plugin_test:
+    class: Drupal\entity_module_bundle_plugin_test\BundlePluginTestManager
+    parent: default_plugin_manager

+ 34 - 0
sites/all/modules/contrib/dev/entity/tests/modules/entity_module_bundle_plugin_test/src/Annotation/BundlePluginTest.php

@@ -0,0 +1,34 @@
+<?php
+
+namespace Drupal\entity_module_bundle_plugin_test\Annotation;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines the BundlePluginTest annotation object.
+ *
+ * Plugin namespace: Plugin\BundlePluginTest.
+ *
+ * @see plugin_api
+ *
+ * @Annotation
+ */
+class BundlePluginTest extends Plugin {
+
+  /**
+   * The plugin ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The plugin label.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   */
+  public $label;
+
+}

+ 35 - 0
sites/all/modules/contrib/dev/entity/tests/modules/entity_module_bundle_plugin_test/src/BundlePluginTestManager.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace Drupal\entity_module_bundle_plugin_test;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+
+/**
+ * Manages discovery and instantiation of BundlePluginTest plugins.
+ *
+ * @see \Drupal\entity_module_bundle_plugin_test\Annotation\BundlePluginTest
+ * @see plugin_api
+ */
+class BundlePluginTestManager extends DefaultPluginManager {
+
+  /**
+   * Constructs a new BundlePluginTestManager object.
+   *
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   The cache backend.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
+    parent::__construct('Plugin/BundlePluginTest', $namespaces, $module_handler, 'Drupal\entity_module_bundle_plugin_test\Plugin\BundlePluginTest\BundlePluginTestInterface', 'Drupal\entity_module_bundle_plugin_test\Annotation\BundlePluginTest');
+
+    $this->alterInfo('bundle_plugin_test_info');
+    $this->setCacheBackend($cache_backend, 'bundle_plugin_test_plugins');
+  }
+
+}

Some files were not shown because too many files changed in this diff