Procházet zdrojové kódy

activated pathauto for url aliases

Bachir Soussi Chiadmi před 6 roky
rodič
revize
4921e8189b
100 změnil soubory, kde provedl 12471 přidání a 0 odebrání
  1. 339 0
      sites/all/modules/contrib/admin/pathauto/LICENSE.txt
  2. 83 0
      sites/all/modules/contrib/admin/pathauto/README.md
  3. 13 0
      sites/all/modules/contrib/admin/pathauto/config/install/pathauto.settings.yml
  4. 9 0
      sites/all/modules/contrib/admin/pathauto/config/optional/system.action.pathauto_update_alias_node.yml
  5. 9 0
      sites/all/modules/contrib/admin/pathauto/config/optional/system.action.pathauto_update_alias_user.yml
  6. 36 0
      sites/all/modules/contrib/admin/pathauto/config/schema/pathauto.schema.yml
  7. 40 0
      sites/all/modules/contrib/admin/pathauto/config/schema/pathauto_pattern.schema.yml
  8. 160 0
      sites/all/modules/contrib/admin/pathauto/pathauto.api.php
  9. 20 0
      sites/all/modules/contrib/admin/pathauto/pathauto.info.yml
  10. 301 0
      sites/all/modules/contrib/admin/pathauto/pathauto.install
  11. 21 0
      sites/all/modules/contrib/admin/pathauto/pathauto.js
  12. 9 0
      sites/all/modules/contrib/admin/pathauto/pathauto.libraries.yml
  13. 6 0
      sites/all/modules/contrib/admin/pathauto/pathauto.links.action.yml
  14. 23 0
      sites/all/modules/contrib/admin/pathauto/pathauto.links.task.yml
  15. 57 0
      sites/all/modules/contrib/admin/pathauto/pathauto.module
  16. 6 0
      sites/all/modules/contrib/admin/pathauto/pathauto.permissions.yml
  17. 54 0
      sites/all/modules/contrib/admin/pathauto/pathauto.routing.yml
  18. 26 0
      sites/all/modules/contrib/admin/pathauto/pathauto.services.yml
  19. 49 0
      sites/all/modules/contrib/admin/pathauto/pathauto.tokens.inc
  20. 351 0
      sites/all/modules/contrib/admin/pathauto/src/AliasCleaner.php
  21. 103 0
      sites/all/modules/contrib/admin/pathauto/src/AliasCleanerInterface.php
  22. 255 0
      sites/all/modules/contrib/admin/pathauto/src/AliasStorageHelper.php
  23. 117 0
      sites/all/modules/contrib/admin/pathauto/src/AliasStorageHelperInterface.php
  24. 31 0
      sites/all/modules/contrib/admin/pathauto/src/AliasTypeBatchUpdateInterface.php
  25. 48 0
      sites/all/modules/contrib/admin/pathauto/src/AliasTypeInterface.php
  26. 71 0
      sites/all/modules/contrib/admin/pathauto/src/AliasTypeManager.php
  27. 167 0
      sites/all/modules/contrib/admin/pathauto/src/AliasUniquifier.php
  28. 42 0
      sites/all/modules/contrib/admin/pathauto/src/AliasUniquifierInterface.php
  29. 37 0
      sites/all/modules/contrib/admin/pathauto/src/Annotation/AliasType.php
  30. 382 0
      sites/all/modules/contrib/admin/pathauto/src/Entity/PathautoPattern.php
  31. 54 0
      sites/all/modules/contrib/admin/pathauto/src/EventSubscriber/PathautoSettingsCacheTag.php
  32. 185 0
      sites/all/modules/contrib/admin/pathauto/src/Form/PathautoAdminDelete.php
  33. 164 0
      sites/all/modules/contrib/admin/pathauto/src/Form/PathautoBulkUpdateForm.php
  34. 264 0
      sites/all/modules/contrib/admin/pathauto/src/Form/PathautoSettingsForm.php
  35. 52 0
      sites/all/modules/contrib/admin/pathauto/src/Form/PatternDisableForm.php
  36. 285 0
      sites/all/modules/contrib/admin/pathauto/src/Form/PatternEditForm.php
  37. 52 0
      sites/all/modules/contrib/admin/pathauto/src/Form/PatternEnableForm.php
  38. 20 0
      sites/all/modules/contrib/admin/pathauto/src/MessengerInterface.php
  39. 49 0
      sites/all/modules/contrib/admin/pathauto/src/PathautoFieldItemList.php
  40. 365 0
      sites/all/modules/contrib/admin/pathauto/src/PathautoGenerator.php
  41. 89 0
      sites/all/modules/contrib/admin/pathauto/src/PathautoGeneratorInterface.php
  42. 51 0
      sites/all/modules/contrib/admin/pathauto/src/PathautoItem.php
  43. 181 0
      sites/all/modules/contrib/admin/pathauto/src/PathautoPatternInterface.php
  44. 51 0
      sites/all/modules/contrib/admin/pathauto/src/PathautoPatternListBuilder.php
  45. 158 0
      sites/all/modules/contrib/admin/pathauto/src/PathautoState.php
  46. 71 0
      sites/all/modules/contrib/admin/pathauto/src/PathautoWidget.php
  47. 36 0
      sites/all/modules/contrib/admin/pathauto/src/Plugin/Action/UpdateAction.php
  48. 94 0
      sites/all/modules/contrib/admin/pathauto/src/Plugin/Deriver/EntityAliasTypeDeriver.php
  49. 24 0
      sites/all/modules/contrib/admin/pathauto/src/Plugin/pathauto/AliasType/Broken.php
  50. 334 0
      sites/all/modules/contrib/admin/pathauto/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php
  51. 106 0
      sites/all/modules/contrib/admin/pathauto/src/Plugin/pathauto/AliasType/ForumAliasType.php
  52. 156 0
      sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoBulkUpdateTest.php
  53. 87 0
      sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoEnablingEntityTypesTest.php
  54. 216 0
      sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoLocaleTest.php
  55. 200 0
      sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoMassDeleteTest.php
  56. 291 0
      sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoNodeWebTest.php
  57. 239 0
      sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoSettingsFormWebTest.php
  58. 102 0
      sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoTaxonomyWebTest.php
  59. 190 0
      sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoTestHelperTrait.php
  60. 191 0
      sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoUiTest.php
  61. 92 0
      sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoUserWebTest.php
  62. 63 0
      sites/all/modules/contrib/admin/pathauto/src/VerboseMessenger.php
  63. 13 0
      sites/all/modules/contrib/admin/pathauto/tests/modules/pathauto_string_id_test/pathauto_string_id_test.info.yml
  64. 50 0
      sites/all/modules/contrib/admin/pathauto/tests/modules/pathauto_string_id_test/src/Entity/PathautoStringIdTest.php
  65. 231 0
      sites/all/modules/contrib/admin/pathauto/tests/modules/pathauto_views_test/config/install/views.view.articles.yml
  66. 14 0
      sites/all/modules/contrib/admin/pathauto/tests/modules/pathauto_views_test/pathauto_views_test.info.yml
  67. 130 0
      sites/all/modules/contrib/admin/pathauto/tests/src/Kernel/PathautoEntityWithStringIdTest.php
  68. 588 0
      sites/all/modules/contrib/admin/pathauto/tests/src/Kernel/PathautoKernelTest.php
  69. 78 0
      sites/all/modules/contrib/admin/pathauto/tests/src/Kernel/PathautoTokenTest.php
  70. 59 0
      sites/all/modules/contrib/admin/pathauto/tests/src/Unit/VerboseMessengerTest.php
  71. 339 0
      sites/all/modules/contrib/dev/ctools/LICENSE.txt
  72. 40 0
      sites/all/modules/contrib/dev/ctools/composer.json
  73. 63 0
      sites/all/modules/contrib/dev/ctools/config/schema/ctools.schema.yml
  74. 11 0
      sites/all/modules/contrib/dev/ctools/ctools.info.yml
  75. 64 0
      sites/all/modules/contrib/dev/ctools/ctools.module
  76. 38 0
      sites/all/modules/contrib/dev/ctools/ctools.services.yml
  77. 28 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_block/config/schema/ctools_block.schema.yml
  78. 14 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_block/ctools_block.info.yml
  79. 376 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_block/src/Plugin/Block/EntityField.php
  80. 65 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_block/src/Plugin/Deriver/EntityFieldDeriver.php
  81. 23 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/config/install/core.base_field_override.node.ctools_block_field_test.promote.yml
  82. 62 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/config/install/core.entity_form_display.node.ctools_block_field_test.default.yml
  83. 18 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/config/install/core.entity_view_display.node.ctools_block_field_test.default.yml
  84. 27 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/config/install/core.entity_view_display.node.ctools_block_field_test.teaser.yml
  85. 23 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/config/install/field.field.node.ctools_block_field_test.body.yml
  86. 37 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/config/install/field.field.node.ctools_block_field_test.field_image.yml
  87. 31 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/config/install/field.storage.node.field_image.yml
  88. 18 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/config/install/node.type.ctools_block_field_test.yml
  89. 20 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/ctools_block_field_test.info.yml
  90. 132 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/src/Functional/EntityFieldBlockTest.php
  91. 15 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_views/ctools_views.info.yml
  92. 104 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_views/ctools_views.module
  93. 438 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_views/src/Plugin/Display/Block.php
  94. 350 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_views/src/Tests/CToolsViewsBasicViewBlockTest.php
  95. 20 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_views/tests/modules/ctools_views_test_views/ctools_views_test_views.info.yml
  96. 543 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_views/tests/modules/ctools_views_test_views/test_views/views.view.ctools_views_entity_test.yml
  97. 949 0
      sites/all/modules/contrib/dev/ctools/modules/ctools_views/tests/modules/ctools_views_test_views/test_views/views.view.ctools_views_test_view.yml
  98. 9 0
      sites/all/modules/contrib/dev/ctools/src/Access/AccessInterface.php
  99. 51 0
      sites/all/modules/contrib/dev/ctools/src/Access/TempstoreAccess.php
  100. 23 0
      sites/all/modules/contrib/dev/ctools/src/Ajax/OpenModalWizardCommand.php

+ 339 - 0
sites/all/modules/contrib/admin/pathauto/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.

+ 83 - 0
sites/all/modules/contrib/admin/pathauto/README.md

@@ -0,0 +1,83 @@
+#Pathauto
+
+If you are developing for this module, have a look at pathauto.api.php.
+
+##Description
+
+The Pathauto module provides support functions for other modules to
+automatically generate aliases based on appropriate criteria, with a
+central settings path for site administrators.
+
+Implementations are provided for core entity types: content, taxonomy terms,
+and users (including blogs and forum pages).
+
+Pathauto also provides a way to delete large numbers of aliases.  This feature
+is available at  Administer > Configuration > Search and metadata > URL aliases >
+Delete aliases.
+
+##Benefits
+
+Besides making the page address more reflective of its content than
+"node/138", it's important to know that modern search engines give
+heavy weight to search terms which appear in a page's URL. By
+automatically using keywords based directly on the page content in the URL,
+relevant search engine hits for your page can be significantly
+enhanced.
+
+##Notices
+
+Pathauto just adds URL aliases to content, users, and taxonomy terms.
+Because it's an alias, the standard Drupal URL (for example node/123 or
+taxonomy/term/1) will still function as normal.  If you have external links
+to your site pointing to standard Drupal URLs, or hardcoded links in a module,
+template, content or menu which point to standard Drupal URLs it will bypass
+the alias set by Pathauto.
+
+There are reasons you might not want two URLs for the same content on your
+site. If this applies to you, please note that you will need to update any
+hard coded links in your content or blocks.
+
+If you use the "system path" (i.e. node/10) for menu items and settings like
+that, Drupal will replace it with the url_alias.
+
+For external links, you might want to consider the Path Redirect or
+Global Redirect modules, which allow you to set forwarding either per item or
+across the site to your aliased URLs.
+
+###URLs (not) Getting Replaced With Aliases:
+Please bear in mind that only URLs passed through Drupal's Drupal's URL and
+Link APIs will be replaced with their aliases during page output. If
+a module or your template contains hardcoded links, such as
+'href="node/$node->nid"', those won't get replaced with their corresponding
+aliases.
+
+## Disabling Pathauto for a specific content type (or taxonomy)
+
+When the pattern for a content type is left blank, the default pattern will be
+used. But if the default pattern is also blank, Pathauto will be disabled
+for that content type.
+
+## Installing Pathauto
+1. Install the module as normal, note that there are two dependencies.
+2. Configure the module at admin/config/search/path/patterns - add a new pattern by creating by clicking "Add Pathauto pattern".
+3. Fill out "Path pattern" with fx [node:title], choose which content types this applies to, give it a label (the name) and save it.
+4. When you save new content from now on, it should automatically be assigned an alternative URL
+
+##Credits:
+
+The original module combined the functionality of Mike Ryan's autopath with
+Tommy Sundstrom's path_automatic.
+
+Significant enhancements were contributed by jdmquin @ www.bcdems.net.
+
+Matt England added the tracker support (tracker support has been removed in
+recent changes).
+
+Other suggestions and patches contributed by the Drupal community.
+
+Current maintainers:
+
+- Dave Reid - http://www.davereid.net
+- Greg Knaddison - http://www.knaddison.com
+- Mike Ryan - http://mikeryan.name
+- Frederik 'Freso' S. Olesen - http://freso.dk

+ 13 - 0
sites/all/modules/contrib/admin/pathauto/config/install/pathauto.settings.yml

@@ -0,0 +1,13 @@
+enabled_entity_types:
+  - user
+punctuation:
+  hyphen: 1
+verbose : FALSE
+separator : '-'
+max_length : 100
+max_component_length: 100
+transliterate : TRUE
+reduce_ascii : FALSE
+case : TRUE
+ignore_words : 'a, an, as, at, before, but, by, for, from, is, in, into, like, of, off, on, onto, per, since, than, the, this, that, to, up, via, with'
+update_action : 2

+ 9 - 0
sites/all/modules/contrib/admin/pathauto/config/optional/system.action.pathauto_update_alias_node.yml

@@ -0,0 +1,9 @@
+id: pathauto_update_alias_node
+label: 'Update URL alias'
+status: true
+langcode: en
+type: node
+plugin: pathauto_update_alias
+dependencies:
+  module:
+    - node

+ 9 - 0
sites/all/modules/contrib/admin/pathauto/config/optional/system.action.pathauto_update_alias_user.yml

@@ -0,0 +1,9 @@
+id: pathauto_update_alias_user
+label: 'Update URL alias'
+status: true
+langcode: en
+type: user
+plugin: pathauto_update_alias
+dependencies:
+  module:
+    - user

+ 36 - 0
sites/all/modules/contrib/admin/pathauto/config/schema/pathauto.schema.yml

@@ -0,0 +1,36 @@
+pathauto.settings:
+  type: config_object
+  mapping:
+    enabled_entity_types:
+      label: Enabled entity types
+      type: sequence
+      sequence:
+        type: string
+    punctuation:
+      type: sequence
+      sequence:
+        type: integer
+    verbose:
+      type: boolean
+    separator:
+      type: string
+    max_length:
+      type: integer
+    max_component_length:
+      type: integer
+    transliterate:
+      type: boolean
+    reduce_ascii:
+      type: boolean
+    ignore_words:
+      type: string
+    case:
+      type: boolean
+    ignore_words:
+      type: string
+    update_action:
+      type: integer
+
+action.configuration.pathauto_update_alias:
+  type: action_configuration_default
+  label: 'Update URL alias'

+ 40 - 0
sites/all/modules/contrib/admin/pathauto/config/schema/pathauto_pattern.schema.yml

@@ -0,0 +1,40 @@
+pathauto.pattern.*:
+  type: config_entity
+  label: 'Pathauto pattern config'
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    label:
+      type: label
+      label: 'Label'
+    uuid:
+      type: string
+    type:
+      type: string
+      label: 'Pattern type'
+    pattern:
+      type: string
+      label: 'Pattern'
+    selection_criteria:
+      type: sequence
+      label: 'Selection criteria'
+      sequence:
+        type: condition.plugin.[id]
+        label: 'Selection condition'
+    selection_logic:
+      type: string
+      label: 'Selection logic'
+    weight:
+      type: integer
+      label: 'Weight'
+    relationships:
+      type: sequence
+      label: 'Context definitions'
+      sequence:
+        - type: mapping
+          label: 'Relationship'
+          mapping:
+            label:
+              type: label
+              label: 'Label'

+ 160 - 0
sites/all/modules/contrib/admin/pathauto/pathauto.api.php

@@ -0,0 +1,160 @@
+<?php
+
+/**
+ * @file
+ * Documentation for pathauto API.
+ */
+
+use Drupal\Core\Language\Language;
+
+/**
+ * @todo Update for 8.x-1.x
+ *
+ * It may be helpful to review some examples of integration from
+ * pathauto.pathauto.inc.
+ *
+ * Pathauto works by using tokens in path patterns.  Thus the simplest
+ * integration is just to provide tokens.  Token support is provided by Drupal
+ * core. To provide additional token from your module, implement the following
+ * hooks:
+ *
+ * hook_tokens() - http://api.drupal.org/api/function/hook_tokens
+ * hook_token_info() - http://api.drupal.org/api/function/hook_token_info
+ *
+ * If you wish to provide pathauto integration for custom paths provided by your
+ * module, there are a few steps involved.
+ *
+ * 1. hook_pathauto()
+ *    Provide information required by pathauto for the settings form as well as
+ *    bulk generation.  See the documentation for hook_pathauto() for more
+ *    details.
+ *
+ * 2. pathauto_create_alias()
+ *    At the appropriate time (usually when a new item is being created for
+ *    which a generated alias is desired), call pathauto_create_alias() with the
+ *    appropriate parameters to generate and create the alias. See the user,
+ *    taxonomy, and node hook implementations in pathauto.module for examples.
+ *    Also see the documentation for pathauto_create_alias().
+ *
+ * 3. pathauto_path_delete_all()
+ *    At the appropriate time (usually when an item is being deleted), call
+ *    pathauto_path_delete_all() to remove any aliases that were created for the
+ *    content being removed.  See the documentation for
+ *    pathauto_path_delete_all() for more details.
+ *
+ * 4. hook_path_alias_types()
+ *    For modules that create new types of content that can be aliased with
+ *    pathauto, a hook implementation is needed to allow the user to delete them
+ *    all at once.  See the documentation for hook_path_alias_types() below for
+ *    more information.
+ *
+ * There are other integration points with pathauto, namely alter hooks that
+ * allow you to change the data used by pathauto at various points in the
+ * process.  See the below hook documentation for details.
+ */
+
+/**
+ * Alter pathauto alias type definitions.
+ *
+ * @param array &$definitions
+ *   Alias type definitions.
+ */
+function hook_path_alias_types_alter(array &$definitions) {
+}
+
+/**
+ * Determine if a possible URL alias would conflict with any existing paths.
+ *
+ * Returning TRUE from this function will trigger pathauto_alias_uniquify() to
+ * generate a similar URL alias with a suffix to avoid conflicts.
+ *
+ * @param string $alias
+ *   The potential URL alias.
+ * @param string $source
+ *   The source path for the alias (e.g. 'node/1').
+ * @param string $langcode
+ *   The language code for the alias (e.g. 'en').
+ *
+ * @return bool
+ *   TRUE if $alias conflicts with an existing, reserved path, or FALSE/NULL if
+ *   it does not match any reserved paths.
+ *
+ * @see pathauto_alias_uniquify()
+ */
+function hook_pathauto_is_alias_reserved($alias, $source, $langcode) {
+  // Check our module's list of paths and return TRUE if $alias matches any of
+  // them.
+  return (bool) \Drupal::database()->query("SELECT 1 FROM {mytable} WHERE path = :path", [':path' => $alias])->fetchField();
+}
+
+/**
+ * Alter the pattern to be used before an alias is generated by Pathauto.
+ *
+ * This hook will only be called if a default pattern is configured (on
+ * admin/config/search/path/patterns).
+ *
+ * @param string $pattern
+ *   The alias pattern for Pathauto to pass to token_replace() to generate the
+ *   URL alias.
+ * @param array $context
+ *   An associative array of additional options, with the following elements:
+ *   - 'module': The module or entity type being aliased.
+ *   - 'op': A string with the operation being performed on the object being
+ *     aliased. Can be either 'insert', 'update', 'return', or 'bulkupdate'.
+ *   - 'source': A string of the source path for the alias (e.g. 'node/1').
+ *   - 'data': An array of keyed objects to pass to token_replace().
+ *   - 'type': The sub-type or bundle of the object being aliased.
+ *   - 'language': A string of the language code for the alias (e.g. 'en').
+ *     This can be altered by reference.
+ */
+function hook_pathauto_pattern_alter(&$pattern, array $context) {
+  // Switch out any [node:created:*] tokens with [node:updated:*] on update.
+  if ($context['module'] == 'node' && ($context['op'] == 'update')) {
+    $pattern = preg_replace('/\[node:created(\:[^]]*)?\]/', '[node:updated$1]', $pattern);
+  }
+}
+
+/**
+ * Alter Pathauto-generated aliases before saving.
+ *
+ * @param string $alias
+ *   The automatic alias after token replacement and strings cleaned.
+ * @param array $context
+ *   An associative array of additional options, with the following elements:
+ *   - 'module': The module or entity type being aliased.
+ *   - 'op': A string with the operation being performed on the object being
+ *     aliased. Can be either 'insert', 'update', 'return', or 'bulkupdate'.
+ *   - 'source': A string of the source path for the alias (e.g. 'node/1').
+ *     This can be altered by reference.
+ *   - 'data': An array of keyed objects to pass to token_replace().
+ *   - 'type': The sub-type or bundle of the object being aliased.
+ *   - 'language': A string of the language code for the alias (e.g. 'en').
+ *     This can be altered by reference.
+ *   - 'pattern': A string of the pattern used for aliasing the object.
+ */
+function hook_pathauto_alias_alter(&$alias, array &$context) {
+  // Add a suffix so that all aliases get saved as 'content/my-title.html'
+  $alias .= '.html';
+
+  // Force all aliases to be saved as language neutral.
+  $context['language'] = Language::LANGCODE_NOT_SPECIFIED;
+}
+
+/**
+ * Alter the list of punctuation characters for Pathauto control.
+ *
+ * @param $punctuation
+ *   An array of punctuation to be controlled by Pathauto during replacement
+ *   keyed by punctuation name. Each punctuation record should be an array
+ *   with the following key/value pairs:
+ *   - value: The raw value of the punctuation mark.
+ *   - name: The human-readable name of the punctuation mark. This must be
+ *     translated using t() already.
+ */
+function hook_pathauto_punctuation_chars_alter(array &$punctuation) {
+  // Add the trademark symbol.
+  $punctuation['trademark'] = array('value' => '™', 'name' => t('Trademark symbol'));
+
+  // Remove the dollar sign.
+  unset($punctuation['dollar']);
+}

+ 20 - 0
sites/all/modules/contrib/admin/pathauto/pathauto.info.yml

@@ -0,0 +1,20 @@
+name : 'Pathauto'
+description : 'Provides a mechanism for modules to automatically generate aliases for the content they manage.'
+# core: 8.x
+type: module
+
+dependencies:
+- ctools:ctools
+- drupal:path
+- token:token
+
+configure: entity.pathauto_pattern.collection
+
+recommends:
+- redirect:redirect
+
+# Information added by Drupal.org packaging script on 2018-03-03
+version: '8.x-1.1'
+core: '8.x'
+project: 'pathauto'
+datestamp: 1520092688

+ 301 - 0
sites/all/modules/contrib/admin/pathauto/pathauto.install

@@ -0,0 +1,301 @@
+<?php
+
+/**
+ * @file
+ * Install, update, and uninstall functions for Pathauto.
+ *
+ * @ingroup pathauto
+ */
+
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\Context\ContextDefinition;
+use Drupal\pathauto\Entity\PathautoPattern;
+
+/**
+ * Implements hook_install().
+ */
+function pathauto_install() {
+  // Set the weight to 1
+  module_set_weight('pathauto', 1);
+
+  // Ensure the url_alias table exists.
+  _pathauto_ensure_url_alias_table_exists();
+}
+
+/**
+ * Helper function to ensure the url_alias table exists.
+ *
+ * Only necessary on Drupal 8.1.x.
+ *
+ * @see https://www.drupal.org/node/2704821
+ */
+function _pathauto_ensure_url_alias_table_exists() {
+  $alias_storage = \Drupal::service('path.alias_storage');
+  if (method_exists($alias_storage, 'schemaDefinition')) {
+    $database_schema = \Drupal::database()->schema();
+    if (!$database_schema->tableExists($alias_storage::TABLE)) {
+      $schema_definition = $alias_storage->schemaDefinition();
+      $database_schema->createTable($alias_storage::TABLE, $schema_definition);
+    }
+  }
+}
+
+/**
+ * Updates pathauto widgets to use the path widget ID.
+ */
+function pathauto_update_8001() {
+
+  // Replace values in the 'entity.definitions.installed' keyvalue collection.
+  $collection = \Drupal::service('keyvalue')->get('entity.definitions.installed');
+  foreach ($collection->getAll() as $key => $definitions) {
+    if (!is_array($definitions) || empty($definitions['path'])) {
+      continue;
+    }
+
+    // Retrieve and change path base field definition.
+    $path_definition = $definitions['path'];
+    if (($options = $path_definition->getDisplayOptions('form')) && $options['type'] = 'pathauto') {
+      $options['type'] = 'path';
+      $path_definition->setDisplayOptions('form', $options);
+      // Save the new value.
+      $collection->set($key, $definitions);
+    }
+
+  }
+
+  foreach (EntityFormDisplay::loadMultiple() as $form_display) {
+    if ($component = $form_display->getComponent('path')) {
+      if (isset($component['type']) && $component['type'] == 'pathauto') {
+        $component['type'] = 'path';
+        $form_display->setComponent('path', $component);
+        $form_display->save();
+      }
+    }
+  }
+}
+
+/**
+ * Converts patterns from configuration objects to configuration entities.
+ */
+function pathauto_update_8100() {
+  \Drupal::service('module_installer')->install(['ctools']);
+
+  $messages = array();
+  /** @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_bundle_info */
+  $entity_bundle_info = \Drupal::service('entity_type.bundle.info');
+  $entity_type_manager = \Drupal::entityTypeManager();
+  $language_manager = \Drupal::languageManager();
+  $entity_type_manager->clearCachedDefinitions();
+  \Drupal::service('plugin.manager.alias_type')->clearCachedDefinitions();
+  $entity_types = $entity_type_manager->getDefinitions();
+
+  // 1. Load all patterns.
+  $config = \Drupal::configFactory()->getEditable('pathauto.pattern');
+  $patterns = $config->get('patterns');
+
+  // 2. Create a configuration entity per pattern.
+  foreach ($patterns as $entity_type => $entity_patterns) {
+    if (!array_key_exists($entity_type, $entity_types)) {
+      // We found an unknown entity type. Report it.
+      $messages[] = t('Entity of type @type was not processed. It defines the following patterns: @patterns', array(
+        '@type' => $entity_type,
+        '@patterns' => print_r($entity_patterns, TRUE),
+      ));
+      continue;
+    }
+    $entity_label = $entity_types[$entity_type]->getLabel();
+
+    if (!empty($entity_patterns['default'])) {
+      // This is a pattern for an entity type, such as "node".
+      $pattern = PathautoPattern::create([
+        'id' => $entity_type,
+        'label' => $entity_label,
+        'type' => 'canonical_entities:' . $entity_type,
+        'pattern' => $entity_patterns['default'],
+        'weight' => 0,
+      ]);
+      $pattern->save();
+    }
+
+    // Loop over bundles and create patterns if they have a value.
+    // Bundle keys may have a language suffix for language-dependant patterns.
+    if (isset($entity_patterns['bundles'])) {
+      $bundle_info = $entity_bundle_info->getBundleInfo($entity_type);
+      foreach ($entity_patterns['bundles'] as $bundle => $bundle_patterns) {
+        if (empty($bundle_patterns['default'])) {
+          // This bundle does not define a pattern. Move on to the next one.
+          continue;
+        }
+
+        if (isset($bundle_info[$bundle])) {
+          // This is a pattern for a bundle, such as "node_article".
+          $pattern = PathautoPattern::create([
+            'id' => $entity_type . '_' . $bundle,
+            'label' => $entity_label . ' ' . $bundle_info[$bundle]['label'],
+            'type' => 'canonical_entities:' . $entity_type,
+            'pattern' => $bundle_patterns['default'],
+            'weight' => -5,
+          ]);
+
+          // Add the bundle condition.
+          $pattern->addSelectionCondition([
+            'id' => 'entity_bundle:' . $entity_type,
+            'bundles' => array($bundle => $bundle),
+            'negate' => FALSE,
+            'context_mapping' => [ $entity_type => $entity_type ],
+          ]);
+
+          $pattern->save();
+        }
+        else {
+          // This is either a language dependent pattern such as "article_es" or
+          // an unknown bundle or langcode. Let's figure it out.
+          $matches = NULL;
+          $langcode = NULL;
+          $extracted_bundle = NULL;
+          $language = NULL;
+          preg_match('/^(.*)_([a-z-]*)$/', $bundle, $matches);
+          if (count($matches) == 3) {
+            list(, $extracted_bundle, $langcode) = $matches;
+            $language = $language_manager->getLanguage($langcode);
+          }
+          // Validate bundle, langcode and language.
+          if (!isset($bundle_info[$extracted_bundle]) || ($langcode == NULL) || ($language == NULL)) {
+            $messages[] = t('Unrecognized entity bundle @entity:@bundle was not processed. It defines the following patterns: @patterns', array(
+              '@entity' => $entity_type,
+              '@bundle' => $bundle,
+              '@patterns' => print_r($entity_patterns, TRUE),
+            ));
+            continue;
+          }
+
+          // This is a pattern for a bundle and a language, such as "node_article_es".
+          $pattern = PathautoPattern::create([
+            'id' => $entity_type . '_' . $extracted_bundle . '_' . str_replace('-', '_', $langcode),
+            'label' => $entity_label . ' ' . $bundle_info[$extracted_bundle]['label'] . ' ' . $language->getName(),
+            'type' => 'canonical_entities:' . $entity_type,
+            'pattern' => $bundle_patterns['default'],
+            'weight' => -10,
+          ]);
+
+          // Add the bundle condition.
+          $pattern->addSelectionCondition([
+            'id' => 'entity_bundle:' . $entity_type,
+            'bundles' => array($extracted_bundle => $extracted_bundle),
+            'negate' => FALSE,
+            'context_mapping' => [ $entity_type => $entity_type ],
+          ]);
+
+          // Add the language condition.
+          $language_mapping = $entity_type . ':' . $entity_type_manager->getDefinition($entity_type)->getKey('langcode') . ':language';
+          $pattern->addSelectionCondition([
+            'id' => 'language',
+            'langcodes' => [ $langcode => $langcode ],
+            'negate' => FALSE,
+            'context_mapping' => [
+              'language' => $language_mapping,
+            ]
+          ]);
+
+          // Add the context relationship for this language.
+          $pattern->addRelationship($language_mapping, 'Language');
+
+          $pattern->save();
+        }
+      }
+    }
+  }
+
+  // 3. Delete the old configuration object that stores patterns.
+  $config->delete();
+
+  // 4. Print out messages.
+  if (!empty($messages)) {
+    return implode('</br>', $messages);
+  }
+}
+
+/**
+ * Update relationship storage.
+ */
+function pathauto_update_8101() {
+  foreach (\Drupal::configFactory()->listAll('pathauto.pattern.') as $pattern_config_name) {
+    $pattern_config = \Drupal::configFactory()->getEditable($pattern_config_name);
+
+    $relationships = [];
+    foreach ((array) $pattern_config->get('context_definitions') as $context_definition) {
+      $relationships[$context_definition['id']] = ['label' => $context_definition['label']];
+    }
+
+    $pattern_config->clear('context_definitions');
+    $pattern_config->set('relationships', $relationships);
+    $pattern_config->save();
+  }
+}
+
+/**
+ * Update node type conditions from entity_bundle to node_type.
+ */
+function pathauto_update_8102() {
+  // Load all pattern configuration entities.
+  foreach (\Drupal::configFactory()->listAll('pathauto.pattern.') as $pattern_config_name) {
+    $pattern_config = \Drupal::configFactory()->getEditable($pattern_config_name);
+
+    // Loop patterns and swap the entity_bundle:node plugin by the node_type
+    // plugin.
+    if ($pattern_config->get('type') == 'canonical_entities:node') {
+      $selection_criteria = $pattern_config->get('selection_criteria');
+      foreach ($selection_criteria as $uuid => $condition) {
+        if ($condition['id'] == 'entity_bundle:node') {
+          $selection_criteria[$uuid]['id'] = 'node_type';
+          $pattern_config->set('selection_criteria', $selection_criteria);
+          $pattern_config->save();
+          break;
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Fix invalid default value for ignore_words.
+ */
+function pathauto_update_8103() {
+  $config_factory = \Drupal::configFactory();
+  $config = $config_factory->getEditable('pathauto.settings');
+  $ignore_words = $config->get('ignore_words');
+  if ($ignore_words === ', in, is,that, the  , this, with, ') {
+    $config->set('ignore_words', 'a, an, as, at, before, but, by, for, from, is, in, into, like, of, off, on, onto, per, since, than, the, this, that, to, up, via, with')->save(TRUE);
+  }
+}
+
+/**
+ * Resave patterns so that lookup keys are updated.
+ */
+function pathauto_update_8104() {
+  \Drupal::entityTypeManager()->clearCachedDefinitions();
+  // Load all pattern configuration entities and save them, so that the new
+  // status lookup keys are saved.
+  foreach (\Drupal::configFactory()->listAll('pathauto.pattern.') as $pattern_config_name) {
+    $pattern_config = \Drupal::configFactory()->getEditable($pattern_config_name);
+    $pattern_config->save();
+  }
+}
+
+/**
+ * Ensure the url_alias table exists.
+ */
+function pathauto_update_8105() {
+  _pathauto_ensure_url_alias_table_exists();
+}
+
+/**
+ * Update default configuration for enabled entity types.
+ */
+function pathauto_update_8106() {
+  $config_factory = \Drupal::configFactory();
+  $config = $config_factory->getEditable('pathauto.settings');
+  $config->set('enabled_entity_types', ['user']);
+  $config->save();
+}

+ 21 - 0
sites/all/modules/contrib/admin/pathauto/pathauto.js

@@ -0,0 +1,21 @@
+(function ($) {
+  'use strict';
+  Drupal.behaviors.pathFieldsetSummaries = {
+    attach: function (context) {
+      $('fieldset.path-form', context).drupalSetSummary(function (context) {
+        var path = $('.form-item-path-alias input', context).val();
+        var automatic = $('.form-item-path-pathauto input', context).attr('checked');
+
+        if (automatic) {
+          return Drupal.t('Automatic alias');
+        }
+        else if (path) {
+          return Drupal.t('Alias: @alias', {'@alias': path});
+        }
+        else {
+          return Drupal.t('No alias');
+        }
+      });
+    }
+  };
+})(jQuery);

+ 9 - 0
sites/all/modules/contrib/admin/pathauto/pathauto.libraries.yml

@@ -0,0 +1,9 @@
+widget:
+  version: 1.0
+  js:
+    pathauto.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupalSettings
+    - core/drupal.form

+ 6 - 0
sites/all/modules/contrib/admin/pathauto/pathauto.links.action.yml

@@ -0,0 +1,6 @@
+entity.pathauto_pattern.add_form:
+  route_name: 'entity.pathauto_pattern.add_form'
+  title: 'Add Pathauto pattern'
+  appears_on:
+    - entity.pathauto_pattern.collection
+

+ 23 - 0
sites/all/modules/contrib/admin/pathauto/pathauto.links.task.yml

@@ -0,0 +1,23 @@
+pathauto.patterns.form:
+  route_name: entity.pathauto_pattern.collection
+  base_route: path.admin_overview
+  title: 'Patterns'
+  weight: 10
+
+pathauto.settings.form:
+  route_name: pathauto.settings.form
+  base_route: path.admin_overview
+  title: 'Settings'
+  weight: 20
+
+pathauto.bulk.update.form:
+  route_name: pathauto.bulk.update.form
+  base_route: path.admin_overview
+  title: 'Bulk generate'
+  weight: 30
+
+pathauto.admin.delete:
+  route_name: pathauto.admin.delete
+  base_route: path.admin_overview
+  title: 'Delete aliases'
+  weight: 40

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 57 - 0
sites/all/modules/contrib/admin/pathauto/pathauto.module


+ 6 - 0
sites/all/modules/contrib/admin/pathauto/pathauto.permissions.yml

@@ -0,0 +1,6 @@
+administer pathauto:
+  title: 'Administer pathauto'
+  description: 'Allows a user to configure patterns for automated aliases and bulk delete URL-aliases.'
+notify of path changes:
+  title: 'Notify of Path Changes'
+  description: 'Determines whether or not users are notified.'

+ 54 - 0
sites/all/modules/contrib/admin/pathauto/pathauto.routing.yml

@@ -0,0 +1,54 @@
+entity.pathauto_pattern.collection:
+  path: '/admin/config/search/path/patterns'
+  defaults:
+    _entity_list: 'pathauto_pattern'
+    _title: 'Patterns'
+  requirements:
+    _permission: 'administer pathauto'
+
+entity.pathauto_pattern.add_form:
+  path: '/admin/config/search/path/patterns/add'
+  defaults:
+    _entity_form: 'pathauto_pattern.default'
+    _title: 'Add Pathauto pattern'
+    tempstore_id: 'pathauto.pattern'
+  requirements:
+    _permission: 'administer pathauto'
+
+pathauto.settings.form:
+  path: '/admin/config/search/path/settings'
+  defaults:
+    _form: '\Drupal\pathauto\Form\PathautoSettingsForm'
+    _title: 'Settings'
+  requirements:
+    _permission: 'administer pathauto'
+
+entity.pathauto_pattern.enable:
+  path: '/admin/config/search/path/patterns/{pathauto_pattern}/enable'
+  defaults:
+    _entity_form: 'pathauto_pattern.enable'
+  requirements:
+    _entity_access: 'pathauto_pattern.update'
+
+entity.pathauto_pattern.disable:
+  path: '/admin/config/search/path/patterns/{pathauto_pattern}/disable'
+  defaults:
+    _entity_form: 'pathauto_pattern.disable'
+  requirements:
+    _entity_access: 'pathauto_pattern.update'
+
+pathauto.bulk.update.form:
+  path: '/admin/config/search/path/update_bulk'
+  defaults:
+    _form: '\Drupal\pathauto\Form\PathautoBulkUpdateForm'
+    _title: 'Bulk generate'
+  requirements:
+    _permission: 'administer url aliases'
+
+pathauto.admin.delete:
+  path: '/admin/config/search/path/delete_bulk'
+  defaults:
+    _form: '\Drupal\pathauto\Form\PathautoAdminDelete'
+    _title: 'Delete aliases'
+  requirements:
+    _permission: 'administer url aliases'

+ 26 - 0
sites/all/modules/contrib/admin/pathauto/pathauto.services.yml

@@ -0,0 +1,26 @@
+services:
+  pathauto.generator:
+    class: Drupal\pathauto\PathautoGenerator
+    arguments: ['@config.factory', '@module_handler', '@token', '@pathauto.alias_cleaner', '@pathauto.alias_storage_helper', '@pathauto.alias_uniquifier', '@pathauto.verbose_messenger', '@string_translation', '@token.entity_mapper', '@entity_type.manager']
+  pathauto.alias_cleaner:
+    class: Drupal\pathauto\AliasCleaner
+    arguments: ['@config.factory', '@pathauto.alias_storage_helper', '@language_manager', '@cache.discovery', '@transliteration', '@module_handler']
+  pathauto.alias_storage_helper:
+    class: Drupal\pathauto\AliasStorageHelper
+    arguments: ['@config.factory', '@path.alias_storage', '@database','@pathauto.verbose_messenger', '@string_translation']
+    tags:
+      - { name: backend_overridable }
+  pathauto.alias_uniquifier:
+    class: Drupal\pathauto\AliasUniquifier
+    arguments: ['@config.factory', '@pathauto.alias_storage_helper','@module_handler', '@router.route_provider', '@path.alias_manager']
+  pathauto.verbose_messenger:
+    class: Drupal\pathauto\VerboseMessenger
+    arguments: ['@config.factory', '@current_user']
+  plugin.manager.alias_type:
+    class: Drupal\pathauto\AliasTypeManager
+    parent: default_plugin_manager
+  pathauto.settings_cache_tag:
+    class: Drupal\pathauto\EventSubscriber\PathautoSettingsCacheTag
+    arguments: ['@entity_field.manager', '@plugin.manager.alias_type']
+    tags:
+      - { name: event_subscriber }

+ 49 - 0
sites/all/modules/contrib/admin/pathauto/pathauto.tokens.inc

@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * @file
+ * Token integration for the Pathauto module.
+ */
+
+use Drupal\Core\Render\BubbleableMetadata;
+
+/**
+ * Implements hook_token_info().
+ */
+function pathauto_token_info() {
+  $info = array();
+
+  $info['tokens']['array']['join-path'] = array(
+    'name' => t('Joined path'),
+    'description' => t('The array values each cleaned by Pathauto and then joined with the slash into a string that resembles an URL.'),
+  );
+
+  return $info;
+}
+
+/**
+ * Implements hook_tokens().
+ */
+function pathauto_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
+  $replacements = array();
+
+  if ($type == 'array' && !empty($data['array'])) {
+    $array = $data['array'];
+
+    foreach ($tokens as $name => $original) {
+      switch ($name) {
+        case 'join-path':
+          $values = array();
+          foreach (token_element_children($array) as $key) {
+            $value = is_array($array[$key]) ? render($array[$key]) : (string) $array[$key];
+            $value = \Drupal::service('pathauto.alias_cleaner')->cleanString($value, $options);
+            $values[] = $value;
+          }
+          $replacements[$original] = implode('/', $values);
+          break;
+      }
+    }
+  }
+
+  return $replacements;
+}

+ 351 - 0
sites/all/modules/contrib/admin/pathauto/src/AliasCleaner.php

@@ -0,0 +1,351 @@
+<?php
+
+namespace Drupal\pathauto;
+
+use Drupal\Component\Render\PlainTextOutput;
+use Drupal\Component\Transliteration\TransliterationInterface;
+use Drupal\Component\Utility\Html;
+use Drupal\Component\Utility\Unicode;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
+
+/**
+ * Provides an alias cleaner.
+ */
+class AliasCleaner implements AliasCleanerInterface {
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The alias storage helper.
+   *
+   * @var AliasStorageHelperInterface
+   */
+  protected $aliasStorageHelper;
+
+  /**
+   * Language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * Cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $cacheBackend;
+
+  /**
+   * Calculated settings cache.
+   *
+   * @todo Split this up into separate properties.
+   *
+   * @var array
+   */
+  protected $cleanStringCache = array();
+
+  /**
+   * Transliteration service.
+   *
+   * @var \Drupal\Component\Transliteration\TransliterationInterface
+   */
+  protected $transliteration;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * Creates a new AliasCleaner.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory.
+   * @param \Drupal\pathauto\AliasStorageHelperInterface $alias_storage_helper
+   *   The alias storage helper.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   The cache backend.
+   * @param \Drupal\Component\Transliteration\TransliterationInterface $transliteration
+   *   The transliteration service.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, AliasStorageHelperInterface $alias_storage_helper, LanguageManagerInterface $language_manager, CacheBackendInterface $cache_backend, TransliterationInterface $transliteration, ModuleHandlerInterface $module_handler) {
+    $this->configFactory = $config_factory;
+    $this->aliasStorageHelper = $alias_storage_helper;
+    $this->languageManager = $language_manager;
+    $this->cacheBackend = $cache_backend;
+    $this->transliteration = $transliteration;
+    $this->moduleHandler = $module_handler;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cleanAlias($alias) {
+    $config = $this->configFactory->get('pathauto.settings');
+    $alias_max_length = min($config->get('max_length'), $this->aliasStorageHelper->getAliasSchemaMaxLength());
+
+    $output = $alias;
+
+    // Trim duplicate, leading, and trailing separators. Do this before cleaning
+    // backslashes since a pattern like "[token1]/[token2]-[token3]/[token4]"
+    // could end up like "value1/-/value2" and if backslashes were cleaned first
+    // this would result in a duplicate blackslash.
+    $output = $this->getCleanSeparators($output);
+
+    // Trim duplicate, leading, and trailing backslashes.
+    $output = $this->getCleanSeparators($output, '/');
+
+    // Shorten to a logical place based on word boundaries.
+    $output = Unicode::truncate($output, $alias_max_length, TRUE);
+
+    return $output;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCleanSeparators($string, $separator = NULL) {
+    $config = $this->configFactory->get('pathauto.settings');
+
+    if (!isset($separator)) {
+      $separator = $config->get('separator');
+    }
+
+    $output = $string;
+
+    if (strlen($separator)) {
+      // Trim any leading or trailing separators.
+      $output = trim($output, $separator);
+
+      // Escape the separator for use in regular expressions.
+      $seppattern = preg_quote($separator, '/');
+
+      // Replace multiple separators with a single one.
+      $output = preg_replace("/$seppattern+/", $separator, $output);
+
+      // Replace trailing separators around slashes.
+      if ($separator !== '/') {
+        $output = preg_replace("/\/+$seppattern\/+|$seppattern\/+|\/+$seppattern/", "/", $output);
+      }
+      else {
+        // If the separator is a slash, we need to re-add the leading slash
+        // dropped by the trim function.
+        $output = '/' . $output;
+      }
+    }
+
+    return $output;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cleanString($string, array $options = array()) {
+    if (empty($this->cleanStringCache)) {
+      // Generate and cache variables used in this method.
+      $config = $this->configFactory->get('pathauto.settings');
+      $this->cleanStringCache = array(
+        'separator' => $config->get('separator'),
+        'strings' => array(),
+        'transliterate' => $config->get('transliterate'),
+        'punctuation' => array(),
+        'reduce_ascii' => (bool) $config->get('reduce_ascii'),
+        'ignore_words_regex' => FALSE,
+        'lowercase' => (bool) $config->get('case'),
+        'maxlength' => min($config->get('max_component_length'), $this->aliasStorageHelper->getAliasSchemaMaxLength()),
+      );
+
+      // Generate and cache the punctuation replacements for strtr().
+      $punctuation = $this->getPunctuationCharacters();
+      foreach ($punctuation as $name => $details) {
+        $action = $config->get('punctuation.' . $name);
+        switch ($action) {
+          case PathautoGeneratorInterface::PUNCTUATION_REMOVE:
+            $this->cleanStringCache['punctuation'][$details['value']] = '';
+            break;
+
+          case PathautoGeneratorInterface::PUNCTUATION_REPLACE:
+            $this->cleanStringCache['punctuation'][$details['value']] = $this->cleanStringCache['separator'];
+            break;
+
+          case PathautoGeneratorInterface::PUNCTUATION_DO_NOTHING:
+            // Literally do nothing.
+            break;
+        }
+      }
+
+      // Generate and cache the ignored words regular expression.
+      $ignore_words = $config->get('ignore_words');
+      $ignore_words_regex = preg_replace(array('/^[,\s]+|[,\s]+$/', '/[,\s]+/'), array('', '\b|\b'), $ignore_words);
+      if ($ignore_words_regex) {
+        $this->cleanStringCache['ignore_words_regex'] = '\b' . $ignore_words_regex . '\b';
+        if (function_exists('mb_eregi_replace')) {
+          mb_regex_encoding('UTF-8');
+          $this->cleanStringCache['ignore_words_callback'] = 'mb_eregi_replace';
+        }
+        else {
+          $this->cleanStringCache['ignore_words_callback'] = 'preg_replace';
+          $this->cleanStringCache['ignore_words_regex'] = '/' . $this->cleanStringCache['ignore_words_regex'] . '/i';
+        }
+      }
+    }
+
+    // Empty strings do not need any processing.
+    if ($string === '' || $string === NULL) {
+      return '';
+    }
+
+    $langcode = NULL;
+    if (!empty($options['language'])) {
+      $langcode = $options['language']->getId();
+    }
+    elseif (!empty($options['langcode'])) {
+      $langcode = $options['langcode'];
+    }
+
+    // Check if the string has already been processed, and if so return the
+    // cached result.
+    if (isset($this->cleanStringCache['strings'][$langcode][(string) $string])) {
+      return $this->cleanStringCache['strings'][$langcode][(string) $string];
+    }
+
+    // Remove all HTML tags from the string.
+    $output = Html::decodeEntities($string);
+    $output = PlainTextOutput::renderFromHtml($output);
+
+    // Optionally transliterate.
+    if ($this->cleanStringCache['transliterate']) {
+      // If the reduce strings to letters and numbers is enabled, don't bother
+      // replacing unknown characters with a question mark. Use an empty string
+      // instead.
+      $output = $this->transliteration->transliterate($output, $langcode, $this->cleanStringCache['reduce_ascii'] ? '' : '?');
+    }
+
+    // Replace or drop punctuation based on user settings.
+    $output = strtr($output, $this->cleanStringCache['punctuation']);
+
+    // Reduce strings to letters and numbers.
+    if ($this->cleanStringCache['reduce_ascii']) {
+      $output = preg_replace('/[^a-zA-Z0-9\/]+/', $this->cleanStringCache['separator'], $output);
+    }
+
+    // Get rid of words that are on the ignore list.
+    if ($this->cleanStringCache['ignore_words_regex']) {
+      $words_removed = $this->cleanStringCache['ignore_words_callback']($this->cleanStringCache['ignore_words_regex'], '', $output);
+      if (Unicode::strlen(trim($words_removed)) > 0) {
+        $output = $words_removed;
+      }
+    }
+
+    // Always replace whitespace with the separator.
+    $output = preg_replace('/\s+/', $this->cleanStringCache['separator'], $output);
+
+    // Trim duplicates and remove trailing and leading separators.
+    $output = $this->getCleanSeparators($this->getCleanSeparators($output, $this->cleanStringCache['separator']));
+
+    // Optionally convert to lower case.
+    if ($this->cleanStringCache['lowercase']) {
+      $output = Unicode::strtolower($output);
+    }
+
+    // Shorten to a logical place based on word boundaries.
+    $output = Unicode::truncate($output, $this->cleanStringCache['maxlength'], TRUE);
+
+    // Cache this result in the static array.
+    $this->cleanStringCache['strings'][$langcode][(string) $string] = $output;
+
+    return $output;
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPunctuationCharacters() {
+    if (empty($this->punctuationCharacters)) {
+      $langcode = $this->languageManager->getCurrentLanguage()->getId();
+
+      $cid = 'pathauto:punctuation:' . $langcode;
+      if ($cache = $this->cacheBackend->get($cid)) {
+        $this->punctuationCharacters = $cache->data;
+      }
+      else {
+        $punctuation = array();
+        $punctuation['double_quotes']      = array('value' => '"', 'name' => t('Double quotation marks'));
+        $punctuation['quotes']             = array('value' => '\'', 'name' => t("Single quotation marks (apostrophe)"));
+        $punctuation['backtick']           = array('value' => '`', 'name' => t('Back tick'));
+        $punctuation['comma']              = array('value' => ',', 'name' => t('Comma'));
+        $punctuation['period']             = array('value' => '.', 'name' => t('Period'));
+        $punctuation['hyphen']             = array('value' => '-', 'name' => t('Hyphen'));
+        $punctuation['underscore']         = array('value' => '_', 'name' => t('Underscore'));
+        $punctuation['colon']              = array('value' => ':', 'name' => t('Colon'));
+        $punctuation['semicolon']          = array('value' => ';', 'name' => t('Semicolon'));
+        $punctuation['pipe']               = array('value' => '|', 'name' => t('Vertical bar (pipe)'));
+        $punctuation['left_curly']         = array('value' => '{', 'name' => t('Left curly bracket'));
+        $punctuation['left_square']        = array('value' => '[', 'name' => t('Left square bracket'));
+        $punctuation['right_curly']        = array('value' => '}', 'name' => t('Right curly bracket'));
+        $punctuation['right_square']       = array('value' => ']', 'name' => t('Right square bracket'));
+        $punctuation['plus']               = array('value' => '+', 'name' => t('Plus sign'));
+        $punctuation['equal']              = array('value' => '=', 'name' => t('Equal sign'));
+        $punctuation['asterisk']           = array('value' => '*', 'name' => t('Asterisk'));
+        $punctuation['ampersand']          = array('value' => '&', 'name' => t('Ampersand'));
+        $punctuation['percent']            = array('value' => '%', 'name' => t('Percent sign'));
+        $punctuation['caret']              = array('value' => '^', 'name' => t('Caret'));
+        $punctuation['dollar']             = array('value' => '$', 'name' => t('Dollar sign'));
+        $punctuation['hash']               = array('value' => '#', 'name' => t('Number sign (pound sign, hash)'));
+        $punctuation['at']                 = array('value' => '@', 'name' => t('At sign'));
+        $punctuation['exclamation']        = array('value' => '!', 'name' => t('Exclamation mark'));
+        $punctuation['tilde']              = array('value' => '~', 'name' => t('Tilde'));
+        $punctuation['left_parenthesis']   = array('value' => '(', 'name' => t('Left parenthesis'));
+        $punctuation['right_parenthesis']  = array('value' => ')', 'name' => t('Right parenthesis'));
+        $punctuation['question_mark']      = array('value' => '?', 'name' => t('Question mark'));
+        $punctuation['less_than']          = array('value' => '<', 'name' => t('Less-than sign'));
+        $punctuation['greater_than']       = array('value' => '>', 'name' => t('Greater-than sign'));
+        $punctuation['slash']              = array('value' => '/', 'name' => t('Slash'));
+        $punctuation['back_slash']         = array('value' => '\\', 'name' => t('Backslash'));
+
+        // Allow modules to alter the punctuation list and cache the result.
+        $this->moduleHandler->alter('pathauto_punctuation_chars', $punctuation);
+        $this->cacheBackend->set($cid, $punctuation);
+        $this->punctuationCharacters = $punctuation;
+      }
+    }
+
+    return $this->punctuationCharacters;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function cleanTokenValues(&$replacements, $data = array(), $options = array()) {
+    foreach ($replacements as $token => $value) {
+      // Only clean non-path tokens.
+      if (!preg_match('/(path|alias|url|url-brief)\]$/', $token)) {
+        $replacements[$token] = $this->cleanString($value, $options);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function resetCaches() {
+    $this->cleanStringCache = array();
+  }
+
+}

+ 103 - 0
sites/all/modules/contrib/admin/pathauto/src/AliasCleanerInterface.php

@@ -0,0 +1,103 @@
+<?php
+
+namespace Drupal\pathauto;
+
+/**
+ * @todo add class comment.
+ */
+interface AliasCleanerInterface {
+
+  /**
+   * Clean up an URL alias.
+   *
+   * Performs the following alterations:
+   * - Trim duplicate, leading, and trailing back-slashes.
+   * - Trim duplicate, leading, and trailing separators.
+   * - Shorten to a desired length and logical position based on word boundaries.
+   *
+   * @param string $alias
+   *   A string with the URL alias to clean up.
+   *
+   * @return string
+   *   The cleaned URL alias.
+   */
+  public function cleanAlias($alias);
+
+  /**
+   * Trims duplicate, leading, and trailing separators from a string.
+   *
+   * @param string $string
+   *   The string to clean path separators from.
+   * @param string $separator
+   *   The path separator to use when cleaning.
+   *
+   * @return string
+   *   The cleaned version of the string.
+   *
+   * @see pathauto_cleanstring()
+   * @see pathauto_clean_alias()
+   */
+  public function getCleanSeparators($string, $separator = NULL);
+
+  /**
+   * Clean up a string segment to be used in an URL alias.
+   *
+   * Performs the following possible alterations:
+   * - Remove all HTML tags.
+   * - Process the string through the transliteration module.
+   * - Replace or remove punctuation with the separator character.
+   * - Remove back-slashes.
+   * - Replace non-ascii and non-numeric characters with the separator.
+   * - Remove common words.
+   * - Replace whitespace with the separator character.
+   * - Trim duplicate, leading, and trailing separators.
+   * - Convert to lower-case.
+   * - Shorten to a desired length and logical position based on word boundaries.
+   *
+   * This function should *not* be called on URL alias or path strings
+   * because it is assumed that they are already clean.
+   *
+   * @param string $string
+   *   A string to clean.
+   * @param array $options
+   *   (optional) A keyed array of settings and flags to control the Pathauto
+   *   clean string replacement process. Supported options are:
+   *   - langcode: A language code to be used when translating strings.
+   *
+   * @return string
+   *   The cleaned string.
+   */
+  public function cleanString($string, array $options = array());
+
+  /**
+   * Return an array of arrays for punctuation values.
+   *
+   * Returns an array of arrays for punctuation values keyed by a name, including
+   * the value and a textual description.
+   * Can and should be expanded to include "all" non text punctuation values.
+   *
+   * @return array
+   *   An array of arrays for punctuation values keyed by a name, including the
+   *   value and a textual description.
+   */
+  public function getPunctuationCharacters();
+
+  /**
+   * Clean tokens so they are URL friendly.
+   *
+   * @param array $replacements
+   *   An array of token replacements
+   *   that need to be "cleaned" for use in the URL.
+   * @param array $data
+   *   An array of objects used to generate the replacements.
+   * @param array $options
+   *   An array of options used to generate the replacements.
+   */
+  public function cleanTokenValues(&$replacements, $data = array(), $options = array());
+
+  /**
+   * Resets internal caches.
+   */
+  public function resetCaches();
+
+}

+ 255 - 0
sites/all/modules/contrib/admin/pathauto/src/AliasStorageHelper.php

@@ -0,0 +1,255 @@
+<?php
+
+namespace Drupal\pathauto;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Path\AliasStorageInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+
+/**
+ * Provides helper methods for accessing alias storage.
+ */
+class AliasStorageHelper implements AliasStorageHelperInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * Alias schema max length.
+   *
+   * @var int
+   */
+  protected $aliasSchemaMaxLength = 255;
+
+  /**
+   * Config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The alias storage.
+   *
+   * @var \Drupal\Core\Path\AliasStorageInterface
+   */
+  protected $aliasStorage;
+
+  /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $database;
+
+  /**
+   * The messenger.
+   *
+   * @var \Drupal\pathauto\MessengerInterface
+   */
+  protected $messenger;
+
+  /**
+   * The config factory.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory.
+   * @param \Drupal\Core\Path\AliasStorageInterface $alias_storage
+   *   The alias storage.
+   * @param \Drupal\Core\Database\Connection $database
+   *   The database connection.
+   * @param MessengerInterface $messenger
+   *   The messenger.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   *   The string translation service.
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, AliasStorageInterface $alias_storage, Connection $database, MessengerInterface $messenger, TranslationInterface $string_translation) {
+    $this->configFactory = $config_factory;
+    $this->aliasStorage = $alias_storage;
+    $this->database = $database;
+    $this->messenger = $messenger;
+    $this->stringTranslation = $string_translation;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAliasSchemaMaxLength() {
+    return $this->aliasSchemaMaxLength;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save(array $path, $existing_alias = NULL, $op = NULL) {
+    $config = $this->configFactory->get('pathauto.settings');
+
+    // Alert users if they are trying to create an alias that is the same as the
+    // internal path.
+    if ($path['source'] == $path['alias']) {
+      $this->messenger->addMessage($this->t('Ignoring alias %alias because it is the same as the internal path.', array('%alias' => $path['alias'])));
+      return NULL;
+    }
+
+    // Skip replacing the current alias with an identical alias.
+    if (empty($existing_alias) || $existing_alias['alias'] != $path['alias']) {
+      $path += array(
+        'pathauto' => TRUE,
+        'original' => $existing_alias,
+        'pid' => NULL,
+      );
+
+      // If there is already an alias, respect some update actions.
+      if (!empty($existing_alias)) {
+        switch ($config->get('update_action')) {
+          case PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW:
+            // Do not create the alias.
+            return NULL;
+
+          case PathautoGeneratorInterface::UPDATE_ACTION_LEAVE:
+            // Create a new alias instead of overwriting the existing by leaving
+            // $path['pid'] empty.
+            break;
+
+          case PathautoGeneratorInterface::UPDATE_ACTION_DELETE:
+            // The delete actions should overwrite the existing alias.
+            $path['pid'] = $existing_alias['pid'];
+            break;
+        }
+      }
+
+      // Save the path array.
+      $this->aliasStorage->save($path['source'], $path['alias'], $path['language'], $path['pid']);
+
+      if (!empty($existing_alias['pid'])) {
+        $this->messenger->addMessage($this->t(
+            'Created new alias %alias for %source, replacing %old_alias.',
+            array(
+              '%alias' => $path['alias'],
+              '%source' => $path['source'],
+              '%old_alias' => $existing_alias['alias'],
+            )
+          )
+        );
+      }
+      else {
+        $this->messenger->addMessage($this->t('Created new alias %alias for %source.', array(
+          '%alias' => $path['alias'],
+          '%source' => $path['source'],
+        )));
+      }
+
+      return $path;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function loadBySource($source, $language = LanguageInterface::LANGCODE_NOT_SPECIFIED) {
+    $alias = $this->aliasStorage->load([
+      'source' => $source,
+      'langcode' => $language,
+    ]);
+    // If no alias was fetched and if a language was specified, fallbacks to
+    // undefined language.
+    if (!$alias && ($language !== LanguageInterface::LANGCODE_NOT_SPECIFIED)) {
+      $alias = $this->aliasStorage->load([
+        'source' => $source,
+        'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
+      ]);
+    }
+    return $alias;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteBySourcePrefix($source) {
+    $pids = $this->loadBySourcePrefix($source);
+    if ($pids) {
+      $this->deleteMultiple($pids);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteAll() {
+    $this->database->truncate('url_alias')->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteEntityPathAll(EntityInterface $entity, $default_uri = NULL) {
+    $this->deleteBySourcePrefix('/' . $entity->toUrl('canonical')->getInternalPath());
+    if (isset($default_uri) && $entity->toUrl('canonical')->toString() != $default_uri) {
+      $this->deleteBySourcePrefix($default_uri);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function loadBySourcePrefix($source) {
+    $select = $this->database->select('url_alias', 'u')
+      ->fields('u', array('pid'));
+
+    $or_group = $select->orConditionGroup()
+      ->condition('source', $source)
+      ->condition('source', rtrim($source, '/') . '/%', 'LIKE');
+
+    return $select
+      ->condition($or_group)
+      ->execute()
+      ->fetchCol();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function countBySourcePrefix($source) {
+    $select = $this->database->select('url_alias', 'u')
+      ->fields('u', array('pid'));
+
+    $or_group = $select->orConditionGroup()
+      ->condition('source', $source)
+      ->condition('source', rtrim($source, '/') . '/%', 'LIKE');
+
+    return $select
+      ->condition($or_group)
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function countAll() {
+    return $this->database->select('url_alias')
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+  }
+
+  /**
+   * Delete multiple URL aliases.
+   *
+   * Intent of this is to abstract a potential path_delete_multiple() function
+   * for Drupal 7 or 8.
+   *
+   * @param int[] $pids
+   *   An array of path IDs to delete.
+   */
+  public function deleteMultiple($pids) {
+    foreach ($pids as $pid) {
+      $this->aliasStorage->delete(array('pid' => $pid));
+    }
+  }
+
+}

+ 117 - 0
sites/all/modules/contrib/admin/pathauto/src/AliasStorageHelperInterface.php

@@ -0,0 +1,117 @@
+<?php
+
+namespace Drupal\pathauto;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Language\LanguageInterface;
+
+/**
+ * Provides helper methods for accessing alias storage.
+ */
+interface AliasStorageHelperInterface {
+
+  /**
+   * Fetch the maximum length of the {url_alias}.alias field from the schema.
+   *
+   * @return int
+   *   An integer of the maximum URL alias length allowed by the database.
+   */
+  public function getAliasSchemaMaxLength();
+
+  /**
+   * Private function for Pathauto to create an alias.
+   *
+   * @param array $path
+   *   An associative array containing the following keys:
+   *   - source: The internal system path.
+   *   - alias: The URL alias.
+   *   - pid: (optional) Unique path alias identifier.
+   *   - language: (optional) The language of the alias.
+   * @param array|bool|null $existing_alias
+   *   (optional) An associative array of the existing path alias.
+   * @param string $op
+   *   An optional string with the operation being performed.
+   *
+   * @return array|bool
+   *   The saved path or NULL if the path was not saved.
+   */
+  public function save(array $path, $existing_alias = NULL, $op = NULL);
+
+  /**
+   * Fetches an existing URL alias given a path and optional language.
+   *
+   * @param string $source
+   *   An internal Drupal path.
+   * @param string $language
+   *   An optional language code to look up the path in.
+   *
+   * @return bool|array
+   *   FALSE if no alias was found or an associative array containing the
+   *   following keys:
+   *   - source (string): The internal system path with a starting slash.
+   *   - alias (string): The URL alias with a starting slash.
+   *   - pid (int): Unique path alias identifier.
+   *   - langcode (string): The language code of the alias.
+   */
+  public function loadBySource($source, $language = LanguageInterface::LANGCODE_NOT_SPECIFIED);
+
+  /**
+   * Delete all aliases by source url.
+   *
+   * @param string $source
+   *   An internal Drupal path.
+   *
+   * @return bool
+   *   The URL alias source.
+   */
+  public function deleteBySourcePrefix($source);
+
+  /**
+   * Delete all aliases (truncate the url_alias table).
+   */
+  public function deleteAll();
+
+  /**
+   * Delete an entity URL alias and any of its sub-paths.
+   *
+   * This function also checks to see if the default entity URI is different
+   * from the current entity URI and will delete any of the default aliases.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   An entity object.
+   * @param string $default_uri
+   *   The optional default uri path for the entity.
+   */
+  public function deleteEntityPathAll(EntityInterface $entity, $default_uri = NULL);
+
+  /**
+   * Fetches an existing URL alias given a path prefix.
+   *
+   * @param string $source
+   *   An internal Drupal path prefix.
+   *
+   * @return integer[]
+   *   An array of PIDs.
+   */
+  public function loadBySourcePrefix($source);
+
+
+  /**
+   * Returns the count of url aliases for the source.
+   *
+   * @param $source
+   *   An internal Drupal path prefix.
+   *
+   * @return int
+   *   Number of url aliases for the source.
+   */
+  public function countBySourcePrefix($source);
+
+  /**
+   * Returns the total count of the url aliases.
+   *
+   * @return int
+   *   Total number of aliases.
+   */
+  public function countAll();
+
+}

+ 31 - 0
sites/all/modules/contrib/admin/pathauto/src/AliasTypeBatchUpdateInterface.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace Drupal\pathauto;
+
+/**
+ * Alias types that support batch updates and deletions.
+ */
+interface AliasTypeBatchUpdateInterface extends AliasTypeInterface {
+
+  /**
+   * Gets called to batch update all entries.
+   *
+   * @param string $action
+   *   One of:
+   *   - 'create' to generate a URL alias for paths having none.
+   *   - 'update' to recreate the URL alias for paths already having one, useful if the pattern changed.
+   *   - 'all' to do both actions above at the same time.
+   * @param array $context
+   *   Batch context.
+   */
+  public function batchUpdate($action, &$context);
+
+  /**
+   * Gets called to batch delete all aliases created by pathauto.
+   *
+   * @param array $context
+   *   Batch context.
+   */
+  public function batchDelete(&$context);
+
+}

+ 48 - 0
sites/all/modules/contrib/admin/pathauto/src/AliasTypeInterface.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace Drupal\pathauto;
+
+use Drupal\Component\Plugin\DerivativeInspectionInterface;
+use Drupal\Core\Plugin\ContextAwarePluginInterface;
+
+/**
+ * Provides an interface for pathauto alias types.
+ */
+interface AliasTypeInterface extends ContextAwarePluginInterface, DerivativeInspectionInterface {
+
+  /**
+   * Get the label.
+   *
+   * @return string
+   *   The label.
+   */
+  public function getLabel();
+
+  /**
+   * Get the token types.
+   *
+   * @return string[]
+   *   The token types.
+   */
+  public function getTokenTypes();
+
+  /**
+   * Returns the source prefix; used for bulk delete.
+   *
+   * @return string
+   *   The source path prefix.
+   */
+  public function getSourcePrefix();
+
+  /**
+   * Determines if this plugin type can apply a given object.
+   *
+   * @param object $object
+   *   The object used to determine if this plugin can apply.
+   *
+   * @return bool
+   *   Whether this plugin applies to the given object.
+   */
+  public function applies($object);
+
+}

+ 71 - 0
sites/all/modules/contrib/admin/pathauto/src/AliasTypeManager.php

@@ -0,0 +1,71 @@
+<?php
+
+namespace Drupal\pathauto;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\Component\Plugin\FallbackPluginManagerInterface;
+
+/**
+ * Manages pathauto alias type plugins.
+ */
+class AliasTypeManager extends DefaultPluginManager implements FallbackPluginManagerInterface {
+
+  /**
+   * Constructs a new AliasType manager instance.
+   *
+   * @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) {
+    parent::__construct('Plugin/pathauto/AliasType', $namespaces, $module_handler, 'Drupal\pathauto\AliasTypeInterface', 'Drupal\pathauto\Annotation\AliasType');
+    $this->alterInfo('pathauto_alias_types');
+    $this->setCacheBackend($cache_backend, 'pathauto_alias_types');
+  }
+
+  /**
+   * Returns plugin definitions that support a given token type.
+   *
+   * @param string $type
+   *   The type of token plugin must support to be useful.
+   *
+   * @return array
+   *   Plugin definitions.
+   */
+  public function getPluginDefinitionByType($type) {
+    $definitions = array_filter($this->getDefinitions(), function ($definition) use ($type) {
+      if (!empty($definition['types']) && in_array($type, $definition['types'])) {
+        return TRUE;
+      }
+      return FALSE;
+    });
+    return $definitions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFallbackPluginId($plugin_id, array $configuration = array()) {
+    return 'broken';
+  }
+
+  /**
+   * Gets the definition of all visible plugins for this type.
+   *
+   * @return array
+   *   An array of plugin definitions (empty array if no definitions were
+   *   found). Keys are plugin IDs.
+   */
+  public function getVisibleDefinitions() {
+    $definitions = $this->getDefinitions();
+    unset($definitions['broken']);
+    return $definitions;
+  }
+
+}

+ 167 - 0
sites/all/modules/contrib/admin/pathauto/src/AliasUniquifier.php

@@ -0,0 +1,167 @@
+<?php
+
+namespace Drupal\pathauto;
+
+use Drupal\Component\Utility\Unicode;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Path\AliasManagerInterface;
+use Drupal\Core\Routing\RouteProviderInterface;
+
+/**
+ * Provides a utility for creating a unique path alias.
+ */
+class AliasUniquifier implements AliasUniquifierInterface {
+
+  /**
+   * Config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The alias storage helper.
+   *
+   * @var \Drupal\pathauto\AliasStorageHelperInterface
+   */
+  protected $aliasStorageHelper;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The route provider service.
+   *
+   * @var \Drupal\Core\Routing\RouteProviderInterface.
+   */
+  protected $routeProvider;
+
+  /**
+   * The alias manager.
+   *
+   * @var \Drupal\Core\Path\AliasManagerInterface
+   */
+  protected $aliasManager;
+
+  /**
+   * Creates a new AliasUniquifier.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory.
+   * @param \Drupal\pathauto\AliasStorageHelperInterface $alias_storage_helper
+   *   The alias storage helper.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
+   *   The route provider service.
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, AliasStorageHelperInterface $alias_storage_helper, ModuleHandlerInterface $module_handler, RouteProviderInterface $route_provider, AliasManagerInterface $alias_manager) {
+    $this->configFactory = $config_factory;
+    $this->aliasStorageHelper = $alias_storage_helper;
+    $this->moduleHandler = $module_handler;
+    $this->routeProvider = $route_provider;
+    $this->aliasManager = $alias_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function uniquify(&$alias, $source, $langcode) {
+    $config = $this->configFactory->get('pathauto.settings');
+
+    if (!$this->isReserved($alias, $source, $langcode)) {
+      return;
+    }
+
+    // If the alias already exists, generate a new, hopefully unique, variant.
+    $maxlength = min($config->get('max_length'), $this->aliasStorageHelper->getAliasSchemaMaxlength());
+    $separator = $config->get('separator');
+    $original_alias = $alias;
+
+    $i = 0;
+    do {
+      // Append an incrementing numeric suffix until we find a unique alias.
+      $unique_suffix = $separator . $i;
+      $alias = Unicode::truncate($original_alias, $maxlength - Unicode::strlen($unique_suffix), TRUE) . $unique_suffix;
+      $i++;
+    } while ($this->isReserved($alias, $source, $langcode));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isReserved($alias, $source, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED) {
+    // Check if this alias already exists.
+    if ($existing_source = $this->aliasManager->getPathByAlias($alias, $langcode)) {
+      if ($existing_source != $alias) {
+        // If it is an alias for the provided source, it is allowed to keep using
+        // it. If not, then it is reserved.
+        return $existing_source != $source;
+      }
+
+    }
+
+    // Then check if there is a route with the same path.
+    if ($this->isRoute($alias)) {
+      return TRUE;
+    }
+    // Finally check if any other modules have reserved the alias.
+    $args = array(
+      $alias,
+      $source,
+      $langcode,
+    );
+    $implementations = $this->moduleHandler->getImplementations('pathauto_is_alias_reserved');
+    foreach ($implementations as $module) {
+
+      $result = $this->moduleHandler->invoke($module, 'pathauto_is_alias_reserved', $args);
+
+      if (!empty($result)) {
+        // As soon as the first module says that an alias is in fact reserved,
+        // then there is no point in checking the rest of the modules.
+        return TRUE;
+      }
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Verify if the given path is a valid route.
+   *
+   * @param string $path
+   *   A string containing a relative path.
+   *
+   * @return bool
+   *   TRUE if the path already exists.
+   *
+   * @throws \InvalidArgumentException
+   */
+  public function isRoute($path) {
+    if (is_file(DRUPAL_ROOT . '/' . $path) || is_dir(DRUPAL_ROOT . '/' . $path)) {
+      // Do not allow existing files or directories to get assigned an automatic
+      // alias. Note that we do not need to use is_link() to check for symbolic
+      // links since this returns TRUE for either is_file() or is_dir() already.
+      return TRUE;
+    }
+
+    $routes = $this->routeProvider->getRoutesByPattern($path);
+
+    // Only return true for an exact match, ignore placeholders.
+    foreach ($routes as $route) {
+      if ($route->getPath() == $path) {
+        return TRUE;
+      }
+    }
+
+    return FALSE;
+
+  }
+
+}

+ 42 - 0
sites/all/modules/contrib/admin/pathauto/src/AliasUniquifierInterface.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace Drupal\pathauto;
+use Drupal\Core\Language\LanguageInterface;
+
+/**
+ * Provides an interface for alias uniquifiers.
+ */
+interface AliasUniquifierInterface {
+
+  /**
+   * Check to ensure a path alias is unique and add suffix variants if necessary.
+   *
+   * Given an alias 'content/test' if a path alias with the exact alias already
+   * exists, the function will change the alias to 'content/test-0' and will
+   * increase the number suffix until it finds a unique alias.
+   *
+   * @param string $alias
+   *   A string with the alias. Can be altered by reference.
+   * @param string $source
+   *   A string with the path source.
+   * @param string $langcode
+   *   A string with a language code.
+   */
+  public function uniquify(&$alias, $source, $langcode);
+
+  /**
+   * Checks if an alias is reserved.
+   *
+   * @param string $alias
+   *   The alias.
+   * @param string $source
+   *   The source.
+   * @param string $langcode
+   *   (optional) The language code.
+   *
+   * @return bool
+   *   Returns TRUE if the alias is reserved.
+   */
+  public function isReserved($alias, $source, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED);
+
+}

+ 37 - 0
sites/all/modules/contrib/admin/pathauto/src/Annotation/AliasType.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace Drupal\pathauto\Annotation;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines an AliasType annotation.
+ *
+ * @Annotation
+ */
+class AliasType extends Plugin {
+
+  /**
+   * The plugin ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The human-readable name of the action plugin.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   */
+  public $label;
+
+  /**
+   * The token types.
+   *
+   * @var string[]
+   */
+  public $types = array();
+
+}

+ 382 - 0
sites/all/modules/contrib/admin/pathauto/src/Entity/PathautoPattern.php

@@ -0,0 +1,382 @@
+<?php
+
+namespace Drupal\pathauto\Entity;
+
+use Drupal\Component\Plugin\Exception\ContextException;
+use Drupal\Core\Condition\ConditionPluginCollection;
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\ContextAwarePluginInterface;
+use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection;
+use Drupal\pathauto\PathautoPatternInterface;
+
+/**
+ * Defines the Pathauto pattern entity.
+ *
+ * @ConfigEntityType(
+ *   id = "pathauto_pattern",
+ *   label = @Translation("Pathauto pattern"),
+ *   handlers = {
+ *     "list_builder" = "Drupal\pathauto\PathautoPatternListBuilder",
+ *     "form" = {
+ *       "default" = "Drupal\pathauto\Form\PatternEditForm",
+ *       "delete" = "Drupal\Core\Entity\EntityDeleteForm",
+ *       "enable" = "Drupal\pathauto\Form\PatternEnableForm",
+ *       "disable" = "Drupal\pathauto\Form\PatternDisableForm"
+ *     },
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
+ *     },
+ *   },
+ *   config_prefix = "pattern",
+ *   admin_permission = "administer pathauto",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "label" = "label",
+ *     "uuid" = "uuid",
+ *     "weight" = "weight",
+ *     "status" = "status"
+ *   },
+ *   config_export = {
+ *     "id",
+ *     "label",
+ *     "type",
+ *     "pattern",
+ *     "selection_criteria",
+ *     "selection_logic",
+ *     "weight",
+ *     "relationships"
+ *   },
+ *   lookup_keys = {
+ *     "type",
+ *     "status",
+ *   },
+ *   links = {
+ *     "collection" = "/admin/config/search/path/patterns",
+ *     "edit-form" = "/admin/config/search/path/patterns/{pathauto_pattern}",
+ *     "delete-form" = "/admin/config/search/path/patterns/{pathauto_pattern}/delete",
+ *     "enable" = "/admin/config/search/path/patterns/{pathauto_pattern}/enable",
+ *     "disable" = "/admin/config/search/path/patterns/{pathauto_pattern}/disable"
+ *   }
+ * )
+ */
+class PathautoPattern extends ConfigEntityBase implements PathautoPatternInterface {
+
+  /**
+   * The Pathauto pattern ID.
+   *
+   * @var string
+   */
+  protected $id;
+
+  /**
+   * The Pathauto pattern label.
+   *
+   * @var string
+   */
+  protected $label;
+
+  /**
+   * The pattern type.
+   *
+   * A string denoting the type of pathauto pattern this is. For a node path
+   * this would be 'node', for users it would be 'user', and so on. This allows
+   * for arbitrary non-entity patterns to be possible if applicable.
+   *
+   * @var string
+   */
+  protected $type;
+
+  /**
+   * @var \Drupal\Core\Plugin\DefaultSingleLazyPluginCollection
+   */
+  protected $aliasTypeCollection;
+
+  /**
+   * A tokenized string for alias generation.
+   *
+   * @var string
+   */
+  protected $pattern;
+
+  /**
+   * The plugin configuration for the selection criteria condition plugins.
+   *
+   * @var array
+   */
+  protected $selection_criteria = [];
+
+  /**
+   * The selection logic for this pattern entity (either 'and' or 'or').
+   *
+   * @var string
+   */
+  protected $selection_logic = 'and';
+
+  /**
+   * @var int
+   */
+  protected $weight = 0;
+
+  /**
+   * @var array[]
+   *   Keys are context tokens, and values are arrays with the following keys:
+   *   - label (string|null, optional): The human-readable label of this
+   *     relationship.
+   */
+  protected $relationships = [];
+
+  /**
+   * The plugin collection that holds the selection criteria condition plugins.
+   *
+   * @var \Drupal\Component\Plugin\LazyPluginCollection
+   */
+  protected $selectionConditionCollection;
+
+  /**
+   * {@inheritdoc}
+   *
+   * Not using core's default logic around ConditionPluginCollection since it
+   * incorrectly assumes no condition will ever be applied twice.
+   */
+  public function preSave(EntityStorageInterface $storage) {
+    parent::preSave($storage);
+    $criteria = [];
+    foreach ($this->getSelectionConditions() as $id => $condition) {
+      $criteria[$id] = $condition->getConfiguration();
+    }
+    $this->selection_criteria = $criteria;
+
+    // Invalidate the static caches.
+    \Drupal::service('pathauto.generator')->resetCaches();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function postDelete(EntityStorageInterface $storage, array $entities) {
+    parent::postDelete($storage, $entities);
+    // Invalidate the static caches.
+    \Drupal::service('pathauto.generator')->resetCaches();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    parent::calculateDependencies();
+
+    $this->calculatePluginDependencies($this->getAliasType());
+
+    foreach ($this->getSelectionConditions() as $instance) {
+      $this->calculatePluginDependencies($instance);
+    }
+
+    return $this->getDependencies();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPattern() {
+    return $this->pattern;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setPattern($pattern) {
+    $this->pattern = $pattern;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getType() {
+    return $this->type;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAliasType() {
+    if (!$this->aliasTypeCollection) {
+      $this->aliasTypeCollection = new DefaultSingleLazyPluginCollection(\Drupal::service('plugin.manager.alias_type'), $this->getType(), ['default' => $this->getPattern()]);
+    }
+    return $this->aliasTypeCollection->get($this->getType());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getWeight() {
+    return $this->weight;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setWeight($weight) {
+    $this->weight = $weight;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getContexts() {
+    $contexts = $this->getAliasType()->getContexts();
+    foreach ($this->getRelationships() as $token => $definition) {
+      /** @var \Drupal\ctools\TypedDataResolver $resolver */
+      $resolver = \Drupal::service('ctools.typed_data.resolver');
+      $context = $resolver->convertTokenToContext($token, $contexts);
+      $context_definition = $context->getContextDefinition();
+      if (!empty($definition['label'])) {
+        $context_definition->setLabel($definition['label']);
+      }
+      $contexts[$token] = $context;
+    }
+    return $contexts;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasRelationship($token) {
+    return isset($this->relationships[$token]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function addRelationship($token, $label = NULL) {
+    if (!$this->hasRelationship($token)) {
+      $this->relationships[$token] = [
+        'label' => $label,
+      ];
+    }
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function replaceRelationship($token, $label) {
+    if ($this->hasRelationship($token)) {
+      $this->relationships[$token] = [
+        'label' => $label,
+      ];
+    }
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function removeRelationship($token) {
+    unset($this->relationships[$token]);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRelationships() {
+    return $this->relationships;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSelectionConditions() {
+    if (!$this->selectionConditionCollection) {
+      $this->selectionConditionCollection = new ConditionPluginCollection(\Drupal::service('plugin.manager.condition'), $this->get('selection_criteria'));
+    }
+    return $this->selectionConditionCollection;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function addSelectionCondition(array $configuration) {
+    $configuration['uuid'] = $this->uuidGenerator()->generate();
+    $this->getSelectionConditions()->addInstanceId($configuration['uuid'], $configuration);
+    return $configuration['uuid'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSelectionCondition($condition_id) {
+    return $this->getSelectionConditions()->get($condition_id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function removeSelectionCondition($condition_id) {
+    $this->getSelectionConditions()->removeInstanceId($condition_id);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSelectionLogic() {
+    return $this->selection_logic;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function applies($object) {
+    if ($this->getAliasType()->applies($object)) {
+      $definitions = $this->getAliasType()->getContextDefinitions();
+      if (count($definitions) > 1) {
+        throw new \Exception("Alias types do not support more than one context.");
+      }
+      $keys = array_keys($definitions);
+      // Set the context object on our Alias plugin before retrieving contexts.
+      $this->getAliasType()->setContextValue($keys[0], $object);
+      /** @var \Drupal\Core\Plugin\Context\ContextInterface[] $base_contexts */
+      $contexts = $this->getContexts();
+      /** @var \Drupal\Core\Plugin\Context\ContextHandler $context_handler */
+      $context_handler = \Drupal::service('context.handler');
+      $conditions = $this->getSelectionConditions();
+      foreach ($conditions as $condition) {
+
+        // As the context object is kept and only the value is switched out,
+        // it can over time grow to a huge number of cache contexts. Reset it
+        // if there are 100 cache tags to prevent cache tag merging getting too
+        // slow.
+        foreach ($condition->getContextDefinitions() as $name => $context_definition) {
+          if (count($condition->getContext($name)->getCacheTags()) > 100) {
+            $condition->setContext($name, new Context($context_definition));
+          }
+        }
+
+        if ($condition instanceof ContextAwarePluginInterface) {
+          try {
+            $context_handler->applyContextMapping($condition, $contexts);
+          }
+          catch (ContextException $e) {
+            watchdog_exception('pathauto', $e);
+            return FALSE;
+          }
+        }
+        $result = $condition->execute();
+        if ($this->getSelectionLogic() == 'and' && !$result) {
+          return FALSE;
+        }
+        elseif ($this->getSelectionLogic() == 'or' && $result) {
+          return TRUE;
+        }
+      }
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+}

+ 54 - 0
sites/all/modules/contrib/admin/pathauto/src/EventSubscriber/PathautoSettingsCacheTag.php

@@ -0,0 +1,54 @@
+<?php
+
+namespace Drupal\pathauto\EventSubscriber;
+
+use Drupal\Core\Config\ConfigCrudEvent;
+use Drupal\Core\Config\ConfigEvents;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\pathauto\AliasTypeManager;
+
+/**
+ * A subscriber to clear fielddefinition cache when saving pathauto settings.
+ */
+class PathautoSettingsCacheTag implements EventSubscriberInterface {
+
+  protected $entityFieldManager;
+  protected $aliasTypeManager;
+
+  /**
+   * Constructs a PathautoSettingsCacheTag object.
+   */
+  public function __construct(EntityFieldManagerInterface $entity_field_manager, AliasTypeManager $alias_type_manager) {
+    $this->entityFieldManager = $entity_field_manager;
+    $this->aliasTypeManager = $alias_type_manager;
+  }
+
+  /**
+   * Invalidate the 'rendered' cache tag whenever the settings are modified.
+   *
+   * @param \Drupal\Core\Config\ConfigCrudEvent $event
+   *   The Event to process.
+   */
+  public function onSave(ConfigCrudEvent $event) {
+    if ($event->getConfig()->getName() === 'pathauto.settings') {
+      $config = $event->getConfig();
+      $original_entity_types = $config->getOriginal('enabled_entity_types');
+
+      // Clear cached field definitions if the values are changed.
+      if ($original_entity_types != $config->get('enabled_entity_types')) {
+        $this->entityFieldManager->clearCachedFieldDefinitions();
+        $this->aliasTypeManager->clearCachedDefinitions();
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[ConfigEvents::SAVE][] = ['onSave'];
+    return $events;
+  }
+
+}

+ 185 - 0
sites/all/modules/contrib/admin/pathauto/src/Form/PathautoAdminDelete.php

@@ -0,0 +1,185 @@
+<?php
+
+namespace Drupal\pathauto\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\pathauto\AliasTypeManager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Alias mass delete form.
+ */
+class PathautoAdminDelete extends FormBase {
+
+  /**
+   * The alias type manager.
+   *
+   * @var \Drupal\pathauto\AliasTypeManager
+   */
+  protected $aliasTypeManager;
+
+  /**
+   * Constructs a PathautoAdminDelete object.
+   *
+   * @param \Drupal\pathauto\AliasTypeManager $alias_type_manager
+   *   The alias type manager.
+   */
+  public function __construct(AliasTypeManager $alias_type_manager) {
+    $this->aliasTypeManager = $alias_type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.alias_type')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'pathauto_admin_delete';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['delete'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t('Choose aliases to delete'),
+      '#tree' => TRUE,
+    ];
+
+    // First we do the "all" case.
+    $storage_helper = \Drupal::service('pathauto.alias_storage_helper');
+    $total_count = $storage_helper->countAll();
+    $form['delete']['all_aliases'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('All aliases'),
+      '#default_value' => FALSE,
+      '#description' => $this->t('Delete all aliases. Number of aliases which will be deleted: %count.', ['%count' => $total_count]),
+    ];
+
+    // Next, iterate over all visible alias types.
+    $definitions = $this->aliasTypeManager->getVisibleDefinitions();
+
+    foreach ($definitions as $id => $definition) {
+      /** @var \Drupal\pathauto\AliasTypeInterface $alias_type */
+      $alias_type = $this->aliasTypeManager->createInstance($id);
+      $count = $storage_helper->countBySourcePrefix($alias_type->getSourcePrefix());
+      $form['delete']['plugins'][$id] = [
+        '#type' => 'checkbox',
+        '#title' => (string) $definition['label'],
+        '#default_value' => FALSE,
+        '#description' => $this->t('Delete aliases for all @label. Number of aliases which will be deleted: %count.', ['@label' => (string) $definition['label'], '%count' => $count]),
+      ];
+    }
+
+    $form['options'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t('Delete options'),
+      '#tree' => TRUE,
+    ];
+
+    // Provide checkbox for not deleting custom aliases.
+    $form['options']['keep_custom_aliases'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Only delete automatically generated aliases'),
+      '#default_value' => TRUE,
+      '#description' => $this->t('When checked, aliases which have been manually set are not affected by this mass-deletion.'),
+    ];
+
+    // Warn them and give a button that shows we mean business.
+    $form['warning'] = ['#value' => '<p>' . $this->t('<strong>Note:</strong> there is no confirmation. Be sure of your action before clicking the "Delete aliases now!" button.<br />You may want to make a backup of the database and/or the url_alias table prior to using this feature.') . '</p>'];
+    $form['buttons']['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Delete aliases now!'),
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $delete_all = $form_state->getValue(['delete', 'all_aliases']);
+    // Keeping custom aliases forces us to go the slow way to correctly check
+    // the automatic/manual flag.
+    if ($form_state->getValue(['options', 'keep_custom_aliases'])) {
+      $batch = [
+        'title' => $this->t('Bulk deleting URL aliases'),
+        'operations' => [['Drupal\pathauto\Form\PathautoAdminDelete::batchStart', [$delete_all]]],
+        'finished' => 'Drupal\pathauto\Form\PathautoAdminDelete::batchFinished',
+      ];
+
+      if ($delete_all) {
+        foreach (array_keys($form_state->getValue(['delete', 'plugins'])) as $id) {
+          $batch['operations'][] = ['Drupal\pathauto\Form\PathautoAdminDelete::batchProcess', [$id]];
+        }
+      }
+      else {
+        foreach (array_keys(array_filter($form_state->getValue(['delete', 'plugins']))) as $id) {
+          $batch['operations'][] = ['Drupal\pathauto\Form\PathautoAdminDelete::batchProcess', [$id]];
+        }
+      }
+
+      batch_set($batch);
+    }
+    else if ($delete_all) {
+      \Drupal::service('pathauto.alias_storage_helper')->deleteAll();
+      drupal_set_message($this->t('All of your path aliases have been deleted.'));
+    }
+    else {
+      $storage_helper = \Drupal::service('pathauto.alias_storage_helper');
+      foreach (array_keys(array_filter($form_state->getValue(['delete', 'plugins']))) as $id) {
+        $alias_type = $this->aliasTypeManager->createInstance($id);
+        $storage_helper->deleteBySourcePrefix((string) $alias_type->getSourcePrefix());
+        drupal_set_message($this->t('All of your %label path aliases have been deleted.', ['%label' => $alias_type->getLabel()]));
+      }
+    }
+  }
+
+  /**
+   * Batch callback; record if aliases of all types must be deleted.
+   */
+  public static function batchStart($delete_all, &$context) {
+    $context['results']['delete_all'] = $delete_all;
+    $context['results']['deletions'] = [];
+  }
+
+  /**
+   * Common batch processing callback for all operations.
+   */
+  public static function batchProcess($id, &$context) {
+    /** @var \Drupal\pathauto\AliasTypeBatchUpdateInterface $alias_type */
+    $alias_type = \Drupal::service('plugin.manager.alias_type')->createInstance($id);
+    $alias_type->batchDelete($context);
+  }
+
+  /**
+   * Batch finished callback.
+   */
+  public static function batchFinished($success, $results, $operations) {
+    if ($success) {
+      if ($results['delete_all']) {
+        drupal_set_message(t('All of your automatically generated path aliases have been deleted.'));
+      }
+      else if (isset($results['deletions'])) {
+        foreach (array_values($results['deletions']) as $label) {
+          drupal_set_message(t('All of your automatically generated %label path aliases have been deleted.', ['%label' => $label]));
+        }
+      }
+    }
+    else {
+      $error_operation = reset($operations);
+      drupal_set_message(t('An error occurred while processing @operation with arguments : @args', array('@operation' => $error_operation[0], '@args' => print_r($error_operation[0], TRUE))));
+    }
+  }
+
+}

+ 164 - 0
sites/all/modules/contrib/admin/pathauto/src/Form/PathautoBulkUpdateForm.php

@@ -0,0 +1,164 @@
+<?php
+
+namespace Drupal\pathauto\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\pathauto\AliasTypeBatchUpdateInterface;
+use Drupal\pathauto\AliasTypeManager;
+use Drupal\pathauto\PathautoGeneratorInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Configure file system settings for this site.
+ */
+class PathautoBulkUpdateForm extends FormBase {
+
+  /**
+   * The alias type manager.
+   *
+   * @var \Drupal\pathauto\AliasTypeManager
+   */
+  protected $aliasTypeManager;
+
+  /**
+   * Constructs a PathautoBulkUpdateForm object.
+   *
+   * @param \Drupal\pathauto\AliasTypeManager $alias_type_manager
+   *   The alias type manager.
+   */
+  public function __construct(AliasTypeManager $alias_type_manager) {
+    $this->aliasTypeManager = $alias_type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.alias_type')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'pathauto_bulk_update_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+
+    $form = array();
+
+    $form['#update_callbacks'] = array();
+
+    $form['update'] = array(
+      '#type' => 'checkboxes',
+      '#title' => $this->t('Select the types of paths for which to generate URL aliases'),
+      '#options' => array(),
+      '#default_value' => array(),
+    );
+
+    $definitions = $this->aliasTypeManager->getVisibleDefinitions();
+
+    foreach ($definitions as $id => $definition) {
+      $alias_type = $this->aliasTypeManager->createInstance($id);
+      if ($alias_type instanceof AliasTypeBatchUpdateInterface) {
+        $form['update']['#options'][$id] = $alias_type->getLabel();
+      }
+    }
+
+    $form['action'] = array(
+      '#type' => 'radios',
+      '#title' => $this->t('Select which URL aliases to generate'),
+      '#options' => ['create' => $this->t('Generate a URL alias for un-aliased paths only')],
+      '#default_value' => 'create',
+    );
+
+    $config = $this->config('pathauto.settings');
+
+    if ($config->get('update_action') == PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW) {
+      // Existing aliases should not be updated.
+      $form['warning'] = array(
+        '#markup' => $this->t('<a href=":url">Pathauto settings</a> are set to ignore paths which already have a URL alias. You can only create URL aliases for paths having none.', [':url' => Url::fromRoute('pathauto.settings.form')->toString()]),
+      );
+    }
+    else {
+      $form['action']['#options']['update'] = $this->t('Update the URL alias for paths having an old URL alias');
+      $form['action']['#options']['all'] = $this->t('Regenerate URL aliases for all paths');
+    }
+
+    $form['actions']['#type'] = 'actions';
+    $form['actions']['submit'] = array(
+      '#type' => 'submit',
+      '#value' => $this->t('Update'),
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $batch = array(
+      'title' => $this->t('Bulk updating URL aliases'),
+      'operations' => array(
+        array('Drupal\pathauto\Form\PathautoBulkUpdateForm::batchStart', array()),
+      ),
+      'finished' => 'Drupal\pathauto\Form\PathautoBulkUpdateForm::batchFinished',
+    );
+
+    $action = $form_state->getValue('action');
+
+    foreach ($form_state->getValue('update') as $id) {
+      if (!empty($id)) {
+        $batch['operations'][] = array('Drupal\pathauto\Form\PathautoBulkUpdateForm::batchProcess', [$id, $action]);
+      }
+    }
+
+    batch_set($batch);
+  }
+
+  /**
+   * Batch callback; initialize the number of updated aliases.
+   */
+  public static function batchStart(&$context) {
+    $context['results']['updates'] = 0;
+  }
+
+  /**
+   * Common batch processing callback for all operations.
+   *
+   * Required to load our include the proper batch file.
+   */
+  public static function batchProcess($id, $action, &$context) {
+    /** @var \Drupal\pathauto\AliasTypeBatchUpdateInterface $alias_type */
+    $alias_type = \Drupal::service('plugin.manager.alias_type')->createInstance($id);
+    $alias_type->batchUpdate($action, $context);
+  }
+
+  /**
+   * Batch finished callback.
+   */
+  public static function batchFinished($success, $results, $operations) {
+    if ($success) {
+      if ($results['updates']) {
+        drupal_set_message(\Drupal::translation()->formatPlural($results['updates'], 'Generated 1 URL alias.', 'Generated @count URL aliases.'));
+      }
+      else {
+        drupal_set_message(t('No new URL aliases to generate.'));
+      }
+    }
+    else {
+      $error_operation = reset($operations);
+      drupal_set_message(t('An error occurred while processing @operation with arguments : @args', array('@operation' => $error_operation[0], '@args' => print_r($error_operation[0], TRUE))));
+    }
+  }
+
+}

+ 264 - 0
sites/all/modules/contrib/admin/pathauto/src/Form/PathautoSettingsForm.php

@@ -0,0 +1,264 @@
+<?php
+
+namespace Drupal\pathauto\Form;
+
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Form\ConfigFormBase;
+use Drupal\Core\Url;
+use Drupal\pathauto\AliasTypeManager;
+use Drupal\pathauto\PathautoGeneratorInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Configure file system settings for this site.
+ */
+class PathautoSettingsForm extends ConfigFormBase {
+
+  /**
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
+   */
+  protected $entityFieldManager;
+
+  /**
+   * @var \Drupal\pathauto\AliasTypeManager
+   */
+  protected $aliasTypeManager;
+
+  /**
+   * {@inheritDoc}
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, AliasTypeManager $alias_type_manager) {
+    parent::__construct($config_factory);
+    $this->entityTypeManager = $entity_type_manager;
+    $this->entityFieldManager = $entity_field_manager;
+    $this->aliasTypeManager = $alias_type_manager;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('config.factory'),
+      $container->get('entity_type.manager'),
+      $container->get('entity_field.manager'),
+      $container->get('plugin.manager.alias_type')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'pathauto_settings_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEditableConfigNames() {
+    return ['pathauto.settings'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $config = $this->config('pathauto.settings');
+
+    $form['enabled_entity_types'] = [
+      '#type' => 'details',
+      '#open' => TRUE,
+      '#title' => $this->t('Enabled entity types'),
+      '#description' => $this->t('Enable to add a path field and allow to define alias patterns for the given type. Disabled types already define a path field themselves or currently have a pattern.'),
+      '#tree' => TRUE,
+    ];
+
+    // Get all applicable entity types.
+    foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
+      // Disable a checkbox if it already exists and if the entity type has
+      // patterns currently defined or if it isn't defined by us.
+      $patterns_count = \Drupal::entityQuery('pathauto_pattern')
+        ->condition('type', 'canonical_entities:' . $entity_type_id)
+        ->count()
+        ->execute();
+
+      if (is_subclass_of($entity_type->getClass(), FieldableEntityInterface::class) && $entity_type->hasLinkTemplate('canonical')) {
+        $field_definitions = $this->entityFieldManager->getBaseFieldDefinitions($entity_type_id);
+        $form['enabled_entity_types'][$entity_type_id] = [
+          '#type' => 'checkbox',
+          '#title' => $entity_type->getLabel(),
+          '#default_value' => isset($field_definitions['path']) || in_array($entity_type_id, $config->get('enabled_entity_types')),
+          '#disabled' => isset($field_definitions['path']) && ($field_definitions['path']->getProvider() != 'pathauto' || $patterns_count),
+        ];
+      }
+    }
+
+    $form['verbose'] = array(
+      '#type' => 'checkbox',
+      '#title' => $this->t('Verbose'),
+      '#default_value' => $config->get('verbose'),
+      '#description' => $this->t('Display alias changes (except during bulk updates).'),
+    );
+
+    $form['separator'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->t('Separator'),
+      '#size' => 1,
+      '#maxlength' => 1,
+      '#default_value' => $config->get('separator'),
+      '#description' => $this->t('Character used to separate words in titles. This will replace any spaces and punctuation characters. Using a space or + character can cause unexpected results.'),
+    );
+
+    $form['case'] = array(
+      '#type' => 'checkbox',
+      '#title' => $this->t('Character case'),
+      '#default_value' => $config->get('case'),
+      '#description' => $this->t('Convert token values to lowercase.'),
+    );
+
+    $max_length = \Drupal::service('pathauto.alias_storage_helper')->getAliasSchemaMaxlength();
+
+    $help_link = '';
+    if (\Drupal::moduleHandler()->moduleExists('help')) {
+      $help_link = ' ' . $this->t('See <a href=":pathauto-help">Pathauto help</a> for details.', [':pathauto-help' => Url::fromRoute('help.page', ['name' => 'pathauto'])->toString()]);
+    }
+
+    $form['max_length'] = array(
+      '#type' => 'number',
+      '#title' => $this->t('Maximum alias length'),
+      '#size' => 3,
+      '#maxlength' => 3,
+      '#default_value' => $config->get('max_length'),
+      '#min' => 1,
+      '#max' => $max_length,
+      '#description' => $this->t('Maximum length of aliases to generate. 100 is the recommended length. @max is the maximum possible length.', array('@max' => $max_length)) . $help_link,
+    );
+
+    $form['max_component_length'] = array(
+      '#type' => 'number',
+      '#title' => $this->t('Maximum component length'),
+      '#size' => 3,
+      '#maxlength' => 3,
+      '#default_value' => $config->get('max_component_length'),
+      '#min' => 1,
+      '#max' => $max_length,
+      '#description' => $this->t('Maximum text length of any component in the alias (e.g., [title]). 100 is the recommended length. @max is the maximum possible length.', ['@max' => $max_length]) . $help_link,
+    );
+
+    $description = $this->t('What should Pathauto do when updating an existing content item which already has an alias?');
+    if (\Drupal::moduleHandler()->moduleExists('redirect')) {
+      $description .= ' ' . $this->t('The <a href=":url">Redirect module settings</a> affect whether a redirect is created when an alias is deleted.', array(':url' => Url::fromRoute('redirect.settings')->toString()));
+    }
+    else {
+      $description .= ' ' . $this->t('Considering installing the <a href=":url">Redirect module</a> to get redirects when your aliases change.', array(':url' => 'http://drupal.org/project/redirect'));
+    }
+
+    $form['update_action'] = array(
+      '#type' => 'radios',
+      '#title' => $this->t('Update action'),
+      '#default_value' => $config->get('update_action'),
+      '#options' => array(
+        PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW => $this->t('Do nothing. Leave the old alias intact.'),
+        PathautoGeneratorInterface::UPDATE_ACTION_LEAVE => $this->t('Create a new alias. Leave the existing alias functioning.'),
+        PathautoGeneratorInterface::UPDATE_ACTION_DELETE => $this->t('Create a new alias. Delete the old alias.'),
+      ),
+      '#description' => $description,
+    );
+
+    $form['transliterate'] = array(
+      '#type' => 'checkbox',
+      '#title' => $this->t('Transliterate prior to creating alias'),
+      '#default_value' => $config->get('transliterate'),
+      '#description' => $this->t('When a pattern includes certain characters (such as those with accents) should Pathauto attempt to transliterate them into the US-ASCII alphabet?'),
+    );
+
+    $form['reduce_ascii'] = array(
+      '#type' => 'checkbox',
+      '#title' => $this->t('Reduce strings to letters and numbers'),
+      '#default_value' => $config->get('reduce_ascii'),
+      '#description' => $this->t('Filters the new alias to only letters and numbers found in the ASCII-96 set.'),
+    );
+
+    $form['ignore_words'] = array(
+      '#type' => 'textarea',
+      '#title' => $this->t('Strings to Remove'),
+      '#default_value' => $config->get('ignore_words'),
+      '#description' => $this->t('Words to strip out of the URL alias, separated by commas. Do not use this to remove punctuation.'),
+      '#wysiwyg' => FALSE,
+    );
+
+    $form['punctuation'] = array(
+      '#type' => 'details',
+      '#title' => $this->t('Punctuation'),
+      '#open' => FALSE,
+      '#tree' => TRUE,
+    );
+
+    $punctuation = \Drupal::service('pathauto.alias_cleaner')->getPunctuationCharacters();
+
+    foreach ($punctuation as $name => $details) {
+      // Use the value from config if it exists.
+      if ($config->get('punctuation.' . $name) !== NULL) {
+        $details['default'] = $config->get('punctuation.' . $name);
+      }
+      else {
+        // Otherwise use the correct default.
+        $details['default'] = $details['value'] == $config->get('separator') ? PathautoGeneratorInterface::PUNCTUATION_REPLACE : PathautoGeneratorInterface::PUNCTUATION_REMOVE;
+      }
+      $form['punctuation'][$name] = array(
+        '#type' => 'select',
+        '#title' => $details['name'] . ' (<code>' . Html::escape($details['value']) . '</code>)',
+        '#default_value' => $details['default'],
+        '#options' => array(
+          PathautoGeneratorInterface::PUNCTUATION_REMOVE => $this->t('Remove'),
+          PathautoGeneratorInterface::PUNCTUATION_REPLACE => $this->t('Replace by separator'),
+          PathautoGeneratorInterface::PUNCTUATION_DO_NOTHING => $this->t('No action (do not replace)'),
+        ),
+      );
+    }
+
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+
+    $config = $this->config('pathauto.settings');
+
+    $form_state->cleanValues();
+
+    foreach ($form_state->getValues() as $key => $value) {
+      if ($key == 'enabled_entity_types') {
+        $enabled_entity_types = [];
+        foreach ($value as $entity_type_id => $enabled) {
+          $field_definitions = $this->entityFieldManager->getBaseFieldDefinitions($entity_type_id);
+          // Verify that the entity type is enabled and that it is not defined
+          // or defined by us before adding it to the configuration, so that
+          // we do not store an entity type that cannot be enabled or disabled.
+          if ($enabled && (!isset($field_definitions['path']) || ($field_definitions['path']->getProvider() === 'pathauto'))) {
+            $enabled_entity_types[] = $entity_type_id;
+          }
+        }
+        $value = $enabled_entity_types;
+      }
+      $config->set($key, $value);
+    }
+    $config->save();
+
+    parent::submitForm($form, $form_state);
+  }
+
+}

+ 52 - 0
sites/all/modules/contrib/admin/pathauto/src/Form/PatternDisableForm.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace Drupal\pathauto\Form;
+
+use Drupal\Core\Entity\EntityConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+
+/**
+ * Provides the pathauto pattern disable disable form.
+ */
+class PatternDisableForm extends EntityConfirmFormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    return $this->t('Are you sure you want to disable the pattern %label?', array('%label' => $this->entity->label()));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    return new Url('entity.pathauto_pattern.collection');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText() {
+    return $this->t('Disable');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return $this->t('Disabled patterns are ignored when generating aliases.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $this->entity->disable()->save();
+    drupal_set_message($this->t('Disabled pattern %label.', array('%label' => $this->entity->label())));
+
+    $form_state->setRedirectUrl($this->getCancelUrl());
+  }
+
+}

+ 285 - 0
sites/all/modules/contrib/admin/pathauto/src/Form/PatternEditForm.php

@@ -0,0 +1,285 @@
+<?php
+
+namespace Drupal\pathauto\Form;
+
+use Drupal\Core\Entity\EntityForm;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\pathauto\AliasTypeManager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Edit form for pathauto patterns.
+ */
+class PatternEditForm extends EntityForm {
+
+  /**
+   * @var \Drupal\pathauto\AliasTypeManager
+   */
+  protected $manager;
+
+  /**
+   * @var \Drupal\pathauto\PathautoPatternInterface
+   */
+  protected $entity;
+
+  /**
+   * The entity type bundle info service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
+   */
+  protected $entityTypeBundleInfo;
+
+  /**
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.alias_type'),
+      $container->get('entity_type.bundle.info'),
+      $container->get('entity_type.manager'),
+      $container->get('language_manager')
+    );
+  }
+
+  /**
+   * PatternEditForm constructor.
+   *
+   * @param \Drupal\pathauto\AliasTypeManager $manager
+   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   */
+  function __construct(AliasTypeManager $manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, EntityTypeManagerInterface $entity_type_manager, LanguageManagerInterface $language_manager) {
+    $this->manager = $manager;
+    $this->entityTypeBundleInfo = $entity_type_bundle_info;
+    $this->entityTypeManager = $entity_type_manager;
+    $this->languageManager = $language_manager;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+
+    $options = [];
+    foreach ($this->manager->getVisibleDefinitions() as $plugin_id => $plugin_definition) {
+      $options[$plugin_id] = $plugin_definition['label'];
+    }
+    $form['type'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Pattern type'),
+      '#default_value' => $this->entity->getType(),
+      '#options' => $options,
+      '#required' => TRUE,
+      '#limit_validation_errors' => array(array('type')),
+      '#submit' => array('::submitSelectType'),
+      '#executes_submit_callback' => TRUE,
+      '#ajax' => array(
+        'callback' => '::ajaxReplacePatternForm',
+        'wrapper' => 'pathauto-pattern',
+        'method' => 'replace',
+      ),
+    ];
+
+    $form['pattern_container'] = [
+      '#type' => 'container',
+      '#prefix' => '<div id="pathauto-pattern">',
+      '#suffix' => '</div>',
+    ];
+
+    // if there is no type yet, stop here.
+    if ($this->entity->getType()) {
+
+      $alias_type = $this->entity->getAliasType();
+
+      $form['pattern_container']['pattern'] = array(
+        '#type' => 'textfield',
+        '#title' => 'Path pattern',
+        '#default_value' => $this->entity->getPattern(),
+        '#size' => 65,
+        '#maxlength' => 1280,
+        '#element_validate' => array('token_element_validate', 'pathauto_pattern_validate'),
+        '#after_build' => array('token_element_validate'),
+        '#token_types' => $alias_type->getTokenTypes(),
+        '#min_tokens' => 1,
+        '#required' => TRUE,
+      );
+
+      // Show the token help relevant to this pattern type.
+      $form['pattern_container']['token_help'] = array(
+        '#theme' => 'token_tree_link',
+        '#token_types' => $alias_type->getTokenTypes(),
+      );
+
+      // Expose bundle and language conditions.
+      if ($alias_type->getDerivativeId() && $entity_type = $this->entityTypeManager->getDefinition($alias_type->getDerivativeId())) {
+
+        $default_bundles = [];
+        $default_languages = [];
+        foreach ($this->entity->getSelectionConditions() as $condition_id => $condition) {
+          if (in_array($condition->getPluginId(), ['entity_bundle:' . $entity_type->id(), 'node_type'])) {
+            $default_bundles = $condition->getConfiguration()['bundles'];
+          }
+          elseif ($condition->getPluginId() == 'language') {
+            $default_languages = $condition->getConfiguration()['langcodes'];
+          }
+        }
+
+        if ($entity_type->hasKey('bundle') && $bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type->id())) {
+          $bundle_options = [];
+          foreach ($bundles as $id => $info) {
+            $bundle_options[$id] = $info['label'];
+          }
+          $form['pattern_container']['bundles'] = array(
+            '#title' => $entity_type->getBundleLabel(),
+            '#type' => 'checkboxes',
+            '#options' => $bundle_options,
+            '#default_value' => $default_bundles,
+            '#description' => $this->t('Check to which types this pattern should be applied. Leave empty to allow any.'),
+          );
+        }
+
+        if ($this->languageManager->isMultilingual() && $entity_type->isTranslatable()) {
+          $language_options = [];
+          foreach ($this->languageManager->getLanguages() as $id => $language) {
+            $language_options[$id] = $language->getName();
+          }
+          $form['pattern_container']['languages'] = array(
+            '#title' => $this->t('Languages'),
+            '#type' => 'checkboxes',
+            '#options' => $language_options,
+            '#default_value' => $default_languages,
+            '#description' => $this->t('Check to which languages this pattern should be applied. Leave empty to allow any.'),
+          );
+        }
+      }
+    }
+
+    $form['label'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->t('Label'),
+      '#maxlength' => 255,
+      '#default_value' => $this->entity->label(),
+      '#required' => TRUE,
+      '#description' => $this->t('A short name to help you identify this pattern in the patterns list.'),
+    );
+
+    $form['id'] = array(
+      '#type' => 'machine_name',
+      '#title' => $this->t('ID'),
+      '#maxlength' => 255,
+      '#default_value' => $this->entity->id(),
+      '#required' => TRUE,
+      '#disabled' => !$this->entity->isNew(),
+      '#machine_name' => array(
+        'exists' => 'Drupal\pathauto\Entity\PathautoPattern::load',
+      ),
+    );
+
+    $form['status'] = [
+      '#title' => $this->t('Enabled'),
+      '#type' => 'checkbox',
+      '#default_value' => $this->entity->status(),
+    ];
+
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public function buildEntity(array $form, FormStateInterface $form_state) {
+    /** @var \Drupal\pathauto\PathautoPatternInterface $entity */
+    $entity = parent::buildEntity($form, $form_state);
+
+    // Will only be used for new patterns.
+    $default_weight = 0;
+
+    $alias_type = $entity->getAliasType();
+    if ($alias_type->getDerivativeId() && $this->entityTypeManager->hasDefinition($alias_type->getDerivativeId())) {
+      $entity_type = $alias_type->getDerivativeId();
+      // First, remove bundle and language conditions.
+      foreach ($entity->getSelectionConditions() as $condition_id => $condition) {
+        if (in_array($condition->getPluginId(), ['entity_bundle:' . $entity_type, 'node_type', 'language'])) {
+          $entity->removeSelectionCondition($condition_id);
+        }
+      }
+
+      if ($bundles = array_filter((array) $form_state->getValue('bundles'))) {
+        $default_weight -= 5;
+        $plugin_id = $entity_type == 'node' ? 'node_type' : 'entity_bundle:' . $entity_type;
+        $entity->addSelectionCondition(
+          [
+            'id' => $plugin_id,
+            'bundles' => $bundles,
+            'negate' => FALSE,
+            'context_mapping' => [
+              $entity_type => $entity_type,
+            ]
+          ]
+        );
+      }
+
+      if ($languages = array_filter((array) $form_state->getValue('languages'))) {
+        $default_weight -= 5;
+        $language_mapping = $entity_type . ':' . $this->entityTypeManager->getDefinition($entity_type)->getKey('langcode') . ':language';
+        $entity->addSelectionCondition(
+          [
+            'id' => 'language',
+            'langcodes' => array_combine($languages, $languages),
+            'negate' => FALSE,
+            'context_mapping' => [
+              'language' => $language_mapping,
+            ]
+          ]
+        );
+        $entity->addRelationship($language_mapping, t('Language'));
+      }
+
+    }
+
+    if ($entity->isNew()) {
+      $entity->setWeight($default_weight);
+    }
+
+    return $entity;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public function save(array $form, FormStateInterface $form_state) {
+    parent::save($form, $form_state);
+    drupal_set_message($this->t('Pattern @label saved.', ['@label' => $this->entity->label()]));
+    $form_state->setRedirectUrl($this->entity->toUrl('collection'));
+  }
+
+  /**
+   * Handles switching the type selector.
+   */
+  public function ajaxReplacePatternForm($form, FormStateInterface $form_state) {
+    return $form['pattern_container'];
+  }
+
+  /**
+   * Handles submit call when alias type is selected.
+   */
+  public function submitSelectType(array $form, FormStateInterface $form_state) {
+    $this->entity = $this->buildEntity($form, $form_state);
+    $form_state->setRebuild();
+  }
+
+}

+ 52 - 0
sites/all/modules/contrib/admin/pathauto/src/Form/PatternEnableForm.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace Drupal\pathauto\Form;
+
+use Drupal\Core\Entity\EntityConfirmFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+
+/**
+ * Provides the pathauto pattern disable disable form.
+ */
+class PatternEnableForm extends EntityConfirmFormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    return $this->t('Are you sure you want to enable the pattern %label?', array('%label' => $this->entity->label()));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    return new Url('entity.pathauto_pattern.collection');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText() {
+    return $this->t('Enable');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return '';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $this->entity->enable()->save();
+    drupal_set_message($this->t('Enabled pattern %label.', array('%label' => $this->entity->label())));
+
+    $form_state->setRedirectUrl($this->getCancelUrl());
+  }
+
+}

+ 20 - 0
sites/all/modules/contrib/admin/pathauto/src/MessengerInterface.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace Drupal\pathauto;
+
+/**
+ * Provides an interface for Messengers.
+ */
+interface MessengerInterface {
+
+  /**
+   * Adds a message.
+   *
+   * @param string $message
+   *   The message to add.
+   * @param string $op
+   *   (optional) The operation being performed.
+   */
+  public function addMessage($message, $op = NULL);
+
+}

+ 49 - 0
sites/all/modules/contrib/admin/pathauto/src/PathautoFieldItemList.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace Drupal\pathauto;
+
+use Drupal\path\Plugin\Field\FieldType\PathFieldItemList;
+
+class PathautoFieldItemList extends PathFieldItemList {
+
+  /**
+   * @inheritDoc
+   */
+  protected function delegateMethod($method) {
+    // @todo Workaround until this is fixed, see
+    //   https://www.drupal.org/project/drupal/issues/2946289.
+    $this->ensureComputedValue();
+
+    // Duplicate the logic instead of calling the parent due to the dynamic
+    // arguments.
+    $result = [];
+    $args = array_slice(func_get_args(), 1);
+    foreach ($this->list as $delta => $item) {
+      // call_user_func_array() is way slower than a direct call so we avoid
+      // using it if have no parameters.
+      $result[$delta] = $args ? call_user_func_array([$item, $method], $args) : $item->{$method}();
+    }
+    return $result;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  protected function computeValue() {
+    parent::computeValue();
+
+    // For a new entity, default to creating a new alias.
+    if ($this->getEntity()->isNew()) {
+      $this->list[0]->set('pathauto', PathautoState::CREATE);
+    }
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function delete() {
+    \Drupal::service('pathauto.alias_storage_helper')->deleteEntityPathAll($this->getEntity());
+    $this->first()->get('pathauto')->purge();
+  }
+
+}

+ 365 - 0
sites/all/modules/contrib/admin/pathauto/src/PathautoGenerator.php

@@ -0,0 +1,365 @@
+<?php
+
+namespace Drupal\pathauto;
+
+use Drupal\Component\Utility\Unicode;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\RevisionableInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\Core\Utility\Token;
+use Drupal\token\TokenEntityMapperInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+
+/**
+ * Provides methods for generating path aliases.
+ */
+class PathautoGenerator implements PathautoGeneratorInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * Config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * Module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * Token service.
+   *
+   * @var \Drupal\Core\Utility\Token
+   */
+  protected $token;
+
+  /**
+   * Calculated pattern for a specific entity.
+   *
+   * @var array
+   */
+  protected $patterns = array();
+
+  /**
+   * Available patterns per entity type ID.
+   *
+   * @var array
+   */
+  protected $patternsByEntityType = array();
+
+  /**
+   * The alias cleaner.
+   *
+   * @var \Drupal\pathauto\AliasCleanerInterface
+   */
+  protected $aliasCleaner;
+
+  /**
+   * The alias storage helper.
+   *
+   * @var \Drupal\pathauto\AliasStorageHelperInterface
+   */
+  protected $aliasStorageHelper;
+
+  /**
+   * The alias uniquifier.
+   *
+   * @var \Drupal\pathauto\AliasUniquifierInterface
+   */
+  protected $aliasUniquifier;
+
+  /**
+   * The messenger service.
+   *
+   * @var \Drupal\pathauto\MessengerInterface
+   */
+  protected $messenger;
+
+  /**
+   * @var \Drupal\token\TokenEntityMapperInterface
+   */
+  protected $tokenEntityMapper;
+
+  /**
+   * @var Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Creates a new Pathauto manager.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   * @param \Drupal\Core\Utility\Token $token
+   *   The token utility.
+   * @param \Drupal\pathauto\AliasCleanerInterface $alias_cleaner
+   *   The alias cleaner.
+   * @param \Drupal\pathauto\AliasStorageHelperInterface $alias_storage_helper
+   *   The alias storage helper.
+   * @param AliasUniquifierInterface $alias_uniquifier
+   *   The alias uniquifier.
+   * @param MessengerInterface $messenger
+   *   The messenger service.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   *   The string translation service.
+   * @param Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, Token $token, AliasCleanerInterface $alias_cleaner, AliasStorageHelperInterface $alias_storage_helper, AliasUniquifierInterface $alias_uniquifier, MessengerInterface $messenger, TranslationInterface $string_translation, TokenEntityMapperInterface $token_entity_mappper, EntityTypeManagerInterface $entity_type_manager) {
+    $this->configFactory = $config_factory;
+    $this->moduleHandler = $module_handler;
+    $this->token = $token;
+    $this->aliasCleaner = $alias_cleaner;
+    $this->aliasStorageHelper = $alias_storage_helper;
+    $this->aliasUniquifier = $alias_uniquifier;
+    $this->messenger = $messenger;
+    $this->stringTranslation = $string_translation;
+    $this->tokenEntityMapper = $token_entity_mappper;
+    $this->entityTypeManager = $entity_type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function createEntityAlias(EntityInterface $entity, $op) {
+    // Retrieve and apply the pattern for this content type.
+    $pattern = $this->getPatternByEntity($entity);
+    if (empty($pattern)) {
+      // No pattern? Do nothing (otherwise we may blow away existing aliases...)
+      return NULL;
+    }
+
+    $source = '/' . $entity->toUrl()->getInternalPath();
+    $config = $this->configFactory->get('pathauto.settings');
+    $langcode = $entity->language()->getId();
+
+    // Core does not handle aliases with language Not Applicable.
+    if ($langcode == LanguageInterface::LANGCODE_NOT_APPLICABLE) {
+      $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED;
+    }
+
+    // Build token data.
+    $data = [
+      $this->tokenEntityMapper->getTokenTypeForEntityType($entity->getEntityTypeId()) => $entity,
+    ];
+
+    // Allow other modules to alter the pattern.
+    $context = array(
+      'module' => $entity->getEntityType()->getProvider(),
+      'op' => $op,
+      'source' => $source,
+      'data' => $data,
+      'bundle' => $entity->bundle(),
+      'language' => &$langcode,
+    );
+    // @todo Is still hook still useful?
+    $this->moduleHandler->alter('pathauto_pattern', $pattern, $context);
+
+    // Special handling when updating an item which is already aliased.
+    $existing_alias = NULL;
+    if ($op == 'update' || $op == 'bulkupdate') {
+      if ($existing_alias = $this->aliasStorageHelper->loadBySource($source, $langcode)) {
+        switch ($config->get('update_action')) {
+          case PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW:
+            // If an alias already exists,
+            // and the update action is set to do nothing,
+            // then gosh-darn it, do nothing.
+            return NULL;
+        }
+      }
+    }
+
+    // Replace any tokens in the pattern.
+    // Uses callback option to clean replacements. No sanitization.
+    // Pass empty BubbleableMetadata object to explicitly ignore cacheablity,
+    // as the result is never rendered.
+    $alias = $this->token->replace($pattern->getPattern(), $data, array(
+      'clear' => TRUE,
+      'callback' => array($this->aliasCleaner, 'cleanTokenValues'),
+      'langcode' => $langcode,
+      'pathauto' => TRUE,
+    ), new BubbleableMetadata());
+
+    // Check if the token replacement has not actually replaced any values. If
+    // that is the case, then stop because we should not generate an alias.
+    // @see token_scan()
+    $pattern_tokens_removed = preg_replace('/\[[^\s\]:]*:[^\s\]]*\]/', '', $pattern->getPattern());
+    if ($alias === $pattern_tokens_removed) {
+      return NULL;
+    }
+
+    $alias = $this->aliasCleaner->cleanAlias($alias);
+
+    // Allow other modules to alter the alias.
+    $context['source'] = &$source;
+    $context['pattern'] = $pattern;
+    $this->moduleHandler->alter('pathauto_alias', $alias, $context);
+
+    // If we have arrived at an empty string, discontinue.
+    if (!Unicode::strlen($alias)) {
+      return NULL;
+    }
+
+    // If the alias already exists, generate a new, hopefully unique, variant.
+    $original_alias = $alias;
+    $this->aliasUniquifier->uniquify($alias, $source, $langcode);
+    if ($original_alias != $alias) {
+      // Alert the user why this happened.
+      $this->messenger->addMessage($this->t('The automatically generated alias %original_alias conflicted with an existing alias. Alias changed to %alias.', array(
+        '%original_alias' => $original_alias,
+        '%alias' => $alias,
+      )), $op);
+    }
+
+    // Return the generated alias if requested.
+    if ($op == 'return') {
+      return $alias;
+    }
+
+    // Build the new path alias array and send it off to be created.
+    $path = array(
+      'source' => $source,
+      'alias' => $alias,
+      'language' => $langcode,
+    );
+
+    return $this->aliasStorageHelper->save($path, $existing_alias, $op);
+  }
+
+  /**
+   * Loads pathauto patterns for a given entity type ID
+   *
+   * @param string $entity_type_id
+   *   An entity type ID.
+   *
+   * @return \Drupal\pathauto\PathautoPatternInterface[]
+   *   A list of patterns, sorted by weight.
+   */
+  protected function getPatternByEntityType($entity_type_id) {
+    if (!isset($this->patternsByEntityType[$entity_type_id])) {
+      $ids = \Drupal::entityQuery('pathauto_pattern')
+        ->condition('type', array_keys(\Drupal::service('plugin.manager.alias_type')
+          ->getPluginDefinitionByType($this->tokenEntityMapper->getTokenTypeForEntityType($entity_type_id))))
+        ->condition('status', 1)
+        ->sort('weight')
+        ->execute();
+
+      $this->patternsByEntityType[$entity_type_id] = \Drupal::entityTypeManager()
+        ->getStorage('pathauto_pattern')
+        ->loadMultiple($ids);
+    }
+
+    return $this->patternsByEntityType[$entity_type_id];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPatternByEntity(EntityInterface $entity) {
+    $langcode = $entity->language()->getId();
+    if (!isset($this->patterns[$entity->getEntityTypeId()][$entity->id()][$langcode])) {
+      foreach ($this->getPatternByEntityType($entity->getEntityTypeId()) as $pattern) {
+        if ($pattern->applies($entity)) {
+          $this->patterns[$entity->getEntityTypeId()][$entity->id()][$langcode] = $pattern;
+          break;
+        }
+      }
+      // If still not set.
+      if (!isset($this->patterns[$entity->getEntityTypeId()][$entity->id()][$langcode])) {
+        $this->patterns[$entity->getEntityTypeId()][$entity->id()][$langcode] = NULL;
+      }
+    }
+    return $this->patterns[$entity->getEntityTypeId()][$entity->id()][$langcode];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function resetCaches() {
+    $this->patterns = [];
+    $this->patternsByEntityType = [];
+    $this->aliasCleaner->resetCaches();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function updateEntityAlias(EntityInterface $entity, $op, array $options = array()) {
+    // Skip if the entity does not have the path field.
+    if (!($entity instanceof ContentEntityInterface) || !$entity->hasField('path')) {
+      return NULL;
+    }
+
+    // Skip if pathauto processing is disabled.
+    if ($entity->path->pathauto != PathautoState::CREATE && empty($options['force'])) {
+      return NULL;
+    }
+
+    // Only act if this is the default revision.
+    if ($entity instanceof RevisionableInterface && !$entity->isDefaultRevision()) {
+      return NULL;
+    }
+
+    $options += array('language' => $entity->language()->getId());
+    $type = $entity->getEntityTypeId();
+
+    // Skip processing if the entity has no pattern.
+    if (!$this->getPatternByEntity($entity)) {
+      return NULL;
+    }
+
+    // Deal with taxonomy specific logic.
+    // @todo Update and test forum related code.
+    if ($type == 'taxonomy_term') {
+
+      $config_forum = $this->configFactory->get('forum.settings');
+      if ($entity->getVocabularyId() == $config_forum->get('vocabulary')) {
+        $type = 'forum';
+      }
+    }
+
+    try {
+      $result = $this->createEntityAlias($entity, $op);
+    }
+    catch (\InvalidArgumentException $e) {
+      drupal_set_message($e->getMessage(), 'error');
+      return NULL;
+    }
+
+    // @todo Move this to a method on the pattern plugin.
+    if ($type == 'taxonomy_term') {
+      foreach ($this->loadTermChildren($entity->id()) as $subterm) {
+        $this->updateEntityAlias($subterm, $op, $options);
+      }
+    }
+
+    return $result;
+  }
+
+  /**
+   * Finds all children of a term ID.
+   *
+   * @param int $tid
+   *   Term ID to retrieve parents for.
+   *
+   * @return \Drupal\taxonomy\TermInterface[]
+   *   An array of term objects that are the children of the term $tid.
+   */
+  protected function loadTermChildren($tid) {
+    return $this->entityTypeManager->getStorage('taxonomy_term')->loadChildren($tid);
+  }
+
+}

+ 89 - 0
sites/all/modules/contrib/admin/pathauto/src/PathautoGeneratorInterface.php

@@ -0,0 +1,89 @@
+<?php
+
+namespace Drupal\pathauto;
+
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Provides and interface for PathautoGenerator.
+ */
+interface PathautoGeneratorInterface {
+
+  /**
+   * "Do nothing. Leave the old alias intact."
+   */
+  const UPDATE_ACTION_NO_NEW = 0;
+
+  /**
+   * "Create a new alias. Leave the existing alias functioning."
+   */
+  const UPDATE_ACTION_LEAVE = 1;
+
+  /**
+   * "Create a new alias. Delete the old alias."
+   */
+  const UPDATE_ACTION_DELETE = 2;
+
+  /**
+   * Remove the punctuation from the alias.
+   */
+  const PUNCTUATION_REMOVE = 0;
+
+  /**
+   * Replace the punctuation with the separator in the alias.
+   */
+  const PUNCTUATION_REPLACE = 1;
+
+  /**
+   * Leave the punctuation as it is in the alias.
+   */
+  const PUNCTUATION_DO_NOTHING = 2;
+
+  /**
+   * Resets internal caches.
+   */
+  public function resetCaches();
+
+  /**
+   * Load an alias pattern entity by entity, bundle, and language.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   An entity.
+   * @return \Drupal\pathauto\PathautoPatternInterface|null
+   */
+  public function getPatternByEntity(EntityInterface $entity);
+
+  /**
+   * Apply patterns to create an alias.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity.
+   * @param string $op
+   *   Operation being performed on the content being aliased
+   *   ('insert', 'update', 'return', or 'bulkupdate').
+   *
+   * @return array|string
+   *   The alias that was created.
+   *
+   * @see _pathauto_set_alias()
+   */
+  public function createEntityAlias(EntityInterface $entity, $op);
+
+  /**
+   * Creates or updates an alias for the given entity.
+   *
+   * @param EntityInterface $entity
+   *   Entity for which to update the alias.
+   * @param string $op
+   *   The operation performed (insert, update)
+   * @param array $options
+   *   - force: will force updating the path
+   *   - language: the language for which to create the alias
+   *
+   * @return array|null
+   *   - An array with alias data in case the alias has been created or updated.
+   *   - NULL if no operation performed.
+   */
+  public function updateEntityAlias(EntityInterface $entity, $op, array $options = array());
+
+}

+ 51 - 0
sites/all/modules/contrib/admin/pathauto/src/PathautoItem.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\pathauto;
+
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinition;
+use Drupal\path\Plugin\Field\FieldType\PathItem;
+
+/**
+ * Extends the default PathItem implementation to generate aliases.
+ */
+class PathautoItem extends PathItem {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
+    $properties = parent::propertyDefinitions($field_definition);
+    $properties['pathauto'] = DataDefinition::create('integer')
+      ->setLabel(t('Pathauto state'))
+      ->setDescription(t('Whether an automated alias should be created or not.'))
+      ->setComputed(TRUE)
+      ->setClass('\Drupal\pathauto\PathautoState');
+    return $properties;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function postSave($update) {
+    // Only allow the parent implementation to act if pathauto will not create
+    // an alias.
+    if ($this->pathauto == PathautoState::SKIP) {
+      parent::postSave($update);
+    }
+    else {
+      \Drupal::service('pathauto.generator')->updateEntityAlias($this->getEntity(), $update ? 'update' : 'insert');
+    }
+    $this->get('pathauto')->persist();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isEmpty() {
+    // Make sure that the pathauto state flag does not get lost if just that is
+    // changed.
+    return !$this->alias && !$this->get('pathauto')->hasValue();
+  }
+
+}

+ 181 - 0
sites/all/modules/contrib/admin/pathauto/src/PathautoPatternInterface.php

@@ -0,0 +1,181 @@
+<?php
+
+namespace Drupal\pathauto;
+
+use Drupal\Core\Config\Entity\ConfigEntityInterface;
+
+/**
+ * Provides an interface for defining Pathauto pattern entities.
+ */
+interface PathautoPatternInterface extends ConfigEntityInterface {
+
+  /**
+   * Get the tokenized pattern used during alias generation.
+   *
+   * @return string
+   */
+  public function getPattern();
+
+  /**
+   * Set the tokenized pattern to use during alias generation.
+   *
+   * @param string $pattern
+   *
+   * @return $this
+   */
+  public function setPattern($pattern);
+
+  /**
+   * Gets the type of this pattern.
+   *
+   * @return string
+   */
+  public function getType();
+
+  /**
+   * @return \Drupal\pathauto\AliasTypeInterface
+   */
+  public function getAliasType();
+
+  /**
+   * Gets the weight of this pattern (compared to other patterns of this type).
+   *
+   * @return int
+   */
+  public function getWeight();
+
+  /**
+   * Sets the weight of this pattern (compared to other patterns of this type).
+   *
+   * @param int $weight
+   *   The weight of the variant.
+   *
+   * @return $this
+   */
+  public function setWeight($weight);
+
+  /**
+   * Returns the contexts of this pattern.
+   *
+   * @return \Drupal\Core\Plugin\Context\ContextInterface[]
+   */
+  public function getContexts();
+
+  /**
+   * Returns whether a relationship exists.
+   *
+   * @param string $token
+   *   Relationship identifier.
+   *
+   * @return bool
+   *   TRUE if the relationship exists, FALSE otherwise.
+   */
+  public function hasRelationship($token);
+
+  /**
+   * Adds a relationship.
+   *
+   * The relationship will not be changed if it already exists.
+   *
+   * @param string $token
+   *   Relationship identifier.
+   * @param string|null $label
+   *   (optional) A label, will use the label of the referenced context if not
+   *   provided.
+   *
+   * @return $this
+   */
+  public function addRelationship($token, $label = NULL);
+
+  /**
+   * Replaces a relationship.
+   *
+   * Only already existing relationships are updated.
+   *
+   * @param string $token
+   *   Relationship identifier.
+   * @param string|null $label
+   *   (optional) A label, will use the label of the referenced context if not
+   *   provided.
+   *
+   * @return $this
+   */
+  public function replaceRelationship($token, $label);
+
+  /**
+   * Removes a relationship.
+   *
+   * @param string $token
+   *   Relationship identifier.
+   *
+   * @return $this
+   */
+  public function removeRelationship($token);
+
+  /**
+   * Returns a list of relationships.
+   *
+   * @return array[]
+   *   Keys are context tokens, and values are arrays with the following keys:
+   *   - label (string|null, optional): The human-readable label of this
+   *     relationship.
+   */
+  public function getRelationships();
+
+  /**
+   * Gets the selection condition collection.
+   *
+   * @return \Drupal\Core\Condition\ConditionInterface[]|\Drupal\Core\Condition\ConditionPluginCollection
+   */
+  public function getSelectionConditions();
+
+  /**
+   * Adds selection criteria.
+   *
+   * @param array $configuration
+   *   Configuration of the selection criteria.
+   *
+   * @return string
+   *   The condition id of the new criteria.
+   */
+  public function addSelectionCondition(array $configuration);
+
+  /**
+   * Gets selection criteria by condition id.
+   *
+   * @param string $condition_id
+   *   The id of the condition.
+   *
+   * @return \Drupal\Core\Condition\ConditionInterface
+   */
+  public function getSelectionCondition($condition_id);
+
+  /**
+   * Removes selection criteria by condition id.
+   *
+   * @param string $condition_id
+   *   The id of the condition.
+   *
+   * @return $this
+   */
+  public function removeSelectionCondition($condition_id);
+
+  /**
+   * Gets the selection logic used by the criteria (ie. "and" or "or").
+   *
+   * @return string
+   *   Either "and" or "or"; represents how the selection criteria are combined.
+   */
+  public function getSelectionLogic();
+
+  /**
+   * Determines if this pattern can apply a given object.
+   *
+   * @param $object
+   *   The object used to determine if this plugin can apply.
+   *
+   * @return bool
+   */
+  public function applies($object);
+
+}

+ 51 - 0
sites/all/modules/contrib/admin/pathauto/src/PathautoPatternListBuilder.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\pathauto;
+
+use Drupal\Core\Config\Entity\DraggableListBuilder;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Provides a listing of Pathauto pattern entities.
+ */
+class PathautoPatternListBuilder extends DraggableListBuilder {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $limit = FALSE;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'pathauto_pattern_list';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildHeader() {
+    $header['label'] = $this->t('Label');
+    $header['pattern'] = $this->t('Pattern');
+    $header['type'] = $this->t('Pattern type');
+    $header['conditions'] = $this->t('Conditions');
+    return $header + parent::buildHeader();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildRow(EntityInterface $entity) {
+    /* @var \Drupal\pathauto\PathautoPatternInterface $entity */
+    $row['label'] = $entity->label();
+    $row['patern']['#markup'] = $entity->getPattern();
+    $row['type']['#markup'] = $entity->getAliasType()->getLabel();
+    $row['conditions']['#theme'] = 'item_list';
+    foreach ($entity->getSelectionConditions() as $condition) {
+      $row['conditions']['#items'][] = $condition->summary();
+    }
+    return $row + parent::buildRow($entity);
+  }
+
+}

+ 158 - 0
sites/all/modules/contrib/admin/pathauto/src/PathautoState.php

@@ -0,0 +1,158 @@
+<?php
+
+namespace Drupal\pathauto;
+
+use Drupal\Component\Utility\Crypt;
+use Drupal\Core\TypedData\TypedData;
+
+/**
+ * A property that stores in keyvalue whether an entity should receive an alias.
+ */
+class PathautoState extends TypedData {
+
+  /**
+   * An automatic alias should not be created.
+   */
+  const SKIP = 0;
+
+  /**
+   * An automatic alias should be created.
+   */
+  const CREATE = 1;
+
+  /**
+   * Pathauto state.
+   *
+   * @var int
+   */
+  protected $value;
+
+  /**
+   * @var \Drupal\Core\Field\FieldItemInterface
+   */
+  protected $parent;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getValue() {
+    if ($this->value === NULL) {
+      // If no value has been set or loaded yet, try to load a value if this
+      // entity has already been saved.
+      $this->value = \Drupal::keyValue($this->getCollection())
+        ->get(static::getPathautoStateKey($this->parent->getEntity()->id()));
+      // If it was not yet saved or no value was found, then set the flag to
+      // create the alias if there is a matching pattern.
+      if ($this->value === NULL) {
+        $entity = $this->parent->getEntity();
+        $pattern = \Drupal::service('pathauto.generator')->getPatternByEntity($entity);
+        $this->value = !empty($pattern) ? static::CREATE : static::SKIP;
+      }
+    }
+    return $this->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setValue($value, $notify = TRUE) {
+    $this->value = $value;
+    // Notify the parent of any changes.
+    if ($notify && isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
+  }
+
+  /**
+   * Returns TRUE if a value was set.
+   */
+  public function hasValue() {
+    return $this->value !== NULL;
+  }
+
+  /**
+   * Persists the state.
+   */
+  public function persist() {
+    \Drupal::keyValue($this->getCollection())
+      ->set(static::getPathautoStateKey($this->parent->getEntity()->id()), $this->getValue());
+  }
+
+  /**
+   * Deletes the stored state.
+   */
+  public function purge() {
+    \Drupal::keyValue($this->getCollection())
+      ->delete(static::getPathautoStateKey($this->parent->getEntity()->id()));
+  }
+
+  /**
+   * Returns the key value collection that should be used for the given entity.
+   * @return string
+   */
+  protected function getCollection() {
+    return 'pathauto_state.' . $this->parent->getEntity()->getEntityTypeId();
+  }
+
+  /**
+   * Deletes the URL aliases for multiple entities of the same type.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID of entities being deleted.
+   * @param int[] $pids_by_id
+   *   A list of path IDs keyed by entity ID.
+   */
+  public static function bulkDelete($entity_type_id, array $pids_by_id) {
+    foreach ($pids_by_id as $id => $pid) {
+      // Some key-values store entries have computed keys.
+      $key = static::getPathautoStateKey($id);
+      if ($key !== $id) {
+        $pids_by_id[$key] = $pid;
+        unset($pids_by_id[$id]);
+      }
+    }
+    $states = \Drupal::keyValue("pathauto_state.$entity_type_id")
+      ->getMultiple(array_keys($pids_by_id));
+
+    $pids = [];
+    foreach ($pids_by_id as $id => $pid) {
+      // Only delete aliases that were created by this module.
+      if (isset($states[$id]) && $states[$id] == PathautoState::CREATE) {
+        $pids[] = $pid;
+      }
+    }
+    \Drupal::service('pathauto.alias_storage_helper')->deleteMultiple($pids);
+  }
+
+  /**
+   * Gets the key-value store entry key for 'pathauto_state.*' collections.
+   *
+   * Normally we want to use the entity ID as key for 'pathauto_state.*'
+   * collection entries. But some entity types may use string IDs. When such IDs
+   * are exceeding 128 characters, which is the limit for the 'name' column in
+   * the {key_value} table, the insertion of the ID in {key_value} will fail.
+   * Thus we test if we can use the plain ID or we need to store a hashed
+   * version of the entity ID. Also, it is not possible to rely on the UUID as
+   * entity types might not have one or might use a non-standard format.
+   *
+   * The code is inspired by
+   * \Drupal\Core\Cache\DatabaseBackend::normalizeCid().
+   *
+   * @param int|string $entity_id
+   *   The entity id for which to compute the key.
+   *
+   * @return int|string
+   *   The key used to store the value in the key-value store.
+   *
+   * @see \Drupal\Core\Cache\DatabaseBackend::normalizeCid()
+   */
+  public static function getPathautoStateKey($entity_id) {
+    $entity_id_is_ascii = mb_check_encoding($entity_id, 'ASCII');
+    if ($entity_id_is_ascii && strlen($entity_id) <= 128) {
+      // The original entity ID, if it's an ASCII of 128 characters or less.
+      return $entity_id;
+    }
+    return Crypt::hashBase64($entity_id);
+  }
+
+}

+ 71 - 0
sites/all/modules/contrib/admin/pathauto/src/PathautoWidget.php

@@ -0,0 +1,71 @@
+<?php
+
+namespace Drupal\pathauto;
+
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\path\Plugin\Field\FieldWidget\PathWidget;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+
+/**
+ * Extends the core path widget.
+ */
+class PathautoWidget extends PathWidget {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
+    $element = parent::formElement($items, $delta, $element, $form, $form_state);
+    $entity = $items->getEntity();
+
+    // Taxonomy terms do not have an actual fieldset for path settings.
+    // Merge in the defaults.
+    // @todo Impossible to do this in widget, use another solution
+    /*
+    $form['path'] += array(
+      '#type' => 'fieldset',
+      '#title' => $this->t('URL path settings'),
+      '#collapsible' => TRUE,
+      '#collapsed' => empty($form['path']['alias']),
+      '#group' => 'additional_settings',
+      '#attributes' => array(
+        'class' => array('path-form'),
+      ),
+      '#access' => \Drupal::currentUser()->hasPermission('create url aliases') || \Drupal::currentUser()->hasPermission('administer url aliases'),
+      '#weight' => 30,
+      '#tree' => TRUE,
+      '#element_validate' => array('path_form_element_validate'),
+    );*/
+
+    $pattern = \Drupal::service('pathauto.generator')->getPatternByEntity($entity);
+    if (empty($pattern)) {
+      return $element;
+    }
+
+    if (\Drupal::currentUser()->hasPermission('administer pathauto')) {
+      $description = $this->t('Uncheck this to create a custom alias below. <a href="@admin_link">Configure URL alias patterns.</a>', ['@admin_link' => Url::fromRoute('entity.pathauto_pattern.collection')->toString()]);
+    }
+    else {
+      $description = $this->t('Uncheck this to create a custom alias below.');
+    }
+
+    $element['pathauto'] = array(
+      '#type' => 'checkbox',
+      '#title' => $this->t('Generate automatic URL alias'),
+      '#default_value' => $entity->path->pathauto,
+      '#description' => $description,
+      '#weight' => -1,
+    );
+
+    // Add JavaScript that will disable the path textfield when the automatic
+    // alias checkbox is checked.
+    $element['alias']['#states']['disabled']['input[name="path[' . $delta . '][pathauto]"]'] = array('checked' => TRUE);
+
+    // Override path.module's vertical tabs summary.
+    $element['alias']['#attached']['library'] = ['pathauto/widget'];
+
+    return $element;
+  }
+
+}

+ 36 - 0
sites/all/modules/contrib/admin/pathauto/src/Plugin/Action/UpdateAction.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\pathauto\Plugin\Action;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Action\ActionBase;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\pathauto\PathautoState;
+
+/**
+ * Pathauto entity update action.
+ *
+ * @Action(
+ *   id = "pathauto_update_alias",
+ *   label = @Translation("Update URL alias of an entity"),
+ * )
+ */
+class UpdateAction extends ActionBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function execute($entity = NULL) {
+    $entity->path->pathauto = PathautoState::CREATE;
+    \Drupal::service('pathauto.generator')->updateEntityAlias($entity, 'bulkupdate', array('message' => TRUE));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
+    $result = AccessResult::allowedIfHasPermission($account, 'create url aliases');
+    return $return_as_object ? $result : $result->isAllowed();
+  }
+
+}

+ 94 - 0
sites/all/modules/contrib/admin/pathauto/src/Plugin/Deriver/EntityAliasTypeDeriver.php

@@ -0,0 +1,94 @@
+<?php
+
+namespace Drupal\pathauto\Plugin\Deriver;
+
+use Drupal\Component\Plugin\Derivative\DeriverBase;
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Plugin\Context\ContextDefinition;
+use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\token\TokenEntityMapperInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Deriver that exposes content entities as alias type plugins.
+ */
+class EntityAliasTypeDeriver extends DeriverBase implements ContainerDeriverInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
+   */
+  protected $entityFieldManager;
+
+  /**
+   * @var \Drupal\token\TokenEntityMapperInterface
+   */
+  protected $tokenEntityMapper;
+
+  /**
+   * Constructs new EntityAliasTypeDeriver.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
+   *   The entity field manager.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   *   The string translation service.
+   * @apram \Drupal\Token\TokenEntityMapperInterface $token_entity_mapper
+   *   The token entity mapper.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, TranslationInterface $string_translation, TokenEntityMapperInterface $token_entity_mapper) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->entityFieldManager = $entity_field_manager;
+    $this->stringTranslation = $string_translation;
+    $this->tokenEntityMapper = $token_entity_mapper;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, $base_plugin_id) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $container->get('entity_field.manager'),
+      $container->get('string_translation'),
+      $container->get('token.entity_mapper')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDerivativeDefinitions($base_plugin_definition) {
+    foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
+      // An entity type must have a canonical link template and support fields.
+      if ($entity_type->hasLinkTemplate('canonical') && is_subclass_of($entity_type->getClass(), FieldableEntityInterface::class)) {
+        $base_fields = $this->entityFieldManager->getBaseFieldDefinitions($entity_type_id);
+        if (!isset($base_fields['path'])) {
+          // The entity type does not have a path field and is therefore not
+          // supported.
+          continue;
+        }
+        $this->derivatives[$entity_type_id] = $base_plugin_definition;
+        $this->derivatives[$entity_type_id]['label'] = $entity_type->getLabel();
+        $this->derivatives[$entity_type_id]['types'] = [$this->tokenEntityMapper->getTokenTypeForEntityType($entity_type_id)];
+        $this->derivatives[$entity_type_id]['provider'] = $entity_type->getProvider();
+        $this->derivatives[$entity_type_id]['context'] = [
+          $entity_type_id => new ContextDefinition("entity:$entity_type_id", $this->t('@label being aliased', ['@label' => $entity_type->getLabel()]))
+        ];
+      }
+    }
+    return $this->derivatives;
+  }
+
+}

+ 24 - 0
sites/all/modules/contrib/admin/pathauto/src/Plugin/pathauto/AliasType/Broken.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace Drupal\pathauto\Plugin\pathauto\AliasType;
+
+/**
+ * Defines a fallback plugin for missing block plugins.
+ *
+ * @AliasType(
+ *   id = "broken",
+ *   label = @Translation("Broken"),
+ *   admin_label = @Translation("Broken/Missing"),
+ *   category = @Translation("AliasType"),
+ * )
+ */
+class Broken extends EntityAliasTypeBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLabel() {
+    return $this->t('Broken type');
+  }
+
+}

+ 334 - 0
sites/all/modules/contrib/admin/pathauto/src/Plugin/pathauto/AliasType/EntityAliasTypeBase.php

@@ -0,0 +1,334 @@
+<?php
+
+namespace Drupal\pathauto\Plugin\pathauto\AliasType;
+
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\ContextAwarePluginBase;
+use Drupal\pathauto\AliasTypeBatchUpdateInterface;
+use Drupal\pathauto\AliasTypeInterface;
+use Drupal\pathauto\PathautoState;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * A pathauto alias type plugin for entities with canonical links.
+ *
+ * @AliasType(
+ *   id = "canonical_entities",
+ *   deriver = "\Drupal\pathauto\Plugin\Deriver\EntityAliasTypeDeriver"
+ * )
+ */
+class EntityAliasTypeBase extends ContextAwarePluginBase implements AliasTypeInterface, AliasTypeBatchUpdateInterface, ContainerFactoryPluginInterface {
+
+  /**
+   * The module handler service.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The language manager service.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * The entity manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The key/value manager service.
+   *
+   * @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface
+   */
+  protected $keyValue;
+
+  /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $database;
+
+  /**
+   * The path prefix for this entity type.
+   *
+   * @var string
+   */
+  protected $prefix;
+
+  /**
+   * Constructs a EntityAliasTypeBase instance.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler service.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager service.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity manager service.
+   * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value
+   *   The key/value manager service.
+   * @param \Drupal\Core\Database\Connection $database
+   *   The database connection.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager, EntityTypeManagerInterface $entity_type_manager, KeyValueFactoryInterface $key_value, Connection $database) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->moduleHandler = $module_handler;
+    $this->languageManager = $language_manager;
+    $this->entityTypeManager = $entity_type_manager;
+    $this->keyValue = $key_value;
+    $this->database = $database;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('module_handler'),
+      $container->get('language_manager'),
+      $container->get('entity_type.manager'),
+      $container->get('keyvalue'),
+      $container->get('database')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLabel() {
+    $definition = $this->getPluginDefinition();
+    // Cast the admin label to a string since it is an object.
+    // @see \Drupal\Core\StringTranslation\TranslationWrapper
+    return (string) $definition['label'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTokenTypes() {
+    $definition = $this->getPluginDefinition();
+    return $definition['types'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function batchUpdate($action, &$context) {
+    if (!isset($context['sandbox']['current'])) {
+      $context['sandbox']['count'] = 0;
+      $context['sandbox']['current'] = 0;
+    }
+
+    $entity_type = $this->entityTypeManager->getDefinition($this->getEntityTypeId());
+    $id_key = $entity_type->getKey('id');
+
+    $query = $this->database->select($entity_type->get('base_table'), 'base_table');
+    $query->leftJoin('url_alias', 'ua', "CONCAT('" . $this->getSourcePrefix() . "' , base_table.$id_key) = ua.source");
+    $query->addField('base_table', $id_key, 'id');
+
+    switch ($action) {
+      case 'create':
+        $query->isNull('ua.source');
+        break;
+      case 'update':
+        $query->isNotNull('ua.source');
+        break;
+      case 'all':
+        // Nothing to do. We want all paths.
+        break;
+      default:
+        // Unknown action. Abort!
+        return;
+    }
+    $query->condition('base_table.' . $id_key, $context['sandbox']['current'], '>');
+    $query->orderBy('base_table.' . $id_key);
+    $query->addTag('pathauto_bulk_update');
+    $query->addMetaData('entity', $this->getEntityTypeId());
+
+    // Get the total amount of items to process.
+    if (!isset($context['sandbox']['total'])) {
+      $context['sandbox']['total'] = $query->countQuery()->execute()->fetchField();
+
+      // If there are no entities to update, then stop immediately.
+      if (!$context['sandbox']['total']) {
+        $context['finished'] = 1;
+        return;
+      }
+    }
+
+    $query->range(0, 25);
+    $ids = $query->execute()->fetchCol();
+
+    $updates = $this->bulkUpdate($ids);
+    $context['sandbox']['count'] += count($ids);
+    $context['sandbox']['current'] = max($ids);
+    $context['results']['updates'] += $updates;
+    $context['message'] = $this->t('Updated alias for %label @id.', array('%label' => $entity_type->getLabel(), '@id' => end($ids)));
+
+    if ($context['sandbox']['count'] != $context['sandbox']['total']) {
+      $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total'];
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function batchDelete(&$context) {
+    if (!isset($context['sandbox']['current'])) {
+      $context['sandbox']['count'] = 0;
+      $context['sandbox']['current'] = 0;
+    }
+
+    $entity_type = $this->entityTypeManager->getDefinition($this->getEntityTypeId());
+    $id_key = $entity_type->getKey('id');
+
+    $query = $this->database->select($entity_type->get('base_table'), 'base_table');
+    $query->innerJoin('url_alias', 'ua', "CONCAT('" . $this->getSourcePrefix() . "' , base_table.$id_key) = ua.source");
+    $query->addField('base_table', $id_key, 'id');
+    $query->addField('ua', 'pid');
+    $query->condition('ua.pid', $context['sandbox']['current'], '>');
+    $query->orderBy('ua.pid');
+    $query->addTag('pathauto_bulk_delete');
+    $query->addMetaData('entity', $this->getEntityTypeId());
+
+    // Get the total amount of items to process.
+    if (!isset($context['sandbox']['total'])) {
+      $context['sandbox']['total'] = $query->countQuery()->execute()->fetchField();
+
+      // If there are no entities to delete, then stop immediately.
+      if (!$context['sandbox']['total']) {
+        $context['finished'] = 1;
+        return;
+      }
+    }
+
+    $query->range(0, 100);
+    $pids_by_id = $query->execute()->fetchAllKeyed();
+
+    PathautoState::bulkDelete($this->getEntityTypeId(), $pids_by_id);
+    $context['sandbox']['count'] += count($pids_by_id);
+    $context['sandbox']['current'] = max($pids_by_id);
+    $context['results']['deletions'][] = $this->getLabel();
+
+    if ($context['sandbox']['count'] != $context['sandbox']['total']) {
+      $context['finished'] = $context['sandbox']['count'] / $context['sandbox']['total'];
+    }
+  }
+
+  /**
+   * Returns the entity type ID.
+   *
+   * @return string
+   *   The entity type ID.
+   */
+  protected function getEntityTypeId() {
+    return $this->getDerivativeId();
+  }
+
+  /**
+   * Update the URL aliases for multiple entities.
+   *
+   * @param array $ids
+   *   An array of entity IDs.
+   * @param array $options
+   *   An optional array of additional options.
+   *
+   * @return int
+   *  The number of updated URL aliases.
+   */
+  protected function bulkUpdate(array $ids, array $options = array()) {
+    $options += array('message' => FALSE);
+    $updates = 0;
+
+    $entities = $this->entityTypeManager->getStorage($this->getEntityTypeId())->loadMultiple($ids);
+    foreach ($entities as $entity) {
+      // Update aliases for the entity's default language and its translations.
+      foreach ($entity->getTranslationLanguages() as $langcode => $language) {
+        $translated_entity = $entity->getTranslation($langcode);
+        $result = \Drupal::service('pathauto.generator')->updateEntityAlias($translated_entity, 'bulkupdate', $options);
+        if ($result) {
+          $updates++;
+        }
+      }
+    }
+
+    if (!empty($options['message'])) {
+      drupal_set_message(\Drupal::translation()->formatPlural(count($ids), 'Updated 1 %label URL alias.', 'Updated @count %label URL aliases.'), array('%label' => $this->getLabel()));
+    }
+
+    return $updates;
+  }
+
+  /**
+   * Deletes the URL aliases for multiple entities.
+   *
+   * @param int[] $pids_by_id
+   *   A list of path IDs keyed by entity ID.
+   *
+   * @deprecated Use \Drupal\pathauto\PathautoState::bulkDelete() instead.
+   */
+  protected function bulkDelete(array $pids_by_id) {
+    PathautoState::bulkDelete($this->getEntityTypeId(), $pids_by_id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    $dependencies = [];
+    $dependencies['module'][] = $this->entityTypeManager->getDefinition($this->getEntityTypeId())->getProvider();
+    return $dependencies;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function applies($object) {
+    return $object instanceof FieldableEntityInterface && $object->getEntityTypeId() == $this->getEntityTypeId();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSourcePrefix() {
+    if (empty($this->prefix)) {
+      $entity_type = $this->entityTypeManager->getDefinition($this->getEntityTypeId());
+      $path = $entity_type->getLinkTemplate('canonical');
+      $this->prefix = substr($path, 0, strpos($path, '{'));
+    }
+    return $this->prefix;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setContextValue($name, $value) {
+    // Overridden to avoid merging existing cacheability metadata, which is not
+    // relevant for alias type plugins.
+    $this->context[$name] = new Context($this->getContextDefinition($name), $value);
+    return $this;
+  }
+
+
+}

+ 106 - 0
sites/all/modules/contrib/admin/pathauto/src/Plugin/pathauto/AliasType/ForumAliasType.php

@@ -0,0 +1,106 @@
+<?php
+
+namespace Drupal\pathauto\Plugin\pathauto\AliasType;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * A pathauto alias type plugin for forum terms.
+ *
+ * @AliasType(
+ *   id = "forum",
+ *   label = @Translation("Forum"),
+ *   types = {"term"},
+ *   provider = "forum",
+ *   context = {
+ *     "taxonomy_term" = @ContextDefinition("entity:taxonomy_term")
+ *   }
+ * )
+ */
+class ForumAliasType extends EntityAliasTypeBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * Constructs a ForumAliasType instance.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler service.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager service.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity manager service.
+   * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value
+   *   The key/value manager service.
+   * @param \Drupal\Core\Database\Connection $database
+   *   The database service.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager, EntityTypeManagerInterface $entity_type_manager, KeyValueFactoryInterface $key_value, Connection $database, ConfigFactoryInterface $config_factory) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $module_handler, $language_manager, $entity_type_manager, $key_value, $database);
+    $this->configFactory = $config_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('module_handler'),
+      $container->get('language_manager'),
+      $container->get('entity_type.manager'),
+      $container->get('keyvalue'),
+      $container->get('database'),
+      $container->get('config.factory')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEntityTypeId() {
+    return 'taxonomy_term';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSourcePrefix() {
+    return '/forum/';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function applies($object) {
+    if (parent::applies($object)) {
+      /** @var \Drupal\taxonomy\TermInterface $object */
+      $vid = $this->configFactory->get('forum.settings')->get('vocabulary');
+      return $object->getVocabularyId() == $vid;
+    }
+    return FALSE;
+  }
+
+}

+ 156 - 0
sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoBulkUpdateTest.php

@@ -0,0 +1,156 @@
+<?php
+
+namespace Drupal\pathauto\Tests;
+
+use Drupal\pathauto\PathautoGeneratorInterface;
+use Drupal\pathauto\PathautoState;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Bulk update functionality tests.
+ *
+ * @group pathauto
+ */
+class PathautoBulkUpdateTest extends WebTestBase {
+
+  use PathautoTestHelperTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('node', 'pathauto', 'forum');
+
+  /**
+   * Admin user.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * The created nodes.
+   *
+   * @var \Drupal\node\NodeInterface
+   */
+  protected $nodes;
+
+  /**
+   * The created patterns.
+   *
+   * @var \Drupal\pathauto\PathautoPatternInterface
+   */
+  protected $patterns;
+
+  /**
+   * {inheritdoc}
+   */
+  function setUp() {
+    parent::setUp();
+
+    // Allow other modules to add additional permissions for the admin user.
+    $permissions = array(
+      'administer pathauto',
+      'administer url aliases',
+      'create url aliases',
+      'administer forums',
+    );
+    $this->adminUser = $this->drupalCreateUser($permissions);
+    $this->drupalLogin($this->adminUser);
+
+    $this->patterns = array();
+    $this->patterns['node'] = $this->createPattern('node', '/content/[node:title]');
+    $this->patterns['user'] = $this->createPattern('user', '/users/[user:name]');
+    $this->patterns['forum'] = $this->createPattern('forum', '/forums/[term:name]');
+  }
+
+  function testBulkUpdate() {
+    // Create some nodes.
+    $this->nodes = array();
+    for ($i = 1; $i <= 5; $i++) {
+      $node = $this->drupalCreateNode();
+      $this->nodes[$node->id()] = $node;
+    }
+
+    // Clear out all aliases.
+    $this->deleteAllAliases();
+
+    // Bulk create aliases.
+    $edit = array(
+      'update[canonical_entities:node]' => TRUE,
+      'update[canonical_entities:user]' => TRUE,
+      'update[forum]' => TRUE,
+    );
+    $this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update'));
+
+    // This has generated 8 aliases: 5 nodes, 2 users and 1 forum.
+    $this->assertText('Generated 8 URL aliases.');
+
+    // Check that aliases have actually been created.
+    foreach ($this->nodes as $node) {
+      $this->assertEntityAliasExists($node);
+    }
+    $this->assertEntityAliasExists($this->adminUser);
+    // This is the default "General discussion" forum.
+    $this->assertAliasExists(['source' => '/taxonomy/term/1']);
+
+    // Add a new node.
+    $new_node = $this->drupalCreateNode(array('path' => array('alias' => '', 'pathauto' => PathautoState::SKIP)));
+
+    // Run the update again which should not run against any nodes.
+    $this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update'));
+    $this->assertText('No new URL aliases to generate.');
+    $this->assertNoEntityAliasExists($new_node);
+
+    // Make sure existing aliases can be overriden.
+    $this->drupalPostForm('admin/config/search/path/settings', ['update_action' => PathautoGeneratorInterface::UPDATE_ACTION_DELETE], t('Save configuration'));
+
+    // Patterns did not change, so no aliases should be regenerated.
+    $edit['action'] = 'all';
+    $this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update'));
+    $this->assertText('No new URL aliases to generate.');
+
+    // Update the node pattern, and leave other patterns alone. Existing nodes should get a new alias,
+    // except the node above whose alias is manually set. Other aliases must be left alone.
+    $this->patterns['node']->delete();
+    $this->patterns['node'] = $this->createPattern('node', '/archive/node-[node:nid]');
+
+    $this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update'));
+    $this->assertText('Generated 5 URL aliases.');
+
+    // Prevent existing aliases to be overriden. The bulk generate page should only offer
+    // to create an alias for paths which have none.
+    $this->drupalPostForm('admin/config/search/path/settings', ['update_action' => PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW], t('Save configuration'));
+
+    $this->drupalGet('admin/config/search/path/update_bulk');
+    $this->assertFieldByName('action', 'create');
+    $this->assertText('Pathauto settings are set to ignore paths which already have a URL alias.');
+    $this->assertNoFieldByName('action', 'update');
+    $this->assertNoFieldByName('action', 'all');
+  }
+
+  /**
+   * Tests alias generation for nodes that existed before installing Pathauto.
+   */
+  function testBulkUpdateExistingContent() {
+    // Create a node.
+    $node = $this->drupalCreateNode();
+
+    // Delete its alias and Pathauto metadata.
+    \Drupal::service('pathauto.alias_storage_helper')->deleteEntityPathAll($node);
+    $node->path->first()->get('pathauto')->purge();
+    \Drupal::entityTypeManager()->getStorage('node')->resetCache(array($node->id()));
+
+    // Execute bulk generation.
+    // Bulk create aliases.
+    $edit = array(
+      'update[canonical_entities:node]' => TRUE,
+    );
+    $this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update'));
+
+    // Verify that the alias was created for the node.
+    $this->assertText('Generated 1 URL alias.');
+  }
+
+}

+ 87 - 0
sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoEnablingEntityTypesTest.php

@@ -0,0 +1,87 @@
+<?php
+
+namespace Drupal\pathauto\Tests;
+
+use Drupal\simpletest\WebTestBase;
+use Drupal\comment\Tests\CommentTestTrait;
+
+/**
+ * Tests pathauto settings form.
+ *
+ * @group pathauto
+ */
+class PathautoEnablingEntityTypesTest extends WebTestBase {
+
+  use PathautoTestHelperTrait;
+  use CommentTestTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('node', 'pathauto', 'comment');
+
+  /**
+   * Admin user.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * {inheritdoc}
+   */
+  function setUp() {
+    parent::setUp();
+
+    $this->drupalCreateContentType(array('type' => 'article'));
+    $this->addDefaultCommentField('node', 'article');
+
+    $permissions = array(
+      'administer pathauto',
+      'administer url aliases',
+      'create url aliases',
+      'administer nodes',
+      'post comments',
+    );
+    $this->adminUser = $this->drupalCreateUser($permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  /**
+   * A suite of tests to verify if the feature to enable and disable the
+   * ability to define alias patterns for a given entity type works. Test with
+   * the comment module, as it is not enabled by default.
+   */
+  function testEnablingEntityTypes() {
+    // Verify that the comment entity type is not available when trying to add
+    // a new pattern, nor "broken".
+    $this->drupalGet('/admin/config/search/path/patterns/add');
+    $this->assertEqual(count($this->cssSelect('option[value = "canonical_entities:comment"]:contains(Comment)')), 0);
+    $this->assertEqual(count($this->cssSelect('option:contains(Broken)')), 0);
+
+    // Enable the entity type and create a pattern for it.
+    $this->drupalGet('/admin/config/search/path/settings');
+    $edit = [
+      'enabled_entity_types[comment]' => TRUE,
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Save configuration');
+    $this->createPattern('comment', '/comment/[comment:body]');
+
+    // Create a node, a comment type and a comment entity.
+    $node = $this->drupalCreateNode(['type' => 'article']);
+    $this->drupalGet('/node/' . $node->id());
+    $edit = [
+      'comment_body[0][value]' => 'test-body',
+    ];
+    $this->drupalPostForm(NULL, $edit, 'Save');
+
+    // Verify that an alias has been generated and that the type can no longer
+    // be disabled.
+    $this->assertAliasExists(['alias' => '/comment/test-body']);
+    $this->drupalGet('/admin/config/search/path/settings');
+    $this->assertEqual(count($this->cssSelect('input[name = "enabled_entity_types[comment]"][disabled = "disabled"]')), 1);
+  }
+
+}

+ 216 - 0
sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoLocaleTest.php

@@ -0,0 +1,216 @@
+<?php
+
+namespace Drupal\pathauto\Tests;
+
+use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\pathauto\PathautoState;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Test pathauto functionality with localization and translation.
+ *
+ * @group pathauto
+ */
+class PathautoLocaleTest extends WebTestBase {
+
+  use PathautoTestHelperTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('node', 'pathauto', 'locale', 'content_translation');
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Create Article node type.
+    $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
+  }
+
+  /**
+   * Test that when an English node is updated, its old English alias is
+   * updated and its newer French alias is left intact.
+   */
+  function testLanguageAliases() {
+
+    $this->createPattern('node', '/content/[node:title]');
+
+    // Add predefined French language.
+    ConfigurableLanguage::createFromLangcode('fr')->save();
+
+    $node = array(
+      'title' => 'English node',
+      'langcode' => 'en',
+      'path' => array(array(
+        'alias' => '/english-node',
+        'pathauto' => FALSE,
+      )),
+    );
+    $node = $this->drupalCreateNode($node);
+    $english_alias = \Drupal::service('path.alias_storage')->load(array('alias' => '/english-node', 'langcode' => 'en'));
+    $this->assertTrue($english_alias, 'Alias created with proper language.');
+
+    // Also save a French alias that should not be left alone, even though
+    // it is the newer alias.
+    $this->saveEntityAlias($node, '/french-node', 'fr');
+
+    // Add an alias with the soon-to-be generated alias, causing the upcoming
+    // alias update to generate a unique alias with the '-0' suffix.
+    $this->saveAlias('/node/invalid', '/content/english-node', Language::LANGCODE_NOT_SPECIFIED);
+
+    // Update the node, triggering a change in the English alias.
+    $node->path->pathauto = PathautoState::CREATE;
+    $node->save();
+
+    // Check that the new English alias replaced the old one.
+    $this->assertEntityAlias($node, '/content/english-node-0', 'en');
+    $this->assertEntityAlias($node, '/french-node', 'fr');
+    $this->assertAliasExists(array('pid' => $english_alias['pid'], 'alias' => '/content/english-node-0'));
+
+    // Create a new node with the same title as before but without
+    // specifying a language.
+    $node = $this->drupalCreateNode(array('title' => 'English node', 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED));
+
+    // Check that the new node had a unique alias generated with the '-0'
+    // suffix.
+    $this->assertEntityAlias($node, '/content/english-node-0', LanguageInterface::LANGCODE_NOT_SPECIFIED);
+  }
+
+  /**
+   * Test that patterns work on multilingual content.
+   */
+  function testLanguagePatterns() {
+
+    // Allow other modules to add additional permissions for the admin user.
+    $permissions = array(
+      'administer pathauto',
+      'administer url aliases',
+      'create url aliases',
+      'bypass node access',
+      'access content overview',
+      'administer languages',
+      'translate any entity',
+      'administer content translation'
+
+    );
+    $admin_user = $this->drupalCreateUser($permissions);
+    $this->drupalLogin($admin_user);
+
+    // Add French language.
+    $edit = array(
+      'predefined_langcode' => 'fr',
+    );
+    $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
+
+    $this->enableArticleTranslation();
+
+    // Create a pattern for English articles.
+    $this->drupalGet('admin/config/search/path/patterns/add');
+    $edit = array(
+      'type' => 'canonical_entities:node',
+    );
+    $this->drupalPostAjaxForm(NULL, $edit, 'type');
+    $edit += array(
+      'pattern' => '/the-articles/[node:title]',
+      'label' => 'English articles',
+      'id' => 'english_articles',
+      'bundles[article]' => TRUE,
+      'languages[en]' => TRUE,
+    );
+    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->assertText('Pattern English articles saved.');
+
+    // Create a pattern for French articles.
+    $this->drupalGet('admin/config/search/path/patterns/add');
+    $edit = array(
+      'type' => 'canonical_entities:node',
+    );
+    $this->drupalPostAjaxForm(NULL, $edit, 'type');
+    $edit += array(
+      'pattern' => '/les-articles/[node:title]',
+      'label' => 'French articles',
+      'id' => 'french_articles',
+      'bundles[article]' => TRUE,
+      'languages[fr]' => TRUE,
+    );
+    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->assertText('Pattern French articles saved.');
+
+    // Create a node and its translation. Assert aliases.
+    $edit = array(
+      'title[0][value]' => 'English node',
+      'langcode[0][value]' => 'en',
+    );
+    $this->drupalPostForm('node/add/article', $edit, t('Save'));
+    $english_node = $this->drupalGetNodeByTitle('English node');
+    return;
+    $this->assertAlias('/node/' . $english_node->id(), '/the-articles/english-node', 'en');
+
+    $this->drupalGet('node/' . $english_node->id() . '/translations');
+    $this->clickLink(t('Add'));
+    $edit = array(
+      'title[0][value]' => 'French node',
+    );
+    $this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
+    $this->rebuildContainer();
+    $english_node = $this->drupalGetNodeByTitle('English node');
+    $french_node = $english_node->getTranslation('fr');
+    $this->assertAlias('/node/' . $french_node->id(), '/les-articles/french-node', 'fr');
+
+    // Bulk delete and Bulk generate patterns. Assert aliases.
+    $this->deleteAllAliases();
+    // Bulk create aliases.
+    $edit = array(
+      'update[canonical_entities:node]' => TRUE,
+    );
+    $this->drupalPostForm('admin/config/search/path/update_bulk', $edit, t('Update'));
+    $this->assertText(t('Generated 2 URL aliases.'));
+    $this->assertAlias('/node/' . $english_node->id(), '/the-articles/english-node', 'en');
+    $this->assertAlias('/node/' . $french_node->id(), '/les-articles/french-node', 'fr');
+  }
+
+  /**
+   * Tests the alias created for a node with language Not Applicable.
+   */
+  public function testLanguageNotApplicable() {
+    $this->drupalLogin($this->rootUser);
+    $this->enableArticleTranslation();
+
+    // Create a pattern for nodes.
+    $pattern = $this->createPattern('node', '/content/[node:title]', -1);
+    $pattern->save();
+
+    // Create a node with language Not Applicable.
+    $node = $this->createNode(['type' => 'article', 'title' => 'Test node', 'langcode' => LanguageInterface::LANGCODE_NOT_APPLICABLE]);
+
+    // Check that the generated alias has language Not Specified.
+    $alias = \Drupal::service('pathauto.alias_storage_helper')->loadBySource('/node/' . $node->id());
+    $this->assertEqual($alias['langcode'], LanguageInterface::LANGCODE_NOT_SPECIFIED, 'PathautoGenerator::createEntityAlias() adjusts the alias langcode from Not Applicable to Not Specified.');
+
+    // Check that the alias works.
+    $this->drupalGet('content/test-node');
+    $this->assertResponse(200);
+  }
+
+  /**
+   * Enables content translation on articles.
+   */
+  protected function enableArticleTranslation() {
+    // Enable content translation on articles.
+    $this->drupalGet('admin/config/regional/content-language');
+    $edit = array(
+      'entity_types[node]' => TRUE,
+      'settings[node][article][translatable]' => TRUE,
+      'settings[node][article][settings][language][language_alterable]' => TRUE,
+    );
+    $this->drupalPostForm(NULL, $edit, t('Save configuration'));
+  }
+
+}

+ 200 - 0
sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoMassDeleteTest.php

@@ -0,0 +1,200 @@
+<?php
+
+namespace Drupal\pathauto\Tests;
+
+use Drupal\pathauto\PathautoState;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Mass delete functionality tests.
+ *
+ * @group pathauto
+ */
+class PathautoMassDeleteTest extends WebTestBase {
+
+  use PathautoTestHelperTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('node', 'taxonomy', 'pathauto');
+
+  /**
+   * Admin user.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * The test nodes.
+   *
+   * @var \Drupal\node\NodeInterface
+   */
+  protected $nodes;
+
+  /**
+   * The test accounts.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $accounts;
+
+  /**
+   * The test terms.
+   *
+   * @var \Drupal\taxonomy\TermInterface
+   */
+  protected $terms;
+
+
+  /**
+   * {inheritdoc}
+   */
+  function setUp() {
+    parent::setUp();
+
+    $permissions = array(
+      'administer pathauto',
+      'administer url aliases',
+      'create url aliases',
+    );
+    $this->adminUser = $this->drupalCreateUser($permissions);
+    $this->drupalLogin($this->adminUser);
+
+    $this->createPattern('node', '/content/[node:title]');
+    $this->createPattern('user', '/users/[user:name]');
+    $this->createPattern('taxonomy_term', '/[term:vocabulary]/[term:name]');
+  }
+
+  /**
+   * Tests the deletion of all the aliases.
+   */
+  function testDeleteAll() {
+    // 1. Test that deleting all the aliases, of any type, works.
+    $this->generateAliases();
+    $edit = array(
+      'delete[all_aliases]' => TRUE,
+      'options[keep_custom_aliases]' => FALSE,
+    );
+    $this->drupalPostForm('admin/config/search/path/delete_bulk', $edit, t('Delete aliases now!'));
+    $this->assertText(t('All of your path aliases have been deleted.'));
+    $this->assertUrl('admin/config/search/path/delete_bulk');
+
+    // Make sure that all of them are actually deleted.
+    $aliases = \Drupal::database()->select('url_alias', 'ua')->fields('ua', array())->execute()->fetchAll();
+    $this->assertEqual($aliases, array(), "All the aliases have been deleted.");
+
+    // 2. Test deleting only specific (entity type) aliases.
+    $manager = $this->container->get('plugin.manager.alias_type');
+    $pathauto_plugins = array('canonical_entities:node' => 'nodes', 'canonical_entities:taxonomy_term' => 'terms', 'canonical_entities:user' => 'accounts');
+    foreach ($pathauto_plugins as $pathauto_plugin => $attribute) {
+      $this->generateAliases();
+      $edit = array(
+        'delete[plugins][' . $pathauto_plugin . ']' => TRUE,
+        'options[keep_custom_aliases]' => FALSE,
+      );
+      $this->drupalPostForm('admin/config/search/path/delete_bulk', $edit, t('Delete aliases now!'));
+      $alias_type = $manager->createInstance($pathauto_plugin);
+      $this->assertRaw(t('All of your %label path aliases have been deleted.', array('%label' => $alias_type->getLabel())));
+      // Check that the aliases were actually deleted.
+      foreach ($this->{$attribute} as $entity) {
+        $this->assertNoEntityAlias($entity);
+      }
+
+      // Check that the other aliases are not deleted.
+      foreach ($pathauto_plugins as $_pathauto_plugin => $_attribute) {
+        // Skip the aliases that should be deleted.
+        if ($_pathauto_plugin == $pathauto_plugin) {
+          continue;
+        }
+        foreach ($this->{$_attribute} as $entity) {
+          $this->assertEntityAliasExists($entity);
+        }
+      }
+    }
+
+    // 3. Test deleting automatically generated aliases only.
+    $this->generateAliases();
+    $edit = array(
+      'delete[all_aliases]' => TRUE,
+      'options[keep_custom_aliases]' => TRUE,
+    );
+    $this->drupalPostForm('admin/config/search/path/delete_bulk', $edit, t('Delete aliases now!'));
+    $this->assertText(t('All of your automatically generated path aliases have been deleted.'));
+    $this->assertUrl('admin/config/search/path/delete_bulk');
+
+    // Make sure that only custom aliases and aliases with no information about
+    // their state still exist.
+    $aliases = \Drupal::database()->select('url_alias', 'ua')->fields('ua', ['source'])->execute()->fetchCol();
+    $this->assertEqual($aliases, ['/node/101', '/node/104', '/node/105'], 'Custom aliases still exist.');
+  }
+
+  /**
+   * Helper function to generate aliases.
+   */
+  function generateAliases() {
+    // Delete all aliases to avoid duplicated aliases. They will be recreated below.
+    $this->deleteAllAliases();
+
+    // We generate a bunch of aliases for nodes, users and taxonomy terms. If
+    // the entities are already created we just update them, otherwise we create
+    // them.
+    if (empty($this->nodes)) {
+      // Create a large number of nodes (100+) to make sure that the batch code works.
+      for ($i = 1; $i <= 105; $i++) {
+        // Set the alias of two nodes manually.
+        $settings = ($i > 103) ? ['path' => ['alias' => "/custom_alias_$i", 'pathauto' => PathautoState::SKIP]] : [];
+        $node = $this->drupalCreateNode($settings);
+        $this->nodes[$node->id()] = $node;
+      }
+    }
+    else {
+      foreach ($this->nodes as $node) {
+        if ($node->id() > 103) {
+          // The alias is set manually.
+          $node->set('path', ['alias' => '/custom_alias_' . $node->id()]);
+        }
+        $node->save();
+      }
+    }
+    // Delete information about the state of an alias to make sure that aliases
+    // with no such data are left alone by default.
+    \Drupal::keyValue('pathauto_state.node')->delete(101);
+
+    if (empty($this->accounts)) {
+      for ($i = 1; $i <= 5; $i++) {
+        $account = $this->drupalCreateUser();
+        $this->accounts[$account->id()] = $account;
+      }
+    }
+    else {
+      foreach ($this->accounts as $id => $account) {
+        $account->save();
+      }
+    }
+
+    if (empty($this->terms)) {
+      $vocabulary = $this->addVocabulary(array('name' => 'test vocabulary', 'vid' => 'test_vocabulary'));
+      for ($i = 1; $i <= 5; $i++) {
+        $term = $this->addTerm($vocabulary);
+        $this->terms[$term->id()] = $term;
+      }
+    }
+    else {
+      foreach ($this->terms as $term) {
+        $term->save();
+      }
+    }
+
+    // Check that we have aliases for the entities.
+    foreach (array('nodes', 'accounts', 'terms') as $attribute) {
+      foreach ($this->{$attribute} as $entity) {
+        $this->assertEntityAliasExists($entity);
+      }
+    }
+  }
+
+}

+ 291 - 0
sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoNodeWebTest.php

@@ -0,0 +1,291 @@
+<?php
+
+namespace Drupal\pathauto\Tests;
+use Drupal\pathauto\Entity\PathautoPattern;
+use Drupal\node\Entity\Node;
+use Drupal\pathauto\PathautoState;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests pathauto node UI integration.
+ *
+ * @group pathauto
+ */
+class PathautoNodeWebTest extends WebTestBase {
+
+  use PathautoTestHelperTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('node', 'pathauto', 'views', 'taxonomy', 'pathauto_views_test');
+
+  /**
+   * Admin user.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * {inheritdoc}
+   */
+  function setUp() {
+    parent::setUp();
+
+    $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
+    $this->drupalCreateContentType(array('type' => 'article'));
+
+    // Allow other modules to add additional permissions for the admin user.
+    $permissions = array(
+      'administer pathauto',
+      'administer url aliases',
+      'create url aliases',
+      'bypass node access',
+      'access content overview',
+    );
+    $this->adminUser = $this->drupalCreateUser($permissions);
+    $this->drupalLogin($this->adminUser);
+
+    $this->createPattern('node', '/content/[node:title]');
+  }
+
+  /**
+   * Tests editing nodes with different settings.
+   */
+  function testNodeEditing() {
+    // Ensure that the Pathauto checkbox is checked by default on the node add form.
+    $this->drupalGet('node/add/page');
+    $this->assertFieldChecked('edit-path-0-pathauto');
+
+    // Create a node by saving the node form.
+    $title = ' Testing: node title [';
+    $automatic_alias = '/content/testing-node-title';
+    $this->drupalPostForm(NULL, array('title[0][value]' => $title), t('Save'));
+    $node = $this->drupalGetNodeByTitle($title);
+
+    // Look for alias generated in the form.
+    $this->drupalGet("node/{$node->id()}/edit");
+    $this->assertFieldChecked('edit-path-0-pathauto');
+    $this->assertFieldByName('path[0][alias]', $automatic_alias, 'Generated alias visible in the path alias field.');
+
+    // Check whether the alias actually works.
+    $this->drupalGet($automatic_alias);
+    $this->assertText($title, 'Node accessible through automatic alias.');
+
+    // Manually set the node's alias.
+    $manual_alias = '/content/' . $node->id();
+    $edit = array(
+      'path[0][pathauto]' => FALSE,
+      'path[0][alias]' => $manual_alias,
+    );
+    $this->drupalPostForm($node->toUrl('edit-form'), $edit, t('Save'));
+    $this->assertText(t('@type @title has been updated.', array('@type' => 'page', '@title' => $title)));
+
+    // Check that the automatic alias checkbox is now unchecked by default.
+    $this->drupalGet("node/{$node->id()}/edit");
+    $this->assertNoFieldChecked('edit-path-0-pathauto');
+    $this->assertFieldByName('path[0][alias]', $manual_alias);
+
+    // Submit the node form with the default values.
+    $this->drupalPostForm(NULL, array('path[0][pathauto]' => FALSE), t('Save'));
+    $this->assertText(t('@type @title has been updated.', array('@type' => 'page', '@title' => $title)));
+
+    // Test that the old (automatic) alias has been deleted and only accessible
+    // through the new (manual) alias.
+    $this->drupalGet($automatic_alias);
+    $this->assertResponse(404, 'Node not accessible through automatic alias.');
+    $this->drupalGet($manual_alias);
+    $this->assertText($title, 'Node accessible through manual alias.');
+
+    // Test that the manual alias is not kept for new nodes when the pathauto
+    // checkbox is ticked.
+    $title = 'Automatic Title';
+    $edit = array(
+      'title[0][value]' => $title,
+      'path[0][pathauto]' => TRUE,
+      'path[0][alias]' => '/should-not-get-created',
+    );
+    $this->drupalPostForm('node/add/page', $edit, t('Save'));
+    $this->assertNoAliasExists(array('alias' => 'should-not-get-created'));
+    $node = $this->drupalGetNodeByTitle($title);
+    $this->assertEntityAlias($node, '/content/automatic-title');
+
+    // Remove the pattern for nodes, the pathauto checkbox should not be
+    // displayed.
+    $ids = \Drupal::entityQuery('pathauto_pattern')
+      ->condition('type', 'canonical_entities:node')
+      ->execute();
+    foreach (PathautoPattern::loadMultiple($ids) as $pattern) {
+      $pattern->delete();
+    }
+
+    $this->drupalGet('node/add/article');
+    $this->assertNoFieldById('edit-path-0-pathauto');
+    $this->assertFieldByName('path[0][alias]', '');
+
+    $edit = array();
+    $edit['title'] = 'My test article';
+    $this->drupalCreateNode($edit);
+    //$this->drupalPostForm(NULL, $edit, t('Save'));
+    $node = $this->drupalGetNodeByTitle($edit['title']);
+
+    // Pathauto checkbox should still not exist.
+    $this->drupalGet($node->toUrl('edit-form'));
+    $this->assertNoFieldById('edit-path-0-pathauto');
+    $this->assertFieldByName('path[0][alias]', '');
+    $this->assertNoEntityAlias($node);
+  }
+
+  /**
+   * Test node operations.
+   */
+  function testNodeOperations() {
+    $node1 = $this->drupalCreateNode(array('title' => 'node1'));
+    $node2 = $this->drupalCreateNode(array('title' => 'node2'));
+
+    // Delete all current URL aliases.
+    $this->deleteAllAliases();
+
+    $this->drupalGet('admin/content');
+
+    // Check which of the two nodes is first.
+    if (strpos($this->getTextContent(), 'node1') < strpos($this->getTextContent(), 'node2')) {
+      $index = 0;
+    }
+    else {
+      $index = 1;
+    }
+
+    $edit = array(
+      'action' => 'pathauto_update_alias_node',
+      'node_bulk_form[' . $index . ']' => TRUE,
+    );
+    $this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
+    $this->assertText('Update URL alias was applied to 1 item.');
+
+    $this->assertEntityAlias($node1, '/content/' . $node1->getTitle());
+    $this->assertEntityAlias($node2, '/node/' . $node2->id());
+  }
+
+  /**
+   * @todo Merge this with existing node test methods?
+   */
+  public function testNodeState() {
+    $nodeNoAliasUser = $this->drupalCreateUser(array('bypass node access'));
+    $nodeAliasUser = $this->drupalCreateUser(array('bypass node access', 'create url aliases'));
+
+    $node = $this->drupalCreateNode(array(
+      'title' => 'Node version one',
+      'type' => 'page',
+      'path' => array(
+        'pathauto' => PathautoState::SKIP,
+      ),
+    ));
+
+    $this->assertNoEntityAlias($node);
+
+    // Set a manual path alias for the node.
+    $node->path->alias = '/test-alias';
+    $node->save();
+
+    // Ensure that the pathauto field was saved to the database.
+    \Drupal::entityTypeManager()->getStorage('node')->resetCache();
+    $node = Node::load($node->id());
+    $this->assertIdentical($node->path->pathauto, PathautoState::SKIP);
+
+    // Ensure that the manual path alias was saved and an automatic alias was not generated.
+    $this->assertEntityAlias($node, '/test-alias');
+    $this->assertNoEntityAliasExists($node, '/content/node-version-one');
+
+    // Save the node as a user who does not have access to path fieldset.
+    $this->drupalLogin($nodeNoAliasUser);
+    $this->drupalGet('node/' . $node->id() . '/edit');
+    $this->assertNoFieldByName('path[0][pathauto]');
+
+    $edit = array('title[0][value]' => 'Node version two');
+    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->assertText('Basic page Node version two has been updated.');
+
+    $this->assertEntityAlias($node, '/test-alias');
+    $this->assertNoEntityAliasExists($node, '/content/node-version-one');
+    $this->assertNoEntityAliasExists($node, '/content/node-version-two');
+
+    // Load the edit node page and check that the Pathauto checkbox is unchecked.
+    $this->drupalLogin($nodeAliasUser);
+    $this->drupalGet('node/' . $node->id() . '/edit');
+    $this->assertNoFieldChecked('edit-path-0-pathauto');
+
+    // Edit the manual alias and save the node.
+    $edit = array(
+      'title[0][value]' => 'Node version three',
+      'path[0][alias]' => '/manually-edited-alias',
+    );
+    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->assertText('Basic page Node version three has been updated.');
+
+    $this->assertEntityAlias($node, '/manually-edited-alias');
+    $this->assertNoEntityAliasExists($node, '/test-alias');
+    $this->assertNoEntityAliasExists($node, '/content/node-version-one');
+    $this->assertNoEntityAliasExists($node, '/content/node-version-two');
+    $this->assertNoEntityAliasExists($node, '/content/node-version-three');
+
+    // Programatically save the node with an automatic alias.
+    \Drupal::entityTypeManager()->getStorage('node')->resetCache();
+    $node = Node::load($node->id());
+    $node->path->pathauto = PathautoState::CREATE;
+    $node->save();
+
+    // Ensure that the pathauto field was saved to the database.
+    \Drupal::entityTypeManager()->getStorage('node')->resetCache();
+    $node = Node::load($node->id());
+    $this->assertIdentical($node->path->pathauto, PathautoState::CREATE);
+
+    $this->assertEntityAlias($node, '/content/node-version-three');
+    $this->assertNoEntityAliasExists($node, '/manually-edited-alias');
+    $this->assertNoEntityAliasExists($node, '/test-alias');
+    $this->assertNoEntityAliasExists($node, '/content/node-version-one');
+    $this->assertNoEntityAliasExists($node, '/content/node-version-two');
+
+    $node->delete();
+    $this->assertNull(\Drupal::keyValue('pathauto_state.node')->get($node->id()), 'Pathauto state was deleted');
+  }
+
+
+  /**
+   * Tests that nodes without a Pathauto pattern can set custom aliases.
+   */
+  public function testCustomAliasWithoutPattern() {
+    // First, delete all patterns to be sure that there will be no match.
+    $entity_ids = \Drupal::entityQuery('pathauto_pattern')->execute();
+    $entities = PathautoPattern::loadMultiple($entity_ids);
+    foreach ($entities as $entity) {
+      $entity->delete();
+    }
+
+    // Next, create a node with a custom alias.
+    $edit = [
+      'title[0][value]' => 'Sample article',
+      'path[0][alias]' => '/sample-article',
+    ];
+    $this->drupalPostForm('node/add/article', $edit, t('Save'));
+    $this->assertText(t('article Sample article has been created.'));
+
+    // Test the alias.
+    $this->assertAliasExists(array('alias' => '/sample-article'));
+    $this->drupalGet('sample-article');
+    $this->assertResponse(200, 'A node without a pattern can have a custom alias.');
+
+    // Now create a node through the API.
+    $node = Node::create(['type' => 'article', 'title' => 'Sample article API', 'path' => ['alias' => '/sample-article-api']]);
+    $node->save();
+
+    // Test the alias.
+    $this->assertAliasExists(['alias' => '/sample-article-api']);
+    $this->drupalGet('sample-article-api');
+    $this->assertResponse(200);
+  }
+
+}

+ 239 - 0
sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoSettingsFormWebTest.php

@@ -0,0 +1,239 @@
+<?php
+
+namespace Drupal\pathauto\Tests;
+
+use Drupal\pathauto\PathautoGeneratorInterface;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests pathauto settings form.
+ *
+ * @group pathauto
+ */
+class PathautoSettingsFormWebTest extends WebTestBase {
+
+  use PathautoTestHelperTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('node', 'pathauto');
+
+  /**
+   * Admin user.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * Form values that are set by default.
+   *
+   * @var array
+   */
+  protected $defaultFormValues = array(
+    'verbose' => FALSE,
+    'separator' => '-',
+    'case' => '1',
+    'max_length' => '100',
+    'max_component_length' => '100',
+    'update_action' => '2',
+    'transliterate' => '1',
+    'reduce_ascii' => FALSE,
+    'ignore_words' => 'a, an, as, at, before, but, by, for, from, is, in, into, like, of, off, on, onto, per, since, than, the, this, that, to, up, via, with',
+  );
+
+  /**
+   * Punctuation form items with default values.
+   *
+   * @var array
+   */
+  protected $defaultPunctuations = array(
+    'punctuation[double_quotes]' => '0',
+    'punctuation[quotes]' => '0',
+    'punctuation[backtick]' => '0',
+    'punctuation[comma]' => '0',
+    'punctuation[period]' => '0',
+    'punctuation[hyphen]' => '1',
+    'punctuation[underscore]' => '0',
+    'punctuation[colon]' => '0',
+    'punctuation[semicolon]' => '0',
+    'punctuation[pipe]' => '0',
+    'punctuation[left_curly]' => '0',
+    'punctuation[left_square]' => '0',
+    'punctuation[right_curly]' => '0',
+    'punctuation[right_square]' => '0',
+    'punctuation[plus]' => '0',
+    'punctuation[equal]' => '0',
+    'punctuation[asterisk]' => '0',
+    'punctuation[ampersand]' => '0',
+    'punctuation[percent]' => '0',
+    'punctuation[caret]' => '0',
+    'punctuation[dollar]' => '0',
+    'punctuation[hash]' => '0',
+    'punctuation[exclamation]' => '0',
+    'punctuation[tilde]' => '0',
+    'punctuation[left_parenthesis]' => '0',
+    'punctuation[right_parenthesis]' => '0',
+    'punctuation[question_mark]' => '0',
+    'punctuation[less_than]' => '0',
+    'punctuation[greater_than]' => '0',
+    'punctuation[slash]' => '0',
+    'punctuation[back_slash]' => '0',
+  );
+
+  /**
+   * {inheritdoc}
+   */
+  function setUp() {
+    parent::setUp();
+
+    $this->drupalCreateContentType(array('type' => 'article'));
+
+    $permissions = array(
+      'administer pathauto',
+      'notify of path changes',
+      'administer url aliases',
+      'create url aliases',
+      'bypass node access',
+    );
+    $this->adminUser = $this->drupalCreateUser($permissions);
+    $this->drupalLogin($this->adminUser);
+    $this->createPattern('node', '/content/[node:title]');
+  }
+
+  /**
+   * Test if the default values are shown correctly in the form.
+   */
+  function testDefaultFormValues() {
+    $this->drupalGet('/admin/config/search/path/settings');
+    $this->assertNoFieldChecked('edit-verbose');
+    $this->assertField('edit-separator', $this->defaultFormValues['separator']);
+    $this->assertFieldChecked('edit-case');
+    $this->assertField('edit-max-length', $this->defaultFormValues['max_length']);
+    $this->assertField('edit-max-component-length', $this->defaultFormValues['max_component_length']);
+    $this->assertFieldChecked('edit-update-action-2');
+    $this->assertFieldChecked('edit-transliterate');
+    $this->assertNoFieldChecked('edit-reduce-ascii');
+    $this->assertField('edit-ignore-words', $this->defaultFormValues['ignore_words']);
+  }
+
+  /**
+   * Test the verbose option.
+   */
+  function testVerboseOption() {
+    $edit = array('verbose' => '1');
+    $this->drupalPostForm('/admin/config/search/path/settings', $edit, t('Save configuration'));
+    $this->assertText(t('The configuration options have been saved.'));
+    $this->assertFieldChecked('edit-verbose');
+
+    $title = 'Verbose settings test';
+    $this->drupalGet('/node/add/article');
+    $this->assertFieldChecked('edit-path-0-pathauto');
+    $this->drupalPostForm(NULL, array('title[0][value]' => $title), t('Save'));
+    $this->assertText('Created new alias /content/verbose-settings-test for');
+
+    $node = $this->drupalGetNodeByTitle($title);
+    $this->drupalPostForm('/node/' . $node->id() . '/edit', array('title[0][value]' => 'Updated title'), t('Save'));
+    $this->assertText('Created new alias /content/updated-title for');
+    $this->assertText('replacing /content/verbose-settings-test.');
+  }
+
+  /**
+   * Tests generating aliases with different settings.
+   */
+  function testSettingsForm() {
+    // Ensure the separator settings apply correctly.
+    $this->checkAlias('My awesome content', '/content/my.awesome.content', array('separator' => '.'));
+
+    // Ensure the character case setting works correctly.
+    // Leave case the same as source token values.
+    $this->checkAlias('My awesome Content', '/content/My-awesome-Content', array('case' => FALSE));
+    $this->checkAlias('Change Lower', '/content/change-lower', array('case' => '1'));
+
+    // Ensure the maximum alias length is working.
+    $this->checkAlias('My awesome Content', '/content/my-awesome', array('max_length' => '23'));
+
+    // Ensure the maximum component length is working.
+    $this->checkAlias('My awesome Content', '/content/my', array('max_component_length' => '2'));
+
+    // Ensure transliteration option is working.
+    $this->checkAlias('è é àl ö äl ü', '/content/e-e-al-o-al-u', array('transliterate' => '1'));
+    $this->checkAlias('è é àl äl ö ü', '/content/è-é-àl-äl-ö-ü', array('transliterate' => FALSE));
+
+    $ignore_words = 'a, new, very, should';
+    $this->checkAlias('a very new alias to test', '/content/alias-to-test', array('ignore_words' => $ignore_words));
+  }
+
+  /**
+   * Test the punctuation setting form items.
+   */
+  function testPunctuationSettings() {
+    // Test the replacement of punctuations.
+    $settings = [];
+    foreach ($this->defaultPunctuations as $key => $punctuation) {
+      $settings[$key] = PathautoGeneratorInterface::PUNCTUATION_REPLACE;
+    }
+
+    $title = 'aa"b`c,d.e-f_g:h;i|j{k[l}m]n+o=p*q%r^s$t#u!v~w(x)y?z>1/2\3';
+    $alias = '/content/aa-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-1-2-3';
+    $this->checkAlias($title, $alias, $settings);
+
+    // Test the removal of punctuations.
+    $settings = [];
+    foreach ($this->defaultPunctuations as $key => $punctuation) {
+      $settings[$key] = PathautoGeneratorInterface::PUNCTUATION_REMOVE;
+    }
+
+    $title = 'a"b`c,d.e-f_g:h;i|j{k[l}m]n+o=p*q%r^s$t#u!v~w(x)y?z>1/2\3';
+    $alias = '/content/abcdefghijklmnopqrstuvwxyz123';
+    $this->checkAlias($title, $alias, $settings);
+
+    // Keep all punctuations in alias.
+    $settings = [];
+    foreach ($this->defaultPunctuations as $key => $punctuation) {
+      $settings[$key] = PathautoGeneratorInterface::PUNCTUATION_DO_NOTHING;
+    }
+
+    $title = 'al"b`c,d.e-f_g:h;i|j{k[l}m]n+o=p*q%r^s$t#u!v~w(x)y?z>1/2\3';
+    $alias = '/content/al"b`c,d.e-f_g:h;i|j{k[l}m]n+o=p*q%r^s$t#u!v~w(x)y?z>1/2\3';
+    $this->checkAlias($title, $alias, $settings);
+  }
+
+  /**
+   * Helper method to check the an aliases.
+   *
+   * @param string $title
+   *   The node title to build the aliases from.
+   * @param string $alias
+   *   The expected alias.
+   * @param array $settings
+   *   The form values the alias should be generated with.
+   */
+  protected function checkAlias($title, $alias, $settings = array()) {
+    // Submit the settings form.
+    $edit = array_merge($this->defaultFormValues + $this->defaultPunctuations, $settings);
+    $this->drupalPostForm('/admin/config/search/path/settings', $edit, t('Save configuration'));
+    $this->assertText(t('The configuration options have been saved.'));
+
+    // If we do not clear the caches here, AliasCleaner will use its
+    // cleanStringCache instance variable. Due to that the creation of aliases
+    // with $this->createNode() will only work correctly on the first call.
+    \Drupal::service('pathauto.generator')->resetCaches();
+
+    // Create a node and check if the settings applied.
+    $node = $this->createNode(
+      array(
+        'title' => $title,
+        'type' => 'article',
+      )
+    );
+
+    $this->drupalGet($alias);
+    $this->assertResponse(200);
+    $this->assertEntityAlias($node, $alias);
+  }
+
+}

+ 102 - 0
sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoTaxonomyWebTest.php

@@ -0,0 +1,102 @@
+<?php
+
+namespace Drupal\pathauto\Tests;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests pathauto taxonomy UI integration.
+ *
+ * @group pathauto
+ */
+class PathautoTaxonomyWebTest extends WebTestBase {
+
+  use PathautoTestHelperTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('taxonomy', 'pathauto', 'views');
+
+  /**
+   * Admin user.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * {inheritdoc}
+   */
+  function setUp() {
+    parent::setUp();
+
+    // Allow other modules to add additional permissions for the admin user.
+    $permissions = array(
+      'administer pathauto',
+      'administer url aliases',
+      'create url aliases',
+      'administer taxonomy',
+    );
+    $this->adminUser = $this->drupalCreateUser($permissions);
+    $this->drupalLogin($this->adminUser);
+
+    $this->createPattern('taxonomy_term', '/[term:vocabulary]/[term:name]');
+  }
+
+
+  /**
+   * Basic functional testing of Pathauto with taxonomy terms.
+   */
+  function testTermEditing() {
+    $this->drupalGet('admin/structure');
+    $this->drupalGet('admin/structure/taxonomy');
+
+    // Add vocabulary "tags".
+    $vocabulary = $this->addVocabulary(array('name' => 'tags', 'vid' => 'tags'));
+
+    // Create term for testing.
+    $name = 'Testing: term name [';
+    $automatic_alias = '/tags/testing-term-name';
+    $this->drupalPostForm('admin/structure/taxonomy/manage/tags/add', array('name[0][value]' => $name), 'Save');
+    $name = trim($name);
+    $this->assertText("Created new term $name.");
+    $term = $this->drupalGetTermByName($name);
+
+    // Look for alias generated in the form.
+    $this->drupalGet("taxonomy/term/{$term->id()}/edit");
+    $this->assertFieldChecked('edit-path-0-pathauto');
+    $this->assertFieldByName('path[0][alias]', $automatic_alias, 'Generated alias visible in the path alias field.');
+
+    // Check whether the alias actually works.
+    $this->drupalGet($automatic_alias);
+    $this->assertText($name, 'Term accessible through automatic alias.');
+
+    // Manually set the term's alias.
+    $manual_alias = '/tags/' . $term->id();
+    $edit = array(
+      'path[0][pathauto]' => FALSE,
+      'path[0][alias]' => $manual_alias,
+    );
+    $this->drupalPostForm("taxonomy/term/{$term->id()}/edit", $edit, t('Save'));
+    $this->assertText("Updated term $name.");
+
+    // Check that the automatic alias checkbox is now unchecked by default.
+    $this->drupalGet("taxonomy/term/{$term->id()}/edit");
+    $this->assertNoFieldChecked('edit-path-0-pathauto');
+    $this->assertFieldByName('path[0][alias]', $manual_alias);
+
+    // Submit the term form with the default values.
+    $this->drupalPostForm(NULL, array('path[0][pathauto]' => FALSE), t('Save'));
+    $this->assertText("Updated term $name.");
+
+    // Test that the old (automatic) alias has been deleted and only accessible
+    // through the new (manual) alias.
+    $this->drupalGet($automatic_alias);
+    $this->assertResponse(404, 'Term not accessible through automatic alias.');
+    $this->drupalGet($manual_alias);
+    $this->assertText($name, 'Term accessible through manual alias.');
+  }
+
+}

+ 190 - 0
sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoTestHelperTrait.php

@@ -0,0 +1,190 @@
+<?php
+
+namespace Drupal\pathauto\Tests;
+
+use Drupal\Component\Utility\Unicode;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\pathauto\Entity\PathautoPattern;
+use Drupal\pathauto\PathautoPatternInterface;
+use Drupal\taxonomy\VocabularyInterface;
+use Drupal\taxonomy\Entity\Vocabulary;
+use Drupal\taxonomy\Entity\Term;
+
+/**
+ * Helper test class with some added functions for testing.
+ */
+trait PathautoTestHelperTrait {
+
+  /**
+   * Creates a pathauto pattern.
+   *
+   * @param string $entity_type_id
+   *   The entity type.
+   * @param string $pattern
+   *   The path pattern.
+   * @param int $weight
+   *   (optional) The pattern weight.
+   *
+   * @return \Drupal\pathauto\PathautoPatternInterface
+   *   The created pattern.
+   */
+  protected function createPattern($entity_type_id, $pattern, $weight = 10) {
+    $type = ($entity_type_id == 'forum') ? 'forum' : 'canonical_entities:' . $entity_type_id;
+
+    $pattern = PathautoPattern::create([
+      'id' => Unicode::strtolower($this->randomMachineName()),
+      'type' => $type,
+      'pattern' => $pattern,
+      'weight' => $weight,
+    ]);
+    $pattern->save();
+    return $pattern;
+  }
+
+  /**
+   * Add a bundle condition to a pathauto pattern.
+   *
+   * @param \Drupal\pathauto\PathautoPatternInterface $pattern
+   *   The pattern.
+   * @param string $entity_type
+   *   The entity type ID.
+   * @param string $bundle
+   *   The bundle
+   */
+  protected function addBundleCondition(PathautoPatternInterface $pattern, $entity_type, $bundle) {
+    $plugin_id = $entity_type == 'node' ? 'node_type' : 'entity_bundle:' . $entity_type;
+
+    $pattern->addSelectionCondition(
+      [
+        'id' => $plugin_id,
+        'bundles' => [
+          $bundle => $bundle,
+        ],
+        'negate' => FALSE,
+        'context_mapping' => [
+          $entity_type => $entity_type,
+        ]
+      ]
+    );
+  }
+
+  public function assertToken($type, $object, $token, $expected) {
+    $bubbleable_metadata = new BubbleableMetadata();
+    $tokens = \Drupal::token()->generate($type, array($token => $token), array($type => $object), [], $bubbleable_metadata);
+    $tokens += array($token => '');
+    $this->assertIdentical($tokens[$token], $expected, t("Token value for [@type:@token] was '@actual', expected value '@expected'.", array('@type' => $type, '@token' => $token, '@actual' => $tokens[$token], '@expected' => $expected)));
+  }
+
+  public function saveAlias($source, $alias, $langcode = Language::LANGCODE_NOT_SPECIFIED) {
+    \Drupal::service('path.alias_storage')->delete(array('source' => $source, 'language', 'langcode' => $langcode));
+    return \Drupal::service('path.alias_storage')->save($source, $alias, $langcode);
+  }
+
+  public function saveEntityAlias(EntityInterface $entity, $alias, $langcode = NULL) {
+    // By default, use the entity language.
+    if (!$langcode) {
+      $langcode = $entity->language()->getId();
+    }
+    return $this->saveAlias('/' . $entity->toUrl()->getInternalPath(), $alias, $langcode);
+  }
+
+  public function assertEntityAlias(EntityInterface $entity, $expected_alias, $langcode = NULL) {
+    // By default, use the entity language.
+    if (!$langcode) {
+      $langcode = $entity->language()->getId();
+    }
+    $this->assertAlias('/' . $entity->toUrl()->getInternalPath(), $expected_alias, $langcode);
+  }
+
+  public function assertEntityAliasExists(EntityInterface $entity) {
+    return $this->assertAliasExists(array('source' => '/' . $entity->toUrl()->getInternalPath()));
+  }
+
+  public function assertNoEntityAlias(EntityInterface $entity, $langcode = NULL) {
+    // By default, use the entity language.
+    if (!$langcode) {
+      $langcode = $entity->language()->getId();
+    }
+    $this->assertEntityAlias($entity, '/' . $entity->toUrl()->getInternalPath(), $langcode);
+  }
+
+  public function assertNoEntityAliasExists(EntityInterface $entity, $alias = NULL) {
+    $path = array('source' => '/' . $entity->toUrl()->getInternalPath());
+    if (!empty($alias)) {
+      $path['alias'] = $alias;
+    }
+    $this->assertNoAliasExists($path);
+  }
+
+  public function assertAlias($source, $expected_alias, $langcode = Language::LANGCODE_NOT_SPECIFIED) {
+    \Drupal::service('path.alias_manager')->cacheClear($source);
+    $this->assertEqual($expected_alias, \Drupal::service('path.alias_manager')->getAliasByPath($source, $langcode), t("Alias for %source with language '@language' is correct.",
+      array('%source' => $source, '@language' => $langcode)));
+  }
+
+  public function assertAliasExists($conditions) {
+    $path = \Drupal::service('path.alias_storage')->load($conditions);
+    $this->assertTrue($path, t('Alias with conditions @conditions found.', array('@conditions' => var_export($conditions, TRUE))));
+    return $path;
+  }
+
+  public function assertNoAliasExists($conditions) {
+    $alias = \Drupal::service('path.alias_storage')->load($conditions);
+    $this->assertFalse($alias, t('Alias with conditions @conditions not found.', array('@conditions' => var_export($conditions, TRUE))));
+  }
+
+  public function deleteAllAliases() {
+    \Drupal::database()->delete('url_alias')->execute();
+    \Drupal::service('path.alias_manager')->cacheClear();
+  }
+
+  /**
+   * @param array $values
+   * @return \Drupal\taxonomy\VocabularyInterface
+   */
+  public function addVocabulary(array $values = array()) {
+    $name = Unicode::strtolower($this->randomMachineName(5));
+    $values += array(
+      'name' => $name,
+      'vid' => $name,
+    );
+    $vocabulary = Vocabulary::create($values);
+    $vocabulary->save();
+
+    return $vocabulary;
+  }
+
+  public function addTerm(VocabularyInterface $vocabulary, array $values = array()) {
+    $values += array(
+      'name' => Unicode::strtolower($this->randomMachineName(5)),
+      'vid' => $vocabulary->id(),
+    );
+
+    $term = Term::create($values);
+    $term->save();
+    return $term;
+  }
+
+  public function assertEntityPattern($entity_type, $bundle, $langcode = Language::LANGCODE_NOT_SPECIFIED, $expected) {
+
+    $values = [
+      'langcode' => $langcode,
+      \Drupal::entityTypeManager()->getDefinition($entity_type)->getKey('bundle') => $bundle,
+    ];
+    $entity = \Drupal::entityTypeManager()->getStorage($entity_type)->create($values);
+
+    $pattern = \Drupal::service('pathauto.generator')->getPatternByEntity($entity);
+    $this->assertIdentical($expected, $pattern->getPattern());
+  }
+
+  public function drupalGetTermByName($name, $reset = FALSE) {
+    if ($reset) {
+      // @todo - implement cache reset.
+    }
+    $terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties(array('name' => $name));
+    return !empty($terms) ? reset($terms) : FALSE;
+  }
+
+}

+ 191 - 0
sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoUiTest.php

@@ -0,0 +1,191 @@
+<?php
+
+namespace Drupal\pathauto\Tests;
+
+use Drupal\Core\Url;
+use Drupal\simpletest\WebTestBase;
+use Drupal\pathauto\Entity\PathautoPattern;
+
+/**
+ * Test basic pathauto functionality.
+ *
+ * @group pathauto
+ */
+class PathautoUiTest extends WebTestBase {
+
+  use PathautoTestHelperTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('pathauto', 'node');
+
+  /**
+   * Admin user.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * {inheritdoc}
+   */
+  function setUp() {
+    parent::setUp();
+
+    $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
+    $this->drupalCreateContentType(array('type' => 'article'));
+
+    // Allow other modules to add additional permissions for the admin user.
+    $permissions = array(
+      'administer pathauto',
+      'administer url aliases',
+      'create url aliases',
+      'administer nodes',
+      'bypass node access',
+      'access content overview',
+    );
+    $this->adminUser = $this->drupalCreateUser($permissions);
+    $this->drupalLogin($this->adminUser);
+  }
+
+  function testSettingsValidation() {
+    $edit = array();
+    $edit['max_length'] = 'abc';
+    $edit['max_component_length'] = 'abc';
+    $this->drupalPostForm('admin/config/search/path/settings', $edit, 'Save configuration');
+    /*$this->assertText('The field Maximum alias length is not a valid number.');
+    $this->assertText('The field Maximum component length is not a valid number.');*/
+    $this->assertNoText('The configuration options have been saved.');
+
+    $edit['max_length'] = '0';
+    $edit['max_component_length'] = '0';
+    $this->drupalPostForm('admin/config/search/path/settings', $edit, 'Save configuration');
+    /*$this->assertText('The field Maximum alias length cannot be less than 1.');
+    $this->assertText('The field Maximum component length cannot be less than 1.');*/
+    $this->assertNoText('The configuration options have been saved.');
+
+    $edit['max_length'] = '999';
+    $edit['max_component_length'] = '999';
+    $this->drupalPostForm('admin/config/search/path/settings', $edit, 'Save configuration');
+    /*$this->assertText('The field Maximum alias length cannot be greater than 255.');
+    $this->assertText('The field Maximum component length cannot be greater than 255.');*/
+    $this->assertNoText('The configuration options have been saved.');
+
+    $edit['max_length'] = '50';
+    $edit['max_component_length'] = '50';
+    $this->drupalPostForm('admin/config/search/path/settings', $edit, 'Save configuration');
+    $this->assertText('The configuration options have been saved.');
+  }
+
+  function testPatternsWorkflow() {
+    // Try to save an empty pattern, should not be allowed.
+    $this->drupalGet('admin/config/search/path/patterns/add');
+    $edit = array(
+      'type' => 'canonical_entities:node',
+    );
+    $this->drupalPostAjaxForm(NULL, $edit, 'type');
+    $edit += array(
+      'bundles[page]' => TRUE,
+      'label' => 'Page pattern',
+      'id' => 'page_pattern',
+    );
+    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->assertText('Path pattern field is required.');
+    $this->assertNoText('The configuration options have been saved.');
+
+    // Try to save an invalid pattern.
+    $edit += array(
+      'pattern' => '[node:title]/[user:name]/[term:name]',
+    );
+    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->assertText('Path pattern is using the following invalid tokens: [user:name], [term:name].');
+    $this->assertNoText('The configuration options have been saved.');
+
+    $edit['pattern'] = '#[node:title]';
+    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->assertText('The Path pattern is using the following invalid characters: #.');
+    $this->assertNoText('The configuration options have been saved.');
+
+    // Checking whitespace ending of the string.
+    $edit['pattern'] = '[node:title] ';
+    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->assertText('The Path pattern doesn\'t allow the patterns ending with whitespace.');
+    $this->assertNoText('The configuration options have been saved.');
+
+    // Fix the pattern, then check that it gets saved successfully.
+    $edit['pattern'] = '[node:title]';
+    $this->drupalPostForm(NULL, $edit, 'Save');
+    $this->assertText('Pattern Page pattern saved.');
+
+    \Drupal::service('pathauto.generator')->resetCaches();
+
+    // Create a node with pattern enabled and check if the pattern applies.
+    $title = 'Page Pattern enabled';
+    $alias = '/page-pattern-enabled';
+    $node = $this->createNode(['title' => $title, 'type' => 'page']);
+    $this->drupalGet($alias);
+    $this->assertResponse(200);
+    $this->assertEntityAlias($node, $alias);
+
+    // Edit workflow, set a new label and weight for the pattern.
+    $this->drupalPostForm('/admin/config/search/path/patterns', ['entities[page_pattern][weight]' => '4'], t('Save'));
+    $this->clickLink(t('Edit'));
+    $destination_query = ['query' => ['destination' => Url::fromRoute('entity.pathauto_pattern.collection')->toString()]];
+    $this->assertUrl('/admin/config/search/path/patterns/page_pattern', $destination_query);
+    $this->assertFieldByName('pattern', '[node:title]');
+    $this->assertFieldByName('label', 'Page pattern');
+    $this->assertFieldChecked('edit-status');
+    $this->assertLink(t('Delete'));
+
+    $edit = array('label' => 'Test');
+    $this->drupalPostForm('/admin/config/search/path/patterns/page_pattern', $edit, t('Save'));
+    $this->assertText('Pattern Test saved.');
+    // Check that the pattern weight did not change.
+    $this->assertOptionSelected('edit-entities-page-pattern-weight', '4');
+
+    // Disable workflow.
+    $this->drupalGet('/admin/config/search/path/patterns');
+    $this->assertNoLink(t('Enable'));
+    $this->clickLink(t('Disable'));
+    $this->assertUrl('/admin/config/search/path/patterns/page_pattern/disable', $destination_query);
+    $this->drupalPostForm(NULL, [], t('Disable'));
+    $this->assertText('Disabled pattern Test.');
+
+    // Load the pattern from storage and check if its disabled.
+    $pattern = PathautoPattern::load('page_pattern');
+    $this->assertFalse($pattern->status());
+
+    \Drupal::service('pathauto.generator')->resetCaches();
+
+    // Create a node with pattern disabled and check that we have no new alias.
+    $title = 'Page Pattern disabled';
+    $node = $this->createNode(['title' => $title, 'type' => 'page']);
+    $this->assertNoEntityAlias($node);
+
+    // Enable workflow.
+    $this->drupalGet('/admin/config/search/path/patterns');
+    $this->assertNoLink(t('Disable'));
+    $this->clickLink(t('Enable'));
+    $this->assertUrl('/admin/config/search/path/patterns/page_pattern/enable', $destination_query);
+    $this->drupalPostForm(NULL, [], t('Enable'));
+    $this->assertText('Enabled pattern Test.');
+
+    // Reload pattern from storage and check if its enabled.
+    $pattern = PathautoPattern::load('page_pattern');
+    $this->assertTrue($pattern->status());
+
+    // Delete workflow.
+    $this->drupalGet('/admin/config/search/path/patterns');
+    $this->clickLink(t('Delete'));
+    $this->assertUrl('/admin/config/search/path/patterns/page_pattern/delete', $destination_query);
+    $this->assertText(t('This action cannot be undone.'));
+    $this->drupalPostForm(NULL, [], t('Delete'));
+    $this->assertText('The pathauto pattern Test has been deleted.');
+
+    $this->assertFalse(PathautoPattern::load('page_pattern'));
+  }
+
+}

+ 92 - 0
sites/all/modules/contrib/admin/pathauto/src/Tests/PathautoUserWebTest.php

@@ -0,0 +1,92 @@
+<?php
+
+namespace Drupal\pathauto\Tests;
+use Drupal\Component\Utility\Unicode;
+use Drupal\simpletest\WebTestBase;
+use Drupal\views\Views;
+
+/**
+ * Tests pathauto user UI integration.
+ *
+ * @group pathauto
+ */
+class PathautoUserWebTest extends WebTestBase {
+
+  use PathautoTestHelperTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('pathauto', 'views');
+
+  /**
+   * Admin user.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  /**
+   * {inheritdoc}
+   */
+  function setUp() {
+    parent::setUp();
+
+    // Allow other modules to add additional permissions for the admin user.
+    $permissions = array(
+      'administer pathauto',
+      'administer url aliases',
+      'create url aliases',
+      'administer users',
+    );
+    $this->adminUser = $this->drupalCreateUser($permissions);
+    $this->drupalLogin($this->adminUser);
+
+    $this->createPattern('user', '/users/[user:name]');
+  }
+
+
+  /**
+   * Basic functional testing of Pathauto with users.
+   */
+  function testUserEditing() {
+    // There should be no Pathauto checkbox on user forms.
+    $this->drupalGet('user/' . $this->adminUser->id() . '/edit');
+    $this->assertNoFieldById('path[0][pathauto]');
+  }
+
+  /**
+   * Test user operations.
+   */
+  function testUserOperations() {
+    $account = $this->drupalCreateUser();
+
+    // Delete all current URL aliases.
+    $this->deleteAllAliases();
+
+    // Find the position of just created account in the user_admin_people view.
+    $view = Views::getView('user_admin_people');
+    $view->initDisplay();
+    $view->preview('page_1');
+
+
+    foreach ($view->result as $key => $row) {
+      if ($view->field['name']->getValue($row) == $account->getUsername()) {
+        break;
+      }
+    }
+
+    $edit = array(
+      'action' => 'pathauto_update_alias_user',
+      "user_bulk_form[$key]" => TRUE,
+    );
+    $this->drupalPostForm('admin/people', $edit, t('Apply to selected items'));
+    $this->assertText('Update URL alias was applied to 1 item.');
+
+    $this->assertEntityAlias($account, '/users/' . Unicode::strtolower($account->getUsername()));
+    $this->assertEntityAlias($this->adminUser, '/user/' . $this->adminUser->id());
+  }
+
+}

+ 63 - 0
sites/all/modules/contrib/admin/pathauto/src/VerboseMessenger.php

@@ -0,0 +1,63 @@
+<?php
+
+namespace Drupal\pathauto;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Provides a verbose messenger.
+ */
+class VerboseMessenger implements MessengerInterface {
+
+  /**
+   * The verbose flag.
+   *
+   * @var bool
+   */
+  protected $isVerbose;
+
+  /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The current user account.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $account;
+
+  /**
+   * Creates a verbose messenger.
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, AccountInterface $account) {
+    $this->configFactory = $config_factory;
+    $this->account = $account;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function addMessage($message, $op = NULL) {
+
+    if (!isset($this->isVerbose)) {
+      $config = $this->configFactory->get('pathauto.settings');
+      $this->isVerbose = $config->get('verbose') && $this->account->hasPermission('notify of path changes');
+    }
+
+    if (!$this->isVerbose || (isset($op) && in_array($op, array('bulkupdate', 'return')))) {
+      return FALSE;
+    }
+
+    if ($message) {
+      drupal_set_message($message);
+    }
+
+    return TRUE;
+  }
+
+}

+ 13 - 0
sites/all/modules/contrib/admin/pathauto/tests/modules/pathauto_string_id_test/pathauto_string_id_test.info.yml

@@ -0,0 +1,13 @@
+name: 'Pathauto testing module'
+type: module
+# core: '8.x'
+description: 'Pathauto for Entity with string ID.'
+package: Testing
+dependencies:
+  - token
+
+# Information added by Drupal.org packaging script on 2018-03-03
+version: '8.x-1.1'
+core: '8.x'
+project: 'pathauto'
+datestamp: 1520092688

+ 50 - 0
sites/all/modules/contrib/admin/pathauto/tests/modules/pathauto_string_id_test/src/Entity/PathautoStringIdTest.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace Drupal\pathauto_string_id_test\Entity;
+
+use Drupal\Core\Entity\ContentEntityBase;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
+
+/**
+ * Defines a test entity with a string ID.
+ *
+ * @ContentEntityType(
+ *   id = "pathauto_string_id_test",
+ *   label = @Translation("Test entity with string ID"),
+ *   handlers = {
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
+ *     },
+ *   },
+ *   base_table = "pathauto_string_id_test",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "label" = "name",
+ *   },
+ *   links = {
+ *     "canonical" = "/pathauto_string_id_test/{pathauto_string_id_test}",
+ *   },
+ * )
+ */
+class PathautoStringIdTest extends ContentEntityBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
+    $fields['id'] = BaseFieldDefinition::create('string')
+      ->setLabel('ID')
+      ->setReadOnly(TRUE)
+      // A bigger value will not be allowed to build the index.
+      ->setSetting('max_length', 191);
+    $fields['name'] = BaseFieldDefinition::create('string')
+      ->setLabel('Name');
+    $fields['path'] = BaseFieldDefinition::create('path')
+      ->setLabel('Path')
+      ->setComputed(TRUE);
+
+    return $fields;
+  }
+
+}

+ 231 - 0
sites/all/modules/contrib/admin/pathauto/tests/modules/pathauto_views_test/config/install/views.view.articles.yml

@@ -0,0 +1,231 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - node
+    - taxonomy
+    - user
+id: articles
+label: articles
+module: pathauto_views_test
+description: ''
+tag: ''
+base_table: node_field_data
+base_field: nid
+core: 8.x
+display:
+  default:
+    display_plugin: default
+    id: default
+    display_title: Master
+    position: 0
+    display_options:
+      access:
+        type: perm
+        options:
+          perm: 'access content'
+      cache:
+        type: tag
+        options: {  }
+      query:
+        type: views_query
+        options:
+          disable_sql_rewrite: false
+          distinct: false
+          replica: false
+          query_comment: ''
+          query_tags: {  }
+      exposed_form:
+        type: basic
+        options:
+          submit_button: Apply
+          reset_button: false
+          reset_button_label: Reset
+          exposed_sorts_label: 'Sort by'
+          expose_sort_order: true
+          sort_asc_label: Asc
+          sort_desc_label: Desc
+      pager:
+        type: full
+        options:
+          items_per_page: 10
+          offset: 0
+          id: 0
+          total_pages: null
+          expose:
+            items_per_page: false
+            items_per_page_label: 'Items per page'
+            items_per_page_options: '5, 10, 25, 50'
+            items_per_page_options_all: false
+            items_per_page_options_all_label: '- All -'
+            offset: false
+            offset_label: Offset
+          tags:
+            previous: '‹ Previous'
+            next: 'Next ›'
+            first: '« First'
+            last: 'Last »'
+          quantity: 9
+      style:
+        type: default
+      row:
+        type: 'entity:node'
+        options:
+          view_mode: teaser
+      fields:
+        title:
+          id: title
+          table: node_field_data
+          field: title
+          entity_type: node
+          entity_field: title
+          label: ''
+          alter:
+            alter_text: false
+            make_link: false
+            absolute: false
+            trim: false
+            word_boundary: false
+            ellipsis: false
+            strip_tags: false
+            html: false
+          hide_empty: false
+          empty_zero: false
+          settings:
+            link_to_entity: true
+          plugin_id: field
+          relationship: none
+          group_type: group
+          admin_label: ''
+          exclude: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_alter_empty: true
+          click_sort_column: value
+          type: string
+          group_column: value
+          group_columns: {  }
+          group_rows: true
+          delta_limit: 0
+          delta_offset: 0
+          delta_reversed: false
+          delta_first_last: false
+          multi_type: separator
+          separator: ', '
+          field_api_classes: false
+      filters:
+        status:
+          value: '1'
+          table: node_field_data
+          field: status
+          plugin_id: boolean
+          entity_type: node
+          entity_field: status
+          id: status
+          expose:
+            operator: ''
+          group: 1
+        type:
+          id: type
+          table: node_field_data
+          field: type
+          value:
+            article: article
+          entity_type: node
+          entity_field: type
+          plugin_id: bundle
+      sorts:
+        created:
+          id: created
+          table: node_field_data
+          field: created
+          order: DESC
+          entity_type: node
+          entity_field: created
+          plugin_id: date
+          relationship: none
+          group_type: group
+          admin_label: ''
+          exposed: false
+          expose:
+            label: ''
+          granularity: second
+      title: articles
+      header: {  }
+      footer: {  }
+      empty: {  }
+      relationships: {  }
+      arguments:
+        tid:
+          id: tid
+          table: taxonomy_index
+          field: tid
+          relationship: none
+          group_type: group
+          admin_label: ''
+          default_action: ignore
+          exception:
+            value: all
+            title_enable: false
+            title: All
+          title_enable: false
+          title: ''
+          default_argument_type: fixed
+          default_argument_options:
+            argument: ''
+          default_argument_skip_url: false
+          summary_options:
+            base_path: ''
+            count: true
+            items_per_page: 25
+            override: false
+          summary:
+            sort_order: asc
+            number_of_records: 0
+            format: default_summary
+          specify_validation: false
+          validate:
+            type: none
+            fail: 'not found'
+          validate_options: {  }
+          break_phrase: false
+          add_table: false
+          require_value: false
+          reduce_duplicates: false
+          plugin_id: taxonomy_index_tid
+      display_extenders: {  }
+    cache_metadata:
+      max-age: -1
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url
+        - url.query_args
+        - 'user.node_grants:view'
+        - user.permissions
+      tags: {  }
+  page_1:
+    display_plugin: page
+    id: page_1
+    display_title: Page
+    position: 1
+    display_options:
+      display_extenders: {  }
+      path: articles
+    cache_metadata:
+      max-age: -1
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url
+        - url.query_args
+        - 'user.node_grants:view'
+        - user.permissions
+      tags: {  }

+ 14 - 0
sites/all/modules/contrib/admin/pathauto/tests/modules/pathauto_views_test/pathauto_views_test.info.yml

@@ -0,0 +1,14 @@
+name: 'Views Test Config'
+type: module
+description: 'Provides default views for tests.'
+package: Testing
+# version: VERSION
+# core: 8.x
+dependencies:
+  - views
+
+# Information added by Drupal.org packaging script on 2018-03-03
+version: '8.x-1.1'
+core: '8.x'
+project: 'pathauto'
+datestamp: 1520092688

+ 130 - 0
sites/all/modules/contrib/admin/pathauto/tests/src/Kernel/PathautoEntityWithStringIdTest.php

@@ -0,0 +1,130 @@
+<?php
+
+namespace Drupal\Tests\pathauto\Kernel;
+
+use Drupal\Component\Serialization\PhpSerialize;
+use Drupal\Component\Utility\Crypt;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\KeyValueStore\KeyValueDatabaseFactory;
+use Drupal\pathauto\PathautoState;
+use Drupal\pathauto\Tests\PathautoTestHelperTrait;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\pathauto_string_id_test\Entity\PathautoStringIdTest;
+
+/**
+ * Tests auto-aliasing of entities that use string IDs.
+ *
+ * @group pathauto
+ */
+class PathautoEntityWithStringIdTest extends KernelTestBase {
+
+  use PathautoTestHelperTrait;
+
+  /**
+   * The alias type plugin instance.
+   *
+   * @var \Drupal\pathauto\AliasTypeBatchUpdateInterface
+   */
+  protected $aliasType;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'system',
+    'user',
+    'field',
+    'token',
+    'path',
+    'pathauto',
+    'pathauto_string_id_test',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function register(ContainerBuilder $container) {
+    parent::register($container);
+    // Kernel tests are using the 'keyvalue.memory' store but we want to test
+    // against the 'keyvalue.database'.
+    $container
+      ->register('keyvalue.database', KeyValueDatabaseFactory::class)
+      ->addArgument(new PhpSerialize())
+      ->addArgument($container->get('database'))
+      ->addTag('persist');
+    $container->setAlias('keyvalue', 'keyvalue.database');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installSchema('system', ['key_value']);
+    $this->installConfig(['system']);
+    $this->installEntitySchema('pathauto_string_id_test');
+    $this->createPattern('pathauto_string_id_test', '/[pathauto_string_id_test:name]');
+    /** @var \Drupal\pathauto\AliasTypeManager $alias_type_manager */
+    $alias_type_manager = $this->container->get('plugin.manager.alias_type');
+    $this->aliasType = $alias_type_manager->createInstance('canonical_entities:pathauto_string_id_test');
+  }
+
+  /**
+   * Test aliasing entities with long string ID.
+   *
+   * @dataProvider entityWithStringIdProvider
+   *
+   * @param string|int $id
+   *   The entity ID
+   * @param string $expected_key
+   *   The expected key for 'pathauto_state.*' collections.
+   */
+  public function testEntityWithStringId($id, $expected_key) {
+    $entity = PathautoStringIdTest::create([
+      'id' => $id,
+      'name' => $name = $this->randomMachineName(),
+    ]);
+    $entity->save();
+
+    // Check that the path was generated.
+    $this->assertEntityAlias($entity, "/$name");
+    // Check that the path auto state was saved with the expected key.
+    $value = \Drupal::keyValue('pathauto_state.pathauto_string_id_test')->get($expected_key);
+    $this->assertEquals(PathautoState::CREATE, $value);
+
+    $context = [];
+    // Batch delete uses the key-value store collection 'pathauto_state.*. We
+    // test that after a bulk delete all aliases are removed. Running only once
+    // the batch delete process is enough as the batch size is 100.
+    $this->aliasType->batchDelete($context);
+
+    // Check that the paths were removed on batch delete.
+    $this->assertNoEntityAliasExists($entity, "/$name");
+  }
+
+  /**
+   * Provides test cases for ::testEntityWithStringId().
+   *
+   * @see \Drupal\Tests\pathauto\Kernel\PathautoEntityWithStringIdTest::testEntityWithStringId()
+   */
+  public function entityWithStringIdProvider() {
+    return [
+      'ascii with less or equal 128 chars' => [
+        str_repeat('a', 128), str_repeat('a', 128)
+      ],
+      'ascii with over 128 chars' => [
+        str_repeat('a', 191), Crypt::hashBase64(str_repeat('a', 191))
+      ],
+      'non-ascii with less or equal 128 chars' => [
+        str_repeat('社', 128), Crypt::hashBase64(str_repeat('社', 128))
+      ],
+      'non-ascii with over 128 chars' => [
+        str_repeat('社', 191), Crypt::hashBase64(str_repeat('社', 191))
+      ],
+      'simulating an integer id' => [
+        123, '123'
+      ],
+    ];
+  }
+
+}

+ 588 - 0
sites/all/modules/contrib/admin/pathauto/tests/src/Kernel/PathautoKernelTest.php

@@ -0,0 +1,588 @@
+<?php
+
+namespace Drupal\Tests\pathauto\Kernel;
+
+use Drupal\Component\Utility\Html;
+use Drupal\Component\Utility\Unicode;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\node\Entity\NodeType;
+use Drupal\pathauto\PathautoGeneratorInterface;
+use Drupal\pathauto\PathautoState;
+use Drupal\pathauto\Tests\PathautoTestHelperTrait;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\taxonomy\Entity\Term;
+use Drupal\taxonomy\Entity\Vocabulary;
+use Drupal\node\Entity\Node;
+use Drupal\user\Entity\User;
+
+/**
+ * Unit tests for Pathauto functions.
+ *
+ * @group pathauto
+ */
+class PathautoKernelTest extends KernelTestBase {
+
+  use PathautoTestHelperTrait;
+
+  public static $modules = array('system', 'field', 'text', 'user', 'node', 'path', 'pathauto', 'taxonomy', 'token', 'filter', 'ctools', 'language');
+
+  protected $currentUser;
+
+  /**
+   * @var \Drupal\pathauto\PathautoPatternInterface
+   */
+  protected $nodePattern;
+
+  /**
+   * @var \Drupal\pathauto\PathautoPatternInterface
+   */
+  protected $userPattern;
+
+  public function setUp() {
+    parent::setup();
+
+    $this->installConfig(array('pathauto', 'taxonomy', 'system', 'node'));
+
+    $this->installEntitySchema('user');
+    $this->installEntitySchema('node');
+    $this->installEntitySchema('taxonomy_term');
+
+    ConfigurableLanguage::createFromLangcode('fr')->save();
+
+    $this->installSchema('node', array('node_access'));
+    $this->installSchema('system', array('url_alias', 'sequences', 'router'));
+
+    $type = NodeType::create(['type' => 'page']);
+    $type->save();
+    node_add_body_field($type);
+
+    $this->nodePattern = $this->createPattern('node', '/content/[node:title]');
+    $this->userPattern = $this->createPattern('user', '/users/[user:name]');
+
+    \Drupal::service('router.builder')->rebuild();
+
+    $this->currentUser = User::create(array('name' => $this->randomMachineName()));
+    $this->currentUser->save();
+  }
+
+  /**
+   * Test _pathauto_get_schema_alias_maxlength().
+   */
+  public function testGetSchemaAliasMaxLength() {
+    $this->assertIdentical(\Drupal::service('pathauto.alias_storage_helper')->getAliasSchemaMaxlength(), 255);
+  }
+
+  /**
+   * Test pathauto_pattern_load_by_entity().
+   */
+  public function testPatternLoadByEntity() {
+    $pattern = $this->createPattern('node', '/article/[node:title]', -1);
+    $this->addBundleCondition($pattern, 'node', 'article');
+    $pattern->save();
+
+    $pattern = $this->createPattern('node', '/article/en/[node:title]', -2);
+    $this->addBundleCondition($pattern, 'node', 'article');
+    $pattern->addSelectionCondition(
+      [
+        'id' => 'language',
+        'langcodes' => [
+          'en' => 'en',
+        ],
+        'negate' => FALSE,
+        'context_mapping' => [
+          'language' => 'node:langcode:language',
+        ]
+      ]
+    );
+
+    $pattern->addRelationship('node:langcode:language');
+    $pattern->save();
+
+    $pattern = $this->createPattern('node', '/[node:title]', -1);
+    $this->addBundleCondition($pattern, 'node', 'page');
+    $pattern->save();
+
+    $tests = array(
+      array(
+        'entity' => 'node',
+        'values' => [
+          'title' => 'Article fr',
+          'type' => 'article',
+          'langcode' => 'fr',
+        ],
+        'expected' => '/article/[node:title]',
+      ),
+      array(
+        'entity' => 'node',
+        'values' => [
+          'title' => 'Article en',
+          'type' => 'article',
+          'langcode' => 'en',
+        ],
+        'expected' => '/article/en/[node:title]',
+      ),
+      array(
+        'entity' => 'node',
+        'values' => [
+          'title' => 'Article und',
+          'type' => 'article',
+          'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
+        ],
+        'expected' => '/article/[node:title]',
+      ),
+      array(
+        'entity' => 'node',
+        'values' => [
+          'title' => 'Page',
+          'type' => 'page',
+        ],
+        'expected' => '/[node:title]',
+      ),
+      array(
+        'entity' => 'user',
+        'values' => [
+          'name' => 'User',
+        ],
+        'expected' => '/users/[user:name]',
+      ),
+    );
+    foreach ($tests as $test) {
+      $entity = \Drupal::entityTypeManager()->getStorage($test['entity'])->create($test['values']);
+      $entity->save();
+      $actual = \Drupal::service('pathauto.generator')->getPatternByEntity($entity);
+      $this->assertIdentical($actual->getPattern(), $test['expected'], t("Correct pattern returned for @entity_type with @values", array(
+        '@entity' => $test['entity'],
+        '@values' => print_r($test['values'], TRUE),
+      )));
+    }
+  }
+
+  /**
+   * Test potential conflicts with the same alias in different languages.
+   */
+  public function testSameTitleDifferentLanguages() {
+    // Create two English articles with the same title.
+    $edit = [
+      'title' => 'Sample page',
+      'type' => 'page',
+      'langcode' => 'en',
+    ];
+    $node1 = $this->drupalCreateNode($edit);
+    $this->assertEntityAlias($node1, '/content/sample-page', 'en');
+
+    $node2 = $this->drupalCreateNode($edit);
+    $this->assertEntityAlias($node2, '/content/sample-page-0', 'en');
+
+    // Now, create a French article with the same title, and verify that it gets
+    // the basic alias with the correct langcode.
+    $edit['langcode'] = 'fr';
+    $node3 = $this->drupalCreateNode($edit);
+    $this->assertEntityAlias($node3, '/content/sample-page', 'fr');
+  }
+
+  /**
+   * Test pathauto_cleanstring().
+   */
+  public function testCleanString() {
+
+    // Test with default settings defined in pathauto.settings.yml.
+    $this->installConfig(array('pathauto'));
+    \Drupal::service('pathauto.generator')->resetCaches();
+
+    $tests = array();
+
+    // Test the 'ignored words' removal.
+    $tests['this'] = 'this';
+    $tests['this with that'] = 'this-with-that';
+    $tests['this thing with that thing'] = 'thing-thing';
+
+    // Test 'ignored words' removal and duplicate separator removal.
+    $tests[' - Pathauto is the greatest - module ever - '] = 'pathauto-greatest-module-ever';
+
+    // Test length truncation and lowering of strings.
+    $long_string = $this->randomMachineName(120);
+    $tests[$long_string] = strtolower(substr($long_string, 0, 100));
+
+    // Test that HTML tags are removed.
+    $tests['This <span class="text">text</span> has <br /><a href="http://example.com"><strong>HTML tags</strong></a>.'] = 'text-has-html-tags';
+    $tests[Html::escape('This <span class="text">text</span> has <br /><a href="http://example.com"><strong>HTML tags</strong></a>.')] = 'text-has-html-tags';
+
+    // Transliteration.
+    $tests['ľščťžýáíéňô'] = 'lsctzyaieno';
+
+    foreach ($tests as $input => $expected) {
+      $output = \Drupal::service('pathauto.alias_cleaner')->cleanString($input);
+      $this->assertEqual($output, $expected, t("Drupal::service('pathauto.alias_cleaner')->cleanString('@input') expected '@expected', actual '@output'", array(
+        '@input' => $input,
+        '@expected' => $expected,
+        '@output' => $output,
+      )));
+    }
+  }
+
+  /**
+   * Test pathauto_clean_alias().
+   */
+  public function testCleanAlias() {
+    $tests = array();
+    $tests['one/two/three'] = '/one/two/three';
+    $tests['/one/two/three/'] = '/one/two/three';
+    $tests['one//two///three'] = '/one/two/three';
+    $tests['one/two--three/-/--/-/--/four---five'] = '/one/two-three/four-five';
+    $tests['one/-//three--/four'] = '/one/three/four';
+
+    foreach ($tests as $input => $expected) {
+      $output = \Drupal::service('pathauto.alias_cleaner')->cleanAlias($input);
+      $this->assertEqual($output, $expected, t("Drupal::service('pathauto.generator')->cleanAlias('@input') expected '@expected', actual '@output'", array(
+        '@input' => $input,
+        '@expected' => $expected,
+        '@output' => $output,
+      )));
+    }
+  }
+
+  /**
+   * Test pathauto_path_delete_multiple().
+   */
+  public function testPathDeleteMultiple() {
+    $this->saveAlias('/node/1', '/node-1-alias');
+    $this->saveAlias('/node/1/view', '/node-1-alias/view');
+    $this->saveAlias('/node/1', '/node-1-alias-en', 'en');
+    $this->saveAlias('/node/1', '/node-1-alias-fr', 'fr');
+    $this->saveAlias('/node/2', '/node-2-alias');
+    $this->saveAlias('/node/10', '/node-10-alias');
+
+    \Drupal::service('pathauto.alias_storage_helper')->deleteBySourcePrefix('/node/1');
+    $this->assertNoAliasExists(array('source' => "/node/1"));
+    $this->assertNoAliasExists(array('source' => "/node/1/view"));
+    $this->assertAliasExists(array('source' => "/node/2"));
+    $this->assertAliasExists(array('source' => "/node/10"));
+  }
+
+  /**
+   * Test the different update actions in \Drupal::service('pathauto.generator')->createEntityAlias().
+   */
+  public function testUpdateActions() {
+    $config = $this->config('pathauto.settings');
+
+    // Test PATHAUTO_UPDATE_ACTION_NO_NEW with unaliased node and 'insert'.
+    $config->set('update_action', PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW);
+    $config->save();
+    $node = $this->drupalCreateNode(array('title' => 'First title'));
+    $this->assertEntityAlias($node, '/content/first-title');
+
+    $node->path->pathauto = PathautoState::CREATE;
+
+    // Default action is PATHAUTO_UPDATE_ACTION_DELETE.
+    $config->set('update_action', PathautoGeneratorInterface::UPDATE_ACTION_DELETE);
+    $config->save();
+    $node->setTitle('Second title');
+    $node->save();
+    $this->assertEntityAlias($node, '/content/second-title');
+    $this->assertNoAliasExists(array('alias' => '/content/first-title'));
+
+    // Test PATHAUTO_UPDATE_ACTION_LEAVE
+    $config->set('update_action', PathautoGeneratorInterface::UPDATE_ACTION_LEAVE);
+    $config->save();
+    $node->setTitle('Third title');
+    $node->save();
+    $this->assertEntityAlias($node, '/content/third-title');
+    $this->assertAliasExists(array('source' => '/' . $node->toUrl()->getInternalPath(), 'alias' => '/content/second-title'));
+
+    $config->set('update_action', PathautoGeneratorInterface::UPDATE_ACTION_DELETE);
+    $config->save();
+    $node->setTitle('Fourth title');
+    $node->save();
+    $this->assertEntityAlias($node, '/content/fourth-title');
+    $this->assertNoAliasExists(array('alias' => '/content/third-title'));
+    // The older second alias is not deleted yet.
+    $older_path = $this->assertAliasExists(array('source' => '/' . $node->toUrl()->getInternalPath(), 'alias' => '/content/second-title'));
+    \Drupal::service('path.alias_storage')->delete($older_path);
+
+    $config->set('update_action', PathautoGeneratorInterface::UPDATE_ACTION_NO_NEW);
+    $config->save();
+    $node->setTitle('Fifth title');
+    $node->save();
+    $this->assertEntityAlias($node, '/content/fourth-title');
+    $this->assertNoAliasExists(array('alias' => '/content/fifth-title'));
+
+    // Test PATHAUTO_UPDATE_ACTION_NO_NEW with unaliased node and 'update'.
+    $this->deleteAllAliases();
+    $node->save();
+    $this->assertEntityAlias($node, '/content/fifth-title');
+
+    // Test PATHAUTO_UPDATE_ACTION_NO_NEW with unaliased node and 'bulkupdate'.
+    $this->deleteAllAliases();
+    $node->setTitle('Sixth title');
+    \Drupal::service('pathauto.generator')->updateEntityAlias($node, 'bulkupdate');
+    $this->assertEntityAlias($node, '/content/sixth-title');
+  }
+
+  /**
+   * Test that \Drupal::service('pathauto.generator')->createEntityAlias() will not create an alias for a pattern
+   * that does not get any tokens replaced.
+   */
+  public function testNoTokensNoAlias() {
+    $this->installConfig(['filter']);
+    $this->nodePattern
+      ->setPattern('/content/[node:body]')
+      ->save();
+
+    $node = $this->drupalCreateNode();
+    $this->assertNoEntityAliasExists($node);
+
+    $node->body->value = 'hello';
+    $node->save();
+    $this->assertEntityAlias($node, '/content/hello');
+  }
+
+  /**
+   * Test the handling of path vs non-path tokens in pathauto_clean_token_values().
+   */
+  public function testPathTokens() {
+    $this->createPattern('taxonomy_term', '/[term:parent:url:path]/[term:name]');
+
+    $vocab = $this->addVocabulary();
+
+    $term1 = $this->addTerm($vocab, array('name' => 'Parent term'));
+    $this->assertEntityAlias($term1, '/parent-term');
+
+    $term2 = $this->addTerm($vocab, array('name' => 'Child term', 'parent' => $term1->id()));
+    $this->assertEntityAlias($term2, '/parent-term/child-term');
+
+    $this->saveEntityAlias($term1, '/My Crazy/Alias/');
+    $term2->save();
+    $this->assertEntityAlias($term2, '/My Crazy/Alias/child-term');
+  }
+
+  /**
+   * Test using fields for path structures.
+   */
+  function testParentChildPathTokens() {
+    // First create a field which will be used to create the path. It must
+    // begin with a letter.
+
+    $this->installEntitySchema('taxonomy_term');
+
+    Vocabulary::create(['vid' => 'tags'])->save();
+
+    $fieldname = 'a' . Unicode::strtolower($this->randomMachineName());
+    $field_storage = FieldStorageConfig::create(['entity_type' => 'taxonomy_term', 'field_name' => $fieldname, 'type' => 'string']);
+    $field_storage->save();
+    $field = FieldConfig::create(['field_storage' => $field_storage, 'bundle' => 'tags']);
+    $field->save();
+
+    $display = entity_get_display('taxonomy_term', 'tags', 'default');
+    $display->setComponent($fieldname, ['type' => 'string']);
+    $display->save();
+
+    // Make the path pattern of a field use the value of this field appended
+    // to the parent taxonomy term's pattern if there is one.
+    $this->createPattern('taxonomy_term', '/[term:parents:join-path]/[term:' . $fieldname . ']');
+
+    // Start by creating a parent term.
+    $parent = Term::create(['vid' => 'tags', $fieldname => $this->randomMachineName(), 'name' => $this->randomMachineName()]);
+    $parent->save();
+
+    // Create the child term.
+    $child = Term::create(['vid' => 'tags', $fieldname => $this->randomMachineName(), 'parent' => $parent, 'name' => $this->randomMachineName()]);
+    $child->save();
+    $this->assertEntityAlias($child, '/' . Unicode::strtolower($parent->getName() . '/' . $child->$fieldname->value));
+
+    // Re-saving the parent term should not modify the child term's alias.
+    $parent->save();
+    $this->assertEntityAlias($child, '/' . Unicode::strtolower($parent->getName() . '/' . $child->$fieldname->value));
+  }
+
+  /**
+   * Tests aliases on taxonomy terms.
+   */
+  public function testTaxonomyPattern() {
+    // Create a vocabulary and test that it's pattern variable works.
+    $vocab = $this->addVocabulary(array('vid' => 'name'));
+    $this->createPattern('taxonomy_term', 'base');
+    $pattern = $this->createPattern('taxonomy_term', 'bundle', -1);
+    $this->addBundleCondition($pattern, 'taxonomy_term', 'name');
+    $pattern->save();
+    $this->assertEntityPattern('taxonomy_term', 'name', Language::LANGCODE_NOT_SPECIFIED, 'bundle');
+  }
+
+  function testNoExistingPathAliases() {
+    $this->config('pathauto.settings')
+      ->set('punctuation.period', PathautoGeneratorInterface::PUNCTUATION_DO_NOTHING)
+      ->save();
+
+    $this->nodePattern
+      ->setPattern('[node:title]')
+      ->save();
+
+    // Check that Pathauto does not create an alias of '/admin'.
+    $node = $this->drupalCreateNode(array('title' => 'Admin', 'type' => 'page'));
+    $this->assertEntityAlias($node, '/admin-0');
+
+    // Check that Pathauto does not create an alias of '/modules'.
+    $node->setTitle('Modules');
+    $node->save();
+    $this->assertEntityAlias($node, '/modules-0');
+
+    // Check that Pathauto does not create an alias of '/index.php'.
+    $node->setTitle('index.php');
+    $node->save();
+    $this->assertEntityAlias($node, '/index.php-0');
+
+    // Check that a safe value gets an automatic alias. This is also a control
+    // to ensure the above tests work properly.
+    $node->setTitle('Safe value');
+    $node->save();
+    $this->assertEntityAlias($node, '/safe-value');
+  }
+
+  /**
+   * Test programmatic entity creation for aliases.
+   */
+  function testProgrammaticEntityCreation() {
+    $this->createPattern('taxonomy_term', '/[term:vocabulary]/[term:name]');
+    $node = $this->drupalCreateNode(array('title' => 'Test node', 'path' => array('pathauto' => TRUE)));
+    $this->assertEntityAlias($node, '/content/test-node');
+
+    $vocabulary = $this->addVocabulary(array('name' => 'Tags'));
+    $term = $this->addTerm($vocabulary, array('name' => 'Test term', 'path' => array('pathauto' => TRUE)));
+    $this->assertEntityAlias($term, '/tags/test-term');
+
+    $edit['name'] = 'Test user';
+    $edit['mail'] = 'test-user@example.com';
+    $edit['pass']   = user_password();
+    $edit['path'] = array('pathauto' => TRUE);
+    $edit['status'] = 1;
+    $account = User::create($edit);
+    $account->save();
+    $this->assertEntityAlias($account, '/users/test-user');
+  }
+
+  /**
+   * Tests word safe alias truncating.
+   */
+  function testPathAliasUniquifyWordsafe() {
+    $this->config('pathauto.settings')
+      ->set('max_length', 26)
+      ->save();
+
+    $node_1 = $this->drupalCreateNode(array('title' => 'thequick brownfox jumpedover thelazydog', 'type' => 'page'));
+    $node_2 = $this->drupalCreateNode(array('title' => 'thequick brownfox jumpedover thelazydog', 'type' => 'page'));
+
+    // Check that alias uniquifying is truncating with $wordsafe param set to
+    // TRUE.
+    // If it doesn't path alias result would be content/thequick-brownf-0
+    $this->assertEntityAlias($node_1, '/content/thequick-brownfox');
+    $this->assertEntityAlias($node_2, '/content/thequick-0');
+  }
+
+  /**
+   * Test if aliases are (not) generated with enabled/disabled patterns.
+   */
+  function testPatternStatus() {
+    // Create a node to get an alias for.
+    $title = 'Pattern enabled';
+    $alias = '/content/pattern-enabled';
+    $node1 = $this->drupalCreateNode(['title' => $title, 'type' => 'page']);
+    $this->assertEntityAlias($node1, $alias);
+
+    // Disable the pattern, save the node again and make sure the alias is still
+    // working.
+    $this->nodePattern->setStatus(FALSE)->save();
+
+    $node1->save();
+    $this->assertEntityAlias($node1, $alias);
+
+    // Create a new node with disabled pattern and make sure there is no new
+    // alias created.
+    $title = 'Pattern disabled';
+    $node2 = $this->drupalCreateNode(['title' => $title, 'type' => 'page']);
+    $this->assertNoEntityAlias($node2);
+  }
+
+  /**
+   * Tests that enabled entity types genrates the necessary fields and plugins.
+   */
+  public function testSettingChangeInvalidatesCache() {
+
+    $this->installConfig(['pathauto']);
+
+    $this->enableModules(['entity_test']);
+
+    $definitions = \Drupal::service('plugin.manager.alias_type')->getDefinitions();
+    $this->assertFalse(isset($definitions['canonical_entities:entity_test']));
+
+    $fields = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions('entity_test');
+    $this->assertFalse(isset($fields['path']));
+
+    $this->config('pathauto.settings')
+      ->set('enabled_entity_types', ['user', 'entity_test'])
+      ->save();
+
+    $definitions = \Drupal::service('plugin.manager.alias_type')->getDefinitions();
+    $this->assertTrue(isset($definitions['canonical_entities:entity_test']));
+
+    $fields = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions('entity_test');
+    $this->assertTrue(isset($fields['path']));
+
+  }
+
+  /**
+   * Tests that aliases are only generated for default revisions.
+   */
+  public function testDefaultRevision() {
+    $node1 = $this->drupalCreateNode(['title' => 'Default revision', 'type' => 'page']);
+    $this->assertEntityAlias($node1, '/content/default-revision');
+
+    $node1->setNewRevision(TRUE);
+    $node1->isDefaultRevision(FALSE);
+    $node1->setTitle('New non-default-revision');
+    $node1->save();
+
+    $this->assertEntityAlias($node1, '/content/default-revision');
+  }
+
+  /**
+   * Tests that the pathauto state property gets set to CREATED for new nodes.
+   *
+   * In some cases, this can trigger $node->path to be set up with no default
+   * value for the pathauto property.
+   */
+  public function testCreateNodeWhileAccessingPath() {
+    $node = Node::create([
+      'type' => 'article',
+      'title' => 'TestAlias',
+    ]);
+    $node->path->langcode;
+    $node->save();
+    $this->assertEntityAlias($node, '/content/testalias');
+  }
+
+  /**
+   * Creates a node programmatically.
+   *
+   * @param array $settings
+   *   The array of values for the node.
+   *
+   * @return \Drupal\node\Entity\Node
+   *   The created node.
+   */
+  protected function drupalCreateNode(array $settings = array()) {
+    // Populate defaults array.
+    $settings += array(
+      'title'     => $this->randomMachineName(8),
+      'type'      => 'page',
+    );
+
+    $node = Node::create($settings);
+    $node->save();
+
+    return $node;
+  }
+
+}

+ 78 - 0
sites/all/modules/contrib/admin/pathauto/tests/src/Kernel/PathautoTokenTest.php

@@ -0,0 +1,78 @@
+<?php
+
+namespace Drupal\Tests\pathauto\Kernel;
+
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * Tests tokens provided by Pathauto.
+ *
+ * @group pathauto
+ */
+class PathautoTokenTest extends KernelTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('system', 'token', 'pathauto');
+
+  public function testPathautoTokens() {
+
+    $this->installConfig(array('pathauto'));
+
+    $array = array(
+      'test first arg',
+      'The Array / value',
+    );
+
+    $tokens = array(
+      'join-path' => 'test-first-arg/array-value',
+    );
+    $data['array'] = $array;
+    $replacements = $this->assertTokens('array', $data, $tokens);
+
+    // Ensure that the cleanTokenValues() method does not alter this token value.
+    /* @var \Drupal\pathauto\AliasCleanerInterface $alias_cleaner */
+    $alias_cleaner = \Drupal::service('pathauto.alias_cleaner');
+    $alias_cleaner->cleanTokenValues($replacements, $data, array());
+    $this->assertEqual($replacements['[array:join-path]'], 'test-first-arg/array-value');
+  }
+
+  /**
+   * Function copied from TokenTestHelper::assertTokens().
+   */
+  public function assertTokens($type, array $data, array $tokens, array $options = array()) {
+    $input = $this->mapTokenNames($type, array_keys($tokens));
+    $bubbleable_metadata = new BubbleableMetadata();
+    $replacements = \Drupal::token()->generate($type, $input, $data, $options, $bubbleable_metadata);
+    foreach ($tokens as $name => $expected) {
+      $token = $input[$name];
+      if (!isset($expected)) {
+        $this->assertTrue(!isset($values[$token]), t("Token value for @token was not generated.", array('@type' => $type, '@token' => $token)));
+      }
+      elseif (!isset($replacements[$token])) {
+        $this->fail(t("Token value for @token was not generated.", array('@type' => $type, '@token' => $token)));
+      }
+      elseif (!empty($options['regex'])) {
+        $this->assertTrue(preg_match('/^' . $expected . '$/', $replacements[$token]), t("Token value for @token was '@actual', matching regular expression pattern '@expected'.", array('@type' => $type, '@token' => $token, '@actual' => $replacements[$token], '@expected' => $expected)));
+      }
+      else {
+        $this->assertIdentical($replacements[$token], $expected, t("Token value for @token was '@actual', expected value '@expected'.", array('@type' => $type, '@token' => $token, '@actual' => $replacements[$token], '@expected' => $expected)));
+      }
+    }
+
+    return $replacements;
+  }
+
+  public function mapTokenNames($type, array $tokens = array()) {
+    $return = array();
+    foreach ($tokens as $token) {
+      $return[$token] = "[$type:$token]";
+    }
+    return $return;
+  }
+
+}

+ 59 - 0
sites/all/modules/contrib/admin/pathauto/tests/src/Unit/VerboseMessengerTest.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace Drupal\Tests\pathauto\Unit {
+
+  use Drupal\pathauto\VerboseMessenger;
+  use Drupal\Tests\UnitTestCase;
+
+  /**
+   * @coversDefaultClass \Drupal\pathauto\VerboseMessenger
+   * @group pathauto
+   */
+  class VerboseMessengerTest extends UnitTestCase {
+
+    /**
+     * The messenger under test.
+     *
+     * @var \Drupal\pathauto\VerboseMessenger
+     */
+    protected $messenger;
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function setUp() {
+      $config_factory = $this->getConfigFactoryStub(array('pathauto.settings' => array('verbose' => TRUE)));
+      $account = $this->getMock('\Drupal\Core\Session\AccountInterface');
+      $account->expects($this->once())
+        ->method('hasPermission')
+        ->withAnyParameters()
+        ->willReturn(TRUE);
+
+      $this->messenger = new VerboseMessenger($config_factory, $account);
+    }
+
+    /**
+     * Tests add messages.
+     * @covers ::addMessage
+     */
+    public function testAddMessage() {
+      $this->assertTrue($this->messenger->addMessage("Test message"), "The message was added");
+    }
+
+    /**
+     * @covers ::addMessage
+     */
+    public function testDoNotAddMessageWhileBulkupdate() {
+      $this->assertFalse($this->messenger->addMessage("Test message", "bulkupdate"), "The message was NOT added");
+    }
+
+}
+
+}
+namespace {
+  // @todo Delete after https://drupal.org/node/1858196 is in.
+  if (!function_exists('drupal_set_message')) {
+    function drupal_set_message() {
+    }
+  }
+}

+ 339 - 0
sites/all/modules/contrib/dev/ctools/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.

+ 40 - 0
sites/all/modules/contrib/dev/ctools/composer.json

@@ -0,0 +1,40 @@
+{
+  "name": "drupal/ctools",
+  "description": "Provides a number of utility and helper APIs for Drupal developers and site builders.",
+  "type": "drupal-module",
+  "license": "GPL-2.0+",
+  "minimum-stability": "dev",
+  "homepage": "https://www.drupal.org/project/ctools",
+  "authors": [
+    {
+      "name": "Kris Vanderwater (EclipseGc)",
+      "homepage": "https://www.drupal.org/u/eclipsegc",
+      "role": "Maintainer"
+    },
+    {
+      "name": "Jakob Perry (japerry)",
+      "homepage": "https://www.drupal.org/u/japerry",
+      "role": "Maintainer"
+    },
+    {
+      "name": "Tim Plunkett (tim.plunkett)",
+      "homepage": "https://www.drupal.org/u/timplunkett",
+      "role": "Maintainer"
+    },
+    {
+      "name": "James Gilliland (neclimdul)",
+      "homepage": "https://www.drupal.org/u/neclimdul",
+      "role": "Maintainer"
+    },
+    {
+      "name": "Daniel Wehner (dawehner)",
+      "homepage": "https://www.drupal.org/u/dawehner",
+      "role": "Maintainer"
+    }
+  ],
+  "support": {
+    "issues": "https://www.drupal.org/project/issues/ctools",
+    "source": "http://cgit.drupalcode.org/ctools"
+  },
+  "require": {}
+}

+ 63 - 0
sites/all/modules/contrib/dev/ctools/config/schema/ctools.schema.yml

@@ -0,0 +1,63 @@
+ctools.context:
+  type: mapping
+  label: Context
+  mapping:
+    label:
+      type: label
+      label: 'Label of the context'
+    type:
+      type: string
+      label: 'Context type'
+    description:
+      type: string
+      label: 'Description of the context'
+    value:
+      type: string
+      label: 'Context value'
+
+ctools.relationship:
+  type: mapping
+  label: 'Relationship'
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    label:
+      type: label
+      label: 'Label'
+
+condition.plugin.entity_bundle:*:
+  type: condition.plugin
+  mapping:
+    bundles:
+      type: sequence
+      sequence:
+        type: string
+
+ctools.block_plugin.*:
+  type: block.settings.[id]
+  mapping:
+    region:
+      type: string
+      label: 'Region'
+    weight:
+      type: integer
+      label: 'Weight'
+    uuid:
+      type: string
+      label: 'UUID'
+    context_mapping:
+      type: sequence
+      label: 'Context assignments'
+      sequence:
+        - type: string
+
+ctools.block_display_variant:
+  type: display_variant.plugin
+  label: 'Block display variant'
+  mapping:
+    blocks:
+      type: sequence
+      label: 'Blocks'
+      sequence:
+        - type: ctools.block_plugin.[id]

+ 11 - 0
sites/all/modules/contrib/dev/ctools/ctools.info.yml

@@ -0,0 +1,11 @@
+name: Chaos tools
+type: module
+description: 'Provides a number of utility and helper APIs for Drupal developers and site builders.'
+package: Chaos tool suite
+# core: 8.x
+
+# Information added by Drupal.org packaging script on 2017-04-28
+version: '8.x-3.0'
+core: '8.x'
+project: 'ctools'
+datestamp: 1493401747

+ 64 - 0
sites/all/modules/contrib/dev/ctools/ctools.module

@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * Implements hook_theme().
+ */
+function ctools_theme($existing, $type, $theme, $path) {
+  return [
+    'ctools_wizard_trail' => [
+      'variables' => [
+        'wizard' => NULL,
+        'cached_values' => [],
+        'trail' => [],
+        'divider' => ' » ',
+        'step' => NULL,
+      ],
+    ],
+    'ctools_wizard_trail_links' => [
+      'variables' => [
+        'wizard' => NULL,
+        'cached_values' => [],
+        'trail' => [],
+        'divider' => ' » ',
+        'step' => NULL,
+      ],
+    ],
+  ];
+}
+
+function template_preprocess_ctools_wizard_trail(&$variables) {
+  /** @var $wizard \Drupal\ctools\Wizard\FormWizardInterface|\Drupal\ctools\Wizard\EntityFormWizardInterface */
+  $wizard = $variables['wizard'];
+  $cached_values = $variables['cached_values'];
+  $trail = $variables['trail'];
+  $variables['step'] = $wizard->getStep($cached_values);
+  foreach ($wizard->getOperations($cached_values) as $step => $operation) {
+    $trail[$step] = !empty($operation['title']) ? $operation['title'] : '';
+  }
+  $variables['trail'] = $trail;
+}
+
+function template_preprocess_ctools_wizard_trail_links(&$variables) {
+  /** @var $wizard \Drupal\ctools\Wizard\FormWizardInterface|\Drupal\ctools\Wizard\EntityFormWizardInterface */
+  $wizard = $variables['wizard'];
+  $cached_values = $variables['cached_values'];
+  $trail = $variables['trail'];
+  $variables['step'] = $wizard->getStep($cached_values);
+  foreach ($wizard->getOperations($cached_values) as $step => $operation) {
+    $parameters = $wizard->getNextParameters($cached_values);
+    // Override step to be the step we want.
+    $parameters['step'] = $step;
+    $trail[$step] = [
+      'title' => !empty($operation['title']) ? $operation['title'] : '',
+      'url' => new \Drupal\Core\Url($wizard->getRouteName(), $parameters),
+    ];
+  }
+  $variables['trail'] = $trail;
+}
+
+function ctools_condition_info_alter(&$definitions) {
+  // If the node_type's class is unaltered, use a custom ctools implementation.
+  if (isset($definitions['node_type']) && $definitions['node_type']['class'] == 'Drupal\node\Plugin\Condition\NodeType') {
+    $definitions['node_type']['class'] = 'Drupal\ctools\Plugin\Condition\NodeType';
+  }
+}

+ 38 - 0
sites/all/modules/contrib/dev/ctools/ctools.services.yml

@@ -0,0 +1,38 @@
+services:
+  ctools.wizard.form:
+    class: Drupal\ctools\Controller\WizardFormController
+    arguments: ['@controller_resolver', '@form_builder', '@ctools.wizard.factory']
+  ctools.wizard.entity.form:
+    class: Drupal\ctools\Controller\WizardEntityFormController
+    arguments: ['@controller_resolver', '@form_builder', '@ctools.wizard.factory', '@entity.manager']
+  ctools.wizard_enhancer:
+    class: Drupal\ctools\Routing\Enhancer\WizardEnhancer
+    tags:
+      - { name: route_enhancer }
+  ctools.wizard.factory:
+    class: Drupal\ctools\Wizard\WizardFactory
+    arguments: ['@form_builder', '@event_dispatcher']
+  ctools.paramconverter.tempstore:
+    class: Drupal\ctools\ParamConverter\TempstoreConverter
+    arguments: ['@user.shared_tempstore', '@entity_type.manager']
+    tags:
+      - { name: paramconverter }
+  ctools.typed_data.resolver:
+    class: Drupal\ctools\TypedDataResolver
+    arguments: ['@typed_data_manager', '@string_translation']
+  ctools.access:
+    class: Drupal\ctools\Access\TempstoreAccess
+    arguments: ['@user.shared_tempstore']
+    tags:
+      - { name: access_check, applies_to: _ctools_access }
+  plugin.manager.ctools.relationship:
+    class: Drupal\ctools\Plugin\RelationshipManager
+    parent: default_plugin_manager
+  ctools.context_mapper:
+    class: Drupal\ctools\ContextMapper
+    arguments: ['@entity.repository']
+  ctools.serializable.tempstore.factory:
+    class: Drupal\ctools\SerializableTempstoreFactory
+    arguments: ['@keyvalue.expirable', '@lock', '@request_stack', '%user.tempstore.expire%']
+    tags:
+      - { name: backend_overridable }

+ 28 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_block/config/schema/ctools_block.schema.yml

@@ -0,0 +1,28 @@
+block.settings.entity_field:*:*:
+  type: block_settings
+  label: 'Entity field block'
+  mapping:
+    formatter:
+      type: mapping
+      label: 'Field formatter'
+      mapping:
+        type:
+          type: string
+          label: 'Format type machine name'
+        weight:
+          type: integer
+          label: 'Weight'
+        region:
+          type: string
+          label: 'Region'
+        label:
+          type: string
+          label: 'Label setting machine name'
+        settings:
+          type: field.formatter.settings.[%parent.type]
+          label: 'Settings'
+        third_party_settings:
+          type: sequence
+          label: 'Third party settings'
+          sequence:
+            type: field.formatter.third_party.[%key]

+ 14 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_block/ctools_block.info.yml

@@ -0,0 +1,14 @@
+name: Chaos tools blocks
+type: module
+description: 'Provides improvements to blocks that will one day be added to Drupal core.'
+package: Chaos tool suite (Experimental)
+# version: 3.x
+# core: 8.x
+dependencies:
+  - ctools
+
+# Information added by Drupal.org packaging script on 2017-04-28
+version: '8.x-3.0'
+core: '8.x'
+project: 'ctools'
+datestamp: 1493401747

+ 376 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_block/src/Plugin/Block/EntityField.php

@@ -0,0 +1,376 @@
+<?php
+
+namespace Drupal\ctools_block\Plugin\Block;
+
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Block\BlockBase;
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\Field\FormatterPluginManager;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Plugin\ContextAwarePluginInterface;
+use Drupal\Core\Render\Element;
+use Drupal\Core\Session\AccountInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a block to a field on an entity.
+ *
+ * @Block(
+ *   id = "entity_field",
+ *   deriver = "Drupal\ctools_block\Plugin\Deriver\EntityFieldDeriver",
+ * )
+ */
+class EntityField extends BlockBase implements ContextAwarePluginInterface, ContainerFactoryPluginInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity field manager.
+   *
+   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
+   */
+  protected $entityFieldManager;
+
+  /**
+   * The field type manager.
+   *
+   * @var \Drupal\Core\Field\FieldTypePluginManagerInterface
+   */
+  protected $fieldTypeManager;
+
+  /**
+   * The formatter manager.
+   *
+   * @var \Drupal\Core\Field\FormatterPluginManager
+   */
+  protected $formatterManager;
+
+  /**
+   * The entity type id.
+   *
+   * @var string
+   */
+  protected $entityTypeId;
+
+  /**
+   * The field name.
+   *
+   * @var string
+   */
+  protected $fieldName;
+
+  /**
+   * The field definition.
+   *
+   * @var \Drupal\Core\Field\FieldDefinitionInterface
+   */
+  protected $fieldDefinition;
+
+  /**
+   * The field storage definition.
+   *
+   * @var \Drupal\Core\Field\FieldStorageDefinitionInterface
+   */
+  protected $fieldStorageDefinition;
+
+  /**
+   * Constructs a new EntityField.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin ID for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
+   *   The entity field manager.
+   * @param \Drupal\Core\Field\FormatterPluginManager $formatter_manager
+   *   The formatter manager.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, FieldTypePluginManagerInterface $field_type_manager, FormatterPluginManager $formatter_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->entityFieldManager = $entity_field_manager;
+    $this->fieldTypeManager = $field_type_manager;
+    $this->formatterManager = $formatter_manager;
+
+    // Get the entity type and field name from the plugin id.
+    list (, $entity_type_id, $field_name) = explode(':', $plugin_id);
+    $this->entityTypeId = $entity_type_id;
+    $this->fieldName = $field_name;
+
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity_type.manager'),
+      $container->get('entity_field.manager'),
+      $container->get('plugin.manager.field.field_type'),
+      $container->get('plugin.manager.field.formatter')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    /** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */
+    $entity = $this->getContextValue('entity');
+    $build = [];
+    /** @var \Drupal\Core\Field\FieldItemListInterface $field */
+    $field = $entity->{$this->fieldName};
+    $display_settings = $this->getConfiguration()['formatter'];
+    $build['field'] = $field->view($display_settings);
+
+    // Set the cache data appropriately.
+    $build['#cache']['contexts'] = $this->getCacheContexts();
+    $build['#cache']['tags'] = $this->getCacheTags();
+    $build['#cache']['max-age'] = $this->getCacheMaxAge();
+
+    return $build;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function blockAccess(AccountInterface $account) {
+    /** @var \Drupal\Core\Entity\EntityInterface $entity */
+    $entity = $this->getContextValue('entity');
+    // Make sure we have access to the entity.
+    $access = $entity->access('view', $account, TRUE);
+    if ($access->isAllowed()) {
+      // Check that the entity in question has this field.
+      if ($entity instanceof FieldableEntityInterface && $entity->hasField($this->fieldName)) {
+        // Check field access.
+        $field_access = $this->entityTypeManager
+          ->getAccessControlHandler($this->entityTypeId)
+          ->fieldAccess('view', $this->getFieldDefinition(), $account);
+
+        if ($field_access) {
+          // Build a renderable array for the field.
+          $build = $entity->get($this->fieldName)->view($this->configuration['formatter']);
+          // If there are actual renderable children, grant access.
+          if (Element::children($build)) {
+            return AccessResult::allowed();
+          }
+        }
+      }
+      // Entity doesn't have this field, so access is denied.
+      return AccessResult::forbidden();
+    }
+    // If we don't have access to the entity, return the forbidden result.
+    return $access;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    $field_type_definition = $this->getFieldTypeDefinition();
+    return [
+      'formatter' => [
+        'label' => 'above',
+        'type' => $field_type_definition['default_formatter'] ?: '',
+        'settings' => [],
+        'third_party_settings' => [],
+        'weight' => 0,
+      ],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockForm($form, FormStateInterface $form_state) {
+    $config = $this->getConfiguration();
+
+    $form['formatter_label'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Label'),
+      '#options' => [
+        'above' => $this->t('Above'),
+        'inline' => $this->t('Inline'),
+        'hidden' => '- ' . $this->t('Hidden') . ' -',
+        'visually_hidden' => '- ' . $this->t('Visually Hidden') . ' -',
+      ],
+      '#default_value' => $config['formatter']['label'],
+    ];
+
+    $form['formatter_type'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Formatter'),
+      '#options' => $this->getFormatterOptions(),
+      '#default_value' => $config['formatter']['type'],
+      '#ajax' => [
+        'callback' => [static::class, 'formatterSettingsAjaxCallback'],
+        'wrapper' => 'formatter-settings-wrapper',
+        'effect' => 'fade',
+      ],
+    ];
+
+    // Add the formatter settings to the form via AJAX.
+    $form['#process'][] = [$this, 'formatterSettingsProcessCallback'];
+    $form['formatter_settings_wrapper'] = [
+      '#prefix' => '<div id="formatter-settings-wrapper">',
+      '#suffix' => '</div>',
+    ];
+    $form['formatter_settings_wrapper']['formatter_settings'] = [
+      '#tree' => TRUE,
+      // The settings from the formatter plugin will be added in the
+      // ::formatterSettingsProcessCallback method.
+    ];
+
+    return $form;
+  }
+
+  /**
+   * Render API callback: builds the formatter settings elements.
+   */
+  public function formatterSettingsProcessCallback(array &$element, FormStateInterface $form_state, array &$complete_form) {
+    $config = $this->getConfiguration();
+    $parents_base = $element['#parents'];
+    $formatter_parent = array_merge($parents_base, ['formatter_type']);
+    $formatter_settings_parent = array_merge($parents_base, ['formatter_settings']);
+
+    $settings_element = &$element['formatter_settings_wrapper']['formatter_settings'];
+
+    // Set the #parents on the formatter_settings so they end up as a peer to
+    // formatter_type.
+    $settings_element['#parents'] = $formatter_settings_parent;
+
+    // Get the formatter name in a way that works regardless of whether we're
+    // getting the value via AJAX or not.
+    $formatter_name = NestedArray::getValue($form_state->getUserInput(), $formatter_parent) ?: $element['formatter_type']['#default_value'];
+
+    // Place the formatter settings on the form if a formatter is selected.
+    $formatter = $this->getFormatter($formatter_name, $form_state->getValue('formatter_label'), $form_state->getValue($formatter_settings_parent, $config['formatter']['settings']), $config['formatter']['third_party_settings']);
+    $settings_element = array_merge($formatter->settingsForm($settings_element, $form_state), $settings_element);
+
+    // Store the array parents for our element so that we can use it to pull out
+    // the formatter settings in our AJAX callback.
+    $complete_form['#formatter_array_parents'] = $element['#array_parents'];
+
+    return $element;
+  }
+
+  /**
+   * Render API callback: gets the layout settings elements.
+   */
+  public static function formatterSettingsAjaxCallback(array $form, FormStateInterface $form_state) {
+    $formatter_array_parents = $form['#formatter_array_parents'];
+    return NestedArray::getValue($form, array_merge($formatter_array_parents, ['formatter_settings_wrapper']));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockSubmit($form, FormStateInterface $form_state) {
+    $this->configuration['formatter']['label'] = $form_state->getValue('formatter_label');
+    $this->configuration['formatter']['type'] = $form_state->getValue('formatter_type');
+    // @todo Remove this manual cast after https://www.drupal.org/node/2635236
+    //   is resolved.
+    $this->configuration['formatter']['settings'] = (array) $form_state->getValue('formatter_settings');
+  }
+
+  /**
+   * Gets the field definition.
+   *
+   * @return \Drupal\Core\Field\FieldDefinitionInterface
+   */
+  protected function getFieldDefinition() {
+    if (empty($this->fieldDefinition)) {
+      $field_map = $this->entityFieldManager->getFieldMap();
+      $bundle = reset($field_map[$this->entityTypeId][$this->fieldName]['bundles']);
+      $field_definitions = $this->entityFieldManager->getFieldDefinitions($this->entityTypeId, $bundle);
+      $this->fieldDefinition = $field_definitions[$this->fieldName];
+    }
+    return $this->fieldDefinition;
+  }
+
+  /**
+   * Gets the field storage definition.
+   *
+   * @return \Drupal\Core\Field\FieldStorageDefinitionInterface
+   */
+  protected function getFieldStorageDefinition() {
+    if (empty($this->fieldStorageDefinition)) {
+      $field_definitions = $this->entityFieldManager->getFieldStorageDefinitions($this->entityTypeId);
+      $this->fieldStorageDefinition = $field_definitions[$this->fieldName];
+    }
+    return $this->fieldStorageDefinition;
+  }
+
+  /**
+   * Gets field type definition.
+   *
+   * @return array
+   *   The field type definition.
+   */
+  protected function getFieldTypeDefinition() {
+    return $this->fieldTypeManager->getDefinition($this->getFieldStorageDefinition()->getType());
+  }
+
+  /**
+   * Gets the formatter options for this field type.
+   *
+   * @return array
+   *   The formatter options.
+   */
+  protected function getFormatterOptions() {
+    return $this->formatterManager->getOptions($this->getFieldStorageDefinition()->getType());
+  }
+
+  /**
+   * Gets the formatter object.
+   *
+   * @param string $type
+   *   The formatter name.
+   * @param string $label
+   *   The label option for the formatter.
+   * @param array $settings
+   *   The formatter settings.
+   * @param array $third_party_settings
+   *   The formatter third party settings.
+   *
+   * @return \Drupal\Core\Field\FormatterInterface
+   *   The formatter object.
+   */
+  protected function getFormatter($type, $label, array $settings, array $third_party_settings) {
+     return $this->formatterManager->createInstance($type, [
+      'field_definition' => $this->getFieldDefinition(),
+      'view_mode' => 'default',
+      'prepare' => TRUE,
+      'label' => $label,
+      'settings' => $settings,
+      'third_party_settings' => $third_party_settings,
+    ]);
+  }
+
+  public function __wakeup() {
+    parent::__wakeup();
+    // @todo figure out why this happens.
+    // prevent $fieldStorageDefinition being erroneously set to $this.
+    $this->fieldStorageDefinition = NULL;
+  }
+
+
+}

+ 65 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_block/src/Plugin/Deriver/EntityFieldDeriver.php

@@ -0,0 +1,65 @@
+<?php
+
+namespace Drupal\ctools_block\Plugin\Deriver;
+
+use Drupal\Core\Plugin\Context\ContextDefinition;
+use Drupal\ctools\Plugin\Deriver\EntityDeriverBase;
+
+/**
+ * Provides entity field block definitions for every field.
+ */
+class EntityFieldDeriver extends EntityDeriverBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDerivativeDefinitions($base_plugin_definition) {
+    $entity_type_labels = $this->entityManager->getEntityTypeLabels();
+    foreach ($this->entityManager->getFieldMap() as $entity_type_id => $entity_field_map) {
+      foreach ($this->entityManager->getFieldStorageDefinitions($entity_type_id) as $field_storage_definition) {
+        $field_name = $field_storage_definition->getName();
+
+        // The blocks are based on fields. However, we are looping through field
+        // storages for which no fields may exist. If that is the case, skip
+        // this field storage.
+        if (!isset($entity_field_map[$field_name])) {
+          continue;
+        }
+
+        $field_info = $entity_field_map[$field_name];
+        $derivative_id = $entity_type_id . ":" . $field_name;
+
+        // Get the admin label for both base and configurable fields.
+        if ($field_storage_definition->isBaseField()) {
+          $admin_label = $field_storage_definition->getLabel();
+        }
+        else {
+          // We take the field label used on the first bundle.
+          $first_bundle = reset($field_info['bundles']);
+          $bundle_field_definitions = $this->entityManager->getFieldDefinitions($entity_type_id, $first_bundle);
+
+          // The field storage config may exist, but it's possible that no
+          // fields are actually using it. If that's the case, skip to the next
+          // field.
+          if (empty($bundle_field_definitions[$field_name])) {
+            continue;
+          }
+          $admin_label = $bundle_field_definitions[$field_name]->getLabel();
+        }
+
+        // Set plugin definition for derivative.
+        $derivative = $base_plugin_definition;
+        $derivative['category'] = $this->t('@entity', ['@entity' => $entity_type_labels[$entity_type_id]]);
+        $derivative['admin_label'] = $admin_label;
+        $derivative['context'] = [
+          'entity' => new ContextDefinition('entity:' . $entity_type_id, $entity_type_labels[$entity_type_id], TRUE),
+        ];
+
+        $this->derivatives[$derivative_id] = $derivative;
+
+      }
+    }
+    return $this->derivatives;
+  }
+
+}

+ 23 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/config/install/core.base_field_override.node.ctools_block_field_test.promote.yml

@@ -0,0 +1,23 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - node.type.ctools_block_field_test
+_core:
+  default_config_hash: hoRuk0InNhhRVGnhQ9hzifTXVz432i9hvPe-tVstUbc
+id: node.ctools_block_field_test.promote
+field_name: promote
+entity_type: node
+bundle: ctools_block_field_test
+label: 'Promoted to front page'
+description: ''
+required: false
+translatable: true
+default_value:
+  -
+    value: 0
+default_value_callback: ''
+settings:
+  on_label: 'On'
+  off_label: 'Off'
+field_type: boolean

+ 62 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/config/install/core.entity_form_display.node.ctools_block_field_test.default.yml

@@ -0,0 +1,62 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.field.node.ctools_block_field_test.body
+    - node.type.ctools_block_field_test
+  module:
+    - path
+    - text
+_core:
+  default_config_hash: xpDeWNLzjX4dDB9YyBlO9y9FR4vHwMv0GKsuqDYy0Bc
+id: node.ctools_block_field_test.default
+targetEntityType: node
+bundle: ctools_block_field_test
+mode: default
+content:
+  body:
+    type: text_textarea_with_summary
+    weight: 31
+    settings:
+      rows: 9
+      summary_rows: 3
+      placeholder: ''
+    third_party_settings: {  }
+  created:
+    type: datetime_timestamp
+    weight: 10
+    settings: {  }
+    third_party_settings: {  }
+  path:
+    type: path
+    weight: 30
+    settings: {  }
+    third_party_settings: {  }
+  promote:
+    type: boolean_checkbox
+    settings:
+      display_label: true
+    weight: 15
+    third_party_settings: {  }
+  sticky:
+    type: boolean_checkbox
+    settings:
+      display_label: true
+    weight: 16
+    third_party_settings: {  }
+  title:
+    type: string_textfield
+    weight: -5
+    settings:
+      size: 60
+      placeholder: ''
+    third_party_settings: {  }
+  uid:
+    type: entity_reference_autocomplete
+    weight: 5
+    settings:
+      match_operator: CONTAINS
+      size: 60
+      placeholder: ''
+    third_party_settings: {  }
+hidden: {  }

+ 18 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/config/install/core.entity_view_display.node.ctools_block_field_test.default.yml

@@ -0,0 +1,18 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.field.node.ctools_block_field_test.body
+    - node.type.ctools_block_field_test
+  module:
+    - user
+_core:
+  default_config_hash: clNnyw6fhh5SwIme5I_3zjbLv-PMfpY-JXofVAC3CV8
+id: node.ctools_block_field_test.default
+targetEntityType: node
+bundle: ctools_block_field_test
+mode: default
+content: {  }
+hidden:
+  body: true
+  links: true

+ 27 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/config/install/core.entity_view_display.node.ctools_block_field_test.teaser.yml

@@ -0,0 +1,27 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - core.entity_view_mode.node.teaser
+    - field.field.node.ctools_block_field_test.body
+    - node.type.ctools_block_field_test
+  module:
+    - text
+    - user
+_core:
+  default_config_hash: gQV5baI7pCIOBUtLkbJ1c2WJwM8CdlKiKOtLLIWnfy0
+id: node.ctools_block_field_test.teaser
+targetEntityType: node
+bundle: ctools_block_field_test
+mode: teaser
+content:
+  body:
+    label: hidden
+    type: text_summary_or_trimmed
+    weight: 101
+    settings:
+      trim_length: 600
+    third_party_settings: {  }
+  links:
+    weight: 100
+hidden: {  }

+ 23 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/config/install/field.field.node.ctools_block_field_test.body.yml

@@ -0,0 +1,23 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.storage.node.body
+    - node.type.ctools_block_field_test
+  module:
+    - text
+_core:
+  default_config_hash: fzUnwtwftRsgKExjpF6XdMqbUzP16ytkjQniBZl1Hqg
+id: node.ctools_block_field_test.body
+field_name: body
+entity_type: node
+bundle: ctools_block_field_test
+label: Body
+description: ''
+required: false
+translatable: true
+default_value: {  }
+default_value_callback: ''
+settings:
+  display_summary: true
+field_type: text_with_summary

+ 37 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/config/install/field.field.node.ctools_block_field_test.field_image.yml

@@ -0,0 +1,37 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - field.storage.node.field_image
+    - node.type.ctools_block_field_test
+  module:
+    - image
+id: node.ctools_block_field_test.field_image
+field_name: field_image
+entity_type: node
+bundle: ctools_block_field_test
+label: Image
+description: ''
+required: false
+translatable: true
+default_value: {  }
+default_value_callback: ''
+settings:
+  file_directory: '[date:custom:Y]-[date:custom:m]'
+  file_extensions: 'png gif jpg jpeg'
+  max_filesize: ''
+  max_resolution: ''
+  min_resolution: ''
+  alt_field: true
+  title_field: false
+  alt_field_required: true
+  title_field_required: false
+  default_image:
+    uuid: null
+    alt: ''
+    title: ''
+    width: null
+    height: null
+  handler: 'default:file'
+  handler_settings: {  }
+field_type: image

+ 31 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/config/install/field.storage.node.field_image.yml

@@ -0,0 +1,31 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - file
+    - image
+    - node
+id: node.field_image
+field_name: field_image
+entity_type: node
+type: image
+settings:
+  uri_scheme: public
+  default_image:
+    uuid: null
+    alt: ''
+    title: ''
+    width: null
+    height: null
+  target_type: file
+  display_field: false
+  display_default: false
+module: image
+locked: false
+cardinality: 1
+translatable: true
+indexes:
+  target_id:
+    - target_id
+persist_with_no_fields: false
+custom_storage: false

+ 18 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/config/install/node.type.ctools_block_field_test.yml

@@ -0,0 +1,18 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - menu_ui
+third_party_settings:
+  menu_ui:
+    available_menus: {  }
+    parent: ''
+_core:
+  default_config_hash: hjC271ZF6B5XYgF6-F5Ak73sJiZWJRmSLsgk8S7Vo-8
+name: 'CTools block field test'
+type: ctools_block_field_test
+description: 'A content type used for the ctools_block field tests.'
+help: ''
+new_revision: false
+preview_mode: 0
+display_submitted: false

+ 20 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/modules/ctools_block_field_test/ctools_block_field_test.info.yml

@@ -0,0 +1,20 @@
+name: 'Chaos tools blocks test'
+type: module
+description: 'Support module for Chaos tools blocks tests.'
+# core: 8.x
+package: Testing
+# version: 8.0.1
+dependencies:
+  - image
+  - menu_ui
+  - node
+  - path
+  - text
+  - user
+features: true
+
+# Information added by Drupal.org packaging script on 2017-04-28
+version: '8.x-3.0'
+core: '8.x'
+project: 'ctools'
+datestamp: 1493401747

+ 132 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_block/tests/src/Functional/EntityFieldBlockTest.php

@@ -0,0 +1,132 @@
+<?php
+
+namespace Drupal\Tests\ctools_block\Functional;
+
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests the entity field block.
+ *
+ * @group ctools_block
+ */
+class EntityFieldBlockTest extends BrowserTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['block', 'ctools_block', 'ctools_block_field_test'];
+
+  /**
+   * Tests using the node body field in a block.
+   */
+  public function testBodyField() {
+    $block = $this->drupalPlaceBlock('entity_field:node:body', [
+      'formatter' => [
+        'type' => 'text_default',
+      ],
+      'context_mapping' => [
+        'entity' => '@node.node_route_context:node',
+      ],
+    ]);
+    $node = $this->drupalCreateNode(['type' => 'ctools_block_field_test']);
+    $this->drupalGet('node/' . $node->id());
+    $assert = $this->assertSession();
+    $assert->pageTextContains($block->label());
+    $assert->pageTextContains($node->body->value);
+
+    $node->set('body', NULL)->save();
+    $this->getSession()->reload();
+    // The block should not appear if there is no value in the field.
+    $assert->pageTextNotContains($block->label());
+  }
+
+  /**
+   * Tests that empty image fields will still render their default value.
+   */
+  public function testEmptyImageField() {
+    $source = \Drupal::moduleHandler()->getModule('image')->getPath() . '/sample.png';
+    file_unmanaged_copy($source, 'public://sample.png');
+
+    /** @var \Drupal\file\FileInterface $file */
+    $file = \Drupal::entityTypeManager()
+      ->getStorage('file')
+      ->create([
+        'uri' => 'public://sample.png',
+      ]);
+    $file->save();
+
+    /** @var \Drupal\field\FieldConfigInterface $field */
+    $field = \Drupal::entityTypeManager()
+      ->getStorage('field_config')
+      ->load('node.ctools_block_field_test.field_image');
+    $settings = $field->getSettings();
+    $settings['default_image']['uuid'] = $file->uuid();
+    $field->set('settings', $settings)->save();
+
+    $this->drupalPlaceBlock('entity_field:node:field_image', [
+      'formatter' => [
+        'type' => 'image_image',
+      ],
+      'context_mapping' => [
+        'entity' => '@node.node_route_context:node',
+      ],
+    ]);
+
+    $node = $this->drupalCreateNode(['type' => 'ctools_block_field_test']);
+    $this->drupalGet('node/' . $node->id());
+
+    $url = $file->getFileUri();
+    $url = file_create_url($url);
+    $url = file_url_transform_relative($url);
+    $this->assertSession()->responseContains('src="' . $url . '"');
+  }
+
+  /**
+   * Tests using the node uid base field in a block.
+   */
+  public function testNodeBaseFields() {
+    $block = $this->drupalPlaceBlock('entity_field:node:title', [
+      'formatter' => [
+        'type' => 'string',
+      ],
+      'context_mapping' => [
+        'entity' => '@node.node_route_context:node',
+      ],
+    ]);
+    $node = $this->drupalCreateNode(['type' => 'ctools_block_field_test', 'uid' => 1]);
+    $this->drupalGet('node/' . $node->id());
+    $assert = $this->assertSession();
+    $assert->pageTextContains($block->label());
+    $assert->pageTextContains($node->getTitle());
+  }
+
+  /**
+   * Tests that we are setting the render cache metadata correctly.
+   */
+  public function testRenderCache() {
+    $this->drupalPlaceBlock('entity_field:node:body', [
+      'formatter' => [
+        'type' => 'text_default',
+      ],
+      'context_mapping' => [
+        'entity' => '@node.node_route_context:node',
+      ],
+    ]);
+    $a = $this->drupalCreateNode(['type' => 'ctools_block_field_test']);
+    $b = $this->drupalCreateNode(['type' => 'ctools_block_field_test']);
+
+    $assert = $this->assertSession();
+    $this->drupalGet('node/' . $a->id());
+    $assert->pageTextContains($a->body->value);
+    $this->drupalGet('node/' . $b->id());
+    $assert->pageTextNotContains($a->body->value);
+    $assert->pageTextContains($b->body->value);
+
+    $text = 'This is my text. Are you not entertained?';
+    $a->body->value = $text;
+    $a->save();
+    $this->drupalGet('node/' . $a->id());
+    $assert->pageTextContains($text);
+  }
+
+}

+ 15 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_views/ctools_views.info.yml

@@ -0,0 +1,15 @@
+name: Chaos tools Views
+type: module
+description: 'A set of improvements to the core Views code that allows for greater control over Blocks.'
+package: Chaos tool suite (Experimental)
+# version: 3.x
+# core: 8.x
+dependencies:
+    - block
+    - views
+
+# Information added by Drupal.org packaging script on 2017-04-28
+version: '8.x-3.0'
+core: '8.x'
+project: 'ctools'
+datestamp: 1493401747

+ 104 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_views/ctools_views.module

@@ -0,0 +1,104 @@
+<?php
+
+use Drupal\views\Plugin\views\display\Block as CoreBlock;
+use Drupal\ctools_views\Plugin\Display\Block;
+
+/**
+ * Implements hook_views_plugins_display_alter().
+ */
+function ctools_views_views_plugins_display_alter(&$displays) {
+  if (!empty($displays['block']['class']) && $displays['block']['class'] == CoreBlock::class) {
+    $displays['block']['class'] = Block::class;
+  }
+}
+
+/**
+ * Implements hook_config_schema_info_alter().
+ */
+function ctools_views_config_schema_info_alter(&$definitions) {
+  // Add to the views block plugin schema.
+  $definitions['views_block']['mapping']['pager'] = [
+    'type' => 'string',
+    'label' => 'Pager type'
+  ];
+  $definitions['views_block']['mapping']['fields'] = [
+    'type' => 'sequence',
+    'label' => 'Fields settings',
+    'sequence' => [
+      [
+        'type' => 'mapping',
+        'label' => 'Field settings',
+        'mapping' => [
+          'hide' => [
+            'type' => 'boolean',
+            'label' => 'Hide field',
+          ],
+          'weight' => [
+            'type' => 'integer',
+            'label' => 'Field weight',
+          ],
+        ],
+      ],
+    ],
+  ];
+  $definitions['views_block']['mapping']['filter'] = [
+    'type' => 'sequence',
+    'label' => 'Filters settings',
+    'sequence' => [
+      [
+        'type' => 'mapping',
+        'label' => 'Filter settings',
+        'mapping' => [
+          'type' => [
+            'type' => 'string',
+            'label' => 'Plugin id',
+          ],
+          'disable' => [
+            'type' => 'boolean',
+            'label' => 'Disable filter',
+          ],
+        ],
+      ],
+    ],
+  ];
+  $definitions['views_block']['mapping']['sort'] = [
+    'type' => 'sequence',
+    'label' => 'Sort settings',
+    'sequence' => [
+      [
+        'type' => 'string',
+        'label' => 'Sort order value',
+      ],
+    ],
+  ];
+  $definitions['views_block']['mapping']['pager_offset'] = [
+    'type' => 'integer',
+    'label' => 'Pager offset'
+  ];
+
+  // Add to the views block display plugin schema.
+  $definitions['views.display.block']['mapping']['allow']['mapping']['offset'] = [
+    'type' => 'string',
+    'label' => 'Pager offset',
+  ];
+  $definitions['views.display.block']['mapping']['allow']['mapping']['pager'] = [
+    'type' => 'string',
+    'label' => 'Pager type',
+  ];
+  $definitions['views.display.block']['mapping']['allow']['mapping']['hide_fields'] = [
+    'type' => 'string',
+    'label' => 'Hide fields',
+  ];
+  $definitions['views.display.block']['mapping']['allow']['mapping']['sort_fields'] = [
+    'type' => 'string',
+    'label' => 'Sort fields',
+  ];
+  $definitions['views.display.block']['mapping']['allow']['mapping']['disable_filters'] = [
+    'type' => 'string',
+    'label' => 'Disable filters',
+  ];
+  $definitions['views.display.block']['mapping']['allow']['mapping']['configure_sorts'] = [
+    'type' => 'string',
+    'label' => 'Configure sorts',
+  ];
+}

+ 438 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_views/src/Plugin/Display/Block.php

@@ -0,0 +1,438 @@
+<?php
+
+namespace Drupal\ctools_views\Plugin\Display;
+
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\views\Plugin\Block\ViewsBlock;
+use Drupal\views\Plugin\views\display\Block as CoreBlock;
+use Drupal\views\Plugin\views\filter\InOperator;
+
+/**
+ * Provides a Block display plugin that allows for greater control over Views
+ * block settings.
+ */
+class Block extends CoreBlock {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function optionsSummary(&$categories, &$options) {
+    parent::optionsSummary($categories, $options);
+    $filtered_allow = array_filter($this->getOption('allow'));
+    $filter_options = [
+      'items_per_page' => $this->t('Items per page'),
+      'offset' => $this->t('Pager offset'),
+      'pager' => $this->t('Pager type'),
+      'hide_fields' => $this->t('Hide fields'),
+      'sort_fields' => $this->t('Reorder fields'),
+      'disable_filters' => $this->t('Disable filters'),
+      'configure_sorts' => $this->t('Configure sorts')
+    ];
+    $filter_intersect = array_intersect_key($filter_options, $filtered_allow);
+
+    $options['allow'] = array(
+      'category' => 'block',
+      'title' => $this->t('Allow settings'),
+      'value' => empty($filtered_allow) ? $this->t('None') : implode(', ', $filter_intersect),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
+    parent::buildOptionsForm($form, $form_state);
+    $options = $form['allow']['#options'];
+    $options['offset'] = $this->t('Pager offset');
+    $options['pager'] = $this->t('Pager type');
+    $options['hide_fields'] = $this->t('Hide fields');
+    $options['sort_fields'] = $this->t('Reorder fields');
+    $options['disable_filters'] = $this->t('Disable filters');
+    $options['configure_sorts'] = $this->t('Configure sorts');
+    $form['allow']['#options'] = $options;
+    // Update the items_per_page if set.
+    $defaults = array_filter($form['allow']['#default_value']);
+    if (isset($defaults['items_per_page'])) {
+      $defaults['items_per_page'] = 'items_per_page';
+    }
+    $form['allow']['#default_value'] = $defaults;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockForm(ViewsBlock $block, array &$form, FormStateInterface $form_state) {
+    $form = parent::blockForm($block, $form, $form_state);
+
+    $allow_settings = array_filter($this->getOption('allow'));
+    $block_configuration = $block->getConfiguration();
+
+    // Modify "Items per page" block settings form.
+    if (!empty($allow_settings['items_per_page'])) {
+      // Items per page
+      $form['override']['items_per_page']['#type'] = 'number';
+      unset($form['override']['items_per_page']['#options']);
+    }
+
+    // Provide "Pager offset" block settings form.
+    if (!empty($allow_settings['offset'])) {
+      $form['override']['pager_offset'] = [
+        '#type' => 'number',
+        '#title' => $this->t('Pager offset'),
+        '#default_value' => isset($block_configuration['pager_offset']) ? $block_configuration['pager_offset'] : 0,
+        '#description' => $this->t('For example, set this to 3 and the first 3 items will not be displayed.'),
+      ];
+    }
+
+    // Provide "Pager type" block settings form.
+    if (!empty($allow_settings['pager'])) {
+      $pager_options = [
+        'view' => $this->t('Inherit from view'),
+        'some' => $this->t('Display a specified number of items'),
+        'none' => $this->t('Display all items')
+      ];
+      $form['override']['pager'] = [
+        '#type' => 'radios',
+        '#title' => $this->t('Pager'),
+        '#options' => $pager_options,
+        '#default_value' => isset($block_configuration['pager']) ? $block_configuration['pager'] : 'view'
+      ];
+    }
+
+    // Provide "Hide fields" / "Reorder fields" block settings form.
+    if (!empty($allow_settings['hide_fields']) || !empty($allow_settings['sort_fields'])) {
+      // Set up the configuration table for hiding / sorting fields.
+      $fields = $this->getHandlers('field');
+      $header = [];
+      if (!empty($allow_settings['hide_fields'])) {
+        $header['hide'] = $this->t('Hide');
+      }
+      $header['label'] = $this->t('Label');
+      if (!empty($allow_settings['sort_fields'])) {
+        $header['weight'] = $this->t('Weight');
+      }
+      $form['override']['order_fields'] = [
+        '#type' => 'table',
+        '#header' => $header,
+        '#rows' => array(),
+      ];
+      if (!empty($allow_settings['sort_fields'])) {
+        $form['override']['order_fields']['#tabledrag'] = [
+          [
+            'action' => 'order',
+            'relationship' => 'sibling',
+            'group' => 'field-weight',
+          ]
+        ];
+        $form['override']['order_fields']['#attributes'] = ['id' => 'order-fields'];
+      }
+
+      // Sort available field plugins by their currently configured weight.
+      $sorted_fields = [];
+      if (!empty($allow_settings['sort_fields']) && isset($block_configuration['fields'])) {
+        uasort($block_configuration['fields'], '\Drupal\ctools_views\Plugin\Display\Block::sortFieldsByWeight');
+        foreach (array_keys($block_configuration['fields']) as $field_name) {
+          if (!empty($fields[$field_name])) {
+            $sorted_fields[$field_name] = $fields[$field_name];
+            unset($fields[$field_name]);
+          }
+        }
+        if (!empty($fields)) {
+          foreach ($fields as $field_name => $field_info) {
+            $sorted_fields[$field_name] = $field_info;
+          }
+        }
+      }
+      else {
+        $sorted_fields = $fields;
+      }
+
+      // Add each field to the configuration table.
+      foreach ($sorted_fields as $field_name => $plugin) {
+        $field_label = $plugin->adminLabel();
+        if (!empty($plugin->options['label'])) {
+          $field_label .= ' (' . $plugin->options['label'] . ')';
+        }
+       if (!empty($allow_settings['sort_fields'])) {
+          $form['override']['order_fields'][$field_name]['#attributes']['class'][] = 'draggable';
+        }
+        $form['override']['order_fields'][$field_name]['#weight'] = !empty($block_configuration['fields'][$field_name]['weight']) ? $block_configuration['fields'][$field_name]['weight'] : '';
+        if (!empty($allow_settings['hide_fields'])) {
+          $form['override']['order_fields'][$field_name]['hide'] = [
+            '#type' => 'checkbox',
+            '#default_value' => !empty($block_configuration['fields'][$field_name]['hide']) ? $block_configuration['fields'][$field_name]['hide'] : 0,
+          ];
+        }
+        $form['override']['order_fields'][$field_name]['label'] = [
+          '#markup' => $field_label,
+        ];
+        if (!empty($allow_settings['sort_fields'])) {
+          $form['override']['order_fields'][$field_name]['weight'] = [
+            '#type' => 'weight',
+            '#title' => $this->t('Weight for @title', ['@title' => $field_label]),
+            '#title_display' => 'invisible',
+            '#delta' => 50,
+            '#default_value' => !empty($block_configuration['fields'][$field_name]['weight']) ? $block_configuration['fields'][$field_name]['weight'] : 0,
+            '#attributes' => ['class' => ['field-weight']],
+          ];
+        }
+      }
+    }
+
+    // Provide "Configure filters" / "Disable filters" block settings form.
+    if (!empty($allow_settings['disable_filters'])) {
+      $items = [];
+      foreach ((array) $this->getOption('filters') as $filter_name => $item) {
+        $item['value'] = isset($block_configuration["filter"][$filter_name]['value']) ? $block_configuration["filter"][$filter_name]['value'] : '';
+        $items[$filter_name] = $item;
+      }
+      $this->setOption('filters', $items);
+      $filters = $this->getHandlers('filter');
+
+      // Add a settings form for each exposed filter to configure or hide it.
+      foreach ($filters as $filter_name => $plugin) {
+        if ($plugin->isExposed() && $exposed_info = $plugin->exposedInfo()) {
+          $form['override']['filters'][$filter_name] = [
+            '#type' => 'details',
+            '#title' => $exposed_info['label'],
+          ];
+          $form['override']['filters'][$filter_name]['plugin'] = [
+            '#type' => 'value',
+            '#value' => $plugin,
+          ];
+          // Render "Disable filters" settings form.
+          if (!empty($allow_settings['disable_filters'])) {
+            $form['override']['filters'][$filter_name]['disable'] = [
+              '#type' => 'checkbox',
+              '#title' => $this->t('Disable'),
+              '#default_value' => !empty($block_configuration['filter'][$filter_name]['disable']) ? $block_configuration['filter'][$filter_name]['disable'] : 0,
+            ];
+          }
+        }
+      }
+    }
+
+    // Provide "Configure sorts" block settings form.
+    if (!empty($allow_settings['configure_sorts'])) {
+      $sorts = $this->getHandlers('sort');
+      $options = array(
+        'ASC' => $this->t('Sort ascending'),
+        'DESC' => $this->t('Sort descending'),
+      );
+      foreach ($sorts as $sort_name => $plugin) {
+        $form['override']['sort'][$sort_name] = [
+          '#type' => 'details',
+          '#title' => $plugin->adminLabel(),
+        ];
+        $form['override']['sort'][$sort_name]['plugin'] = [
+          '#type' => 'value',
+          '#value' => $plugin,
+        ];
+        $form['override']['sort'][$sort_name]['order'] = array(
+          '#title' => $this->t('Order'),
+          '#type' => 'radios',
+          '#options' => $options,
+          '#default_value' => $plugin->options['order']
+        );
+
+        // Set default values for sorts for this block.
+        if (!empty($block_configuration["sort"][$sort_name])) {
+          $form['override']['sort'][$sort_name]['order']['#default_value'] = $block_configuration["sort"][$sort_name];
+        }
+      }
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockSubmit(ViewsBlock $block, $form, FormStateInterface $form_state) {
+    // Set default value for items_per_page if left blank.
+    if (empty($form_state->getValue(array('override', 'items_per_page')))) {
+      $form_state->setValue(array('override', 'items_per_page'), "none");
+    }
+
+    parent::blockSubmit($block, $form, $form_state);
+    $configuration = $block->getConfiguration();
+    $allow_settings = array_filter($this->getOption('allow'));
+
+    // Save "Pager type" settings to block configuration.
+    if (!empty($allow_settings['pager'])) {
+      if ($pager = $form_state->getValue(['override', 'pager'])) {
+        $configuration['pager'] = $pager;
+      }
+    }
+
+    // Save "Pager offset" settings to block configuration.
+    if (!empty($allow_settings['offset'])) {
+      $configuration['pager_offset'] = $form_state->getValue(['override', 'pager_offset']);
+    }
+
+    // Save "Hide fields" / "Reorder fields" settings to block configuration.
+    if (!empty($allow_settings['hide_fields']) || !empty($allow_settings['sort_fields'])) {
+      if ($fields = array_filter($form_state->getValue(['override', 'order_fields']))) {
+        uasort($fields, '\Drupal\ctools_views\Plugin\Display\Block::sortFieldsByWeight');
+        $configuration['fields'] = $fields;
+      }
+    }
+
+    // Save "Configure filters" / "Disable filters" settings to block
+    // configuration.
+    unset($configuration['filter']);
+    if (!empty($allow_settings['disable_filters'])) {
+      if ($filters = $form_state->getValue(['override', 'filters'])) {
+        foreach ($filters as $filter_name => $filter) {
+          /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $plugin */
+          $plugin = $form_state->getValue(['override', 'filters', $filter_name, 'plugin']);
+          $configuration["filter"][$filter_name]['type'] = $plugin->getPluginId();
+
+          // Check if we want to disable this filter.
+          if (!empty($allow_settings['disable_filters'])) {
+            $disable = $form_state->getValue(['override', 'filters', $filter_name, 'disable']);
+            // If marked disabled, we don't really care about other stuff.
+            if ($disable) {
+              $configuration["filter"][$filter_name]['disable'] = $disable;
+              continue;
+            }
+          }
+        }
+      }
+    }
+
+    // Save "Configure sorts" settings to block configuration.
+    if (!empty($allow_settings['configure_sorts'])) {
+      $sorts = $form_state->getValue(['override', 'sort']);
+      foreach ($sorts as $sort_name => $sort) {
+        $plugin = $sort['plugin'];
+        // Check if we want to override the default sort order
+        if ($plugin->options['order'] != $sort['order']) {
+          $configuration['sort'][$sort_name] = $sort['order'];
+        }
+      }
+    }
+
+    $block->setConfiguration($configuration);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preBlockBuild(ViewsBlock $block) {
+    parent::preBlockBuild($block);
+
+    $allow_settings = array_filter($this->getOption('allow'));
+    $config = $block->getConfiguration();
+    list(, $display_id) = explode('-', $block->getDerivativeId(), 2);
+
+    // Change pager offset settings based on block configuration.
+    if (!empty($allow_settings['offset'])) {
+      $this->view->setOffset($config['pager_offset']);
+    }
+
+    // Change pager style settings based on block configuration.
+    if (!empty($allow_settings['pager'])) {
+      $pager = $this->view->display_handler->getOption('pager');
+      if (!empty($config['pager']) && $config['pager'] != 'view') {
+        $pager['type'] = $config['pager'];
+      }
+      $this->view->display_handler->setOption('pager', $pager);
+    }
+
+    // Change fields output based on block configuration.
+    if (!empty($allow_settings['hide_fields']) || !empty($allow_settings['sort_fields'])) {
+      if (!empty($config['fields']) && $this->view->getStyle()->usesFields()) {
+        $fields = $this->view->getHandlers('field');
+        uasort($config['fields'], '\Drupal\ctools_views\Plugin\Display\Block::sortFieldsByWeight');
+        $iterate_fields = !empty($allow_settings['sort_fields']) ? $config['fields'] : $fields;
+        foreach (array_keys($iterate_fields) as $field_name) {
+          // Remove each field in sequence and re-add them to sort
+          // appropriately or hide if disabled.
+          $this->view->removeHandler($display_id, 'field', $field_name);
+          if (empty($allow_settings['hide_fields']) || (!empty($allow_settings['hide_fields']) && empty($config['fields'][$field_name]['hide']))) {
+            $this->view->addHandler($display_id, 'field', $fields[$field_name]['table'], $fields[$field_name]['field'], $fields[$field_name], $field_name);
+          }
+        }
+      }
+    }
+
+    // Change filters output based on block configuration.
+    if (!empty($allow_settings['disable_filters'])) {
+      $filters = $this->view->getHandlers('filter', $display_id);
+      foreach ($filters as $filter_name => $filter) {
+        // If we allow disabled filters and this filter is disabled, disable it
+        // and continue.
+        if (!empty($allow_settings['disable_filters']) && !empty($config["filter"][$filter_name]['disable'])) {
+          $this->view->removeHandler($display_id, 'filter', $filter_name);
+          continue;
+        }
+      }
+    }
+
+    // Change sorts based on block configuration.
+    if (!empty($allow_settings['configure_sorts'])) {
+      $sorts = $this->view->getHandlers('sort', $display_id);
+      foreach ($sorts as $sort_name => $sort) {
+        if (!empty($config["sort"][$sort_name])) {
+          $sort['order'] = $config["sort"][$sort_name];
+          $this->view->setHandler($display_id, 'sort', $sort_name, $sort);
+        }
+      }
+    }
+  }
+
+  protected function getFilterOptionsValue(array $filter, array $config) {
+    $plugin_definition = \Drupal::service('plugin.manager.views.filter')->getDefinition($config['type']);
+    if (is_subclass_of($plugin_definition['class'], '\Drupal\views\Plugin\views\filter\InOperator')) {
+      return array_values($config['value']);
+    }
+    return $config['value'][$filter['expose']['identifier']];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function usesExposed() {
+    $filters = $this->getHandlers('filter');
+    foreach ($filters as $filter_name => $filter) {
+      if ($filter->isExposed() && !empty($filter->exposedInfo())) {
+        return TRUE;
+      }
+    }
+    return FALSE;
+  }
+
+  /**
+   * Exposed widgets typically only work with ajax in Drupal core, however
+   * #2605218 totally breaks the rest of the functionality in this display and
+   * in Core's Block display as well, so we allow non-ajax block views to use
+   * exposed filters and manually set the #action to the current request uri.
+   */
+  public function elementPreRender(array $element) {
+    /** @var \Drupal\views\ViewExecutable $view */
+    $view = $element['#view'];
+    if (!empty($view->exposed_widgets['#action']) && !$view->ajaxEnabled()) {
+      $view->exposed_widgets['#action'] = \Drupal::request()->getRequestUri();
+    }
+    return parent::elementPreRender($element);
+  }
+
+  /**
+   * Sort field config array by weight.
+   *
+   * @param $a
+   * @param $b
+   * @return int
+   */
+  public static function sortFieldsByWeight($a, $b) {
+    $a_weight = isset($a['weight']) ? $a['weight'] : 0;
+    $b_weight = isset($b['weight']) ? $b['weight'] : 0;
+    if ($a_weight == $b_weight) {
+      return 0;
+    }
+    return ($a_weight < $b_weight) ? -1 : 1;
+  }
+
+}

+ 350 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_views/src/Tests/CToolsViewsBasicViewBlockTest.php

@@ -0,0 +1,350 @@
+<?php
+
+namespace Drupal\ctools_views\Tests;
+
+use Drupal\views_ui\Tests\UITestBase;
+use Drupal\views\Tests\ViewTestData;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Tests the ctools_views block display plugin
+ * overriding settings from a basic View.
+ *
+ * @group ctools_views
+ * @see \Drupal\ctools_views\Plugin\Display\Block
+ */
+class CToolsViewsBasicViewBlockTest extends UITestBase {
+
+  use StringTranslationTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('ctools_views', 'ctools_views_test_views');
+
+  /**
+   * Views used by this test.
+   *
+   * @var array
+   */
+  public static $testViews = array('ctools_views_test_view');
+
+  /**
+   * The block storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $storage;
+
+  /**
+   * @inheritdoc
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    ViewTestData::createTestViews(get_class($this), array('ctools_views_test_views'));
+    $this->storage = $this->container->get('entity.manager')->getStorage('block');
+  }
+
+  /**
+   * Test ctools_views "items_per_page" configuration.
+   */
+  public function testItemsPerPage() {
+    $default_theme = $this->config('system.theme')->get('default');
+
+    // Get the "Configure block" form for our Views block.
+    $this->drupalGet('admin/structure/block/add/views_block:ctools_views_test_view-block_pager/' . $default_theme);
+    $this->assertFieldByXPath('//input[@type="number" and @name="settings[override][items_per_page]"]', NULL, 'items_per_page setting is a number field');
+    // Add block to sidebar_first region with default settings.
+    $edit = array();
+    $edit['region'] = 'sidebar_first';
+    $edit['settings[override][items_per_page]'] = 0;
+    $this->drupalPostForm('admin/structure/block/add/views_block:ctools_views_test_view-block_pager/' . $default_theme, $edit, $this->t('Save block'));
+
+    // Assert items per page default settings.
+    $this->drupalGet('<front>');
+    $result = $this->xpath('//div[contains(@class, "region-sidebar-first")]/div[contains(@class, "block-views")]/h2');
+    $this->assertEqual((string) $result[0], 'CTools Views Pager Block');
+    $this->assertRaw('Showing 3 records on page 1');
+    $this->assertEqual(3, count($this->xpath('//div[contains(@class, "view-display-id-block_pager")]//table/tbody/tr')));
+
+    // Override items per page settings.
+    $edit = array();
+    $edit['region'] = 'sidebar_first';
+    $edit['settings[override][items_per_page]'] = 2;
+    $this->drupalPostForm('admin/structure/block/manage/views_block__ctools_views_test_view_block_pager', $edit, $this->t('Save block'));
+
+    $block = $this->storage->load('views_block__ctools_views_test_view_block_pager');
+    $config = $block->getPlugin()->getConfiguration();
+    $this->assertEqual(2, $config['items_per_page'], "'Items per page' is properly saved.");
+
+    // Assert items per page overridden settings.
+    $this->drupalGet('<front>');
+    $result = $this->xpath('//div[contains(@class, "region-sidebar-first")]/div[contains(@class, "block-views")]/h2');
+    $this->assertEqual((string) $result[0], 'CTools Views Pager Block');
+    $this->assertRaw('Showing 2 records on page 1');
+    $this->assertEqual(2, count($this->xpath('//div[contains(@class, "view-display-id-block_pager")]//table/tbody/tr')));
+    $this->assertEqual([1, 2], $this->xpath('//div[contains(@class, "view-display-id-block_pager")]//table//tr//td[contains(@class, "views-field-id")]'));
+  }
+
+  /**
+   * Test ctools_views "offset" configuration.
+   */
+  public function testOffset() {
+    $default_theme = $this->config('system.theme')->get('default');
+
+    // Get the "Configure block" form for our Views block.
+    $this->drupalGet('admin/structure/block/add/views_block:ctools_views_test_view-block_pager/' . $default_theme);
+    $this->assertFieldByXPath('//input[@type="number" and @name="settings[override][pager_offset]"]', NULL, 'items_per_page setting is a number field');
+    // Add block to sidebar_first region with default settings.
+    $edit = array();
+    $edit['region'] = 'sidebar_first';
+    $edit['settings[override][items_per_page]'] = 0;
+    $this->drupalPostForm('admin/structure/block/add/views_block:ctools_views_test_view-block_pager/' . $default_theme, $edit, $this->t('Save block'));
+
+    // Assert pager offset default settings.
+    $this->drupalGet('<front>');
+    $this->assertEqual([1, 2, 3], $this->xpath('//div[contains(@class, "view-display-id-block_pager")]//table//tr//td[contains(@class, "views-field-id")]'));
+
+    // Override pager offset settings.
+    $edit = array();
+    $edit['region'] = 'sidebar_first';
+    $edit['settings[override][items_per_page]'] = 0;
+    $edit['settings[override][pager_offset]'] = 1;
+    $this->drupalPostForm('admin/structure/block/manage/views_block__ctools_views_test_view_block_pager', $edit, $this->t('Save block'));
+
+    $block = $this->storage->load('views_block__ctools_views_test_view_block_pager');
+    $config = $block->getPlugin()->getConfiguration();
+    $this->assertEqual(1, $config['pager_offset'], "'Pager offset' is properly saved.");
+
+    // Assert pager offset overridden settings.
+    $this->drupalGet('<front>');
+    $this->assertEqual([2, 3, 4], $this->xpath('//div[contains(@class, "view-display-id-block_pager")]//table//tr//td[contains(@class, "views-field-id")]'));
+  }
+
+  /**
+   * Test ctools_views "pager" configuration.
+   */
+  public function testPager() {
+    $default_theme = $this->config('system.theme')->get('default');
+
+    // Get the "Configure block" form for our Views block.
+    $this->drupalGet('admin/structure/block/add/views_block:ctools_views_test_view-block_pager/' . $default_theme);
+    $this->assertFieldById('edit-settings-override-pager-view', 'view');
+    $this->assertFieldById('edit-settings-override-pager-some');
+    $this->assertFieldById('edit-settings-override-pager-none');
+
+    // Add block to sidebar_first region with default settings.
+    $edit = array();
+    $edit['region'] = 'sidebar_first';
+    $edit['settings[override][items_per_page]'] = 0;
+    $this->drupalPostForm('admin/structure/block/add/views_block:ctools_views_test_view-block_pager/' . $default_theme, $edit, $this->t('Save block'));
+
+    // Assert pager default settings.
+    $this->drupalGet('<front>');
+    $this->assertText('Page 1');
+    $this->assertText('Next ›');
+
+    // Override pager settings to 'some'.
+    $edit = array();
+    $edit['region'] = 'sidebar_first';
+    $edit['settings[override][items_per_page]'] = 0;
+    $edit['settings[override][pager]'] = 'some';
+    $this->drupalPostForm('admin/structure/block/manage/views_block__ctools_views_test_view_block_pager', $edit, $this->t('Save block'));
+
+    $block = $this->storage->load('views_block__ctools_views_test_view_block_pager');
+    $config = $block->getPlugin()->getConfiguration();
+    $this->assertEqual('some', $config['pager'], "'Pager' setting is properly saved.");
+
+    // Assert pager overridden settings to 'some', showing no pager.
+    $this->drupalGet('<front>');
+    $this->assertEqual(3, count($this->xpath('//div[contains(@class, "view-display-id-block_pager")]//table/tbody/tr')));
+    $this->assertNoText('Page 1');
+    $this->assertNoText('Next ›');
+
+    // Override pager settings to 'none'.
+    $edit = array();
+    $edit['region'] = 'sidebar_first';
+    $edit['settings[override][items_per_page]'] = 0;
+    $edit['settings[override][pager]'] = 'none';
+    $this->drupalPostForm('admin/structure/block/manage/views_block__ctools_views_test_view_block_pager', $edit, $this->t('Save block'));
+
+    $block = $this->storage->load('views_block__ctools_views_test_view_block_pager');
+    $config = $block->getPlugin()->getConfiguration();
+    $this->assertEqual('none', $config['pager'], "'Pager' setting is properly saved.");
+
+    // Assert pager overridden settings to 'some', showing no pager.
+    $this->drupalGet('<front>');
+    $this->assertEqual(5, count($this->xpath('//div[contains(@class, "view-display-id-block_pager")]//table/tbody/tr')));
+    $this->assertNoText('Page 1');
+    $this->assertNoText('Next ›');
+  }
+
+  /**
+   * Test ctools_views 'hide_fields' configuration.
+   */
+  public function testHideFields() {
+    $default_theme = $this->config('system.theme')->get('default');
+
+    // Get the "Configure block" form for our Views block.
+    $this->drupalGet('admin/structure/block/add/views_block:ctools_views_test_view-block_fields/' . $default_theme);
+    $this->assertFieldById('edit-settings-override-order-fields-id-hide');
+
+    // Add block to sidebar_first region with default settings.
+    $edit = array();
+    $edit['region'] = 'sidebar_first';
+    $this->drupalPostForm('admin/structure/block/add/views_block:ctools_views_test_view-block_fields/' . $default_theme, $edit, $this->t('Save block'));
+
+    // Assert hide_fields default settings.
+    $this->drupalGet('<front>');
+    $this->assertEqual(5, count($this->xpath('//div[contains(@class, "view-display-id-block_fields")]//table//td[contains(@class, "views-field-id")]')));
+
+    // Override hide_fields settings.
+    $edit = array();
+    $edit['region'] = 'sidebar_first';
+    $edit['settings[override][order_fields][id][hide]'] = 1;
+    $this->drupalPostForm('admin/structure/block/manage/views_block__ctools_views_test_view_block_fields', $edit, $this->t('Save block'));
+
+    $block = $this->storage->load('views_block__ctools_views_test_view_block_fields');
+    $config = $block->getPlugin()->getConfiguration();
+    $this->assertEqual(1, $config['fields']['id']['hide'], "'hide_fields' setting is properly saved.");
+    $this->assertEqual(0, $config['fields']['name']['hide'], "'hide_fields' setting is properly saved.");
+
+    // Assert hide_fields overridden settings.
+    $this->drupalGet('<front>');
+    $this->assertEqual(0, count($this->xpath('//div[contains(@class, "view-display-id-block_fields")]//table//td[contains(@class, "views-field-id")]')));
+  }
+
+  /**
+   * Test ctools_views 'sort_fields' configuration.
+   */
+  public function testOrderFields() {
+    $default_theme = $this->config('system.theme')->get('default');
+
+    // Get the "Configure block" form for our Views block.
+    $this->drupalGet('admin/structure/block/add/views_block:ctools_views_test_view-block_fields/' . $default_theme);
+    $this->assertFieldById('edit-settings-override-order-fields-id-weight', 0);
+
+    // Add block to sidebar_first region with default settings.
+    $edit = array();
+    $edit['region'] = 'sidebar_first';
+    $this->drupalPostForm('admin/structure/block/add/views_block:ctools_views_test_view-block_fields/' . $default_theme, $edit, $this->t('Save block'));
+
+    // Assert sort_fields default settings.
+    $this->drupalGet('<front>');
+    // Check that the td with class "views-field-id" is the first td in the first tr element.
+    $this->assertEqual(0, count($this->xpath('count(//div[contains(@class, "view-display-id-block_fields")]//table//tr[1]//td[contains(@class, "views-field-id")]/preceding-sibling::td)')));
+
+    // Override sort_fields settings.
+    $edit = array();
+    $edit['region'] = 'sidebar_first';
+    $edit['settings[override][order_fields][name][weight]'] = -50;
+    $edit['settings[override][order_fields][age][weight]'] = -49;
+    $edit['settings[override][order_fields][job][weight]'] = -48;
+    $edit['settings[override][order_fields][created][weight]'] = -47;
+    $edit['settings[override][order_fields][id][weight]'] = -46;
+    $edit['settings[override][order_fields][name_1][weight]'] = -45;
+    $this->drupalPostForm('admin/structure/block/manage/views_block__ctools_views_test_view_block_fields', $edit, $this->t('Save block'));
+
+    $block = $this->storage->load('views_block__ctools_views_test_view_block_fields');
+    $config = $block->getPlugin()->getConfiguration();
+    $this->assertEqual(-46, $config['fields']['id']['weight'], "'sort_fields' setting is properly saved.");
+    $this->assertEqual(-50, $config['fields']['name']['weight'], "'sort_fields' setting is properly saved.");
+
+    // Assert sort_fields overridden settings.
+    $this->drupalGet('<front>');
+
+    // Check that the td with class "views-field-id" is the 5th td in the first tr element.
+    $this->assertEqual(4, count($this->xpath('//div[contains(@class, "view-display-id-block_fields")]//table//tr[1]//td[contains(@class, "views-field-id")]/preceding-sibling::td')));
+
+    // Check that duplicate fields in the View produce expected outpu
+    $name1_element = $this->xpath('//div[contains(@class, "view-display-id-block_fields")]//table//tr[1]/td[contains(@class, "views-field-name")]/text()');
+    $name1 = (string) $name1_element[0];
+    $this->assertEqual("John", trim($name1));
+    $name2_element = $this->xpath('//div[contains(@class, "view-display-id-block_fields")]//table//tr[1]/td[contains(@class, "views-field-name-1")]/text()');
+    $name2 = (string) $name2_element[0];
+    $this->assertEqual("John", trim($name2));
+  }
+
+  /**
+   * Test ctools_views 'disable_filters' configuration.
+   */
+  public function testDisableFilters() {
+    $default_theme = $this->config('system.theme')->get('default');
+
+    // Get the "Configure block" form for our Views block.
+    $this->drupalGet('admin/structure/block/add/views_block:ctools_views_test_view-block_filter/' . $default_theme);
+    $this->assertFieldById('edit-settings-override-filters-status-disable');
+    $this->assertFieldById('edit-settings-override-filters-job-disable');
+
+    // Add block to sidebar_first region with default settings.
+    $edit = array();
+    $edit['region'] = 'sidebar_first';
+    $this->drupalPostForm('admin/structure/block/add/views_block:ctools_views_test_view-block_filter/' . $default_theme, $edit, $this->t('Save block'));
+
+    // Assert disable_filters default settings.
+    $this->drupalGet('<front>');
+    // Check that the default settings show both filters
+    $this->assertFieldByXPath('//select[@name="status"]');
+    $this->assertFieldByXPath('//input[@name="job"]');
+
+    // Override disable_filters settings.
+    $edit = array();
+    $edit['region'] = 'sidebar_first';
+    $edit['settings[override][filters][status][disable]'] = 1;
+    $edit['settings[override][filters][job][disable]'] = 1;
+    $this->drupalPostForm('admin/structure/block/manage/views_block__ctools_views_test_view_block_filter', $edit, $this->t('Save block'));
+
+    $block = $this->storage->load('views_block__ctools_views_test_view_block_filter');
+    $config = $block->getPlugin()->getConfiguration();
+    $this->assertEqual(1, $config['filter']['status']['disable'], "'disable_filters' setting is properly saved.");
+    $this->assertEqual(1, $config['filter']['job']['disable'], "'disable_filters' setting is properly saved.");
+
+    // Assert disable_filters overridden settings.
+    $this->drupalGet('<front>');
+    $this->assertNoFieldByXPath('//select[@name="status"]');
+    $this->assertNoFieldByXPath('//input[@name="job"]');
+  }
+
+  /**
+   * Test ctools_views 'configure_sorts' configuration.
+   */
+  public function testConfigureSorts() {
+    $default_theme = $this->config('system.theme')->get('default');
+
+    // Get the "Configure block" form for our Views block.
+    $this->drupalGet('admin/structure/block/add/views_block:ctools_views_test_view-block_sort/' . $default_theme);
+    $this->assertFieldByXPath('//input[@name="settings[override][sort][id][order]"]');
+
+    // Add block to sidebar_first region with default settings.
+    $edit = array();
+    $edit['region'] = 'sidebar_first';
+    $this->drupalPostForm('admin/structure/block/add/views_block:ctools_views_test_view-block_sort/' . $default_theme, $edit, $this->t('Save block'));
+
+    // Assert configure_sorts default settings.
+    $this->drupalGet('<front>');
+    // Check that the results are sorted ASC
+    $element = $this->xpath('//div[contains(@class, "view-display-id-block_sort")]//table//tr[1]/td[1]/text()');
+    $value = (string) $element[0];
+    $this->assertEqual("1", trim($value));
+
+    // Override configure_sorts settings.
+    $edit = array();
+    $edit['region'] = 'sidebar_first';
+    $edit['settings[override][sort][id][order]'] = "DESC";
+    $this->drupalPostForm('admin/structure/block/manage/views_block__ctools_views_test_view_block_sort', $edit, $this->t('Save block'));
+
+    $block = $this->storage->load('views_block__ctools_views_test_view_block_sort');
+    $config = $block->getPlugin()->getConfiguration();
+    $this->assertEqual("DESC", $config['sort']['id'], "'configure_sorts' setting is properly saved.");
+
+    // Assert configure_sorts overridden settings.
+    // Check that the results are sorted DESC
+    $this->drupalGet('<front>');
+    $element = $this->xpath('//div[contains(@class, "view-display-id-block_sort")]//table//tr[1]/td[1]/text()');
+    $value = (string) $element[0];
+    $this->assertEqual("5", trim($value));
+  }
+}

+ 20 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_views/tests/modules/ctools_views_test_views/ctools_views_test_views.info.yml

@@ -0,0 +1,20 @@
+name: 'CTools Views test views'
+type: module
+description: 'Provides default views for CTools Views tests.'
+package: Testing
+# core: 8.x
+dependencies:
+    - views
+    - block
+    - entity_test
+    - ctools_views
+    - text
+    - user
+    - node
+    - taxonomy
+
+# Information added by Drupal.org packaging script on 2017-04-28
+version: '8.x-3.0'
+core: '8.x'
+project: 'ctools'
+datestamp: 1493401747

+ 543 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_views/tests/modules/ctools_views_test_views/test_views/views.view.ctools_views_entity_test.yml

@@ -0,0 +1,543 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - node.type.ctools_views
+    - taxonomy.vocabulary.tags
+  module:
+    - datetime
+    - node
+    - options
+    - taxonomy
+    - user
+id: ctools_views_entity_test
+label: 'CTools Views Entity Test View'
+module: views
+description: ''
+tag: ''
+base_table: node_field_data
+base_field: nid
+core: 8.x
+display:
+  default:
+    display_plugin: default
+    id: default
+    display_title: Master
+    position: 0
+    display_options:
+      access:
+        type: perm
+        options:
+          perm: 'access content'
+      cache:
+        type: tag
+        options: {  }
+      query:
+        type: views_query
+        options:
+          disable_sql_rewrite: false
+          distinct: false
+          replica: false
+          query_comment: ''
+          query_tags: {  }
+      exposed_form:
+        type: basic
+        options:
+          submit_button: Apply
+          reset_button: false
+          reset_button_label: Reset
+          exposed_sorts_label: 'Sort by'
+          expose_sort_order: true
+          sort_asc_label: Asc
+          sort_desc_label: Desc
+      pager:
+        type: none
+        options:
+          offset: 0
+      style:
+        type: table
+      row:
+        type: fields
+      fields:
+        title:
+          id: title
+          table: node_field_data
+          field: title
+          entity_type: node
+          entity_field: title
+          alter:
+            alter_text: false
+            make_link: false
+            absolute: false
+            trim: false
+            word_boundary: false
+            ellipsis: false
+            strip_tags: false
+            html: false
+          hide_empty: false
+          empty_zero: false
+          settings:
+            link_to_entity: true
+          plugin_id: field
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Title
+          exclude: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: true
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_alter_empty: true
+          click_sort_column: value
+          type: string
+          group_column: value
+          group_columns: {  }
+          group_rows: true
+          delta_limit: 0
+          delta_offset: 0
+          delta_reversed: false
+          delta_first_last: false
+          multi_type: separator
+          separator: ', '
+          field_api_classes: false
+      filters:
+        status:
+          value: true
+          table: node_field_data
+          field: status
+          plugin_id: boolean
+          entity_type: node
+          entity_field: status
+          id: status
+          expose:
+            operator: ''
+          group: 1
+        type:
+          id: type
+          table: node_field_data
+          field: type
+          value:
+            ctools_views: ctools_views
+          entity_type: node
+          entity_field: type
+          plugin_id: bundle
+      sorts:
+        created:
+          id: created
+          table: node_field_data
+          field: created
+          order: DESC
+          entity_type: node
+          entity_field: created
+          plugin_id: date
+          relationship: none
+          group_type: group
+          admin_label: ''
+          exposed: false
+          expose:
+            label: ''
+          granularity: second
+      title: 'CTools Views Entity Test View'
+      header: {  }
+      footer: {  }
+      empty: {  }
+      relationships: {  }
+      arguments: {  }
+      display_extenders: {  }
+    cache_metadata:
+      max-age: 0
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - 'user.node_grants:view'
+        - user.permissions
+      tags: {  }
+  block_filter_date:
+    display_plugin: block
+    id: block_filter_date
+    display_title: 'Date filter'
+    position: 4
+    display_options:
+      display_extenders: {  }
+      display_description: ''
+      title: 'Date filter'
+      defaults:
+        title: false
+        filters: false
+        filter_groups: false
+      filters:
+        status:
+          value: true
+          table: node_field_data
+          field: status
+          plugin_id: boolean
+          entity_type: node
+          entity_field: status
+          id: status
+          expose:
+            operator: ''
+          group: 1
+        type:
+          id: type
+          table: node_field_data
+          field: type
+          value:
+            ctools_views: ctools_views
+          entity_type: node
+          entity_field: type
+          plugin_id: bundle
+          group: 1
+        field_ctools_views_date_value:
+          id: field_ctools_views_date_value
+          table: node__field_ctools_views_date
+          field: field_ctools_views_date_value
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: between
+          group: 1
+          exposed: true
+          expose:
+            operator_id: field_ctools_views_date_value_op
+            label: 'CTools Views Date (field_ctools_views_date)'
+            description: ''
+            use_operator: false
+            operator: field_ctools_views_date_value_op
+            identifier: field_ctools_views_date_value
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator1: '0'
+              administrator: '0'
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          plugin_id: datetime
+      filter_groups:
+        operator: AND
+        groups:
+          1: AND
+      allow:
+        items_per_page: false
+        offset: '0'
+        pager: '0'
+        hide_fields: '0'
+        sort_fields: '0'
+        disable_filters: '0'
+    cache_metadata:
+      max-age: 0
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url
+        - 'user.node_grants:view'
+        - user.permissions
+      tags: {  }
+  block_filter_list:
+    display_plugin: block
+    id: block_filter_list
+    display_title: 'List filter'
+    position: 3
+    display_options:
+      display_extenders: {  }
+      display_description: ''
+      title: 'List filter'
+      defaults:
+        title: false
+        filters: false
+        filter_groups: false
+      filters:
+        status:
+          value: true
+          table: node_field_data
+          field: status
+          plugin_id: boolean
+          entity_type: node
+          entity_field: status
+          id: status
+          expose:
+            operator: ''
+          group: 1
+        type:
+          id: type
+          table: node_field_data
+          field: type
+          value:
+            ctools_views: ctools_views
+          entity_type: node
+          entity_field: type
+          plugin_id: bundle
+        field_ctools_views_list_value:
+          id: field_ctools_views_list_value
+          table: node__field_ctools_views_list
+          field: field_ctools_views_list_value
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: or
+          value: {  }
+          group: 1
+          exposed: true
+          expose:
+            operator_id: field_ctools_views_list_value_op
+            label: 'Ctools Views List (field_ctools_views_list)'
+            description: ''
+            use_operator: false
+            operator: field_ctools_views_list_value_op
+            identifier: field_ctools_views_list_value
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator1: '0'
+              administrator: '0'
+            reduce: false
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          reduce_duplicates: false
+          plugin_id: list_field
+      filter_groups:
+        operator: AND
+        groups:
+          1: AND
+      allow:
+        items_per_page: false
+        offset: '0'
+        pager: '0'
+        hide_fields: '0'
+        sort_fields: '0'
+        disable_filters: '0'
+    cache_metadata:
+      max-age: 0
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url
+        - 'user.node_grants:view'
+        - user.permissions
+      tags: {  }
+  block_filter_tax:
+    display_plugin: block
+    id: block_filter_tax
+    display_title: 'Taxonomy filter'
+    position: 2
+    display_options:
+      display_extenders: {  }
+      display_description: ''
+      title: 'Taxonomy filter'
+      defaults:
+        title: false
+        filters: false
+        filter_groups: false
+      filters:
+        status:
+          value: true
+          table: node_field_data
+          field: status
+          plugin_id: boolean
+          entity_type: node
+          entity_field: status
+          id: status
+          expose:
+            operator: ''
+          group: 1
+        type:
+          id: type
+          table: node_field_data
+          field: type
+          value:
+            ctools_views: ctools_views
+          entity_type: node
+          entity_field: type
+          plugin_id: bundle
+          group: 1
+        field_ctools_views_tags_target_id:
+          id: field_ctools_views_tags_target_id
+          table: node__field_ctools_views_tags
+          field: field_ctools_views_tags_target_id
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: or
+          value: {  }
+          group: 1
+          exposed: true
+          expose:
+            operator_id: field_ctools_views_tags_target_id_op
+            label: 'Tags (field_ctools_views_tags)'
+            description: ''
+            use_operator: false
+            operator: field_ctools_views_tags_target_id_op
+            identifier: field_ctools_views_tags_target_id
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator1: '0'
+              administrator: '0'
+            reduce: false
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          reduce_duplicates: false
+          type: select
+          limit: true
+          vid: tags
+          hierarchy: false
+          error_message: true
+          plugin_id: taxonomy_index_tid
+      filter_groups:
+        operator: AND
+        groups:
+          1: AND
+      allow:
+        items_per_page: false
+        offset: '0'
+        pager: '0'
+        hide_fields: '0'
+        sort_fields: '0'
+        disable_filters: '0'
+    cache_metadata:
+      max-age: 0
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url
+        - user
+        - 'user.node_grants:view'
+        - user.permissions
+      tags: {  }
+  block_filter_text:
+    display_plugin: block
+    id: block_filter_text
+    display_title: 'Textfield filter'
+    position: 1
+    display_options:
+      display_extenders: {  }
+      display_description: ''
+      title: 'Textfield filter'
+      defaults:
+        title: false
+        filters: false
+        filter_groups: false
+      filters:
+        status:
+          value: true
+          table: node_field_data
+          field: status
+          plugin_id: boolean
+          entity_type: node
+          entity_field: status
+          id: status
+          expose:
+            operator: ''
+          group: 1
+        type:
+          id: type
+          table: node_field_data
+          field: type
+          value:
+            ctools_views: ctools_views
+          entity_type: node
+          entity_field: type
+          plugin_id: bundle
+        field_ctools_views_text_value:
+          id: field_ctools_views_text_value
+          table: node__field_ctools_views_text
+          field: field_ctools_views_text_value
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: '='
+          value: ''
+          group: 1
+          exposed: true
+          expose:
+            operator_id: field_ctools_views_text_value_op
+            label: 'Text (field_ctools_views_text)'
+            description: ''
+            use_operator: false
+            operator: field_ctools_views_text_value_op
+            identifier: field_ctools_views_text_value
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator1: '0'
+              administrator: '0'
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          plugin_id: string
+      filter_groups:
+        operator: AND
+        groups:
+          1: AND
+      allow:
+        items_per_page: false
+        offset: '0'
+        pager: '0'
+        hide_fields: '0'
+        sort_fields: '0'
+        disable_filters: '0'
+    cache_metadata:
+      max-age: 0
+      contexts:
+        - 'languages:language_content'
+        - 'languages:language_interface'
+        - url
+        - 'user.node_grants:view'
+        - user.permissions
+      tags: {  }

+ 949 - 0
sites/all/modules/contrib/dev/ctools/modules/ctools_views/tests/modules/ctools_views_test_views/test_views/views.view.ctools_views_test_view.yml

@@ -0,0 +1,949 @@
+langcode: en
+status: true
+dependencies: {  }
+id: ctools_views_test_view
+label: 'CTools Views Test View'
+module: views
+description: ''
+tag: ''
+base_table: views_test_data
+base_field: id
+core: 8.x
+display:
+  default:
+    display_plugin: default
+    id: default
+    display_title: Master
+    position: 0
+    display_options:
+      access:
+        type: none
+        options: {  }
+      cache:
+        type: tag
+        options: {  }
+      query:
+        type: views_query
+        options:
+          disable_sql_rewrite: false
+          distinct: false
+          replica: false
+          query_comment: ''
+          query_tags: {  }
+      exposed_form:
+        type: basic
+        options:
+          submit_button: Apply
+          reset_button: false
+          reset_button_label: Reset
+          exposed_sorts_label: 'Sort by'
+          expose_sort_order: true
+          sort_asc_label: Asc
+          sort_desc_label: Desc
+      pager:
+        type: none
+        options:
+          offset: 0
+      style:
+        type: table
+        options:
+          grouping: {  }
+          row_class: ''
+          default_row_class: true
+          override: true
+          sticky: false
+          caption: ''
+          summary: ''
+          description: ''
+          columns:
+            id: id
+            age: age
+            created: created
+            id_1: id_1
+            job: job
+            name: name
+          info:
+            id:
+              sortable: false
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            age:
+              sortable: false
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            created:
+              sortable: false
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            id_1:
+              sortable: false
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            job:
+              sortable: false
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+            name:
+              sortable: false
+              default_sort_order: asc
+              align: ''
+              separator: ''
+              empty_column: false
+              responsive: ''
+          default: '-1'
+          empty_table: false
+      row:
+        type: fields
+      fields:
+        id:
+          id: id
+          table: views_test_data
+          field: id
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: ID
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          set_precision: false
+          precision: 0
+          decimal: .
+          separator: ''
+          format_plural: false
+          format_plural_string: "1\x03@count"
+          prefix: ''
+          suffix: ''
+          entity_type: null
+          entity_field: null
+          plugin_id: numeric
+        name:
+          id: name
+          table: views_test_data
+          field: name
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Name
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          plugin_id: standard
+        age:
+          id: age
+          table: views_test_data
+          field: age
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Age
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          set_precision: false
+          precision: 0
+          decimal: .
+          separator: ''
+          format_plural: false
+          format_plural_string: "1\x03@count"
+          prefix: ''
+          suffix: ''
+          plugin_id: numeric
+        job:
+          id: job
+          table: views_test_data
+          field: job
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Job
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          plugin_id: standard
+        created:
+          id: created
+          table: views_test_data
+          field: created
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Created
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          date_format: fallback
+          custom_date_format: ''
+          timezone: ''
+          plugin_id: date
+      filters: {  }
+      sorts:
+        id:
+          id: id
+          table: views_test_data
+          field: id
+          relationship: none
+          group_type: group
+          admin_label: ''
+          order: ASC
+          exposed: false
+          expose:
+            label: ''
+          plugin_id: standard
+      title: 'CTools Views Test View'
+      header: {  }
+      footer: {  }
+      empty: {  }
+      relationships: {  }
+      arguments: {  }
+      display_extenders: {  }
+      use_ajax: false
+      filter_groups:
+        operator: AND
+        groups: {  }
+    cache_metadata:
+      max-age: 0
+      contexts:
+        - 'languages:language_interface'
+      tags: {  }
+  block_fields:
+    display_plugin: block
+    id: block_fields
+    display_title: 'CTools Views Fields Block'
+    position: 2
+    display_options:
+      display_extenders: {  }
+      block_category: 'CTools Views'
+      allow:
+        hide_fields: hide_fields
+        sort_fields: sort_fields
+        items_per_page: false
+        offset: '0'
+        pager: '0'
+        disable_filters: '0'
+      block_description: 'CTools Views Fields Block'
+      display_description: ''
+      pager:
+        type: none
+        options:
+          offset: 0
+      defaults:
+        pager: false
+        title: false
+        fields: false
+      title: 'CTools Views Fields Block'
+      fields:
+        id:
+          id: id
+          table: views_test_data
+          field: id
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: ID
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          set_precision: false
+          precision: 0
+          decimal: .
+          separator: ''
+          format_plural: false
+          format_plural_string: "1\x03@count"
+          prefix: ''
+          suffix: ''
+          entity_type: null
+          entity_field: null
+          plugin_id: numeric
+        name:
+          id: name
+          table: views_test_data
+          field: name
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Name
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          plugin_id: standard
+        age:
+          id: age
+          table: views_test_data
+          field: age
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Age
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          set_precision: false
+          precision: 0
+          decimal: .
+          separator: ''
+          format_plural: false
+          format_plural_string: "1\x03@count"
+          prefix: ''
+          suffix: ''
+          plugin_id: numeric
+        job:
+          id: job
+          table: views_test_data
+          field: job
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Job
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          plugin_id: standard
+        created:
+          id: created
+          table: views_test_data
+          field: created
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: Created
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          date_format: fallback
+          custom_date_format: ''
+          timezone: ''
+          plugin_id: date
+        name_1:
+          id: name_1
+          table: views_test_data
+          field: name
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: '2nd name field'
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: false
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: 0
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: false
+            trim: false
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          plugin_id: standard
+    cache_metadata:
+      max-age: 0
+      contexts:
+        - 'languages:language_interface'
+      tags: {  }
+  block_filter:
+    display_plugin: block
+    id: block_filter
+    display_title: 'CTools Views Filter Block'
+    position: 3
+    display_options:
+      display_extenders: {  }
+      display_description: ''
+      block_category: 'CTools Views'
+      block_description: 'CTools Views Filter Block'
+      allow:
+        disable_filters: disable_filters
+        items_per_page: false
+        offset: '0'
+        pager: '0'
+        hide_fields: '0'
+        sort_fields: '0'
+      filters:
+        status:
+          id: status
+          table: views_test_data
+          field: status
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: '='
+          value: true
+          group: 1
+          exposed: true
+          expose:
+            operator_id: ''
+            label: Status
+            description: ''
+            use_operator: false
+            operator: status_op
+            identifier: status
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator1: '0'
+              administrator: '0'
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          plugin_id: boolean
+        job:
+          id: job
+          table: views_test_data
+          field: job
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: '='
+          value: ''
+          group: 1
+          exposed: true
+          expose:
+            operator_id: job_op
+            label: Job
+            description: ''
+            use_operator: false
+            operator: job_op
+            identifier: job
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+              anonymous: '0'
+              administrator1: '0'
+              administrator: '0'
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          plugin_id: string
+      defaults:
+        filters: false
+        filter_groups: false
+        title: false
+      filter_groups:
+        operator: AND
+        groups:
+          1: AND
+      title: 'CTools Views Filter Block'
+    cache_metadata:
+      max-age: 0
+      contexts:
+        - 'languages:language_interface'
+        - url
+      tags: {  }
+  block_pager:
+    display_plugin: block
+    id: block_pager
+    display_title: 'CTools Views Pager Block'
+    position: 1
+    display_options:
+      display_extenders: {  }
+      block_description: 'CTools Views Pager Block'
+      block_category: 'CTools Views'
+      allow:
+        items_per_page: true
+        offset: offset
+        pager: pager
+        hide_fields: '0'
+        sort_fields: '0'
+        disable_filters: '0'
+      display_description: ''
+      header:
+        result:
+          id: result
+          table: views
+          field: result
+          relationship: none
+          group_type: group
+          admin_label: ''
+          empty: false
+          content: "Displaying @start - @end of @total\nShowing @current_record_count records on page @current_page"
+          plugin_id: result
+      defaults:
+        header: false
+        pager: false
+        title: false
+      pager:
+        type: mini
+        options:
+          items_per_page: 3
+          offset: 0
+          id: 0
+          total_pages: null
+          tags:
+            previous: '‹ Previous'
+            next: 'Next ›'
+          expose:
+            items_per_page: false
+            items_per_page_label: 'Items per page'
+            items_per_page_options: '5, 10, 25, 50'
+            items_per_page_options_all: false
+            items_per_page_options_all_label: '- All -'
+            offset: false
+            offset_label: Offset
+      title: 'CTools Views Pager Block'
+    cache_metadata:
+      max-age: 0
+      contexts:
+        - 'languages:language_interface'
+        - url.query_args
+      tags: {  }
+  block_sort:
+    display_plugin: block
+    id: block_sort
+    display_title: 'CTools Views Sort Block'
+    position: 4
+    display_options:
+      display_extenders: {  }
+      display_description: ''
+      title: 'CTools Views Sort Block'
+      defaults:
+        title: false
+      block_description: 'CTools Views Sort Block'
+      block_category: 'CTools Views'
+      allow:
+        configure_sorts: configure_sorts
+        items_per_page: false
+        offset: '0'
+        pager: '0'
+        hide_fields: '0'
+        sort_fields: '0'
+        disable_filters: '0'
+    cache_metadata:
+      max-age: 0
+      contexts:
+        - 'languages:language_interface'
+      tags: {  }

+ 9 - 0
sites/all/modules/contrib/dev/ctools/src/Access/AccessInterface.php

@@ -0,0 +1,9 @@
+<?php
+
+namespace Drupal\ctools\Access;
+
+use Drupal\Core\Session\AccountInterface;
+
+interface AccessInterface {
+  public function access(AccountInterface $account);
+}

+ 51 - 0
sites/all/modules/contrib/dev/ctools/src/Access/TempstoreAccess.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\ctools\Access;
+
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Routing\Access\AccessInterface as CoreAccessInterface;
+use Drupal\Core\Routing\RouteMatch;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\ctools\Access\AccessInterface as CToolsAccessInterface;
+use Drupal\user\SharedTempStoreFactory;
+use Symfony\Component\Routing\Route;
+
+class TempstoreAccess implements CoreAccessInterface {
+
+  /**
+   * The shared tempstore factory.
+   *
+   * @var \Drupal\user\SharedTempStoreFactory
+   */
+  protected $tempstore;
+
+  public function __construct(SharedTempStoreFactory $tempstore) {
+    $this->tempstore = $tempstore;
+  }
+
+  protected function getTempstore() {
+    return $this->tempstore;
+  }
+
+  public function access(Route $route, RouteMatch $match, AccountInterface $account) {
+    $tempstore_id = $match->getParameter('tempstore_id') ? $match->getParameter('tempstore_id') : $route->getDefault('tempstore_id');
+    $id = $match->getParameter($route->getRequirement('_ctools_access'));
+    if ($tempstore_id && $id) {
+      $cached_values = $this->getTempstore()->get($tempstore_id)->get($id);
+      if (!empty($cached_values['access']) && ($cached_values['access'] instanceof CToolsAccessInterface)) {
+        $access = $cached_values['access']->access($account);
+      }
+      else {
+        $access = AccessResult::allowed();
+      }
+    }
+    else {
+      $access = AccessResult::forbidden();
+    }
+    // The different wizards will have different tempstore ids and adding this
+    // cache context allows us to nuance the access per wizard.
+    $access->addCacheContexts(['url.query_args:tempstore_id']);
+    return $access;
+  }
+}

+ 23 - 0
sites/all/modules/contrib/dev/ctools/src/Ajax/OpenModalWizardCommand.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\ctools\Ajax;
+
+use Drupal\Core\Ajax\OpenModalDialogCommand;
+
+class OpenModalWizardCommand extends OpenModalDialogCommand {
+
+  public function __construct($object, $tempstore_id, array $parameters = array(), array $dialog_options = array(), $settings = NULL) {
+    // Instantiate the wizard class properly.
+    $parameters += [
+      'tempstore_id' => $tempstore_id,
+      'machine_name' => NULL,
+      'step' => NULL,
+    ];
+    $form = \Drupal::service('ctools.wizard.factory')->getWizardForm($object, $parameters, TRUE);
+    $title = isset($form['#title']) ? $form['#title'] : '';
+    $content = $form;
+
+    parent::__construct($title, $content, $dialog_options, $settings);
+  }
+
+}

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů