Browse Source

updated contrib modules

Bachir Soussi Chiadmi 6 years ago
parent
commit
c57b3644fe
100 changed files with 6178 additions and 757 deletions
  1. 139 0
      README.txt
  2. 21 0
      sites/all/modules/contrib/admin/admin_toolbar/CHANGELOG.txt
  3. 4 4
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar.info.yml
  4. 3 1
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_links_access_filter/README.txt
  5. 5 4
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_links_access_filter/admin_toolbar_links_access_filter.info.yml
  6. 17 0
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_links_access_filter/admin_toolbar_links_access_filter.module
  7. 4 4
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/admin_toolbar_tools.info.yml
  8. 44 0
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/admin_toolbar_tools.links.menu.yml
  9. 43 18
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/admin_toolbar_tools.module
  10. 0 4
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/admin_toolbar_tools.services.yml
  11. 1 1
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/src/Tests/AdminToolbarToolsAlterTest.php
  12. 0 135
      sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/src/ToolbarHandler.php
  13. 1 1
      sites/all/modules/contrib/admin/admin_toolbar/css/admin.toolbar.css
  14. 48 45
      sites/all/modules/contrib/admin/admin_toolbar/js/admin_toolbar.js
  15. 3 3
      sites/all/modules/contrib/admin/config_update/config_update.info.yml
  16. 3 3
      sites/all/modules/contrib/admin/config_update/config_update_ui/config_update_ui.info.yml
  17. 1 3
      sites/all/modules/contrib/admin/config_update/config_update_ui/config_update_ui.routing.yml
  18. 0 20
      sites/all/modules/contrib/admin/config_update/config_update_ui/src/Controller/ConfigUpdateController.php
  19. 12 11
      sites/all/modules/contrib/admin/config_update/config_update_ui/src/Form/ConfigDeleteConfirmForm.php
  20. 146 0
      sites/all/modules/contrib/admin/config_update/config_update_ui/src/Form/ConfigImportConfirmForm.php
  21. 14 11
      sites/all/modules/contrib/admin/config_update/config_update_ui/src/Form/ConfigRevertConfirmForm.php
  22. 15 0
      sites/all/modules/contrib/admin/config_update/config_update_ui/src/Tests/ConfigUpdateTest.php
  23. 8 2
      sites/all/modules/contrib/admin/config_update/src/ConfigLister.php
  24. 265 0
      sites/all/modules/contrib/admin/config_update/tests/src/Unit/ConfigListerTest.php
  25. 3 3
      sites/all/modules/contrib/fields/audiofield/audiofield.info.yml
  26. 1 1
      sites/all/modules/contrib/fields/audiofield/audiofield.module
  27. 18 0
      sites/all/modules/contrib/fields/audiofield/composer.json
  28. 3 0
      sites/all/modules/contrib/fields/audiofield/js/jplayer.builder.es6.js
  29. 4 1
      sites/all/modules/contrib/fields/audiofield/js/jplayer.builder.js
  30. 3 0
      sites/all/modules/contrib/fields/audiofield/src/Plugin/AudioPlayer/JPlayerAudioPlayer.php
  31. 75 11
      sites/all/modules/contrib/fields/audiofield/src/Plugin/Field/FieldFormatter/AudioFieldFieldFormatter.php
  32. 1 1
      sites/all/modules/contrib/fields/audiofield/templates/audioplayer--audiojs.html.twig
  33. 1 1
      sites/all/modules/contrib/fields/audiofield/templates/audioplayer--mediaelement.html.twig
  34. 1 1
      sites/all/modules/contrib/fields/audiofield/templates/audioplayer--projekktor.html.twig
  35. 1 1
      sites/all/modules/contrib/fields/audiofield/templates/audioplayer.html.twig
  36. 339 0
      sites/all/modules/contrib/fields/filefield_sources/LICENSE.txt
  37. 43 0
      sites/all/modules/contrib/fields/filefield_sources/README.txt
  38. 5 0
      sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.data_types.schema.yml
  39. 9 0
      sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.schema.yml
  40. 13 0
      sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.setting.source_attach.schema.yml
  41. 7 0
      sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.setting.source_imce.schema.yml
  42. 10 0
      sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.setting.source_reference.schema.yml
  43. 5 0
      sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.setting.sources.schema.yml
  44. 3 0
      sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.source.attach.schema.yml
  45. 3 0
      sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.source.clipboard.schema.yml
  46. 3 0
      sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.source.imce.schema.yml
  47. 3 0
      sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.source.reference.schema.yml
  48. 3 0
      sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.source.remote.schema.yml
  49. 5 0
      sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.source.upload.schema.yml
  50. 44 0
      sites/all/modules/contrib/fields/filefield_sources/css/filefield_sources.css
  51. 42 0
      sites/all/modules/contrib/fields/filefield_sources/filefield_sources.api.php
  52. 14 0
      sites/all/modules/contrib/fields/filefield_sources/filefield_sources.info.yml
  53. 23 0
      sites/all/modules/contrib/fields/filefield_sources/filefield_sources.install
  54. 9 0
      sites/all/modules/contrib/fields/filefield_sources/filefield_sources.libraries.yml
  55. 771 0
      sites/all/modules/contrib/fields/filefield_sources/filefield_sources.module
  56. 2 0
      sites/all/modules/contrib/fields/filefield_sources/filefield_sources.routing.yml
  57. 12 0
      sites/all/modules/contrib/fields/filefield_sources/filefield_sources.services.yml
  58. 262 0
      sites/all/modules/contrib/fields/filefield_sources/js/filefield_sources.js
  59. 38 0
      sites/all/modules/contrib/fields/filefield_sources/src/Access/FieldAccessCheck.php
  60. 62 0
      sites/all/modules/contrib/fields/filefield_sources/src/Annotation/FilefieldSource.php
  61. 111 0
      sites/all/modules/contrib/fields/filefield_sources/src/Controller/ImceController.php
  62. 75 0
      sites/all/modules/contrib/fields/filefield_sources/src/File/MimeType/ExtensionMimeTypeGuesser.php
  63. 55 0
      sites/all/modules/contrib/fields/filefield_sources/src/FilefieldSourceInterface.php
  64. 54 0
      sites/all/modules/contrib/fields/filefield_sources/src/FilefieldSourceManager.php
  65. 26 0
      sites/all/modules/contrib/fields/filefield_sources/src/FilefieldSourcesServiceProvider.php
  66. 102 0
      sites/all/modules/contrib/fields/filefield_sources/src/ImceScanner.php
  67. 356 0
      sites/all/modules/contrib/fields/filefield_sources/src/Plugin/FilefieldSource/Attach.php
  68. 152 0
      sites/all/modules/contrib/fields/filefield_sources/src/Plugin/FilefieldSource/Clipboard.php
  69. 216 0
      sites/all/modules/contrib/fields/filefield_sources/src/Plugin/FilefieldSource/Imce.php
  70. 239 0
      sites/all/modules/contrib/fields/filefield_sources/src/Plugin/FilefieldSource/Reference.php
  71. 400 0
      sites/all/modules/contrib/fields/filefield_sources/src/Plugin/FilefieldSource/Remote.php
  72. 108 0
      sites/all/modules/contrib/fields/filefield_sources/src/ProxyClass/File/MimeType/ExtensionMimeTypeGuesser.php
  73. 35 0
      sites/all/modules/contrib/fields/filefield_sources/src/Routing/FilefieldSourcesRoutes.php
  74. 168 0
      sites/all/modules/contrib/fields/filefield_sources/src/Tests/AttachSourceTest.php
  75. 37 0
      sites/all/modules/contrib/fields/filefield_sources/src/Tests/ClipboardSourceTest.php
  76. 90 0
      sites/all/modules/contrib/fields/filefield_sources/src/Tests/EmptyValuesTest.php
  77. 450 0
      sites/all/modules/contrib/fields/filefield_sources/src/Tests/FileFieldSourcesTestBase.php
  78. 52 0
      sites/all/modules/contrib/fields/filefield_sources/src/Tests/ImceSourceTest.php
  79. 133 0
      sites/all/modules/contrib/fields/filefield_sources/src/Tests/MultipleValuesTest.php
  80. 109 0
      sites/all/modules/contrib/fields/filefield_sources/src/Tests/ReferenceSourceTest.php
  81. 38 0
      sites/all/modules/contrib/fields/filefield_sources/src/Tests/RemoteSourceTest.php
  82. 62 0
      sites/all/modules/contrib/fields/filefield_sources/src/Tests/UploadSourceTest.php
  83. 2 2
      sites/all/modules/contrib/migrate/migrate_plus/composer.json
  84. 2 2
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration.beer_node.yml
  85. 1 1
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration.beer_term.yml
  86. 1 1
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration.beer_user.yml
  87. 5 3
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example.info.yml
  88. 3 3
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/migrate_example_setup.info.yml
  89. 132 130
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/migrate_example_setup.install
  90. 3 3
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrations/beer_comment.yml
  91. 2 2
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerComment.php
  92. 2 2
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerNode.php
  93. 3 3
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerTerm.php
  94. 1 1
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_terms.yml
  95. 1 1
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_variety_list.yml.txt
  96. 1 1
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_variety_multi_xml.yml
  97. 3 3
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced.info.yml
  98. 3 3
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/migrate_example_advanced_setup.info.yml
  99. 303 302
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/migrate_example_advanced_setup.install
  100. 4 4
      sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/src/Plugin/rest/resource/VarietyItems.php

+ 139 - 0
README.txt

@@ -0,0 +1,139 @@
+
+CONTENTS OF THIS FILE
+---------------------
+
+ * About Drupal
+ * Configuration and features
+ * Installation profiles
+ * Appearance
+ * Developing for Drupal
+ * More information
+
+ABOUT DRUPAL
+------------
+
+Drupal is an open source content management platform supporting a variety of
+websites ranging from personal weblogs to large community-driven websites. For
+more information, see the Drupal website at https://www.drupal.org, and join
+the Drupal community at https://www.drupal.org/community.
+
+Legal information about Drupal:
+ * Know your rights when using Drupal:
+   See LICENSE.txt in the "core" directory.
+ * Learn about the Drupal trademark and logo policy:
+   https://www.drupal.com/trademark
+
+CONFIGURATION AND FEATURES
+--------------------------
+
+Drupal core (what you get when you download and extract a drupal-x.y.tar.gz or
+drupal-x.y.zip file from https://www.drupal.org/project/drupal) has what you
+need to get started with your website. It includes several modules (extensions
+that add functionality) for common website features, such as managing content,
+user accounts, image uploading, and search. Core comes with many options that
+allow site-specific configuration. In addition to the core modules, there are
+thousands of contributed modules (for functionality not included with Drupal
+core) available for download.
+
+More about configuration:
+ * Install, update, and maintain Drupal:
+   See INSTALL.txt and UPDATE.txt in the "core" directory.
+ * Learn about how to use Drupal to create your site:
+   https://www.drupal.org/documentation
+ * Follow best practices:
+   https://www.drupal.org/best-practices
+ * Download contributed modules to /modules to extend Drupal's functionality:
+   https://www.drupal.org/project/modules
+ * See also: "Developing for Drupal" for writing your own modules, below.
+
+
+INSTALLATION PROFILES
+---------------------
+
+Installation profiles define additional steps (such as enabling modules,
+defining content types, etc.) that run after the base installation provided
+by core when Drupal is first installed. There are two basic installation
+profiles provided with Drupal core.
+
+Installation profiles from the Drupal community modify the installation process
+to provide a website for a specific use case, such as a CMS for media
+publishers, a web-based project tracking tool, or a full-fledged CRM for
+non-profit organizations raising money and accepting donations. They can be
+distributed as bare installation profiles or as "distributions". Distributions
+include Drupal core, the installation profile, and all other required
+extensions, such as contributed and custom modules, themes, and third-party
+libraries. Bare installation profiles require you to download Drupal Core and
+the required extensions separately; place the downloaded profile in the
+/profiles directory before you start the installation process.
+
+More about installation profiles and distributions:
+ * Read about the difference between installation profiles and distributions:
+   https://www.drupal.org/node/1089736
+ * Download contributed installation profiles and distributions:
+   https://www.drupal.org/project/distributions
+ * Develop your own installation profile or distribution:
+   https://www.drupal.org/developing/distributions
+
+
+APPEARANCE
+----------
+
+In Drupal, the appearance of your site is set by the theme (themes are
+extensions that set fonts, colors, and layout). Drupal core comes with several
+themes. More themes are available for download, and you can also create your own
+custom theme.
+
+More about themes:
+ * Download contributed themes to /themes to modify Drupal's appearance:
+   https://www.drupal.org/project/themes
+ * Develop your own theme:
+   https://www.drupal.org/documentation/theme
+
+DEVELOPING FOR DRUPAL
+---------------------
+
+Drupal contains an extensive API that allows you to add to and modify the
+functionality of your site. The API consists of "hooks", which allow modules to
+react to system events and customize Drupal's behavior, and functions that
+standardize common operations such as database queries and form generation. The
+flexible hook architecture means that you should never need to directly modify
+the files that come with Drupal core to achieve the functionality you want;
+instead, functionality modifications take the form of modules.
+
+When you need new functionality for your Drupal site, search for existing
+contributed modules. If you find a module that matches except for a bug or an
+additional needed feature, change the module and contribute your improvements
+back to the project in the form of a "patch". Create new custom modules only
+when nothing existing comes close to what you need.
+
+More about developing:
+ * Search for existing contributed modules:
+   https://www.drupal.org/project/modules
+ * Contribute a patch:
+   https://www.drupal.org/patch/submit
+ * Develop your own module:
+   https://www.drupal.org/developing/modules
+ * Follow programming best practices:
+   https://www.drupal.org/developing/best-practices
+ * Refer to the API documentation:
+   https://api.drupal.org/api/drupal/8
+ * Learn from documented Drupal API examples:
+   https://www.drupal.org/project/examples
+
+MORE INFORMATION
+----------------
+
+ * See the Drupal.org online documentation:
+   https://www.drupal.org/documentation
+
+ * For a list of security announcements, see the "Security advisories" page at
+   https://www.drupal.org/security (available as an RSS feed). This page also
+   describes how to subscribe to these announcements via email.
+
+ * For information about the Drupal security process, or to find out how to
+   report a potential security issue to the Drupal security team, see the
+   "Security team" page at https://www.drupal.org/security-team
+
+ * For information about the wide range of available support options, visit
+   https://www.drupal.org and click on Community and Support in the top or
+   bottom navigation.

+ 21 - 0
sites/all/modules/contrib/admin/admin_toolbar/CHANGELOG.txt

@@ -1,3 +1,24 @@
+Admin Toolbar 8.1.22, 2017-XX-XX
+--------------------------------
+Changes since 8.1.21:
+
+- #2929061 by romainj, dsnopek, adriancid: admin_toolbar_links_access_filter
+  doesn't do anything unless admin_toolbar is enabled.
+- #2928836 by eme: One extra pixel line under the admin toolbar.
+- #2925501 by eme, adriancid, RumyanaRuseva: Menu does not take changes into
+  account (caching issue).
+- #2927914 by adriancid: Parameter comment indentation must be 3 spaces.
+- #2927911 by adriancid: Remove unused variables.
+- #2927905 by adriancid: All dependencies must be prefixed with the project 
+  name.
+- #2925327 by Berdir, adriancid: Better check for is-link-in-admin-menu for
+  local task links.
+- #2922046 by mikejw, adriancid, Berdir, BrianLewisDesign: Notice: Undefined
+  index: entity.eform_type.collection in 
+  admin_toolbar_tools_menu_links_discovered_alter.
+- #2925128 by adriancid: Create the module help page for the Admin Toolbar Links
+  Access Filter submodule.
+
 Admin Toolbar 8.1.21, 2017-11-20
 --------------------------------
 Changes since 8.1.20:

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

@@ -6,10 +6,10 @@ type: module
 # core: 8.x
 
 dependencies:
-  - toolbar
+  - drupal:toolbar
 
-# Information added by Drupal.org packaging script on 2017-11-20
-version: '8.x-1.21'
+# Information added by Drupal.org packaging script on 2018-01-02
+version: '8.x-1.22'
 core: '8.x'
 project: 'admin_toolbar'
-datestamp: 1511190792
+datestamp: 1514888588

+ 3 - 1
sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_links_access_filter/README.txt

@@ -27,7 +27,9 @@ https://www.drupal.org/node/296693 be solved, this module will be deprecated.
 REQUIREMENTS
 ------------
 
-No special requirements.
+This module requires the following modules:
+
+ * Admin Toolbar (https://www.drupal.org/project/admin_toolbar)
 
 
 RECOMMENDED MODULES

+ 5 - 4
sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_links_access_filter/admin_toolbar_links_access_filter.info.yml

@@ -1,12 +1,13 @@
 name: Admin Toolbar Links Access Filter
 description: Provides a workaround for the common problem that users with 'Use the administration pages and help' permission see menu links they don't have access permission for. Once the issue <a href='https://www.drupal.org/node/296693'>https://www.drupal.org/node/296693</a> be solved, this module will be deprecated.
 package: Administration
-
+dependencies:
+  - admin_toolbar:admin_toolbar
 type: module
 # core: 8.x
 
-# Information added by Drupal.org packaging script on 2017-11-20
-version: '8.x-1.21'
+# Information added by Drupal.org packaging script on 2018-01-02
+version: '8.x-1.22'
 core: '8.x'
 project: 'admin_toolbar'
-datestamp: 1511190792
+datestamp: 1514888588

+ 17 - 0
sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_links_access_filter/admin_toolbar_links_access_filter.module

@@ -9,6 +9,22 @@ use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Url;
 use Drupal\user\Entity\Role;
 use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Drupal\Core\Routing\RouteMatchInterface;
+
+/**
+ * Implements hook_help().
+ */
+function admin_toolbar_links_access_filter_help($route_name, RouteMatchInterface $route_match) {
+  switch ($route_name) {
+    // Main module help.
+    case 'help.page.admin_toolbar_links_access_filter':
+      $output = '';
+      $output .= '<h3>' . t('About') . '</h3>';
+      $output .= '<p>' . t("The Admin Toolbar Links Access Filter module Provides a workaround for the common problem that users with <em>Use the administration pages and help</em> permission see menu links they don't have access permission for. Once the issue <a href='@url'>Hide empty admin categories</a> be solved, this module will be deprecated.", ['@url' => Url::fromUri('https://www.drupal.org/node/296693')->toString()]) . '</p>';
+
+      return $output;
+  }
+}
 
 /**
  * Implements hook_preprocess_menu().
@@ -152,6 +168,7 @@ function admin_toolbar_links_access_filter_is_overview_page($route_name) {
     return !empty($controller) && in_array($controller, $overview_page_controllers);
   }
   catch (RouteNotFoundException $ex) {
+
   }
   return FALSE;
 }

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

@@ -6,10 +6,10 @@ type: module
 # core: 8.x
 
 dependencies:
-  - admin_toolbar
+  - admin_toolbar:admin_toolbar
 
-# Information added by Drupal.org packaging script on 2017-11-20
-version: '8.x-1.21'
+# Information added by Drupal.org packaging script on 2018-01-02
+version: '8.x-1.22'
 core: '8.x'
 project: 'admin_toolbar'
-datestamp: 1511190792
+datestamp: 1514888588

+ 44 - 0
sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/admin_toolbar_tools.links.menu.yml

@@ -12,6 +12,13 @@ system.admin_index:
   parent: admin_toolbar_tools.help
   weight: -100
 
+system.run_cron:
+  title: 'Run cron'
+  route_name: admin_toolbar.run.cron
+  menu_name: admin
+  parent: admin_toolbar_tools.help
+  weight: -8
+
 system.db_update:
   title: 'Run updates'
   route_name: system.db_update
@@ -25,6 +32,43 @@ system.modules_uninstall:
   menu_name: admin
   parent: system.modules_list
 
+admin_toolbar_tools.flush:
+  title: 'Flush all caches'
+  route_name: admin_toolbar_tools.flush
+  weight: -9
+  parent: admin_toolbar_tools.help
+  menu_name: admin
+
+admin_toolbar_tools.cssjs:
+  title: 'Flush CSS and Javascript'
+  route_name: admin_toolbar_tools.cssjs
+  parent: admin_toolbar_tools.flush
+  menu_name: admin
+
+admin_toolbar_tools.plugin:
+  title: 'Flush plugins cache'
+  route_name: admin_toolbar_tools.plugin
+  parent: admin_toolbar_tools.flush
+  menu_name: admin
+
+admin_toolbar_tools.flush_static:
+  title: 'Flush static cache'
+  route_name: admin_toolbar_tools.flush_static
+  parent: admin_toolbar_tools.flush
+  menu_name: admin
+
+admin_toolbar_tools.flush_menu:
+  title: 'Flush routing and links cache'
+  route_name: admin_toolbar_tools.flush_menu
+  parent: admin_toolbar_tools.flush
+  menu_name: admin
+
+admin_toolbar_tools.flush_rendercache:
+  title: 'Flush render cache'
+  route_name: admin_toolbar_tools.flush_rendercache
+  parent: admin_toolbar_tools.flush
+  menu_name: admin
+
 admin_toolbar_tools.drupalorg:
   title: 'Drupal.org'
   weight: -5

+ 43 - 18
sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/admin_toolbar_tools.module

@@ -28,17 +28,6 @@ function admin_toolbar_tools_toolbar() {
   return $items;
 }
 
-/**
- * Implements hook_toolbar_alter().
- */
-function admin_toolbar_tools_toolbar_alter(&$items) {
-  // Use lazybuilder for links so the page is cacheable.
-  $items['administration']['tray']['toolbar_administration'] = [
-    '#lazy_builder' => ['admin_toolbar_tools.toolbar_handler:lazyBuilder', []],
-    '#create_placeholder' => TRUE,
-  ];
-}
-
 /**
  * Implements hook_help().
  */
@@ -88,8 +77,18 @@ function admin_toolbar_tools_menu_links_discovered_alter(&$links) {
     }
   }
 
+  // Adding a menu link to clean the Views cache.
+  if ($moduleHandler->moduleExists('views')) {
+    $links['admin_toolbar_tools.flush_views'] = [
+      'title' => t('Flush views cache'),
+      'route_name' => 'admin_toolbar_tools.flush_views',
+      'menu_name' => 'admin',
+      'parent' => 'admin_toolbar_tools.flush',
+    ];
+  }
+
   // Adds common links to entities.
-  foreach ($content_entities as $module_name => $entities) {
+  foreach ($content_entities as $entities) {
     $content_entity_bundle = $entities['content_entity_bundle'];
     $content_entity = $entities['content_entity'];
     foreach ($entityTypeManager->getStorage($content_entity_bundle)->loadMultiple() as $machine_name => $bundle) {
@@ -539,19 +538,45 @@ function admin_toolbar_tools_menu_links_discovered_alter(&$links) {
   $manager = \Drupal::service('plugin.manager.menu.local_task');
   foreach ($links as $key => $link) {
 
-    // Ignore menu links that have no route name or are not in the admin menu.
-    // @todo Use configuration for admin menu name once added in
-    //   https://www.drupal.org/node/1869638.
-    if (empty($link['route_name']) || !isset($link['menu_name']) || $link['menu_name'] !== 'admin') {
+    // Ignore menu links that have no route name.
+    if (empty($link['route_name'])) {
       continue;
     }
+
+    // Ignore links that are not in the admin menu, include parents in the
+    // check as they inherit the menu name from the parent if not set.
+    $link_to_check = $link;
+    do {
+      // @todo Use configuration for admin menu name once added in
+      // https://www.drupal.org/node/1869638.
+      if (isset($link_to_check['menu_name'])) {
+        // If the link belongs to the admin menu, then skip this loop and
+        // look for local tasks.
+        if ($link_to_check['menu_name'] === 'admin') {
+          break;
+        }
+        else {
+          // If it is explicitly not in the admin menu, skip.
+          continue 2;
+        }
+      }
+
+      if (!empty($link_to_check['parent']) && isset($links[$link_to_check['parent']])) {
+        $link_to_check = $links[$link_to_check['parent']];
+      }
+      else {
+        // No parent and we found no menu_name, skip.
+        continue 2;
+      }
+    } while ($link_to_check);
+
     $route = $link['route_name'];
     $route_local_tasks = $manager->getLocalTasksForRoute($route);
     if (empty($route_local_tasks[0])) {
       continue;
     }
 
-    foreach ($route_local_tasks[0] as $plugin_id => $task_plugin) {
+    foreach ($route_local_tasks[0] as $task_plugin) {
       $definition = $task_plugin->getPluginDefinition();
       $local_route = $definition['route_name'];
       if (empty($local_route)) {
@@ -596,7 +621,7 @@ function admin_toolbar_tools_menu_links_discovered_alter(&$links) {
       // "Content>Comment" also has "File" as a tab then we decide to
       // set this one as parent of the added link if not the current tab
       // will be the parent.
-      if (!empty($link['parent'])) {
+      if (!empty($link['parent']) && isset($links[$link['parent']])) {
         $parentlink = $links[$link['parent']];
         $parentlink_route = $parentlink['route_name'];
         $parent_primary = $manager->getLocalTasks($parentlink_route, 0);

+ 0 - 4
sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/admin_toolbar_tools.services.yml

@@ -1,4 +0,0 @@
-services:
-  admin_toolbar_tools.toolbar_handler:
-    class: Drupal\admin_toolbar_tools\ToolbarHandler
-    arguments: ['@module_handler']

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

@@ -44,7 +44,7 @@ class AdminToolbarToolsAlterTest extends WebTestBase {
    */
   public function testAdminToolbarTools() {
     // Assert that special menu items are present in the HTML.
-    $this->assertRaw('class="toolbar-icon toolbar-icon-admin-toolbar-tools-help"');
+    $this->assertRaw('class="toolbar-icon toolbar-icon-admin-toolbar-tools-flush"');
   }
 
 }

+ 0 - 135
sites/all/modules/contrib/admin/admin_toolbar/admin_toolbar_tools/src/ToolbarHandler.php

@@ -1,135 +0,0 @@
-<?php
-
-namespace Drupal\admin_toolbar_tools;
-
-use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
-use Drupal\Core\Extension\ModuleHandler;
-use Drupal\Core\StringTranslation\StringTranslationTrait;
-use Drupal\Core\Template\Attribute;
-use Drupal\Core\Url;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Toolbar integration handler.
- */
-class ToolbarHandler implements ContainerInjectionInterface {
-
-  use StringTranslationTrait;
-
-  /**
-   * The module service.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandler
-   */
-  protected $moduleHandler;
-
-  /**
-   * ToolbarHandler constructor.
-   *
-   * @param \Drupal\Core\Extension\ModuleHandler $module_handler
-   *   The module service.
-   */
-  public function __construct(ModuleHandler $module_handler) {
-    $this->moduleHandler = $module_handler;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('module_handler')
-    );
-  }
-
-  /**
-   * Lazy builder callback for the admin_toolbar_tool items.
-   *
-   * @return array
-   *   A renderable array as expected by the renderer service.
-   */
-  public function lazyBuilder() {
-    // Render the pre_render callback we disabled earlier.
-    $build = admin_toolbar_prerender_toolbar_administration_tray([]);
-
-    // Add links that are uncacheable.
-    // Core toolbar module calculates cachability in advance so we have to build
-    // a fake menu tree here, including access checks.
-    $tools_menu = &$build['administration_menu']['#items']['admin_toolbar_tools.help']['below'];
-
-    // Adding the 'Flush all caches' menu in the correct place.
-    $menu_render_array = $this->createMenuRenderArray('admin_toolbar_tools.flush', $this->t('Flush all caches'), TRUE);
-    $this->arrayInsert($tools_menu, 1, $menu_render_array);
-
-    // Adding the submenus to 'Flush all caches' menu.
-    if (!empty($tools_menu['admin_toolbar_tools.flush'])) {
-      $tools_sub_menu = &$tools_menu['admin_toolbar_tools.flush']['below'];
-      $tools_sub_menu += $this->createMenuRenderArray('admin_toolbar_tools.cssjs', $this->t('Flush CSS and Javascript'));
-      $tools_sub_menu += $this->createMenuRenderArray('admin_toolbar_tools.plugin', $this->t('Flush plugins cache'));
-      $tools_sub_menu += $this->createMenuRenderArray('admin_toolbar_tools.flush_static', $this->t('Flush static cache'));
-      $tools_sub_menu += $this->createMenuRenderArray('admin_toolbar_tools.flush_menu', $this->t('Flush routing and links  cache'));
-      $tools_sub_menu += $this->createMenuRenderArray('admin_toolbar_tools.flush_rendercache', $this->t('Flush render cache'));
-
-      // Adding a menu link to clean the Views cache.
-      if ($this->moduleHandler->moduleExists('views')) {
-        $tools_sub_menu += $this->createMenuRenderArray('admin_toolbar_tools.flush_views', $this->t('Flush views cache'));
-      }
-    }
-
-    // Adding the 'Run Cron' menu in the correct place.
-    $menu_render_array = $this->createMenuRenderArray('system.run_cron', $this->t('Run cron'));
-    $this->arrayInsert($tools_menu, 3, $menu_render_array);
-
-    return $build;
-  }
-
-  /**
-   * Create the menu render array.
-   *
-   * @param string $route
-   *    The route.
-   * @param string $title
-   *    The menu title.
-   * @param bool $submenu
-   *    Specify if the current menu element have a submenu.
-   *
-   * @return array
-   *   A renderable array as expected by the renderer service.
-   */
-  private function createMenuRenderArray($route, $title, $submenu = FALSE) {
-    $data = [];
-    $url = Url::fromRoute($route);
-    if ($url->access()) {
-      $data[$route] = [
-        'title' => $title,
-        'url' => $url,
-        'attributes' => new Attribute(['class' => ['menu-item'] + ($submenu ? ['menu-item--expanded'] : [])]),
-      ];
-      if ($submenu) {
-        $data[$route]['below'] = [];
-        $data[$route]['is_expanded'] = TRUE;
-      }
-    }
-    return $data;
-  }
-
-  /**
-   * Insert an array in a given position of another array.
-   *
-   * @param array $array
-   *    The array where we need to insert new elements.
-   * @param int $position
-   *    The position where we will add the new array.
-   * @param array $insert_array
-   *    The array that will be inserted.
-   *
-   * @see http://php.net/manual/en/function.array-splice.php#56794
-   */
-  private function arrayInsert(array &$array, $position, array $insert_array) {
-    // Getting the first part of the array.
-    $first_array = array_splice($array, 0, $position);
-    // Inserting the new part in the desired position.
-    $array = array_merge($first_array, $insert_array, $array);
-  }
-
-}

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

@@ -23,7 +23,7 @@
   background-color: #f5f5f2;
 }
 
-.toolbar-tray-horizontal ul li.menu-item {
+.toolbar-tray-horizontal ul li li.menu-item {
   border-top: none transparent;
   border-right: 1px solid #dddddd;
   border-bottom: 1px solid #dddddd;

+ 48 - 45
sites/all/modules/contrib/admin/admin_toolbar/js/admin_toolbar.js

@@ -1,50 +1,53 @@
 (function ($) {
-  $(document).ready(function () {
-    $('a.toolbar-icon').removeAttr('title');
+  Drupal.behaviors.adminToolbar = {
+    attach: function (context, settings) {
 
-    $('.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: 250
-    });
-
-    // Make the toolbar menu navigable with keyboard.
-    $('ul.toolbar-menu li.menu-item--expanded a').on('focusin', function () {
-      $('li.menu-item--expanded').removeClass('hover-intent');
-      $(this).parents('li.menu-item--expanded').addClass('hover-intent');
-    });
-
-    $('ul.toolbar-menu li.menu-item a').keydown(function (e) {
-      if ((e.shiftKey && (e.keyCode || e.which) == 9)) {
-        if ($(this).parent('.menu-item').prev().hasClass('menu-item--expanded')) {
-          $(this).parent('.menu-item').prev().addClass('hover-intent');
+      $('a.toolbar-icon', context).removeAttr('title');
+  
+      $('.toolbar-tray-horizontal li.menu-item--expanded, .toolbar-tray-horizontal ul li.menu-item--expanded .menu-item', context).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: 250
+      });
+  
+      // Make the toolbar menu navigable with keyboard.
+      $('ul.toolbar-menu li.menu-item--expanded a', context).on('focusin', function () {
+        $('li.menu-item--expanded', context).removeClass('hover-intent');
+        $(this).parents('li.menu-item--expanded').addClass('hover-intent');
+      });
+  
+      $('ul.toolbar-menu li.menu-item a', context).keydown(function (e) {
+        if ((e.shiftKey && (e.keyCode || e.which) == 9)) {
+          if ($(this).parent('.menu-item').prev().hasClass('menu-item--expanded')) {
+            $(this).parent('.menu-item').prev().addClass('hover-intent');
+          }
         }
-      }
-    });
-
-    $('.toolbar-menu:first-child > .menu-item:not(.menu-item--expanded) a, .toolbar-tab > a').on('focusin', function () {
-      $('.menu-item--expanded').removeClass('hover-intent');
-    });
-
-    $('.toolbar-menu:first-child > .menu-item').on('hover', function () {
-      $(this,'a').css("background: #fff;");
-    });
-
-    $('ul:not(.toolbar-menu)').on({
-      mousemove: function () {
-        $('li.menu-item--expanded').removeClass('hover-intent');
-      },
-      hover: function () {
-        $('li.menu-item--expanded').removeClass('hover-intent');
-      }
-    });
+      });
+  
+      $('.toolbar-menu:first-child > .menu-item:not(.menu-item--expanded) a, .toolbar-tab > a', context).on('focusin', function () {
+        $('.menu-item--expanded').removeClass('hover-intent');
+      });
+  
+      $('.toolbar-menu:first-child > .menu-item', context).on('hover', function () {
+        $(this,'a').css("background: #fff;");
+      });
+  
+      $('ul:not(.toolbar-menu)', context).on({
+        mousemove: function () {
+          $('li.menu-item--expanded').removeClass('hover-intent');
+        },
+        hover: function () {
+          $('li.menu-item--expanded').removeClass('hover-intent');
+        }
+      });
 
-  });
+    }
+  };
 })(jQuery);

+ 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-09-18
-version: '8.x-1.4'
+# Information added by Drupal.org packaging script on 2017-12-05
+version: '8.x-1.5'
 core: '8.x'
 project: 'config_update'
-datestamp: 1505755746
+datestamp: 1512514387

+ 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-09-18
-version: '8.x-1.4'
+# Information added by Drupal.org packaging script on 2017-12-05
+version: '8.x-1.5'
 core: '8.x'
 project: 'config_update'
-datestamp: 1505755746
+datestamp: 1512514387

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

@@ -12,9 +12,7 @@ config_update_ui.import:
   path: '/admin/config/development/configuration/report/import/{config_type}/{config_name}'
   defaults:
     _title: 'Import'
-    _controller: '\Drupal\config_update_ui\Controller\ConfigUpdateController::import'
-    config_type: NULL
-    config_name: NULL
+    _form: '\Drupal\config_update_ui\Form\ConfigImportConfirmForm'
   requirements:
     _permission: 'import configuration'
 

+ 0 - 20
sites/all/modules/contrib/admin/config_update/config_update_ui/src/Controller/ConfigUpdateController.php

@@ -112,26 +112,6 @@ class ConfigUpdateController extends ControllerBase {
     );
   }
 
-  /**
-   * Imports configuration from a module, theme, or profile.
-   *
-   * Configuration is assumed not to currently exist.
-   *
-   * @param string $config_type
-   *   The type of configuration.
-   * @param string $config_name
-   *   The name of the config item, without the prefix.
-   *
-   * @return \Symfony\Component\HttpFoundation\RedirectResponse
-   *   Redirects to the updates report.
-   */
-  public function import($config_type, $config_name) {
-    $this->configRevert->import($config_type, $config_name);
-
-    drupal_set_message($this->t('The configuration was imported.'));
-    return $this->redirect('config_update_ui.report');
-  }
-
   /**
    * Shows the diff between active and provided configuration.
    *

+ 12 - 11
sites/all/modules/contrib/admin/config_update/config_update_ui/src/Form/ConfigDeleteConfirmForm.php

@@ -8,6 +8,7 @@ use Drupal\Core\Url;
 use Drupal\config_update\ConfigListInterface;
 use Drupal\config_update\ConfigRevertInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
  * Defines a confirmation form for deleting configuration.
@@ -81,9 +82,20 @@ class ConfigDeleteConfirmForm extends ConfirmFormBase {
     }
     else {
       $definition = $this->configList->getType($this->type);
+      if (!$definition) {
+        // Make a 404 error if the type doesn't exist.
+        throw new NotFoundHttpException();
+      }
       $type_label = $definition->get('label');
     }
 
+    // To delete, the configuration item must exist in active storage. Check
+    // that and make a 404 error if not.
+    $active = $this->configRevert->getFromActive($this->type, $this->name);
+    if (!$active) {
+      throw new NotFoundHttpException();
+    }
+
     return $this->t('Are you sure you want to delete the %type config %item?', ['%type' => $type_label, '%item' => $this->name]);
   }
 
@@ -119,17 +131,6 @@ class ConfigDeleteConfirmForm extends ConfirmFormBase {
     return $form;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    $value = $this->configRevert->getFromActive($this->type, $this->name);
-    if (!$value) {
-      $form_state->setErrorByName('', $this->t('There is no configuration @type named @name to delete', ['@type' => $this->type, '@name' => $this->name]));
-      return;
-    }
-  }
-
   /**
    * {@inheritdoc}
    */

+ 146 - 0
sites/all/modules/contrib/admin/config_update/config_update_ui/src/Form/ConfigImportConfirmForm.php

@@ -0,0 +1,146 @@
+<?php
+
+namespace Drupal\config_update_ui\Form;
+
+use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\config_update\ConfigListInterface;
+use Drupal\config_update\ConfigRevertInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+
+/**
+ * Defines a confirmation form for importing configuration.
+ */
+class ConfigImportConfirmForm extends ConfirmFormBase {
+
+  /**
+   * The type of config being imported.
+   *
+   * @var string
+   */
+  protected $type;
+
+  /**
+   * The name of the config item being imported, without the prefix.
+   *
+   * @var string
+   */
+  protected $name;
+
+  /**
+   * The config lister.
+   *
+   * @var \Drupal\config_update\ConfigListInterface
+   */
+  protected $configList;
+
+  /**
+   * The config reverter.
+   *
+   * @var \Drupal\config_update\ConfigRevertInterface
+   */
+  protected $configRevert;
+
+  /**
+   * Constructs a ConfigImportConfirmForm object.
+   *
+   * @param \Drupal\config_update\ConfigListInterface $config_list
+   *   The config lister.
+   * @param \Drupal\config_update\ConfigRevertInterface $config_update
+   *   The config reverter.
+   */
+  public function __construct(ConfigListInterface $config_list, ConfigRevertInterface $config_update) {
+    $this->configList = $config_list;
+    $this->configRevert = $config_update;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('config_update.config_list'),
+      $container->get('config_update.config_update')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'config_import_confirm';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    if ($this->type == 'system.simple') {
+      $type_label = $this->t('Simple configuration');
+    }
+    else {
+      $definition = $this->configList->getType($this->type);
+      if (!$definition) {
+        // Make a 404 error if the type doesn't exist.
+        throw new NotFoundHttpException();
+      }
+      $type_label = $definition->get('label');
+    }
+
+    // To import (as opposed to revert), the configuration item must exist in
+    // extension storage but not active storage, so check that, and make a 404
+    // error if not.
+    $extension = $this->configRevert->getFromExtension($this->type, $this->name);
+    $active = $this->configRevert->getFromActive($this->type, $this->name);
+    if (!$extension || $active) {
+      throw new NotFoundHttpException();
+    }
+
+    return $this->t('Are you sure you want to import the %type config %item from its source configuration?', ['%type' => $type_label, '%item' => $this->name]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    return new Url('config_update_ui.report');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return $this->t('Configuration will be added to your site. This action cannot be undone.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText() {
+    return $this->t('Import');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $config_type = NULL, $config_name = NULL) {
+    $this->type = $config_type;
+    $this->name = $config_name;
+
+    $form = parent::buildForm($form, $form_state);
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $this->configRevert->import($this->type, $this->name);
+
+    drupal_set_message($this->t('The configuration was imported from its source.'));
+    $form_state->setRedirectUrl($this->getCancelUrl());
+  }
+
+}

+ 14 - 11
sites/all/modules/contrib/admin/config_update/config_update_ui/src/Form/ConfigRevertConfirmForm.php

@@ -8,6 +8,7 @@ use Drupal\Core\Url;
 use Drupal\config_update\ConfigListInterface;
 use Drupal\config_update\ConfigRevertInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
  * Defines a confirmation form for reverting configuration.
@@ -81,9 +82,22 @@ class ConfigRevertConfirmForm extends ConfirmFormBase {
     }
     else {
       $definition = $this->configList->getType($this->type);
+      if (!$definition) {
+        // Make a 404 error if the type doesn't exist.
+        throw new NotFoundHttpException();
+      }
       $type_label = $definition->get('label');
     }
 
+    // To revert (as opposed to import), the configuration item must exist in
+    // both active storage and extension storage, so check that and make a 404
+    // error if not.
+    $extension = $this->configRevert->getFromExtension($this->type, $this->name);
+    $active = $this->configRevert->getFromActive($this->type, $this->name);
+    if (!$extension || !$active) {
+      throw new NotFoundHttpException();
+    }
+
     return $this->t('Are you sure you want to revert the %type config %item to its source configuration?', ['%type' => $type_label, '%item' => $this->name]);
   }
 
@@ -119,17 +133,6 @@ class ConfigRevertConfirmForm extends ConfirmFormBase {
     return $form;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    $value = $this->configRevert->getFromExtension($this->type, $this->name);
-    if (!$value) {
-      $form_state->setErrorByName('', $this->t('There is no configuration @type named @name to import', ['@type' => $this->type, '@name' => $this->name]));
-      return;
-    }
-  }
-
   /**
    * {@inheritdoc}
    */

+ 15 - 0
sites/all/modules/contrib/admin/config_update/config_update_ui/src/Tests/ConfigUpdateTest.php

@@ -106,6 +106,11 @@ class ConfigUpdateTest extends WebTestBase {
     $this->assertText('Testing profile');
     $this->assertDrushReports('profile', '', [], [], [], array_keys($inactive));
 
+    // Verify that the user search page cannot be imported (because it already
+    // exists).
+    $this->drupalGet('admin/config/development/configuration/report/import/search_page/user_search');
+    $this->assertResponse(404);
+
     // Delete the user search page from the search UI and verify report for
     // both the search page config type and user module.
     $this->drupalGet('admin/config/search/pages');
@@ -130,10 +135,19 @@ class ConfigUpdateTest extends WebTestBase {
         'views.view.who_s_online',
       ], ['changed']);
 
+    // Verify that the user search page cannot be reverted (because it does
+    // not already exist).
+    $this->drupalGet('admin/config/development/configuration/report/revert/search_page/user_search');
+    $this->assertResponse(404);
+    // Verify that the delete URL doesn't work either.
+    $this->drupalGet('admin/config/development/configuration/report/delete/search_page/user_search');
+    $this->assertResponse(404);
+
     // Use the import link to get it back. Do this from the search page
     // report to make sure we are importing the right config.
     $this->drupalGet('admin/config/development/configuration/report/type/search_page');
     $this->clickLink('Import from source');
+    $this->drupalPostForm(NULL, [], 'Import');
     $this->assertText('The configuration was imported');
     $this->assertNoReport();
     $this->drupalGet('admin/config/development/configuration/report/type/search_page');
@@ -277,6 +291,7 @@ class ConfigUpdateTest extends WebTestBase {
     $this->assertText('cannot be undone');
     $this->drupalPostForm(NULL, [], 'Delete');
     $this->assertText('The configuration was deleted');
+
     // And verify the report again.
     $this->drupalGet('admin/config/development/configuration/report/type/search_page');
     $this->assertReport('Search page', [], [], [], []);

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

@@ -123,7 +123,7 @@ class ConfigLister implements ConfigListInterface {
   public function getTypeNameByConfigName($name) {
     $definitions = $this->listTypes();
     foreach ($this->typesByPrefix as $prefix => $entity_type) {
-      if (strpos($name, $prefix) === 0) {
+      if (strpos($name, $prefix . '.') === 0) {
         return $entity_type;
       }
     }
@@ -174,6 +174,12 @@ class ConfigLister implements ConfigListInterface {
         break;
     }
 
+    // This only seems to be a problem in unit tests, where a mock object
+    // is returning NULL instead of an empy array for some reason.
+    if (!is_array($optional_list)) {
+      $optional_list = [];
+    }
+
     return [$active_list, $install_list, $optional_list];
   }
 
@@ -211,7 +217,7 @@ class ConfigLister implements ConfigListInterface {
     $list = array_combine($list, $list);
     foreach ($list as $name) {
       foreach ($prefixes as $prefix) {
-        if (strpos($name, $prefix) === 0) {
+        if (strpos($name, $prefix . '.') === 0) {
           unset($list[$name]);
         }
       }

+ 265 - 0
sites/all/modules/contrib/admin/config_update/tests/src/Unit/ConfigListerTest.php

@@ -0,0 +1,265 @@
+<?php
+
+namespace Drupal\Tests\config_update\Unit;
+
+use Drupal\config_update\ConfigLister;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the \Drupal\config_update\ConfigLister class.
+ *
+ * @group config_update
+ *
+ * @coversDefaultClass \Drupal\config_update\ConfigLister
+ */
+class ConfigListerTest extends UnitTestCase {
+
+  /**
+   * The config lister to test.
+   *
+   * @var \Drupal\config_update\ConfigLister
+   */
+  protected $configLister;
+
+  /**
+   * The mocked entity definition information.
+   *
+   * @var string[]
+   */
+  protected $entityDefinitionInformation;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->configLister = new ConfigLister($this->getEntityManagerMock(), $this->getConfigStorageMock('active'), $this->getConfigStorageMock('extension'), $this->getConfigStorageMock('optional'));
+  }
+
+  /**
+   * Creates a mock entity manager for the test.
+   */
+  protected function getEntityManagerMock() {
+    // Make a list of fake entity definitions. Make sure they are not sorted,
+    // to test that the methods sort them. Also make sure there are a couple
+    // with prefixes that are subsets of each other.
+    $this->entityDefinitionInformation = [
+      ['prefix' => 'foo.bar', 'type' => 'foo'],
+      ['prefix' => 'foo.barbaz', 'type' => 'bar'],
+      ['prefix' => 'baz.foo', 'type' => 'baz'],
+    ];
+
+    $definitions = [];
+    foreach ($this->entityDefinitionInformation as $info) {
+      $def = $this->getMockBuilder('Drupal\Core\Config\Entity\ConfigEntityTypeInterface')->getMock();
+      $def
+        ->expects($this->any())
+        ->method('getConfigPrefix')
+        ->willReturn($info['prefix']);
+      $def
+        ->expects($this->any())
+        ->method('isSubclassOf')
+        ->willReturn(TRUE);
+
+      $def->getConfigPrefix();
+
+      $definitions[$info['type']] = $def;
+    }
+
+    $manager = $this->getMockBuilder('Drupal\Core\Entity\EntityTypeManagerInterface')->getMock();
+    $manager
+      ->method('getDefinitions')
+      ->willReturn($definitions);
+
+    return($manager);
+  }
+
+  /**
+   * Creates a mock config storage object for the test.
+   *
+   * @param string $type
+   *   Type of storage object to return: 'active', 'extension', or 'optional'.
+   */
+  protected function getConfigStorageMock($type) {
+    if ($type == 'active') {
+      $storage = $this->getMockBuilder('Drupal\Core\Config\StorageInterface')->getMock();
+
+      // The only use of the read() method on active storage is
+      // with the core.extension config, to get the profile name.
+      $storage
+        ->method('read')
+        ->willReturn(['profile' => 'standard']);
+
+      $map = [
+        ['foo.bar', ['foo.bar.one', 'foo.bar.two', 'foo.bar.three']],
+        ['foo.barbaz', ['foo.barbaz.four', 'foo.barbaz.five', 'foo.barbaz.six']],
+        ['baz.foo'], [],
+        ['',
+          [
+            'foo.bar.one',
+            'foo.bar.two',
+            'foo.bar.three',
+            'foo.barbaz.four',
+            'foo.barbaz.five',
+            'foo.barbaz.six',
+            'something.else',
+            'another.one',
+          ],
+        ],
+      ];
+
+      $storage
+        ->method('listAll')
+        ->will($this->returnValueMap($map));
+    }
+    elseif ($type == 'extension') {
+      $storage = $this->getMockBuilder('Drupal\Core\Config\ExtensionInstallStorage')->disableOriginalConstructor()->getMock();
+      $storage
+        ->method('getComponentNames')
+        ->willReturn([
+          'foo.bar.one' => 'ignored',
+          'foo.bar.two' => 'ignored',
+          'foo.bar.seven' => 'ignored',
+          'foo.barnot.three' => 'ignored',
+          'something.else' => 'ignored',
+        ]);
+
+      $map = [
+        ['foo.bar', ['foo.bar.one', 'foo.bar.two', 'foo.bar.seven']],
+        ['baz.foo'], [],
+        ['',
+          [
+            'foo.bar.one',
+            'foo.bar.two',
+            'foo.bar.seven',
+            'foo.barbaz.four',
+            'foo.barnot.three',
+            'something.else',
+          ],
+        ],
+      ];
+
+      $storage
+        ->method('listAll')
+        ->will($this->returnValueMap($map));
+    }
+    else {
+      $storage = $this->getMockBuilder('Drupal\Core\Config\ExtensionInstallStorage')->disableOriginalConstructor()->getMock();
+      $storage
+        ->method('getComponentNames')
+        ->willReturn([
+          'foo.barbaz.four' => 'ignored',
+        ]);
+
+      $map = [
+        ['foo.bar'], [],
+        ['foo.barbaz', ['foo.barbaz.four']],
+        ['', ['foo.barbaz.four']],
+      ];
+      $storage
+        ->method('listAll')
+        ->will($this->returnValueMap($map));
+    }
+    return $storage;
+
+  }
+
+  /**
+   * @covers \Drupal\config_update\ConfigLister::listConfig
+   * @dataProvider listConfigProvider
+   */
+  public function testListConfig($a, $b, $expected) {
+    $this->assertEquals($expected, $this->configLister->listConfig($a, $b));
+  }
+
+  /**
+   * Data provider for self:testListConfig().
+   */
+  public function listConfigProvider() {
+    return [
+      // Arguments are $list_type, $name.
+      // We cannot really test the extension types here, because they rely
+      // on the going out to the file system to find out what config objects
+      // are there. This is too complex to mock. It is tested in the tests for
+      // the report output in the config_update_ui module tests. Anyway, we
+      // can test the other types.
+      ['type', 'system.all',
+        [
+          [
+            'foo.bar.one',
+            'foo.bar.two',
+            'foo.bar.three',
+            'foo.barbaz.four',
+            'foo.barbaz.five',
+            'foo.barbaz.six',
+            'something.else',
+            'another.one',
+          ],
+          [
+            'foo.bar.one',
+            'foo.bar.two',
+            'foo.bar.seven',
+            'foo.barbaz.four',
+            'foo.barnot.three',
+            'something.else',
+          ],
+          ['foo.barbaz.four'],
+        ],
+      ],
+      ['type', 'system.simple',
+        [
+          ['something.else', 'another.one'],
+          ['foo.barnot.three', 'something.else'],
+          [],
+        ],
+      ],
+      ['type', 'foo',
+        [
+          ['foo.bar.one', 'foo.bar.two', 'foo.bar.three'],
+          ['foo.bar.one', 'foo.bar.two', 'foo.bar.seven'],
+          [],
+        ],
+      ],
+      ['type', 'unknown.type', [[], [], []]],
+    ];
+  }
+
+  /**
+   * @covers \Drupal\config_update\ConfigLister::getType
+   */
+  public function testGetType() {
+    $return = $this->configLister->getType('not_in_list');
+    $this->assertNull($return);
+
+    foreach ($this->entityDefinitionInformation as $info) {
+      $return = $this->configLister->getType($info['type']);
+      $this->assertEquals($return->getConfigPrefix(), $info['prefix']);
+    }
+  }
+
+  /**
+   * @covers \Drupal\config_update\ConfigLister::getTypeByPrefix
+   */
+  public function testGetTypeByPrefix() {
+    $return = $this->configLister->getTypeByPrefix('not_in_list');
+    $this->assertNull($return);
+
+    foreach ($this->entityDefinitionInformation as $info) {
+      $return = $this->configLister->getTypeByPrefix($info['prefix']);
+      $this->assertEquals($return->getConfigPrefix(), $info['prefix']);
+    }
+  }
+
+  /**
+   * @covers \Drupal\config_update\ConfigLister::getTypeNameByConfigName
+   */
+  public function testGetTypeNameByConfigName() {
+    $return = $this->configLister->getTypeNameByConfigName('not_in_list');
+    $this->assertNull($return);
+
+    foreach ($this->entityDefinitionInformation as $info) {
+      $return = $this->configLister->getTypeNameByConfigName($info['prefix'] . '.something');
+      $this->assertEquals($return, $info['type']);
+    }
+  }
+
+}

+ 3 - 3
sites/all/modules/contrib/fields/audiofield/audiofield.info.yml

@@ -5,8 +5,8 @@ type: module
 package: Field types
 # core: '8.x'
 
-# Information added by Drupal.org packaging script on 2017-10-23
-version: '8.x-1.5'
+# Information added by Drupal.org packaging script on 2017-12-27
+version: '8.x-1.6'
 core: '8.x'
 project: 'audiofield'
-datestamp: 1508783950
+datestamp: 1514414887

+ 1 - 1
sites/all/modules/contrib/fields/audiofield/audiofield.module

@@ -78,7 +78,7 @@ function audiofield_theme($existing, $type, $theme, $path) {
 }
 
 /**
- * Implements hook_theme_suggestions_HOOK().
+ * Implements hook_theme_suggestions_HOOK_alter().
  */
 function audiofield_theme_suggestions_audioplayer_alter(array &$suggestions, array $variables) {
   // Suggest a template using the plugin name.

+ 18 - 0
sites/all/modules/contrib/fields/audiofield/composer.json

@@ -0,0 +1,18 @@
+{
+  "name": "drupal/audiofield",
+  "description": "AudioField Module",
+  "type": "drupal-module",
+  "license": "GPL-2.0+",
+  "homepage": "https://www.drupal.org/project/audiofield",
+  "minimum-stability": "dev",
+  "authors": [
+    {
+      "name": "Daniel Moberly",
+      "homepage": "https://www.drupal.org/u/danielmoberly",
+      "role": "Maintainer"
+    }
+  ],
+  "support": {
+    "issues": "https://www.drupal.org/project/issues/audiofield"
+  }
+}

+ 3 - 0
sites/all/modules/contrib/fields/audiofield/js/jplayer.builder.es6.js

@@ -41,6 +41,7 @@
         wmode: 'window',
         useStateClassSkin: true,
         autoBlur: false,
+        preload: settings.lazyload,
         smoothPlayBar: true,
         keyEnabled: true,
         remainingDuration: false,
@@ -78,6 +79,7 @@
         wmode: 'window',
         useStateClassSkin: true,
         autoBlur: false,
+        preload: settings.lazyload,
         smoothPlayBar: true,
         keyEnabled: true,
         volume: settings.volume,
@@ -125,6 +127,7 @@
           },
           swfPath: '/libraries/jplayer/dist/jplayer',
           wmode: 'window',
+          preload: settings.lazyload,
           keyEnabled: true,
           supplied: file.filetype,
         },

+ 4 - 1
sites/all/modules/contrib/fields/audiofield/js/jplayer.builder.js

@@ -31,6 +31,7 @@
       wmode: 'window',
       useStateClassSkin: true,
       autoBlur: false,
+      preload: settings.lazyload,
       smoothPlayBar: true,
       keyEnabled: true,
       remainingDuration: false,
@@ -57,6 +58,7 @@
         wmode: 'window',
         useStateClassSkin: true,
         autoBlur: false,
+        preload: settings.lazyload,
         smoothPlayBar: true,
         keyEnabled: true,
         volume: settings.volume
@@ -88,6 +90,7 @@
         swfPath: '/libraries/jplayer/dist/jplayer',
         wmode: 'window',
         keyEnabled: true,
+        preload: settings.lazyload,
         supplied: file.filetype
       });
     });
@@ -108,4 +111,4 @@
       });
     }
   };
-})(jQuery, Drupal);
+})(jQuery, Drupal);

+ 3 - 0
sites/all/modules/contrib/fields/audiofield/src/Plugin/AudioPlayer/JPlayerAudioPlayer.php

@@ -64,6 +64,7 @@ class JPlayerAudioPlayer extends AudioFieldPluginBase {
           'filetype' => $renderInfo->filetype,
           'fid' => $renderInfo->id,
           'autoplay' => $settings['audio_player_autoplay'],
+          'lazyload' => $settings['audio_player_lazyload'],
         ];
       }
     }
@@ -98,6 +99,7 @@ class JPlayerAudioPlayer extends AudioFieldPluginBase {
             // JPlayer expects this as a 0 - 1 value.
             'volume' => ($settings['audio_player_initial_volume'] / 10),
             'autoplay' => $settings['audio_player_autoplay'],
+            'lazyload' => $settings['audio_player_lazyload'],
           ];
 
           // Store the unique id for the template.
@@ -120,6 +122,7 @@ class JPlayerAudioPlayer extends AudioFieldPluginBase {
           'files' => [],
           'filetypes' => [],
           'autoplay' => $settings['audio_player_autoplay'],
+          'lazyload' => $settings['audio_player_lazyload'],
         ];
 
         // Format files for output.

+ 75 - 11
sites/all/modules/contrib/fields/audiofield/src/Plugin/Field/FieldFormatter/AudioFieldFieldFormatter.php

@@ -7,7 +7,6 @@ use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Field\FormatterBase;
-use Drupal\Core\Render\Markup;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -216,6 +215,33 @@ class AudioFieldFieldFormatter extends FormatterBase implements ContainerFactory
         ],
       ],
     ];
+    // Settings for autoplay.
+    $elements['audio_player_lazyload'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Lazy Load audio'),
+      '#description' => $this->t("This setting causes audio not to be loaded until it is played."),
+      '#default_value' => $this->getSetting('audio_player_lazyload'),
+      '#states' => [
+        'visible' => [
+          [':input[name="fields[' . $fieldname . '][settings_edit_form][settings][audio_player]"]' => ['value' => 'audiojs_audio_player']],
+          [':input[name="fields[' . $fieldname . '][settings_edit_form][settings][audio_player]"]' => ['value' => 'default_mp3_player']],
+          [':input[name="fields[' . $fieldname . '][settings_edit_form][settings][audio_player]"]' => ['value' => 'jplayer_audio_player']],
+          [':input[name="fields[' . $fieldname . '][settings_edit_form][settings][audio_player]"]' => ['value' => 'mediaelement_audio_player']],
+          [':input[name="fields[' . $fieldname . '][settings_edit_form][settings][audio_player]"]' => ['value' => 'projekktor_audio_player']],
+        ],
+      ],
+    ];
+    // Settings for download button.
+    $elements['download_button'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Display download button on player'),
+      '#default_value' => $this->getSetting('download_button'),
+      '#states' => [
+        'visible' => [
+          [':input[name="fields[' . $fieldname . '][settings_edit_form][settings][audio_player]"]' => ['value' => 'default_mp3_player']],
+        ],
+      ],
+    ];
     // Setting for optional download link.
     $elements['download_link'] = [
       '#type' => 'checkbox',
@@ -236,7 +262,9 @@ class AudioFieldFieldFormatter extends FormatterBase implements ContainerFactory
 
     // Show which player we are currently using for the field.
     $summary = [
-      Markup::create('Selected player: <strong>' . $plugin_definitions[$settings['audio_player']]['title'] . '</strong>'),
+      $this->t('Selected player: <strong>@player</strong>', [
+        '@player' => $plugin_definitions[$settings['audio_player']]['title'],
+      ]),
     ];
     // If this is jPlayer, add those settings.
     if ($settings['audio_player'] == 'jplayer_audio_player') {
@@ -254,16 +282,24 @@ class AudioFieldFieldFormatter extends FormatterBase implements ContainerFactory
           }
         }
       }
-      $summary[] = Markup::create('Skin: <strong>' . $theme . '</strong>');
+      $summary[] = $this->t('Skin: <strong>@theme</strong>', [
+        '@theme' => $theme,
+      ]);
     }
     // If this is wavesurfer, add those settings.
     elseif ($settings['audio_player'] == 'wavesurfer_audio_player') {
-      $summary[] = Markup::create('Combine files into single player? <strong>' . ($settings['audio_player_wavesurfer_combine_files'] ? 'Yes' : 'No') . '</strong>');
+      $summary[] = $this->t('Combine files into single player? <strong>@combine</strong>', [
+        '@combine' => ($settings['audio_player_wavesurfer_combine_files'] ? 'Yes' : 'No'),
+      ]);
     }
     // If this is wordpress, add those settings.
     elseif ($settings['audio_player'] == 'wordpress_audio_player') {
-      $summary[] = Markup::create('Combine files into single player? <strong>' . ($settings['audio_player_wordpress_combine_files'] ? 'Yes' : 'No') . '</strong>');
-      $summary[] = Markup::create('Animate player? <strong>' . ($settings['audio_player_wordpress_animation'] ? 'Yes' : 'No') . '</strong>');
+      $summary[] = $this->t('Combine files into single player? <strong>@combine</strong>', [
+        '@combine' => ($settings['audio_player_wordpress_combine_files'] ? 'Yes' : 'No'),
+      ]);
+      $summary[] = $this->t('Animate player? <strong>@animate</strong>', [
+        '@animate' => ($settings['audio_player_wordpress_animation'] ? 'Yes' : 'No'),
+      ]);
     }
     // If this is soundmanager, add those settings.
     elseif ($settings['audio_player'] == 'soundmanager_audio_player') {
@@ -273,7 +309,9 @@ class AudioFieldFieldFormatter extends FormatterBase implements ContainerFactory
         'barui' => 'Bar UI',
         'inlineplayer' => 'Inline Player',
       ];
-      $summary[] = Markup::create('Skin: <strong>' . $skins[$settings['audio_player_soundmanager_theme']] . '</strong>');
+      $summary[] = $this->t('Skin: <strong>@skin</strong>', [
+        '@skin' => $skins[$settings['audio_player_soundmanager_theme']],
+      ]);
     }
 
     // Show combined settings for multiple players.
@@ -286,7 +324,9 @@ class AudioFieldFieldFormatter extends FormatterBase implements ContainerFactory
       'wordpress_audio_player',
     ])) {
       // Display volume.
-      $summary[] = Markup::create('Initial volume: <strong>' . $settings['audio_player_initial_volume'] . ' out of 10</strong>');
+      $summary[] = $this->t('Initial volume: <strong>@volume out of 10</strong>', [
+        '@volume' => $settings['audio_player_initial_volume'],
+      ]);
     }
 
     // Display autoplay.
@@ -299,17 +339,39 @@ class AudioFieldFieldFormatter extends FormatterBase implements ContainerFactory
       'wavesurfer_audio_player',
       'wordpress_audio_player',
     ])) {
-      $summary[] = Markup::create('Autoplay: <strong>' . ($settings['audio_player_autoplay'] ? 'Yes' : 'No') . '</strong>');
+      $summary[] = $this->t('Autoplay: <strong>@autoplay</strong>', [
+        '@autoplay' => ($settings['audio_player_autoplay'] ? 'Yes' : 'No'),
+      ]);
+    }
+
+    // Display lazy load.
+    if (in_array($settings['audio_player'], [
+      'audiojs_audio_player',
+      'default_mp3_player',
+      'jplayer_audio_player',
+      'mediaelement_audio_player',
+      'projekktor_audio_player',
+    ])) {
+      $summary[] = $this->t('Lazy Load: <strong>@lazyload</strong>', [
+        '@lazyload' => ($settings['audio_player_lazyload'] ? 'Yes' : 'No'),
+      ]);
     }
 
     // Check to make sure the library is installed.
     $player = $this->audioPlayerManager->createInstance($settings['audio_player']);
     if (!$player->checkInstalled()) {
-      $summary[] = Markup::create('<strong style="color:red;">' . t('Error: this player library is currently not installed. Please select another player or reinstall the library.') . '</strong>');
+      $summary[] = $this->t('<strong style="color:red;">Error: this player library is currently not installed. Please select another player or reinstall the library.</strong>');
     }
 
+    // Show whether or not we are displaying download buttons.
+    $summary[] = $this->t('Display download button: <strong>@display_link</strong>', [
+      '@display_link' => ($settings['download_button'] ? 'Yes' : 'No'),
+    ]);
+
     // Show whether or not we are displaying direct downloads.
-    $summary[] = Markup::create('Display download link: <strong>' . ($settings['download_link'] ? 'Yes' : 'No') . '</strong>');
+    $summary[] = $this->t('Display download link: <strong>@display_link</strong>', [
+      '@display_link' => ($settings['download_link'] ? 'Yes' : 'No'),
+    ]);
 
     return $summary;
   }
@@ -327,6 +389,8 @@ class AudioFieldFieldFormatter extends FormatterBase implements ContainerFactory
       'audio_player_soundmanager_theme' => 'default',
       'audio_player_initial_volume' => 8,
       'audio_player_autoplay' => FALSE,
+      'audio_player_lazyload' => FALSE,
+      'download_button' => FALSE,
       'download_link' => FALSE,
     ] + parent::defaultSettings();
   }

+ 1 - 1
sites/all/modules/contrib/fields/audiofield/templates/audioplayer--audiojs.html.twig

@@ -15,7 +15,7 @@
 <div class="audiofield">
   <div id="{{ settings.id }}" class="audiofield-audiojs-frame">
     <div class="audiofield-audiojs">
-      <audio preload="auto" {% if settings.audio_player_autoplay == 1 %} autoplay="autoplay" {% endif %}></audio>
+      <audio preload="{% if settings.audio_player_lazyload == 1 %}none{% else %}auto{% endif %}" {% if settings.audio_player_autoplay == 1 %} autoplay="autoplay" {% endif %}></audio>
       <div class="play-pauseZ">
         <p class="playZ"></p>
         <p class="pauseZ"></p>

+ 1 - 1
sites/all/modules/contrib/fields/audiofield/templates/audioplayer--mediaelement.html.twig

@@ -15,7 +15,7 @@
 <div class="audiofield">
   {% for file in files %}
     <div class="mediaelementaudio_frame">
-      <audio id="mediaelement_player_{{ file.id }}" {% if settings.audio_player_autoplay == 1 %} autoplay="autoplay" {% endif %} controls>
+      <audio id="mediaelement_player_{{ file.id }}" preload="{% if settings.audio_player_lazyload == 1 %}none{% else %}auto{% endif %}" {% if settings.audio_player_autoplay == 1 %} autoplay="autoplay" {% endif %} controls>
         <source src="{{ file.url }}">
         Your browser does not support the audio element.
       </audio>

+ 1 - 1
sites/all/modules/contrib/fields/audiofield/templates/audioplayer--projekktor.html.twig

@@ -14,7 +14,7 @@
 #}
 <div class="audiofield">
   {% for file in files %}
-    <audio id="{{ file.id }}" class="audiofield-{{ plugin_id }} {{ plugin_id }}" controls>
+    <audio id="{{ file.id }}" preload="{% if settings.audio_player_lazyload == 1 %}none{% else %}auto{% endif %}" class="audiofield-{{ plugin_id }} {{ plugin_id }}" controls>
       <source src="{{ file.url }}" type="audio/mpeg">
        Your browser does not support the audio element.
     </audio>

+ 1 - 1
sites/all/modules/contrib/fields/audiofield/templates/audioplayer.html.twig

@@ -14,7 +14,7 @@
 #}
 <div class="audiofield">
   {% for file in files %}
-    <audio controls {% if settings.audio_player_autoplay == 1 %} autoplay {% endif %}>
+    <audio preload="{% if settings.audio_player_lazyload == 1 %}none{% else %}auto{% endif %}" controls {% if settings.audio_player_autoplay == 1 %} autoplay {% endif %} {% if settings.download_button != 1 %} controlsList="nodownload" {% endif %}>
        <source src="{{ file.url }}" type="audio/mpeg">
        Your browser does not support the audio element.
     </audio>

+ 339 - 0
sites/all/modules/contrib/fields/filefield_sources/LICENSE.txt

@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.

+ 43 - 0
sites/all/modules/contrib/fields/filefield_sources/README.txt

@@ -0,0 +1,43 @@
+-----------------
+FileField Sources
+-----------------
+
+Description
+-----------
+FileField Sources is a module that enhances the generic and image upload fields
+in Drupal. Typically such fields only allow you to upload a file from your
+desktop. FileField Sources makes it so that you can populate any file field
+from a variety of sources, such as entering remote URLs directly, re-use
+existing uploaded files, pull from a server directory, or a variety of other
+possibilities.
+
+This module built by Robots: http://www.lullabot.com
+Author: Nathan Haug (quicksketch)
+
+Requirements
+------------
+1) Drupal 7.36 or greater is required beginning in FileField Sources 7.x-1.10.
+
+Installation
+------------
+1) Place this module directory in your modules folder (this will usually be
+   "modules/").
+
+2) Enable the module within your Drupal site.
+
+3) Add or configure an existing file or image field. To configure a typical node
+   field, visit Manage -> Structure -> Content types and click
+   "Manage form display" on a type you'd like to modify. Add a new file field or
+   edit an existing one.
+
+   While editing the file or image field, you'll have new options available
+   under a "File sources" details. You can enable the desired sources for that
+   particular field.
+
+4) Create a piece of content that uses your file and try it out.
+
+Support
+-------
+Please file bug reports in the FileField Sources issue queue. Do not use the
+Drupal.org forums or send bug reports via e-mail.
+http://drupal.org/project/issues/filefield_sources?categories=All

+ 5 - 0
sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.data_types.schema.yml

@@ -0,0 +1,5 @@
+# Basic data types for FileField Sources.
+
+filefield_sources_source:
+  type: boolean
+  label: 'Plugin'

+ 9 - 0
sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.schema.yml

@@ -0,0 +1,9 @@
+field.widget.third_party.filefield_sources:
+  type: mapping
+  label: 'filefield_sources entity form display settings'
+  mapping:
+    filefield_sources:
+      type: sequence
+      label: 'filefield_sources settings'
+      sequence:
+        - type: filefield_sources.setting.[%key]

+ 13 - 0
sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.setting.source_attach.schema.yml

@@ -0,0 +1,13 @@
+filefield_sources.setting.source_attach:
+  type: mapping
+  label: 'File attach settings'
+  mapping:
+    path:
+      type: string
+      label: 'File attach path'
+    absolute:
+      type: integer
+      label: 'File attach location'
+    attach_mode:
+      type: string
+      label: 'Attach method'

+ 7 - 0
sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.setting.source_imce.schema.yml

@@ -0,0 +1,7 @@
+filefield_sources.setting.source_imce:
+  type: mapping
+  label: 'IMCE file browser settings'
+  mapping:
+    imce_mode:
+      type: integer
+      label: 'File browser mode'

+ 10 - 0
sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.setting.source_reference.schema.yml

@@ -0,0 +1,10 @@
+filefield_sources.setting.source_reference:
+  type: mapping
+  label: 'Autocomplete reference options'
+  mapping:
+    autocomplete:
+      type: string
+      label: 'Match file name'
+    search_all_fields:
+      type: string
+      label: 'Search all file fields'

+ 5 - 0
sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.setting.sources.schema.yml

@@ -0,0 +1,5 @@
+filefield_sources.setting.sources:
+  type: sequence
+  label: 'filefield_sources source'
+  sequence:
+    - type: filefield_sources.source.[%key]

+ 3 - 0
sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.source.attach.schema.yml

@@ -0,0 +1,3 @@
+filefield_sources.source.attach:
+  type: filefield_sources_source
+  label: 'File attach'

+ 3 - 0
sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.source.clipboard.schema.yml

@@ -0,0 +1,3 @@
+filefield_sources.source.clipboard:
+  type: filefield_sources_source
+  label: 'Clipboard'

+ 3 - 0
sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.source.imce.schema.yml

@@ -0,0 +1,3 @@
+filefield_sources.source.imce:
+  type: filefield_sources_source
+  label: 'File browser'

+ 3 - 0
sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.source.reference.schema.yml

@@ -0,0 +1,3 @@
+filefield_sources.source.reference:
+  type: filefield_sources_source
+  label: 'Reference existing'

+ 3 - 0
sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.source.remote.schema.yml

@@ -0,0 +1,3 @@
+filefield_sources.source.remote:
+  type: filefield_sources_source
+  label: 'Remote URL'

+ 5 - 0
sites/all/modules/contrib/fields/filefield_sources/config/schema/filefield_sources.source.upload.schema.yml

@@ -0,0 +1,5 @@
+# Schema for the filefield_sources source plugins.
+
+filefield_sources.source.upload:
+  type: filefield_sources_source
+  label: 'Upload'

+ 44 - 0
sites/all/modules/contrib/fields/filefield_sources/css/filefield_sources.css

@@ -0,0 +1,44 @@
+/* Generic display for all sources. */
+
+div.filefield-source input.form-text,
+div.filefield-source select.form-select {
+  display: inline;
+  width: 20em;
+}
+
+div.filefield-source .form-item {
+  white-space: normal;
+}
+
+div.filefield-source .hint {
+  color: #999;
+}
+
+div.filefield-sources-list a.active {
+  font-weight: bold;
+}
+
+/* Clipboard source. */
+div.filefield-source-clipboard-capture {
+  border: 1px solid #ccc;
+  width: 20em;
+  height: 1.4em;
+  padding: 2px;
+  display: inline-block;
+  vertical-align: top;
+  overflow: hidden;
+}
+div.filefield-source-clipboard-capture img {
+  display: none;
+}
+
+/* Reference source. */
+div.filefield-source-reference-item {
+  font-size: 90%;
+}
+
+/* Remote source. */
+div.filefield-source-remote input.form-text {
+  /* Helps with display consistency since references has a background. */
+  background-image: inherit;
+}

+ 42 - 0
sites/all/modules/contrib/fields/filefield_sources/filefield_sources.api.php

@@ -0,0 +1,42 @@
+<?php
+/**
+ * @file
+ * This file documents hooks provided by the FileField Sources module.
+ *
+ * Note that none of this code is executed by using FileField Sources module,
+ * it is provided here for reference as an example how to implement these hooks
+ * in your own module.
+ */
+
+/**
+ * Returns a list of widgets that are compatible with FileField Sources.
+ *
+ * FileField Sources works with the most common widgets used with Drupal (the
+ * standard Image and File widgets). Any module that provides another widget
+ * for uploading files may add compatibility with FileField Sources by
+ * implementing this hook and returning the widgets that their module supports.
+ */
+function hook_filefield_sources_widgets() {
+  // Add any widgets that your module supports here.
+  return array('mymodule_file_widgetname');
+}
+
+/**
+ * Allows altering the sources available on a field.
+ *
+ * This hook allows other modules to modify the sources available to a user.
+ *
+ * @param array $sources
+ *   List of filefiled sources plugins.
+ *
+ * @param mixed $context
+ *   Contains 'enabled_sources', 'element', 'form_state'.
+ */
+function hook_filefield_sources_sources_alter(&$sources, $context) {
+  // This example will exclude sources the user doesn't have access to.
+  foreach (array_keys($sources) as $type) {
+    if (!user_access("use $type filefield source")) {
+      unset($sources[$type]);
+    }
+  }
+}

+ 14 - 0
sites/all/modules/contrib/fields/filefield_sources/filefield_sources.info.yml

@@ -0,0 +1,14 @@
+name: File Field Sources
+type: module
+description: 'Extends File fields to allow referencing of existing files, remote files, and server files.'
+package: Fields
+# core: 8.x
+dependencies:
+  - file
+  - system
+
+# Information added by Drupal.org packaging script on 2017-02-21
+version: '8.x-1.x-dev'
+core: '8.x'
+project: 'filefield_sources'
+datestamp: 1487711285

+ 23 - 0
sites/all/modules/contrib/fields/filefield_sources/filefield_sources.install

@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * @file
+ * Update and install functions for FileField Sources.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function filefield_sources_install() {
+  // FileField Sources needs to load after both ImageField and FileField.
+  try {
+    $file_weight = module_get_weight('file');
+    $image_weight = module_get_weight('image');
+    $weight = max(array($file_weight, $image_weight));
+    $weight++;
+  }
+  catch (Exception $e) {
+    $weight = 5;
+  }
+  module_set_weight('filefield_sources', $weight);
+}

+ 9 - 0
sites/all/modules/contrib/fields/filefield_sources/filefield_sources.libraries.yml

@@ -0,0 +1,9 @@
+drupal.filefield_sources:
+  version: VERSION
+  js:
+    js/filefield_sources.js: {}
+  css:
+    theme:
+      css/filefield_sources.css: {}
+  dependencies:
+    - file/drupal.file

+ 771 - 0
sites/all/modules/contrib/fields/filefield_sources/filefield_sources.module

@@ -0,0 +1,771 @@
+<?php
+
+/**
+ * @file
+ * Extend FileField to allow files from multiple sources.
+ */
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Field\WidgetBase;
+use Drupal\Core\Field\WidgetInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Render\Element;
+use Drupal\imce\Imce;
+
+const FILEFIELD_SOURCE_ATTACH_DEFAULT_PATH = 'file_attach';
+const FILEFIELD_SOURCE_ATTACH_RELATIVE = 0;
+const FILEFIELD_SOURCE_ATTACH_ABSOLUTE = 1;
+const FILEFIELD_SOURCE_ATTACH_MODE_MOVE = 'move';
+const FILEFIELD_SOURCE_ATTACH_MODE_COPY = 'copy';
+
+const FILEFIELD_SOURCE_REFERENCE_HINT_TEXT = 'example.png [fid:123]';
+const FILEFIELD_SOURCE_REMOTE_HINT_TEXT = 'http://example.com/files/file.png';
+
+const FILEFIELD_SOURCE_REFERENCE_MATCH_STARTS_WITH = '0';
+const FILEFIELD_SOURCE_REFERENCE_MATCH_CONTAINS = '1';
+const FILEFIELD_SOURCE_REFERENCE_SEARCH_ALL_NO = '0';
+const FILEFIELD_SOURCE_REFERENCE_SEARCH_ALL_YES = '1';
+
+/**
+ * Implements hook_element_info_alter().
+ */
+function filefield_sources_element_info_alter(&$type) {
+  if (isset($type['managed_file'])) {
+    $type['managed_file']['#process'][] = 'filefield_sources_field_process';
+    $type['managed_file']['#pre_render'][] = 'filefield_sources_field_pre_render';
+    $type['managed_file']['#element_validate'][] = 'filefield_sources_field_validate';
+    $type['managed_file']['#file_value_callbacks'][] = 'filefield_sources_field_value';
+  }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function filefield_sources_theme() {
+  $theme = array();
+
+  $theme['filefield_sources_element'] = array(
+    'render element' => 'element',
+    'function' => 'theme_filefield_sources_element',
+  );
+
+  $theme['filefield_sources_list'] = array(
+    'variables' => array('element' => NULL, 'sources' => NULL),
+    'function' => 'theme_filefield_sources_list',
+  );
+
+  return $theme;
+}
+
+/**
+ * Implements hook_field_widget_third_party_settings_form().
+ *
+ * Add file field sources settings form to supported field widget forms.
+ *
+ * @see \Drupal\field_ui\FormDisplayOverview
+ */
+function filefield_sources_field_widget_third_party_settings_form(WidgetInterface $plugin, FieldDefinitionInterface $field_definition, $form_mode, $form, FormStateInterface $form_state) {
+  $element = array();
+  if (in_array($plugin->getPluginId(), \Drupal::moduleHandler()->invokeAll('filefield_sources_widgets'))) {
+    $element = filefield_sources_form($plugin, $form_state);
+  }
+  return $element;
+}
+
+/**
+ * Implements hook_field_widget_settings_summary_alter().
+ *
+ * Add file field sources information to the field widget settings summary.
+ *
+ * @see \Drupal\field_ui\FormDisplayOverview
+ */
+function filefield_sources_field_widget_settings_summary_alter(&$summary, $context) {
+  $plugin = $context['widget'];
+  if (in_array($plugin->getPluginId(), \Drupal::moduleHandler()->invokeAll('filefield_sources_widgets'))) {
+    $settings = $plugin->getThirdPartySetting('filefield_sources', 'filefield_sources');
+    $enabled_sources = _filefield_sources_enabled($settings);
+    $summary[] = t('File field sources:') . ' ' . implode(', ', array_keys($enabled_sources));
+  }
+}
+
+/**
+ * Implements hook_field_widget_form_alter().
+ *
+ * Add file field sources widget's settings to element.
+ */
+function filefield_sources_field_widget_form_alter(&$element, FormStateInterface $form_state, $context) {
+  $plugin = $context['widget'];
+  if (in_array($plugin->getPluginId(), \Drupal::moduleHandler()->invokeAll('filefield_sources_widgets'))) {
+    $element['#filefield_sources_settings'] = $plugin->getThirdPartySetting('filefield_sources', 'filefield_sources');
+
+    // Bundle is missing in element.
+    $items = $context['items'];
+    $element['#bundle'] = $items->getEntity()->bundle();
+  }
+}
+
+/**
+ * Implements hook_filefield_sources_widgets().
+ *
+ * This returns a list of widgets that are compatible with FileField Sources.
+ */
+function filefield_sources_filefield_sources_widgets() {
+  return array('file_generic', 'image_image');
+}
+
+/**
+ * Configuration form for editing FileField Sources settings for a widget.
+ */
+function filefield_sources_form($plugin, FormStateInterface $form_state) {
+  $settings = $plugin->getThirdPartySetting('filefield_sources', 'filefield_sources');
+
+  // Backward compatibility: auto-enable 'upload'.
+  $enabled = _filefield_sources_enabled($settings);
+
+  $form['filefield_sources'] = array(
+    '#type' => 'details',
+    '#title' => t('File sources'),
+    '#weight' => 20,
+  );
+
+  $sources = filefield_sources_list();
+  $form['filefield_sources']['sources'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Enabled sources'),
+    '#options' => $sources,
+    '#default_value' => $enabled,
+    '#description' => t('Select the available locations from which this widget may select files.'),
+  );
+
+  $params = array($plugin);
+  $form['filefield_sources'] = array_merge($form['filefield_sources'], filefield_sources_invoke_all('settings', $params));
+
+  return $form;
+}
+
+/**
+ * A #process callback to extend the filefield_widget element type.
+ *
+ * Add the central JavaScript and CSS files that allow switching between
+ * different sources. Third-party modules can also add to the list of sources
+ * by implementing hook_filefield_sources_info().
+ */
+function filefield_sources_field_process(&$element, FormStateInterface $form_state, &$complete_form) {
+  static $js_added;
+
+  // Check if we are processing file field sources.
+  if (!isset($element['#filefield_sources_settings'])) {
+    return $element;
+  }
+
+  // Do all processing as needed by each source.
+  $sources = filefield_sources_info();
+  $settings = $element['#filefield_sources_settings'];
+  $enabled_sources = _filefield_sources_enabled($settings);
+
+  $context = array(
+    'enabled_sources' => &$enabled_sources,
+    'element'         => $element,
+    'form_state'      => $form_state,
+  );
+
+  // Allow other modules to alter the sources.
+  \Drupal::moduleHandler()->alter('filefield_sources_sources', $sources, $context);
+
+  foreach ($sources as $source_name => $source) {
+    if (empty($enabled_sources[$source_name])) {
+      unset($sources[$source_name]);
+    }
+    // Default upload plugin does not have class.
+    elseif (isset($source['class'])) {
+      $callback = array($source['class'], 'process');
+      if (is_callable($callback)) {
+        $element = call_user_func_array($callback, array(
+          &$element,
+          $form_state,
+          &$complete_form,
+        ));
+      }
+    }
+  }
+  $element['#filefield_sources'] = $sources;
+
+  // Exit out if not adding any sources.
+  if (empty($sources)) {
+    return $element;
+  }
+
+  // Hide default 'upload' type?
+  if (!isset($enabled_sources['upload'])) {
+    foreach (array('upload_button', 'upload') as $field) {
+      if (isset($element[$field])) {
+        $element[$field]['#access'] = FALSE;
+      }
+    }
+  }
+
+  // Add class to upload button.
+  $element['upload_button']['#attributes']['class'][] = 'upload-button';
+
+  $element['#attached']['library'][] = 'filefield_sources/drupal.filefield_sources';
+
+  // Check the element for hint text that might need to be added.
+  foreach (Element::children($element) as $key) {
+    if (isset($element[$key]['#filefield_sources_hint_text']) && !isset($js_added[$key])) {
+      $type = str_replace('filefield_', '', $key);
+
+      $element['#attached']['drupalSettings']['fileFieldSources'][$type] = array(
+        'hintText' => $element[$key]['#filefield_sources_hint_text'],
+      );
+
+      $js_added[$key] = TRUE;
+    }
+  }
+
+  // Adjust the Ajax settings so that on upload and remove of any individual
+  // file, the entire group of file fields is updated together.
+  // Clone of Drupal\file\Plugin\Field\FieldWidget\FileWidget::process().
+  if ($element['#cardinality'] != 1) {
+    $parents = array_slice($element['#array_parents'], 0, -1);
+    $new_options = array(
+      'query' => array(
+        'element_parents' => implode('/', $parents),
+      ),
+    );
+    $field_element = NestedArray::getValue($complete_form, $parents);
+    $new_wrapper = $field_element['#id'] . '-ajax-wrapper';
+    foreach (Element::children($element) as $key) {
+      foreach (Element::children($element[$key]) as $subkey) {
+        if (isset($element[$key][$subkey]['#ajax'])) {
+          $element[$key][$subkey]['#ajax']['options'] = $new_options;
+          $element[$key][$subkey]['#ajax']['wrapper'] = $new_wrapper;
+          $element[$key][$subkey]['#limit_validation_errors'] = array(
+            array_slice($element['#array_parents'], 0, -2),
+          );
+        }
+      }
+    }
+    unset($element['#prefix'], $element['#suffix']);
+  }
+
+  // Add the list of sources to the element for toggling between sources.
+  if (empty($element['fids']['#value'])) {
+    if (count($enabled_sources) > 1) {
+      $element['filefield_sources_list'] = array(
+        '#theme' => 'filefield_sources_list',
+        '#element' => $element,
+        '#sources' => $sources,
+        '#weight' => -20,
+      );
+    }
+  }
+
+  return $element;
+}
+
+/**
+ * A #pre_render function to hide sources if a file is currently uploaded.
+ */
+function filefield_sources_field_pre_render($element) {
+  // If we already have a file, we don't want to show the upload controls.
+  if (!empty($element['#value']['fids'])) {
+    foreach (Element::children($element) as $key) {
+      if (!empty($element[$key]['#filefield_source'])) {
+        $element[$key]['#access'] = FALSE;
+      }
+    }
+  }
+  return $element;
+}
+
+/**
+ * An #element_validate function to run source validations.
+ */
+function filefield_sources_field_validate(&$element, FormStateInterface $form_state, &$complete_form) {
+  // Do all processing as needed by each source.
+  $sources = filefield_sources_info();
+  foreach ($sources as $source) {
+    if (!isset($source['class'])) {
+      continue;
+    }
+
+    $callback = array($source['class'], 'validate');
+    if (is_callable($callback)) {
+      call_user_func_array($callback, array(
+        $element,
+        $form_state,
+        $complete_form,
+      ));
+    }
+  }
+}
+
+/**
+ * Form submission handler for all FileField Source buttons.
+ *
+ * Clone of \Drupal\file\Plugin\Field\FieldWidget\FileWidget::submit(), with
+ * a few changes:
+ *   - Submit button is one level down compare to 'Upload' source's submit
+ *     button.
+ *   - Replace static in static::getWidgetState and static::setWidgetState by
+ *     WidgetBase.
+ *   - Rebuild the form after all.
+ */
+function filefield_sources_field_submit(&$form, FormStateInterface $form_state) {
+  // During the form rebuild, formElement() will create field item widget
+  // elements using re-indexed deltas, so clear out FormState::$input to
+  // avoid a mismatch between old and new deltas. The rebuilt elements will
+  // have #default_value set appropriately for the current state of the field,
+  // so nothing is lost in doing this.
+  $button = $form_state->getTriggeringElement();
+  $parents = array_slice($button['#parents'], 0, -3);
+  NestedArray::setValue($form_state->getUserInput(), $parents, NULL);
+
+  // Go one level up in the form, to the widgets container.
+  $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -2));
+  $field_name = $element['#field_name'];
+  $parents = $element['#field_parents'];
+
+  $submitted_values = NestedArray::getValue($form_state->getValues(), array_slice($button['#parents'], 0, -3));
+  foreach ($submitted_values as $delta => $submitted_value) {
+    if (empty($submitted_value['fids'])) {
+      unset($submitted_values[$delta]);
+    }
+  }
+
+  // If there are more files uploaded via the same widget, we have to separate
+  // them, as we display each file in it's own widget.
+  $new_values = array();
+  foreach ($submitted_values as $delta => $submitted_value) {
+    if (is_array($submitted_value['fids'])) {
+      foreach ($submitted_value['fids'] as $fid) {
+        $new_value = $submitted_value;
+        $new_value['fids'] = array($fid);
+        $new_values[] = $new_value;
+      }
+    }
+    else {
+      $new_value = $submitted_value;
+    }
+  }
+
+  // Re-index deltas after removing empty items.
+  $submitted_values = array_values($new_values);
+
+  // Update form_state values.
+  NestedArray::setValue($form_state->getValues(), array_slice($button['#parents'], 0, -3), $submitted_values);
+
+  // Update items.
+  $field_state = WidgetBase::getWidgetState($parents, $field_name, $form_state);
+  $field_state['items'] = $submitted_values;
+  WidgetBase::setWidgetState($parents, $field_name, $form_state, $field_state);
+
+  // We need to rebuild the form, so that uploaded file can be displayed.
+  $form_state->setRebuild();
+}
+
+/**
+ * A #filefield_value_callback to run source value callbacks.
+ */
+function filefield_sources_field_value(&$element, &$input, FormStateInterface $form_state) {
+  // Do all processing as needed by each source.
+  $sources = filefield_sources_info();
+  foreach ($sources as $source) {
+    if (isset($source['class'])) {
+      $callback = array($source['class'], 'value');
+      if (is_callable($callback)) {
+        call_user_func_array($callback, array(&$element, &$input, $form_state));
+      }
+    }
+  }
+}
+
+/**
+ * Call all FileField Source hooks stored in the available include files.
+ */
+function filefield_sources_invoke_all($method, &$params) {
+  $return = array();
+  foreach (\Drupal::service('filefield_sources')->getDefinitions() as $definition) {
+    if (!isset($definition['class'])) {
+      continue;
+    }
+    // Get routes defined by each plugin.
+    $callback = array($definition['class'], $method);
+    if (is_callable($callback)) {
+      $result = call_user_func_array($callback, $params);
+      if (isset($result) && is_array($result)) {
+        $return = array_merge_recursive($return, $result);
+      }
+      elseif (isset($result)) {
+        $return[] = $result;
+      }
+    }
+  }
+  return $return;
+}
+
+/**
+ * Load hook_filefield_sources_info() data from all modules.
+ */
+function filefield_sources_info($include_default = TRUE) {
+  $info = \Drupal::service('filefield_sources')->getDefinitions();
+  if (isset($info['imce']) && !Imce::access()) {
+    unset($info['imce']);
+  }
+  if ($include_default) {
+    $info['upload'] = array(
+      'name' => t('Upload (default)'),
+      'label' => t('Upload'),
+      'description' => t('Upload a file from your computer.'),
+      'weight' => -10,
+    );
+  }
+
+  uasort($info, '_filefield_sources_sort');
+
+  return $info;
+}
+
+/**
+ * Create a list of FileField Sources by name, suitable for a select list.
+ */
+function filefield_sources_list($include_default = TRUE) {
+  $info = filefield_sources_info($include_default);
+  $list = array();
+
+  foreach ($info as $key => $source) {
+    $list[$key] = $source['name'];
+  }
+
+  return $list;
+}
+
+/**
+ * Save a file into the database after validating it.
+ *
+ * This function is identical to the core function file_save_upload() except
+ * that it accepts an input file path instead of an input file source name.
+ *
+ * @see file_save_upload()
+ */
+function filefield_sources_save_file($filepath, $validators = array(), $destination = FALSE, $replace = FILE_EXISTS_RENAME) {
+  $user = \Drupal::currentUser();
+
+  // Begin building file object.
+  $file = entity_create('file', array(
+    'uri' => $filepath,
+    'uid' => $user->id(),
+    'status' => FILE_EXISTS_RENAME,
+  ));
+  $file->setFilename(trim(basename($filepath), '.'));
+  $file->setMimeType(\Drupal::service('file.mime_type.guesser')->guess($file->getFilename()));
+  $file->setSize(filesize($filepath));
+
+  $extensions = '';
+  if (isset($validators['file_validate_extensions'])) {
+    if (isset($validators['file_validate_extensions'][0])) {
+      // Build the list of non-munged extensions if the caller provided them.
+      $extensions = $validators['file_validate_extensions'][0];
+    }
+    else {
+      // If 'file_validate_extensions' is set and the list is empty then the
+      // caller wants to allow any extension. In this case we have to remove the
+      // validator or else it will reject all extensions.
+      unset($validators['file_validate_extensions']);
+    }
+  }
+  else {
+    // No validator was provided, so add one using the default list.
+    // Build a default non-munged safe list for file_munge_filename().
+    $extensions = 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp';
+    $validators['file_validate_extensions'] = array();
+    $validators['file_validate_extensions'][0] = $extensions;
+  }
+
+  if (!empty($extensions)) {
+    // Munge the filename to protect against possible malicious extension hiding
+    // within an unknown file type (ie: filename.html.foo).
+    $file->setFilename(file_munge_filename($file->getFilename(), $extensions));
+  }
+
+  // Rename potentially executable files, to help prevent exploits (i.e. will
+  // rename filename.php.foo and filename.php to filename.php.foo.txt and
+  // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads'
+  // evaluates to TRUE.
+  if (!\Drupal::config('system.file')->get('allow_insecure_uploads') && preg_match('/\.(php|pl|py|cgi|asp|js)(\.|$)/i', $file->getFilename()) && (substr($file->getFilename(), -4) != '.txt')) {
+    $file->setMimeType('text/plain');
+    $file->setFileUri($file->getFileUri() . '.txt');
+    $file->setFilename($file->getFilename() . '.txt');
+    // The .txt extension may not be in the allowed list of extensions. We have
+    // to add it here or else the file upload will fail.
+    if (!empty($extensions)) {
+      $validators['file_validate_extensions'][0] .= ' txt';
+      drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $file->getFilename())));
+    }
+  }
+
+  // If the destination is not provided, use the temporary directory.
+  if (empty($destination)) {
+    $destination = 'temporary://';
+  }
+
+  // Assert that the destination contains a valid stream.
+  $destination_scheme = file_uri_scheme($destination);
+  if (!$destination_scheme || !file_stream_wrapper_valid_scheme($destination_scheme)) {
+    drupal_set_message(t('The file could not be uploaded, because the destination %destination is invalid.', array('%destination' => $destination)), 'error');
+    return FALSE;
+  }
+
+  // A URI may already have a trailing slash or look like "public://".
+  if (substr($destination, -1) != '/') {
+    $destination .= '/';
+  }
+
+  // Ensure the destination is writable.
+  file_prepare_directory($destination, FILE_CREATE_DIRECTORY);
+
+  // Check if this is actually the same file being "attached" to a file record.
+  // If so, it acts as a file replace, except no file is actually moved.
+  $reuse_file = ($destination . $file->getFilename() === $file->getFileUri());
+  if ($reuse_file) {
+    $replace = FILE_EXISTS_REPLACE;
+  }
+
+  $file->destination = file_destination($destination . $file->getFilename(), $replace);
+  // If file_destination() returns FALSE then $replace == FILE_EXISTS_ERROR and
+  // there's an existing file so we need to bail.
+  if ($file->destination === FALSE) {
+    drupal_set_message(t('The file %source could not be uploaded because a file by that name already exists in the destination %directory.', array('%source' => $file->getFilename(), '%directory' => $destination)), 'error');
+    return FALSE;
+  }
+
+  // Add in our check of the the file name length.
+  $validators['file_validate_name_length'] = array();
+
+  // Call the validation functions specified by this function's caller.
+  $errors = file_validate($file, $validators);
+
+  // Check for errors.
+  if (!empty($errors)) {
+    $message = t('The specified file %name could not be uploaded.', array('%name' => $file->getFilename()));
+    if (count($errors) > 1) {
+      $message .= theme('item_list', array('items' => $errors));
+    }
+    else {
+      $message .= ' ' . array_pop($errors);
+    }
+    drupal_set_message($message, 'error');
+    return FALSE;
+  }
+
+  // Move uploaded files from PHP's upload_tmp_dir to Drupal's temporary
+  // directory. This overcomes open_basedir restrictions for future file
+  // operations.
+  $file->setFileUri($file->destination);
+  if (!$reuse_file && !file_unmanaged_copy($filepath, $file->getFileUri(), $replace)) {
+    drupal_set_message(t('File upload error. Could not move uploaded file.'), 'error');
+    \Drupal::logger('filefield_sources')->log(E_NOTICE, 'Upload error. Could not move uploaded file %file to destination %destination.', array('%file' => $file->getFilename(), '%destination' => $file->getFileUri()));
+    return FALSE;
+  }
+
+  // Set the permissions on the new file.
+  drupal_chmod($file->getFileUri());
+
+  // If we are replacing an existing file re-use its database record.
+  if ($replace == FILE_EXISTS_REPLACE) {
+    $existing_files = file_load_multiple(array(), array('uri' => $file->getFileUri()));
+    if (count($existing_files)) {
+      $existing = reset($existing_files);
+      $file->setOriginalId($existing->id());
+    }
+  }
+
+  // If we made it this far it's safe to record this file in the database.
+  $file->save();
+  return $file;
+}
+
+/**
+ * Clean up the file name, munging extensions and transliterating.
+ *
+ * @param string $filepath
+ *   A string containing a file name or full path. Only the file name will
+ *   actually be modified.
+ *
+ * @return string
+ *   A file path with a cleaned-up file name.
+ */
+function filefield_sources_clean_filename($filepath, $extensions) {
+  $filename = basename($filepath);
+
+  if (\Drupal::moduleHandler()->moduleExists('transliteration')) {
+    module_load_include('inc', 'transliteration');
+
+    $langcode = NULL;
+    if (!empty($_POST['language'])) {
+      $languages = language_list();
+      $langcode = isset($languages[$_POST['language']]) ? $_POST['language'] : NULL;
+    }
+    $filename = transliteration_clean_filename($filename, $langcode);
+  }
+
+  // Because this transfer mechanism does not use file_save_upload(), we need
+  // to manually munge the filename to prevent dangerous extensions.
+  // See file_save_upload().
+  if (empty($extensions)) {
+    $extensions = 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp';
+  }
+  $filename = file_munge_filename($filename, $extensions);
+  $directory = drupal_dirname($filepath);
+  return ($directory != '.' ? $directory . '/' : '') . $filename;
+}
+
+/**
+ * Theme the display of the source element.
+ */
+function theme_filefield_sources_element($variables) {
+  $element = $variables['element'];
+  $source_id = $element['#source_id'];
+  $method = isset($element['#method']) ? $element['#method'] : 'element';
+  $extra_variables = isset($element['#variables']) ? $element['#variables'] : array();
+
+  $sources = filefield_sources_info();
+  if (isset($sources[$source_id]['class'])) {
+    $callback = array($sources[$source_id]['class'], $method);
+    if (is_callable($callback)) {
+      $variables = array_merge($variables, $extra_variables);
+      return call_user_func_array($callback, array($variables));
+    }
+  }
+
+  return '';
+}
+
+/**
+ * Theme the display of the sources list.
+ */
+function theme_filefield_sources_list($variables) {
+  $element = $variables['element'];
+  $sources = $variables['sources'];
+
+  $links = array();
+
+  foreach ($sources as $name => $source) {
+    $links[] = '<a href="#" onclick="return false;" title="' . $source['description'] . '" id="' . $element['#id'] . '-' . $name . '-source" class="filefield-source filefield-source-' . $name . '">' . $source['label'] . '</a>';
+  }
+  return '<div class="filefield-sources-list">' . implode(' | ', $links) . '</div>';
+}
+
+/**
+ * Validate a file based on the $element['#upload_validators'] property.
+ */
+function filefield_sources_element_validate($element, $file, FormStateInterface $form_state) {
+  $validators = $element['#upload_validators'];
+  $errors = array();
+
+  // Since this frequently is used to reference existing files, check that
+  // they exist first in addition to the normal validations.
+  if (!file_exists($file->getFileUri())) {
+    $errors[] = t('The file does not exist.');
+  }
+  // Call the validation functions.
+  else {
+    foreach ($validators as $function => $args) {
+      // Add the $file variable to the list of arguments and pass it by
+      // reference (required for PHP 5.3 and higher).
+      array_unshift($args, NULL);
+      $args[0] = &$file;
+      $errors = array_merge($errors, call_user_func_array($function, $args));
+    }
+  }
+
+  // Check for validation errors.
+  if (!empty($errors)) {
+    $message = t('The selected file %name could not be referenced.', array('%name' => $file->filename));
+    if (count($errors) > 1) {
+      $message .= '<ul><li>' . implode('</li><li>', $errors) . '</li></ul>';
+    }
+    else {
+      $message .= ' ' . array_pop($errors);
+    }
+    $form_state->setError($element, $message);
+    return 0;
+  }
+
+  return 1;
+}
+
+/**
+ * Generate help text based on the $element['#upload_validators'] property.
+ */
+function filefield_sources_element_validation_help($validators) {
+  $desc = array();
+  foreach ($validators as $callback => $arguments) {
+    $help_func = $callback . '_help';
+    if (function_exists($help_func)) {
+      $desc[] = call_user_func_array($help_func, $arguments);
+    }
+  }
+  return empty($desc) ? '' : implode('<br />', $desc);
+}
+
+/**
+ * Custom sort function for ordering sources.
+ */
+function _filefield_sources_sort($a, $b) {
+  $a = (array) $a + array('weight' => 0, 'label' => '');
+  $b = (array) $b + array('weight' => 0, 'label' => '');
+  return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : strnatcasecmp($a['label'], $b['label']));
+}
+
+/**
+ * Helper to return enabled sources for a field.
+ *
+ * This provides backward compatibility for 'upload' type.
+ *
+ * @see http://drupal.org/node/932994
+ */
+function _filefield_sources_enabled($settings) {
+  if (!isset($settings['sources']['upload'])) {
+    $settings['sources']['upload'] = 'upload';
+  }
+
+  $enabled = array_keys(array_filter($settings['sources']));
+  asort($enabled);
+  return array_combine($enabled, $enabled);
+}
+
+// @todo Remove once https://www.drupal.org/node/1808132 is finished.
+if (!function_exists('module_get_weight')) {
+  /**
+   * Gets weight of a particular module.
+   *
+   * @param string $module
+   *   The name of the module (without the .module extension).
+   *
+   * @return int
+   *   The configured weight of the module.
+   *
+   * @throws InvalidArgumentException
+   *   Thrown in case the given module is not installed in the system.
+   */
+  function module_get_weight($module) {
+    $weight = \Drupal::config('core.extension')->get("module.$module");
+    if ($weight !== NULL) {
+      return (int) $weight;
+    }
+    $weight = \Drupal::config('core.extension')->get("disabled.module.$module");
+    if ($weight !== NULL) {
+      return (int) $weight;
+    }
+    throw new InvalidArgumentException(format_string('The module %module is not installed.', array('%module' => $module)));
+  }
+}
+
+/**
+ * Check for CURL extension enabled.
+ */
+function filefield_sources_curl_enabled() {
+  return function_exists('curl_version');
+}

+ 2 - 0
sites/all/modules/contrib/fields/filefield_sources/filefield_sources.routing.yml

@@ -0,0 +1,2 @@
+route_callbacks:
+  - '\Drupal\filefield_sources\Routing\FilefieldSourcesRoutes::routes'

+ 12 - 0
sites/all/modules/contrib/fields/filefield_sources/filefield_sources.services.yml

@@ -0,0 +1,12 @@
+services:
+  plugin.manager.filefield_sources:
+    class: Drupal\filefield_sources\FilefieldSourceManager
+    parent: default_plugin_manager
+  filefield_sources:
+    alias: plugin.manager.filefield_sources
+  access_check.filefield_sources.field:
+    class: Drupal\filefield_sources\Access\FieldAccessCheck
+    tags:
+      - { name: access_check, applies_to: _access_filefield_sources_field }
+  filefield_sources.imce_scanner:
+    class: Drupal\filefield_sources\ImceScanner

+ 262 - 0
sites/all/modules/contrib/fields/filefield_sources/js/filefield_sources.js

@@ -0,0 +1,262 @@
+/**
+ * @file
+ * Defines Javascript behaviors for the filefield_sources module.
+ */
+
+(function ($, Drupal) {
+
+"use strict";
+
+// Behavior to add source options to configured fields.
+Drupal.behaviors.fileFieldSources = {};
+Drupal.behaviors.fileFieldSources.attach = function(context, settings) {
+  $('div.filefield-sources-list:not(.filefield-sources-processed)', context).each(function() {
+    $(this).addClass('filefield-sources-processed');
+    var $fileFieldElement = $(this).parents('div.form-managed-file:first');
+    $(this).find('a').click(function() {
+      // Remove the active class.
+      $(this).parents('div.filefield-sources-list').find('a.active').removeClass('active');
+
+      // Find the unique FileField Source class name.
+      var fileFieldSourceClass = this.className.match(/filefield-source-[0-9a-z_]+/i)[0];
+
+      // The default upload element is a special case.
+      if ($(this).is('.filefield-source-upload')) {
+        $fileFieldElement.find('div.filefield-sources-list').siblings('.form-file, .form-submit').css('display', '');
+        $fileFieldElement.find('div.filefield-source').css('display', 'none');
+      }
+      else {
+        $fileFieldElement.find('div.filefield-sources-list').siblings('.form-file, .form-submit').css('display', 'none');
+        $fileFieldElement.find('div.filefield-source').not('div.' + fileFieldSourceClass).css('display', 'none');
+        $fileFieldElement.find('div.' + fileFieldSourceClass).css('display', '');
+      }
+
+      // Add the active class.
+      $(this).addClass('active');
+      Drupal.fileFieldSources.updateHintText($fileFieldElement.get(0));
+    }).first().triggerHandler('click');
+
+    // Clipboard support.
+    $fileFieldElement.find('.filefield-source-clipboard-capture')
+      .bind('paste', Drupal.fileFieldSources.pasteEvent)
+      .bind('focus', Drupal.fileFieldSources.pasteFocus)
+      .bind('blur', Drupal.fileFieldSources.pasteBlur);
+
+    // Imce support.
+    $fileFieldElement.find('.filefield-sources-imce-browse')
+      .bind('click', Drupal.fileFieldSources.imceBrowse);
+  });
+
+  if (context === document) {
+    $('form').submit(function() {
+      Drupal.fileFieldSources.removeHintText();
+    });
+  }
+};
+
+// Helper functions used by FileField Sources.
+Drupal.fileFieldSources = {
+  /**
+   * Update the hint text when clicking between source types.
+   */
+  updateHintText: function(fileFieldElement) {
+    // Add default value hint text to text fields.
+    $(fileFieldElement).find('div.filefield-source').each(function() {
+      var matches = this.className.match(/filefield-source-([a-z]+)/);
+      var sourceType = matches[1];
+      var textfield = $(this).find('input.form-text:first').get(0);
+      var defaultText = (drupalSettings.fileFieldSources && drupalSettings.fileFieldSources[sourceType]) ? drupalSettings.fileFieldSources[sourceType].hintText : '';
+
+      // If the field doesn't exist, just return.
+      if (!textfield) {
+        return;
+      }
+
+      // If this field is not shown, remove its value and be done.
+      if (!$(this).is(':visible') && textfield.value == defaultText) {
+        textfield.value = '';
+        return;
+      }
+
+      // Set a default value:
+      if (textfield.value == '') {
+        textfield.value = defaultText;
+      }
+
+      // Set a default class.
+      if (textfield.value == defaultText) {
+        $(textfield).addClass('hint');
+      }
+
+      $(textfield).focus(hideHintText);
+      $(textfield).blur(showHintText);
+
+      function showHintText() {
+        if (this.value == '') {
+          this.value = defaultText;
+          $(this).addClass('hint');
+        }
+      }
+
+      function hideHintText() {
+        if (this.value == defaultText) {
+          this.value = '';
+          $(this).removeClass('hint');
+        }
+      }
+    });
+  },
+
+  /**
+   * Delete all hint text from a form before submit.
+   */
+  removeHintText: function() {
+    $('div.filefield-source input.hint').val('').removeClass('hint');
+  },
+
+  /**
+   * Clean up the default value on focus.
+   */
+  pasteFocus: function(e) {
+    // Set default text.
+    if (!this.defaultText) {
+      this.defaultText = this.innerHTML;
+      this.innerHTML = '';
+    }
+    // Remove non-text nodes.
+    $(this).children().remove();
+  },
+
+  /**
+   * Restore default value on blur.
+   */
+  pasteBlur: function(e) {
+    if (this.defaultText && !this.innerHTML) {
+      this.innerHTML = this.defaultText;
+    }
+  },
+
+  pasteEvent: function(e) {
+    var clipboardData = null;
+    var targetElement = this;
+    var userAgent = navigator.userAgent.toLowerCase();
+
+    // Chrome.
+    if (window.event && window.event.clipboardData && window.event.clipboardData.items) {
+      clipboardData = window.event.clipboardData;
+    }
+    // All browsers in the future (hopefully).
+    else if (e.originalEvent && e.originalEvent.clipboardData && e.originalEvent.clipboardData.items) {
+      clipboardData = e.originalEvent.clipboardData;
+    }
+    // Firefox with content editable pastes as img tag with data href.
+    else if (userAgent.match(/mozilla/) && !userAgent.match(/webkit/)) {
+      Drupal.fileFieldSources.waitForPaste(targetElement);
+      return true;
+    }
+    else {
+      Drupal.fileFieldSources.pasteError(targetElement, Drupal.t('Paste from clipboard not supported in this browser.'));
+      return false;
+    }
+
+    var items = clipboardData.items;
+    var types = clipboardData.types;
+    var filename = targetElement.firstChild ? targetElement.firstChild.textContent : '';
+
+    // Handle files and image content directly in the clipboard.
+    var fileFound = false;
+    for (var n = 0; n < items.length; n++) {
+      if (items[n] && items[n].kind === 'file') {
+        var fileBlob = items[n].getAsFile();
+        var fileReader = new FileReader();
+        // Define events to fire after the file is read into memory.
+        fileReader.onload = function() {
+          Drupal.fileFieldSources.pasteSubmit(targetElement, filename, this.result);
+        };
+        fileReader.onerror = function() {
+          Drupal.fileFieldSources.pasteError(targetElement, Drupal.t('Error reading file from clipboard.'));
+        };
+        // Read in the file to fire the above events.
+        fileReader.readAsDataURL(fileBlob);
+        fileFound = true;
+        break;
+      }
+      // Handle files that a copy/pasted as a file reference.
+      /* if (types[n] && types[n] === 'Files') {
+          TODO: Figure out how to capture copy/paste of entire files from desktop.
+       }*/
+    }
+    if (!fileFound) {
+      Drupal.fileFieldSources.pasteError(targetElement, Drupal.t('No file in clipboard.'));
+    }
+    return false;
+  },
+
+  /**
+   * For browsers that don't support native clipboardData attributes.
+   */
+  waitForPaste: function(targetElement) {
+    if (targetElement.children && targetElement.children.length > 0) {
+      var filename = targetElement.firstChild ? targetElement.firstChild.textContent : '';
+      var tagFound = false;
+      $(targetElement).find('img[src^="data:image"]').each(function(n, element) {
+        Drupal.fileFieldSources.pasteSubmit(targetElement, filename, element.src);
+        tagFound = true;
+      });
+      $(targetElement).html(filename);
+      if (!tagFound) {
+        Drupal.fileFieldSources.pasteError(targetElement, Drupal.t('No file in clipboard.'));
+      }
+    }
+    else {
+      setTimeout(function() {
+        Drupal.fileFieldSources.waitForPaste(targetElement);
+      }, 200);
+    }
+  },
+
+  /**
+   * Set an error on the paste field temporarily then clear it.
+   */
+  pasteError: function(domElement, errorMessage) {
+    var $description = $(domElement).parents('.filefield-source-clipboard:first').find('.description');
+    if (!$description.data('originalDescription')) {
+      $description.data('originalDescription', $description.html())
+    }
+    $description.html(errorMessage);
+    var errorTimeout = setTimeout(function() {
+      $description.html($description.data('originalDescription'));
+      $(this).unbind('click.pasteError');
+    }, 3000);
+    $(domElement).bind('click.pasteError', function() {
+      clearTimeout(errorTimeout);
+      $description.html($description.data('originalDescription'));
+      $(this).unbind('click.pasteError');
+    });
+  },
+
+  /**
+   * After retreiving a clipboard, post the results to the server.
+   */
+  pasteSubmit: function(targetElement, filename, contents) {
+    var $wrapper = $(targetElement).parents('.filefield-source-clipboard');
+    $wrapper.find('.filefield-source-clipboard-filename').val(filename);
+    $wrapper.find('.filefield-source-clipboard-contents').val(contents);
+    $wrapper.find('input.form-submit').trigger('mousedown');
+  },
+
+  /**
+   * Click event for the imce browse link.
+   */
+  imceBrowse: function (e) {
+    window.open(this.href, '', 'width=760,height=560,resizable=1');
+    e.preventDefault();
+  }
+};
+
+// Override triggerUploadButton method from file.js.
+Drupal.file.triggerUploadButton = function (event) {
+  $(event.target).closest('.form-managed-file').find('.form-submit.upload-button').trigger('mousedown');
+}
+
+})(jQuery, Drupal);

+ 38 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Access/FieldAccessCheck.php

@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filefield_sources\Access\FieldAccessCheck.
+ */
+
+namespace Drupal\filefield_sources\Access;
+
+use Drupal\Core\Routing\Access\AccessInterface as RoutingAccessInterface;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Access check for file field source routes.
+ */
+class FieldAccessCheck implements RoutingAccessInterface {
+
+  /**
+   * Checks access.
+   *
+   * @param string $entity_type
+   *   Entity type.
+   * @param string $bundle_name
+   *   Bundle name.
+   * @param string $field_name
+   *   Field name.
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The currently logged in account.
+   *
+   * @return string
+   *   A \Drupal\Core\Access\AccessInterface constant value.
+   */
+  public function access($entity_type, $bundle_name, $field_name, AccountInterface $account) {
+    $field = entity_load('field_config', $entity_type . '.' . $bundle_name . '.' . $field_name);
+    return $field->access('edit', $account, TRUE);
+  }
+
+}

+ 62 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Annotation/FilefieldSource.php

@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filefield_sources\Annotation\FilefieldSource.
+ */
+
+namespace Drupal\filefield_sources\Annotation;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines a Search API processor annotation object.
+ *
+ * @Annotation
+ */
+class FilefieldSource extends Plugin {
+
+  /**
+   * The file field source plugin ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The name of the file field source plugin.
+   *
+   * It will be displayed in a select list.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   */
+  public $name;
+
+  /**
+   * The human-readable name of the file field source plugin.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   */
+  public $label;
+
+  /**
+   * The description of the file field source plugin.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   */
+  public $description;
+
+  /**
+   * The weight of file field source plugin.
+   *
+   * @var integer
+   */
+  public $weight;
+
+}

+ 111 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Controller/ImceController.php

@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\imce\Controller\ImceController.
+ */
+
+namespace Drupal\filefield_sources\Controller;
+
+use Symfony\Component\HttpFoundation\Request;
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\imce\Imce;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Drupal\Core\Render\BubbleableMetadata;
+
+/**
+ * Controller routines for imce routes.
+ */
+class ImceController extends ControllerBase {
+
+  /**
+   * Outputs the IMCE browser for FileField.
+   */
+  public function page($entity_type, $bundle_name, $field_name, Request $request) {
+    // Check access.
+    if (!\Drupal::moduleHandler()->moduleExists('imce') || !Imce::access() || !$instance = entity_load('field_config', $entity_type . '.' . $bundle_name . '.' . $field_name)) {
+      throw new AccessDeniedHttpException();
+    }
+
+    $settings = $instance->getSettings();
+    $imceFM = Imce::userFM(\Drupal::currentUser(), $settings['uri_scheme'], $request);
+
+    // Override scanner.
+    if (!empty($imceFM)) {
+      $scanner = \Drupal::service('filefield_sources.imce_scanner');
+      $widget = entity_get_form_display($entity_type, $bundle_name, 'default')->getComponent($field_name);
+      // Full mode.
+      if (!empty($widget['third_party_settings']['filefield_sources']['filefield_sources']['source_imce']['imce_mode'])) {
+        $imceFM->setConf('scanner', array($scanner, 'customScanFull'));
+        // Set context.
+        $scanner->setContext(array(
+          'scheme' => $imceFM->getConf('scheme'),
+        ));
+      }
+      // Restricted mode.
+      else {
+        $imceFM->setConf('scanner', array($scanner, 'customScanRestricted'));
+
+        // Make field directory the only accessible one.
+        $field_uri = static::getUploadLocation($settings);
+        static::disablePerms($imceFM, $field_uri, array('browse_files'));
+
+        // Set context.
+        $scanner->setContext(array(
+          'entity_type' => $entity_type,
+          'field_name' => $field_name,
+          'uri' => $field_uri,
+          'is_rool' => $is_root,
+        ));
+      }
+
+      // Disable absolute URLs.
+      \Drupal::configFactory()->getEditable('imce.settings')->set('abs_urls', FALSE);
+
+      return $imceFM->pageResponse();
+    }
+  }
+
+  /**
+   * Determines the URI for a file field.
+   *
+   * @param array $data
+   *   An array of token objects to pass to token_replace().
+   *
+   * @return string
+   *   A file directory URI with tokens replaced.
+   *
+   * @see token_replace()
+   */
+  protected static function getUploadLocation($settings, $data = array()) {
+    $destination = trim($settings['file_directory'], '/');
+
+    // Replace tokens. To ensure that render context is empty, pass a bubbleable
+    // metadata object to the replace method.
+    $bubbleable_metadata = new BubbleableMetadata();
+    $destination = \Drupal::token()->replace($destination, $data, [], $bubbleable_metadata);
+
+    return $settings['uri_scheme'] . '://' . $destination;
+  }
+
+  /**
+   * Disable IMCE profile permissions.
+   */
+  protected static function disablePerms($imceFM, $field_uri, $exceptions = array()) {
+    $scheme = $imceFM->getConf('scheme');
+    $root = $scheme . '://';
+    $is_root = $field_uri == $root;
+    $path = $is_root ? '.' : substr($field_uri, strlen($root));
+
+    $folders = $imceFM->getConf('folders');
+    $perms = \Drupal::service('plugin.manager.imce.plugin')->permissionInfo();
+    $folders['.']['permissions']['all'] = FALSE;
+    $folders[$path]['permissions']['all'] = FALSE;
+    foreach ($perms as $perm => $title) {
+      $folders['.']['permissions'][$perm] = FALSE;
+      $folders[$path]['permissions'][$perm] = in_array($perm, array('browse_files')) ? TRUE : FALSE;
+    }
+    $imceFM->setConf('folders', $folders);
+  }
+
+}

+ 75 - 0
sites/all/modules/contrib/fields/filefield_sources/src/File/MimeType/ExtensionMimeTypeGuesser.php

@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filefield_sources\File\MimeType\ExtensionMimeTypeGuesser.
+ */
+
+namespace Drupal\filefield_sources\File\MimeType;
+
+use Drupal\Core\File\MimeType\ExtensionMimeTypeGuesser as CoreExtensionMimeTypeGuesser;
+
+/**
+ * Add methods to core guesser.
+ */
+class ExtensionMimeTypeGuesser extends CoreExtensionMimeTypeGuesser {
+
+  /**
+   * Convert mime type to extension.
+   *
+   * @param string $mimetype
+   *   Mime type.
+   *
+   * @return string|bool
+   *   Return extension if found, FALSE otherwise.
+   */
+  public function convertMimeTypeToExtension($mimetype) {
+    $this->checkDefaultMapping();
+
+    $mime_key = array_search($mimetype, $this->mapping['mimetypes']);
+    $extension = array_search($mime_key, $this->mapping['extensions']);
+
+    return $extension;
+  }
+
+  /**
+   * Convert mime type to most common extension.
+   *
+   * @param string $mimetype
+   *   Mime type.
+   *
+   * @return string|bool
+   *   Return extension if found, FALSE otherwise.
+   */
+  public function convertMimeTypeToMostCommonExtension($mimetype) {
+    $this->checkDefaultMapping();
+
+    $extension = FALSE;
+    if (isset($mimetype)) {
+      // See if this matches a known MIME type.
+      $mime_key = array_search($mimetype, $this->mapping['mimetypes']);
+      if ($mime_key !== FALSE) {
+        // If we have a match, get this list of likely extensions. For some
+        // reason Drupal lists the "most common" extension last for most file
+        // types including php, jpg, and doc.
+        if ($extensions = array_keys($this->mapping['extensions'], $mime_key)) {
+          $extension = end($extensions);
+        }
+      }
+    }
+    return $extension;
+  }
+
+  /**
+   * Check for default mapping.
+   */
+  private function checkDefaultMapping() {
+    if ($this->mapping === NULL) {
+      $mapping = $this->defaultMapping;
+      // Allow modules to alter the default mapping.
+      $this->moduleHandler->alter('file_mimetype_mapping', $mapping);
+      $this->mapping = $mapping;
+    }
+  }
+
+}

+ 55 - 0
sites/all/modules/contrib/fields/filefield_sources/src/FilefieldSourceInterface.php

@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filefield_sources\FilefieldSourceInterface.
+ */
+
+namespace Drupal\filefield_sources;
+
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Provides an interface for file field source plugins.
+ *
+ * @see \Drupal\filefield_sources\FilefieldSourceManager
+ * @see \Drupal\filefield_sources\Annotation\FilefieldSource
+ * @see plugin_api
+ *
+ * @ingroup filefield_sources
+ */
+interface FilefieldSourceInterface {
+
+  /**
+   * Value callback for file field source plugin.
+   *
+   * @param array $element
+   *   An associative array containing the properties of the element.
+   * @param mixed $input
+   *   The incoming input to populate the form element. If this is FALSE,
+   *   the element's default value should be returned.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   *
+   * @return mixed
+   *   The value to assign to the element.
+   */
+  public static function value(array &$element, &$input, FormStateInterface $form_state);
+
+  /**
+   * Process callback for file field source plugin.
+   *
+   * @param array $element
+   *   An associative array containing the properties and children of the
+   *   generic input element.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   * @param array $complete_form
+   *   The complete form structure.
+   *
+   * @return array
+   *   The processed element.
+   */
+  public static function process(array &$element, FormStateInterface $form_state, array &$complete_form);
+
+}

+ 54 - 0
sites/all/modules/contrib/fields/filefield_sources/src/FilefieldSourceManager.php

@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filefield_sources\FilefieldSourceManager.
+ */
+
+namespace Drupal\filefield_sources;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+
+/**
+ * Provides a plugin manager for file field source.
+ *
+ * @see \Drupal\filefield_sources\Annotation\FilefieldSource
+ * @see \Drupal\filefield_sources\FilefieldSourceInterface
+ * @see plugin_api
+ */
+class FilefieldSourceManager extends DefaultPluginManager {
+
+  /**
+   * Constructs a FilefieldSourceManager 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
+   *   Cache backend instance to use.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler to invoke the alter hook with.
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
+    $this->setCacheBackend($cache_backend, 'filefield_sources');
+
+    parent::__construct('Plugin/FilefieldSource', $namespaces, $module_handler, 'Drupal\filefield_sources\FilefieldSourceInterface', 'Drupal\filefield_sources\Annotation\FilefieldSource');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinitions() {
+    $definitions = parent::getDefinitions();
+    if (!\Drupal::moduleHandler()->moduleExists('imce')) {
+      unset($definitions['imce']);
+    }
+    if (!filefield_sources_curl_enabled()) {
+      unset($definitions['remote']);
+    }
+    return $definitions;
+  }
+
+}

+ 26 - 0
sites/all/modules/contrib/fields/filefield_sources/src/FilefieldSourcesServiceProvider.php

@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filefield_sources\FilefieldSourcesServiceProvider.
+ */
+
+namespace Drupal\filefield_sources;
+
+use Drupal\Core\DependencyInjection\ServiceModifierInterface;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+
+/**
+ * Alter file.mime_type.guesser.extension service.
+ */
+class FilefieldSourcesServiceProvider implements ServiceModifierInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function alter(ContainerBuilder $container) {
+    $definition = $container->getDefinition('file.mime_type.guesser.extension');
+    $definition->setClass('Drupal\filefield_sources\File\MimeType\ExtensionMimeTypeGuesser');
+  }
+
+}

+ 102 - 0
sites/all/modules/contrib/fields/filefield_sources/src/ImceScanner.php

@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filefield_sources\ImceScanner.
+ */
+
+namespace Drupal\filefield_sources;
+
+use Drupal\imce\Imce;
+
+/**
+ * Imce scanner service.
+ */
+class ImceScanner {
+
+  /**
+   * Scanner context.
+   *
+   * @var mixed
+   */
+  private $context = NULL;
+
+  /**
+   * Sets scanner context.
+   */
+  public function setContext($context) {
+    $this->context = $context;
+  }
+
+  /**
+   * Scan and return files, subdirectories.
+   */
+  public function customScanFull($dirname, $options) {
+    // Get a list of files in the database for this directory.
+    $scheme = $this->context['scheme'];
+    $sql_uri_name = $dirname == '.' ? $scheme . '://' : $dirname . '/';
+
+    $result = db_select('file_managed', 'f')
+      ->fields('f', array('uri'))
+      ->condition('f.uri', $sql_uri_name . '%', 'LIKE')
+      ->condition('f.uri', $sql_uri_name . '_%/%', 'NOT LIKE')
+      ->execute();
+
+    $db_files = array();
+    foreach ($result as $row) {
+      $db_files[basename($row->uri)] = 1;
+    }
+
+    // Get the default IMCE directory scan, then filter down to database files.
+    $content = Imce::scanDir($dirname, $options);
+    foreach ($content['files'] as $filename => $file) {
+      if (!isset($db_files[$filename])) {
+        unset($content['files'][$filename]);
+      }
+    }
+
+    return $content;
+  }
+
+  /**
+   * Scan directory and return file list.
+   *
+   * This only work on Restricted Mode.
+   */
+  public function customScanRestricted($dirname, $options) {
+    $content = array('files' => array(), 'subfolders' => array());
+    $field_uri = $this->context['uri'];
+    $is_root = $this->context['is_root'];
+
+    if ($dirname !== $field_uri) {
+      return $content;
+    }
+
+    $entity_type = $this->context['entity_type'];
+    $field_name = $this->context['field_name'];
+    $field_storage = entity_load('field_storage_config', $entity_type . '.' . $field_name);
+    $entity_manager = \Drupal::entityManager();
+    if ($entity_manager->hasDefinition($entity_type)) {
+      $storage = $entity_manager->getStorage($entity_type);
+      $table_mapping = $storage->getTableMapping();
+      $field_table = $table_mapping->getDedicatedDataTableName($field_storage);
+      $field_column_name = $table_mapping->getFieldColumnName($field_storage, 'target_id');
+
+      $sql_uri = $field_uri . ($is_root ? '' : '/');
+      $query = db_select($field_table, 'cf');
+      $query->innerJoin('file_managed', 'f', 'f.fid = cf.' . $field_column_name);
+      $result = $query->fields('f')
+        ->condition('f.status', 1)
+        ->condition('f.uri', $sql_uri . '%', 'LIKE')
+        ->condition('f.uri', $sql_uri . '%/%', 'NOT LIKE')
+        ->execute();
+      foreach ($result as $file) {
+        // Get real name.
+        $name = basename($file->uri);
+        $content['files'][$name] = $file->uri;
+      }
+    }
+
+    return $content;
+  }
+}

+ 356 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Plugin/FilefieldSource/Attach.php

@@ -0,0 +1,356 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filefield_sources\Plugin\FilefieldSource\Attach.
+ */
+
+namespace Drupal\filefield_sources\Plugin\FilefieldSource;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\filefield_sources\FilefieldSourceInterface;
+use Drupal\Core\Field\WidgetInterface;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Site\Settings;
+use Drupal\Core\Template\Attribute;
+
+/**
+ * A FileField source plugin to allow use of files within a server directory.
+ *
+ * @FilefieldSource(
+ *   id = "attach",
+ *   name = @Translation("File attach from server directory"),
+ *   label = @Translation("File attach"),
+ *   description = @Translation("Select a file from a directory on the server."),
+ *   weight = 3
+ * )
+ */
+class Attach implements FilefieldSourceInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function value(array &$element, &$input, FormStateInterface $form_state) {
+    if (!empty($input['filefield_attach']['filename'])) {
+      $instance = entity_load('field_config', $element['#entity_type'] . '.' . $element['#bundle'] . '.' . $element['#field_name']);
+      $filepath = $input['filefield_attach']['filename'];
+
+      // Check that the destination is writable.
+      $directory = $element['#upload_location'];
+      $mode = Settings::get('file_chmod_directory', FILE_CHMOD_DIRECTORY);
+
+      // This first chmod check is for other systems such as S3, which don't
+      // work with file_prepare_directory().
+      if (!drupal_chmod($directory, $mode) && !file_prepare_directory($directory, FILE_CREATE_DIRECTORY)) {
+        \Drupal::logger('filefield_sources')->log(E_NOTICE, 'File %file could not be copied, because the destination directory %destination is not configured correctly.', array(
+          '%file' => $filepath,
+          '%destination' => drupal_realpath($directory),
+        ));
+        drupal_set_message(t('The specified file %file could not be copied, because the destination directory is not properly configured. This may be caused by a problem with file or directory permissions. More information is available in the system log.', array('%file' => $filepath)), 'error');
+        return;
+      }
+
+      // Clean up the file name extensions and transliterate.
+      $original_filepath = $filepath;
+      $new_filepath = filefield_sources_clean_filename($filepath, $instance->getSetting('file_extensions'));
+      rename($filepath, $new_filepath);
+      $filepath = $new_filepath;
+
+      // Run all the normal validations, minus file size restrictions.
+      $validators = $element['#upload_validators'];
+      if (isset($validators['file_validate_size'])) {
+        unset($validators['file_validate_size']);
+      }
+
+      // Save the file to the new location.
+      if ($file = filefield_sources_save_file($filepath, $validators, $directory)) {
+        if (!in_array($file->id(), $input['fids'])) {
+          $input['fids'][] = $file->id();
+        }
+
+        // Delete the original file if "moving" the file instead of copying.
+        if ($element['#filefield_sources_settings']['source_attach']['attach_mode'] !== FILEFIELD_SOURCE_ATTACH_MODE_COPY) {
+          @unlink($filepath);
+        }
+      }
+
+      // Restore the original file name if the file still exists.
+      if (file_exists($filepath) && $filepath != $original_filepath) {
+        rename($filepath, $original_filepath);
+      }
+
+      $input['filefield_attach']['filename'] = '';
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function process(array &$element, FormStateInterface $form_state, array &$complete_form) {
+    $settings = $element['#filefield_sources_settings']['source_attach'];
+    $field_name = $element['#field_name'];
+    $instance = entity_load('field_config', $element['#entity_type'] . '.' . $element['#bundle'] . '.' . $field_name);
+
+    $element['filefield_attach'] = array(
+      '#weight' => 100.5,
+      '#theme' => 'filefield_sources_element',
+      '#source_id' => 'attach',
+      // Required for proper theming.
+      '#filefield_source' => TRUE,
+    );
+
+    $path = static::getDirectory($settings);
+    $options = static::getAttachOptions($path, $instance->getSetting('file_extensions'));
+
+    // If we have built this element before, append the list of options that we
+    // had previously. This allows files to be deleted after copying them and
+    // still be considered a valid option during the validation and submit.
+    $triggering_element = $form_state->getTriggeringElement();
+    $property = array(
+      'filefield_sources',
+      $field_name,
+      'attach_options',
+    );
+    if (!isset($triggering_element) && $form_state->has($property)) {
+      $attach_options = $form_state->get($property);
+      $options = $options + $attach_options;
+    }
+    // On initial form build and rebuilds after processing input, save the
+    // original list of options so they can be restored in the line above.
+    else {
+      $form_state->set(array('filefield_sources', $field_name, 'attach_options'), $options);
+    }
+
+    $description = t('This method may be used to attach files that exceed the file size limit. Files may be attached from the %directory directory on the server, usually uploaded through FTP.', array('%directory' => realpath($path)));
+
+    // Error messages.
+    if ($options === FALSE || empty($settings['path'])) {
+      $attach_message = t('A file attach directory could not be located.');
+      $attach_description = t('Please check your settings for the %field field.', array('%field' => $instance->getLabel()));
+    }
+    elseif (!count($options)) {
+      $attach_message = t('There currently are no files to attach.');
+      $attach_description = $description;
+    }
+
+    if (isset($attach_message)) {
+      $element['filefield_attach']['attach_message'] = array(
+        '#markup' => $attach_message,
+      );
+      $element['filefield_attach']['#description'] = $attach_description;
+    }
+    else {
+      $validators = $element['#upload_validators'];
+      if (isset($validators['file_validate_size'])) {
+        unset($validators['file_validate_size']);
+      }
+      $description .= '<br />' . filefield_sources_element_validation_help($validators);
+      $element['filefield_attach']['filename'] = array(
+        '#type' => 'select',
+        '#options' => $options,
+      );
+      $element['filefield_attach']['#description'] = $description;
+    }
+    $class = '\Drupal\file\Element\ManagedFile';
+    $ajax_settings = [
+      'callback' => [$class, 'uploadAjaxCallback'],
+      'options' => [
+        'query' => [
+          'element_parents' => implode('/', $element['#array_parents']),
+        ],
+      ],
+      'wrapper' => $element['upload_button']['#ajax']['wrapper'],
+      'effect' => 'fade',
+    ];
+    $element['filefield_attach']['attach'] = [
+      '#name' => implode('_', $element['#parents']) . '_attach',
+      '#type' => 'submit',
+      '#value' => t('Attach'),
+      '#validate' => [],
+      '#submit' => ['filefield_sources_field_submit'],
+      '#limit_validation_errors' => [$element['#parents']],
+      '#ajax' => $ajax_settings,
+    ];
+
+    return $element;
+  }
+
+  /**
+   * Theme the output of the attach element.
+   */
+  public static function element($variables) {
+    $element = $variables['element'];
+    if (isset($element['attach_message'])) {
+      $output = $element['attach_message']['#markup'];
+    }
+    elseif (isset($element['filename'])) {
+      // Get rendered options.
+      $options = form_select_options($element['filename']);
+      $option_output = '';
+      foreach ($options as $key => $value) {
+        $option_output .= '<option value=' . $value["value"] . '>' . $value["label"] . '</option>';
+      }
+      // Get rendered select.
+      $size = !empty($element['filename']['#size']) ? ' size="' . $element['filename']['#size'] . '"' : '';
+      $element['filename']['#attributes']['class'][] = 'form-select';
+      $multiple = !empty($element['#multiple']);
+      $output = '<select name="' . $element['filename']['#name'] . '' . ($multiple ? '[]' : '') . '"' . ($multiple ? ' multiple="multiple" ' : '') . new Attribute($element['filename']['#attributes']) . ' id="' . $element['filename']['#id'] . '" ' . $size . '>' . $option_output . '</select>';
+    }
+
+    $output .= drupal_render($element['attach']);
+    $element['#children'] = $output;
+    $element['#theme_wrappers'] = array('form_element');
+    return '<div class="filefield-source filefield-source-attach clear-block">' . drupal_render($element) . '</div>';
+  }
+
+  /**
+   * Get directory from settings.
+   *
+   * @param array $settings
+   *   Attach source's settings.
+   * @param object $account
+   *   User to replace token.
+   *
+   * @return string
+   *   Path that contains files to attach.
+   */
+  protected static function getDirectory(array $settings, $account = NULL) {
+    $account = isset($account) ? $account : \Drupal::currentUser();
+    $path = $settings['path'];
+    $absolute = !empty($settings['absolute']);
+
+    // Replace user level tokens.
+    // Node level tokens require a lot of complexity like temporary storage
+    // locations when values don't exist. See the filefield_paths module.
+    if (\Drupal::moduleHandler()->moduleExists('token')) {
+      $token = \Drupal::token();
+      $path = $token->replace($path, array('user' => $account));
+    }
+
+    return $absolute ? $path : file_default_scheme() . '://' . $path;
+  }
+
+  /**
+   * Get attach options.
+   *
+   * @param string $path
+   *   Path to scan files.
+   * @param string $extensions
+   *   Path to scan files.
+   *
+   * @return array
+   *   List of options.
+   */
+  protected static function getAttachOptions($path, $extensions = FALSE) {
+    if (!file_prepare_directory($path, FILE_CREATE_DIRECTORY)) {
+      drupal_set_message(t('Specified file attach path %path must exist or be writable.', array('%path' => $path)), 'error');
+      return FALSE;
+    }
+
+    $options = array();
+    $pattern = !empty($extensions) ? '/\.(' . strtr($extensions, ' ', '|') . ')$/' : '/.*/';
+    $files = file_scan_directory($path, $pattern);
+
+    if (count($files)) {
+      $options = array('' => t('-- Select file --'));
+      foreach ($files as $file) {
+        $options[$file->uri] = str_replace($path . '/', '', $file->uri);
+      }
+      natcasesort($options);
+    }
+
+    return $options;
+  }
+
+  /**
+   * Implements hook_filefield_source_settings().
+   */
+  public static function settings(WidgetInterface $plugin) {
+    $settings = $plugin->getThirdPartySetting('filefield_sources', 'filefield_sources', array(
+      'source_attach' => array(
+        'path' => FILEFIELD_SOURCE_ATTACH_DEFAULT_PATH,
+        'absolute' => FILEFIELD_SOURCE_ATTACH_RELATIVE,
+        'attach_mode' => FILEFIELD_SOURCE_ATTACH_MODE_MOVE,
+      ),
+    ));
+
+    $return['source_attach'] = array(
+      '#title' => t('File attach settings'),
+      '#type' => 'details',
+      '#description' => t('File attach allows for selecting a file from a directory on the server, commonly used in combination with FTP.') . ' <strong>' . t('This file source will ignore file size checking when used.') . '</strong>',
+      '#element_validate' => array(array(get_called_class(), 'filePathValidate')),
+      '#weight' => 3,
+    );
+    $return['source_attach']['path'] = array(
+      '#type' => 'textfield',
+      '#title' => t('File attach path'),
+      '#default_value' => $settings['source_attach']['path'],
+      '#size' => 60,
+      '#maxlength' => 128,
+      '#description' => t('The directory within the <em>File attach location</em> that will contain attachable files.'),
+    );
+    if (\Drupal::moduleHandler()->moduleExists('token')) {
+      $return['source_attach']['tokens'] = array(
+        '#theme' => 'token_tree',
+        '#token_types' => array('user'),
+      );
+    }
+    $return['source_attach']['absolute'] = array(
+      '#type' => 'radios',
+      '#title' => t('File attach location'),
+      '#options' => array(
+        FILEFIELD_SOURCE_ATTACH_RELATIVE => t('Within the files directory'),
+        FILEFIELD_SOURCE_ATTACH_ABSOLUTE => t('Absolute server path'),
+      ),
+      '#default_value' => $settings['source_attach']['absolute'],
+      '#description' => t('The <em>File attach path</em> may be with the files directory (%file_directory) or from the root of your server. If an absolute path is used and it does not start with a "/" your path will be relative to your site directory: %realpath.', array('%file_directory' => drupal_realpath(file_default_scheme() . '://'), '%realpath' => realpath('./'))),
+    );
+    $return['source_attach']['attach_mode'] = array(
+      '#type' => 'radios',
+      '#title' => t('Attach method'),
+      '#options' => array(
+        FILEFIELD_SOURCE_ATTACH_MODE_MOVE => t('Move the file directly to the final location'),
+        FILEFIELD_SOURCE_ATTACH_MODE_COPY => t('Leave a copy of the file in the attach directory'),
+      ),
+      '#default_value' => isset($settings['source_attach']['attach_mode']) ? $settings['source_attach']['attach_mode'] : 'move',
+    );
+
+    return $return;
+  }
+
+  /**
+   * Validate file path.
+   *
+   * @param array $element
+   *   Form element.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   Form state.
+   * @param array $complete_form
+   *   Complete form.
+   */
+  public static function filePathValidate(array &$element, FormStateInterface $form_state, array &$complete_form) {
+    $parents = $element['#parents'];
+    array_pop($parents);
+    $input_exists = FALSE;
+
+    // Get input of the whole parent element.
+    $input = NestedArray::getValue($form_state->getValues(), $parents, $input_exists);
+    if ($input_exists) {
+      // Only validate if this source is enabled.
+      if (!$input['sources']['attach']) {
+        return;
+      }
+
+      // Strip slashes from the end of the file path.
+      $filepath = rtrim($element['path']['#value'], '\\/');
+      $form_state->setValueForElement($element['path'], $filepath);
+      $filepath = static::getDirectory($input['source_attach']);
+
+      // Check that the directory exists and is writable.
+      if (!file_prepare_directory($filepath, FILE_CREATE_DIRECTORY)) {
+        $form_state->setError($element['path'], t('Specified file attach path %path must exist or be writable.', array('%path' => $filepath)));
+      }
+    }
+  }
+
+}

+ 152 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Plugin/FilefieldSource/Clipboard.php

@@ -0,0 +1,152 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filefield_sources\Plugin\FilefieldSource\Clipboard.
+ */
+
+namespace Drupal\filefield_sources\Plugin\FilefieldSource;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\filefield_sources\FilefieldSourceInterface;
+use Drupal\Core\Site\Settings;
+
+/**
+ * A FileField source plugin to allow transfer of files through the clipboard.
+ *
+ * @FilefieldSource(
+ *   id = "clipboard",
+ *   name = @Translation("Paste from clipboard (<a href=""http://drupal.org/node/1775902"">limited browser support</a>)"),
+ *   label = @Translation("Clipboard"),
+ *   description = @Translation("Allow users to paste a file directly from the clipboard."),
+ *   weight = 1
+ * )
+ */
+class Clipboard implements FilefieldSourceInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function value(array &$element, &$input, FormStateInterface $form_state) {
+    if (isset($input['filefield_clipboard']['contents']) && strlen($input['filefield_clipboard']['contents']) > 0) {
+      // Check that the destination is writable.
+      $temporary_directory = 'temporary://';
+      if (!file_prepare_directory($temporary_directory, FILE_MODIFY_PERMISSIONS)) {
+        \Drupal::logger('filefield_sources')->log(E_NOTICE, 'The directory %directory is not writable, because it does not have the correct permissions set.', array('%directory' => drupal_realpath($temporary_directory)));
+        drupal_set_message(t('The file could not be transferred because the temporary directory is not writable.'), 'error');
+        return;
+      }
+      // Check that the destination is writable.
+      $directory = $element['#upload_location'];
+      $mode = Settings::get('file_chmod_directory', FILE_CHMOD_DIRECTORY);
+
+      // This first chmod check is for other systems such as S3, which don't
+      // work with file_prepare_directory().
+      if (!drupal_chmod($directory, $mode) && !file_prepare_directory($directory, FILE_CREATE_DIRECTORY)) {
+        $url = $input['filefield_clipboard']['filename'];
+        \Drupal::logger('filefield_sources')->log(E_NOTICE, 'File %file could not be copied, because the destination directory %destination is not configured correctly.', array('%file' => $url, '%destination' => drupal_realpath($directory)));
+        drupal_set_message(t('The specified file %file could not be copied, because the destination directory is not properly configured. This may be caused by a problem with file or directory permissions. More information is available in the system log.', array('%file' => $url)), 'error');
+        return;
+      }
+
+      // Split the file information in mimetype and base64 encoded binary.
+      $base64_data = $input['filefield_clipboard']['contents'];
+      $comma_position = strpos($base64_data, ',');
+      $semicolon_position = strpos($base64_data, ';');
+      $file_contents = base64_decode(substr($base64_data, $comma_position + 1));
+      $mimetype = substr($base64_data, 5, $semicolon_position - 5);
+
+      $extension = \Drupal::service('file.mime_type.guesser.extension')->convertMimeTypeToExtension($mimetype);
+
+      $filename = trim($input['filefield_clipboard']['filename']);
+      $filename = preg_replace('/\.[a-z0-9]{3,4}$/', '', $filename);
+      $filename = (empty($filename) ? 'paste_' . REQUEST_TIME : $filename) . '.' . $extension;
+      $filepath = file_create_filename($filename, $temporary_directory);
+
+      $copy_success = FALSE;
+      if ($fp = @fopen($filepath, 'w')) {
+        fwrite($fp, $file_contents);
+        fclose($fp);
+        $copy_success = TRUE;
+      }
+
+      if ($copy_success && $file = filefield_sources_save_file($filepath, $element['#upload_validators'], $element['#upload_location'])) {
+        if (!in_array($file->id(), $input['fids'])) {
+          $input['fids'][] = $file->id();
+        }
+      }
+
+      // Remove the temporary file generated from paste.
+      @unlink($filepath);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function process(array &$element, FormStateInterface $form_state, array &$complete_form) {
+    $element['filefield_clipboard'] = array(
+      '#weight' => 100.5,
+      '#theme' => 'filefield_sources_element',
+      '#source_id' => 'clipboard',
+      // Required for proper theming.
+      '#filefield_source' => TRUE,
+      '#filefield_sources_hint_text' => t('Enter filename then paste.'),
+      '#description' => filefield_sources_element_validation_help($element['#upload_validators']),
+    );
+
+    $element['filefield_clipboard']['capture'] = array(
+      '#type' => 'item',
+      '#markup' => '<div class="filefield-source-clipboard-capture" contenteditable="true"><span class="hint">example_filename.png</span></div> <span class="hint">' . t('ctrl + v') . '</span>',
+      '#description' => t('Enter a file name and paste an image from the clipboard. This feature only works in <a href="http://drupal.org/node/1775902">limited browsers</a>.'),
+    );
+
+    $element['filefield_clipboard']['filename'] = array(
+      '#type' => 'hidden',
+      '#attributes' => array('class' => array('filefield-source-clipboard-filename')),
+    );
+    $element['filefield_clipboard']['contents'] = array(
+      '#type' => 'hidden',
+      '#attributes' => array('class' => array('filefield-source-clipboard-contents')),
+    );
+
+    $class = '\Drupal\file\Element\ManagedFile';
+    $ajax_settings = [
+      'callback' => [$class, 'uploadAjaxCallback'],
+      'options' => [
+        'query' => [
+          'element_parents' => implode('/', $element['#array_parents']),
+        ],
+      ],
+      'wrapper' => $element['upload_button']['#ajax']['wrapper'],
+      'effect' => 'fade',
+      'progress' => [
+        'type' => 'throbber',
+        'message' => t('Transfering file...'),
+      ],
+    ];
+
+    $element['filefield_clipboard']['upload'] = [
+      '#name' => implode('_', $element['#parents']) . '_clipboard_upload_button',
+      '#type' => 'submit',
+      '#value' => t('Upload'),
+      '#attributes' => ['class' => ['js-hide']],
+      '#validate' => [],
+      '#submit' => ['filefield_sources_field_submit'],
+      '#limit_validation_errors' => [$element['#parents']],
+      '#ajax' => $ajax_settings,
+    ];
+
+    return $element;
+  }
+
+  /**
+   * Theme the output of the clipboard element.
+   */
+  public static function element($variables) {
+    $element = $variables['element'];
+
+    return '<div class="filefield-source filefield-source-clipboard clear-block">' . drupal_render_children($element) . '</div>';
+  }
+
+}

+ 216 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Plugin/FilefieldSource/Imce.php

@@ -0,0 +1,216 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filefield_sources\Plugin\FilefieldSource\Imce.
+ */
+
+namespace Drupal\filefield_sources\Plugin\FilefieldSource;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\filefield_sources\FilefieldSourceInterface;
+use Symfony\Component\Routing\Route;
+use Drupal\Core\Field\WidgetInterface;
+
+/**
+ * A FileField source plugin to allow referencing of files from IMCE.
+ *
+ * @FilefieldSource(
+ *   id = "imce",
+ *   name = @Translation("IMCE file browser"),
+ *   label = @Translation("File browser"),
+ *   description = @Translation("Select a file to use from a file browser."),
+ *   weight = -1
+ * )
+ */
+class Imce implements FilefieldSourceInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function value(array &$element, &$input, FormStateInterface $form_state) {
+    if (isset($input['filefield_imce']['imce_paths']) && $input['filefield_imce']['imce_paths'] != '') {
+      $instance = entity_load('field_config', $element['#entity_type'] . '.' . $element['#bundle'] . '.' . $element['#field_name']);
+      $field_settings = $instance->getSettings();
+      $scheme = $field_settings['uri_scheme'];
+      $imce_paths = explode(':', $input['filefield_imce']['imce_paths']);
+      $uris = [];
+
+      foreach ($imce_paths as $imce_path) {
+        //$wrapper = \Drupal::service('stream_wrapper_manager')->getViaScheme($scheme);
+        //$file_directory_prefix = $scheme == 'private' ? 'system/files' : $wrapper->getDirectoryPath();
+        //$uri = preg_replace('/^' . preg_quote(base_path() . $file_directory_prefix . '/', '/') . '/', $scheme . '://', $imce_path);
+        $uri = rawurldecode($scheme . '://' . $imce_path);
+        $uris[] = $uri;
+      }
+
+      // Resolve the file path to an FID.
+      $fids = db_select('file_managed', 'f')
+        ->condition('uri', $uris, 'IN')
+        ->fields('f', array('fid'))
+        ->execute()
+        ->fetchCol();
+      if ($fids) {
+        $files = file_load_multiple($fids);
+        foreach ($files as $file) {
+          if (filefield_sources_element_validate($element, $file, $form_state)) {
+            if (!in_array($file->id(), $input['fids'])) {
+              $input['fids'][] = $file->id();
+            }
+          }
+        }
+      }
+      else {
+        $form_state->setError($element, t('The selected file could not be used because the file does not exist in the database.'));
+      }
+      // No matter what happens, clear the value from the file path field.
+      $input['filefield_imce']['imce_paths'] = '';
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function process(array &$element, FormStateInterface $form_state, array &$complete_form) {
+    $instance = entity_load('field_config', $element['#entity_type'] . '.' . $element['#bundle'] . '.' . $element['#field_name']);
+
+    $element['filefield_imce'] = array(
+      '#weight' => 100.5,
+      '#theme' => 'filefield_sources_element',
+      '#source_id' => 'imce',
+      // Required for proper theming.
+      '#filefield_source' => TRUE,
+      '#description' => filefield_sources_element_validation_help($element['#upload_validators']),
+    );
+
+    $imce_url = \Drupal::url('filefield_sources.imce', array(
+      'entity_type' => $element['#entity_type'],
+      'bundle_name' => $element['#bundle'],
+      'field_name' => $element['#field_name'],
+    ),
+    array(
+      'query' => array(
+        'sendto' => 'imceFileField.sendto',
+        'fieldId' => $element['#attributes']['data-drupal-selector'] . '-filefield-imce',
+      ),
+    ));
+    $element['filefield_imce']['browse'] = array(
+      '#type' => 'markup',
+      '#markup' => '<span>' . t('No file selected') . '</span> (<a class="filefield-sources-imce-browse" href="' . $imce_url . '">' . t('browse') . '</a>)',
+    );
+
+    $element['#attached']['library'][] = 'imce/drupal.imce.filefield';
+    // Set the pre-renderer to conditionally disable the elements.
+    $element['#pre_render'][] = array(get_called_class(), 'preRenderWidget');
+
+    // Path input
+    $element['filefield_imce']['imce_paths'] = array(
+      '#type' => 'hidden',
+      // Reset value to prevent consistent errors
+      '#value' => '',
+    );
+
+    $class = '\Drupal\file\Element\ManagedFile';
+    $ajax_settings = [
+      'callback' => [$class, 'uploadAjaxCallback'],
+      'options' => [
+        'query' => [
+          'element_parents' => implode('/', $element['#array_parents']),
+        ],
+      ],
+      'wrapper' => $element['upload_button']['#ajax']['wrapper'],
+      'effect' => 'fade',
+    ];
+
+    $element['filefield_imce']['imce_button'] = array(
+      '#name' => implode('_', $element['#parents']) . '_imce_select',
+      '#type' => 'submit',
+      '#value' => t('Select'),
+      '#attributes' => ['class' => ['js-hide']],
+      '#validate' => [],
+      '#submit' => ['filefield_sources_field_submit'],
+      '#limit_validation_errors' => [$element['#parents']],
+      '#ajax' => $ajax_settings,
+    );
+
+    return $element;
+  }
+
+  /**
+   * Theme the output of the imce element.
+   */
+  public static function element($variables) {
+    $element = $variables['element'];
+
+    $output = drupal_render_children($element);
+    return '<div class="filefield-source filefield-source-imce clear-block">' . $output . '</div>';
+  }
+
+  /**
+   * Define routes for Imce source.
+   *
+   * @return array
+   *   Array of routes.
+   */
+  public static function routes() {
+    $routes = array();
+
+    $routes['filefield_sources.imce'] = new Route(
+      '/file/imce/{entity_type}/{bundle_name}/{field_name}',
+      array(
+        '_controller' => '\Drupal\filefield_sources\Controller\ImceController::page',
+        '_title' => 'File Manager',
+      ),
+      array(
+        '_access_filefield_sources_field' => 'TRUE',
+      )
+    );
+
+    return $routes;
+  }
+
+  /**
+   * Implements hook_filefield_source_settings().
+   */
+  public static function settings(WidgetInterface $plugin) {
+    $settings = $plugin->getThirdPartySetting('filefield_sources', 'filefield_sources', array(
+      'source_imce' => array(
+        'imce_mode' => 0,
+      ),
+    ));
+
+    $return['source_imce'] = array(
+      '#title' => t('IMCE file browser settings'),
+      '#type' => 'details',
+      '#access' => \Drupal::moduleHandler()->moduleExists('imce'),
+    );
+
+    // $imce_admin_url = \Drupal::url('imce.admin');
+    $imce_admin_url = 'admin/config/media/imce';
+    $return['source_imce']['imce_mode'] = array(
+      '#type' => 'radios',
+      '#title' => t('File browser mode'),
+      '#options' => array(
+        0 => t('Restricted: Users can only browse the field directory. No file operations are allowed.'),
+        1 => t('Full: Browsable directories are defined by <a href=":imce-admin-url">IMCE configuration profiles</a>. File operations are allowed.', array(':imce-admin-url' => $imce_admin_url)),
+      ),
+      '#default_value' => isset($settings['source_imce']['imce_mode']) ? $settings['source_imce']['imce_mode'] : 0,
+    );
+
+    return $return;
+
+  }
+
+  /**
+   * Pre-renders widget form.
+   */
+  public static function preRenderWidget($element) {
+    // Hide elements if there is already an uploaded file.
+    if (!empty($element['#value']['fids'])) {
+      $element['filefield_imce']['imce_paths']['#access'] = FALSE;
+      $element['filefield_imce']['imce_button']['#access'] = FALSE;
+    }
+    return $element;
+  }
+
+}

+ 239 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Plugin/FilefieldSource/Reference.php

@@ -0,0 +1,239 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filefield_sources\Plugin\FilefieldSource\Reference.
+ */
+
+namespace Drupal\filefield_sources\Plugin\FilefieldSource;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\filefield_sources\FilefieldSourceInterface;
+use Symfony\Component\Routing\Route;
+use Drupal\Core\Field\WidgetInterface;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Request;
+use Drupal\Component\Utility\Unicode;
+use Drupal\Component\Utility\Html;
+
+/**
+ * A FileField source plugin to allow referencing of existing files.
+ *
+ * @FilefieldSource(
+ *   id = "reference",
+ *   name = @Translation("Autocomplete reference textfield"),
+ *   label = @Translation("Reference existing"),
+ *   description = @Translation("Reuse an existing file by entering its file name."),
+ *   weight = 1
+ * )
+ */
+class Reference implements FilefieldSourceInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function value(array &$element, &$input, FormStateInterface $form_state) {
+    if (isset($input['filefield_reference']['autocomplete']) && strlen($input['filefield_reference']['autocomplete']) > 0 && $input['filefield_reference']['autocomplete'] != FILEFIELD_SOURCE_REFERENCE_HINT_TEXT) {
+      $matches = array();
+      if (preg_match('/\[fid:(\d+)\]/', $input['filefield_reference']['autocomplete'], $matches)) {
+        $fid = $matches[1];
+        if ($file = file_load($fid)) {
+
+          // Remove file size restrictions, since the file already exists on
+          // disk.
+          if (isset($element['#upload_validators']['file_validate_size'])) {
+            unset($element['#upload_validators']['file_validate_size']);
+          }
+
+          // Check that the user has access to this file through
+          // hook_download().
+          if (!$file->access('download')) {
+            $form_state->setError($element, t('You do not have permission to use the selected file.'));
+          }
+          elseif (filefield_sources_element_validate($element, (object) $file, $form_state)) {
+            if (!in_array($file->id(), $input['fids'])) {
+              $input['fids'][] = $file->id();
+            }
+          }
+        }
+        else {
+          $form_state->setError($element, t('The referenced file could not be used because the file does not exist in the database.'));
+        }
+      }
+      // No matter what happens, clear the value from the autocomplete.
+      $input['filefield_reference']['autocomplete'] = '';
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function process(array &$element, FormStateInterface $form_state, array &$complete_form) {
+
+    $element['filefield_reference'] = array(
+      '#weight' => 100.5,
+      '#theme' => 'filefield_sources_element',
+      '#source_id' => 'reference',
+      // Required for proper theming.
+      '#filefield_source' => TRUE,
+      '#filefield_sources_hint_text' => FILEFIELD_SOURCE_REFERENCE_HINT_TEXT,
+    );
+
+    $autocomplete_route_parameters = array(
+      'entity_type' => $element['#entity_type'],
+      'bundle_name' => $element['#bundle'],
+      'field_name' => $element['#field_name'],
+    );
+
+    $element['filefield_reference']['autocomplete'] = array(
+      '#type' => 'textfield',
+      '#autocomplete_route_name' => 'filefield_sources.autocomplete',
+      '#autocomplete_route_parameters' => $autocomplete_route_parameters,
+      '#description' => filefield_sources_element_validation_help($element['#upload_validators']),
+    );
+
+    $class = '\Drupal\file\Element\ManagedFile';
+    $ajax_settings = [
+      'callback' => [$class, 'uploadAjaxCallback'],
+      'options' => [
+        'query' => [
+          'element_parents' => implode('/', $element['#array_parents']),
+        ],
+      ],
+      'wrapper' => $element['upload_button']['#ajax']['wrapper'],
+      'effect' => 'fade',
+    ];
+
+    $element['filefield_reference']['select'] = [
+      '#name' => implode('_', $element['#parents']) . '_autocomplete_select',
+      '#type' => 'submit',
+      '#value' => t('Select'),
+      '#validate' => [],
+      '#submit' => ['filefield_sources_field_submit'],
+      '#limit_validation_errors' => [$element['#parents']],
+      '#ajax' => $ajax_settings,
+    ];
+
+    return $element;
+  }
+
+  /**
+   * Theme the output of the reference element.
+   */
+  public static function element($variables) {
+    $element = $variables['element'];
+
+    $element['autocomplete']['#field_suffix'] = drupal_render($element['select']);
+    return '<div class="filefield-source filefield-source-reference clear-block">' . drupal_render($element['autocomplete']) . '</div>';
+  }
+
+  /**
+   * Menu callback; autocomplete.js callback to return a list of files.
+   */
+  public static function autocomplete(Request $request, $entity_type, $bundle_name, $field_name) {
+    $matches = array();
+    $string = Unicode::strtolower($request->query->get('q'));
+
+    if (isset($string)) {
+      $widget = entity_get_form_display($entity_type, $bundle_name, 'default')->getComponent($field_name);
+      if ($widget) {
+        // // If we are looking at a single field, cache its settings, in case we want to search all fields.
+        $setting_autocomplete = $widget['third_party_settings']['filefield_sources']['filefield_sources']['source_reference']['autocomplete'];
+        $setting_search_all_fields = $widget['third_party_settings']['filefield_sources']['filefield_sources']['source_reference']['search_all_fields'];
+      }
+
+      $field_definition = entity_load('field_config', $entity_type . '.' . $bundle_name . '.' . $field_name);
+      if (!isset($field_definition) || $setting_search_all_fields) {
+        $field_definitions = \Drupal::entityManager()->getStorage('field_config')->loadByProperties(array('type' => array('file', 'image')));
+      }
+      else {
+        $field_definitions = array($field_definition);
+      }
+
+      foreach ($field_definitions as $field_definition) {
+        $handler = \Drupal::getContainer()->get('plugin.manager.entity_reference_selection')->getSelectionHandler($field_definition);
+
+        // If we are searching all fields, use the autocomplete settings from the source field.
+        $match_operator = empty($setting_autocomplete) ? 'STARTS_WITH' : 'CONTAINS';
+        // Get an array of matching entities.
+        $entity_labels = $handler->getReferenceableEntities($string, $match_operator, 10);
+
+        // Loop through the entities and convert them into autocomplete output.
+        foreach ($entity_labels as $values) {
+          foreach ($values as $entity_id => $label) {
+            $key = "$label [fid:$entity_id]";
+            // Strip things like starting/trailing white spaces, line breaks and
+            // tags.
+            $key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(Html::decodeEntities(strip_tags($key)))));
+            // Names containing commas or quotes must be wrapped in quotes.
+            $matches[] = array('value' => $key, 'label' => $label);
+          }
+        }
+      }
+    }
+
+    return new JsonResponse($matches);
+  }
+
+  /**
+   * Define routes for Reference source.
+   *
+   * @return array
+   *   Array of routes.
+   */
+  public static function routes() {
+    $routes = array();
+
+    $routes['filefield_sources.autocomplete'] = new Route(
+      '/file/reference/{entity_type}/{bundle_name}/{field_name}',
+      array(
+        '_controller' => get_called_class() . '::autocomplete',
+      ),
+      array(
+        '_access_filefield_sources_field' => 'TRUE',
+      )
+    );
+
+    return $routes;
+  }
+
+  /**
+   * Implements hook_filefield_source_settings().
+   */
+  public static function settings(WidgetInterface $plugin) {
+    $settings = $plugin->getThirdPartySetting('filefield_sources', 'filefield_sources', array(
+      'source_reference' => array(
+        'autocomplete' => FILEFIELD_SOURCE_REFERENCE_MATCH_STARTS_WITH,
+        'search_all_fields' => FILEFIELD_SOURCE_REFERENCE_SEARCH_ALL_NO,
+      ),
+    ));
+
+    $return['source_reference'] = array(
+      '#title' => t('Autocomplete reference options'),
+      '#type' => 'details',
+    );
+
+    $return['source_reference']['autocomplete'] = array(
+      '#title' => t('Match file name'),
+      '#options' => array(
+        FILEFIELD_SOURCE_REFERENCE_MATCH_STARTS_WITH => t('Starts with'),
+        FILEFIELD_SOURCE_REFERENCE_MATCH_CONTAINS => t('Contains'),
+      ),
+      '#type' => 'radios',
+      '#default_value' => isset($settings['source_reference']['autocomplete']) ? $settings['source_reference']['autocomplete'] : FILEFIELD_SOURCE_REFERENCE_MATCH_STARTS_WITH,
+    );
+
+    $return['source_reference']['search_all_fields'] = array(
+      '#title' => t('Search all file fields'),
+      '#options' => array(
+        FILEFIELD_SOURCE_REFERENCE_SEARCH_ALL_NO => t('No (only fields with the same field base will be searched)'),
+        FILEFIELD_SOURCE_REFERENCE_SEARCH_ALL_YES => t('Yes (all file fields will be searched, regardless of type)'),
+      ),
+      '#type' => 'radios',
+      '#default_value' => isset($settings['source_reference']['search_all_fields']) ? $settings['source_reference']['search_all_fields'] : FILEFIELD_SOURCE_REFERENCE_SEARCH_ALL_NO,
+     );
+
+    return $return;
+  }
+
+}

+ 400 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Plugin/FilefieldSource/Remote.php

@@ -0,0 +1,400 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filefield_sources\Plugin\FilefieldSource\Remote.
+ */
+
+namespace Drupal\filefield_sources\Plugin\FilefieldSource;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\filefield_sources\FilefieldSourceInterface;
+use Symfony\Component\Routing\Route;
+use Drupal\Core\Field\WidgetInterface;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Site\Settings;
+use Drupal\Component\Utility\Unicode;
+
+/**
+ * A FileField source plugin to allow downloading a file from a remote server.
+ *
+ * @FilefieldSource(
+ *   id = "remote",
+ *   name = @Translation("Remote URL textfield"),
+ *   label = @Translation("Remote URL"),
+ *   description = @Translation("Download a file from a remote server.")
+ * )
+ */
+class Remote implements FilefieldSourceInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function value(array &$element, &$input, FormStateInterface $form_state) {
+    if (isset($input['filefield_remote']['url']) && strlen($input['filefield_remote']['url']) > 0 && UrlHelper::isValid($input['filefield_remote']['url']) && $input['filefield_remote']['url'] != FILEFIELD_SOURCE_REMOTE_HINT_TEXT) {
+      $field = entity_load('field_config', $element['#entity_type'] . '.' . $element['#bundle'] . '.' . $element['#field_name']);
+      $url = $input['filefield_remote']['url'];
+
+      // Check that the destination is writable.
+      $temporary_directory = 'temporary://';
+      if (!file_prepare_directory($temporary_directory, FILE_MODIFY_PERMISSIONS)) {
+        \Drupal::logger('filefield_sources')->log(E_NOTICE, 'The directory %directory is not writable, because it does not have the correct permissions set.', array('%directory' => drupal_realpath($temporary_directory)));
+        drupal_set_message(t('The file could not be transferred because the temporary directory is not writable.'), 'error');
+        return;
+      }
+
+      // Check that the destination is writable.
+      $directory = $element['#upload_location'];
+      $mode = Settings::get('file_chmod_directory', FILE_CHMOD_DIRECTORY);
+
+      // This first chmod check is for other systems such as S3, which don't
+      // work with file_prepare_directory().
+      if (!drupal_chmod($directory, $mode) && !file_prepare_directory($directory, FILE_CREATE_DIRECTORY)) {
+        \Drupal::logger('filefield_sources')->log(E_NOTICE, 'File %file could not be copied, because the destination directory %destination is not configured correctly.', array('%file' => $url, '%destination' => drupal_realpath($directory)));
+        drupal_set_message(t('The specified file %file could not be copied, because the destination directory is not properly configured. This may be caused by a problem with file or directory permissions. More information is available in the system log.', array('%file' => $url)), 'error');
+        return;
+      }
+
+      // Check the headers to make sure it exists and is within the allowed
+      // size.
+      $ch = curl_init();
+      curl_setopt($ch, CURLOPT_URL, $url);
+      curl_setopt($ch, CURLOPT_HEADER, TRUE);
+      curl_setopt($ch, CURLOPT_NOBODY, TRUE);
+      curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
+      curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(get_called_class(), 'parseHeader'));
+      // Causes a warning if PHP safe mode is on.
+      @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
+      curl_exec($ch);
+      $info = curl_getinfo($ch);
+      if ($info['http_code'] != 200) {
+        curl_setopt($ch, CURLOPT_HTTPGET, TRUE);
+        $file_contents = curl_exec($ch);
+        $info = curl_getinfo($ch);
+      }
+      curl_close($ch);
+
+      if ($info['http_code'] != 200) {
+        switch ($info['http_code']) {
+          case 403:
+            $form_state->setError($element, t('The remote file could not be transferred because access to the file was denied.'));
+            break;
+
+          case 404:
+            $form_state->setError($element, t('The remote file could not be transferred because it was not found.'));
+            break;
+
+          default:
+            $form_state->setError($element, t('The remote file could not be transferred due to an HTTP error (@code).', array('@code' => $info['http_code'])));
+        }
+        return;
+      }
+
+      // Update the $url variable to reflect any redirects.
+      $url = $info['url'];
+      $url_info = parse_url($url);
+
+      // Determine the proper filename by reading the filename given in the
+      // Content-Disposition header. If the server fails to send this header,
+      // fall back on the basename of the URL.
+      //
+      // We prefer to use the Content-Disposition header, because we can then
+      // use URLs like http://example.com/get_file/23 which would otherwise be
+      // rejected because the URL basename lacks an extension.
+      $filename = static::filename();
+      if (empty($filename)) {
+        $filename = rawurldecode(basename($url_info['path']));
+      }
+
+      $pathinfo = pathinfo($filename);
+
+      // Create the file extension from the MIME header if all else has failed.
+      if (empty($pathinfo['extension']) && $extension = static::mimeExtension()) {
+        $filename = $filename . '.' . $extension;
+        $pathinfo = pathinfo($filename);
+      }
+
+      $filename = filefield_sources_clean_filename($filename, $field->getSetting('file_extensions'));
+      $filepath = file_create_filename($filename, $temporary_directory);
+
+      if (empty($pathinfo['extension'])) {
+        $form_state->setError($element, t('The remote URL must be a file and have an extension.'));
+        return;
+      }
+
+      // Perform basic extension check on the file before trying to transfer.
+      $extensions = $field->getSetting('file_extensions');
+      $regex = '/\.(' . preg_replace('/[ +]/', '|', preg_quote($extensions)) . ')$/i';
+      if (!empty($extensions) && !preg_match($regex, $filename)) {
+        $form_state->setError($element, t('Only files with the following extensions are allowed: %files-allowed.', array('%files-allowed' => $extensions)));
+        return;
+      }
+
+      // Check file size based off of header information.
+      if (!empty($element['#upload_validators']['file_validate_size'][0])) {
+        $max_size = $element['#upload_validators']['file_validate_size'][0];
+        $file_size = $info['download_content_length'];
+        if ($file_size > $max_size) {
+          $form_state->setError($element, t('The remote file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size($file_size), '%maxsize' => format_size($max_size))));
+          return;
+        }
+      }
+
+      // Set progress bar information.
+      $options = array(
+        'key' => $element['#entity_type'] . '_' . $element['#bundle'] . '_' . $element['#field_name'] . '_' . $element['#delta'],
+        'filepath' => $filepath,
+      );
+      static::setTransferOptions($options);
+
+      $transfer_success = FALSE;
+      // If we've already downloaded the entire file because the
+      // header-retrieval failed, just ave the contents we have.
+      if (isset($file_contents)) {
+        if ($fp = @fopen($filepath, 'w')) {
+          fwrite($fp, $file_contents);
+          fclose($fp);
+          $transfer_success = TRUE;
+        }
+      }
+      // If we don't have the file contents, download the actual file.
+      else {
+        $ch = curl_init();
+        curl_setopt($ch, CURLOPT_URL, $url);
+        curl_setopt($ch, CURLOPT_HEADER, FALSE);
+        curl_setopt($ch, CURLOPT_WRITEFUNCTION, array(get_called_class(), 'curlWrite'));
+        // Causes a warning if PHP safe mode is on.
+        @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
+        $transfer_success = curl_exec($ch);
+        curl_close($ch);
+      }
+      if ($transfer_success && $file = filefield_sources_save_file($filepath, $element['#upload_validators'], $element['#upload_location'])) {
+        if (!in_array($file->id(), $input['fids'])) {
+          $input['fids'][] = $file->id();
+        }
+      }
+
+      // Delete the temporary file.
+      @unlink($filepath);
+    }
+  }
+
+  /**
+   * Set a transfer key that can be retreived by the progress function.
+   */
+  protected static function setTransferOptions($options = NULL) {
+    static $current = FALSE;
+    if (isset($options)) {
+      $current = $options;
+    }
+    return $current;
+  }
+
+  /**
+   * Get a transfer key that can be retrieved by the progress function.
+   */
+  protected static function getTransferOptions() {
+    return static::setTransferOptions();
+  }
+
+  /**
+   * Save the file to disk. Also updates progress bar.
+   */
+  protected static function curlWrite(&$ch, $data) {
+    $progress_update = 0;
+    $options = static::getTransferOptions();
+
+    // Get the current progress and update the progress value.
+    // Only update every 64KB to reduce Drupal::cache()->set() calls.
+    // cURL usually writes in 16KB chunks.
+    if (curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD) / 65536 > $progress_update) {
+      $progress_update++;
+      $progress = array(
+        'current' => curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD),
+        'total' => curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD),
+      );
+      // Set a cache so that we can retrieve this value from the progress bar.
+      $cid = 'filefield_transfer:' . session_id() . ':' . $options['key'];
+      if ($progress['current'] != $progress['total']) {
+        \Drupal::cache()->set($cid, $progress, time() + 300);
+      }
+      else {
+        \Drupal::cache()->delete($cid);
+      }
+    }
+
+    $data_length = 0;
+    if ($fp = @fopen($options['filepath'], 'a')) {
+      fwrite($fp, $data);
+      fclose($fp);
+      $data_length = strlen($data);
+    }
+
+    return $data_length;
+  }
+
+  /**
+   * Parse cURL header and record the filename specified in Content-Disposition.
+   */
+  protected static function parseHeader(&$ch, $header) {
+    if (preg_match('/Content-Disposition:.*?filename="(.+?)"/', $header, $matches)) {
+      // Content-Disposition: attachment; filename="FILE NAME HERE"
+      static::filename($matches[1]);
+    }
+    elseif (preg_match('/Content-Disposition:.*?filename=([^; ]+)/', $header, $matches)) {
+      // Content-Disposition: attachment; filename=file.ext
+      $uri = trim($matches[1]);
+      static::filename($uri);
+    }
+    elseif (preg_match('/Content-Type:[ ]*([a-z0-9_\-]+\/[a-z0-9_\-]+)/i', $header, $matches)) {
+      $mime_type = $matches[1];
+      static::mimeExtension($mime_type);
+    }
+
+    // This is required by cURL.
+    return strlen($header);
+  }
+
+  /**
+   * Get/set the remote file extension in a static variable.
+   */
+  protected static function mimeExtension($curl_mime_type = NULL) {
+    static $extension = NULL;
+    $mimetype = Unicode::strtolower($curl_mime_type);
+    $result = \Drupal::service('file.mime_type.guesser.extension')->convertMimeTypeToMostCommonExtension($mimetype);
+    if ($result) {
+      $extension = $result;
+    }
+    return $extension;
+  }
+
+  /**
+   * Get/set the remote file name in a static variable.
+   */
+  protected static function filename($curl_filename = NULL) {
+    static $filename = NULL;
+    if (isset($curl_filename)) {
+      $filename = $curl_filename;
+    }
+    return $filename;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function process(array &$element, FormStateInterface $form_state, array &$complete_form) {
+
+    $element['filefield_remote'] = array(
+      '#weight' => 100.5,
+      '#theme' => 'filefield_sources_element',
+      '#source_id' => 'remote',
+       // Required for proper theming.
+      '#filefield_source' => TRUE,
+      '#filefield_sources_hint_text' => FILEFIELD_SOURCE_REMOTE_HINT_TEXT,
+    );
+
+    $element['filefield_remote']['url'] = array(
+      '#type' => 'textfield',
+      '#description' => filefield_sources_element_validation_help($element['#upload_validators']),
+      '#maxlength' => NULL,
+    );
+
+    $class = '\Drupal\file\Element\ManagedFile';
+    $ajax_settings = [
+      'callback' => [$class, 'uploadAjaxCallback'],
+      'options' => [
+        'query' => [
+          'element_parents' => implode('/', $element['#array_parents']),
+        ],
+      ],
+      'wrapper' => $element['upload_button']['#ajax']['wrapper'],
+      'effect' => 'fade',
+      'progress' => [
+        'type' => 'bar',
+        'path' => 'file/remote/progress/' . $element['#entity_type'] . '/' . $element['#bundle'] . '/' . $element['#field_name'] . '/' . $element['#delta'],
+        'message' => t('Starting transfer...'),
+      ],
+    ];
+
+    $element['filefield_remote']['transfer'] = [
+      '#name' => implode('_', $element['#parents']) . '_transfer',
+      '#type' => 'submit',
+      '#value' => t('Transfer'),
+      '#validate' => array(),
+      '#submit' => ['filefield_sources_field_submit'],
+      '#limit_validation_errors' => [$element['#parents']],
+      '#ajax' => $ajax_settings,
+    ];
+
+    return $element;
+  }
+
+  /**
+   * Theme the output of the remote element.
+   */
+  public static function element($variables) {
+    $element = $variables['element'];
+
+    $element['url']['#field_suffix'] = drupal_render($element['transfer']);
+    return '<div class="filefield-source filefield-source-remote clear-block">' . drupal_render($element['url']) . '</div>';
+  }
+
+  /**
+   * Menu callback; progress.js callback to return upload progress.
+   */
+  public static function progress($entity_type, $bundle_name, $field_name, $delta) {
+    $key = $entity_type . '_' . $bundle_name . '_' . $field_name . '_' . $delta;
+    $progress = array(
+      'message' => t('Starting transfer...'),
+      'percentage' => -1,
+    );
+
+    if ($cache = \Drupal::cache()->get('filefield_transfer:' . session_id() . ':' . $key)) {
+      $current = $cache->data['current'];
+      $total = $cache->data['total'];
+      $progress['message'] = t('Transferring... (@current of @total)', array('@current' => format_size($current), '@total' => format_size($total)));
+      $progress['percentage'] = round(100 * $current / $total);
+    }
+
+    return new JsonResponse($progress);
+  }
+
+  /**
+   * Define routes for Remote source.
+   *
+   * @return array
+   *   Array of routes.
+   */
+  public static function routes() {
+    $routes = array();
+
+    $routes['filefield_sources.remote'] = new Route(
+      '/file/remote/progress/{entity_type}/{bundle_name}/{field_name}/{delta}',
+      array(
+        '_controller' => get_called_class() . '::progress',
+      ),
+      array(
+        '_access' => 'TRUE',
+      )
+    );
+
+    return $routes;
+  }
+
+  /**
+   * Implements hook_filefield_source_settings().
+   */
+  public static function settings(WidgetInterface $plugin) {
+    $return = array();
+
+    // Add settings to the FileField widget form.
+    if (!filefield_sources_curl_enabled()) {
+      drupal_set_message(t('<strong>Filefield sources:</strong> remote plugin will be disabled without php-curl extension.'), 'warning');
+    }
+
+    return $return;
+
+  }
+
+}

+ 108 - 0
sites/all/modules/contrib/fields/filefield_sources/src/ProxyClass/File/MimeType/ExtensionMimeTypeGuesser.php

@@ -0,0 +1,108 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filefield_sources\ProxyClass\File\MimeType\ExtensionMimeTypeGuesser.
+ */
+
+/**
+ * This file was generated via php core/scripts/generate-proxy-class.php 'Drupal\filefield_sources\File\MimeType\ExtensionMimeTypeGuesser' "modules/filefield_sources/src".
+ */
+
+namespace Drupal\filefield_sources\ProxyClass\File\MimeType {
+
+    /**
+     * Provides a proxy class for \Drupal\filefield_sources\File\MimeType\ExtensionMimeTypeGuesser.
+     *
+     * @see \Drupal\Component\ProxyBuilder
+     */
+    class ExtensionMimeTypeGuesser implements \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface
+    {
+
+        use \Drupal\Core\DependencyInjection\DependencySerializationTrait;
+
+        /**
+         * The id of the original proxied service.
+         *
+         * @var string
+         */
+        protected $drupalProxyOriginalServiceId;
+
+        /**
+         * The real proxied service, after it was lazy loaded.
+         *
+         * @var \Drupal\filefield_sources\File\MimeType\ExtensionMimeTypeGuesser
+         */
+        protected $service;
+
+        /**
+         * The service container.
+         *
+         * @var \Symfony\Component\DependencyInjection\ContainerInterface
+         */
+        protected $container;
+
+        /**
+         * Constructs a ProxyClass Drupal proxy object.
+         *
+         * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+         *   The container.
+         * @param string $drupal_proxy_original_service_id
+         *   The service ID of the original service.
+         */
+        public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container, $drupal_proxy_original_service_id)
+        {
+            $this->container = $container;
+            $this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id;
+        }
+
+        /**
+         * Lazy loads the real service from the container.
+         *
+         * @return object
+         *   Returns the constructed real service.
+         */
+        protected function lazyLoadItself()
+        {
+            if (!isset($this->service)) {
+                $this->service = $this->container->get($this->drupalProxyOriginalServiceId);
+            }
+
+            return $this->service;
+        }
+
+        /**
+         * {@inheritdoc}
+         */
+        public function convertMimeTypeToExtension($mimetype)
+        {
+            return $this->lazyLoadItself()->convertMimeTypeToExtension($mimetype);
+        }
+
+        /**
+         * {@inheritdoc}
+         */
+        public function convertMimeTypeToMostCommonExtension($mimetype)
+        {
+            return $this->lazyLoadItself()->convertMimeTypeToMostCommonExtension($mimetype);
+        }
+
+        /**
+         * {@inheritdoc}
+         */
+        public function guess($path)
+        {
+            return $this->lazyLoadItself()->guess($path);
+        }
+
+        /**
+         * {@inheritdoc}
+         */
+        public function setMapping(array $mapping = NULL)
+        {
+            return $this->lazyLoadItself()->setMapping($mapping);
+        }
+
+    }
+
+}

+ 35 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Routing/FilefieldSourcesRoutes.php

@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filefield_sources\Routing\FilefieldSourcesRoutes.
+ */
+
+namespace Drupal\filefield_sources\Routing;
+
+/**
+ * Defines a route subscriber to register a url for serving filefield sources.
+ */
+class FilefieldSourcesRoutes {
+
+  /**
+   * Returns an array of route objects.
+   *
+   * @return \Symfony\Component\Routing\Route[]
+   *   An array of route objects.
+   */
+  public function routes() {
+    $routes = array();
+
+    foreach (\Drupal::service('filefield_sources')->getDefinitions() as $definition) {
+      // Get routes defined by each plugin.
+      $callback = array($definition['class'], 'routes');
+      if (is_callable($callback)) {
+        $routes = array_merge($routes, call_user_func($callback));
+      }
+    }
+
+    return $routes;
+  }
+
+}

+ 168 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Tests/AttachSourceTest.php

@@ -0,0 +1,168 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\filefield_sources\Tests\AttachSourceTest.
+ */
+
+namespace Drupal\filefield_sources\Tests;
+
+use Drupal\Component\Render\PlainTextOutput;
+
+/**
+ * Tests the attach source.
+ *
+ * @group filefield_sources
+ */
+class AttachSourceTest extends FileFieldSourcesTestBase {
+
+  /**
+   * Check to see if a option is present.
+   *
+   * @param string $uri
+   *   The option to check.
+   *
+   * @return bool
+   *   TRUE if the option is present, FALSE otherwise.
+   */
+  public function isOptionPresent($uri) {
+    $options = $this->xpath('//select[@name=:name]/option[@value=:option]', array(
+      ':name' => $this->fieldName . '[0][filefield_attach][filename]',
+      ':option' => $uri,
+    ));
+    return isset($options[0]);
+  }
+
+  /**
+   * Check to see if can attach file.
+   *
+   * @param object $file
+   *   File to attach.
+   */
+  public function assertCanAttachFile($file) {
+    // Ensure option is present.
+    $this->assertTrue($this->isOptionPresent($file->uri), 'File option is present.');
+
+    // Ensure empty message is not present.
+    $this->assertNoText('There currently are no files to attach.', "Empty message is not present.");
+
+    // Attach button is always present.
+    $this->assertFieldByXpath('//input[@type="submit"]', t('Attach'), 'Attach button is present.');
+  }
+
+  /**
+   * Check to see if can attach file.
+   *
+   * @param object $file
+   *   File to attach.
+   */
+  public function assertCanNotAttachFile($file) {
+    // Ensure option is not present.
+    $this->assertFalse($this->isOptionPresent($file->uri), 'File option is not present.');
+
+    // Ensure empty message is present.
+    $this->assertText('There currently are no files to attach.', "Empty message is present.");
+
+    // Attach button is always present.
+    $this->assertFieldByXpath('//input[@type="submit"]', t('Attach'), 'Attach button is present.');
+  }
+
+  /**
+   * Tests move file from relative path.
+   *
+   * Default settings: Move file from 'public://file_attach' to 'public://'.
+   */
+  public function testMoveFileFromRelativePath() {
+    $uri_scheme = $this->getFieldSetting('uri_scheme');
+    $path = $uri_scheme . '://' . FILEFIELD_SOURCE_ATTACH_DEFAULT_PATH . '/';
+
+    // Create test file.
+    $file = $this->createTemporaryFile($path);
+    $dest_uri = $this->getDestinationUri($file, $uri_scheme);
+
+    $this->enableSources(array(
+      'attach' => TRUE,
+    ));
+
+    $this->assertCanAttachFile($file);
+
+    // Upload a file.
+    $this->uploadFileByAttachSource($file->uri, $file->filename, 0);
+
+    // We can only attach one file on single value field.
+    $this->assertNoFieldByXPath('//input[@type="submit"]', t('Attach'), 'After uploading a file, "Attach" button is no longer displayed.');
+
+    // Ensure file is moved.
+    $this->assertFalse(is_file($file->uri), 'Source file has been removed.');
+    $this->assertTrue(is_file($dest_uri), 'Destination file has been created.');
+
+    $this->removeFile($file->filename, 0);
+
+    $this->assertCanNotAttachFile($file);
+  }
+
+  /**
+   * Calculate custom absolute path.
+   */
+  public function getCustomAttachPath() {
+    $path = drupal_realpath($this->getFieldSetting('uri_scheme') . '://');
+    $path = str_replace(realpath('./'), '', $path);
+    $path = ltrim($path, '/');
+    $path = $path . '/custom_file_attach/';
+    return $path;
+  }
+
+  /**
+   * Tests copy file from absolute path.
+   *
+   * Copy file from 'sites/default/files/custom_file_attach' to 'public://'.
+   */
+  public function testCopyFileFromAbsolutePath() {
+    $uri_scheme = $this->getFieldSetting('uri_scheme');
+    $path = $this->getCustomAttachPath();
+
+    // Create test file.
+    $file = $this->createTemporaryFile($path);
+    $dest_uri = $this->getDestinationUri($file, $uri_scheme);
+
+    // Change settings.
+    $this->updateFilefieldSourcesSettings('source_attach', 'path', $path);
+    $this->updateFilefieldSourcesSettings('source_attach', 'absolute', FILEFIELD_SOURCE_ATTACH_ABSOLUTE);
+    $this->updateFilefieldSourcesSettings('source_attach', 'attach_mode', FILEFIELD_SOURCE_ATTACH_MODE_COPY);
+
+    $this->enableSources(array(
+      'attach' => TRUE,
+    ));
+
+    $this->assertCanAttachFile($file);
+
+    // Upload a file.
+    $this->uploadFileByAttachSource($file->uri, $file->filename, 0);
+
+    // We can only attach one file on single value field.
+    $this->assertNoFieldByXPath('//input[@type="submit"]', t('Attach'), 'After uploading a file, "Attach" button is no longer displayed.');
+
+    // Ensure file is copied.
+    $this->assertTrue(is_file($file->uri), 'Source file still exists.');
+    $this->assertTrue(is_file($dest_uri), 'Destination file has been created.');
+
+    $this->removeFile($file->filename, 0);
+
+    $this->assertCanAttachFile($file);
+  }
+
+  /**
+   * Get destination uri of a .
+   *
+   * @param object $file
+   *   File.
+   * @param string $uri_scheme
+   *   Uri scheme.
+   */
+  public function getDestinationUri($file, $uri_scheme) {
+    $destination = trim($this->getFieldSetting('file_directory'), '/');
+    $destination = PlainTextOutput::renderFromHtml(\Drupal::token()->replace($destination));
+    return $uri_scheme . '://' . $destination . '/' . $file->filename;
+  }
+
+}

+ 37 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Tests/ClipboardSourceTest.php

@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\filefield_sources\Tests\ClipboardSourceTest.
+ */
+
+namespace Drupal\filefield_sources\Tests;
+
+/**
+ * Tests the clipboard source.
+ *
+ * @group filefield_sources
+ */
+class ClipboardSourceTest extends FileFieldSourcesTestBase {
+
+  /**
+   * Tests clipboard source enabled.
+   */
+  public function testClipboardSourceEnabled() {
+    $this->enableSources(array(
+      'clipboard' => TRUE,
+    ));
+    $file = $this->createTemporaryFileEntity();
+
+    $this->uploadFileByClipboardSource($file->getFileUri(), $file->getFilename(), 0);
+
+    // We can only upload one file on single value field.
+    $this->assertNoFieldByXPath('//input[@type="submit"]', t('Upload'), t('After uploading a file, "Upload" button is no longer displayed.'));
+
+    $this->removeFile($file->getFilename(), 0);
+
+    // Can upload file again.
+    $this->assertFieldByXpath('//input[@type="submit"]', t('Upload'), 'After clicking the "Remove" button, the "Upload" button is displayed.');
+  }
+
+}

+ 90 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Tests/EmptyValuesTest.php

@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\filefield_sources\Tests\EmptyValuesTest.
+ */
+
+namespace Drupal\filefield_sources\Tests;
+
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+
+/**
+ * Tests empty values.
+ *
+ * @group filefield_sources
+ */
+class EmptyValuesTest extends FileFieldSourcesTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('imce');
+
+  /**
+   * Sets up for empty values test case.
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->setUpImce();
+  }
+
+  /**
+   * Tests all sources enabled.
+   */
+  public function testAllSourcesEnabled() {
+    // Change allowed number of values.
+    $this->drupalPostForm('admin/structure/types/manage/' . $this->typeName . '/fields/node.' . $this->typeName . '.' . $this->fieldName . '/storage', array('cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED), t('Save field settings'));
+
+    $this->enableSources(array(
+      'upload' => TRUE,
+      'remote' => TRUE,
+      'clipboard' => TRUE,
+      'reference' => TRUE,
+      'attach' => TRUE,
+      'imce' => TRUE,
+    ));
+
+    // Upload a file by 'Remote' source.
+    $this->uploadFileByRemoteSource();
+
+    // Upload a file by 'Reference' source.
+    $this->uploadFileByReferenceSource();
+
+    // Upload a file by 'Clipboard' source.
+    $this->uploadFileByClipboardSource();
+
+    // Upload a file by 'Attach' source.
+    $this->uploadFileByAttachSource();
+
+    // Upload a file by 'Upload' source.
+    $this->uploadFileByUploadSource('', '', 0, TRUE);
+
+    // Upload a file by 'Imce' source.
+    $this->uploadFileByImceSource();
+
+    $this->assertUniqueSubmitButtons();
+  }
+
+  /**
+   * Check that there is only one submit button of a source.
+   */
+  protected function assertUniqueSubmitButtons() {
+    $buttons = array(
+      $this->fieldName . '_0_attach' => t('Attach'),
+      $this->fieldName . '_0_clipboard_upload_button' => t('Upload'),
+      $this->fieldName . '_0_autocomplete_select' => t('Select'),
+      $this->fieldName . '_0_transfer' => t('Transfer'),
+      $this->fieldName . '_0_upload_button' => t('Upload'),
+      $this->fieldName . '_0_imce_select' => t('Select'),
+    );
+    foreach ($buttons as $button_name => $button_label) {
+      // Ensure that there is only one button with name.
+      $buttons = $this->xpath('//input[@name="' . $button_name . '" and @value="' . $button_label . '"]');
+      $this->assertEqual(count($buttons), 1, format_string('There is only one button with name %name and label %label', array('%name' => $button_name, '%label' => $button_label)));
+    }
+  }
+
+}

+ 450 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Tests/FileFieldSourcesTestBase.php

@@ -0,0 +1,450 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\filefield_sources\Tests\FileFieldSourcesTestBase.
+ */
+
+namespace Drupal\filefield_sources\Tests;
+
+use Drupal\file\Tests\FileFieldTestBase;
+use Drupal\simpletest\WebTestBase;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\user\Entity\Role;
+
+/**
+ * Base class for File Field Sources test cases.
+ */
+abstract class FileFieldSourcesTestBase extends FileFieldTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('filefield_sources');
+
+  protected $adminUser;
+
+  protected $typeName;
+  protected $fieldName;
+  protected $node;
+
+  /**
+   * Sets up for file field sources test cases.
+   */
+  protected function setUp() {
+    WebTestBase::setUp();
+
+    // Create admin user, then login.
+    $this->adminUser = $this->drupalCreateUser(array(
+      'access content',
+      'access administration pages',
+      'administer site configuration',
+      'administer users',
+      'administer permissions',
+      'administer content types',
+      'administer node fields',
+      'administer node display',
+      'administer node form display',
+      'administer nodes',
+      'bypass node access',
+    ));
+    $this->drupalLogin($this->adminUser);
+
+    // Create content type.
+    $this->typeName = 'article';
+    $this->drupalCreateContentType(array('type' => $this->typeName, 'name' => 'Article'));
+
+    // Add node.
+    $this->node = $this->drupalCreateNode();
+
+    // Add file field.
+    $this->fieldName = strtolower($this->randomMachineName());
+    $this->createFileField($this->fieldName, 'node', $this->typeName);
+  }
+
+  /**
+   * Sets up for imce test cases.
+   */
+  protected function setUpImce() {
+    foreach ($this->adminUser->getRoles(TRUE) as $rid) {
+      // Grant permission.
+      $role = Role::load($rid);
+      $this->grantPermissions($role, ['administer imce']);
+      // Assign member profile to user's role.
+      $edit["roles_profiles[$rid][public]"] = 'member';
+      $this->drupalPostForm('admin/config/media/imce', $edit, t('Save configuration'));
+    }
+  }
+
+  /**
+   * Enable file field sources.
+   *
+   * @param array $sources
+   *   List of sources to enable or disable. e.g
+   *   array(
+   *     'upload' => FALSE,
+   *     'remote' => TRUE,
+   *   ).
+   */
+  public function enableSources($sources = array()) {
+    $sources += array('upload' => TRUE);
+    $map = array(
+      'upload' => 'Upload',
+      'remote' => 'Remote URL',
+      'clipboard' => 'Clipboard',
+      'reference' => 'Reference existing',
+      'attach' => 'File attach',
+      'imce' => 'File browser',
+    );
+    $sources = array_intersect_key($sources, $map);
+    ksort($sources);
+
+    // Upload source enabled by default.
+    $manage_display = 'admin/structure/types/manage/' . $this->typeName . '/form-display';
+    $this->drupalGet($manage_display);
+    $this->assertText("File field sources: upload", 'The expected summary is displayed.');
+
+    // Click on the widget settings button to open the widget settings form.
+    $this->drupalPostAjaxForm(NULL, array(), $this->fieldName . "_settings_edit");
+
+    // Enable sources.
+    $prefix = 'fields[' . $this->fieldName . '][settings_edit_form][third_party_settings][filefield_sources][filefield_sources][sources]';
+    $edit = array();
+    foreach ($sources as $source => $enabled) {
+      $edit[$prefix . '[' . $source . ']'] = $enabled ? TRUE : FALSE;
+    }
+    $this->drupalPostAjaxForm(NULL, $edit, array($this->fieldName . '_plugin_settings_update' => t('Update')));
+    $this->assertText("File field sources: " . implode(', ', array_keys($sources)), 'The expected summary is displayed.');
+
+    // Save the form to save the third party settings.
+    $this->drupalPostForm(NULL, array(), t('Save'));
+
+    $add_node = 'node/add/' . $this->typeName;
+    $this->drupalGet($add_node);
+    if (count($sources) > 1) {
+      // We can swith between sources.
+      foreach ($sources as $source => $enabled) {
+        $label = $map[$source];
+        $this->assertLink($label);
+      }
+    }
+    else {
+      foreach ($map as $source => $label) {
+        $this->assertNoLink($label);
+      }
+    }
+  }
+
+  /**
+   * Create permanent file entity.
+   *
+   * @return object
+   *   Permanent file entity.
+   */
+  public function createPermanentFileEntity() {
+    $file = $this->createTemporaryFileEntity();
+    // Only permanent file can be referred.
+    $file->status = FILE_STATUS_PERMANENT;
+    // Author has permission to access file.
+    $file->uid = $this->adminUser->id();
+    $file->save();
+
+    // Permanent file must be used by an entity.
+    \Drupal::service('file.usage')->add($file, 'file', 'node', $this->node->id());
+
+    return $file;
+  }
+
+  /**
+   * Create temporary file entity.
+   *
+   * @return object
+   *   Temporary file entity.
+   */
+  public function createTemporaryFileEntity() {
+    $file = $this->createTemporaryFile();
+
+    // Add a filesize property to files as would be read by file_load().
+    $file->filesize = filesize($file->uri);
+
+    return entity_create('file', (array) $file);
+  }
+
+  /**
+   * Create temporary file.
+   *
+   * @return object
+   *   Permanent file object.
+   */
+  public function createTemporaryFile($path = '') {
+    $filename = $this->randomMachineName() . '.txt';
+    if (empty($path)) {
+      $path = file_default_scheme() . '://';
+    }
+    $uri = $path . $filename;
+    $contents = $this->randomString();
+
+    // Change mode so that we can create files.
+    file_prepare_directory($path, FILE_CREATE_DIRECTORY);
+    drupal_chmod($path, FILE_CHMOD_DIRECTORY);
+
+    file_put_contents($uri, $contents);
+    $this->assertTrue(is_file($uri), 'The temporary file has been created.');
+
+    // Change mode so that we can delete created file.
+    drupal_chmod($uri, FILE_CHMOD_FILE);
+
+    // Return object similar to file_scan_directory().
+    $file = new \stdClass();
+    $file->uri = $uri;
+    $file->filename = $filename;
+    $file->name = pathinfo($filename, PATHINFO_FILENAME);
+    return $file;
+  }
+
+  /**
+   * Update file field sources settings.
+   *
+   * @param string $source_key
+   *   Wrapper, defined by each source.
+   * @param string $key
+   *   Key, defined by each source.
+   * @param mixed $value
+   *   Value to set.
+   */
+  public function updateFilefieldSourcesSettings($source_key, $key, $value) {
+    $manage_display = 'admin/structure/types/manage/' . $this->typeName . '/form-display';
+    $this->drupalGet($manage_display);
+
+    // Click on the widget settings button to open the widget settings form.
+    $this->drupalPostAjaxForm(NULL, array(), $this->fieldName . "_settings_edit");
+
+    // Update settings.
+    $name = 'fields[' . $this->fieldName . '][settings_edit_form][third_party_settings][filefield_sources][filefield_sources]' . "[$source_key][$key]";
+    $edit = array($name => $value);
+    $this->drupalPostAjaxForm(NULL, $edit, array($this->fieldName . '_plugin_settings_update' => t('Update')));
+
+    // Save the form to save the third party settings.
+    $this->drupalPostForm(NULL, array(), t('Save'));
+  }
+
+  /**
+   * Upload file by 'Attach' source.
+   *
+   * @param string $uri
+   *   File uri.
+   * @param string $filename
+   *   File name.
+   * @param int $delta
+   *   Delta in multiple values field.
+   */
+  public function uploadFileByAttachSource($uri = '', $filename = '', $delta = 0) {
+    if ($uri) {
+      $edit = array(
+        $this->fieldName . '[' . $delta . '][filefield_attach][filename]' => $uri,
+      );
+    }
+    else {
+      $edit = array();
+    }
+    $this->drupalPostAjaxForm(NULL, $edit, array($this->fieldName . '_' . $delta . '_attach' => t('Attach')));
+
+    if ($filename) {
+      $this->assertFileUploaded($filename, $delta);
+    }
+    else {
+      $this->assertFileNotUploaded($delta);
+    }
+  }
+
+  /**
+   * Upload file by 'Reference' source.
+   *
+   * @param int $fid
+   *   File id.
+   * @param string $filename
+   *   File name.
+   * @param int $delta
+   *   Delta in multiple values field.
+   */
+  public function uploadFileByReferenceSource($fid = 0, $filename = '', $delta = 0) {
+    $name = $this->fieldName . '[' . $delta . '][filefield_reference][autocomplete]';
+    $value = $fid ? $filename . ' [fid:' . $fid . ']' : '';
+    $edit = array($name => $value);
+    $this->drupalPostAjaxForm(NULL, $edit, array($this->fieldName . '_' . $delta . '_autocomplete_select' => t('Select')));
+
+    if ($filename) {
+      $this->assertFileUploaded($filename, $delta);
+    }
+    else {
+      $this->assertFileNotUploaded($delta);
+    }
+  }
+
+  /**
+   * Upload file by 'Clipboard' source.
+   *
+   * @param string $uri
+   *   File uri.
+   * @param string $filename
+   *   File name.
+   * @param int $delta
+   *   Delta in multiple values field.
+   */
+  public function uploadFileByClipboardSource($uri = '', $filename = '', $delta = 0) {
+    $prefix = $this->fieldName . '[' . $delta . '][filefield_clipboard]';
+    $file_content = $uri ? 'data:text/plain;base64,' . base64_encode(file_get_contents($uri)) : '';
+    $edit = array(
+      $prefix . '[filename]' => $filename,
+      $prefix . '[contents]' => $file_content,
+    );
+    $this->drupalPostAjaxForm(NULL, $edit, array($this->fieldName . '_' . $delta . '_clipboard_upload_button' => t('Upload')));
+
+    if ($filename) {
+      $this->assertFileUploaded($filename, $delta);
+    }
+    else {
+      $this->assertFileNotUploaded($delta);
+    }
+  }
+
+  /**
+   * Upload file by 'Imce' source.
+   *
+   * @param string $uri
+   *   File uri.
+   * @param string $filename
+   *   File name.
+   * @param int $delta
+   *   Delta in multiple values field.
+   */
+  public function uploadFileByImceSource($uri = '', $filename = '', $delta = 0) {
+    $scheme = parse_url($uri, PHP_URL_SCHEME);
+    $imce_path = str_replace("$scheme://", '', $uri);
+    $edit = array(
+      $this->fieldName . '[' . $delta . '][filefield_imce][imce_paths]' => $imce_path,
+    );
+    $this->drupalPostAjaxForm(NULL, $edit, array($this->fieldName . '_' . $delta . '_imce_select' => t('Select')));
+
+
+    if ($filename) {
+      $this->assertFileUploaded($filename, $delta);
+    }
+    else {
+      $this->assertFileNotUploaded($delta);
+    }
+  }
+
+  /**
+   * Upload file by 'Remote' source.
+   *
+   * @param string $url
+   *   File url.
+   * @param string $filename
+   *   File name.
+   * @param int $delta
+   *   Delta in multiple values field.
+   */
+  public function uploadFileByRemoteSource($url = '', $filename = '', $delta = 0) {
+    $name = $this->fieldName . '[' . $delta . '][filefield_remote][url]';
+    $edit = array($name => $url);
+    $this->drupalPostAjaxForm(NULL, $edit, array($this->fieldName . '_' . $delta . '_transfer' => t('Transfer')));
+
+    if ($filename) {
+      $this->assertFileUploaded($filename, $delta);
+    }
+    else {
+      $this->assertFileNotUploaded($delta);
+    }
+  }
+
+  /**
+   * Upload file by 'Upload' source.
+   *
+   * @param string $uri
+   *   File uri.
+   * @param string $filename
+   *   File name.
+   * @param int $delta
+   *   Delta in multiple values field.
+   */
+  public function uploadFileByUploadSource($uri = '', $filename = '', $delta = 0, $multiple = FALSE) {
+    $name = 'files[' . $this->fieldName . '_' . $delta . ']';
+    if ($multiple) {
+      $name .= '[]';
+    }
+    $edit = array(
+      $name => $uri ? drupal_realpath($uri) : '',
+    );
+    $this->drupalPostAjaxForm(NULL, $edit, array($this->fieldName . '_' . $delta . '_upload_button' => t('Upload')));
+
+    if ($filename) {
+      $this->assertFileUploaded($filename, $delta);
+    }
+    else {
+      $this->assertFileNotUploaded($delta);
+    }
+  }
+
+  /**
+   * Check to see if file is uploaded.
+   *
+   * @param string $filename
+   *   File name.
+   * @param int $delta
+   *   Delta in multiple values field.
+   */
+  public function assertFileUploaded($filename, $delta = 0) {
+    $this->assertLink($filename);
+    $this->assertFieldByXPath('//input[@name="' . $this->fieldName . '_' . $delta . '_remove_button"]', t('Remove'), 'After uploading a file, "Remove" button is displayed.');
+  }
+
+  /**
+   * Check to see if file is not uploaded.
+   *
+   * @param int $delta
+   *   Delta in multiple values field.
+   */
+  public function assertFileNotUploaded($delta = 0) {
+    $this->assertNoFieldByXPath('//input[@name="' . $this->fieldName . '_' . $delta . '_remove_button"]', t('Remove'), '"Remove" button is not displayed.');
+  }
+
+  /**
+   * Remove uploaded file.
+   *
+   * @param string $filename
+   *   File name.
+   * @param int $delta
+   *   Delta in multiple values field.
+   */
+  public function removeFile($filename, $delta = 0) {
+    $this->drupalPostAjaxForm(NULL, array(), array($this->fieldName . '_' . $delta . '_remove_button' => t('Remove')));
+
+    // Ensure file is removed.
+    $this->assertFileRemoved($filename);
+  }
+
+  /**
+   * Check to see if file is removed.
+   *
+   * @param string $filename
+   *   File name.
+   */
+  public function assertFileRemoved($filename) {
+    $this->assertNoLink($filename);
+  }
+
+  /**
+   * Get field setting.
+   *
+   * @param string $setting_name
+   *   Setting name.
+   */
+  public function getFieldSetting($setting_name) {
+    $field_definition = FieldConfig::load("node.{$this->typeName}.{$this->fieldName}");
+    return $field_definition->getSetting($setting_name);
+  }
+
+}

+ 52 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Tests/ImceSourceTest.php

@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\filefield_sources\Tests\ClipboardSourceTest.
+ */
+
+namespace Drupal\filefield_sources\Tests;
+
+/**
+ * Tests the imce source.
+ *
+ * @group filefield_sources
+ */
+class ImceSourceTest extends FileFieldSourcesTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('imce');
+
+  /**
+   * Sets up for imce source test case.
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->setUpImce();
+  }
+
+  /**
+   * Tests imce source enabled.
+   */
+  public function testImceSourceEnabled() {
+    $this->enableSources(array(
+      'imce' => TRUE,
+    ));
+    $file = $this->createPermanentFileEntity();
+
+    $this->uploadFileByImceSource($file->getFileUri(), $file->getFilename(), 0);
+
+    // We can only upload one file on single value field.
+    $this->assertNoFieldByXPath('//input[@type="submit"]', t('Select'), t('After uploading a file, "Select" button is no longer displayed.'));
+
+    $this->removeFile($file->getFilename(), 0);
+
+    // Can upload file again.
+    $this->assertFieldByXpath('//input[@type="submit"]', t('Select'), 'After clicking the "Remove" button, the "Select" button is displayed.');
+  }
+
+}

+ 133 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Tests/MultipleValuesTest.php

@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\filefield_sources\Tests\MultipleValuesTest.
+ */
+
+namespace Drupal\filefield_sources\Tests;
+
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+
+/**
+ * Tests multiple sources on multiple values field.
+ *
+ * @group filefield_sources
+ */
+class MultipleValuesTest extends FileFieldSourcesTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('imce');
+
+  /**
+   * Sets up for multiple values test case.
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->setUpImce();
+
+    // Create test files.
+    $this->permanent_file_entity_1 = $this->createPermanentFileEntity();
+    $this->permanent_file_entity_2 = $this->createPermanentFileEntity();
+    $this->temporary_file_entity_1 = $this->createTemporaryFileEntity();
+    $this->temporary_file_entity_2 = $this->createTemporaryFileEntity();
+
+    $path = file_default_scheme() . '://' . FILEFIELD_SOURCE_ATTACH_DEFAULT_PATH . '/';
+    $this->temporary_file = $this->createTemporaryFile($path);
+
+    // Change allowed number of values.
+    $this->drupalPostForm('admin/structure/types/manage/' . $this->typeName . '/fields/node.' . $this->typeName . '.' . $this->fieldName . '/storage', array('cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED), t('Save field settings'));
+
+    $this->enableSources(array(
+      'upload' => TRUE,
+      'remote' => TRUE,
+      'clipboard' => TRUE,
+      'reference' => TRUE,
+      'attach' => TRUE,
+      'imce' => TRUE,
+    ));
+  }
+
+  /**
+   * Tests uploading then removing files.
+   */
+  public function testUploadThenRemoveFiles() {
+    $this->uploadFiles();
+
+    // Remove all uploaded files.
+    $this->removeFile($this->temporary_file_entity_2->getFilename(), 4);
+    $this->removeFile('INSTALL.txt', 0);
+    $this->removeFile($this->temporary_file_entity_1->getFilename(), 1);
+    $this->removeFile($this->temporary_file->filename, 1);
+    $this->removeFile($this->permanent_file_entity_1->getFilename(), 0);
+    $this->removeFile($this->permanent_file_entity_2->getFilename(), 0);
+
+    // Ensure all files have been removed.
+    $this->assertNoFieldByXPath('//input[@type="submit"]', t('Remove'), 'All files have been removed.');
+  }
+
+  /**
+   * Tests uploading files and saving node.
+   */
+  public function testUploadFilesThenSaveNode() {
+    $this->uploadFiles();
+
+    $this->drupalPostForm(NULL, array('title[0][value]' => $this->randomMachineName()), t('Save and publish'));
+
+    // Ensure all files are saved to node.
+    $this->assertLink('INSTALL.txt');
+    $this->assertLink($this->permanent_file_entity_1->getFilename());
+    $this->assertLink($this->permanent_file_entity_2->getFilename());
+    $this->assertLink($this->temporary_file_entity_1->getFilename());
+    $this->assertLink($this->temporary_file_entity_2->getFilename());
+    $this->assertLink($this->temporary_file->filename);
+  }
+
+  /**
+   * Upload files.
+   *
+   * @return int
+   *   Number of files uploaded.
+   */
+  protected function uploadFiles() {
+    $uploaded_files = 0;
+
+    // Ensure no files has been uploaded.
+    $this->assertNoFieldByXPath('//input[@type="submit"]', t('Remove'), 'There are no file have been uploaded.');
+
+    // Upload a file by 'Remote' source.
+    $this->uploadFileByRemoteSource($GLOBALS['base_url'] . '/core/INSTALL.txt', 'INSTALL.txt', $uploaded_files);
+    $uploaded_files++;
+
+    // Upload a file by 'Reference' source.
+    $this->uploadFileByReferenceSource($this->permanent_file_entity_1->id(), $this->permanent_file_entity_1->getFilename(), $uploaded_files);
+    $uploaded_files++;
+
+    // Upload a file by 'Clipboard' source.
+    $this->uploadFileByClipboardSource($this->temporary_file_entity_1->getFileUri(), $this->temporary_file_entity_1->getFileName(), $uploaded_files);
+    $uploaded_files++;
+
+    // Upload a file by 'Attach' source.
+    $this->uploadFileByAttachSource($this->temporary_file->uri, $this->temporary_file->filename, $uploaded_files);
+    $uploaded_files++;
+
+    // Upload a file by 'Upload' source.
+    $this->uploadFileByUploadSource($this->temporary_file_entity_2->getFileUri(), $this->temporary_file_entity_2->getFilename(), $uploaded_files, TRUE);
+    $uploaded_files++;
+
+    // Upload a file by 'Imce' source.
+    $this->uploadFileByImceSource($this->permanent_file_entity_2->getFileUri(), $this->permanent_file_entity_2->getFileName(), $uploaded_files);
+    $uploaded_files++;
+
+    // Ensure files have been uploaded.
+    $remove_buttons = $this->xpath('//input[@type="submit" and @value="' . t('Remove') . '"]');
+    $this->assertEqual(count($remove_buttons), $uploaded_files, "There are $uploaded_files files have been uploaded.");
+
+    return $uploaded_files;
+  }
+
+}

+ 109 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Tests/ReferenceSourceTest.php

@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\filefield_sources\Tests\ReferenceSourceTest.
+ */
+
+namespace Drupal\filefield_sources\Tests;
+
+use Drupal\Component\Utility\Unicode;
+
+/**
+ * Tests the reference source.
+ *
+ * @group filefield_sources
+ */
+class ReferenceSourceTest extends FileFieldSourcesTestBase {
+
+  /**
+   * Tests reference source enabled.
+   */
+  public function testReferenceSourceEnabled() {
+
+    // Create test file.
+    $file = $this->createPermanentFileEntity();
+
+    $this->enableSources(array(
+      'reference' => TRUE,
+    ));
+
+    // Upload a file by 'Reference' source.
+    $this->uploadFileByReferenceSource($file->id(), $file->getFilename(), 0);
+
+    // We can only refer one file on single value field.
+    $this->assertNoFieldByXPath('//input[@type="submit"]', t('Select'), t('After uploading a file, "Select" button is no longer displayed.'));
+
+    // Remove uploaded file.
+    $this->removeFile($file->getFileName(), 0);
+
+    // Can select file again.
+    $this->assertFieldByXpath('//input[@type="submit"]', t('Select'), 'After clicking the "Remove" button, the "Select" button is displayed.');
+  }
+
+  /**
+   * Test autocompletion.
+   */
+  public function testAutocompletion() {
+    // Create test file.
+    $file = $this->createPermanentFileEntity();
+    $filename = $file->getFileName();
+    $first_character = substr($filename, 0, 1);
+    $second_character = substr($filename, 1, 1);
+
+    // Switch to 'Starts with' match type.
+    $this->updateFilefieldSourcesSettings('source_reference', 'autocomplete', '0');
+
+    // STARTS_WITH: empty results.
+    $query = $this->findCharacterNotInString($first_character);
+    $autocomplete_result = $this->drupalGetJSON('file/reference/node/' . $this->typeName . '/' . $this->fieldName, array('query' => array('q' => $query)));
+    $this->assertEqual($autocomplete_result, array(), "No files that have name starts with '$query'");
+
+    // STARTS_WITH: not empty results.
+    $query = $first_character;
+    $autocomplete_result = $this->drupalGetJSON('file/reference/node/' . $this->typeName . '/' . $this->fieldName, array('query' => array('q' => $query)));
+    $this->assertEqual($autocomplete_result[0]['label'], $filename, 'Autocompletion return correct label.');
+    $this->assertEqual($autocomplete_result[0]['value'], $filename . ' [fid:' . $file->id() . ']', 'Autocompletion return correct value.');
+
+    // Switch to 'Contains' match type.
+    $this->updateFilefieldSourcesSettings('source_reference', 'autocomplete', '1');
+
+    // CONTAINS: empty results.
+    $query = $this->findCharacterNotInString($filename);
+    $autocomplete_result = $this->drupalGetJSON('file/reference/node/' . $this->typeName . '/' . $this->fieldName, array('query' => array('q' => $query)));
+    $this->assertEqual($autocomplete_result, array(), "No files that have name contains '$query'");
+
+    // CONTAINS: not empty results.
+    $query = $second_character;
+    $autocomplete_result = $this->drupalGetJSON('file/reference/node/' . $this->typeName . '/' . $this->fieldName, array('query' => array('q' => $query)));
+    $this->assertEqual($autocomplete_result[0]['label'], $filename, 'Autocompletion return correct label.');
+    $this->assertEqual($autocomplete_result[0]['value'], $filename . ' [fid:' . $file->id() . ']', 'Autocompletion return correct value.');
+  }
+
+  /**
+   * Find the first character that is not in string.
+   *
+   * Only find for lower case character.
+   *
+   * @param string $string
+   *   String to check.
+   *
+   * @return string
+   *   First character that is not in the string.
+   */
+  protected function findCharacterNotInString($string) {
+    // Only check for lower case string.
+    $string = Unicode::strtolower($string);
+
+    // Lower case characters and numbers generated by
+    // \Drupal\simpletest\TestBase::randomMachineName().
+    $values = array_merge(range(97, 122), range(48, 57));
+    foreach ($values as $value) {
+      $character = chr($value);
+      if (strpos($string, $character) === FALSE) {
+        return $character;
+      }
+    }
+  }
+
+}

+ 38 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Tests/RemoteSourceTest.php

@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\filefield_sources\Tests\RemoteSourceTest.
+ */
+
+namespace Drupal\filefield_sources\Tests;
+
+/**
+ * Tests the remote source.
+ *
+ * @group filefield_sources
+ */
+class RemoteSourceTest extends FileFieldSourcesTestBase {
+
+  /**
+   * Tests remote source enabled.
+   */
+  public function testRemoteSourceEnabled() {
+    $this->enableSources(array(
+      'remote' => TRUE,
+    ));
+
+    // Upload a file by 'Remote' source.
+    $this->uploadFileByRemoteSource($GLOBALS['base_url'] . '/README.txt', 'README.txt', 0);
+
+    // We can only transfer one file on single value field.
+    $this->assertNoFieldByXPath('//input[@type="submit"]', t('Transfer'), t('After uploading a file, "Transfer" button is no longer displayed.'));
+
+    // Remove uploaded file.
+    $this->removeFile('README.txt', 0);
+
+    // Can transfer file again.
+    $this->assertFieldByXpath('//input[@type="submit"]', t('Transfer'), 'After clicking the "Remove" button, the "Transfer" button is displayed.');
+  }
+
+}

+ 62 - 0
sites/all/modules/contrib/fields/filefield_sources/src/Tests/UploadSourceTest.php

@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\filefield_sources\Tests\UploadSourceTest.
+ */
+
+namespace Drupal\filefield_sources\Tests;
+
+/**
+ * Tests the upload source.
+ *
+ * @group filefield_sources
+ */
+class UploadSourceTest extends FileFieldSourcesTestBase {
+
+  /**
+   * Tests upload source enabled.
+   */
+  public function testUploadSourceEnabled() {
+    $this->enableSources(array(
+      'upload' => TRUE,
+    ));
+
+    $this->assertUploadSourceWorkProperly();
+  }
+
+  /**
+   * Tests all sources enabled.
+   */
+  public function testAllSourcesEnabled() {
+    $this->enableSources(array(
+      'upload' => TRUE,
+      'remote' => TRUE,
+      'clipboard' => TRUE,
+      'reference' => TRUE,
+      'attach' => TRUE,
+    ));
+
+    $this->assertUploadSourceWorkProperly();
+  }
+
+  /**
+   * Tests upload source still working properly.
+   */
+  protected function assertUploadSourceWorkProperly() {
+    $file = $this->createTemporaryFileEntity();
+
+    // Upload a file by 'Upload' source.
+    $this->uploadFileByUploadSource($file->getFileUri(), $file->getFilename(), 0, FALSE);
+
+    // We can only upload one file on single value field.
+    $this->assertNoFieldByXPath('//input[@type="submit"]', t('Upload'), t('After uploading a file, "Upload" button is no longer displayed.'));
+
+    // Remove uploaded file.
+    $this->removeFile($file->getFilename(), 0);
+
+    // Can upload file again.
+    $this->assertFieldByXpath('//input[@type="submit"]', t('Upload'), 'After clicking the "Remove" button, the "Upload" button is displayed.');
+  }
+
+}

+ 2 - 2
sites/all/modules/contrib/migrate/migrate_plus/composer.json

@@ -17,8 +17,8 @@
     "source": "https://cgit.drupalcode.org/migrate_plus"
   },
   "minimum-stability": "dev",
-  "require": {},
   "suggest": {
-    "sainsburys/guzzle-oauth2-plugin": "3.0 required for the OAuth2 authentication plugin"
+    "sainsburys/guzzle-oauth2-plugin": "3.0 required for the OAuth2 authentication plugin",
+    "ext-soap": "*"
   }
 }

+ 2 - 2
sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration.beer_node.yml

@@ -14,7 +14,7 @@ process:
   title: name
   nid: bid
   uid:
-    plugin: migration
+    plugin: migration_lookup
     migration: beer_user
     source: aid
   sticky:
@@ -22,7 +22,7 @@ process:
     default_value: 0
   field_migrate_example_country: countries
   field_migrate_example_beer_style:
-    plugin: migration
+    plugin: migration_lookup
     migration: beer_term
     source: terms
   # Some Drupal fields may have multiple components we may want to set

+ 1 - 1
sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration.beer_term.yml

@@ -60,7 +60,7 @@ process:
   # IDs in map tables, and the migration plugin is the means of performing a
   # lookup in those map tables during processing.
   parent:
-    plugin: migration
+    plugin: migration_lookup
     # Here we reference the migration whose map table we're performing a lookup
     # against. You'll note that in this case we're actually referencing this
     # migration itself, since category parents are imported by the same

+ 1 - 1
sites/all/modules/contrib/migrate/migrate_plus/migrate_example/config/install/migrate_plus.migration.beer_user.yml

@@ -90,7 +90,7 @@ process:
   # when that migration runs it knows that each incoming beer should overwrite
   # its stub instead of creating a new node.
   field_migrate_example_favbeers:
-    plugin: migration
+    plugin: migration_lookup
     source: beers
     migration: beer_node
 

+ 5 - 3
sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example.info.yml

@@ -7,9 +7,11 @@ dependencies:
   - drupal:migrate
   - migrate_plus:migrate_example_setup
   - migrate_plus:migrate_plus
+  - drupal:menu_ui
+  - drupal:path
 
-# Information added by Drupal.org packaging script on 2017-05-10
-version: '8.x-4.0-beta1'
+# Information added by Drupal.org packaging script on 2017-12-12
+version: '8.x-4.0-beta2'
 core: '8.x'
 project: 'migrate_plus'
-datestamp: 1494450189
+datestamp: 1513088305

+ 3 - 3
sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/migrate_example_setup.info.yml

@@ -11,8 +11,8 @@ dependencies:
   - drupal:options
   - drupal:taxonomy
 
-# Information added by Drupal.org packaging script on 2017-05-10
-version: '8.x-4.0-beta1'
+# Information added by Drupal.org packaging script on 2017-12-12
+version: '8.x-4.0-beta2'
 core: '8.x'
 project: 'migrate_plus'
-datestamp: 1494450189
+datestamp: 1513088305

+ 132 - 130
sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrate_example_setup/migrate_example_setup.install

@@ -27,270 +27,272 @@ function migrate_example_setup_install() {
 }
 
 function migrate_example_beer_schema_node() {
-  return array(
+  return [
     'description' => 'Beers of the world.',
-    'fields' => array(
-      'bid'  => array(
+    'fields' => [
+      'bid'  => [
         'type' => 'serial',
         'not null' => TRUE,
         'description' => 'Beer ID.',
-      ),
-      'name'  => array(
+      ],
+      'name'  => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => TRUE,
-      ),
-      'body' => array(
+      ],
+      'body' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Full description of the beer.',
-      ),
-      'excerpt' => array(
+      ],
+      'excerpt' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Abstract for this beer.',
-      ),
-      'countries' => array(
+      ],
+      'countries' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Countries of origin. Multiple values, delimited by pipe',
-      ),
-      'aid' => array(
+      ],
+      'aid' => [
         'type' => 'int',
         'not null' => FALSE,
         'description' => 'Account Id of the author.',
-      ),
-      'image' => array(
+      ],
+      'image' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Image path',
-      ),
-      'image_alt' => array(
+      ],
+      'image_alt' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Image ALT',
-      ),
-      'image_title' => array(
+      ],
+      'image_title' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Image title',
-      ),
-      'image_description' => array(
+      ],
+      'image_description' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Image description',
-      ),
-    ),
-    'primary key' => array('bid'),
-  );
+      ],
+    ],
+    'primary key' => ['bid'],
+  ];
 }
 
 function migrate_example_beer_schema_topic() {
-  return array(
+  return [
     'description' => 'Categories',
-    'fields' => array(
-      'style'  => array(
+    'fields' => [
+      'style'  => [
         'type' => 'varchar_ascii',
         'length' => 255,
         'not null' => TRUE,
-      ),
-      'details' => array(
+      ],
+      'details' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
-      ),
-      'style_parent' => array(
+      ],
+      'style_parent' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Parent topic, if any',
-      ),
-      'region' => array(
+      ],
+      'region' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Region first associated with this style',
-      ),
-      'hoppiness' => array(
+      ],
+      'hoppiness' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Relative hoppiness of the beer',
-      ),
-    ),
-    'primary key' => array('style'),
-  );
+      ],
+    ],
+    'primary key' => ['style'],
+  ];
 }
 
 function migrate_example_beer_schema_topic_node() {
-  return array(
+  return [
     'description' => 'Beers topic pairs.',
-    'fields' => array(
-      'bid'  => array(
+    'fields' => [
+      'bid'  => [
         'type' => 'int',
         'not null' => TRUE,
         'description' => 'Beer ID.',
-      ),
-      'style'  => array(
+      ],
+      'style'  => [
         'type' => 'varchar_ascii',
         'length' => 255,
         'not null' => TRUE,
         'description' => 'Topic name',
-      ),
-    ),
-    'primary key' => array('style', 'bid'),
-  );
+      ],
+    ],
+    'primary key' => ['style', 'bid'],
+  ];
 }
 
 function migrate_example_beer_schema_comment() {
-  return array(
+  return [
     'description' => 'Beers comments.',
-    'fields' => array(
-      'cid'  => array(
+    'fields' => [
+      'cid'  => [
         'type' => 'serial',
         'not null' => TRUE,
         'description' => 'Comment ID.',
-      ),
-      'bid'  => array(
+      ],
+      'bid'  => [
         'type' => 'int',
         'not null' => TRUE,
         'description' => 'Beer ID that is being commented upon',
-      ),
-      'cid_parent' => array(
+      ],
+      'cid_parent' => [
         'type' => 'int',
         'not null' => FALSE,
         'description' => 'Parent comment ID in case of comment replies.',
-      ),
-      'subject' => array(
+      ],
+      'subject' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Comment subject',
-      ),
-      'body' => array(
+      ],
+      'body' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Comment body',
-      ),
-      'name' => array(
+      ],
+      'name' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Comment name (if anon)',
-      ),
-      'mail' => array(
+      ],
+      'mail' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Comment email (if anon)',
-      ),
-      'aid' => array(
+      ],
+      'aid' => [
         'type' => 'int',
         'not null' => FALSE,
         'description' => 'Account ID (if any).',
-      ),
-    ),
-    'primary key' => array('cid'),
-  );
+      ],
+    ],
+    'primary key' => ['cid'],
+  ];
 }
 
 function migrate_example_beer_schema_account() {
-  return array(
+  return [
     'description' => 'Beers accounts.',
-    'fields' => array(
-      'aid'  => array(
+    'fields' => [
+      'aid'  => [
         'type' => 'serial',
         'not null' => TRUE,
         'description' => 'Account ID',
-      ),
-      'status'  => array(
+      ],
+      'status'  => [
         'type' => 'int',
         'not null' => TRUE,
         'description' => 'Blocked_Allowed',
-      ),
-      'registered' => array(
+      ],
+      'registered' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => TRUE,
         'description' => 'Registration date',
-      ),
-      'username' => array(
+      ],
+      'username' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Account name (for login)',
-      ),
-      'nickname' => array(
+      ],
+      'nickname' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Account name (for display)',
-      ),
-      'password' => array(
+      ],
+      'password' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Account password (raw)',
-      ),
-      'email' => array(
+      ],
+      'email' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Account email',
-      ),
-      'sex' => array(
+      ],
+      'sex' => [
         'type' => 'int',
         'not null' => FALSE,
         'description' => 'Gender (0 for male, 1 for female)',
-      ),
-      'beers' => array(
+      ],
+      'beers' => [
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
         'description' => 'Favorite Beers',
-      ),
-    ),
-    'primary key' => array('aid'),
-  );
+      ],
+    ],
+    'primary key' => ['aid'],
+  ];
 }
 
 function migrate_example_beer_data_node() {
-  $fields = array('bid', 'name', 'body', 'excerpt', 'countries', 'aid', 'image',
-    'image_alt', 'image_title', 'image_description');
+  $fields = ['bid', 'name', 'body', 'excerpt', 'countries', 'aid', 'image',
+    'image_alt', 'image_title', 'image_description'];
   $query = db_insert('migrate_example_beer_node')
-           ->fields($fields);
+    ->fields($fields);
   // Use high bid numbers to avoid overwriting an existing node id.
-  $data = array(
-    array(99999999, 'Heineken', 'Blab Blah Blah Green', 'Green', 'Netherlands|Belgium', 0, 'heineken.jpg', 'Heinekin alt', 'Heinekin title', 'Heinekin description'), // comes with migrate_example project.
-    array(99999998, 'Miller Lite', 'We love Miller Brewing', 'Tasteless', 'USA|Canada', 1, NULL, NULL, NULL, NULL),
-    array(99999997, 'Boddington', 'English occasionally get something right', 'A treat', 'United Kingdom', 1, NULL, NULL, NULL, NULL),
-  );
+  $data = [
+    [99999999, 'Heineken', 'Blab Blah Blah Green', 'Green', 'Netherlands|Belgium', 0, 'heineken.jpg', 'Heinekin alt', 'Heinekin title', 'Heinekin description'], // comes with migrate_example project.
+    [99999998, 'Miller Lite', 'We love Miller Brewing', 'Tasteless', 'USA|Canada', 1, NULL, NULL, NULL, NULL],
+    [99999997, 'Boddington', 'English occasionally get something right', 'A treat', 'United Kingdom', 1, NULL, NULL, NULL, NULL],
+  ];
   foreach ($data as $row) {
     $query->values(array_combine($fields, $row));
   }
   $query->execute();
 }
 
-// Note that alice has duplicate username. Exercises dedupe_entity plugin.
-// @TODO duplicate email also.
+/**
+ * Note that alice has duplicate username. Exercises dedupe_entity plugin.
+ *@TODO duplicate email also.
+ */
 function migrate_example_beer_data_account() {
-  $fields = array('status', 'registered', 'username', 'nickname', 'password', 'email', 'sex', 'beers');
+  $fields = ['status', 'registered', 'username', 'nickname', 'password', 'email', 'sex', 'beers'];
   $query = db_insert('migrate_example_beer_account')
     ->fields($fields);
-  $data = array(
-    array(1, '2010-03-30 10:31:05', 'alice', 'alice in beerland', 'alicepass', 'alice@example.com', '1', '99999999|99999998|99999997'),
-    array(1, '2010-04-04 10:31:05', 'alice', 'alice in aleland', 'alicepass', 'alice2@example.com', '1', '99999999|99999998|99999997'),
-    array(0, '2007-03-15 10:31:05', 'bob', 'rebob', 'bobpass', 'bob@example.com', '0', '99999999|99999997'),
-    array(1, '2004-02-29 10:31:05', 'charlie', 'charlie chocolate', 'mykids', 'charlie@example.com', '0', '99999999|99999998'),
-  );
+  $data = [
+    [1, '2010-03-30 10:31:05', 'alice', 'alice in beerland', 'alicepass', 'alice@example.com', '1', '99999999|99999998|99999997'],
+    [1, '2010-04-04 10:31:05', 'alice', 'alice in aleland', 'alicepass', 'alice2@example.com', '1', '99999999|99999998|99999997'],
+    [0, '2007-03-15 10:31:05', 'bob', 'rebob', 'bobpass', 'bob@example.com', '0', '99999999|99999997'],
+    [1, '2004-02-29 10:31:05', 'charlie', 'charlie chocolate', 'mykids', 'charlie@example.com', '0', '99999999|99999998'],
+  ];
   foreach ($data as $row) {
     $query->values(array_combine($fields, $row));
   }
@@ -298,16 +300,16 @@ function migrate_example_beer_data_account() {
 }
 
 function migrate_example_beer_data_comment() {
-  $fields = array('bid', 'cid_parent', 'subject', 'body', 'name', 'mail', 'aid');
+  $fields = ['bid', 'cid_parent', 'subject', 'body', 'name', 'mail', 'aid'];
   $query = db_insert('migrate_example_beer_comment')
     ->fields($fields);
-  $data = array(
-    array(99999998, NULL, 'im first', 'full body', 'alice', 'alice@example.com', 0),
-    array(99999998, NULL, 'im second', 'aromatic', 'alice', 'alice@example.com', 0),
-    array(99999999, NULL, 'im parent', 'malty', 'alice', 'alice@example.com', 0),
-    array(99999999, 1, 'im child', 'cold body', 'bob', NULL, 1),
-    array(99999999, 4, 'im grandchild', 'bitter body', 'charlie@example.com', NULL, 1),
-  );
+  $data = [
+    [99999998, NULL, 'im first', 'full body', 'alice', 'alice@example.com', 0],
+    [99999998, NULL, 'im second', 'aromatic', 'alice', 'alice@example.com', 0],
+    [99999999, NULL, 'im parent', 'malty', 'alice', 'alice@example.com', 0],
+    [99999999, 1, 'im child', 'cold body', 'bob', NULL, 1],
+    [99999999, 4, 'im grandchild', 'bitter body', 'charlie@example.com', NULL, 1],
+  ];
   foreach ($data as $row) {
     $query->values(array_combine($fields, $row));
   }
@@ -315,14 +317,14 @@ function migrate_example_beer_data_comment() {
 }
 
 function migrate_example_beer_data_topic() {
-  $fields = array('style', 'details', 'style_parent', 'region', 'hoppiness');
+  $fields = ['style', 'details', 'style_parent', 'region', 'hoppiness'];
   $query = db_insert('migrate_example_beer_topic')
     ->fields($fields);
-  $data = array(
-    array('ale', 'traditional', NULL, 'Medieval British Isles', 'Medium'),
-    array('red ale', 'colorful', 'ale', NULL, NULL),
-    array('pilsner', 'refreshing', NULL, 'Pilsen, Bohemia (now Czech Republic)', 'Low'),
-  );
+  $data = [
+    ['ale', 'traditional', NULL, 'Medieval British Isles', 'Medium'],
+    ['red ale', 'colorful', 'ale', NULL, NULL],
+    ['pilsner', 'refreshing', NULL, 'Pilsen, Bohemia (now Czech Republic)', 'Low'],
+  ];
   foreach ($data as $row) {
     $query->values(array_combine($fields, $row));
   }
@@ -330,14 +332,14 @@ function migrate_example_beer_data_topic() {
 }
 
 function migrate_example_beer_data_topic_node() {
-  $fields = array('bid', 'style');
+  $fields = ['bid', 'style'];
   $query = db_insert('migrate_example_beer_topic_node')
     ->fields($fields);
-  $data = array(
-    array(99999999, 'pilsner'),
-    array(99999999, 'red ale'),
-    array(99999998, 'red ale'),
-  );
+  $data = [
+    [99999999, 'pilsner'],
+    [99999999, 'red ale'],
+    [99999998, 'red ale'],
+  ];
   foreach ($data as $row) {
     $query->values(array_combine($fields, $row));
   }

+ 3 - 3
sites/all/modules/contrib/migrate/migrate_plus/migrate_example/migrations/beer_comment.yml

@@ -8,11 +8,11 @@ destination:
   plugin: entity:comment
 process:
   pid:
-    plugin: migration
+    plugin: migration_lookup
     migration: beer_comment
     source: cid_parent
   entity_id:
-    plugin: migration
+    plugin: migration_lookup
     migration: beer_node
     source: bid
   entity_type:
@@ -26,7 +26,7 @@ process:
     default_value: node_comments
   subject: subject
   uid:
-    plugin: migration
+    plugin: migration_lookup
     migration: beer_user
     source: aid
   name: name

+ 2 - 2
sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerComment.php

@@ -18,9 +18,9 @@ class BeerComment extends SqlBase {
    */
   public function query() {
     $query = $this->select('migrate_example_beer_comment', 'mec')
-                 ->fields('mec', ['cid', 'cid_parent', 'name', 'mail', 'aid',
+      ->fields('mec', ['cid', 'cid_parent', 'name', 'mail', 'aid',
                    'body', 'bid', 'subject'])
-                 ->orderBy('cid_parent', 'ASC');
+      ->orderBy('cid_parent', 'ASC');
     return $query;
   }
 

+ 2 - 2
sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerNode.php

@@ -30,7 +30,7 @@ class BeerNode extends SqlBase {
      * below.
      */
     $query = $this->select('migrate_example_beer_node', 'b')
-                 ->fields('b', ['bid', 'name', 'body', 'excerpt', 'aid',
+      ->fields('b', ['bid', 'name', 'body', 'excerpt', 'aid',
                    'countries', 'image', 'image_alt', 'image_title',
                    'image_description']);
     return $query;
@@ -82,7 +82,7 @@ class BeerNode extends SqlBase {
      * the beer_term migration).
      */
     $terms = $this->select('migrate_example_beer_topic_node', 'bt')
-                 ->fields('bt', ['style'])
+      ->fields('bt', ['style'])
       ->condition('bid', $row->getSourceProperty('bid'))
       ->execute()
       ->fetchCol();

+ 3 - 3
sites/all/modules/contrib/migrate/migrate_plus/migrate_example/src/Plugin/migrate/source/BeerTerm.php

@@ -50,9 +50,9 @@ class BeerTerm extends SqlBase {
      * to humans what the field represents. You should always
      */
     $fields = [
-      'style' => $this->t('Account ID'),
-      'details' => $this->t('Blocked/Allowed'),
-      'style_parent' => $this->t('Registered date'),
+      'style' => $this->t('Beer style'),
+      'details' => $this->t('Style details'),
+      'style_parent' => $this->t('Parent style'),
       // These values are not currently migrated - it's OK to skip fields you
       // don't need.
       'region' => $this->t('Region the style is associated with'),

+ 1 - 1
sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_terms.yml

@@ -26,7 +26,7 @@ process:
       region: migrate_example_wine_regions
       variety: migrate_example_wine_varieties
   parent:
-    plugin: migration
+    plugin: migration_lookup
     migration: wine_terms
     source: category_parent
   weight: ordering

+ 1 - 1
sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_variety_list.yml.txt

@@ -46,7 +46,7 @@ process:
   name: category_name
   description: category_details
   parent:
-    plugin: migration
+    plugin: migration_lookup
     migration: wine_terms
     source: category_parent
 destination:

+ 1 - 1
sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/config/install/migrate_plus.migration.wine_variety_multi_xml.yml

@@ -54,7 +54,7 @@ process:
   name: category_name
   description: category_details
   parent:
-    plugin: migration
+    plugin: migration_lookup
     migration: wine_terms
     source: category_parent
   field_variety_attributes: category_attributes

+ 3 - 3
sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced.info.yml

@@ -8,8 +8,8 @@ dependencies:
   - migrate_plus:migrate_example_advanced_setup
   - migrate_plus:migrate_plus
 
-# Information added by Drupal.org packaging script on 2017-05-10
-version: '8.x-4.0-beta1'
+# Information added by Drupal.org packaging script on 2017-12-12
+version: '8.x-4.0-beta2'
 core: '8.x'
 project: 'migrate_plus'
-datestamp: 1494450189
+datestamp: 1513088305

+ 3 - 3
sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/migrate_example_advanced_setup.info.yml

@@ -11,8 +11,8 @@ dependencies:
   - drupal:taxonomy
   - drupal:rest
 
-# Information added by Drupal.org packaging script on 2017-05-10
-version: '8.x-4.0-beta1'
+# Information added by Drupal.org packaging script on 2017-12-12
+version: '8.x-4.0-beta2'
 core: '8.x'
 project: 'migrate_plus'
-datestamp: 1494450189
+datestamp: 1513088305

File diff suppressed because it is too large
+ 303 - 302
sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/migrate_example_advanced_setup.install


+ 4 - 4
sites/all/modules/contrib/migrate/migrate_plus/migrate_example_advanced/migrate_example_advanced_setup/src/Plugin/rest/resource/VarietyItems.php

@@ -31,22 +31,22 @@ class VarietyItems extends ResourceBase {
     $varieties = [
       'retsina' => [
         'name' => 'Retsina',
-        'parent' => 1,  // categoryid for 'white'.
+        'parent' => 1, // categoryid for 'white'.
         'details' => 'Greek',
       ],
       'trebbiano' => [
         'name' => 'Trebbiano',
-        'parent' => 1,  // categoryid for 'white'.
+        'parent' => 1, // categoryid for 'white'.
         'details' => 'Italian',
       ],
       'valpolicella' => [
         'name' => 'Valpolicella',
-        'parent' => 3,  // categoryid for 'red'.
+        'parent' => 3, // categoryid for 'red'.
         'details' => 'Italian Venoto region',
       ],
       'bardolino' => [
         'name' => 'Bardolino',
-        'parent' => 3,  // categoryid for 'red'.
+        'parent' => 3, // categoryid for 'red'.
         'details' => 'Italian Venoto region',
       ],
     ];

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