Repository: acquia/lightning Branch: 5.1.x Commit: 607a46eb062a Files: 85 Total size: 136.3 KB Directory structure: gitextract_211omb_v/ ├── .gitattributes ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── UPDATE.md ├── acquia-pipelines.yml ├── composer.json ├── config/ │ ├── install/ │ │ ├── user.role.administrator.yml │ │ └── user.settings.yml │ ├── optional/ │ │ ├── block.block.bartik_account_menu.yml │ │ ├── block.block.bartik_branding.yml │ │ ├── block.block.bartik_breadcrumbs.yml │ │ ├── block.block.bartik_content.yml │ │ ├── block.block.bartik_footer.yml │ │ ├── block.block.bartik_help.yml │ │ ├── block.block.bartik_local_actions.yml │ │ ├── block.block.bartik_local_tasks.yml │ │ ├── block.block.bartik_main_menu.yml │ │ ├── block.block.bartik_messages.yml │ │ ├── block.block.bartik_page_title.yml │ │ ├── block.block.bartik_powered.yml │ │ ├── block.block.seven_breadcrumbs.yml │ │ ├── block.block.seven_content.yml │ │ ├── block.block.seven_help.yml │ │ ├── block.block.seven_local_actions.yml │ │ ├── block.block.seven_login.yml │ │ ├── block.block.seven_messages.yml │ │ ├── block.block.seven_page_title.yml │ │ ├── block.block.seven_primary_local_tasks.yml │ │ ├── block.block.seven_secondary_local_tasks.yml │ │ ├── block_content.type.text.yml │ │ ├── comment.type.comment.yml │ │ ├── core.entity_form_display.block_content.text.default.yml │ │ ├── core.entity_form_display.comment.comment.default.yml │ │ ├── core.entity_view_display.block_content.text.default.yml │ │ ├── core.entity_view_display.comment.comment.default.yml │ │ ├── field.field.block_content.text.body.yml │ │ └── field.field.comment.comment.comment_body.yml │ └── schema/ │ └── lightning.schema.yml ├── drush.services.yml ├── grumphp.yml ├── help/ │ └── content_roles.md ├── hooks/ │ ├── README.md │ └── common/ │ ├── post-code-deploy/ │ │ ├── .gitignore │ │ └── reinstall.sh │ └── post-code-update/ │ ├── .gitignore │ └── reinstall.sh ├── install-drupal.sh ├── lightning.info.yml ├── lightning.install ├── lightning.profile ├── modules/ │ └── lightning_install/ │ ├── composer.json │ └── lightning_install.info.yml ├── phpunit.xml ├── release-branch.sh ├── settings.local.php ├── src/ │ ├── Commands/ │ │ └── Uninstaller.php │ ├── Composer/ │ │ ├── AssetPackagist.php │ │ ├── ConfigureLegacyProject.php │ │ └── PatchedConstraint.php │ ├── Generators/ │ │ ├── SubProfileGenerator.php │ │ ├── install.twig │ │ └── profile.twig │ ├── LightningServiceProvider.php │ ├── ProxyClass/ │ │ └── RequiredModuleUninstallValidator.php │ ├── RequiredModuleUninstallValidator.php │ └── Update/ │ ├── Update320.php │ └── Update405.php ├── tarball.sh └── tests/ ├── lightning_extender/ │ ├── lightning_extender.info.yml │ └── lightning_extender.profile ├── packages.yml ├── packages_alter.yml ├── src/ │ ├── ExistingSite/ │ │ ├── ApiTest.php │ │ ├── ConfigIntegrityTest.php │ │ └── ViewModeTest.php │ ├── Functional/ │ │ ├── LightningTest.php │ │ ├── SubprofileGeneratorTest.php │ │ ├── UpdatePath3xTest.php │ │ └── UpdatePath4xTest.php │ └── Kernel/ │ └── Update405Test.php ├── travis/ │ └── install.sh └── update.php ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ /grumphp.yml export-ignore /hooks export-ignore /tests/fixtures export-ignore /.travis.yml export-ignore /acquia-pipelines.yml export-ignore /phpunit.xml export-ignore /settings.local.php export-ignore /*.sh export-ignore ================================================ FILE: .gitignore ================================================ .DS_Store bin/ docroot/ vendor/ node_modules/ bower_components/ .sass-cache/ console/cache/ /.behat.yml ================================================ FILE: CHANGELOG.md ================================================ The information in this file has been moved to https://github.com/acquia/lightning/wiki/Changelog. ================================================ FILE: CONTRIBUTING.md ================================================ ## Contributing to Lightning ### Local development **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. This 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: * You have Git installed in your PATH. You can confirm this by running `git --version`. * You have PHP 7.1 or later installed in your PATH. You can confirm this by running `php --version`. * 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. * 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`. * 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. Now, get your Lightning code base set up: 1. Clone the git repository, e.g. `git clone git@github.com:acquia/lightning.git` 2. Enter the repository and run `composer install` to install all dependencies. 3. 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: ``` DB_URL=mysql://user:password@server/drupal ./install-drupal.sh ``` 4. Run the web server. The quickest option is to use PHP's built-in server: `drush runserver 8080` 5. You should now be able to access your Lightning site at `http://localhost:8080`. ================================================ FILE: LICENSE.txt ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ================================================ FILE: README.md ================================================ # Drupal Lightning ![Lightning logo of a bolt of lightning](https://raw.githubusercontent.com/acquia/lightning/5.0.x/lightning-logo.png) ## November 2, 2021: So long and thanks for all the fish! Acquia **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. ================================================ FILE: UPDATE.md ================================================ The information in this file has moved to https://github.com/acquia/lightning/wiki/Update-Instructions. ================================================ FILE: acquia-pipelines.yml ================================================ # This file is used by Acquia Pipelines continuous integration. It builds # Lightning and runs its functional tests. Upon success, an artifact is deployed # to the lightningnightly (92170259-5e36-4052-a2ad-0dd5ee5b3ec2) sub on Acquia # cloud. version: 1.1.0 services: - mysql - php: version: 7.2 events: build: steps: - setup: type: script script: # Add Composer's local bin directory to the PATH so that we will be # running our installed versions of Drush, PHPCS, Behat, etc. - export PATH="$HOME/.composer/vendor/bin:$SOURCE_DIR/vendor/bin:$PATH" - composer validate --no-check-all --ansi --no-interaction - composer update - cleanup: type: script script: # Setup settings file and codebase with minimum required for cloud. - chmod -R +w docroot/sites/default - cp docroot/sites/default/default.settings.php docroot/sites/default/settings.php - echo "if (file_exists('/var/www/site-php')) { require '/var/www/site-php/lightningnightly/lightningnightly-settings.inc';}" >> docroot/sites/default/settings.php # Clear any config directories that Cloud tries to set in th include # file. - echo "\$config_directories = [];" >> docroot/sites/default/settings.php # Use the existing `config` directory we already have. Cloud get's # confused because we have a config directory above docroot already # and manipulating it in Pipelines doesn't seem to work. This # deployment is ephemeral, so using the existing config directory # isn't a problem. - echo "\$config_directories['sync'] = '../config';" >> docroot/sites/default/settings.php ================================================ FILE: composer.json ================================================ { "name": "acquia/lightning", "type": "drupal-profile", "description": "The best of Drupal, curated by Acquia", "license": "GPL-2.0-or-later", "require": { "ext-dom": "*", "composer/composer": "^1.10.22 || ^2.0.13", "cweagans/composer-patches": "^1.7", "drupal/acquia_connector": "^1.24-rc3 || ^2.0-rc1 || ^3", "drupal/core": "~9.1.13", "drupal/inline_entity_form": "^1.0-rc7", "drupal/lightning_api": "^4.6", "drupal/lightning_core": "^5", "drupal/lightning_layout": "^2.10", "drupal/lightning_media": "^4.6", "drupal/lightning_workflow": "^3.16", "drupal/page_manager": "^4.0-beta6", "drupal/panelizer": "^5.0-beta3", "drupal/pendo": "^1@alpha", "drupal/profile_switcher": "^1.0-alpha5", "drupal/search_api": "^1.17" }, "require-dev": { "composer/installers": "^1.9", "drupal/core-composer-scaffold": "^9", "drupal/core-dev": "^9", "drupal/devel": "^4.1", "drush/drush": ">=9.7", "phpspec/prophecy-phpunit": "^2", "weitzman/drupal-test-traits": "dev-master" }, "config": { "preferred-install": { "drupal/core": "dist", "drupal/lightning_*": "source" } }, "extra": { "branch-alias": { "dev-8.x-4.x": "4.x-dev" }, "composer-exit-on-patch-failure": "true", "drupal-scaffold": { "file-mapping": { "[project-root]/.editorconfig": false, "[project-root]/.gitattributes": false, "[web-root]/.csslintrc": false, "[web-root]/INSTALL.txt": false, "[web-root]/example.gitignore": false, "[web-root]/modules/README.txt": false, "[web-root]/profiles/README.txt": false, "[web-root]/robots.txt": false, "[web-root]/sites/README.txt": false, "[web-root]/themes/README.txt": false, "[web-root]/web.config": false }, "locations": { "web-root": "docroot/" } }, "drush": { "services": { "drush.services.yml": "^9 || ^10" } }, "enable-patching": true, "installer-paths": { "docroot/core": [ "type:drupal-core" ], "docroot/libraries/{$name}": [ "type:drupal-library", "type:bower-asset", "type:npm-asset" ], "docroot/modules/contrib/{$name}": [ "type:drupal-module" ], "docroot/profiles/contrib/{$name}": [ "type:drupal-profile" ], "docroot/themes/contrib/{$name}": [ "type:drupal-theme" ] }, "installer-types": [ "bower-asset", "npm-asset" ], "patchLevel": { "drupal/core": "-p2" }, "patches": { "drupal/core": { "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", "[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", "REMOVE: 2031261 - Fix SQLite variable limit": "https://www.drupal.org/files/issues/2020-04-01/2031261-118.patch", "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" } }, "patches-ignore": { "drupal/lightning_core": { "drupal/core": { "2869592 - Disabled update module shouldn't produce a status report warning": "https://www.drupal.org/files/issues/2869592-remove-update-warning-7.patch" } } } }, "autoload": { "psr-4": { "Acquia\\Lightning\\": "src" }, "classmap": [ "src/Composer/ConfigureLegacyProject.php" ] }, "repositories": { "drupal": { "type": "composer", "url": "https://packages.drupal.org/8" }, "assets": { "type": "composer", "url": "https://asset-packagist.org" } }, "minimum-stability": "dev", "prefer-stable": true, "scripts": { "post-install-cmd": [ "@putenv DIR=./docroot/profiles/lightning", "mkdir -p $DIR", "ln -s -f $PWD/config $DIR", "ln -s -f $PWD/drush.services.yml $DIR", "ln -s -f $PWD/favicon.ico $DIR", "ln -s -f $PWD/help $DIR", "ln -s -f $PWD/lightning-logo.png $DIR", "ln -s -f $PWD/modules $DIR", "ln -s -f $PWD/src $DIR", "ln -s -f $PWD/tests $DIR", "find $PWD -name 'lightning.*' -depth 1 -exec ln -s -f {} $DIR ';'", "cp -f phpunit.xml ./docroot/core" ], "configure-tarball": "Acquia\\Lightning\\Composer\\ConfigureLegacyProject::execute", "enable-asset-packagist": "Acquia\\Lightning\\Composer\\AssetPackagist::execute", "nuke": "rm -r -f docroot vendor", "verify-patched-constraints": "Acquia\\Lightning\\Composer\\PatchedConstraint::execute" } } ================================================ FILE: config/install/user.role.administrator.yml ================================================ id: administrator label: Administrator weight: 2 langcode: en is_admin: true ================================================ FILE: config/install/user.settings.yml ================================================ anonymous: Anonymous verify_mail: true notify: cancel_confirm: true password_reset: true status_activated: true status_blocked: false status_canceled: false register_admin_created: true register_no_approval_required: true register_pending_approval: true register: admin_only cancel_method: user_cancel_block password_reset_timeout: 86400 password_strength: true langcode: en ================================================ FILE: config/optional/block.block.bartik_account_menu.yml ================================================ langcode: en status: true dependencies: config: - system.menu.account module: - system theme: - bartik id: bartik_account_menu theme: bartik region: secondary_menu weight: 0 provider: null plugin: 'system_menu_block:account' settings: id: 'system_menu_block:account' label: 'User account menu' provider: system label_display: '0' level: 1 depth: 1 visibility: { } ================================================ FILE: config/optional/block.block.bartik_branding.yml ================================================ langcode: en status: true dependencies: module: - system theme: - bartik id: bartik_branding theme: bartik region: header weight: 0 provider: null plugin: system_branding_block settings: id: system_branding_block label: 'Site branding' provider: system label_display: '0' use_site_logo: true use_site_name: true use_site_slogan: true visibility: { } ================================================ FILE: config/optional/block.block.bartik_breadcrumbs.yml ================================================ langcode: en status: true dependencies: module: - system theme: - bartik id: bartik_breadcrumbs theme: bartik region: breadcrumb weight: 0 provider: null plugin: system_breadcrumb_block settings: id: system_breadcrumb_block label: Breadcrumbs provider: system label_display: '0' visibility: { } ================================================ FILE: config/optional/block.block.bartik_content.yml ================================================ langcode: en status: true dependencies: module: - system theme: - bartik id: bartik_content theme: bartik region: content weight: 0 provider: null plugin: system_main_block settings: id: system_main_block label: 'Main page content' provider: system label_display: '0' visibility: { } ================================================ FILE: config/optional/block.block.bartik_footer.yml ================================================ langcode: en status: true dependencies: config: - system.menu.footer module: - system theme: - bartik id: bartik_footer theme: bartik region: footer_fifth weight: 0 provider: null plugin: 'system_menu_block:footer' settings: id: 'system_menu_block:footer' label: 'Footer menu' provider: system label_display: '0' level: 1 depth: 0 visibility: { } ================================================ FILE: config/optional/block.block.bartik_help.yml ================================================ langcode: en status: true dependencies: module: - help theme: - bartik id: bartik_help theme: bartik region: content weight: -30 provider: null plugin: help_block settings: id: help_block label: Help provider: help label_display: '0' visibility: { } ================================================ FILE: config/optional/block.block.bartik_local_actions.yml ================================================ langcode: en status: true dependencies: theme: - bartik id: bartik_local_actions theme: bartik region: content weight: -20 provider: null plugin: local_actions_block settings: id: local_actions_block label: 'Primary admin actions' provider: core label_display: '0' visibility: { } ================================================ FILE: config/optional/block.block.bartik_local_tasks.yml ================================================ langcode: en status: true dependencies: theme: - bartik id: bartik_local_tasks theme: bartik region: content weight: -40 provider: null plugin: local_tasks_block settings: id: local_tasks_block label: Tabs provider: core label_display: '0' primary: true secondary: true visibility: { } ================================================ FILE: config/optional/block.block.bartik_main_menu.yml ================================================ langcode: en status: true dependencies: config: - system.menu.main module: - system theme: - bartik id: bartik_main_menu theme: bartik region: primary_menu weight: 0 provider: null plugin: 'system_menu_block:main' settings: id: 'system_menu_block:main' label: 'Main navigation' provider: system label_display: '0' level: 1 depth: 1 visibility: { } ================================================ FILE: config/optional/block.block.bartik_messages.yml ================================================ langcode: en status: true dependencies: module: - system theme: - bartik id: bartik_messages theme: bartik region: highlighted weight: 0 provider: null plugin: system_messages_block settings: id: system_messages_block label: 'Status messages' provider: system label_display: '0' visibility: { } ================================================ FILE: config/optional/block.block.bartik_page_title.yml ================================================ langcode: en status: true dependencies: theme: - bartik id: bartik_page_title theme: bartik region: content weight: -50 provider: null plugin: page_title_block settings: id: page_title_block label: 'Page title' provider: core label_display: '0' visibility: { } ================================================ FILE: config/optional/block.block.bartik_powered.yml ================================================ langcode: en status: true dependencies: module: - system theme: - bartik id: bartik_powered theme: bartik region: footer_fifth weight: 10 provider: null plugin: system_powered_by_block settings: id: system_powered_by_block label: 'Powered by Drupal' provider: system label_display: '0' visibility: { } ================================================ FILE: config/optional/block.block.seven_breadcrumbs.yml ================================================ id: seven_breadcrumbs theme: seven weight: 0 status: true langcode: en region: breadcrumb plugin: system_breadcrumb_block settings: id: system_breadcrumb_block label: Breadcrumbs provider: system label_display: '0' dependencies: module: - system theme: - seven visibility: { } ================================================ FILE: config/optional/block.block.seven_content.yml ================================================ id: seven_content theme: seven weight: 0 status: true langcode: en region: content plugin: system_main_block settings: id: system_main_block label: 'Main page content' provider: system label_display: '0' dependencies: module: - system theme: - seven visibility: { } ================================================ FILE: config/optional/block.block.seven_help.yml ================================================ id: seven_help theme: seven weight: 0 status: true langcode: en region: help plugin: help_block settings: id: help_block label: 'Help' provider: help label_display: '0' dependencies: module: - help theme: - seven visibility: { } ================================================ FILE: config/optional/block.block.seven_local_actions.yml ================================================ id: seven_local_actions theme: seven weight: -10 status: true langcode: en region: content plugin: local_actions_block settings: id: local_actions_block label: Primary admin actions label_display: '0' dependencies: theme: - seven visibility: { } ================================================ FILE: config/optional/block.block.seven_login.yml ================================================ id: seven_login theme: seven weight: 10 status: true langcode: en region: content plugin: user_login_block settings: id: user_login_block label: 'User login' provider: user label_display: visible dependencies: module: - user theme: - seven visibility: { } ================================================ FILE: config/optional/block.block.seven_messages.yml ================================================ id: seven_messages theme: seven weight: 0 status: true langcode: en region: highlighted plugin: system_messages_block settings: id: system_messages_block label: 'Status messages' provider: system label_display: '0' dependencies: module: - system theme: - seven ================================================ FILE: config/optional/block.block.seven_page_title.yml ================================================ langcode: en status: true dependencies: theme: - seven id: seven_page_title theme: seven region: header weight: -30 provider: null plugin: page_title_block settings: id: page_title_block label: 'Page title' provider: core label_display: '0' visibility: { } ================================================ FILE: config/optional/block.block.seven_primary_local_tasks.yml ================================================ id: seven_primary_local_tasks theme: seven weight: 0 status: true langcode: en region: header plugin: local_tasks_block settings: id: local_tasks_block label: Primary tabs label_display: '0' primary: true secondary: false dependencies: theme: - seven visibility: { } ================================================ FILE: config/optional/block.block.seven_secondary_local_tasks.yml ================================================ id: seven_secondary_local_tasks theme: seven weight: 0 status: true langcode: en region: pre_content plugin: local_tasks_block settings: id: local_tasks_block label: Secondary tabs label_display: '0' primary: false secondary: true dependencies: theme: - seven visibility: { } ================================================ FILE: config/optional/block_content.type.text.yml ================================================ id: text label: 'Text' revision: 0 description: 'A text block contains a title and a body.' langcode: en ================================================ FILE: config/optional/comment.type.comment.yml ================================================ langcode: en status: true dependencies: { } id: comment label: 'Default comments' target_entity_type_id: node description: 'Allows commenting on content' ================================================ FILE: config/optional/core.entity_form_display.block_content.text.default.yml ================================================ langcode: en status: true dependencies: config: - block_content.type.text - field.field.block_content.text.body module: - text id: block_content.text.default targetEntityType: block_content bundle: text mode: default content: body: type: text_textarea_with_summary weight: 0 settings: rows: 9 summary_rows: 3 placeholder: '' third_party_settings: { } region: content info: type: string_textfield weight: -5 settings: size: 60 placeholder: '' third_party_settings: { } region: content hidden: { } ================================================ FILE: config/optional/core.entity_form_display.comment.comment.default.yml ================================================ langcode: en status: true dependencies: config: - comment.type.comment - field.field.comment.comment.comment_body module: - text id: comment.comment.default targetEntityType: comment bundle: comment mode: default content: author: weight: -2 region: content comment_body: type: text_textarea weight: 11 region: content settings: rows: 5 placeholder: '' third_party_settings: { } subject: type: string_textfield weight: 10 region: content settings: size: 60 placeholder: '' third_party_settings: { } hidden: { } ================================================ FILE: config/optional/core.entity_view_display.block_content.text.default.yml ================================================ langcode: en status: true dependencies: config: - block_content.type.text - field.field.block_content.text.body module: - text id: block_content.text.default targetEntityType: block_content bundle: text mode: default content: body: type: text_default weight: 0 label: hidden settings: { } third_party_settings: { } region: content hidden: { } ================================================ FILE: config/optional/core.entity_view_display.comment.comment.default.yml ================================================ langcode: en status: true dependencies: config: - comment.type.comment - field.field.comment.comment.comment_body module: - text id: comment.comment.default targetEntityType: comment bundle: comment mode: default content: comment_body: label: hidden type: text_default weight: 0 region: content settings: { } third_party_settings: { } links: weight: 100 region: content hidden: { } ================================================ FILE: config/optional/field.field.block_content.text.body.yml ================================================ langcode: en status: true dependencies: config: - block_content.type.text - field.storage.block_content.body module: - text id: block_content.text.body field_name: body entity_type: block_content bundle: text label: Body description: '' required: false translatable: true default_value: { } default_value_callback: '' settings: display_summary: false field_type: text_with_summary ================================================ FILE: config/optional/field.field.comment.comment.comment_body.yml ================================================ langcode: en status: true dependencies: config: - comment.type.comment - field.storage.comment.comment_body module: - text id: comment.comment.comment_body field_name: comment_body entity_type: comment bundle: comment label: Comment description: '' required: true translatable: true default_value: { } default_value_callback: '' settings: { } field_type: text_long ================================================ FILE: config/schema/lightning.schema.yml ================================================ user.role.*.third_party.lightning: type: mapping label: 'Lightning settings' mapping: bundled: type: boolean label: 'Bundled with Lightning' lightning.versions: type: sequence label: 'Lightning component versions' sequence: type: string label: 'Version' ================================================ FILE: drush.services.yml ================================================ services: subprofile.generator: class: 'Drupal\lightning\Generators\SubProfileGenerator' arguments: - '@extension.list.module' tags: - { name: drush.generator } lightning.uninstaller: class: 'Drupal\lightning\Commands\Uninstaller' arguments: - '@module_handler' - '@theme_handler' - '@extension.list.profile' - '@file_system' - '%app.root%' - '%install_profile%' tags: - { name: drush.command } ================================================ FILE: grumphp.yml ================================================ parameters: ascii: failed: vendor/vijaycs85/drupal-quality-checker/resources/ascii/grumpy.txt succeeded: vendor/vijaycs85/drupal-quality-checker/resources/ascii/happy.txt git_dir: . bin_dir: vendor/bin tasks: phplint: ~ yamllint: ~ composer: ~ composer_normalize: ~ jsonlint: ~ phpcpd: directory: ['./src'] phpcs: standard: Drupal ignore_patterns: - .github - .gitlab - bower_components - node_modules - vendor triggered_by: - php - module - inc - install - test - profile - theme - css - info - txt - md ================================================ FILE: help/content_roles.md ================================================ ## Content Roles ### Creator Creator roles are automatically created for every content type and automatically destroyed for deleted content types. The creator roles have limited permissions -- they can create content or edit their own. They *cannot* edit content created by anybody else, and they cannot delete content (even their own). They can save drafts and request review of their content, but they cannot publish it. ### Reviewer Like creator roles, reviewer roles are also automatically created for every content type and destroyed for deleted content types. Reviewers are intended to *extend* creators, meaning that you will typically grant the reviewer role *and* the creator role to users. Reviewers can edit, delete, and publish **any** content, even content they don't own. ### Administering content roles Content roles are configurable. They can be enabled or disabled. When disabled, they will not be automatically created or destroyed for any content type. To configure content roles, visit to *Manage > Configuration > System > Lightning*. It's possible to define additional content roles and change the permissions associated with them, but there is currently no UI for this. You'll need to alter the `lightning_core.settings` configuration object directly, which beyond the scope of this documentation. ================================================ FILE: hooks/README.md ================================================ These hooks are used by Acquia Cloud as part of Lightning's CI. They are not executed on sites that use Lightning. For more information, see [Acquia's CLoud Hook Documentation](https://docs.acquia.com/acquia-cloud/api/cloud-hooks). ================================================ FILE: hooks/common/post-code-deploy/.gitignore ================================================ ================================================ FILE: hooks/common/post-code-deploy/reinstall.sh ================================================ #!/bin/sh # # Cloud Hook: Reinstall Lightning # # Run `drush site-install lightning` in the target environment. which drush drush --version site="$1" target_env="$2" # Fresh install of Lightning. /usr/local/bin/drush9 @$site.$target_env site-install lightning --account-pass=admin --yes ================================================ FILE: hooks/common/post-code-update/.gitignore ================================================ ================================================ FILE: hooks/common/post-code-update/reinstall.sh ================================================ #!/bin/sh # # Cloud Hook: Reinstall Lightning # # Run `drush site-install lightning` in the target environment. which drush drush --version site="$1" target_env="$2" # Fresh install of Lightning. /usr/local/bin/drush9 @$site.$target_env site-install lightning --account-pass=admin --yes ================================================ FILE: install-drupal.sh ================================================ #!/usr/bin/env bash SITE_DIR=$(pwd)/docroot/sites/default SETTINGS=$SITE_DIR/settings.php DB_URL=${DB_URL:-sqlite://db.sqlite} # Delete previous settings. if [[ -f $SETTINGS ]]; then chmod +w $SITE_DIR $SETTINGS rm $SETTINGS fi # Install Drupal. drush site:install lightning --yes --config ./drush.yml --account-pass admin --db-url $DB_URL # Make settings writable. chmod +w $SITE_DIR $SETTINGS # Copy development settings into the site directory. cp settings.local.php $SITE_DIR # Add Acquia Cloud subscription info to settings.php. echo "if (file_exists('/var/www/site-php')) {" >> $SETTINGS echo " require '/var/www/site-php/lightningnightly/lightningnightly-settings.inc';" >> $SETTINGS echo " \$settings['install_profile'] = 'standard';" >> $SETTINGS echo "}" >> $SETTINGS echo "else {" >> $SETTINGS echo " require __DIR__ . '/settings.local.php';" >> $SETTINGS echo "}" >> $SETTINGS echo "require __DIR__ . '/settings.local.php';" >> $SETTINGS # Copy PHPUnit configuration into core directory. cp -f phpunit.xml ./docroot/core ================================================ FILE: lightning.info.yml ================================================ name: Lightning core_version_requirement: '^8.8 || ^9' type: profile description: 'A fast and feature-rich Drupal distribution.' install: - autosave_form - block_content - breakpoint - ckeditor - config - conflict - contextual - menu_link_content - datetime - quickedit - editor - entity_block - help - history - menu_ui - node - options - path - pathauto - page_cache - redirect - taxonomy - text - dblog - shortcut - toolbar - field_ui - file - rdf - views - views_ui - diff - image_widget_crop - metatag - moderation_dashboard - moderation_sidebar - lightning_core - lightning_api - lightning_layout - lightning_media - lightning_workflow - lightning_contact_form - lightning_page - lightning_roles - lightning_search - lightning_landing_page - lightning_media_audio - lightning_media_bulk_upload - lightning_media_document - lightning_media_image - lightning_media_instagram - lightning_media_slideshow - lightning_media_twitter - lightning_media_video - lightning_scheduler - lightning_banner_block - lightning_map_block themes: - bartik - claro ================================================ FILE: lightning.install ================================================ getEntity('field_config', 'block_content.basic.body')->save(); $display = EntityViewDisplay::load('block_content.basic.default'); if ($display) { /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */ $display->setComponent('body', [ 'type' => 'text_default', 'weight' => 0, 'label' => 'hidden', 'settings' => [], 'third_party_settings' => [], ])->save(); } else { $config->getEntity('entity_view_display', 'block_content.basic.default')->save(); } } // Delete the `Create Landing Page` toolbar shortcut. $properties = ['link__uri' => 'internal:/admin/structure/landing-page']; $shortcuts = \Drupal::entityTypeManager()->getStorage('shortcut')->loadByProperties($properties); foreach ($shortcuts as $shortcut) { $shortcut->delete(); } // Uninstall the Media Demo Content module. \Drupal::service('module_installer')->uninstall(['lightning_media_democontent']); } /** * Removed in Lightning 8.x-2.06. * * Formerly created roles for editing content types. */ function lightning_update_8002() { } /** * Enables the lightning_core module. */ function lightning_update_8003() { Drupal::service('module_installer')->install(['lightning_core']); } /** * Enables Entity API. */ function lightning_update_8004() { /* * This ancient line is disabled in order to evade a strict coding standards * check. It should not be necessary at this point anyway, since it dates from * the days of Lightning 1.x, which has long since been end-of-life. * * eval('interface Drupal\entity\Entity\RevisionableEntityBundleInterface{}'); */ Drupal::service('module_installer')->install(['entity']); } /** * Removed in Lightning 8.x-2.04. * * Formerly installed Contact Storage. */ function lightning_update_8005() { } /** * Removed in Lightning 8.x-2.04. * * Formerly Granted permission to use contact form(s). */ function lightning_update_8006() { } /** * Uninstalls the lightning_install module. */ function lightning_update_8007() { Drupal::service('module_installer')->uninstall(['lightning_install']); } /** * Installs the profile_switcher module. */ function lightning_update_8008() { Drupal::service('module_installer')->install(['profile_switcher']); } /** * Implements hook_update_dependencies(). */ function lightning_update_dependencies() { return [ 'media_entity' => [ // Lightning 8004 must run before Media Entity 8002. 8002 => [ 'lightning' => 8004, ], ], ]; } ================================================ FILE: lightning.profile ================================================ moduleExists('node')) { Drupal::configFactory() ->getEditable('system.site') ->set('page.front', '/node') ->save(TRUE); } } /** * Allows authenticated users to use shortcuts. */ function lightning_grant_shortcut_access() { if (Drupal::moduleHandler()->moduleExists('shortcut')) { user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, ['access shortcuts']); } } /** * Sets the default and administration themes. */ function lightning_set_default_theme() { Drupal::configFactory() ->getEditable('system.theme') ->set('default', 'bartik') ->set('admin', 'claro') ->save(TRUE); // Use the admin theme for creating content. if (Drupal::moduleHandler()->moduleExists('node')) { Drupal::configFactory() ->getEditable('node.settings') ->set('use_admin_theme', TRUE) ->save(TRUE); } } /** * Set the path to the logo, favicon and README file based on install directory. */ function lightning_set_logo() { $lightning_path = drupal_get_path('profile', 'lightning'); Drupal::configFactory() ->getEditable('system.theme.global') ->set('logo', [ 'path' => $lightning_path . '/lightning.png', 'url' => '', 'use_default' => FALSE, ]) ->set('favicon', [ 'mimetype' => 'image/vnd.microsoft.icon', 'path' => $lightning_path . '/favicon.ico', 'url' => '', 'use_default' => FALSE, ]) ->save(TRUE); } /** * Alters the frontpage view, if it exists. */ function lightning_alter_frontpage_view() { $front_page = Drupal::configFactory()->getEditable('views.view.frontpage'); if (!$front_page->isNew()) { $section = 'display.default.display_options.empty.area_text_custom'; $front_page ->set("$section.tokenize", TRUE) ->set("$section.content", '

Welcome to [site:name]. No front page content has been created yet.

Would you like to view the README?

') ->save(TRUE); } } ================================================ FILE: modules/lightning_install/composer.json ================================================ { "name": "drupal/lightning_install", "type": "drupal-module", "description": "Provides helpful under-the-hood functionality used while installing Lightning.", "license": "GPL-2.0-or-later" } ================================================ FILE: modules/lightning_install/lightning_install.info.yml ================================================ name: 'Install Helper' core_version_requirement: '^8.8 || ^9' type: module hidden: true package: Lightning description: 'Provides helpful under-the-hood functionality used while installing Lightning.' ================================================ FILE: phpunit.xml ================================================ ./tests/TestSuites/UnitTestSuite.php ./tests/TestSuites/KernelTestSuite.php ./tests/TestSuites/FunctionalTestSuite.php ./tests/TestSuites/FunctionalJavascriptTestSuite.php ./includes ./lib ./modules ../modules ../sites ./ ./ ================================================ FILE: release-branch.sh ================================================ #!/bin/bash set -e # Prepares a release branch. # Example usage: ./release-branch 4.1.0 RELEASE_BRANCH=release/$1 # Ensure we are on a mainline release branch. BRANCH=$(git rev-parse --abbrev-ref HEAD) if [[ $BRANCH =~ ^([0-9]+\.){2}x$ ]]; then git pull git checkout -b $RELEASE_BRANCH composer update cp composer.lock tests/fixtures/$1.lock git add . git commit --message "$1 Release" git push --set-upstream origin $RELEASE_BRANCH else echo "This can only be done from a mainline release branch, e.g. 5.0.x." exit 1 fi ================================================ FILE: settings.local.php ================================================ [], 'services' => [ 'cache.container' => [ 'class' => 'Drupal\Core\Cache\MemoryBackend', ], ], ]; ================================================ FILE: src/Commands/Uninstaller.php ================================================ moduleHandler = $module_handler; $this->themeHandler = $theme_handler; $this->profileList = $profile_list; $this->fileSystem = $file_system; $this->appRoot = $app_root; $this->installProfile = $install_profile; } /** * Defines dynamic options if uninstalling Lightning. * * @param \Symfony\Component\Console\Command\Command $command * The command object. * * @hook option pm:uninstall */ public function options(Command $command) : void { if ($this->getUninstall()) { $command->addOption( 'profile', NULL, InputOption::VALUE_REQUIRED, 'The profile to switch to.', 'minimal' ); $command->addOption( 'composer', NULL, InputOption::VALUE_REQUIRED, 'The path of the project-level composer.json.', $this->locateProjectFile() ); } } /** * Runs a Drush command, with the --yes option. * * @param string $command * The command name (e.g., 'status'). * @param array $arguments * (optional) Arguments to pass to the command. */ private function drush(string $command, array $arguments = []) : void { $alias = $this->siteAliasManager()->getSelf(); $this->processManager() ->drush($alias, $command, $arguments, ['yes' => NULL]) ->mustRun(); } /** * Returns the profile being uninstalled. * * @return string|null * If an uninstall of Lightning or Headless Lightning is being attempted, * the machine name of the profile being uninstalled. NULL otherwise. */ private function getUninstall() : ?string { $modules = array_intersect($this->input()->getArgument('modules'), [ 'lightning', 'headless_lightning', ]); return reset($modules) ?: NULL; } /** * Validates the pm:uninstall command if Lightning is being uninstalled. * * @param \Consolidation\AnnotatedCommand\CommandData $data * The current command data. * * @hook validate pm:uninstall * * @throws \RuntimeException * Thrown if: * - The user is trying to uninstall Lightning (or Headless Lightning) at * the same time as other extension(s). * - Any installed extensions are physically located inside the Lightning * profile directory. * - Any profiles (installed or not) are using Lightning or Headless * Lightning as their immediate parent, and the user declines to modify * them automatically. */ public function validate(CommandData $data) : void { $profile = $this->getUninstall(); if ($profile) { $info = $this->profileList->getExtensionInfo($profile); $this->io()->title('Welcome to the ' . $info['name'] . ' uninstaller!'); if (count($data->input()->getArgument('modules')) > 1) { throw new \RuntimeException('You cannot uninstall ' . $info['name'] . ' and other extensions at the same time.'); } // Ensure that there are no installed modules or themes in the Lightning // profile directory. $extensions = $this->getExtensionsInProfileDirectory(); if ($extensions) { $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)); throw new \RuntimeException($error); } // Ensure that there are no other profiles available that use the profile // as a parent. If there are, offer to automatically fix them, and error // out if the user declines. $children = $this->getChildren($profile); if ($children) { $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)); $this->io()->warning($warning); $fix_it = $this->confirm('These profiles can be automatically decoupled from ' . $info['name'] . '. Should I do that now?', TRUE); if ($fix_it) { array_walk($children, [$this, 'decoupleProfile']); } else { throw new \RuntimeException('These profiles must be decoupled from ' . $info['name'] . ' before uninstallation can continue.'); } } } } /** * Performs required actions before Lightning is uninstalled. * * @hook pre-command pm:uninstall */ public function preCommand() : void { if ($this->getUninstall()) { if ($this->installProfile === 'lightning' || $this->installProfile === 'headless_lightning') { // The lightning_install module was created to prevent broken builds of // Lightning (created by drupal.org's legacy packaging system) from // being installed. $this->drush('pm:uninstall', ['lightning_install']); $profile = $this->input()->getOption('profile'); $this->boldlySay("Switching to $profile profile..."); $this->drush('pm:enable', ['profile_switcher']); $this->drush('switch:profile', [$profile]); } $this->alterProject(); } } /** * Performs required actions after Lightning is uninstalled. * * @hook post-command pm:uninstall */ public function postCommand() : void { $profile = $this->getUninstall(); if ($profile) { $this->drush('pm:uninstall', ['profile_switcher']); $info = $this->profileList->getExtensionInfo($profile); $this->io()->success([ "Congrats, " . $info['name'] . " has been uninstalled!", "You should now commit code and configuration changes, and deploy them to your hosting environment.", ]); } } /** * Returns installed extensions in the Lightning profile directory. * * @return string[] * The names of installed extensions in the Lightning profile directory. * If there are any, Lightning cannot be uninstalled. There shouldn't * normally be any -- Lightning doesn't ship with any runtime modules -- * but it's wise to check anyway in case the current site has an exotic * set-up. */ private function getExtensionsInProfileDirectory() : array { $extensions = array_merge( $this->moduleHandler->getModuleList(), $this->themeHandler->listInfo() ); $profile_path = $extensions['lightning']->getPath(); unset($extensions['lightning']); // The lightning_install module is a special module that was created to // prevent installation of broken builds of Lightning created by the legacy // drupal.org packaging system. This utility uninstalls it along with the // profile. unset($extensions['lightning_install']); $filter = function (Extension $extension) use ($profile_path) : bool { return strpos($extension->getPath(), $profile_path) !== FALSE; }; $extensions = array_filter($extensions, $filter); return array_keys($extensions); } /** * Lists all profiles that have a specific profile as their parent. * * @param string $parent * The name of the parent profile. * * @return string[] * The machine names of all profiles, installed or not, that have the given * parent. */ private function getChildren(string $parent) : array { $children = []; foreach ($this->profileList->getAllAvailableInfo() as $name => $info) { if (isset($info['base profile']) && $info['base profile'] === $parent) { $children[] = $name; } } return $children; } /** * Uncouples a profile from its parent. * * This will modify the profile's info file to remove the dependency on * the parent, then copy all of the parent's default configuration into the * profile's optional config directory. Existing config is preserved, as are * any info file customizations. * * @param string $name * The machine name of the sub-profile. */ private function decoupleProfile(string $name) : void { $target = $this->readInfo($name); $parent_key = $target['base profile']; $parent = $this->readInfo($parent_key); $io = $this->io(); $io->section("Decoupling $name from " . $parent['name']); unset($target['base profile']); // This strips out the project prefix from a dependency. For example, this // will convert 'drupal:views' to just 'views'. $map = function (string $name) : string { $name = explode(':', $name, 2); return end($name); }; $exclude = array_map($map, $target['exclude'] ?? []); unset($target['exclude']); $install = array_map($map, $target['install'] ?? []); // Add all of the parent's dependencies, except for excluded ones. $install = array_merge($install, $parent['install']); $target['install'] = $this->arrayDiff($install, $exclude); // Add all of the parent's themes, except for excluded ones. $themes = array_merge($target['themes'] ?? [], $parent['themes']); $target['themes'] = $this->arrayDiff($themes, $exclude); // If the parent is listed as an explicit dependency, remove that. if (isset($target['dependencies'])) { $target['dependencies'] = $this->arrayDiff($target['dependencies'], [$parent_key]); } $destination = $this->profileList->getPathname($name); $success = file_put_contents($destination, Yaml::encode($target)); if ($success) { $this->say("Updated $destination."); } else { throw new IOException("Unable to write to $destination."); } $this->copyConfiguration($parent_key, $name); $io->success("$name has been decoupled from " . $parent['name'] . "."); } /** * Returns the difference between two arrays. * * @param array $a * An array of values. * @param array $b * Another array of values. * * @return array * The items which are in $a but not $b, numerically re-indexed. All * duplicate values will be removed. */ private function arrayDiff(array $a, array $b) : array { $c = array_diff($a, $b); $c = array_unique($c); return array_values($c); } /** * Reads the info file of a profile. * * @param string $name * The machine name of the profile. * * @return array * The parsed profile info. */ private function readInfo(string $name) : array { $info = $this->profileList->getPathname($name); $info = file_get_contents($info); return Yaml::decode($info); } /** * Copies all config from one profile into another. * * @param string $source * The profile from which the config should be copied. * @param string $target * The profile into which the config should be copied. */ private function copyConfiguration(string $source, string $target) : void { $destination_dir = $this->profileList->getPath($target) . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY; $this->fileSystem->prepareDirectory($destination_dir, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); $info = $this->profileList->getExtensionInfo($source); $this->boldlySay("Copying " . $info['name'] . " configuration to $target..."); foreach ($this->getConfigurationToCopy($source) as $name => $path) { $destination = sprintf('%s/%s.%s', $destination_dir, $name, FileStorage::getFileExtension()); $this->say($destination); try { $this->fileSystem->copy($path, $destination, FileSystemInterface::EXISTS_ERROR); } catch (FileExistsException $e) { $this->io()->note($e->getMessage()); } } } /** * Lists all config that ships with the a profile. * * @param string $profile * The name of the profile. * * @return string[] * An array of config that ships with the given profile. The keys will be * the config names, and the values will be the paths of the config files, * relative to the Drupal root. */ private function getConfigurationToCopy(string $profile) : array { $base_dir = $this->profileList->getPath($profile); $directories = array_filter([ $base_dir . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY, $base_dir . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY, ], 'is_dir'); $list = []; foreach ($directories as $dir) { $storage = new FileStorage($dir); foreach ($storage->listAll() as $name) { $list[$name] = $storage->getFilePath($name); } } return $list; } /** * Returns the location of the project-level composer.json. * * @return string * The path of the project-level composer.json. */ private function locateProjectFile() : string { $finder = new DrupalFinder(); if ($finder->locateRoot($this->appRoot) == FALSE) { throw new \LogicException("Could not locate the Drupal root."); } $target = $finder->getComposerRoot() . DIRECTORY_SEPARATOR . 'composer.json'; assert(file_exists($target), "Expected $target to exist, but it does not."); return $target; } /** * Alters the project-level composer.json to uninstall Lightning. */ private function alterProject() : void { $target = $this->input()->getOption('composer'); $this->boldlySay("Modifying $target..."); $this->io()->listing([ 'Ensuring direct Lightning dependencies are required', 'Ensuring required repositories are present', 'Adding required patches', 'Checking patcher configuration', 'Checking installer configuration', 'Checking scaffold configuration', ]); $file = new JsonFile($target); $target = $file->read(); // Read Lightning's composer.json, since we will need to merge in many // default values from it. $source = new JsonFile(__DIR__ . '/../../composer.json'); $source = $source->read(); $source += [ 'extra' => [], ]; // Sanity check that we have the correct composer.json. assert($source['name'] === 'acquia/lightning'); $data = $this->mergeCanadian($target, [ 'require' => $this->getRequirements($target, $source), 'extra' => [ 'composer-exit-on-patch-failure' => $extra['composer-exit-on-patch-failure'] ?? TRUE, 'drupal-scaffold' => [ 'locations' => [ 'web-root' => $this->getDrupalRoot($target, $source) . '/', ], ], 'enable-patching' => $source['extra']['enable-patching'] ?? TRUE, 'installer-paths' => $this->getPaths($target, $source), 'installer-types' => $this->getPackageTypes($target), 'patchLevel' => $source['extra']['patchLevel'] ?? [], 'patches' => $source['extra']['patches'] ?? [], 'patches-ignore' => $source['extra']['patches-ignore'] ?? [], ], 'repositories' => $this->getRepositories($target), ]); // If the project requires Headless Lightning, we need to switch it to // Headless Lightning 2 to ensure that the modules which are part of // Headless Lightning will continue to be present after uninstall. if (isset($data['require']['acquia/headless_lightning'])) { $data['require']['acquia/headless_lightning'] = 'dev-eol'; } // Delete any empty arrays, since they will be encoded as empty arrays and // may therefore break the composer.json schema. // @todo Handle this recursively. $data = array_filter($data, function ($item) { return is_array($item) ? (bool) $item : TRUE; }); $file->write($data); } /** * Returns the combined requirements for the target package. * * @param array $target * The target package's configuration. * @param array $source * The source package's configuration. * * @return array * The combined requirements to add to the target package. The keys will * be package names and the values will be version constraints. */ private function getRequirements(array $target, array $source) : array { $requirements = []; // Lightning requires composer/composer in order for this uninstaller to // alter composer.json correctly. When that's done, we don't need it // anymore. unset($source['require']['composer/composer']); // The target package's existing dependencies should supersede any // dependencies defined by the source package (Lightning). $requirements += ($target['require'] ?? []); $requirements += ($source['require'] ?? []); // If the target package is not using the deprecated scaffold plugin, use // the one that ships with Drupal core. On the other hand, if the target // package *is* using the deprecated plugin, they are on their own. if (empty($requirements['drupal-composer/drupal-scaffold'])) { $requirements += [ 'drupal/core-composer-scaffold' => $requirements['drupal/core'], ]; } return $requirements; } /** * Returns the package types to expose to the Composer installers extender. * * @param array $target * The target package's configuration. * * @return string[] * The package types to expose to the Composer installers extender plugin * (oomphinc/composer-installers-extender), if available. */ private function getPackageTypes(array $target) : array { $installer_types = $target['extra']['installer-types'] ?? []; // Ensure that npm-asset and bower-asset are known package types. array_push($installer_types, 'npm-asset', 'bower-asset'); return array_unique($installer_types); } /** * Returns the combined installer paths for the target package. * * @param array $target * The target package's configuration. * @param array $source * The source package's configuration. * * @return array[] * An array of paths to be used by the composer/installers plugin. */ private function getPaths(array $target, array $source) : array { $root_dir = $this->getDrupalRoot($target, $source); // If we don't know where Drupal core is installed, we cannot possibly // determine where modules, themes, etc. should go. if (empty($root_dir)) { throw new \LogicException("Cannot determine the Drupal root."); } $path_map = $this->getPathMap($target, $source); $path_map += [ 'type:drupal-module' => $root_dir . '/modules/contrib/{$name}', 'type:drupal-custom-module' => $root_dir . '/modules/custom/{$name}', 'type:drupal-profile' => $root_dir . '/profiles/contrib/{$name}', 'type:drupal-theme' => $root_dir . '/themes/contrib/{$name}', 'type:drupal-custom-theme' => $root_dir . '/themes/custom/{$name}', 'type:drupal-library' => $root_dir . '/libraries/{$name}', 'type:npm-asset' => $root_dir . '/libraries/{$name}', 'type:bower-asset' => $root_dir . '/libraries/{$name}', ]; // If the target package uses Headless Lightning, it should be treated as // a normal module. if (isset($target['require']['acquia/headless_lightning'])) { $path_map += [ 'acquia/headless_lightning' => $path_map['type:drupal-module'], ]; } $paths = []; foreach ($path_map as $package => $location) { $paths[$location][] = $package; } return $paths; } /** * Returns the combined repositories for the target package. * * @param array $target * The target package's configuration. * * @return array[] * An array of Composer repository definitions to add to the target package. */ private function getRepositories(array $target) : array { $repositories = []; $source_repositories = [ 'https://packages.drupal.org/8', 'https://asset-packagist.org', ]; $target_repositories = []; foreach (($target['repositories'] ?? []) as $repository) { if ($repository['type'] === 'composer') { $target_repositories[] = $repository['url']; } } // Ensure that the two repositories listed in $source_repositories are // added to the target package's repositories. $repositories_to_add = array_diff($source_repositories, $target_repositories); foreach ($repositories_to_add as $url) { $repositories[] = [ 'type' => 'composer', 'url' => $url, ]; } return $repositories; } /** * Returns a map of locations where packages will be installed. * * @param array $target * The target package's configuration. * @param array $source * The source package's configuration. * * @return string[] * A map where the keys are the package, or package type, to install (e.g., * 'drupal/dropzonejs' or 'type:drupal-theme') and the values are the * location where that package or package type will be installed, relative * to the target package. */ private function getPathMap(array $target, array $source) : array { // Try to get the installer-paths configuration from the target package, // falling back to the source package (Lightning) in the unlikely event // that the target package has not configured this. $extra = isset($target['extra']['installer-paths']) ? $target['extra'] : $source['extra']; $path_map = []; foreach ($extra['installer-paths'] as $location => $packages) { foreach ($packages as $package) { $path_map[$package] = $location; } } return $path_map; } /** * Returns the path to the Drupal root, relative to the target package. * * @param array $target * The target package's configuration. * @param array $source * The source package (i.e., Lightning)'s configuration. * * @return string|null * The path to the Drupal root, relative to the target package, e.g., * 'docroot', or NULL if it cannot be determined. */ private function getDrupalRoot(array $target, array $source) : ?string { $path_map = $this->getPathMap($target, $source); // We expect that the path map has an install location for Drupal core. If // it doesn't, that's a pretty major error condition; in such a case, it's // not clear how their code base could even be working. Maybe it's a bizarre // set-up (symlink jungle?) that we don't support. $core_location = $path_map['drupal/core'] ?? $path_map['type:drupal-core']; return $core_location ? dirname($core_location) : NULL; } /** * Recursively merges two associative arrays, preserving existing items. * * @param array $a * The array which $b will be merged into. * @param array $b * The array to merge into $a. * * @return array * The merged array. */ private function mergeCanadian(array $a, array $b) : array { $a += $b; foreach ($a as $k => $v) { if (is_array($v) && isset($b[$k]) && is_array($b[$k])) { $a[$k] = $this->mergeCanadian($a[$k], $b[$k]); } } return $a; } /** * Wrapper around ::say() which displays the text in bold. * * @param string $text * The text to display. */ private function boldlySay(string $text) : void { $this->writeln("$text"); } } ================================================ FILE: src/Composer/AssetPackagist.php ================================================ exists()) { $info = $file->read(); if (isset($info['require']['acquia/lightning'])) { return $file; } } chdir('..'); array_pop($dir); } while ($dir); throw new \RuntimeException('Could not locate the root package.'); } /** * Executes the script. * * @param \Composer\Script\Event $event * The script event. */ public static function execute(Event $event) { $io = $event->getIO(); // Search upwards for a composer.json which depends on acquia/lightning. $io->write('Searching for root package...'); $file = static::getRootPackage(); $package = $file->read(); // Add the Asset Packagist repository if it does not already exist. if (isset($package['repositories'])) { $repository_key = NULL; foreach ($package['repositories'] as $key => $repository) { if ($repository['type'] == 'composer' && strpos($repository['url'], 'https://asset-packagist.org') === 0) { $repository_key = $key; break; } } if (is_null($repository_key)) { $package['repositories']['asset-packagist'] = [ 'type' => 'composer', 'url' => 'https://asset-packagist.org', ]; } } // oomphinc/composer-installers-extender is required by Lightning and // depends on composer/installers, so it does not need to be specifically // included. unset( $package['require']['composer/installers'], $package['require']['oomphinc/composer-installers-extender'] ); $package['extra']['installer-types'][] = 'bower-asset'; $package['extra']['installer-types'][] = 'npm-asset'; $package['extra']['installer-paths']['docroot/libraries/{$name}'][] = 'type:bower-asset'; $package['extra']['installer-paths']['docroot/libraries/{$name}'][] = 'type:npm-asset'; $file->write($package); $io->write('Successfully updated your root composer.json file. Switch back to your project root and run "composer update".'); } } ================================================ FILE: src/Composer/ConfigureLegacyProject.php ================================================ getArguments(); $target = new JsonFile($arguments[0] . '/composer.json'); $project = $target->read(); $project['extra']['installer-paths']['libraries/{$name}'] = [ 'type:drupal-library', 'type:bower-asset', 'type:npm-asset', ]; $project['extra']['installer-types'] = ['bower-asset', 'npm-asset']; $project['extra']['patchLevel']['drupal/core'] = '-p2'; $project['extra']['enable-patching'] = TRUE; // Composer doesn't like empty sections of composer.json, so // filter those out before we write the configuration. $project = array_filter($project); $target->write($project); } } ================================================ FILE: src/Composer/PatchedConstraint.php ================================================ getComposer()->getPackage(); $patched_dependencies = static::getPatchedDependencyConstraints($root_package); $error = []; /** @var \Composer\Package\Link $package */ foreach ($patched_dependencies as $package) { if (static::packageIsUnpinned($package)) { $error[] = $package->getTarget() . ': ' . $package->getPrettyConstraint(); } } if (!empty($error)) { array_unshift($error, 'The following dependencies are patched but don\'t have pinned dependency constraints:'); $event->getIO()->writeError($error); return FALSE; } else { $event->getIO()->write('Patched dependencies have constraints that are properly pinned.'); } } /** * Filters the requires section to packages that are patched. * * @param \Composer\Package\RootPackageInterface $root_package * The root composer.json package. * * @return \Composer\Package\Link * List of required packages that are patched. */ protected static function getPatchedDependencyConstraints(RootPackageInterface $root_package) { $required = $root_package->getRequires(); $extra = $root_package->getExtra(); $patched = $extra['patches']; return array_intersect_key($required, $patched); } /** * Determines if a given package's constraint is pinned or not. * * @param \Composer\Package\Link $package * The package to check. * * @return bool * True if the constraint appears to be unpinned. */ protected static function packageIsUnpinned(Link $package) { if ($package->getTarget() == 'drupal/core') { // Bail out if the patched package is drupal/core since we release with // each version of core and always ensure core patches still apply. return FALSE; } $constraint = $package->getPrettyConstraint(); if (preg_match('/[\^~*|]/', $constraint)) { // If ^, ~, *, or | characters are being used, the dependency is not // pinned to a specific release. return TRUE; } } } ================================================ FILE: src/Generators/SubProfileGenerator.php ================================================ moduleList = $module_list; // Drush doesn't support setting $this->destination to 'profiles/custom', // so we need to use a callback function instead. This is ignored if // the --directory option is set; in that case, the profile will be // generated at $custom_directory/$machine_name. // @see \Drush\Commands\generate\Helper\InputHandler::collectVars() // @see \Drush\Commands\generate\Helper\InputHandler::getDirectory() $this->setDestination(function () { return 'profiles/custom'; }); } /** * {@inheritdoc} */ protected function interact(InputInterface $input, OutputInterface $output) { $io = new DrushStyle($input, $output); $questions['name'] = new Question('Profile Name'); $questions['name']->setValidator([Utils::class, 'validateRequired']); $questions['machine_name'] = new Question('Profile Machine Name (enter for default)'); $questions['machine_name']->setValidator([Utils::class, 'validateMachineName']); $questions['description'] = new Question('Enter the description (optional)'); $questions['install'] = new Question('Additional modules to include (optional), separated by commas (e.g. context, rules, file_entity)', NULL); $questions['install']->setNormalizer([static::class, 'toArray']); $vars = &$this->collectVars($input, $output, $questions); if ($io->confirm('Do you want to exclude any components of Lightning?', FALSE)) { $filter = function ($name) { return strpos($name, 'lightning_') === 0; }; $modules = array_filter($this->moduleList->getAllAvailableInfo(), $filter, ARRAY_FILTER_USE_KEY); $map = function (array $info) { return $info['name']; }; $modules = array_map($map, $modules); $questions['exclude'] = new ChoiceQuestion('Lightning components to exclude (optional), entered as keys separated by commas (e.g. lightning_media, lightning_layout)', $modules); $questions['exclude']->setMultiselect(TRUE); $this->collectVars($input, $output, $questions); } $info_array = [ 'name' => $vars['name'], 'type' => 'profile', 'description' => '', 'core_version_requirement' => '^8.8 || ^9', 'install' => [], 'themes' => [ 'bartik', 'seven', ], 'base profile' => 'lightning', 'exclude' => [], ]; if ($vars['description']) { $info_array['description'] = $vars['description']; } if ($vars['install']) { $info_array['install'] = $vars['install']; } if ($vars['exclude']) { $info_array['exclude'] = is_string($vars['exclude']) ? static::toArray($vars['exclude']) : $vars['exclude']; } $info_array = array_filter($info_array); $this->addFile() ->path("{machine_name}/{machine_name}.info.yml") ->content(Yaml::encode($info_array)); $this->addFile() ->path("{machine_name}/{machine_name}.install") ->template('install.twig'); $this->addFile() ->path("{machine_name}/{machine_name}.profile") ->template('profile.twig'); } /** * Converts a comma-separated string to an array of trimmed values. * * @param string $string * The comma-separated string to split up. * * @return string[] * The comma-separated values, trimmed of white space. */ public static function toArray($string) { return $string ? array_map('trim', explode(',', $string)) : []; } } ================================================ FILE: src/Generators/install.twig ================================================ getDefinition('required_module_uninstall_validator') ->setClass(RequiredModuleUninstallValidator::class); } } ================================================ FILE: src/ProxyClass/RequiredModuleUninstallValidator.php ================================================ container = $container; $this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id; } /** * Lazy loads the real service from the container. * * @return object * Returns the constructed real service. */ protected function lazyLoadItself() { if (!isset($this->service)) { $this->service = $this->container->get($this->drupalProxyOriginalServiceId); } return $this->service; } /** * {@inheritdoc} */ public function validate($module) { return $this->lazyLoadItself()->validate($module); } /** * {@inheritdoc} */ public function setStringTranslation(\Drupal\Core\StringTranslation\TranslationInterface $translation) { return $this->lazyLoadItself()->setStringTranslation($translation); } } } ================================================ FILE: src/RequiredModuleUninstallValidator.php ================================================ appRoot = $app_root; if ($translation) { $this->setStringTranslation($translation); } } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('app.root'), $container->get('string_translation') ); } /** * Converts sub-profile info keys to the 8.6.x API. * * @param \Symfony\Component\Console\Style\StyleInterface $io * The I/O handler. * * @update * * @ask Do you want to update all sub-profiles to be Drupal 8.6 compatible? */ public function updateProfiles(StyleInterface $io) { $discovery = new ExtensionDiscovery($this->appRoot); $profiles = $discovery->scan('profile'); foreach ($profiles as $profile) { $info_file = $profile->getPathname(); if (is_writable($info_file)) { $info = file_get_contents($info_file); if (strstr($info, 'base profile:')) { $info = Yaml::decode($info); if (is_array($info['base profile'])) { $info['base profile'] = $info['base profile']['name']; } if (isset($info['dependencies'])) { $info['install'] = $info['dependencies']; unset($info['dependencies']); } $exclude = []; if (isset($info['excluded_dependencies'])) { $exclude = array_merge($exclude, $info['excluded_dependencies']); unset($info['excluded_dependencies']); } if (isset($info['excluded_themes'])) { $exclude = array_merge($exclude, $info['excluded_themes']); unset($info['excluded_themes']); } if ($exclude) { $info['exclude'] = $exclude; } file_put_contents($info_file, Yaml::encode($info)); $message = $this->t('Updated @profile.', [ '@profile' => $profile->getName(), ]); $io->success((string) $message); } } else { $message = $this->t('Cannot write to @path, skipping.', [ '@path' => $info_file, ]); $io->warning((string) $message); } } } } ================================================ FILE: src/Update/Update405.php ================================================ moduleInstaller = $module_installer; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('module_installer') ); } /** * Enables the Autosave Form and Conflict modules. * * @update * * @ask Do you want to enable the Autosave Form and Conflict modules? */ public function enableAutosaveForm() { $this->moduleInstaller->install(['autosave_form', 'conflict']); } /** * Enables the Redirect module. * * @update * * @ask Do you want to enable the Redirect module? */ public function enableRedirect() { $this->moduleInstaller->install(['redirect']); } } ================================================ FILE: tarball.sh ================================================ #!/bin/bash ARCHIVE=lightning-$1 composer create-project --stability beta --no-install drupal/legacy-project:~9.1.0 $ARCHIVE composer dump-autoload composer configure-tarball $ARCHIVE cd $ARCHIVE composer config minimum-stability dev composer config prefer-stable true composer config repositories.assets composer https://asset-packagist.org composer remove --no-update composer/installers composer require --no-update "ext-dom:*" "acquia/lightning:~5.1.0" cweagans/composer-patches composer update # Add the version number to the info file. echo "version: $1" >> ./profiles/contrib/lightning/lightning.info.yml # Wrap it all up in a nice compressed tarball. cd .. tar --exclude='.DS_Store' --exclude='._*' -c -z -f $ARCHIVE.tar.gz $ARCHIVE # Clean up. rm -r -f $ARCHIVE ================================================ FILE: tests/lightning_extender/lightning_extender.info.yml ================================================ name: 'Lightning Extender' type: profile core_version_requirement: '^8.8 || ^9' install: - ban themes: - bartik - seven 'base profile': lightning exclude: - lightning_search ================================================ FILE: tests/lightning_extender/lightning_extender.profile ================================================ container->get('config.factory'); $config_factory->getEditable('lightning_api.settings') ->set('entity_json', TRUE) ->save(); // If the samlauth module is installed, ensure that it is configured (in // this case, using its own test data) to avoid errors when creating user // accounts in this test. if ($this->container->get('module_handler')->moduleExists('samlauth')) { $path = $this->container->get('extension.list.module') ->getPath('samlauth'); $data = file_get_contents("$path/test_resources/samlauth.authentication.yml"); $data = Yaml::decode($data); $this->container->get('config.factory') ->getEditable('samlauth.authentication') ->setData($data) ->save(); } } /** * Tests viewing a configuration entity as JSON via the API. */ public function testViewConfigEntityAsJson() { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); $account = $this->createUser([], NULL, TRUE); $this->drupalLogin($account); $this->drupalGet('/admin/structure/contact'); $page->clickLink('View JSON'); $assert_session->statusCodeEquals(200); $this->drupalGet('/admin/structure/media'); $page->clickLink('View JSON'); $assert_session->statusCodeEquals(200); } } ================================================ FILE: tests/src/ExistingSite/ConfigIntegrityTest.php ================================================ container->get('module_handler')->moduleExists('samlauth')) { $path = $this->container->get('extension.list.module') ->getPath('samlauth'); $data = file_get_contents("$path/test_resources/samlauth.authentication.yml"); $data = Yaml::decode($data); $this->container->get('config.factory') ->getEditable('samlauth.authentication') ->setData($data) ->save(); } } /** * Tests configuration values were set correctly during installation. */ public function testConfig() { $assert_session = $this->assertSession(); // Assert that all install tasks have done what they should do. // @see lightning_install_tasks() $this->assertSame('/node', $this->config('system.site')->get('page.front')); $this->assertSame(UserInterface::REGISTER_ADMINISTRATORS_ONLY, $this->config('user.settings')->get('register')); $this->assertTrue(Role::load(Role::AUTHENTICATED_ID)->hasPermission('access shortcuts')); $theme_config = $this->config('system.theme'); $this->assertSame('bartik', $theme_config->get('default')); $this->assertSame('claro', $theme_config->get('admin')); $this->assertTrue($this->config('node.settings')->get('use_admin_theme')); $theme_global = $this->config('system.theme.global'); $this->assertStringContainsString('/lightning/lightning.png', $theme_global->get('logo.path')); $this->assertStringContainsString('/lightning/favicon.ico', $theme_global->get('favicon.path')); /** @var \Drupal\views\ViewEntityInterface $view */ $view = View::load('frontpage'); $this->assertInstanceOf(View::class, $view); $display = &$view->getDisplay('default'); $this->assertTrue($display['display_options']['empty']['area_text_custom']['tokenize']); $this->assertStringContainsString('/lightning/README.md', $display['display_options']['empty']['area_text_custom']['content']); // lightning_core_update_8002() marks a couple of core view modes as // internal. $view_modes = EntityViewMode::loadMultiple([ 'node.rss', 'node.search_index', ]); /** @var \Drupal\Core\Entity\EntityViewModeInterface $view_mode */ foreach ($view_modes as $view_mode) { $this->assertTrue($view_mode->getThirdPartySetting('lightning_core', 'internal')); } // All users should be able to view media items. $this->assertPermissions('anonymous', 'view media'); $this->assertPermissions('authenticated', 'view media'); // Media creators can use bulk upload. $this->assertPermissions('media_creator', 'dropzone upload files'); $this->assertEntityExists('node_type', [ 'page', 'landing_page', ]); $this->assertEntityExists('user_role', [ 'landing_page_creator', 'landing_page_reviewer', 'layout_manager', 'media_creator', 'media_manager', 'page_creator', 'page_reviewer', ]); $this->assertEntityExists('crop_type', 'freeform'); $this->assertEntityExists('image_style', 'crop_freeform'); // Assert that the editorial workflow exists and has the review state and // transition. $workflow = Workflow::load('editorial'); $this->assertInstanceOf(Workflow::class, $workflow); /** @var \Drupal\workflows\WorkflowTypeInterface $type_plugin */ $type_plugin = $workflow->getTypePlugin(); // getState() throws an exception if the state does not exist. $type_plugin->getState('review'); // getTransition() throws an exception if the transition does not exist. /** @var \Drupal\workflows\TransitionInterface $transition */ $transition = $type_plugin->getTransition('review'); $this->assertEquals('review', $transition->to()->id()); $from = array_keys($transition->from()); $this->assertContainsAll(['draft', 'review'], $from); $this->assertNotContains('published', $from); $creator_permissions = [ 'use text format rich_text', 'access image_browser entity browser pages', ]; $this->assertPermissions('page_creator', $creator_permissions); $this->assertPermissions('landing_page_creator', $creator_permissions); $this->assertPermissions('layout_manager', [ 'administer node display', 'configure any layout', ]); $node_types = \Drupal::entityQuery('node_type')->execute(); foreach ($node_types as $node_type) { $this->assertPermissions("{$node_type}_creator", [ "create $node_type content", "edit own $node_type content", "view $node_type revisions", 'view own unpublished content', 'create url aliases', 'access in-place editing', 'access contextual links', 'access toolbar', ]); $this->assertPermissions("{$node_type}_reviewer", [ 'access content overview', "edit any $node_type content", "delete any $node_type content", ]); } // Assert that bundled content types have meta tags enabled. $this->assertMetatag(['page', 'landing_page']); // Assert that Lightning configuration pages are accessible to users who // have an administrative role. $this->assertForbidden('/admin/config/system/lightning'); $this->assertForbidden('/admin/config/system/lightning/api'); $this->assertForbidden('/admin/config/system/lightning/layout'); $this->assertForbidden('/admin/config/system/lightning/media'); $account = $this->createUser([], NULL, TRUE); $this->drupalLogin($account); $this->assertAllowed('/admin/config/system/lightning'); $assert_session->linkByHrefExists('/admin/config/system/lightning/api'); $assert_session->linkByHrefExists('/admin/config/system/lightning/layout'); $assert_session->linkByHrefExists('/admin/config/system/lightning/media'); $this->assertAllowed('/admin/config/system/lightning/api'); $this->assertAllowed('/admin/config/system/lightning/api/keys'); $this->assertAllowed('/admin/config/system/lightning/layout'); $this->assertAllowed('/admin/config/system/lightning/media'); } /** * Data provider for testModeratedContentTypes(). * * @return array[] * Sets of arguments to pass to the test method. */ public function providerModeratedContentTypes() { return [ ['page', 'page_creator'], ['page', 'administrator'], ['landing_page', 'landing_page_creator'], ['landing_page', 'administrator'], ]; } /** * Tests that moderated content types do not show a Published checkbox. * * @param string $node_type * The machine name of the content type to test. * @param string $role * The machine name of the user role to log in with. * * @dataProvider providerModeratedContentTypes */ public function testModeratedContentTypes($node_type, $role) { $assert_session = $this->assertSession(); $account = $this->createUser(); $account->addRole($role); $account->save(); $this->drupalLogin($account); $this->drupalGet("/node/add/$node_type"); $assert_session->statusCodeEquals(200); $assert_session->buttonExists('Save'); $assert_session->fieldNotExists('status[value]'); $assert_session->buttonNotExists('Save and publish'); $assert_session->buttonNotExists('Save as unpublished'); } /** * Asserts that meta tags are enabled for specific content types. * * @param string[] $node_types * The node type IDs to check. */ private function assertMetatag(array $node_types) { $assert = $this->assertSession(); $permissions = array_map( function ($node_type) { return "create $node_type content"; }, $node_types ); $account = $this->createUser($permissions); $this->drupalLogin($account); foreach ($node_types as $node_type) { $this->assertAllowed("/node/add/$node_type"); $assert->fieldExists('field_meta_tags[0][basic][title]'); $assert->fieldExists('field_meta_tags[0][basic][description]'); } $this->drupalLogout(); } /** * Asserts the existence of an entity. * * @param string $entity_type * The entity type ID. * @param mixed|mixed[] $id * The entity ID, or a set of IDs. */ private function assertEntityExists($entity_type, $id) { $this->assertContainsAll( (array) $id, \Drupal::entityQuery($entity_type)->execute() ); } /** * Asserts that a user role has a set of permissions. * * @param \Drupal\user\RoleInterface|string $role * The user role, or its ID. * @param string|string[] $permissions * The permission(s) to check. */ private function assertPermissions($role, $permissions) { if (is_string($role)) { $role = Role::load($role); } $this->assertContainsAll((array) $permissions, $role->getPermissions()); } /** * Asserts that a haystack contains a set of needles. * * @param mixed[] $needles * The needles expected to be in the haystack. * @param mixed[] $haystack * The haystack. */ private function assertContainsAll(array $needles, array $haystack) { $diff = array_diff($needles, $haystack); $this->assertSame([], $diff); } /** * Asserts that the current user can access a Drupal route. * * @param string $path * The route path to visit. */ private function assertAllowed($path) { $this->drupalGet($path); $this->assertSession()->statusCodeEquals(200); } /** * Asserts that the current user cannot access a Drupal route. * * @param string $path * The route path to visit. */ private function assertForbidden($path) { $this->drupalGet($path); $this->assertSession()->statusCodeEquals(403); } /** * Returns a config object by its name. * * @param string $name * The name of the config object to return. * * @return \Drupal\Core\Config\Config * The config object. */ private function config($name) { return $this->container->get('config.factory')->getEditable($name); } } ================================================ FILE: tests/src/ExistingSite/ViewModeTest.php ================================================ 'node.foobaz', 'label' => 'Foobaz', 'targetEntityType' => 'node', ]); $view_mode ->setThirdPartySetting('lightning_core', 'internal', TRUE) ->setThirdPartySetting('lightning_core', 'description', 'Behold, my glorious view mode.') ->save(); } /** * Tests that users are informed about internal view modes. */ public function testInternalViewMode() { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); $account = $this->createUser(['administer node display']); $this->drupalLogin($account); $this->drupalGet('/admin/structure/types/manage/page/display'); $page->checkField('Foobaz'); $page->pressButton('Save'); $assert_session->elementTextContains('css', '.messages--status', 'The Foobaz mode now uses custom display settings.'); $page->find('css', '.messages--status')->clickLink('configure them'); $assert_session->elementTextContains('css', '.messages--warning', "This display is internal and will not be seen by normal users."); $assert_session->pageTextContains('Behold, my glorious view mode.'); } /** * {@inheritdoc} */ public function tearDown() { EntityViewMode::load('node.foobaz')->delete(); parent::tearDown(); } } ================================================ FILE: tests/src/Functional/LightningTest.php ================================================ assertNotEmpty($root); // Symlink the sub-profile into a place where Drupal will be able to find // it. The symlink is deleted in tearDown(). If the symlink cannot be // created, fail the test. $target = __DIR__ . '/../../' . $this->profile; $this->assertDirectoryIsReadable($target); $link = "$root/profiles/$this->profile"; $this->assertDirectoryIsWritable(dirname($link)); // symlink() is called strangely in order to evade a too-strict coding // standards check. $success = call_user_func('symlink', $target, $link); $this->assertTrue($success, "Could not symlink $link to $target"); $this->assertDirectoryIsReadable($target); parent::setUp(); } /** * {@inheritdoc} */ protected function tearDown() { unlink("$this->root/profiles/$this->profile"); parent::tearDown(); } /** * Tests integrated functionality of the Lightning profile. * * Because it takes aeons to install the Lightning profile, or any of its * descendants, this test only has one public test method, with private helper * methods covering specific test scenarios. This is done purely for * performance reasons. */ public function testLightning() { // Test that the sub-profile was installed... $this->assertSame('lightning_extender', $this->container->getParameter('install_profile')); $module_list = $this->container->get('extension.list.module')->getAllInstalledInfo(); // ...and that the changes it makes are reflected in the system. $this->assertArrayHasKey('ban', $module_list); $this->assertArrayNotHasKey('lightning_search', $module_list); // Lightning's configuration overrides should have taken effect. $this->assertSame(UserInterface::REGISTER_ADMINISTRATORS_ONLY, $this->config('user.settings')->get('register')); $this->doModerationDashboardTest(); $this->doTextBlockTest(); } /** * Tests that the moderation dashboard works. */ private function doModerationDashboardTest() { $account = $this->drupalCreateUser(['use moderation dashboard']); $this->drupalLogin($account); $this->drupalGet('/user/' . $account->id() . '/moderation/dashboard'); $this->assertSession()->statusCodeEquals(200); $this->drupalLogout(); } /** * Tests the 'text' custom block type that ships with Lightning. */ private function doTextBlockTest() { $assert_session = $this->assertSession(); // Assert that basic blocks expose a Body field. $account = $this->createUser(['administer blocks']); $this->drupalLogin($account); $this->drupalGet('/block/add/text'); $assert_session->statusCodeEquals(200); $assert_session->fieldExists('Body'); $this->drupalLogout(); } } ================================================ FILE: tests/src/Functional/SubprofileGeneratorTest.php ================================================ [ 'answers' => [ 'install' => ['consumers'], ], ], 'with exclusions' => [ 'answers' => [ 'exclude' => 'lightning_search, lightning_media_instagram', ], ], 'inclusions and exclusions' => [ 'answers' => [ 'install' => ['consumers', 'book'], 'exclude' => 'lightning_media_instagram', ], ], 'default answers' => [ 'answers' => [], ], 'specific machine name and description' => [ 'answers' => [ 'machine_name' => 'something_different', 'description' => 'This profile rules.', ], ], ]; } /** * Tests the lightning-subprofile generator for Drush. * * @param array $answers * The answers to the generator's questions, keyed by the questions' machine * names. * * @see \Drupal\lightning\Generators\SubProfileGenerator::interact() * * @dataProvider provider */ public function test(array $answers = []) { $drupal_root = $this->getDrupalRoot(); $this->assertFileExists("$drupal_root/../composer.json", 'A composer.json file must exist in the directory above the Drupal root.'); // We need Drush to run commands defined by the Lightning profile, but we // don't want to actually install the Lightning profile into the test site // because it takes freaking forever. So, this hack is a quick way to expose // our generator to Drush. $this->config('core.extension') ->set('module.lightning', 0) ->set('profile', 'lightning') ->save(); $answers += [ 'name' => 'Wizards', 'machine_name' => 'wizards', // Ideally, these would be empty strings, but it seems that Symfony // Console (or possibly Drush) mangles empty strings in JSON-encoded data, // results in the --answers option containing invalid JSON. Setting these // to NULL is a workaround, but it achieves the desired result. 'description' => NULL, 'install' => NULL, 'exclude' => NULL, ]; $options = [ 'answers' => Json::encode($answers), // Generate the profile in the test site's directory, so that it will be // automatically cleaned up when the test is done. 'directory' => "$drupal_root/$this->siteDirectory/profiles/custom", ]; // The generator will ask if we want to exclude any components. We need to // explicitly answer the prompt either way, since we are running the command // quasi-interactively. if ($answers['exclude']) { $options['yes'] = NULL; } else { $options['no'] = NULL; } $machine_name = $answers['machine_name']; // DrushTestTrait::drush() will invoke the command with the --no-interaction // option, bypassing the generator's interact() method -- which contains all // the logic we want to test! The SHELL_INTERACTIVE environment variable // forces Symfony Console to run the method anyway. $this->drush('generate', ['lightning-subprofile'], $options, NULL, "$drupal_root/..", 0, NULL, [ 'SHELL_INTERACTIVE' => 1, ]); // Ensure that the new profile is picked up by the extension system. Because // ExtensionDiscovery does not expect the available extensions to change in // the course of a single request, and has no public method to reset its // internal static cache, we need to pry open its static $files property // and force-reset it. $reflector = new \ReflectionClass('\Drupal\Core\Extension\ExtensionDiscovery'); $property = $reflector->getProperty('files'); $property->setAccessible(TRUE); $property->setValue([]); $extension = $this->container->get('extension.list.profile') ->reset() ->get($machine_name); // Get the raw info for the generated profile. The profile list's // getExtensionInfo() method would merge in default values from the // extension system and ancestral profiles, which interfere with these // assertions. $info = file_get_contents($extension->getPathname()); $info = Yaml::decode($info); // Assert the constant values. $this->assertSame($answers['name'], $info['name']); $this->assertSame('profile', $info['type']); $this->assertArrayNotHasKey('core', $info); $this->assertNotEmpty($info['core_version_requirement']); $this->assertSame(['bartik', 'seven'], $info['themes']); $this->assertSame('lightning', $info['base profile']); // Assert the stuff that can change depending on user input. if ($answers['description']) { $this->assertSame($answers['description'], $info['description']); } else { $this->assertArrayNotHasKey('description', $info); } if ($answers['install']) { $this->assertSame($answers['install'], $info['install']); } else { $this->assertArrayNotHasKey('install', $info); } if ($answers['exclude']) { $exclude = SubProfileGenerator::toArray($answers['exclude']); foreach ($exclude as $module) { $this->assertContains($module, $info['exclude']); } } else { $this->assertArrayNotHasKey('exclude', $info); } // Load up the new profile to ensure it's valid PHP and includes an install // hook. $module_handler = $this->container->get('module_handler'); $module_handler->addProfile($machine_name, $extension->getPath()); $module_handler->load($machine_name); $module_handler->loadInclude($machine_name, 'install'); $this->assertTrue(function_exists($machine_name . '_install')); // Ensure the .profile file at least includes a comment. $file = $module_handler->getModule($machine_name)->getExtensionPathname(); $this->assertNotEmpty(file_get_contents($file)); } } ================================================ FILE: tests/src/Functional/UpdatePath3xTest.php ================================================ databaseDumpFiles = [ __DIR__ . '/../../fixtures/3.4.0.php.gz', ]; } /** * Tests updating from Lightning 3.4.0 via the UI. */ public function test() { require_once __DIR__ . '/../../update.php'; $this->getRandomGenerator()->image('public://star_2.png', '16x16', '16x16'); $this->runUpdates(); $this->drush('update:lightning', [], ['yes' => NULL]); } } ================================================ FILE: tests/src/Functional/UpdatePath4xTest.php ================================================ databaseDumpFiles = [ __DIR__ . '/../../fixtures/4.1.0-beta1.php.gz', ]; } /** * {@inheritdoc} */ protected function replaceUser1() { // When the parent method re-saves the user account, an obscure code path // through Layout Builder and Lightning Media is triggered, resulting in // the use of an old media source plugin ID that will not exist after the // updates are run. Normally, Lightning Media accounts for this by aliasing // the old plugin ID, but that is only done if the kernel is an // UpdateKernel...which is NOT the case in this test's memory space. So, // although this is dirty and brittle, I don't know of a better way around // it; we simply need to have a (mocked) UpdateKernel in place when the // media source plugin definition cache is rebuilt. $kernel = $this->container->get('kernel'); $this->container->set('kernel', $this->prophesize(UpdateKernel::class)->reveal()); // Clear the cached plugin definitions, then rebuild them. The fact that // an UpdateKernel is present means that the old media source plugin ID // should be aliased and cached correctly. // @see lightning_media_instagram_media_source_info_alter() $plugin_manager = $this->container->get('plugin.manager.media.source'); $plugin_manager->clearCachedDefinitions(); $plugin_manager->getDefinitions(); // Restore the real kernel and proceed merrily on our way. $this->container->set('kernel', $kernel); parent::replaceUser1(); } /** * Tests updating from Lightning 4.1.0-beta1 via the UI. */ public function test() { require_once __DIR__ . '/../../update.php'; $this->getRandomGenerator()->image('public://star.png', '16x16', '16x16'); $this->runUpdates(); $this->drush('update:lightning', [], ['yes' => NULL]); } } ================================================ FILE: tests/src/Kernel/Update405Test.php ================================================ container->get('module_handler'); $this->assertFalse($moduleHandler->moduleExists('autosave_form')); $this->assertFalse($moduleHandler->moduleExists('conflict')); $this->assertFalse($moduleHandler->moduleExists('redirect')); $update = Update405::create($this->container); $update->enableAutosaveForm(); $update->enableRedirect(); $moduleHandler = $this->container->get('module_handler'); $this->assertTrue($moduleHandler->moduleExists('autosave_form')); $this->assertTrue($moduleHandler->moduleExists('conflict')); $this->assertTrue($moduleHandler->moduleExists('redirect')); } } ================================================ FILE: tests/travis/install.sh ================================================ #!/usr/bin/env bash # NAME # install.sh - Install Travis CI dependencies # # SYNOPSIS # install.sh # # DESCRIPTION # Creates the test fixture. cd "$(dirname "$0")" # Reuse ORCA's own includes. source ../../../orca/bin/travis/_includes.sh # Handle the special case of scanning for deprecations in contrib dependencies. # We need to ensure that the components are included as part of the SUT, # but none of the other Acquia product modules are. if [[ "$ORCA_JOB" == "DEPRECATED_CODE_SCAN_W_CONTRIB" ]]; then export ORCA_PACKAGES_CONFIG=../lightning/tests/packages.yml orca fixture:init -f --sut="$ORCA_SUT_NAME" --dev --no-site-install exit 0 fi # When testing the SUT in isolation using dev package versions, treat the # components as part of the SUT, to be installed in an isolated (SUT-only) # fixture. if [[ "$ORCA_JOB" == "ISOLATED_TEST_ON_CURRENT_DEV" ]]; then export ORCA_PACKAGES_CONFIG=../lightning/tests/packages.yml orca fixture:init -f --sut="$ORCA_SUT_NAME" --dev --profile=lightning else # Run ORCA's standard installation script. ../../../orca/bin/travis/install.sh fi # If there is no fixture, there's nothing else for us to do. [[ -d "$ORCA_FIXTURE_DIR" ]] || exit 0 # Add testing dependencies. composer -d"$ORCA_FIXTURE_DIR" require --dev weitzman/drupal-test-traits:dev-master phpspec/prophecy-phpunit:^2 ================================================ FILE: tests/update.php ================================================ getEditable('core.extension') ->clear('module.libraries') ->clear('module.openapi_redoc') ->save(); Drupal::configFactory() ->getEditable('libraries.settings') ->delete(); foreach (['default', 'embedded'] as $view_mode) { Drupal::configFactory() ->getEditable("core.entity_view_display.media.instagram.$view_mode") ->set('content.embed_code.settings.hidecaption', NULL) ->save(); } Drupal::keyValue('system.schema')->deleteMultiple([ 'libraries', 'openapi_redoc', ]); $view = View::load('media'); $display = &$view->getDisplay('default'); $display['display_options']['fields']['media_bulk_form']['plugin_id'] = 'bulk_form'; $view->save(); Drupal::service('extension.list.profile')->reset();