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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
================================================
FILE: README.md
================================================
# Drupal Lightning

## 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
================================================
<?php
/**
* @file
* Install and uninstall functions for the Lightning installation profile.
*/
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\field\Entity\FieldConfig;
use Drupal\lightning_core\ConfigHelper as Config;
/**
* Makes updates to some basic config that ships with Lightning.
*
* Install the body field on the basic block_content type, delete the Create
* landing page shortcut, and uninstall the Media Demo Content Module.
*/
function lightning_update_8001() {
// Install the body field on the basic block_content type.
$field = FieldConfig::load('block_content.basic.body');
if (empty($field)) {
$config = Config::forModule('lightning');
$config->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
================================================
<?php
/**
* @file
* The Lightning profile.
*/
use Drupal\user\RoleInterface;
/**
* Implements hook_install_tasks().
*/
function lightning_install_tasks(array &$install_state) {
$tasks = [];
// All of these tasks modify configuration, so don't do any of them if
// we're installing from existing config.
if (empty($install_state['config_install_path'])) {
$tasks['lightning_set_front_page'] = [];
$tasks['lightning_grant_shortcut_access'] = [];
$tasks['lightning_set_default_theme'] = [];
$tasks['lightning_set_logo'] = [];
$tasks['lightning_alter_frontpage_view'] = [];
}
return $tasks;
}
/**
* Sets the front page path to /node.
*/
function lightning_set_front_page() {
if (Drupal::moduleHandler()->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", '<p>Welcome to [site:name]. No front page content has been created yet.</p><p>Would you like to <a href="/' . drupal_get_path('profile', 'lightning') . '/README.md">view the README</a>?</p>')
->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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!-- TODO set checkForUnintentionallyCoveredCode="true" once https://www.drupal.org/node/2626832 is resolved. -->
<!-- PHPUnit expects functional tests to be run with either a privileged user
or your current system user. See core/tests/README.md and
https://www.drupal.org/node/2116263 for details.
-->
<phpunit bootstrap="../../vendor/weitzman/drupal-test-traits/src/bootstrap.php" colors="true"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutChangesToGlobalState="true">
<!-- TODO set printerClass="\Drupal\Tests\Listeners\HtmlOutputPrinter" once
https://youtrack.jetbrains.com/issue/WI-24808 is resolved. Drupal provides a
result printer that links to the html output results for functional tests.
Unfortunately, this breaks the output of PHPStorm's PHPUnit runner. However, if
using the command line you can add
- -printer="\Drupal\Tests\Listeners\HtmlOutputPrinter" to use it (note there
should be no spaces between the hyphens).
-->
<php>
<!-- Set error reporting to E_ALL. -->
<ini name="error_reporting" value="32767"/>
<!-- Do not limit the amount of memory tests take to run. -->
<ini name="memory_limit" value="-1"/>
<!-- Example SIMPLETEST_BASE_URL value: http://localhost -->
<env name="SIMPLETEST_BASE_URL" value="http://127.0.0.1:8080"/>
<env name="DTT_BASE_URL" value="http://127.0.0.1:8080"/>
<!-- Example SIMPLETEST_DB value: mysql://username:password@localhost/databasename#table_prefix -->
<env name="SIMPLETEST_DB" value="sqlite://localhost/db.sqlite"/>
<!-- Example BROWSERTEST_OUTPUT_DIRECTORY value: /path/to/webroot/sites/simpletest/browser_output -->
<env name="BROWSERTEST_OUTPUT_DIRECTORY" value=""/>
<!-- To disable deprecation testing completely uncomment the next line. -->
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak"/>
<!-- Example for changing the driver class for mink tests MINK_DRIVER_CLASS value: 'Drupal\FunctionalJavascriptTests\DrupalSelenium2Driver' -->
<!-- Example for changing the driver args to mink tests MINK_DRIVER_ARGS value: '["http://127.0.0.1:8510"]' -->
<!-- Example for changing the driver args to phantomjs tests MINK_DRIVER_ARGS_PHANTOMJS value: '["http://127.0.0.1:8510"]' -->
<!-- Example for changing the driver args to webdriver tests MINK_DRIVER_ARGS_WEBDRIVER value: '["firefox", null, "http://localhost:4444/wd/hub"]' -->
<env name="MINK_DRIVER_ARGS_WEBDRIVER" value='["chrome", {"chrome": {"switches": ["headless", "disable-gpu", "no-sandbox", "disable-dev-shm-usage", "disable-extensions"]}}, "http://localhost:4444"]'/>
</php>
<testsuites>
<testsuite name="unit">
<file>./tests/TestSuites/UnitTestSuite.php</file>
</testsuite>
<testsuite name="kernel">
<file>./tests/TestSuites/KernelTestSuite.php</file>
</testsuite>
<testsuite name="functional">
<file>./tests/TestSuites/FunctionalTestSuite.php</file>
</testsuite>
<testsuite name="functional-javascript">
<file>./tests/TestSuites/FunctionalJavascriptTestSuite.php</file>
</testsuite>
</testsuites>
<listeners>
<listener class="\Drupal\Tests\Listeners\DrupalListener">
</listener>
<!-- The Symfony deprecation listener has to come after the Drupal listener -->
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener">
</listener>
</listeners>
<!-- Filter for coverage reports. -->
<filter>
<whitelist>
<directory>./includes</directory>
<directory>./lib</directory>
<directory>./modules</directory>
<directory>../modules</directory>
<directory>../sites</directory>
<!-- By definition test classes have no tests. -->
<exclude>
<directory suffix="Test.php">./</directory>
<directory suffix="TestBase.php">./</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
================================================
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
================================================
<?php
// @codingStandardsIgnoreFile
/**
* @file
* Local development override configuration feature.
*
* To activate this feature, copy and rename it such that its path plus
* filename is 'sites/default/settings.local.php'. Then, go to the bottom of
* 'sites/default/settings.php' and uncomment the commented lines that mention
* 'settings.local.php'.
*
* If you are using a site name in the path, such as 'sites/example.com', copy
* this file to 'sites/example.com/settings.local.php', and uncomment the lines
* at the bottom of 'sites/example.com/settings.php'.
*/
/**
* Assertions.
*
* The Drupal project primarily uses runtime assertions to enforce the
* expectations of the API by failing when incorrect calls are made by code
* under development.
*
* @see http://php.net/assert
* @see https://www.drupal.org/node/2492225
*
* If you are using PHP 7.0 it is strongly recommended that you set
* zend.assertions=1 in the PHP.ini file (It cannot be changed from .htaccess
* or runtime) on development machines and to 0 in production.
*
* @see https://wiki.php.net/rfc/expectations
*/
assert_options(ASSERT_ACTIVE, TRUE);
\Drupal\Component\Assertion\Handle::register();
/**
* Enable local development services.
*/
$settings['container_yamls'][] = DRUPAL_ROOT . '/sites/development.services.yml';
/**
* Show all error messages, with backtrace information.
*
* In case the error level could not be fetched from the database, as for
* example the database connection failed, we rely only on this value.
*/
$config['system.logging']['error_level'] = 'verbose';
/**
* Disable CSS and JS aggregation.
*/
$config['system.performance']['css']['preprocess'] = FALSE;
$config['system.performance']['js']['preprocess'] = FALSE;
/**
* Disable the render cache.
*
* Note: you should test with the render cache enabled, to ensure the correct
* cacheability metadata is present. However, in the early stages of
* development, you may want to disable it.
*
* This setting disables the render cache by using the Null cache back-end
* defined by the development.services.yml file above.
*
* Only use this setting once the site has been installed.
*/
# $settings['cache']['bins']['render'] = 'cache.backend.null';
/**
* Disable caching for migrations.
*
* Uncomment the code below to only store migrations in memory and not in the
* database. This makes it easier to develop custom migrations.
*/
# $settings['cache']['bins']['discovery_migration'] = 'cache.backend.memory';
/**
* Disable Internal Page Cache.
*
* Note: you should test with Internal Page Cache enabled, to ensure the correct
* cacheability metadata is present. However, in the early stages of
* development, you may want to disable it.
*
* This setting disables the page cache by using the Null cache back-end
* defined by the development.services.yml file above.
*
* Only use this setting once the site has been installed.
*/
# $settings['cache']['bins']['page'] = 'cache.backend.null';
/**
* Disable Dynamic Page Cache.
*
* Note: you should test with Dynamic Page Cache enabled, to ensure the correct
* cacheability metadata is present (and hence the expected behavior). However,
* in the early stages of development, you may want to disable it.
*/
# $settings['cache']['bins']['dynamic_page_cache'] = 'cache.backend.null';
/**
* Allow test modules and themes to be installed.
*
* Drupal ignores test modules and themes by default for performance reasons.
* During development it can be useful to install test extensions for debugging
* purposes.
*/
# $settings['extension_discovery_scan_tests'] = TRUE;
/**
* Enable access to rebuild.php.
*
* This setting can be enabled to allow Drupal's php and database cached
* storage to be cleared via the rebuild.php page. Access to this page can also
* be gained by generating a query string from rebuild_token_calculator.sh and
* using these parameters in a request to rebuild.php.
*/
$settings['rebuild_access'] = TRUE;
/**
* Skip file system permissions hardening.
*
* The system module will periodically check the permissions of your site's
* site directory to ensure that it is not writable by the website user. For
* sites that are managed with a version control system, this can cause problems
* when files in that directory such as settings.php are updated, because the
* user pulling in the changes won't have permissions to modify files in the
* directory.
*/
$settings['skip_permissions_hardening'] = TRUE;
$settings['bootstrap_container_definition'] = [
'parameters' => [],
'services' => [
'cache.container' => [
'class' => 'Drupal\Core\Cache\MemoryBackend',
],
],
];
================================================
FILE: src/Commands/Uninstaller.php
================================================
<?php
namespace Drupal\lightning\Commands;
use Composer\Json\JsonFile;
use Consolidation\AnnotatedCommand\CommandData;
use Consolidation\SiteAlias\SiteAliasManagerAwareInterface;
use Consolidation\SiteAlias\SiteAliasManagerAwareTrait;
use Drupal\Component\Serialization\Yaml;
use Drupal\Core\Config\FileStorage;
use Drupal\Core\Config\InstallStorage;
use Drupal\Core\Extension\Extension;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Extension\ProfileExtensionList;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\Core\File\Exception\FileExistsException;
use Drupal\Core\File\FileSystemInterface;
use DrupalFinder\DrupalFinder;
use Drush\Commands\DrushCommands;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Filesystem\Exception\IOException;
/**
* Hooks into Drush to assist with uninstalling Lightning.
*
* @internal
* This class is a completely internal part of Lightning's uninstall system
* and can be changed in any way, or removed outright, at any time without
* warning. External code should not use this class in any way.
*/
final class Uninstaller extends DrushCommands implements SiteAliasManagerAwareInterface {
use SiteAliasManagerAwareTrait;
/**
* The module handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
private $moduleHandler;
/**
* The theme handler service.
*
* @var \Drupal\Core\Extension\ThemeHandlerInterface
*/
private $themeHandler;
/**
* The profile extension list.
*
* @var \Drupal\Core\Extension\ProfileExtensionList
*/
private $profileList;
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
private $fileSystem;
/**
* The Drupal root.
*
* @var string
*/
private $appRoot;
/**
* The current installation profile.
*
* @var string
*/
private $installProfile;
/**
* Uninstaller constructor.
*
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler service.
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
* The theme handler service.
* @param \Drupal\Core\Extension\ProfileExtensionList $profile_list
* The profile extension list.
* @param \Drupal\Core\File\FileSystemInterface $file_system
* The file system service.
* @param string $app_root
* The Drupal root.
* @param string $install_profile
* The current installation profile.
*/
public function __construct(ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, ProfileExtensionList $profile_list, FileSystemInterface $file_system, $app_root, $install_profile) {
$this->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("<options=bold>$text</options=bold>");
}
}
================================================
FILE: src/Composer/AssetPackagist.php
================================================
<?php
namespace Acquia\Lightning\Composer;
use Composer\Json\JsonFile;
use Composer\Script\Event;
/**
* Adds Asset Packagist support to a composer.json.
*/
final class AssetPackagist {
/**
* Reads the root package's composer.json.
*
* This will be the composer.json closest to the current working directory
* that contains a dependency on acquia/lightning.
*
* @return \Composer\Json\JsonFile
* File wrapper around the root package's composer.json.
*/
protected static function getRootPackage() {
$file = new JsonFile('composer.json');
// Split the current working directory into an array, accounting for leading
// and trailing directory separators.
$dir = explode(DIRECTORY_SEPARATOR, trim(getcwd(), DIRECTORY_SEPARATOR));
do {
if ($file->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
================================================
<?php
namespace Acquia\Lightning\Composer;
use Composer\Json\JsonFile;
use Composer\Script\Event;
/**
* Configures an instance of drupal/legacy-project to install Lightning.
*/
final class ConfigureLegacyProject {
/**
* Executes the script.
*
* @param \Composer\Script\Event $event
* The script event.
*/
public static function execute(Event $event) {
$arguments = $event->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
================================================
<?php
namespace Acquia\Lightning\Composer;
use Composer\Package\Link;
use Composer\Package\RootPackageInterface;
use Composer\Script\Event;
/**
* Ensures that all patched dependencies are pinned to a specific version.
*/
class PatchedConstraint {
/**
* Script entry point.
*
* @param \Composer\Script\Event $event
* The script event.
*
* @return bool
* False if there are unpinned patched dependencies.
*/
public static function execute(Event $event) {
$root_package = $event->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
================================================
<?php
namespace Drupal\lightning\Generators;
use Drupal\Component\Serialization\Yaml;
use Drupal\Core\Extension\ModuleExtensionList;
use DrupalCodeGenerator\Command\BaseGenerator;
use DrupalCodeGenerator\Utils;
use Drush\Style\DrushStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
/**
* Implements a Lightning sub-profile generator for Drush.
*
* The generator will ask for a human-readable name, machine name, optional
* description, and optional lists of modules to include in the generated
* profile, and Lightning modules to exclude.
*/
final class SubProfileGenerator extends BaseGenerator {
/**
* The module extension list service.
*
* @var \Drupal\Core\Extension\ModuleExtensionList
*/
private $moduleList;
/**
* {@inheritdoc}
*/
protected $name = 'lightning-subprofile';
/**
* {@inheritdoc}
*/
protected $description = 'Generates a Lightning Subprofile.';
/**
* {@inheritdoc}
*/
protected $alias = 'lsp';
/**
* {@inheritdoc}
*/
protected $templatePath = __DIR__;
/**
* SubProfileGenerator constructor.
*
* @param \Drupal\Core\Extension\ModuleExtensionList $module_list
* The module extension list service.
*/
public function __construct(ModuleExtensionList $module_list) {
parent::__construct();
$this->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
================================================
<?php
/**
* @file
* Install, update and uninstall hooks for the {{ name }} subprofile.
*/
/**
* Implements hook_install().
*/
function {{ machine_name }}_install() {
// Add code here perform additional site information tasks, generate sample
// content, etc.
}
================================================
FILE: src/Generators/profile.twig
================================================
<?php
/**
* @file
* Enables modules and site configuration for the {{ name }} profile.
*/
// Add any custom code here, like hook implementations.
================================================
FILE: src/LightningServiceProvider.php
================================================
<?php
namespace Drupal\lightning;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;
/**
* Modifies container services.
*
* @internal
* This class is a completely internal part of Lightning's uninstall system
* and can be changed in any way, or removed outright, at any time without
* warning. External code should not use this class in any way.
*/
final class LightningServiceProvider extends ServiceProviderBase {
/**
* {@inheritdoc}
*/
public function alter(ContainerBuilder $container) {
$container->getDefinition('required_module_uninstall_validator')
->setClass(RequiredModuleUninstallValidator::class);
}
}
================================================
FILE: src/ProxyClass/RequiredModuleUninstallValidator.php
================================================
<?php
// @codingStandardsIgnoreFile
/**
* This file was generated via php core/scripts/generate-proxy-class.php 'Drupal\lightning\RequiredModuleUninstallValidator' "profiles/contrib/lightning/src".
*/
namespace Drupal\lightning\ProxyClass {
/**
* Provides a proxy class for \Drupal\lightning\RequiredModuleUninstallValidator.
*
* @see \Drupal\Component\ProxyBuilder
*/
class RequiredModuleUninstallValidator implements \Drupal\Core\Extension\ModuleUninstallValidatorInterface
{
use \Drupal\Core\DependencyInjection\DependencySerializationTrait;
/**
* The id of the original proxied service.
*
* @var string
*/
protected $drupalProxyOriginalServiceId;
/**
* The real proxied service, after it was lazy loaded.
*
* @var \Drupal\lightning\RequiredModuleUninstallValidator
*/
protected $service;
/**
* The service container.
*
* @var \Symfony\Component\DependencyInjection\ContainerInterface
*/
protected $container;
/**
* Constructs a ProxyClass Drupal proxy object.
*
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* The container.
* @param string $drupal_proxy_original_service_id
* The service ID of the original service.
*/
public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container, $drupal_proxy_original_service_id)
{
$this->container = $container;
$this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id;
}
/**
* Lazy loads the real service from the container.
*
* @return object
* Returns the constructed real service.
*/
protected function lazyLoadItself()
{
if (!isset($this->service)) {
$this->service = $this->container->get($this->drupalProxyOriginalServiceId);
}
return $this->service;
}
/**
* {@inheritdoc}
*/
public function 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
================================================
<?php
namespace Drupal\lightning;
use Drupal\Core\Extension\RequiredModuleUninstallValidator as BaseValidator;
/**
* Validates module uninstallation.
*
* @internal
* This class is a completely internal part of Lightning's uninstall system
* and can be changed in any way, or removed outright, at any time without
* warning. External code should not use this class in any way.
*/
final class RequiredModuleUninstallValidator extends BaseValidator {
/**
* {@inheritdoc}
*/
protected function getModuleInfoByModule($module) {
$info = parent::getModuleInfoByModule($module);
if ($module === 'lightning' || $module === 'headless_lightning') {
$info['required'] = FALSE;
}
return $info;
}
}
================================================
FILE: src/Update/Update320.php
================================================
<?php
namespace Drupal\lightning\Update;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Extension\ExtensionDiscovery;
use Drupal\Core\Serialization\Yaml;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Symfony\Component\Console\Style\StyleInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Contains optional updates targeting Lightning 3.2.0.
*
* @Update("3.2.0")
*/
final class Update320 implements ContainerInjectionInterface {
use StringTranslationTrait;
/**
* The Drupal application root.
*
* @var string
*/
protected $appRoot;
/**
* Update320 constructor.
*
* @param string $app_root
* The Drupal application root.
* @param \Drupal\Core\StringTranslation\TranslationInterface $translation
* (optional) The string translation service.
*/
public function __construct($app_root, TranslationInterface $translation = NULL) {
$this->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
================================================
<?php
namespace Drupal\lightning\Update;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Extension\ModuleInstallerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Contains optional updates targeting Lightning 4.0.5.
*
* @Update("4.0.5")
*/
final class Update405 implements ContainerInjectionInterface {
/**
* The module installer.
*
* @var \Drupal\Core\Extension\ModuleInstallerInterface
*/
private $moduleInstaller;
/**
* Update405 constructor.
*
* @param \Drupal\Core\Extension\ModuleInstallerInterface $module_installer
* The module installer.
*/
public function __construct(ModuleInstallerInterface $module_installer) {
$this->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
================================================
<?php
/**
* @file
* Enables modules and site configuration for the Lightning Extender profile.
*/
// Add any custom code here, like hook implementations.
================================================
FILE: tests/packages.yml
================================================
---
acquia/lightning:
type: drupal-profile
drupal/lightning_api:
version: 4.x
version_dev: 4.x-dev
drupal/lightning_core:
version: 5.x
version_dev: 5.x-dev
drupal/lightning_layout:
version: 2.x
version_dev: 2.x-dev
drupal/lightning_media:
version: 4.x
version_dev: 4.x-dev
drupal/lightning_workflow:
version: 3.x
version_dev: 3.x-dev
================================================
FILE: tests/packages_alter.yml
================================================
---
acquia/lightning:
type: drupal-profile
# Remove drupal/acquia_search because it depends on the
# search_api_solr module, which is not yet compatible with
# Drupal 9 and therefore breaks the ORCA fixture build.
drupal/acquia_search: ~
================================================
FILE: tests/src/ExistingSite/ApiTest.php
================================================
<?php
namespace Drupal\Tests\lightning\ExistingSite;
use Drupal\Component\Serialization\Yaml;
use weitzman\DrupalTestTraits\ExistingSiteBase;
/**
* Tests the decoupled API shipped with the Lightning profile.
*
* @group lightning
*/
class ApiTest extends ExistingSiteBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
/** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
$config_factory = $this->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
================================================
<?php
namespace Drupal\Tests\lightning\ExistingSite;
use Drupal\Component\Serialization\Yaml;
use Drupal\Core\Entity\Entity\EntityViewMode;
use Drupal\user\Entity\Role;
use Drupal\user\UserInterface;
use Drupal\views\Entity\View;
use Drupal\workflows\Entity\Workflow;
use weitzman\DrupalTestTraits\ExistingSiteBase;
/**
* Ensures the integrity and correctness of Lightning's bundled config.
*
* @group lightning
*/
class ConfigIntegrityTest extends ExistingSiteBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// 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 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
================================================
<?php
namespace Drupal\Tests\lightning\ExistingSite;
use Drupal\Core\Entity\Entity\EntityViewMode;
use weitzman\DrupalTestTraits\ExistingSiteBase;
/**
* Tests specialized handling for view modes.
*
* @group lightning
*/
class ViewModeTest extends ExistingSiteBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
/** @var \Drupal\Core\Entity\EntityViewModeInterface $view_mode */
$view_mode = EntityViewMode::create([
'id' => '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
================================================
<?php
namespace Drupal\Tests\lightning\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\UserInterface;
/**
* Tests integrated functionality of the Lightning profile.
*
* @group lightning
* @group orca_public
*/
class LightningTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $configSchemaCheckerExclusions = [
// @todo Remove this when depending on slick_entityreference 1.2 or later.
'core.entity_view_display.block_content.media_slideshow.default',
// @todo Remove when requiring Lightning Layout 2.2 or later.
'core.entity_view_display.block_content.banner.default',
];
/**
* {@inheritdoc}
*/
protected $profile = 'lightning_extender';
/**
* {@inheritdoc}
*/
protected function setUp() {
$root = static::getDrupalRoot();
$this->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
================================================
<?php
namespace Drupal\Tests\lightning\Functional;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Serialization\Yaml;
use Drupal\lightning\Generators\SubProfileGenerator;
use Drupal\Tests\BrowserTestBase;
use Drush\TestTraits\DrushTestTrait;
/**
* Tests the Drush command to generate a Lightning sub-profile.
*
* @covers \Drupal\lightning\Generators\SubProfileGenerator
*
* @group lightning
*/
class SubprofileGeneratorTest extends BrowserTestBase {
use DrushTestTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Data provider for ::test().
*
* @return array
* The sets of arguments to pass to the test method.
*/
public function provider() {
return [
'additional modules in install list' => [
'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
================================================
<?php
namespace Drupal\Tests\lightning\Functional;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drush\TestTraits\DrushTestTrait;
/**
* Tests the update path from Lightning 3.x.
*
* @group lightning
*/
class UpdatePath3xTest extends UpdatePathTestBase {
use DrushTestTrait;
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->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
================================================
<?php
namespace Drupal\Tests\lightning\Functional;
use Drupal\Core\Update\UpdateKernel;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drush\TestTraits\DrushTestTrait;
/**
* Tests the update path from Lightning 4.x.
*
* @group lightning
*/
class UpdatePath4xTest extends UpdatePathTestBase {
use DrushTestTrait;
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->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
================================================
<?php
namespace Drupal\Tests\lightning\Kernel;
use Drupal\KernelTests\KernelTestBase;
use Drupal\lightning\Update\Update405;
/**
* @coversDefaultClass \Drupal\lightning\Update\Update405
*
* @group lightning
*/
class Update405Test extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['system'];
/**
* @covers ::enableAutosaveForm
* @covers ::enableRedirect
*/
public function testUpdate() {
$moduleHandler = $this->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
================================================
<?php
/**
* @file
* Prepares a code base to update to Drupal 9.
*/
use Drupal\views\Entity\View;
Drupal::configFactory()
->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();
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
SYMBOL INDEX (93 symbols across 18 files)
FILE: src/Commands/Uninstaller.php
class Uninstaller (line 32) | final class Uninstaller extends DrushCommands implements SiteAliasManage...
method __construct (line 94) | public function __construct(ModuleHandlerInterface $module_handler, Th...
method options (line 111) | public function options(Command $command) : void {
method drush (line 138) | private function drush(string $command, array $arguments = []) : void {
method getUninstall (line 153) | private function getUninstall() : ?string {
method validate (line 179) | public function validate(CommandData $data) : void {
method preCommand (line 221) | public function preCommand() : void {
method postCommand (line 243) | public function postCommand() : void {
method getExtensionsInProfileDirectory (line 266) | private function getExtensionsInProfileDirectory() : array {
method getChildren (line 297) | private function getChildren(string $parent) : array {
method decoupleProfile (line 318) | private function decoupleProfile(string $name) : void {
method arrayDiff (line 376) | private function arrayDiff(array $a, array $b) : array {
method readInfo (line 391) | private function readInfo(string $name) : array {
method copyConfiguration (line 405) | private function copyConfiguration(string $source, string $target) : v...
method getConfigurationToCopy (line 436) | private function getConfigurationToCopy(string $profile) : array {
method locateProjectFile (line 460) | private function locateProjectFile() : string {
method alterProject (line 475) | private function alterProject() : void {
method getRequirements (line 549) | private function getRequirements(array $target, array $source) : array {
method getPackageTypes (line 581) | private function getPackageTypes(array $target) : array {
method getPaths (line 600) | private function getPaths(array $target, array $source) : array {
method getRepositories (line 643) | private function getRepositories(array $target) : array {
method getPathMap (line 685) | private function getPathMap(array $target, array $source) : array {
method getDrupalRoot (line 714) | private function getDrupalRoot(array $target, array $source) : ?string {
method mergeCanadian (line 736) | private function mergeCanadian(array $a, array $b) : array {
method boldlySay (line 752) | private function boldlySay(string $text) : void {
FILE: src/Composer/AssetPackagist.php
class AssetPackagist (line 11) | final class AssetPackagist {
method getRootPackage (line 22) | protected static function getRootPackage() {
method execute (line 50) | public static function execute(Event $event) {
FILE: src/Composer/ConfigureLegacyProject.php
class ConfigureLegacyProject (line 11) | final class ConfigureLegacyProject {
method execute (line 19) | public static function execute(Event $event) {
FILE: src/Composer/PatchedConstraint.php
class PatchedConstraint (line 12) | class PatchedConstraint {
method execute (line 23) | public static function execute(Event $event) {
method getPatchedDependencyConstraints (line 53) | protected static function getPatchedDependencyConstraints(RootPackageI...
method packageIsUnpinned (line 69) | protected static function packageIsUnpinned(Link $package) {
FILE: src/Generators/SubProfileGenerator.php
class SubProfileGenerator (line 22) | final class SubProfileGenerator extends BaseGenerator {
method __construct (line 57) | public function __construct(ModuleExtensionList $module_list) {
method interact (line 75) | protected function interact(InputInterface $input, OutputInterface $ou...
method toArray (line 154) | public static function toArray($string) {
FILE: src/LightningServiceProvider.php
class LightningServiceProvider (line 16) | final class LightningServiceProvider extends ServiceProviderBase {
method alter (line 21) | public function alter(ContainerBuilder $container) {
FILE: src/ProxyClass/RequiredModuleUninstallValidator.php
class RequiredModuleUninstallValidator (line 15) | class RequiredModuleUninstallValidator implements \Drupal\Core\Extension...
method __construct (line 49) | public function __construct(\Symfony\Component\DependencyInjection\Con...
method lazyLoadItself (line 61) | protected function lazyLoadItself()
method validate (line 73) | public function validate($module)
method setStringTranslation (line 81) | public function setStringTranslation(\Drupal\Core\StringTranslation\Tr...
FILE: src/RequiredModuleUninstallValidator.php
class RequiredModuleUninstallValidator (line 15) | final class RequiredModuleUninstallValidator extends BaseValidator {
method getModuleInfoByModule (line 20) | protected function getModuleInfoByModule($module) {
FILE: src/Update/Update320.php
class Update320 (line 18) | final class Update320 implements ContainerInjectionInterface {
method __construct (line 37) | public function __construct($app_root, TranslationInterface $translati...
method create (line 48) | public static function create(ContainerInterface $container) {
method updateProfiles (line 65) | public function updateProfiles(StyleInterface $io) {
FILE: src/Update/Update405.php
class Update405 (line 14) | final class Update405 implements ContainerInjectionInterface {
method __construct (line 29) | public function __construct(ModuleInstallerInterface $module_installer) {
method create (line 36) | public static function create(ContainerInterface $container) {
method enableAutosaveForm (line 49) | public function enableAutosaveForm() {
method enableRedirect (line 60) | public function enableRedirect() {
FILE: tests/src/ExistingSite/ApiTest.php
class ApiTest (line 13) | class ApiTest extends ExistingSiteBase {
method setUp (line 18) | protected function setUp() {
method testViewConfigEntityAsJson (line 46) | public function testViewConfigEntityAsJson() {
FILE: tests/src/ExistingSite/ConfigIntegrityTest.php
class ConfigIntegrityTest (line 18) | class ConfigIntegrityTest extends ExistingSiteBase {
method setUp (line 23) | protected function setUp() {
method testConfig (line 45) | public function testConfig() {
method providerModeratedContentTypes (line 176) | public function providerModeratedContentTypes() {
method testModeratedContentTypes (line 195) | public function testModeratedContentTypes($node_type, $role) {
method assertMetatag (line 217) | private function assertMetatag(array $node_types) {
method assertEntityExists (line 245) | private function assertEntityExists($entity_type, $id) {
method assertPermissions (line 260) | private function assertPermissions($role, $permissions) {
method assertContainsAll (line 275) | private function assertContainsAll(array $needles, array $haystack) {
method assertAllowed (line 286) | private function assertAllowed($path) {
method assertForbidden (line 297) | private function assertForbidden($path) {
method config (line 311) | private function config($name) {
FILE: tests/src/ExistingSite/ViewModeTest.php
class ViewModeTest (line 13) | class ViewModeTest extends ExistingSiteBase {
method setUp (line 18) | protected function setUp() {
method testInternalViewMode (line 36) | public function testInternalViewMode() {
method tearDown (line 55) | public function tearDown() {
FILE: tests/src/Functional/LightningTest.php
class LightningTest (line 14) | class LightningTest extends BrowserTestBase {
method setUp (line 39) | protected function setUp() {
method tearDown (line 64) | protected function tearDown() {
method testLightning (line 77) | public function testLightning() {
method doModerationDashboardTest (line 96) | private function doModerationDashboardTest() {
method doTextBlockTest (line 108) | private function doTextBlockTest() {
FILE: tests/src/Functional/SubprofileGeneratorTest.php
class SubprofileGeneratorTest (line 18) | class SubprofileGeneratorTest extends BrowserTestBase {
method provider (line 33) | public function provider() {
method test (line 74) | public function test(array $answers = []) {
FILE: tests/src/Functional/UpdatePath3xTest.php
class UpdatePath3xTest (line 13) | class UpdatePath3xTest extends UpdatePathTestBase {
method setDatabaseDumpFiles (line 20) | protected function setDatabaseDumpFiles() {
method test (line 29) | public function test() {
FILE: tests/src/Functional/UpdatePath4xTest.php
class UpdatePath4xTest (line 14) | class UpdatePath4xTest extends UpdatePathTestBase {
method setDatabaseDumpFiles (line 21) | protected function setDatabaseDumpFiles() {
method replaceUser1 (line 30) | protected function replaceUser1() {
method test (line 59) | public function test() {
FILE: tests/src/Kernel/Update405Test.php
class Update405Test (line 13) | class Update405Test extends KernelTestBase {
method testUpdate (line 24) | public function testUpdate() {
Condensed preview — 85 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (151K chars).
[
{
"path": ".gitattributes",
"chars": 222,
"preview": "/grumphp.yml export-ignore\n/hooks export-ignore\n/tests/fixtures export-ignore\n/.travis.yml export-ignore\n/acquia-pipelin"
},
{
"path": ".gitignore",
"chars": 104,
"preview": ".DS_Store\nbin/\ndocroot/\nvendor/\nnode_modules/\nbower_components/\n.sass-cache/\nconsole/cache/\n/.behat.yml\n"
},
{
"path": "CHANGELOG.md",
"chars": 99,
"preview": "The information in this file has been moved to https://github.com/acquia/lightning/wiki/Changelog.\n"
},
{
"path": "CONTRIBUTING.md",
"chars": 2027,
"preview": "## Contributing to Lightning\n\n### Local development\n**Note that these instructions won't work on old, unsupported branch"
},
{
"path": "LICENSE.txt",
"chars": 18092,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 2, June 1991\n\n Copyright (C) 1989, 1991 Fr"
},
{
"path": "README.md",
"chars": 828,
"preview": "# Drupal Lightning\n/docroot/sites/default\nSETTINGS=$SITE_DIR/settings.php\n\nDB_URL=${DB_URL:-sqlite://db"
},
{
"path": "lightning.info.yml",
"chars": 1169,
"preview": "name: Lightning\ncore_version_requirement: '^8.8 || ^9'\ntype: profile\ndescription: 'A fast and feature-rich Drupal distri"
},
{
"path": "lightning.install",
"chars": 3269,
"preview": "<?php\n\n/**\n * @file\n * Install and uninstall functions for the Lightning installation profile.\n */\n\nuse Drupal\\Core\\Enti"
},
{
"path": "lightning.profile",
"chars": 2775,
"preview": "<?php\n\n/**\n * @file\n * The Lightning profile.\n */\n\nuse Drupal\\user\\RoleInterface;\n\n/**\n * Implements hook_install_tasks("
},
{
"path": "modules/lightning_install/composer.json",
"chars": 208,
"preview": "{\n \"name\": \"drupal/lightning_install\",\n \"type\": \"drupal-module\",\n \"description\": \"Provides helpful under-the-ho"
},
{
"path": "modules/lightning_install/lightning_install.info.yml",
"chars": 201,
"preview": "name: 'Install Helper'\ncore_version_requirement: '^8.8 || ^9'\ntype: module\nhidden: true\npackage: Lightning\ndescription: "
},
{
"path": "phpunit.xml",
"chars": 3960,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!-- TODO set checkForUnintentionallyCoveredCode=\"true\" once https://www.drupal."
},
{
"path": "release-branch.sh",
"chars": 546,
"preview": "#!/bin/bash\n\nset -e\n\n# Prepares a release branch.\n# Example usage: ./release-branch 4.1.0\n\nRELEASE_BRANCH=release/$1\n\n# "
},
{
"path": "settings.local.php",
"chars": 4696,
"preview": "<?php\n\n// @codingStandardsIgnoreFile\n\n/**\n * @file\n * Local development override configuration feature.\n *\n * To activat"
},
{
"path": "src/Commands/Uninstaller.php",
"chars": 25915,
"preview": "<?php\n\nnamespace Drupal\\lightning\\Commands;\n\nuse Composer\\Json\\JsonFile;\nuse Consolidation\\AnnotatedCommand\\CommandData;"
},
{
"path": "src/Composer/AssetPackagist.php",
"chars": 2848,
"preview": "<?php\n\nnamespace Acquia\\Lightning\\Composer;\n\nuse Composer\\Json\\JsonFile;\nuse Composer\\Script\\Event;\n\n/**\n * Adds Asset P"
},
{
"path": "src/Composer/ConfigureLegacyProject.php",
"chars": 1051,
"preview": "<?php\n\nnamespace Acquia\\Lightning\\Composer;\n\nuse Composer\\Json\\JsonFile;\nuse Composer\\Script\\Event;\n\n/**\n * Configures a"
},
{
"path": "src/Composer/PatchedConstraint.php",
"chars": 2548,
"preview": "<?php\n\nnamespace Acquia\\Lightning\\Composer;\n\nuse Composer\\Package\\Link;\nuse Composer\\Package\\RootPackageInterface;\nuse C"
},
{
"path": "src/Generators/SubProfileGenerator.php",
"chars": 4945,
"preview": "<?php\n\nnamespace Drupal\\lightning\\Generators;\n\nuse Drupal\\Component\\Serialization\\Yaml;\nuse Drupal\\Core\\Extension\\Module"
},
{
"path": "src/Generators/install.twig",
"chars": 272,
"preview": "<?php\n\n/**\n * @file\n * Install, update and uninstall hooks for the {{ name }} subprofile.\n */\n\n/**\n * Implements hook_in"
},
{
"path": "src/Generators/profile.twig",
"chars": 151,
"preview": "<?php\n\n/**\n * @file\n * Enables modules and site configuration for the {{ name }} profile.\n */\n\n// Add any custom code he"
},
{
"path": "src/LightningServiceProvider.php",
"chars": 714,
"preview": "<?php\n\nnamespace Drupal\\lightning;\n\nuse Drupal\\Core\\DependencyInjection\\ContainerBuilder;\nuse Drupal\\Core\\DependencyInje"
},
{
"path": "src/ProxyClass/RequiredModuleUninstallValidator.php",
"chars": 2593,
"preview": "<?php\n// @codingStandardsIgnoreFile\n\n/**\n * This file was generated via php core/scripts/generate-proxy-class.php 'Drupa"
},
{
"path": "src/RequiredModuleUninstallValidator.php",
"chars": 737,
"preview": "<?php\n\nnamespace Drupal\\lightning;\n\nuse Drupal\\Core\\Extension\\RequiredModuleUninstallValidator as BaseValidator;\n\n/**\n *"
},
{
"path": "src/Update/Update320.php",
"chars": 3198,
"preview": "<?php\n\nnamespace Drupal\\lightning\\Update;\n\nuse Drupal\\Core\\DependencyInjection\\ContainerInjectionInterface;\nuse Drupal\\C"
},
{
"path": "src/Update/Update405.php",
"chars": 1450,
"preview": "<?php\n\nnamespace Drupal\\lightning\\Update;\n\nuse Drupal\\Core\\DependencyInjection\\ContainerInjectionInterface;\nuse Drupal\\C"
},
{
"path": "tarball.sh",
"chars": 777,
"preview": "#!/bin/bash\n\nARCHIVE=lightning-$1\n\ncomposer create-project --stability beta --no-install drupal/legacy-project:~9.1.0 $A"
},
{
"path": "tests/lightning_extender/lightning_extender.info.yml",
"chars": 182,
"preview": "name: 'Lightning Extender'\ntype: profile\ncore_version_requirement: '^8.8 || ^9'\ninstall:\n - ban\nthemes:\n - bartik\n - "
},
{
"path": "tests/lightning_extender/lightning_extender.profile",
"chars": 159,
"preview": "<?php\n\n/**\n * @file\n * Enables modules and site configuration for the Lightning Extender profile.\n */\n\n// Add any custom"
},
{
"path": "tests/packages.yml",
"chars": 361,
"preview": "---\nacquia/lightning:\n type: drupal-profile\n\ndrupal/lightning_api:\n version: 4.x\n version_dev: 4.x-dev\n\ndrupal/lightn"
},
{
"path": "tests/packages_alter.yml",
"chars": 241,
"preview": "---\nacquia/lightning:\n type: drupal-profile\n\n# Remove drupal/acquia_search because it depends on the\n# search_api_solr "
},
{
"path": "tests/src/ExistingSite/ApiTest.php",
"chars": 1800,
"preview": "<?php\n\nnamespace Drupal\\Tests\\lightning\\ExistingSite;\n\nuse Drupal\\Component\\Serialization\\Yaml;\nuse weitzman\\DrupalTestT"
},
{
"path": "tests/src/ExistingSite/ConfigIntegrityTest.php",
"chars": 10785,
"preview": "<?php\n\nnamespace Drupal\\Tests\\lightning\\ExistingSite;\n\nuse Drupal\\Component\\Serialization\\Yaml;\nuse Drupal\\Core\\Entity\\E"
},
{
"path": "tests/src/ExistingSite/ViewModeTest.php",
"chars": 1763,
"preview": "<?php\n\nnamespace Drupal\\Tests\\lightning\\ExistingSite;\n\nuse Drupal\\Core\\Entity\\Entity\\EntityViewMode;\nuse weitzman\\Drupal"
},
{
"path": "tests/src/Functional/LightningTest.php",
"chars": 3656,
"preview": "<?php\n\nnamespace Drupal\\Tests\\lightning\\Functional;\n\nuse Drupal\\Tests\\BrowserTestBase;\nuse Drupal\\user\\UserInterface;\n\n/"
},
{
"path": "tests/src/Functional/SubprofileGeneratorTest.php",
"chars": 6567,
"preview": "<?php\n\nnamespace Drupal\\Tests\\lightning\\Functional;\n\nuse Drupal\\Component\\Serialization\\Json;\nuse Drupal\\Core\\Serializat"
},
{
"path": "tests/src/Functional/UpdatePath3xTest.php",
"chars": 779,
"preview": "<?php\n\nnamespace Drupal\\Tests\\lightning\\Functional;\n\nuse Drupal\\FunctionalTests\\Update\\UpdatePathTestBase;\nuse Drush\\Tes"
},
{
"path": "tests/src/Functional/UpdatePath4xTest.php",
"chars": 2268,
"preview": "<?php\n\nnamespace Drupal\\Tests\\lightning\\Functional;\n\nuse Drupal\\Core\\Update\\UpdateKernel;\nuse Drupal\\FunctionalTests\\Upd"
},
{
"path": "tests/src/Kernel/Update405Test.php",
"chars": 1101,
"preview": "<?php\n\nnamespace Drupal\\Tests\\lightning\\Kernel;\n\nuse Drupal\\KernelTests\\KernelTestBase;\nuse Drupal\\lightning\\Update\\Upda"
},
{
"path": "tests/travis/install.sh",
"chars": 1355,
"preview": "#!/usr/bin/env bash\n\n# NAME\n# install.sh - Install Travis CI dependencies\n#\n# SYNOPSIS\n# install.sh\n#\n# DESCRIPT"
},
{
"path": "tests/update.php",
"chars": 855,
"preview": "<?php\n\n/**\n * @file\n * Prepares a code base to update to Drupal 9.\n */\n\nuse Drupal\\views\\Entity\\View;\n\nDrupal::configFac"
}
]
About this extraction
This page contains the full source code of the acquia/lightning GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 85 files (136.3 KB), approximately 36.1k tokens, and a symbol index with 93 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.