diff --git a/core/lib/Drupal/Component/VendorCleanup/Config.php b/core/lib/Drupal/Component/VendorCleanup/Config.php new file mode 100644 index 0000000000..b2e00ae3c7 --- /dev/null +++ b/core/lib/Drupal/Component/VendorCleanup/Config.php @@ -0,0 +1,196 @@ + ['tests', 'driver-testsuite'], + 'behat/mink-browserkit-driver' => ['tests'], + 'behat/mink-goutte-driver' => ['tests'], + 'behat/mink-selenium2-driver' => ['tests'], + 'brumann/polyfill-unserialize' => ['tests'], + 'composer/composer' => ['bin'], + 'drupal/coder' => [ + 'coder_sniffer/Drupal/Test', + 'coder_sniffer/DrupalPractice/Test', + ], + 'doctrine/cache' => ['tests'], + 'doctrine/collections' => ['tests'], + 'doctrine/common' => ['tests'], + 'doctrine/inflector' => ['tests'], + 'doctrine/instantiator' => ['tests'], + 'easyrdf/easyrdf' => ['scripts'], + 'egulias/email-validator' => ['documentation', 'tests'], + 'fabpot/goutte' => ['Goutte/Tests'], + 'guzzlehttp/promises' => ['tests'], + 'guzzlehttp/psr7' => ['tests'], + 'instaclick/php-webdriver' => ['doc', 'test'], + 'jcalderonzumba/gastonjs' => ['docs', 'examples', 'tests'], + 'jcalderonzumba/mink-phantomjs-driver' => ['tests'], + 'justinrainbow/json-schema' => ['demo'], + 'masterminds/html5' => ['bin', 'test'], + 'mikey179/vfsStream' => ['src/test'], + 'myclabs/deep-copy' => ['doc'], + 'paragonie/random_compat' => ['tests'], + 'pear/archive_tar' => ['docs', 'tests'], + 'pear/console_getopt' => ['tests'], + 'pear/pear-core-minimal' => ['tests'], + 'pear/pear_exception' => ['tests'], + 'phar-io/manifest' => ['examples', 'tests'], + 'phar-io/version' => ['tests'], + 'phpdocumentor/reflection-docblock' => ['tests'], + 'phpspec/prophecy' => ['fixtures', 'spec', 'tests'], + 'phpunit/php-code-coverage' => ['tests'], + 'phpunit/php-timer' => ['tests'], + 'phpunit/php-token-stream' => ['tests'], + 'phpunit/phpunit' => ['tests'], + 'phpunit/phpunit-mock-objects' => ['tests'], + 'sebastian/code-unit-reverse-lookup' => ['tests'], + 'sebastian/comparator' => ['tests'], + 'sebastian/diff' => ['tests'], + 'sebastian/environment' => ['tests'], + 'sebastian/exporter' => ['tests'], + 'sebastian/global-state' => ['tests'], + 'sebastian/object-enumerator' => ['tests'], + 'sebastian/object-reflector' => ['tests'], + 'sebastian/recursion-context' => ['tests'], + 'seld/jsonlint' => ['tests'], + 'squizlabs/php_codesniffer' => ['tests'], + 'stack/builder' => ['tests'], + 'symfony/browser-kit' => ['Tests'], + 'symfony/class-loader' => ['Tests'], + 'symfony/console' => ['Tests'], + 'symfony/css-selector' => ['Tests'], + 'symfony/debug' => ['Tests'], + 'symfony/dependency-injection' => ['Tests'], + 'symfony/dom-crawler' => ['Tests'], + 'symfony/filesystem' => ['Tests'], + 'symfony/finder' => ['Tests'], + 'symfony/event-dispatcher' => ['Tests'], + 'symfony/http-foundation' => ['Tests'], + 'symfony/http-kernel' => ['Tests'], + 'symfony/phpunit-bridge' => ['Tests'], + 'symfony/process' => ['Tests'], + 'symfony/psr-http-message-bridge' => ['Tests'], + 'symfony/routing' => ['Tests'], + 'symfony/serializer' => ['Tests'], + 'symfony/translation' => ['Tests'], + 'symfony/validator' => ['Tests', 'Resources'], + 'symfony/yaml' => ['Tests'], + 'symfony-cmf/routing' => ['Test', 'Tests'], + 'theseer/tokenizer' => ['tests'], + 'twig/twig' => ['doc', 'ext', 'test'], + 'zendframework/zend-escaper' => ['doc'], + 'zendframework/zend-feed' => ['doc'], + 'zendframework/zend-stdlib' => ['doc'], + ]; + + /** + * The root package. + * + * @var Composer\Package\RootPackageInterface + */ + protected $rootPackage; + + /** + * Configuration gleaned from the root package. + * + * @var array + */ + protected $configData = []; + + public function __construct(RootPackageInterface $root_package) { + $this->rootPackage = $root_package; + } + + /** + * Get the configured list of directories to remove from the root package. + * + * This is stored in composer.json extra:drupal-core-vendor-cleanup. + * + * @return array[] + * An array keyed by package name. Each array value is an array of paths, + * relative to the package. + */ + public function getAllCleanupPaths() { + if ($this->configData) { + return $this->configData; + } + // Get the root package config. + $package_config = $this->rootPackage->getExtra(); + if (isset($package_config['drupal-core-vendor-cleanup'])) { + $this->configData = $package_config['drupal-core-vendor-cleanup']; + } + // Ensure the values are arrays. + foreach ($this->configData as $package => $paths) { + if (!is_array($paths)) { + $this->configData[$package] = [$paths]; + } + } + // Merge root config with defaults. + foreach (static::$defaultConfig as $package => $paths) { + if (isset($this->configData[$package])) { + $this->configData[$package] = array_merge($this->configData[$package], $paths); + } + else { + $this->configData[$package] = $paths; + } + } + return $this->configData; + } + + /** + * Get a list of paths to remove for the given package. + * + * @param string $package + * The package name. + * + * @return string[] + * Array of paths to remove, relative to the package. + */ + public function getPathsForPackage($package) { + $paths = []; + $config = $this->getAllCleanupPaths(); + if (isset($config[$package])) { + $paths = $config[$package]; + } + else { + // Handle any mismatch in case between the package name and array key. For + // example, the array key 'mikey179/vfsStream' needs to be found when + // composer returns a package name of 'mikey179/vfsstream'. + foreach (array_keys($config) as $key) { + if (strtolower($key) == strtolower($package)) { + $paths = $config[$key]; + break; + } + } + } + return $paths; + } + + /** + * If there is no configuration in the root package, it is unconfigured. + * + * @return bool + * TRUE if the root package does not have a list of packages with + * directories to clean. + */ + public function isUnconfigured() { + return empty($this->getAllCleanupPaths()); + } + +} diff --git a/core/lib/Drupal/Component/VendorCleanup/LICENSE.txt b/core/lib/Drupal/Component/VendorCleanup/LICENSE.txt new file mode 100644 index 0000000000..94fb84639c --- /dev/null +++ b/core/lib/Drupal/Component/VendorCleanup/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/core/lib/Drupal/Component/VendorCleanup/README.txt b/core/lib/Drupal/Component/VendorCleanup/README.txt new file mode 100644 index 0000000000..8a6a97ea45 --- /dev/null +++ b/core/lib/Drupal/Component/VendorCleanup/README.txt @@ -0,0 +1,56 @@ +The Drupal Vendor Cleanup Composer Plugin +========================================= + +Thanks for using this Drupal component. + +You can participate in its development on Drupal.org, through our issue system: +https://www.drupal.org/project/issues/drupal + +You can get the full Drupal repo here: +https://www.drupal.org/project/drupal/git-instructions + +You can browse the full Drupal repo here: +http://cgit.drupalcode.org/drupal + +What does it do? +---------------- + +This Composer plugin removes extraneous directories from the project's vendor +directory. They're typically directories which might contain executable files, +such as test directories. + +This sort of processing is required for projects that have a vendor directory +inside the HTTP server docroot. This is a common layout for Drupal. + +By default, the plugin knows how to clean up packages for Drupal core, so you +can require drupal/core-vendor-cleanup in your project and the rest will happen +auto-magically. + +The plugin can also be configured to clean up additional packages using the +project's composer.json extra field. + +How do I set it up? +------------------- + +Require this Composer plugin into your project: + + composer require drupal/core-vendor-cleanup + +When you install or update, this plugin will look through each package and +remove directories it knows about. + +You can see the list of default package cleanups for this plugin in Config.php. +If you discover that this list needs updating, please file an issue about it: +https://www.drupal.org/project/issues/drupal + +In addition to the default list of packages, you can configure the plugin using +the root package's composer.json extra field, like this: + + "extra": { + "drupal-core-vendor-cleanup": { + "vendor/package": ["test", "documentation"] + } + } + +The above code will tell the plugin to remove the test/ and documentation/ +directories from the 'vendor/package' package when it is installed or updated. diff --git a/core/lib/Drupal/Component/VendorCleanup/TESTING.txt b/core/lib/Drupal/Component/VendorCleanup/TESTING.txt new file mode 100644 index 0000000000..7c4cbd7759 --- /dev/null +++ b/core/lib/Drupal/Component/VendorCleanup/TESTING.txt @@ -0,0 +1,18 @@ +HOW-TO: Test this Drupal component + +In order to test this component, you'll need to get the entire Drupal repo and +run the tests there. + +You'll find the tests under core/tests/Drupal/Tests/Component. + +You can get the full Drupal repo here: +https://www.drupal.org/project/drupal/git-instructions + +You can find more information about running PHPUnit tests with Drupal here: +https://www.drupal.org/node/2116263 + +Each component in the Drupal\Component namespace has its own annotated test +group. You can use this group to run only the tests for this component. Like +this: + +$ ./vendor/bin/phpunit -c core --group VendorCleanup diff --git a/core/lib/Drupal/Component/VendorCleanup/VendorCleanupPlugin.php b/core/lib/Drupal/Component/VendorCleanup/VendorCleanupPlugin.php new file mode 100644 index 0000000000..e8e3cfa1c8 --- /dev/null +++ b/core/lib/Drupal/Component/VendorCleanup/VendorCleanupPlugin.php @@ -0,0 +1,222 @@ +composer = $composer; + $this->io = $io; + + // Set up configuration. + $this->config = new Config($this->composer->getPackage()); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + return [ + ScriptEvents::POST_UPDATE_CMD => 'onPostCmd', + ScriptEvents::POST_INSTALL_CMD => 'onPostCmd', + PackageEvents::POST_PACKAGE_INSTALL => 'onPostPackageInstall', + PackageEvents::POST_PACKAGE_UPDATE => 'onPostPackageUpdate', + ]; + } + + /** + * POST_UPDATE_CMD and POST_INSTALL_CMD event handler. + * + * @param \Composer\Script\Event $event + * The Composer event. + */ + public function onPostCmd(Event $event) { + $this->cleanAllPackages($this->composer->getConfig()->get('vendor-dir')); + } + + /** + * POST_PACKAGE_INSTALL event handler. + * + * @param \Composer\Installer\PackageEvent $event + */ + public function onPostPackageInstall(PackageEvent $event) { + /** @var \Composer\Package\CompletePackage $package */ + $package = $event->getOperation()->getPackage(); + $package_name = $package->getName(); + $this->cleanPackage($this->composer->getConfig()->get('vendor-dir'), $package_name); + } + + /** + * POST_PACKAGE_UPDATE event handler. + * + * @param \Composer\Installer\PackageEvent $event + */ + public function onPostPackageUpdate(PackageEvent $event) { + /** @var \Composer\Package\CompletePackage $package */ + $package = $event->getOperation()->getTargetPackage(); + $package_name = $package->getName(); + $this->cleanPackage($this->composer->getConfig()->get('vendor-dir'), $package_name); + } + + /** + * Convenience method to get all installed packages from Composer. + * + * @return \Composer\Package\PackageInterface[] + * The list of installed packages. + */ + protected function getInstalledPackages() { + return $this->composer->getRepositoryManager()->getLocalRepository()->getPackages(); + } + + /** + * Clean all configured packages. + * + * This applies in the context of a post-command event. + * + * @param string $vendor_dir + * Path to vendor directory + */ + public function cleanAllPackages($vendor_dir) { + // Get a list of all the packages available after the update or install + // command. + $installed_packages = []; + foreach ($this->getInstalledPackages() as $package) { + // Normalize package names to lower case. + $installed_packages[strtolower($package->getName())] = $package; + } + + // Get all the packages that we should clean up but haven't already. + $cleanup_packages = array_diff_key($this->config->getAllCleanupPaths(), $this->packagesAlreadyCleaned); + + // Get all the packages that are installed that we should clean up. + $packages_to_be_cleaned = array_intersect_key($cleanup_packages, $installed_packages); + + if (!$packages_to_be_cleaned) { + $this->io->write('Vendor directory already clean.'); + return; + } + $this->io->write('Cleaning vendor directory.'); + + foreach ($packages_to_be_cleaned as $package_name => $paths_for_package) { + $this->cleanPathsForPackage($vendor_dir, $package_name, $paths_for_package); + } + } + + /** + * Clean a single package. + * + * This applies in the context of a package post-install or post-update event. + * + * @param string $vendor_dir + * Path to vendor directory + * @param string $package_name + * Name of the package to clean + */ + public function cleanPackage($vendor_dir, $package_name) { + // Normalize package names to lower case. + $package_name = strtolower($package_name); + if (isset($this->packagesAlreadyCleaned[$package_name])) { + $this->io->write(sprintf(' %s already cleaned.', $package_name), TRUE, IOInterface::VERY_VERBOSE); + return; + } + + $paths_for_package = $this->config->getPathsForPackage($package_name); + if ($paths_for_package) { + $this->io->write(' Cleaning: ' . $package_name . ''); + $this->cleanPathsForPackage($vendor_dir, $package_name, $paths_for_package); + } + } + + /** + * Clean the installed directories for a named package. + * + * @param string $vendor_dir + * Path to vendor directory. + * @param string $package_name + * Name of package to sanitize. + * @param string $paths_for_package + * List of directories in $package_name to remove + */ + protected function cleanPathsForPackage($vendor_dir, $package_name, $paths_for_package) { + // Whatever happens here, this package counts as cleaned so that we don't + // process it more than once. + $this->packagesAlreadyCleaned[$package_name] = TRUE; + + $package_dir = $vendor_dir . '/' . $package_name; + if (is_dir($package_dir)) { + $this->io->write(sprintf(" Cleaning directories in %s", $package_name), TRUE, IOInterface::VERY_VERBOSE); + $fs = new Filesystem(); + foreach ($paths_for_package as $cleanup_item) { + $cleanup_path = $package_dir . '/' . $cleanup_item; + if (is_dir($cleanup_path)) { + if ($fs->removeDirectory($cleanup_path)) { + $this->io->write(sprintf(" Removing directory '%s'", $cleanup_item), TRUE, IOInterface::VERBOSE); + } + else { + // Always display a message if this fails as it means something + // has gone wrong. Therefore the message has to include the + // package name as the first informational message might not + // exist. + $this->io->write(sprintf(" Failure removing directory '%s' in package %s.", $cleanup_item, $package_name), TRUE, IOInterface::NORMAL); + } + } + else { + // If the package has changed or the --prefer-dist version does not + // include the directory. This is not an error. + $this->io->write(sprintf(" Directory '%s' does not exist.", $cleanup_path), TRUE, IOInterface::VERY_VERBOSE); + } + } + } + } + +} diff --git a/core/lib/Drupal/Component/VendorCleanup/composer.json b/core/lib/Drupal/Component/VendorCleanup/composer.json new file mode 100644 index 0000000000..0e1ce68a3e --- /dev/null +++ b/core/lib/Drupal/Component/VendorCleanup/composer.json @@ -0,0 +1,20 @@ +{ + "name": "drupal/core-vendor-cleanup", + "description": "Removes directories from the vendor directory.", + "keywords": ["drupal"], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "type": "composer-plugin", + "autoload": { + "psr-4": { + "Drupal\\Component\\VendorCleanup\\": "" + } + }, + "extra": { + "class": "Drupal\\Component\\VendorCleanup\\VendorCleanupPlugin" + }, + "require": { + "php": ">=7.0.8", + "composer-plugin-api": "^1.1" + } +} diff --git a/core/modules/media_library/config/install/views.view.media_library.yml b/core/modules/media_library/config/install/views.view.media_library.yml index b52edd811b..bfab376bb0 100644 --- a/core/modules/media_library/config/install/views.view.media_library.yml +++ b/core/modules/media_library/config/install/views.view.media_library.yml @@ -414,288 +414,9 @@ display: weight: 5 context: '0' menu_name: admin - fields: - media_bulk_form: - id: media_bulk_form - table: media - field: media_bulk_form - relationship: none - group_type: group - admin_label: '' - label: '' - exclude: false - alter: - alter_text: false - text: '' - make_link: false - path: '' - absolute: false - external: false - replace_spaces: false - path_case: none - trim_whitespace: false - alt: '' - rel: '' - link_class: '' - prefix: '' - suffix: '' - target: '' - nl2br: false - max_length: 0 - word_boundary: true - ellipsis: true - more_link: false - more_link_text: '' - more_link_path: '' - strip_tags: false - trim: false - preserve_tags: '' - html: false - element_type: '' - element_class: js-click-to-select-checkbox - element_label_type: '' - element_label_class: '' - element_label_colon: false - element_wrapper_type: '' - element_wrapper_class: '' - element_default_classes: true - empty: '' - hide_empty: false - empty_zero: false - hide_alter_empty: true - action_title: Action - include_exclude: exclude - selected_actions: { } - entity_type: media - plugin_id: bulk_form - name: - id: name - table: media_field_data - field: name - relationship: none - group_type: group - admin_label: '' - label: '' - exclude: true - alter: - alter_text: false - text: '' - make_link: false - path: '' - absolute: false - external: false - replace_spaces: false - path_case: none - trim_whitespace: false - alt: '' - rel: '' - link_class: '' - prefix: '' - suffix: '' - target: '' - nl2br: false - max_length: 0 - word_boundary: true - ellipsis: true - more_link: false - more_link_text: '' - more_link_path: '' - strip_tags: false - trim: false - preserve_tags: '' - html: false - element_type: '' - element_class: '' - element_label_type: '' - element_label_class: '' - element_label_colon: false - element_wrapper_type: '' - element_wrapper_class: '' - element_default_classes: true - empty: '' - hide_empty: false - empty_zero: false - hide_alter_empty: true - click_sort_column: value - type: string - settings: - link_to_entity: false - group_column: value - group_columns: { } - group_rows: true - delta_limit: 0 - delta_offset: 0 - delta_reversed: false - delta_first_last: false - multi_type: separator - separator: ', ' - field_api_classes: false - entity_type: media - entity_field: name - plugin_id: field - edit_media: - id: edit_media - table: media - field: edit_media - relationship: none - group_type: group - admin_label: '' - label: '' - exclude: false - alter: - alter_text: true - text: 'Edit {{ name }}' - make_link: true - path: '' - absolute: false - external: false - replace_spaces: false - path_case: none - trim_whitespace: false - alt: 'Edit {{ name }}' - rel: '' - link_class: media-library-item__edit - prefix: '' - suffix: '' - target: '' - nl2br: false - max_length: 0 - word_boundary: true - ellipsis: true - more_link: false - more_link_text: '' - more_link_path: '' - strip_tags: false - trim: false - preserve_tags: '' - html: false - element_type: '' - element_class: '' - element_label_type: '' - element_label_class: '' - element_label_colon: false - element_wrapper_type: '0' - element_wrapper_class: '' - element_default_classes: false - empty: '' - hide_empty: false - empty_zero: false - hide_alter_empty: true - text: 'Edit' - output_url_as_text: false - absolute: false - entity_type: media - plugin_id: entity_link_edit - delete_media: - id: delete_media - table: media - field: delete_media - relationship: none - group_type: group - admin_label: '' - label: '' - exclude: false - alter: - alter_text: true - text: 'Delete {{ name }}' - make_link: true - path: '' - absolute: false - external: false - replace_spaces: false - path_case: none - trim_whitespace: false - alt: 'Delete {{ name }}' - rel: '' - link_class: media-library-item__remove - prefix: '' - suffix: '' - target: '' - nl2br: false - max_length: 0 - word_boundary: true - ellipsis: true - more_link: false - more_link_text: '' - more_link_path: '' - strip_tags: false - trim: false - preserve_tags: '' - html: false - element_type: '' - element_class: '' - element_label_type: '' - element_label_class: '' - element_label_colon: false - element_wrapper_type: '0' - element_wrapper_class: '' - element_default_classes: false - empty: '' - hide_empty: false - empty_zero: false - hide_alter_empty: true - text: 'Delete' - output_url_as_text: false - absolute: false - entity_type: media - plugin_id: entity_link_delete - rendered_entity: - id: rendered_entity - table: media - field: rendered_entity - relationship: none - group_type: group - admin_label: '' - label: '' - exclude: false - alter: - alter_text: false - text: '' - make_link: false - path: '' - absolute: false - external: false - replace_spaces: false - path_case: none - trim_whitespace: false - alt: '' - rel: '' - link_class: '' - prefix: '' - suffix: '' - target: '' - nl2br: false - max_length: 0 - word_boundary: true - ellipsis: true - more_link: false - more_link_text: '' - more_link_path: '' - strip_tags: false - trim: false - preserve_tags: '' - html: false - element_type: '' - element_class: media-library-item__content - element_label_type: '' - element_label_class: '' - element_label_colon: false - element_wrapper_type: '' - element_wrapper_class: '' - element_default_classes: true - empty: '' - hide_empty: false - empty_zero: false - hide_alter_empty: true - view_mode: media_library - entity_type: media - plugin_id: rendered_entity - defaults: - fields: false cache_metadata: max-age: 0 contexts: - - 'languages:language_content' - 'languages:language_interface' - url - url.query_args diff --git a/core/modules/media_library/css/media_library.module.css b/core/modules/media_library/css/media_library.module.css index 3e2cdde7d1..ed428b20e0 100644 --- a/core/modules/media_library/css/media_library.module.css +++ b/core/modules/media_library/css/media_library.module.css @@ -51,25 +51,22 @@ .media-library-item .js-click-to-select-checkbox { position: absolute; z-index: 1; - top: 16px; - left: 16px; /* LTR */ + top: 5px; + right: 5px; /* LTR */ display: block; } + [dir="rtl"] .media-library-item .js-click-to-select-checkbox { - right: 16px; - left: auto; + right: 0; + left: 5px; } .media-library-item__status { position: absolute; - top: 40px; - left: 5px; /* LTR */ + top: 10px; + left: 2px; pointer-events: none; } -[dir="rtl"] .media-library-item__status { - right: 5px; - left: auto; -} .media-library-select-all { flex-basis: 100%; @@ -88,6 +85,13 @@ cursor: move; } +/* @todo Remove or re-work in https://www.drupal.org/node/2985168 */ +.media-library-widget .media-library-item__name a, +.media-library-view--widget .media-library-item__name a, +.media-library-add-form__selected-media .media-library-item__name a { + pointer-events: none; +} + .media-library-widget-modal .ui-dialog-buttonpane { display: flex; align-items: center; @@ -102,8 +106,3 @@ flex-basis: 100%; } } - -/* @todo Remove in https://www.drupal.org/project/drupal/issues/3064914 */ -.views-live-preview .media-library-view div.views-row + div.views-row { - margin-top: 0; -} diff --git a/core/modules/media_library/css/media_library.theme.css b/core/modules/media_library/css/media_library.theme.css index 75f2e343e1..d5a5b7ac87 100644 --- a/core/modules/media_library/css/media_library.theme.css +++ b/core/modules/media_library/css/media_library.theme.css @@ -318,7 +318,6 @@ height: calc(100% - 16px); content: ""; transition: border-color 0.2s, color 0.2s, background 0.2s; - pointer-events: none; border: 1px solid #dbdbdb; } @@ -405,8 +404,8 @@ } .media-library-item--grid .js-click-to-select-checkbox input { - width: 20px; - height: 20px; + width: 30px; + height: 30px; } .media-library-item--grid .js-click-to-select-checkbox .form-item { @@ -447,20 +446,27 @@ font-size: 14px; } -.media-library-item__name { +.media-library-item__name a { display: block; overflow: hidden; margin: 2px; white-space: nowrap; + text-decoration: underline; text-overflow: ellipsis; } -.media-library-item__attributes:hover .media-library-item__name, -.media-library-item--grid.is-focus .media-library-item__name, -.media-library-item--grid.checked .media-library-item__name { +.media-library-item__attributes:hover .media-library-item__name a, +.media-library-item__name a:focus, +.media-library-item--grid.is-focus .media-library-item__name a, +.media-library-item--grid.checked .media-library-item__name a { white-space: normal; } +.media-library-item__name a:focus { + margin: 0; + border: 2px solid; +} + .media-library-item__type { color: #696969; font-size: 12px; @@ -491,11 +497,7 @@ .media-library-widget__toggle-weight { position: absolute; top: 5px; - right: 5px; /* LTR */ -} -[dir="rtl"] .media-library-widget__toggle-weight { - right: auto; - left: 5px; + right: 5px; } /* Add negative margin for flex grid. */ @@ -508,15 +510,7 @@ margin: 0.75em; } -/** - * Media library widget edit and delete button styles. - * - * We have to override the .button styles since buttons make heavy use of - * background and border property changes. - */ -.media-library-item__edit, -.media-library-item__edit:hover, -.media-library-item__edit:focus, +/* Media library widget remove button styles. */ .media-library-item__remove, .media-library-item__remove:hover, .media-library-item__remove:focus, @@ -529,52 +523,20 @@ position: absolute; z-index: 1; top: 10px; - overflow: hidden; - width: 21px; - height: 21px; + right: 10px; + width: 24px; + height: 24px; margin: 5px; padding: 0; transition: 0.2s border-color; color: transparent; border: 2px solid #ccc; border-radius: 20px; - background-size: 13px; + background: url("../../../misc/icons/787878/ex.svg") #fff center no-repeat; + background-size: 16px 16px; text-shadow: none; - font-size: 0; -} - -.media-library-item__edit { - right: 40px; /* LTR */ -} -[dir="rtl"] .media-library-item__edit { - right: auto; - left: 40px; } -.media-library-item__remove { - right: 10px; /* LTR */ -} -[dir="rtl"] .media-library-item__remove { - right: auto; - left: 10px; -} - -.media-library-item__edit { - background: url("../../../misc/icons/787878/pencil.svg") #fff center no-repeat; - background-size: 13px; -} -.media-library-item__remove, -.media-library-item__remove.button, -.media-library-item__remove.button:first-child, -.media-library-item__remove.button:disabled, -.media-library-item__remove.button:disabled:active, -.media-library-item__remove.button:hover, -.media-library-item__remove.button:focus { - background: url("../../../misc/icons/787878/ex.svg") #fff center no-repeat; - background-size: 13px; -} -.media-library-item__edit:hover, -.media-library-item__edit:focus, .media-library-item__remove:hover, .media-library-item__remove:focus, .media-library-item__remove.button:hover, @@ -693,3 +655,11 @@ [dir="rtl"] .media-library-add-form__remove-button.button:hover { background-position: left 2px; } + +/* @todo Remove or re-work in https://www.drupal.org/node/2985168 */ +.media-library-widget .media-library-item__name a, +.media-library-view--widget .media-library-item__name a, +.media-library-add-form__selected-media .media-library-item__name a { + text-decoration: none; + color: black; +} diff --git a/core/modules/media_library/media_library.install b/core/modules/media_library/media_library.install index 81ecb72306..e2b5edca09 100644 --- a/core/modules/media_library/media_library.install +++ b/core/modules/media_library/media_library.install @@ -218,218 +218,3 @@ function media_library_update_8702() { $view->save(TRUE); } } - -/** - * Add edit and delete button to media library view page display. - */ -function media_library_update_8703() { - $view = \Drupal::configFactory()->getEditable('views.view.media_library'); - if (!$view->isNew() && $view->get('display.page')) { - // Fetch the fields from the page display, if the fields are not yet - // overridden, get the fields from the default display. - $fields = $view->get('display.page.display_options.fields'); - if (!$fields) { - $fields = $view->get('display.default.display_options.fields'); - // Override the fields for the page display. - $view->set('display.page.display_options.defaults.fields', FALSE); - } - - // Check if the name field already exists and add if it doesn't. - if (!isset($fields['name'])) { - $fields['name'] = [ - 'id' => 'name', - 'table' => 'media_field_data', - 'field' => 'name', - 'relationship' => 'none', - 'group_type' => 'group', - 'admin_label' => '', - 'label' => '', - 'exclude' => TRUE, - 'alter' => [ - 'alter_text' => FALSE, - 'text' => '', - 'make_link' => FALSE, - 'path' => '', - 'absolute' => FALSE, - 'external' => FALSE, - 'replace_spaces' => FALSE, - 'path_case' => 'none', - 'trim_whitespace' => FALSE, - 'alt' => '', - 'rel' => '', - 'link_class' => '', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'nl2br' => FALSE, - 'max_length' => 0, - 'word_boundary' => TRUE, - 'ellipsis' => TRUE, - 'more_link' => FALSE, - 'more_link_text' => '', - 'more_link_path' => '', - 'strip_tags' => FALSE, - 'trim' => FALSE, - 'preserve_tags' => '', - 'html' => FALSE, - ], - 'element_type' => '', - 'element_class' => '', - 'element_label_type' => '', - 'element_label_class' => '', - 'element_label_colon' => FALSE, - 'element_wrapper_type' => '', - 'element_wrapper_class' => '', - 'element_default_classes' => TRUE, - 'empty' => '', - 'hide_empty' => FALSE, - 'empty_zero' => FALSE, - 'hide_alter_empty' => TRUE, - 'click_sort_column' => 'value', - 'type' => 'string', - 'settings' => [ - 'link_to_entity' => FALSE, - ], - 'group_column' => 'value', - 'group_columns' => [], - 'group_rows' => TRUE, - 'delta_limit' => 0, - 'delta_offset' => 0, - 'delta_reversed' => FALSE, - 'delta_first_last' => FALSE, - 'multi_type' => 'separator', - 'separator' => ', ', - 'field_api_classes' => FALSE, - 'entity_type' => 'media', - 'entity_field' => 'name', - 'plugin_id' => 'field', - ]; - } - - // Check if the edit link field already exists and add if it doesn't. - if (!isset($fields['edit_media'])) { - $fields['edit_media'] = [ - 'id' => 'edit_media', - 'table' => 'media', - 'field' => 'edit_media', - 'relationship' => 'none', - 'group_type' => 'group', - 'admin_label' => '', - 'label' => '', - 'exclude' => FALSE, - 'alter' => [ - 'alter_text' => TRUE, - 'text' => 'Edit {{ name }}', - 'make_link' => TRUE, - 'path' => '', - 'absolute' => FALSE, - 'external' => FALSE, - 'replace_spaces' => FALSE, - 'path_case' => 'none', - 'trim_whitespace' => FALSE, - 'alt' => 'Edit {{ name }}', - 'rel' => '', - 'link_class' => 'media-library-item__edit', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'nl2br' => FALSE, - 'max_length' => 0, - 'word_boundary' => TRUE, - 'ellipsis' => TRUE, - 'more_link' => FALSE, - 'more_link_text' => '', - 'more_link_path' => '', - 'strip_tags' => FALSE, - 'trim' => FALSE, - 'preserve_tags' => '', - 'html' => FALSE, - ], - 'element_type' => '', - 'element_class' => '', - 'element_label_type' => '', - 'element_label_class' => '', - 'element_label_colon' => FALSE, - 'element_wrapper_type' => '0', - 'element_wrapper_class' => '', - 'element_default_classes' => FALSE, - 'empty' => '', - 'hide_empty' => FALSE, - 'empty_zero' => FALSE, - 'hide_alter_empty' => TRUE, - 'text' => 'Edit', - 'output_url_as_text' => FALSE, - 'absolute' => FALSE, - 'entity_type' => 'media', - 'plugin_id' => 'entity_link_edit', - ]; - } - - // Check if the delete link field already exists and add if it doesn't. - if (!isset($fields['delete_media'])) { - $fields['delete_media'] = [ - 'id' => 'delete_media', - 'table' => 'media', - 'field' => 'delete_media', - 'relationship' => 'none', - 'group_type' => 'group', - 'admin_label' => '', - 'label' => '', - 'exclude' => FALSE, - 'alter' => [ - 'alter_text' => TRUE, - 'text' => 'Delete {{ name }}', - 'make_link' => TRUE, - 'path' => '', - 'absolute' => FALSE, - 'external' => FALSE, - 'replace_spaces' => FALSE, - 'path_case' => 'none', - 'trim_whitespace' => FALSE, - 'alt' => 'Delete {{ name }}', - 'rel' => '', - 'link_class' => 'media-library-item__remove', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'nl2br' => FALSE, - 'max_length' => 0, - 'word_boundary' => TRUE, - 'ellipsis' => TRUE, - 'more_link' => FALSE, - 'more_link_text' => '', - 'more_link_path' => '', - 'strip_tags' => FALSE, - 'trim' => FALSE, - 'preserve_tags' => '', - 'html' => FALSE, - ], - 'element_type' => '', - 'element_class' => '', - 'element_label_type' => '', - 'element_label_class' => '', - 'element_label_colon' => FALSE, - 'element_wrapper_type' => '0', - 'element_wrapper_class' => '', - 'element_default_classes' => FALSE, - 'empty' => '', - 'hide_empty' => FALSE, - 'empty_zero' => FALSE, - 'hide_alter_empty' => TRUE, - 'text' => 'Delete', - 'output_url_as_text' => FALSE, - 'absolute' => FALSE, - 'entity_type' => 'media', - 'plugin_id' => 'entity_link_delete', - ]; - } - - // Move the rendered entity field to the last position for accessibility. - $rendered_entity = $fields['rendered_entity']; - unset($fields['rendered_entity']); - $fields['rendered_entity'] = $rendered_entity; - - $view->set('display.page.display_options.fields', $fields) - ->save(TRUE); - } -} diff --git a/core/modules/media_library/media_library.services.yml b/core/modules/media_library/media_library.services.yml index b2d06643b5..1a35b2805d 100644 --- a/core/modules/media_library/media_library.services.yml +++ b/core/modules/media_library/media_library.services.yml @@ -1,7 +1,7 @@ services: media_library.ui_builder: class: Drupal\media_library\MediaLibraryUiBuilder - arguments: ['@entity_type.manager', '@request_stack', '@views.executable', '@form_builder', '@media_library.opener_resolver'] + arguments: ['@entity_type.manager', '@request_stack', '@views.executable', '@form_builder'] media_library.route_subscriber: class: Drupal\media_library\Routing\RouteSubscriber tags: @@ -12,4 +12,3 @@ services: - [setContainer, ['@service_container']] media_library.opener.field_widget: class: Drupal\media_library\MediaLibraryFieldWidgetOpener - arguments: ['@entity_type.manager'] diff --git a/core/modules/media_library/src/MediaLibraryFieldWidgetOpener.php b/core/modules/media_library/src/MediaLibraryFieldWidgetOpener.php index b34b8d308b..71ef10b2f2 100644 --- a/core/modules/media_library/src/MediaLibraryFieldWidgetOpener.php +++ b/core/modules/media_library/src/MediaLibraryFieldWidgetOpener.php @@ -2,12 +2,8 @@ namespace Drupal\media_library; -use Drupal\Core\Access\AccessResult; use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\InvokeCommand; -use Drupal\Core\Cache\RefinableCacheableDependencyInterface; -use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Session\AccountInterface; /** @@ -15,92 +11,11 @@ */ class MediaLibraryFieldWidgetOpener implements MediaLibraryOpenerInterface { - /** - * The entity type manager. - * - * @var \Drupal\Core\Entity\EntityTypeManagerInterface - */ - protected $entityTypeManager; - - /** - * MediaLibraryFieldWidgetOpener constructor. - * - * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager - * The entity type manager. - */ - public function __construct(EntityTypeManagerInterface $entity_type_manager) { - $this->entityTypeManager = $entity_type_manager; - } - /** * {@inheritdoc} */ public function checkAccess(MediaLibraryState $state, AccountInterface $account) { - $parameters = $state->getOpenerParameters() + ['entity_id' => NULL]; - - $process_result = function ($result) { - if ($result instanceof RefinableCacheableDependencyInterface) { - $result->addCacheContexts(['url.query_args']); - } - return $result; - }; - - // Forbid access if any of the required parameters are missing. - foreach (['entity_type_id', 'bundle', 'field_name'] as $key) { - if (empty($parameters[$key])) { - return $process_result(AccessResult::forbidden("$key parameter is missing.")); - } - } - - $entity_type_id = $parameters['entity_type_id']; - $bundle = $parameters['bundle']; - $field_name = $parameters['field_name']; - - // Since we defer to a field to determine access, ensure we are dealing with - // a fieldable entity type. - $entity_type = $this->entityTypeManager->getDefinition($entity_type_id); - if (!$entity_type->entityClassImplements(FieldableEntityInterface::class)) { - throw new \LogicException("The media library can only be opened by fieldable entities."); - } - - $storage = $this->entityTypeManager->getStorage($entity_type_id); - $access_handler = $this->entityTypeManager->getAccessControlHandler($entity_type_id); - - if ($parameters['entity_id']) { - $entity = $storage->load($parameters['entity_id']); - $entity_access = $access_handler->access($entity, 'update', $account, TRUE); - } - else { - $entity_access = $access_handler->createAccess($bundle, $account, [], TRUE); - } - - // If entity-level access is denied, there's no point in continuing. - if (!$entity_access->isAllowed()) { - return $process_result($entity_access); - } - - // If the entity has not been loaded, create it in memory now. - if (!isset($entity)) { - $values = []; - if ($bundle_key = $entity_type->getKey('bundle')) { - $values[$bundle_key] = $bundle; - } - /** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */ - $entity = $storage->create($values); - } - - $items = $entity->get($field_name); - $field_definition = $items->getFieldDefinition(); - - if ($field_definition->getType() !== 'entity_reference') { - throw new \LogicException('Expected the media library to be opened by an entity reference field.'); - } - if ($field_definition->getFieldStorageDefinition()->getSetting('target_type') !== 'media') { - throw new \LogicException('Expected the media library to be opened by an entity reference field that target media items.'); - } - - $field_access = $access_handler->fieldAccess('edit', $field_definition, $account, $items, TRUE); - return $process_result($entity_access->andIf($field_access)); + throw new \Exception('Not yet implemented, see https://www.drupal.org/project/drupal/issues/3038254.'); } /** diff --git a/core/modules/media_library/src/MediaLibraryUiBuilder.php b/core/modules/media_library/src/MediaLibraryUiBuilder.php index 32262c962d..56095b09a2 100644 --- a/core/modules/media_library/src/MediaLibraryUiBuilder.php +++ b/core/modules/media_library/src/MediaLibraryUiBuilder.php @@ -53,13 +53,6 @@ class MediaLibraryUiBuilder { */ protected $viewsExecutableFactory; - /** - * The media library opener resolver. - * - * @var \Drupal\media_library\OpenerResolverInterface - */ - protected $openerResolver; - /** * Constructs a MediaLibraryUiBuilder instance. * @@ -71,19 +64,12 @@ class MediaLibraryUiBuilder { * The views executable factory. * @param \Drupal\Core\Form\FormBuilderInterface $form_builder * The currently active request object. - * @param \Drupal\media_library\OpenerResolverInterface $opener_resolver - * The opener resolver. */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, RequestStack $request_stack, ViewExecutableFactory $views_executable_factory, FormBuilderInterface $form_builder, OpenerResolverInterface $opener_resolver = NULL) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, RequestStack $request_stack, ViewExecutableFactory $views_executable_factory, FormBuilderInterface $form_builder) { $this->entityTypeManager = $entity_type_manager; $this->request = $request_stack->getCurrentRequest(); $this->viewsExecutableFactory = $views_executable_factory; $this->formBuilder = $form_builder; - if (!$opener_resolver) { - @trigger_error('The media_library.opener_resolver service must be passed to ' . __METHOD__ . ' and will be required before Drupal 9.0.0.', E_USER_DEPRECATED); - $opener_resolver = \Drupal::service('media_library.opener_resolver'); - } - $this->openerResolver = $opener_resolver; } /** @@ -173,7 +159,7 @@ protected function buildLibraryContent(MediaLibraryState $state) { * Check access to the media library. * * @param \Drupal\Core\Session\AccountInterface $account - * Run access checks for this account. + * (optional) Run access checks for this account. * @param \Drupal\media_library\MediaLibraryState $state * (optional) The current state of the media library, derived from the * current request. @@ -181,10 +167,10 @@ protected function buildLibraryContent(MediaLibraryState $state) { * @return \Drupal\Core\Access\AccessResult * The access result. */ - public function checkAccess(AccountInterface $account, MediaLibraryState $state = NULL) { + public function checkAccess(AccountInterface $account = NULL, MediaLibraryState $state = NULL) { if (!$state) { try { - $state = MediaLibraryState::fromRequest($this->request); + MediaLibraryState::fromRequest($this->request); } catch (BadRequestHttpException $e) { return AccessResult::forbidden($e->getMessage()); @@ -203,16 +189,8 @@ public function checkAccess(AccountInterface $account, MediaLibraryState $state return AccessResult::forbidden('The media library widget display does not exist.') ->addCacheableDependency($view); } - - // The user must at least be able to view media in order to access the media - // library. - $can_view_media = AccessResult::allowedIfHasPermission($account, 'view media') + return AccessResult::allowedIfHasPermission($account, 'view media') ->addCacheableDependency($view); - - // Delegate any further access checking to the opener service nominated by - // the media library state. - return $this->openerResolver->get($state)->checkAccess($state, $account) - ->andIf($can_view_media); } /** diff --git a/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php index 419cf66174..e612c3e4f4 100644 --- a/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php +++ b/core/modules/media_library/src/Plugin/Field/FieldWidget/MediaLibraryWidget.php @@ -461,17 +461,9 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen // This particular media library opener needs some extra metadata for its // \Drupal\media_library\MediaLibraryOpenerInterface::getSelectionResponse() // to be able to target the element whose 'data-media-library-widget-value' - // attribute is the same as $field_widget_id. The entity ID, entity type ID, - // bundle, field name are used for access checking. - $entity = $items->getEntity(); + // attribute is the same as $field_widget_id. $state = MediaLibraryState::create('media_library.opener.field_widget', $allowed_media_type_ids, $selected_type_id, $remaining, [ 'field_widget_id' => $field_widget_id, - 'entity_type_id' => $entity->getEntityTypeId(), - 'bundle' => $entity->bundle(), - 'field_name' => $field_name, - // The entity ID needs to be a string to ensure that the media library - // state generates its tamper-proof hash in a consistent way. - 'entity_id' => (string) $entity->id(), ]); // Add a button that will load the Media library in a modal using AJAX. diff --git a/core/modules/media_library/templates/media--media-library.html.twig b/core/modules/media_library/templates/media--media-library.html.twig index 9e5e0dfc50..a55024a22d 100644 --- a/core/modules/media_library/templates/media--media-library.html.twig +++ b/core/modules/media_library/templates/media--media-library.html.twig @@ -43,7 +43,7 @@ {% endif %}
- {{ name }} + {{ name }}
{% endif %} diff --git a/core/modules/media_library/tests/fixtures/update/drupal-8.7.2-media_library_installed.php b/core/modules/media_library/tests/fixtures/update/drupal-8.7.2-media_library_installed.php deleted file mode 100644 index d8f4b14acb..0000000000 --- a/core/modules/media_library/tests/fixtures/update/drupal-8.7.2-media_library_installed.php +++ /dev/null @@ -1,109 +0,0 @@ -merge('key_value') - ->fields([ - 'value' => 'i:8000;', - 'name' => 'media_library', - 'collection' => 'system.schema', - ]) - ->condition('collection', 'system.schema') - ->condition('name', 'media_library') - ->execute(); - -// Update core.extension. -$extensions = $connection->select('config') - ->fields('config', ['data']) - ->condition('collection', '') - ->condition('name', 'core.extension') - ->execute() - ->fetchField(); -$extensions = unserialize($extensions); -$extensions['module']['media_library'] = 0; -$connection->update('config') - ->fields([ - 'data' => serialize($extensions), - 'collection' => '', - 'name' => 'core.extension', - ]) - ->condition('collection', '') - ->condition('name', 'core.extension') - ->execute(); - -// Insert media library config objects. -$connection->insert('config') -->fields(array( - 'collection', - 'name', - 'data', -)) -->values(array( - 'collection' => '', - 'name' => 'core.entity_form_display.media.file.media_library', - 'data' => 'a:11:{s:4:"uuid";s:36:"86ab9619-c970-4416-971d-e5c8614b3368";s:8:"langcode";s:2:"en";s:6:"status";b:1;s:12:"dependencies";a:1:{s:6:"config";a:3:{i:0;s:41:"core.entity_form_mode.media.media_library";i:1;s:39:"field.field.media.file.field_media_file";i:2;s:15:"media.type.file";}}s:5:"_core";a:1:{s:19:"default_config_hash";s:43:"RFmywOcuem167havmD4VLgBTO1Swq9hyA-_f5aYTi8c";}s:2:"id";s:24:"media.file.media_library";s:16:"targetEntityType";s:5:"media";s:6:"bundle";s:4:"file";s:4:"mode";s:13:"media_library";s:7:"content";a:1:{s:4:"name";a:5:{s:4:"type";s:16:"string_textfield";s:6:"weight";i:0;s:6:"region";s:7:"content";s:8:"settings";a:2:{s:4:"size";i:60;s:11:"placeholder";s:0:"";}s:20:"third_party_settings";a:0:{}}}s:6:"hidden";a:5:{s:7:"created";b:1;s:16:"field_media_file";b:1;s:4:"path";b:1;s:6:"status";b:1;s:3:"uid";b:1;}}', -)) -->values(array( - 'collection' => '', - 'name' => 'core.entity_form_display.media.image.media_library', - 'data' => 'a:11:{s:4:"uuid";s:36:"2bbea060-3cd8-4881-a3aa-c898d6619b16";s:8:"langcode";s:2:"en";s:6:"status";b:1;s:12:"dependencies";a:2:{s:6:"config";a:4:{i:0;s:41:"core.entity_form_mode.media.media_library";i:1;s:41:"field.field.media.image.field_media_image";i:2;s:21:"image.style.thumbnail";i:3;s:16:"media.type.image";}s:6:"module";a:1:{i:0;s:5:"image";}}s:5:"_core";a:1:{s:19:"default_config_hash";s:43:"PlyfyVZfALLkP7nbxLpaVKIDUWRioZghWpFDv0_rJ68";}s:2:"id";s:25:"media.image.media_library";s:16:"targetEntityType";s:5:"media";s:6:"bundle";s:5:"image";s:4:"mode";s:13:"media_library";s:7:"content";a:2:{s:17:"field_media_image";a:5:{s:4:"type";s:11:"image_image";s:6:"weight";i:1;s:6:"region";s:7:"content";s:8:"settings";a:2:{s:18:"progress_indicator";s:8:"throbber";s:19:"preview_image_style";s:9:"thumbnail";}s:20:"third_party_settings";a:0:{}}s:4:"name";a:5:{s:4:"type";s:16:"string_textfield";s:6:"weight";i:0;s:6:"region";s:7:"content";s:8:"settings";a:2:{s:4:"size";i:60;s:11:"placeholder";s:0:"";}s:20:"third_party_settings";a:0:{}}}s:6:"hidden";a:4:{s:7:"created";b:1;s:4:"path";b:1;s:6:"status";b:1;s:3:"uid";b:1;}}', -)) -->values(array( - 'collection' => '', - 'name' => 'core.entity_view_display.media.file.media_library', - 'data' => 'a:11:{s:4:"uuid";s:36:"67e6d857-8ecb-49f5-95e1-6b1c4306c31f";s:8:"langcode";s:2:"en";s:6:"status";b:1;s:12:"dependencies";a:2:{s:6:"config";a:4:{i:0;s:41:"core.entity_view_mode.media.media_library";i:1;s:39:"field.field.media.file.field_media_file";i:2;s:21:"image.style.thumbnail";i:3;s:15:"media.type.file";}s:6:"module";a:1:{i:0;s:5:"image";}}s:5:"_core";a:1:{s:19:"default_config_hash";s:43:"vhAK2lCOWK2paUpJawj7yiSLFO9wwsx6WE8_oDmvbwU";}s:2:"id";s:24:"media.file.media_library";s:16:"targetEntityType";s:5:"media";s:6:"bundle";s:4:"file";s:4:"mode";s:13:"media_library";s:7:"content";a:1:{s:9:"thumbnail";a:6:{s:4:"type";s:5:"image";s:6:"weight";i:0;s:6:"region";s:7:"content";s:5:"label";s:6:"hidden";s:8:"settings";a:2:{s:11:"image_style";s:9:"thumbnail";s:10:"image_link";s:0:"";}s:20:"third_party_settings";a:0:{}}}s:6:"hidden";a:4:{s:7:"created";b:1;s:16:"field_media_file";b:1;s:4:"name";b:1;s:3:"uid";b:1;}}', -)) -->values(array( - 'collection' => '', - 'name' => 'core.entity_view_display.media.image.media_library', - 'data' => 'a:11:{s:4:"uuid";s:36:"277ca98b-2ada-4251-ad69-aa73e72d60fe";s:8:"langcode";s:2:"en";s:6:"status";b:1;s:12:"dependencies";a:2:{s:6:"config";a:4:{i:0;s:41:"core.entity_view_mode.media.media_library";i:1;s:41:"field.field.media.image.field_media_image";i:2;s:18:"image.style.medium";i:3;s:16:"media.type.image";}s:6:"module";a:1:{i:0;s:5:"image";}}s:5:"_core";a:1:{s:19:"default_config_hash";s:43:"PaGXvzRcL9eII--JV4eCVfObjrNo0l-u1dB_WJtB9ig";}s:2:"id";s:25:"media.image.media_library";s:16:"targetEntityType";s:5:"media";s:6:"bundle";s:5:"image";s:4:"mode";s:13:"media_library";s:7:"content";a:1:{s:9:"thumbnail";a:6:{s:4:"type";s:5:"image";s:6:"weight";i:0;s:6:"region";s:7:"content";s:5:"label";s:6:"hidden";s:8:"settings";a:2:{s:11:"image_style";s:6:"medium";s:10:"image_link";s:0:"";}s:20:"third_party_settings";a:0:{}}}s:6:"hidden";a:4:{s:7:"created";b:1;s:17:"field_media_image";b:1;s:4:"name";b:1;s:3:"uid";b:1;}}', -)) -->values(array( - 'collection' => '', - 'name' => 'core.entity_view_mode.media.media_library', - 'data' => 'a:9:{s:4:"uuid";s:36:"20b2f1f7-a864-4d41-a15f-32f66789f73d";s:8:"langcode";s:2:"en";s:6:"status";b:1;s:12:"dependencies";a:2:{s:8:"enforced";a:1:{s:6:"module";a:1:{i:0;s:13:"media_library";}}s:6:"module";a:1:{i:0;s:5:"media";}}s:5:"_core";a:1:{s:19:"default_config_hash";s:43:"pkq0uj-IoqEQRBOP_ddUDV0ZJ-dKQ_fLcppsEDF2UO8";}s:2:"id";s:19:"media.media_library";s:5:"label";s:13:"Media library";s:16:"targetEntityType";s:5:"media";s:5:"cache";b:1;}', -)) -->values(array( - 'collection' => '', - 'name' => 'views.view.media_library', - 'data' => 'a:14:{s:4:"uuid";s:36:"e42c9697-889f-41f8-a752-4d91aa8997a7";s:8:"langcode";s:2:"en";s:6:"status";b:1;s:12:"dependencies";a:3:{s:6:"config";a:2:{i:0;s:41:"core.entity_view_mode.media.media_library";i:1;s:25:"image.style.media_library";}s:8:"enforced";a:1:{s:6:"module";a:1:{i:0;s:13:"media_library";}}s:6:"module";a:4:{i:0;s:5:"image";i:1;s:5:"media";i:2;s:13:"media_library";i:3;s:4:"user";}}s:5:"_core";a:1:{s:19:"default_config_hash";s:43:"WM0EWBXTR1beBFa860NA1kUdffL9bM1548gUS9DS5fg";}s:2:"id";s:13:"media_library";s:5:"label";s:13:"Media library";s:6:"module";s:5:"views";s:11:"description";s:0:"";s:3:"tag";s:0:"";s:10:"base_table";s:16:"media_field_data";s:10:"base_field";s:3:"mid";s:4:"core";s:3:"8.x";s:7:"display";a:4:{s:7:"default";a:6:{s:14:"display_plugin";s:7:"default";s:2:"id";s:7:"default";s:13:"display_title";s:6:"Master";s:8:"position";i:0;s:15:"display_options";a:18:{s:6:"access";a:2:{s:4:"type";s:4:"perm";s:7:"options";a:1:{s:4:"perm";s:21:"access media overview";}}s:5:"cache";a:2:{s:4:"type";s:3:"tag";s:7:"options";a:0:{}}s:5:"query";a:2:{s:4:"type";s:11:"views_query";s:7:"options";a:5:{s:19:"disable_sql_rewrite";b:0;s:8:"distinct";b:0;s:7:"replica";b:0;s:13:"query_comment";s:0:"";s:10:"query_tags";a:0:{}}}s:12:"exposed_form";a:2:{s:4:"type";s:5:"basic";s:7:"options";a:7:{s:13:"submit_button";s:13:"Apply filters";s:12:"reset_button";b:0;s:18:"reset_button_label";s:5:"Reset";s:19:"exposed_sorts_label";s:7:"Sort by";s:17:"expose_sort_order";b:0;s:14:"sort_asc_label";s:3:"Asc";s:15:"sort_desc_label";s:4:"Desc";}}s:5:"pager";a:2:{s:4:"type";s:4:"mini";s:7:"options";a:6:{s:14:"items_per_page";i:24;s:6:"offset";i:0;s:2:"id";i:0;s:11:"total_pages";N;s:6:"expose";a:7:{s:14:"items_per_page";b:0;s:20:"items_per_page_label";s:14:"Items per page";s:22:"items_per_page_options";s:13:"6, 12, 24, 48";s:26:"items_per_page_options_all";b:0;s:32:"items_per_page_options_all_label";s:7:"- All -";s:6:"offset";b:0;s:12:"offset_label";s:6:"Offset";}s:4:"tags";a:2:{s:8:"previous";s:6:"‹‹";s:4:"next";s:6:"››";}}}s:5:"style";a:2:{s:4:"type";s:7:"default";s:7:"options";a:3:{s:8:"grouping";a:0:{}s:9:"row_class";s:84:"media-library-item media-library-item--grid js-media-library-item js-click-to-select";s:17:"default_row_class";b:1;}}s:3:"row";a:2:{s:4:"type";s:6:"fields";s:7:"options";a:4:{s:22:"default_field_elements";b:1;s:6:"inline";a:0:{}s:9:"separator";s:0:"";s:10:"hide_empty";b:0;}}s:6:"fields";a:2:{s:15:"media_bulk_form";a:26:{s:2:"id";s:15:"media_bulk_form";s:5:"table";s:5:"media";s:5:"field";s:15:"media_bulk_form";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:5:"label";s:0:"";s:7:"exclude";b:0;s:5:"alter";a:26:{s:10:"alter_text";b:0;s:4:"text";s:0:"";s:9:"make_link";b:0;s:4:"path";s:0:"";s:8:"absolute";b:0;s:8:"external";b:0;s:14:"replace_spaces";b:0;s:9:"path_case";s:4:"none";s:15:"trim_whitespace";b:0;s:3:"alt";s:0:"";s:3:"rel";s:0:"";s:10:"link_class";s:0:"";s:6:"prefix";s:0:"";s:6:"suffix";s:0:"";s:6:"target";s:0:"";s:5:"nl2br";b:0;s:10:"max_length";i:0;s:13:"word_boundary";b:1;s:8:"ellipsis";b:1;s:9:"more_link";b:0;s:14:"more_link_text";s:0:"";s:14:"more_link_path";s:0:"";s:10:"strip_tags";b:0;s:4:"trim";b:0;s:13:"preserve_tags";s:0:"";s:4:"html";b:0;}s:12:"element_type";s:0:"";s:13:"element_class";s:27:"js-click-to-select-checkbox";s:18:"element_label_type";s:0:"";s:19:"element_label_class";s:0:"";s:19:"element_label_colon";b:0;s:20:"element_wrapper_type";s:0:"";s:21:"element_wrapper_class";s:0:"";s:23:"element_default_classes";b:1;s:5:"empty";s:0:"";s:10:"hide_empty";b:0;s:10:"empty_zero";b:0;s:16:"hide_alter_empty";b:1;s:12:"action_title";s:6:"Action";s:15:"include_exclude";s:7:"exclude";s:16:"selected_actions";a:0:{}s:11:"entity_type";s:5:"media";s:9:"plugin_id";s:9:"bulk_form";}s:15:"rendered_entity";a:24:{s:2:"id";s:15:"rendered_entity";s:5:"table";s:5:"media";s:5:"field";s:15:"rendered_entity";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:5:"label";s:0:"";s:7:"exclude";b:0;s:5:"alter";a:26:{s:10:"alter_text";b:0;s:4:"text";s:0:"";s:9:"make_link";b:0;s:4:"path";s:0:"";s:8:"absolute";b:0;s:8:"external";b:0;s:14:"replace_spaces";b:0;s:9:"path_case";s:4:"none";s:15:"trim_whitespace";b:0;s:3:"alt";s:0:"";s:3:"rel";s:0:"";s:10:"link_class";s:0:"";s:6:"prefix";s:0:"";s:6:"suffix";s:0:"";s:6:"target";s:0:"";s:5:"nl2br";b:0;s:10:"max_length";i:0;s:13:"word_boundary";b:1;s:8:"ellipsis";b:1;s:9:"more_link";b:0;s:14:"more_link_text";s:0:"";s:14:"more_link_path";s:0:"";s:10:"strip_tags";b:0;s:4:"trim";b:0;s:13:"preserve_tags";s:0:"";s:4:"html";b:0;}s:12:"element_type";s:0:"";s:13:"element_class";s:27:"media-library-item__content";s:18:"element_label_type";s:0:"";s:19:"element_label_class";s:0:"";s:19:"element_label_colon";b:0;s:20:"element_wrapper_type";s:0:"";s:21:"element_wrapper_class";s:0:"";s:23:"element_default_classes";b:1;s:5:"empty";s:0:"";s:10:"hide_empty";b:0;s:10:"empty_zero";b:0;s:16:"hide_alter_empty";b:1;s:9:"view_mode";s:13:"media_library";s:11:"entity_type";s:5:"media";s:9:"plugin_id";s:15:"rendered_entity";}}s:7:"filters";a:3:{s:6:"status";a:16:{s:2:"id";s:6:"status";s:5:"table";s:16:"media_field_data";s:5:"field";s:6:"status";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:8:"operator";s:1:"=";s:5:"value";s:1:"1";s:5:"group";i:1;s:7:"exposed";b:1;s:6:"expose";a:12:{s:11:"operator_id";s:0:"";s:5:"label";s:17:"Publishing status";s:11:"description";N;s:12:"use_operator";b:0;s:8:"operator";s:9:"status_op";s:10:"identifier";s:6:"status";s:8:"required";b:1;s:8:"remember";b:0;s:8:"multiple";b:0;s:14:"remember_roles";a:1:{s:13:"authenticated";s:13:"authenticated";}s:24:"operator_limit_selection";b:0;s:13:"operator_list";a:0:{}}s:10:"is_grouped";b:1;s:10:"group_info";a:10:{s:5:"label";s:9:"Published";s:11:"description";s:0:"";s:10:"identifier";s:6:"status";s:8:"optional";b:1;s:6:"widget";s:6:"select";s:8:"multiple";b:0;s:8:"remember";b:0;s:13:"default_group";s:3:"All";s:22:"default_group_multiple";a:0:{}s:11:"group_items";a:2:{i:1;a:3:{s:5:"title";s:9:"Published";s:8:"operator";s:1:"=";s:5:"value";s:1:"1";}i:2;a:3:{s:5:"title";s:11:"Unpublished";s:8:"operator";s:1:"=";s:5:"value";s:1:"0";}}}s:9:"plugin_id";s:7:"boolean";s:11:"entity_type";s:5:"media";s:12:"entity_field";s:6:"status";}s:4:"name";a:16:{s:2:"id";s:4:"name";s:5:"table";s:16:"media_field_data";s:5:"field";s:4:"name";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:8:"operator";s:8:"contains";s:5:"value";s:0:"";s:5:"group";i:1;s:7:"exposed";b:1;s:6:"expose";a:12:{s:11:"operator_id";s:7:"name_op";s:5:"label";s:4:"Name";s:11:"description";s:0:"";s:12:"use_operator";b:0;s:8:"operator";s:7:"name_op";s:10:"identifier";s:4:"name";s:8:"required";b:0;s:8:"remember";b:0;s:8:"multiple";b:0;s:14:"remember_roles";a:3:{s:13:"authenticated";s:13:"authenticated";s:9:"anonymous";s:1:"0";s:13:"administrator";s:1:"0";}s:24:"operator_limit_selection";b:0;s:13:"operator_list";a:0:{}}s:10:"is_grouped";b:0;s:10:"group_info";a:10:{s:5:"label";s:0:"";s:11:"description";s:0:"";s:10:"identifier";s:0:"";s:8:"optional";b:1;s:6:"widget";s:6:"select";s:8:"multiple";b:0;s:8:"remember";b:0;s:13:"default_group";s:3:"All";s:22:"default_group_multiple";a:0:{}s:11:"group_items";a:0:{}}s:11:"entity_type";s:5:"media";s:12:"entity_field";s:4:"name";s:9:"plugin_id";s:6:"string";}s:6:"bundle";a:16:{s:2:"id";s:6:"bundle";s:5:"table";s:16:"media_field_data";s:5:"field";s:6:"bundle";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:8:"operator";s:2:"in";s:5:"value";a:0:{}s:5:"group";i:1;s:7:"exposed";b:1;s:6:"expose";a:13:{s:11:"operator_id";s:9:"bundle_op";s:5:"label";s:10:"Media type";s:11:"description";s:0:"";s:12:"use_operator";b:0;s:8:"operator";s:9:"bundle_op";s:10:"identifier";s:4:"type";s:8:"required";b:0;s:8:"remember";b:0;s:8:"multiple";b:0;s:14:"remember_roles";a:3:{s:13:"authenticated";s:13:"authenticated";s:9:"anonymous";s:1:"0";s:13:"administrator";s:1:"0";}s:6:"reduce";b:0;s:24:"operator_limit_selection";b:0;s:13:"operator_list";a:0:{}}s:10:"is_grouped";b:0;s:10:"group_info";a:10:{s:5:"label";s:10:"Media type";s:11:"description";N;s:10:"identifier";s:6:"bundle";s:8:"optional";b:1;s:6:"widget";s:6:"select";s:8:"multiple";b:0;s:8:"remember";b:0;s:13:"default_group";s:3:"All";s:22:"default_group_multiple";a:0:{}s:11:"group_items";a:3:{i:1;a:0:{}i:2;a:0:{}i:3;a:0:{}}}s:11:"entity_type";s:5:"media";s:12:"entity_field";s:6:"bundle";s:9:"plugin_id";s:6:"bundle";}}s:5:"sorts";a:3:{s:7:"created";a:13:{s:2:"id";s:7:"created";s:5:"table";s:16:"media_field_data";s:5:"field";s:7:"created";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:5:"order";s:4:"DESC";s:7:"exposed";b:1;s:6:"expose";a:1:{s:5:"label";s:12:"Newest first";}s:11:"granularity";s:6:"second";s:11:"entity_type";s:5:"media";s:12:"entity_field";s:7:"created";s:9:"plugin_id";s:4:"date";}s:4:"name";a:12:{s:2:"id";s:4:"name";s:5:"table";s:16:"media_field_data";s:5:"field";s:4:"name";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:5:"order";s:3:"ASC";s:7:"exposed";b:1;s:6:"expose";a:1:{s:5:"label";s:10:"Name (A-Z)";}s:11:"entity_type";s:5:"media";s:12:"entity_field";s:4:"name";s:9:"plugin_id";s:8:"standard";}s:6:"name_1";a:12:{s:2:"id";s:6:"name_1";s:5:"table";s:16:"media_field_data";s:5:"field";s:4:"name";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:5:"order";s:4:"DESC";s:7:"exposed";b:1;s:6:"expose";a:1:{s:5:"label";s:10:"Name (Z-A)";}s:11:"entity_type";s:5:"media";s:12:"entity_field";s:4:"name";s:9:"plugin_id";s:8:"standard";}}s:5:"title";s:5:"Media";s:6:"header";a:0:{}s:6:"footer";a:0:{}s:5:"empty";a:1:{s:16:"area_text_custom";a:10:{s:2:"id";s:16:"area_text_custom";s:5:"table";s:5:"views";s:5:"field";s:16:"area_text_custom";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:5:"empty";b:1;s:8:"tokenize";b:0;s:7:"content";s:19:"No media available.";s:9:"plugin_id";s:11:"text_custom";}}s:13:"relationships";a:0:{}s:17:"display_extenders";a:0:{}s:8:"use_ajax";b:1;s:9:"css_class";s:40:"media-library-view js-media-library-view";}s:14:"cache_metadata";a:3:{s:7:"max-age";i:0;s:8:"contexts";a:5:{i:0;s:28:"languages:language_interface";i:1;s:3:"url";i:2;s:14:"url.query_args";i:3;s:22:"url.query_args:sort_by";i:4;s:16:"user.permissions";}s:4:"tags";a:0:{}}}s:4:"page";a:6:{s:14:"display_plugin";s:4:"page";s:2:"id";s:4:"page";s:13:"display_title";s:4:"Page";s:8:"position";i:1;s:15:"display_options";a:3:{s:17:"display_extenders";a:0:{}s:4:"path";s:19:"admin/content/media";s:4:"menu";a:8:{s:4:"type";s:3:"tab";s:5:"title";s:5:"Media";s:11:"description";s:49:"Allows users to browse and administer media items";s:8:"expanded";b:0;s:6:"parent";s:20:"system.admin_content";s:6:"weight";i:5;s:7:"context";s:1:"0";s:9:"menu_name";s:5:"admin";}}s:14:"cache_metadata";a:3:{s:7:"max-age";i:0;s:8:"contexts";a:5:{i:0;s:28:"languages:language_interface";i:1;s:3:"url";i:2;s:14:"url.query_args";i:3;s:22:"url.query_args:sort_by";i:4;s:16:"user.permissions";}s:4:"tags";a:0:{}}}s:6:"widget";a:6:{s:14:"display_plugin";s:4:"page";s:2:"id";s:6:"widget";s:13:"display_title";s:6:"Widget";s:8:"position";i:2;s:15:"display_options";a:11:{s:17:"display_extenders";a:0:{}s:4:"path";s:26:"admin/content/media-widget";s:6:"fields";a:2:{s:15:"rendered_entity";a:24:{s:2:"id";s:15:"rendered_entity";s:5:"table";s:5:"media";s:5:"field";s:15:"rendered_entity";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:5:"label";s:0:"";s:7:"exclude";b:0;s:5:"alter";a:26:{s:10:"alter_text";b:0;s:4:"text";s:0:"";s:9:"make_link";b:0;s:4:"path";s:0:"";s:8:"absolute";b:0;s:8:"external";b:0;s:14:"replace_spaces";b:0;s:9:"path_case";s:4:"none";s:15:"trim_whitespace";b:0;s:3:"alt";s:0:"";s:3:"rel";s:0:"";s:10:"link_class";s:0:"";s:6:"prefix";s:0:"";s:6:"suffix";s:0:"";s:6:"target";s:0:"";s:5:"nl2br";b:0;s:10:"max_length";i:0;s:13:"word_boundary";b:1;s:8:"ellipsis";b:1;s:9:"more_link";b:0;s:14:"more_link_text";s:0:"";s:14:"more_link_path";s:0:"";s:10:"strip_tags";b:0;s:4:"trim";b:0;s:13:"preserve_tags";s:0:"";s:4:"html";b:0;}s:12:"element_type";s:0:"";s:13:"element_class";s:27:"media-library-item__content";s:18:"element_label_type";s:0:"";s:19:"element_label_class";s:0:"";s:19:"element_label_colon";b:0;s:20:"element_wrapper_type";s:0:"";s:21:"element_wrapper_class";s:0:"";s:23:"element_default_classes";b:1;s:5:"empty";s:0:"";s:10:"hide_empty";b:0;s:10:"empty_zero";b:0;s:16:"hide_alter_empty";b:1;s:9:"view_mode";s:13:"media_library";s:11:"entity_type";s:5:"media";s:9:"plugin_id";s:15:"rendered_entity";}s:25:"media_library_select_form";a:23:{s:2:"id";s:25:"media_library_select_form";s:5:"table";s:5:"media";s:5:"field";s:25:"media_library_select_form";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:5:"label";s:0:"";s:7:"exclude";b:0;s:5:"alter";a:26:{s:10:"alter_text";b:0;s:4:"text";s:0:"";s:9:"make_link";b:0;s:4:"path";s:0:"";s:8:"absolute";b:0;s:8:"external";b:0;s:14:"replace_spaces";b:0;s:9:"path_case";s:4:"none";s:15:"trim_whitespace";b:0;s:3:"alt";s:0:"";s:3:"rel";s:0:"";s:10:"link_class";s:0:"";s:6:"prefix";s:0:"";s:6:"suffix";s:0:"";s:6:"target";s:0:"";s:5:"nl2br";b:0;s:10:"max_length";i:0;s:13:"word_boundary";b:1;s:8:"ellipsis";b:1;s:9:"more_link";b:0;s:14:"more_link_text";s:0:"";s:14:"more_link_path";s:0:"";s:10:"strip_tags";b:0;s:4:"trim";b:0;s:13:"preserve_tags";s:0:"";s:4:"html";b:0;}s:12:"element_type";s:0:"";s:13:"element_class";s:0:"";s:18:"element_label_type";s:0:"";s:19:"element_label_class";s:0:"";s:19:"element_label_colon";b:0;s:20:"element_wrapper_type";s:0:"";s:21:"element_wrapper_class";s:27:"js-click-to-select-checkbox";s:23:"element_default_classes";b:1;s:5:"empty";s:0:"";s:10:"hide_empty";b:0;s:10:"empty_zero";b:0;s:16:"hide_alter_empty";b:1;s:11:"entity_type";s:5:"media";s:9:"plugin_id";s:25:"media_library_select_form";}}s:8:"defaults";a:7:{s:6:"fields";b:0;s:6:"access";b:0;s:7:"filters";b:0;s:13:"filter_groups";b:0;s:9:"arguments";b:0;s:6:"header";b:0;s:9:"css_class";b:0;}s:19:"display_description";s:0:"";s:6:"access";a:2:{s:4:"type";s:4:"perm";s:7:"options";a:1:{s:4:"perm";s:10:"view media";}}s:7:"filters";a:2:{s:6:"status";a:16:{s:2:"id";s:6:"status";s:5:"table";s:16:"media_field_data";s:5:"field";s:6:"status";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:8:"operator";s:1:"=";s:5:"value";s:1:"1";s:5:"group";i:1;s:7:"exposed";b:0;s:6:"expose";a:12:{s:11:"operator_id";s:0:"";s:5:"label";s:0:"";s:11:"description";s:0:"";s:12:"use_operator";b:0;s:8:"operator";s:0:"";s:10:"identifier";s:0:"";s:8:"required";b:0;s:8:"remember";b:0;s:8:"multiple";b:0;s:14:"remember_roles";a:1:{s:13:"authenticated";s:13:"authenticated";}s:24:"operator_limit_selection";b:0;s:13:"operator_list";a:0:{}}s:10:"is_grouped";b:0;s:10:"group_info";a:10:{s:5:"label";s:0:"";s:11:"description";s:0:"";s:10:"identifier";s:0:"";s:8:"optional";b:1;s:6:"widget";s:6:"select";s:8:"multiple";b:0;s:8:"remember";b:0;s:13:"default_group";s:3:"All";s:22:"default_group_multiple";a:0:{}s:11:"group_items";a:0:{}}s:11:"entity_type";s:5:"media";s:12:"entity_field";s:6:"status";s:9:"plugin_id";s:7:"boolean";}s:4:"name";a:16:{s:2:"id";s:4:"name";s:5:"table";s:16:"media_field_data";s:5:"field";s:4:"name";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:8:"operator";s:8:"contains";s:5:"value";s:0:"";s:5:"group";i:1;s:7:"exposed";b:1;s:6:"expose";a:12:{s:11:"operator_id";s:7:"name_op";s:5:"label";s:4:"Name";s:11:"description";s:0:"";s:12:"use_operator";b:0;s:8:"operator";s:7:"name_op";s:10:"identifier";s:4:"name";s:8:"required";b:0;s:8:"remember";b:0;s:8:"multiple";b:0;s:14:"remember_roles";a:3:{s:13:"authenticated";s:13:"authenticated";s:9:"anonymous";s:1:"0";s:13:"administrator";s:1:"0";}s:24:"operator_limit_selection";b:0;s:13:"operator_list";a:0:{}}s:10:"is_grouped";b:0;s:10:"group_info";a:10:{s:5:"label";s:0:"";s:11:"description";s:0:"";s:10:"identifier";s:0:"";s:8:"optional";b:1;s:6:"widget";s:6:"select";s:8:"multiple";b:0;s:8:"remember";b:0;s:13:"default_group";s:3:"All";s:22:"default_group_multiple";a:0:{}s:11:"group_items";a:0:{}}s:11:"entity_type";s:5:"media";s:12:"entity_field";s:4:"name";s:9:"plugin_id";s:6:"string";}}s:13:"filter_groups";a:2:{s:8:"operator";s:3:"AND";s:6:"groups";a:1:{i:1;s:3:"AND";}}s:9:"arguments";a:1:{s:6:"bundle";a:27:{s:2:"id";s:6:"bundle";s:5:"table";s:16:"media_field_data";s:5:"field";s:6:"bundle";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:14:"default_action";s:6:"ignore";s:9:"exception";a:3:{s:5:"value";s:3:"all";s:12:"title_enable";b:0;s:5:"title";s:3:"All";}s:12:"title_enable";b:0;s:5:"title";s:0:"";s:21:"default_argument_type";s:5:"fixed";s:24:"default_argument_options";a:1:{s:8:"argument";s:0:"";}s:25:"default_argument_skip_url";b:0;s:15:"summary_options";a:4:{s:9:"base_path";s:0:"";s:5:"count";b:1;s:14:"items_per_page";i:24;s:8:"override";b:0;}s:7:"summary";a:3:{s:10:"sort_order";s:3:"asc";s:17:"number_of_records";i:0;s:6:"format";s:15:"default_summary";}s:18:"specify_validation";b:0;s:8:"validate";a:2:{s:4:"type";s:4:"none";s:4:"fail";s:9:"not found";}s:16:"validate_options";a:0:{}s:8:"glossary";b:0;s:5:"limit";i:0;s:4:"case";s:4:"none";s:9:"path_case";s:4:"none";s:14:"transform_dash";b:0;s:12:"break_phrase";b:0;s:11:"entity_type";s:5:"media";s:12:"entity_field";s:6:"bundle";s:9:"plugin_id";s:6:"string";}}s:6:"header";a:2:{s:17:"display_link_grid";a:7:{s:2:"id";s:17:"display_link_grid";s:5:"table";s:5:"views";s:5:"field";s:12:"display_link";s:10:"display_id";s:6:"widget";s:5:"label";s:4:"Grid";s:9:"plugin_id";s:12:"display_link";s:5:"empty";b:1;}s:18:"display_link_table";a:7:{s:2:"id";s:18:"display_link_table";s:5:"table";s:5:"views";s:5:"field";s:12:"display_link";s:10:"display_id";s:12:"widget_table";s:5:"label";s:5:"Table";s:9:"plugin_id";s:12:"display_link";s:5:"empty";b:1;}}s:9:"css_class";s:67:"media-library-view js-media-library-view media-library-view--widget";}s:14:"cache_metadata";a:3:{s:7:"max-age";i:-1;s:8:"contexts";a:5:{i:0;s:28:"languages:language_interface";i:1;s:3:"url";i:2;s:14:"url.query_args";i:3;s:22:"url.query_args:sort_by";i:4;s:16:"user.permissions";}s:4:"tags";a:0:{}}}s:12:"widget_table";a:6:{s:14:"display_plugin";s:4:"page";s:2:"id";s:12:"widget_table";s:13:"display_title";s:14:"Widget (table)";s:8:"position";i:3;s:15:"display_options";a:12:{s:17:"display_extenders";a:0:{}s:4:"path";s:32:"admin/content/media-widget-table";s:5:"style";a:2:{s:4:"type";s:5:"table";s:7:"options";a:2:{s:9:"row_class";s:85:"media-library-item media-library-item--table js-media-library-item js-click-to-select";s:17:"default_row_class";b:1;}}s:8:"defaults";a:9:{s:5:"style";b:0;s:3:"row";b:0;s:6:"fields";b:0;s:6:"access";b:0;s:7:"filters";b:0;s:13:"filter_groups";b:0;s:9:"arguments";b:0;s:6:"header";b:0;s:9:"css_class";b:0;}s:3:"row";a:1:{s:4:"type";s:6:"fields";}s:6:"fields";a:5:{s:25:"media_library_select_form";a:9:{s:2:"id";s:25:"media_library_select_form";s:5:"label";s:0:"";s:5:"table";s:5:"media";s:5:"field";s:25:"media_library_select_form";s:12:"relationship";s:4:"none";s:11:"entity_type";s:5:"media";s:9:"plugin_id";s:25:"media_library_select_form";s:21:"element_wrapper_class";s:27:"js-click-to-select-checkbox";s:13:"element_class";s:0:"";}s:20:"thumbnail__target_id";a:10:{s:2:"id";s:20:"thumbnail__target_id";s:5:"label";s:9:"Thumbnail";s:5:"table";s:16:"media_field_data";s:5:"field";s:20:"thumbnail__target_id";s:12:"relationship";s:4:"none";s:4:"type";s:5:"image";s:11:"entity_type";s:5:"media";s:12:"entity_field";s:9:"thumbnail";s:9:"plugin_id";s:5:"field";s:8:"settings";a:2:{s:11:"image_style";s:13:"media_library";s:10:"image_link";s:0:"";}}s:4:"name";a:10:{s:2:"id";s:4:"name";s:5:"label";s:4:"Name";s:5:"table";s:16:"media_field_data";s:5:"field";s:4:"name";s:12:"relationship";s:4:"none";s:4:"type";s:6:"string";s:11:"entity_type";s:5:"media";s:12:"entity_field";s:4:"name";s:9:"plugin_id";s:5:"field";s:8:"settings";a:1:{s:14:"link_to_entity";b:0;}}s:3:"uid";a:10:{s:2:"id";s:3:"uid";s:5:"label";s:6:"Author";s:5:"table";s:20:"media_field_revision";s:5:"field";s:3:"uid";s:12:"relationship";s:4:"none";s:4:"type";s:22:"entity_reference_label";s:11:"entity_type";s:5:"media";s:12:"entity_field";s:3:"uid";s:9:"plugin_id";s:5:"field";s:8:"settings";a:1:{s:4:"link";b:1;}}s:7:"changed";a:10:{s:2:"id";s:7:"changed";s:5:"label";s:7:"Updated";s:5:"table";s:16:"media_field_data";s:5:"field";s:7:"changed";s:12:"relationship";s:4:"none";s:4:"type";s:9:"timestamp";s:11:"entity_type";s:5:"media";s:12:"entity_field";s:7:"changed";s:9:"plugin_id";s:5:"field";s:8:"settings";a:3:{s:11:"date_format";s:5:"short";s:18:"custom_date_format";s:0:"";s:8:"timezone";s:0:"";}}}s:6:"access";a:2:{s:4:"type";s:4:"perm";s:7:"options";a:1:{s:4:"perm";s:10:"view media";}}s:7:"filters";a:2:{s:6:"status";a:16:{s:2:"id";s:6:"status";s:5:"table";s:16:"media_field_data";s:5:"field";s:6:"status";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:8:"operator";s:1:"=";s:5:"value";s:1:"1";s:5:"group";i:1;s:7:"exposed";b:0;s:6:"expose";a:12:{s:11:"operator_id";s:0:"";s:5:"label";s:0:"";s:11:"description";s:0:"";s:12:"use_operator";b:0;s:8:"operator";s:0:"";s:10:"identifier";s:0:"";s:8:"required";b:0;s:8:"remember";b:0;s:8:"multiple";b:0;s:14:"remember_roles";a:1:{s:13:"authenticated";s:13:"authenticated";}s:24:"operator_limit_selection";b:0;s:13:"operator_list";a:0:{}}s:10:"is_grouped";b:0;s:10:"group_info";a:10:{s:5:"label";s:0:"";s:11:"description";s:0:"";s:10:"identifier";s:0:"";s:8:"optional";b:1;s:6:"widget";s:6:"select";s:8:"multiple";b:0;s:8:"remember";b:0;s:13:"default_group";s:3:"All";s:22:"default_group_multiple";a:0:{}s:11:"group_items";a:0:{}}s:11:"entity_type";s:5:"media";s:12:"entity_field";s:6:"status";s:9:"plugin_id";s:7:"boolean";}s:4:"name";a:16:{s:2:"id";s:4:"name";s:5:"table";s:16:"media_field_data";s:5:"field";s:4:"name";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:8:"operator";s:8:"contains";s:5:"value";s:0:"";s:5:"group";i:1;s:7:"exposed";b:1;s:6:"expose";a:12:{s:11:"operator_id";s:7:"name_op";s:5:"label";s:4:"Name";s:11:"description";s:0:"";s:12:"use_operator";b:0;s:8:"operator";s:7:"name_op";s:10:"identifier";s:4:"name";s:8:"required";b:0;s:8:"remember";b:0;s:8:"multiple";b:0;s:14:"remember_roles";a:3:{s:13:"authenticated";s:13:"authenticated";s:9:"anonymous";s:1:"0";s:13:"administrator";s:1:"0";}s:24:"operator_limit_selection";b:0;s:13:"operator_list";a:0:{}}s:10:"is_grouped";b:0;s:10:"group_info";a:10:{s:5:"label";s:0:"";s:11:"description";s:0:"";s:10:"identifier";s:0:"";s:8:"optional";b:1;s:6:"widget";s:6:"select";s:8:"multiple";b:0;s:8:"remember";b:0;s:13:"default_group";s:3:"All";s:22:"default_group_multiple";a:0:{}s:11:"group_items";a:0:{}}s:11:"entity_type";s:5:"media";s:12:"entity_field";s:4:"name";s:9:"plugin_id";s:6:"string";}}s:13:"filter_groups";a:2:{s:8:"operator";s:3:"AND";s:6:"groups";a:1:{i:1;s:3:"AND";}}s:9:"arguments";a:1:{s:6:"bundle";a:27:{s:2:"id";s:6:"bundle";s:5:"table";s:16:"media_field_data";s:5:"field";s:6:"bundle";s:12:"relationship";s:4:"none";s:10:"group_type";s:5:"group";s:11:"admin_label";s:0:"";s:14:"default_action";s:6:"ignore";s:9:"exception";a:3:{s:5:"value";s:3:"all";s:12:"title_enable";b:0;s:5:"title";s:3:"All";}s:12:"title_enable";b:0;s:5:"title";s:0:"";s:21:"default_argument_type";s:5:"fixed";s:24:"default_argument_options";a:1:{s:8:"argument";s:0:"";}s:25:"default_argument_skip_url";b:0;s:15:"summary_options";a:4:{s:9:"base_path";s:0:"";s:5:"count";b:1;s:14:"items_per_page";i:24;s:8:"override";b:0;}s:7:"summary";a:3:{s:10:"sort_order";s:3:"asc";s:17:"number_of_records";i:0;s:6:"format";s:15:"default_summary";}s:18:"specify_validation";b:0;s:8:"validate";a:2:{s:4:"type";s:4:"none";s:4:"fail";s:9:"not found";}s:16:"validate_options";a:0:{}s:8:"glossary";b:0;s:5:"limit";i:0;s:4:"case";s:4:"none";s:9:"path_case";s:4:"none";s:14:"transform_dash";b:0;s:12:"break_phrase";b:0;s:11:"entity_type";s:5:"media";s:12:"entity_field";s:6:"bundle";s:9:"plugin_id";s:6:"string";}}s:6:"header";a:2:{s:17:"display_link_grid";a:7:{s:2:"id";s:17:"display_link_grid";s:5:"table";s:5:"views";s:5:"field";s:12:"display_link";s:10:"display_id";s:6:"widget";s:5:"label";s:4:"Grid";s:9:"plugin_id";s:12:"display_link";s:5:"empty";b:1;}s:18:"display_link_table";a:7:{s:2:"id";s:18:"display_link_table";s:5:"table";s:5:"views";s:5:"field";s:12:"display_link";s:10:"display_id";s:12:"widget_table";s:5:"label";s:5:"Table";s:9:"plugin_id";s:12:"display_link";s:5:"empty";b:1;}}s:9:"css_class";s:67:"media-library-view js-media-library-view media-library-view--widget";}s:14:"cache_metadata";a:3:{s:7:"max-age";i:-1;s:8:"contexts";a:6:{i:0;s:26:"languages:language_content";i:1;s:28:"languages:language_interface";i:2;s:3:"url";i:3;s:14:"url.query_args";i:4;s:22:"url.query_args:sort_by";i:5;s:16:"user.permissions";}s:4:"tags";a:0:{}}}}}', -)) -->execute(); - -// Insert media library key_value entries. -$connection->insert('key_value') -->fields(array( - 'collection', - 'name', - 'value', -)) -->values(array( - 'collection' => 'config.entity.key_store.entity_view_display', - 'name' => 'uuid:67e6d857-8ecb-49f5-95e1-6b1c4306c31f', - 'value' => 'a:1:{i:0;s:49:"core.entity_view_display.media.file.media_library";}', -)) -->values(array( - 'collection' => 'config.entity.key_store.entity_view_display', - 'name' => 'uuid:277ca98b-2ada-4251-ad69-aa73e72d60fe', - 'value' => 'a:1:{i:0;s:50:"core.entity_view_display.media.image.media_library";}', -)) -->values(array( - 'collection' => 'config.entity.key_store.entity_view_mode', - 'name' => 'uuid:20b2f1f7-a864-4d41-a15f-32f66789f73d', - 'value' => 'a:1:{i:0;s:41:"core.entity_view_mode.media.media_library";}', -)) -->values(array( - 'collection' => 'config.entity.key_store.view', - 'name' => 'uuid:3bc9cf0f-cb66-4dbe-8d7e-862cb85e5932', - 'value' => 'a:1:{i:0;s:24:"views.view.media_library";}', -)) -->execute(); diff --git a/core/modules/media_library/tests/modules/media_library_test/media_library_test.module b/core/modules/media_library/tests/modules/media_library_test/media_library_test.module deleted file mode 100644 index 3d9576bc1a..0000000000 --- a/core/modules/media_library/tests/modules/media_library_test/media_library_test.module +++ /dev/null @@ -1,18 +0,0 @@ -getName() === 'field_media_no_access', 'Field access denied by test module'); -} diff --git a/core/modules/media_library/tests/src/Functional/Update/MediaLibraryUpdateViewPageDisplayEditDeleteLinkTest.php b/core/modules/media_library/tests/src/Functional/Update/MediaLibraryUpdateViewPageDisplayEditDeleteLinkTest.php deleted file mode 100644 index 38a30a8b6a..0000000000 --- a/core/modules/media_library/tests/src/Functional/Update/MediaLibraryUpdateViewPageDisplayEditDeleteLinkTest.php +++ /dev/null @@ -1,52 +0,0 @@ -databaseDumpFiles = [ - __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.4.0.bare.standard.php.gz', - __DIR__ . '/../../../../../media/tests/fixtures/update/drupal-8.4.0-media_installed.php', - __DIR__ . '/../../../fixtures/update/drupal-8.7.2-media_library_installed.php', - ]; - } - - /** - * Tests that the media library view config is updated. - * - * @see media_library_update_8703() - */ - public function testMediaLibraryViewsConfig() { - $config = $this->config('views.view.media_library'); - $this->assertNull($config->get('display.page.display_options.defaults.fields')); - $this->assertNull($config->get('display.page.display_options.fields.name')); - $this->assertNull($config->get('display.page.display_options.fields.edit_media')); - $this->assertNull($config->get('display.page.display_options.fields.delete_media')); - - $this->runUpdates(); - - $config = $this->config('views.view.media_library'); - $this->assertFalse($config->get('display.page.display_options.defaults.fields')); - $this->assertSame('field', $config->get('display.page.display_options.fields.name.plugin_id')); - $this->assertSame('name', $config->get('display.page.display_options.fields.name.entity_field')); - $this->assertSame('entity_link_edit', $config->get('display.page.display_options.fields.edit_media.plugin_id')); - $this->assertSame('entity_link_delete', $config->get('display.page.display_options.fields.delete_media.plugin_id')); - // Check if the rendered entity is last in the field. - $fields = $config->get('display.page.display_options.fields'); - end($fields); - $this->assertSame('rendered_entity', key($fields)); - } - -} diff --git a/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php index d4601eef0f..999a1f5528 100644 --- a/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php +++ b/core/modules/media_library/tests/src/FunctionalJavascript/MediaLibraryTest.php @@ -78,7 +78,6 @@ protected function setUp() { 'edit own basic_page content', 'create basic_page content', 'create media', - 'update any media', 'delete any media', 'view media', 'administer node form display', @@ -107,12 +106,6 @@ public function testAdministrationPage() { $assert_session->pageTextContains('Dog'); $assert_session->pageTextContains('Turtle'); - // Verify that the media name does not contain a link. - $assert_session->elementNotExists('css', '.media-library-item__name a'); - // Verify that there are links to edit and delete media items. - $assert_session->elementExists('css', '.media-library-item .media-library-item__edit'); - $assert_session->elementExists('css', '.media-library-item .media-library-item__remove'); - // Test that users can filter by type. $page->selectFieldOption('Media type', 'Type One'); $page->pressButton('Apply filters'); @@ -339,13 +332,7 @@ public function testWidgetAccess() { // Create a working state. $allowed_types = ['type_one', 'type_two', 'type_three', 'type_four']; - // The opener parameters are not relevant to the test, but the opener - // expects them to be there or it will deny access. - $state = MediaLibraryState::create('media_library.opener.field_widget', $allowed_types, 'type_three', 2, [ - 'entity_type_id' => 'node', - 'bundle' => 'basic_page', - 'field_name' => 'field_unlimited_media', - ]); + $state = MediaLibraryState::create('test', $allowed_types, 'type_three', 2); $url_options = ['query' => $state->all()]; // Verify that unprivileged users can't access the widget view. @@ -357,10 +344,8 @@ public function testWidgetAccess() { $assert_session->responseContains('Access denied'); // Allow users with 'view media' permission to access the media library view - // and controller. Since we are using the node entity type in the state - // object, ensure the user also has permission to work with those. + // and controller. $this->grantPermissions($role, [ - 'create basic_page content', 'view media', ]); $this->drupalGet('admin/content/media-widget', $url_options); @@ -436,7 +421,7 @@ public function testWidget() { $assert_session->assertWaitOnAjaxRequest(); $assert_session->pageTextContains('Add or select media'); $this->assertFalse($assert_session->elementExists('css', '.media-library-select-all')->isVisible()); - $assert_session->elementExists('css', '.ui-dialog-titlebar-close')->click(); + $page->find('css', '.ui-dialog-titlebar-close')->click(); // Assert that the media type menu is available when more than 1 type is // configured for the field. @@ -447,16 +432,7 @@ public function testWidget() { $this->assertFalse($menu->hasLink('Type Two')); $this->assertTrue($menu->hasLink('Type Three')); $this->assertFalse($menu->hasLink('Type Four')); - $assert_session->elementExists('css', '.ui-dialog-titlebar-close')->click(); - $assert_session->assertWaitOnAjaxRequest(); - - // Assert that there are no links in the media library view. - $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click(); - $assert_session->assertWaitOnAjaxRequest(); - $assert_session->elementNotExists('css', '.media-library-item__name a'); - $assert_session->elementNotExists('css', '.media-library-view .media-library-item__edit'); - $assert_session->elementNotExists('css', '.media-library-view .media-library-item__remove'); - $assert_session->elementExists('css', '.ui-dialog-titlebar-close')->click(); + $page->find('css', '.ui-dialog-titlebar-close')->click(); $assert_session->assertWaitOnAjaxRequest(); // Assert that the media type menu is available when the target_bundles @@ -470,7 +446,7 @@ public function testWidget() { $this->assertTrue($menu->hasLink('Type Three')); $this->assertTrue($menu->hasLink('Type Four')); $this->assertTrue($menu->hasLink('Type Five')); - $assert_session->elementExists('css', '.ui-dialog-titlebar-close')->click(); + $page->find('css', '.ui-dialog-titlebar-close')->click(); // Assert that the media type menu is not available when only 1 type is // configured for the field. @@ -485,7 +461,7 @@ public function testWidget() { $assert_session->hiddenFieldValueEquals('media-library-modal-selection', '4'); $assert_session->elementTextContains('css', '.media-library-selected-count', '1 of 1 item selected'); $assert_session->elementNotExists('css', '.media-library-menu'); - $assert_session->elementExists('css', '.ui-dialog-titlebar-close')->click(); + $page->find('css', '.ui-dialog-titlebar-close')->click(); // Assert the menu links can be sorted through the widget configuration. $assert_session->elementExists('css', '.media-library-open-button[name^="field_twin_media"]')->click(); @@ -515,7 +491,7 @@ public function testWidget() { return $link->getText(); }, $page->findAll('css', '.media-library-menu a')); $this->assertSame($link_titles, ['Show Type One media (selected)', 'Show Type Three media', 'Show Type Four media', 'Show Type Two media']); - $assert_session->elementExists('css', '.ui-dialog-titlebar-close')->click(); + $page->find('css', '.ui-dialog-titlebar-close')->click(); // Assert the announcements for media type navigation in the media library. $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click(); @@ -530,7 +506,7 @@ public function testWidget() { $assert_session->elementExists('named', ['link', 'Type Three'])->keyPress(32); $assert_session->assertWaitOnAjaxRequest(); $this->assertNotEmpty($assert_session->waitForText('Showing Type Three media.')); - $assert_session->elementExists('css', '.ui-dialog-titlebar-close')->click(); + $page->find('css', '.ui-dialog-titlebar-close')->click(); // Assert media is only visible on the tab for the related media type. $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click(); @@ -545,7 +521,7 @@ public function testWidget() { $assert_session->pageTextNotContains('Dog'); $assert_session->pageTextNotContains('Bear'); $assert_session->pageTextNotContains('Turtle'); - $assert_session->elementExists('css', '.ui-dialog-titlebar-close')->click(); + $page->find('css', '.ui-dialog-titlebar-close')->click(); // Assert the exposed name filter of the view. $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click(); @@ -561,7 +537,7 @@ public function testWidget() { $assert_session->assertWaitOnAjaxRequest(); $assert_session->pageTextContains('Dog'); $assert_session->pageTextContains('Bear'); - $assert_session->elementExists('css', '.ui-dialog-titlebar-close')->click(); + $page->find('css', '.ui-dialog-titlebar-close')->click(); // Assert the media library contains header links to switch between the grid // and table display. @@ -634,11 +610,6 @@ public function testWidget() { $assert_session->assertWaitOnAjaxRequest(); // Assert the focus is set back on the open button of the media field. $this->assertJsCondition('jQuery("#field_twin_media-media-library-wrapper .js-media-library-open-button").is(":focus")'); - // Assert the weight field can be focused via a mouse click. - $assert_session->elementExists('named', ['button', 'Show media item weights'])->click(); - $assert_session->elementExists('css', '#field_twin_media-media-library-wrapper .media-library-item__weight')->click(); - $assert_session->elementExists('css', '#field_twin_media-media-library-wrapper .js-media-library-widget-toggle-weight')->click(); - // Remove the selected item. $assert_session->elementAttributeContains('css', '.media-library-item__remove', 'aria-label', 'Remove Dog'); $assert_session->elementExists('css', '.media-library-item__remove')->click(); $this->assertNotEmpty($assert_session->waitForText('Removed Dog.')); @@ -812,7 +783,7 @@ public function testWidget() { $this->assertFalse($checkboxes[2]->isChecked()); $this->assertFalse($checkboxes[3]->isChecked()); // Close the dialog, reopen it and assert not is selected again. - $assert_session->elementExists('css', '.ui-dialog-titlebar-close')->click(); + $page->find('css', '.ui-dialog-titlebar-close')->click(); $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click(); $assert_session->assertWaitOnAjaxRequest(); $checkboxes = $page->findAll('css', '.media-library-view .js-click-to-select-checkbox input'); @@ -821,7 +792,7 @@ public function testWidget() { $this->assertFalse($checkboxes[1]->isChecked()); $this->assertFalse($checkboxes[2]->isChecked()); $this->assertFalse($checkboxes[3]->isChecked()); - $assert_session->elementExists('css', '.ui-dialog-titlebar-close')->click(); + $page->find('css', '.ui-dialog-titlebar-close')->click(); // Finally, save the form. $assert_session->elementExists('css', '.js-media-library-widget-toggle-weight')->click(); @@ -1238,7 +1209,7 @@ public function testWidgetUpload() { $this->assertJsCondition('jQuery("#media-library-add-form-wrapper :tabbable").is(":focus")'); $assert_session->elementNotExists('css', '.media-library-add-form__fields'); $assert_session->elementExists('css', '.media-library-menu'); - $assert_session->elementExists('css', '.ui-dialog-titlebar-close')->click(); + $page->find('css', '.ui-dialog-titlebar-close')->click(); // Assert uploading multiple files. $assert_session->elementExists('css', '.media-library-open-button[name^="field_unlimited_media"]')->click(); diff --git a/core/modules/media_library/tests/src/Kernel/MediaLibraryAccessTest.php b/core/modules/media_library/tests/src/Kernel/MediaLibraryAccessTest.php index 75d064a5e9..2b83916e56 100644 --- a/core/modules/media_library/tests/src/Kernel/MediaLibraryAccessTest.php +++ b/core/modules/media_library/tests/src/Kernel/MediaLibraryAccessTest.php @@ -2,12 +2,6 @@ namespace Drupal\Tests\media_library\Kernel; -use Drupal\Core\Access\AccessResult; -use Drupal\Core\Access\AccessResultReasonInterface; -use Drupal\entity_test\Entity\EntityTest; -use Drupal\entity_test\Entity\EntityTestBundle; -use Drupal\field\Entity\FieldConfig; -use Drupal\field\Entity\FieldStorageConfig; use Drupal\image\Entity\ImageStyle; use Drupal\KernelTests\KernelTestBase; use Drupal\media_library\MediaLibraryState; @@ -27,10 +21,8 @@ class MediaLibraryAccessTest extends KernelTestBase { * {@inheritdoc} */ protected static $modules = [ - 'entity_test', 'media', 'media_library', - 'media_library_test', 'file', 'field', 'image', @@ -49,7 +41,6 @@ protected function setUp() { $this->installEntitySchema('file'); $this->installSchema('file', 'file_usage'); $this->installSchema('system', ['sequences', 'key_value_expire']); - $this->installEntitySchema('entity_test'); $this->installEntitySchema('media'); $this->installConfig([ 'field', @@ -60,23 +51,6 @@ protected function setUp() { 'media_library', ]); - EntityTestBundle::create(['id' => 'test'])->save(); - - $field_storage = FieldStorageConfig::create([ - 'type' => 'entity_reference', - 'field_name' => 'field_test_media', - 'entity_type' => 'entity_test', - 'settings' => [ - 'target_type' => 'media', - ], - ]); - $field_storage->save(); - - FieldConfig::create([ - 'field_storage' => $field_storage, - 'bundle' => 'test', - ])->save(); - // Create an account with special UID 1. $this->createUser([]); } @@ -98,180 +72,29 @@ public function testMediaLibraryImageStyleAccess() { } /** - * Tests that the field widget opener respects entity creation permissions. + * Tests the Media Library access. */ - public function testFieldWidgetEntityCreateAccess() { + public function testMediaLibraryAccess() { /** @var \Drupal\media_library\MediaLibraryUiBuilder $ui_builder */ $ui_builder = $this->container->get('media_library.ui_builder'); // Create a media library state to test access. - $state = MediaLibraryState::create('media_library.opener.field_widget', ['file', 'image'], 'file', 2, [ - 'entity_type_id' => 'entity_test', - 'bundle' => 'test', - 'field_name' => 'field_test_media', - ]); - - $access_result = $ui_builder->checkAccess($this->createUser(), $state); - $this->assertAccess($access_result, FALSE, "The following permissions are required: 'administer entity_test content' OR 'administer entity_test_with_bundle content' OR 'create test entity_test_with_bundle entities'.", [], ['url.query_args', 'user.permissions']); - - // Create a user with the appropriate permissions and assert that access is - // granted. - $account = $this->createUser([ - 'create test entity_test_with_bundle entities', - 'view media', - ]); - $access_result = $ui_builder->checkAccess($account, $state); - $this->assertAccess($access_result, TRUE, NULL, Views::getView('media_library')->storage->getCacheTags(), ['url.query_args', 'user.permissions']); - } - - /** - * Tests that the field widget opener respects entity-specific access. - */ - public function testFieldWidgetEntityEditAccess() { - /** @var \Drupal\media_library\MediaLibraryUiBuilder $ui_builder */ - $ui_builder = $this->container->get('media_library.ui_builder'); - - $forbidden_entity = EntityTest::create([ - 'type' => 'test', - // This label will automatically cause an access denial. - // @see \Drupal\entity_test\EntityTestAccessControlHandler::checkAccess() - 'name' => 'forbid_access', - ]); - $forbidden_entity->save(); - - // Create a media library state to test access. - $state = MediaLibraryState::create('media_library.opener.field_widget', ['file', 'image'], 'file', 2, [ - 'entity_type_id' => $forbidden_entity->getEntityTypeId(), - 'bundle' => $forbidden_entity->bundle(), - 'field_name' => 'field_test_media', - 'entity_id' => $forbidden_entity->id(), - ]); - - $access_result = $ui_builder->checkAccess($this->createUser(), $state); - $this->assertAccess($access_result, FALSE, NULL, [], ['url.query_args']); - - $neutral_entity = EntityTest::create([ - 'type' => 'test', - // This label will result in neutral access. - // @see \Drupal\entity_test\EntityTestAccessControlHandler::checkAccess() - 'name' => $this->randomString(), - ]); - $neutral_entity->save(); - - $parameters = $state->getOpenerParameters(); - $parameters['entity_id'] = $neutral_entity->id(); - $state = MediaLibraryState::create( - $state->getOpenerId(), - $state->getAllowedTypeIds(), - $state->getSelectedTypeId(), - $state->getAvailableSlots(), - $parameters - ); - - $access_result = $ui_builder->checkAccess($this->createUser(), $state); - $this->assertTrue($access_result->isNeutral()); - $this->assertAccess($access_result, FALSE, NULL, [], ['url.query_args', 'user.permissions']); - - // Give the user permission to edit the entity and assert that access is - // granted. - $account = $this->createUser([ - 'administer entity_test content', - 'view media', - ]); - $access_result = $ui_builder->checkAccess($account, $state); - $this->assertAccess($access_result, TRUE, NULL, Views::getView('media_library')->storage->getCacheTags(), ['url.query_args', 'user.permissions']); - } - - /** - * Tests that the field widget opener respects entity field-level access. - */ - public function testFieldWidgetEntityFieldAccess() { - $field_storage = FieldStorageConfig::create([ - 'type' => 'entity_reference', - 'entity_type' => 'entity_test', - // The media_library_test module will deny access to this field. - // @see media_library_test_entity_field_access() - 'field_name' => 'field_media_no_access', - 'settings' => [ - 'target_type' => 'media', - ], - ]); - $field_storage->save(); - - FieldConfig::create([ - 'field_storage' => $field_storage, - 'bundle' => 'test', - ])->save(); - - /** @var \Drupal\media_library\MediaLibraryUiBuilder $ui_builder */ - $ui_builder = $this->container->get('media_library.ui_builder'); - - // Create an account with administrative access to the test entity type, - // so that we can be certain that field access is checked. - $account = $this->createUser(['administer entity_test content']); - - // Test that access is denied even without an entity to work with. - $state = MediaLibraryState::create('media_library.opener.field_widget', ['file', 'image'], 'file', 2, [ - 'entity_type_id' => 'entity_test', - 'bundle' => 'test', - 'field_name' => $field_storage->getName(), - ]); - $access_result = $ui_builder->checkAccess($account, $state); - $this->assertAccess($access_result, FALSE, 'Field access denied by test module', [], ['url.query_args', 'user.permissions']); - - // Assert that field access is also checked with a real entity. - $entity = EntityTest::create([ - 'type' => 'test', - 'name' => $this->randomString(), - ]); - $entity->save(); - - $parameters = $state->getOpenerParameters(); - $parameters['entity_id'] = $entity->id(); - - $state = MediaLibraryState::create( - $state->getOpenerId(), - $state->getAllowedTypeIds(), - $state->getSelectedTypeId(), - $state->getAvailableSlots(), - $parameters - ); - $access_result = $ui_builder->checkAccess($account, $state); - $this->assertAccess($access_result, FALSE, 'Field access denied by test module', [], ['url.query_args', 'user.permissions']); - } - - /** - * Tests that media library access respects the media_library view. - */ - public function testViewAccess() { - /** @var \Drupal\media_library\MediaLibraryUiBuilder $ui_builder */ - $ui_builder = $this->container->get('media_library.ui_builder'); - - // Create a media library state to test access. - $state = MediaLibraryState::create('media_library.opener.field_widget', ['file', 'image'], 'file', 2, [ - 'entity_type_id' => 'entity_test', - 'bundle' => 'test', - 'field_name' => 'field_test_media', - ]); + $state = MediaLibraryState::create('test', ['file', 'image'], 'file', 2); // Create a clone of the view so we can reset the original later. $view_original = clone Views::getView('media_library'); - // Create our test users. Both have permission to create entity_test content - // so that we can specifically test Views-related access checking. - // @see ::testEntityCreateAccess() - $forbidden_account = $this->createUser([ - 'create test entity_test_with_bundle entities', - ]); - $allowed_account = $this->createUser([ - 'create test entity_test_with_bundle entities', - 'view media', - ]); + // Create our test users. + $forbidden_account = $this->createUser([]); + $allowed_account = $this->createUser(['view media']); // Assert the 'view media' permission is needed to access the library and // validate the cache dependencies. $access_result = $ui_builder->checkAccess($forbidden_account, $state); - $this->assertAccess($access_result, FALSE, "The 'view media' permission is required.", $view_original->storage->getCacheTags(), ['url.query_args', 'user.permissions']); + $this->assertFalse($access_result->isAllowed()); + $this->assertSame("The 'view media' permission is required.", $access_result->getReason()); + $this->assertSame($view_original->storage->getCacheTags(), $access_result->getCacheTags()); + $this->assertSame(['user.permissions'], $access_result->getCacheContexts()); // Assert that the media library access is denied when the view widget // display is deleted. @@ -281,42 +104,27 @@ public function testViewAccess() { $view_storage->set('display', $displays); $view_storage->save(); $access_result = $ui_builder->checkAccess($allowed_account, $state); - $this->assertAccess($access_result, FALSE, 'The media library widget display does not exist.', $view_original->storage->getCacheTags()); + $this->assertFalse($access_result->isAllowed()); + $this->assertSame('The media library widget display does not exist.', $access_result->getReason()); + $this->assertSame($view_original->storage->getCacheTags(), $access_result->getCacheTags()); + $this->assertSame([], $access_result->getCacheContexts()); // Restore the original view and assert that the media library controller // works again. $view_original->storage->save(); $access_result = $ui_builder->checkAccess($allowed_account, $state); - $this->assertAccess($access_result, TRUE, NULL, $view_original->storage->getCacheTags(), ['url.query_args', 'user.permissions']); + $this->assertTrue($access_result->isAllowed()); + $this->assertSame($view_original->storage->getCacheTags(), $access_result->getCacheTags()); + $this->assertSame(['user.permissions'], $access_result->getCacheContexts()); // Assert that the media library access is denied when the entire media // library view is deleted. Views::getView('media_library')->storage->delete(); $access_result = $ui_builder->checkAccess($allowed_account, $state); - $this->assertAccess($access_result, FALSE, 'The media library view does not exist.'); - } - - /** - * Asserts various aspects of an access result. - * - * @param \Drupal\Core\Access\AccessResult $access_result - * The access result. - * @param bool $is_allowed - * The expected access status. - * @param string $expected_reason - * (optional) The expected reason attached to the access result. - * @param string[] $expected_cache_tags - * (optional) The expected cache tags attached to the access result. - * @param string[] $expected_cache_contexts - * (optional) The expected cache contexts attached to the access result. - */ - private function assertAccess(AccessResult $access_result, $is_allowed, $expected_reason = NULL, array $expected_cache_tags = [], array $expected_cache_contexts = []) { - $this->assertSame($is_allowed, $access_result->isAllowed()); - if ($access_result instanceof AccessResultReasonInterface && isset($expected_reason)) { - $this->assertSame($expected_reason, $access_result->getReason()); - } - $this->assertSame($expected_cache_tags, $access_result->getCacheTags()); - $this->assertSame($expected_cache_contexts, $access_result->getCacheContexts()); + $this->assertFalse($access_result->isAllowed()); + $this->assertSame('The media library view does not exist.', $access_result->getReason()); + $this->assertSame([], $access_result->getCacheTags()); + $this->assertSame([], $access_result->getCacheContexts()); } } diff --git a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php index 5ca3712a4c..120b680521 100644 --- a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php +++ b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php @@ -676,23 +676,14 @@ public function saveMessage(array $source_id_values, $message, $level = Migratio * {@inheritdoc} */ public function getMessageIterator(array $source_id_values = [], $level = NULL) { - $query = $this->getDatabase()->select($this->messageTableName(), 'msg'); - $condition = sprintf('msg.%s = map.%s', $this::SOURCE_IDS_HASH, $this::SOURCE_IDS_HASH); - $query->addJoin('LEFT', $this->mapTableName(), 'map', $condition); - // Explicitly define the fields we want. The order will be preserved: source - // IDs, destination IDs (if possible), and then the rest. - foreach ($this->sourceIdFields() as $id => $column_name) { - $query->addField('map', $column_name, "src_$id"); - } - foreach ($this->destinationIdFields() as $id => $column_name) { - $query->addField('map', $column_name, "dest_$id"); - } - $query->fields('msg', ['msgid', $this::SOURCE_IDS_HASH, 'level', 'message']); + $query = $this->getDatabase()->select($this->messageTableName(), 'msg') + ->fields('msg'); if ($source_id_values) { - $query->condition('msg.' . $this::SOURCE_IDS_HASH, $this->getSourceIdsHash($source_id_values)); + $query->condition($this::SOURCE_IDS_HASH, $this->getSourceIdsHash($source_id_values)); } + if ($level) { - $query->condition('msg.level', $level); + $query->condition('level', $level); } return $query->execute(); } diff --git a/core/modules/migrate/tests/src/Kernel/MigrateMessageTest.php b/core/modules/migrate/tests/src/Kernel/MigrateMessageTest.php index 8399232852..6a9990f193 100644 --- a/core/modules/migrate/tests/src/Kernel/MigrateMessageTest.php +++ b/core/modules/migrate/tests/src/Kernel/MigrateMessageTest.php @@ -8,7 +8,6 @@ use Drupal\migrate\Event\MigrateIdMapMessageEvent; use Drupal\migrate\MigrateExecutable; use Drupal\migrate\MigrateMessageInterface; -use Drupal\migrate\Plugin\migrate\id_map\Sql; /** * Tests whether idmap messages are sent to message interface when requested. @@ -97,31 +96,6 @@ public function testMessagesTeed() { $this->assertIdentical(reset($this->messages), "source_message: 'a message' is not an array"); } - /** - * Tests the return value of getMessageIterator(). - * - * This method returns an iterator of StdClass objects. Check that these - * objects have the expected keys. - */ - public function testGetMessageIterator() { - $expected_message = (object) [ - 'src_name' => 'source_message', - 'dest_config_name' => NULL, - 'msgid' => '1', - Sql::SOURCE_IDS_HASH => '170cde81762e22552d1b1578cf3804c89afefe9efbc7cc835185d7141060b032', - 'level' => '1', - 'message' => "'a message' is not an array", - ]; - $executable = new MigrateExecutable($this->migration, $this); - $executable->import(); - $count = 0; - foreach ($this->migration->getIdMap()->getMessageIterator() as $message) { - ++$count; - $this->assertEqual($message, $expected_message); - } - $this->assertEqual($count, 1); - } - /** * Reacts to map message event. * diff --git a/core/tests/Drupal/Tests/Component/VendorCleanup/ConfigTest.php b/core/tests/Drupal/Tests/Component/VendorCleanup/ConfigTest.php new file mode 100644 index 0000000000..ee00ed72cc --- /dev/null +++ b/core/tests/Drupal/Tests/Component/VendorCleanup/ConfigTest.php @@ -0,0 +1,86 @@ +getMockBuilder(Config::class) + ->setMethods(['getAllCleanupPaths']) + ->disableOriginalConstructor() + ->getMock(); + + $config->expects($this->once()) + ->method('getAllCleanupPaths') + ->willReturn(['PackAge' => ['path']]); + + $this->assertSame(['path'], $config->getPathsForPackage('pACKage')); + } + + /** + * @covers ::getAllCleanupPaths + */ + public function testNoRootMergeConfig() { + // Root package has no extra field. + $root = $this->getMockBuilder(RootPackageInterface::class) + ->setMethods(['getExtra']) + ->getMockForAbstractClass(); + $root->expects($this->once()) + ->method('getExtra') + ->willReturn([]); + + $config = new Config($root); + + $ref_default = new \ReflectionProperty($config, 'defaultConfig'); + $ref_default->setAccessible(TRUE); + + $ref_plugin_config = new \ReflectionMethod($config, 'getAllCleanupPaths'); + $ref_plugin_config->setAccessible(TRUE); + + $this->assertEquals( + $ref_default->getValue($config), $ref_plugin_config->invoke($config) + ); + } + + /** + * @covers ::getAllCleanupPaths + */ + public function testRootMergeConfig() { + // Root package has configuration in extra. + $root = $this->getMockBuilder(RootPackageInterface::class) + ->setMethods(['getExtra']) + ->getMockForAbstractClass(); + $root->expects($this->once()) + ->method('getExtra') + ->willReturn([ + 'drupal-core-vendor-cleanup' => [ + 'isa/string' => 'test_dir', + 'an/array' => ['test_dir', 'doc_dir'], + ], + ]); + + $config = new Config($root); + + $ref_plugin_config = new \ReflectionMethod($config, 'getAllCleanupPaths'); + $ref_plugin_config->setAccessible(TRUE); + + $plugin_config = $ref_plugin_config->invoke($config); + + $this->assertArraySubset([ + 'isa/string' => ['test_dir'], + 'an/array' => ['test_dir', 'doc_dir'], + ], $plugin_config); + } + +} diff --git a/core/tests/Drupal/Tests/Component/VendorCleanup/VendorCleanupPluginTest.php b/core/tests/Drupal/Tests/Component/VendorCleanup/VendorCleanupPluginTest.php new file mode 100644 index 0000000000..853818029c --- /dev/null +++ b/core/tests/Drupal/Tests/Component/VendorCleanup/VendorCleanupPluginTest.php @@ -0,0 +1,123 @@ + [ + 'package' => [ + 'tests' => [ + 'SomeTest.php' => 'getMockBuilder(Config::class) + ->setMethods(['getPathsForPackage']) + ->disableOriginalConstructor() + ->getMock(); + $config->expects($this->once()) + ->method('getPathsForPackage') + ->willReturn(['tests']); + + $plugin = new VendorCleanupPlugin(); + $ref_config = new \ReflectionProperty($plugin, 'config'); + $ref_config->setAccessible(TRUE); + $ref_config->setValue($plugin, $config); + + $io = $this->prophesize(IOInterface::class); + $ref_io = new \ReflectionProperty($plugin, 'io'); + $ref_io->setAccessible(TRUE); + $ref_io->setValue($plugin, $io->reveal()); + + $this->assertFileExists(vfsStream::url('vendor/drupal/package/tests/SomeTest.php')); + + $plugin->cleanPackage(vfsStream::url('vendor'), 'drupal/package'); + + $this->assertFileNotExists(vfsStream::url('vendor/drupal/package/tests')); + } + + /** + * @covers ::cleanPathsForPackage + */ + public function testCleanPathsForPackage() { + $plugin = new VendorCleanupPlugin(); + + $io = $this->prophesize(IOInterface::class); + $ref_io = new \ReflectionProperty($plugin, 'io'); + $ref_io->setAccessible(TRUE); + $ref_io->setValue($plugin, $io->reveal()); + + $this->assertFileExists(vfsStream::url('vendor/drupal/package/tests/SomeTest.php')); + + $ref_clean = new \ReflectionMethod($plugin, 'cleanPathsForPackage'); + $ref_clean->setAccessible(TRUE); + $ref_clean->invokeArgs($plugin, [vfsStream::url('vendor'), 'drupal/package', ['tests']]); + + $this->assertFileNotExists(vfsStream::url('vendor/drupal/package/tests')); + } + + /** + * @covers ::cleanAllPackages + */ + public function testCleanAllPackages() { + $config = $this->getMockBuilder(Config::class) + ->setMethods(['getAllCleanupPaths']) + ->disableOriginalConstructor() + ->getMock(); + $config->expects($this->once()) + ->method('getAllCleanupPaths') + ->willReturn(['drupal/package' => ['tests']]); + + $package = $this->getMockBuilder(PackageInterface::class) + ->setMethods(['getName']) + ->getMockForAbstractClass(); + $package->expects($this->any()) + ->method('getName') + ->willReturn('drupal/package'); + + $plugin = $this->getMockBuilder(VendorCleanupPlugin::class) + ->setMethods(['getInstalledPackages']) + ->getMock(); + $plugin->expects($this->once()) + ->method('getInstalledPackages') + ->willReturn([$package]); + + $io = $this->prophesize(IOInterface::class); + $ref_io = new \ReflectionProperty($plugin, 'io'); + $ref_io->setAccessible(TRUE); + $ref_io->setValue($plugin, $io->reveal()); + + $ref_config = new \ReflectionProperty($plugin, 'config'); + $ref_config->setAccessible(TRUE); + $ref_config->setValue($plugin, $config); + + $this->assertFileExists(vfsStream::url('vendor/drupal/package/tests/SomeTest.php')); + + $plugin->cleanAllPackages(vfsStream::url('vendor')); + + $this->assertFileNotExists(vfsStream::url('vendor/drupal/package/tests')); + } + +} diff --git a/core/tests/Drupal/Tests/ComposerIntegrationTest.php b/core/tests/Drupal/Tests/ComposerIntegrationTest.php index 4bed30bcc9..1050641a94 100644 --- a/core/tests/Drupal/Tests/ComposerIntegrationTest.php +++ b/core/tests/Drupal/Tests/ComposerIntegrationTest.php @@ -63,6 +63,7 @@ protected function getPaths() { $this->root . '/core/lib/Drupal/Component/Transliteration', $this->root . '/core/lib/Drupal/Component/Utility', $this->root . '/core/lib/Drupal/Component/Uuid', + $this->root . '/core/lib/Drupal/Component/VendorCleanup', $this->root . '/core/lib/Drupal/Component/Version', ]; }