Переглянути джерело

added configured xmlsitemap

Bachir Soussi Chiadmi 5 роки тому
батько
коміт
94e7719baa
76 змінених файлів з 8378 додано та 5 видалено
  1. 339 0
      sites/all/modules/contrib/admin/simple_sitemap/LICENSE.txt
  2. 249 0
      sites/all/modules/contrib/admin/simple_sitemap/README.md
  3. 32 0
      sites/all/modules/contrib/admin/simple_sitemap/composer.json
  4. 5 0
      sites/all/modules/contrib/admin/simple_sitemap/config/install/simple_sitemap.custom_links.default.yml
  5. 14 0
      sites/all/modules/contrib/admin/simple_sitemap/config/install/simple_sitemap.settings.yml
  6. 4 0
      sites/all/modules/contrib/admin/simple_sitemap/config/install/simple_sitemap.variants.default_hreflang.yml
  7. 94 0
      sites/all/modules/contrib/admin/simple_sitemap/config/schema/simple_sitemap.schema.yml
  8. 7 0
      sites/all/modules/contrib/admin/simple_sitemap/drush.services.yml
  9. 27 0
      sites/all/modules/contrib/admin/simple_sitemap/js/simple_sitemap.fieldsetSummaries.js
  10. 26 0
      sites/all/modules/contrib/admin/simple_sitemap/js/simple_sitemap.form.js
  11. 53 0
      sites/all/modules/contrib/admin/simple_sitemap/js/simple_sitemap.sitemapEntities.js
  12. 142 0
      sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.api.php
  13. 45 0
      sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.drush.inc
  14. 12 0
      sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.info.yml
  15. 678 0
      sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.install
  16. 19 0
      sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.libraries.yml
  17. 5 0
      sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.links.menu.yml
  18. 25 0
      sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.links.task.yml
  19. 236 0
      sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.module
  20. 4 0
      sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.permissions.yml
  21. 51 0
      sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.routing.yml
  22. 100 0
      sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.services.yml
  23. 50 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Annotation/SitemapGenerator.php
  24. 57 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Annotation/SitemapType.php
  25. 50 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Annotation/UrlGenerator.php
  26. 59 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Commands/SimplesitemapCommands.php
  27. 70 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Controller/SimplesitemapController.php
  28. 164 0
      sites/all/modules/contrib/admin/simple_sitemap/src/EntityHelper.php
  29. 539 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Form/FormHelper.php
  30. 72 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Form/SimplesitemapCustomLinksForm.php
  31. 192 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Form/SimplesitemapEntitiesForm.php
  32. 62 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Form/SimplesitemapFormBase.php
  33. 409 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Form/SimplesitemapSettingsForm.php
  34. 36 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Form/SimplesitemapVariantsForm.php
  35. 100 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Logger.php
  36. 25 0
      sites/all/modules/contrib/admin/simple_sitemap/src/PathProcessor/PathProcessorSitemapVariant.php
  37. 36 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SimplesitemapPluginBase.php
  38. 184 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapGenerator/DefaultSitemapGenerator.php
  39. 303 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapGenerator/SitemapGeneratorBase.php
  40. 22 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapGenerator/SitemapGeneratorInterface.php
  41. 37 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapGenerator/SitemapGeneratorManager.php
  42. 13 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapGenerator/SitemapWriter.php
  43. 23 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapType/DefaultHreflangSitemapType.php
  44. 12 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapType/SitemapTypeBase.php
  45. 10 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapType/SitemapTypeInterface.php
  46. 37 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapType/SitemapTypeManager.php
  47. 82 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/UrlGenerator/ArbitraryUrlGenerator.php
  48. 143 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/UrlGenerator/CustomUrlGenerator.php
  49. 198 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/UrlGenerator/EntityMenuLinkContentUrlGenerator.php
  50. 181 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/UrlGenerator/EntityUrlGenerator.php
  51. 218 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/UrlGenerator/EntityUrlGeneratorBase.php
  52. 113 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/UrlGenerator/UrlGeneratorBase.php
  53. 18 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/UrlGenerator/UrlGeneratorInterface.php
  54. 37 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/UrlGenerator/UrlGeneratorManager.php
  55. 102 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Queue/BatchTrait.php
  56. 388 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Queue/QueueWorker.php
  57. 82 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Queue/SimplesitemapQueue.php
  58. 948 0
      sites/all/modules/contrib/admin/simple_sitemap/src/Simplesitemap.php
  59. 316 0
      sites/all/modules/contrib/admin/simple_sitemap/src/SimplesitemapManager.php
  60. 68 0
      sites/all/modules/contrib/admin/simple_sitemap/src/SimplesitemapSettings.php
  61. 566 0
      sites/all/modules/contrib/admin/simple_sitemap/tests/src/Functional/SimplesitemapTest.php
  62. 123 0
      sites/all/modules/contrib/admin/simple_sitemap/tests/src/Functional/SimplesitemapTestBase.php
  63. 1 0
      sites/default/config/sync/core.extension.yml
  64. 1 1
      sites/default/config/sync/language/en/views.view.redirect.yml
  65. 4 0
      sites/default/config/sync/simple_sitemap.bundle_settings.default.menu_link_content.footer.yml
  66. 4 0
      sites/default/config/sync/simple_sitemap.bundle_settings.default.menu_link_content.productions.yml
  67. 4 0
      sites/default/config/sync/simple_sitemap.bundle_settings.default.node.enregistrement.yml
  68. 4 0
      sites/default/config/sync/simple_sitemap.bundle_settings.default.node.evenement.yml
  69. 4 0
      sites/default/config/sync/simple_sitemap.bundle_settings.default.node.page.yml
  70. 4 0
      sites/default/config/sync/simple_sitemap.bundle_settings.default.node.static.yml
  71. 4 0
      sites/default/config/sync/simple_sitemap.bundle_settings.default.taxonomy_term.entrees.yml
  72. 8 0
      sites/default/config/sync/simple_sitemap.custom_links.default.yml
  73. 17 0
      sites/default/config/sync/simple_sitemap.settings.yml
  74. 7 0
      sites/default/config/sync/simple_sitemap.variants.default_hreflang.yml
  75. 3 3
      sites/default/config/sync/views.view.content_translations.yml
  76. 1 1
      sites/default/config/sync/views.view.redirect.yml

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

+ 249 - 0
sites/all/modules/contrib/admin/simple_sitemap/README.md

@@ -0,0 +1,249 @@
+## CONTENTS OF THIS FILE ##
+
+ * Introduction
+ * Installation
+ * Configuration
+ * Usage
+ * Extending the module
+ * How Can You Contribute?
+ * Maintainers
+
+## INTRODUCTION ##
+
+Author and maintainer: Pawel Ginalski (gbyte.co)
+ * Drupal: https://www.drupal.org/u/gbyte.co
+ * Personal: https://gbyte.co/
+
+The module generates multilingual XML sitemaps which adhere to Google's new
+hreflang standard. Out of the box the sitemaps index most of Drupal's
+content entity types including:
+
+ * nodes
+ * taxonomy terms
+ * menu links
+ * users
+ * ...
+
+Contributed entity types like commerce products or media entities can be indexed
+as well. On top of that custom links can be added to the sitemap.
+
+To learn about XML sitemaps, see https://en.wikipedia.org/wiki/Sitemaps.
+
+The module also provides an API allowing to create any type of sitemap (not
+necessary an XML one) holding links to a local or remote source.
+
+## INSTALLATION ##
+
+See https://www.drupal.org/documentation/install/modules-themes/modules-8
+for instructions on how to install or update Drupal modules.
+
+## CONFIGURATION ##
+
+### PERMISSIONS ###
+
+The module permission 'administer sitemap settings' can be configured under
+/admin/people/permissions.
+
+### ENTITIES ###
+
+Initially only the home page is indexed in the sitemap. To include content into
+the sitemap, visit /admin/config/search/simplesitemap/entities to enable support
+for entity types of your choosing. Entity types which feature bundles can then
+be configured on a per-bundle basis, e.g.
+
+ * /admin/structure/types/manage/[content type] for nodes
+ * /admin/structure/taxonomy/manage/[taxonomy vocabulary] for taxonomy terms
+ * /admin/structure/menu/manage/[menu] for menu items
+ * ...
+
+When including an entity type or bundle into the sitemap, the priority setting
+can be set which will set the 'priority' parameter for all entities of that
+type. Same goes for the 'changefreq' setting. All Images referenced by the
+entities can be indexed as well. See https://en.wikipedia.org/wiki/Sitemaps to
+learn more about these parameters.
+
+Inclusion settings of bundles can be overridden on a per-entity
+basis. Just head over to a bundle instance edit form (e.g. node/1/edit) to
+override its sitemap settings.
+
+If you wish for the sitemap to reflect the new configuration instantly, check
+'Regenerate sitemaps after clicking save'. This setting only appears if a change
+in the settings has been detected.
+
+As the sitemap is accessible to anonymous users, bear in mind that only links
+will be included which are accessible to anonymous users. There are no access
+checks for links added through the module's hooks (see below).
+
+### CUSTOM LINKS ###
+
+To include custom links into the sitemap, visit
+/admin/config/search/simplesitemap/custom.
+
+### SETTINGS ###
+
+The settings page can be found under admin/config/search/simplesitemap.
+Here the module can be configured and the sitemaps can be manually regenerated.
+
+#### VARIANTS ####
+
+It is possible to have several sitemap instances of different sitemap types with
+specific links accessible under certain URLs. These sitemap variants can be
+configured under admin/config/search/simplesitemap/variants.
+
+## USAGE ## 
+
+The sitemaps are accessible to the whole world under [variant name]/sitemap.xml.
+In addition to that, the default sitemap is accessible under /sitemap.xml.
+
+If the cron generation is turned on, the sitemaps will be regenerated according
+to the 'Sitemap generation interval' setting.
+
+A manual generation is possible on admin/config/search/simplesitemap. This is
+also the place that shows the overall and variant specific generation status.
+
+The sitemap can be also generated via drush: Use the command
+'drush simple-sitemap:generate' ('ssg'), or 'drush simple-sitemap:rebuild-queue'
+('ssr').
+
+Generation of hundreds of thousands of links can take time. Each variant gets
+published as soon as all of its links have been generated. The previous version
+of the sitemap variant is accessible during the generation process.
+
+## EXTENDING THE MODULE ##
+
+### API ###
+
+There are API methods for altering stored inclusion settings, status queries and
+programmatic sitemap generation. These include:
+
+ * getSetting
+ * saveSetting
+ * setVariants
+ * getSitemap
+ * removeSitemap
+ * generateSitemap
+ * rebuildQueue
+ * enableEntityType
+ * disableEntityType
+ * setBundleSettings
+ * getBundleSettings
+ * removeBundleSettings
+ * supplementDefaultSettings
+ * setEntityInstanceSettings
+ * getEntityInstanceSettings
+ * removeEntityInstanceSettings
+ * bundleIsIndexed
+ * entityTypeIsEnabled
+ * addCustomLink
+ * getCustomLinks
+ * removeCustomLinks
+ * getSitemapManager
+    * getSitemapVariants
+    * addSitemapVariant
+    * removeSitemapVariants
+ * getQueueWorker
+    * deleteQueue
+    * rebuildQueue
+    * getInitialElementCount
+    * getQueuedElementCount
+    * getStashedResultCount
+    * getProcessedElementCount
+    * generationInProgress
+
+
+These service methods can be chained like so:
+
+```php
+$generator = \Drupal::service('simple_sitemap.generator');
+
+$generator
+  ->getSitemapManager()
+  ->addSitemapVariant('test');
+  
+$generator
+  ->saveSetting('remove_duplicates', TRUE)
+  ->enableEntityType('node')
+  ->setVariants(['default', 'test'])
+  ->setBundleSettings('node', 'page', ['index' => TRUE, 'priority' = 0.5])
+  ->removeCustomLinks()
+  ->addCustomLink('/some/view/page', ['priority' = 0.5])
+  ->generateSitemap();
+```
+
+See https://gbyte.co/projects/simple-xml-sitemap and code documentation in 
+Drupal\simple_sitemap\Simplesitemap for further details.
+
+### API HOOKS ###
+
+It is possible to hook into link generation by implementing
+`hook_simple_sitemap_links_alter(&$links){}` in a custom module and altering the
+link array shortly before it is transformed to XML.
+
+Adding arbitrary links is possible through the use of
+`hook_simple_sitemap_arbitrary_links_alter(&$arbitrary_links){}`. There are no
+checks performed on these links (i.e. if they are internal/valid/accessible)
+and parameters like priority/lastmod/changefreq have to be added manually.
+
+Altering sitemap attributes and sitemap index attributes is possible through the
+use of `hook_simple_sitemap_attributes_alter(&$attributes){}` and
+`hook_simple_sitemap_index_attributes_alter(&$index_attributes){}`.
+
+Altering URL generators is possible through
+the use of `hook_simple_sitemap_url_generators_alter(&$generators){}`.
+
+Altering sitemap generators is possible through
+the use of `hook_simple_sitemap_sitemap_generators_alter(&$generators){}`.
+
+Altering sitemap types is possible through
+the use of `hook_simple_sitemap_sitemap_types_alter(&$generators){}`.
+
+### WRITING PLUGINS ###
+
+There are three types of plugins that allow to create any type of sitemap. See
+the generator plugins included in this module and check the API docs
+(https://www.drupal.org/docs/8/api/plugin-api/plugin-api-overview) to learn how
+to implement plugins.
+
+#### SITEMAP TYPE PLUGINS ####
+
+This plugin defines a sitemap type. A sitemap type consists of a sitemap
+generator and several URL generators. This plugin is very simple, as it
+only requires some class annotation to define which sitemap/URL plugins to use.
+
+#### SITEMAP GENERATOR PLUGINS ####
+
+This plugin defines how a sitemap type is supposed to look. It handles all
+aspects of the sitemap except its links/URLs.
+
+#### URL GENERATOR PLUGINS ####
+
+This plugin defines a way of generating URLs for one or more sitemap types.
+
+Note:
+Overwriting the default EntityUrlGenerator for a single entity type is possible
+through the flag "overrides_entity_type" = "[entity_type_to_be_overwritten]" in
+the settings array of the new generator plugin's annotation. See how the
+EntityUrlGenerator is overwritten by the EntityMenuLinkContentUrlGenerator to
+facilitate a different logic for menu links.
+
+See https://gbyte.co/projects/simple-xml-sitemap for further details.
+
+## HOW CAN YOU CONTRIBUTE? ##
+
+ * Report any bugs, feature or support requests in the issue tracker; if
+   possible help out by submitting patches.
+   http://drupal.org/project/issues/simple_sitemap
+
+ * Do you know a non-English language? Help translating the module.
+   https://localize.drupal.org/translate/projects/simple_sitemap
+
+ * If you would like to say thanks and support the development of this module, a
+   donation will be much appreciated.
+   https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5AFYRSBLGSC3W
+   
+ * Feel free to contact me for paid support: https://gbyte.co/contact
+
+## MAINTAINERS ##
+
+Current maintainers:
+ * Pawel Ginalski (gbyte.co) - https://www.drupal.org/u/gbyte.co

+ 32 - 0
sites/all/modules/contrib/admin/simple_sitemap/composer.json

@@ -0,0 +1,32 @@
+{
+  "name": "drupal/simple_sitemap",
+  "description": "Creates a standard conform hreflang XML sitemap of the site content and provides a framework for developing other sitemap types.",
+  "type": "drupal-module",
+  "homepage": "https://drupal.org/project/simple_sitemap",
+  "authors": [
+    {
+      "name": "Pawel Ginalski (gbyte.co)",
+      "email": "contact@gbyte.co",
+      "homepage": "https://www.drupal.org/u/gbyte.co",
+      "role": "Maintainer"
+    }
+  ],
+  "support": {
+    "issues": "https://drupal.org/project/issues/simple_sitemap",
+    "irc": "irc://irc.freenode.org/drupal-contribute",
+    "source": "https://cgit.drupalcode.org/simple_sitemap"
+  },
+  "license": "GPL-2.0+",
+  "minimum-stability": "dev",
+  "require": {
+    "ext-xmlwriter": "*"
+  },
+  "extra": {
+    "drush": {
+      "services": {
+        "drush.services.yml": "^9"
+      }
+    }
+  }
+}
+

+ 5 - 0
sites/all/modules/contrib/admin/simple_sitemap/config/install/simple_sitemap.custom_links.default.yml

@@ -0,0 +1,5 @@
+links:
+  -
+    path: '/'
+    priority: '1.0'
+    changefreq: 'daily'

+ 14 - 0
sites/all/modules/contrib/admin/simple_sitemap/config/install/simple_sitemap.settings.yml

@@ -0,0 +1,14 @@
+max_links: 2000
+cron_generate: true
+cron_generate_interval: 0
+generate_duration: 10000
+remove_duplicates: true
+skip_untranslated: true
+base_url: ''
+default_variant: 'default'
+custom_links_include_images: false
+excluded_languages: []
+enabled_entity_types:
+  - 'node'
+  - 'taxonomy_term'
+  - 'menu_link_content'

+ 4 - 0
sites/all/modules/contrib/admin/simple_sitemap/config/install/simple_sitemap.variants.default_hreflang.yml

@@ -0,0 +1,4 @@
+variants:
+  default:
+    label: 'Default'
+    weight: 0

+ 94 - 0
sites/all/modules/contrib/admin/simple_sitemap/config/schema/simple_sitemap.schema.yml

@@ -0,0 +1,94 @@
+simple_sitemap.settings:
+  type: config_object
+  mapping:
+    max_links:
+      label: 'Max links'
+      type: integer
+    cron_generate:
+      label: 'Cron generation'
+      type: boolean
+    cron_generate_interval:
+      label: 'Cron generation interval'
+      type: integer
+    generate_duration:
+      label: 'Generation duration'
+      type: integer
+    remove_duplicates:
+      label: 'Remove duplicates'
+      type: boolean
+    skip_untranslated:
+      label: 'Skip untranslated'
+      type: boolean
+    base_url:
+      label: 'Base URL'
+      type: string
+    default_variant:
+      label: 'Default variant'
+      type: string
+    custom_links_include_images:
+      label: 'Include images of custom links'
+      type: boolean
+    excluded_languages:
+      label: 'Excluded languages'
+      type: sequence
+      sequence:
+        type: string
+    enabled_entity_types:
+      label: 'Enabled entity types'
+      type: sequence
+      sequence:
+        type: string
+
+simple_sitemap.bundle_settings.*.*.*:
+  label: 'Entity bundle settings'
+  type: config_object
+  mapping:
+    index:
+      label: 'Index'
+      type: boolean
+    priority:
+      label: 'Priority'
+      type: string
+    changefreq:
+      label: 'Change frequency'
+      type: string
+    include_images:
+      label: 'Include images'
+      type: boolean
+
+simple_sitemap.custom_links.*:
+  label: 'Custom links'
+  type: config_object
+  mapping:
+    links:
+      label: 'Custom links'
+      type: sequence
+      sequence:
+        type: mapping
+        mapping:
+          path:
+            label: 'Path'
+            type: string
+          priority:
+            label: 'Priority'
+            type: string
+          changefreq:
+            label: 'Change frequency'
+            type: string
+
+simple_sitemap.variants.*:
+  label: 'Sitemap variants'
+  type: config_object
+  mapping:
+    variants:
+      label: 'Sitemap variants'
+      type: sequence
+      sequence:
+        type: mapping
+        mapping:
+          label:
+            label: 'Variant label'
+            type: string
+          weight:
+            label: 'Weight'
+            type: integer

+ 7 - 0
sites/all/modules/contrib/admin/simple_sitemap/drush.services.yml

@@ -0,0 +1,7 @@
+services:
+  simple_sitemap.commands:
+    class: \Drupal\simple_sitemap\Commands\SimplesitemapCommands
+    arguments:
+      - '@simple_sitemap.generator'
+    tags:
+      - { name: drush.command }

+ 27 - 0
sites/all/modules/contrib/admin/simple_sitemap/js/simple_sitemap.fieldsetSummaries.js

@@ -0,0 +1,27 @@
+/**
+ * @file
+ * Attaches simple_sitemap behaviors to the entity form.
+ */
+(function($) {
+
+  "use strict";
+
+  Drupal.behaviors.simple_sitemapFieldsetSummaries = {
+    attach: function(context) {
+      $(context).find('#edit-simple-sitemap').drupalSetSummary(function(context) {
+        var vals = [];
+        if ($(context).find('#edit-simple-sitemap-index-content-1').is(':checked')) {
+          vals.push(Drupal.t('Included in sitemap'));
+          vals.push(Drupal.t('Variant') + ': ' + $('#edit-simple-sitemap-variant option:selected', context).text());
+          vals.push(Drupal.t('Priority') + ': ' + $('#edit-simple-sitemap-priority option:selected', context).text());
+          vals.push(Drupal.t('Change frequency') + ': ' + $('#edit-simple-sitemap-changefreq option:selected', context).text());
+          vals.push(Drupal.t('Include images') + ': ' + $('#edit-simple-sitemap-include-images option:selected', context).text());
+        }
+        else {
+          vals.push(Drupal.t('Excluded from sitemap'));
+        }
+        return vals.join('<br />');
+      });
+    }
+  };
+})(jQuery);

+ 26 - 0
sites/all/modules/contrib/admin/simple_sitemap/js/simple_sitemap.form.js

@@ -0,0 +1,26 @@
+/**
+ * @file
+ * Attaches simple_sitemap behaviors to the entity form.
+ */
+(function($) {
+
+  "use strict";
+
+  Drupal.behaviors.simple_sitemapForm = {
+    attach: function(context) {
+
+      // On load: Hide the 'Regenerate sitemap' field to only display it if settings have changed.
+      $('.form-item-simple-sitemap-regenerate-now').hide();
+
+      // Show 'Regenerate sitemap' field if settings have changed.
+      $("#edit-simple-sitemap-index-content"
+          + ", #edit-simple-sitemap-variant"
+          + ", #edit-simple-sitemap-priority"
+          + ", #edit-simple-sitemap-changefreq"
+          + ", #edit-simple-sitemap-include-images"
+      ).change(function() {
+        $('.form-item-simple-sitemap-regenerate-now').show();
+      });
+    }
+  };
+})(jQuery);

+ 53 - 0
sites/all/modules/contrib/admin/simple_sitemap/js/simple_sitemap.sitemapEntities.js

@@ -0,0 +1,53 @@
+/**
+ * @file
+ * Attaches simple_sitemap behaviors to the sitemap entities form.
+ */
+(function($) {
+
+  "use strict";
+
+  Drupal.behaviors.simple_sitemapSitemapEntities = {
+    attach: function(context, settings) {
+      var allEntities = settings.simple_sitemap.all_entities;
+      var atomicEntities = settings.simple_sitemap.atomic_entities;
+
+      // Hide the 'Regenerate sitemap' field to only display it if settings have changed.
+      $('.form-item-simple-sitemap-regenerate-now').hide();
+
+      $.each(allEntities, function(index, value) {
+
+        // On load: hide all warning messages.
+        $('#warning-' + value).hide();
+
+        // On change: Show or hide warning message dependent on 'enabled' checkbox.
+        var enabledId = '#edit-' + value + '-enabled';
+        $(enabledId).change(function() {
+          if ($(enabledId).is(':checked')) {
+            $('#warning-' + value).hide();
+            $('#indexed-bundles-' + value).show();
+          }
+          else {
+            $('#warning-' + value).show();
+            $('#indexed-bundles-' + value).hide();
+          }
+
+          // Show 'Regenerate sitemap' field if 'enabled' setting has changed.
+          $('.form-item-simple-sitemap-regenerate-now').show();
+        });
+      });
+
+      // todo
+      // Show 'Regenerate sitemap' field if settings have changed.
+      // $.each(atomicEntities, function(index, value) {
+      //   var variant = '.form-item-' + value + '-simple-sitemap-variant';
+      //   var priorityId = '.form-item-' + value + '-simple-sitemap-priority';
+      //   var changefreqId = '.form-item-' + value + '-simple-sitemap-changefreq';
+      //   var includeImagesId = '.form-item-' + value + '-simple-sitemap-include-images';
+      //
+      //   $(variant, priorityId, changefreqId, includeImagesId).change(function() {
+      //     $('.form-item-simple-sitemap-regenerate-now').show();
+      //   });
+      // });
+    }
+  };
+})(jQuery);

+ 142 - 0
sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.api.php

@@ -0,0 +1,142 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by the Simple XML sitemap module.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Alter the generated link data before the sitemap is saved.
+ * This hook gets invoked for every sitemap chunk generated.
+ *
+ * @param array &$links
+ *   Array containing multilingual links generated for each path to be indexed
+ *
+ * @param string|null $sitemap_variant
+ */
+function hook_simple_sitemap_links_alter(array &$links, $sitemap_variant) {
+
+  // Remove German URL for a certain path in the hreflang sitemap.
+  foreach ($links as $key => $link) {
+    if ($link['path'] === 'node/1') {
+
+      // Remove 'loc' URL if it points to a german site.
+      if ($link['langcode'] === 'de') {
+        unset($links[$key]);
+      }
+
+      // If this 'loc' URL points to a non-german site, make sure to remove
+      // its german alternate URL.
+      else {
+        if ($link['alternate_urls']['de']) {
+          unset($links[$key]['alternate_urls']['de']);
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Add arbitrary links to the sitemap.
+ *
+ * @param array &$arbitrary_links
+ * @param string|null $sitemap_variant
+ */
+function hook_simple_sitemap_arbitrary_links_alter(array &$arbitrary_links, $sitemap_variant) {
+
+  // Add an arbitrary link to all sitemap variants.
+  $arbitrary_links[] = [
+    'url' => 'http://some-arbitrary-link/',
+    'priority' => '0.5',
+
+    // An ISO8601 formatted date.
+    'lastmod' => '2012-10-12T17:40:30+02:00',
+
+    'changefreq' => 'weekly',
+    'images' => [
+      ['path' => 'http://path-to-image.png']
+    ],
+
+    // Add alternate URLs for every language of a multilingual site.
+    // Not necessary for monolingual sites.
+    'alternate_urls' => [
+      'en' => 'http://this-is-your-life.net/de/tyler',
+      'de' => 'http://this-is-your-life.net/en/tyler',
+    ]
+  ];
+
+  // Add an arbitrary link to the 'fight_club' sitemap variant only.
+  switch ($sitemap_variant) {
+    case 'fight_club':
+      $arbitrary_links[] = [
+        'url' => 'http://this-is-your-life.net/tyler',
+      ];
+      break;
+  }
+}
+
+/**
+ * Alters the sitemap attributes shortly before XML document generation.
+ * Attributes can be added, changed and removed.
+ *
+ * @param array &$attributes
+ * @param string|null $sitemap_variant
+ */
+function hook_simple_sitemap_attributes_alter(array &$attributes, $sitemap_variant) {
+
+  // Remove the xhtml attribute e.g. if no xhtml sitemap elements are present.
+  unset($attributes['xmlns:xhtml']);
+}
+
+/**
+ * Alters attributes of the sitemap index shortly before XML document generation.
+ * Attributes can be added, changed and removed.
+ *
+ * @param array &$index_attributes
+ * @param string|null $sitemap_variant
+ */
+function hook_simple_sitemap_index_attributes_alter(array &$index_attributes, $sitemap_variant) {
+
+  // Add some attribute to the sitemap index.
+  $index_attributes['name'] = 'value';
+}
+
+/**
+ * Alter properties of and remove URL generator plugins.
+ *
+ * @param array $url_generators
+ */
+function hook_simple_sitemap_url_generators_alter(array &$url_generators) {
+
+  // Remove the entity generator.
+  unset($url_generators['entity']);
+}
+
+/**
+ * Alter properties of and remove sitemap generator plugins.
+ *
+ * @param array $sitemap_generators
+ */
+function hook_simple_sitemap_sitemap_generators_alter(array &$sitemap_generators) {
+
+  // Remove the default generator.
+  unset($sitemap_generators['default']);
+}
+
+/**
+ * Alter properties of and remove sitemap type plugins.
+ *
+ * @param array $sitemap_types
+ */
+function hook_simple_sitemap_sitemap_types_alter(array &$sitemap_types) {
+
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */

+ 45 - 0
sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.drush.inc

@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file
+ * Drush (< 9) integration.
+ */
+
+/**
+ * Implements hook_drush_command().
+ */
+function simple_sitemap_drush_command() {
+  $items['simple-sitemap-generate'] = [
+    'description' => 'Regenerate the XML sitemaps according to the module settings.',
+    'callback' => 'drush_simple_sitemap_generate',
+    'drupal dependencies' => ['simple_sitemap'],
+    'aliases' => ['ssg'],
+  ];
+
+  $items['simple-sitemap-rebuild-queue'] = [
+    'description' => 'Rebuild the sitemap queue for all sitemap variants.',
+    'callback' => 'drush_simple_sitemap_rebuild_queue',
+    'drupal dependencies' => ['simple_sitemap'],
+    'aliases' => ['ssr'],
+  ];
+
+  return $items;
+}
+
+/**
+ * Callback function for hook_drush_command().
+ *
+ * Regenerate the XML sitemaps according to the module settings.
+ */
+function drush_simple_sitemap_generate() {
+  \Drupal::service('simple_sitemap.generator')->generateSitemap('drush');
+}
+
+/**
+ * Callback function for hook_drush_command().
+ *
+ * Rebuild the sitemap queue for all sitemap variants.
+ */
+function drush_simple_sitemap_rebuild_queue() {
+  \Drupal::service('simple_sitemap.generator')->rebuildQueue();
+}

+ 12 - 0
sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.info.yml

@@ -0,0 +1,12 @@
+name: 'Simple XML Sitemap'
+type: module
+description: 'Creates a standard conform hreflang XML sitemap of the site content and provides a framework for developing other sitemap types.'
+configure: simple_sitemap.settings
+package: SEO
+# core: 8.x
+
+# Information added by Drupal.org packaging script on 2018-12-07
+version: '8.x-3.0'
+core: '8.x'
+project: 'simple_sitemap'
+datestamp: 1544174900

+ 678 - 0
sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.install

@@ -0,0 +1,678 @@
+<?php
+
+/**
+ * @file
+ * Module install and update procedures.
+ */
+
+/**
+ * Implements hook_requirements().
+ *
+ * @param $phase
+ * @return array
+ */
+function simple_sitemap_requirements($phase) {
+  $requirements = [];
+
+  if (!extension_loaded('xmlwriter')) {
+    $requirements['simple_sitemap_php_extensions'] = [
+      'title' => t('Simple XML sitemap PHP extensions'),
+      'value' => t('Missing PHP xmlwriter extension'),
+      'description' => t('In order to be able to generate sitemaps, the Simple XML sitemap module requires the <em>xmlwriter</em> PHP extension to be enabled.'),
+      'severity' => REQUIREMENT_ERROR,
+    ];
+  }
+
+  switch ($phase) {
+
+    case 'runtime':
+
+      // todo Implement for 3.x
+//      /** @var \Drupal\simple_sitemap\Simplesitemap $generator */
+//      $generator = \Drupal::service('simple_sitemap.generator');
+//      $generated_ago = $generator->getGeneratedAgo();
+//      $cron_generation = $generator->getSetting('cron_generate');
+//
+//      if (!$generated_ago) {
+//        $value = t('Not available');
+//        $description = t($cron_generation
+//          ? 'Run cron, or <a href="@generate">generate</a> the sitemap manually.'
+//          : 'Generation on cron run is disabled. <a href="@generate">Generate</a> the sitemap manually.', [
+//            '@generate' => $GLOBALS['base_url'] . '/admin/config/search/simplesitemap'
+//          ]
+//        );
+//        $severity = REQUIREMENT_WARNING;
+//      }
+//      else {
+//        $value = t('XML sitemaps are available');
+//        $description = t('The last generation finished @ago ago.'
+//          . ' ' . ($cron_generation
+//            ? 'Run cron, or <a href="@generate">regenerate</a> the sitemaps manually.'
+//            : 'Generation on cron run is disabled. <a href="@generate">Regenerate</a> the sitemaps manually.'), [
+//              '@ago' => $generated_ago,
+//              '@generate' => $GLOBALS['base_url'] . '/admin/config/search/simplesitemap'
+//            ]
+//          );
+//        $severity = REQUIREMENT_INFO;
+//      }
+//
+//      $requirements['simple_sitemap_generated'] = [
+//        'title' => 'Simple XML sitemap',
+//        'value' => $value,
+//        'description' => $description,
+//        'severity' => $severity,
+//      ];
+      break;
+  }
+  return $requirements;
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function simple_sitemap_uninstall() {
+  \Drupal::service('state')->deleteMultiple([
+    'simple_sitemap.last_cron_generate',
+    'simple_sitemap.queue_items_initial_amount',
+    'simple_sitemap.queue_stashed_results',
+  ]);
+
+  \Drupal::service('queue')
+    ->get('simple_sitemap_elements')
+    ->deleteQueue();
+}
+
+/**
+ * Implements hook_schema().
+ */
+function simple_sitemap_schema() {
+  $schema['simple_sitemap'] = [
+    'description' => 'Holds XML sitemaps as strings for quick retrieval.',
+    'fields' => [
+      'id' => [
+        'description' => 'Sitemap chunk unique identifier.',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => TRUE,
+        'unsigned' => TRUE,
+      ],
+      'type' => [
+        'description' => 'Type of sitemap this chunk belongs to.',
+        'type' => 'varchar',
+        'length' => 50,
+        'not null' => TRUE,
+        'default' => '',
+      ],
+      'delta' => [
+        'description' => 'Delta of the chunk within the type scope.',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => TRUE,
+        'unsigned' => TRUE,
+      ],
+      'sitemap_string' => [
+        'description' => 'XML sitemap chunk string.',
+        'type' => 'text',
+        'size' => 'big',
+        'not null' => TRUE,
+      ],
+      'sitemap_created' => [
+        'description' => 'Timestamp of sitemap chunk generation.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'unsigned' => TRUE,
+        'default' => 0,
+      ],
+      'status' => [
+        'description' => "Flag indicating the publishing status of the chunk.",
+        'type' => 'int',
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'unsigned' => TRUE,
+        'default' => 0,
+      ],
+    ],
+    'primary key' => ['id'],
+  ];
+
+  $schema['simple_sitemap_entity_overrides'] = [
+    'description' => 'Holds sitemap settings overridden by entities.',
+    'fields' => [
+      'id' => [
+        'description' => 'Override unique identifier.',
+        'type' => 'serial',
+        'not null' => TRUE,
+        'unsigned' => TRUE,
+      ],
+      'type' => [
+        'description' => 'Type of sitemap this override belongs to.',
+        'type' => 'varchar',
+        'length' => 50,
+        'not null' => TRUE,
+      ],
+      'entity_type' => [
+        'description' => 'Entity type of the overriding entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+      ],
+      'entity_id' => [
+        'description' => 'ID of the overriding entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+      ],
+      'inclusion_settings' => [
+        'description' => 'Setting for the overriding entity.',
+        'type' => 'blob',
+      ],
+    ],
+    'primary key' => ['id'],
+  ];
+  return $schema;
+}
+
+function _simple_sitemap_update_8216_get_default_variant() {
+  $config_factory = \Drupal::service('config.factory');
+  $default_variant = $config_factory->get('simple_sitemap.settings')->get('default_variant');
+  if (empty($default_variant)) {
+    $default_variant = 'default';
+    $config_factory->getEditable('simple_sitemap.settings')
+      ->set('default_variant', $default_variant)
+      ->save();
+  }
+
+  /** @var \Drupal\simple_sitemap\SimplesitemapManager $manager */
+  $manager = \Drupal::service('simple_sitemap.manager');
+  $variants = $manager->getSitemapVariants();
+  if (!isset($variants[$default_variant])) {
+    $manager->addSitemapVariant($default_variant);
+  }
+
+  return $default_variant;
+}
+
+/**
+ * Changing the data structure of the module's configuration.
+ */
+function simple_sitemap_update_8201() {
+  $entity_types = \Drupal::config('simple_sitemap.settings')->get('entity_types');
+  $entity_types = is_array($entity_types) ? $entity_types : [];
+  $naming_changes = [
+    'node_type' => 'node',
+    'taxonomy_vocabulary' => 'taxonomy_term',
+    'menu' => 'menu_link_content',
+    'commerce_product_type' => 'commerce_product',
+    'media_bundle' => 'media',
+  ];
+  foreach ($entity_types as $entity_type_name => $settings) {
+    if (isset($naming_changes[$entity_type_name])) {
+      $entity_types[$naming_changes[$entity_type_name]] = $entity_types[$entity_type_name];
+      unset($entity_types[$entity_type_name]);
+    }
+  }
+  \Drupal::service('config.factory')->getEditable('simple_sitemap.settings')
+    ->set('entity_types', $entity_types)->save();
+}
+
+/**
+ * Moving entity overrides from configuration to database table.
+ */
+function simple_sitemap_update_8202() {
+  $database = \Drupal::database();
+
+  // Create database table.
+  if (!$database->schema()->tableExists('simple_sitemap_entity_overrides')) {
+    $database->schema()->createTable('simple_sitemap_entity_overrides', [
+      'description' => 'Holds sitemap settings overridden by entities.',
+      'fields' => [
+        'id' => [
+          'description' => 'Override unique identifier.',
+          'type' => 'serial',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+        ],
+        'entity_type' => [
+          'description' => 'Entity type of the overriding entity.',
+          'type' => 'varchar',
+          'length' => 32,
+          'not null' => TRUE,
+        ],
+        'entity_id' => [
+          'description' => 'ID of the overriding entity.',
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+        ],
+        'inclusion_settings' => [
+          'description' => 'Setting for the overriding entity.',
+          'type' => 'blob',
+        ],
+      ],
+      'primary key' => ['id'],
+    ]);
+  }
+
+  // Populate database table with config values.
+  $entity_types = \Drupal::config('simple_sitemap.settings')->get('entity_types');
+  $entity_types = is_array($entity_types) ? $entity_types : [];
+
+  foreach ($entity_types as $entity_type_name => &$entity_type) {
+    if (is_array($entity_type)) {
+      foreach ($entity_type as $bundle_name => &$bundle) {
+        if (isset($bundle['entities'])) {
+          foreach ($bundle['entities'] as $entity_id => $entity_settings) {
+            $database->insert('simple_sitemap_entity_overrides')
+              ->fields([
+                'entity_type' => $entity_type_name,
+                'entity_id' => $entity_id,
+                'inclusion_settings' => serialize($entity_settings),
+              ])
+              ->execute();
+          }
+          // Remove entity overrides from configuration.
+          unset($bundle['entities']);
+        }
+      }
+    }
+  }
+
+  \Drupal::service('config.factory')->getEditable('simple_sitemap.settings')
+    ->set('entity_types', $entity_types)->save();
+}
+
+/**
+ * Splitting simple_sitemap.settings configuration into simple_sitemap.settings,
+ * simple_sitemap.entity_types and simple_sitemap.custom.
+ */
+function simple_sitemap_update_8203() {
+  $old_config = $config = \Drupal::config('simple_sitemap.settings');
+  foreach (['entity_types', 'custom'] as $config_name) {
+    if (!$config = $old_config->get($config_name)) {
+      continue;
+    }
+    \Drupal::service('config.factory')->getEditable("simple_sitemap.$config_name")
+      ->setData($config)->save();
+  }
+  $settings = $old_config->get('settings');
+  \Drupal::service('config.factory')->getEditable("simple_sitemap.settings")
+    ->setData($settings)->save();
+}
+
+/**
+ * Removing entity type settings for entity types which do not have the canonical
+ * link template.
+ */
+function simple_sitemap_update_8204() {
+  $sitemap_entity_types = \Drupal::service('entity_type.manager')->getDefinitions();
+  $entity_types = \Drupal::config('simple_sitemap.entity_types')->get();
+  unset($entity_types['_core']);
+  foreach ($entity_types as $entity_type_id => $entity_type) {
+    if (!isset($sitemap_entity_types[$entity_type_id])
+      || !$sitemap_entity_types[$entity_type_id]->hasLinkTemplate('canonical')) {
+
+      // Delete entity overrides.
+      \Drupal::database()->delete('simple_sitemap_entity_overrides')
+        ->condition('entity_type', $entity_type_id)
+        ->execute();
+
+      // Delete entity type settings.
+      unset($entity_types[$entity_type_id]);
+    }
+  }
+  \Drupal::service('config.factory')->getEditable("simple_sitemap.entity_types")
+    ->setData($entity_types)->save();
+}
+
+/**
+ * Splitting simple_sitemap.entity_types into individual configuration objects
+ * for each bundle.
+ */
+function simple_sitemap_update_8205() {
+  $entity_types = \Drupal::config('simple_sitemap.entity_types')->get();
+  unset($entity_types['_core']);
+  $enabled_entity_types = [];
+  foreach ($entity_types as $entity_type_id => $bundles) {
+    $enabled_entity_types[] = $entity_type_id;
+    foreach ($bundles as $bundle_name => $bundle_settings) {
+      \Drupal::service('config.factory')
+        ->getEditable("simple_sitemap.bundle_settings.$entity_type_id.$bundle_name")
+        ->setData($bundle_settings)->save();
+    }
+  }
+
+  // Add enabled entity type settings.
+  \Drupal::service('config.factory')
+    ->getEditable('simple_sitemap.settings')
+    ->set('enabled_entity_types', $enabled_entity_types)
+    ->save();
+
+  // Remove old configuration object.
+  \Drupal::service('config.factory')
+    ->getEditable('simple_sitemap.entity_types')
+    ->delete();
+}
+
+/**
+ * Placing custom links in a subkey of simple_sitemap.custom configuration.
+ */
+function simple_sitemap_update_8206() {
+  $custom_links = \Drupal::config('simple_sitemap.custom')->get();
+  foreach ($custom_links as $i => $custom_link) {
+    if (!isset($custom_link['path'])) {
+      unset($custom_links[$i]);
+    }
+  }
+  \Drupal::service('config.factory')->getEditable('simple_sitemap.custom')
+    ->setData(['links' => $custom_links])->save();
+}
+
+/**
+ * Updating entity_id field of simple_sitemap_entity_overrides table to varchar(32).
+ */
+function simple_sitemap_update_8207() {
+  \Drupal::database()->schema()->changeField(
+    'simple_sitemap_entity_overrides',
+    'entity_id',
+    'entity_id', [
+      'description' => 'ID of the overriding entity.',
+      'type' => 'varchar',
+      'length' => 32,
+      'not null' => TRUE,
+    ]
+  );
+}
+
+/**
+ * Adding changefreq setting to all existing bundle and entity instance settings.
+ */
+function simple_sitemap_update_8208() {
+
+  // Update existing bundle settings.
+  $config_factory = \Drupal::service('config.factory');
+  $entity_types = $config_factory->listAll('simple_sitemap.bundle_settings.');
+
+  foreach ($entity_types as $entity_type) {
+    $config = $config_factory->get($entity_type)->get();
+    if (!isset($config['changefreq'])) {
+      $config_factory->getEditable($entity_type)
+        ->setData($config + ['changefreq' => ''])
+        ->save();
+    }
+  }
+
+  // Update existing entity override data.
+  $results = \Drupal::database()->select('simple_sitemap_entity_overrides', 'o')
+    ->fields('o', ['id', 'inclusion_settings'])
+    ->execute()->fetchAll(\PDO::FETCH_OBJ);
+
+  foreach ($results as $row) {
+    $settings = unserialize($row->inclusion_settings);
+    if (!isset($settings['changefreq'])) {
+      \Drupal::database()->update('simple_sitemap_entity_overrides')
+        ->fields(['inclusion_settings' => serialize($settings + ['changefreq' => '']),])
+        ->condition('id', $row->id)
+        ->execute();
+    }
+  }
+
+  return t('You may now want to configure the new changefreq setting for the XML sitemap entities and custom links.');
+}
+
+/**
+ * Adding image inclusion setting to all existing bundle and entity instance settings.
+ */
+function simple_sitemap_update_8209() {
+
+  // Update existing bundle settings.
+  $config_factory = \Drupal::service('config.factory');
+  $all_bundle_settings = $config_factory->listAll('simple_sitemap.bundle_settings.');
+
+  foreach ($all_bundle_settings as $bundle_settings) {
+    $config = $config_factory->get($bundle_settings)->get();
+    if (!isset($config['include_images'])) {
+      $config_factory->getEditable($bundle_settings)
+        ->setData($config + ['include_images' => 0])
+        ->save();
+    }
+  }
+
+  // Update existing entity override data.
+  $results = \Drupal::database()->select('simple_sitemap_entity_overrides', 'o')
+    ->fields('o', ['id', 'inclusion_settings'])
+    ->execute()->fetchAll(\PDO::FETCH_OBJ);
+
+  foreach ($results as $row) {
+    $settings = unserialize($row->inclusion_settings);
+    if (!isset($settings['include_images'])) {
+      \Drupal::database()->update('simple_sitemap_entity_overrides')
+        ->fields(['inclusion_settings' => serialize($settings + ['include_images' => 0]),])
+        ->condition('id', $row->id)
+        ->execute();
+    }
+  }
+
+  return t('You may now want to configure your XML sitemap entities to include images.');
+}
+
+/**
+ * Adding 'type' and 'delta' fields to simple_sitemap table.
+ */
+function simple_sitemap_update_8210() {
+
+  $database = \Drupal::database();
+  $database->truncate('simple_sitemap')->execute();
+
+  if (!$database->schema()->fieldExists('simple_sitemap', 'type')) {
+    $database->schema()->addField(
+      'simple_sitemap',
+      'type', [
+        'description' => 'Type of sitemap this chunk belongs to.',
+        'type' => 'varchar',
+        'length' => 50,
+        'not null' => TRUE,
+        'default' => '',
+      ]
+    );
+  }
+  if (!$database->schema()->fieldExists('simple_sitemap', 'delta')) {
+    $database->schema()->addField(
+      'simple_sitemap',
+      'delta', [
+        'description' => 'Delta of the chunk within the type scope.',
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => TRUE,
+        'unsigned' => TRUE,
+      ]
+    );
+  }
+}
+
+/**
+ * Adding simple_sitemap.variants and simple_sitemap.types to configuration.
+ */
+function simple_sitemap_update_8211() {
+  $config_factory = \Drupal::service('config.factory');
+
+  // Add simple_sitemap.types.
+  $config_factory
+    ->getEditable('simple_sitemap.types.default_hreflang')
+    ->setData([
+      'label' => 'Default hreflang',
+      'description' => 'The default hreflang sitemap type.',
+      'sitemap_generator' => 'default',
+      'url_generators' => [
+        'custom',
+        'entity',
+        'entity_menu_link_content',
+        'arbitrary',
+      ],
+    ])->save();
+
+  // Add simple_sitemap.variants.
+  $config_factory
+    ->getEditable('simple_sitemap.variants')
+    ->set('variants', [
+      'default' => [
+        'label' => 'Default',
+        'type' => 'default_hreflang',
+      ]
+    ])->save();
+}
+
+/**
+ * Changing storage data type of 'index' and 'include_images' from integer to boolean.
+ */
+function simple_sitemap_update_8212() {
+
+  // Update existing bundle settings.
+  $config_factory = \Drupal::service('config.factory');
+  $all_bundle_settings = $config_factory->listAll('simple_sitemap.bundle_settings.');
+
+  foreach ($all_bundle_settings as $bundle_settings) {
+    $config = $config_factory->get($bundle_settings)->get();
+
+    $config['include_images'] = isset($config['include_images'])
+      ? (bool) $config['include_images']
+      : FALSE;
+
+    $config['index'] = isset($config['index'])
+      ? (bool) $config['index']
+      : FALSE;
+
+    $config_factory->getEditable($bundle_settings)->setData($config)->save();
+  }
+
+  // Update existing entity override data.
+  $results = \Drupal::database()->select('simple_sitemap_entity_overrides', 'o')
+    ->fields('o', ['id', 'inclusion_settings'])
+    ->execute()->fetchAll(\PDO::FETCH_OBJ);
+
+  foreach ($results as $row) {
+    $settings = unserialize($row->inclusion_settings);
+
+    if (isset($settings['index'])) {
+      $settings['index'] = (bool) $settings['index'];
+    }
+
+    if (isset($settings['include_images'])) {
+      $settings['include_images'] = (bool) $settings['include_images'];
+    }
+
+    \Drupal::database()->update('simple_sitemap_entity_overrides')
+      ->fields(['inclusion_settings' => serialize($settings)])
+      ->condition('id', $row->id)
+      ->execute();
+  }
+}
+
+/**
+ * Altering the configuration storage of variants.
+ */
+function simple_sitemap_update_8213() {
+  $config_factory = \Drupal::service('config.factory');
+  $new_variants = [];
+  foreach ($config_factory->get('simple_sitemap.variants')->get('variants') as $variant_name => $variant_definition) {
+    $new_variants[$variant_definition['type']][$variant_name] = ['label' => $variant_definition['label']];
+  }
+
+  // Create new configuration objects.
+  foreach ($new_variants as $type => $variants) {
+    $config_factory
+      ->getEditable('simple_sitemap.variants.' . $type)
+      ->set('variants', $variants)
+      ->save();
+  }
+
+  // Remove old configuration object.
+  $config_factory->getEditable('simple_sitemap.variants')->delete();
+}
+
+/**
+ * Removing sitemap types from configuration as they are to be stored as plugins in code.
+ */
+function simple_sitemap_update_8214() {
+  $config_factory = \Drupal::service('config.factory');
+  $sitemap_types = $config_factory->listAll('simple_sitemap.types.');
+
+  // Remove sitemap type configuration objects.
+  foreach ($sitemap_types as $type) {
+    $config_factory->getEditable($type)->delete();
+  }
+}
+
+/**
+ * Adding 'status' field to simple_sitemap table and weight to variants.
+ */
+function simple_sitemap_update_8215() {
+  $database = \Drupal::database();
+  $database->truncate('simple_sitemap')->execute();
+
+  if (!$database->schema()->fieldExists('simple_sitemap', 'status')) {
+    $database->schema()->addField(
+      'simple_sitemap',
+      'status', [
+        'description' => "Flag indicating the publishing status of the chunk.",
+        'type' => 'int',
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'unsigned' => TRUE,
+        'default' => 0,
+      ]
+    );
+  }
+
+  $config_factory = \Drupal::service('config.factory');
+  foreach ($config_factory->listAll('simple_sitemap.variants.') as $type) {
+    $type = $config_factory->getEditable($type);
+    $variants = $type->get('variants');
+    foreach($variants as $i => $variant) {
+      $variants[$i]['weight'] = 0;
+    }
+    $type->set('variants', $variants)->save();
+  }
+}
+
+/**
+ * Adding per-variant bundle and entity override configuration.
+ */
+function simple_sitemap_update_8216() {
+  $config_factory = \Drupal::service('config.factory');
+  foreach ($config_factory->listAll('simple_sitemap.bundle_settings.') as $bundle_config_name) {
+    $config = $config_factory->getEditable($bundle_config_name);
+    $config_name_parts = explode('.', $bundle_config_name);
+    $config_factory->getEditable($config_name_parts[0] . '.' . $config_name_parts[1]
+      . '.' . _simple_sitemap_update_8216_get_default_variant() . '.' . $config_name_parts[2] . '.' . $config_name_parts[3])
+      ->setData($config->get())->save();
+
+    $config->delete();
+  }
+
+  $database = \Drupal::database();
+  if (!$database->schema()->fieldExists('simple_sitemap_entity_overrides', 'type')) {
+    $database->schema()->addField(
+      'simple_sitemap_entity_overrides',
+      'type', [
+        'description' => 'Type of sitemap this override belongs to.',
+        'type' => 'varchar',
+        'length' => 50,
+        'not null' => TRUE,
+        'initial' => 'default',
+      ]
+    );
+  }
+}
+
+/**
+ * Adding per-variant custom link configuration.
+ */
+function simple_sitemap_update_8217() {
+  $config_factory = \Drupal::service('config.factory');
+  $old_config = $config_factory->getEditable('simple_sitemap.custom');
+  $config_factory->getEditable('simple_sitemap.custom_links.' . _simple_sitemap_update_8216_get_default_variant())
+    ->setData($old_config->get())->save();
+  $old_config->delete();
+
+  return t('The XML sitemaps need to be regenerated.');
+}

+ 19 - 0
sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.libraries.yml

@@ -0,0 +1,19 @@
+fieldsetSummaries:
+  version: VERSION
+  js:
+    js/simple_sitemap.fieldsetSummaries.js: {}
+  dependencies:
+    - core/jquery
+form:
+  version: VERSION
+  js:
+    js/simple_sitemap.form.js: {}
+  dependencies:
+    - core/jquery
+sitemapEntities:
+  version: VERSION
+  js:
+    js/simple_sitemap.sitemapEntities.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupalSettings

+ 5 - 0
sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.links.menu.yml

@@ -0,0 +1,5 @@
+simple_sitemap.settings:
+  title: 'Simple XML sitemap'
+  description: 'Configure and generate the XML sitemap, add custom links to it.'
+  parent: system.admin_config_search
+  route_name: simple_sitemap.settings

+ 25 - 0
sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.links.task.yml

@@ -0,0 +1,25 @@
+simple_sitemap.settings:
+  route_name: simple_sitemap.settings
+  title: 'Settings'
+  base_route: simple_sitemap.settings
+  weight: -1
+
+simple_sitemap.settings_entities:
+  route_name: simple_sitemap.settings_entities
+  title: 'Sitemap entities'
+  base_route: simple_sitemap.settings
+  weight: 0
+
+simple_sitemap.variants:
+  route_name: simple_sitemap.settings_variants
+  title: 'Sitemap variants'
+  base_route: simple_sitemap.settings
+  weight: 1
+
+simple_sitemap.settings_custom:
+  route_name: simple_sitemap.settings_custom
+  title: 'Custom links'
+  base_route: simple_sitemap.settings
+  weight: 2
+
+

+ 236 - 0
sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.module

@@ -0,0 +1,236 @@
+<?php
+
+/**
+ * @file
+ * Main module file containing hooks.
+ */
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\system\MenuInterface;
+use Drupal\language\ConfigurableLanguageInterface;
+
+/**
+ *Implements hook_help().
+ *
+ * @param $route_name
+ * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+ * @return \Drupal\Component\Render\MarkupInterface|null
+ */
+function simple_sitemap_help($route_name, RouteMatchInterface $route_match) {
+  return $route_name === 'help.page.simple_sitemap' ?
+    check_markup(file_get_contents(dirname(__FILE__) . "/README.md")) : NULL;
+}
+
+/**
+ * Implements hook_form_alter().
+ *
+ * Adds sitemap settings to entity types that are supported via plugins.
+ *
+ * @param $form
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * @param $form_id
+ */
+function simple_sitemap_form_alter(&$form, FormStateInterface $form_state, $form_id) {
+
+  /** @var Drupal\simple_sitemap\Form\FormHelper $f */
+  $f = \Drupal::service('simple_sitemap.form_helper');
+  if (!$f->processForm($form_state)) {
+    return;
+  }
+
+  $form['simple_sitemap'] = [
+    '#type' => 'details',
+    '#group' => isset($form['additional_settings']) ? 'additional_settings' : 'advanced',
+    '#title' => t('Simple XML sitemap'),
+    '#description' => $f->getEntityCategory() === 'instance' ? t('Settings for this entity can be overridden here.') : '',
+    '#weight' => 10,
+  ];
+
+  // Attach some js magic to forms.
+  if ($f->getEntityCategory() !== 'instance') {
+    $form['#attached']['library'][] = 'simple_sitemap/form';
+  }
+
+  // Only attach fieldset summary js to 'additional settings' vertical tabs.
+  if (isset($form['additional_settings'])) {
+    $form['#attached']['library'][] = 'simple_sitemap/fieldsetSummaries';
+  }
+
+  $f->displayEntitySettings($form['simple_sitemap'])
+  // todo: do not show setting when creating new bundle.
+    ->displayRegenerateNow($form['simple_sitemap']);
+
+  // Add submission handler.
+  if (isset($form['actions']['submit']['#submit'])) {
+    foreach (array_keys($form['actions']) as $action) {
+      if ($action !== 'preview'
+        && isset($form['actions'][$action]['#type'])
+        && $form['actions'][$action]['#type'] === 'submit') {
+        $form['actions'][$action]['#submit'][] = 'simple_sitemap_entity_form_submit';
+      }
+    }
+  }
+  // Fix for account page rendering other submit handlers not usable.
+  else {
+    $form['#submit'][] = 'simple_sitemap_entity_form_submit';
+  }
+}
+
+/**
+ * Form submission handler called in hook_form_alter.
+ *
+ * @param $form
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ */
+function simple_sitemap_entity_form_submit($form, FormStateInterface &$form_state) {
+
+  /** @var Drupal\simple_sitemap\Form\FormHelper $f */
+  $f = \Drupal::service('simple_sitemap.form_helper');
+  if (!$f->processForm($form_state)) {
+    return;
+  }
+
+  $values = $form_state->getValues();
+
+  // Fix for values appearing in a sub array on a commerce product entity.
+  $values = isset($values['simple_sitemap']) ? $values['simple_sitemap'] : $values;
+
+  // Only make changes in DB if sitemap settings actually changed.
+  if ($f->valuesChanged($form, $values)) {
+
+    /** @var \Drupal\simple_sitemap\Simplesitemap $generator */
+    $generator = \Drupal::service('simple_sitemap.generator');
+
+    $settings = [
+      'index' => (bool) $values['simple_sitemap_index_content'],
+      'priority' => $values['simple_sitemap_priority'],
+      'changefreq' => $values['simple_sitemap_changefreq'],
+      'include_images' => (bool) $values['simple_sitemap_include_images'],
+    ];
+
+    // Deleting bundle settings for old bundle.
+    // See SimplesitemapEntitiesForm::submitForm().
+    // todo: This will not be necessary if "multiple variants pro bundle" is implemented.
+    if (isset($form['simple_sitemap']['simple_sitemap_variant']['#default_value'])) {
+      $old_variant = $form['simple_sitemap']['simple_sitemap_variant']['#default_value'];
+      if ($old_variant !== $values['simple_sitemap_variant']) {
+        $generator->setVariants($old_variant)->removeBundleSettings($f->getEntityTypeId(), $f->getBundleName());
+      }
+    }
+
+    switch ($f->getEntityCategory()) {
+
+      case 'bundle':
+        $generator->setVariants($values['simple_sitemap_variant'])
+          ->setBundleSettings($f->getEntityTypeId(),
+            !empty($f->getBundleName()) ? $f->getBundleName() : $f->getFormEntityId(),
+          $settings
+        );
+        break;
+
+      case 'instance':
+        $generator->setVariants($values['simple_sitemap_variant'])
+          ->setEntityInstanceSettings($f->getEntityTypeId(),
+            !empty($f->getInstanceId()) ? $f->getInstanceId() : $f->getFormEntityId(),
+            $settings
+        );
+        break;
+    }
+
+    // Regenerate sitemaps according to user setting.
+    if ($values['simple_sitemap_regenerate_now']) {
+      $generator->rebuildQueue()->generateSitemap();
+    }
+  }
+}
+
+/**
+ * Implements hook_cron().
+ */
+function simple_sitemap_cron() {
+
+  /** @var \Drupal\simple_sitemap\Simplesitemap $generator */
+  $generator = \Drupal::service('simple_sitemap.generator');
+
+  if ($generator->getSetting('cron_generate')) {
+    $interval = (int) $generator->getSetting('cron_generate_interval', 0) * 60 * 60;
+    $request_time = \Drupal::service('datetime.time')->getRequestTime();
+    $generation_in_progress = $generator->getQueueWorker()->generationInProgress();
+    $state = \Drupal::state();
+
+    if ($interval === 0
+      || $generation_in_progress
+      || (($state->get('simple_sitemap.last_cron_generate', 0) + $interval) <= $request_time)) {
+
+      if (!$generation_in_progress) {
+        $state->set('simple_sitemap.last_cron_generate', $request_time);
+      }
+
+      $generator->generateSitemap('cron');
+    }
+  }
+}
+
+/**
+ * Implements hook_ENTITY_TYPE_delete().
+ *
+ * When a language is removed from the system remove it also from settings.
+ */
+function simple_sitemap_configurable_language_delete(ConfigurableLanguageInterface $language) {
+
+  /** @var \Drupal\simple_sitemap\Simplesitemap $generator */
+  $generator = \Drupal::service('simple_sitemap.generator');
+
+  $excluded_languages = $generator->getSetting('excluded_languages');
+  if (isset($excluded_languages[$language->id()])) {
+    unset($excluded_languages[$language->id()]);
+    $generator->saveSetting('excluded_languages', $excluded_languages);
+  }
+}
+
+/**
+ * Implements hook_entity_delete().
+ *
+ * Removes settings of the removed entity.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ */
+function simple_sitemap_entity_delete(EntityInterface $entity) {
+
+  /** @var \Drupal\simple_sitemap\Simplesitemap $generator */
+  $generator = \Drupal::service('simple_sitemap.generator');
+  $generator->setVariants(TRUE)->removeEntityInstanceSettings(
+    $entity->getEntityTypeId(), $entity->id()
+  );
+}
+
+/**
+ * Implements hook_entity_bundle_delete().
+ *
+ * Removes settings of the removed bundle.
+ *
+ * @param string $entity_type_id
+ * @param string $bundle
+ */
+function simple_sitemap_entity_bundle_delete($entity_type_id, $bundle) {
+
+  /** @var \Drupal\simple_sitemap\Simplesitemap $generator */
+  $generator = \Drupal::service('simple_sitemap.generator');
+  $generator->setVariants(TRUE)->removeBundleSettings($entity_type_id, $bundle);
+}
+
+/**
+ * Implements hook_menu_delete().
+ *
+ * Removes settings for the removed menu.
+ *
+ * @param \Drupal\system\MenuInterface $menu
+ */
+function simple_sitemap_menu_delete(MenuInterface $menu) {
+
+  /** @var \Drupal\simple_sitemap\Simplesitemap $generator */
+  $generator = \Drupal::service('simple_sitemap.generator');
+  $generator->setVariants(TRUE)->removeBundleSettings('menu_link_content', $menu->id());
+}

+ 4 - 0
sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.permissions.yml

@@ -0,0 +1,4 @@
+administer sitemap settings:
+  title: 'Administer sitemap settings'
+  description: 'Administer Simple XML sitemap settings, alter inclusion settings of content and generate the sitemap on demand.'
+  restrict access: false

+ 51 - 0
sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.routing.yml

@@ -0,0 +1,51 @@
+simple_sitemap.sitemap_default:
+  path: '/sitemap.xml'
+  defaults:
+    _controller: '\Drupal\simple_sitemap\Controller\SimplesitemapController::getSitemap'
+    _disable_route_normalizer: 'TRUE'
+  requirements:
+    _access: 'TRUE'
+
+# The actual path to a variant is '/{variant}/sitemap.xml'. Because Drupal 8
+# cannot handle a parameter as first route argument,
+# Drupal\simple_sitemap\PathProcessor\PathProcessorSitemapVariant::processInbound()
+# is being used to catch the request and redirect to the following route.
+simple_sitemap.sitemap_variant:
+  path: '/sitemaps/{variant}/sitemap.xml'
+  defaults:
+    _controller: '\Drupal\simple_sitemap\Controller\SimplesitemapController::getSitemap'
+    _disable_route_normalizer: 'TRUE'
+  requirements:
+    _access: 'TRUE'
+
+simple_sitemap.settings:
+  path: '/admin/config/search/simplesitemap'
+  defaults:
+    _form: '\Drupal\simple_sitemap\Form\SimplesitemapSettingsForm'
+    _title: 'Simple XML Sitemap Settings'
+  requirements:
+    _permission: 'administer sitemap settings'
+
+simple_sitemap.settings_entities:
+  path: '/admin/config/search/simplesitemap/entities'
+  defaults:
+    _form: '\Drupal\simple_sitemap\Form\SimplesitemapEntitiesForm'
+    _title: 'Simple XML Sitemap Settings'
+  requirements:
+    _permission: 'administer sitemap settings'
+
+simple_sitemap.settings_custom:
+  path: '/admin/config/search/simplesitemap/custom'
+  defaults:
+    _form: '\Drupal\simple_sitemap\Form\SimplesitemapCustomLinksForm'
+    _title: 'Simple XML Sitemap Settings'
+  requirements:
+    _permission: 'administer sitemap settings'
+
+simple_sitemap.settings_variants:
+  path: '/admin/config/search/simplesitemap/variants'
+  defaults:
+    _form: '\Drupal\simple_sitemap\Form\SimplesitemapVariantsForm'
+    _title: 'Simple XML Sitemap Settings'
+  requirements:
+    _permission: 'administer sitemap settings'

+ 100 - 0
sites/all/modules/contrib/admin/simple_sitemap/simple_sitemap.services.yml

@@ -0,0 +1,100 @@
+services:
+  simple_sitemap.generator:
+    class: Drupal\simple_sitemap\Simplesitemap
+    public: true
+    arguments:
+      - '@simple_sitemap.entity_helper'
+      - '@simple_sitemap.settings'
+      - '@simple_sitemap.manager'
+      - '@config.factory'
+      - '@database'
+      - '@entity_type.manager'
+      - '@entity_type.bundle.info'
+      - '@path.validator'
+      - '@date.formatter'
+      - '@datetime.time'
+      - '@simple_sitemap.queue_worker'
+
+  simple_sitemap.manager:
+    class: Drupal\simple_sitemap\SimplesitemapManager
+    public: true
+    arguments:
+    - '@config.factory'
+    - '@database'
+    - '@plugin.manager.simple_sitemap.sitemap_type'
+    - '@plugin.manager.simple_sitemap.url_generator'
+    - '@plugin.manager.simple_sitemap.sitemap_generator'
+    - '@simple_sitemap.settings'
+
+  simple_sitemap.settings:
+    class: Drupal\simple_sitemap\SimplesitemapSettings
+    public: false
+    arguments:
+    - '@config.factory'
+
+  simple_sitemap.queue_worker:
+    class: Drupal\simple_sitemap\Queue\QueueWorker
+    public: true
+    arguments:
+    - '@simple_sitemap.settings'
+    - '@simple_sitemap.manager'
+    - '@state'
+    - '@simple_sitemap.queue'
+    - '@simple_sitemap.logger'
+
+  simple_sitemap.queue:
+    class: Drupal\simple_sitemap\Queue\SimplesitemapQueue
+    public: false
+    arguments:
+    - 'simple_sitemap_elements'
+    - '@database'
+
+  simple_sitemap.sitemap_writer:
+    class: Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator\SitemapWriter
+    public: true
+
+  simple_sitemap.entity_helper:
+    class: Drupal\simple_sitemap\EntityHelper
+    public: true
+    arguments:
+      - '@entity_type.manager'
+      - '@database'
+
+  simple_sitemap.form_helper:
+    class: Drupal\simple_sitemap\Form\FormHelper
+    public: true
+    arguments:
+      - '@simple_sitemap.generator'
+      - '@simple_sitemap.entity_helper'
+      - '@current_user'
+
+  simple_sitemap.logger:
+    class: Drupal\simple_sitemap\Logger
+    public: true
+    arguments:
+      - '@logger.channel.simple_sitemap'
+      - '@messenger'
+      - '@current_user'
+
+  simple_sitemap.path_processor_variant:
+    class: Drupal\simple_sitemap\PathProcessor\PathProcessorSitemapVariant
+    tags:
+      - { name: path_processor_inbound, priority: 300 }
+
+  logger.channel.simple_sitemap:
+    parent: logger.channel_base
+    public: false
+    arguments:
+      - simple_sitemap
+
+  plugin.manager.simple_sitemap.url_generator:
+    class: Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator\UrlGeneratorManager
+    parent: default_plugin_manager
+
+  plugin.manager.simple_sitemap.sitemap_generator:
+    class: Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator\SitemapGeneratorManager
+    parent: default_plugin_manager
+
+  plugin.manager.simple_sitemap.sitemap_type:
+    class: Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapType\SitemapTypeManager
+    parent: default_plugin_manager

+ 50 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Annotation/SitemapGenerator.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace Drupal\simple_sitemap\Annotation;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines a SitemapGenerator item annotation object.
+ *
+ * @package Drupal\simple_sitemap\Annotation
+ *
+ * @see \Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator\SitemapGeneratorManager
+ * @see plugin_api
+ *
+ * @Annotation
+ */
+class SitemapGenerator extends Plugin {
+
+  /**
+   * The generator ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The human-readable name of the generator.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   */
+  public $label;
+
+  /**
+   * A short description of the generator.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   */
+  public $description;
+
+  /**
+   * Default generator settings.
+   *
+   * @var array
+   */
+  public $settings = [];
+}

+ 57 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Annotation/SitemapType.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace Drupal\simple_sitemap\Annotation;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines a SitemapType item annotation object.
+ *
+ * @package Drupal\simple_sitemap\Annotation
+ *
+ * @see \Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapType\SitemapTypeManager
+ * @see plugin_api
+ *
+ * @Annotation
+ */
+class SitemapType extends Plugin {
+
+  /**
+   * The sitemap type ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The human-readable name of the sitemap type.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   */
+  public $label;
+
+  /**
+   * A short description of the sitemap type.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   */
+  public $description;
+
+  /**
+   * The ID of the sitemap generator.
+   *
+   * @var string
+   */
+  public $sitemapGenerator;
+
+  /**
+   * The IDs of the URL generators.
+   *
+   * @var[] string
+   */
+  public $urlGenerators = [];
+}

+ 50 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Annotation/UrlGenerator.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace Drupal\simple_sitemap\Annotation;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines a UrlGenerator item annotation object.
+ *
+ * @package Drupal\simple_sitemap\Annotation
+ *
+ * @see \Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator\UrlGeneratorManager
+ * @see plugin_api
+ *
+ * @Annotation
+ */
+class UrlGenerator extends Plugin {
+
+  /**
+   * The generator ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The human-readable name of the generator.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   */
+  public $label;
+
+  /**
+   * A short description of the generator.
+   *
+   * @ingroup plugin_translatable
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   */
+  public $description;
+
+  /**
+   * Default generator settings.
+   *
+   * @var array
+   */
+  public $settings = [];
+}

+ 59 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Commands/SimplesitemapCommands.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace Drupal\simple_sitemap\Commands;
+
+use Drupal\simple_sitemap\Simplesitemap;
+use Drush\Commands\DrushCommands;
+
+/**
+ * Class SimplesitemapCommands
+ * @package Drupal\simple_sitemap\Commands
+ */
+class SimplesitemapCommands extends DrushCommands {
+
+  /**
+   * @var \Drupal\simple_sitemap\Simplesitemap
+   */
+  protected $generator;
+
+  /**
+   * SimplesitemapCommands constructor.
+   * @param \Drupal\simple_sitemap\Simplesitemap $generator
+   */
+  public function __construct(Simplesitemap $generator) {
+    $this->generator = $generator;
+  }
+
+  /**
+   * Regenerate the XML sitemaps according to the module settings.
+   *
+   * @command simple-sitemap:generate
+   *
+   * @usage drush simple-sitemap:generate
+   *   Regenerate the XML sitemaps according to the module settings.
+   *
+   * @validate-module-enabled simple_sitemap
+   *
+   * @aliases ssg, simple-sitemap-generate
+   */
+  public function generate() {
+    $this->generator->generateSitemap('drush');
+  }
+
+  /**
+   * Rebuild the sitemap queue for all sitemap variants.
+   *
+   * @command simple-sitemap:rebuild-queue
+   *
+   * @usage drush simple-sitemap:rebuild-queue
+   *   Rebuild the sitemap queue for all sitemap variants.
+   *
+   * @validate-module-enabled simple_sitemap
+   *
+   * @aliases ssr, simple-sitemap-rebuild-queue
+   */
+  public function rebuildQueue() {
+    $this->generator->rebuildQueue();
+  }
+
+}

+ 70 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Controller/SimplesitemapController.php

@@ -0,0 +1,70 @@
+<?php
+
+namespace Drupal\simple_sitemap\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Drupal\simple_sitemap\Simplesitemap;
+use Symfony\Component\HttpFoundation\Request;
+use Drupal\simple_sitemap\SimplesitemapManager;
+
+/**
+ * Class SimplesitemapController
+ * @package Drupal\simple_sitemap\Controller
+ */
+class SimplesitemapController extends ControllerBase {
+
+  /**
+   * @var \Drupal\simple_sitemap\Simplesitemap
+   */
+  protected $generator;
+
+  /**
+   * SimplesitemapController constructor.
+   * @param \Drupal\simple_sitemap\Simplesitemap $generator
+   */
+  public function __construct(Simplesitemap $generator) {
+    $this->generator = $generator;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('simple_sitemap.generator')
+    );
+  }
+
+  /**
+   * Returns the whole sitemap variant, its requested chunk,
+   * or its sitemap index file.
+   * Caches the response in case of expected output, prevents caching otherwise.
+   *
+   * @param string $variant
+   *  Optional name of sitemap variant.
+   *  @see \hook_simple_sitemap_variants_alter()
+   *  @see SimplesitemapManager::getSitemapVariants()
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *  The request object.
+   *
+   * @throws NotFoundHttpException
+   *
+   * @return object
+   *  Returns an XML response.
+   */
+  public function getSitemap(Request $request, $variant = NULL) {
+    $output = $this->generator->setVariants($variant)->getSitemap($request->query->getInt('page'));
+    if (!$output) {
+      throw new NotFoundHttpException();
+    }
+
+    return new Response($output, Response::HTTP_OK, [
+      'content-type' => 'application/xml',
+      'X-Robots-Tag' => 'noindex', // Tell search engines not to index the sitemap itself.
+    ]);
+  }
+}

+ 164 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/EntityHelper.php

@@ -0,0 +1,164 @@
+<?php
+
+namespace Drupal\simple_sitemap;
+
+use Drupal\Core\Entity\ContentEntityTypeInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Url;
+
+/**
+ * Class EntityHelper
+ * @package Drupal\simple_sitemap
+ */
+class EntityHelper {
+
+  /**
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $db;
+
+  /**
+   * EntityHelper constructor.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
+   * @param \Drupal\Core\Database\Connection $database
+   */
+  public function __construct(EntityTypeManagerInterface $entityTypeManager, Connection $database) {
+    $this->entityTypeManager = $entityTypeManager;
+    $this->db = $database;
+  }
+
+  /**
+   * Gets an entity's bundle name.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   * @return string
+   */
+  public function getEntityInstanceBundleName(EntityInterface $entity) {
+    return $entity->getEntityTypeId() === 'menu_link_content'
+      // Menu fix.
+      ? $entity->getMenuName() : $entity->bundle();
+  }
+
+  /**
+   * Gets the entity type id for a bundle.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   * @return null|string
+   */
+  public function getBundleEntityTypeId(EntityInterface $entity) {
+    return $entity->getEntityTypeId() === 'menu'
+      // Menu fix.
+      ? 'menu_link_content' : $entity->getEntityType()->getBundleOf();
+  }
+
+  /**
+   * Returns objects of entity types that can be indexed.
+   *
+   * @return array
+   *   Objects of entity types that can be indexed by the sitemap.
+   */
+  public function getSupportedEntityTypes() {
+
+    /** @var \Drupal\Core\Entity\ContentEntityTypeInterface[] $entity_types */
+    $entity_types = $this->entityTypeManager->getDefinitions();
+    foreach ($entity_types as $entity_type_id => $entity_type) {
+      if (!$entity_type instanceof ContentEntityTypeInterface
+        || !method_exists($entity_type, 'getBundleEntityType')
+        || !$entity_type->hasLinkTemplate('canonical')) {
+        unset($entity_types[$entity_type_id]);
+      }
+    }
+    return $entity_types;
+  }
+
+  /**
+   * Checks whether an entity type does not provide bundles.
+   *
+   * @param string $entity_type_id
+   * @return bool
+   */
+  public function entityTypeIsAtomic($entity_type_id) {
+
+    // Menu fix.
+    if ($entity_type_id === 'menu_link_content') {
+      return FALSE;
+    }
+
+    $entity_types = $this->entityTypeManager->getDefinitions();
+
+    if (!isset($entity_types[$entity_type_id])) {
+      // todo: Throw exception.
+    }
+
+    return empty($entity_types[$entity_type_id]->getBundleEntityType()) ? TRUE : FALSE;
+  }
+
+  /**
+   * @param \Drupal\Core\Url $url_object
+   * @return \Drupal\Core\Entity\EntityInterface|null
+   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+   */
+  public function getEntityFromUrlObject(Url $url_object) {
+    return $url_object->isRouted()
+    && !empty($route_parameters = $url_object->getRouteParameters())
+    && $this->entityTypeManager->getDefinition($entity_type_id = key($route_parameters), FALSE)
+      ? $this->entityTypeManager->getStorage($entity_type_id)
+        ->load($route_parameters[$entity_type_id])
+      : NULL;
+  }
+
+  /**
+   * @param string $entity_type_id
+   * @param string|null $bundle_name
+   * @return array|int
+   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+   */
+  public function getEntityInstanceIds($entity_type_id, $bundle_name = NULL) {
+    $sitemap_entity_types = $this->getSupportedEntityTypes();
+    if (!isset($sitemap_entity_types[$entity_type_id])) {
+      return [];
+    }
+
+    $entity_query = $this->entityTypeManager->getStorage($entity_type_id)->getQuery();
+    if (!$this->entityTypeIsAtomic($entity_type_id) && NULL !== $bundle_name) {
+      $keys = $sitemap_entity_types[$entity_type_id]->getKeys();
+
+      // Menu fix.
+      $keys['bundle'] = $entity_type_id === 'menu_link_content' ? 'menu_name' : $keys['bundle'];
+
+      $entity_query->condition($keys['bundle'], $bundle_name);
+    }
+
+    return $entity_query->execute();
+  }
+
+  /**
+   * @param string $entity_type_name
+   * @param string $entity_id
+   * @return array
+   */
+  public function getEntityImageUrls($entity_type_name, $entity_id) {
+    $query = $this->db->select('file_managed', 'fm');
+    $query->fields('fm', ['uri']);
+    $query->join('file_usage', 'fu', 'fu.fid = fm.fid');
+    $query->condition('fm.filemime', 'image/%', 'LIKE');
+    $query->condition('fu.type', $entity_type_name);
+    $query->condition('fu.id', $entity_id);
+
+    foreach ($query->execute() as $row) {
+      $imageUris[] = file_create_url($row->uri);
+    }
+
+    return !empty($imageUris) ? $imageUris : [];
+  }
+
+}

+ 539 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Form/FormHelper.php

@@ -0,0 +1,539 @@
+<?php
+
+namespace Drupal\simple_sitemap\Form;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\simple_sitemap\EntityHelper;
+use Drupal\simple_sitemap\Simplesitemap;
+use Drupal\Core\Session\AccountProxyInterface;
+
+/**
+ * Class FormHelper
+ * @package Drupal\simple_sitemap\Form
+ */
+class FormHelper {
+  use StringTranslationTrait;
+
+  const PRIORITY_DEFAULT = 0.5;
+  const PRIORITY_HIGHEST = 10;
+  const PRIORITY_DIVIDER = 10;
+
+  /**
+   * @var \Drupal\simple_sitemap\Simplesitemap
+   */
+  protected $generator;
+
+  /**
+   * @var \Drupal\simple_sitemap\EntityHelper
+   */
+  protected $entityHelper;
+
+  /**
+   * @var \Drupal\Core\Session\AccountProxyInterface
+   */
+  protected $currentUser;
+
+  /**
+   * @var \Drupal\Core\Form\FormState
+   */
+  protected $formState;
+
+  /**
+   * @var string|null
+   */
+  protected $entityCategory;
+
+  /**
+   * @var string
+   */
+  protected $entityTypeId;
+
+  /**
+   * @var string
+   */
+  protected $bundleName;
+
+  /**
+   * @var string
+   */
+  protected $instanceId;
+
+  /**
+   * @var string
+   */
+  protected $variant;
+
+  /**
+   * @var array
+   */
+  protected $bundleSettings;
+
+  protected static $allowedFormOperations = [
+    'default',
+    'edit',
+    'add',
+    'register',
+  ];
+
+  protected static $changefreqValues = [
+    'always',
+    'hourly',
+    'daily',
+    'weekly',
+    'monthly',
+    'yearly',
+    'never',
+  ];
+
+  protected static $valuesToCheck = [
+    'simple_sitemap_variant',
+    'simple_sitemap_index_content',
+    'simple_sitemap_priority',
+    'simple_sitemap_changefreq',
+    'simple_sitemap_include_images',
+    'simple_sitemap_regenerate_now',
+  ];
+
+  /**
+   * FormHelper constructor.
+   * @param \Drupal\simple_sitemap\Simplesitemap $generator
+   * @param \Drupal\simple_sitemap\EntityHelper $entityHelper
+   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
+   */
+  public function __construct(
+    Simplesitemap $generator,
+    EntityHelper $entityHelper,
+    AccountProxyInterface $current_user
+  ) {
+    $this->generator = $generator;
+    $this->entityHelper = $entityHelper;
+    $this->currentUser = $current_user;
+  }
+
+  /**
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   * @return bool
+   */
+  public function processForm(FormStateInterface $form_state) {
+    $this->formState = $form_state;
+    $this->cleanUpFormInfo();
+    $this->getEntityDataFromFormEntity();
+    $this->negotiateVariant();
+    return $this->supports();
+  }
+
+  /**
+   * @param string $entity_category
+   * @return $this
+   */
+  public function setEntityCategory($entity_category) {
+    $this->entityCategory = $entity_category;
+    return $this;
+  }
+
+  /**
+   * @return null|string
+   */
+  public function getEntityCategory() {
+    return $this->entityCategory;
+  }
+
+  /**
+ * @param string $entity_type_id
+ * @return $this
+ */
+  public function setEntityTypeId($entity_type_id) {
+    $this->entityTypeId = $entity_type_id;
+    return $this;
+  }
+
+  /**
+   * @return string
+   */
+  public function getEntityTypeId() {
+    return $this->entityTypeId;
+  }
+
+  /**
+   * @param string $bundle_name
+   * @return $this
+   */
+  public function setBundleName($bundle_name) {
+    $this->bundleName = $bundle_name;
+    return $this;
+  }
+
+  /**
+   * @return string
+   */
+  public function getBundleName() {
+    return $this->bundleName;
+  }
+
+  /**
+   * @param string $instance_id
+   * @return $this
+   */
+  public function setInstanceId($instance_id) {
+    $this->instanceId = $instance_id;
+    return $this;
+  }
+
+  /**
+   * @return string
+   */
+  public function getInstanceId() {
+    return $this->instanceId;
+  }
+
+  /**
+   * @return bool
+   */
+  protected function supports() {
+
+    // Do not alter the form if user lacks certain permissions.
+    if (!$this->currentUser->hasPermission('administer sitemap settings')) {
+      return FALSE;
+    }
+
+    // Do not alter the form if it is irrelevant to sitemap generation.
+    elseif (empty($this->getEntityCategory())) {
+      return FALSE;
+    }
+
+    // Do not alter the form if entity is not enabled in sitemap settings.
+    elseif (!$this->generator->entityTypeIsEnabled($this->getEntityTypeId())) {
+      return FALSE;
+    }
+
+    // Do not alter the form, if sitemap is disabled for the entity type of this
+    // entity instance.
+    elseif ($this->getEntityCategory() === 'instance') {
+      if (NULL === $this->variant || !$this->generator
+          ->setVariants($this->variant)
+          ->bundleIsIndexed($this->getEntityTypeId(), $this->getBundleName())) {
+        return FALSE;
+      }
+    }
+
+    return TRUE;
+  }
+
+  /**
+   * @param array $form_fragment
+   */
+  public function displayRegenerateNow(&$form_fragment) {
+    $form_fragment['simple_sitemap_regenerate_now'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Regenerate sitemap after hitting <em>Save</em>'),
+      '#description' => $this->t('This setting will regenerate all sitemaps including the above changes.'),
+      '#default_value' => FALSE,
+    ];
+    if ($this->generator->getSetting('cron_generate')) {
+      $form_fragment['simple_sitemap_regenerate_now']['#description'] .= '</br>' . $this->t('Otherwise the sitemap will be regenerated during a future cron run.');
+    }
+  }
+
+  protected function negotiateVariant() {
+    $all_bundle_settings = $this->generator->setVariants(TRUE)
+      ->getBundleSettings($this->getEntityTypeId(), $this->getBundleName(), FALSE, TRUE);
+    $this->bundleSettings = NULL !== ($variant = key($all_bundle_settings))
+      ? $all_bundle_settings[$variant]
+      : [];
+    $this->variant = $variant;
+  }
+
+  /**
+   * @param array $form_fragment
+   * @param bool $multiple
+   * @return $this
+   */
+  public function displayEntitySettings(&$form_fragment, $multiple = FALSE) {
+    $prefix = $multiple ? $this->getEntityTypeId() . '_' : '';
+
+    $settings = $this->getEntityCategory() === 'instance' && NULL !== $this->variant && NULL !== $this->getInstanceId()
+      ? $this->generator->setVariants($this->variant)->getEntityInstanceSettings($this->getEntityTypeId(), $this->getInstanceId())
+      : $this->bundleSettings;
+    Simplesitemap::supplementDefaultSettings('entity', $settings);
+
+    $bundle_name = !empty($this->getBundleName()) ? $this->getBundleName() : $this->t('undefined');
+
+    // Index
+    if (!$multiple) {
+      $form_fragment[$prefix . 'simple_sitemap_index_content'] = [
+        '#type' => 'radios',
+        '#default_value' => (int) $settings['index'],
+        '#options' => [
+          0 => $this->getEntityCategory() === 'instance'
+            ? $this->t('Do not index this @bundle entity', ['@bundle' => $bundle_name])
+            : $this->t('Do not index entities of this type'),
+          1 => $this->getEntityCategory() === 'instance'
+            ? $this->t('Index this @bundle entity', ['@bundle' => $bundle_name])
+            : $this->t('Index entities of this type'),
+        ],
+      ];
+
+      if ($this->getEntityCategory() === 'instance' && isset($this->bundleSettings['index'])) {
+        $form_fragment[$prefix . 'simple_sitemap_index_content']['#options'][(int) $this->bundleSettings['index']] .= ' <em>(' . $this->t('default') . ')</em>';
+      }
+    }
+
+    // Variant
+    $form_fragment[$prefix . 'simple_sitemap_variant'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Sitemap variant'),
+      '#description' => $this->t('The sitemap variant entities of this type are to be indexed in.'),
+      '#options' => array_map(
+        function($variant) { return $this->t($variant['label']); },
+        $this->generator->getSitemapManager()->getSitemapVariants(NULL, FALSE)
+      ),
+      '#default_value' => $this->variant,
+      '#states' => [
+        'visible' => !$multiple
+          ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
+          : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
+        'required' => !$multiple // todo Should implement server side validation on top of this.
+          ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
+          : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
+      ],
+      '#disabled' => $this->getEntityCategory() === 'instance'
+    ];
+
+    // Priority
+    $form_fragment[$prefix . 'simple_sitemap_priority'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Priority'),
+      '#description' => $this->getEntityCategory() === 'instance'
+        ? $this->t('The priority this @bundle entity will have in the eyes of search engine bots.', ['@bundle' => $bundle_name])
+        : $this->t('The priority entities of this type will have in the eyes of search engine bots.'),
+      '#default_value' => $settings['priority'],
+      '#options' => $this->getPrioritySelectValues(),
+      '#states' => [
+        'visible' => !$multiple
+          ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
+          : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
+      ],
+    ];
+
+    if ($this->getEntityCategory() === 'instance' && isset($this->bundleSettings['priority'])) {
+      $form_fragment[$prefix . 'simple_sitemap_priority']['#options'][$this->formatPriority($this->bundleSettings['priority'])] .= ' (' . $this->t('default') . ')';
+    }
+
+    // Changefreq
+    $form_fragment[$prefix . 'simple_sitemap_changefreq'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Change frequency'),
+      '#description' => $this->getEntityCategory() === 'instance'
+      ? $this->t('The frequency with which this @bundle entity changes. Search engine bots may take this as an indication of how often to index it.', ['@bundle' => $bundle_name])
+      : $this->t('The frequency with which entities of this type change. Search engine bots may take this as an indication of how often to index them.'),
+      '#default_value' => $settings['changefreq'],
+      '#options' => $this->getChangefreqSelectValues(),
+      '#states' => [
+        'visible' => !$multiple
+          ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
+          : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
+      ],
+    ];
+
+    if ($this->getEntityCategory() === 'instance' && isset($this->bundleSettings['changefreq'])) {
+      $form_fragment[$prefix . 'simple_sitemap_changefreq']['#options'][$this->bundleSettings['changefreq']] .= ' (' . $this->t('default') . ')';
+    }
+
+    // Images
+    $form_fragment[$prefix . 'simple_sitemap_include_images'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Include images'),
+      '#description' => $this->getEntityCategory() === 'instance'
+        ? $this->t('Determines if images referenced by this @bundle entity should be included in the sitemap.', ['@bundle' => $bundle_name])
+        : $this->t('Determines if images referenced by entities of this type should be included in the sitemap.'),
+      '#default_value' => (int) $settings['include_images'],
+      '#options' => [0 => $this->t('No'), 1 => $this->t('Yes')],
+      '#states' => [
+        'visible' => !$multiple
+          ? [':input[name="' . $prefix . 'simple_sitemap_index_content"]' => ['value' => 1]]
+          : [':input[name="' . $prefix . 'enabled"]' => ['checked' => TRUE]],
+      ],
+    ];
+
+    if ($this->getEntityCategory() === 'instance' && isset($this->bundleSettings['include_images'])) {
+      $form_fragment[$prefix . 'simple_sitemap_include_images']['#options'][(int) $this->bundleSettings['include_images']] .= ' (' . $this->t('default') . ')';
+    }
+
+    return $this;
+  }
+
+  /**
+   * Checks if this particular form is a bundle form, or a bundle instance form
+   * and gathers sitemap settings from the database.
+   *
+   * @return bool
+   *   TRUE if this is a bundle or bundle instance form, FALSE otherwise.
+   */
+  protected function getEntityDataFromFormEntity() {
+    if (!$form_entity = $this->getFormEntity()) {
+      return FALSE;
+    }
+
+    $entity_type_id = $form_entity->getEntityTypeId();
+    $sitemap_entity_types = $this->entityHelper->getSupportedEntityTypes();
+    if (isset($sitemap_entity_types[$entity_type_id])) {
+      $this->setEntityCategory('instance');
+    }
+    else {
+      /** @var \Drupal\Core\Entity\EntityType $sitemap_entity_type */
+      foreach ($sitemap_entity_types as $sitemap_entity_type) {
+        if ($sitemap_entity_type->getBundleEntityType() === $entity_type_id) {
+          $this->setEntityCategory('bundle');
+          break;
+        }
+      }
+    }
+
+    // Menu fix.
+    $this->setEntityCategory(NULL === $this->getEntityCategory() && $entity_type_id === 'menu' ? 'bundle' : $this->getEntityCategory());
+
+    switch ($this->getEntityCategory()) {
+      case 'bundle':
+        $this->setEntityTypeId($this->entityHelper->getBundleEntityTypeId($form_entity));
+        $this->setBundleName($form_entity->id());
+        $this->setInstanceId(NULL);
+        break;
+
+      case 'instance':
+        $this->setEntityTypeId($entity_type_id);
+        $this->setBundleName($this->entityHelper->getEntityInstanceBundleName($form_entity));
+        // New menu link's id is '' instead of NULL, hence checking for empty.
+        $this->setInstanceId(!empty($form_entity->id()) ? $form_entity->id() : NULL);
+        break;
+
+      default:
+        return FALSE;
+    }
+    return TRUE;
+  }
+
+  /**
+   * Gets the object entity of the form if available.
+   *
+   * @return \Drupal\Core\Entity\Entity|false
+   *   Entity or FALSE if non-existent or if form operation is
+   *   'delete'.
+   */
+  protected function getFormEntity() {
+    $form_object = $this->formState->getFormObject();
+    if (NULL !== $form_object
+      && method_exists($form_object, 'getOperation')
+      && method_exists($form_object, 'getEntity')
+      && in_array($form_object->getOperation(), self::$allowedFormOperations)) {
+      return $form_object->getEntity();
+    }
+    return FALSE;
+  }
+
+  /**
+   * Removes gathered form information from service object.
+   *
+   * Needed because this service may contain form info from the previous
+   * operation when revived from the container.
+   */
+  protected function cleanUpFormInfo() {
+    $this->entityCategory = NULL;
+    $this->entityTypeId = NULL;
+    $this->bundleName = NULL;
+    $this->instanceId = NULL;
+    $this->variant = NULL;
+    $this->bundleSettings = NULL;
+  }
+
+  /**
+   * Gets new entity Id after entity creation.
+   * To be used in an entity form submit.
+   *
+   * @return int
+   *   Entity ID.
+   */
+  public function getFormEntityId() {
+    return $this->formState->getFormObject()->getEntity()->id();
+  }
+
+  /**
+   * Checks if simple_sitemap values have been changed after submitting the form.
+   * To be used in an entity form submit.
+   *
+   * @param $form
+   * @param array $values
+   *
+   * @return bool
+   *   TRUE if simple_sitemap form values have been altered by the user.
+   */
+  public function valuesChanged($form, array $values) {
+    foreach (self::$valuesToCheck as $field_name) {
+      if (!isset($form['simple_sitemap'][$field_name]['#default_value'])
+        || (isset($values[$field_name]) && $values[$field_name] != $form['simple_sitemap'][$field_name]['#default_value'])) {
+        return TRUE;
+      }
+    }
+    return FALSE;
+  }
+
+  /**
+   * Gets the values needed to display the priority dropdown setting.
+   *
+   * @return array
+   *   Select options.
+   */
+  public function getPrioritySelectValues() {
+    $options = [];
+    foreach (range(0, self::PRIORITY_HIGHEST) as $value) {
+      $value = $this->formatPriority($value / self::PRIORITY_DIVIDER);
+      $options[$value] = $value;
+    }
+    return $options;
+  }
+
+  /**
+   * Gets the values needed to display the changefreq dropdown setting.
+   *
+   * @return array
+   *   Select options.
+   */
+  public function getChangefreqSelectValues() {
+    $options = ['' => $this->t('- Not specified -')];
+    foreach (self::$changefreqValues as $setting) {
+      $options[$setting] = $this->t($setting);
+    }
+    return $options;
+  }
+
+  /**
+   * @return array
+   */
+  public static function getChangefreqOptions() {
+    return self::$changefreqValues;
+  }
+
+  /**
+   * @param string $priority
+   * @return string
+   */
+  public function formatPriority($priority) {
+    return number_format((float) $priority, 1, '.', '');
+  }
+
+  /**
+   * @param string|int $priority
+   * @return bool
+   */
+  public static function isValidPriority($priority) {
+    return is_numeric($priority) && $priority >= 0 && $priority <= 1;
+  }
+
+  /**
+   * @param string $changefreq
+   * @return bool
+   */
+  public static function isValidChangefreq($changefreq) {
+    return in_array($changefreq, self::$changefreqValues);
+  }
+}

Різницю між файлами не показано, бо вона завелика
+ 72 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Form/SimplesitemapCustomLinksForm.php


+ 192 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Form/SimplesitemapEntitiesForm.php

@@ -0,0 +1,192 @@
+<?php
+
+namespace Drupal\simple_sitemap\Form;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\simple_sitemap\Simplesitemap;
+use Drupal\simple_sitemap\EntityHelper;
+
+/**
+ * Class SimplesitemapEntitiesForm
+ * @package Drupal\simple_sitemap\Form
+ */
+class SimplesitemapEntitiesForm extends SimplesitemapFormBase {
+
+  /**
+   * @var \Drupal\simple_sitemap\EntityHelper
+   */
+  protected $entityHelper;
+
+  /**
+   * SimplesitemapEntitiesForm constructor.
+   * @param \Drupal\simple_sitemap\Simplesitemap $generator
+   * @param \Drupal\simple_sitemap\Form\FormHelper $form_helper
+   * @param \Drupal\simple_sitemap\EntityHelper $entity_helper
+   */
+  public function __construct(
+    Simplesitemap $generator,
+    FormHelper $form_helper,
+    EntityHelper $entity_helper
+  ) {
+    parent::__construct(
+      $generator,
+      $form_helper
+    );
+    $this->entityHelper = $entity_helper;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('simple_sitemap.generator'),
+      $container->get('simple_sitemap.form_helper'),
+      $container->get('simple_sitemap.entity_helper')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'simple_sitemap_entities_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+
+    $form['simple_sitemap_entities']['#prefix'] = $this->getDonationText();
+
+    $form['simple_sitemap_entities']['entities'] = [
+      '#title' => $this->t('Sitemap entities'),
+      '#type' => 'fieldset',
+      '#markup' => '<div class="description">' . $this->t('Simple XML sitemap settings will be added only to entity forms of entity types enabled here. For all entity types featuring bundles (e.g. <em>node</em>) sitemap settings have to be set on their bundle pages (e.g. <em>page</em>).') . '</div>',
+    ];
+
+    $form['#attached']['library'][] = 'simple_sitemap/sitemapEntities';
+    $form['#attached']['drupalSettings']['simple_sitemap'] = ['all_entities' => [], 'atomic_entities' => []];
+
+    $entity_type_labels = [];
+    foreach ($this->entityHelper->getSupportedEntityTypes() as $entity_type_id => $entity_type) {
+      $entity_type_labels[$entity_type_id] = $entity_type->getLabel() ? : $entity_type_id;
+    }
+    asort($entity_type_labels);
+
+    $this->formHelper->processForm($form_state);
+
+    $bundle_settings = $this->generator->getBundleSettings();
+
+    foreach ($entity_type_labels as $entity_type_id => $entity_type_label) {
+;
+      $enabled_entity_type = $this->generator->entityTypeIsEnabled($entity_type_id);
+      $atomic_entity_type = $this->entityHelper->entityTypeIsAtomic($entity_type_id);
+      $css_entity_type_id = str_replace('_', '-', $entity_type_id);
+
+      $form['simple_sitemap_entities']['entities'][$entity_type_id] = [
+        '#type' => 'details',
+        '#title' => $entity_type_label,
+        '#open' => $enabled_entity_type,
+      ];
+
+      $form['simple_sitemap_entities']['entities'][$entity_type_id][$entity_type_id . '_enabled'] = [
+        '#type' => 'checkbox',
+        '#title' => $this->t('Enable @entity_type_label <em>(@entity_type_id)</em> support', ['@entity_type_label' => strtolower($entity_type_label), '@entity_type_id' => $entity_type_id]),
+        '#description' => $atomic_entity_type
+          ? $this->t('Sitemap settings for the entity type <em>@entity_type_label</em> can be set below and overridden on its entity pages.', ['@entity_type_label' => strtolower($entity_type_label)])
+          : $this->t('Sitemap settings for the entity type <em>@entity_type_label</em> can be set on its bundle pages and overridden on its entity pages.', ['@entity_type_label' => strtolower($entity_type_label)]),
+        '#default_value' => $enabled_entity_type,
+      ];
+
+      if ($form['simple_sitemap_entities']['entities'][$entity_type_id][$entity_type_id . '_enabled']['#default_value']) {
+
+        $bundle_info = '';
+        $indexed_bundles = isset($bundle_settings[$entity_type_id])
+          ? implode(array_keys(array_filter($bundle_settings[$entity_type_id], function ($val) {return $val['index'];})), ', ')
+          : '';
+
+        if (!$atomic_entity_type) {
+          $bundle_info .= '<div id="indexed-bundles-' . $css_entity_type_id . '">'
+            . (!empty($indexed_bundles)
+              ? $this->t("<em>@entity_type_label</em> bundles set to be indexed:", ['@entity_type_label' => ucfirst(strtolower($entity_type_label))]) . ' ' . '<em>' . $indexed_bundles . '</em>'
+              : $this->t('No <em>@entity_type_label</em> bundles are set to be indexed yet.', ['@entity_type_label' => strtolower($entity_type_label)]))
+            . '</div>';
+        }
+
+        if (!empty($indexed_bundles)) {
+          $bundle_info .= '<div id="warning-' . $css_entity_type_id . '">'
+            . ($atomic_entity_type
+              ? $this->t("<strong>Warning:</strong> This entity type's sitemap settings including per-entity overrides will be deleted after hitting <em>Save</em>.")
+              : $this->t("<strong>Warning:</strong> The sitemap settings for <em>@bundles</em> and any per-entity overrides will be deleted after hitting <em>Save</em>.",
+                ['@bundles' => $indexed_bundles]))
+            . '</div>';
+        }
+
+        $form['simple_sitemap_entities']['entities'][$entity_type_id][$entity_type_id . '_enabled']['#suffix'] = $bundle_info;
+      }
+
+      $form['#attached']['drupalSettings']['simple_sitemap']['all_entities'][] = $css_entity_type_id;
+
+      if ($atomic_entity_type) {
+        $this->formHelper->setEntityCategory('bundle')
+          ->setEntityTypeId($entity_type_id)
+          ->setBundleName($entity_type_id)
+          ->displayEntitySettings($form['simple_sitemap_entities']['entities'][$entity_type_id][$entity_type_id . '_settings'], TRUE);
+        $form['#attached']['drupalSettings']['simple_sitemap']['atomic_entities'][] = $css_entity_type_id;
+      }
+    }
+
+    $this->formHelper->displayRegenerateNow($form['simple_sitemap_entities']['entities']);
+
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $values = $form_state->getValues();
+    foreach ($values as $field_name => $value) {
+      if (substr($field_name, -strlen('_enabled')) === '_enabled') {
+        $entity_type_id = substr($field_name, 0, -8);
+        if ($value) {
+          $this->generator->enableEntityType($entity_type_id);
+          if ($this->entityHelper->entityTypeIsAtomic($entity_type_id)) {
+
+            // Deleting bundle settings for old bundle.
+            // See simple_sitemap.module::simple_sitemap_entity_form_submit().
+            // todo: This will not be necessary if "multiple variants pro bundle" is implemented.
+            if (isset($form['simple_sitemap_entities']['entities'][$entity_type_id][$entity_type_id . '_settings'][$entity_type_id . '_simple_sitemap_variant']['#default_value'])) {
+              $old_variant = $form['simple_sitemap_entities']['entities'][$entity_type_id][$entity_type_id . '_settings'][$entity_type_id . '_simple_sitemap_variant']['#default_value'];
+              if ($old_variant !== $values[$entity_type_id . '_simple_sitemap_variant']) {
+                $this->generator->setVariants($old_variant)->removeBundleSettings($entity_type_id);
+              }
+            }
+
+            $this->generator
+              ->setVariants($values[$entity_type_id . '_simple_sitemap_variant'])
+              ->setBundleSettings($entity_type_id, $entity_type_id, [
+                'index' => TRUE,
+                'priority' => $values[$entity_type_id . '_simple_sitemap_priority'],
+                'changefreq' => $values[$entity_type_id . '_simple_sitemap_changefreq'],
+                'include_images' => (bool) $values[$entity_type_id . '_simple_sitemap_include_images'],
+            ]);
+          }
+        }
+        else {
+          $this->generator->disableEntityType($entity_type_id);
+        }
+      }
+    }
+    parent::submitForm($form, $form_state);
+
+    // Regenerate sitemaps according to user setting.
+    if ($form_state->getValue('simple_sitemap_regenerate_now')) {
+      $this->generator->generateSitemap();
+    }
+  }
+
+}

+ 62 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Form/SimplesitemapFormBase.php

@@ -0,0 +1,62 @@
+<?php
+
+namespace Drupal\simple_sitemap\Form;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Form\ConfigFormBase;
+use Drupal\simple_sitemap\Simplesitemap;
+
+/**
+ * Class SimplesitemapFormBase
+ * @package Drupal\simple_sitemap\Form
+ */
+abstract class SimplesitemapFormBase extends ConfigFormBase {
+
+  /**
+   * @var \Drupal\simple_sitemap\Simplesitemap
+   */
+  protected $generator;
+
+  /**
+   * @var \Drupal\simple_sitemap\Form\FormHelper
+   */
+  protected $formHelper;
+
+  /**
+   * SimplesitemapFormBase constructor.
+   * @param \Drupal\simple_sitemap\Simplesitemap $generator
+   * @param \Drupal\simple_sitemap\Form\FormHelper $form_helper
+   */
+  public function __construct(
+    Simplesitemap $generator,
+    FormHelper $form_helper
+  ) {
+    $this->generator = $generator;
+    $this->formHelper = $form_helper;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('simple_sitemap.generator'),
+      $container->get('simple_sitemap.form_helper')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEditableConfigNames() {
+    return ['simple_sitemap.settings'];
+  }
+
+  /**
+   *
+   */
+  protected function getDonationText() {
+    return '<div class="description">' . $this->t('If you would like to say thanks and support the development of this module, a <a target="_blank" href="@url">donation</a> will be much appreciated.', ['@url' => 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=5AFYRSBLGSC3W']) . '</div>';
+  }
+
+}

+ 409 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Form/SimplesitemapSettingsForm.php

@@ -0,0 +1,409 @@
+<?php
+
+namespace Drupal\simple_sitemap\Form;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\simple_sitemap\Simplesitemap;
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Database\Connection;
+
+/**
+ * Class SimplesitemapSettingsForm
+ * @package Drupal\simple_sitemap\Form
+ */
+class SimplesitemapSettingsForm extends SimplesitemapFormBase {
+
+  /**
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $db;
+
+  /**
+   * SimplesitemapSettingsForm constructor.
+   * @param \Drupal\simple_sitemap\Simplesitemap $generator
+   * @param \Drupal\simple_sitemap\Form\FormHelper $form_helper
+   * @param \Drupal\Core\Language\LanguageManager $language_manager
+   * @param \Drupal\Core\Database\Connection $database
+   */
+  public function __construct(
+    Simplesitemap $generator,
+    FormHelper $form_helper,
+    LanguageManager $language_manager,
+    Connection $database
+  ) {
+    parent::__construct(
+      $generator,
+      $form_helper
+    );
+    $this->languageManager = $language_manager;
+    $this->db = $database;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('simple_sitemap.generator'),
+      $container->get('simple_sitemap.form_helper'),
+      $container->get('language_manager'),
+      $container->get('database')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'simple_sitemap_settings_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+
+    $form['simple_sitemap_settings']['#prefix'] = $this->getDonationText();
+
+    $form['simple_sitemap_settings']['status'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t('Sitemap status'),
+      '#markup' => '<div class="description">' . $this->t('Sitemaps can be regenerated on demand here.') . '</div>',
+      '#description' => $this->t('Variants can be configured <a href="@url">here</a>.', ['@url' => $GLOBALS['base_url'] . '/admin/config/search/simplesitemap/variants']),
+    ];
+
+    $form['simple_sitemap_settings']['status']['actions'] = [
+      '#prefix' => '<div class="clearfix"><div class="form-item">',
+      '#suffix' => '</div></div>',
+    ];
+
+    $form['simple_sitemap_settings']['status']['actions']['regenerate_submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Generate from queue'),
+      '#submit' => ['::generateSitemap'],
+      '#validate' => [],
+    ];
+
+//    $form['simple_sitemap_settings']['status']['actions']['regenerate_backend_submit'] = [
+//      '#type' => 'submit',
+//      '#value' => $this->t('Generate from queue (background)'),
+//      '#submit' => ['::generateSitemapBackend'],
+//      '#validate' => [],
+//    ];
+
+    $form['simple_sitemap_settings']['status']['actions']['rebuild_queue_submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Rebuild queue'),
+      '#submit' => ['::rebuildQueue'],
+      '#validate' => [],
+    ];
+
+    $form['simple_sitemap_settings']['status']['progress'] = [
+      '#prefix' => '<div class="clearfix">',
+      '#suffix' => '</div>',
+    ];
+
+    $form['simple_sitemap_settings']['status']['progress']['title']['#markup'] = $this->t('Progress of sitemap regeneration');
+
+    $queue_worker = $this->generator->getQueueWorker();
+    $total_count = $queue_worker->getInitialElementCount();
+    if (!empty($total_count)) {
+      $indexed_count = $queue_worker->getProcessedElementCount();
+      $percent = round(100 * $indexed_count / $total_count);
+
+      // With all results processed, there still may be some stashed results to be indexed.
+      $percent = $percent === 100 && $queue_worker->generationInProgress() ? 99 : $percent;
+
+      $index_progress = [
+        '#theme' => 'progress_bar',
+        '#percent' => $percent,
+        '#message' => t('@indexed out of @total items have been processed.', ['@indexed' => $indexed_count, '@total' => $total_count]),
+      ];
+      $form['simple_sitemap_settings']['status']['progress']['bar']['#markup'] = render($index_progress);
+    }
+    else {
+      $form['simple_sitemap_settings']['status']['progress']['bar']['#markup'] = '<div class="description">' . $this->t('There are no items to be indexed.') . '</div>';
+    }
+
+    $sitemap_manager = $this->generator->getSitemapManager();
+    $sitemap_statuses = $this->fetchSitemapInstanceStatuses();
+
+    foreach ($sitemap_manager->getSitemapTypes() as $type_name => $type_definition) {
+      if (!empty($variants = $sitemap_manager->getSitemapVariants($type_name, FALSE))) {
+        $form['simple_sitemap_settings']['status']['types'][$type_name] = [
+          '#type' => 'details',
+          '#title' => '<em>' . $type_definition['label'] . '</em> ' . $this->t('sitemaps'),
+          '#open' => !empty($variants) && count($variants) <= 5,
+          '#description' => !empty($type_definition['description']) ? '<div class="description">' . $type_definition['description'] . '</div>' : '',
+        ];
+        $form['simple_sitemap_settings']['status']['types'][$type_name]['table'] = [
+          '#type' => 'table',
+          '#header' => [$this->t('Variant'), $this->t('Status'), /*$this->t('Actions')*/],
+          '#attributes' => ['class' => ['form-item', 'clearfix']],
+        ];
+        foreach ($variants as $variant_name => $variant_definition) {
+          $row = [];
+          $row['name']['data']['#markup'] = '<span title="' . $variant_name . '">' . $variant_definition['label'] . '</span>';
+          if (!isset($sitemap_statuses[$variant_name])) {
+            $row['status'] = $this->t('pending');
+          }
+          else {
+            $url = $GLOBALS['base_url'] . '/' . $variant_name . '/sitemap.xml';
+            switch ($sitemap_statuses[$variant_name]) {
+              case 0:
+                $row['status'] = $this->t('generating');
+                break;
+              case 1:
+                $row['status']['data']['#markup'] = $this->t('<a href="@url" target="_blank">published</a>', ['@url' => $url]);
+                break;
+              case 2:
+                $row['status'] = $this->t('<a href="@url" target="_blank">published</a>, regenerating', ['@url' => $url]);
+                break;
+            }
+          }
+
+//          $row['actions'] = '';
+          $form['simple_sitemap_settings']['status']['types'][$type_name]['table']['#rows'][$variant_name] = $row;
+          unset($sitemap_statuses[$variant_name]);
+        }
+      }
+    }
+    if (empty($form['simple_sitemap_settings']['status']['types'])) {
+      $form['simple_sitemap_settings']['status']['types']['#markup'] = $this->t('No variants have been defined');
+    }
+
+/*    if (!empty($sitemap_statuses)) {
+      $form['simple_sitemap_settings']['status']['types']['&orphans'] = [
+        '#type' => 'details',
+        '#title' => $this->t('Orphans'),
+        '#open' => TRUE,
+      ];
+
+      $form['simple_sitemap_settings']['status']['types']['&orphans']['table'] = [
+        '#type' => 'table',
+        '#header' => [$this->t('Variant'), $this->t('Status'), $this->t('Actions')],
+      ];
+      foreach ($sitemap_statuses as $orphan_name => $orphan_info) {
+        $form['simple_sitemap_settings']['status']['types']['&orphans']['table']['#rows'][$orphan_name] = [
+          'name' => $orphan_name,
+          'status' => $this->t('orphaned'),
+          'actions' => '',
+        ];
+      }
+    }*/
+
+    $form['simple_sitemap_settings']['settings'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t('Settings'),
+    ];
+
+    $form['simple_sitemap_settings']['settings']['cron_generate'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Regenerate the sitemaps during cron runs'),
+      '#description' => $this->t('Uncheck this if you intend to only regenerate the sitemaps manually or via drush.'),
+      '#default_value' => $this->generator->getSetting('cron_generate', TRUE),
+    ];
+
+    $form['simple_sitemap_settings']['settings']['cron_generate_interval'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Sitemap generation interval'),
+      '#description' => $this->t('The sitemap will be generated according to this interval.'),
+      '#default_value' => $this->generator->getSetting('cron_generate_interval', 0),
+      '#options' => [
+        0 => $this->t('On every cron run'),
+        1 => $this->t('Once an hour'),
+        3 => $this->t('Once every @hours hours', ['@hours' => 3]),
+        6 => $this->t('Once every @hours hours', ['@hours' => 6]),
+        12 => $this->t('Once every @hours hours', ['@hours' => 12]),
+        24 => $this->t('Once a day'),
+        48 => $this->t('Once every @days days', ['@days' => 48/24]),
+        72 => $this->t('Once every @days days', ['@days' => 72/24]),
+        96 => $this->t('Once every @days days', ['@days' => 96/24]),
+        120 => $this->t('Once every @days days', ['@days' => 120/24]),
+        144 => $this->t('Once every @days days', ['@days' => 144/24]),
+        168 => $this->t('Once a week'),
+      ],
+      '#states' => [
+        'visible' => [
+          ':input[name="cron_generate"]' => ['checked' => TRUE],
+        ],
+      ],
+    ];
+
+    $form['simple_sitemap_settings']['settings']['languages'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Language settings'),
+      '#open' => FALSE,
+    ];
+
+    $language_options = [];
+    foreach ($this->languageManager->getLanguages() as $language) {
+      if (!$language->isDefault()) {
+        $language_options[$language->getId()] = $language->getName();
+      }
+    }
+
+    $form['simple_sitemap_settings']['settings']['languages']['skip_untranslated'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Skip non-existent translations'),
+      '#description' => $this->t('If checked, entity links are generated exclusively for languages the entity has been translated to as long as the language is not excluded below.<br/>Otherwise entity links are generated for every language installed on the site apart from languages excluded below.<br/>Bear in mind that non-entity paths like homepage will always be generated for every non-excluded language.'),
+      '#default_value' => $this->generator->getSetting('skip_untranslated', FALSE),
+    ];
+
+    $form['simple_sitemap_settings']['settings']['languages']['excluded_languages'] = [
+      '#title' => $this->t('Exclude languages'),
+      '#type' => 'checkboxes',
+      '#options' => $language_options,
+      '#description' => !empty($language_options)
+        ? $this->t('There will be no links generated for languages checked here.')
+        : $this->t('There are no languages other than the default language <a href="@url">available</a>.', ['@url' => $GLOBALS['base_url'] . '/admin/config/regional/language']),
+      '#default_value' => $this->generator->getSetting('excluded_languages', []),
+    ];
+
+    $form['simple_sitemap_settings']['advanced'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Advanced settings'),
+      '#open' => TRUE,
+    ];
+
+    $variants = $this->generator->getSitemapManager()->getSitemapVariants(NULL, FALSE);
+    $default_variant = $this->generator->getSetting('default_variant');
+    $form['simple_sitemap_settings']['advanced']['default_variant'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Default sitemap variant'),
+      '#description' => $this->t('This sitemap variant will be available under <em>/sitemap.xml</em> in addition to its default path <em>/variant-name/sitemap.xml</em>.<br/>Variants can be configured <a href="@url">here</a>.', ['@url' => $GLOBALS['base_url'] . '/admin/config/search/simplesitemap/variants']),
+      '#default_value' => isset($variants[$default_variant]) ? $default_variant : '',
+      '#options' => ['' => $this->t('- None -')] + array_map(function($variant) { return $this->t($variant['label']); }, $variants),
+      ];
+
+    $form['simple_sitemap_settings']['advanced']['base_url'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Default base URL'),
+      '#default_value' => $this->generator->getSetting('base_url', ''),
+      '#size' => 30,
+      '#description' => $this->t('On some hosting providers it is impossible to pass parameters to cron to tell Drupal which URL to bootstrap with. In this case the base URL of sitemap links can be overridden here.<br/>Example: <em>@url</em>', ['@url' => $GLOBALS['base_url']]),
+    ];
+
+    $form['simple_sitemap_settings']['advanced']['remove_duplicates'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Exclude duplicate links'),
+      '#description' => $this->t('Prevent per-sitemap variant duplicate links.<br/>Uncheck this to significantly speed up the sitemap generation process on a huge site (more than 20 000 indexed entities).'),
+      '#default_value' => $this->generator->getSetting('remove_duplicates', TRUE),
+    ];
+
+    $form['simple_sitemap_settings']['advanced']['max_links'] = [
+      '#type' => 'number',
+      '#title' => $this->t('Maximum links in a sitemap'),
+      '#min' => 1,
+      '#description' => $this->t('The maximum number of links one sitemap can hold. If more links are generated than set here, a sitemap index will be created and the links split into several sub-sitemaps.<br/>50 000 links is the maximum Google will parse per sitemap, but an equally important consideration is generation performance: Splitting sitemaps into chunks <em>greatly</em> increases it.<br/>If left blank, all links will be shown on a single sitemap.'),
+      '#default_value' => $this->generator->getSetting('max_links'),
+    ];
+
+    $form['simple_sitemap_settings']['advanced']['generate_duration'] = [
+      '#type' => 'number',
+      '#title' => $this->t('Sitemap generation max duration'),
+      '#min' => 1,
+      '#description' => $this->t('The maximum duration <strong>in seconds</strong> the generation task can run during a single cron run or during one batch process iteration.<br/>The higher the number, the quicker the generation process, but higher the risk of PHP timeout errors.'),
+      '#default_value' => $this->generator->getSetting('generate_duration', 10000) / 1000,
+      '#required' => TRUE,
+    ];
+
+    $this->formHelper->displayRegenerateNow($form['simple_sitemap_settings']);
+
+    return parent::buildForm($form, $form_state);
+  }
+
+  /**
+   * @return array
+   *  Array of sitemap statuses keyed by variant name.
+   *  Status values:
+   *  0: Instance is unpublished
+   *  1: Instance is published
+   *  2: Instance is published but is being regenerated
+   */
+  protected function fetchSitemapInstanceStatuses() {
+    $results = $this->db
+      ->query('SELECT type, status FROM {simple_sitemap} GROUP BY type, status')
+      ->fetchAll();
+
+    $instances = [];
+    foreach ($results as $i => $result) {
+      $instances[$result->type] = isset($instances[$result->type])
+        ? $result->status + 1
+        : (int) $result->status;
+    }
+
+    return $instances;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    $base_url = $form_state->getValue('base_url');
+    $form_state->setValue('base_url', rtrim($base_url, '/'));
+    if ($base_url !== '' && !UrlHelper::isValid($base_url, TRUE)) {
+      $form_state->setErrorByName('base_url', t('The base URL is invalid.'));
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    foreach (['max_links',
+               'cron_generate',
+               'cron_generate_interval',
+               'remove_duplicates',
+               'skip_untranslated',
+               'base_url',
+               'default_variant'] as $setting_name) {
+      $this->generator->saveSetting($setting_name, $form_state->getValue($setting_name));
+    }
+    $this->generator->saveSetting('excluded_languages', array_filter($form_state->getValue('excluded_languages')));
+    $this->generator->saveSetting('generate_duration', $form_state->getValue('generate_duration') * 1000);
+
+    parent::submitForm($form, $form_state);
+
+    // Regenerate sitemaps according to user setting.
+    if ($form_state->getValue('simple_sitemap_regenerate_now')) {
+      $this->generator->rebuildQueue()->generateSitemap();
+    }
+  }
+
+  /**
+   * @param array $form
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function generateSitemap(array &$form, FormStateInterface $form_state) {
+    $this->generator->generateSitemap();
+  }
+
+  /**
+   * @param array $form
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function generateSitemapBackend (array &$form, FormStateInterface $form_state) {
+    $this->generator->generateSitemap('backend');
+  }
+
+
+  /**
+   * @param array $form
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function rebuildQueue(array &$form, FormStateInterface $form_state) {
+    $this->generator->rebuildQueue();
+  }
+
+}

Різницю між файлами не показано, бо вона завелика
+ 36 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Form/SimplesitemapVariantsForm.php


+ 100 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Logger.php

@@ -0,0 +1,100 @@
+<?php
+
+namespace Drupal\simple_sitemap;
+
+use Drupal\Core\Messenger\Messenger;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\Session\AccountProxyInterface;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Class Logger
+ * @package Drupal\simple_sitemap
+ */
+class Logger {
+
+  use StringTranslationTrait;
+
+  /*
+   * Can be debug/info/notice/warning/error.
+   */
+  const LOG_SEVERITY_LEVEL_DEFAULT = 'notice';
+
+  /*
+   * Can be status/warning/error.
+   */
+  const DISPLAY_MESSAGE_TYPE_DEFAULT = 'status';
+
+  /**
+   * @var \Psr\Log\LoggerInterface
+   */
+  protected $logger;
+
+  /**
+   * @var \Drupal\Core\Messenger\Messenger
+   */
+  protected $messenger;
+
+  /**
+   * @var \Drupal\Core\Session\AccountProxyInterface
+   */
+  protected $currentUser;
+
+  /**
+   * @var string
+   */
+  protected $message = '';
+
+  /**
+   * @var array
+   */
+  protected $substitutions = [];
+
+  /**
+   * Logger constructor.
+   * @param \Psr\Log\LoggerInterface $logger
+   * @param \Drupal\Core\Messenger\Messenger $messenger
+   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
+   */
+  public function __construct(
+    LoggerInterface $logger,
+    Messenger $messenger,
+    AccountProxyInterface $current_user
+  ) {
+    $this->logger = $logger;
+    $this->messenger = $messenger;
+    $this->currentUser = $current_user;
+  }
+
+  /**
+   * @param $message
+   * @param array $substitutions
+   * @return $this
+   */
+  public function m($message, $substitutions = []) {
+    $this->message = $message;
+    $this->substitutions = $substitutions;
+    return $this;
+  }
+
+  /**
+   * @param string $logSeverityLevel
+   * @return $this
+   */
+  public function log($logSeverityLevel = self::LOG_SEVERITY_LEVEL_DEFAULT) {
+    $this->logger->$logSeverityLevel(strtr($this->message, $this->substitutions));
+    return $this;
+  }
+
+  /**
+   * @param string $displayMessageType
+   * @param string $permission
+   * @return $this
+   */
+  public function display($displayMessageType = self::DISPLAY_MESSAGE_TYPE_DEFAULT, $permission = '') {
+    if (empty($permission) || $this->currentUser->hasPermission($permission)) {
+      $this->messenger->addMessage($this->t($this->message, $this->substitutions), $displayMessageType);
+    }
+    return $this;
+  }
+}

+ 25 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/PathProcessor/PathProcessorSitemapVariant.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace Drupal\simple_sitemap\PathProcessor;
+
+use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class PathProcessorSitemapVariant
+ * @package Drupal\simple_sitemap\PathProcessor
+ */
+class PathProcessorSitemapVariant implements InboundPathProcessorInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processInbound($path, Request $request) {
+    $args = explode('/', $path);
+    if (count($args) === 3 && $args[2] === 'sitemap.xml') {
+      $path = '/sitemaps/' . $args[1] . '/sitemap.xml';
+    }
+
+    return $path;
+  }
+}

+ 36 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SimplesitemapPluginBase.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap;
+
+use Drupal\Core\Plugin\PluginBase;
+use Drupal\Component\Plugin\PluginInspectionInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Class UrlGeneratorBase
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator
+ */
+abstract class SimplesitemapPluginBase extends PluginBase implements PluginInspectionInterface, ContainerFactoryPluginInterface {
+
+  /**
+   * SimplesitemapPluginBase constructor.
+   * @param array $configuration
+   * @param string $plugin_id
+   * @param mixed $plugin_definition
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+  }
+
+  /**
+   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+   * @param array $configuration
+   * @param string $plugin_id
+   * @param mixed $plugin_definition
+   * @return static
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static($configuration, $plugin_id, $plugin_definition);
+  }
+}

+ 184 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapGenerator/DefaultSitemapGenerator.php

@@ -0,0 +1,184 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Extension\ModuleHandler;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Component\Datetime\Time;
+
+/**
+ * Class DefaultSitemapGenerator
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator
+ *
+ * @SitemapGenerator(
+ *   id = "default",
+ *   label = @Translation("Default sitemap generator"),
+ *   description = @Translation("Generates a standard conform hreflang sitemap of your content."),
+ * )
+ */
+class DefaultSitemapGenerator extends SitemapGeneratorBase {
+
+  const XMLNS_XHTML = 'http://www.w3.org/1999/xhtml';
+  const XMLNS_IMAGE = 'http://www.google.com/schemas/sitemap-image/1.1';
+
+  /**
+   * @var bool
+   */
+  protected $isHreflangSitemap;
+
+  /**
+   * @var array
+   */
+  protected static $attributes = [
+    'xmlns' => self::XMLNS,
+    'xmlns:xhtml' => self::XMLNS_XHTML,
+    'xmlns:image' => self::XMLNS_IMAGE,
+  ];
+
+  /**
+   * DefaultSitemapGenerator constructor.
+   * @param array $configuration
+   * @param string $plugin_id
+   * @param mixed $plugin_definition
+   * @param \Drupal\Core\Database\Connection $database
+   * @param \Drupal\Core\Extension\ModuleHandler $module_handler
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   * @param \Drupal\Component\Datetime\Time $time
+   * @param \Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator\SitemapWriter $sitemapWriter
+   */
+  public function __construct(
+    array $configuration,
+    $plugin_id,
+    $plugin_definition,
+    Connection $database,
+    ModuleHandler $module_handler,
+    LanguageManagerInterface $language_manager,
+    Time $time,
+    SitemapWriter $sitemapWriter
+  ) {
+    parent::__construct(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $database,
+      $module_handler,
+      $language_manager,
+      $time,
+      $sitemapWriter
+    );
+  }
+
+  public static function create(
+    ContainerInterface $container,
+    array $configuration,
+    $plugin_id,
+    $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('database'),
+      $container->get('module_handler'),
+      $container->get('language_manager'),
+      $container->get('datetime.time'),
+      $container->get('simple_sitemap.sitemap_writer')
+    );
+  }
+
+  /**
+   * Generates and returns a sitemap chunk.
+   *
+   * @param array $links
+   *   All links with their multilingual versions and settings.
+   *
+   * @return string
+   *   Sitemap chunk
+   */
+  protected function getXml(array $links) {
+    $this->writer->openMemory();
+    $this->writer->setIndent(TRUE);
+    $this->writer->startDocument(self::XML_VERSION, self::ENCODING);
+    $this->writer->writeComment(self::GENERATED_BY);
+    $this->writer->startElement('urlset');
+
+    // Add attributes to document.
+    $attributes = self::$attributes;
+    if (!$this->isHreflangSitemap()) {
+      unset($attributes['xmlns:xhtml']);
+    }
+    $sitemap_variant = $this->sitemapVariant;
+    $this->moduleHandler->alter('simple_sitemap_attributes', $attributes, $sitemap_variant);
+    foreach ($attributes as $name => $value) {
+      $this->writer->writeAttribute($name, $value);
+    }
+
+    // Add URLs to document.
+    $sitemap_variant = $this->sitemapVariant;
+    $this->moduleHandler->alter('simple_sitemap_links', $links, $sitemap_variant);
+    foreach ($links as $link) {
+
+      // Add each translation variant URL as location to the sitemap.
+      $this->writer->startElement('url');
+      $this->writer->writeElement('loc', $link['url']);
+
+      // If more than one language is enabled, add all translation variant URLs
+      // as alternate links to this location turning the sitemap into a hreflang
+      // sitemap.
+      if (isset($link['alternate_urls']) && $this->isHreflangSitemap()) {
+        foreach ($link['alternate_urls'] as $language_id => $alternate_url) {
+          $this->writer->startElement('xhtml:link');
+          $this->writer->writeAttribute('rel', 'alternate');
+          $this->writer->writeAttribute('hreflang', $language_id);
+          $this->writer->writeAttribute('href', $alternate_url);
+          $this->writer->endElement();
+        }
+      }
+
+      // Add lastmod if any.
+      if (isset($link['lastmod'])) {
+        $this->writer->writeElement('lastmod', $link['lastmod']);
+      }
+
+      // Add changefreq if any.
+      if (isset($link['changefreq'])) {
+        $this->writer->writeElement('changefreq', $link['changefreq']);
+      }
+
+      // Add priority if any.
+      if (isset($link['priority'])) {
+        $this->writer->writeElement('priority', $link['priority']);
+      }
+
+      // Add images if any.
+      if (!empty($link['images'])) {
+        foreach ($link['images'] as $image) {
+          $this->writer->startElement('image:image');
+          $this->writer->writeElement('image:loc', $image['path']);
+          $this->writer->endElement();
+        }
+      }
+
+      $this->writer->endElement();
+    }
+    $this->writer->endElement();
+    $this->writer->endDocument();
+
+    return $this->writer->outputMemory();
+  }
+
+  /**
+   * @return bool
+   */
+  protected function isHreflangSitemap() {
+    if (NULL === $this->isHreflangSitemap) {
+      $this->isHreflangSitemap = count(
+        array_diff_key($this->languageManager->getLanguages(),
+          $this->settings['excluded_languages'])
+        ) > 1;
+    }
+    return $this->isHreflangSitemap;
+  }
+
+}

+ 303 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapGenerator/SitemapGeneratorBase.php

@@ -0,0 +1,303 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator;
+
+use Drupal\simple_sitemap\Plugin\simple_sitemap\SimplesitemapPluginBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Extension\ModuleHandler;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Component\Datetime\Time;
+
+/**
+ * Class SitemapGeneratorBase
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator
+ */
+abstract class SitemapGeneratorBase extends SimplesitemapPluginBase implements SitemapGeneratorInterface {
+
+  const FIRST_CHUNK_DELTA = 1;
+  const INDEX_DELTA = 0;
+
+  const GENERATED_BY = 'Generated by the Simple XML sitemap Drupal module: https://drupal.org/project/simple_sitemap.';
+  const XML_VERSION = '1.0';
+  const ENCODING = 'UTF-8';
+  const XMLNS = 'http://www.sitemaps.org/schemas/sitemap/0.9';
+
+  /**
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $db;
+
+  /**
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * @var \Drupal\Core\Extension\ModuleHandler
+   */
+  protected $moduleHandler;
+
+  /**
+   * @var \Drupal\Component\Datetime\Time
+   */
+  protected $time;
+
+  /**
+   * @var array
+   */
+  protected $settings;
+
+  /**
+   * @var \Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator\SitemapWriter
+   */
+  protected $writer;
+
+  /**
+   * @var string
+   */
+  protected $sitemapVariant;
+
+  /**
+   * @var array
+   */
+  protected static $indexAttributes = [
+    'xmlns' => self::XMLNS,
+  ];
+
+  /**
+   * SitemapGeneratorBase constructor.
+   * @param array $configuration
+   * @param string $plugin_id
+   * @param mixed $plugin_definition
+   * @param \Drupal\Core\Database\Connection $database
+   * @param \Drupal\Core\Extension\ModuleHandler $module_handler
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   * @param \Drupal\Component\Datetime\Time $time
+   * @param \Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator\SitemapWriter $sitemap_writer
+   */
+  public function __construct(
+    array $configuration,
+    $plugin_id,
+    $plugin_definition,
+    Connection $database,
+    ModuleHandler $module_handler,
+    LanguageManagerInterface $language_manager,
+    Time $time,
+    SitemapWriter $sitemap_writer
+  ) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->db = $database;
+    $this->moduleHandler = $module_handler;
+    $this->languageManager = $language_manager;
+    $this->time = $time;
+    $this->writer = $sitemap_writer;
+    $this->sitemapVariant = $this->settings['default_variant'];
+  }
+
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('database'),
+      $container->get('module_handler'),
+      $container->get('language_manager'),
+      $container->get('datetime.time'),
+      $container->get('simple_sitemap.sitemap_writer')
+    );
+  }
+
+  /**
+   * @param string $sitemap_variant
+   * @return $this
+   */
+  public function setSitemapVariant($sitemap_variant) {
+    $this->sitemapVariant = $sitemap_variant;
+    return $this;
+  }
+
+  /**
+   * @return bool
+   */
+  protected function isDefaultVariant() {
+    return $this->sitemapVariant === $this->settings['default_variant'];
+  }
+
+  /**
+   * @param array $links
+   * @return string
+   */
+  abstract protected function getXml(array $links);
+
+  protected function getChunkInfo() {
+    return $this->db->select('simple_sitemap', 's')
+      ->fields('s', ['delta', 'sitemap_created', 'type'])
+      ->condition('s.type', $this->sitemapVariant)
+      ->condition('s.delta', self::INDEX_DELTA, '<>')
+      ->condition('s.status', 0)
+      ->execute()
+      ->fetchAllAssoc('delta');
+  }
+
+  /**
+   * @param array $chunk_info
+   * @return string
+   */
+  protected function getIndexXml(array $chunk_info) {
+    $this->writer->openMemory();
+    $this->writer->setIndent(TRUE);
+    $this->writer->startDocument(self::XML_VERSION, self::ENCODING);
+    $this->writer->writeComment(self::GENERATED_BY);
+    $this->writer->startElement('sitemapindex');
+
+    // Add attributes to document.
+    $attributes = self::$indexAttributes;
+    $sitemap_variant = $this->sitemapVariant;
+    $this->moduleHandler->alter('simple_sitemap_index_attributes', $attributes, $sitemap_variant);
+    foreach ($attributes as $name => $value) {
+      $this->writer->writeAttribute($name, $value);
+    }
+
+    // Add sitemap chunk locations to document.
+    foreach ($chunk_info as $chunk_data) {
+      $this->writer->startElement('sitemap');
+      $this->writer->writeElement('loc', $this->getCustomBaseUrl()
+        . '/' . (!$this->isDefaultVariant() ? ($chunk_data->type . '/') : '') . 'sitemap.xml?page=' . $chunk_data->delta);
+      $this->writer->writeElement('lastmod', date_iso8601($chunk_data->sitemap_created));
+      $this->writer->endElement();
+    }
+
+    $this->writer->endElement();
+    $this->writer->endDocument();
+
+    return $this->writer->outputMemory();
+  }
+
+  /**
+   * @param string $mode
+   * @return $this
+   */
+  public function remove($mode = 'all') {
+    self::purgeSitemapVariants($this->sitemapVariant, $mode);
+
+    return $this;
+  }
+
+  public static function purgeSitemapVariants($variants = NULL, $mode = 'all') {
+    if (NULL === $variants || !empty((array) $variants)) {
+      $delete_query = \Drupal::database()->delete('simple_sitemap');
+
+      switch($mode) {
+        case 'published':
+          $delete_query->condition('status', 1);
+          break;
+
+        case 'unpublished':
+          $delete_query->condition('status', 0);
+          break;
+
+        case 'all':
+          break;
+
+        default:
+          //todo: throw error
+      }
+
+      if (NULL !== $variants) {
+        $delete_query->condition('type', (array) $variants, 'IN');
+      }
+
+      $delete_query->execute();
+    }
+  }
+
+  /**
+   * @param array $links
+   * @return $this
+   * @throws \Exception
+   */
+  public function generate(array $links) {
+    $highest_id = $this->db->query('SELECT MAX(id) FROM {simple_sitemap}')->fetchField();
+    $highest_delta = $this->db->query('SELECT MAX(delta) FROM {simple_sitemap} WHERE type = :type AND status = :status', [':type' => $this->sitemapVariant, ':status' => 0])
+      ->fetchField();
+
+    $this->db->insert('simple_sitemap')->fields([
+      'id' => NULL === $highest_id ? 0 : $highest_id + 1,
+      'delta' => NULL === $highest_delta ? self::FIRST_CHUNK_DELTA : $highest_delta + 1,
+      'type' =>  $this->sitemapVariant,
+      'sitemap_string' => $this->getXml($links),
+      'sitemap_created' => $this->time->getRequestTime(),
+      'status' => 0,
+    ])->execute();
+
+    return $this;
+  }
+
+  /**
+   * @return $this
+   * @throws \Exception
+   */
+  public function generateIndex() {
+    if (!empty($chunk_info = $this->getChunkInfo()) && count($chunk_info) > 1) {
+      $index_xml = $this->getIndexXml($chunk_info);
+      $highest_id = $this->db->query('SELECT MAX(id) FROM {simple_sitemap}')->fetchField();
+      $this->db->merge('simple_sitemap')
+        ->keys([
+          'delta' => self::INDEX_DELTA,
+          'type' => $this->sitemapVariant,
+          'status' => 0
+        ])
+        ->insertFields([
+          'id' => NULL === $highest_id ? 0 : $highest_id + 1,
+          'delta' => self::INDEX_DELTA,
+          'type' =>  $this->sitemapVariant,
+          'sitemap_string' => $index_xml,
+          'sitemap_created' => $this->time->getRequestTime(),
+          'status' => 0,
+        ])
+        ->updateFields([
+          'sitemap_string' => $index_xml,
+          'sitemap_created' => $this->time->getRequestTime(),
+        ])
+        ->execute();
+    }
+
+    return $this;
+  }
+
+  /**
+   * @return $this
+   */
+  public function publish() {
+    $unpublished_chunk = $this->db->query('SELECT MAX(id) FROM {simple_sitemap} WHERE type = :type AND status = :status', [':type' => $this->sitemapVariant, ':status' => 0])
+      ->fetchField();
+
+    // Only allow publishing a sitemap variant if there is an unpublished
+    // sitemap variant, as publishing involves deleting the currently published
+    // variant.
+    if (FALSE !== $unpublished_chunk) {
+      $this->remove('published');
+      $this->db->query('UPDATE {simple_sitemap} SET status = :status WHERE type = :type', [':type' => $this->sitemapVariant, ':status' => 1]);
+    }
+
+    return $this;
+  }
+
+  /**
+   * @param array $settings
+   * @return $this
+   */
+  public function setSettings(array $settings) {
+    $this->settings = $settings;
+    return $this;
+  }
+
+  /**
+   * @return string
+   */
+  protected function getCustomBaseUrl() {
+    $customBaseUrl = $this->settings['base_url'];
+    return !empty($customBaseUrl) ? $customBaseUrl : $GLOBALS['base_url'];
+  }
+}

+ 22 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapGenerator/SitemapGeneratorInterface.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator;
+
+/**
+ * Interface SitemapGeneratorInterface
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator
+ */
+interface SitemapGeneratorInterface {
+
+  function setSitemapVariant($sitemap_variant);
+
+  function setSettings(array $settings);
+
+  function generate(array $links);
+
+  function generateIndex();
+
+  function publish();
+
+  function remove();
+}

+ 37 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapGenerator/SitemapGeneratorManager.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator;
+
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+
+/**
+ * Class SitemapGeneratorManager
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator
+ */
+class SitemapGeneratorManager extends DefaultPluginManager {
+
+  /**
+   * SitemapGeneratorManager constructor.
+   * @param \Traversable $namespaces
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   */
+  public function __construct(
+    \Traversable $namespaces,
+    CacheBackendInterface $cache_backend,
+    ModuleHandlerInterface $module_handler
+  ) {
+    parent::__construct(
+      'Plugin/simple_sitemap/SitemapGenerator',
+      $namespaces,
+      $module_handler,
+      'Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator\SitemapGeneratorInterface',
+      'Drupal\simple_sitemap\Annotation\SitemapGenerator'
+    );
+
+    $this->alterInfo('simple_sitemap_sitemap_generators');
+    $this->setCacheBackend($cache_backend, 'simple_sitemap:sitemap_generator');
+  }
+}

+ 13 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapGenerator/SitemapWriter.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator;
+
+use XMLWriter;
+
+/**
+ * Class SitemapWriter
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator
+ */
+class SitemapWriter extends XMLWriter {
+
+}

+ 23 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapType/DefaultHreflangSitemapType.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapType;
+
+/**
+ * Class DefaultHreflangSitemapType
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapType
+ *
+ * @SitemapType(
+ *   id = "default_hreflang",
+ *   label = @Translation("Default hreflang"),
+ *   description = @Translation("The default hreflang sitemap type."),
+ *   sitemapGenerator = "default",
+ *   urlGenerators = {
+ *     "custom",
+ *     "entity",
+ *     "entity_menu_link_content",
+ *     "arbitrary",
+ *   },
+ * )
+ */
+class DefaultHreflangSitemapType extends SitemapTypeBase {
+}

+ 12 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapType/SitemapTypeBase.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapType;
+
+use Drupal\simple_sitemap\Plugin\simple_sitemap\SimplesitemapPluginBase;
+
+/**
+ * Class SitemapTypeBase
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapType
+ */
+abstract class SitemapTypeBase extends SimplesitemapPluginBase implements SitemapTypeInterface {
+}

+ 10 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapType/SitemapTypeInterface.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapType;
+
+/**
+ * Interface SitemapTypeInterface
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapType
+ */
+interface SitemapTypeInterface {
+}

+ 37 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/SitemapType/SitemapTypeManager.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapType;
+
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+
+/**
+ * Class SitemapTypeManager
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapType
+ */
+class SitemapTypeManager extends DefaultPluginManager {
+
+  /**
+   * SitemapTypeManager constructor.
+   * @param \Traversable $namespaces
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   */
+  public function __construct(
+    \Traversable $namespaces,
+    CacheBackendInterface $cache_backend,
+    ModuleHandlerInterface $module_handler
+  ) {
+    parent::__construct(
+      'Plugin/simple_sitemap/SitemapType',
+      $namespaces,
+      $module_handler,
+      'Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapType\SitemapTypeInterface',
+      'Drupal\simple_sitemap\Annotation\SitemapType'
+    );
+
+    $this->alterInfo('simple_sitemap_sitemap_types');
+    $this->setCacheBackend($cache_backend, 'simple_sitemap:sitemap_type');
+  }
+}

+ 82 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/UrlGenerator/ArbitraryUrlGenerator.php

@@ -0,0 +1,82 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator;
+
+use Drupal\simple_sitemap\Logger;
+use Drupal\simple_sitemap\Simplesitemap;
+use Drupal\Core\Extension\ModuleHandler;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Class ArbitraryUrlGenerator
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator
+ *
+ * @UrlGenerator(
+ *   id = "arbitrary",
+ *   label = @Translation("Arbitrary URL generator"),
+ *   description = @Translation("Generates URLs from data sets collected in the hook_arbitrary_links_alter hook."),
+ * )
+ */
+class ArbitraryUrlGenerator extends UrlGeneratorBase {
+
+  protected $moduleHandler;
+
+  /**
+   * ArbitraryUrlGenerator constructor.
+   * @param array $configuration
+   * @param $plugin_id
+   * @param $plugin_definition
+   * @param \Drupal\simple_sitemap\Simplesitemap $generator
+   * @param \Drupal\simple_sitemap\Logger $logger
+   * @param \Drupal\Core\Extension\ModuleHandler $module_handler
+   */
+  public function __construct(
+    array $configuration,
+    $plugin_id,
+    $plugin_definition,
+    Simplesitemap $generator,
+    Logger $logger,
+    ModuleHandler $module_handler
+  ) {
+    parent::__construct(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $generator,
+      $logger
+    );
+    $this->moduleHandler = $module_handler;
+  }
+
+  public static function create(
+    ContainerInterface $container,
+    array $configuration,
+    $plugin_id,
+    $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('simple_sitemap.generator'),
+      $container->get('simple_sitemap.logger'),
+      $container->get('module_handler')
+    );
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function getDataSets() {
+    $arbitrary_links = [];
+    $sitemap_variant = $this->sitemapVariant;
+    $this->moduleHandler->alter('simple_sitemap_arbitrary_links', $arbitrary_links, $sitemap_variant);
+    return array_values($arbitrary_links);
+  }
+
+  /**
+   * @inheritdoc
+   */
+  protected function processDataSet($data_set) {
+    return $data_set;
+  }
+}

+ 143 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/UrlGenerator/CustomUrlGenerator.php

@@ -0,0 +1,143 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator;
+
+use Drupal\Core\Url;
+use Drupal\simple_sitemap\Annotation\UrlGenerator;
+use Drupal\simple_sitemap\EntityHelper;
+use Drupal\simple_sitemap\Logger;
+use Drupal\simple_sitemap\Simplesitemap;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Path\PathValidator;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Class CustomUrlGenerator
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator
+ *
+ * @UrlGenerator(
+ *   id = "custom",
+ *   label = @Translation("Custom URL generator"),
+ *   description = @Translation("Generates URLs set in admin/config/search/simplesitemap/custom."),
+ * )
+ *
+ */
+class CustomUrlGenerator extends EntityUrlGeneratorBase {
+
+  const PATH_DOES_NOT_EXIST_MESSAGE = 'The custom path @path has been omitted from the XML sitemaps as it does not exist. You can review custom paths <a href="@custom_paths_url">here</a>.';
+
+
+  /**
+   * @var \Drupal\Core\Path\PathValidator
+   */
+  protected $pathValidator;
+
+  /**
+   * @var bool
+   */
+  protected $includeImages;
+
+  /**
+   * CustomUrlGenerator constructor.
+   * @param array $configuration
+   * @param $plugin_id
+   * @param $plugin_definition
+   * @param \Drupal\simple_sitemap\Simplesitemap $generator
+   * @param \Drupal\simple_sitemap\Logger $logger
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   * @param \Drupal\simple_sitemap\EntityHelper $entityHelper
+   * @param \Drupal\Core\Path\PathValidator $path_validator
+   */
+  public function __construct(
+    array $configuration,
+    $plugin_id,
+    $plugin_definition,
+    Simplesitemap $generator,
+    Logger $logger,
+    LanguageManagerInterface $language_manager,
+    EntityTypeManagerInterface $entity_type_manager,
+    EntityHelper $entityHelper,
+    PathValidator $path_validator) {
+    parent::__construct(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $generator,
+      $logger,
+      $language_manager,
+      $entity_type_manager,
+      $entityHelper
+    );
+    $this->pathValidator = $path_validator;
+  }
+
+  public static function create(
+    ContainerInterface $container,
+    array $configuration,
+    $plugin_id,
+    $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('simple_sitemap.generator'),
+      $container->get('simple_sitemap.logger'),
+      $container->get('language_manager'),
+      $container->get('entity_type.manager'),
+      $container->get('simple_sitemap.entity_helper'),
+      $container->get('path.validator')
+    );
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function getDataSets() {
+    $this->includeImages = $this->generator->getSetting('custom_links_include_images', FALSE);
+    return array_values($this->generator->setVariants($this->sitemapVariant)->getCustomLinks());
+  }
+
+  /**
+   * @inheritdoc
+   */
+  protected function processDataSet($data_set) {
+    if (!(bool) $this->pathValidator->getUrlIfValidWithoutAccessCheck($data_set['path'])) {
+      $this->logger->m(self::PATH_DOES_NOT_EXIST_MESSAGE,
+        ['@path' => $data_set['path'], '@custom_paths_url' => $GLOBALS['base_url'] . '/admin/config/search/simplesitemap/custom'])
+        ->display('warning', 'administer sitemap settings')
+        ->log('warning');
+      return FALSE;
+    }
+
+    $url_object = Url::fromUserInput($data_set['path'], ['absolute' => TRUE]);
+    $path = $url_object->getInternalPath();
+
+    $entity = $this->entityHelper->getEntityFromUrlObject($url_object);
+
+    $path_data = [
+      'url' => $url_object,
+      'lastmod' => method_exists($entity, 'getChangedTime')
+        ? date_iso8601($entity->getChangedTime()) : NULL,
+      'priority' => isset($data_set['priority']) ? $data_set['priority'] : NULL,
+      'changefreq' => !empty($data_set['changefreq']) ? $data_set['changefreq'] : NULL,
+      'images' => $this->includeImages && method_exists($entity, 'getEntityTypeId')
+        ? $this->getImages($entity->getEntityTypeId(), $entity->id())
+        : [],
+      'meta' => [
+        'path' => $path,
+      ]
+    ];
+
+    // Additional info useful in hooks.
+    if (NULL !== $entity) {
+      $path_data['meta']['entity_info'] = [
+        'entity_type' => $entity->getEntityTypeId(),
+        'id' => $entity->id(),
+      ];
+    }
+
+    return $path_data;
+  }
+}

+ 198 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/UrlGenerator/EntityMenuLinkContentUrlGenerator.php

@@ -0,0 +1,198 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator;
+
+use Drupal\simple_sitemap\EntityHelper;
+use Drupal\simple_sitemap\Logger;
+use Drupal\simple_sitemap\Simplesitemap;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Menu\MenuTreeParameters;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Menu\MenuLinkTree;
+use Drupal\Core\Menu\MenuLinkBase;
+
+/**
+ * Class EntityMenuLinkContentUrlGenerator
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator
+ *
+ * @UrlGenerator(
+ *   id = "entity_menu_link_content",
+ *   label = @Translation("Menu link URL generator"),
+ *   description = @Translation("Generates menu link URLs by overriding the 'entity' URL generator."),
+ *   settings = {
+ *     "overrides_entity_type" = "menu_link_content",
+ *   },
+ * )
+ *
+ * @todo Find way of adding just a menu link item pointer to the queue instead of whole object.
+ */
+class EntityMenuLinkContentUrlGenerator extends EntityUrlGeneratorBase {
+
+  /**
+   * @var \Drupal\Core\Menu\MenuLinkTree
+   */
+  protected $menuLinkTree;
+
+  /**
+   * EntityMenuLinkContentUrlGenerator constructor.
+   * @param array $configuration
+   * @param $plugin_id
+   * @param $plugin_definition
+   * @param \Drupal\simple_sitemap\Simplesitemap $generator
+   * @param \Drupal\simple_sitemap\Logger $logger
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   * @param \Drupal\simple_sitemap\EntityHelper $entityHelper
+   * @param \Drupal\Core\Menu\MenuLinkTree $menu_link_tree
+   */
+  public function __construct(
+    array $configuration,
+    $plugin_id,
+    $plugin_definition,
+    Simplesitemap $generator,
+    Logger $logger,
+    LanguageManagerInterface $language_manager,
+    EntityTypeManagerInterface $entity_type_manager,
+    EntityHelper $entityHelper,
+    MenuLinkTree $menu_link_tree
+  ) {
+    parent::__construct(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $generator,
+      $logger,
+      $language_manager,
+      $entity_type_manager,
+      $entityHelper
+    );
+    $this->menuLinkTree = $menu_link_tree;
+  }
+
+  public static function create(
+    ContainerInterface $container,
+    array $configuration,
+    $plugin_id,
+    $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('simple_sitemap.generator'),
+      $container->get('simple_sitemap.logger'),
+      $container->get('language_manager'),
+      $container->get('entity_type.manager'),
+      $container->get('simple_sitemap.entity_helper'),
+      $container->get('menu.link_tree')
+    );
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function getDataSets() {
+    $data_sets = [];
+    $bundle_settings = $this->generator
+      ->setVariants($this->sitemapVariant)
+      ->getBundleSettings();
+    if (!empty($bundle_settings['menu_link_content'])) {
+      foreach ($bundle_settings['menu_link_content'] as $bundle_name => $bundle_settings) {
+        if (!empty($bundle_settings['index'])) {
+
+          // Retrieve the expanded tree.
+          $tree = $this->menuLinkTree->load($bundle_name, new MenuTreeParameters());
+          $tree = $this->menuLinkTree->transform($tree, [
+            ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
+            ['callable' => 'menu.default_tree_manipulators:flatten'],
+          ]);
+
+          foreach ($tree as $i => $item) {
+            $data_sets[] = $item->link;
+          }
+        }
+      }
+    }
+
+    return $data_sets;
+  }
+
+  /**
+   * @inheritdoc
+   *
+   * @todo Check if menu link has not been deleted after the queue has been built.
+   */
+  protected function processDataSet($data_set) {
+
+    /** @var  MenuLinkBase $data_set */
+    if (!$data_set->isEnabled()) {
+      return FALSE;
+    }
+
+    $url_object = $data_set->getUrlObject();
+
+    // Do not include external paths.
+    if ($url_object->isExternal()) {
+      return FALSE;
+    }
+
+    // If not a menu_link_content link, use bundle settings.
+    $meta_data = $data_set->getMetaData();
+    if (empty($meta_data['entity_id'])) {
+      $entity_settings = $this->generator
+        ->setVariants($this->sitemapVariant)
+        ->getBundleSettings('menu_link_content', $data_set->getMenuName());
+    }
+
+    // If menu link is of entity type menu_link_content, take under account its entity override.
+    else {
+      $entity_settings = $this->generator->getEntityInstanceSettings('menu_link_content', $meta_data['entity_id']);
+
+      if (empty($entity_settings['index'])) {
+        return FALSE;
+      }
+    }
+
+    // There can be internal paths that are not rooted, like 'base:/path'.
+    if ($url_object->isRouted()) {
+      $path = $url_object->getInternalPath();
+    }
+    else { // Handle base scheme.
+      if (strpos($uri = $url_object->toUriString(), 'base:/') === 0 ) {
+        $path = $uri[6] === '/' ? substr($uri, 7) : substr($uri, 6);
+      }
+      else { // Handle unforeseen schemes.
+        $path = $uri;
+      }
+    }
+
+    $url_object->setOption('absolute', TRUE);
+
+    $entity = $this->entityHelper->getEntityFromUrlObject($url_object);
+
+    $path_data = [
+      'url' => $url_object,
+      'lastmod' => !empty($entity) && method_exists($entity, 'getChangedTime')
+        ? date_iso8601($entity->getChangedTime())
+        : NULL,
+      'priority' => isset($entity_settings['priority']) ? $entity_settings['priority'] : NULL,
+      'changefreq' => !empty($entity_settings['changefreq']) ? $entity_settings['changefreq'] : NULL,
+      'images' => !empty($entity_settings['include_images']) && !empty($entity)
+        ? $this->getImages($entity->getEntityTypeId(), $entity->id())
+        : [],
+
+      // Additional info useful in hooks.
+      'meta' => [
+        'path' => $path,
+      ]
+    ];
+    if (!empty($entity)) {
+      $path_data['meta']['entity_info'] = [
+        'entity_type' => $entity->getEntityTypeId(),
+        'id' => $entity->id(),
+      ];
+    }
+
+    return $path_data;
+  }
+}

+ 181 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/UrlGenerator/EntityUrlGenerator.php

@@ -0,0 +1,181 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator;
+
+use Drupal\simple_sitemap\EntityHelper;
+use Drupal\simple_sitemap\Logger;
+use Drupal\simple_sitemap\Simplesitemap;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Class EntityUrlGenerator
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator
+ *
+ * @UrlGenerator(
+ *   id = "entity",
+ *   label = @Translation("Entity URL generator"),
+ *   description = @Translation("Generates URLs for entity bundles and bundle overrides."),
+ * )
+ */
+class EntityUrlGenerator extends EntityUrlGeneratorBase {
+
+  /**
+   * @var \Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator\UrlGeneratorManager
+   */
+  protected $urlGeneratorManager;
+
+  /**
+   * EntityUrlGenerator constructor.
+   * @param array $configuration
+   * @param $plugin_id
+   * @param $plugin_definition
+   * @param \Drupal\simple_sitemap\Simplesitemap $generator
+   * @param \Drupal\simple_sitemap\Logger $logger
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   * @param \Drupal\simple_sitemap\EntityHelper $entityHelper
+   * @param \Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator\UrlGeneratorManager $url_generator_manager
+   */
+  public function __construct(
+    array $configuration,
+    $plugin_id,
+    $plugin_definition,
+    Simplesitemap $generator,
+    Logger $logger,
+    LanguageManagerInterface $language_manager,
+    EntityTypeManagerInterface $entity_type_manager,
+    EntityHelper $entityHelper,
+    UrlGeneratorManager $url_generator_manager
+  ) {
+    parent::__construct(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $generator,
+      $logger,
+      $language_manager,
+      $entity_type_manager,
+      $entityHelper
+    );
+    $this->urlGeneratorManager = $url_generator_manager;
+  }
+
+  public static function create(
+    ContainerInterface $container,
+    array $configuration,
+    $plugin_id,
+    $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('simple_sitemap.generator'),
+      $container->get('simple_sitemap.logger'),
+      $container->get('language_manager'),
+      $container->get('entity_type.manager'),
+      $container->get('simple_sitemap.entity_helper'),
+      $container->get('plugin.manager.simple_sitemap.url_generator')
+    );
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function getDataSets() {
+    $data_sets = [];
+    $sitemap_entity_types = $this->entityHelper->getSupportedEntityTypes();
+
+    foreach ($this->generator->setVariants($this->sitemapVariant)->getBundleSettings() as $entity_type_name => $bundles) {
+      if (isset($sitemap_entity_types[$entity_type_name])) {
+
+        // Skip this entity type if another plugin is written to override its generation.
+        foreach ($this->urlGeneratorManager->getDefinitions() as $plugin) {
+          if (isset($plugin['settings']['overrides_entity_type'])
+            && $plugin['settings']['overrides_entity_type'] === $entity_type_name) {
+            continue 2;
+          }
+        }
+
+        $entityTypeStorage = $this->entityTypeManager->getStorage($entity_type_name);
+        $keys = $sitemap_entity_types[$entity_type_name]->getKeys();
+
+        foreach ($bundles as $bundle_name => $bundle_settings) {
+          if (!empty($bundle_settings['index'])) {
+            $query = $entityTypeStorage->getQuery();
+
+            if (empty($keys['id'])) {
+              $query->sort($keys['id'], 'ASC');
+            }
+            if (!empty($keys['bundle'])) {
+              $query->condition($keys['bundle'], $bundle_name);
+            }
+            if (!empty($keys['status'])) {
+              $query->condition($keys['status'], 1);
+            }
+
+            foreach ($query->execute() as $entity_id) {
+              $data_sets[] = [
+                'entity_type' => $entity_type_name,
+                'id' => $entity_id,
+              ];
+            }
+          }
+        }
+      }
+    }
+
+    return $data_sets;
+  }
+
+  /**
+   * @inheritdoc
+   */
+  protected function processDataSet($data_set) {
+    if (empty($entity = $this->entityTypeManager->getStorage($data_set['entity_type'])->load($data_set['id']))) {
+      return FALSE;
+    }
+
+    $entity_id = $entity->id();
+    $entity_type_name = $entity->getEntityTypeId();
+
+    $entity_settings = $this->generator
+      ->setVariants($this->sitemapVariant)
+      ->getEntityInstanceSettings($entity_type_name, $entity_id);
+
+    if (empty($entity_settings['index'])) {
+      return FALSE;
+    }
+
+    $url_object = $entity->toUrl();
+
+    // Do not include external paths.
+    if (!$url_object->isRouted()) {
+      return FALSE;
+    }
+
+    $path = $url_object->getInternalPath();
+
+    $url_object->setOption('absolute', TRUE);
+
+    return [
+      'url' => $url_object,
+      'lastmod' => method_exists($entity, 'getChangedTime') ? date_iso8601($entity->getChangedTime()) : NULL,
+      'priority' => isset($entity_settings['priority']) ? $entity_settings['priority'] : NULL,
+      'changefreq' => !empty($entity_settings['changefreq']) ? $entity_settings['changefreq'] : NULL,
+      'images' => !empty($entity_settings['include_images'])
+        ? $this->getImages($entity_type_name, $entity_id)
+        : [],
+
+      // Additional info useful in hooks.
+      'meta' => [
+        'path' => $path,
+        'entity_info' => [
+          'entity_type' => $entity_type_name,
+          'id' => $entity_id,
+        ],
+      ]
+    ];
+  }
+}

+ 218 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/UrlGenerator/EntityUrlGeneratorBase.php

@@ -0,0 +1,218 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Entity\ContentEntityBase;
+use Drupal\Core\Url;
+use Drupal\simple_sitemap\EntityHelper;
+use Drupal\simple_sitemap\Logger;
+use Drupal\simple_sitemap\Simplesitemap;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Session\AnonymousUserSession;
+
+/**
+ * Class EntityUrlGeneratorBase
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator
+ */
+abstract class EntityUrlGeneratorBase extends UrlGeneratorBase {
+
+  /**
+   * @var \Drupal\Core\Language\LanguageInterface[]
+   */
+  protected $languages;
+
+  /**
+   * @var string
+   */
+  protected $defaultLanguageId;
+
+  /**
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * @var \Drupal\Core\Entity\EntityInterface|null
+   */
+  protected $anonUser;
+
+  /**
+   * @var \Drupal\simple_sitemap\EntityHelper
+   */
+  protected $entityHelper;
+
+  /**
+   * UrlGeneratorBase constructor.
+   * @param array $configuration
+   * @param $plugin_id
+   * @param $plugin_definition
+   * @param \Drupal\simple_sitemap\Simplesitemap $generator
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   * @param \Drupal\simple_sitemap\Logger $logger
+   * @param \Drupal\simple_sitemap\EntityHelper $entityHelper
+   */
+  public function __construct(
+    array $configuration,
+    $plugin_id,
+    $plugin_definition,
+    Simplesitemap $generator,
+    Logger $logger,
+    LanguageManagerInterface $language_manager,
+    EntityTypeManagerInterface $entity_type_manager,
+    EntityHelper $entityHelper
+  ) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $generator, $logger);
+    $this->languages = $language_manager->getLanguages();
+    $this->defaultLanguageId = $language_manager->getDefaultLanguage()->getId();
+    $this->entityTypeManager = $entity_type_manager;
+    $this->anonUser = new AnonymousUserSession();
+    $this->entityHelper = $entityHelper;
+  }
+
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('simple_sitemap.generator'),
+      $container->get('simple_sitemap.logger'),
+      $container->get('language_manager'),
+      $container->get('entity_type.manager'),
+      $container->get('simple_sitemap.entity_helper')
+    );
+  }
+
+  /**
+   * @param array $path_data
+   * @param \Drupal\Core\Url $url_object
+   * @return array
+   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+   */
+  protected function getUrlVariants(array $path_data, Url $url_object) {
+    $url_variants = [];
+
+    if (!$url_object->isRouted()) {
+      // Not a routed URL, including only default variant.
+      $alternate_urls = $this->getAlternateUrlsForDefaultLanguage($url_object);
+    }
+    elseif ($this->settings['skip_untranslated']
+      && ($entity = $this->entityHelper->getEntityFromUrlObject($url_object)) instanceof ContentEntityBase) {
+
+      /** @var ContentEntityBase $entity */
+      $translation_languages = $entity->getTranslationLanguages();
+      if (isset($translation_languages[Language::LANGCODE_NOT_SPECIFIED])
+        || isset($translation_languages[Language::LANGCODE_NOT_APPLICABLE])) {
+
+        // Content entity's language is unknown, including only default variant.
+        $alternate_urls = $this->getAlternateUrlsForDefaultLanguage($url_object);
+      }
+      else {
+        // Including only translated variants of content entity.
+        $alternate_urls = $this->getAlternateUrlsForTranslatedLanguages($entity, $url_object);
+      }
+    }
+    else {
+      // Not a content entity or including all untranslated variants.
+      $alternate_urls = $this->getAlternateUrlsForAllLanguages($url_object);
+    }
+
+    foreach ($alternate_urls as $langcode => $url) {
+      $url_variants[] = $path_data + [
+        'langcode' => $langcode,
+          'url' => $url,
+          'alternate_urls' => $alternate_urls
+        ];
+    }
+
+    return $url_variants;
+  }
+
+  /**
+   * @param \Drupal\Core\Url $url_object
+   * @return array
+   */
+  protected function getAlternateUrlsForDefaultLanguage(Url $url_object) {
+    $alternate_urls = [];
+    if ($url_object->access($this->anonUser)) {
+      $alternate_urls[$this->defaultLanguageId] = $this->replaceBaseUrlWithCustom($url_object
+        ->setOption('language', $this->languages[$this->defaultLanguageId])->toString()
+      );
+    }
+    return $alternate_urls;
+  }
+
+  /**
+   * @param \Drupal\Core\Entity\ContentEntityBase $entity
+   * @param \Drupal\Core\Url $url_object
+   * @return array
+   */
+  protected function getAlternateUrlsForTranslatedLanguages(ContentEntityBase $entity, Url $url_object) {
+    $alternate_urls = [];
+
+    /** @var Language $language */
+    foreach ($entity->getTranslationLanguages() as $language) {
+      if (!isset($this->settings['excluded_languages'][$language->getId()]) || $language->isDefault()) {
+        if ($entity->getTranslation($language->getId())->access('view', $this->anonUser)) {
+          $alternate_urls[$language->getId()] = $this->replaceBaseUrlWithCustom($url_object
+            ->setOption('language', $language)->toString()
+          );
+        }
+      }
+    }
+    return $alternate_urls;
+  }
+
+  /**
+   * @param \Drupal\Core\Url $url_object
+   * @return array
+   */
+  protected function getAlternateUrlsForAllLanguages(Url $url_object) {
+    $alternate_urls = [];
+    if ($url_object->access($this->anonUser)) {
+      foreach ($this->languages as $language) {
+        if (!isset($this->settings['excluded_languages'][$language->getId()]) || $language->isDefault()) {
+          $alternate_urls[$language->getId()] = $this->replaceBaseUrlWithCustom($url_object
+            ->setOption('language', $language)->toString()
+          );
+        }
+      }
+    }
+    return $alternate_urls;
+  }
+
+  /**
+   * @param mixed $data_set
+   * @return array
+   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+   */
+  public function generate($data_set) {
+    $path_data = $this->processDataSet($data_set);
+    if (isset($path_data['url']) && $path_data['url'] instanceof Url) {
+      $url_object = $path_data['url'];
+      unset($path_data['url']);
+      return $this->getUrlVariants($path_data, $url_object);
+    }
+    else {
+      return FALSE !== $path_data ? [$path_data] : [];
+    }
+  }
+
+  /**
+   * @param string $entity_type_name
+   * @param string $entity_id
+   * @return array
+   */
+  protected function getImages($entity_type_name, $entity_id) {
+    $images = [];
+    foreach ($this->entityHelper->getEntityImageUrls($entity_type_name, $entity_id) as $url) {
+      $images[]['path'] = $this->replaceBaseUrlWithCustom($url);
+    }
+    return $images;
+  }
+}

+ 113 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/UrlGenerator/UrlGeneratorBase.php

@@ -0,0 +1,113 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator;
+
+use Drupal\simple_sitemap\Plugin\simple_sitemap\SimplesitemapPluginBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\simple_sitemap\Logger;
+use Drupal\simple_sitemap\Simplesitemap;
+
+/**
+ * Class UrlGeneratorBase
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator
+ */
+abstract class UrlGeneratorBase extends SimplesitemapPluginBase implements UrlGeneratorInterface {
+
+  /**
+   * @var \Drupal\simple_sitemap\Simplesitemap
+   */
+  protected $generator;
+
+  /**
+   * @var \Drupal\simple_sitemap\Logger
+   */
+  protected $logger;
+
+  /**
+   * @var array
+   */
+  protected $settings;
+
+  /**
+   * @var string
+   */
+  protected $sitemapVariant;
+
+  /**
+   * UrlGeneratorBase constructor.
+   * @param array $configuration
+   * @param $plugin_id
+   * @param $plugin_definition
+   * @param \Drupal\simple_sitemap\Simplesitemap $generator
+   * @param \Drupal\simple_sitemap\Logger $logger
+   */
+  public function __construct(
+    array $configuration,
+    $plugin_id,
+    $plugin_definition,
+    Simplesitemap $generator,
+    Logger $logger
+  ) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->generator = $generator;
+    $this->logger = $logger;
+  }
+
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('simple_sitemap.generator'),
+      $container->get('simple_sitemap.logger')
+    );
+  }
+
+  /**
+   * @param array $settings
+   * @return $this
+   */
+  public function setSettings(array $settings) {
+    $this->settings = $settings;
+    return $this;
+  }
+
+  /**
+   * @param string $sitemap_variant
+   * @return $this
+   */
+  public function setSitemapVariant($sitemap_variant) {
+    $this->sitemapVariant = $sitemap_variant;
+    return $this;
+  }
+
+  /**
+   * @param string $url
+   * @return string
+   */
+  protected function replaceBaseUrlWithCustom($url) {
+    return !empty($this->settings['base_url'])
+      ? str_replace($GLOBALS['base_url'], $this->settings['base_url'], $url)
+      : $url;
+  }
+
+  /**
+   * @return mixed
+   */
+  abstract public function getDataSets();
+
+  /**
+   * @param $data_set
+   * @return mixed
+   */
+  abstract protected function processDataSet($data_set);
+
+  /**
+   * @param $data_set
+   * @return array
+   */
+  public function generate($data_set) {
+    $path_data = $this->processDataSet($data_set);
+    return FALSE !== $path_data ? [$path_data] : [];
+  }
+}

+ 18 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/UrlGenerator/UrlGeneratorInterface.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator;
+
+/**
+ * Interface UrlGeneratorInterface
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator
+ */
+interface UrlGeneratorInterface {
+
+  function setSettings(array $settings);
+
+  function setSitemapVariant($sitemap_variant);
+
+  function getDataSets();
+
+  function generate($data_set);
+}

+ 37 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Plugin/simple_sitemap/UrlGenerator/UrlGeneratorManager.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator;
+
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+
+/**
+ * Class UrlGeneratorManager
+ * @package Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator
+ */
+class UrlGeneratorManager extends DefaultPluginManager {
+
+  /**
+   * UrlGeneratorManager constructor.
+   * @param \Traversable $namespaces
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   */
+  public function __construct(
+    \Traversable $namespaces,
+    CacheBackendInterface $cache_backend,
+    ModuleHandlerInterface $module_handler
+  ) {
+    parent::__construct(
+      'Plugin/simple_sitemap/UrlGenerator',
+      $namespaces,
+      $module_handler,
+      'Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator\UrlGeneratorInterface',
+      'Drupal\simple_sitemap\Annotation\UrlGenerator'
+    );
+
+    $this->alterInfo('simple_sitemap_url_generators');
+    $this->setCacheBackend($cache_backend, 'simple_sitemap:url_generator');
+  }
+}

+ 102 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Queue/BatchTrait.php

@@ -0,0 +1,102 @@
+<?php
+
+namespace Drupal\simple_sitemap\Queue;
+
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+trait BatchTrait {
+
+  use StringTranslationTrait;
+
+  /**
+   * @var array
+   */
+  protected $batch;
+
+  protected static $batchErrorMessage = 'The generation failed to finish. It can be continued manually on the module\'s setting page, or via drush.';
+
+  /**
+   * @param string $from
+   * @param array|null $variants
+   * @return bool
+   */
+  public function batchGenerateSitemap($from = 'form', $variants = NULL) {
+    $this->batch = [
+      'title' => $this->t('Generating XML sitemaps'),
+      'init_message' => $this->t('Initializing...'),
+      'error_message' => $this->t(self::$batchErrorMessage),
+      'progress_message' => $this->t('Processing items from queue. Each sitemap variant is published as soon as its items have been processed.'),
+      'operations' => [[ __CLASS__ . '::' . 'doBatchGenerateSitemap', []]],
+      'finished' => [__CLASS__, 'finishGeneration'],
+    ];
+
+    switch ($from) {
+
+      case 'form':
+        // Start batch process.
+        batch_set($this->batch);
+        return TRUE;
+
+      case 'drush':
+        // Start drush batch process.
+        batch_set($this->batch);
+
+        // See https://www.drupal.org/node/638712
+        $this->batch =& batch_get();
+        $this->batch['progressive'] = FALSE;
+
+        drush_backend_batch_process();
+        return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * @param $context
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   *
+   * @todo Make sure batch does not run at the same time as cron.
+   * @todo Variants into generateSitemap().
+   */
+  public static function doBatchGenerateSitemap(&$context) {
+
+    /** @var \Drupal\simple_sitemap\Queue\QueueWorker $queue_worker */
+    $queue_worker = \Drupal::service('simple_sitemap.queue_worker');
+
+    $queue_worker->generateSitemap();
+    $processed_element_count = $queue_worker->getProcessedElementCount();
+    $original_element_count = $queue_worker->getInitialElementCount();
+
+    $context['message'] = t('@indexed out of @total total items have been processed.', [
+      '@indexed' => $processed_element_count, '@total' => $original_element_count]);
+    $context['finished'] = $original_element_count > 0 ? ($processed_element_count / $original_element_count) : 1;
+  }
+
+  /**
+   * Callback function called by the batch API when all operations are finished.
+   *
+   * @param bool $success
+   * @param array $results
+   * @param array $operations
+   *
+   * @return bool
+   *
+   * @see https://api.drupal.org/api/drupal/core!includes!form.inc/group/batch/8
+   */
+  public static function finishGeneration($success, $results, $operations) {
+    if ($success) {
+      \Drupal::service('simple_sitemap.logger')
+        ->m('The XML sitemaps have been regenerated.')
+        ->log('info');
+    }
+    else {
+      \Drupal::service('simple_sitemap.logger')
+        ->m(self::$batchErrorMessage)
+        ->display('error', 'administer sitemap settings')
+        ->log('error');
+    }
+
+    return $success;
+  }
+}
+

+ 388 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Queue/QueueWorker.php

@@ -0,0 +1,388 @@
+<?php
+
+namespace Drupal\simple_sitemap\Queue;
+
+use Drupal\Component\Utility\Timer;
+use Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator\SitemapGeneratorBase;
+use Drupal\simple_sitemap\SimplesitemapSettings;
+use Drupal\simple_sitemap\SimplesitemapManager;
+use Drupal\Core\State\StateInterface;
+use Drupal\simple_sitemap\Logger;
+
+
+class QueueWorker {
+
+  use BatchTrait;
+
+  const REBUILD_QUEUE_CHUNK_ITEM_SIZE = 5000;
+
+  /**
+   * @var \Drupal\simple_sitemap\SimplesitemapSettings
+   */
+  protected $settings;
+
+  /**
+   * @var \Drupal\simple_sitemap\SimplesitemapManager
+   */
+  protected $manager;
+
+  /**
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * @var \Drupal\simple_sitemap\Queue\SimplesitemapQueue
+   */
+  protected $queue;
+
+  /**
+   * @var \Drupal\simple_sitemap\Logger
+   */
+  protected $logger;
+
+  /**
+   * @var string|null
+   */
+  protected $variantProcessedNow;
+
+  /**
+   * @var string|null
+   */
+  protected $generatorProcessedNow;
+
+  /**
+   * @var array
+   */
+  protected $results = [];
+
+  /**
+   * @var array
+   */
+  protected $processedPaths = [];
+
+  /**
+   * @var array
+   */
+  protected $generatorSettings;
+
+  /**
+   * @var int|null
+   */
+  protected $maxLinks;
+
+  /**
+   * @var int|null
+   */
+  protected $elementsRemaining;
+
+  /**
+   * @var int|null
+   */
+  protected $elementsTotal;
+
+  /**
+   * QueueWorker constructor.
+   * @param \Drupal\simple_sitemap\SimplesitemapSettings $settings
+   * @param \Drupal\simple_sitemap\SimplesitemapManager $manager
+   * @param \Drupal\Core\State\StateInterface $state
+   * @param \Drupal\simple_sitemap\Queue\SimplesitemapQueue $element_queue
+   * @param \Drupal\simple_sitemap\Logger $logger
+   */
+  public function __construct(SimplesitemapSettings $settings,
+                              SimplesitemapManager $manager,
+                              StateInterface $state,
+                              SimplesitemapQueue $element_queue,
+                              Logger $logger) {
+    $this->settings = $settings;
+    $this->manager = $manager;
+    $this->state = $state;
+    $this->queue = $element_queue;
+    $this->logger = $logger;
+  }
+
+  /**
+   * @return $this
+   */
+  public function deleteQueue() {
+    $this->queue->deleteQueue();
+    SitemapGeneratorBase::purgeSitemapVariants(NULL, 'unpublished');
+    $this->variantProcessedNow = NULL;
+    $this->generatorProcessedNow = NULL;
+    $this->results = [];
+    $this->processedPaths = [];
+    $this->state->set('simple_sitemap.queue_items_initial_amount', 0);
+    $this->state->delete('simple_sitemap.queue_stashed_results');
+    $this->elementsTotal = NULL;
+    $this->elementsRemaining = NULL;
+
+    return $this;
+  }
+
+  /**
+   * @param array|null $variants
+   * @return $this
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function rebuildQueue($variants = NULL) {
+    $all_data_sets = [];
+    $sitemap_variants = $this->manager->getSitemapVariants();
+
+    $type_definitions = $this->manager->getSitemapTypes();
+    $this->deleteQueue();
+
+    foreach ($sitemap_variants as $variant_name => $variant_definition) {
+
+      // Skipping unwanted sitemap variants.
+      if (NULL !== $variants && !in_array($variant_name, (array) $variants)) {
+        continue;
+      }
+
+      $type = $variant_definition['type'];
+
+      // Adding generate_sitemap operations for all data sets.
+      foreach ($type_definitions[$type]['urlGenerators'] as $url_generator_id) {
+
+        $data_sets = $this->manager->getUrlGenerator($url_generator_id)
+          ->setSitemapVariant($variant_name)
+          ->getDataSets();
+
+        if (!empty($data_sets)) {
+          $sitemap_variants[$variant_name]['data'] = TRUE;
+          foreach ($data_sets as $data_set) {
+            $all_data_sets[] = [
+              'data' => $data_set,
+              'sitemap_variant' => $variant_name,
+              'url_generator' => $url_generator_id,
+              'sitemap_generator' => $type_definitions[$type]['sitemapGenerator'],
+            ];
+
+            if (count($all_data_sets) === self::REBUILD_QUEUE_CHUNK_ITEM_SIZE) {
+              $this->queueElements($all_data_sets);
+              $all_data_sets = [];
+            }
+          }
+        }
+      }
+    }
+
+    if (!empty($all_data_sets)) {
+      $this->queueElements($all_data_sets);
+    }
+    $this->getQueuedElementCount(TRUE);
+
+    // todo: May not be clean to remove sitemap variants data when queuing elements.
+    // todo: Add test.
+    // Remove all sitemap variant instances where no results have been queued.
+    $this->manager->removeSitemap(array_keys(array_filter($sitemap_variants, function($e) { return empty($e['data']); })));
+
+    return $this;
+  }
+
+  protected function queueElements($elements) {
+    $this->queue->createItems($elements);
+    $this->state->set('simple_sitemap.queue_items_initial_amount', ($this->state->get('simple_sitemap.queue_items_initial_amount') + count($elements)));
+  }
+
+  /**
+   * @param string $from
+   * @return $this
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function generateSitemap($from = 'form') {
+
+    $this->generatorSettings = [
+      'base_url' => $this->settings->getSetting('base_url', ''),
+      'default_variant' => $this->settings->getSetting('default_variant', NULL),
+      'skip_untranslated' => $this->settings->getSetting('skip_untranslated', FALSE),
+      'remove_duplicates' => $this->settings->getSetting('remove_duplicates', TRUE),
+      'excluded_languages' => $this->settings->getSetting('excluded_languages', []),
+    ];
+    $this->maxLinks = $this->settings->getSetting('max_links');
+    $max_execution_time = $this->settings->getSetting('generate_duration', 10000);
+    Timer::start('simple_sitemap_generator');
+
+    $this->unstashResults();
+
+    if (!$this->generationInProgress()) {
+      $this->rebuildQueue();
+    }
+
+    while ($element = $this->queue->claimItem()) {
+
+      if (!empty($max_execution_time) && Timer::read('simple_sitemap_generator') >= $max_execution_time) {
+        break;
+      }
+
+      try {
+        if ($element->data['sitemap_variant'] !== $this->variantProcessedNow) {
+
+          if (NULL !== $this->variantProcessedNow) {
+            $this->generateVariantChunksFromResults(TRUE);
+            $this->publishCurrentVariant();
+          }
+
+          $this->variantProcessedNow = $element->data['sitemap_variant'];
+          $this->generatorProcessedNow = $element->data['sitemap_generator'];
+          $this->processedPaths = [];
+        }
+
+        $this->generateResultsFromElement($element);
+
+        if (!empty($this->maxLinks) && count($this->results) >= $this->maxLinks) {
+          $this->generateVariantChunksFromResults();
+        }
+      }
+      catch (\Exception $e) {
+        watchdog_exception('simple_sitemap', $e);
+      }
+
+      $this->queue->deleteItem($element); //todo May want to use deleteItems() instead.
+      $this->elementsRemaining--;
+    }
+
+    if ($this->getQueuedElementCount() === 0) {
+      $this->generateVariantChunksFromResults(TRUE);
+      $this->publishCurrentVariant();
+    }
+    else {
+      $this->stashResults();
+    }
+
+    return $this;
+  }
+
+  /**
+   * @param $element
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  protected function generateResultsFromElement($element) {
+    $results = $this->manager->getUrlGenerator($element->data['url_generator'])
+      ->setSitemapVariant($this->variantProcessedNow)
+      ->setSettings($this->generatorSettings)
+      ->generate($element->data['data']);
+
+    $this->removeDuplicates($results);
+    $this->results = array_merge($this->results, $results);
+  }
+
+  /**
+   * @param array $results
+   */
+  protected function removeDuplicates(&$results) {
+    if ($this->generatorSettings['remove_duplicates'] && !empty($results)) {
+      $result = $results[key($results)];
+      if (!empty($result['meta']['path'])) {
+        if (in_array($result['meta']['path'], $this->processedPaths)) {
+          $results = [];
+        }
+        else {
+          $this->processedPaths[] = $result['meta']['path'];
+        }
+      }
+    }
+  }
+
+  /**
+   * @param bool $complete
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  protected function generateVariantChunksFromResults($complete = FALSE) {
+    if (!empty($this->results)) {
+      $generator = $this->manager->getSitemapGenerator($this->generatorProcessedNow)
+        ->setSitemapVariant($this->variantProcessedNow)
+        ->setSettings($this->generatorSettings);
+
+      if (empty($this->maxLinks) || $complete) {
+        $generator->generate($this->results);
+        $this->results = [];
+      }
+      else {
+        foreach (array_chunk($this->results, $this->maxLinks, TRUE) as $chunk_links) {
+          if (count($chunk_links) === $this->maxLinks || $complete) {
+            $generator->generate($chunk_links);
+            $this->results = array_diff_key($this->results, $chunk_links);
+          }
+        }
+      }
+    };
+  }
+
+  protected function publishCurrentVariant() {
+    if ($this->variantProcessedNow !== NULL) {
+      $this->manager->getSitemapGenerator($this->generatorProcessedNow)
+        ->setSitemapVariant($this->variantProcessedNow)
+        ->setSettings($this->generatorSettings)
+        ->generateIndex()
+        ->publish();
+    }
+  }
+
+  protected function stashResults() {
+    $this->state->set('simple_sitemap.queue_stashed_results', [
+      'variant' => $this->variantProcessedNow,
+      'generator' => $this->generatorProcessedNow,
+      'results' => $this->results,
+      'processed_paths' => $this->processedPaths,
+    ]);
+    $this->results = [];
+    $this->processedPaths = [];
+    $this->generatorProcessedNow = NULL;
+    $this->variantProcessedNow = NULL;
+  }
+
+  protected function unstashResults() {
+    if (NULL !== $results = $this->state->get('simple_sitemap.queue_stashed_results')) {
+      $this->state->delete('simple_sitemap.queue_stashed_results');
+      $this->results = !empty($results['results']) ? $results['results'] : [];
+      $this->processedPaths = !empty($results['processed_paths']) ? $results['processed_paths'] : [];
+      $this->variantProcessedNow = $results['variant'];
+      $this->generatorProcessedNow = $results['generator'];
+    }
+  }
+
+  public function getInitialElementCount() {
+    if (NULL === $this->elementsTotal) {
+      $this->elementsTotal = (int) $this->state->get('simple_sitemap.queue_items_initial_amount', 0);
+    }
+
+    return $this->elementsTotal;
+  }
+
+  /**
+   * @param bool $force_recount
+   * @return int
+   */
+  public function getQueuedElementCount($force_recount = FALSE) {
+    if ($force_recount || NULL === $this->elementsRemaining) {
+      $this->elementsRemaining = $this->queue->numberOfItems();
+    }
+
+    return $this->elementsRemaining;
+  }
+
+  /**
+   * @return int
+   */
+  public function getStashedResultCount() {
+    return count($this->state->get('simple_sitemap.queue_stashed_results', ['results' => []])['results']);
+  }
+
+  /**
+   * @return int
+   */
+  public function getProcessedElementCount() {
+    $initial = $this->getInitialElementCount();
+    $remaining = $this->getQueuedElementCount();
+
+    return $initial > $remaining ? ($initial - $remaining) : 0;
+  }
+
+  /**
+   * @return bool
+   */
+  public function generationInProgress() {
+    return 0 < ($this->getQueuedElementCount() + $this->getStashedResultCount());
+  }
+}
+

+ 82 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Queue/SimplesitemapQueue.php

@@ -0,0 +1,82 @@
+<?php
+
+namespace Drupal\simple_sitemap\Queue;
+
+use Drupal\Core\Queue\DatabaseQueue;
+
+/**
+ * Class SimplesitemapQueue
+ * @package Drupal\simple_sitemap\Queue
+ */
+class SimplesitemapQueue extends DatabaseQueue {
+
+  /**
+   * Overrides \Drupal\Core\Queue\DatabaseQueue::claimItem().
+   *
+   * Unlike \Drupal\Core\Queue\DatabaseQueue::claimItem(), this method provides
+   * a default lease time of 0 (no expiration) instead of 30. This allows the
+   * item to be claimed repeatedly until it is deleted.
+   */
+  public function claimItem($lease_time = 0) {
+    try {
+      $item = $this->connection->queryRange('SELECT data, item_id FROM {queue} q WHERE name = :name ORDER BY item_id ASC', 0, 1, [':name' => $this->name])->fetchObject();
+      if ($item) {
+        $item->data = unserialize($item->data);
+        return $item;
+      }
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
+    }
+
+    return FALSE;
+  }
+
+  public function createItems($data_sets) {
+    $try_again = FALSE;
+    try {
+      $id = $this->doCreateItems($data_sets);
+    }
+    catch (\Exception $e) {
+      // If there was an exception, try to create the table.
+      if (!$try_again = $this->ensureTableExists()) {
+        // If the exception happened for other reason than the missing table,
+        // propagate the exception.
+        throw $e;
+      }
+    }
+    // Now that the table has been created, try again if necessary.
+    if ($try_again) {
+      $id = $this->doCreateItems($data_sets);
+    }
+
+    return $id;
+  }
+
+  protected function doCreateItems($data_sets) {
+    $query = $this->connection->insert(static::TABLE_NAME)
+      ->fields(['name', 'data', 'created']);
+
+    foreach ($data_sets as $i => $data) {
+      $query->values([
+        $this->name,
+        serialize($data),
+        time(),
+      ]);
+    }
+
+    return $query->execute();
+  }
+
+  public function deleteItems($item_ids) {
+    try {
+      $this->connection->delete(static::TABLE_NAME)
+        ->condition('item_id', $item_ids, 'IN')
+        ->execute();
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
+    }
+  }
+
+}

+ 948 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/Simplesitemap.php

@@ -0,0 +1,948 @@
+<?php
+
+namespace Drupal\simple_sitemap;
+
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\simple_sitemap\Queue\QueueWorker;
+use Drupal\Core\Path\PathValidator;
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Datetime\DateFormatter;
+use Drupal\Component\Datetime\Time;
+use Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator\SitemapGeneratorBase;
+
+/**
+ * Class Simplesitemap
+ * @package Drupal\simple_sitemap
+ */
+class Simplesitemap {
+  /**
+   * @var \Drupal\simple_sitemap\EntityHelper
+   */
+  protected $entityHelper;
+
+  /**
+   * @var \Drupal\simple_sitemap\SimplesitemapSettings
+   */
+  protected $settings;
+
+  /**
+   * @var \Drupal\simple_sitemap\SimplesitemapManager
+   */
+  protected $manager;
+
+  /**
+   * @var \Drupal\Core\Config\ConfigFactory
+   */
+  protected $configFactory;
+
+  /**
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $db;
+
+  /**
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
+   */
+  protected $entityTypeBundleInfo;
+
+  /**
+   * @var \Drupal\Core\Path\PathValidator
+   */
+  protected $pathValidator;
+
+  /**
+   * @var \Drupal\Core\Datetime\DateFormatter
+   */
+  protected $dateFormatter;
+
+  /**
+   * @var \Drupal\Component\Datetime\Time
+   */
+  protected $time;
+
+  /**
+   * @var \Drupal\simple_sitemap\Queue\QueueWorker
+   */
+  protected $queueWorker;
+
+  /**
+   * @var array
+   */
+  protected $variants;
+
+  /**
+   * @var array
+   */
+  protected static $allowedLinkSettings = [
+    'entity' => ['index', 'priority', 'changefreq', 'include_images'],
+    'custom' => ['priority', 'changefreq'],
+  ];
+
+  /**
+   * @var array
+   */
+  protected static $linkSettingDefaults = [
+    'index' => FALSE,
+    'priority' => '0.5',
+    'changefreq' => '',
+    'include_images' => FALSE,
+  ];
+
+  /**
+   * Simplesitemap constructor.
+   * @param \Drupal\simple_sitemap\EntityHelper $entity_helper
+   * @param \Drupal\simple_sitemap\SimplesitemapSettings $settings
+   * @param \Drupal\simple_sitemap\SimplesitemapManager $manager
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   * @param \Drupal\Core\Database\Connection $database
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
+   * @param \Drupal\Core\Path\PathValidator $path_validator
+   * @param \Drupal\Core\Datetime\DateFormatter $date_formatter
+   * @param \Drupal\Component\Datetime\Time $time
+   * @param \Drupal\simple_sitemap\Queue\QueueWorker $queue_worker
+   */
+  public function __construct(
+    EntityHelper $entity_helper,
+    SimplesitemapSettings $settings,
+    SimplesitemapManager $manager,
+    ConfigFactory $config_factory,
+    Connection $database,
+    EntityTypeManagerInterface $entity_type_manager,
+    EntityTypeBundleInfoInterface $entity_type_bundle_info,
+    PathValidator $path_validator,
+    DateFormatter $date_formatter,
+    Time $time,
+    QueueWorker $queue_worker
+  ) {
+    $this->entityHelper = $entity_helper;
+    $this->settings = $settings;
+    $this->manager = $manager;
+    $this->configFactory = $config_factory;
+    $this->db = $database;
+    $this->entityTypeManager = $entity_type_manager;
+    $this->entityTypeBundleInfo = $entity_type_bundle_info;
+    $this->pathValidator = $path_validator;
+    $this->dateFormatter = $date_formatter;
+    $this->time = $time;
+    $this->queueWorker = $queue_worker;
+  }
+
+  /**
+   * Returns a specific sitemap setting or a default value if setting does not
+   * exist.
+   *
+   * @param string $name
+   *  Name of the setting, like 'max_links'.
+   *
+   * @param mixed $default
+   *  Value to be returned if the setting does not exist in the configuration.
+   *
+   * @return mixed
+   *  The current setting from configuration or a default value.
+   */
+  public function getSetting($name, $default = FALSE) {
+    return $this->settings->getSetting($name, $default);
+  }
+
+  /**
+   * Stores a specific sitemap setting in configuration.
+   *
+   * @param string $name
+   *  Setting name, like 'max_links'.
+   *
+   * @param mixed $setting
+   *  The setting to be saved.
+   *
+   * @return $this
+   */
+  public function saveSetting($name, $setting) {
+    $this->settings->saveSetting($name, $setting);
+    return $this;
+  }
+
+  /**
+   * @return \Drupal\simple_sitemap\Queue\QueueWorker
+   */
+  public function getQueueWorker() {
+    return $this->queueWorker;
+  }
+
+  /**
+   * @return \Drupal\simple_sitemap\SimplesitemapManager
+   */
+  public function getSitemapManager() {
+    return $this->manager;
+  }
+
+  /**
+   * @param array|string|true|null $variants
+   *  array: Array of variants to be set.
+   *  string: A particular variant to be set.
+   *  null: Default variant will be set.
+   *  true: All existing variants will be set.
+   *
+   * @return $this
+   */
+  public function setVariants($variants = NULL) {
+    if (NULL === $variants) {
+      $this->variants = FALSE !== ($default_variant = $this->getSetting('default_variant')) ? [$default_variant] : [];
+    }
+    elseif ($variants === TRUE) {
+      $this->variants = array_keys(
+        $this->manager->getSitemapVariants(NULL, FALSE));
+    }
+    else {
+      $this->variants = (array) $variants;
+    }
+
+    return $this;
+  }
+
+  /**
+   * Gets the currently set variants, the default variant, or all variants.
+   *
+   * @param bool $default_get_all
+   *  If true and no variants are set, all variants are returned. If false and
+   *  no variants are set, only the default variant is returned.
+   *
+   * @return array
+   */
+  protected function getVariants($default_get_all = TRUE) {
+    if (NULL === $this->variants) {
+      $this->setVariants($default_get_all ? TRUE : NULL);
+    }
+
+    return $this->variants;
+  }
+
+  /**
+   * Returns the whole sitemap, a requested sitemap chunk,
+   * or the sitemap index file.
+   *
+   * @param int $delta
+   *
+   * @return string|false
+   *  If no sitemap delta is provided, either a sitemap index is returned, or the
+   *  whole sitemap variant, if the amount of links does not exceed the max
+   *  links setting. If a sitemap delta is provided, a sitemap chunk is returned.
+   *  Returns false if the sitemap is not retrievable from the database.
+   */
+  public function getSitemap($delta = NULL) {
+    $chunk_info = $this->fetchSitemapVariantInfo();
+
+    if (empty($delta) || !isset($chunk_info[$delta])) {
+
+      if (isset($chunk_info[SitemapGeneratorBase::INDEX_DELTA])) {
+        // Return sitemap index if one exists.
+        return $this->fetchSitemapChunk($chunk_info[SitemapGeneratorBase::INDEX_DELTA]->id)
+          ->sitemap_string;
+      }
+      else {
+        // Return sitemap chunk if there is only one chunk.
+        return isset($chunk_info[SitemapGeneratorBase::FIRST_CHUNK_DELTA])
+          ? $this->fetchSitemapChunk($chunk_info[SitemapGeneratorBase::FIRST_CHUNK_DELTA]->id)
+            ->sitemap_string
+          : FALSE;
+      }
+    }
+    else {
+      // Return specific sitemap chunk.
+      return $this->fetchSitemapChunk($chunk_info[$delta]->id)->sitemap_string;
+    }
+  }
+
+  /**
+   * Fetches info about all published sitemap variants and their chunks.
+   *
+   * @return array
+   *  An array containing all published sitemap chunk IDs, deltas and creation
+   *  timestamps keyed by the currently set variants, or in case of only one
+   *  variant set the above keyed by sitemap delta.
+   */
+  protected function fetchSitemapVariantInfo() {
+    $result = $this->db->select('simple_sitemap', 's')
+      ->fields('s', ['id', 'delta', 'sitemap_created', 'type'])
+      ->condition('s.status', 1)
+      ->condition('s.type', $this->getVariants(), 'IN')
+      ->execute();
+
+    return count($this->getVariants()) > 1
+      ? $result->fetchAllAssoc('type')
+      : $result->fetchAllAssoc('delta');
+  }
+
+  /**
+   * Fetches a single sitemap chunk by ID.
+   *
+   * @param int $id
+   *   The chunk ID.
+   *
+   * @return object
+   *   A sitemap chunk object.
+   */
+  protected function fetchSitemapChunk($id) {
+    return $this->db->query('SELECT * FROM {simple_sitemap} WHERE id = :id',
+      [':id' => $id])->fetchObject();
+  }
+
+  /**
+   * Removes sitemap instances for the currently set variants.
+   *
+   * @return $this
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function removeSitemap() {
+    $this->manager->removeSitemap($this->getVariants(FALSE));
+
+    return $this;
+  }
+
+  /**
+   * Generates all sitemaps.
+   *
+   * @param string $from
+   *  Can be 'form', 'drush', 'cron' and 'backend'.
+   *
+   * @return $this
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   *
+   * @todo Respect $this->variants and generate for specific variants.
+   * @todo Implement lock functionality.
+   */
+  public function generateSitemap($from = 'form') {
+    switch($from) {
+      case 'form':
+      case 'drush':
+        $this->queueWorker->batchGenerateSitemap($from);
+        break;
+
+      case 'cron':
+      case 'backend':
+        $this->queueWorker->generateSitemap($from);
+        break;
+    }
+
+    return $this;
+  }
+
+  /**
+   * Rebuilds the queue for the currently set variants.
+   *
+   * @return $this
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function rebuildQueue() {
+    $this->queueWorker->rebuildQueue($this->getVariants());
+
+    return $this;
+  }
+
+  /**
+   * Returns a 'time ago' string of last timestamp generation.
+   *
+   * @param string|null $variant
+   *
+   * @return string|array|false
+   *  Formatted timestamp of last sitemap generation, otherwise FALSE.
+   */
+/*  public function getGeneratedAgo() {
+    $chunks = $this->fetchSitemapVariantInfo();
+    return isset($chunks[DefaultSitemapGenerator::FIRST_CHUNK_DELTA]->sitemap_created)
+      ? $this->dateFormatter
+        ->formatInterval($this->time->getRequestTime() - $chunks[DefaultSitemapGenerator::FIRST_CHUNK_DELTA]
+            ->sitemap_created)
+      : FALSE;
+  }*/
+
+  /**
+   * Enables sitemap support for an entity type. Enabled entity types show
+   * sitemap settings on their bundle setting forms. If an enabled entity type
+   * features bundles (e.g. 'node'), it needs to be set up with
+   * setBundleSettings() as well.
+   *
+   * @param string $entity_type_id
+   *  Entity type id like 'node'.
+   *
+   * @return $this
+   */
+  public function enableEntityType($entity_type_id) {
+    $enabled_entity_types = $this->getSetting('enabled_entity_types');
+    if (!in_array($entity_type_id, $enabled_entity_types)) {
+      $enabled_entity_types[] = $entity_type_id;
+      $this->saveSetting('enabled_entity_types', $enabled_entity_types);
+    }
+    return $this;
+  }
+
+  /**
+   * Disables sitemap support for an entity type. Disabling support for an
+   * entity type deletes its sitemap settings permanently and removes sitemap
+   * settings from entity forms.
+   *
+   * @param string $entity_type_id
+   *
+   * @return $this
+   */
+  public function disableEntityType($entity_type_id) {
+
+    // Updating settings.
+    $enabled_entity_types = $this->getSetting('enabled_entity_types');
+    if (FALSE !== ($key = array_search($entity_type_id, $enabled_entity_types))) {
+      unset ($enabled_entity_types[$key]);
+      $this->saveSetting('enabled_entity_types', array_values($enabled_entity_types));
+    }
+
+    // Deleting inclusion settings.
+    $config_names = $this->configFactory->listAll('simple_sitemap.bundle_settings.');
+    foreach ($config_names as $config_name) {
+      $config_name_parts = explode('.', $config_name);
+      if ($config_name_parts[3] === $entity_type_id) {
+        $this->configFactory->getEditable($config_name)->delete();
+      }
+    }
+
+    // Deleting entity overrides.
+    $this->setVariants(TRUE)->removeEntityInstanceSettings($entity_type_id);
+
+    return $this;
+  }
+
+  /**
+   * Sets settings for bundle or non-bundle entity types. This is done for the
+   * currently set variant.
+   *
+   * @param $entity_type_id
+   * @param null $bundle_name
+   * @param array $settings
+   *
+   * @return $this
+   *
+   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+   *
+   * @todo: enableEntityType automatically
+   * @todo multiple variants
+   */
+  public function setBundleSettings($entity_type_id, $bundle_name = NULL, $settings = ['index' => TRUE]) {
+    if (empty($variants = $this->getVariants(FALSE))) {
+      return $this;
+    }
+
+    $bundle_name = NULL !== $bundle_name ? $bundle_name : $entity_type_id;
+
+    if (!empty($old_settings = $this->getBundleSettings($entity_type_id, $bundle_name))) {
+      $settings = array_merge($old_settings, $settings);
+    }
+    self::supplementDefaultSettings('entity', $settings);
+
+    if ($settings != $old_settings) {
+
+      // Save new bundle settings to configuration.
+      $bundle_settings = $this->configFactory
+        ->getEditable("simple_sitemap.bundle_settings.$variants[0].$entity_type_id.$bundle_name");
+      foreach ($settings as $setting_key => $setting) {
+        $bundle_settings->set($setting_key, $setting);
+      }
+      $bundle_settings->save();
+
+      // Delete entity overrides which are identical to new bundle settings.
+      $entity_ids = $this->entityHelper->getEntityInstanceIds($entity_type_id, $bundle_name);
+      $query = $this->db->select('simple_sitemap_entity_overrides', 'o')
+        ->fields('o', ['id', 'inclusion_settings'])
+        ->condition('o.entity_type', $entity_type_id)
+        ->condition('o.type', $variants[0]);
+      if (!empty($entity_ids)) {
+        $query->condition('o.entity_id', $entity_ids, 'IN');
+      }
+
+      $delete_instances = [];
+      foreach ($query->execute()->fetchAll() as $result) {
+        $delete = TRUE;
+        $instance_settings = unserialize($result->inclusion_settings);
+        foreach ($instance_settings as $setting_key => $instance_setting) {
+          if ($instance_setting != $settings[$setting_key]) {
+            $delete = FALSE;
+            break;
+          }
+        }
+        if ($delete) {
+          $delete_instances[] = $result->id;
+        }
+      }
+      if (!empty($delete_instances)) {
+        $this->db->delete('simple_sitemap_entity_overrides')
+          ->condition('id', $delete_instances, 'IN')
+          ->execute();
+      }
+    }
+
+    return $this;
+  }
+
+  /**
+   * Gets settings for bundle or non-bundle entity types. This is done for the
+   * currently set variants.
+   *
+   * @param string|null $entity_type_id
+   *  Limit the result set to a specific entity type.
+   *
+   * @param string|null $bundle_name
+   *  Limit the result set to a specific bundle name.
+   *
+   * @param bool $supplement_defaults
+   *  Supplements the result set with default custom link settings.
+   *
+   * @param bool $multiple_variants
+   *  If true, returns an array of results keyed by variant name, otherwise it
+   *  returns the result set for the first variant only.
+   *
+   * @return array|false
+   *  Array of settings or array of settings keyed by variant name. False if
+   *  entity type does not exist.
+   */
+  public function getBundleSettings($entity_type_id = NULL, $bundle_name = NULL, $supplement_defaults = TRUE, $multiple_variants = FALSE) {
+
+    $all_bundle_settings = [];
+
+    foreach ($variants = $this->getVariants(FALSE) as $variant) {
+      if (NULL !== $entity_type_id) {
+        $bundle_name = NULL !== $bundle_name ? $bundle_name : $entity_type_id;
+
+        $bundle_settings = $this->configFactory
+          ->get("simple_sitemap.bundle_settings.$variant.$entity_type_id.$bundle_name")
+          ->get();
+
+        // If not found and entity type is enabled, return default bundle settings.
+        if (empty($bundle_settings) && $supplement_defaults) {
+          if ($this->entityTypeIsEnabled($entity_type_id)
+            && isset($this->entityTypeBundleInfo->getBundleInfo($entity_type_id)[$bundle_name])) {
+            self::supplementDefaultSettings('entity', $bundle_settings);
+          }
+          else {
+            $bundle_settings = NULL;
+          }
+        }
+      }
+      else {
+        $config_names = $this->configFactory->listAll("simple_sitemap.bundle_settings.$variant.");
+        $bundle_settings = [];
+        foreach ($config_names as $config_name) {
+          $config_name_parts = explode('.', $config_name);
+          $bundle_settings[$config_name_parts[3]][$config_name_parts[4]] = $this->configFactory->get($config_name)->get();
+        }
+
+        // Supplement default bundle settings for all bundles not found in simple_sitemap.bundle_settings.*.* configuration.
+        if ($supplement_defaults) {
+          foreach ($this->entityHelper->getSupportedEntityTypes() as $type_id => $type_definition) {
+            if ($this->entityTypeIsEnabled($type_id)) {
+              foreach($this->entityTypeBundleInfo->getBundleInfo($type_id) as $bundle => $bundle_definition) {
+                if (!isset($bundle_settings[$type_id][$bundle])) {
+                  self::supplementDefaultSettings('entity', $bundle_settings[$type_id][$bundle]);
+                }
+              }
+            }
+          }
+        }
+      }
+      if ($multiple_variants) {
+        if (!empty($bundle_settings)) {
+          $all_bundle_settings[$variant] = $bundle_settings;
+        }
+      }
+      else {
+        return $bundle_settings;
+      }
+    }
+
+    return $all_bundle_settings;
+  }
+
+  /**
+   * Removes settings for bundle or a non-bundle entity types. This is done for
+   * the currently set variants.
+   *
+   * @param string|null $entity_type_id
+   *  Limit the removal to a specific entity type.
+   *
+   * @param string|null $bundle_name
+   *  Limit the removal to a specific bundle name.
+   *
+   * @return $this
+   *
+   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+   */
+  public function removeBundleSettings($entity_type_id = NULL, $bundle_name = NULL) {
+    if (empty($variants = $this->getVariants(FALSE))) {
+      return $this;
+    }
+
+    if (NULL !== $entity_type_id) {
+      $bundle_name = NULL !== $bundle_name ? $bundle_name : $entity_type_id;
+
+      foreach ($variants as $variant) {
+        $this->configFactory
+          ->getEditable("simple_sitemap.bundle_settings.$variant.$entity_type_id.$bundle_name")->delete();
+      }
+
+      $this->removeEntityInstanceSettings($entity_type_id, (
+        empty($ids)
+          ? NULL
+          : $this->entityHelper->getEntityInstanceIds($entity_type_id, $bundle_name)
+      ));
+    }
+    else {
+      foreach ($variants as $variant) {
+        $config_names = $this->configFactory->listAll("simple_sitemap.bundle_settings.$variant.");
+        foreach ($config_names as $config_name) {
+          $this->configFactory->getEditable($config_name)->delete();
+        }
+        $this->removeEntityInstanceSettings();
+      }
+    }
+
+    return $this;
+  }
+
+  /**
+   * Supplements all missing link setting with default values.
+   *
+   * @param string $type
+   *  Can be 'entity' or 'custom'.
+   *
+   * @param array &$settings
+   * @param array $overrides
+   */
+  public static function supplementDefaultSettings($type, &$settings, $overrides = []) {
+    foreach (self::$allowedLinkSettings[$type] as $allowed_link_setting) {
+      if (!isset($settings[$allowed_link_setting])
+        && isset(self::$linkSettingDefaults[$allowed_link_setting])) {
+        $settings[$allowed_link_setting] = isset($overrides[$allowed_link_setting])
+          ? $overrides[$allowed_link_setting]
+          : self::$linkSettingDefaults[$allowed_link_setting];
+      }
+    }
+  }
+
+  /**
+   * Overrides sitemap settings for a single entity for the currently set
+   * variants.
+   *
+   * @param string $entity_type_id
+   * @param string $id
+   * @param array $settings
+   *
+   * @return $this
+   *
+   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+   */
+  public function setEntityInstanceSettings($entity_type_id, $id, $settings) {
+    if (empty($variants = $this->getVariants(FALSE))) {
+      return $this;
+    }
+
+    $entity = $this->entityTypeManager->getStorage($entity_type_id)->load($id);
+
+    $all_bundle_settings = $this->getBundleSettings(
+      $entity_type_id, $this->entityHelper->getEntityInstanceBundleName($entity), TRUE, TRUE
+    );
+
+    foreach ($all_bundle_settings as $variant => $bundle_settings) {
+      if (!empty($bundle_settings)) {
+
+        // Check if overrides are different from bundle setting before saving.
+        $override = FALSE;
+        foreach ($settings as $key => $setting) {
+          if (!isset($bundle_settings[$key]) || $setting != $bundle_settings[$key]) {
+            $override = TRUE;
+            break;
+          }
+        }
+
+        // Save overrides for this entity if something is different.
+        if ($override) {
+          $this->db->merge('simple_sitemap_entity_overrides')
+            ->keys([
+              'type' => $variant,
+              'entity_type' => $entity_type_id,
+              'entity_id' => $id])
+            ->fields([
+              'type' => $variant,
+              'entity_type' => $entity_type_id,
+              'entity_id' => $id,
+              'inclusion_settings' => serialize(array_merge($bundle_settings, $settings))])
+            ->execute();
+        }
+        // Else unset override.
+        else {
+          $this->removeEntityInstanceSettings($entity_type_id, $id);
+        }
+      }
+    }
+
+    return $this;
+  }
+
+  /**
+   * Gets sitemap settings for an entity instance which overrides bundle
+   * settings, or gets bundle settings, if they are not overridden. This is
+   * done for the currently set variant.
+   *
+   * @param string $entity_type_id
+   * @param string $id
+   *
+   * @return array|false
+   *  Array of entity instance settings or the settings of its bundle. False if
+   *  entity type does not exist.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+   *
+   * @todo multiple variants
+   */
+  public function getEntityInstanceSettings($entity_type_id, $id) {
+    if (empty($variants = $this->getVariants(FALSE))) {
+      return FALSE;
+    }
+
+    $results = $this->db->select('simple_sitemap_entity_overrides', 'o')
+      ->fields('o', ['inclusion_settings'])
+      ->condition('o.type', $variants[0])
+      ->condition('o.entity_type', $entity_type_id)
+      ->condition('o.entity_id', $id)
+      ->execute()
+      ->fetchField();
+
+    if (!empty($results)) {
+      return unserialize($results);
+    }
+    else {
+      $entity = $this->entityTypeManager->getStorage($entity_type_id)
+        ->load($id);
+      return $this->getBundleSettings(
+        $entity_type_id,
+        $this->entityHelper->getEntityInstanceBundleName($entity)
+      );
+    }
+  }
+
+  /**
+   * Removes sitemap settings for entities that override bundle settings. This
+   * is done for the currently set variants.
+   *
+   * @param string|null $entity_type_id
+   *  Limits the removal to a certain entity type.
+   *
+   * @param string|null $entity_ids
+   *  Limits the removal to entities with certain IDs.
+   *
+   * @return $this
+   */
+  public function removeEntityInstanceSettings($entity_type_id = NULL, $entity_ids = NULL) {
+    if (empty($variants = $this->getVariants(FALSE))) {
+      return $this;
+    }
+
+    $query = $this->db->delete('simple_sitemap_entity_overrides')
+      ->condition('type', $variants, 'IN');
+
+    if (NULL !== $entity_type_id) {
+      $query->condition('entity_type', $entity_type_id);
+
+      if (NULL !== $entity_ids) {
+        $query->condition('entity_id', (array) $entity_ids, 'IN');
+      }
+    }
+
+    $query->execute();
+
+    return $this;
+  }
+
+  /**
+   * Checks if an entity bundle (or a non-bundle entity type) is set to be
+   * indexed for the currently set variant.
+   *
+   * @param string $entity_type_id
+   * @param string|null $bundle_name
+   *
+   * @return bool
+   *
+   * @todo multiple variants?
+   */
+  public function bundleIsIndexed($entity_type_id, $bundle_name = NULL) {
+    $settings = $this->getBundleSettings($entity_type_id, $bundle_name);
+    return !empty($settings['index']);
+  }
+
+  /**
+   * Checks if an entity type is enabled in the sitemap settings.
+   *
+   * @param string $entity_type_id
+   *
+   * @return bool
+   */
+  public function entityTypeIsEnabled($entity_type_id) {
+    return in_array($entity_type_id, $this->getSetting('enabled_entity_types', []));
+  }
+
+  /**
+   * Stores a custom path along with its settings to configuration for the
+   * currently set variants.
+   *
+   * @param string $path
+   *
+   * @param array $settings
+   *  Settings that are not provided are supplemented by defaults.
+   *
+   * @return $this
+   *
+   * @todo Validate $settings and throw exceptions
+   */
+  public function addCustomLink($path, $settings = []) {
+    if (empty($variants = $this->getVariants(FALSE))) {
+      return $this;
+    }
+
+    if (!(bool) $this->pathValidator->getUrlIfValidWithoutAccessCheck($path)) {
+      // todo: log error.
+      return $this;
+    }
+    if ($path[0] !== '/') {
+      // todo: log error.
+      return $this;
+    }
+
+    $variant_links = $this->getCustomLinks(NULL, FALSE, TRUE);
+    foreach ($variants as $variant) {
+      $links = [];
+      $link_key = 0;
+      if (isset($variant_links[$variant])) {
+        $links = $variant_links[$variant];
+        $link_key = count($links);
+        foreach ($links as $key => $link) {
+          if ($link['path'] === $path) {
+            $link_key = $key;
+            break;
+          }
+        }
+      }
+
+      $links[$link_key] = ['path' => $path] + $settings;
+      $this->configFactory->getEditable("simple_sitemap.custom_links.$variant")
+        ->set('links', $links)->save();
+    }
+
+    return $this;
+  }
+
+  /**
+   * Gets custom link settings for the currently set variants.
+   *
+   * @param string|null $path
+   *  Limits the result set by an internal path.
+   *
+   * @param bool $supplement_defaults
+   *  Supplements the result set with default custom link settings.
+   *
+   * @param bool $multiple_variants
+   *  If true, returns an array of results keyed by variant name, otherwise it
+   *  returns the result set for the first variant only.
+   *
+   * @return array|mixed|null
+   */
+  public function getCustomLinks($path = NULL, $supplement_defaults = TRUE, $multiple_variants = FALSE) {
+    $all_custom_links = [];
+    foreach ($variants = $this->getVariants(FALSE) as $variant) {
+      $custom_links = $this->configFactory
+        ->get("simple_sitemap.custom_links.$variant")
+        ->get('links');
+
+      $custom_links = !empty($custom_links) ? $custom_links : [];
+
+      if (!empty($custom_links) && $path !== NULL) {
+        foreach ($custom_links as $key => $link) {
+          if ($link['path'] !== $path) {
+            unset($custom_links[$key]);
+          }
+        }
+      }
+
+      if (!empty($custom_links) && $supplement_defaults) {
+        foreach ($custom_links as $i => $link_settings) {
+          self::supplementDefaultSettings('custom', $link_settings);
+          $custom_links[$i] = $link_settings;
+        }
+      }
+
+      $custom_links = $path !== NULL && !empty($custom_links)
+        ? array_values($custom_links)[0]
+        : array_values($custom_links);
+
+
+      if (!empty($custom_links)) {
+        if ($multiple_variants) {
+          $all_custom_links[$variant] = $custom_links;
+        }
+        else {
+          return $custom_links;
+        }
+      }
+    }
+
+    return $all_custom_links;
+  }
+
+  /**
+   * Removes custom links from currently set variants.
+   *
+   * @param array|null $paths
+   *  Limits the removal to certain paths.
+   *
+   * @return $this
+   */
+  public function removeCustomLinks($paths = NULL) {
+    if (empty($variants = $this->getVariants(FALSE))) {
+      return $this;
+    }
+
+    if (NULL === $paths) {
+      foreach ($variants as $variant) {
+        $this->configFactory
+          ->getEditable("simple_sitemap.custom_links.$variant")->delete();
+      }
+    }
+    else {
+      $variant_links = $this->getCustomLinks(NULL, FALSE, TRUE);
+      foreach ($variant_links as $variant => $links) {
+        $custom_links = $links;
+        $save = FALSE;
+        foreach ((array) $paths  as $path) {
+          foreach ($custom_links as $key => $link) {
+            if ($link['path'] === $path) {
+              unset($custom_links[$key]);
+              $save = TRUE;
+              break 2;
+            }
+          }
+        }
+        if ($save) {
+          $this->configFactory->getEditable("simple_sitemap.custom_links.$variant")
+            ->set('links', array_values($custom_links))->save();
+        }
+      }
+    }
+
+    return $this;
+  }
+}

+ 316 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/SimplesitemapManager.php

@@ -0,0 +1,316 @@
+<?php
+
+namespace Drupal\simple_sitemap;
+
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Database\Connection;
+use Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapType\SitemapTypeBase;
+use Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapType\SitemapTypeManager;
+use Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator\SitemapGeneratorBase;
+use Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator\SitemapGeneratorManager;
+use Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator\UrlGeneratorBase;
+use Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator\UrlGeneratorManager;
+
+/**
+ * Class SimplesitemapManager
+ * @package Drupal\simple_sitemap
+ */
+class SimplesitemapManager {
+
+  const DEFAULT_SITEMAP_TYPE = 'default_hreflang';
+  const DEFAULT_SITEMAP_GENERATOR = 'default';
+
+  /**
+   * @var \Drupal\Core\Config\ConfigFactory
+   */
+  protected $configFactory;
+
+  /**
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $db;
+
+  /**
+   * @var \Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapType\SitemapTypeManager
+   */
+  protected $sitemapTypeManager;
+
+  /**
+   * @var \Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator\UrlGeneratorManager
+   */
+  protected $urlGeneratorManager;
+
+  /**
+   * @var \Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator\SitemapGeneratorManager
+   */
+  protected $sitemapGeneratorManager;
+
+  /**
+   * @var \Drupal\simple_sitemap\SimplesitemapSettings
+   */
+  protected $settings;
+
+  /**
+   * @var SitemapTypeBase[] $sitemapTypes
+   */
+  protected $sitemapTypes = [];
+
+  /**
+   * @var UrlGeneratorBase[] $urlGenerators
+   */
+  protected $urlGenerators = [];
+
+  /**
+   * @var SitemapGeneratorBase[] $sitemapGenerators
+   */
+  protected $sitemapGenerators = [];
+
+  /**
+   * SimplesitemapManager constructor.
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   * @param \Drupal\Core\Database\Connection $database
+   * @param \Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapType\SitemapTypeManager $sitemap_type_manager
+   * @param \Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator\UrlGeneratorManager $url_generator_manager
+   * @param \Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator\SitemapGeneratorManager $sitemap_generator_manager
+   * @param \Drupal\simple_sitemap\SimplesitemapSettings $settings
+   */
+  public function __construct(
+    ConfigFactory $config_factory,
+    Connection $database,
+    SitemapTypeManager $sitemap_type_manager,
+    UrlGeneratorManager $url_generator_manager,
+    SitemapGeneratorManager $sitemap_generator_manager,
+    SimplesitemapSettings $settings
+  ) {
+    $this->configFactory = $config_factory;
+    $this->db = $database;
+    $this->sitemapTypeManager = $sitemap_type_manager;
+    $this->urlGeneratorManager = $url_generator_manager;
+    $this->sitemapGeneratorManager = $sitemap_generator_manager;
+    $this->settings = $settings;
+  }
+
+  /**
+   * @param string $sitemap_generator_id
+   * @return \Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapGenerator\SitemapGeneratorBase
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function getSitemapGenerator($sitemap_generator_id) {
+    if (!isset($this->sitemapGenerators[$sitemap_generator_id])) {
+      $this->sitemapGenerators[$sitemap_generator_id]
+        = $this->sitemapGeneratorManager->createInstance($sitemap_generator_id);
+    }
+
+    return $this->sitemapGenerators[$sitemap_generator_id];
+  }
+
+  /**
+   * @param string $url_generator_id
+   * @return \Drupal\simple_sitemap\Plugin\simple_sitemap\UrlGenerator\UrlGeneratorBase
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function getUrlGenerator($url_generator_id) {
+    if (!isset($this->urlGenerators[$url_generator_id])) {
+      $this->urlGenerators[$url_generator_id]
+        = $this->urlGeneratorManager->createInstance($url_generator_id);
+    }
+
+    return $this->urlGenerators[$url_generator_id];
+  }
+
+  /**
+   * @return \Drupal\simple_sitemap\Plugin\simple_sitemap\SitemapType\SitemapTypeBase[]
+   */
+  public function getSitemapTypes() {
+    if (empty($this->sitemapTypes)) {
+      $this->sitemapTypes = $this->sitemapTypeManager->getDefinitions();
+    }
+
+    return $this->sitemapTypes;
+  }
+
+  /**
+   * @param string|null $sitemap_type
+   * @param bool $attach_type_info
+   * @return array
+   */
+  public function getSitemapVariants($sitemap_type = NULL, $attach_type_info = TRUE) {
+    if (NULL === $sitemap_type) {
+      $variants = [];
+      foreach ($this->configFactory->listAll('simple_sitemap.variants.') as $config_name) {
+        $config_name_parts = explode('.', $config_name);
+        $saved_variants = $this->configFactory->get($config_name)->get('variants');
+        $saved_variants = $attach_type_info ? $this->attachSitemapTypeToVariants($saved_variants, $config_name_parts[2]) : $saved_variants;
+        $variants = array_merge($variants, (is_array($saved_variants) ? $saved_variants : []));
+      }
+    }
+    else {
+      $variants = $this->configFactory->get("simple_sitemap.variants.$sitemap_type")->get('variants');
+      $variants = is_array($variants) ? $variants : [];
+      $variants = $attach_type_info ? $this->attachSitemapTypeToVariants($variants, $sitemap_type) : $variants;
+    }
+    array_multisort(array_column($variants, "weight"), SORT_ASC, $variants);
+    return $variants;
+  }
+
+  /**
+   * @param array $variants
+   * @param string $type
+   * @return array
+   */
+  protected function attachSitemapTypeToVariants(array $variants, $type) {
+    return array_map(function($variant) use ($type) { return $variant + ['type' => $type]; }, $variants);
+  }
+
+  /**
+   * @param array $variants
+   * @return array
+   */
+  protected function detachSitemapTypeFromVariants(array $variants) {
+    return array_map(function($variant) { unset($variant['type']); return $variant; }, $variants);
+  }
+
+  /**
+   * @param string $name
+   * @param array $definition
+   * @return $this
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function addSitemapVariant($name, $definition = []) {
+    $all_variants = $this->getSitemapVariants();
+    if (isset($all_variants[$name])) {
+      $old_variant = $all_variants[$name];
+      if (!empty($definition['type']) && $old_variant['type'] !== $definition['type']) {
+        $this->removeSitemapVariants($name);
+        unset($old_variant);
+      }
+      else {
+        unset($old_variant['type']);
+      }
+    }
+
+    if (!isset($old_variant) && empty($definition['label'])) {
+      $definition['label'] = (string) $name;
+    }
+
+    if (!isset($old_variant) && empty($definition['type'])) {
+      $definition['type'] = self::DEFAULT_SITEMAP_TYPE;
+    }
+
+    if (isset($definition['weight'])) {
+      $definition['weight'] = (int) $definition['weight'];
+    }
+    elseif (!isset($old_variant)) {
+      $definition['weight'] = 0;
+    }
+
+    if (isset($old_variant)) {
+      $definition = $definition + $old_variant;
+    }
+
+    $variants = array_merge($this->getSitemapVariants($definition['type'], FALSE), [$name => ['label' => $definition['label'], 'weight' => $definition['weight']]]);
+    $this->configFactory->getEditable('simple_sitemap.variants.' . $definition['type'])
+      ->set('variants', $variants)
+      ->save();
+
+    return $this;
+  }
+
+  /**
+   * @param null|array|string $variant_names
+   *  Limit removal by specific variants.
+   *
+   * @return $this
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function removeSitemap($variant_names = NULL) {
+    if (NULL === $variant_names || !empty((array) $variant_names)) {
+      $saved_variants = $this->getSitemapVariants();
+      $remove_variants = NULL === $variant_names
+        ? $saved_variants
+        : array_intersect_key($saved_variants, array_flip((array) $variant_names));
+
+      if (!empty($remove_variants)) {
+        $type_definitions = $this->getSitemapTypes();
+        foreach ($remove_variants as $variant_name => $variant_definition) {
+          $this->getSitemapGenerator($type_definitions[$variant_definition['type']]['sitemapGenerator'])
+            ->setSitemapVariant($variant_name)
+            ->remove();
+        }
+      }
+    }
+
+    return $this;
+  }
+
+  /**
+   * @param null|array|string $variant_names
+   *  Limit removal by specific variants.
+   *
+   * @return $this
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function removeSitemapVariants($variant_names = NULL) {
+    if (NULL === $variant_names || !empty((array) $variant_names)) {
+
+      // Remove sitemap instances.
+      $this->removeSitemap($variant_names);
+
+      if (NULL === $variant_names) {
+        // Remove all variants and their bundle settings.
+        foreach(['variants', 'bundle_settings', 'custom_links'] as $config_name_part) {
+          foreach ($this->configFactory->listAll("simple_sitemap.$config_name_part.") as $config_name) {
+            $this->configFactory->getEditable($config_name)->delete();
+          }
+        }
+      }
+      else {
+        // Remove bundle settings for specific variants.
+        foreach ((array) $variant_names as $variant_name) {
+          foreach ($this->configFactory->listAll("simple_sitemap.bundle_settings.$variant_name.") as $config_name) {
+            $this->configFactory->getEditable($config_name)->delete();
+          }
+        }
+
+        // Remove custom links for specific variants.
+        foreach ((array) $variant_names as $variant_name) {
+          foreach ($this->configFactory->listAll("simple_sitemap.custom_links.$variant_name") as $config_name) {
+            $this->configFactory->getEditable($config_name)->delete();
+          }
+        }
+
+        // Remove specific variants from configuration.
+        $remove_variants = [];
+        $variants = $this->getSitemapVariants();
+        foreach ((array) $variant_names as $variant_name) {
+          if (isset($variants[$variant_name])) {
+            $remove_variants[$variants[$variant_name]['type']][$variant_name] = $variant_name;
+          }
+        }
+        foreach ($remove_variants as $type => $variants_per_type) {
+          $this->configFactory->getEditable("simple_sitemap.variants.$type")
+            ->set('variants', array_diff_key($this->getSitemapVariants($type, FALSE), $variants_per_type))
+            ->save();
+        }
+      }
+
+      // Remove bundle setting overrides for entities.
+      $query = $this->db->delete('simple_sitemap_entity_overrides');
+      if (NULL !== $variant_names) {
+        $query->condition('type', (array) $variant_names, 'IN');
+      }
+      $query->execute();
+
+
+      // Remove default variant setting.
+      if (NULL === $variant_names
+        || in_array($this->settings->getSetting('default_variant', ''), (array) $variant_names)) {
+        $this->settings->saveSetting('default_variant', '');
+      }
+    }
+
+    return $this;
+  }
+}

+ 68 - 0
sites/all/modules/contrib/admin/simple_sitemap/src/SimplesitemapSettings.php

@@ -0,0 +1,68 @@
+<?php
+
+namespace Drupal\simple_sitemap;
+
+use Drupal\Core\Config\ConfigFactory;
+
+/**
+ * Class SimplesitemapSettings
+ * @package Drupal\simple_sitemap
+ */
+class SimplesitemapSettings {
+
+  /**
+   * @var \Drupal\Core\Config\ConfigFactory
+   */
+  protected $configFactory;
+
+
+  /**
+   * SimplesitemapSettings constructor.
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   */
+  public function __construct(ConfigFactory $config_factory) {
+    $this->configFactory = $config_factory;
+  }
+
+  /**
+   * Returns a specific sitemap setting or a default value if setting does not
+   * exist.
+   *
+   * @param string $name
+   *  Name of the setting, like 'max_links'.
+   *
+   * @param mixed $default
+   *  Value to be returned if the setting does not exist in the configuration.
+   *
+   * @return mixed
+   *  The current setting from configuration or a default value.
+   */
+  public function getSetting($name, $default = FALSE) {
+    $setting = $this->configFactory
+      ->get('simple_sitemap.settings')
+      ->get($name);
+    return NULL !== $setting ? $setting : $default;
+  }
+
+  public function getSettings() {
+    return $this->configFactory
+      ->get('simple_sitemap.settings')
+      ->get();
+  }
+
+  /**
+   * Stores a specific sitemap setting in configuration.
+   *
+   * @param string $name
+   *  Setting name, like 'max_links'.
+   * @param mixed $setting
+   *  The setting to be saved.
+   *
+   * @return $this
+   */
+  public function saveSetting($name, $setting) {
+    $this->configFactory->getEditable('simple_sitemap.settings')
+      ->set($name, $setting)->save();
+    return $this;
+  }
+}

+ 566 - 0
sites/all/modules/contrib/admin/simple_sitemap/tests/src/Functional/SimplesitemapTest.php

@@ -0,0 +1,566 @@
+<?php
+
+namespace Drupal\Tests\simple_sitemap\Functional;
+
+use Drupal\Core\Url;
+
+/**
+ * Tests Simple XML sitemap functional integration.
+ *
+ * @group simple_sitemap
+ */
+class SimplesitemapTest extends SimplesitemapTestBase {
+
+  /**
+   * Verify sitemap.xml has the link to the front page after first generation.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   * @throws \Behat\Mink\Exception\ExpectationException
+   */
+  public function testInitialGeneration() {
+    $this->generator->generateSitemap('backend');
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('urlset');
+    $this->assertSession()->responseContains(
+      Url::fromRoute('<front>')->setAbsolute()->toString()
+    );
+    $this->assertSession()->responseContains('1.0');
+    $this->assertSession()->responseContains('daily');
+  }
+
+  /**
+   * Test custom link.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   * @throws \Behat\Mink\Exception\ExpectationException
+   */
+  public function testAddCustomLink() {
+    $this->generator->addCustomLink(
+      '/node/' . $this->node->id(),
+      ['priority' => 0.2, 'changefreq' => 'monthly']
+    )->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('node/' . $this->node->id());
+    $this->assertSession()->responseContains('0.2');
+    $this->assertSession()->responseContains('monthly');
+
+    $this->drupalLogin($this->privilegedUser);
+
+    $this->drupalGet('admin/config/search/simplesitemap/custom');
+    $this->assertSession()->pageTextContains(
+      '/node/' . $this->node->id() . ' 0.2 monthly'
+    );
+
+    $this->generator->addCustomLink(
+      '/node/' . $this->node->id(),
+      ['changefreq' => 'yearly']
+    )->generateSitemap('backend');
+
+    $this->drupalGet('admin/config/search/simplesitemap/custom');
+    $this->assertSession()->pageTextContains(
+      '/node/' . $this->node->id() . ' yearly'
+    );
+  }
+
+  /**
+   * Test default settings of custom links.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   * @throws \Behat\Mink\Exception\ExpectationException
+   */
+  public function testAddCustomLinkDefaults() {
+    $this->generator->removeCustomLinks()
+      ->addCustomLink('/node/' . $this->node->id())
+      ->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('node/' . $this->node->id());
+    $this->assertSession()->responseContains('0.5');
+    $this->assertSession()->responseNotContains('changefreq');
+  }
+
+  /**
+   * Test removing custom paths from the sitemap settings.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   * @throws \Behat\Mink\Exception\ExpectationException
+   */
+  public function testRemoveCustomLinks() {
+
+    // Test removing one custom path from the sitemap.
+    $this->generator->addCustomLink('/node/' . $this->node->id())
+      ->removeCustomLinks('/node/' . $this->node->id())
+      ->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseNotContains('node/' . $this->node->id());
+
+    //todo Not working
+//    // Test removing all custom paths from the sitemap.
+//    $this->generator->removeCustomLinks()
+//      ->generateSitemap('backend');
+//
+//    $this->drupalGet($this->defaultSitemapUrl);
+//    $this->assertSession()->responseNotContains(
+//      Url::fromRoute('<front>')->setAbsolute()->toString()
+//    );
+  }
+
+  /**
+   * Tests setting bundle settings.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   * @throws \Behat\Mink\Exception\ExpectationException
+   *
+   * @todo Add form tests
+   */
+  public function testSetBundleSettings() {
+    $this->assertFalse($this->generator->bundleIsIndexed('node', 'page'));
+
+    // Index new bundle.
+    $this->generator->removeCustomLinks()
+      ->setBundleSettings('node', 'page', [
+        'index' => TRUE,
+        'priority' => 0.5,
+        'changefreq' => 'hourly',
+      ])
+      ->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('node/' . $this->node->id());
+    $this->assertSession()->responseContains('0.5');
+    $this->assertSession()->responseContains('hourly');
+
+    $this->assertTrue($this->generator->bundleIsIndexed('node', 'page'));
+
+    // Only change bundle priority.
+    $this->generator->setBundleSettings('node', 'page', ['priority' => 0.9])
+      ->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('node/' . $this->node->id());
+    $this->assertSession()->responseNotContains('0.5');
+    $this->assertSession()->responseContains('0.9');
+
+    // Only change bundle changefreq.
+    $this->generator->setBundleSettings(
+      'node',
+      'page',
+      ['changefreq' => 'daily']
+    )->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('node/' . $this->node->id());
+    $this->assertSession()->responseNotContains('hourly');
+    $this->assertSession()->responseContains('daily');
+
+    // Remove changefreq setting.
+    $this->generator->setBundleSettings('node', 'page', ['changefreq' => ''])
+      ->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('node/' . $this->node->id());
+    $this->assertSession()->responseNotContains('changefreq');
+    $this->assertSession()->responseNotContains('daily');
+
+    // Index two bundles.
+    $this->drupalCreateContentType(['type' => 'blog']);
+
+    $node3 = $this->createNode(['title' => 'Node3', 'type' => 'blog']);
+    $this->generator->setBundleSettings('node', 'page', ['index' => TRUE])
+      ->setBundleSettings('node', 'blog', ['index' => TRUE])
+      ->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('node/' . $this->node->id());
+    $this->assertSession()->responseContains('node/' . $node3->id());
+
+    // todo Now working
+//    // Set bundle 'index' setting to false.
+//    $this->generator
+//      ->setBundleSettings('node', 'page', ['index' => FALSE])
+//      ->setBundleSettings('node', 'blog', ['index' => FALSE])
+//      ->generateSitemap('backend');
+//
+//    $this->drupalGet($this->defaultSitemapUrl);
+//
+//    $this->assertSession()->responseNotContains('node/' . $this->node->id());
+//    $this->assertSession()->responseNotContains('node/' . $node3->id());
+  }
+
+  /**
+   * Test default settings of bundles.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   * @throws \Behat\Mink\Exception\ExpectationException
+   */
+  public function testSetBundleSettingsDefaults() {
+    $this->generator->setBundleSettings('node', 'page')
+      ->removeCustomLinks()
+      ->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('node/' . $this->node->id());
+    $this->assertSession()->responseContains('0.5');
+    $this->assertSession()->responseNotContains('changefreq');
+  }
+
+  /**
+   * Test the lastmod parameter in different scenarios.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   * @throws \Behat\Mink\Exception\ExpectationException
+   */
+  public function testLastmod() {
+    // Entity links should have 'lastmod'.
+    $this->generator->setBundleSettings('node', 'page')
+      ->removeCustomLinks()
+      ->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('lastmod');
+
+    // Entity custom links should have 'lastmod'.
+    $this->generator->setBundleSettings('node', 'page', ['index' => FALSE])
+      ->addCustomLink('/node/' . $this->node->id())
+      ->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('lastmod');
+
+    // Non-entity custom links should not have 'lastmod'.
+    $this->generator->removeCustomLinks()
+      ->addCustomLink('/')
+      ->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseNotContains('lastmod');
+  }
+
+  /**
+   * Tests the duplicate setting.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function testRemoveDuplicatesSetting() {
+    $this->generator->setBundleSettings('node', 'page')
+      ->addCustomLink('/node/1')
+      ->saveSetting('remove_duplicates', TRUE)
+      ->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertUniqueTextWorkaround('node/' . $this->node->id());
+
+    $this->generator->saveSetting('remove_duplicates', FALSE)
+      ->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertNoUniqueTextWorkaround('node/' . $this->node->id());
+  }
+
+  /**
+   * Test max links setting and the sitemap index.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   * @throws \Behat\Mink\Exception\ExpectationException
+   */
+  public function testMaxLinksSetting() {
+    $this->generator->setBundleSettings('node', 'page')
+      ->saveSetting('max_links', 1)
+      ->removeCustomLinks()
+      ->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('sitemap.xml?page=1');
+    $this->assertSession()->responseContains('sitemap.xml?page=2');
+
+    $this->drupalGet('sitemap.xml', ['query' => ['page' => 1]]);
+    $this->assertSession()->responseContains('node/' . $this->node->id());
+    $this->assertSession()->responseContains('0.5');
+    $this->assertSession()->responseNotContains('node/' . $this->node2->id());
+
+    $this->drupalGet('sitemap.xml', ['query' => ['page' => 2]]);
+    $this->assertSession()->responseContains('node/' . $this->node2->id());
+    $this->assertSession()->responseContains('0.5');
+    $this->assertSession()->responseNotContains('node/' . $this->node->id());
+  }
+
+  /**
+   * @todo testGenerateDurationSetting
+   */
+
+  /**
+   * Test setting the base URL.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   * @throws \Behat\Mink\Exception\ExpectationException
+   */
+  public function testBaseUrlSetting() {
+    $this->generator->setBundleSettings('node', 'page')
+      ->saveSetting('base_url', 'http://base_url_test')
+      ->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('http://base_url_test');
+
+    // Set base URL in the sitemap index.
+    $this->generator->saveSetting('max_links', 1)
+      ->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('http://base_url_test/sitemap.xml?page=1');
+  }
+
+  /**
+   * Test overriding of bundle settings for a single entity.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   * @throws \Behat\Mink\Exception\ExpectationException
+   *
+   * @todo: Use form testing instead of responseContains().
+   */
+  public function testSetEntityInstanceSettings() {
+    $this->generator->setBundleSettings('node', 'page')
+      ->removeCustomLinks()
+      ->setEntityInstanceSettings('node', $this->node->id(), ['priority' => 0.1, 'changefreq' => 'never'])
+      ->setEntityInstanceSettings('node', $this->node2->id(), ['index' => FALSE])
+      ->generateSitemap('backend');
+
+    // Test sitemap result.
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('node/' . $this->node->id());
+    $this->assertSession()->responseContains('0.1');
+    $this->assertSession()->responseContains('never');
+    $this->assertSession()->responseNotContains('node/' . $this->node2->id());
+    $this->assertSession()->responseNotContains('0.5');
+
+    $this->drupalLogin($this->privilegedUser);
+
+    // Test UI changes.
+    $this->drupalGet('node/' . $this->node->id() . '/edit');
+    $this->assertSession()->responseContains('<option value="0.1" selected="selected">0.1</option>');
+    $this->assertSession()->responseContains('<option value="never" selected="selected">never</option>');
+
+    // Test database changes.
+    $result = $this->database->select('simple_sitemap_entity_overrides', 'o')
+      ->fields('o', ['inclusion_settings'])
+      ->condition('o.entity_type', 'node')
+      ->condition('o.entity_id', $this->node->id())
+      ->execute()
+      ->fetchField();
+    $this->assertFalse(empty($result));
+
+    $this->generator->setBundleSettings('node', 'page', ['priority' => 0.1, 'changefreq' => 'never'])
+      ->generateSitemap('backend');
+
+    // Test sitemap result.
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('node/' . $this->node->id());
+    $this->assertSession()->responseContains('0.1');
+    $this->assertSession()->responseContains('never');
+    $this->assertSession()->responseNotContains('node/' . $this->node2->id());
+    $this->assertSession()->responseNotContains('0.5');
+
+    // Test UI changes.
+    $this->drupalGet('node/' . $this->node->id() . '/edit');
+    $this->assertSession()->responseContains('<option value="0.1" selected="selected">0.1 (default)</option>');
+    $this->assertSession()->responseContains('<option value="never" selected="selected">never (default)</option>');
+
+    // Test if entity override has been removed from database after its equal to
+    // its bundle settings.
+    $result = $this->database->select('simple_sitemap_entity_overrides', 'o')
+      ->fields('o', ['inclusion_settings'])
+      ->condition('o.entity_type', 'node')
+      ->condition('o.entity_id', $this->node->id())
+      ->execute()
+      ->fetchField();
+    $this->assertTrue(empty($result));
+  }
+
+  /**
+   * Test indexing an atomic entity (here: a user)
+   */
+  public function testAtomicEntityIndexation() {
+    $user_id = $this->privilegedUser->id();
+    $this->generator->setBundleSettings('user')
+      ->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseNotContains('user/' . $user_id);
+
+    user_role_grant_permissions('anonymous', ['access user profiles']);
+    drupal_flush_all_caches(); //todo Not pretty.
+
+    $this->generator->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('user/' . $user_id);
+  }
+
+  /**
+   * @todo Test indexing menu.
+   */
+
+  /**
+   * @todo Test deleting a bundle.
+   */
+
+  /**
+   * Test disabling sitemap support for an entity type.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   * @throws \Behat\Mink\Exception\ExpectationException
+   */
+  public function testDisableEntityType() {
+    $this->generator->setBundleSettings('node', 'page')
+      ->disableEntityType('node');
+
+    $this->drupalLogin($this->privilegedUser);
+    $this->drupalGet('admin/structure/types/manage/page');
+    $this->assertSession()->pageTextNotContains('Simple XML sitemap');
+
+    $this->generator->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseNotContains('node/' . $this->node->id());
+
+    $this->assertFalse($this->generator->entityTypeIsEnabled('node'));
+  }
+
+  /**
+   * Test enabling sitemap support for an entity type.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   * @throws \Behat\Mink\Exception\ExpectationException
+   *
+   * @todo Test admin/config/search/simplesitemap/entities form.
+   */
+  public function testEnableEntityType() {
+    $this->generator->disableEntityType('node')
+      ->enableEntityType('node')
+      ->setBundleSettings('node', 'page');
+
+    $this->drupalLogin($this->privilegedUser);
+    $this->drupalGet('admin/structure/types/manage/page');
+    $this->assertSession()->pageTextContains('Simple XML sitemap');
+
+    $this->generator->generateSitemap('backend');
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('node/' . $this->node->id());
+
+    $this->assertTrue($this->generator->entityTypeIsEnabled('node'));
+  }
+
+  /**
+   * @todo testSitemapLanguages
+   */
+
+  /**
+   * Test adding and removing sitemap variants.
+   *
+   * @throws \Behat\Mink\Exception\ExpectationException
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function testSitemapVariants() {
+
+    // Test adding a variant.
+    $this->generator->getSitemapManager()->addSitemapVariant('test');
+
+    $this->generator
+      ->setBundleSettings('node', 'page')
+      ->generateSitemap('backend');
+
+    $variants = $this->generator->getSitemapManager()->getSitemapVariants();
+    $this->assertTrue(isset($variants['test']));
+
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('node/' . $this->node->id());
+
+    // Test if generation affected the default variant only.
+    $this->drupalGet('test/sitemap.xml');
+    $this->assertSession()->responseNotContains('node/' . $this->node->id());
+
+    $this->generator
+      ->setVariants('test')
+      ->setBundleSettings('node', 'page')
+      ->generateSitemap('backend');
+
+    // Test if bundle settings have been set for correct variant.
+    $this->drupalGet($this->defaultSitemapUrl);
+    $this->assertSession()->responseContains('node/' . $this->node->id());
+
+    $this->generator->getSitemapManager()->removeSitemapVariants('test');
+
+    $variants = $this->generator->getSitemapManager()->getSitemapVariants();
+    $this->assertFalse(isset($variants['test']));
+
+    // Test if sitemap has been removed along with the variant.
+    $this->drupalGet('test/sitemap.xml');
+    $this->assertSession()->statusCodeEquals(404);
+  }
+
+  /**
+   * @todo Test removeSitemap().
+   */
+
+  /**
+   * Test cases for ::testGenerationResume.
+   */
+  public function generationResumeProvider() {
+    return [
+      [1000, 500, 1],
+      [1000, 500, 3, ['de']],
+      [1000, 500, 5, ['de', 'es']],
+      [10, 10000, 10],
+    ];
+  }
+
+  /**
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   *
+   * @dataProvider generationResumeProvider
+   */
+  public function testGenerationResume($element_count, $generate_duration, $max_links, $langcodes = []) {
+
+    $this->addLanguages($langcodes);
+
+    $expected_sitemap_count = (int) ceil(($element_count * (count($langcodes) + 1)) / $max_links);
+
+    $this->drupalCreateContentType(['type' => 'blog']);
+    for ($i = 1; $i <= $element_count; $i++) {
+      $this->createNode(['title' => 'node-' . $i, 'type' => 'blog']);
+    }
+
+    $this->generator
+      ->removeCustomLinks()
+      ->saveSetting('generate_duration', $generate_duration)
+      ->saveSetting('max_links', $max_links)
+      ->saveSetting('skip_untranslated', FALSE)
+      ->setBundleSettings('node', 'blog');
+
+    $queue = $this->generator->getQueueWorker()->rebuildQueue();
+    $generate_count = 0;
+    while ($queue->generationInProgress()) {
+      $generate_count++;
+      $this->generator->generateSitemap('backend');
+    }
+
+    // Test if sitemap generation has been resumed when time limit is very low.
+    $this->assertTrue($generate_duration > $element_count || $generate_count > 1, 'This assertion tests if the sitemap generation is split up into batches due to a low generation time limit setting. The failing of this assertion can mean that the sitemap was wrongfully generated in one go, but it can also mean that the assumed low time setting is still high enough for a one pass generation.');
+
+    // Test if correct number of sitemaps have been created.
+    $chunks = $this->database->query('SELECT id FROM {simple_sitemap} WHERE delta != 0 AND status = 1');
+    $chunks->allowRowCount = TRUE;
+    $chunk_count = $chunks->rowCount();
+    $this->assertTrue($chunk_count === $expected_sitemap_count);
+
+    // Test if index has been created when necessary.
+    $index = $this->database->query('SELECT id FROM {simple_sitemap} WHERE delta = 0 AND status = 1')
+      ->fetchField();
+    $this->assertTrue($chunk_count > 1 ? (FALSE !== $index) : !$index);
+  }
+
+}
+

+ 123 - 0
sites/all/modules/contrib/admin/simple_sitemap/tests/src/Functional/SimplesitemapTestBase.php

@@ -0,0 +1,123 @@
+<?php
+
+namespace Drupal\Tests\simple_sitemap\Functional;
+
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Tests\BrowserTestBase;
+use Drupal\language\Entity\ConfigurableLanguage;
+
+/**
+ * Provides the base class for web tests for Simple sitemap.
+ */
+abstract class SimplesitemapTestBase extends BrowserTestBase {
+
+  use StringTranslationTrait;
+
+  /**
+   * Modules to enable for this test.
+   *
+   * @var string[]
+   */
+  public static $modules = [
+    'simple_sitemap',
+    'node',
+    'content_translation',
+  ];
+
+  /**
+   * Simple sitemap generator.
+   *
+   * @var \Drupal\simple_sitemap\Simplesitemap
+   */
+  protected $generator;
+
+  /**
+   * Database service.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $database;
+
+  /**
+   * A user with all the permissions.
+   *
+   * @var \Drupal\user\Entity\User
+   */
+  protected $privilegedUser;
+
+  /**
+   * A node.
+   *
+   * @var \Drupal\node\Entity\Node
+   */
+  protected $node;
+
+  /**
+   * A node.
+   *
+   * @var \Drupal\node\Entity\Node
+   */
+  protected $node2;
+
+  protected $defaultSitemapUrl = 'sitemap.xml';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->generator = $this->container->get('simple_sitemap.generator');
+    $this->database = $this->container->get('database');
+
+    $this->drupalCreateContentType(['type' => 'page']);
+    $this->node = $this->createNode(['title' => 'Node', 'type' => 'page']);
+    $this->node2 = $this->createNode(['title' => 'Node2', 'type' => 'page']);
+
+    // Create a user with all the permissions.
+    $permissions = array_keys($this->container->get('user.permissions')->getPermissions());
+    $this->privilegedUser = $this->drupalCreateUser($permissions);
+  }
+
+  /**
+   * Helper function to replace assertUniqueText.
+   *
+   * Also adapt the legacy trait method because it can't be applied to Non-HTML
+   * pages.
+   *
+   * @param string $text
+   *   The text to look for.
+   *
+   * @See \Drupal\FunctionalTests\AssertLegacyTrait::assertUniqueText().
+   */
+  protected function assertUniqueTextWorkaround($text) {
+    $page_content = $this->getSession()->getPage()->getContent();
+    $nr_found = substr_count($page_content, $text);
+    $this->assertSame(1, $nr_found);
+  }
+
+  /**
+   * Helper function to replace assertNoUniqueText.
+   *
+   * Also adapt the legacy trait method because it can't be applied to Non-HTML
+   * pages.
+   *
+   * @param string $text
+   *   The text to look for.
+   *
+   * @See \Drupal\FunctionalTests\AssertLegacyTrait::assertNoUniqueText().
+   */
+  protected function assertNoUniqueTextWorkaround($text) {
+    $page_text = $this->getSession()->getPage()->getContent();
+    $nr_found = substr_count($page_text, $text);
+    $this->assertGreaterThan(1, $nr_found);
+  }
+
+  protected function addLanguages($langcodes = 'de') {
+    foreach ((array) $langcodes as $langcode) {
+      ConfigurableLanguage::createFromLangcode($langcode)
+        ->save();
+    }
+  }
+
+}

+ 1 - 0
sites/default/config/sync/core.extension.yml

@@ -88,6 +88,7 @@ module:
   responsive_image: 0
   search_api: 0
   search_api_db: 0
+  simple_sitemap: 0
   synonyms: 0
   system: 0
   taxonomy: 0

+ 1 - 1
sites/default/config/sync/language/en/views.view.redirect.yml

@@ -6,7 +6,7 @@ display:
     display_options:
       exposed_form:
         options:
-          submit_button: Apply
+          submit_button: Filter
           reset_button_label: Reset
           exposed_sorts_label: 'Sort by'
           sort_asc_label: Asc

+ 4 - 0
sites/default/config/sync/simple_sitemap.bundle_settings.default.menu_link_content.footer.yml

@@ -0,0 +1,4 @@
+index: true
+priority: '0.8'
+changefreq: ''
+include_images: false

+ 4 - 0
sites/default/config/sync/simple_sitemap.bundle_settings.default.menu_link_content.productions.yml

@@ -0,0 +1,4 @@
+index: true
+priority: '0.8'
+changefreq: ''
+include_images: false

+ 4 - 0
sites/default/config/sync/simple_sitemap.bundle_settings.default.node.enregistrement.yml

@@ -0,0 +1,4 @@
+index: true
+priority: '0.5'
+changefreq: ''
+include_images: false

+ 4 - 0
sites/default/config/sync/simple_sitemap.bundle_settings.default.node.evenement.yml

@@ -0,0 +1,4 @@
+index: true
+priority: '0.5'
+changefreq: ''
+include_images: false

+ 4 - 0
sites/default/config/sync/simple_sitemap.bundle_settings.default.node.page.yml

@@ -0,0 +1,4 @@
+index: true
+priority: '0.6'
+changefreq: ''
+include_images: true

+ 4 - 0
sites/default/config/sync/simple_sitemap.bundle_settings.default.node.static.yml

@@ -0,0 +1,4 @@
+index: true
+priority: '0.5'
+changefreq: ''
+include_images: true

+ 4 - 0
sites/default/config/sync/simple_sitemap.bundle_settings.default.taxonomy_term.entrees.yml

@@ -0,0 +1,4 @@
+index: true
+priority: '0.5'
+changefreq: ''
+include_images: false

+ 8 - 0
sites/default/config/sync/simple_sitemap.custom_links.default.yml

@@ -0,0 +1,8 @@
+links:
+  -
+    path: /
+    priority: '1.0'
+    changefreq: daily
+_core:
+  default_config_hash: 25hWeYa4sasuJtHqKKcEN_nYiuEC1lMPYHsn5dawJEw
+langcode: fr

+ 17 - 0
sites/default/config/sync/simple_sitemap.settings.yml

@@ -0,0 +1,17 @@
+max_links: 2000
+cron_generate: true
+cron_generate_interval: 0
+generate_duration: 10000
+remove_duplicates: true
+skip_untranslated: true
+base_url: ''
+default_variant: default
+custom_links_include_images: false
+excluded_languages: {  }
+enabled_entity_types:
+  - node
+  - taxonomy_term
+  - menu_link_content
+_core:
+  default_config_hash: FoTCiVMOlTpj9EU5bUJB7lS8JK2gnnvqh-AdIC0ktBQ
+langcode: fr

+ 7 - 0
sites/default/config/sync/simple_sitemap.variants.default_hreflang.yml

@@ -0,0 +1,7 @@
+variants:
+  default:
+    label: Default
+    weight: 0
+_core:
+  default_config_hash: nQAXscP-SDpsmqHlQ6u0iBvcuJPrAikb09c3cP2_n0k
+langcode: fr

+ 3 - 3
sites/default/config/sync/views.view.content_translations.yml

@@ -682,7 +682,7 @@ display:
           group_type: group
           admin_label: ''
           operator: '='
-          value: 2
+          value: '2'
           group: 1
           exposed: true
           expose:
@@ -758,7 +758,7 @@ display:
           group_type: group
           admin_label: ''
           operator: '='
-          value: 0
+          value: '0'
           group: 2
           exposed: false
           expose:
@@ -831,7 +831,7 @@ display:
           group_type: group
           admin_label: ''
           operator: '='
-          value: 0
+          value: '0'
           group: 3
           exposed: false
           expose:

+ 1 - 1
sites/default/config/sync/views.view.redirect.yml

@@ -41,7 +41,7 @@ display:
       exposed_form:
         type: basic
         options:
-          submit_button: Apply
+          submit_button: Filter
           reset_button: false
           reset_button_label: Reset
           exposed_sorts_label: 'Sort by'

Деякі файли не було показано, через те що забагато файлів було змінено