updated contrib modules
This commit is contained in:
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "drupal/config_devel",
|
||||
"description": "Helps developers work with configuration.",
|
||||
"type": "drupal-module",
|
||||
"license": "GPL-2.0+",
|
||||
"minimum-stability": "dev",
|
||||
"require": { }
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
auto_import: []
|
||||
auto_export: []
|
||||
@@ -0,0 +1,23 @@
|
||||
config_devel.settings:
|
||||
type: config_object
|
||||
label: config devel settings
|
||||
mapping:
|
||||
auto_import:
|
||||
type: sequence
|
||||
sequence:
|
||||
- type: mapping
|
||||
mapping:
|
||||
filename:
|
||||
type: string
|
||||
hash:
|
||||
type: string
|
||||
auto_export:
|
||||
type: sequence
|
||||
sequence:
|
||||
- type: string
|
||||
config_devel.test:
|
||||
type: config_object
|
||||
label: config devel test
|
||||
mapping:
|
||||
label:
|
||||
type: label
|
||||
@@ -0,0 +1,13 @@
|
||||
name: Configuration development module
|
||||
type: module
|
||||
description: 'Helps developers work with configuration.'
|
||||
package: Configuration
|
||||
# version: VERSION
|
||||
# core: 8.x
|
||||
configure: config_devel.settings
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-09-11
|
||||
version: '8.x-1.1'
|
||||
core: '8.x'
|
||||
project: 'config_devel'
|
||||
datestamp: 1505141047
|
||||
@@ -0,0 +1,5 @@
|
||||
config_devel.settings:
|
||||
title: Config devel
|
||||
description: 'Specify which configuration objects to automatically import/export.'
|
||||
route_name: config_devel.settings
|
||||
parent: system.admin_config_development
|
||||
@@ -0,0 +1,7 @@
|
||||
config_devel.settings:
|
||||
path: '/admin/config/development/config_devel'
|
||||
defaults:
|
||||
_form: 'Drupal\config_devel\Form\ConfigDevelSettingsForm'
|
||||
_title: 'Config Devel'
|
||||
requirements:
|
||||
_permission: 'import configuration'
|
||||
@@ -0,0 +1,11 @@
|
||||
services:
|
||||
config_devel.writeback_subscriber:
|
||||
class: Drupal\config_devel\EventSubscriber\ConfigDevelAutoExportSubscriber
|
||||
arguments: ['@config.factory', '@config.manager', '@event_dispatcher']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
config_devel.auto_import_subscriber:
|
||||
class: Drupal\config_devel\EventSubscriber\ConfigDevelAutoImportSubscriber
|
||||
arguments: ['@config.factory', '@config.manager']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Configuration development module drush integration.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
|
||||
/**
|
||||
* Implements hook_drush_command().
|
||||
*/
|
||||
function config_devel_drush_command() {
|
||||
$items = array();
|
||||
|
||||
$description = '';
|
||||
$description .= "List which configuration settings you want to export in the\n";
|
||||
$description .= "module's info file by listing them under 'config_devel', as shown below:\n";
|
||||
$description .= "\n";
|
||||
$description .= "config_devel:\n";
|
||||
$description .= " install:\n";
|
||||
$description .= " - entity.view_display.node.article.default\n";
|
||||
$description .= " - entity.view_display.node.article.teaser\n";
|
||||
$description .= " - field.instance.node.article.body\n";
|
||||
$description .= " optional:\n";
|
||||
$description .= " - field.instance.node.article.tags\n";
|
||||
|
||||
$items['config-devel-export'] = array(
|
||||
'description' => "Write back configuration to module's config directory.\n" . $description,
|
||||
'arguments' => array(
|
||||
'module' => 'Module machine name.',
|
||||
),
|
||||
'options' => array(
|
||||
),
|
||||
'required-arguments' => TRUE,
|
||||
'examples' => array(
|
||||
'drush config-devel-export MODULE_NAME' => 'Write back configuration to the specified module, based on .info file.',
|
||||
),
|
||||
'aliases' => array('cde', 'cd-em'),
|
||||
);
|
||||
|
||||
$items['config-devel-import'] = array(
|
||||
'description' => "Import configuration from module's config directory to active storage.\n" . $description,
|
||||
'arguments' => array(
|
||||
'module' => 'Module machine name.',
|
||||
),
|
||||
'options' => array(
|
||||
),
|
||||
'required-arguments' => TRUE,
|
||||
'examples' => array(
|
||||
'drush config-devel-import MODULE_NAME' => 'Import configuration from the specified module into the active storage, based on .info file.',
|
||||
),
|
||||
'aliases' => array('cdi', 'cd-im'),
|
||||
);
|
||||
|
||||
$items['config-devel-import-one'] = array(
|
||||
'description' => "Import a single configuration item from module's config directory to active storage.\n" . $description,
|
||||
'arguments' => array(
|
||||
'path' => 'Config file name.',
|
||||
),
|
||||
'options' => array(
|
||||
),
|
||||
'required-arguments' => TRUE,
|
||||
'examples' => array(
|
||||
'drush config-devel-import-one system.site.yml' => 'Import the contents of system.site.yml into the config object system.site.',
|
||||
'drush config-devel-import-one system.site' => 'Import the standard input into the config object system.site. Helpful for scripting copying to remote',
|
||||
),
|
||||
'aliases' => array('cdi1', 'cd-i1'),
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drush command callback.
|
||||
*/
|
||||
function drush_config_devel_export($extension) {
|
||||
// Determine the type of extension we're dealing with.
|
||||
$type = drush_config_devel_get_type($extension);
|
||||
|
||||
if ($type) {
|
||||
// Get the config
|
||||
$config = drush_config_devel_get_config($type, $extension);
|
||||
|
||||
// Process config
|
||||
if (isset($config['install'])) {
|
||||
drush_config_devel_process_config($config['install'], $type, $extension, InstallStorage::CONFIG_INSTALL_DIRECTORY);
|
||||
}
|
||||
|
||||
// If we have any optional configuration, process that as well.
|
||||
if (isset($config['optional'])) {
|
||||
drush_config_devel_process_config($config['optional'], $type, $extension, InstallStorage::CONFIG_OPTIONAL_DIRECTORY);
|
||||
}
|
||||
}
|
||||
else {
|
||||
drush_set_error("Couldn't export configuration. The '$extension' extension is not enabled.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drush command callback.
|
||||
*/
|
||||
function drush_config_devel_import($extension) {
|
||||
// Determine the type of extension we're dealing with.
|
||||
$type = drush_config_devel_get_type($extension);
|
||||
|
||||
if ($type) {
|
||||
// Get the config
|
||||
$config = drush_config_devel_get_config($type, $extension);
|
||||
|
||||
// Import config
|
||||
if (isset($config['install'])) {
|
||||
drush_config_devel_import_config($config['install'], $type, $extension, InstallStorage::CONFIG_INSTALL_DIRECTORY);
|
||||
}
|
||||
|
||||
// Import optional config
|
||||
if (isset($config['optional'])) {
|
||||
drush_config_devel_import_config($config['optional'], $type, $extension, InstallStorage::CONFIG_OPTIONAL_DIRECTORY);
|
||||
}
|
||||
}
|
||||
else {
|
||||
drush_set_error("Couldn't import configuration. The '$extension' extension is not enabled.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drush command callback.
|
||||
*/
|
||||
function drush_config_devel_import_one($path) {
|
||||
$contents = '';
|
||||
if (!file_exists($path)) {
|
||||
if (substr($path, -4) != '.yml') {
|
||||
$contents = file_get_contents('php://stdin');
|
||||
}
|
||||
elseif (!empty($_SERVER['PWD'])) {
|
||||
$path = $_SERVER['PWD'] . '/' . trim($path, '/');
|
||||
}
|
||||
}
|
||||
if ($contents || file_exists($path)) {
|
||||
\Drupal::service('config_devel.auto_import_subscriber')->importOne($path, '', $contents);
|
||||
}
|
||||
else {
|
||||
drush_log("file '$path' not found", 'error');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports a list of configuration entities.
|
||||
*
|
||||
* @param array $config_list
|
||||
* An array of configuration entities.
|
||||
* @param string $type
|
||||
* The type of extension we're exporting, one of module or theme.
|
||||
* @param string $extension
|
||||
* The module, theme or install profile we're exporting.
|
||||
* @param string $directory
|
||||
* The directory we're exporting to.
|
||||
*/
|
||||
function drush_config_devel_process_config($config_list, $type, $extension, $directory) {
|
||||
$config_path = drupal_get_path($type, $extension) . "/$directory";
|
||||
foreach ($config_list as $name) {
|
||||
$config = \Drupal::config($name);
|
||||
$file_names = array($config_path . '/' . $name . '.yml');
|
||||
|
||||
\Drupal::service('config_devel.writeback_subscriber')->writeBackConfig($config, $file_names);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a list of configuration entities
|
||||
*
|
||||
* @param array $config_list
|
||||
* An array of configuration entities.
|
||||
* @param string $type
|
||||
* The type of extension we're exporting, one of module or theme.
|
||||
* @param string $extension
|
||||
* The module, theme or install profile we're exporting.
|
||||
* @param string $directory
|
||||
* The directory we're exporting to.
|
||||
*/
|
||||
function drush_config_devel_import_config($config_list, $type, $extension, $directory) {
|
||||
$config_path = drupal_get_path($type, $extension) . "/$directory";
|
||||
foreach ($config_list as $name) {
|
||||
$file_name = $config_path . '/' . $name . '.yml';
|
||||
drush_config_devel_import_one($file_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the config.
|
||||
*
|
||||
* @param string $type
|
||||
* module, theme or profile
|
||||
* @param string $extension
|
||||
* extension name
|
||||
* @return array
|
||||
* An array containing install and optional config
|
||||
*/
|
||||
function drush_config_devel_get_config($type, $extension) {
|
||||
$filename = drupal_get_path($type, $extension) . '/' . $extension .'.info.yml';
|
||||
$info = \Drupal::service('info_parser')->parse($filename);
|
||||
|
||||
$config = array();
|
||||
if (isset($info['config_devel'])) {
|
||||
// Keep backwards compatibility for the old format. This has config names
|
||||
// listed directly beneath 'config_devel', rather than an intermediate level
|
||||
// for 'install' and 'optional'.
|
||||
// Detect the old format based on whether there's neither of these two keys.
|
||||
if (!isset($info['config_devel']['install']) && !isset($info['config_devel']['optional'])) {
|
||||
$info['config_devel']['install'] = $info['config_devel'];
|
||||
}
|
||||
|
||||
$config['install'] = $info['config_devel']['install'];
|
||||
|
||||
// If we have any optional configuration, fetch that as well.
|
||||
if (isset($info['config_devel']['optional'])) {
|
||||
$config['optional'] = $info['config_devel']['optional'];
|
||||
}
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type for the given extension.
|
||||
*
|
||||
* @param string $extension
|
||||
* extension name
|
||||
* @return string
|
||||
* module, theme, profile, or FALSE if no valid extension provided.
|
||||
*/
|
||||
function drush_config_devel_get_type($extension) {
|
||||
$type = NULL;
|
||||
if (\Drupal::moduleHandler()->moduleExists($extension)) {
|
||||
$type = 'module';
|
||||
}
|
||||
elseif (\Drupal::service('theme_handler')->themeExists($extension)) {
|
||||
$type = 'theme';
|
||||
}
|
||||
elseif (drupal_get_profile() === $extension) {
|
||||
$type = 'profile';
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\config_devel\Event;
|
||||
|
||||
/**
|
||||
* Defines events for config devel.
|
||||
*
|
||||
* @see \Drupal\config_devel\Event\ConfigDevelSaveEvent
|
||||
*/
|
||||
final class ConfigDevelEvents {
|
||||
|
||||
/**
|
||||
* Name of the event fired when saving a config entity to disk.
|
||||
*
|
||||
* This event allows other modules to impact the configuration that is being
|
||||
* written to disk
|
||||
*
|
||||
* @Event
|
||||
*
|
||||
* @see \Drupal\config_devel\Event\ConfigDevelSaveEvent
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const SAVE = 'config_devel.save';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\config_devel\Event;
|
||||
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* The ConfigDevelSaveEvent class.
|
||||
*/
|
||||
class ConfigDevelSaveEvent extends Event {
|
||||
|
||||
/**
|
||||
* An array of files names that will be written.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fileNames = [];
|
||||
|
||||
/**
|
||||
* An array representing the config object to be written.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* ConfigDevelSaveEvent constructor.
|
||||
*
|
||||
* @param array $file_names
|
||||
* The config file names.
|
||||
* @param array $data
|
||||
* The config data.
|
||||
*/
|
||||
public function __construct(array $file_names, array $data) {
|
||||
$this->fileNames = $file_names;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the filenames that will be saved.
|
||||
*
|
||||
* @return array
|
||||
* An array of file names.
|
||||
*/
|
||||
public function getFileNames() {
|
||||
return $this->fileNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file names.
|
||||
*
|
||||
* @param array $file_names
|
||||
* An array of file names to be written out
|
||||
*/
|
||||
public function setFileNames(array $file_names) {
|
||||
$this->fileNames = $file_names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data representing the config object.
|
||||
*
|
||||
* @return array
|
||||
* An array of config data.
|
||||
*/
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data for the config object.
|
||||
*
|
||||
* @param array $data
|
||||
* The config data.
|
||||
*/
|
||||
public function setData(array $data) {
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\config_devel\EventSubscriber;
|
||||
|
||||
use Drupal\config_devel\Event\ConfigDevelEvents;
|
||||
use Drupal\config_devel\Event\ConfigDevelSaveEvent;
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Config\ConfigManagerInterface;
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Yaml\Exception\DumpException;
|
||||
use Drupal\Core\Config\ConfigCrudEvent;
|
||||
use Drupal\Core\Config\ConfigRenameEvent;
|
||||
use Drupal\Core\Config\ConfigEvents;
|
||||
|
||||
/**
|
||||
* ConfigDevelAutoExportSubscriber subscriber for configuration CRUD events.
|
||||
*/
|
||||
class ConfigDevelAutoExportSubscriber extends ConfigDevelSubscriberBase implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The event dispatcher.
|
||||
*
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* Constructs the ConfigDevelAutoExportSubscriber object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The configuration factory.
|
||||
* @param \Drupal\Core\Config\ConfigManagerInterface $config_manager
|
||||
* The configuration manager.
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
|
||||
* The event dispatcher.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, ConfigManagerInterface $config_manager, EventDispatcherInterface $event_dispatcher) {
|
||||
parent::__construct($config_factory, $config_manager);
|
||||
$this->configFactory = $config_factory;
|
||||
$this->configManager = $config_manager;
|
||||
$this->eventDispatcher = $event_dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* The files to automatically export.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $autoExportFiles;
|
||||
|
||||
/**
|
||||
* React to configuration ConfigEvent::SAVE events.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigCrudEvent $event
|
||||
* The event to process.
|
||||
*/
|
||||
public function onConfigSave(ConfigCrudEvent $event) {
|
||||
$this->autoExportConfig($event->getConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* React to configuration ConfigEvent::RENAME events.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigRenameEvent $event
|
||||
* The event to process.
|
||||
*/
|
||||
public function onConfigRename(ConfigRenameEvent $event) {
|
||||
$this->autoExportConfig($event->getConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically export configuration.
|
||||
*
|
||||
* @param Config $config
|
||||
* The config object.
|
||||
*/
|
||||
protected function autoExportConfig(Config $config) {
|
||||
$config_name = $config->getName();
|
||||
$file_names = array_filter($this->getSettings()->get('auto_export') ?: [], function ($file_name) use ($config_name) {
|
||||
return basename($file_name, '.' . FileStorage::getFileExtension()) == $config_name;
|
||||
});
|
||||
$this->writeBackConfig($config, $file_names);
|
||||
}
|
||||
|
||||
/**
|
||||
* write configuration back to files.
|
||||
*
|
||||
* @param \Drupal\Core\Config\Config $config
|
||||
* The config object.
|
||||
* @param array $file_names
|
||||
* The file names to which the configuration should be written.
|
||||
*/
|
||||
public function writeBackConfig(Config $config, array $file_names) {
|
||||
if ($file_names) {
|
||||
$data = $config->get();
|
||||
$config_name = $config->getName();
|
||||
unset($data['_core']);
|
||||
if ($entity_type_id = $this->configManager->getEntityTypeIdByName($config_name)) {
|
||||
unset($data['uuid']);
|
||||
}
|
||||
|
||||
// Let everyone else have a change to update the exported data.
|
||||
$event = new ConfigDevelSaveEvent($file_names, $data);
|
||||
$this->eventDispatcher->dispatch(ConfigDevelEvents::SAVE, $event);
|
||||
$data = $event->getData();
|
||||
$file_names = $event->getFileNames();
|
||||
|
||||
foreach ($file_names as $file_name) {
|
||||
try {
|
||||
file_put_contents($file_name, (new InstallStorage())->encode($data));
|
||||
}
|
||||
catch (DumpException $e) {
|
||||
// Do nothing. What could we do?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the methods in this class that should be listeners.
|
||||
*
|
||||
* @return array
|
||||
* An array of event listener definitions.
|
||||
*/
|
||||
static function getSubscribedEvents() {
|
||||
$events[ConfigEvents::SAVE][] = array('onConfigSave', 10);
|
||||
$events[ConfigEvents::RENAME][] = array('onConfigRename', 10);
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\config_devel\EventSubscriber;
|
||||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
class ConfigDevelAutoImportSubscriber extends ConfigDevelSubscriberBase implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* Reinstall changed config files.
|
||||
*/
|
||||
public function autoImportConfig() {
|
||||
$config = $this->getSettings();
|
||||
$changed = FALSE;
|
||||
foreach ($config->get('auto_import') as $key => $file) {
|
||||
if ($new_hash = $this->importOne($file['filename'], $file['hash'])) {
|
||||
$config->set("auto_import.$key.hash", $new_hash);
|
||||
$changed = TRUE;
|
||||
}
|
||||
}
|
||||
if ($changed) {
|
||||
$config->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the methods in this class that should be listeners.
|
||||
*
|
||||
* @return array
|
||||
* An array of event listener definitions.
|
||||
*/
|
||||
static function getSubscribedEvents() {
|
||||
$events[KernelEvents::REQUEST][] = array('autoImportConfig', 20);
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @param string $original_hash
|
||||
* @return bool
|
||||
*/
|
||||
public function importOne($filename, $original_hash = '', $contents = '') {
|
||||
$hash = '';
|
||||
if (!$contents && (!$contents = @file_get_contents($filename))) {
|
||||
return $hash;
|
||||
}
|
||||
$needs_import = TRUE;
|
||||
if ($original_hash) {
|
||||
$hash = Crypt::hashBase64($contents);
|
||||
if ($hash == $original_hash) {
|
||||
$needs_import = FALSE;
|
||||
}
|
||||
}
|
||||
if ($needs_import) {
|
||||
$data = (new InstallStorage())->decode($contents);
|
||||
$config_name = basename($filename, '.yml');
|
||||
$entity_type_id = $this->configManager->getEntityTypeIdByName($config_name);
|
||||
if ($entity_type_id) {
|
||||
$entity_storage = $this->getStorage($entity_type_id);
|
||||
$entity_id = $this->getEntityId($entity_storage, $config_name);
|
||||
$entity_type = $entity_storage->getEntityType();
|
||||
$id_key = $entity_type->getKey('id');
|
||||
$data[$id_key] = $entity_id;
|
||||
/** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $entity */
|
||||
$entity = $entity_storage->create($data);
|
||||
if ($existing_entity = $entity_storage->load($entity_id)) {
|
||||
$entity
|
||||
->set('uuid', $existing_entity->uuid())
|
||||
->enforceIsNew(FALSE);
|
||||
}
|
||||
$entity_storage->save($entity);
|
||||
}
|
||||
else {
|
||||
$this->configFactory->getEditable($config_name)->setData($data)->save();
|
||||
}
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\config_devel\EventSubscriber;
|
||||
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Config\ConfigManagerInterface;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
|
||||
|
||||
class ConfigDevelSubscriberBase {
|
||||
|
||||
/**
|
||||
* The configuration factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The configuration manager.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigManagerInterface
|
||||
*/
|
||||
protected $configManager;
|
||||
|
||||
/**
|
||||
* Constructs the ConfigDevelAutoExportSubscriber object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The configuration factory.
|
||||
* @param \Drupal\Core\Config\ConfigManagerInterface $config_manager
|
||||
* The configuration manager.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory, ConfigManagerInterface $config_manager) {
|
||||
$this->configFactory = $config_factory;
|
||||
$this->configManager = $config_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entity_type_id
|
||||
*
|
||||
* @return \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
|
||||
*/
|
||||
protected function getStorage($entity_type_id) {
|
||||
return $this->configManager->getEntityManager()->getStorage($entity_type_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $entity_storage
|
||||
* @param string $config_name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getEntityId(ConfigEntityStorageInterface $entity_storage, $config_name) {
|
||||
// getIDFromConfigName adds a dot but getConfigPrefix has a dot already.
|
||||
return $entity_storage::getIDFromConfigName($config_name, $entity_storage->getEntityType()->getConfigPrefix());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Drupal\Core\Config\Config
|
||||
*/
|
||||
protected function getSettings() {
|
||||
return $this->configFactory->getEditable('config_devel.settings');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\config_devel\Form;
|
||||
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Settings form for config devel.
|
||||
*/
|
||||
class ConfigDevelSettingsForm extends ConfigFormBase {
|
||||
|
||||
/**
|
||||
* Name of the config being edited.
|
||||
*/
|
||||
const CONFIGNAME = 'config_devel.settings';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $keys;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$default_value = '';
|
||||
foreach ($this->config(static::CONFIGNAME)->get('auto_import') as $file) {
|
||||
$default_value .= $file['filename'] . "\n";
|
||||
}
|
||||
$form['auto_import'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => $this->t('Auto import'),
|
||||
'#default_value' => $default_value,
|
||||
'#description' => $this->t('When these files change, they will be automatically imported at the beginning of the next request.') . '<br>' .
|
||||
$this->t('Enter one item per line. Each item should consist of a path relative to the Drupal root and a filename in the same form as a config YML file.') . '<br>' .
|
||||
$this->t('For example: "modules/mymodule/config/install/node.type.mytype.yml".'),
|
||||
);
|
||||
$form['auto_export'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => $this->t('Auto export'),
|
||||
'#default_value' => implode("\n", $this->config(static::CONFIGNAME)->get('auto_export')),
|
||||
'#description' => $this->t('Automatically export to the files specified when the corresponding config item is changed in the admin UI.') . '<br>' .
|
||||
$this->t('Enter one item per line. Each item should consist of a path relative to the Drupal root and a filename in the same form as a config YML file.') . '<br>' .
|
||||
$this->t('For example: "modules/mymodule/config/install/node.type.mytype.yml".'),
|
||||
);
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
foreach (array('auto_import', 'auto_export') as $key) {
|
||||
$form_state->setValue($key, array_filter(preg_split("/\r\n/", $form_state->getValues()[$key])));
|
||||
}
|
||||
foreach ($form_state->getValues()['auto_import'] as $file) {
|
||||
$name = basename($file, '.' . FileStorage::getFileExtension());
|
||||
if (in_array($name, array('system.site', 'core.extension', 'simpletest.settings'))) {
|
||||
$form_state->setErrorByName('auto_import', $this->t('@name is not compatible with this module', array('@name' => $name)));
|
||||
}
|
||||
}
|
||||
parent::validateForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$auto_import = array();
|
||||
foreach ($form_state->getValues()['auto_import'] as $file) {
|
||||
$auto_import[] = array(
|
||||
'filename' => $file,
|
||||
'hash' => '',
|
||||
);
|
||||
}
|
||||
$this->config(static::CONFIGNAME)
|
||||
->set('auto_import', $auto_import)
|
||||
->set('auto_export', $form_state->getValues()['auto_export'])
|
||||
->save();
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'config_devel_settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames() {
|
||||
return ['config_devel.settings'];
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\config_devel\Kernel;
|
||||
|
||||
/**
|
||||
* Tests the automated importer for config entities.
|
||||
*
|
||||
* @group config_devel
|
||||
*/
|
||||
class ConfigDevelSubscriberEntityTest extends ConfigDevelSubscriberTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
const CONFIGNAME = 'config_test.dynamic.test';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doAssert(array $data, array $exported_data) {
|
||||
$entity = entity_load('config_test', 'test', TRUE);
|
||||
$this->assertIdentical($data['label'], $entity->get('label'));
|
||||
$this->assertIdentical($exported_data['label'], $data['label']);
|
||||
$this->assertIdentical($exported_data['id'], 'test');
|
||||
$this->assertFalse(isset($exported_data['uuid']));
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\config_devel\Kernel;
|
||||
|
||||
/**
|
||||
* Tests the automated importer for raw config objects.
|
||||
*
|
||||
* @group config_devel
|
||||
*/
|
||||
class ConfigDevelSubscriberRawTest extends ConfigDevelSubscriberTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
const CONFIGNAME = 'config_devel.test';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doAssert(array $data, array $exported_data) {
|
||||
$this->assertIdentical($data, $this->storage->read(static::CONFIGNAME));
|
||||
$this->assertIdentical($data, $exported_data);
|
||||
}
|
||||
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\config_devel\Kernel;
|
||||
|
||||
use Drupal\Component\Serialization\Yaml;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
abstract class ConfigDevelSubscriberTestBase extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('config_devel');
|
||||
|
||||
/**
|
||||
* Name of the config object.
|
||||
*/
|
||||
const CONFIGNAME = '';
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Config\StorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* Test the import subscriber.
|
||||
*/
|
||||
public function testSubscribers() {
|
||||
// Without this the config exporter breaks.
|
||||
\Drupal::service('config.installer')->installDefaultConfig('module', 'config_devel');
|
||||
$filename = 'public://'. static::CONFIGNAME . '.yml';
|
||||
drupal_mkdir('public://exported');
|
||||
$exported_filename = 'public://exported/' . static::CONFIGNAME . '.yml';
|
||||
\Drupal::configFactory()->getEditable('config_devel.settings')
|
||||
->set('auto_import', array(array(
|
||||
'filename' => $filename,
|
||||
'hash' => '',
|
||||
)))
|
||||
->set('auto_export', array(
|
||||
$exported_filename,
|
||||
))
|
||||
->save();
|
||||
$this->storage = \Drupal::service('config.storage');
|
||||
$this->assertFalse($this->storage->exists(static::CONFIGNAME));
|
||||
$subscriber = \Drupal::service('config_devel.auto_import_subscriber');
|
||||
for ($i = 2; $i; $i--) {
|
||||
$data['label'] = $this->randomString();
|
||||
file_put_contents($filename, Yaml::encode($data));
|
||||
// The import fires an export too.
|
||||
$subscriber->autoImportConfig();
|
||||
$this->doAssert($data, Yaml::decode(file_get_contents($exported_filename)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the config import succeeded.
|
||||
*
|
||||
* @param array $writen_data
|
||||
* The config data as written.
|
||||
* @param array $exported_data
|
||||
* The config data exported.
|
||||
*/
|
||||
abstract protected function doAssert(array $data, array $exported_data);
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\config_devel\Unit;
|
||||
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
use Drupal\Component\Serialization\Yaml;
|
||||
|
||||
use Drupal\config_devel\EventSubscriber\ConfigDevelAutoExportSubscriber;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\config_devel\EventSubscriber\ConfigDevelAutoExportSubscriber
|
||||
* @group config_devel
|
||||
*/
|
||||
class ConfigDevelAutoExportSubscriberTest extends ConfigDevelTestBase {
|
||||
|
||||
/**
|
||||
* Test ConfigDevelAutoExportSubscriber::writeBackConfig().
|
||||
*/
|
||||
public function testWriteBackConfig() {
|
||||
$config_data = array(
|
||||
'id' => $this->randomMachineName(),
|
||||
'langcode' => 'en',
|
||||
'uuid' => '836769f4-6791-402d-9046-cc06e20be87f',
|
||||
);
|
||||
|
||||
$config = $this->getMockBuilder('\Drupal\Core\Config\Config')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$config->expects($this->any())
|
||||
->method('getName')
|
||||
->will($this->returnValue($this->randomMachineName()));
|
||||
$config->expects($this->any())
|
||||
->method('get')
|
||||
->will($this->returnValue($config_data));
|
||||
|
||||
$file_names = array(
|
||||
vfsStream::url('public://' . $this->randomMachineName() . '.yml'),
|
||||
vfsStream::url('public://' . $this->randomMachineName() . '.yml'),
|
||||
);
|
||||
|
||||
$configDevelSubscriber = new ConfigDevelAutoExportSubscriber($this->configFactory, $this->configManager, $this->eventDispatcher);
|
||||
$configDevelSubscriber->writeBackConfig($config, $file_names);
|
||||
|
||||
$data = $config_data;
|
||||
unset($data['uuid']);
|
||||
unset($data['_core']);
|
||||
|
||||
foreach ($file_names as $file_name) {
|
||||
$this->assertEquals($data, Yaml::decode(file_get_contents($file_name)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\config_devel\Unit;
|
||||
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Helper class with mock objects.
|
||||
*/
|
||||
abstract class ConfigDevelTestBase extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Config\ConfigManagerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $configManager;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->configFactory = $this->getMock('Drupal\Core\Config\ConfigFactoryInterface');
|
||||
|
||||
$this->eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
||||
|
||||
$this->configManager = $this->getMock('Drupal\Core\Config\ConfigManagerInterface');
|
||||
$this->configManager->expects($this->any())
|
||||
->method('getEntityTypeIdByName')
|
||||
->will($this->returnArgument(0));
|
||||
|
||||
vfsStream::setup('public://');
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ configure: devel.admin_settings
|
||||
tags:
|
||||
- developer
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-14
|
||||
version: '8.x-1.0'
|
||||
# Information added by Drupal.org packaging script on 2017-10-05
|
||||
version: '8.x-1.2'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1502732047
|
||||
datestamp: 1507197848
|
||||
|
||||
@@ -6,8 +6,8 @@ package: Development
|
||||
tags:
|
||||
- developer
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-14
|
||||
version: '8.x-1.0'
|
||||
# Information added by Drupal.org packaging script on 2017-10-05
|
||||
version: '8.x-1.2'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1502732047
|
||||
datestamp: 1507197848
|
||||
|
||||
+2
-1
@@ -141,6 +141,7 @@ class DevelGenerateCommands extends DrushCommands {
|
||||
* @param $max_width Max width of first level of links.
|
||||
* @option kill Delete all content before generating new content.
|
||||
* @aliases genm
|
||||
* @validate-module-enabled menu_link_content
|
||||
*/
|
||||
public function menus($number_menus = 2, $number_links = 50, $max_depth = 3, $max_width = 8, $options = ['kill' => FALSE]) {
|
||||
$this->generate();
|
||||
@@ -179,7 +180,7 @@ class DevelGenerateCommands extends DrushCommands {
|
||||
/** @var DevelGenerateBaseInterface $instance */
|
||||
$instance = $manager->createInstance($commandData->annotationData()->get('pluginId'), array());
|
||||
$this->setPluginInstance($instance);
|
||||
$parameters = $instance->validateDrushParams($args);
|
||||
$parameters = $instance->validateDrushParams($args, $commandData->input()->getOptions());
|
||||
$this->setParameters($parameters);
|
||||
}
|
||||
|
||||
|
||||
@@ -165,4 +165,8 @@ abstract class DevelGenerateBase extends PluginBase implements DevelGenerateBase
|
||||
}
|
||||
return $this->random;
|
||||
}
|
||||
|
||||
protected function isDrush8() {
|
||||
return function_exists('drush_drupal_load_autoloader');
|
||||
}
|
||||
}
|
||||
|
||||
+9
-9
@@ -316,7 +316,7 @@ class ContentDevelGenerate extends DevelGenerateBase implements ContainerFactory
|
||||
$start = time();
|
||||
for ($i = 1; $i <= $values['num']; $i++) {
|
||||
$this->develGenerateContentAddNode($values);
|
||||
if (function_exists('drush_log') && $i % drush_get_option('feedback', 1000) == 0) {
|
||||
if ($this->isDrush8() && function_exists('drush_log') && $i % drush_get_option('feedback', 1000) == 0) {
|
||||
$now = time();
|
||||
drush_log(dt('Completed @feedback nodes (@rate nodes/min)', array('@feedback' => drush_get_option('feedback', 1000), '@rate' => (drush_get_option('feedback', 1000) * 60) / ($now - $start))), 'ok');
|
||||
$start = $now;
|
||||
@@ -372,8 +372,8 @@ class ContentDevelGenerate extends DevelGenerateBase implements ContainerFactory
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateDrushParams($args) {
|
||||
$add_language = drush_get_option('languages');
|
||||
public function validateDrushParams($args, $options = []) {
|
||||
$add_language = $this->isDrush8() ? drush_get_option('languages') : $options['languages'];
|
||||
if (!empty($add_language)) {
|
||||
$add_language = explode(',', str_replace(' ', '', $add_language));
|
||||
// Intersect with the enabled languages to make sure the language args
|
||||
@@ -381,17 +381,17 @@ class ContentDevelGenerate extends DevelGenerateBase implements ContainerFactory
|
||||
$values['values']['add_language'] = array_intersect($add_language, array_keys($this->languageManager->getLanguages(LanguageInterface::STATE_ALL)));
|
||||
}
|
||||
|
||||
$values['kill'] = drush_get_option('kill');
|
||||
$values['kill'] = $this->isDrush8() ? drush_get_option('kill') : $options['kill'];
|
||||
$values['title_length'] = 6;
|
||||
$values['num'] = array_shift($args);
|
||||
$values['max_comments'] = array_shift($args);
|
||||
$all_types = array_keys(node_type_get_names());
|
||||
$default_types = array_intersect(array('page', 'article'), $all_types);
|
||||
$selected_types = StringUtils::csvToArray(drush_get_option('types', $default_types));
|
||||
|
||||
// Validates the input format for content types option.
|
||||
if (drush_get_option('types', $default_types) === TRUE) {
|
||||
throw new \Exception(dt('Wrong syntax or no content type selected. The correct syntax uses "=", eg.: --types=page,article'));
|
||||
if ($this->isDrush8()) {
|
||||
$selected_types = _convert_csv_to_array(drush_get_option('types', $default_types));
|
||||
}
|
||||
else {
|
||||
$selected_types = StringUtils::csvToArray($options['types'] ?: $default_types);
|
||||
}
|
||||
|
||||
if (empty($selected_types)) {
|
||||
|
||||
+16
-18
@@ -221,14 +221,14 @@ class MenuDevelGenerate extends DevelGenerateBase implements ContainerFactoryPlu
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateDrushParams($args) {
|
||||
public function validateDrushParams($args, $options = []) {
|
||||
|
||||
$link_types = array('node', 'front', 'external');
|
||||
$values = array(
|
||||
'num_menus' => array_shift($args),
|
||||
'num_links' => array_shift($args),
|
||||
'kill' => drush_get_option('kill'),
|
||||
'pipe' => drush_get_option('pipe'),
|
||||
'kill' => $this->isDrush8() ? drush_get_option('kill') : $options['kill'],
|
||||
'pipe' => $this->isDrush8() ? drush_get_option('pipe') : $options['pipe'],
|
||||
'link_types' => array_combine($link_types, $link_types),
|
||||
);
|
||||
|
||||
@@ -240,16 +240,16 @@ class MenuDevelGenerate extends DevelGenerateBase implements ContainerFactoryPlu
|
||||
$values['existing_menus']['__new-menu__'] = TRUE;
|
||||
|
||||
if ($this->isNumber($values['num_menus']) == FALSE) {
|
||||
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of menus'));
|
||||
throw new \Exception(dt('Invalid number of menus'));
|
||||
}
|
||||
if ($this->isNumber($values['num_links']) == FALSE) {
|
||||
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of links'));
|
||||
throw new \Exception(dt('Invalid number of links'));
|
||||
}
|
||||
if ($this->isNumber($values['max_depth']) == FALSE || $values['max_depth'] > 9 || $values['max_depth'] < 1) {
|
||||
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid maximum link depth. Use a value between 1 and 9'));
|
||||
throw new \Exception(dt('Invalid maximum link depth. Use a value between 1 and 9'));
|
||||
}
|
||||
if ($this->isNumber($values['max_width']) == FALSE || $values['max_width'] < 1) {
|
||||
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid maximum menu width. Use a positive numeric value.'));
|
||||
throw new \Exception(dt('Invalid maximum menu width. Use a positive numeric value.'));
|
||||
}
|
||||
|
||||
return $values;
|
||||
@@ -300,19 +300,17 @@ class MenuDevelGenerate extends DevelGenerateBase implements ContainerFactoryPlu
|
||||
protected function generateMenus($num_menus, $title_length = 12) {
|
||||
$menus = array();
|
||||
|
||||
if ($this->moduleHandler->moduleExists('menu_ui')) {
|
||||
for ($i = 1; $i <= $num_menus; $i++) {
|
||||
$name = $this->getRandom()->word(mt_rand(2, max(2, $title_length)));
|
||||
for ($i = 1; $i <= $num_menus; $i++) {
|
||||
$name = $this->getRandom()->word(mt_rand(2, max(2, $title_length)));
|
||||
|
||||
$menu = $this->menuStorage->create(array(
|
||||
'label' => $name,
|
||||
'id' => 'devel-' . Unicode::strtolower($name),
|
||||
'description' => $this->t('Description of @name', array('@name' => $name)),
|
||||
));
|
||||
$menu = $this->menuStorage->create(array(
|
||||
'label' => $name,
|
||||
'id' => 'devel-' . Unicode::strtolower($name),
|
||||
'description' => $this->t('Description of @name', array('@name' => $name)),
|
||||
));
|
||||
|
||||
$menu->save();
|
||||
$menus[$menu->id()] = $menu->label();
|
||||
}
|
||||
$menu->save();
|
||||
$menus[$menu->id()] = $menu->label();
|
||||
}
|
||||
|
||||
return $menus;
|
||||
|
||||
+5
-14
@@ -210,15 +210,6 @@ class TermDevelGenerate extends DevelGenerateBase implements ContainerFactoryPlu
|
||||
|
||||
$max++;
|
||||
|
||||
if (function_exists('drush_log')) {
|
||||
$feedback = drush_get_option('feedback', 1000);
|
||||
if ($i % $feedback == 0) {
|
||||
$now = time();
|
||||
drush_log(dt('Completed @feedback terms (@rate terms/min)', array('@feedback' => $feedback, '@rate' => $feedback * 60 / ($now - $start))), 'ok');
|
||||
$start = $now;
|
||||
}
|
||||
}
|
||||
|
||||
// Limit memory usage. Only report first 20 created terms.
|
||||
if ($i < 20) {
|
||||
$terms[] = $term->label();
|
||||
@@ -233,7 +224,7 @@ class TermDevelGenerate extends DevelGenerateBase implements ContainerFactoryPlu
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateDrushParams($args) {
|
||||
public function validateDrushParams($args, $options = []) {
|
||||
$vocabulary_name = array_shift($args);
|
||||
$number = array_shift($args);
|
||||
|
||||
@@ -242,21 +233,21 @@ class TermDevelGenerate extends DevelGenerateBase implements ContainerFactoryPlu
|
||||
}
|
||||
|
||||
if (!$vocabulary_name) {
|
||||
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Please provide a vocabulary machine name.'));
|
||||
throw new \Exception(dt('Please provide a vocabulary machine name.'));
|
||||
}
|
||||
|
||||
if (!$this->isNumber($number)) {
|
||||
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of terms: @num', array('@num' => $number)));
|
||||
throw new \Exception(dt('Invalid number of terms: @num', array('@num' => $number)));
|
||||
}
|
||||
|
||||
// Try to convert machine name to a vocabulary id.
|
||||
if (!$vocabulary = $this->vocabularyStorage->load($vocabulary_name)) {
|
||||
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid vocabulary name: @name', array('@name' => $vocabulary_name)));
|
||||
throw new \Exception(dt('Invalid vocabulary name: @name', array('@name' => $vocabulary_name)));
|
||||
}
|
||||
|
||||
$values = [
|
||||
'num' => $number,
|
||||
'kill' => drush_get_option('kill'),
|
||||
'kill' => $this->isDrush8() ? drush_get_option('kill') : $options['kill'],
|
||||
'title_length' => 12,
|
||||
'vids' => [$vocabulary->id()],
|
||||
];
|
||||
|
||||
+17
-4
@@ -7,6 +7,7 @@ use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\devel_generate\DevelGenerateBase;
|
||||
use Drush\Utils\StringUtils;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
@@ -177,14 +178,26 @@ class UserDevelGenerate extends DevelGenerateBase implements ContainerFactoryPlu
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateDrushParams($args) {
|
||||
public function validateDrushParams($args, $options = []) {
|
||||
$values = array(
|
||||
'num' => array_shift($args),
|
||||
'roles' => drush_get_option('roles') ? explode(',', drush_get_option('roles')) : array(),
|
||||
'kill' => drush_get_option('kill'),
|
||||
'pass' => drush_get_option('pass', NULL),
|
||||
'time_range' => 0,
|
||||
);
|
||||
|
||||
if ($this->isDrush8()) {
|
||||
$values += [
|
||||
'roles' => explode(',', drush_get_option('roles', '')),
|
||||
'kill' => drush_get_option('kill'),
|
||||
'pass' => drush_get_option('pass', NULL),
|
||||
];
|
||||
}
|
||||
else {
|
||||
$values += [
|
||||
'roles' => StringUtils::csvToArray($options['roles']),
|
||||
'kill' => $options['kill'],
|
||||
'pass' => $options['pass'],
|
||||
];
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -158,15 +158,15 @@ class VocabularyDevelGenerate extends DevelGenerateBase implements ContainerFact
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateDrushParams($args) {
|
||||
public function validateDrushParams($args, $options = []) {
|
||||
$values = array(
|
||||
'num' => array_shift($args),
|
||||
'kill' => drush_get_option('kill'),
|
||||
'kill' => $this->isDrush8() ? drush_get_option('kill') : $options['kill'],
|
||||
'title_length' => 12,
|
||||
);
|
||||
|
||||
if ($this->isNumber($values['num']) == FALSE) {
|
||||
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of vocabularies: @num.', array('@num' => $values['num'])));
|
||||
throw new \Exception(dt('Invalid number of vocabularies: @num.', array('@num' => $values['num'])));
|
||||
}
|
||||
|
||||
return $values;
|
||||
|
||||
+3
-3
@@ -7,8 +7,8 @@ configure: admin/config/development/generate
|
||||
tags:
|
||||
- developer
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-14
|
||||
version: '8.x-1.0'
|
||||
# Information added by Drupal.org packaging script on 2017-10-05
|
||||
version: '8.x-1.2'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1502732047
|
||||
datestamp: 1507197848
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
devel.command:
|
||||
class: Drupal\devel\Commands\DevelCommands
|
||||
arguments: ['@token', '@service_container', '@event_dispatcher']
|
||||
arguments: ['@token', '@service_container', '@event_dispatcher', '@module_handler']
|
||||
tags:
|
||||
- { name: drush.command }
|
||||
|
||||
@@ -6,8 +6,8 @@ package: Development
|
||||
tags:
|
||||
- developer
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-14
|
||||
version: '8.x-1.0'
|
||||
# Information added by Drupal.org packaging script on 2017-10-05
|
||||
version: '8.x-1.2'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1502732047
|
||||
datestamp: 1507197848
|
||||
|
||||
@@ -5,6 +5,9 @@ use Drupal\Component\Uuid\Php;
|
||||
use Drupal\Core\Utility\Token;
|
||||
use Drush\Commands\DrushCommands;
|
||||
use Drush\Exceptions\UserAbortException;
|
||||
use Drush\Utils\StringUtils;
|
||||
use Symfony\Component\Console\Input\Input;
|
||||
use Symfony\Component\Console\Output\Output;
|
||||
|
||||
/**
|
||||
* For commands that are parts of modules, Drush expects to find commandfiles in
|
||||
@@ -21,11 +24,21 @@ class DevelCommands extends DrushCommands {
|
||||
|
||||
protected $eventDispatcher;
|
||||
|
||||
public function __construct(Token $token, $container, $eventDispatcher) {
|
||||
protected $moduleHandler;
|
||||
|
||||
public function __construct(Token $token, $container, $eventDispatcher, $moduleHandler) {
|
||||
parent::__construct();
|
||||
$this->token = $token;
|
||||
$this->container = $container;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->moduleHandler = $moduleHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
public function getModuleHandler() {
|
||||
return $this->moduleHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,93 +63,110 @@ class DevelCommands extends DrushCommands {
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall, and Install a list of modules.
|
||||
* Uninstall, and Install modules.
|
||||
|
||||
* @command devel-reinstall
|
||||
* @command devel:reinstall
|
||||
* @param $modules A comma-separated list of module names.
|
||||
* @aliases dre
|
||||
* @aliases dre,devel-reinstall
|
||||
* @allow-additional-options pm-uninstall,pm-enable
|
||||
*/
|
||||
public function reinstall($projects) {
|
||||
$projects = _convert_csv_to_array($projects);
|
||||
public function reinstall($modules) {
|
||||
$modules = StringUtils::csvToArray($modules);
|
||||
|
||||
// This is faster than 3 separate bootstraps.
|
||||
$args = array_merge(array('pm-uninstall'), $projects);
|
||||
// @todo. Use $application dispatch instead of drush_invoke().
|
||||
call_user_func_array('drush_invoke', $args);
|
||||
|
||||
$args = array_merge(array('pm-enable'), $projects);
|
||||
call_user_func_array('drush_invoke', $args);
|
||||
$modules_str = implode(',', $modules);
|
||||
drush_invoke_process('@self', 'pm:uninstall', [$modules_str], []);
|
||||
drush_invoke_process('@self', 'pm:enable', [$modules_str], []);
|
||||
}
|
||||
|
||||
/**
|
||||
* List implementations of a given hook and optionally edit one.
|
||||
*
|
||||
* @command devel-hook
|
||||
* @command devel:hook
|
||||
* @param $hook The name of the hook to explore.
|
||||
* @param $implementation The name of the implementation to edit. Usually omitted.
|
||||
* @usage devel-hook cron
|
||||
* List implementations of hook_cron().
|
||||
* @aliases fnh,fn-hook,hook
|
||||
* @aliases fnh,fn-hook,hook,devel-hook
|
||||
* @optionset_get_editor
|
||||
*/
|
||||
function hook($hook) {
|
||||
function hook($hook, $implementation) {
|
||||
// Get implementations in the .install files as well.
|
||||
include_once './core/includes/install.inc';
|
||||
drupal_load_updates();
|
||||
$info = $this->codeLocate($implementation . "_$hook");
|
||||
$exec = drush_get_editor();
|
||||
drush_shell_exec_interactive($exec, $info['file']);
|
||||
}
|
||||
|
||||
if ($hook_implementations = \Drupal::moduleHandler()->getImplementations($hook)) {
|
||||
if ($choice = drush_choice(array_combine($hook_implementations, $hook_implementations), 'Enter the number of the hook implementation you wish to view.')) {
|
||||
$info= $this->codeLocate($choice . "_$hook");
|
||||
$exec = drush_get_editor();
|
||||
drush_shell_exec_interactive($exec, $info['file']);
|
||||
/**
|
||||
* @hook interact hook
|
||||
*/
|
||||
public function hookInteract(Input $input, Output $output) {
|
||||
if (!$input->getArgument('implementation')) {
|
||||
if ($hook_implementations = $this->getModuleHandler()->getImplementations($input->getArgument('hook'))) {
|
||||
if (!$choice = $this->io()->choice('Enter the number of the hook implementation you wish to view.', array_combine($hook_implementations, $hook_implementations))) {
|
||||
throw new UserAbortException();
|
||||
}
|
||||
$input->setArgument('implementation', $choice);
|
||||
}
|
||||
else {
|
||||
throw new \Exception(dt('No implementations'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->logger()->success(dt('No implementations.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List implementations of a given event and optionally edit one.
|
||||
*
|
||||
* @command devel-event
|
||||
* @command devel:event
|
||||
* @param $event The name of the event to explore. If omitted, a list of events is shown.
|
||||
* @param $implementation The name of the implementation to show. Usually omitted.
|
||||
* @usage devel-event
|
||||
* Pick a Kernel event, then pick an implementation, and then view its source code.
|
||||
* @usage devel-event kernel.terminate
|
||||
* Pick a terminate subscribers and view its source code.
|
||||
* Pick a terminate subscribers implementation and view its source code.
|
||||
* @aliases fne,fn-event,event
|
||||
*/
|
||||
function event($event) {
|
||||
function event($event, $implementation) {
|
||||
$info= $this->codeLocate($implementation);
|
||||
$exec = drush_get_editor();
|
||||
drush_shell_exec_interactive($exec, $info['file']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hook interact devel:event
|
||||
*/
|
||||
public function interactEvent(Input $input, Output $output) {
|
||||
$dispatcher = $this->getEventDispatcher();
|
||||
if (empty($event)) {
|
||||
// @todo Expand this list and move to interact().
|
||||
if (!$input->getArgument('event')) {
|
||||
// @todo Expand this list.
|
||||
$events = array('kernel.controller', 'kernel.exception', 'kernel.request', 'kernel.response', 'kernel.terminate', 'kernel.view');
|
||||
$events = array_combine($events, $events);
|
||||
if (!$event = drush_choice($events, 'Enter the event you wish to explore.')) {
|
||||
if (!$event = $this->io()->choice('Enter the event you wish to explore.', $events)) {
|
||||
throw new UserAbortException();
|
||||
}
|
||||
$input->setArgument('event', $event);
|
||||
}
|
||||
if ($implementations = $dispatcher->getListeners($event)) {
|
||||
foreach ($implementations as $implementation) {
|
||||
$callable = get_class($implementation[0]) . '::' . $implementation[1];
|
||||
$choices[$callable] = $callable;
|
||||
}
|
||||
if ($choice = drush_choice($choices, 'Enter the number of the implementation you wish to view.')) {
|
||||
$info= $this->codeLocate($choice);
|
||||
$exec = drush_get_editor();
|
||||
drush_shell_exec_interactive($exec, $info['file']);
|
||||
if (!$choice = $this->io()->choice('Enter the number of the implementation you wish to view.', $choices)) {
|
||||
throw new UserAbortException();
|
||||
}
|
||||
$input->setArgument('implementation', $choice);
|
||||
}
|
||||
else {
|
||||
$this->logger()->success(dt('No implementations.'));
|
||||
throw new \Exception(dt('No implementations.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List available tokens.
|
||||
*
|
||||
* @command devel-token
|
||||
* @aliases token
|
||||
* @command devel:token
|
||||
* @aliases token,devel-token
|
||||
* @field-labels
|
||||
* group: Group
|
||||
* token: Token
|
||||
@@ -162,8 +192,8 @@ class DevelCommands extends DrushCommands {
|
||||
/**
|
||||
* Generate a UUID.
|
||||
*
|
||||
* @command devel-uuid
|
||||
* @aliases uuid
|
||||
* @command devel:uuid
|
||||
* @aliases uuid,devel-uuid
|
||||
* @usage drush devel-uuid
|
||||
* Outputs a Universally Unique Identifier.
|
||||
*
|
||||
@@ -203,9 +233,9 @@ class DevelCommands extends DrushCommands {
|
||||
/**
|
||||
* Get a list of available container services.
|
||||
*
|
||||
* @command devel-services
|
||||
* @command devel:services
|
||||
* @param $prefix A prefix to filter the service list by.
|
||||
* @aliases devel-container-services,dcs
|
||||
* @aliases devel-container-services,dcs,devel-services
|
||||
* @usage drush devel-services
|
||||
* Gets a list of all available container services
|
||||
* @usage drush dcs plugin.manager
|
||||
|
||||
+3
-3
@@ -5,8 +5,8 @@ package: Testing
|
||||
# version: VERSION
|
||||
# core: 8.x
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-14
|
||||
version: '8.x-1.0'
|
||||
# Information added by Drupal.org packaging script on 2017-10-05
|
||||
version: '8.x-1.2'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1502732047
|
||||
datestamp: 1507197848
|
||||
|
||||
+3
-3
@@ -9,8 +9,8 @@ dependencies:
|
||||
- text
|
||||
- entity_test
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-14
|
||||
version: '8.x-1.0'
|
||||
# Information added by Drupal.org packaging script on 2017-10-05
|
||||
version: '8.x-1.2'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1502732047
|
||||
datestamp: 1507197848
|
||||
|
||||
@@ -5,8 +5,8 @@ package: Testing
|
||||
# version: VERSION
|
||||
# core: 8.x
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-14
|
||||
version: '8.x-1.0'
|
||||
# Information added by Drupal.org packaging script on 2017-10-05
|
||||
version: '8.x-1.2'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1502732047
|
||||
datestamp: 1507197848
|
||||
|
||||
+10
-10
@@ -1,22 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Tests;
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests Devel controller.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelControllerTest extends WebTestBase {
|
||||
class DevelControllerTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('devel', 'node', 'entity_test', 'devel_entity_test', 'block');
|
||||
public static $modules = ['devel', 'node', 'entity_test', 'devel_entity_test', 'block'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -26,35 +26,35 @@ class DevelControllerTest extends WebTestBase {
|
||||
|
||||
// Create a test entity.
|
||||
$random_label = $this->randomMachineName();
|
||||
$data = array('type' => 'entity_test', 'name' => $random_label);
|
||||
$data = ['type' => 'entity_test', 'name' => $random_label];
|
||||
$this->entity = entity_create('entity_test', $data);
|
||||
$this->entity->save();
|
||||
|
||||
// Create a test entity with only canonical route.
|
||||
$random_label = $this->randomMachineName();
|
||||
$data = array('type' => 'devel_entity_test_canonical', 'name' => $random_label);
|
||||
$data = ['type' => 'devel_entity_test_canonical', 'name' => $random_label];
|
||||
$this->entity_canonical = entity_create('devel_entity_test_canonical', $data);
|
||||
$this->entity_canonical->save();
|
||||
|
||||
// Create a test entity with only edit route.
|
||||
$random_label = $this->randomMachineName();
|
||||
$data = array('type' => 'devel_entity_test_edit', 'name' => $random_label);
|
||||
$data = ['type' => 'devel_entity_test_edit', 'name' => $random_label];
|
||||
$this->entity_edit = entity_create('devel_entity_test_edit', $data);
|
||||
$this->entity_edit->save();
|
||||
|
||||
// Create a test entity with no routes.
|
||||
$random_label = $this->randomMachineName();
|
||||
$data = array('type' => 'devel_entity_test_no_links', 'name' => $random_label);
|
||||
$data = ['type' => 'devel_entity_test_no_links', 'name' => $random_label];
|
||||
$this->entity_no_links = entity_create('devel_entity_test_no_links', $data);
|
||||
$this->entity_no_links->save();
|
||||
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
|
||||
$web_user = $this->drupalCreateUser(array(
|
||||
$web_user = $this->drupalCreateUser([
|
||||
'view test entity',
|
||||
'administer entity_test content',
|
||||
'access devel information',
|
||||
));
|
||||
]);
|
||||
$this->drupalLogin($web_user);
|
||||
}
|
||||
|
||||
+33
-27
@@ -1,16 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Tests;
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests pluggable dumper feature.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelDumperTest extends WebTestBase {
|
||||
class DevelDumperTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
@@ -36,36 +36,42 @@ class DevelDumperTest extends WebTestBase {
|
||||
$this->drupalGet('admin/config/development/devel');
|
||||
|
||||
// Ensures that the dumper input is present on the config page.
|
||||
$this->assertFieldByName('dumper');
|
||||
$this->assertSession()->fieldExists('dumper');
|
||||
|
||||
// Ensures that the 'default' dumper is enabled by default.
|
||||
$this->assertFieldChecked('edit-dumper-default');
|
||||
$this->assertSession()->checkboxChecked('edit-dumper-default');
|
||||
|
||||
// Ensures that all dumpers declared by devel are present on the config page
|
||||
// and that only the available dumpers are selectable.
|
||||
$dumpers = ['default', 'drupal_variable', 'firephp', 'chromephp', 'var_dumper'];
|
||||
$dumpers = [
|
||||
'default',
|
||||
'drupal_variable',
|
||||
'firephp',
|
||||
'chromephp',
|
||||
'var_dumper',
|
||||
];
|
||||
$available_dumpers = ['default', 'drupal_variable'];
|
||||
|
||||
foreach ($dumpers as $dumper) {
|
||||
$this->assertFieldByXPath('//input[@type="radio" and @name="dumper"]', $dumper, new FormattableMarkup('Radio button for @dumper found.', ['@dumper' => $dumper]));
|
||||
$this->assertFieldByXPath('//input[@type="radio" and @name="dumper"]', $dumper);
|
||||
if (in_array($dumper, $available_dumpers)) {
|
||||
$this->assertFieldByXPath('//input[@name="dumper" and not(@disabled="disabled")]', $dumper, new FormattableMarkup('Dumper @dumper is available.', ['@dumper' => $dumper]));
|
||||
$this->assertFieldByXPath('//input[@name="dumper" and not(@disabled="disabled")]', $dumper);
|
||||
}
|
||||
else {
|
||||
$this->assertFieldByXPath('//input[@name="dumper" and @disabled="disabled"]', $dumper, new FormattableMarkup('Dumper @dumper is disabled.', ['@dumper' => $dumper]));
|
||||
$this->assertFieldByXPath('//input[@name="dumper" and @disabled="disabled"]', $dumper);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensures that dumper plugins declared by other modules are present on the
|
||||
// config page and that only the available dumpers are selectable.
|
||||
$this->assertFieldByName('dumper', 'available_test_dumper');
|
||||
$this->assertText('Available test dumper.', 'Available dumper label is present');
|
||||
$this->assertText('Drupal dumper for testing purposes (available).', 'Available dumper description is present');
|
||||
$this->assertFieldByXPath('//input[@name="dumper"]', 'available_test_dumper');
|
||||
$this->assertSession()->pageTextContains('Available test dumper.');
|
||||
$this->assertSession()->pageTextContains('Drupal dumper for testing purposes (available).');
|
||||
$this->assertFieldByXPath('//input[@name="dumper" and not(@disabled="disabled")]', 'available_test_dumper', 'Available dumper input not is disabled.');
|
||||
|
||||
$this->assertFieldByName('dumper', 'not_available_test_dumper');
|
||||
$this->assertText('Not available test dumper.', 'Non available dumper label is present');
|
||||
$this->assertText('Drupal dumper for testing purposes (not available).Not available. You may need to install external dependencies for use this plugin.', 'Non available dumper description is present');
|
||||
$this->assertFieldByXPath('//input[@name="dumper"]', 'not_available_test_dumper');
|
||||
$this->assertSession()->pageTextContains('Not available test dumper.');
|
||||
$this->assertSession()->pageTextContains('Drupal dumper for testing purposes (not available).Not available. You may need to install external dependencies for use this plugin.');
|
||||
$this->assertFieldByXPath('//input[@name="dumper" and @disabled="disabled"]', 'not_available_test_dumper', 'Non available dumper input is disabled.');
|
||||
|
||||
// Ensures that saving of the dumpers configuration works as expected.
|
||||
@@ -73,10 +79,10 @@ class DevelDumperTest extends WebTestBase {
|
||||
'dumper' => 'drupal_variable',
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'));
|
||||
$this->assertSession()->pageTextContains(t('The configuration options have been saved.'));
|
||||
|
||||
$config = \Drupal::config('devel.settings')->get('devel_dumper');
|
||||
$this->assertEqual('drupal_variable', $config, 'The configuration options have been properly saved');
|
||||
$this->assertEquals('drupal_variable', $config, 'The configuration options have been properly saved');
|
||||
|
||||
// Ensure that if the chosen dumper is not available (e.g. the module that
|
||||
// provide it is uninstalled) the 'default' dumper appears selected in the
|
||||
@@ -84,22 +90,22 @@ class DevelDumperTest extends WebTestBase {
|
||||
\Drupal::service('module_installer')->install(['kint']);
|
||||
|
||||
$this->drupalGet('admin/config/development/devel');
|
||||
$this->assertFieldByName('dumper', 'kint');
|
||||
$this->assertFieldByXPath('//input[@name="dumper"]', 'kint');
|
||||
|
||||
$edit = [
|
||||
'dumper' => 'kint',
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'));
|
||||
$this->assertSession()->pageTextContains(t('The configuration options have been saved.'));
|
||||
|
||||
$config = \Drupal::config('devel.settings')->get('devel_dumper');
|
||||
$this->assertEqual('kint', $config, 'The configuration options have been properly saved');
|
||||
$this->assertEquals('kint', $config, 'The configuration options have been properly saved');
|
||||
|
||||
\Drupal::service('module_installer')->uninstall(['kint']);
|
||||
|
||||
$this->drupalGet('admin/config/development/devel');
|
||||
$this->assertNoFieldByName('dumper', 'kint');
|
||||
$this->assertFieldChecked('edit-dumper-default');
|
||||
$this->assertNoFieldByXPath('//input[@name="dumper"]', 'kint');
|
||||
$this->assertSession()->checkboxChecked('edit-dumper-default');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,7 +116,7 @@ class DevelDumperTest extends WebTestBase {
|
||||
'dumper' => 'available_test_dumper',
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/devel', $edit, t('Save configuration'));
|
||||
$this->assertText(t('The configuration options have been saved.'));
|
||||
$this->assertSession()->pageTextContains(t('The configuration options have been saved.'));
|
||||
|
||||
$this->drupalGet('devel_dumper_test/dump');
|
||||
$elements = $this->xpath('//body/pre[contains(text(), :message)]', [':message' => 'AvailableTestDumper::dump() Test output']);
|
||||
@@ -129,8 +135,8 @@ class DevelDumperTest extends WebTestBase {
|
||||
$this->assertTrue(!empty($elements), 'Dumped message is present.');
|
||||
// Ensures that plugins can add libraries to the page when the
|
||||
// ::exportAsRenderable() method is used.
|
||||
$this->assertRaw('devel_dumper_test/css/devel_dumper_test.css');
|
||||
$this->assertRaw('devel_dumper_test/js/devel_dumper_test.js');
|
||||
$this->assertSession()->responseContains('devel_dumper_test/css/devel_dumper_test.css');
|
||||
$this->assertSession()->responseContains('devel_dumper_test/js/devel_dumper_test.js');
|
||||
|
||||
$debug_filename = file_directory_temp() . '/drupal_debug.txt';
|
||||
|
||||
@@ -140,7 +146,7 @@ class DevelDumperTest extends WebTestBase {
|
||||
<pre>AvailableTestDumper::export() Test output</pre>
|
||||
|
||||
EOF;
|
||||
$this->assertEqual($file_content, $expected, 'Dumped message is present.');
|
||||
$this->assertEquals($file_content, $expected, 'Dumped message is present.');
|
||||
|
||||
// Ensures that the DevelDumperManager::debug() is not access checked and
|
||||
// that the dump is written in the debug file even if the user has not the
|
||||
@@ -153,7 +159,7 @@ EOF;
|
||||
<pre>AvailableTestDumper::export() Test output</pre>
|
||||
|
||||
EOF;
|
||||
$this->assertEqual($file_content, $expected, 'Dumped message is present.');
|
||||
$this->assertEquals($file_content, $expected, 'Dumped message is present.');
|
||||
}
|
||||
|
||||
}
|
||||
+3
-3
@@ -1,16 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Tests;
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests devel menu links.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelMenuLinksTest extends WebTestBase {
|
||||
class DevelMenuLinksTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
+8
-8
@@ -1,22 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Tests;
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests reinstall modules.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelReinstallTest extends WebTestBase {
|
||||
class DevelModulesReinstallTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('devel');
|
||||
public static $modules = ['devel'];
|
||||
|
||||
/**
|
||||
* The profile to install as a basis for testing.
|
||||
@@ -31,7 +31,7 @@ class DevelReinstallTest extends WebTestBase {
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$web_user = $this->drupalCreateUser(array('administer site configuration'));
|
||||
$web_user = $this->drupalCreateUser(['administer site configuration']);
|
||||
$this->drupalLogin($web_user);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class DevelReinstallTest extends WebTestBase {
|
||||
*/
|
||||
public function testDevelReinstallModules() {
|
||||
// Minimal profile enables only dblog, block and node.
|
||||
$modules = array('dblog', 'block');
|
||||
$modules = ['dblog', 'block'];
|
||||
|
||||
// Needed for compare correctly the message.
|
||||
sort($modules);
|
||||
@@ -48,13 +48,13 @@ class DevelReinstallTest extends WebTestBase {
|
||||
$this->drupalGet('devel/reinstall');
|
||||
|
||||
// Prepare field data in an associative array
|
||||
$edit = array();
|
||||
$edit = [];
|
||||
foreach ($modules as $module) {
|
||||
$edit["reinstall[$module]"] = TRUE;
|
||||
}
|
||||
|
||||
$this->drupalPostForm('devel/reinstall', $edit, t('Reinstall'));
|
||||
$this->assertText(t('Uninstalled and installed: @names.', array('@names' => implode(', ', $modules))));
|
||||
$this->assertText(t('Uninstalled and installed: @names.', ['@names' => implode(', ', $modules)]));
|
||||
}
|
||||
|
||||
}
|
||||
+6
-6
@@ -1,16 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\devel\Tests;
|
||||
namespace Drupal\Tests\devel\Functional;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests switch user.
|
||||
*
|
||||
* @group devel
|
||||
*/
|
||||
class DevelSwitchUserTest extends WebTestBase {
|
||||
class DevelSwitchUserTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
@@ -276,12 +276,12 @@ class DevelSwitchUserTest extends WebTestBase {
|
||||
*/
|
||||
protected function assertSessionByUid($uid) {
|
||||
$query = \Drupal::database()->select('sessions');
|
||||
$query->fields('sessions', array('uid'));
|
||||
$query->fields('sessions', ['uid']);
|
||||
$query->condition('uid', $uid);
|
||||
$result = $query->execute()->fetchAll();
|
||||
|
||||
if (empty($result)) {
|
||||
$this->fail(new FormattableMarkup('No session found for uid @uid', array('@uid' => $uid)));
|
||||
$this->fail(new FormattableMarkup('No session found for uid @uid', ['@uid' => $uid]));
|
||||
}
|
||||
elseif (count($result) > 1) {
|
||||
// If there is more than one session, then that must be unexpected.
|
||||
@@ -304,7 +304,7 @@ class DevelSwitchUserTest extends WebTestBase {
|
||||
*/
|
||||
protected function assertNoSessionByUid($uid) {
|
||||
$query = \Drupal::database()->select('sessions');
|
||||
$query->fields('sessions', array('uid'));
|
||||
$query->fields('sessions', ['uid']);
|
||||
$query->condition('uid', $uid);
|
||||
$result = $query->execute()->fetchAll();
|
||||
$this->assert(empty($result), "No session for uid $uid found.");
|
||||
+6
-3
@@ -56,7 +56,7 @@ class DatabaseDataCollector extends DataCollector implements DrupalDataCollector
|
||||
unset($query['caller']['args']);
|
||||
|
||||
// Remove query args element if empty.
|
||||
if (empty($query['args'])) {
|
||||
if (isset($query['args']) && empty($query['args'])) {
|
||||
unset($query['args']);
|
||||
}
|
||||
|
||||
@@ -223,8 +223,11 @@ class DatabaseDataCollector extends DataCollector implements DrupalDataCollector
|
||||
$query['type'] = $type;
|
||||
|
||||
$quoted = [];
|
||||
foreach ((array) $query['args'] as $key => $val) {
|
||||
$quoted[$key] = is_null($val) ? 'NULL' : $conn->quote($val);
|
||||
|
||||
if (isset($query['args'])) {
|
||||
foreach ((array) $query['args'] as $key => $val) {
|
||||
$quoted[$key] = is_null($val) ? 'NULL' : $conn->quote($val);
|
||||
}
|
||||
}
|
||||
|
||||
$query['query_args'] = strtr($query['query'], $quoted);
|
||||
|
||||
+7
@@ -108,6 +108,13 @@ class ConfigEntityStorageDecorator extends EntityDecorator implements ConfigEnti
|
||||
return $this->getOriginalObject()->save($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasData() {
|
||||
return $this->getOriginalObject()->hasData();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
||||
+29
-31
@@ -4,6 +4,7 @@ namespace Drupal\webprofiler\EventDispatcher;
|
||||
|
||||
use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher;
|
||||
use Drupal\webprofiler\Stopwatch;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
@@ -29,10 +30,19 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
|
||||
protected $notCalledListeners;
|
||||
|
||||
/**
|
||||
* @param \Drupal\webprofiler\Stopwatch $stopwatch
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setStopwatch(Stopwatch $stopwatch) {
|
||||
$this->stopwatch = $stopwatch;
|
||||
public function __construct(ContainerInterface $container, array $listeners = []) {
|
||||
parent::__construct($container, $listeners);
|
||||
$this->notCalledListeners = $listeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addListener($event_name, $listener, $priority = 0) {
|
||||
parent::addListener($event_name, $listener, $priority);
|
||||
$this->notCalledListeners[$event_name][$priority][] = ['callable' => $listener];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,6 +107,13 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
|
||||
return $this->notCalledListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Drupal\webprofiler\Stopwatch $stopwatch
|
||||
*/
|
||||
public function setStopwatch(Stopwatch $stopwatch) {
|
||||
$this->stopwatch = $stopwatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before dispatching the event.
|
||||
*
|
||||
@@ -158,38 +175,19 @@ class TraceableEventDispatcher extends ContainerAwareEventDispatcher implements
|
||||
'method' => $definition['callable'][1],
|
||||
];
|
||||
|
||||
// Remove this listener from the $notCalledListeners array.
|
||||
if (!$this->notCalledListeners) {
|
||||
$this->notCalledListeners = $this->cloneListeners($this->listeners);
|
||||
}
|
||||
|
||||
foreach ($this->notCalledListeners[$event_name][$priority] as $key => $listener) {
|
||||
if ($listener['service'][0] == $definition['service'][0] && $listener['service'][1] == $definition['service'][1]) {
|
||||
unset($this->notCalledListeners[$event_name][$priority][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $listeners
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function cloneListeners($listeners) {
|
||||
$clone = [];
|
||||
|
||||
foreach ($listeners as $eventName => $events) {
|
||||
foreach ($events as $priorityValue => $priorities) {
|
||||
foreach ($priorities as $key => $listener) {
|
||||
$clone[$eventName][$priorityValue][$key]['service'] = [
|
||||
$listener['service'][0],
|
||||
$listener['service'][1]
|
||||
];
|
||||
if (isset($listener['service'])) {
|
||||
if ($listener['service'][0] == $definition['service'][0] && $listener['service'][1] == $definition['service'][1]) {
|
||||
unset($this->notCalledListeners[$event_name][$priority][$key]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (get_class($listener['callable'][0]) == get_class($definition['callable'][0]) && $listener['callable'][1] == $definition['callable'][1]) {
|
||||
unset($this->notCalledListeners[$event_name][$priority][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ class ConfigForm extends ConfigFormBase {
|
||||
'#states' => array(
|
||||
'visible' => array(
|
||||
array(
|
||||
':input[name="active_toolbar_items[database]' => array('checked' => TRUE),
|
||||
'input[name="active_toolbar_items[database]"]' => array('checked' => TRUE),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ class TraceableViewExecutable extends ViewExecutable {
|
||||
* @return float
|
||||
*/
|
||||
public function getExecuteTime() {
|
||||
return $this->execute_time;
|
||||
return property_exists($this, 'execute_time') ? $this->execute_time : 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,8 +9,8 @@ tags:
|
||||
dependencies:
|
||||
- devel
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-14
|
||||
version: '8.x-1.0'
|
||||
# Information added by Drupal.org packaging script on 2017-10-05
|
||||
version: '8.x-1.2'
|
||||
core: '8.x'
|
||||
project: 'devel'
|
||||
datestamp: 1502732047
|
||||
datestamp: 1507197848
|
||||
|
||||
@@ -38,7 +38,7 @@ before_script:
|
||||
# Export database variable for kernel tests.
|
||||
- export SIMPLETEST_DB=mysql://root:@127.0.0.1/entity
|
||||
# Download Drupal 8 core.
|
||||
- travis_retry git clone --branch 8.1.x --depth 1 http://git.drupal.org/project/drupal.git
|
||||
- travis_retry git clone --branch 8.3.x --depth 1 http://git.drupal.org/project/drupal.git
|
||||
- cd drupal
|
||||
- composer self-update
|
||||
- composer install -n
|
||||
|
||||
@@ -5,6 +5,6 @@
|
||||
"homepage": "http://drupal.org/project/entity",
|
||||
"license": "GPL-2.0+",
|
||||
"require": {
|
||||
"drupal/core": "~8.1"
|
||||
"drupal/core": "~8.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ description: Provides expanded entity APIs, which will be moved to Drupal core o
|
||||
type: module
|
||||
# core: 8.x
|
||||
dependencies:
|
||||
- system (>=8.1.0)
|
||||
- drupal:system (>=8.3.0)
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-12-08
|
||||
version: '8.x-1.0-alpha4'
|
||||
# Information added by Drupal.org packaging script on 2017-09-20
|
||||
version: '8.x-1.0-beta1'
|
||||
core: '8.x'
|
||||
project: 'entity'
|
||||
datestamp: 1481194986
|
||||
datestamp: 1505895847
|
||||
|
||||
@@ -4,3 +4,85 @@
|
||||
* @file
|
||||
* Provides expanded entity APIs.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\entity\BundlePlugin\BundlePluginHandler;
|
||||
|
||||
/**
|
||||
* Gets the entity types which use bundle plugins.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityTypeInterface[]
|
||||
* The entity types.
|
||||
*/
|
||||
function entity_get_bundle_plugin_entity_types() {
|
||||
$entity_types = \Drupal::entityTypeManager()->getDefinitions();
|
||||
$entity_types = array_filter($entity_types, function (EntityTypeInterface $entity_type) {
|
||||
return $entity_type->hasHandlerClass('bundle_plugin');
|
||||
});
|
||||
|
||||
return $entity_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_type_build().
|
||||
*/
|
||||
function entity_entity_type_build(array &$entity_types) {
|
||||
foreach ($entity_types as $entity_type) {
|
||||
if ($entity_type->get('bundle_plugin_type')) {
|
||||
$entity_type->setHandlerClass('bundle_plugin', BundlePluginHandler::class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_bundle_info().
|
||||
*/
|
||||
function entity_entity_bundle_info() {
|
||||
$bundles = [];
|
||||
foreach (entity_get_bundle_plugin_entity_types() as $entity_type) {
|
||||
/** @var \Drupal\entity\BundlePlugin\BundlePluginHandler $bundle_handler */
|
||||
$bundle_handler = \Drupal::entityTypeManager()->getHandler($entity_type->id(), 'bundle_plugin');
|
||||
$bundles[$entity_type->id()] = $bundle_handler->getBundleInfo();
|
||||
}
|
||||
return $bundles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_field_storage_info().
|
||||
*/
|
||||
function entity_entity_field_storage_info(EntityTypeInterface $entity_type) {
|
||||
if ($entity_type->hasHandlerClass('bundle_plugin')) {
|
||||
/** @var \Drupal\entity\BundlePlugin\BundlePluginHandler $bundle_handler */
|
||||
$bundle_handler = \Drupal::entityTypeManager()->getHandler($entity_type->id(), 'bundle_plugin');
|
||||
return $bundle_handler->getFieldStorageDefinitions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_bundle_field_info().
|
||||
*/
|
||||
function entity_entity_bundle_field_info(EntityTypeInterface $entity_type, $bundle) {
|
||||
if ($entity_type->hasHandlerClass('bundle_plugin')) {
|
||||
/** @var \Drupal\entity\BundlePlugin\BundlePluginHandler $bundle_handler */
|
||||
$bundle_handler = \Drupal::entityTypeManager()->getHandler($entity_type->id(), 'bundle_plugin');
|
||||
return $bundle_handler->getFieldDefinitions($bundle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_modules_installed().
|
||||
*/
|
||||
function entity_modules_installed($modules) {
|
||||
foreach (entity_get_bundle_plugin_entity_types() as $entity_type) {
|
||||
\Drupal::service('entity.bundle_plugin_installer')->installBundles($entity_type, $modules);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_module_preuninstall().
|
||||
*/
|
||||
function entity_module_preuninstall($module) {
|
||||
foreach (entity_get_bundle_plugin_entity_types() as $entity_type) {
|
||||
\Drupal::service('entity.bundle_plugin_installer')->uninstallBundles($entity_type, [$module]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
services:
|
||||
access_checker.entity_revision:
|
||||
class: \Drupal\entity\Access\EntityRevisionRouteAccessChecker
|
||||
arguments: ['@entity_type.manager', '@request_stack']
|
||||
arguments: ['@entity_type.manager', '@current_route_match']
|
||||
tags:
|
||||
- { name: access_check, applies_to: _entity_access_revision }
|
||||
|
||||
entity.bundle_plugin_installer:
|
||||
class: Drupal\entity\BundlePlugin\BundlePluginInstaller
|
||||
arguments: ['@entity_type.manager', '@entity_bundle.listener', '@field_storage_definition.listener', '@field_definition.listener']
|
||||
|
||||
entity.bundle_plugin.uninstall_validator:
|
||||
class: \Drupal\entity\BundlePlugin\BundlePluginUninstallValidator
|
||||
tags:
|
||||
- { name: module_install.uninstall_validator }
|
||||
arguments: ['@entity_type.manager', '@string_translation']
|
||||
|
||||
@@ -9,7 +9,7 @@ use Drupal\Core\Entity\EntityTypeInterface;
|
||||
function entity_views_data() {
|
||||
$entity_types = \Drupal::entityTypeManager()->getDefinitions();
|
||||
$entity_types = array_filter($entity_types, function (EntityTypeInterface $entity_type) {
|
||||
return $entity_type->isSubclassOf(ContentEntityInterface::class);
|
||||
return $entity_type->entityClassImplements(ContentEntityInterface::class);
|
||||
});
|
||||
|
||||
$data = [];
|
||||
|
||||
+19
-16
@@ -7,9 +7,8 @@ use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Routing\Access\AccessInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
@@ -30,42 +29,42 @@ class EntityRevisionRouteAccessChecker implements AccessInterface {
|
||||
protected $accessCache = array();
|
||||
|
||||
/**
|
||||
* The request stack.
|
||||
* The currently active route match object.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\RequestStack
|
||||
* @var \Drupal\Core\Routing\RouteMatchInterface
|
||||
*/
|
||||
protected $requestStack;
|
||||
protected $routeMatch;
|
||||
|
||||
/**
|
||||
* Creates a new EntityRevisionRouteAccessChecker instance.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity manager.
|
||||
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
|
||||
* The request stack.
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The currently active route match object.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager, RequestStack $request_stack) {
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager, RouteMatchInterface $route_match) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->requestStack = $request_stack;
|
||||
$this->routeMatch = $route_match;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function access(Route $route, AccountInterface $account, Request $request = NULL) {
|
||||
if (empty($request)) {
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
public function access(Route $route, AccountInterface $account, RouteMatchInterface $route_match = NULL) {
|
||||
if (empty($route_match)) {
|
||||
$route_match = $this->routeMatch;
|
||||
}
|
||||
|
||||
$operation = $route->getRequirement('_entity_access_revision');
|
||||
list(, $operation) = explode('.', $operation, 2);
|
||||
list($entity_type_id, $operation) = explode('.', $operation, 2);
|
||||
|
||||
if ($operation === 'list') {
|
||||
$_entity = $request->attributes->get('_entity', $request->attributes->get($route->getOption('entity_type_id')));
|
||||
$_entity = $route_match->getParameter($entity_type_id);
|
||||
return AccessResult::allowedIf($this->checkAccess($_entity, $account, $operation))->cachePerPermissions();
|
||||
}
|
||||
else {
|
||||
$_entity_revision = $request->attributes->get('_entity_revision');
|
||||
$_entity_revision = $route_match->getParameter($entity_type_id . '_revision');
|
||||
return AccessResult::allowedIf($_entity_revision && $this->checkAccess($_entity_revision, $account, $operation))->cachePerPermissions();
|
||||
}
|
||||
}
|
||||
@@ -104,8 +103,10 @@ class EntityRevisionRouteAccessChecker implements AccessInterface {
|
||||
$cid = $entity->getRevisionId() . ':' . $langcode . ':' . $account->id() . ':' . $operation;
|
||||
|
||||
if (!isset($this->accessCache[$cid])) {
|
||||
$admin_permission = $entity_type->getAdminPermission();
|
||||
|
||||
// Perform basic permission checks first.
|
||||
if (!$account->hasPermission($map[$operation]) && !$account->hasPermission($type_map[$operation]) && !$account->hasPermission('administer nodes')) {
|
||||
if (!$account->hasPermission($map[$operation]) && !$account->hasPermission($type_map[$operation]) && ($admin_permission && !$account->hasPermission($admin_permission))) {
|
||||
$this->accessCache[$cid] = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
@@ -114,6 +115,8 @@ class EntityRevisionRouteAccessChecker implements AccessInterface {
|
||||
$this->accessCache[$cid] = TRUE;
|
||||
}
|
||||
else {
|
||||
// Entity access handlers are generally not aware of the "list" operation.
|
||||
$operation = $operation == 'list' ? 'view' : $operation;
|
||||
// First check the access to the default revision and finally, if the
|
||||
// node passed in is not the default revision then access to that, too.
|
||||
$this->accessCache[$cid] = $entity_access->access($entity_storage->load($entity->id()), $operation, $account) && ($entity->isDefaultRevision() || $entity_access->access($entity, $operation, $account));
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity;
|
||||
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
|
||||
/**
|
||||
* Provides a field definition class for bundle fields.
|
||||
*
|
||||
* Core currently doesn't provide one, the hook_entity_bundle_field_info()
|
||||
* example uses BaseFieldDefinition, which is wrong. Tracked in #2346347.
|
||||
*
|
||||
* Note that this class implements both FieldStorageDefinitionInterface and
|
||||
* FieldDefinitionInterface. This is a simplification for DX reasons,
|
||||
* allowing code to return just the bundle definitions instead of having to
|
||||
* return both storage definitions and bundle definitions.
|
||||
*/
|
||||
class BundleFieldDefinition extends BaseFieldDefinition {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isBaseField() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity\BundlePlugin;
|
||||
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
class BundlePluginHandler implements BundlePluginHandlerInterface {
|
||||
|
||||
/**
|
||||
* The entity type.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeInterface
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
/**
|
||||
* The bundle plugin manager.
|
||||
*
|
||||
* @var \Drupal\Component\Plugin\PluginManagerInterface
|
||||
*/
|
||||
protected $pluginManager;
|
||||
|
||||
/**
|
||||
* Constructs a new BundlePluginHandler object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
* @param \Drupal\Component\Plugin\PluginManagerInterface $plugin_manager
|
||||
* The bundle plugin manager.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, PluginManagerInterface $plugin_manager) {
|
||||
$this->entityType = $entity_type;
|
||||
$this->pluginManager = $plugin_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('plugin.manager.' . $entity_type->get('bundle_plugin_type'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBundleInfo() {
|
||||
$bundles = [];
|
||||
foreach ($this->pluginManager->getDefinitions() as $plugin_id => $definition) {
|
||||
$bundles[$plugin_id] = [
|
||||
'label' => $definition['label'],
|
||||
'description' => isset($definition['description']) ? $definition['description'] : '',
|
||||
'translatable' => $this->entityType->isTranslatable(),
|
||||
'provider' => $definition['provider'],
|
||||
];
|
||||
}
|
||||
return $bundles;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldStorageDefinitions() {
|
||||
$definitions = [];
|
||||
foreach (array_keys($this->pluginManager->getDefinitions()) as $plugin_id) {
|
||||
/** @var \Drupal\entity\BundlePlugin\BundlePluginInterface $plugin */
|
||||
$plugin = $this->pluginManager->createInstance($plugin_id);
|
||||
$definitions += $plugin->buildFieldDefinitions();
|
||||
}
|
||||
// Ensure the presence of required keys which aren't set by the plugin.
|
||||
foreach ($definitions as $field_name => $definition) {
|
||||
$definition->setName($field_name);
|
||||
$definition->setTargetEntityTypeId($this->entityType->id());
|
||||
$definitions[$field_name] = $definition;
|
||||
}
|
||||
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldDefinitions($bundle) {
|
||||
/** @var \Drupal\entity\BundlePlugin\BundlePluginInterface $plugin */
|
||||
$plugin = $this->pluginManager->createInstance($bundle);
|
||||
$definitions = $plugin->buildFieldDefinitions();
|
||||
// Ensure the presence of required keys which aren't set by the plugin.
|
||||
foreach ($definitions as $field_name => $definition) {
|
||||
$definition->setName($field_name);
|
||||
$definition->setTargetEntityTypeId($this->entityType->id());
|
||||
$definition->setTargetBundle($bundle);
|
||||
$definitions[$field_name] = $definition;
|
||||
}
|
||||
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity\BundlePlugin;
|
||||
|
||||
use Drupal\Core\Entity\EntityHandlerInterface;
|
||||
|
||||
/**
|
||||
* Handles plugin-provided bundles.
|
||||
*/
|
||||
interface BundlePluginHandlerInterface extends EntityHandlerInterface {
|
||||
|
||||
/**
|
||||
* Gets the bundle info.
|
||||
*
|
||||
* @return array
|
||||
* An array of bundle information keyed by the bundle name.
|
||||
* The format expected by hook_entity_bundle_info().
|
||||
*/
|
||||
public function getBundleInfo();
|
||||
|
||||
/**
|
||||
* Gets the field storage definitions.
|
||||
*/
|
||||
public function getFieldStorageDefinitions();
|
||||
|
||||
/**
|
||||
* Gets the field definitions for a specific bundle.
|
||||
*
|
||||
* @param string $bundle
|
||||
* The bundle name.
|
||||
*
|
||||
* @return \Drupal\entity\BundleFieldDefinition[]
|
||||
* An array of bundle field definitions, keyed by field name.
|
||||
*/
|
||||
public function getFieldDefinitions($bundle);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity\BundlePlugin;
|
||||
|
||||
use Drupal\Core\Entity\EntityBundleListenerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionListenerInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
|
||||
|
||||
class BundlePluginInstaller implements BundlePluginInstallerInterface {
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The entity bundle listener.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityBundleListenerInterface
|
||||
*/
|
||||
protected $entityBundleListener;
|
||||
|
||||
/**
|
||||
* The field storage definition listener.
|
||||
*
|
||||
* @var \Drupal\Core\Field\FieldStorageDefinitionListenerInterface
|
||||
*/
|
||||
protected $fieldStorageDefinitionListener;
|
||||
|
||||
/**
|
||||
* The field definition listener.
|
||||
*
|
||||
* @var \Drupal\Core\Field\FieldDefinitionListenerInterface
|
||||
*/
|
||||
protected $fieldDefinitionListener;
|
||||
|
||||
/**
|
||||
* Constructs a new BundlePluginInstaller object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
* @param \Drupal\Core\Entity\EntityBundleListenerInterface $entity_bundle_listener
|
||||
* The entity bundle listener.
|
||||
* @param \Drupal\Core\Field\FieldStorageDefinitionListenerInterface $field_storage_definition_listener
|
||||
* The field storage definition listener.
|
||||
* @param \Drupal\Core\Field\FieldDefinitionListenerInterface $field_definition_listener
|
||||
* The field definition listener.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityBundleListenerInterface $entity_bundle_listener, FieldStorageDefinitionListenerInterface $field_storage_definition_listener, FieldDefinitionListenerInterface $field_definition_listener) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->entityBundleListener = $entity_bundle_listener;
|
||||
$this->fieldStorageDefinitionListener = $field_storage_definition_listener;
|
||||
$this->fieldDefinitionListener = $field_definition_listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function installBundles(EntityTypeInterface $entity_type, array $modules) {
|
||||
$bundle_handler = $this->entityTypeManager->getHandler($entity_type->id(), 'bundle_plugin');
|
||||
$bundles = array_filter($bundle_handler->getBundleInfo(), function ($bundle_info) use ($modules) {
|
||||
return in_array($bundle_info['provider'], $modules, TRUE);
|
||||
});
|
||||
foreach (array_keys($bundles) as $bundle) {
|
||||
$this->entityBundleListener->onBundleCreate($bundle, $entity_type->id());
|
||||
foreach ($bundle_handler->getFieldDefinitions($bundle) as $definition) {
|
||||
$this->fieldStorageDefinitionListener->onFieldStorageDefinitionCreate($definition);
|
||||
$this->fieldDefinitionListener->onFieldDefinitionCreate($definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function uninstallBundles(EntityTypeInterface $entity_type, array $modules) {
|
||||
$bundle_handler = $this->entityTypeManager->getHandler($entity_type->id(), 'bundle_plugin');
|
||||
$bundles = array_filter($bundle_handler->getBundleInfo(), function ($bundle_info) use ($modules) {
|
||||
return in_array($bundle_info['provider'], $modules, TRUE);
|
||||
});
|
||||
foreach (array_keys($bundles) as $bundle) {
|
||||
$this->entityBundleListener->onBundleDelete($bundle, $entity_type->id());
|
||||
foreach ($bundle_handler->getFieldDefinitions($bundle) as $definition) {
|
||||
$this->fieldDefinitionListener->onFieldDefinitionDelete($definition);
|
||||
$this->fieldStorageDefinitionListener->onFieldStorageDefinitionDelete($definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity\BundlePlugin;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
|
||||
/**
|
||||
* Installs and uninstalls bundle plugins.
|
||||
*
|
||||
* Ensures that the fields provided by the bundle plugins are created/deleted.
|
||||
*/
|
||||
interface BundlePluginInstallerInterface {
|
||||
|
||||
/**
|
||||
* Installs the bundle plugins provided by the specified modules.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
* @param array $modules
|
||||
* The modules.
|
||||
*/
|
||||
public function installBundles(EntityTypeInterface $entity_type, array $modules);
|
||||
|
||||
/**
|
||||
* Uninstalls the bundle plugins provided by the specified modules.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
* @param array $modules
|
||||
* The modules.
|
||||
*/
|
||||
public function uninstallBundles(EntityTypeInterface $entity_type, array $modules);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity\BundlePlugin;
|
||||
|
||||
use Drupal\Component\Plugin\PluginInspectionInterface;
|
||||
|
||||
/**
|
||||
* Interface for plugins which act as entity bundles.
|
||||
*/
|
||||
interface BundlePluginInterface extends PluginInspectionInterface {
|
||||
|
||||
/**
|
||||
* Builds the field definitions for entities of this bundle.
|
||||
*
|
||||
* Important:
|
||||
* Field names must be unique across all bundles.
|
||||
* It is recommended to prefix them with the bundle name (plugin ID).
|
||||
*
|
||||
* @return \Drupal\entity\BundleFieldDefinition[]
|
||||
* An array of bundle field definitions, keyed by field name.
|
||||
*/
|
||||
public function buildFieldDefinitions();
|
||||
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity\BundlePlugin;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Extension\ModuleUninstallValidatorInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
|
||||
/**
|
||||
* Prevents uninstalling modules with bundle plugins in case of found data.
|
||||
*/
|
||||
class BundlePluginUninstallValidator implements ModuleUninstallValidatorInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs the object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
|
||||
* The string translation service.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->stringTranslation = $string_translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($module) {
|
||||
$reasons = [];
|
||||
|
||||
foreach (entity_get_bundle_plugin_entity_types() as $entity_type) {
|
||||
/** @var \Drupal\entity\BundlePlugin\BundlePluginHandler $bundle_handler */
|
||||
$bundle_handler = $this->entityTypeManager->getHandler($entity_type->id(), 'bundle_plugin');
|
||||
$bundles = $bundle_handler->getBundleInfo();
|
||||
|
||||
// We find all bundles which have to be removed due to the uninstallation.
|
||||
$bundles_filtered_by_module = array_filter($bundles, function ($bundle_info) use ($module) {
|
||||
return $module === $bundle_info['provider'];
|
||||
});
|
||||
|
||||
if (!empty($bundles_filtered_by_module)) {
|
||||
$bundle_keys_with_content = array_filter(array_keys($bundles_filtered_by_module), function ($bundle) use ($entity_type) {
|
||||
$result = $this->entityTypeManager->getStorage($entity_type->id())->getQuery()
|
||||
->condition($entity_type->getKey('bundle'), $bundle)
|
||||
->range(0, 1)
|
||||
->execute();
|
||||
return !empty($result);
|
||||
});
|
||||
|
||||
$bundles_with_content = array_intersect_key($bundles_filtered_by_module, array_flip($bundle_keys_with_content));
|
||||
|
||||
foreach ($bundles_with_content as $bundle) {
|
||||
$reasons[] = $this->t('There is data for the bundle @bundle on the entity type @entity_type. Please remove all content before uninstalling the module.', [
|
||||
'@bundle' => $bundle['label'],
|
||||
'@entity_type' => $entity_type->getLabel(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $reasons;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,7 +11,6 @@ use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Entity\RevisionLogInterface;
|
||||
use Drupal\user\EntityOwnerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,16 +5,30 @@ namespace Drupal\entity;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityAccessControlHandler as CoreEntityAccessControlHandler;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityPublishedInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\user\EntityOwnerInterface;
|
||||
|
||||
/**
|
||||
* Controls access based on the generic entity permissions.
|
||||
*
|
||||
* @see \Drupal\entity\EntityPermissionProvider
|
||||
* @see \Drupal\entity\UncacheableEntityPermissionProvider
|
||||
*/
|
||||
class EntityAccessControlHandler extends CoreEntityAccessControlHandler {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type) {
|
||||
parent::__construct($entity_type);
|
||||
|
||||
if (!$entity_type->hasHandlerClass('permission_provider') || !is_a($entity_type->getHandlerClass('permission_provider'), EntityPermissionProvider::class, TRUE)) {
|
||||
throw new \Exception("This entity access control handler requires the entity permissions provider: {EntityPermissionProvider::class}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -51,10 +65,18 @@ class EntityAccessControlHandler extends CoreEntityAccessControlHandler {
|
||||
* The access result.
|
||||
*/
|
||||
protected function checkEntityPermissions(EntityInterface $entity, $operation, AccountInterface $account) {
|
||||
return AccessResult::allowedIfHasPermissions($account, [
|
||||
"$operation {$entity->getEntityTypeId()}",
|
||||
"$operation {$entity->bundle()} {$entity->getEntityTypeId()}",
|
||||
], 'OR');
|
||||
if ($operation === 'view') {
|
||||
$permissions = [
|
||||
"view {$entity->getEntityTypeId()}"
|
||||
];
|
||||
}
|
||||
else {
|
||||
$permissions = [
|
||||
"$operation {$entity->getEntityTypeId()}",
|
||||
"$operation {$entity->bundle()} {$entity->getEntityTypeId()}",
|
||||
];
|
||||
}
|
||||
return AccessResult::allowedIfHasPermissions($account, $permissions, 'OR');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,23 +94,39 @@ class EntityAccessControlHandler extends CoreEntityAccessControlHandler {
|
||||
* The access result.
|
||||
*/
|
||||
protected function checkEntityOwnerPermissions(EntityInterface $entity, $operation, AccountInterface $account) {
|
||||
/** @var \Drupal\Core\Entity\EntityInterface|\Drupal\user\EntityOwnerInterface $entity */
|
||||
if (($account->id() == $entity->getOwnerId())) {
|
||||
$result = AccessResult::allowedIfHasPermissions($account, [
|
||||
"$operation own {$entity->getEntityTypeId()}",
|
||||
"$operation any {$entity->getEntityTypeId()}",
|
||||
"$operation own {$entity->bundle()} {$entity->getEntityTypeId()}",
|
||||
"$operation any {$entity->bundle()} {$entity->getEntityTypeId()}",
|
||||
], 'OR');
|
||||
if ($operation === 'view') {
|
||||
if ($entity instanceof EntityPublishedInterface && !$entity->isPublished()) {
|
||||
if (($account->id() == $entity->getOwnerId())) {
|
||||
$permissions = [
|
||||
"view own unpublished {$entity->getEntityTypeId()}",
|
||||
];
|
||||
return AccessResult::allowedIfHasPermissions($account, $permissions)->cachePerUser();
|
||||
}
|
||||
return AccessResult::neutral()->cachePerUser();
|
||||
}
|
||||
else {
|
||||
return AccessResult::allowedIfHasPermissions($account, [
|
||||
"view {$entity->getEntityTypeId()}",
|
||||
]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$result = AccessResult::allowedIfHasPermissions($account, [
|
||||
"$operation any {$entity->getEntityTypeId()}",
|
||||
"$operation any {$entity->bundle()} {$entity->getEntityTypeId()}",
|
||||
], 'OR');
|
||||
if (($account->id() == $entity->getOwnerId())) {
|
||||
$result = AccessResult::allowedIfHasPermissions($account, [
|
||||
"$operation own {$entity->getEntityTypeId()}",
|
||||
"$operation any {$entity->getEntityTypeId()}",
|
||||
"$operation own {$entity->bundle()} {$entity->getEntityTypeId()}",
|
||||
"$operation any {$entity->bundle()} {$entity->getEntityTypeId()}",
|
||||
], 'OR');
|
||||
}
|
||||
else {
|
||||
$result = AccessResult::allowedIfHasPermissions($account, [
|
||||
"$operation any {$entity->getEntityTypeId()}",
|
||||
"$operation any {$entity->bundle()} {$entity->getEntityTypeId()}",
|
||||
], 'OR');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $result->cachePerUser();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace Drupal\entity;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityHandlerInterface;
|
||||
use Drupal\Core\Entity\EntityPublishedInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
@@ -11,10 +11,21 @@ use Drupal\user\EntityOwnerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides generic entity permissions.
|
||||
* Provides generic entity permissions which are still cacheable.
|
||||
*
|
||||
* Supports both entity_type and bundle granularities.
|
||||
* Supports entity ownership (own/any permissions).
|
||||
* This includes:
|
||||
*
|
||||
* - administer $entity_type
|
||||
* - access $entity_type overview
|
||||
* - view $entity_type
|
||||
* - view own unpublished $entity_type
|
||||
* - update (own|any) ($bundle) $entity_type
|
||||
* - delete (own|any) ($bundle) $entity_type
|
||||
* - create $bundle $entity_type
|
||||
*
|
||||
* This class does not support "view own ($bundle) $entity_type", because this
|
||||
* results in caching per user. If you need this use case, please use
|
||||
* \Drupal\entity\UncacheableEntityPermissionProvider instead.
|
||||
*
|
||||
* Intended for content entity types, since config entity types usually rely
|
||||
* on a single "administer" permission.
|
||||
@@ -29,218 +40,26 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
* @see \Drupal\entity\EntityAccessControlHandler
|
||||
* @see \Drupal\entity\EntityPermissions
|
||||
*/
|
||||
class EntityPermissionProvider implements EntityPermissionProviderInterface, EntityHandlerInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The entity type bundle info.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* Constructs a new EntityPermissionProvider object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
|
||||
* The entity type bundle info.
|
||||
*/
|
||||
public function __construct(EntityTypeBundleInfoInterface $entity_type_bundle_info) {
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$container->get('entity_type.bundle.info')
|
||||
);
|
||||
}
|
||||
class EntityPermissionProvider extends EntityPermissionProviderBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildPermissions(EntityTypeInterface $entity_type) {
|
||||
$entity_type_id = $entity_type->id();
|
||||
$has_owner = $entity_type->isSubclassOf(EntityOwnerInterface::class);
|
||||
$singular_label = $entity_type->getSingularLabel();
|
||||
$plural_label = $entity_type->getPluralLabel();
|
||||
|
||||
$permissions = [];
|
||||
$permissions["administer {$entity_type_id}"] = [
|
||||
'title' => $this->t('Administer @type', ['@type' => $plural_label]),
|
||||
'restrict access' => TRUE,
|
||||
];
|
||||
$permissions["access {$entity_type_id} overview"] = [
|
||||
'title' => $this->t('Access the @type overview page', ['@type' => $plural_label]),
|
||||
];
|
||||
$permissions = parent::buildPermissions($entity_type);
|
||||
|
||||
// View permissions are the same for both granularities.
|
||||
if ($has_owner) {
|
||||
$permissions["view any {$entity_type_id}"] = [
|
||||
'title' => $this->t('View any @type', [
|
||||
'@type' => $singular_label,
|
||||
]),
|
||||
];
|
||||
$permissions["view own {$entity_type_id}"] = [
|
||||
'title' => $this->t('View own @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
else {
|
||||
$permissions["view {$entity_type_id}"] = [
|
||||
'title' => $this->t('View @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
// Generate the other permissions based on granularity.
|
||||
if ($entity_type->getPermissionGranularity() == 'entity_type') {
|
||||
$permissions += $this->buildEntityTypePermissions($entity_type);
|
||||
}
|
||||
else {
|
||||
$permissions += $this->buildBundlePermissions($entity_type);
|
||||
}
|
||||
|
||||
foreach ($permissions as $name => $permission) {
|
||||
// Permissions are grouped by provider on admin/people/permissions.
|
||||
$permissions[$name]['provider'] = $entity_type->getProvider();
|
||||
// TranslatableMarkup objects don't sort properly.
|
||||
$permissions[$name]['title'] = (string) $permission['title'];
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds permissions for the entity_type granularity.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
*
|
||||
* @return array
|
||||
* The permissions.
|
||||
*/
|
||||
protected function buildEntityTypePermissions(EntityTypeInterface $entity_type) {
|
||||
$entity_type_id = $entity_type->id();
|
||||
$has_owner = $entity_type->isSubclassOf(EntityOwnerInterface::class);
|
||||
$singular_label = $entity_type->getSingularLabel();
|
||||
$plural_label = $entity_type->getPluralLabel();
|
||||
|
||||
$permissions = [];
|
||||
$permissions["create {$entity_type_id}"] = [
|
||||
'title' => $this->t('Create @type', [
|
||||
$permissions["view {$entity_type_id}"] = [
|
||||
'title' => $this->t('View @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
if ($has_owner) {
|
||||
$permissions["update any {$entity_type_id}"] = [
|
||||
'title' => $this->t('Update any @type', [
|
||||
'@type' => $singular_label,
|
||||
]),
|
||||
];
|
||||
$permissions["update own {$entity_type_id}"] = [
|
||||
'title' => $this->t('Update own @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
$permissions["delete any {$entity_type_id}"] = [
|
||||
'title' => $this->t('Delete any @type', [
|
||||
'@type' => $singular_label,
|
||||
]),
|
||||
];
|
||||
$permissions["delete own {$entity_type_id}"] = [
|
||||
'title' => $this->t('Delete own @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
else {
|
||||
$permissions["update {$entity_type_id}"] = [
|
||||
'title' => $this->t('Update @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
$permissions["delete {$entity_type_id}"] = [
|
||||
'title' => $this->t('Delete @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
return $this->processPermissions($permissions, $entity_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds permissions for the bundle granularity.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
*
|
||||
* @return array
|
||||
* The permissions.
|
||||
*/
|
||||
protected function buildBundlePermissions(EntityTypeInterface $entity_type) {
|
||||
$entity_type_id = $entity_type->id();
|
||||
$bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id);
|
||||
$has_owner = $entity_type->isSubclassOf(EntityOwnerInterface::class);
|
||||
$singular_label = $entity_type->getSingularLabel();
|
||||
$plural_label = $entity_type->getPluralLabel();
|
||||
|
||||
$permissions = [];
|
||||
foreach ($bundles as $bundle_name => $bundle_info) {
|
||||
$permissions["create {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: Create @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
|
||||
if ($has_owner) {
|
||||
$permissions["update any {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: Update any @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $singular_label,
|
||||
]),
|
||||
];
|
||||
$permissions["update own {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: Update own @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
$permissions["delete any {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: Delete any @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $singular_label,
|
||||
]),
|
||||
];
|
||||
$permissions["delete own {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: Delete own @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
else {
|
||||
$permissions["update {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: Update @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
$permissions["delete {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: Delete @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity;
|
||||
|
||||
use Drupal\Core\Entity\EntityHandlerInterface;
|
||||
use Drupal\Core\Entity\EntityPublishedInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\user\EntityOwnerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class EntityPermissionProviderBase implements EntityPermissionProviderInterface, EntityHandlerInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The entity type bundle info.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
|
||||
*/
|
||||
protected $entityTypeBundleInfo;
|
||||
|
||||
/**
|
||||
* Constructs a new EntityPermissionProvider object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
|
||||
* The entity type bundle info.
|
||||
*/
|
||||
public function __construct(EntityTypeBundleInfoInterface $entity_type_bundle_info) {
|
||||
$this->entityTypeBundleInfo = $entity_type_bundle_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$container->get('entity_type.bundle.info')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildPermissions(EntityTypeInterface $entity_type) {
|
||||
$entity_type_id = $entity_type->id();
|
||||
$has_owner = $entity_type->entityClassImplements(EntityOwnerInterface::class);
|
||||
$plural_label = $entity_type->getPluralLabel();
|
||||
|
||||
$permissions = [];
|
||||
$permissions["administer {$entity_type_id}"] = [
|
||||
'title' => $this->t('Administer @type', ['@type' => $plural_label]),
|
||||
'restrict access' => TRUE,
|
||||
];
|
||||
$permissions["access {$entity_type_id} overview"] = [
|
||||
'title' => $this->t('Access the @type overview page', ['@type' => $plural_label]),
|
||||
];
|
||||
if ($has_owner && $entity_type->entityClassImplements(EntityPublishedInterface::class)) {
|
||||
$permissions["view own unpublished {$entity_type_id}"] = [
|
||||
'title' => $this->t('View own unpublished @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
// Generate the other permissions based on granularity.
|
||||
if ($entity_type->getPermissionGranularity() === 'entity_type') {
|
||||
$permissions += $this->buildEntityTypePermissions($entity_type);
|
||||
}
|
||||
else {
|
||||
$permissions += $this->buildBundlePermissions($entity_type);
|
||||
}
|
||||
|
||||
return $this->processPermissions($permissions, $entity_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the provider and converts the titles to strings to allow sorting.
|
||||
*
|
||||
* @param array $permissions
|
||||
* The array of permissions
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
*
|
||||
* @return array
|
||||
* An array of processed permissions.
|
||||
*/
|
||||
protected function processPermissions(array $permissions, EntityTypeInterface $entity_type) {
|
||||
foreach ($permissions as $name => $permission) {
|
||||
// Permissions are grouped by provider on admin/people/permissions.
|
||||
$permissions[$name]['provider'] = $entity_type->getProvider();
|
||||
// TranslatableMarkup objects don't sort properly.
|
||||
$permissions[$name]['title'] = (string) $permission['title'];
|
||||
}
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds permissions for the entity_type granularity.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
*
|
||||
* @return array
|
||||
* The permissions.
|
||||
*/
|
||||
protected function buildEntityTypePermissions(EntityTypeInterface $entity_type) {
|
||||
$entity_type_id = $entity_type->id();
|
||||
$has_owner = $entity_type->entityClassImplements(EntityOwnerInterface::class);
|
||||
$singular_label = $entity_type->getSingularLabel();
|
||||
$plural_label = $entity_type->getPluralLabel();
|
||||
|
||||
$permissions = [];
|
||||
$permissions["create {$entity_type_id}"] = [
|
||||
'title' => $this->t('Create @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
if ($has_owner) {
|
||||
$permissions["update any {$entity_type_id}"] = [
|
||||
'title' => $this->t('Update any @type', [
|
||||
'@type' => $singular_label,
|
||||
]),
|
||||
];
|
||||
$permissions["update own {$entity_type_id}"] = [
|
||||
'title' => $this->t('Update own @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
$permissions["delete any {$entity_type_id}"] = [
|
||||
'title' => $this->t('Delete any @type', [
|
||||
'@type' => $singular_label,
|
||||
]),
|
||||
];
|
||||
$permissions["delete own {$entity_type_id}"] = [
|
||||
'title' => $this->t('Delete own @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
else {
|
||||
$permissions["update {$entity_type_id}"] = [
|
||||
'title' => $this->t('Update @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
$permissions["delete {$entity_type_id}"] = [
|
||||
'title' => $this->t('Delete @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds permissions for the bundle granularity.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
*
|
||||
* @return array
|
||||
* The permissions.
|
||||
*/
|
||||
protected function buildBundlePermissions(EntityTypeInterface $entity_type) {
|
||||
$entity_type_id = $entity_type->id();
|
||||
$bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id);
|
||||
$has_owner = $entity_type->entityClassImplements(EntityOwnerInterface::class);
|
||||
$singular_label = $entity_type->getSingularLabel();
|
||||
$plural_label = $entity_type->getPluralLabel();
|
||||
|
||||
$permissions = [];
|
||||
foreach ($bundles as $bundle_name => $bundle_info) {
|
||||
$permissions["create {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: Create @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
|
||||
if ($has_owner) {
|
||||
$permissions["update any {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: Update any @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $singular_label,
|
||||
]),
|
||||
];
|
||||
$permissions["update own {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: Update own @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
$permissions["delete any {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: Delete any @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $singular_label,
|
||||
]),
|
||||
];
|
||||
$permissions["delete own {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: Delete own @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
else {
|
||||
$permissions["update {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: Update @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
$permissions["delete {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: Delete @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,6 +10,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
* Generates entity permissions via their permission providers.
|
||||
*
|
||||
* @see \Drupal\entity\EntityPermissionProvider
|
||||
* @see \Drupal\entity\UncacheableEntityPermissionProvider
|
||||
*/
|
||||
class EntityPermissions implements ContainerInjectionInterface {
|
||||
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ class DeleteActionDeriver extends DeriverBase implements ContainerDeriverInterfa
|
||||
protected function getParticipatingEntityTypes() {
|
||||
$entity_types = $this->entityTypeManager->getDefinitions();
|
||||
$entity_types = array_filter($entity_types, function (EntityTypeInterface $entity_type) {
|
||||
return $entity_type->isSubclassOf(ContentEntityInterface::class) && $entity_type->hasLinkTemplate('delete-multiple-form');
|
||||
return $entity_type->entityClassImplements(ContentEntityInterface::class) && $entity_type->hasLinkTemplate('delete-multiple-form');
|
||||
});
|
||||
|
||||
return $entity_types;
|
||||
|
||||
+3
-2
@@ -3,6 +3,7 @@
|
||||
namespace Drupal\entity\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
@@ -24,7 +25,7 @@ class RevisionsOverviewDeriver extends DeriverBase implements ContainerDeriverIn
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
|
||||
* The entity type manager.
|
||||
*/
|
||||
public function __construct(\Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager) {
|
||||
public function __construct(EntityTypeManagerInterface $entityTypeManager) {
|
||||
$this->entityTypeManager = $entityTypeManager;
|
||||
}
|
||||
|
||||
@@ -55,7 +56,7 @@ class RevisionsOverviewDeriver extends DeriverBase implements ContainerDeriverIn
|
||||
|
||||
$this->derivatives[$entity_type_id] = [
|
||||
'route_name' => "entity.$entity_type_id.version_history",
|
||||
'title' => 'Revisions',
|
||||
'title' => t('Revisions'),
|
||||
'base_route' => "entity.$entity_type_id.canonical",
|
||||
'weight' => 20,
|
||||
] + $base_plugin_definition;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Drupal\entity\Revision;
|
||||
|
||||
use Drupal\Core\Entity\RevisionableContentEntityBase as BaseRevisionableContentEntityBase;
|
||||
use Drupal\Core\Entity\ContentEntityBase;
|
||||
|
||||
/**
|
||||
* Improves the url route handling of core's revisionable content entity base.
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityAccessControlHandler as CoreEntityAccessControlHandler;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityPublishedInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\user\EntityOwnerInterface;
|
||||
|
||||
/**
|
||||
* Controls access based on the uncacheable entity permissions.
|
||||
*
|
||||
* @see \Drupal\entity\UncacheableEntityPermissionProvider
|
||||
*
|
||||
* Note: this access control handler will cause pages to be cached per user.
|
||||
*/
|
||||
class UncacheableEntityAccessControlHandler extends CoreEntityAccessControlHandler {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type) {
|
||||
parent::__construct($entity_type);
|
||||
|
||||
if (!$entity_type->hasHandlerClass('permission_provider') || !is_a($entity_type->getHandlerClass('permission_provider'), UncacheableEntityPermissionProvider::class, TRUE)) {
|
||||
throw new \Exception("This entity access control handler requires the entity permissions provider: {EntityPermissionProvider::class}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
|
||||
$account = $this->prepareUser($account);
|
||||
/** @var \Drupal\Core\Access\AccessResult $result */
|
||||
$result = parent::checkAccess($entity, $operation, $account);
|
||||
|
||||
if ($result->isNeutral()) {
|
||||
if ($entity instanceof EntityOwnerInterface) {
|
||||
$result = $this->checkEntityOwnerPermissions($entity, $operation, $account);
|
||||
}
|
||||
else {
|
||||
$result = $this->checkEntityPermissions($entity, $operation, $account);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that access is evaluated again when the entity changes.
|
||||
return $result->addCacheableDependency($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the entity operation and bundle permissions.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity for which to check access.
|
||||
* @param string $operation
|
||||
* The entity operation. Usually one of 'view', 'view label', 'update' or
|
||||
* 'delete'.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user for which to check access.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
protected function checkEntityPermissions(EntityInterface $entity, $operation, AccountInterface $account) {
|
||||
return AccessResult::allowedIfHasPermissions($account, [
|
||||
"$operation {$entity->getEntityTypeId()}",
|
||||
"$operation {$entity->bundle()} {$entity->getEntityTypeId()}",
|
||||
], 'OR');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the entity operation and bundle permissions, with owners.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity for which to check access.
|
||||
* @param string $operation
|
||||
* The entity operation. Usually one of 'view', 'view label', 'update' or
|
||||
* 'delete'.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The user for which to check access.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
protected function checkEntityOwnerPermissions(EntityInterface $entity, $operation, AccountInterface $account) {
|
||||
/** @var \Drupal\Core\Entity\EntityInterface|\Drupal\user\EntityOwnerInterface $entity */
|
||||
if (($account->id() == $entity->getOwnerId())) {
|
||||
if ($operation === 'view' && $entity instanceof EntityPublishedInterface && !$entity->isPublished()) {
|
||||
$permissions = [
|
||||
"view own unpublished {$entity->getEntityTypeId()}",
|
||||
];
|
||||
}
|
||||
else {
|
||||
$permissions = [
|
||||
"$operation own {$entity->getEntityTypeId()}",
|
||||
"$operation any {$entity->getEntityTypeId()}",
|
||||
"$operation own {$entity->bundle()} {$entity->getEntityTypeId()}",
|
||||
"$operation any {$entity->bundle()} {$entity->getEntityTypeId()}",
|
||||
];
|
||||
}
|
||||
$result = AccessResult::allowedIfHasPermissions($account, $permissions, 'OR');
|
||||
}
|
||||
else {
|
||||
$result = AccessResult::allowedIfHasPermissions($account, [
|
||||
"$operation any {$entity->getEntityTypeId()}",
|
||||
"$operation any {$entity->bundle()} {$entity->getEntityTypeId()}",
|
||||
], 'OR');
|
||||
}
|
||||
|
||||
return $result->cachePerUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
|
||||
$result = parent::checkCreateAccess($account, $context, $entity_bundle);
|
||||
if ($result->isNeutral()) {
|
||||
$permissions = [
|
||||
'administer ' . $this->entityTypeId,
|
||||
'create ' . $this->entityTypeId,
|
||||
];
|
||||
if ($entity_bundle) {
|
||||
$permissions[] = 'create ' . $entity_bundle . ' ' . $this->entityTypeId;
|
||||
}
|
||||
|
||||
$result = AccessResult::allowedIfHasPermissions($account, $permissions, 'OR');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity;
|
||||
|
||||
use Drupal\Core\Entity\EntityHandlerInterface;
|
||||
use Drupal\Core\Entity\EntityPublishedInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\user\EntityOwnerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides generic entity permissions which are cached per user.
|
||||
*
|
||||
* This includes:
|
||||
*
|
||||
* - administer $entity_type
|
||||
* - access $entity_type overview
|
||||
* - view an ($bundle) $entity_type
|
||||
* - view own ($bundle) $entity_type
|
||||
* - view own unpublished $entity_type
|
||||
* - update (own|any) ($bundle) $entity_type
|
||||
* - delete (own|any) ($bundle) $entity_type
|
||||
* - create $bundle $entity_type
|
||||
*
|
||||
* As this class supports "view own ($bundle) $entity_type" it is just cacheable
|
||||
* per user, which might harm performance of sites. Given that please use
|
||||
* \Drupal\entity\EntityPermissionProvider unless you need the feature, or your
|
||||
* entity type is not really user facing (commerce orders for example).
|
||||
*
|
||||
* Intended for content entity types, since config entity types usually rely
|
||||
* on a single "administer" permission.
|
||||
* Example annotation:
|
||||
* @code
|
||||
* handlers = {
|
||||
* "access" = "Drupal\entity\UncacheableEntityAccessControlHandler",
|
||||
* "permission_provider" = "Drupal\entity\UncacheableEntityPermissionProvider",
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @see \Drupal\entity\EntityAccessControlHandler
|
||||
* @see \Drupal\entity\EntityPermissions
|
||||
*/
|
||||
class UncacheableEntityPermissionProvider extends EntityPermissionProviderBase {
|
||||
|
||||
/**
|
||||
* Builds permissions for the entity_type granularity.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
*
|
||||
* @return array
|
||||
* The permissions.
|
||||
*/
|
||||
protected function buildEntityTypePermissions(EntityTypeInterface $entity_type) {
|
||||
$permissions = parent::buildEntityTypePermissions($entity_type);
|
||||
|
||||
$entity_type_id = $entity_type->id();
|
||||
$has_owner = $entity_type->entityClassImplements(EntityOwnerInterface::class);
|
||||
$plural_label = $entity_type->getPluralLabel();
|
||||
|
||||
if ($has_owner) {
|
||||
$permissions["view any {$entity_type_id}"] = [
|
||||
'title' => $this->t('View any @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
$permissions["view own {$entity_type_id}"] = [
|
||||
'title' => $this->t('View own @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
else {
|
||||
$permissions["view any {$entity_type_id}"] = [
|
||||
'title' => $this->t('View any @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds permissions for the bundle granularity.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
*
|
||||
* @return array
|
||||
* The permissions.
|
||||
*/
|
||||
protected function buildBundlePermissions(EntityTypeInterface $entity_type) {
|
||||
$permissions = parent::buildBundlePermissions($entity_type);
|
||||
$entity_type_id = $entity_type->id();
|
||||
$bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id);
|
||||
$has_owner = $entity_type->entityClassImplements(EntityOwnerInterface::class);
|
||||
$plural_label = $entity_type->getPluralLabel();
|
||||
|
||||
$permissions["view any {$entity_type_id}"] = [
|
||||
'title' => $this->t('View any @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
if ($has_owner) {
|
||||
$permissions["view own {$entity_type_id}"] = [
|
||||
'title' => $this->t('View own @type', [
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($bundles as $bundle_name => $bundle_info) {
|
||||
if ($has_owner) {
|
||||
$permissions["view any {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: View any @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
$permissions["view own {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: View own @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
else {
|
||||
$permissions["view any {$bundle_name} {$entity_type_id}"] = [
|
||||
'title' => $this->t('@bundle: View any @type', [
|
||||
'@bundle' => $bundle_info['label'],
|
||||
'@type' => $plural_label,
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
name: 'Entity bundle plugin examples test'
|
||||
type: module
|
||||
description: 'Module for testing bundle plugins.'
|
||||
package: Testing
|
||||
# core: 8.x
|
||||
dependencies:
|
||||
- entity
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-09-20
|
||||
version: '8.x-1.0-beta1'
|
||||
core: '8.x'
|
||||
project: 'entity'
|
||||
datestamp: 1505895847
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity_module_bundle_plugin_examples_test\Plugin\BundlePluginTest;
|
||||
|
||||
use Drupal\entity\BundleFieldDefinition;
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
use Drupal\entity_module_bundle_plugin_test\Plugin\BundlePluginTest\BundlePluginTestInterface;
|
||||
|
||||
/**
|
||||
* Provides the second bundle plugin.
|
||||
*
|
||||
* @BundlePluginTest(
|
||||
* id = "second",
|
||||
* label = @Translation("Second"),
|
||||
* )
|
||||
*/
|
||||
class Second extends PluginBase implements BundlePluginTestInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildFieldDefinitions() {
|
||||
$fields = [];
|
||||
$fields['second_mail'] = BundleFieldDefinition::create('email')
|
||||
->setLabel(t('Email'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
name: 'Entity bundle plugin test'
|
||||
type: module
|
||||
description: 'Module for testing bundle plugins.'
|
||||
package: Testing
|
||||
# core: 8.x
|
||||
dependencies:
|
||||
- entity
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-09-20
|
||||
version: '8.x-1.0-beta1'
|
||||
core: '8.x'
|
||||
project: 'entity'
|
||||
datestamp: 1505895847
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
services:
|
||||
plugin.manager.bundle_plugin_test:
|
||||
class: Drupal\entity_module_bundle_plugin_test\BundlePluginTestManager
|
||||
parent: default_plugin_manager
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity_module_bundle_plugin_test\Annotation;
|
||||
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
|
||||
/**
|
||||
* Defines the BundlePluginTest annotation object.
|
||||
*
|
||||
* Plugin namespace: Plugin\BundlePluginTest.
|
||||
*
|
||||
* @see plugin_api
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
class BundlePluginTest extends Plugin {
|
||||
|
||||
/**
|
||||
* The plugin ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* The plugin label.
|
||||
*
|
||||
* @ingroup plugin_translatable
|
||||
*
|
||||
* @var \Drupal\Core\Annotation\Translation
|
||||
*/
|
||||
public $label;
|
||||
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity_module_bundle_plugin_test;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
|
||||
/**
|
||||
* Manages discovery and instantiation of BundlePluginTest plugins.
|
||||
*
|
||||
* @see \Drupal\entity_module_bundle_plugin_test\Annotation\BundlePluginTest
|
||||
* @see plugin_api
|
||||
*/
|
||||
class BundlePluginTestManager extends DefaultPluginManager {
|
||||
|
||||
/**
|
||||
* Constructs a new BundlePluginTestManager object.
|
||||
*
|
||||
* @param \Traversable $namespaces
|
||||
* An object that implements \Traversable which contains the root paths
|
||||
* keyed by the corresponding namespace to look for plugin implementations.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||
* The cache backend.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
*/
|
||||
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
|
||||
parent::__construct('Plugin/BundlePluginTest', $namespaces, $module_handler, 'Drupal\entity_module_bundle_plugin_test\Plugin\BundlePluginTest\BundlePluginTestInterface', 'Drupal\entity_module_bundle_plugin_test\Annotation\BundlePluginTest');
|
||||
|
||||
$this->alterInfo('bundle_plugin_test_info');
|
||||
$this->setCacheBackend($cache_backend, 'bundle_plugin_test_plugins');
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity_module_bundle_plugin_test\Entity;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityBase;
|
||||
|
||||
/**
|
||||
* Defines the 'entity_test_bundle_plugin' entity class.
|
||||
*
|
||||
* @ContentEntityType(
|
||||
* id = "entity_test_bundle_plugin",
|
||||
* label = @Translation("Entity test bundle plugin"),
|
||||
* bundle_label = @Translation("Bundle Plugin Test"),
|
||||
* bundle_plugin_type = "bundle_plugin_test",
|
||||
* base_table = "entity_test_bundle_plugin",
|
||||
* admin_permission = "administer content",
|
||||
* fieldable = TRUE,
|
||||
* entity_keys = {
|
||||
* "id" = "method_id",
|
||||
* "uuid" = "uuid",
|
||||
* "bundle" = "type"
|
||||
* },
|
||||
* )
|
||||
*/
|
||||
class EntityTestBundlePlugin extends ContentEntityBase {
|
||||
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity_module_bundle_plugin_test\Plugin\BundlePluginTest;
|
||||
|
||||
use Drupal\entity\BundlePlugin\BundlePluginInterface;
|
||||
|
||||
/**
|
||||
* Defines the interface for BundlePluginTest plugins.
|
||||
*/
|
||||
interface BundlePluginTestInterface extends BundlePluginInterface {
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\entity_module_bundle_plugin_test\Plugin\BundlePluginTest;
|
||||
|
||||
use Drupal\entity\BundleFieldDefinition;
|
||||
use Drupal\Core\Plugin\PluginBase;
|
||||
|
||||
/**
|
||||
* Provides the first bundle plugin.
|
||||
*
|
||||
* @BundlePluginTest(
|
||||
* id = "first",
|
||||
* label = @Translation("First"),
|
||||
* description = @Translation("Some description"),
|
||||
* )
|
||||
*/
|
||||
class First extends PluginBase implements BundlePluginTestInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildFieldDefinitions() {
|
||||
$fields = [];
|
||||
$fields['first_mail'] = BundleFieldDefinition::create('email')
|
||||
->setLabel(t('Email'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
}
|
||||
+4
-3
@@ -1,9 +1,10 @@
|
||||
name: Entity test
|
||||
type: module
|
||||
package: Testing
|
||||
# core: 8.x
|
||||
|
||||
# Information added by Drupal.org packaging script on 2016-12-08
|
||||
version: '8.x-1.0-alpha4'
|
||||
# Information added by Drupal.org packaging script on 2017-09-20
|
||||
version: '8.x-1.0-beta1'
|
||||
core: '8.x'
|
||||
project: 'entity'
|
||||
datestamp: 1481194986
|
||||
datestamp: 1505895847
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ use Drupal\entity\Revision\RevisionableContentEntityBase;
|
||||
* label = @Translation("Entity test with enhancements"),
|
||||
* handlers = {
|
||||
* "storage" = "\Drupal\Core\Entity\Sql\SqlContentEntityStorage",
|
||||
* "access" = "\Drupal\entity\EntityAccessControlHandler",
|
||||
* "access" = "\Drupal\Core\Entity\EntityAccessControlHandler",
|
||||
* "permission_provider" = "\Drupal\entity\EntityPermissionProvider",
|
||||
* "form" = {
|
||||
* "add" = "\Drupal\entity\Form\RevisionableContentEntityForm",
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace Drupal\Tests\entity\Functional;
|
||||
|
||||
use Drupal\entity_module_test\Entity\EnhancedEntity;
|
||||
use Drupal\entity_module_test\Entity\EnhancedEntityBundle;
|
||||
use Drupal\simpletest\BrowserTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the delete multiple confirmation form.
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\entity\Kernel;
|
||||
|
||||
use Drupal\entity_module_bundle_plugin_test\Entity\EntityTestBundlePlugin;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the bundle plugin API.
|
||||
*
|
||||
* @group entity
|
||||
*/
|
||||
class BundlePluginTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'system',
|
||||
'entity',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('system', 'router');
|
||||
|
||||
// Install the modules properly. Putting them into static::$modules doesn't trigger the install
|
||||
// hooks, like hook_modules_installed, so entity_modules_installed is not triggered().
|
||||
/** @var \Drupal\Core\Extension\ModuleInstallerInterface $module_installer */
|
||||
$module_installer = \Drupal::service('module_installer');
|
||||
$module_installer->install(['entity_module_bundle_plugin_test', 'entity_module_bundle_plugin_examples_test']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the bundle plugins.
|
||||
*/
|
||||
public function testPluginBundles() {
|
||||
$bundled_entity_types = entity_get_bundle_plugin_entity_types();
|
||||
/** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type */
|
||||
$entity_type = $bundled_entity_types['entity_test_bundle_plugin'];
|
||||
$this->assertEquals('entity_test_bundle_plugin', $entity_type->id());
|
||||
$this->assertTrue($entity_type->hasHandlerClass('bundle_plugin'));
|
||||
|
||||
/** @var \Drupal\Core\Entity\EntityTypeBundleInfo $entity_type_bundle_info */
|
||||
$entity_type_bundle_info = \Drupal::service('entity_type.bundle.info');
|
||||
$bundle_info = $entity_type_bundle_info->getBundleInfo('entity_test_bundle_plugin');
|
||||
$this->assertEquals(2, count($bundle_info));
|
||||
$this->assertArrayHasKey('first', $bundle_info);
|
||||
$this->assertArrayHasKey('second', $bundle_info);
|
||||
$this->assertEquals('First', $bundle_info['first']['label']);
|
||||
$this->assertEquals('Some description', $bundle_info['first']['description']);
|
||||
|
||||
/** @var \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager */
|
||||
$entity_field_manager = \Drupal::service('entity_field.manager');
|
||||
$field_storage_definitions = $entity_field_manager->getFieldStorageDefinitions('entity_test_bundle_plugin');
|
||||
$this->assertArrayHasKey('first_mail', $field_storage_definitions);
|
||||
$this->assertArrayHasKey('second_mail', $field_storage_definitions);
|
||||
$first_field_definitions = $entity_field_manager->getFieldDefinitions('entity_test_bundle_plugin', 'first');
|
||||
$this->assertArrayHasKey('first_mail', $first_field_definitions);
|
||||
$this->assertArrayNotHasKey('second_mail', $first_field_definitions);
|
||||
$second_field_definitions = $entity_field_manager->getFieldDefinitions('entity_test_bundle_plugin', 'second');
|
||||
$this->assertArrayNotHasKey('first_mail', $second_field_definitions);
|
||||
$this->assertArrayHasKey('second_mail', $second_field_definitions);
|
||||
|
||||
$first_entity = EntityTestBundlePlugin::create([
|
||||
'type' => 'first',
|
||||
'first_mail' => 'admin@test.com',
|
||||
]);
|
||||
$first_entity->save();
|
||||
$first_entity = EntityTestBundlePlugin::load($first_entity->id());
|
||||
$this->assertEquals('admin@test.com', $first_entity->first_mail->value);
|
||||
|
||||
$second_entity = EntityTestBundlePlugin::create([
|
||||
'type' => 'second',
|
||||
'second_mail' => 'admin@example.com',
|
||||
]);
|
||||
$second_entity->save();
|
||||
$second_entity = EntityTestBundlePlugin::load($second_entity->id());
|
||||
$this->assertEquals('admin@example.com', $second_entity->second_mail->value);
|
||||
|
||||
// Also test entity queries.
|
||||
$result = \Drupal::entityTypeManager()->getStorage('entity_test_bundle_plugin')
|
||||
->getQuery()
|
||||
->condition('second_mail', 'admin@example.com')
|
||||
->execute();
|
||||
$this->assertEquals([$second_entity->id() => $second_entity->id()], $result);
|
||||
|
||||
$result = \Drupal::entityTypeManager()->getStorage('entity_test_bundle_plugin')
|
||||
->getQuery()
|
||||
->condition('type', 'first')
|
||||
->execute();
|
||||
$this->assertEquals([$first_entity->id() => $first_entity->id()], $result);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the uninstallation for a bundle provided by a module.
|
||||
*/
|
||||
public function testBundlePluginModuleUninstallation() {
|
||||
/** @var \Drupal\Core\Extension\ModuleInstallerInterface $module_installer */
|
||||
$module_installer = \Drupal::service('module_installer');
|
||||
|
||||
// One should be possible to uninstall without any actual content.
|
||||
$violations = $module_installer->validateUninstall(['entity_module_bundle_plugin_examples_test']);
|
||||
$this->assertEmpty($violations);
|
||||
|
||||
$first_entity = EntityTestBundlePlugin::create([
|
||||
'type' => 'first',
|
||||
'first_mail' => 'admin@test.com',
|
||||
]);
|
||||
$first_entity->save();
|
||||
$second_entity = EntityTestBundlePlugin::create([
|
||||
'type' => 'second',
|
||||
'second_mail' => 'admin@example.com',
|
||||
]);
|
||||
$second_entity->save();
|
||||
|
||||
$violations = $module_installer->validateUninstall(['entity_module_bundle_plugin_examples_test']);
|
||||
$this->assertCount(1, $violations);
|
||||
$this->assertCount(1, $violations['entity_module_bundle_plugin_examples_test']);
|
||||
$this->assertEquals('There is data for the bundle Second on the entity type Entity test bundle plugin. Please remove all content before uninstalling the module.', $violations['entity_module_bundle_plugin_examples_test'][0]);
|
||||
|
||||
$second_entity->delete();
|
||||
|
||||
// The first entity is defined by entity_module_bundle_plugin_test, so it should be possible
|
||||
// to uninstall the module providing the second bundle plugin.
|
||||
$violations = $module_installer->validateUninstall(['entity_module_bundle_plugin_examples_test']);
|
||||
$this->assertEmpty($violations);
|
||||
|
||||
$module_installer->uninstall(['entity_module_bundle_plugin_examples_test']);
|
||||
|
||||
// The first entity is provided by entity_module_bundle_plugin_test which we cannot uninstall,
|
||||
// until all the entities are deleted.
|
||||
$violations = $module_installer->validateUninstall(['entity_module_bundle_plugin_test']);
|
||||
$this->assertNotEmpty($violations);
|
||||
|
||||
$first_entity->delete();
|
||||
$violations = $module_installer->validateUninstall(['entity_module_bundle_plugin_test']);
|
||||
$this->assertEmpty($violations);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -57,22 +57,36 @@ class RevisionBasicUITest extends KernelTestBase {
|
||||
|
||||
/** @var \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel */
|
||||
$http_kernel = \Drupal::service('http_kernel');
|
||||
$request = Request::create($revision->url('version-history'));
|
||||
$request = Request::create($revision->toUrl('version-history')->toString());
|
||||
$response = $http_kernel->handle($request);
|
||||
$this->assertEquals(403, $response->getStatusCode());
|
||||
|
||||
$role_admin = Role::create(['id' => 'test_role_admin']);
|
||||
$role_admin->grantPermission('administer entity_test_enhanced');
|
||||
$role_admin->save();
|
||||
|
||||
$role = Role::create(['id' => 'test_role']);
|
||||
$role->grantPermission('view all entity_test_enhanced revisions');
|
||||
$role->grantPermission('administer entity_test_enhanced');
|
||||
$role->save();
|
||||
|
||||
$user_admin = User::create([
|
||||
'name' => 'Test user admin',
|
||||
]);
|
||||
$user_admin->addRole($role_admin->id());
|
||||
\Drupal::service('account_switcher')->switchTo($user_admin);
|
||||
|
||||
$request = Request::create($revision->toUrl('version-history')->toString());
|
||||
$response = $http_kernel->handle($request);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
|
||||
$user = User::create([
|
||||
'name' => 'Test user',
|
||||
]);
|
||||
$user->addRole($role->id());
|
||||
\Drupal::service('account_switcher')->switchTo($user);
|
||||
|
||||
$request = Request::create($revision->url('version-history'));
|
||||
$request = Request::create($revision->toUrl('version-history')->toString());
|
||||
$response = $http_kernel->handle($request);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
|
||||
@@ -87,7 +101,7 @@ class RevisionBasicUITest extends KernelTestBase {
|
||||
$revision->isDefaultRevision(TRUE);
|
||||
$revision->save();
|
||||
|
||||
$request = Request::create($revision->url('version-history'));
|
||||
$request = Request::create($revision->toUrl('version-history')->toString());
|
||||
$response = $http_kernel->handle($request);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
|
||||
@@ -110,22 +124,36 @@ class RevisionBasicUITest extends KernelTestBase {
|
||||
|
||||
/** @var \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel */
|
||||
$http_kernel = \Drupal::service('http_kernel');
|
||||
$request = Request::create($revision->url('revision'));
|
||||
$request = Request::create($revision->toUrl('revision')->toString());
|
||||
$response = $http_kernel->handle($request);
|
||||
$this->assertEquals(403, $response->getStatusCode());
|
||||
|
||||
$role_admin = Role::create(['id' => 'test_role_admin']);
|
||||
$role_admin->grantPermission('administer entity_test_enhanced');
|
||||
$role_admin->save();
|
||||
|
||||
$role = Role::create(['id' => 'test_role']);
|
||||
$role->grantPermission('view all entity_test_enhanced revisions');
|
||||
$role->grantPermission('administer entity_test_enhanced');
|
||||
$role->save();
|
||||
|
||||
$user_admin = User::create([
|
||||
'name' => 'Test user admin',
|
||||
]);
|
||||
$user_admin->addRole($role_admin->id());
|
||||
\Drupal::service('account_switcher')->switchTo($user_admin);
|
||||
|
||||
$request = Request::create($revision->toUrl('version-history')->toString());
|
||||
$response = $http_kernel->handle($request);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
|
||||
$user = User::create([
|
||||
'name' => 'Test user',
|
||||
]);
|
||||
$user->addRole($role->id());
|
||||
\Drupal::service('account_switcher')->switchTo($user);
|
||||
|
||||
$request = Request::create($revision->url('revision'));
|
||||
$request = Request::create($revision->toUrl('revision')->toString());
|
||||
$response = $http_kernel->handle($request);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertNotContains('rev 1', $response->getContent());
|
||||
@@ -156,7 +184,7 @@ class RevisionBasicUITest extends KernelTestBase {
|
||||
|
||||
/** @var \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel */
|
||||
$http_kernel = \Drupal::service('http_kernel');
|
||||
$request = Request::create($entity->url('revision-revert-form'));
|
||||
$request = Request::create($entity->toUrl('revision-revert-form')->toString());
|
||||
$response = $http_kernel->handle($request);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
+54
-2
@@ -8,12 +8,14 @@ use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityPublishedInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\entity\EntityAccessControlHandler;
|
||||
use Drupal\entity\EntityPermissionProvider;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\user\EntityOwnerInterface;
|
||||
use Prophecy\Argument;
|
||||
@@ -80,6 +82,8 @@ class EntityAccessControlHandlerTest extends UnitTestCase {
|
||||
$entity_type = $this->prophesize(ContentEntityTypeInterface::class);
|
||||
$entity_type->id()->willReturn('green_entity');
|
||||
$entity_type->getAdminPermission()->willReturn('administer green_entity');
|
||||
$entity_type->hasHandlerClass('permission_provider')->willReturn(TRUE);
|
||||
$entity_type->getHandlerClass('permission_provider')->willReturn(EntityPermissionProvider::class);
|
||||
|
||||
// User with the admin permission can do anything.
|
||||
$entity = $this->buildMockEntity($entity_type->reveal());
|
||||
@@ -126,6 +130,32 @@ class EntityAccessControlHandlerTest extends UnitTestCase {
|
||||
$data[] = [$entity->reveal(), 'update', $second_account->reveal(), FALSE];
|
||||
$data[] = [$entity->reveal(), 'update', $third_account->reveal(), TRUE];
|
||||
|
||||
// Test the unpublished permissions.
|
||||
$entity_first_other_up = $this->buildMockEntity($entity_type->reveal(), 9999, 'first', FALSE);
|
||||
$entity_first_own_up = $this->buildMockEntity($entity_type->reveal(), 14, 'first', FALSE);
|
||||
$entity_first_own_bundle_up = $this->buildMockEntity($entity_type->reveal(), 15, 'first', FALSE);
|
||||
|
||||
$entity_second_other_up = $this->buildMockEntity($entity_type->reveal(), 9999, 'second', FALSE);
|
||||
$entity_second_own_up = $this->buildMockEntity($entity_type->reveal(), 14, 'second', FALSE);
|
||||
$entity_second_own_bundle_up = $this->buildMockEntity($entity_type->reveal(), 15, 'second', FALSE);
|
||||
|
||||
$user_view_own_up = $this->buildMockUser(14, 'view own unpublished green_entity');
|
||||
$user_view_other = $this->buildMockUser(15, 'view green_entity');
|
||||
|
||||
$data['entity_first_other_up user_view_own_up'] = [$entity_first_other_up->reveal(), 'view', $user_view_own_up->reveal(), FALSE];
|
||||
$data['entity_first_own_up user_view_own_up'] = [$entity_first_own_up->reveal(), 'view', $user_view_own_up->reveal(), TRUE];
|
||||
$data['entity_first_own_bundle_up user_view_own_up'] = [$entity_first_own_bundle_up->reveal(), 'view', $user_view_own_up->reveal(), FALSE];
|
||||
$data['entity_second_other_up user_view_own_up'] = [$entity_second_other_up->reveal(), 'view', $user_view_own_up->reveal(), FALSE];
|
||||
$data['entity_second_own_up user_view_own_up'] = [$entity_second_own_up->reveal(), 'view', $user_view_own_up->reveal(), TRUE];
|
||||
$data['entity_second_own_bundle_up user_view_own_up'] = [$entity_second_own_bundle_up->reveal(), 'view', $user_view_own_up->reveal(), FALSE];
|
||||
|
||||
$data['entity_first_other_up user_view_other'] = [$entity_first_other_up->reveal(), 'view', $user_view_other->reveal(), FALSE];
|
||||
$data['entity_first_own_up user_view_other'] = [$entity_first_own_up->reveal(), 'view', $user_view_other->reveal(), FALSE];
|
||||
$data['entity_first_own_bundle_up user_view_other'] = [$entity_first_own_bundle_up->reveal(), 'view', $user_view_other->reveal(), FALSE];
|
||||
$data['entity_second_other_up user_view_other'] = [$entity_second_other_up->reveal(), 'view', $user_view_other->reveal(), FALSE];
|
||||
$data['entity_second_own_up user_view_other'] = [$entity_second_own_up->reveal(), 'view', $user_view_other->reveal(), FALSE];
|
||||
$data['entity_second_own_bundle_up user_view_other'] = [$entity_second_own_bundle_up->reveal(), 'view', $user_view_other->reveal(), FALSE];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
@@ -141,6 +171,8 @@ class EntityAccessControlHandlerTest extends UnitTestCase {
|
||||
$entity_type = $this->prophesize(ContentEntityTypeInterface::class);
|
||||
$entity_type->id()->willReturn('green_entity');
|
||||
$entity_type->getAdminPermission()->willReturn('administer green_entity');
|
||||
$entity_type->hasHandlerClass('permission_provider')->willReturn(TRUE);
|
||||
$entity_type->getHandlerClass('permission_provider')->willReturn(EntityPermissionProvider::class);
|
||||
|
||||
// User with the admin permission.
|
||||
$account = $this->prophesize(AccountInterface::class);
|
||||
@@ -182,16 +214,27 @@ class EntityAccessControlHandlerTest extends UnitTestCase {
|
||||
* @return \Prophecy\Prophecy\ObjectProphecy
|
||||
* The entity mock.
|
||||
*/
|
||||
protected function buildMockEntity(EntityTypeInterface $entity_type, $owner_id = NULL) {
|
||||
protected function buildMockEntity(EntityTypeInterface $entity_type, $owner_id = NULL, $bundle = NULL, $published = NULL) {
|
||||
$langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED;
|
||||
$entity = $this->prophesize(ContentEntityInterface::class);
|
||||
if (isset($published)) {
|
||||
$entity->willImplement(EntityPublishedInterface::class);
|
||||
}
|
||||
if ($owner_id) {
|
||||
$entity->willImplement(EntityOwnerInterface::class);
|
||||
}
|
||||
if (isset($published)) {
|
||||
$entity->isPublished()->willReturn($published);
|
||||
}
|
||||
if ($owner_id) {
|
||||
$entity->getOwnerId()->willReturn($owner_id);
|
||||
}
|
||||
$entity->bundle()->willReturn($entity_type->id());
|
||||
|
||||
$entity->bundle()->willReturn($bundle ?: $entity_type->id());
|
||||
$entity->isNew()->willReturn(FALSE);
|
||||
$entity->uuid()->willReturn('fake uuid');
|
||||
$entity->id()->willReturn('fake id');
|
||||
$entity->getRevisionId()->willReturn(NULL);
|
||||
$entity->language()->willReturn(new Language(['id' => $langcode]));
|
||||
$entity->getEntityTypeId()->willReturn($entity_type->id());
|
||||
$entity->getEntityType()->willReturn($entity_type);
|
||||
@@ -199,7 +242,16 @@ class EntityAccessControlHandlerTest extends UnitTestCase {
|
||||
$entity->getCacheTags()->willReturn([]);
|
||||
$entity->getCacheMaxAge()->willReturn(Cache::PERMANENT);
|
||||
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
protected function buildMockUser($uid, $permission) {
|
||||
$account = $this->prophesize(AccountInterface::class);
|
||||
$account->id()->willReturn($uid);
|
||||
$account->hasPermission($permission)->willReturn(TRUE);
|
||||
$account->hasPermission(Argument::any())->willReturn(FALSE);
|
||||
return $account;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+38
-11
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace Drupal\Tests\entity\Unit;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityPublishedInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\entity\EntityPermissionProvider;
|
||||
@@ -37,6 +37,9 @@ class EntityPermissionProviderTest extends UnitTestCase {
|
||||
$entity_type_bundle_info->getBundleInfo('black_entity')->willReturn([
|
||||
'third' => ['label' => 'Third'],
|
||||
]);
|
||||
$entity_type_bundle_info->getBundleInfo('pink_entity')->willReturn([
|
||||
'third' => ['label' => 'Third'],
|
||||
]);
|
||||
$this->permissionProvider = new EntityPermissionProvider($entity_type_bundle_info->reveal());
|
||||
$this->permissionProvider->setStringTranslation($this->getStringTranslationStub());
|
||||
}
|
||||
@@ -69,15 +72,16 @@ class EntityPermissionProviderTest extends UnitTestCase {
|
||||
$entity_type->id()->willReturn('green_entity');
|
||||
$entity_type->getSingularLabel()->willReturn('green entity');
|
||||
$entity_type->getPluralLabel()->willReturn('green entities');
|
||||
$entity_type->isSubclassOf(EntityOwnerInterface::class)->willReturn(FALSE);
|
||||
$entity_type->entityClassImplements(EntityOwnerInterface::class)->willReturn(FALSE);
|
||||
$entity_type->entityClassImplements(EntityPublishedInterface::class)->willReturn(FALSE);
|
||||
$entity_type->getPermissionGranularity()->willReturn('entity_type');
|
||||
$expected_permissions = [
|
||||
'administer green_entity' => 'Administer green entities',
|
||||
'access green_entity overview' => 'Access the green entities overview page',
|
||||
'view green_entity' => 'View green entities',
|
||||
'create green_entity' => 'Create green entities',
|
||||
'update green_entity' => 'Update green entities',
|
||||
'delete green_entity' => 'Delete green entities',
|
||||
'view green_entity' => 'View green entities',
|
||||
];
|
||||
$data[] = [$entity_type->reveal(), $expected_permissions];
|
||||
|
||||
@@ -87,18 +91,18 @@ class EntityPermissionProviderTest extends UnitTestCase {
|
||||
$entity_type->id()->willReturn('blue_entity');
|
||||
$entity_type->getSingularLabel()->willReturn('blue entity');
|
||||
$entity_type->getPluralLabel()->willReturn('blue entities');
|
||||
$entity_type->isSubclassOf(EntityOwnerInterface::class)->willReturn(TRUE);
|
||||
$entity_type->entityClassImplements(EntityOwnerInterface::class)->willReturn(TRUE);
|
||||
$entity_type->entityClassImplements(EntityPublishedInterface::class)->willReturn(FALSE);
|
||||
$entity_type->getPermissionGranularity()->willReturn('entity_type');
|
||||
$expected_permissions = [
|
||||
'administer blue_entity' => 'Administer blue entities',
|
||||
'access blue_entity overview' => 'Access the blue entities overview page',
|
||||
'view any blue_entity' => 'View any blue entity',
|
||||
'view own blue_entity' => 'View own blue entities',
|
||||
'create blue_entity' => 'Create blue entities',
|
||||
'update any blue_entity' => 'Update any blue entity',
|
||||
'update own blue_entity' => 'Update own blue entities',
|
||||
'delete any blue_entity' => 'Delete any blue entity',
|
||||
'delete own blue_entity' => 'Delete own blue entities',
|
||||
'view blue_entity' => 'View blue entities',
|
||||
];
|
||||
$data[] = [$entity_type->reveal(), $expected_permissions];
|
||||
|
||||
@@ -108,18 +112,19 @@ class EntityPermissionProviderTest extends UnitTestCase {
|
||||
$entity_type->id()->willReturn('white_entity');
|
||||
$entity_type->getSingularLabel()->willReturn('white entity');
|
||||
$entity_type->getPluralLabel()->willReturn('white entities');
|
||||
$entity_type->isSubclassOf(EntityOwnerInterface::class)->willReturn(FALSE);
|
||||
$entity_type->entityClassImplements(EntityOwnerInterface::class)->willReturn(FALSE);
|
||||
$entity_type->entityClassImplements(EntityPublishedInterface::class)->willReturn(FALSE);
|
||||
$entity_type->getPermissionGranularity()->willReturn('bundle');
|
||||
$expected_permissions = [
|
||||
'administer white_entity' => 'Administer white entities',
|
||||
'access white_entity overview' => 'Access the white entities overview page',
|
||||
'view white_entity' => 'View white entities',
|
||||
'create first white_entity' => 'First: Create white entities',
|
||||
'update first white_entity' => 'First: Update white entities',
|
||||
'delete first white_entity' => 'First: Delete white entities',
|
||||
'create second white_entity' => 'Second: Create white entities',
|
||||
'update second white_entity' => 'Second: Update white entities',
|
||||
'delete second white_entity' => 'Second: Delete white entities',
|
||||
'view white_entity' => 'View white entities',
|
||||
];
|
||||
$data[] = [$entity_type->reveal(), $expected_permissions];
|
||||
|
||||
@@ -129,18 +134,40 @@ class EntityPermissionProviderTest extends UnitTestCase {
|
||||
$entity_type->id()->willReturn('black_entity');
|
||||
$entity_type->getSingularLabel()->willReturn('black entity');
|
||||
$entity_type->getPluralLabel()->willReturn('black entities');
|
||||
$entity_type->isSubclassOf(EntityOwnerInterface::class)->willReturn(TRUE);
|
||||
$entity_type->entityClassImplements(EntityOwnerInterface::class)->willReturn(TRUE);
|
||||
$entity_type->entityClassImplements(EntityPublishedInterface::class)->willReturn(FALSE);
|
||||
$entity_type->getPermissionGranularity()->willReturn('bundle');
|
||||
$expected_permissions = [
|
||||
'administer black_entity' => 'Administer black entities',
|
||||
'access black_entity overview' => 'Access the black entities overview page',
|
||||
'view any black_entity' => 'View any black entity',
|
||||
'view own black_entity' => 'View own black entities',
|
||||
'create third black_entity' => 'Third: Create black entities',
|
||||
'update any third black_entity' => 'Third: Update any black entity',
|
||||
'update own third black_entity' => 'Third: Update own black entities',
|
||||
'delete any third black_entity' => 'Third: Delete any black entity',
|
||||
'delete own third black_entity' => 'Third: Delete own black entities',
|
||||
'view black_entity' => 'View black entities',
|
||||
];
|
||||
$data[] = [$entity_type->reveal(), $expected_permissions];
|
||||
|
||||
// Content entity type with bundles and owner and entity published.
|
||||
$entity_type = $this->prophesize(ContentEntityTypeInterface::class);
|
||||
$entity_type->getProvider()->willReturn('entity_module_test');
|
||||
$entity_type->id()->willReturn('pink_entity');
|
||||
$entity_type->getSingularLabel()->willReturn('pink entity');
|
||||
$entity_type->getPluralLabel()->willReturn('pink entities');
|
||||
$entity_type->entityClassImplements(EntityOwnerInterface::class)->willReturn(TRUE);
|
||||
$entity_type->entityClassImplements(EntityPublishedInterface::class)->willReturn(TRUE);
|
||||
$entity_type->getPermissionGranularity()->willReturn('bundle');
|
||||
$expected_permissions = [
|
||||
'administer pink_entity' => 'Administer pink entities',
|
||||
'access pink_entity overview' => 'Access the pink entities overview page',
|
||||
'view own unpublished pink_entity' => 'View own unpublished pink entities',
|
||||
'create third pink_entity' => 'Third: Create pink entities',
|
||||
'update any third pink_entity' => 'Third: Update any pink entity',
|
||||
'update own third pink_entity' => 'Third: Update own pink entities',
|
||||
'delete any third pink_entity' => 'Third: Delete any pink entity',
|
||||
'delete own third pink_entity' => 'Third: Delete own pink entities',
|
||||
'view pink_entity' => 'View pink entities',
|
||||
];
|
||||
$data[] = [$entity_type->reveal(), $expected_permissions];
|
||||
|
||||
|
||||
+299
@@ -0,0 +1,299 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\entity\Unit;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\Context\CacheContextsManager;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityPublishedInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\entity\UncacheableEntityAccessControlHandler;
|
||||
use Drupal\entity\UncacheableEntityPermissionProvider;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\user\EntityOwnerInterface;
|
||||
use Prophecy\Argument;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\entity\EntityAccessControlHandler
|
||||
* @group entity
|
||||
*/
|
||||
class UncacheableEntityAccessControlHandlerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
|
||||
$module_handler->invokeAll(Argument::any(), Argument::any())->willReturn([]);
|
||||
$cache_contexts_manager = $this->prophesize(CacheContextsManager::class);
|
||||
$cache_contexts_manager->assertValidTokens(Argument::any())->willReturn(TRUE);
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('module_handler', $module_handler->reveal());
|
||||
$container->set('cache_contexts_manager', $cache_contexts_manager->reveal());
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::checkAccess
|
||||
* @covers ::checkEntityPermissions
|
||||
* @covers ::checkEntityOwnerPermissions
|
||||
* @covers ::checkCreateAccess
|
||||
*
|
||||
* @dataProvider accessProvider
|
||||
*/
|
||||
public function testAccess(EntityInterface $entity, $operation, $account, $allowed) {
|
||||
$handler = new UncacheableEntityAccessControlHandler($entity->getEntityType());
|
||||
$handler->setStringTranslation($this->getStringTranslationStub());
|
||||
$result = $handler->access($entity, $operation, $account);
|
||||
$this->assertEquals($allowed, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::checkCreateAccess
|
||||
*
|
||||
* @dataProvider createAccessProvider
|
||||
*/
|
||||
public function testCreateAccess(EntityTypeInterface $entity_type, $bundle, $account, $allowed) {
|
||||
$handler = new UncacheableEntityAccessControlHandler($entity_type);
|
||||
$handler->setStringTranslation($this->getStringTranslationStub());
|
||||
$result = $handler->createAccess($bundle, $account);
|
||||
$this->assertEquals($allowed, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testAccess().
|
||||
*
|
||||
* @return array
|
||||
* A list of testAccess method arguments.
|
||||
*/
|
||||
public function accessProvider() {
|
||||
$data = [];
|
||||
|
||||
$entity_type = $this->prophesize(ContentEntityTypeInterface::class);
|
||||
$entity_type->id()->willReturn('green_entity');
|
||||
$entity_type->getAdminPermission()->willReturn('administer green_entity');
|
||||
$entity_type->hasHandlerClass('permission_provider')->willReturn(TRUE);
|
||||
$entity_type->getHandlerClass('permission_provider')->willReturn(UncacheableEntityPermissionProvider::class);
|
||||
|
||||
// User with the admin permission can do anything.
|
||||
$entity = $this->buildMockEntity($entity_type->reveal());
|
||||
$account = $this->prophesize(AccountInterface::class);
|
||||
$account->id()->willReturn(6);
|
||||
$account->hasPermission('administer green_entity')->willReturn(TRUE);
|
||||
$data[] = [$entity->reveal(), 'view', $account->reveal(), TRUE];
|
||||
$data[] = [$entity->reveal(), 'update', $account->reveal(), TRUE];
|
||||
$data[] = [$entity->reveal(), 'delete', $account->reveal(), TRUE];
|
||||
|
||||
// Entity with no owner.
|
||||
$entity = $this->buildMockEntity($entity_type->reveal());
|
||||
// User who has access.
|
||||
$first_account = $this->prophesize(AccountInterface::class);
|
||||
$first_account->id()->willReturn(6);
|
||||
$first_account->hasPermission('view green_entity')->willReturn(TRUE);
|
||||
$first_account->hasPermission(Argument::any())->willReturn(FALSE);
|
||||
// User who doesn't have access.
|
||||
$second_account = $this->prophesize(AccountInterface::class);
|
||||
$second_account->id()->willReturn(7);
|
||||
$second_account->hasPermission('view green_entity')->willReturn(FALSE);
|
||||
$second_account->hasPermission(Argument::any())->willReturn(FALSE);
|
||||
$data[] = [$entity->reveal(), 'view', $first_account->reveal(), TRUE];
|
||||
$data[] = [$entity->reveal(), 'view', $second_account->reveal(), FALSE];
|
||||
|
||||
// Entity with owner.
|
||||
$entity = $this->buildMockEntity($entity_type->reveal(), 6);
|
||||
// Owner.
|
||||
$first_account = $this->prophesize(AccountInterface::class);
|
||||
$first_account->id()->willReturn(6);
|
||||
$first_account->hasPermission('update own green_entity')->willReturn(TRUE);
|
||||
$first_account->hasPermission(Argument::any())->willReturn(FALSE);
|
||||
// Non-owner.
|
||||
$second_account = $this->prophesize(AccountInterface::class);
|
||||
$second_account->id()->willReturn(7);
|
||||
$second_account->hasPermission('update own green_entity')->willReturn(TRUE);
|
||||
$second_account->hasPermission(Argument::any())->willReturn(FALSE);
|
||||
// User who can update any.
|
||||
$third_account = $this->prophesize(AccountInterface::class);
|
||||
$third_account->id()->willReturn(8);
|
||||
$third_account->hasPermission('update any green_entity')->willReturn(TRUE);
|
||||
$third_account->hasPermission(Argument::any())->willReturn(FALSE);
|
||||
$data[] = [$entity->reveal(), 'update', $first_account->reveal(), TRUE];
|
||||
$data[] = [$entity->reveal(), 'update', $second_account->reveal(), FALSE];
|
||||
$data[] = [$entity->reveal(), 'update', $third_account->reveal(), TRUE];
|
||||
|
||||
// Per bundle permissions.
|
||||
$entity_first_other = $this->buildMockEntity($entity_type->reveal(), 9999, 'first');
|
||||
$entity_first_own = $this->buildMockEntity($entity_type->reveal(), 10, 'first');
|
||||
$entity_first_own_bundle = $this->buildMockEntity($entity_type->reveal(), 12, 'first');
|
||||
|
||||
$entity_second_other = $this->buildMockEntity($entity_type->reveal(), 9999, 'second');
|
||||
$entity_second_own = $this->buildMockEntity($entity_type->reveal(), 10, 'second');
|
||||
$entity_second_own_bundle = $this->buildMockEntity($entity_type->reveal(), 12, 'second');
|
||||
|
||||
$user_view_any = $this->buildMockUser(9, 'view any green_entity');
|
||||
$user_view_own = $this->buildMockUser(10, 'view own green_entity');
|
||||
$user_view_bundle_any = $this->buildMockUser(11, 'view any first green_entity');
|
||||
$user_view_bundle_own = $this->buildMockUser(12, 'view own first green_entity');
|
||||
|
||||
$data['entity_first_other user_view_any'] = [$entity_first_other->reveal(), 'view', $user_view_any->reveal(), TRUE];
|
||||
$data['entity_first_own user_view_any'] = [$entity_first_own->reveal(), 'view', $user_view_any->reveal(), TRUE];
|
||||
$data['entity_first_own_bundle user_view_any'] = [$entity_first_own_bundle->reveal(), 'view', $user_view_any->reveal(), TRUE];
|
||||
$data['entity_second_other user_view_any'] = [$entity_second_other->reveal(), 'view', $user_view_any->reveal(), TRUE];
|
||||
$data['entity_second_own user_view_any'] = [$entity_second_own->reveal(), 'view', $user_view_any->reveal(), TRUE];
|
||||
$data['entity_second_own_bundle user_view_any'] = [$entity_second_own_bundle->reveal(), 'view', $user_view_any->reveal(), TRUE];
|
||||
|
||||
$data['entity_first_other user_view_own'] = [$entity_first_other->reveal(), 'view', $user_view_own->reveal(), FALSE];
|
||||
$data['entity_first_own user_view_own'] = [$entity_first_own->reveal(), 'view', $user_view_own->reveal(), TRUE];
|
||||
$data['entity_first_own_bundle user_view_own'] = [$entity_first_own_bundle->reveal(), 'view', $user_view_own->reveal(), FALSE];
|
||||
$data['entity_second_other user_view_own'] = [$entity_second_other->reveal(), 'view', $user_view_own->reveal(), FALSE];
|
||||
$data['entity_second_own user_view_own'] = [$entity_second_own->reveal(), 'view', $user_view_own->reveal(), TRUE];
|
||||
$data['entity_second_own_bundle user_view_own'] = [$entity_second_own_bundle->reveal(), 'view', $user_view_own->reveal(), FALSE];
|
||||
|
||||
$data['entity_first_other user_view_bundle_any'] = [$entity_first_other->reveal(), 'view', $user_view_bundle_any->reveal(), TRUE];
|
||||
$data['entity_first_own user_view_bundle_any'] = [$entity_first_own->reveal(), 'view', $user_view_bundle_any->reveal(), TRUE];
|
||||
$data['entity_first_own_bundle user_view_bundle_any'] = [$entity_first_own_bundle->reveal(), 'view', $user_view_bundle_any->reveal(), TRUE];
|
||||
$data['entity_second_other user_view_bundle_any'] = [$entity_second_other->reveal(), 'view', $user_view_bundle_any->reveal(), FALSE];
|
||||
$data['entity_second_own user_view_bundle_any'] = [$entity_second_own->reveal(), 'view', $user_view_bundle_any->reveal(), FALSE];
|
||||
$data['entity_second_own_bundle user_view_bundle_any'] = [$entity_second_own_bundle->reveal(), 'view', $user_view_bundle_any->reveal(), FALSE];
|
||||
|
||||
$data['entity_first_other user_view_bundle_any'] = [$entity_first_other->reveal(), 'view', $user_view_bundle_own->reveal(), FALSE];
|
||||
$data['entity_first_own user_view_bundle_any'] = [$entity_first_own->reveal(), 'view', $user_view_bundle_own->reveal(), FALSE];
|
||||
$data['entity_first_own_bundle user_view_bundle_any'] = [$entity_first_own_bundle->reveal(), 'view', $user_view_bundle_own->reveal(), TRUE];
|
||||
$data['entity_second_other user_view_bundle_any'] = [$entity_second_other->reveal(), 'view', $user_view_bundle_own->reveal(), FALSE];
|
||||
$data['entity_second_own user_view_bundle_any'] = [$entity_second_own->reveal(), 'view', $user_view_bundle_own->reveal(), FALSE];
|
||||
$data['entity_second_own_bundle user_view_bundle_any'] = [$entity_second_own_bundle->reveal(), 'view', $user_view_bundle_own->reveal(), FALSE];
|
||||
|
||||
// Test the unpublished permissions.
|
||||
$entity_first_other_up = $this->buildMockEntity($entity_type->reveal(), 9999, 'first', FALSE);
|
||||
$entity_first_own_up = $this->buildMockEntity($entity_type->reveal(), 14, 'first', FALSE);
|
||||
$entity_first_own_bundle_up = $this->buildMockEntity($entity_type->reveal(), 15, 'first', FALSE);
|
||||
|
||||
$entity_second_other_up = $this->buildMockEntity($entity_type->reveal(), 9999, 'second', FALSE);
|
||||
$entity_second_own_up = $this->buildMockEntity($entity_type->reveal(), 14, 'second', FALSE);
|
||||
$entity_second_own_bundle_up = $this->buildMockEntity($entity_type->reveal(), 15, 'second', FALSE);
|
||||
|
||||
$user_view_own_up = $this->buildMockUser(14, 'view own unpublished green_entity');
|
||||
$user_view_other = $this->buildMockUser(15, 'view green_entity');
|
||||
|
||||
$data['entity_first_other_up user_view_own_up'] = [$entity_first_other_up->reveal(), 'view', $user_view_own_up->reveal(), FALSE];
|
||||
$data['entity_first_own_up user_view_own_up'] = [$entity_first_own_up->reveal(), 'view', $user_view_own_up->reveal(), TRUE];
|
||||
$data['entity_first_own_bundle_up user_view_own_up'] = [$entity_first_own_bundle_up->reveal(), 'view', $user_view_own_up->reveal(), FALSE];
|
||||
$data['entity_second_other_up user_view_own_up'] = [$entity_second_other_up->reveal(), 'view', $user_view_own_up->reveal(), FALSE];
|
||||
$data['entity_second_own_up user_view_own_up'] = [$entity_second_own_up->reveal(), 'view', $user_view_own_up->reveal(), TRUE];
|
||||
$data['entity_second_own_bundle_up user_view_own_up'] = [$entity_second_own_bundle_up->reveal(), 'view', $user_view_own_up->reveal(), FALSE];
|
||||
|
||||
$data['entity_first_other_up user_view_other'] = [$entity_first_other_up->reveal(), 'view', $user_view_other->reveal(), FALSE];
|
||||
$data['entity_first_own_up user_view_other'] = [$entity_first_own_up->reveal(), 'view', $user_view_other->reveal(), FALSE];
|
||||
$data['entity_first_own_bundle_up user_view_other'] = [$entity_first_own_bundle_up->reveal(), 'view', $user_view_other->reveal(), FALSE];
|
||||
$data['entity_second_other_up user_view_other'] = [$entity_second_other_up->reveal(), 'view', $user_view_other->reveal(), FALSE];
|
||||
$data['entity_second_own_up user_view_other'] = [$entity_second_own_up->reveal(), 'view', $user_view_other->reveal(), FALSE];
|
||||
$data['entity_second_own_bundle_up user_view_other'] = [$entity_second_own_bundle_up->reveal(), 'view', $user_view_other->reveal(), FALSE];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testCreateAccess().
|
||||
*
|
||||
* @return array
|
||||
* A list of testCreateAccess method arguments.
|
||||
*/
|
||||
public function createAccessProvider() {
|
||||
$data = [];
|
||||
|
||||
$entity_type = $this->prophesize(ContentEntityTypeInterface::class);
|
||||
$entity_type->id()->willReturn('green_entity');
|
||||
$entity_type->getAdminPermission()->willReturn('administer green_entity');
|
||||
$entity_type->hasHandlerClass('permission_provider')->willReturn(TRUE);
|
||||
$entity_type->getHandlerClass('permission_provider')->willReturn(UncacheableEntityPermissionProvider::class);
|
||||
|
||||
// User with the admin permission.
|
||||
$account = $this->prophesize(AccountInterface::class);
|
||||
$account->id()->willReturn(6);
|
||||
$account->hasPermission('administer green_entity')->willReturn(TRUE);
|
||||
$data[] = [$entity_type->reveal(), NULL, $account->reveal(), TRUE];
|
||||
|
||||
// Ordinary user.
|
||||
$account = $this->prophesize(AccountInterface::class);
|
||||
$account->id()->willReturn(6);
|
||||
$account->hasPermission('create green_entity')->willReturn(TRUE);
|
||||
$account->hasPermission(Argument::any())->willReturn(FALSE);
|
||||
$data[] = [$entity_type->reveal(), NULL, $account->reveal(), TRUE];
|
||||
|
||||
// Ordinary user, entity with a bundle.
|
||||
$account = $this->prophesize(AccountInterface::class);
|
||||
$account->id()->willReturn(6);
|
||||
$account->hasPermission('create first_bundle green_entity')->willReturn(TRUE);
|
||||
$account->hasPermission(Argument::any())->willReturn(FALSE);
|
||||
$data[] = [$entity_type->reveal(), 'first_bundle', $account->reveal(), TRUE];
|
||||
|
||||
// User with no permissions.
|
||||
$account = $this->prophesize(AccountInterface::class);
|
||||
$account->id()->willReturn(6);
|
||||
$account->hasPermission(Argument::any())->willReturn(FALSE);
|
||||
$data[] = [$entity_type->reveal(), NULL, $account->reveal(), FALSE];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a mock entity.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type.
|
||||
* @param string $owner_id
|
||||
* The owner ID.
|
||||
*
|
||||
* @return \Prophecy\Prophecy\ObjectProphecy
|
||||
* The entity mock.
|
||||
*/
|
||||
protected function buildMockEntity(EntityTypeInterface $entity_type, $owner_id = NULL, $bundle = NULL, $published = NULL) {
|
||||
$langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED;
|
||||
$entity = $this->prophesize(ContentEntityInterface::class);
|
||||
if (isset($published)) {
|
||||
$entity->willImplement(EntityPublishedInterface::class);
|
||||
}
|
||||
if ($owner_id) {
|
||||
$entity->willImplement(EntityOwnerInterface::class);
|
||||
}
|
||||
if (isset($published)) {
|
||||
$entity->isPublished()->willReturn($published);
|
||||
}
|
||||
if ($owner_id) {
|
||||
$entity->getOwnerId()->willReturn($owner_id);
|
||||
}
|
||||
|
||||
$entity->bundle()->willReturn($bundle ?: $entity_type->id());
|
||||
$entity->isNew()->willReturn(FALSE);
|
||||
$entity->uuid()->willReturn('fake uuid');
|
||||
$entity->id()->willReturn('fake id');
|
||||
$entity->getRevisionId()->willReturn(NULL);
|
||||
$entity->language()->willReturn(new Language(['id' => $langcode]));
|
||||
$entity->getEntityTypeId()->willReturn($entity_type->id());
|
||||
$entity->getEntityType()->willReturn($entity_type);
|
||||
$entity->getCacheContexts()->willReturn([]);
|
||||
$entity->getCacheTags()->willReturn([]);
|
||||
$entity->getCacheMaxAge()->willReturn(Cache::PERMANENT);
|
||||
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
protected function buildMockUser($uid, $permission) {
|
||||
$account = $this->prophesize(AccountInterface::class);
|
||||
$account->id()->willReturn($uid);
|
||||
$account->hasPermission($permission)->willReturn(TRUE);
|
||||
$account->hasPermission(Argument::any())->willReturn(FALSE);
|
||||
return $account;
|
||||
}
|
||||
|
||||
}
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\entity\Unit;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityPublishedInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\entity\UncacheableEntityPermissionProvider;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\user\EntityOwnerInterface;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\entity\UncacheableEntityPermissionProvider
|
||||
* @group entity
|
||||
*/
|
||||
class UncacheableEntityPermissionProviderTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The entity permission provider.
|
||||
*
|
||||
* @var \Drupal\entity\EntityPermissionProviderInterface
|
||||
*/
|
||||
protected $permissionProvider;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$entity_type_bundle_info = $this->prophesize(EntityTypeBundleInfoInterface::class);
|
||||
$entity_type_bundle_info->getBundleInfo('white_entity')->willReturn([
|
||||
'first' => ['label' => 'First'],
|
||||
'second' => ['label' => 'Second'],
|
||||
]);
|
||||
$entity_type_bundle_info->getBundleInfo('black_entity')->willReturn([
|
||||
'third' => ['label' => 'Third'],
|
||||
]);
|
||||
$entity_type_bundle_info->getBundleInfo('pink_entity')->willReturn([
|
||||
'third' => ['label' => 'Third'],
|
||||
]);
|
||||
$this->permissionProvider = new UncacheableEntityPermissionProvider($entity_type_bundle_info->reveal());
|
||||
$this->permissionProvider->setStringTranslation($this->getStringTranslationStub());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildPermissions
|
||||
*
|
||||
* @dataProvider entityTypeProvider
|
||||
*/
|
||||
public function testBuildPermissions(EntityTypeInterface $entity_type, array $expected_permissions) {
|
||||
$permissions = $this->permissionProvider->buildPermissions($entity_type);
|
||||
$this->assertEquals(array_keys($expected_permissions), array_keys($permissions));
|
||||
foreach ($permissions as $name => $permission) {
|
||||
$this->assertEquals('entity_module_test', $permission['provider']);
|
||||
$this->assertEquals($expected_permissions[$name], $permission['title']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testBuildPermissions().
|
||||
*
|
||||
* @return array
|
||||
* A list of testBuildPermissions method arguments.
|
||||
*/
|
||||
public function entityTypeProvider() {
|
||||
$data = [];
|
||||
// Content entity type.
|
||||
$entity_type = $this->prophesize(ContentEntityTypeInterface::class);
|
||||
$entity_type->getProvider()->willReturn('entity_module_test');
|
||||
$entity_type->id()->willReturn('green_entity');
|
||||
$entity_type->getSingularLabel()->willReturn('green entity');
|
||||
$entity_type->getPluralLabel()->willReturn('green entities');
|
||||
$entity_type->entityClassImplements(EntityOwnerInterface::class)->willReturn(FALSE);
|
||||
$entity_type->entityClassImplements(EntityPublishedInterface::class)->willReturn(FALSE);
|
||||
$entity_type->getPermissionGranularity()->willReturn('entity_type');
|
||||
$expected_permissions = [
|
||||
'administer green_entity' => 'Administer green entities',
|
||||
'access green_entity overview' => 'Access the green entities overview page',
|
||||
'create green_entity' => 'Create green entities',
|
||||
'update green_entity' => 'Update green entities',
|
||||
'delete green_entity' => 'Delete green entities',
|
||||
'view any green_entity' => 'View any green entities',
|
||||
];
|
||||
$data[] = [$entity_type->reveal(), $expected_permissions];
|
||||
|
||||
// Content entity type with owner.
|
||||
$entity_type = $this->prophesize(ContentEntityTypeInterface::class);
|
||||
$entity_type->getProvider()->willReturn('entity_module_test');
|
||||
$entity_type->id()->willReturn('blue_entity');
|
||||
$entity_type->getSingularLabel()->willReturn('blue entity');
|
||||
$entity_type->getPluralLabel()->willReturn('blue entities');
|
||||
$entity_type->entityClassImplements(EntityOwnerInterface::class)->willReturn(TRUE);
|
||||
$entity_type->entityClassImplements(EntityPublishedInterface::class)->willReturn(FALSE);
|
||||
$entity_type->getPermissionGranularity()->willReturn('entity_type');
|
||||
$expected_permissions = [
|
||||
'administer blue_entity' => 'Administer blue entities',
|
||||
'access blue_entity overview' => 'Access the blue entities overview page',
|
||||
'create blue_entity' => 'Create blue entities',
|
||||
'update any blue_entity' => 'Update any blue entity',
|
||||
'update own blue_entity' => 'Update own blue entities',
|
||||
'delete any blue_entity' => 'Delete any blue entity',
|
||||
'delete own blue_entity' => 'Delete own blue entities',
|
||||
'view any blue_entity' => 'View any blue entities',
|
||||
'view own blue_entity' => 'View own blue entities',
|
||||
];
|
||||
$data[] = [$entity_type->reveal(), $expected_permissions];
|
||||
|
||||
// Content entity type with bundles.
|
||||
$entity_type = $this->prophesize(ContentEntityTypeInterface::class);
|
||||
$entity_type->getProvider()->willReturn('entity_module_test');
|
||||
$entity_type->id()->willReturn('white_entity');
|
||||
$entity_type->getSingularLabel()->willReturn('white entity');
|
||||
$entity_type->getPluralLabel()->willReturn('white entities');
|
||||
$entity_type->entityClassImplements(EntityOwnerInterface::class)->willReturn(FALSE);
|
||||
$entity_type->entityClassImplements(EntityPublishedInterface::class)->willReturn(FALSE);
|
||||
$entity_type->getPermissionGranularity()->willReturn('bundle');
|
||||
$expected_permissions = [
|
||||
'administer white_entity' => 'Administer white entities',
|
||||
'access white_entity overview' => 'Access the white entities overview page',
|
||||
'create first white_entity' => 'First: Create white entities',
|
||||
'update first white_entity' => 'First: Update white entities',
|
||||
'delete first white_entity' => 'First: Delete white entities',
|
||||
'create second white_entity' => 'Second: Create white entities',
|
||||
'update second white_entity' => 'Second: Update white entities',
|
||||
'delete second white_entity' => 'Second: Delete white entities',
|
||||
'view any white_entity' => 'View any white entities',
|
||||
'view any first white_entity' => 'First: View any white entities',
|
||||
'view any second white_entity' => 'Second: View any white entities',
|
||||
];
|
||||
$data[] = [$entity_type->reveal(), $expected_permissions];
|
||||
|
||||
// Content entity type with bundles and owner.
|
||||
$entity_type = $this->prophesize(ContentEntityTypeInterface::class);
|
||||
$entity_type->getProvider()->willReturn('entity_module_test');
|
||||
$entity_type->id()->willReturn('black_entity');
|
||||
$entity_type->getSingularLabel()->willReturn('black entity');
|
||||
$entity_type->getPluralLabel()->willReturn('black entities');
|
||||
$entity_type->entityClassImplements(EntityOwnerInterface::class)->willReturn(TRUE);
|
||||
$entity_type->entityClassImplements(EntityPublishedInterface::class)->willReturn(FALSE);
|
||||
$entity_type->getPermissionGranularity()->willReturn('bundle');
|
||||
$expected_permissions = [
|
||||
'administer black_entity' => 'Administer black entities',
|
||||
'access black_entity overview' => 'Access the black entities overview page',
|
||||
'create third black_entity' => 'Third: Create black entities',
|
||||
'update any third black_entity' => 'Third: Update any black entity',
|
||||
'update own third black_entity' => 'Third: Update own black entities',
|
||||
'delete any third black_entity' => 'Third: Delete any black entity',
|
||||
'delete own third black_entity' => 'Third: Delete own black entities',
|
||||
'view any black_entity' => 'View any black entities',
|
||||
'view own black_entity' => 'View own black entities',
|
||||
'view any third black_entity' => 'Third: View any black entities',
|
||||
'view own third black_entity' => 'Third: View own black entities',
|
||||
];
|
||||
$data[] = [$entity_type->reveal(), $expected_permissions];
|
||||
|
||||
// Content entity type with bundles and owner and entity published.
|
||||
$entity_type = $this->prophesize(ContentEntityTypeInterface::class);
|
||||
$entity_type->getProvider()->willReturn('entity_module_test');
|
||||
$entity_type->id()->willReturn('pink_entity');
|
||||
$entity_type->getSingularLabel()->willReturn('pink entity');
|
||||
$entity_type->getPluralLabel()->willReturn('pink entities');
|
||||
$entity_type->entityClassImplements(EntityOwnerInterface::class)->willReturn(TRUE);
|
||||
$entity_type->entityClassImplements(EntityPublishedInterface::class)->willReturn(TRUE);
|
||||
$entity_type->getPermissionGranularity()->willReturn('bundle');
|
||||
$expected_permissions = [
|
||||
'administer pink_entity' => 'Administer pink entities',
|
||||
'access pink_entity overview' => 'Access the pink entities overview page',
|
||||
'view own unpublished pink_entity' => 'View own unpublished pink entities',
|
||||
'create third pink_entity' => 'Third: Create pink entities',
|
||||
'update any third pink_entity' => 'Third: Update any pink entity',
|
||||
'update own third pink_entity' => 'Third: Update own pink entities',
|
||||
'delete any third pink_entity' => 'Third: Delete any pink entity',
|
||||
'delete own third pink_entity' => 'Third: Delete own pink entities',
|
||||
'view any pink_entity' => 'View any pink entities',
|
||||
'view own pink_entity' => 'View own pink entities',
|
||||
'view any third pink_entity' => 'Third: View any pink entities',
|
||||
'view own third pink_entity' => 'Third: View own pink entities',
|
||||
];
|
||||
$data[] = [$entity_type->reveal(), $expected_permissions];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user