[
  {
    "path": ".gitattributes",
    "content": "/grumphp.yml export-ignore\n/hooks export-ignore\n/tests/fixtures export-ignore\n/.travis.yml export-ignore\n/acquia-pipelines.yml export-ignore\n/phpunit.xml export-ignore\n/settings.local.php export-ignore\n/*.sh export-ignore\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nbin/\ndocroot/\nvendor/\nnode_modules/\nbower_components/\n.sass-cache/\nconsole/cache/\n/.behat.yml\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "The information in this file has been moved to https://github.com/acquia/lightning/wiki/Changelog.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "## Contributing to Lightning\n\n### Local development\n**Note that these instructions won't work on old, unsupported branches of Lightning.** At the time of this writing, that includes the `8.x-1.x` and `8.x-2.x` branches, which have long since reached the end of their lives.\n\nThis documentation describes how to set up Lightning (or any of its components) for development on a machine running a Unix-like operating system (e.g., Linux or macOS). We assume that:\n\n* You have Git installed in your PATH. You can confirm this by running `git --version`.\n* You have PHP 7.1 or later installed in your PATH. You can confirm this by running `php --version`.\n* You have Composer installed in your PATH. You can confirm this by running `composer --version`. You should also have Composer's global binary directory (usually `$HOME/.composer/vendor/bin`) in your PATH.\n* You will need `drush/drush-launcher` globally installed. To confirm this, run `drush --version`. If the command is not found, run `composer global require drush/drush-launcher`.\n* You will also need a database server installed. Lightning uses SQLite by default for development, since it is the most lightweight option supported by Drupal core.\n\nNow, get your Lightning code base set up:\n\n1. Clone the git repository, e.g. `git clone git@github.com:acquia/lightning.git`\n2. Enter the repository and run `composer install` to install all dependencies.\n3. Install Lightning and all necessary components by running `./install-drupal.sh`. By default, this will try to install a SQLite database file called `db.sqlite` in the `docroot` directory. You can override this by passing a `DB_URL` environment variable to `install-drupal.sh`, containing the Drush-compatible URL of the database you want to use. For example:\n\n```\nDB_URL=mysql://user:password@server/drupal ./install-drupal.sh\n```\n4. Run the web server. The quickest option is to use PHP's built-in server: `drush runserver 8080`\n5. You should now be able to access your Lightning site at `http://localhost:8080`.\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "README.md",
    "content": "# Drupal Lightning\n![Lightning logo of a bolt of lightning](https://raw.githubusercontent.com/acquia/lightning/5.0.x/lightning-logo.png)\n\n## November 2, 2021: So long and thanks for all the fish!\nAcquia **ended support for the Lightning distribution on November 2, 2021**, simultaneously with Drupal 8. Lightning 3, 4, and 5 no longer receive any security updates or bug fixes. It is possible to safely uninstall Lightning from your site; please see [the official announcement](https://www.acquia.com/blog/acquia-lightning-eol-2021-acquia-cms-future), [FAQ for site owners](https://support.acquia.com/hc/en-us/articles/1500006393601-Frequently-Asked-Questions-FAQ-regarding-End-of-Support-for-Acquia-Lightning), and [developer instructions](https://github.com/acquia/lightning/wiki/Uninstalling-Lightning) for more information.\n"
  },
  {
    "path": "UPDATE.md",
    "content": "The information in this file has moved to https://github.com/acquia/lightning/wiki/Update-Instructions.\n"
  },
  {
    "path": "acquia-pipelines.yml",
    "content": "# This file is used by Acquia Pipelines continuous integration. It builds\n# Lightning and runs its functional tests. Upon success, an artifact is deployed\n# to the lightningnightly (92170259-5e36-4052-a2ad-0dd5ee5b3ec2) sub on Acquia\n# cloud.\nversion: 1.1.0\nservices:\n  - mysql\n  - php:\n      version: 7.2\n\nevents:\n  build:\n    steps:\n      - setup:\n          type: script\n          script:\n            # Add Composer's local bin directory to the PATH so that we will be\n            # running our installed versions of Drush, PHPCS, Behat, etc.\n            - export PATH=\"$HOME/.composer/vendor/bin:$SOURCE_DIR/vendor/bin:$PATH\"\n            - composer validate --no-check-all --ansi --no-interaction\n            - composer update\n      - cleanup:\n          type: script\n          script:\n            # Setup settings file and codebase with minimum required for cloud.\n            - chmod -R +w docroot/sites/default\n            - cp docroot/sites/default/default.settings.php docroot/sites/default/settings.php\n            - echo \"if (file_exists('/var/www/site-php')) { require '/var/www/site-php/lightningnightly/lightningnightly-settings.inc';}\" >> docroot/sites/default/settings.php\n            # Clear any config directories that Cloud tries to set in th include\n            # file.\n            - echo \"\\$config_directories = [];\" >> docroot/sites/default/settings.php\n            # Use the existing `config` directory we already have. Cloud get's\n            # confused because we have a config directory above docroot already\n            # and manipulating it in Pipelines doesn't seem to work. This\n            # deployment is ephemeral, so using the existing config directory\n            # isn't a problem.\n            - echo \"\\$config_directories['sync'] = '../config';\" >> docroot/sites/default/settings.php\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"acquia/lightning\",\n    \"type\": \"drupal-profile\",\n    \"description\": \"The best of Drupal, curated by Acquia\",\n    \"license\": \"GPL-2.0-or-later\",\n    \"require\": {\n        \"ext-dom\": \"*\",\n        \"composer/composer\": \"^1.10.22 || ^2.0.13\",\n        \"cweagans/composer-patches\": \"^1.7\",\n        \"drupal/acquia_connector\": \"^1.24-rc3 || ^2.0-rc1 || ^3\",\n        \"drupal/core\": \"~9.1.13\",\n        \"drupal/inline_entity_form\": \"^1.0-rc7\",\n        \"drupal/lightning_api\": \"^4.6\",\n        \"drupal/lightning_core\": \"^5\",\n        \"drupal/lightning_layout\": \"^2.10\",\n        \"drupal/lightning_media\": \"^4.6\",\n        \"drupal/lightning_workflow\": \"^3.16\",\n        \"drupal/page_manager\": \"^4.0-beta6\",\n        \"drupal/panelizer\": \"^5.0-beta3\",\n        \"drupal/pendo\": \"^1@alpha\",\n        \"drupal/profile_switcher\": \"^1.0-alpha5\",\n        \"drupal/search_api\": \"^1.17\"\n    },\n    \"require-dev\": {\n        \"composer/installers\": \"^1.9\",\n        \"drupal/core-composer-scaffold\": \"^9\",\n        \"drupal/core-dev\": \"^9\",\n        \"drupal/devel\": \"^4.1\",\n        \"drush/drush\": \">=9.7\",\n        \"phpspec/prophecy-phpunit\": \"^2\",\n        \"weitzman/drupal-test-traits\": \"dev-master\"\n    },\n    \"config\": {\n        \"preferred-install\": {\n            \"drupal/core\": \"dist\",\n            \"drupal/lightning_*\": \"source\"\n        }\n    },\n    \"extra\": {\n        \"branch-alias\": {\n            \"dev-8.x-4.x\": \"4.x-dev\"\n        },\n        \"composer-exit-on-patch-failure\": \"true\",\n        \"drupal-scaffold\": {\n            \"file-mapping\": {\n                \"[project-root]/.editorconfig\": false,\n                \"[project-root]/.gitattributes\": false,\n                \"[web-root]/.csslintrc\": false,\n                \"[web-root]/INSTALL.txt\": false,\n                \"[web-root]/example.gitignore\": false,\n                \"[web-root]/modules/README.txt\": false,\n                \"[web-root]/profiles/README.txt\": false,\n                \"[web-root]/robots.txt\": false,\n                \"[web-root]/sites/README.txt\": false,\n                \"[web-root]/themes/README.txt\": false,\n                \"[web-root]/web.config\": false\n            },\n            \"locations\": {\n                \"web-root\": \"docroot/\"\n            }\n        },\n        \"drush\": {\n            \"services\": {\n                \"drush.services.yml\": \"^9 || ^10\"\n            }\n        },\n        \"enable-patching\": true,\n        \"installer-paths\": {\n            \"docroot/core\": [\n                \"type:drupal-core\"\n            ],\n            \"docroot/libraries/{$name}\": [\n                \"type:drupal-library\",\n                \"type:bower-asset\",\n                \"type:npm-asset\"\n            ],\n            \"docroot/modules/contrib/{$name}\": [\n                \"type:drupal-module\"\n            ],\n            \"docroot/profiles/contrib/{$name}\": [\n                \"type:drupal-profile\"\n            ],\n            \"docroot/themes/contrib/{$name}\": [\n                \"type:drupal-theme\"\n            ]\n        },\n        \"installer-types\": [\n            \"bower-asset\",\n            \"npm-asset\"\n        ],\n        \"patchLevel\": {\n            \"drupal/core\": \"-p2\"\n        },\n        \"patches\": {\n            \"drupal/core\": {\n                \"2869592 - Disabled update module shouldn't produce a status report warning\": \"https://www.drupal.org/files/issues/2020-02-07/2869592-remove-update-warning-34.patch\",\n                \"[subprofile support] 1356276 - Allow profiles to define a base/parent profile and load them in the correct order / 2914389 - Allow profiles to exclude dependencies of their parent\": \"https://www.drupal.org/files/issues/2021-05-25/3143958-15-subprofile-support-9.1.x.patch\",\n                \"REMOVE: 2031261 - Fix SQLite variable limit\": \"https://www.drupal.org/files/issues/2020-04-01/2031261-118.patch\",\n                \"REMOVE: Allow installation profile to be changed from Lightning during config sync\": \"https://www.drupal.org/files/issues/2021-01-21/3143958-12-change-profile-config-sync-9.1.x.patch\"\n            }\n        },\n        \"patches-ignore\": {\n            \"drupal/lightning_core\": {\n                \"drupal/core\": {\n                    \"2869592 - Disabled update module shouldn't produce a status report warning\": \"https://www.drupal.org/files/issues/2869592-remove-update-warning-7.patch\"\n                }\n            }\n        }\n    },\n    \"autoload\": {\n        \"psr-4\": {\n            \"Acquia\\\\Lightning\\\\\": \"src\"\n        },\n        \"classmap\": [\n            \"src/Composer/ConfigureLegacyProject.php\"\n        ]\n    },\n    \"repositories\": {\n        \"drupal\": {\n            \"type\": \"composer\",\n            \"url\": \"https://packages.drupal.org/8\"\n        },\n        \"assets\": {\n            \"type\": \"composer\",\n            \"url\": \"https://asset-packagist.org\"\n        }\n    },\n    \"minimum-stability\": \"dev\",\n    \"prefer-stable\": true,\n    \"scripts\": {\n        \"post-install-cmd\": [\n            \"@putenv DIR=./docroot/profiles/lightning\",\n            \"mkdir -p $DIR\",\n            \"ln -s -f $PWD/config $DIR\",\n            \"ln -s -f $PWD/drush.services.yml $DIR\",\n            \"ln -s -f $PWD/favicon.ico $DIR\",\n            \"ln -s -f $PWD/help $DIR\",\n            \"ln -s -f $PWD/lightning-logo.png $DIR\",\n            \"ln -s -f $PWD/modules $DIR\",\n            \"ln -s -f $PWD/src $DIR\",\n            \"ln -s -f $PWD/tests $DIR\",\n            \"find $PWD -name 'lightning.*' -depth 1 -exec ln -s -f {} $DIR ';'\",\n            \"cp -f phpunit.xml ./docroot/core\"\n        ],\n        \"configure-tarball\": \"Acquia\\\\Lightning\\\\Composer\\\\ConfigureLegacyProject::execute\",\n        \"enable-asset-packagist\": \"Acquia\\\\Lightning\\\\Composer\\\\AssetPackagist::execute\",\n        \"nuke\": \"rm -r -f docroot vendor\",\n        \"verify-patched-constraints\": \"Acquia\\\\Lightning\\\\Composer\\\\PatchedConstraint::execute\"\n    }\n}\n"
  },
  {
    "path": "config/install/user.role.administrator.yml",
    "content": "id: administrator\nlabel: Administrator\nweight: 2\nlangcode: en\nis_admin: true\n"
  },
  {
    "path": "config/install/user.settings.yml",
    "content": "anonymous: Anonymous\nverify_mail: true\nnotify:\n  cancel_confirm: true\n  password_reset: true\n  status_activated: true\n  status_blocked: false\n  status_canceled: false\n  register_admin_created: true\n  register_no_approval_required: true\n  register_pending_approval: true\nregister: admin_only\ncancel_method: user_cancel_block\npassword_reset_timeout: 86400\npassword_strength: true\nlangcode: en\n"
  },
  {
    "path": "config/optional/block.block.bartik_account_menu.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  config:\n    - system.menu.account\n  module:\n    - system\n  theme:\n    - bartik\nid: bartik_account_menu\ntheme: bartik\nregion: secondary_menu\nweight: 0\nprovider: null\nplugin: 'system_menu_block:account'\nsettings:\n  id: 'system_menu_block:account'\n  label: 'User account menu'\n  provider: system\n  label_display: '0'\n  level: 1\n  depth: 1\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.bartik_branding.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  module:\n    - system\n  theme:\n    - bartik\nid: bartik_branding\ntheme: bartik\nregion: header\nweight: 0\nprovider: null\nplugin: system_branding_block\nsettings:\n  id: system_branding_block\n  label: 'Site branding'\n  provider: system\n  label_display: '0'\n  use_site_logo: true\n  use_site_name: true\n  use_site_slogan: true\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.bartik_breadcrumbs.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  module:\n    - system\n  theme:\n    - bartik\nid: bartik_breadcrumbs\ntheme: bartik\nregion: breadcrumb\nweight: 0\nprovider: null\nplugin: system_breadcrumb_block\nsettings:\n  id: system_breadcrumb_block\n  label: Breadcrumbs\n  provider: system\n  label_display: '0'\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.bartik_content.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  module:\n    - system\n  theme:\n    - bartik\nid: bartik_content\ntheme: bartik\nregion: content\nweight: 0\nprovider: null\nplugin: system_main_block\nsettings:\n  id: system_main_block\n  label: 'Main page content'\n  provider: system\n  label_display: '0'\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.bartik_footer.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  config:\n    - system.menu.footer\n  module:\n    - system\n  theme:\n    - bartik\nid: bartik_footer\ntheme: bartik\nregion: footer_fifth\nweight: 0\nprovider: null\nplugin: 'system_menu_block:footer'\nsettings:\n  id: 'system_menu_block:footer'\n  label: 'Footer menu'\n  provider: system\n  label_display: '0'\n  level: 1\n  depth: 0\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.bartik_help.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  module:\n    - help\n  theme:\n    - bartik\nid: bartik_help\ntheme: bartik\nregion: content\nweight: -30\nprovider: null\nplugin: help_block\nsettings:\n  id: help_block\n  label: Help\n  provider: help\n  label_display: '0'\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.bartik_local_actions.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  theme:\n    - bartik\nid: bartik_local_actions\ntheme: bartik\nregion: content\nweight: -20\nprovider: null\nplugin: local_actions_block\nsettings:\n  id: local_actions_block\n  label: 'Primary admin actions'\n  provider: core\n  label_display: '0'\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.bartik_local_tasks.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  theme:\n    - bartik\nid: bartik_local_tasks\ntheme: bartik\nregion: content\nweight: -40\nprovider: null\nplugin: local_tasks_block\nsettings:\n  id: local_tasks_block\n  label: Tabs\n  provider: core\n  label_display: '0'\n  primary: true\n  secondary: true\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.bartik_main_menu.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  config:\n    - system.menu.main\n  module:\n    - system\n  theme:\n    - bartik\nid: bartik_main_menu\ntheme: bartik\nregion: primary_menu\nweight: 0\nprovider: null\nplugin: 'system_menu_block:main'\nsettings:\n  id: 'system_menu_block:main'\n  label: 'Main navigation'\n  provider: system\n  label_display: '0'\n  level: 1\n  depth: 1\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.bartik_messages.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  module:\n    - system\n  theme:\n    - bartik\nid: bartik_messages\ntheme: bartik\nregion: highlighted\nweight: 0\nprovider: null\nplugin: system_messages_block\nsettings:\n  id: system_messages_block\n  label: 'Status messages'\n  provider: system\n  label_display: '0'\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.bartik_page_title.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  theme:\n    - bartik\nid: bartik_page_title\ntheme: bartik\nregion: content\nweight: -50\nprovider: null\nplugin: page_title_block\nsettings:\n  id: page_title_block\n  label: 'Page title'\n  provider: core\n  label_display: '0'\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.bartik_powered.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  module:\n    - system\n  theme:\n    - bartik\nid: bartik_powered\ntheme: bartik\nregion: footer_fifth\nweight: 10\nprovider: null\nplugin: system_powered_by_block\nsettings:\n  id: system_powered_by_block\n  label: 'Powered by Drupal'\n  provider: system\n  label_display: '0'\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.seven_breadcrumbs.yml",
    "content": "id: seven_breadcrumbs\ntheme: seven\nweight: 0\nstatus: true\nlangcode: en\nregion: breadcrumb\nplugin: system_breadcrumb_block\nsettings:\n  id: system_breadcrumb_block\n  label: Breadcrumbs\n  provider: system\n  label_display: '0'\ndependencies:\n  module:\n    - system\n  theme:\n    - seven\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.seven_content.yml",
    "content": "id: seven_content\ntheme: seven\nweight: 0\nstatus: true\nlangcode: en\nregion: content\nplugin: system_main_block\nsettings:\n  id: system_main_block\n  label: 'Main page content'\n  provider: system\n  label_display: '0'\ndependencies:\n  module:\n    - system\n  theme:\n    - seven\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.seven_help.yml",
    "content": "id: seven_help\ntheme: seven\nweight: 0\nstatus: true\nlangcode: en\nregion: help\nplugin: help_block\nsettings:\n  id: help_block\n  label: 'Help'\n  provider: help\n  label_display: '0'\ndependencies:\n  module:\n    - help\n  theme:\n    - seven\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.seven_local_actions.yml",
    "content": "id: seven_local_actions\ntheme: seven\nweight: -10\nstatus: true\nlangcode: en\nregion: content\nplugin: local_actions_block\nsettings:\n  id: local_actions_block\n  label: Primary admin actions\n  label_display: '0'\ndependencies:\n  theme:\n    - seven\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.seven_login.yml",
    "content": "id: seven_login\ntheme: seven\nweight: 10\nstatus: true\nlangcode: en\nregion: content\nplugin: user_login_block\nsettings:\n  id: user_login_block\n  label: 'User login'\n  provider: user\n  label_display: visible\ndependencies:\n  module:\n    - user\n  theme:\n    - seven\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.seven_messages.yml",
    "content": "id: seven_messages\ntheme: seven\nweight: 0\nstatus: true\nlangcode: en\nregion: highlighted\nplugin: system_messages_block\nsettings:\n  id: system_messages_block\n  label: 'Status messages'\n  provider: system\n  label_display: '0'\ndependencies:\n  module:\n    - system\n  theme:\n    - seven\n"
  },
  {
    "path": "config/optional/block.block.seven_page_title.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  theme:\n    - seven\nid: seven_page_title\ntheme: seven\nregion: header\nweight: -30\nprovider: null\nplugin: page_title_block\nsettings:\n  id: page_title_block\n  label: 'Page title'\n  provider: core\n  label_display: '0'\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.seven_primary_local_tasks.yml",
    "content": "id: seven_primary_local_tasks\ntheme: seven\nweight: 0\nstatus: true\nlangcode: en\nregion: header\nplugin: local_tasks_block\nsettings:\n  id: local_tasks_block\n  label: Primary tabs\n  label_display: '0'\n  primary: true\n  secondary: false\ndependencies:\n  theme:\n    - seven\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block.block.seven_secondary_local_tasks.yml",
    "content": "id: seven_secondary_local_tasks\ntheme: seven\nweight: 0\nstatus: true\nlangcode: en\nregion: pre_content\nplugin: local_tasks_block\nsettings:\n  id: local_tasks_block\n  label: Secondary tabs\n  label_display: '0'\n  primary: false\n  secondary: true\ndependencies:\n  theme:\n    - seven\nvisibility: {  }\n"
  },
  {
    "path": "config/optional/block_content.type.text.yml",
    "content": "id: text\nlabel: 'Text'\nrevision: 0\ndescription: 'A text block contains a title and a body.'\nlangcode: en\n"
  },
  {
    "path": "config/optional/comment.type.comment.yml",
    "content": "langcode: en\nstatus: true\ndependencies: {  }\nid: comment\nlabel: 'Default comments'\ntarget_entity_type_id: node\ndescription: 'Allows commenting on content'\n"
  },
  {
    "path": "config/optional/core.entity_form_display.block_content.text.default.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  config:\n    - block_content.type.text\n    - field.field.block_content.text.body\n  module:\n    - text\nid: block_content.text.default\ntargetEntityType: block_content\nbundle: text\nmode: default\ncontent:\n  body:\n    type: text_textarea_with_summary\n    weight: 0\n    settings:\n      rows: 9\n      summary_rows: 3\n      placeholder: ''\n    third_party_settings: {  }\n    region: content\n  info:\n    type: string_textfield\n    weight: -5\n    settings:\n      size: 60\n      placeholder: ''\n    third_party_settings: {  }\n    region: content\nhidden: {  }\n"
  },
  {
    "path": "config/optional/core.entity_form_display.comment.comment.default.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  config:\n    - comment.type.comment\n    - field.field.comment.comment.comment_body\n  module:\n    - text\nid: comment.comment.default\ntargetEntityType: comment\nbundle: comment\nmode: default\ncontent:\n  author:\n    weight: -2\n    region: content\n  comment_body:\n    type: text_textarea\n    weight: 11\n    region: content\n    settings:\n      rows: 5\n      placeholder: ''\n    third_party_settings: {  }\n  subject:\n    type: string_textfield\n    weight: 10\n    region: content\n    settings:\n      size: 60\n      placeholder: ''\n    third_party_settings: {  }\nhidden: {  }\n"
  },
  {
    "path": "config/optional/core.entity_view_display.block_content.text.default.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  config:\n    - block_content.type.text\n    - field.field.block_content.text.body\n  module:\n    - text\nid: block_content.text.default\ntargetEntityType: block_content\nbundle: text\nmode: default\ncontent:\n  body:\n    type: text_default\n    weight: 0\n    label: hidden\n    settings: {  }\n    third_party_settings: {  }\n    region: content\nhidden: {  }\n"
  },
  {
    "path": "config/optional/core.entity_view_display.comment.comment.default.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  config:\n    - comment.type.comment\n    - field.field.comment.comment.comment_body\n  module:\n    - text\nid: comment.comment.default\ntargetEntityType: comment\nbundle: comment\nmode: default\ncontent:\n  comment_body:\n    label: hidden\n    type: text_default\n    weight: 0\n    region: content\n    settings: {  }\n    third_party_settings: {  }\n  links:\n    weight: 100\n    region: content\nhidden: {  }\n"
  },
  {
    "path": "config/optional/field.field.block_content.text.body.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  config:\n    - block_content.type.text\n    - field.storage.block_content.body\n  module:\n    - text\nid: block_content.text.body\nfield_name: body\nentity_type: block_content\nbundle: text\nlabel: Body\ndescription: ''\nrequired: false\ntranslatable: true\ndefault_value: {  }\ndefault_value_callback: ''\nsettings:\n  display_summary: false\nfield_type: text_with_summary\n"
  },
  {
    "path": "config/optional/field.field.comment.comment.comment_body.yml",
    "content": "langcode: en\nstatus: true\ndependencies:\n  config:\n    - comment.type.comment\n    - field.storage.comment.comment_body\n  module:\n    - text\nid: comment.comment.comment_body\nfield_name: comment_body\nentity_type: comment\nbundle: comment\nlabel: Comment\ndescription: ''\nrequired: true\ntranslatable: true\ndefault_value: {  }\ndefault_value_callback: ''\nsettings: {  }\nfield_type: text_long\n"
  },
  {
    "path": "config/schema/lightning.schema.yml",
    "content": "user.role.*.third_party.lightning:\n  type: mapping\n  label: 'Lightning settings'\n  mapping:\n    bundled:\n      type: boolean\n      label: 'Bundled with Lightning'\n\nlightning.versions:\n  type: sequence\n  label: 'Lightning component versions'\n  sequence:\n    type: string\n    label: 'Version'\n"
  },
  {
    "path": "drush.services.yml",
    "content": "services:\n  subprofile.generator:\n    class: 'Drupal\\lightning\\Generators\\SubProfileGenerator'\n    arguments:\n      - '@extension.list.module'\n    tags:\n      - { name: drush.generator }\n\n  lightning.uninstaller:\n    class: 'Drupal\\lightning\\Commands\\Uninstaller'\n    arguments:\n      - '@module_handler'\n      - '@theme_handler'\n      - '@extension.list.profile'\n      - '@file_system'\n      - '%app.root%'\n      - '%install_profile%'\n    tags:\n      - { name: drush.command }\n"
  },
  {
    "path": "grumphp.yml",
    "content": "parameters:\n  ascii:\n    failed: vendor/vijaycs85/drupal-quality-checker/resources/ascii/grumpy.txt\n    succeeded: vendor/vijaycs85/drupal-quality-checker/resources/ascii/happy.txt\n  git_dir: .\n  bin_dir: vendor/bin\n  tasks:\n    phplint: ~\n    yamllint: ~\n    composer: ~\n    composer_normalize: ~\n    jsonlint: ~\n    phpcpd:\n      directory: ['./src']\n    phpcs:\n      standard: Drupal\n      ignore_patterns:\n        - .github\n        - .gitlab\n        - bower_components\n        - node_modules\n        - vendor\n      triggered_by:\n        - php\n        - module\n        - inc\n        - install\n        - test\n        - profile\n        - theme\n        - css\n        - info\n        - txt\n        - md\n"
  },
  {
    "path": "help/content_roles.md",
    "content": "## Content Roles\n\n### Creator\nCreator roles are automatically created for every content type and automatically\ndestroyed for deleted content types. The creator roles have limited permissions\n-- they can create content or edit their own. They *cannot* edit content created\nby anybody else, and they cannot delete content (even their own). They can save\ndrafts and request review of their content, but they cannot publish it.\n\n### Reviewer\nLike creator roles, reviewer roles are also automatically created for every\ncontent type and destroyed for deleted content types. Reviewers are intended to\n*extend* creators, meaning that you will typically grant the reviewer role *and*\nthe creator role to users. Reviewers can edit, delete, and publish **any**\ncontent, even content they don't own.\n\n### Administering content roles\nContent roles are configurable. They can be enabled or disabled. When disabled,\nthey will not be automatically created or destroyed for any content type. To\nconfigure content roles, visit to *Manage > Configuration > System > Lightning*.\n\nIt's possible to define additional content roles and change the permissions\nassociated with them, but there is currently no UI for this. You'll need to alter the\n`lightning_core.settings` configuration object directly, which beyond the scope of\nthis documentation.\n"
  },
  {
    "path": "hooks/README.md",
    "content": "These hooks are used by Acquia Cloud as part of Lightning's CI. They are not\nexecuted on sites that use Lightning. For more information, see \n[Acquia's CLoud Hook Documentation](https://docs.acquia.com/acquia-cloud/api/cloud-hooks).\n"
  },
  {
    "path": "hooks/common/post-code-deploy/.gitignore",
    "content": ""
  },
  {
    "path": "hooks/common/post-code-deploy/reinstall.sh",
    "content": "#!/bin/sh\n#\n# Cloud Hook: Reinstall Lightning\n#\n# Run `drush site-install lightning` in the target environment.\n\nwhich drush\ndrush --version\n\nsite=\"$1\"\ntarget_env=\"$2\"\n\n# Fresh install of Lightning.\n/usr/local/bin/drush9 @$site.$target_env site-install lightning --account-pass=admin --yes\n"
  },
  {
    "path": "hooks/common/post-code-update/.gitignore",
    "content": ""
  },
  {
    "path": "hooks/common/post-code-update/reinstall.sh",
    "content": "#!/bin/sh\n#\n# Cloud Hook: Reinstall Lightning\n#\n# Run `drush site-install lightning` in the target environment.\n\nwhich drush\ndrush --version\n\nsite=\"$1\"\ntarget_env=\"$2\"\n\n# Fresh install of Lightning.\n/usr/local/bin/drush9 @$site.$target_env site-install lightning --account-pass=admin --yes\n"
  },
  {
    "path": "install-drupal.sh",
    "content": "#!/usr/bin/env bash\n\nSITE_DIR=$(pwd)/docroot/sites/default\nSETTINGS=$SITE_DIR/settings.php\n\nDB_URL=${DB_URL:-sqlite://db.sqlite}\n\n# Delete previous settings.\nif [[ -f $SETTINGS ]]; then\n    chmod +w $SITE_DIR $SETTINGS\n    rm $SETTINGS\nfi\n\n# Install Drupal.\ndrush site:install lightning --yes --config ./drush.yml --account-pass admin --db-url $DB_URL\n\n# Make settings writable.\nchmod +w $SITE_DIR $SETTINGS\n\n# Copy development settings into the site directory.\ncp settings.local.php $SITE_DIR\n\n# Add Acquia Cloud subscription info to settings.php.\necho \"if (file_exists('/var/www/site-php')) {\" >> $SETTINGS\necho \"  require '/var/www/site-php/lightningnightly/lightningnightly-settings.inc';\" >> $SETTINGS\necho \"  \\$settings['install_profile'] = 'standard';\" >> $SETTINGS\necho \"}\" >> $SETTINGS\necho \"else {\" >> $SETTINGS\necho \"  require __DIR__ . '/settings.local.php';\" >> $SETTINGS\necho \"}\" >> $SETTINGS\necho \"require __DIR__ . '/settings.local.php';\" >> $SETTINGS\n\n# Copy PHPUnit configuration into core directory.\ncp -f phpunit.xml ./docroot/core\n"
  },
  {
    "path": "lightning.info.yml",
    "content": "name: Lightning\ncore_version_requirement: '^8.8 || ^9'\ntype: profile\ndescription: 'A fast and feature-rich Drupal distribution.'\ninstall:\n  - autosave_form\n  - block_content\n  - breakpoint\n  - ckeditor\n  - config\n  - conflict\n  - contextual\n  - menu_link_content\n  - datetime\n  - quickedit\n  - editor\n  - entity_block\n  - help\n  - history\n  - menu_ui\n  - node\n  - options\n  - path\n  - pathauto\n  - page_cache\n  - redirect\n  - taxonomy\n  - text\n  - dblog\n  - shortcut\n  - toolbar\n  - field_ui\n  - file\n  - rdf\n  - views\n  - views_ui\n  - diff\n  - image_widget_crop\n  - metatag\n  - moderation_dashboard\n  - moderation_sidebar\n  - lightning_core\n  - lightning_api\n  - lightning_layout\n  - lightning_media\n  - lightning_workflow\n  - lightning_contact_form\n  - lightning_page\n  - lightning_roles\n  - lightning_search\n  - lightning_landing_page\n  - lightning_media_audio\n  - lightning_media_bulk_upload\n  - lightning_media_document\n  - lightning_media_image\n  - lightning_media_instagram\n  - lightning_media_slideshow\n  - lightning_media_twitter\n  - lightning_media_video\n  - lightning_scheduler\n  - lightning_banner_block\n  - lightning_map_block\nthemes:\n  - bartik\n  - claro\n"
  },
  {
    "path": "lightning.install",
    "content": "<?php\n\n/**\n * @file\n * Install and uninstall functions for the Lightning installation profile.\n */\n\nuse Drupal\\Core\\Entity\\Entity\\EntityViewDisplay;\nuse Drupal\\field\\Entity\\FieldConfig;\nuse Drupal\\lightning_core\\ConfigHelper as Config;\n\n/**\n * Makes updates to some basic config that ships with Lightning.\n *\n * Install the body field on the basic block_content type, delete the Create\n * landing page shortcut, and uninstall the Media Demo Content Module.\n */\nfunction lightning_update_8001() {\n  // Install the body field on the basic block_content type.\n  $field = FieldConfig::load('block_content.basic.body');\n  if (empty($field)) {\n    $config = Config::forModule('lightning');\n    $config->getEntity('field_config', 'block_content.basic.body')->save();\n\n    $display = EntityViewDisplay::load('block_content.basic.default');\n    if ($display) {\n      /** @var \\Drupal\\Core\\Entity\\Display\\EntityViewDisplayInterface $display */\n      $display->setComponent('body', [\n        'type' => 'text_default',\n        'weight' => 0,\n        'label' => 'hidden',\n        'settings' => [],\n        'third_party_settings' => [],\n      ])->save();\n    }\n    else {\n      $config->getEntity('entity_view_display', 'block_content.basic.default')->save();\n    }\n  }\n\n  // Delete the `Create Landing Page` toolbar shortcut.\n  $properties = ['link__uri' => 'internal:/admin/structure/landing-page'];\n  $shortcuts = \\Drupal::entityTypeManager()->getStorage('shortcut')->loadByProperties($properties);\n  foreach ($shortcuts as $shortcut) {\n    $shortcut->delete();\n  }\n\n  // Uninstall the Media Demo Content module.\n  \\Drupal::service('module_installer')->uninstall(['lightning_media_democontent']);\n}\n\n/**\n * Removed in Lightning 8.x-2.06.\n *\n * Formerly created roles for editing content types.\n */\nfunction lightning_update_8002() {\n}\n\n/**\n * Enables the lightning_core module.\n */\nfunction lightning_update_8003() {\n  Drupal::service('module_installer')->install(['lightning_core']);\n}\n\n/**\n * Enables Entity API.\n */\nfunction lightning_update_8004() {\n  /*\n   * This ancient line is disabled in order to evade a strict coding standards\n   * check. It should not be necessary at this point anyway, since it dates from\n   * the days of Lightning 1.x, which has long since been end-of-life.\n   *\n   * eval('interface Drupal\\entity\\Entity\\RevisionableEntityBundleInterface{}');\n   */\n  Drupal::service('module_installer')->install(['entity']);\n}\n\n/**\n * Removed in Lightning 8.x-2.04.\n *\n * Formerly installed Contact Storage.\n */\nfunction lightning_update_8005() {\n}\n\n/**\n * Removed in Lightning 8.x-2.04.\n *\n * Formerly Granted permission to use contact form(s).\n */\nfunction lightning_update_8006() {\n}\n\n/**\n * Uninstalls the lightning_install module.\n */\nfunction lightning_update_8007() {\n  Drupal::service('module_installer')->uninstall(['lightning_install']);\n}\n\n/**\n * Installs the profile_switcher module.\n */\nfunction lightning_update_8008() {\n  Drupal::service('module_installer')->install(['profile_switcher']);\n}\n\n/**\n * Implements hook_update_dependencies().\n */\nfunction lightning_update_dependencies() {\n  return [\n    'media_entity' => [\n      // Lightning 8004 must run before Media Entity 8002.\n      8002 => [\n        'lightning' => 8004,\n      ],\n    ],\n  ];\n}\n"
  },
  {
    "path": "lightning.profile",
    "content": "<?php\n\n/**\n * @file\n * The Lightning profile.\n */\n\nuse Drupal\\user\\RoleInterface;\n\n/**\n * Implements hook_install_tasks().\n */\nfunction lightning_install_tasks(array &$install_state) {\n  $tasks = [];\n\n  // All of these tasks modify configuration, so don't do any of them if\n  // we're installing from existing config.\n  if (empty($install_state['config_install_path'])) {\n    $tasks['lightning_set_front_page'] = [];\n    $tasks['lightning_grant_shortcut_access'] = [];\n    $tasks['lightning_set_default_theme'] = [];\n    $tasks['lightning_set_logo'] = [];\n    $tasks['lightning_alter_frontpage_view'] = [];\n  }\n  return $tasks;\n}\n\n/**\n * Sets the front page path to /node.\n */\nfunction lightning_set_front_page() {\n  if (Drupal::moduleHandler()->moduleExists('node')) {\n    Drupal::configFactory()\n      ->getEditable('system.site')\n      ->set('page.front', '/node')\n      ->save(TRUE);\n  }\n}\n\n/**\n * Allows authenticated users to use shortcuts.\n */\nfunction lightning_grant_shortcut_access() {\n  if (Drupal::moduleHandler()->moduleExists('shortcut')) {\n    user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, ['access shortcuts']);\n  }\n}\n\n/**\n * Sets the default and administration themes.\n */\nfunction lightning_set_default_theme() {\n  Drupal::configFactory()\n    ->getEditable('system.theme')\n    ->set('default', 'bartik')\n    ->set('admin', 'claro')\n    ->save(TRUE);\n\n  // Use the admin theme for creating content.\n  if (Drupal::moduleHandler()->moduleExists('node')) {\n    Drupal::configFactory()\n      ->getEditable('node.settings')\n      ->set('use_admin_theme', TRUE)\n      ->save(TRUE);\n  }\n}\n\n/**\n * Set the path to the logo, favicon and README file based on install directory.\n */\nfunction lightning_set_logo() {\n  $lightning_path = drupal_get_path('profile', 'lightning');\n\n  Drupal::configFactory()\n    ->getEditable('system.theme.global')\n    ->set('logo', [\n      'path' => $lightning_path . '/lightning.png',\n      'url' => '',\n      'use_default' => FALSE,\n    ])\n    ->set('favicon', [\n      'mimetype' => 'image/vnd.microsoft.icon',\n      'path' => $lightning_path . '/favicon.ico',\n      'url' => '',\n      'use_default' => FALSE,\n    ])\n    ->save(TRUE);\n}\n\n/**\n * Alters the frontpage view, if it exists.\n */\nfunction lightning_alter_frontpage_view() {\n  $front_page = Drupal::configFactory()->getEditable('views.view.frontpage');\n\n  if (!$front_page->isNew()) {\n    $section = 'display.default.display_options.empty.area_text_custom';\n    $front_page\n      ->set(\"$section.tokenize\", TRUE)\n      ->set(\"$section.content\", '<p>Welcome to [site:name]. No front page content has been created yet.</p><p>Would you like to <a href=\"/' . drupal_get_path('profile', 'lightning') . '/README.md\">view the README</a>?</p>')\n      ->save(TRUE);\n  }\n}\n"
  },
  {
    "path": "modules/lightning_install/composer.json",
    "content": "{\n    \"name\": \"drupal/lightning_install\",\n    \"type\": \"drupal-module\",\n    \"description\": \"Provides helpful under-the-hood functionality used while installing Lightning.\",\n    \"license\": \"GPL-2.0-or-later\"\n}\n"
  },
  {
    "path": "modules/lightning_install/lightning_install.info.yml",
    "content": "name: 'Install Helper'\ncore_version_requirement: '^8.8 || ^9'\ntype: module\nhidden: true\npackage: Lightning\ndescription: 'Provides helpful under-the-hood functionality used while installing Lightning.'\n"
  },
  {
    "path": "phpunit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!-- TODO set checkForUnintentionallyCoveredCode=\"true\" once https://www.drupal.org/node/2626832 is resolved. -->\n<!-- PHPUnit expects functional tests to be run with either a privileged user\n or your current system user. See core/tests/README.md and\n https://www.drupal.org/node/2116263 for details.\n-->\n<phpunit bootstrap=\"../../vendor/weitzman/drupal-test-traits/src/bootstrap.php\" colors=\"true\"\n         beStrictAboutTestsThatDoNotTestAnything=\"true\"\n         beStrictAboutOutputDuringTests=\"true\"\n         beStrictAboutChangesToGlobalState=\"true\">\n<!-- TODO set printerClass=\"\\Drupal\\Tests\\Listeners\\HtmlOutputPrinter\" once\n https://youtrack.jetbrains.com/issue/WI-24808 is resolved. Drupal provides a\n result printer that links to the html output results for functional tests.\n Unfortunately, this breaks the output of PHPStorm's PHPUnit runner. However, if\n using the command line you can add\n - -printer=\"\\Drupal\\Tests\\Listeners\\HtmlOutputPrinter\" to use it (note there\n should be no spaces between the hyphens).\n-->\n  <php>\n    <!-- Set error reporting to E_ALL. -->\n    <ini name=\"error_reporting\" value=\"32767\"/>\n    <!-- Do not limit the amount of memory tests take to run. -->\n    <ini name=\"memory_limit\" value=\"-1\"/>\n    <!-- Example SIMPLETEST_BASE_URL value: http://localhost -->\n    <env name=\"SIMPLETEST_BASE_URL\" value=\"http://127.0.0.1:8080\"/>\n    <env name=\"DTT_BASE_URL\" value=\"http://127.0.0.1:8080\"/>\n    <!-- Example SIMPLETEST_DB value: mysql://username:password@localhost/databasename#table_prefix -->\n    <env name=\"SIMPLETEST_DB\" value=\"sqlite://localhost/db.sqlite\"/>\n    <!-- Example BROWSERTEST_OUTPUT_DIRECTORY value: /path/to/webroot/sites/simpletest/browser_output -->\n    <env name=\"BROWSERTEST_OUTPUT_DIRECTORY\" value=\"\"/>\n    <!-- To disable deprecation testing completely uncomment the next line. -->\n    <env name=\"SYMFONY_DEPRECATIONS_HELPER\" value=\"weak\"/>\n    <!-- Example for changing the driver class for mink tests MINK_DRIVER_CLASS value: 'Drupal\\FunctionalJavascriptTests\\DrupalSelenium2Driver' -->\n    <!-- Example for changing the driver args to mink tests MINK_DRIVER_ARGS value: '[\"http://127.0.0.1:8510\"]' -->\n    <!-- Example for changing the driver args to phantomjs tests MINK_DRIVER_ARGS_PHANTOMJS value: '[\"http://127.0.0.1:8510\"]' -->\n    <!-- Example for changing the driver args to webdriver tests MINK_DRIVER_ARGS_WEBDRIVER value: '[\"firefox\", null, \"http://localhost:4444/wd/hub\"]' -->\n    <env name=\"MINK_DRIVER_ARGS_WEBDRIVER\" value='[\"chrome\", {\"chrome\": {\"switches\": [\"headless\", \"disable-gpu\", \"no-sandbox\", \"disable-dev-shm-usage\", \"disable-extensions\"]}}, \"http://localhost:4444\"]'/>\n  </php>\n  <testsuites>\n    <testsuite name=\"unit\">\n      <file>./tests/TestSuites/UnitTestSuite.php</file>\n    </testsuite>\n    <testsuite name=\"kernel\">\n      <file>./tests/TestSuites/KernelTestSuite.php</file>\n    </testsuite>\n    <testsuite name=\"functional\">\n      <file>./tests/TestSuites/FunctionalTestSuite.php</file>\n    </testsuite>\n    <testsuite name=\"functional-javascript\">\n      <file>./tests/TestSuites/FunctionalJavascriptTestSuite.php</file>\n    </testsuite>\n  </testsuites>\n  <listeners>\n    <listener class=\"\\Drupal\\Tests\\Listeners\\DrupalListener\">\n    </listener>\n    <!-- The Symfony deprecation listener has to come after the Drupal listener -->\n    <listener class=\"Symfony\\Bridge\\PhpUnit\\SymfonyTestsListener\">\n    </listener>\n  </listeners>\n  <!-- Filter for coverage reports. -->\n  <filter>\n    <whitelist>\n      <directory>./includes</directory>\n      <directory>./lib</directory>\n      <directory>./modules</directory>\n      <directory>../modules</directory>\n      <directory>../sites</directory>\n      <!-- By definition test classes have no tests. -->\n      <exclude>\n        <directory suffix=\"Test.php\">./</directory>\n        <directory suffix=\"TestBase.php\">./</directory>\n      </exclude>\n     </whitelist>\n  </filter>\n</phpunit>\n"
  },
  {
    "path": "release-branch.sh",
    "content": "#!/bin/bash\n\nset -e\n\n# Prepares a release branch.\n# Example usage: ./release-branch 4.1.0\n\nRELEASE_BRANCH=release/$1\n\n# Ensure we are on a mainline release branch.\nBRANCH=$(git rev-parse --abbrev-ref HEAD)\n\nif [[ $BRANCH =~ ^([0-9]+\\.){2}x$ ]]; then\n  git pull\n  git checkout -b $RELEASE_BRANCH\n\n  composer update\n  cp composer.lock tests/fixtures/$1.lock\n\n  git add .\n  git commit --message \"$1 Release\"\n  git push --set-upstream origin $RELEASE_BRANCH\nelse\n  echo \"This can only be done from a mainline release branch, e.g. 5.0.x.\"\n  exit 1\nfi\n"
  },
  {
    "path": "settings.local.php",
    "content": "<?php\n\n// @codingStandardsIgnoreFile\n\n/**\n * @file\n * Local development override configuration feature.\n *\n * To activate this feature, copy and rename it such that its path plus\n * filename is 'sites/default/settings.local.php'. Then, go to the bottom of\n * 'sites/default/settings.php' and uncomment the commented lines that mention\n * 'settings.local.php'.\n *\n * If you are using a site name in the path, such as 'sites/example.com', copy\n * this file to 'sites/example.com/settings.local.php', and uncomment the lines\n * at the bottom of 'sites/example.com/settings.php'.\n */\n\n/**\n * Assertions.\n *\n * The Drupal project primarily uses runtime assertions to enforce the\n * expectations of the API by failing when incorrect calls are made by code\n * under development.\n *\n * @see http://php.net/assert\n * @see https://www.drupal.org/node/2492225\n *\n * If you are using PHP 7.0 it is strongly recommended that you set\n * zend.assertions=1 in the PHP.ini file (It cannot be changed from .htaccess\n * or runtime) on development machines and to 0 in production.\n *\n * @see https://wiki.php.net/rfc/expectations\n */\nassert_options(ASSERT_ACTIVE, TRUE);\n\\Drupal\\Component\\Assertion\\Handle::register();\n\n/**\n * Enable local development services.\n */\n$settings['container_yamls'][] = DRUPAL_ROOT . '/sites/development.services.yml';\n\n/**\n * Show all error messages, with backtrace information.\n *\n * In case the error level could not be fetched from the database, as for\n * example the database connection failed, we rely only on this value.\n */\n$config['system.logging']['error_level'] = 'verbose';\n\n/**\n * Disable CSS and JS aggregation.\n */\n$config['system.performance']['css']['preprocess'] = FALSE;\n$config['system.performance']['js']['preprocess'] = FALSE;\n\n/**\n * Disable the render cache.\n *\n * Note: you should test with the render cache enabled, to ensure the correct\n * cacheability metadata is present. However, in the early stages of\n * development, you may want to disable it.\n *\n * This setting disables the render cache by using the Null cache back-end\n * defined by the development.services.yml file above.\n *\n * Only use this setting once the site has been installed.\n */\n# $settings['cache']['bins']['render'] = 'cache.backend.null';\n\n/**\n * Disable caching for migrations.\n *\n * Uncomment the code below to only store migrations in memory and not in the\n * database. This makes it easier to develop custom migrations.\n */\n# $settings['cache']['bins']['discovery_migration'] = 'cache.backend.memory';\n\n/**\n * Disable Internal Page Cache.\n *\n * Note: you should test with Internal Page Cache enabled, to ensure the correct\n * cacheability metadata is present. However, in the early stages of\n * development, you may want to disable it.\n *\n * This setting disables the page cache by using the Null cache back-end\n * defined by the development.services.yml file above.\n *\n * Only use this setting once the site has been installed.\n */\n# $settings['cache']['bins']['page'] = 'cache.backend.null';\n\n/**\n * Disable Dynamic Page Cache.\n *\n * Note: you should test with Dynamic Page Cache enabled, to ensure the correct\n * cacheability metadata is present (and hence the expected behavior). However,\n * in the early stages of development, you may want to disable it.\n */\n# $settings['cache']['bins']['dynamic_page_cache'] = 'cache.backend.null';\n\n/**\n * Allow test modules and themes to be installed.\n *\n * Drupal ignores test modules and themes by default for performance reasons.\n * During development it can be useful to install test extensions for debugging\n * purposes.\n */\n# $settings['extension_discovery_scan_tests'] = TRUE;\n\n/**\n * Enable access to rebuild.php.\n *\n * This setting can be enabled to allow Drupal's php and database cached\n * storage to be cleared via the rebuild.php page. Access to this page can also\n * be gained by generating a query string from rebuild_token_calculator.sh and\n * using these parameters in a request to rebuild.php.\n */\n$settings['rebuild_access'] = TRUE;\n\n/**\n * Skip file system permissions hardening.\n *\n * The system module will periodically check the permissions of your site's\n * site directory to ensure that it is not writable by the website user. For\n * sites that are managed with a version control system, this can cause problems\n * when files in that directory such as settings.php are updated, because the\n * user pulling in the changes won't have permissions to modify files in the\n * directory.\n */\n$settings['skip_permissions_hardening'] = TRUE;\n\n$settings['bootstrap_container_definition'] = [\n  'parameters' => [],\n  'services' => [\n    'cache.container' => [\n      'class' => 'Drupal\\Core\\Cache\\MemoryBackend',\n    ],\n  ],\n];\n"
  },
  {
    "path": "src/Commands/Uninstaller.php",
    "content": "<?php\n\nnamespace Drupal\\lightning\\Commands;\n\nuse Composer\\Json\\JsonFile;\nuse Consolidation\\AnnotatedCommand\\CommandData;\nuse Consolidation\\SiteAlias\\SiteAliasManagerAwareInterface;\nuse Consolidation\\SiteAlias\\SiteAliasManagerAwareTrait;\nuse Drupal\\Component\\Serialization\\Yaml;\nuse Drupal\\Core\\Config\\FileStorage;\nuse Drupal\\Core\\Config\\InstallStorage;\nuse Drupal\\Core\\Extension\\Extension;\nuse Drupal\\Core\\Extension\\ModuleHandlerInterface;\nuse Drupal\\Core\\Extension\\ProfileExtensionList;\nuse Drupal\\Core\\Extension\\ThemeHandlerInterface;\nuse Drupal\\Core\\File\\Exception\\FileExistsException;\nuse Drupal\\Core\\File\\FileSystemInterface;\nuse DrupalFinder\\DrupalFinder;\nuse Drush\\Commands\\DrushCommands;\nuse Symfony\\Component\\Console\\Command\\Command;\nuse Symfony\\Component\\Console\\Input\\InputOption;\nuse Symfony\\Component\\Filesystem\\Exception\\IOException;\n\n/**\n * Hooks into Drush to assist with uninstalling Lightning.\n *\n * @internal\n *   This class is a completely internal part of Lightning's uninstall system\n *   and can be changed in any way, or removed outright, at any time without\n *   warning. External code should not use this class in any way.\n */\nfinal class Uninstaller extends DrushCommands implements SiteAliasManagerAwareInterface {\n\n  use SiteAliasManagerAwareTrait;\n\n  /**\n   * The module handler service.\n   *\n   * @var \\Drupal\\Core\\Extension\\ModuleHandlerInterface\n   */\n  private $moduleHandler;\n\n  /**\n   * The theme handler service.\n   *\n   * @var \\Drupal\\Core\\Extension\\ThemeHandlerInterface\n   */\n  private $themeHandler;\n\n  /**\n   * The profile extension list.\n   *\n   * @var \\Drupal\\Core\\Extension\\ProfileExtensionList\n   */\n  private $profileList;\n\n  /**\n   * The file system service.\n   *\n   * @var \\Drupal\\Core\\File\\FileSystemInterface\n   */\n  private $fileSystem;\n\n  /**\n   * The Drupal root.\n   *\n   * @var string\n   */\n  private $appRoot;\n\n  /**\n   * The current installation profile.\n   *\n   * @var string\n   */\n  private $installProfile;\n\n  /**\n   * Uninstaller constructor.\n   *\n   * @param \\Drupal\\Core\\Extension\\ModuleHandlerInterface $module_handler\n   *   The module handler service.\n   * @param \\Drupal\\Core\\Extension\\ThemeHandlerInterface $theme_handler\n   *   The theme handler service.\n   * @param \\Drupal\\Core\\Extension\\ProfileExtensionList $profile_list\n   *   The profile extension list.\n   * @param \\Drupal\\Core\\File\\FileSystemInterface $file_system\n   *   The file system service.\n   * @param string $app_root\n   *   The Drupal root.\n   * @param string $install_profile\n   *   The current installation profile.\n   */\n  public function __construct(ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, ProfileExtensionList $profile_list, FileSystemInterface $file_system, $app_root, $install_profile) {\n    $this->moduleHandler = $module_handler;\n    $this->themeHandler = $theme_handler;\n    $this->profileList = $profile_list;\n    $this->fileSystem = $file_system;\n    $this->appRoot = $app_root;\n    $this->installProfile = $install_profile;\n  }\n\n  /**\n   * Defines dynamic options if uninstalling Lightning.\n   *\n   * @param \\Symfony\\Component\\Console\\Command\\Command $command\n   *   The command object.\n   *\n   * @hook option pm:uninstall\n   */\n  public function options(Command $command) : void {\n    if ($this->getUninstall()) {\n      $command->addOption(\n        'profile',\n        NULL,\n        InputOption::VALUE_REQUIRED,\n        'The profile to switch to.',\n        'minimal'\n      );\n      $command->addOption(\n        'composer',\n        NULL,\n        InputOption::VALUE_REQUIRED,\n        'The path of the project-level composer.json.',\n        $this->locateProjectFile()\n      );\n    }\n  }\n\n  /**\n   * Runs a Drush command, with the --yes option.\n   *\n   * @param string $command\n   *   The command name (e.g., 'status').\n   * @param array $arguments\n   *   (optional) Arguments to pass to the command.\n   */\n  private function drush(string $command, array $arguments = []) : void {\n    $alias = $this->siteAliasManager()->getSelf();\n\n    $this->processManager()\n      ->drush($alias, $command, $arguments, ['yes' => NULL])\n      ->mustRun();\n  }\n\n  /**\n   * Returns the profile being uninstalled.\n   *\n   * @return string|null\n   *   If an uninstall of Lightning or Headless Lightning is being attempted,\n   *   the machine name of the profile being uninstalled. NULL otherwise.\n   */\n  private function getUninstall() : ?string {\n    $modules = array_intersect($this->input()->getArgument('modules'), [\n      'lightning',\n      'headless_lightning',\n    ]);\n    return reset($modules) ?: NULL;\n  }\n\n  /**\n   * Validates the pm:uninstall command if Lightning is being uninstalled.\n   *\n   * @param \\Consolidation\\AnnotatedCommand\\CommandData $data\n   *   The current command data.\n   *\n   * @hook validate pm:uninstall\n   *\n   * @throws \\RuntimeException\n   *   Thrown if:\n   *   - The user is trying to uninstall Lightning (or Headless Lightning) at\n   *     the same time as other extension(s).\n   *   - Any installed extensions are physically located inside the Lightning\n   *     profile directory.\n   *   - Any profiles (installed or not) are using Lightning or Headless\n   *     Lightning as their immediate parent, and the user declines to modify\n   *     them automatically.\n   */\n  public function validate(CommandData $data) : void {\n    $profile = $this->getUninstall();\n    if ($profile) {\n      $info = $this->profileList->getExtensionInfo($profile);\n      $this->io()->title('Welcome to the ' . $info['name'] . ' uninstaller!');\n\n      if (count($data->input()->getArgument('modules')) > 1) {\n        throw new \\RuntimeException('You cannot uninstall ' . $info['name'] . ' and other extensions at the same time.');\n      }\n\n      // Ensure that there are no installed modules or themes in the Lightning\n      // profile directory.\n      $extensions = $this->getExtensionsInProfileDirectory();\n      if ($extensions) {\n        $error = sprintf('The following modules and/or themes are located inside the Lightning profile directory. They must be moved elsewhere before Lightning can be uninstalled: %s', implode(', ', $extensions));\n        throw new \\RuntimeException($error);\n      }\n\n      // Ensure that there are no other profiles available that use the profile\n      // as a parent. If there are, offer to automatically fix them, and error\n      // out if the user declines.\n      $children = $this->getChildren($profile);\n      if ($children) {\n        $warning = sprintf('The following install profiles use %s as a base profile. They must stand alone, or use a different base profile, before Lightning can be uninstalled: %s', $info['name'], implode(', ', $children));\n        $this->io()->warning($warning);\n\n        $fix_it = $this->confirm('These profiles can be automatically decoupled from ' . $info['name'] . '. Should I do that now?', TRUE);\n        if ($fix_it) {\n          array_walk($children, [$this, 'decoupleProfile']);\n        }\n        else {\n          throw new \\RuntimeException('These profiles must be decoupled from ' . $info['name'] . ' before uninstallation can continue.');\n        }\n      }\n    }\n  }\n\n  /**\n   * Performs required actions before Lightning is uninstalled.\n   *\n   * @hook pre-command pm:uninstall\n   */\n  public function preCommand() : void {\n    if ($this->getUninstall()) {\n      if ($this->installProfile === 'lightning' || $this->installProfile === 'headless_lightning') {\n        // The lightning_install module was created to prevent broken builds of\n        // Lightning (created by drupal.org's legacy packaging system) from\n        // being installed.\n        $this->drush('pm:uninstall', ['lightning_install']);\n\n        $profile = $this->input()->getOption('profile');\n        $this->boldlySay(\"Switching to $profile profile...\");\n        $this->drush('pm:enable', ['profile_switcher']);\n        $this->drush('switch:profile', [$profile]);\n      }\n      $this->alterProject();\n    }\n  }\n\n  /**\n   * Performs required actions after Lightning is uninstalled.\n   *\n   * @hook post-command pm:uninstall\n   */\n  public function postCommand() : void {\n    $profile = $this->getUninstall();\n    if ($profile) {\n      $this->drush('pm:uninstall', ['profile_switcher']);\n\n      $info = $this->profileList->getExtensionInfo($profile);\n      $this->io()->success([\n        \"Congrats, \" . $info['name'] . \" has been uninstalled!\",\n        \"You should now commit code and configuration changes, and deploy them to your hosting environment.\",\n      ]);\n    }\n  }\n\n  /**\n   * Returns installed extensions in the Lightning profile directory.\n   *\n   * @return string[]\n   *   The names of installed extensions in the Lightning profile directory.\n   *   If there are any, Lightning cannot be uninstalled. There shouldn't\n   *   normally be any -- Lightning doesn't ship with any runtime modules --\n   *   but it's wise to check anyway in case the current site has an exotic\n   *   set-up.\n   */\n  private function getExtensionsInProfileDirectory() : array {\n    $extensions = array_merge(\n      $this->moduleHandler->getModuleList(),\n      $this->themeHandler->listInfo()\n    );\n\n    $profile_path = $extensions['lightning']->getPath();\n    unset($extensions['lightning']);\n    // The lightning_install module is a special module that was created to\n    // prevent installation of broken builds of Lightning created by the legacy\n    // drupal.org packaging system. This utility uninstalls it along with the\n    // profile.\n    unset($extensions['lightning_install']);\n\n    $filter = function (Extension $extension) use ($profile_path) : bool {\n      return strpos($extension->getPath(), $profile_path) !== FALSE;\n    };\n    $extensions = array_filter($extensions, $filter);\n    return array_keys($extensions);\n  }\n\n  /**\n   * Lists all profiles that have a specific profile as their parent.\n   *\n   * @param string $parent\n   *   The name of the parent profile.\n   *\n   * @return string[]\n   *   The machine names of all profiles, installed or not, that have the given\n   *   parent.\n   */\n  private function getChildren(string $parent) : array {\n    $children = [];\n    foreach ($this->profileList->getAllAvailableInfo() as $name => $info) {\n      if (isset($info['base profile']) && $info['base profile'] === $parent) {\n        $children[] = $name;\n      }\n    }\n    return $children;\n  }\n\n  /**\n   * Uncouples a profile from its parent.\n   *\n   * This will modify the profile's info file to remove the dependency on\n   * the parent, then copy all of the parent's default configuration into the\n   * profile's optional config directory. Existing config is preserved, as are\n   * any info file customizations.\n   *\n   * @param string $name\n   *   The machine name of the sub-profile.\n   */\n  private function decoupleProfile(string $name) : void {\n    $target = $this->readInfo($name);\n    $parent_key = $target['base profile'];\n    $parent = $this->readInfo($parent_key);\n\n    $io = $this->io();\n    $io->section(\"Decoupling $name from \" . $parent['name']);\n    unset($target['base profile']);\n\n    // This strips out the project prefix from a dependency. For example, this\n    // will convert 'drupal:views' to just 'views'.\n    $map = function (string $name) : string {\n      $name = explode(':', $name, 2);\n      return end($name);\n    };\n\n    $exclude = array_map($map, $target['exclude'] ?? []);\n    unset($target['exclude']);\n\n    $install = array_map($map, $target['install'] ?? []);\n    // Add all of the parent's dependencies, except for excluded ones.\n    $install = array_merge($install, $parent['install']);\n    $target['install'] = $this->arrayDiff($install, $exclude);\n\n    // Add all of the parent's themes, except for excluded ones.\n    $themes = array_merge($target['themes'] ?? [], $parent['themes']);\n    $target['themes'] = $this->arrayDiff($themes, $exclude);\n\n    // If the parent is listed as an explicit dependency, remove that.\n    if (isset($target['dependencies'])) {\n      $target['dependencies'] = $this->arrayDiff($target['dependencies'], [$parent_key]);\n    }\n\n    $destination = $this->profileList->getPathname($name);\n    $success = file_put_contents($destination, Yaml::encode($target));\n    if ($success) {\n      $this->say(\"Updated $destination.\");\n    }\n    else {\n      throw new IOException(\"Unable to write to $destination.\");\n    }\n\n    $this->copyConfiguration($parent_key, $name);\n    $io->success(\"$name has been decoupled from \" . $parent['name'] . \".\");\n  }\n\n  /**\n   * Returns the difference between two arrays.\n   *\n   * @param array $a\n   *   An array of values.\n   * @param array $b\n   *   Another array of values.\n   *\n   * @return array\n   *   The items which are in $a but not $b, numerically re-indexed. All\n   *   duplicate values will be removed.\n   */\n  private function arrayDiff(array $a, array $b) : array {\n    $c = array_diff($a, $b);\n    $c = array_unique($c);\n    return array_values($c);\n  }\n\n  /**\n   * Reads the info file of a profile.\n   *\n   * @param string $name\n   *   The machine name of the profile.\n   *\n   * @return array\n   *   The parsed profile info.\n   */\n  private function readInfo(string $name) : array {\n    $info = $this->profileList->getPathname($name);\n    $info = file_get_contents($info);\n    return Yaml::decode($info);\n  }\n\n  /**\n   * Copies all config from one profile into another.\n   *\n   * @param string $source\n   *   The profile from which the config should be copied.\n   * @param string $target\n   *   The profile into which the config should be copied.\n   */\n  private function copyConfiguration(string $source, string $target) : void {\n    $destination_dir = $this->profileList->getPath($target) . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;\n    $this->fileSystem->prepareDirectory($destination_dir, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);\n\n    $info = $this->profileList->getExtensionInfo($source);\n    $this->boldlySay(\"Copying \" . $info['name'] . \" configuration to $target...\");\n\n    foreach ($this->getConfigurationToCopy($source) as $name => $path) {\n      $destination = sprintf('%s/%s.%s', $destination_dir, $name, FileStorage::getFileExtension());\n      $this->say($destination);\n\n      try {\n        $this->fileSystem->copy($path, $destination, FileSystemInterface::EXISTS_ERROR);\n      }\n      catch (FileExistsException $e) {\n        $this->io()->note($e->getMessage());\n      }\n    }\n  }\n\n  /**\n   * Lists all config that ships with the a profile.\n   *\n   * @param string $profile\n   *   The name of the profile.\n   *\n   * @return string[]\n   *   An array of config that ships with the given profile. The keys will be\n   *   the config names, and the values will be the paths of the config files,\n   *   relative to the Drupal root.\n   */\n  private function getConfigurationToCopy(string $profile) : array {\n    $base_dir = $this->profileList->getPath($profile);\n\n    $directories = array_filter([\n      $base_dir . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY,\n      $base_dir . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY,\n    ], 'is_dir');\n\n    $list = [];\n    foreach ($directories as $dir) {\n      $storage = new FileStorage($dir);\n      foreach ($storage->listAll() as $name) {\n        $list[$name] = $storage->getFilePath($name);\n      }\n    }\n    return $list;\n  }\n\n  /**\n   * Returns the location of the project-level composer.json.\n   *\n   * @return string\n   *   The path of the project-level composer.json.\n   */\n  private function locateProjectFile() : string {\n    $finder = new DrupalFinder();\n    if ($finder->locateRoot($this->appRoot) == FALSE) {\n      throw new \\LogicException(\"Could not locate the Drupal root.\");\n    }\n\n    $target = $finder->getComposerRoot() . DIRECTORY_SEPARATOR . 'composer.json';\n    assert(file_exists($target), \"Expected $target to exist, but it does not.\");\n\n    return $target;\n  }\n\n  /**\n   * Alters the project-level composer.json to uninstall Lightning.\n   */\n  private function alterProject() : void {\n    $target = $this->input()->getOption('composer');\n\n    $this->boldlySay(\"Modifying $target...\");\n    $this->io()->listing([\n      'Ensuring direct Lightning dependencies are required',\n      'Ensuring required repositories are present',\n      'Adding required patches',\n      'Checking patcher configuration',\n      'Checking installer configuration',\n      'Checking scaffold configuration',\n    ]);\n\n    $file = new JsonFile($target);\n    $target = $file->read();\n\n    // Read Lightning's composer.json, since we will need to merge in many\n    // default values from it.\n    $source = new JsonFile(__DIR__ . '/../../composer.json');\n    $source = $source->read();\n    $source += [\n      'extra' => [],\n    ];\n    // Sanity check that we have the correct composer.json.\n    assert($source['name'] === 'acquia/lightning');\n\n    $data = $this->mergeCanadian($target, [\n      'require' => $this->getRequirements($target, $source),\n      'extra' => [\n        'composer-exit-on-patch-failure' => $extra['composer-exit-on-patch-failure'] ?? TRUE,\n        'drupal-scaffold' => [\n          'locations' => [\n            'web-root' => $this->getDrupalRoot($target, $source) . '/',\n          ],\n        ],\n        'enable-patching' => $source['extra']['enable-patching'] ?? TRUE,\n        'installer-paths' => $this->getPaths($target, $source),\n        'installer-types' => $this->getPackageTypes($target),\n        'patchLevel' => $source['extra']['patchLevel'] ?? [],\n        'patches' => $source['extra']['patches'] ?? [],\n        'patches-ignore' => $source['extra']['patches-ignore'] ?? [],\n      ],\n      'repositories' => $this->getRepositories($target),\n    ]);\n\n    // If the project requires Headless Lightning, we need to switch it to\n    // Headless Lightning 2 to ensure that the modules which are part of\n    // Headless Lightning will continue to be present after uninstall.\n    if (isset($data['require']['acquia/headless_lightning'])) {\n      $data['require']['acquia/headless_lightning'] = 'dev-eol';\n    }\n\n    // Delete any empty arrays, since they will be encoded as empty arrays and\n    // may therefore break the composer.json schema.\n    // @todo Handle this recursively.\n    $data = array_filter($data, function ($item) {\n      return is_array($item) ? (bool) $item : TRUE;\n    });\n\n    $file->write($data);\n  }\n\n  /**\n   * Returns the combined requirements for the target package.\n   *\n   * @param array $target\n   *   The target package's configuration.\n   * @param array $source\n   *   The source package's configuration.\n   *\n   * @return array\n   *   The combined requirements to add to the target package. The keys will\n   *   be package names and the values will be version constraints.\n   */\n  private function getRequirements(array $target, array $source) : array {\n    $requirements = [];\n    // Lightning requires composer/composer in order for this uninstaller to\n    // alter composer.json correctly. When that's done, we don't need it\n    // anymore.\n    unset($source['require']['composer/composer']);\n    // The target package's existing dependencies should supersede any\n    // dependencies defined by the source package (Lightning).\n    $requirements += ($target['require'] ?? []);\n    $requirements += ($source['require'] ?? []);\n\n    // If the target package is not using the deprecated scaffold plugin, use\n    // the one that ships with Drupal core. On the other hand, if the target\n    // package *is* using the deprecated plugin, they are on their own.\n    if (empty($requirements['drupal-composer/drupal-scaffold'])) {\n      $requirements += [\n        'drupal/core-composer-scaffold' => $requirements['drupal/core'],\n      ];\n    }\n    return $requirements;\n  }\n\n  /**\n   * Returns the package types to expose to the Composer installers extender.\n   *\n   * @param array $target\n   *   The target package's configuration.\n   *\n   * @return string[]\n   *   The package types to expose to the Composer installers extender plugin\n   *   (oomphinc/composer-installers-extender), if available.\n   */\n  private function getPackageTypes(array $target) : array {\n    $installer_types = $target['extra']['installer-types'] ?? [];\n\n    // Ensure that npm-asset and bower-asset are known package types.\n    array_push($installer_types, 'npm-asset', 'bower-asset');\n    return array_unique($installer_types);\n  }\n\n  /**\n   * Returns the combined installer paths for the target package.\n   *\n   * @param array $target\n   *   The target package's configuration.\n   * @param array $source\n   *   The source package's configuration.\n   *\n   * @return array[]\n   *   An array of paths to be used by the composer/installers plugin.\n   */\n  private function getPaths(array $target, array $source) : array {\n    $root_dir = $this->getDrupalRoot($target, $source);\n    // If we don't know where Drupal core is installed, we cannot possibly\n    // determine where modules, themes, etc. should go.\n    if (empty($root_dir)) {\n      throw new \\LogicException(\"Cannot determine the Drupal root.\");\n    }\n\n    $path_map = $this->getPathMap($target, $source);\n    $path_map += [\n      'type:drupal-module' => $root_dir . '/modules/contrib/{$name}',\n      'type:drupal-custom-module' => $root_dir . '/modules/custom/{$name}',\n      'type:drupal-profile' => $root_dir . '/profiles/contrib/{$name}',\n      'type:drupal-theme' => $root_dir . '/themes/contrib/{$name}',\n      'type:drupal-custom-theme' => $root_dir . '/themes/custom/{$name}',\n      'type:drupal-library' => $root_dir . '/libraries/{$name}',\n      'type:npm-asset' => $root_dir . '/libraries/{$name}',\n      'type:bower-asset' => $root_dir . '/libraries/{$name}',\n    ];\n    // If the target package uses Headless Lightning, it should be treated as\n    // a normal module.\n    if (isset($target['require']['acquia/headless_lightning'])) {\n      $path_map += [\n        'acquia/headless_lightning' => $path_map['type:drupal-module'],\n      ];\n    }\n\n    $paths = [];\n    foreach ($path_map as $package => $location) {\n      $paths[$location][] = $package;\n    }\n    return $paths;\n  }\n\n  /**\n   * Returns the combined repositories for the target package.\n   *\n   * @param array $target\n   *   The target package's configuration.\n   *\n   * @return array[]\n   *   An array of Composer repository definitions to add to the target package.\n   */\n  private function getRepositories(array $target) : array {\n    $repositories = [];\n\n    $source_repositories = [\n      'https://packages.drupal.org/8',\n      'https://asset-packagist.org',\n    ];\n\n    $target_repositories = [];\n    foreach (($target['repositories'] ?? []) as $repository) {\n      if ($repository['type'] === 'composer') {\n        $target_repositories[] = $repository['url'];\n      }\n    }\n\n    // Ensure that the two repositories listed in $source_repositories are\n    // added to the target package's repositories.\n    $repositories_to_add = array_diff($source_repositories, $target_repositories);\n\n    foreach ($repositories_to_add as $url) {\n      $repositories[] = [\n        'type' => 'composer',\n        'url' => $url,\n      ];\n    }\n    return $repositories;\n  }\n\n  /**\n   * Returns a map of locations where packages will be installed.\n   *\n   * @param array $target\n   *   The target package's configuration.\n   * @param array $source\n   *   The source package's configuration.\n   *\n   * @return string[]\n   *   A map where the keys are the package, or package type, to install (e.g.,\n   *   'drupal/dropzonejs' or 'type:drupal-theme') and the values are the\n   *   location where that package or package type will be installed, relative\n   *   to the target package.\n   */\n  private function getPathMap(array $target, array $source) : array {\n    // Try to get the installer-paths configuration from the target package,\n    // falling back to the source package (Lightning) in the unlikely event\n    // that the target package has not configured this.\n    $extra = isset($target['extra']['installer-paths'])\n      ? $target['extra']\n      : $source['extra'];\n\n    $path_map = [];\n    foreach ($extra['installer-paths'] as $location => $packages) {\n      foreach ($packages as $package) {\n        $path_map[$package] = $location;\n      }\n    }\n    return $path_map;\n  }\n\n  /**\n   * Returns the path to the Drupal root, relative to the target package.\n   *\n   * @param array $target\n   *   The target package's configuration.\n   * @param array $source\n   *   The source package (i.e., Lightning)'s configuration.\n   *\n   * @return string|null\n   *   The path to the Drupal root, relative to the target package, e.g.,\n   *   'docroot', or NULL if it cannot be determined.\n   */\n  private function getDrupalRoot(array $target, array $source) : ?string {\n    $path_map = $this->getPathMap($target, $source);\n\n    // We expect that the path map has an install location for Drupal core. If\n    // it doesn't, that's a pretty major error condition; in such a case, it's\n    // not clear how their code base could even be working. Maybe it's a bizarre\n    // set-up (symlink jungle?) that we don't support.\n    $core_location = $path_map['drupal/core'] ?? $path_map['type:drupal-core'];\n    return $core_location ? dirname($core_location) : NULL;\n  }\n\n  /**\n   * Recursively merges two associative arrays, preserving existing items.\n   *\n   * @param array $a\n   *   The array which $b will be merged into.\n   * @param array $b\n   *   The array to merge into $a.\n   *\n   * @return array\n   *   The merged array.\n   */\n  private function mergeCanadian(array $a, array $b) : array {\n    $a += $b;\n    foreach ($a as $k => $v) {\n      if (is_array($v) && isset($b[$k]) && is_array($b[$k])) {\n        $a[$k] = $this->mergeCanadian($a[$k], $b[$k]);\n      }\n    }\n    return $a;\n  }\n\n  /**\n   * Wrapper around ::say() which displays the text in bold.\n   *\n   * @param string $text\n   *   The text to display.\n   */\n  private function boldlySay(string $text) : void {\n    $this->writeln(\"<options=bold>$text</options=bold>\");\n  }\n\n}\n"
  },
  {
    "path": "src/Composer/AssetPackagist.php",
    "content": "<?php\n\nnamespace Acquia\\Lightning\\Composer;\n\nuse Composer\\Json\\JsonFile;\nuse Composer\\Script\\Event;\n\n/**\n * Adds Asset Packagist support to a composer.json.\n */\nfinal class AssetPackagist {\n\n  /**\n   * Reads the root package's composer.json.\n   *\n   * This will be the composer.json closest to the current working directory\n   * that contains a dependency on acquia/lightning.\n   *\n   * @return \\Composer\\Json\\JsonFile\n   *   File wrapper around the root package's composer.json.\n   */\n  protected static function getRootPackage() {\n    $file = new JsonFile('composer.json');\n\n    // Split the current working directory into an array, accounting for leading\n    // and trailing directory separators.\n    $dir = explode(DIRECTORY_SEPARATOR, trim(getcwd(), DIRECTORY_SEPARATOR));\n\n    do {\n      if ($file->exists()) {\n        $info = $file->read();\n\n        if (isset($info['require']['acquia/lightning'])) {\n          return $file;\n        }\n      }\n      chdir('..');\n      array_pop($dir);\n    } while ($dir);\n\n    throw new \\RuntimeException('Could not locate the root package.');\n  }\n\n  /**\n   * Executes the script.\n   *\n   * @param \\Composer\\Script\\Event $event\n   *   The script event.\n   */\n  public static function execute(Event $event) {\n    $io = $event->getIO();\n\n    // Search upwards for a composer.json which depends on acquia/lightning.\n    $io->write('Searching for root package...');\n\n    $file = static::getRootPackage();\n\n    $package = $file->read();\n\n    // Add the Asset Packagist repository if it does not already exist.\n    if (isset($package['repositories'])) {\n      $repository_key = NULL;\n\n      foreach ($package['repositories'] as $key => $repository) {\n        if ($repository['type'] == 'composer' && strpos($repository['url'], 'https://asset-packagist.org') === 0) {\n          $repository_key = $key;\n          break;\n        }\n      }\n\n      if (is_null($repository_key)) {\n        $package['repositories']['asset-packagist'] = [\n          'type' => 'composer',\n          'url' => 'https://asset-packagist.org',\n        ];\n      }\n    }\n\n    // oomphinc/composer-installers-extender is required by Lightning and\n    // depends on composer/installers, so it does not need to be specifically\n    // included.\n    unset(\n      $package['require']['composer/installers'],\n      $package['require']['oomphinc/composer-installers-extender']\n    );\n\n    $package['extra']['installer-types'][] = 'bower-asset';\n    $package['extra']['installer-types'][] = 'npm-asset';\n    $package['extra']['installer-paths']['docroot/libraries/{$name}'][] = 'type:bower-asset';\n    $package['extra']['installer-paths']['docroot/libraries/{$name}'][] = 'type:npm-asset';\n\n    $file->write($package);\n    $io->write('Successfully updated your root composer.json file. Switch back to your project root and run \"composer update\".');\n  }\n\n}\n"
  },
  {
    "path": "src/Composer/ConfigureLegacyProject.php",
    "content": "<?php\n\nnamespace Acquia\\Lightning\\Composer;\n\nuse Composer\\Json\\JsonFile;\nuse Composer\\Script\\Event;\n\n/**\n * Configures an instance of drupal/legacy-project to install Lightning.\n */\nfinal class ConfigureLegacyProject {\n\n  /**\n   * Executes the script.\n   *\n   * @param \\Composer\\Script\\Event $event\n   *   The script event.\n   */\n  public static function execute(Event $event) {\n    $arguments = $event->getArguments();\n\n    $target = new JsonFile($arguments[0] . '/composer.json');\n    $project = $target->read();\n\n    $project['extra']['installer-paths']['libraries/{$name}'] = [\n      'type:drupal-library',\n      'type:bower-asset',\n      'type:npm-asset',\n    ];\n    $project['extra']['installer-types'] = ['bower-asset', 'npm-asset'];\n    $project['extra']['patchLevel']['drupal/core'] = '-p2';\n    $project['extra']['enable-patching'] = TRUE;\n\n    // Composer doesn't like empty sections of composer.json, so\n    // filter those out before we write the configuration.\n    $project = array_filter($project);\n    $target->write($project);\n  }\n\n}\n"
  },
  {
    "path": "src/Composer/PatchedConstraint.php",
    "content": "<?php\n\nnamespace Acquia\\Lightning\\Composer;\n\nuse Composer\\Package\\Link;\nuse Composer\\Package\\RootPackageInterface;\nuse Composer\\Script\\Event;\n\n/**\n * Ensures that all patched dependencies are pinned to a specific version.\n */\nclass PatchedConstraint {\n\n  /**\n   * Script entry point.\n   *\n   * @param \\Composer\\Script\\Event $event\n   *   The script event.\n   *\n   * @return bool\n   *   False if there are unpinned patched dependencies.\n   */\n  public static function execute(Event $event) {\n    $root_package = $event->getComposer()->getPackage();\n    $patched_dependencies = static::getPatchedDependencyConstraints($root_package);\n    $error = [];\n\n    /** @var \\Composer\\Package\\Link $package */\n    foreach ($patched_dependencies as $package) {\n      if (static::packageIsUnpinned($package)) {\n        $error[] = $package->getTarget() . ': ' . $package->getPrettyConstraint();\n      }\n    }\n    if (!empty($error)) {\n      array_unshift($error, 'The following dependencies are patched but don\\'t have pinned dependency constraints:');\n      $event->getIO()->writeError($error);\n      return FALSE;\n    }\n    else {\n      $event->getIO()->write('Patched dependencies have constraints that are properly pinned.');\n    }\n  }\n\n  /**\n   * Filters the requires section to packages that are patched.\n   *\n   * @param \\Composer\\Package\\RootPackageInterface $root_package\n   *   The root composer.json package.\n   *\n   * @return \\Composer\\Package\\Link\n   *   List of required packages that are patched.\n   */\n  protected static function getPatchedDependencyConstraints(RootPackageInterface $root_package) {\n    $required = $root_package->getRequires();\n    $extra = $root_package->getExtra();\n    $patched = $extra['patches'];\n    return array_intersect_key($required, $patched);\n  }\n\n  /**\n   * Determines if a given package's constraint is pinned or not.\n   *\n   * @param \\Composer\\Package\\Link $package\n   *   The package to check.\n   *\n   * @return bool\n   *   True if the constraint appears to be unpinned.\n   */\n  protected static function packageIsUnpinned(Link $package) {\n    if ($package->getTarget() == 'drupal/core') {\n      // Bail out if the patched package is drupal/core since we release with\n      // each version of core and always ensure core patches still apply.\n      return FALSE;\n    }\n    $constraint = $package->getPrettyConstraint();\n    if (preg_match('/[\\^~*|]/', $constraint)) {\n      // If ^, ~, *, or | characters are being used, the dependency is not\n      // pinned to a specific release.\n      return TRUE;\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/Generators/SubProfileGenerator.php",
    "content": "<?php\n\nnamespace Drupal\\lightning\\Generators;\n\nuse Drupal\\Component\\Serialization\\Yaml;\nuse Drupal\\Core\\Extension\\ModuleExtensionList;\nuse DrupalCodeGenerator\\Command\\BaseGenerator;\nuse DrupalCodeGenerator\\Utils;\nuse Drush\\Style\\DrushStyle;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\nuse Symfony\\Component\\Console\\Question\\ChoiceQuestion;\nuse Symfony\\Component\\Console\\Question\\Question;\n\n/**\n * Implements a Lightning sub-profile generator for Drush.\n *\n * The generator will ask for a human-readable name, machine name, optional\n * description, and optional lists of modules to include in the generated\n * profile, and Lightning modules to exclude.\n */\nfinal class SubProfileGenerator extends BaseGenerator {\n\n  /**\n   * The module extension list service.\n   *\n   * @var \\Drupal\\Core\\Extension\\ModuleExtensionList\n   */\n  private $moduleList;\n\n  /**\n   * {@inheritdoc}\n   */\n  protected $name = 'lightning-subprofile';\n\n  /**\n   * {@inheritdoc}\n   */\n  protected $description = 'Generates a Lightning Subprofile.';\n\n  /**\n   * {@inheritdoc}\n   */\n  protected $alias = 'lsp';\n\n  /**\n   * {@inheritdoc}\n   */\n  protected $templatePath = __DIR__;\n\n  /**\n   * SubProfileGenerator constructor.\n   *\n   * @param \\Drupal\\Core\\Extension\\ModuleExtensionList $module_list\n   *   The module extension list service.\n   */\n  public function __construct(ModuleExtensionList $module_list) {\n    parent::__construct();\n    $this->moduleList = $module_list;\n\n    // Drush doesn't support setting $this->destination to 'profiles/custom',\n    // so we need to use a callback function instead. This is ignored if\n    // the --directory option is set; in that case, the profile will be\n    // generated at $custom_directory/$machine_name.\n    // @see \\Drush\\Commands\\generate\\Helper\\InputHandler::collectVars()\n    // @see \\Drush\\Commands\\generate\\Helper\\InputHandler::getDirectory()\n    $this->setDestination(function () {\n      return 'profiles/custom';\n    });\n  }\n\n  /**\n   * {@inheritdoc}\n   */\n  protected function interact(InputInterface $input, OutputInterface $output) {\n    $io = new DrushStyle($input, $output);\n\n    $questions['name'] = new Question('Profile Name');\n    $questions['name']->setValidator([Utils::class, 'validateRequired']);\n    $questions['machine_name'] = new Question('Profile Machine Name (enter for default)');\n    $questions['machine_name']->setValidator([Utils::class, 'validateMachineName']);\n    $questions['description'] = new Question('Enter the description (optional)');\n    $questions['install'] = new Question('Additional modules to include (optional), separated by commas (e.g. context, rules, file_entity)', NULL);\n    $questions['install']->setNormalizer([static::class, 'toArray']);\n\n    $vars = &$this->collectVars($input, $output, $questions);\n\n    if ($io->confirm('Do you want to exclude any components of Lightning?', FALSE)) {\n      $filter = function ($name) {\n        return strpos($name, 'lightning_') === 0;\n      };\n      $modules = array_filter($this->moduleList->getAllAvailableInfo(), $filter, ARRAY_FILTER_USE_KEY);\n\n      $map = function (array $info) {\n        return $info['name'];\n      };\n      $modules = array_map($map, $modules);\n\n      $questions['exclude'] = new ChoiceQuestion('Lightning components to exclude (optional), entered as keys separated by commas (e.g. lightning_media, lightning_layout)', $modules);\n      $questions['exclude']->setMultiselect(TRUE);\n\n      $this->collectVars($input, $output, $questions);\n    }\n\n    $info_array = [\n      'name' => $vars['name'],\n      'type' => 'profile',\n      'description' => '',\n      'core_version_requirement' => '^8.8 || ^9',\n      'install' => [],\n      'themes' => [\n        'bartik', 'seven',\n      ],\n      'base profile' => 'lightning',\n      'exclude' => [],\n    ];\n\n    if ($vars['description']) {\n      $info_array['description'] = $vars['description'];\n    }\n    if ($vars['install']) {\n      $info_array['install'] = $vars['install'];\n    }\n    if ($vars['exclude']) {\n      $info_array['exclude'] = is_string($vars['exclude'])\n        ? static::toArray($vars['exclude'])\n        : $vars['exclude'];\n    }\n\n    $info_array = array_filter($info_array);\n\n    $this->addFile()\n      ->path(\"{machine_name}/{machine_name}.info.yml\")\n      ->content(Yaml::encode($info_array));\n\n    $this->addFile()\n      ->path(\"{machine_name}/{machine_name}.install\")\n      ->template('install.twig');\n\n    $this->addFile()\n      ->path(\"{machine_name}/{machine_name}.profile\")\n      ->template('profile.twig');\n  }\n\n  /**\n   * Converts a comma-separated string to an array of trimmed values.\n   *\n   * @param string $string\n   *   The comma-separated string to split up.\n   *\n   * @return string[]\n   *   The comma-separated values, trimmed of white space.\n   */\n  public static function toArray($string) {\n    return $string ? array_map('trim', explode(',', $string)) : [];\n  }\n\n}\n"
  },
  {
    "path": "src/Generators/install.twig",
    "content": "<?php\n\n/**\n * @file\n * Install, update and uninstall hooks for the {{ name }} subprofile.\n */\n\n/**\n * Implements hook_install().\n */\nfunction {{ machine_name }}_install() {\n  // Add code here perform additional site information tasks, generate sample\n  // content, etc.\n}\n"
  },
  {
    "path": "src/Generators/profile.twig",
    "content": "<?php\n\n/**\n * @file\n * Enables modules and site configuration for the {{ name }} profile.\n */\n\n// Add any custom code here, like hook implementations.\n"
  },
  {
    "path": "src/LightningServiceProvider.php",
    "content": "<?php\n\nnamespace Drupal\\lightning;\n\nuse Drupal\\Core\\DependencyInjection\\ContainerBuilder;\nuse Drupal\\Core\\DependencyInjection\\ServiceProviderBase;\n\n/**\n * Modifies container services.\n *\n * @internal\n *   This class is a completely internal part of Lightning's uninstall system\n *   and can be changed in any way, or removed outright, at any time without\n *   warning. External code should not use this class in any way.\n */\nfinal class LightningServiceProvider extends ServiceProviderBase {\n\n  /**\n   * {@inheritdoc}\n   */\n  public function alter(ContainerBuilder $container) {\n    $container->getDefinition('required_module_uninstall_validator')\n      ->setClass(RequiredModuleUninstallValidator::class);\n  }\n\n}\n"
  },
  {
    "path": "src/ProxyClass/RequiredModuleUninstallValidator.php",
    "content": "<?php\n// @codingStandardsIgnoreFile\n\n/**\n * This file was generated via php core/scripts/generate-proxy-class.php 'Drupal\\lightning\\RequiredModuleUninstallValidator' \"profiles/contrib/lightning/src\".\n */\n\nnamespace Drupal\\lightning\\ProxyClass {\n\n    /**\n     * Provides a proxy class for \\Drupal\\lightning\\RequiredModuleUninstallValidator.\n     *\n     * @see \\Drupal\\Component\\ProxyBuilder\n     */\n    class RequiredModuleUninstallValidator implements \\Drupal\\Core\\Extension\\ModuleUninstallValidatorInterface\n    {\n\n        use \\Drupal\\Core\\DependencyInjection\\DependencySerializationTrait;\n\n        /**\n         * The id of the original proxied service.\n         *\n         * @var string\n         */\n        protected $drupalProxyOriginalServiceId;\n\n        /**\n         * The real proxied service, after it was lazy loaded.\n         *\n         * @var \\Drupal\\lightning\\RequiredModuleUninstallValidator\n         */\n        protected $service;\n\n        /**\n         * The service container.\n         *\n         * @var \\Symfony\\Component\\DependencyInjection\\ContainerInterface\n         */\n        protected $container;\n\n        /**\n         * Constructs a ProxyClass Drupal proxy object.\n         *\n         * @param \\Symfony\\Component\\DependencyInjection\\ContainerInterface $container\n         *   The container.\n         * @param string $drupal_proxy_original_service_id\n         *   The service ID of the original service.\n         */\n        public function __construct(\\Symfony\\Component\\DependencyInjection\\ContainerInterface $container, $drupal_proxy_original_service_id)\n        {\n            $this->container = $container;\n            $this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id;\n        }\n\n        /**\n         * Lazy loads the real service from the container.\n         *\n         * @return object\n         *   Returns the constructed real service.\n         */\n        protected function lazyLoadItself()\n        {\n            if (!isset($this->service)) {\n                $this->service = $this->container->get($this->drupalProxyOriginalServiceId);\n            }\n\n            return $this->service;\n        }\n\n        /**\n         * {@inheritdoc}\n         */\n        public function validate($module)\n        {\n            return $this->lazyLoadItself()->validate($module);\n        }\n\n        /**\n         * {@inheritdoc}\n         */\n        public function setStringTranslation(\\Drupal\\Core\\StringTranslation\\TranslationInterface $translation)\n        {\n            return $this->lazyLoadItself()->setStringTranslation($translation);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "src/RequiredModuleUninstallValidator.php",
    "content": "<?php\n\nnamespace Drupal\\lightning;\n\nuse Drupal\\Core\\Extension\\RequiredModuleUninstallValidator as BaseValidator;\n\n/**\n * Validates module uninstallation.\n *\n * @internal\n *   This class is a completely internal part of Lightning's uninstall system\n *   and can be changed in any way, or removed outright, at any time without\n *   warning. External code should not use this class in any way.\n */\nfinal class RequiredModuleUninstallValidator extends BaseValidator {\n\n  /**\n   * {@inheritdoc}\n   */\n  protected function getModuleInfoByModule($module) {\n    $info = parent::getModuleInfoByModule($module);\n    if ($module === 'lightning' || $module === 'headless_lightning') {\n      $info['required'] = FALSE;\n    }\n    return $info;\n  }\n\n}\n"
  },
  {
    "path": "src/Update/Update320.php",
    "content": "<?php\n\nnamespace Drupal\\lightning\\Update;\n\nuse Drupal\\Core\\DependencyInjection\\ContainerInjectionInterface;\nuse Drupal\\Core\\Extension\\ExtensionDiscovery;\nuse Drupal\\Core\\Serialization\\Yaml;\nuse Drupal\\Core\\StringTranslation\\StringTranslationTrait;\nuse Drupal\\Core\\StringTranslation\\TranslationInterface;\nuse Symfony\\Component\\Console\\Style\\StyleInterface;\nuse Symfony\\Component\\DependencyInjection\\ContainerInterface;\n\n/**\n * Contains optional updates targeting Lightning 3.2.0.\n *\n * @Update(\"3.2.0\")\n */\nfinal class Update320 implements ContainerInjectionInterface {\n\n  use StringTranslationTrait;\n\n  /**\n   * The Drupal application root.\n   *\n   * @var string\n   */\n  protected $appRoot;\n\n  /**\n   * Update320 constructor.\n   *\n   * @param string $app_root\n   *   The Drupal application root.\n   * @param \\Drupal\\Core\\StringTranslation\\TranslationInterface $translation\n   *   (optional) The string translation service.\n   */\n  public function __construct($app_root, TranslationInterface $translation = NULL) {\n    $this->appRoot = $app_root;\n\n    if ($translation) {\n      $this->setStringTranslation($translation);\n    }\n  }\n\n  /**\n   * {@inheritdoc}\n   */\n  public static function create(ContainerInterface $container) {\n    return new static(\n      $container->get('app.root'),\n      $container->get('string_translation')\n    );\n  }\n\n  /**\n   * Converts sub-profile info keys to the 8.6.x API.\n   *\n   * @param \\Symfony\\Component\\Console\\Style\\StyleInterface $io\n   *   The I/O handler.\n   *\n   * @update\n   *\n   * @ask Do you want to update all sub-profiles to be Drupal 8.6 compatible?\n   */\n  public function updateProfiles(StyleInterface $io) {\n    $discovery = new ExtensionDiscovery($this->appRoot);\n\n    $profiles = $discovery->scan('profile');\n    foreach ($profiles as $profile) {\n      $info_file = $profile->getPathname();\n\n      if (is_writable($info_file)) {\n        $info = file_get_contents($info_file);\n\n        if (strstr($info, 'base profile:')) {\n          $info = Yaml::decode($info);\n\n          if (is_array($info['base profile'])) {\n            $info['base profile'] = $info['base profile']['name'];\n          }\n          if (isset($info['dependencies'])) {\n            $info['install'] = $info['dependencies'];\n            unset($info['dependencies']);\n          }\n\n          $exclude = [];\n          if (isset($info['excluded_dependencies'])) {\n            $exclude = array_merge($exclude, $info['excluded_dependencies']);\n            unset($info['excluded_dependencies']);\n          }\n          if (isset($info['excluded_themes'])) {\n            $exclude = array_merge($exclude, $info['excluded_themes']);\n            unset($info['excluded_themes']);\n          }\n          if ($exclude) {\n            $info['exclude'] = $exclude;\n          }\n          file_put_contents($info_file, Yaml::encode($info));\n\n          $message = $this->t('Updated @profile.', [\n            '@profile' => $profile->getName(),\n          ]);\n          $io->success((string) $message);\n        }\n      }\n      else {\n        $message = $this->t('Cannot write to @path, skipping.', [\n          '@path' => $info_file,\n        ]);\n        $io->warning((string) $message);\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/Update/Update405.php",
    "content": "<?php\n\nnamespace Drupal\\lightning\\Update;\n\nuse Drupal\\Core\\DependencyInjection\\ContainerInjectionInterface;\nuse Drupal\\Core\\Extension\\ModuleInstallerInterface;\nuse Symfony\\Component\\DependencyInjection\\ContainerInterface;\n\n/**\n * Contains optional updates targeting Lightning 4.0.5.\n *\n * @Update(\"4.0.5\")\n */\nfinal class Update405 implements ContainerInjectionInterface {\n\n  /**\n   * The module installer.\n   *\n   * @var \\Drupal\\Core\\Extension\\ModuleInstallerInterface\n   */\n  private $moduleInstaller;\n\n  /**\n   * Update405 constructor.\n   *\n   * @param \\Drupal\\Core\\Extension\\ModuleInstallerInterface $module_installer\n   *   The module installer.\n   */\n  public function __construct(ModuleInstallerInterface $module_installer) {\n    $this->moduleInstaller = $module_installer;\n  }\n\n  /**\n   * {@inheritdoc}\n   */\n  public static function create(ContainerInterface $container) {\n    return new static(\n      $container->get('module_installer')\n    );\n  }\n\n  /**\n   * Enables the Autosave Form and Conflict modules.\n   *\n   * @update\n   *\n   * @ask Do you want to enable the Autosave Form and Conflict modules?\n   */\n  public function enableAutosaveForm() {\n    $this->moduleInstaller->install(['autosave_form', 'conflict']);\n  }\n\n  /**\n   * Enables the Redirect module.\n   *\n   * @update\n   *\n   * @ask Do you want to enable the Redirect module?\n   */\n  public function enableRedirect() {\n    $this->moduleInstaller->install(['redirect']);\n  }\n\n}\n"
  },
  {
    "path": "tarball.sh",
    "content": "#!/bin/bash\n\nARCHIVE=lightning-$1\n\ncomposer create-project --stability beta --no-install drupal/legacy-project:~9.1.0 $ARCHIVE\ncomposer dump-autoload\ncomposer configure-tarball $ARCHIVE\n\ncd $ARCHIVE\ncomposer config minimum-stability dev\ncomposer config prefer-stable true\ncomposer config repositories.assets composer https://asset-packagist.org\ncomposer remove --no-update composer/installers\ncomposer require --no-update \"ext-dom:*\" \"acquia/lightning:~5.1.0\" cweagans/composer-patches\ncomposer update\n\n# Add the version number to the info file.\necho \"version: $1\" >> ./profiles/contrib/lightning/lightning.info.yml\n\n# Wrap it all up in a nice compressed tarball.\ncd ..\ntar --exclude='.DS_Store' --exclude='._*' -c -z -f $ARCHIVE.tar.gz $ARCHIVE\n\n# Clean up.\nrm -r -f $ARCHIVE\n"
  },
  {
    "path": "tests/lightning_extender/lightning_extender.info.yml",
    "content": "name: 'Lightning Extender'\ntype: profile\ncore_version_requirement: '^8.8 || ^9'\ninstall:\n  - ban\nthemes:\n  - bartik\n  - seven\n'base profile': lightning\nexclude:\n  - lightning_search\n"
  },
  {
    "path": "tests/lightning_extender/lightning_extender.profile",
    "content": "<?php\n\n/**\n * @file\n * Enables modules and site configuration for the Lightning Extender profile.\n */\n\n// Add any custom code here, like hook implementations.\n"
  },
  {
    "path": "tests/packages.yml",
    "content": "---\nacquia/lightning:\n  type: drupal-profile\n\ndrupal/lightning_api:\n  version: 4.x\n  version_dev: 4.x-dev\n\ndrupal/lightning_core:\n  version: 5.x\n  version_dev: 5.x-dev\n\ndrupal/lightning_layout:\n  version: 2.x\n  version_dev: 2.x-dev\n\ndrupal/lightning_media:\n  version: 4.x\n  version_dev: 4.x-dev\n\ndrupal/lightning_workflow:\n  version: 3.x\n  version_dev: 3.x-dev\n"
  },
  {
    "path": "tests/packages_alter.yml",
    "content": "---\nacquia/lightning:\n  type: drupal-profile\n\n# Remove drupal/acquia_search because it depends on the\n# search_api_solr module, which is not yet compatible with\n# Drupal 9 and therefore breaks the ORCA fixture build.\ndrupal/acquia_search: ~\n"
  },
  {
    "path": "tests/src/ExistingSite/ApiTest.php",
    "content": "<?php\n\nnamespace Drupal\\Tests\\lightning\\ExistingSite;\n\nuse Drupal\\Component\\Serialization\\Yaml;\nuse weitzman\\DrupalTestTraits\\ExistingSiteBase;\n\n/**\n * Tests the decoupled API shipped with the Lightning profile.\n *\n * @group lightning\n */\nclass ApiTest extends ExistingSiteBase {\n\n  /**\n   * {@inheritdoc}\n   */\n  protected function setUp() {\n    parent::setUp();\n\n    /** @var \\Drupal\\Core\\Config\\ConfigFactoryInterface $config_factory */\n    $config_factory = $this->container->get('config.factory');\n    $config_factory->getEditable('lightning_api.settings')\n      ->set('entity_json', TRUE)\n      ->save();\n\n    // If the samlauth module is installed, ensure that it is configured (in\n    // this case, using its own test data) to avoid errors when creating user\n    // accounts in this test.\n    if ($this->container->get('module_handler')->moduleExists('samlauth')) {\n      $path = $this->container->get('extension.list.module')\n        ->getPath('samlauth');\n      $data = file_get_contents(\"$path/test_resources/samlauth.authentication.yml\");\n      $data = Yaml::decode($data);\n\n      $this->container->get('config.factory')\n        ->getEditable('samlauth.authentication')\n        ->setData($data)\n        ->save();\n    }\n  }\n\n  /**\n   * Tests viewing a configuration entity as JSON via the API.\n   */\n  public function testViewConfigEntityAsJson() {\n    $assert_session = $this->assertSession();\n    $page = $this->getSession()->getPage();\n\n    $account = $this->createUser([], NULL, TRUE);\n    $this->drupalLogin($account);\n\n    $this->drupalGet('/admin/structure/contact');\n    $page->clickLink('View JSON');\n    $assert_session->statusCodeEquals(200);\n\n    $this->drupalGet('/admin/structure/media');\n    $page->clickLink('View JSON');\n    $assert_session->statusCodeEquals(200);\n  }\n\n}\n"
  },
  {
    "path": "tests/src/ExistingSite/ConfigIntegrityTest.php",
    "content": "<?php\n\nnamespace Drupal\\Tests\\lightning\\ExistingSite;\n\nuse Drupal\\Component\\Serialization\\Yaml;\nuse Drupal\\Core\\Entity\\Entity\\EntityViewMode;\nuse Drupal\\user\\Entity\\Role;\nuse Drupal\\user\\UserInterface;\nuse Drupal\\views\\Entity\\View;\nuse Drupal\\workflows\\Entity\\Workflow;\nuse weitzman\\DrupalTestTraits\\ExistingSiteBase;\n\n/**\n * Ensures the integrity and correctness of Lightning's bundled config.\n *\n * @group lightning\n */\nclass ConfigIntegrityTest extends ExistingSiteBase {\n\n  /**\n   * {@inheritdoc}\n   */\n  protected function setUp() {\n    parent::setUp();\n\n    // If the samlauth module is installed, ensure that it is configured (in\n    // this case, using its own test data) to avoid errors when creating user\n    // accounts in this test.\n    if ($this->container->get('module_handler')->moduleExists('samlauth')) {\n      $path = $this->container->get('extension.list.module')\n        ->getPath('samlauth');\n      $data = file_get_contents(\"$path/test_resources/samlauth.authentication.yml\");\n      $data = Yaml::decode($data);\n\n      $this->container->get('config.factory')\n        ->getEditable('samlauth.authentication')\n        ->setData($data)\n        ->save();\n    }\n  }\n\n  /**\n   * Tests configuration values were set correctly during installation.\n   */\n  public function testConfig() {\n    $assert_session = $this->assertSession();\n\n    // Assert that all install tasks have done what they should do.\n    // @see lightning_install_tasks()\n    $this->assertSame('/node', $this->config('system.site')->get('page.front'));\n    $this->assertSame(UserInterface::REGISTER_ADMINISTRATORS_ONLY, $this->config('user.settings')->get('register'));\n    $this->assertTrue(Role::load(Role::AUTHENTICATED_ID)->hasPermission('access shortcuts'));\n    $theme_config = $this->config('system.theme');\n    $this->assertSame('bartik', $theme_config->get('default'));\n    $this->assertSame('claro', $theme_config->get('admin'));\n    $this->assertTrue($this->config('node.settings')->get('use_admin_theme'));\n    $theme_global = $this->config('system.theme.global');\n    $this->assertStringContainsString('/lightning/lightning.png', $theme_global->get('logo.path'));\n    $this->assertStringContainsString('/lightning/favicon.ico', $theme_global->get('favicon.path'));\n    /** @var \\Drupal\\views\\ViewEntityInterface $view */\n    $view = View::load('frontpage');\n    $this->assertInstanceOf(View::class, $view);\n    $display = &$view->getDisplay('default');\n    $this->assertTrue($display['display_options']['empty']['area_text_custom']['tokenize']);\n    $this->assertStringContainsString('/lightning/README.md', $display['display_options']['empty']['area_text_custom']['content']);\n\n    // lightning_core_update_8002() marks a couple of core view modes as\n    // internal.\n    $view_modes = EntityViewMode::loadMultiple([\n      'node.rss',\n      'node.search_index',\n    ]);\n    /** @var \\Drupal\\Core\\Entity\\EntityViewModeInterface $view_mode */\n    foreach ($view_modes as $view_mode) {\n      $this->assertTrue($view_mode->getThirdPartySetting('lightning_core', 'internal'));\n    }\n\n    // All users should be able to view media items.\n    $this->assertPermissions('anonymous', 'view media');\n    $this->assertPermissions('authenticated', 'view media');\n    // Media creators can use bulk upload.\n    $this->assertPermissions('media_creator', 'dropzone upload files');\n\n    $this->assertEntityExists('node_type', [\n      'page',\n      'landing_page',\n    ]);\n    $this->assertEntityExists('user_role', [\n      'landing_page_creator',\n      'landing_page_reviewer',\n      'layout_manager',\n      'media_creator',\n      'media_manager',\n      'page_creator',\n      'page_reviewer',\n    ]);\n    $this->assertEntityExists('crop_type', 'freeform');\n    $this->assertEntityExists('image_style', 'crop_freeform');\n\n    // Assert that the editorial workflow exists and has the review state and\n    // transition.\n    $workflow = Workflow::load('editorial');\n    $this->assertInstanceOf(Workflow::class, $workflow);\n    /** @var \\Drupal\\workflows\\WorkflowTypeInterface $type_plugin */\n    $type_plugin = $workflow->getTypePlugin();\n    // getState() throws an exception if the state does not exist.\n    $type_plugin->getState('review');\n    // getTransition() throws an exception if the transition does not exist.\n    /** @var \\Drupal\\workflows\\TransitionInterface $transition */\n    $transition = $type_plugin->getTransition('review');\n    $this->assertEquals('review', $transition->to()->id());\n    $from = array_keys($transition->from());\n    $this->assertContainsAll(['draft', 'review'], $from);\n    $this->assertNotContains('published', $from);\n\n    $creator_permissions = [\n      'use text format rich_text',\n      'access image_browser entity browser pages',\n    ];\n    $this->assertPermissions('page_creator', $creator_permissions);\n    $this->assertPermissions('landing_page_creator', $creator_permissions);\n    $this->assertPermissions('layout_manager', [\n      'administer node display',\n      'configure any layout',\n    ]);\n\n    $node_types = \\Drupal::entityQuery('node_type')->execute();\n\n    foreach ($node_types as $node_type) {\n      $this->assertPermissions(\"{$node_type}_creator\", [\n        \"create $node_type content\",\n        \"edit own $node_type content\",\n        \"view $node_type revisions\",\n        'view own unpublished content',\n        'create url aliases',\n        'access in-place editing',\n        'access contextual links',\n        'access toolbar',\n      ]);\n      $this->assertPermissions(\"{$node_type}_reviewer\", [\n        'access content overview',\n        \"edit any $node_type content\",\n        \"delete any $node_type content\",\n      ]);\n    }\n\n    // Assert that bundled content types have meta tags enabled.\n    $this->assertMetatag(['page', 'landing_page']);\n\n    // Assert that Lightning configuration pages are accessible to users who\n    // have an administrative role.\n    $this->assertForbidden('/admin/config/system/lightning');\n    $this->assertForbidden('/admin/config/system/lightning/api');\n    $this->assertForbidden('/admin/config/system/lightning/layout');\n    $this->assertForbidden('/admin/config/system/lightning/media');\n\n    $account = $this->createUser([], NULL, TRUE);\n    $this->drupalLogin($account);\n\n    $this->assertAllowed('/admin/config/system/lightning');\n    $assert_session->linkByHrefExists('/admin/config/system/lightning/api');\n    $assert_session->linkByHrefExists('/admin/config/system/lightning/layout');\n    $assert_session->linkByHrefExists('/admin/config/system/lightning/media');\n    $this->assertAllowed('/admin/config/system/lightning/api');\n    $this->assertAllowed('/admin/config/system/lightning/api/keys');\n    $this->assertAllowed('/admin/config/system/lightning/layout');\n    $this->assertAllowed('/admin/config/system/lightning/media');\n  }\n\n  /**\n   * Data provider for testModeratedContentTypes().\n   *\n   * @return array[]\n   *   Sets of arguments to pass to the test method.\n   */\n  public function providerModeratedContentTypes() {\n    return [\n      ['page', 'page_creator'],\n      ['page', 'administrator'],\n      ['landing_page', 'landing_page_creator'],\n      ['landing_page', 'administrator'],\n    ];\n  }\n\n  /**\n   * Tests that moderated content types do not show a Published checkbox.\n   *\n   * @param string $node_type\n   *   The machine name of the content type to test.\n   * @param string $role\n   *   The machine name of the user role to log in with.\n   *\n   * @dataProvider providerModeratedContentTypes\n   */\n  public function testModeratedContentTypes($node_type, $role) {\n    $assert_session = $this->assertSession();\n\n    $account = $this->createUser();\n    $account->addRole($role);\n    $account->save();\n\n    $this->drupalLogin($account);\n    $this->drupalGet(\"/node/add/$node_type\");\n    $assert_session->statusCodeEquals(200);\n    $assert_session->buttonExists('Save');\n    $assert_session->fieldNotExists('status[value]');\n    $assert_session->buttonNotExists('Save and publish');\n    $assert_session->buttonNotExists('Save as unpublished');\n  }\n\n  /**\n   * Asserts that meta tags are enabled for specific content types.\n   *\n   * @param string[] $node_types\n   *   The node type IDs to check.\n   */\n  private function assertMetatag(array $node_types) {\n    $assert = $this->assertSession();\n\n    $permissions = array_map(\n      function ($node_type) {\n        return \"create $node_type content\";\n      },\n      $node_types\n    );\n    $account = $this->createUser($permissions);\n    $this->drupalLogin($account);\n\n    foreach ($node_types as $node_type) {\n      $this->assertAllowed(\"/node/add/$node_type\");\n      $assert->fieldExists('field_meta_tags[0][basic][title]');\n      $assert->fieldExists('field_meta_tags[0][basic][description]');\n    }\n    $this->drupalLogout();\n  }\n\n  /**\n   * Asserts the existence of an entity.\n   *\n   * @param string $entity_type\n   *   The entity type ID.\n   * @param mixed|mixed[] $id\n   *   The entity ID, or a set of IDs.\n   */\n  private function assertEntityExists($entity_type, $id) {\n    $this->assertContainsAll(\n      (array) $id,\n      \\Drupal::entityQuery($entity_type)->execute()\n    );\n  }\n\n  /**\n   * Asserts that a user role has a set of permissions.\n   *\n   * @param \\Drupal\\user\\RoleInterface|string $role\n   *   The user role, or its ID.\n   * @param string|string[] $permissions\n   *   The permission(s) to check.\n   */\n  private function assertPermissions($role, $permissions) {\n    if (is_string($role)) {\n      $role = Role::load($role);\n    }\n    $this->assertContainsAll((array) $permissions, $role->getPermissions());\n  }\n\n  /**\n   * Asserts that a haystack contains a set of needles.\n   *\n   * @param mixed[] $needles\n   *   The needles expected to be in the haystack.\n   * @param mixed[] $haystack\n   *   The haystack.\n   */\n  private function assertContainsAll(array $needles, array $haystack) {\n    $diff = array_diff($needles, $haystack);\n    $this->assertSame([], $diff);\n  }\n\n  /**\n   * Asserts that the current user can access a Drupal route.\n   *\n   * @param string $path\n   *   The route path to visit.\n   */\n  private function assertAllowed($path) {\n    $this->drupalGet($path);\n    $this->assertSession()->statusCodeEquals(200);\n  }\n\n  /**\n   * Asserts that the current user cannot access a Drupal route.\n   *\n   * @param string $path\n   *   The route path to visit.\n   */\n  private function assertForbidden($path) {\n    $this->drupalGet($path);\n    $this->assertSession()->statusCodeEquals(403);\n  }\n\n  /**\n   * Returns a config object by its name.\n   *\n   * @param string $name\n   *   The name of the config object to return.\n   *\n   * @return \\Drupal\\Core\\Config\\Config\n   *   The config object.\n   */\n  private function config($name) {\n    return $this->container->get('config.factory')->getEditable($name);\n  }\n\n}\n"
  },
  {
    "path": "tests/src/ExistingSite/ViewModeTest.php",
    "content": "<?php\n\nnamespace Drupal\\Tests\\lightning\\ExistingSite;\n\nuse Drupal\\Core\\Entity\\Entity\\EntityViewMode;\nuse weitzman\\DrupalTestTraits\\ExistingSiteBase;\n\n/**\n * Tests specialized handling for view modes.\n *\n * @group lightning\n */\nclass ViewModeTest extends ExistingSiteBase {\n\n  /**\n   * {@inheritdoc}\n   */\n  protected function setUp() {\n    parent::setUp();\n\n    /** @var \\Drupal\\Core\\Entity\\EntityViewModeInterface $view_mode */\n    $view_mode = EntityViewMode::create([\n      'id' => 'node.foobaz',\n      'label' => 'Foobaz',\n      'targetEntityType' => 'node',\n    ]);\n    $view_mode\n      ->setThirdPartySetting('lightning_core', 'internal', TRUE)\n      ->setThirdPartySetting('lightning_core', 'description', 'Behold, my glorious view mode.')\n      ->save();\n  }\n\n  /**\n   * Tests that users are informed about internal view modes.\n   */\n  public function testInternalViewMode() {\n    $assert_session = $this->assertSession();\n    $page = $this->getSession()->getPage();\n\n    $account = $this->createUser(['administer node display']);\n    $this->drupalLogin($account);\n\n    $this->drupalGet('/admin/structure/types/manage/page/display');\n    $page->checkField('Foobaz');\n    $page->pressButton('Save');\n    $assert_session->elementTextContains('css', '.messages--status', 'The Foobaz mode now uses custom display settings.');\n    $page->find('css', '.messages--status')->clickLink('configure them');\n    $assert_session->elementTextContains('css', '.messages--warning', \"This display is internal and will not be seen by normal users.\");\n    $assert_session->pageTextContains('Behold, my glorious view mode.');\n  }\n\n  /**\n   * {@inheritdoc}\n   */\n  public function tearDown() {\n    EntityViewMode::load('node.foobaz')->delete();\n    parent::tearDown();\n  }\n\n}\n"
  },
  {
    "path": "tests/src/Functional/LightningTest.php",
    "content": "<?php\n\nnamespace Drupal\\Tests\\lightning\\Functional;\n\nuse Drupal\\Tests\\BrowserTestBase;\nuse Drupal\\user\\UserInterface;\n\n/**\n * Tests integrated functionality of the Lightning profile.\n *\n * @group lightning\n * @group orca_public\n */\nclass LightningTest extends BrowserTestBase {\n\n  /**\n   * {@inheritdoc}\n   */\n  protected $defaultTheme = 'stark';\n\n  /**\n   * {@inheritdoc}\n   */\n  protected static $configSchemaCheckerExclusions = [\n    // @todo Remove this when depending on slick_entityreference 1.2 or later.\n    'core.entity_view_display.block_content.media_slideshow.default',\n    // @todo Remove when requiring Lightning Layout 2.2 or later.\n    'core.entity_view_display.block_content.banner.default',\n  ];\n\n  /**\n   * {@inheritdoc}\n   */\n  protected $profile = 'lightning_extender';\n\n  /**\n   * {@inheritdoc}\n   */\n  protected function setUp() {\n    $root = static::getDrupalRoot();\n    $this->assertNotEmpty($root);\n\n    // Symlink the sub-profile into a place where Drupal will be able to find\n    // it. The symlink is deleted in tearDown(). If the symlink cannot be\n    // created, fail the test.\n    $target = __DIR__ . '/../../' . $this->profile;\n    $this->assertDirectoryIsReadable($target);\n\n    $link = \"$root/profiles/$this->profile\";\n    $this->assertDirectoryIsWritable(dirname($link));\n\n    // symlink() is called strangely in order to evade a too-strict coding\n    // standards check.\n    $success = call_user_func('symlink', $target, $link);\n    $this->assertTrue($success, \"Could not symlink $link to $target\");\n    $this->assertDirectoryIsReadable($target);\n\n    parent::setUp();\n  }\n\n  /**\n   * {@inheritdoc}\n   */\n  protected function tearDown() {\n    unlink(\"$this->root/profiles/$this->profile\");\n    parent::tearDown();\n  }\n\n  /**\n   * Tests integrated functionality of the Lightning profile.\n   *\n   * Because it takes aeons to install the Lightning profile, or any of its\n   * descendants, this test only has one public test method, with private helper\n   * methods covering specific test scenarios. This is done purely for\n   * performance reasons.\n   */\n  public function testLightning() {\n    // Test that the sub-profile was installed...\n    $this->assertSame('lightning_extender', $this->container->getParameter('install_profile'));\n\n    $module_list = $this->container->get('extension.list.module')->getAllInstalledInfo();\n    // ...and that the changes it makes are reflected in the system.\n    $this->assertArrayHasKey('ban', $module_list);\n    $this->assertArrayNotHasKey('lightning_search', $module_list);\n\n    // Lightning's configuration overrides should have taken effect.\n    $this->assertSame(UserInterface::REGISTER_ADMINISTRATORS_ONLY, $this->config('user.settings')->get('register'));\n\n    $this->doModerationDashboardTest();\n    $this->doTextBlockTest();\n  }\n\n  /**\n   * Tests that the moderation dashboard works.\n   */\n  private function doModerationDashboardTest() {\n    $account = $this->drupalCreateUser(['use moderation dashboard']);\n    $this->drupalLogin($account);\n    $this->drupalGet('/user/' . $account->id() . '/moderation/dashboard');\n    $this->assertSession()->statusCodeEquals(200);\n\n    $this->drupalLogout();\n  }\n\n  /**\n   * Tests the 'text' custom block type that ships with Lightning.\n   */\n  private function doTextBlockTest() {\n    $assert_session = $this->assertSession();\n\n    // Assert that basic blocks expose a Body field.\n    $account = $this->createUser(['administer blocks']);\n    $this->drupalLogin($account);\n\n    $this->drupalGet('/block/add/text');\n    $assert_session->statusCodeEquals(200);\n    $assert_session->fieldExists('Body');\n\n    $this->drupalLogout();\n  }\n\n}\n"
  },
  {
    "path": "tests/src/Functional/SubprofileGeneratorTest.php",
    "content": "<?php\n\nnamespace Drupal\\Tests\\lightning\\Functional;\n\nuse Drupal\\Component\\Serialization\\Json;\nuse Drupal\\Core\\Serialization\\Yaml;\nuse Drupal\\lightning\\Generators\\SubProfileGenerator;\nuse Drupal\\Tests\\BrowserTestBase;\nuse Drush\\TestTraits\\DrushTestTrait;\n\n/**\n * Tests the Drush command to generate a Lightning sub-profile.\n *\n * @covers \\Drupal\\lightning\\Generators\\SubProfileGenerator\n *\n * @group lightning\n */\nclass SubprofileGeneratorTest extends BrowserTestBase {\n\n  use DrushTestTrait;\n\n  /**\n   * {@inheritdoc}\n   */\n  protected $defaultTheme = 'stark';\n\n  /**\n   * Data provider for ::test().\n   *\n   * @return array\n   *   The sets of arguments to pass to the test method.\n   */\n  public function provider() {\n    return [\n      'additional modules in install list' => [\n        'answers' => [\n          'install' => ['consumers'],\n        ],\n      ],\n      'with exclusions' => [\n        'answers' => [\n          'exclude' => 'lightning_search, lightning_media_instagram',\n        ],\n      ],\n      'inclusions and exclusions' => [\n        'answers' => [\n          'install' => ['consumers', 'book'],\n          'exclude' => 'lightning_media_instagram',\n        ],\n      ],\n      'default answers' => [\n        'answers' => [],\n      ],\n      'specific machine name and description' => [\n        'answers' => [\n          'machine_name' => 'something_different',\n          'description' => 'This profile rules.',\n        ],\n      ],\n    ];\n  }\n\n  /**\n   * Tests the lightning-subprofile generator for Drush.\n   *\n   * @param array $answers\n   *   The answers to the generator's questions, keyed by the questions' machine\n   *   names.\n   *\n   * @see \\Drupal\\lightning\\Generators\\SubProfileGenerator::interact()\n   *\n   * @dataProvider provider\n   */\n  public function test(array $answers = []) {\n    $drupal_root = $this->getDrupalRoot();\n    $this->assertFileExists(\"$drupal_root/../composer.json\", 'A composer.json file must exist in the directory above the Drupal root.');\n\n    // We need Drush to run commands defined by the Lightning profile, but we\n    // don't want to actually install the Lightning profile into the test site\n    // because it takes freaking forever. So, this hack is a quick way to expose\n    // our generator to Drush.\n    $this->config('core.extension')\n      ->set('module.lightning', 0)\n      ->set('profile', 'lightning')\n      ->save();\n\n    $answers += [\n      'name' => 'Wizards',\n      'machine_name' => 'wizards',\n      // Ideally, these would be empty strings, but it seems that Symfony\n      // Console (or possibly Drush) mangles empty strings in JSON-encoded data,\n      // results in the --answers option containing invalid JSON. Setting these\n      // to NULL is a workaround, but it achieves the desired result.\n      'description' => NULL,\n      'install' => NULL,\n      'exclude' => NULL,\n    ];\n    $options = [\n      'answers' => Json::encode($answers),\n      // Generate the profile in the test site's directory, so that it will be\n      // automatically cleaned up when the test is done.\n      'directory' => \"$drupal_root/$this->siteDirectory/profiles/custom\",\n    ];\n\n    // The generator will ask if we want to exclude any components. We need to\n    // explicitly answer the prompt either way, since we are running the command\n    // quasi-interactively.\n    if ($answers['exclude']) {\n      $options['yes'] = NULL;\n    }\n    else {\n      $options['no'] = NULL;\n    }\n\n    $machine_name = $answers['machine_name'];\n    // DrushTestTrait::drush() will invoke the command with the --no-interaction\n    // option, bypassing the generator's interact() method -- which contains all\n    // the logic we want to test! The SHELL_INTERACTIVE environment variable\n    // forces Symfony Console to run the method anyway.\n    $this->drush('generate', ['lightning-subprofile'], $options, NULL, \"$drupal_root/..\", 0, NULL, [\n      'SHELL_INTERACTIVE' => 1,\n    ]);\n\n    // Ensure that the new profile is picked up by the extension system. Because\n    // ExtensionDiscovery does not expect the available extensions to change in\n    // the course of a single request, and has no public method to reset its\n    // internal static cache, we need to pry open its static $files property\n    // and force-reset it.\n    $reflector = new \\ReflectionClass('\\Drupal\\Core\\Extension\\ExtensionDiscovery');\n    $property = $reflector->getProperty('files');\n    $property->setAccessible(TRUE);\n    $property->setValue([]);\n\n    $extension = $this->container->get('extension.list.profile')\n      ->reset()\n      ->get($machine_name);\n\n    // Get the raw info for the generated profile. The profile list's\n    // getExtensionInfo() method would merge in default values from the\n    // extension system and ancestral profiles, which interfere with these\n    // assertions.\n    $info = file_get_contents($extension->getPathname());\n    $info = Yaml::decode($info);\n\n    // Assert the constant values.\n    $this->assertSame($answers['name'], $info['name']);\n    $this->assertSame('profile', $info['type']);\n    $this->assertArrayNotHasKey('core', $info);\n    $this->assertNotEmpty($info['core_version_requirement']);\n    $this->assertSame(['bartik', 'seven'], $info['themes']);\n    $this->assertSame('lightning', $info['base profile']);\n\n    // Assert the stuff that can change depending on user input.\n    if ($answers['description']) {\n      $this->assertSame($answers['description'], $info['description']);\n    }\n    else {\n      $this->assertArrayNotHasKey('description', $info);\n    }\n\n    if ($answers['install']) {\n      $this->assertSame($answers['install'], $info['install']);\n    }\n    else {\n      $this->assertArrayNotHasKey('install', $info);\n    }\n\n    if ($answers['exclude']) {\n      $exclude = SubProfileGenerator::toArray($answers['exclude']);\n\n      foreach ($exclude as $module) {\n        $this->assertContains($module, $info['exclude']);\n      }\n    }\n    else {\n      $this->assertArrayNotHasKey('exclude', $info);\n    }\n\n    // Load up the new profile to ensure it's valid PHP and includes an install\n    // hook.\n    $module_handler = $this->container->get('module_handler');\n    $module_handler->addProfile($machine_name, $extension->getPath());\n    $module_handler->load($machine_name);\n    $module_handler->loadInclude($machine_name, 'install');\n    $this->assertTrue(function_exists($machine_name . '_install'));\n\n    // Ensure the .profile file at least includes a comment.\n    $file = $module_handler->getModule($machine_name)->getExtensionPathname();\n    $this->assertNotEmpty(file_get_contents($file));\n  }\n\n}\n"
  },
  {
    "path": "tests/src/Functional/UpdatePath3xTest.php",
    "content": "<?php\n\nnamespace Drupal\\Tests\\lightning\\Functional;\n\nuse Drupal\\FunctionalTests\\Update\\UpdatePathTestBase;\nuse Drush\\TestTraits\\DrushTestTrait;\n\n/**\n * Tests the update path from Lightning 3.x.\n *\n * @group lightning\n */\nclass UpdatePath3xTest extends UpdatePathTestBase {\n\n  use DrushTestTrait;\n\n  /**\n   * {@inheritdoc}\n   */\n  protected function setDatabaseDumpFiles() {\n    $this->databaseDumpFiles = [\n      __DIR__ . '/../../fixtures/3.4.0.php.gz',\n    ];\n  }\n\n  /**\n   * Tests updating from Lightning 3.4.0 via the UI.\n   */\n  public function test() {\n    require_once __DIR__ . '/../../update.php';\n    $this->getRandomGenerator()->image('public://star_2.png', '16x16', '16x16');\n    $this->runUpdates();\n    $this->drush('update:lightning', [], ['yes' => NULL]);\n  }\n\n}\n"
  },
  {
    "path": "tests/src/Functional/UpdatePath4xTest.php",
    "content": "<?php\n\nnamespace Drupal\\Tests\\lightning\\Functional;\n\nuse Drupal\\Core\\Update\\UpdateKernel;\nuse Drupal\\FunctionalTests\\Update\\UpdatePathTestBase;\nuse Drush\\TestTraits\\DrushTestTrait;\n\n/**\n * Tests the update path from Lightning 4.x.\n *\n * @group lightning\n */\nclass UpdatePath4xTest extends UpdatePathTestBase {\n\n  use DrushTestTrait;\n\n  /**\n   * {@inheritdoc}\n   */\n  protected function setDatabaseDumpFiles() {\n    $this->databaseDumpFiles = [\n      __DIR__ . '/../../fixtures/4.1.0-beta1.php.gz',\n    ];\n  }\n\n  /**\n   * {@inheritdoc}\n   */\n  protected function replaceUser1() {\n    // When the parent method re-saves the user account, an obscure code path\n    // through Layout Builder and Lightning Media is triggered, resulting in\n    // the use of an old media source plugin ID that will not exist after the\n    // updates are run. Normally, Lightning Media accounts for this by aliasing\n    // the old plugin ID, but that is only done if the kernel is an\n    // UpdateKernel...which is NOT the case in this test's memory space. So,\n    // although this is dirty and brittle, I don't know of a better way around\n    // it; we simply need to have a (mocked) UpdateKernel in place when the\n    // media source plugin definition cache is rebuilt.\n    $kernel = $this->container->get('kernel');\n    $this->container->set('kernel', $this->prophesize(UpdateKernel::class)->reveal());\n\n    // Clear the cached plugin definitions, then rebuild them. The fact that\n    // an UpdateKernel is present means that the old media source plugin ID\n    // should be aliased and cached correctly.\n    // @see lightning_media_instagram_media_source_info_alter()\n    $plugin_manager = $this->container->get('plugin.manager.media.source');\n    $plugin_manager->clearCachedDefinitions();\n    $plugin_manager->getDefinitions();\n\n    // Restore the real kernel and proceed merrily on our way.\n    $this->container->set('kernel', $kernel);\n    parent::replaceUser1();\n  }\n\n  /**\n   * Tests updating from Lightning 4.1.0-beta1 via the UI.\n   */\n  public function test() {\n    require_once __DIR__ . '/../../update.php';\n    $this->getRandomGenerator()->image('public://star.png', '16x16', '16x16');\n    $this->runUpdates();\n    $this->drush('update:lightning', [], ['yes' => NULL]);\n  }\n\n}\n"
  },
  {
    "path": "tests/src/Kernel/Update405Test.php",
    "content": "<?php\n\nnamespace Drupal\\Tests\\lightning\\Kernel;\n\nuse Drupal\\KernelTests\\KernelTestBase;\nuse Drupal\\lightning\\Update\\Update405;\n\n/**\n * @coversDefaultClass \\Drupal\\lightning\\Update\\Update405\n *\n * @group lightning\n */\nclass Update405Test extends KernelTestBase {\n\n  /**\n   * {@inheritdoc}\n   */\n  protected static $modules = ['system'];\n\n  /**\n   * @covers ::enableAutosaveForm\n   * @covers ::enableRedirect\n   */\n  public function testUpdate() {\n    $moduleHandler = $this->container->get('module_handler');\n\n    $this->assertFalse($moduleHandler->moduleExists('autosave_form'));\n    $this->assertFalse($moduleHandler->moduleExists('conflict'));\n    $this->assertFalse($moduleHandler->moduleExists('redirect'));\n\n    $update = Update405::create($this->container);\n    $update->enableAutosaveForm();\n    $update->enableRedirect();\n\n    $moduleHandler = $this->container->get('module_handler');\n\n    $this->assertTrue($moduleHandler->moduleExists('autosave_form'));\n    $this->assertTrue($moduleHandler->moduleExists('conflict'));\n    $this->assertTrue($moduleHandler->moduleExists('redirect'));\n  }\n\n}\n"
  },
  {
    "path": "tests/travis/install.sh",
    "content": "#!/usr/bin/env bash\n\n# NAME\n#     install.sh - Install Travis CI dependencies\n#\n# SYNOPSIS\n#     install.sh\n#\n# DESCRIPTION\n#     Creates the test fixture.\n\ncd \"$(dirname \"$0\")\"\n\n# Reuse ORCA's own includes.\nsource ../../../orca/bin/travis/_includes.sh\n\n# Handle the special case of scanning for deprecations in contrib dependencies.\n# We need to ensure that the components are included as part of the SUT,\n# but none of the other Acquia product modules are.\nif [[ \"$ORCA_JOB\" == \"DEPRECATED_CODE_SCAN_W_CONTRIB\" ]]; then\n  export ORCA_PACKAGES_CONFIG=../lightning/tests/packages.yml\n  orca fixture:init -f --sut=\"$ORCA_SUT_NAME\" --dev --no-site-install\n  exit 0\nfi\n\n# When testing the SUT in isolation using dev package versions, treat the\n# components as part of the SUT, to be installed in an isolated (SUT-only)\n# fixture.\nif [[ \"$ORCA_JOB\" == \"ISOLATED_TEST_ON_CURRENT_DEV\" ]]; then\n  export ORCA_PACKAGES_CONFIG=../lightning/tests/packages.yml\n  orca fixture:init -f --sut=\"$ORCA_SUT_NAME\" --dev --profile=lightning\nelse\n  # Run ORCA's standard installation script.\n  ../../../orca/bin/travis/install.sh\nfi\n\n# If there is no fixture, there's nothing else for us to do.\n[[ -d \"$ORCA_FIXTURE_DIR\" ]] || exit 0\n\n# Add testing dependencies.\ncomposer -d\"$ORCA_FIXTURE_DIR\" require --dev weitzman/drupal-test-traits:dev-master phpspec/prophecy-phpunit:^2\n"
  },
  {
    "path": "tests/update.php",
    "content": "<?php\n\n/**\n * @file\n * Prepares a code base to update to Drupal 9.\n */\n\nuse Drupal\\views\\Entity\\View;\n\nDrupal::configFactory()\n  ->getEditable('core.extension')\n  ->clear('module.libraries')\n  ->clear('module.openapi_redoc')\n  ->save();\n\nDrupal::configFactory()\n  ->getEditable('libraries.settings')\n  ->delete();\n\nforeach (['default', 'embedded'] as $view_mode) {\n  Drupal::configFactory()\n    ->getEditable(\"core.entity_view_display.media.instagram.$view_mode\")\n    ->set('content.embed_code.settings.hidecaption', NULL)\n    ->save();\n}\n\nDrupal::keyValue('system.schema')->deleteMultiple([\n  'libraries',\n  'openapi_redoc',\n]);\n\n$view = View::load('media');\n$display = &$view->getDisplay('default');\n$display['display_options']['fields']['media_bulk_form']['plugin_id'] = 'bulk_form';\n$view->save();\n\nDrupal::service('extension.list.profile')->reset();\n"
  }
]