updated core from 8.4 to 8.5 : bug with login_destination

This commit is contained in:
Bachir Soussi Chiadmi
2018-03-13 14:01:21 +01:00
parent 0d78da249b
commit e668535a4e
3486 changed files with 89604 additions and 33283 deletions
@@ -3,10 +3,10 @@
"description": "Annotation discovery and implementation of plugins.",
"keywords": ["drupal"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9",
"doctrine/common": "2.5.*",
"doctrine/common": "^2.5",
"doctrine/annotations": "1.2.*",
"drupal/core-file-cache": "^8.2",
"drupal/core-plugin": "^8.2",
@@ -13,7 +13,7 @@ use Traversable;
*
* Example call:
* @code
* assert('Drupal\\Component\\Assertion\\Inspector::assertAllStrings($array)');
* assert(Inspector::assertAllStrings($array));
* @endcode
*
* @ingroup php_assert
@@ -187,8 +187,8 @@ class Inspector {
* As an example, this assertion tests for the keys of a theme registry.
*
* @code
* assert('Drupal\\Component\\Assertion\\Inspector::assertAllHaveKey(
* $arrayToTest, "type", "theme path", "function", "template", "variables", "render element", "preprocess functions")');
* assert(Inspector::assertAllHaveKey(
* $arrayToTest, "type", "theme path", "function", "template", "variables", "render element", "preprocess functions"));
* @endcode
*
* Note: If a method requires certain keys to be present it will usually be
@@ -375,16 +375,13 @@ class Inspector {
* Here are some examples:
* @code
* // Just test all are objects, like a cache.
* assert('Drupal\\Component\\Assertion\\Inspector::assertAllObjects(
* $collection');
* assert(Inspector::assertAllObjects($collection));
*
* // Test if traversable objects (arrays won't pass this)
* assert('Drupal\\Component\\Assertion\\Inspector::assertAllObjects(
* $collection', \'\\Traversable\');
* assert(Inspector::assertAllObjects($collection, '\\Traversable'));
*
* // Test for the Foo class or Bar\None interface
* assert('Drupal\\Component\\Assertion\\Inspector::assertAllObjects(
* $collection', \'\\Foo\', \'\\Bar\\None\'');
* assert(Inspector::assertAllObjects($collection, '\\Foo', '\\Bar\\None'));
* @endcode
*
* @param mixed $traversable
@@ -3,7 +3,7 @@
"description": "Provides runtime assertions similar to those in PHP 7, under PHP 5.",
"keywords": ["drupal"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9"
},
@@ -3,7 +3,7 @@
"description": "Bridge.",
"keywords": ["drupal"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9",
"zendframework/zend-feed": "^2.4"
@@ -3,10 +3,10 @@
"description": "This class provides a class finding utility.",
"keywords": ["drupal"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9",
"doctrine/common": "2.5.*"
"doctrine/common": "^2.5"
},
"autoload": {
"psr-4": {
@@ -280,7 +280,7 @@ class DateTimePlus {
* parameter and the current timezone are ignored when the $time parameter
* either is a UNIX timestamp (e.g. @946684800) or specifies a timezone
* (e.g. 2010-01-28T15:00:00+02:00).
* @see http://php.net/manual/en/datetime.construct.php
* @see http://php.net/manual/datetime.construct.php
* @param array $settings
* (optional) Keyed array of settings. Defaults to empty array.
* - langcode: (optional) String two letter language code used to control
@@ -704,4 +704,15 @@ class DateTimePlus {
return $value;
}
/**
* Sets the default time for an object built from date-only data.
*
* The default time for a date without time can be anything, so long as it is
* consistently applied. If we use noon, dates in most timezones will have the
* same value for in both the local timezone and UTC.
*/
public function setDefaultDateTime() {
$this->dateTimeObject->setTime(12, 0, 0);
}
}
@@ -3,7 +3,7 @@
"description": "This class wraps the PHP DateTime class with more flexible initialization parameters, allowing a date to be created from an existing date object, a timestamp, a string with an unknown format, a string with a known format, or an array of date parts. It also adds an errors array and a __toString() method to the date object.",
"keywords": ["drupal"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9",
"drupal/core-utility": "^8.2"
@@ -65,7 +65,7 @@ class OptimizedPhpArrayDumper extends Dumper {
$definition['aliases'] = $this->getAliases();
$definition['parameters'] = $this->getParameters();
$definition['services'] = $this->getServiceDefinitions();
$definition['frozen'] = $this->container->isFrozen();
$definition['frozen'] = $this->container->isCompiled();
$definition['machine_format'] = $this->supportsMachineFormat();
return $definition;
}
@@ -103,8 +103,8 @@ class OptimizedPhpArrayDumper extends Dumper {
}
$parameters = $this->container->getParameterBag()->all();
$is_frozen = $this->container->isFrozen();
return $this->prepareParameters($parameters, $is_frozen);
$is_compiled = $this->container->isCompiled();
return $this->prepareParameters($parameters, $is_compiled);
}
/**
@@ -309,10 +309,10 @@ class OptimizedPhpArrayDumper extends Dumper {
}
}
else {
if (is_object($value)) {
$code[$key] = $this->dumpValue($value);
if (is_object($code[$key])) {
$resolve = TRUE;
}
$code[$key] = $this->dumpValue($value);
}
}
@@ -403,6 +403,9 @@ class OptimizedPhpArrayDumper extends Dumper {
elseif ($value instanceof Parameter) {
return $this->getParameterCall((string) $value);
}
elseif (is_string($value) && preg_match('/^\%(.*)\%$/', $value, $matches)) {
return $this->getParameterCall($matches[1]);
}
elseif ($value instanceof Expression) {
throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
}
@@ -4,7 +4,7 @@
"keywords": ["drupal", "dependency injection"],
"type": "library",
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"support": {
"issues": "https://www.drupal.org/project/issues/drupal",
"irc": "irc://irc.freenode.net/drupal-contribute",
@@ -12,7 +12,7 @@
},
"require": {
"php": ">=5.5.9",
"symfony/dependency-injection": "^2.8"
"symfony/dependency-injection": ">=2.8 <4.0.0"
},
"suggest": {
"symfony/expression-language": "For using expressions in service container configuration"
-1
View File
@@ -148,7 +148,6 @@ class Diff {
trigger_error("Reversed closing doesn't match", E_USER_ERROR);
}
$prevtype = 'none';
foreach ($this->edits as $edit) {
if ( $prevtype == $edit->type ) {
@@ -201,28 +201,30 @@ class DiffEngine {
continue;
}
$matches = $ymatches[$line];
reset($matches);
while (list ($junk, $y) = each($matches)) {
if (empty($this->in_seq[$y])) {
$k = $this->_lcs_pos($y);
$this::USE_ASSERTS && assert($k > 0);
$ymids[$k] = $ymids[$k - 1];
break;
$found_empty = FALSE;
foreach ($matches as $y) {
if (!$found_empty) {
if (empty($this->in_seq[$y])) {
$k = $this->_lcs_pos($y);
$this::USE_ASSERTS && assert($k > 0);
$ymids[$k] = $ymids[$k - 1];
$found_empty = TRUE;
}
}
}
while (list ($junk, $y) = each($matches)) {
if ($y > $this->seq[$k - 1]) {
$this::USE_ASSERTS && assert($y < $this->seq[$k]);
// Optimization: this is a common case:
// next match is just replacing previous match.
$this->in_seq[$this->seq[$k]] = FALSE;
$this->seq[$k] = $y;
$this->in_seq[$y] = 1;
}
elseif (empty($this->in_seq[$y])) {
$k = $this->_lcs_pos($y);
$this::USE_ASSERTS && assert($k > 0);
$ymids[$k] = $ymids[$k - 1];
else {
if ($y > $this->seq[$k - 1]) {
$this::USE_ASSERTS && assert($y < $this->seq[$k]);
// Optimization: this is a common case:
// next match is just replacing previous match.
$this->in_seq[$this->seq[$k]] = FALSE;
$this->seq[$k] = $y;
$this->in_seq[$y] = 1;
}
elseif (empty($this->in_seq[$y])) {
$k = $this->_lcs_pos($y);
$this::USE_ASSERTS && assert($k > 0);
$ymids[$k] = $ymids[$k - 1];
}
}
}
}
@@ -343,7 +345,7 @@ class DiffEngine {
$i = 0;
$j = 0;
$this::USE_ASSERTS && assert('sizeof($lines) == sizeof($changed)');
$this::USE_ASSERTS && assert(sizeof($lines) == sizeof($changed));
$len = sizeof($lines);
$other_len = sizeof($other_changed);
@@ -363,7 +365,7 @@ class DiffEngine {
$j++;
}
while ($i < $len && !$changed[$i]) {
$this::USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]');
$this::USE_ASSERTS && assert($j < $other_len && ! $other_changed[$j]);
$i++;
$j++;
while ($j < $other_len && $other_changed[$j]) {
@@ -399,11 +401,11 @@ class DiffEngine {
while ($start > 0 && $changed[$start - 1]) {
$start--;
}
$this::USE_ASSERTS && assert('$j > 0');
$this::USE_ASSERTS && assert($j > 0);
while ($other_changed[--$j]) {
continue;
}
$this::USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]');
$this::USE_ASSERTS && assert($j >= 0 && !$other_changed[$j]);
}
/*
@@ -426,7 +428,7 @@ class DiffEngine {
while ($i < $len && $changed[$i]) {
$i++;
}
$this::USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]');
$this::USE_ASSERTS && assert($j < $other_len && ! $other_changed[$j]);
$j++;
if ($j < $other_len && $other_changed[$j]) {
$corresponding = $i;
@@ -444,11 +446,11 @@ class DiffEngine {
while ($corresponding < $i) {
$changed[--$start] = 1;
$changed[--$i] = 0;
$this::USE_ASSERTS && assert('$j > 0');
$this::USE_ASSERTS && assert($j > 0);
while ($other_changed[--$j]) {
continue;
}
$this::USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]');
$this::USE_ASSERTS && assert($j >= 0 && !$other_changed[$j]);
}
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
"description": "Diff.",
"keywords": ["drupal"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9",
"drupal/core-utility": "^8.2"
@@ -3,7 +3,7 @@
"description": "Discovery.",
"keywords": ["drupal"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9",
"drupal/core-file-cache": "^8.2",
@@ -104,8 +104,11 @@ class ContainerAwareEventDispatcher implements EventDispatcherInterface {
if (!isset($definition['callable'])) {
$definition['callable'] = [$this->container->get($definition['service'][0]), $definition['service'][1]];
}
if (is_array($definition['callable']) && isset($definition['callable'][0]) && $definition['callable'][0] instanceof \Closure) {
$definition['callable'][0] = $definition['callable'][0]();
}
$definition['callable']($event, $event_name, $this);
call_user_func($definition['callable'], $event, $event_name, $this);
if ($event->isPropagationStopped()) {
return $event;
}
@@ -144,6 +147,9 @@ class ContainerAwareEventDispatcher implements EventDispatcherInterface {
if (!isset($definition['callable'])) {
$definition['callable'] = [$this->container->get($definition['service'][0]), $definition['service'][1]];
}
if (is_array($definition['callable']) && isset($definition['callable'][0]) && $definition['callable'][0] instanceof \Closure) {
$definition['callable'][0] = $definition['callable'][0]();
}
$result[] = $definition['callable'];
}
@@ -156,27 +162,29 @@ class ContainerAwareEventDispatcher implements EventDispatcherInterface {
/**
* {@inheritdoc}
*/
public function getListenerPriority($eventName, $listener) {
// Parts copied from \Symfony\Component\EventDispatcher, that's why you see
// a yoda condition here.
if (!isset($this->listeners[$eventName])) {
public function getListenerPriority($event_name, $listener) {
if (!isset($this->listeners[$event_name])) {
return;
}
foreach ($this->listeners[$eventName] as $priority => $listeners) {
if (FALSE !== ($key = array_search(['callable' => $listener], $listeners, TRUE))) {
return $priority;
}
if (is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
$listener[0] = $listener[0]();
}
// Resolve service definitions if the listener has not been found so far.
foreach ($this->listeners[$eventName] as $priority => &$definitions) {
foreach ($this->listeners[$event_name] as $priority => &$definitions) {
foreach ($definitions as $key => &$definition) {
if (!isset($definition['callable'])) {
// Once the callable is retrieved we keep it for subsequent method
// invocations on this class.
$definition['callable'] = [$this->container->get($definition['service'][0]), $definition['service'][1]];
if ($definition['callable'] === $listener) {
return $priority;
}
$definition['callable'] = [
$this->container->get($definition['service'][0]),
$definition['service'][1],
];
}
if (is_array($definition['callable']) && isset($definition['callable'][0]) && $definition['callable'][0] instanceof \Closure) {
$definition['callable'][0] = $definition['callable'][0]();
}
if ($definition['callable'] === $listener) {
return $priority;
}
}
}
@@ -186,7 +194,17 @@ class ContainerAwareEventDispatcher implements EventDispatcherInterface {
* {@inheritdoc}
*/
public function hasListeners($event_name = NULL) {
return (bool) count($this->getListeners($event_name));
if ($event_name !== NULL) {
return !empty($this->listeners[$event_name]);
}
foreach ($this->listeners as $event_listeners) {
if ($event_listeners) {
return TRUE;
}
}
return FALSE;
}
/**
@@ -214,9 +232,22 @@ class ContainerAwareEventDispatcher implements EventDispatcherInterface {
$definition['callable'] = [$this->container->get($definition['service'][0]), $definition['service'][1]];
}
if ($definition['callable'] === $listener) {
unset($this->listeners[$event_name][$priority][$key]);
if (is_array($definition['callable']) && isset($definition['callable'][0]) && $definition['callable'][0] instanceof \Closure && !$listener instanceof \Closure) {
$definition['callable'][0] = $definition['callable'][0]();
}
if (is_array($definition['callable']) && isset($definition['callable'][0]) && !$definition['callable'][0] instanceof \Closure && is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
$listener[0] = $listener[0]();
}
if ($definition['callable'] === $listener) {
unset($definitions[$key]);
}
}
if ($definitions) {
$this->listeners[$event_name][$priority] = $definitions;
}
else {
unset($this->listeners[$event_name][$priority]);
}
}
}
@@ -3,11 +3,11 @@
"description": "EventDispatcher.",
"keywords": ["drupal"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9",
"symfony/dependency-injection": "^2.8",
"symfony/event-dispatcher": "^2.7"
"symfony/dependency-injection": ">=2.8 <4.0.0",
"symfony/event-dispatcher": ">=2.7 <4.0.0"
},
"autoload": {
"psr-4": {
@@ -3,7 +3,7 @@
"description": "FileCache.",
"keywords": ["drupal"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9"
},
@@ -3,7 +3,7 @@
"description": "FileSystem.",
"keywords": ["drupal"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9"
},
@@ -188,7 +188,7 @@ class PoHeader {
* - 'nplurals': The number of plural forms defined by the plural formula.
* - 'plurals': Array of plural positions keyed by plural value.
*
* @throws Exception
* @throws \Exception
*/
public function parsePluralForms($pluralforms) {
$plurals = [];
@@ -473,7 +473,7 @@ class PoHeader {
* Number of the plural string to be used for the given plural value.
*
* @see parseArithmetic()
* @throws Exception
* @throws \Exception
*/
protected function evaluatePlural($element_stack, $n) {
$count = count($element_stack);
@@ -146,7 +146,7 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
* Opens the stream and reads the header. The stream is ready for reading
* items after.
*
* @throws Exception
* @throws \Exception
* If the URI is not yet set.
*/
public function open() {
@@ -162,7 +162,7 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
/**
* Implements Drupal\Component\Gettext\PoStreamInterface::close().
*
* @throws Exception
* @throws \Exception
* If the stream is not open.
*/
public function close() {
@@ -506,6 +506,8 @@ class PoStreamReader implements PoStreamInterface, PoReaderInterface {
$this->_errors[] = SafeMarkup::format('The translation stream %uri ended unexpectedly at line %line.', $log_vars);
return FALSE;
}
return;
}
/**
@@ -81,7 +81,7 @@ class PoStreamWriter implements PoWriterInterface, PoStreamInterface {
/**
* Implements Drupal\Component\Gettext\PoStreamInterface::close().
*
* @throws Exception
* @throws \Exception
* If the stream is not open.
*/
public function close() {
@@ -89,7 +89,7 @@ class PoStreamWriter implements PoWriterInterface, PoStreamInterface {
fclose($this->_fd);
}
else {
throw new Exception('Cannot close stream that is not open.');
throw new \Exception('Cannot close stream that is not open.');
}
}
@@ -100,13 +100,13 @@ class PoStreamWriter implements PoWriterInterface, PoStreamInterface {
* Piece of string to write to the stream. If the value is not directly a
* string, casting will happen in writing.
*
* @throws Exception
* @throws \Exception
* If writing the data is not possible.
*/
private function write($data) {
$result = fwrite($this->_fd, $data);
if ($result === FALSE) {
throw new Exception('Unable to write data: ' . substr($data, 0, 20));
if ($result === FALSE || $result != strlen($data)) {
throw new \Exception('Unable to write data: ' . substr($data, 0, 20));
}
}
@@ -137,12 +137,12 @@ class PoStreamWriter implements PoWriterInterface, PoStreamInterface {
/**
* Implements Drupal\Component\Gettext\PoStreamInterface::getURI().
*
* @throws Exception
* @throws \Exception
* If the URI is not set.
*/
public function getURI() {
if (empty($this->_uri)) {
throw new Exception('No URI set.');
throw new \Exception('No URI set.');
}
return $this->_uri;
}
@@ -2,7 +2,7 @@
"name": "drupal/core-gettext",
"description": "PHP library for reading PO files.",
"type": "library",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"support": {
"issues": "https://www.drupal.org/project/issues/drupal",
"irc": "irc://irc.freenode.net/drupal-contribute",
@@ -3,7 +3,7 @@
"description": "Graph.",
"keywords": ["drupal"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9"
},
@@ -3,10 +3,10 @@
"description": "HttpFoundation.",
"keywords": ["drupal"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9",
"symfony/http-foundation": "^2.7"
"symfony/http-foundation": ">=2.7 <4.0.0"
},
"autoload": {
"psr-4": {
@@ -3,7 +3,7 @@
"description": "PhpStorage.",
"keywords": ["drupal"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9"
},
@@ -3,10 +3,10 @@
"description": "Base building block for a scalable and extensible plug-in system for PHP components and application framework extensions.",
"keywords": ["drupal", "plugin", "plugins"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9",
"symfony/validator": "^2.7"
"symfony/validator": ">=2.7 <4.0.0"
},
"autoload": {
"psr-4": {
@@ -3,7 +3,7 @@
"description": "Provides a lightweight mechanism to provide lazy loaded proxies.",
"keywords": ["drupal", "proxy"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9"
},
@@ -61,6 +61,13 @@ use Drupal\Component\Utility\UrlHelper;
*/
class FormattableMarkup implements MarkupInterface, \Countable {
/**
* The string containing placeholders.
*
* @var string
*/
protected $string;
/**
* The arguments to replace placeholders with.
*
@@ -3,7 +3,7 @@
"description": "Renders placeholder variables for HTML and plain-text display.",
"keywords": ["drupal"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9",
"drupal/core-utility": "^8.2"
@@ -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.
@@ -47,7 +47,7 @@ class YamlPecl implements SerializationInterface {
// and then restore it after decoding has occurred. This allows us to turn
// parsing errors into a throwable exception.
// @see Drupal\Component\Serialization\Exception\InvalidDataTypeException
// @see http://php.net/manual/en/class.errorexception.php
// @see http://php.net/manual/class.errorexception.php
set_error_handler([__CLASS__, 'errorHandler']);
$ndocs = 0;
$data = yaml_parse($raw, 0, $ndocs, [
@@ -34,7 +34,7 @@ class YamlSymfony implements SerializationInterface {
$yaml = new Parser();
// Make sure we have a single trailing newline. A very simple config like
// 'foo: bar' with no newline will fail to parse otherwise.
return $yaml->parse($raw, SymfonyYaml::PARSE_EXCEPTION_ON_INVALID_TYPE);
return $yaml->parse($raw, SymfonyYaml::PARSE_EXCEPTION_ON_INVALID_TYPE | SymfonyYaml::PARSE_KEYS_AS_STRINGS);
}
catch (\Exception $e) {
throw new InvalidDataTypeException($e->getMessage(), $e->getCode(), $e);
@@ -3,10 +3,10 @@
"description": "Serialization.",
"keywords": ["drupal"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9",
"symfony/yaml": "^2.7"
"symfony/yaml": ">=2.7 <4.0.0"
},
"autoload": {
"psr-4": {
@@ -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.
@@ -2,7 +2,7 @@
"name": "drupal/core-transliteration",
"description": "Transliteration.",
"type": "library",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"support": {
"issues": "https://www.drupal.org/project/issues/drupal",
"irc": "irc://irc.freenode.net/drupal-contribute",
@@ -94,4 +94,28 @@ class Color {
return '#' . str_pad(dechex($out), 6, 0, STR_PAD_LEFT);
}
/**
* Normalize the hex color length to 6 characters for comparison.
*
* @param string $hex
* The hex color to normalize.
*
* @return string
* The 6 character hex color.
*/
public static function normalizeHexLength($hex) {
// Ignore '#' prefixes.
$hex = ltrim($hex, '#');
if (strlen($hex) === 3) {
$hex[5] = $hex[2];
$hex[4] = $hex[2];
$hex[3] = $hex[1];
$hex[2] = $hex[1];
$hex[1] = $hex[0];
}
return '#' . $hex;
}
}
+3 -3
View File
@@ -451,9 +451,9 @@ EOD;
* The updated (X)HTML snippet.
*/
public static function transformRootRelativeUrlsToAbsolute($html, $scheme_and_host) {
assert('empty(array_diff(array_keys(parse_url($scheme_and_host)), ["scheme", "host", "port"]))', '$scheme_and_host contains scheme, host and port at most.');
assert('isset(parse_url($scheme_and_host)["scheme"])', '$scheme_and_host is absolute and hence has a scheme.');
assert('isset(parse_url($scheme_and_host)["host"])', '$base_url is absolute and hence has a host.');
assert(empty(array_diff(array_keys(parse_url($scheme_and_host)), ["scheme", "host", "port"])), '$scheme_and_host contains scheme, host and port at most.');
assert(isset(parse_url($scheme_and_host)["scheme"]), '$scheme_and_host is absolute and hence has a scheme.');
assert(isset(parse_url($scheme_and_host)["host"]), '$base_url is absolute and hence has a host.');
$html_dom = Html::load($html);
$xpath = new \DOMXpath($html_dom);
@@ -69,7 +69,7 @@ class NestedArray {
public static function &getValue(array &$array, array $parents, &$key_exists = NULL) {
$ref = &$array;
foreach ($parents as $parent) {
if (is_array($ref) && array_key_exists($parent, $ref)) {
if (is_array($ref) && (isset($ref[$parent]) || array_key_exists($parent, $ref))) {
$ref = &$ref[$parent];
}
else {
@@ -219,7 +219,7 @@ class NestedArray {
public static function unsetValue(array &$array, array $parents, &$key_existed = NULL) {
$unset_key = array_pop($parents);
$ref = &self::getValue($array, $parents, $key_existed);
if ($key_existed && is_array($ref) && array_key_exists($unset_key, $ref)) {
if ($key_existed && is_array($ref) && (isset($ref[$unset_key]) || array_key_exists($unset_key, $ref))) {
$key_existed = TRUE;
unset($ref[$unset_key]);
}
@@ -548,7 +548,7 @@ EOD;
// Find the last word boundary, if there is one within $min_wordsafe_length
// to $max_length characters. preg_match() is always greedy, so it will
// find the longest string possible.
$found = preg_match('/^(.{' . $min_wordsafe_length . ',' . $max_length . '})[' . Unicode::PREG_CLASS_WORD_BOUNDARY . ']/u', $string, $matches);
$found = preg_match('/^(.{' . $min_wordsafe_length . ',' . $max_length . '})[' . Unicode::PREG_CLASS_WORD_BOUNDARY . ']/us', $string, $matches);
if ($found) {
$string = $matches[1];
}
@@ -3,7 +3,7 @@
"description": "Mostly static utility classes for string, xss, array, image, and other commonly needed manipulations.",
"keywords": ["drupal"],
"homepage": "https://www.drupal.org/project/drupal",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"require": {
"php": ">=5.5.9",
"paragonie/random_compat": "^1.0|^2.0",
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "drupal/core-uuid",
"description": "PHP library for reading PO files.",
"type": "library",
"license": "GPL-2.0+",
"license": "GPL-2.0-or-later",
"support": {
"issues": "https://www.drupal.org/project/issues/drupal",
"irc": "irc://irc.freenode.net/drupal-contribute",
+7 -4
View File
@@ -39,7 +39,7 @@ abstract class AccessResult implements AccessResultInterface, RefinableCacheable
* isNeutral() will be TRUE.
*/
public static function neutral($reason = NULL) {
assert('is_string($reason) || is_null($reason)');
assert(is_string($reason) || is_null($reason));
return new AccessResultNeutral($reason);
}
@@ -64,7 +64,7 @@ abstract class AccessResult implements AccessResultInterface, RefinableCacheable
* isForbidden() will be TRUE.
*/
public static function forbidden($reason = NULL) {
assert('is_string($reason) || is_null($reason)');
assert(is_string($reason) || is_null($reason));
return new AccessResultForbidden($reason);
}
@@ -87,13 +87,16 @@ abstract class AccessResult implements AccessResultInterface, RefinableCacheable
*
* @param bool $condition
* The condition to evaluate.
* @param string|null $reason
* (optional) The reason why access is forbidden. Intended for developers,
* hence not translatable
*
* @return \Drupal\Core\Access\AccessResult
* If $condition is TRUE, isForbidden() will be TRUE, otherwise isNeutral()
* will be TRUE.
*/
public static function forbiddenIf($condition) {
return $condition ? static::forbidden() : static::neutral();
public static function forbiddenIf($condition, $reason = NULL) {
return $condition ? static::forbidden($reason) : static::neutral();
}
/**
@@ -0,0 +1,89 @@
<?php
namespace Drupal\Core\Action\Plugin\Action\Derivative;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a base action for each entity type with specific interfaces.
*/
abstract class EntityActionDeriverBase extends DeriverBase implements ContainerDeriverInterface {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Constructs a new EntityActionDeriverBase object.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $base_plugin_id) {
return new static($container->get('entity_type.manager'));
}
/**
* Indicates whether the deriver can be used for the provided entity type.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type.
*
* @return bool
* TRUE if the entity type can be used, FALSE otherwise.
*/
abstract protected function isApplicable(EntityTypeInterface $entity_type);
/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
if (empty($this->derivatives)) {
$definitions = [];
foreach ($this->getApplicableEntityTypes() as $entity_type_id => $entity_type) {
$definition = $base_plugin_definition;
$definition['type'] = $entity_type_id;
$definition['label'] = sprintf('%s %s', $base_plugin_definition['action_label'], $entity_type->getSingularLabel());
$definitions[$entity_type_id] = $definition;
}
$this->derivatives = $definitions;
}
return parent::getDerivativeDefinitions($base_plugin_definition);
}
/**
* Gets a list of applicable entity types.
*
* The list consists of all entity types which match the conditions for the
* given deriver.
* For example, if the action applies to entities that are publishable,
* this method will find all entity types that are publishable.
*
* @return \Drupal\Core\Entity\EntityTypeInterface[]
* The applicable entity types, keyed by entity type ID.
*/
protected function getApplicableEntityTypes() {
$entity_types = $this->entityTypeManager->getDefinitions();
$entity_types = array_filter($entity_types, function (EntityTypeInterface $entity_type) {
return $this->isApplicable($entity_type);
});
return $entity_types;
}
}
@@ -0,0 +1,22 @@
<?php
namespace Drupal\Core\Action\Plugin\Action\Derivative;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Provides an action deriver that finds entity types of EntityChangedInterface.
*
* @see \Drupal\Core\Action\Plugin\Action\SaveAction
*/
class EntityChangedActionDeriver extends EntityActionDeriverBase {
/**
* {@inheritdoc}
*/
protected function isApplicable(EntityTypeInterface $entity_type) {
return $entity_type->entityClassImplements(EntityChangedInterface::class);
}
}
@@ -0,0 +1,23 @@
<?php
namespace Drupal\Core\Action\Plugin\Action\Derivative;
use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Provides an action deriver that finds publishable entity types.
*
* @see \Drupal\Core\Action\Plugin\Action\PublishAction
* @see \Drupal\Core\Action\Plugin\Action\UnpublishAction
*/
class EntityPublishedActionDeriver extends EntityActionDeriverBase {
/**
* {@inheritdoc}
*/
protected function isApplicable(EntityTypeInterface $entity_type) {
return $entity_type->entityClassImplements(EntityPublishedInterface::class);
}
}
@@ -0,0 +1,62 @@
<?php
namespace Drupal\Core\Action\Plugin\Action;
use Drupal\Component\Plugin\DependentPluginInterface;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Base class for entity-based actions.
*/
abstract class EntityActionBase extends ActionBase implements DependentPluginInterface, ContainerFactoryPluginInterface {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Constructs a EntityActionBase object.
*
* @param mixed[] $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager')
);
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
$module_name = $this->entityTypeManager
->getDefinition($this->getPluginDefinition()['type'])
->getProvider();
return ['module' => [$module_name]];
}
}
@@ -0,0 +1,38 @@
<?php
namespace Drupal\Core\Action\Plugin\Action;
use Drupal\Core\Session\AccountInterface;
/**
* Publishes an entity.
*
* @Action(
* id = "entity:publish_action",
* action_label = @Translation("Publish"),
* deriver = "Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver",
* )
*/
class PublishAction extends EntityActionBase {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setPublished()->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
$key = $object->getEntityType()->getKey('published');
/** @var \Drupal\Core\Entity\EntityInterface $object */
$result = $object->access('update', $account, TRUE)
->andIf($object->$key->access('edit', $account, TRUE));
return $return_as_object ? $result : $result->isAllowed();
}
}
@@ -0,0 +1,79 @@
<?php
namespace Drupal\Core\Action\Plugin\Action;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides an action that can save any entity.
*
* @Action(
* id = "entity:save_action",
* action_label = @Translation("Save"),
* deriver = "Drupal\Core\Action\Plugin\Action\Derivative\EntityChangedActionDeriver",
* )
*/
class SaveAction extends EntityActionBase {
/**
* The time service.
*
* @var \Drupal\Component\Datetime\TimeInterface
*/
protected $time;
/**
* Constructs a SaveAction object.
*
* @param mixed[] $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, TimeInterface $time) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
$this->time = $time;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
$container->get('datetime.time')
);
}
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setChangedTime($this->time->getRequestTime())->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
// It's not necessary to check the changed field access here, because
// Drupal\Core\Field\ChangedFieldItemList would anyway return 'not allowed'.
// Also changing the changed field value is only a workaround to trigger an
// entity resave. Without a field change, this would not be possible.
/** @var \Drupal\Core\Entity\EntityInterface $object */
return $object->access('update', $account, $return_as_object);
}
}
@@ -0,0 +1,38 @@
<?php
namespace Drupal\Core\Action\Plugin\Action;
use Drupal\Core\Session\AccountInterface;
/**
* Unpublishes an entity.
*
* @Action(
* id = "entity:unpublish_action",
* action_label = @Translation("Unpublish"),
* deriver = "Drupal\Core\Action\Plugin\Action\Derivative\EntityPublishedActionDeriver",
* )
*/
class UnpublishAction extends EntityActionBase {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setUnpublished()->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
$key = $object->getEntityType()->getKey('published');
/** @var \Drupal\Core\Entity\EntityInterface $object */
$result = $object->access('update', $account, TRUE)
->andIf($object->$key->access('edit', $account, TRUE));
return $return_as_object ? $result : $result->isAllowed();
}
}
@@ -0,0 +1,69 @@
<?php
namespace Drupal\Core\Ajax;
/**
* Defines an AJAX command to open content in a dialog in a off-canvas dialog.
*
* @ingroup ajax
*/
class OpenOffCanvasDialogCommand extends OpenDialogCommand {
/**
* The dialog width to use if none is provided.
*/
const DEFAULT_DIALOG_WIDTH = 300;
/**
* Constructs an OpenOffCanvasDialogCommand object.
*
* The off-canvas dialog differs from the normal modal provided by
* OpenDialogCommand in that a off-canvas has built in positioning and
* behaviours. Drupal provides a built-in off-canvas dialog for this purpose,
* so the selector is hard-coded in the call to the parent constructor.
*
* @param string $title
* The title of the dialog.
* @param string|array $content
* The content that will be placed in the dialog, either a render array
* or an HTML string.
* @param array $dialog_options
* (optional) Settings to be passed to the dialog implementation. Any
* jQuery UI option can be used. See http://api.jqueryui.com/dialog.
* @param array|null $settings
* (optional) Custom settings that will be passed to the Drupal behaviors
* on the content of the dialog. If left empty, the settings will be
* populated automatically from the current request.
*/
public function __construct($title, $content, array $dialog_options = [], $settings = NULL) {
parent::__construct('#drupal-off-canvas', $title, $content, $dialog_options, $settings);
$this->dialogOptions['modal'] = FALSE;
$this->dialogOptions['autoResize'] = FALSE;
$this->dialogOptions['resizable'] = 'w';
$this->dialogOptions['draggable'] = FALSE;
$this->dialogOptions['drupalAutoButtons'] = FALSE;
// @todo drupal.ajax.js does not respect drupalAutoButtons properly, pass an
// empty set of buttons until https://www.drupal.org/node/2793343 is in.
$this->dialogOptions['buttons'] = [];
if (empty($dialog_options['dialogClass'])) {
$this->dialogOptions['dialogClass'] = 'ui-dialog-off-canvas';
}
// If no width option is provided then use the default width to avoid the
// dialog staying at the width of the previous instance when opened
// more than once, with different widths, on a single page.
if (!isset($this->dialogOptions['width'])) {
$this->dialogOptions['width'] = static::DEFAULT_DIALOG_WIDTH;
}
}
/**
* {@inheritdoc}
*/
public function render() {
$build = parent::render();
$build['effect'] = 'fade';
$build['speed'] = 1000;
return $build;
}
}
@@ -129,11 +129,11 @@ class LibraryDiscoveryParser {
// properly resolve dependencies for all (css) libraries per category,
// and only once prior to rendering out an HTML page.
if ($type == 'css' && !empty($library[$type])) {
assert('\Drupal\Core\Asset\LibraryDiscoveryParser::validateCssLibrary($library[$type]) < 2', 'CSS files should be specified as key/value pairs, where the values are configuration options. See https://www.drupal.org/node/2274843.');
assert('\Drupal\Core\Asset\LibraryDiscoveryParser::validateCssLibrary($library[$type]) === 0', 'CSS must be nested under a category. See https://www.drupal.org/node/2274843.');
assert(static::validateCssLibrary($library[$type]) < 2, 'CSS files should be specified as key/value pairs, where the values are configuration options. See https://www.drupal.org/node/2274843.');
assert(static::validateCssLibrary($library[$type]) === 0, 'CSS must be nested under a category. See https://www.drupal.org/node/2274843.');
foreach ($library[$type] as $category => $files) {
$category_weight = 'CSS_' . strtoupper($category);
assert('defined($category_weight)', 'Invalid CSS category: ' . $category . '. See https://www.drupal.org/node/2274843.');
assert(defined($category_weight), 'Invalid CSS category: ' . $category . '. See https://www.drupal.org/node/2274843.');
foreach ($files as $source => $options) {
if (!isset($options['weight'])) {
$options['weight'] = 0;
@@ -11,6 +11,9 @@ use Drupal\Core\Block\TitleBlockPluginInterface;
* @Block(
* id = "page_title_block",
* admin_label = @Translation("Page title"),
* forms = {
* "settings_tray" = FALSE,
* },
* )
*/
class PageTitleBlock extends BlockBase implements TitleBlockPluginInterface {
+3 -1
View File
@@ -2,6 +2,8 @@
namespace Drupal\Core\Cache;
use Drupal\Component\Assertion\Inspector;
/**
* Stores cache items in the Alternative PHP Cache User Cache (APCu).
*/
@@ -161,7 +163,7 @@ class ApcuBackend implements CacheBackendInterface {
* {@inheritdoc}
*/
public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = []) {
assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($tags)', 'Cache tags must be strings.');
assert(Inspector::assertAllStrings($tags), 'Cache tags must be strings.');
$tags = array_unique($tags);
$cache = new \stdClass();
$cache->cid = $cid;
+4 -3
View File
@@ -2,6 +2,7 @@
namespace Drupal\Core\Cache;
use Drupal\Component\Assertion\Inspector;
use Drupal\Core\Database\Query\SelectInterface;
/**
@@ -29,7 +30,7 @@ class Cache {
*/
public static function mergeContexts(array $a = [], array $b = []) {
$cache_contexts = array_unique(array_merge($a, $b));
assert('\Drupal::service(\'cache_contexts_manager\')->assertValidTokens($cache_contexts)');
assert(\Drupal::service('cache_contexts_manager')->assertValidTokens($cache_contexts));
sort($cache_contexts);
return $cache_contexts;
}
@@ -54,7 +55,7 @@ class Cache {
* The merged array of cache tags.
*/
public static function mergeTags(array $a = [], array $b = []) {
assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($a) && \Drupal\Component\Assertion\Inspector::assertAllStrings($b)', 'Cache tags must be valid strings');
assert(Inspector::assertAllStrings($a) && Inspector::assertAllStrings($b), 'Cache tags must be valid strings');
$cache_tags = array_unique(array_merge($a, $b));
sort($cache_tags);
@@ -96,7 +97,7 @@ class Cache {
* An array of cache tags.
*
* @deprecated
* Use assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($tags)');
* Use assert(Inspector::assertAllStrings($tags));
*
* @throws \LogicException
*/
@@ -2,6 +2,7 @@
namespace Drupal\Core\Cache;
use Drupal\Component\Assertion\Inspector;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\DestructableInterface;
use Drupal\Core\Lock\LockBackendInterface;
@@ -111,7 +112,7 @@ abstract class CacheCollector implements CacheCollectorInterface, DestructableIn
* (optional) The tags to specify for the cache item.
*/
public function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, array $tags = []) {
assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($tags)', 'Cache tags must be strings.');
assert(Inspector::assertAllStrings($tags), 'Cache tags must be strings.');
$this->cid = $cid;
$this->cache = $cache;
$this->tags = $tags;
@@ -2,6 +2,7 @@
namespace Drupal\Core\Cache;
use Drupal\Component\Assertion\Inspector;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
/**
@@ -22,7 +23,7 @@ class CacheTagsInvalidator implements CacheTagsInvalidatorInterface {
* {@inheritdoc}
*/
public function invalidateTags(array $tags) {
assert('Drupal\Component\Assertion\Inspector::assertAllStrings($tags)', 'Cache tags must be strings.');
assert(Inspector::assertAllStrings($tags), 'Cache tags must be strings.');
// Notify all added cache tags invalidators.
foreach ($this->invalidators as $invalidator) {
@@ -0,0 +1,67 @@
<?php
namespace Drupal\Core\Cache;
/**
* Trait for \Drupal\Core\Cache\CacheableDependencyInterface.
*/
trait CacheableDependencyTrait {
/**
* Cache contexts.
*
* @var string[]
*/
protected $cacheContexts = [];
/**
* Cache tags.
*
* @var string[]
*/
protected $cacheTags = [];
/**
* Cache max-age.
*
* @var int
*/
protected $cacheMaxAge = Cache::PERMANENT;
/**
* Sets cacheability; useful for value object constructors.
*
* @param \Drupal\Core\Cache\CacheableDependencyInterface $cacheability
* The cacheability to set.
*
* @return $this
*/
protected function setCacheability(CacheableDependencyInterface $cacheability) {
$this->cacheContexts = $cacheability->getCacheContexts();
$this->cacheTags = $cacheability->getCacheTags();
$this->cacheMaxAge = $cacheability->getCacheMaxAge();
return $this;
}
/**
* {@inheritdoc}
*/
public function getCacheTags() {
return $this->cacheTags;
}
/**
* {@inheritdoc}
*/
public function getCacheContexts() {
return $this->cacheContexts;
}
/**
* {@inheritdoc}
*/
public function getCacheMaxAge() {
return $this->cacheMaxAge;
}
}
@@ -100,7 +100,7 @@ class CacheContextsManager {
* cacheability metadata.
*/
public function convertTokensToKeys(array $context_tokens) {
assert('$this->assertValidTokens($context_tokens)');
assert($this->assertValidTokens($context_tokens));
$cacheable_metadata = new CacheableMetadata();
$optimized_tokens = $this->optimizeTokens($context_tokens);
// Iterate over cache contexts that have been optimized away and get their
@@ -2,6 +2,7 @@
namespace Drupal\Core\Cache;
use Drupal\Component\Assertion\Inspector;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\SchemaObjectExistsException;
@@ -222,7 +223,7 @@ class DatabaseBackend implements CacheBackendInterface {
'tags' => [],
];
assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($item[\'tags\'])', 'Cache Tags must be strings.');
assert(Inspector::assertAllStrings($item['tags']), 'Cache Tags must be strings.');
$item['tags'] = array_unique($item['tags']);
// Sort the cache tags so that they are stored consistently in the DB.
sort($item['tags']);
+3 -1
View File
@@ -2,6 +2,8 @@
namespace Drupal\Core\Cache;
use Drupal\Component\Assertion\Inspector;
/**
* Defines a memory cache implementation.
*
@@ -98,7 +100,7 @@ class MemoryBackend implements CacheBackendInterface, CacheTagsInvalidatorInterf
* {@inheritdoc}
*/
public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = []) {
assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($tags)', 'Cache Tags must be strings.');
assert(Inspector::assertAllStrings($tags), 'Cache Tags must be strings.');
$tags = array_unique($tags);
// Sort the cache tags so that they are stored consistently in the database.
sort($tags);
+3 -1
View File
@@ -2,6 +2,7 @@
namespace Drupal\Core\Cache;
use Drupal\Component\Assertion\Inspector;
use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Component\Utility\Crypt;
@@ -143,7 +144,8 @@ class PhpBackend implements CacheBackendInterface {
* {@inheritdoc}
*/
public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = []) {
assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($tags)', 'Cache Tags must be strings.');
assert(Inspector::assertAllStrings($tags), 'Cache Tags must be strings.');
$item = (object) [
'cid' => $cid,
'data' => $data,
@@ -7,47 +7,7 @@ namespace Drupal\Core\Cache;
*/
trait RefinableCacheableDependencyTrait {
/**
* Cache contexts.
*
* @var string[]
*/
protected $cacheContexts = [];
/**
* Cache tags.
*
* @var string[]
*/
protected $cacheTags = [];
/**
* Cache max-age.
*
* @var int
*/
protected $cacheMaxAge = Cache::PERMANENT;
/**
* {@inheritdoc}
*/
public function getCacheTags() {
return $this->cacheTags;
}
/**
* {@inheritdoc}
*/
public function getCacheContexts() {
return $this->cacheContexts;
}
/**
* {@inheritdoc}
*/
public function getCacheMaxAge() {
return $this->cacheMaxAge;
}
use CacheableDependencyTrait;
/**
* {@inheritdoc}
@@ -143,6 +143,48 @@ EOT;
}
}
/**
* Fires the drupal-phpunit-upgrade script event if necessary.
*
* @param \Composer\Script\Event $event
*/
public static function upgradePHPUnit(Event $event) {
$repository = $event->getComposer()->getRepositoryManager()->getLocalRepository();
// This is, essentially, a null constraint. We only care whether the package
// is present in the vendor directory yet, but findPackage() requires it.
$constraint = new Constraint('>', '');
$phpunit_package = $repository->findPackage('phpunit/phpunit', $constraint);
if (!$phpunit_package) {
// There is nothing to do. The user is probably installing using the
// --no-dev flag.
return;
}
// If the PHP version is 7.2 or above and PHPUnit is less than version 6
// call the drupal-phpunit-upgrade script to upgrade PHPUnit.
if (!static::upgradePHPUnitCheck($phpunit_package->getVersion())) {
$event->getComposer()
->getEventDispatcher()
->dispatchScript('drupal-phpunit-upgrade');
}
}
/**
* Determines if PHPUnit needs to be upgraded.
*
* This method is located in this file because it is possible that it is
* called before the autoloader is available.
*
* @param string $phpunit_version
* The PHPUnit version string.
*
* @return bool
* TRUE if the PHPUnit needs to be upgraded, FALSE if not.
*/
public static function upgradePHPUnitCheck($phpunit_version) {
return !(version_compare(PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION, '7.2') >= 0 && version_compare($phpunit_version, '6.1') < 0);
}
/**
* Remove possibly problematic test files from vendored projects.
*
+38
View File
@@ -303,4 +303,42 @@ class Config extends StorableConfigBase {
}
}
/**
* Determines if overrides are applied to a key for this configuration object.
*
* @param string $key
* (optional) A string that maps to a key within the configuration data.
* For instance in the following configuration array:
* @code
* array(
* 'foo' => array(
* 'bar' => 'baz',
* ),
* );
* @endcode
* A key of 'foo.bar' would map to the string 'baz'. However, a key of 'foo'
* would map to the array('bar' => 'baz').
* If not supplied TRUE will be returned if there are any overrides at all
* for this configuration object.
*
* @return bool
* TRUE if there are any overrides for the key, otherwise FALSE.
*/
public function hasOverrides($key = '') {
if (empty($key)) {
return !(empty($this->moduleOverrides) && empty($this->settingsOverrides));
}
else {
$parts = explode('.', $key);
$override_exists = FALSE;
if (isset($this->moduleOverrides) && is_array($this->moduleOverrides)) {
$override_exists = NestedArray::keyExists($this->moduleOverrides, $parts);
}
if (!$override_exists && isset($this->settingsOverrides) && is_array($this->settingsOverrides)) {
$override_exists = NestedArray::keyExists($this->settingsOverrides, $parts);
}
return $override_exists;
}
}
}
@@ -788,9 +788,8 @@ class ConfigImporter {
// services.
$this->reInjectMe();
// During a module install or uninstall the container is rebuilt and the
// module handler is called from drupal_get_complete_schema(). This causes
// the container's instance of the module handler not to have loaded all
// the enabled modules.
// module handler is called. This causes the container's instance of the
// module handler not to have loaded all the enabled modules.
$this->moduleHandler->loadAll();
}
if ($type == 'theme') {
@@ -37,14 +37,14 @@ class ConfigEntityListBuilder extends EntityListBuilder {
$operations['enable'] = [
'title' => t('Enable'),
'weight' => -10,
'url' => $entity->urlInfo('enable'),
'url' => $this->ensureDestination($entity->toUrl('enable')),
];
}
elseif ($entity->hasLinkTemplate('disable')) {
$operations['disable'] = [
'title' => t('Disable'),
'weight' => 40,
'url' => $entity->urlInfo('disable'),
'url' => $this->ensureDestination($entity->toUrl('disable')),
];
}
}
@@ -9,6 +9,7 @@ use Drupal\Core\Routing\RedirectDestinationTrait;
use Drupal\Core\Routing\UrlGeneratorTrait;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Messenger\MessengerTrait;
/**
* Utility base class for thin controllers.
@@ -35,6 +36,7 @@ abstract class ControllerBase implements ContainerInjectionInterface {
use LinkGeneratorTrait;
use LoggerChannelTrait;
use MessengerTrait;
use RedirectDestinationTrait;
use StringTranslationTrait;
use UrlGeneratorTrait;
@@ -10,8 +10,6 @@ use Drupal\Core\DependencyInjection\Compiler\CorsCompilerPass;
use Drupal\Core\DependencyInjection\Compiler\GuzzleMiddlewarePass;
use Drupal\Core\DependencyInjection\Compiler\ContextProvidersPass;
use Drupal\Core\DependencyInjection\Compiler\ProxyServicesPass;
use Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteEnhancers;
use Drupal\Core\DependencyInjection\Compiler\RegisterLazyRouteFilters;
use Drupal\Core\DependencyInjection\Compiler\DependencySerializationTraitPass;
use Drupal\Core\DependencyInjection\Compiler\StackedKernelPass;
use Drupal\Core\DependencyInjection\Compiler\StackedSessionHandlerPass;
@@ -84,8 +82,6 @@ class CoreServiceProvider implements ServiceProviderInterface, ServiceModifierIn
$container->addCompilerPass(new RegisterEventSubscribersPass(), PassConfig::TYPE_AFTER_REMOVING);
$container->addCompilerPass(new RegisterAccessChecksPass());
$container->addCompilerPass(new RegisterLazyRouteEnhancers());
$container->addCompilerPass(new RegisterLazyRouteFilters());
// Add a compiler pass for registering services needing destruction.
$container->addCompilerPass(new RegisterServicesForDestructionPass());
+14 -2
View File
@@ -2,6 +2,7 @@
namespace Drupal\Core;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Component\Utility\Timer;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Queue\QueueWorkerManagerInterface;
@@ -69,6 +70,13 @@ class Cron implements CronInterface {
*/
protected $queueManager;
/**
* The time service.
*
* @var \Drupal\Component\Datetime\TimeInterface
*/
protected $time;
/**
* Constructs a cron object.
*
@@ -86,8 +94,10 @@ class Cron implements CronInterface {
* A logger instance.
* @param \Drupal\Core\Queue\QueueWorkerManagerInterface $queue_manager
* The queue plugin manager.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
*/
public function __construct(ModuleHandlerInterface $module_handler, LockBackendInterface $lock, QueueFactory $queue_factory, StateInterface $state, AccountSwitcherInterface $account_switcher, LoggerInterface $logger, QueueWorkerManagerInterface $queue_manager) {
public function __construct(ModuleHandlerInterface $module_handler, LockBackendInterface $lock, QueueFactory $queue_factory, StateInterface $state, AccountSwitcherInterface $account_switcher, LoggerInterface $logger, QueueWorkerManagerInterface $queue_manager, TimeInterface $time = NULL) {
$this->moduleHandler = $module_handler;
$this->lock = $lock;
$this->queueFactory = $queue_factory;
@@ -95,6 +105,7 @@ class Cron implements CronInterface {
$this->accountSwitcher = $account_switcher;
$this->logger = $logger;
$this->queueManager = $queue_manager;
$this->time = $time ?: \Drupal::service('datetime.time');
}
/**
@@ -143,7 +154,8 @@ class Cron implements CronInterface {
*/
protected function setCronLastTime() {
// Record cron time.
$this->state->set('system.cron_last', REQUEST_TIME);
$request_time = $this->time->getRequestTime();
$this->state->set('system.cron_last', $request_time);
$this->logger->notice('Cron run completed.');
}
@@ -108,6 +108,16 @@ class Tasks extends InstallTasks {
// Now, attempt the connection again; if it's successful, attempt to
// create the database.
Database::getConnection()->createDatabase($database);
Database::closeConnection();
// Now, restore the database config.
Database::removeConnection('default');
$connection_info['default']['database'] = $database;
Database::addConnectionInfo('default', 'default', $connection_info['default']);
// Check the database connection.
Database::getConnection();
$this->pass('Drupal can CONNECT to the database ok.');
}
catch (DatabaseNotFoundException $e) {
// Still no dice; probably a permission issue. Raise the error to the
@@ -628,6 +628,15 @@ EOD;
$this->connection->query('ALTER TABLE {' . $table . '} ALTER COLUMN "' . $field . '" DROP DEFAULT');
}
/**
* {@inheritdoc}
*/
public function fieldExists($table, $column) {
$prefixInfo = $this->getPrefixInfo($table);
return (bool) $this->connection->query("SELECT 1 FROM pg_attribute WHERE attrelid = :key::regclass AND attname = :column AND NOT attisdropped AND attnum > 0", [':key' => $prefixInfo['schema'] . '.' . $prefixInfo['table'], ':column' => $column])->fetchField();
}
/**
* {@inheritdoc}
*/
@@ -413,7 +413,7 @@ class Schema extends DatabaseSchema {
// Now add the fields.
foreach ($mapping as $field_alias => $field_source) {
// Just ignore this field (ie. use it's default value).
// Just ignore this field (ie. use its default value).
if (!isset($field_source)) {
continue;
}
@@ -450,7 +450,7 @@ class Schema extends DatabaseSchema {
* Name of the table.
*
* @return
* An array representing the schema, from drupal_get_schema().
* An array representing the schema.
*
* @throws \Exception
* If a column of the table could not be parsed.
@@ -174,7 +174,7 @@ class Merge extends Query implements ConditionInterface {
* Specifies fields to be updated as an expression.
*
* Expression fields are cases such as counter = counter + 1. This method
* takes precedence over MergeQuery::updateFields() and it's wrappers,
* takes precedence over MergeQuery::updateFields() and its wrappers,
* MergeQuery::key() and MergeQuery::fields().
*
* @param $field
@@ -95,7 +95,7 @@ interface SelectInterface extends ConditionInterface, AlterableInterface, Extend
* Note that this method must be called by reference as well:
*
* @code
* $fields =& $query->getTables();
* $tables =& $query->getTables();
* @endcode
*
* @return
@@ -38,7 +38,7 @@ class DrupalDateTime extends DateTimePlus {
* timezone are ignored when the $time parameter either is a UNIX timestamp
* (e.g. @946684800) or specifies a timezone
* (e.g. 2010-01-28T15:00:00+02:00).
* @see http://php.net/manual/en/datetime.construct.php
* @see http://php.net/manual/datetime.construct.php
* @param array $settings
* - validate_format: (optional) Boolean choice to validate the
* created date using the input format. The format used in
@@ -1,32 +0,0 @@
<?php
namespace Drupal\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Registers all lazy route enhancers onto the lazy route enhancers.
*/
class RegisterLazyRouteEnhancers implements CompilerPassInterface {
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container) {
if (!$container->hasDefinition('route_enhancer.lazy_collector')) {
return;
}
$service_ids = [];
foreach ($container->findTaggedServiceIds('route_enhancer') as $id => $attributes) {
$service_ids[$id] = $id;
}
$container
->getDefinition('route_enhancer.lazy_collector')
->addArgument($service_ids);
}
}
@@ -1,32 +0,0 @@
<?php
namespace Drupal\Core\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Registers all lazy route filters onto the lazy route filter.
*/
class RegisterLazyRouteFilters implements CompilerPassInterface {
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container) {
if (!$container->hasDefinition('route_filter.lazy_collector')) {
return;
}
$service_ids = [];
foreach ($container->findTaggedServiceIds('route_filter') as $id => $attributes) {
$service_ids[$id] = $id;
}
$container
->getDefinition('route_filter.lazy_collector')
->addArgument($service_ids);
}
}
@@ -27,8 +27,8 @@ class ContainerBuilder extends SymfonyContainerBuilder {
* {@inheritdoc}
*/
public function __construct(ParameterBagInterface $parameterBag = NULL) {
$this->setResourceTracking(FALSE);
parent::__construct($parameterBag);
$this->setResourceTracking(FALSE);
}
/**
@@ -46,9 +46,12 @@ class ContainerBuilder extends SymfonyContainerBuilder {
}
/**
* {@inheritdoc}
* A 1to1 copy of parent::shareService.
*
* @todo https://www.drupal.org/project/drupal/issues/2937010 Since Symfony
* 3.4 this is not a 1to1 copy.
*/
protected function shareService(Definition $definition, $service, $id)
protected function shareService(Definition $definition, $service, $id, array &$inlineServices)
{
if ($definition->isShared()) {
$this->services[$lowerId = strtolower($id)] = $service;
@@ -88,6 +91,32 @@ class ContainerBuilder extends SymfonyContainerBuilder {
return parent::register($id, $class);
}
/**
* {@inheritdoc}
*/
public function setAlias($alias, $id) {
$alias = parent::setAlias($alias, $id);
// As of Symfony 3.4 all aliases are private by default.
$alias->setPublic(TRUE);
return $alias;
}
/**
* {@inheritdoc}
*/
public function setDefinition($id, Definition $definition) {
$definition = parent::setDefinition($id, $definition);
// As of Symfony 3.4 all definitions are private by default.
// \Symfony\Component\DependencyInjection\Compiler\ResolvePrivatesPassOnly
// removes services marked as private from the container even if they are
// also marked as public. Drupal requires services that are public to
// remain in the container and not be removed.
if ($definition->isPublic()) {
$definition->setPrivate(FALSE);
}
return $definition;
}
/**
* {@inheritdoc}
*/
@@ -100,8 +129,11 @@ class ContainerBuilder extends SymfonyContainerBuilder {
/**
* A 1to1 copy of parent::callMethod.
*
* @todo https://www.drupal.org/project/drupal/issues/2937010 Since Symfony
* 3.4 this is not a 1to1 copy.
*/
protected function callMethod($service, $call) {
protected function callMethod($service, $call, array &$inlineServices = array()) {
$services = self::getServiceConditionals($call[1]);
foreach ($services as $s) {
@@ -8,7 +8,7 @@ use Drupal\Core\Serialization\Yaml;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
@@ -155,7 +155,7 @@ class YamlFileLoader
}
if (isset($service['parent'])) {
$definition = new DefinitionDecorator($service['parent']);
$definition = new ChildDefinition($service['parent']);
} else {
$definition = new Definition();
}
@@ -63,7 +63,7 @@ class EntityReferenceSelection extends Plugin {
public $entity_types = [];
/**
* The weight of the plugin in it's group.
* The weight of the plugin in its group.
*
* @var int
*/
@@ -22,7 +22,7 @@ class BundleEntityFormBase extends EntityForm {
protected function protectBundleIdElement(array $form) {
$entity = $this->getEntity();
$id_key = $entity->getEntityType()->getKey('id');
assert('isset($form[$id_key])');
assert(isset($form[$id_key]));
$element = &$form[$id_key];
// Make sure the element is not accidentally re-enabled if it has already
+120 -31
View File
@@ -20,6 +20,10 @@ use Drupal\Core\TypedData\TypedDataInterface;
*/
abstract class ContentEntityBase extends Entity implements \IteratorAggregate, ContentEntityInterface, TranslationStatusInterface {
use EntityChangesDetectionTrait {
getFieldsToSkipFromTranslationChangesCheck as traitGetFieldsToSkipFromTranslationChangesCheck;
}
/**
* The plain data values of the contained fields.
*
@@ -156,6 +160,22 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
*/
protected $loadedRevisionId;
/**
* The revision translation affected entity key.
*
* @var string
*/
protected $revisionTranslationAffectedKey;
/**
* Whether the revision translation affected flag has been enforced.
*
* An array, keyed by the translation language code.
*
* @var bool[]
*/
protected $enforceRevisionTranslationAffected = [];
/**
* {@inheritdoc}
*/
@@ -164,6 +184,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
$this->entityKeys['bundle'] = $bundle ? $bundle : $this->entityTypeId;
$this->langcodeKey = $this->getEntityType()->getKey('langcode');
$this->defaultLangcodeKey = $this->getEntityType()->getKey('default_langcode');
$this->revisionTranslationAffectedKey = $this->getEntityType()->getKey('revision_translation_affected');
foreach ($values as $key => $value) {
// If the key matches an existing property set the value to the property
@@ -269,15 +290,11 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
// When saving a new revision, set any existing revision ID to NULL so as
// to ensure that a new revision will actually be created.
$this->set($this->getEntityType()->getKey('revision'), NULL);
// Make sure that the flag tracking which translations are affected by the
// current revision is reset.
foreach ($this->translations as $langcode => $data) {
// But skip removed translations.
if ($this->hasTranslation($langcode)) {
$this->getTranslation($langcode)->setRevisionTranslationAffected(NULL);
}
}
}
elseif (!$value && $this->newRevision) {
// If ::setNewRevision(FALSE) is called after ::setNewRevision(TRUE) we
// have to restore the loaded revision ID.
$this->set($this->getEntityType()->getKey('revision'), $this->getLoadedRevisionId());
}
$this->newRevision = $value;
@@ -318,25 +335,73 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
return $this->isNew() || $return;
}
/**
* {@inheritdoc}
*/
public function wasDefaultRevision() {
/** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */
$entity_type = $this->getEntityType();
if (!$entity_type->isRevisionable()) {
return TRUE;
}
$revision_default_key = $entity_type->getRevisionMetadataKey('revision_default');
$value = $this->isNew() || $this->get($revision_default_key)->value;
return $value;
}
/**
* {@inheritdoc}
*/
public function isLatestRevision() {
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
$storage = $this->entityTypeManager()->getStorage($this->getEntityTypeId());
return $this->getLoadedRevisionId() == $storage->getLatestRevisionId($this->id());
}
/**
* {@inheritdoc}
*/
public function isLatestTranslationAffectedRevision() {
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
$storage = $this->entityTypeManager()->getStorage($this->getEntityTypeId());
return $this->getLoadedRevisionId() == $storage->getLatestTranslationAffectedRevisionId($this->id(), $this->language()->getId());
}
/**
* {@inheritdoc}
*/
public function isRevisionTranslationAffected() {
$field_name = $this->getEntityType()->getKey('revision_translation_affected');
return $this->hasField($field_name) ? $this->get($field_name)->value : TRUE;
return $this->hasField($this->revisionTranslationAffectedKey) ? $this->get($this->revisionTranslationAffectedKey)->value : TRUE;
}
/**
* {@inheritdoc}
*/
public function setRevisionTranslationAffected($affected) {
$field_name = $this->getEntityType()->getKey('revision_translation_affected');
if ($this->hasField($field_name)) {
$this->set($field_name, $affected);
if ($this->hasField($this->revisionTranslationAffectedKey)) {
$this->set($this->revisionTranslationAffectedKey, $affected);
}
return $this;
}
/**
* {@inheritdoc}
*/
public function isRevisionTranslationAffectedEnforced() {
return !empty($this->enforceRevisionTranslationAffected[$this->activeLangcode]);
}
/**
* {@inheritdoc}
*/
public function setRevisionTranslationAffectedEnforced($enforced) {
$this->enforceRevisionTranslationAffected[$this->activeLangcode] = $enforced;
return $this;
}
/**
* {@inheritdoc}
*/
@@ -355,8 +420,8 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
* {@inheritdoc}
*/
public function isTranslatable() {
// Check that the bundle is translatable, the entity has a language defined
// and if we have more than one language on the site.
// Check the bundle is translatable, the entity has a language defined, and
// the site has more than one language.
$bundles = $this->entityManager()->getBundleInfo($this->entityTypeId);
return !empty($bundles[$this->bundle()]['translatable']) && !$this->getUntranslated()->language()->isLocked() && $this->languageManager()->isMultilingual();
}
@@ -401,6 +466,12 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
}
}
$this->translations = array_diff_key($this->translations, $removed);
// Reset the new revision flag.
$this->newRevision = FALSE;
// Reset the enforcement of the revision translation affected flag.
$this->enforceRevisionTranslationAffected = [];
}
/**
@@ -716,7 +787,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
// If the revision identifier field is being populated with the original
// value, we need to make sure the "new revision" flag is reset
// accordingly.
if ($key === 'revision' && $this->getRevisionId() == $this->getLoadedRevisionId()) {
if ($key === 'revision' && $this->getRevisionId() == $this->getLoadedRevisionId() && !$this->isNew()) {
$this->newRevision = FALSE;
}
}
@@ -754,6 +825,12 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
throw new \LogicException($message);
}
break;
case $this->revisionTranslationAffectedKey:
// If the revision translation affected flag is being set then enforce
// its value.
$this->setRevisionTranslationAffectedEnforced(TRUE);
break;
}
}
@@ -837,6 +914,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
$translation->typedData = NULL;
$translation->loadedRevisionId = &$this->loadedRevisionId;
$translation->isDefaultRevision = &$this->isDefaultRevision;
$translation->enforceRevisionTranslationAffected = &$this->enforceRevisionTranslationAffected;
return $translation;
}
@@ -1049,7 +1127,9 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
$duplicate = clone $this;
$entity_type = $this->getEntityType();
$duplicate->{$entity_type->getKey('id')}->value = NULL;
if ($entity_type->hasKey('id')) {
$duplicate->{$entity_type->getKey('id')}->value = NULL;
}
$duplicate->enforceIsNew();
// Check if the entity type supports UUIDs and generate a new one if so.
@@ -1104,7 +1184,8 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
// Ensure that the following properties are actually cloned by
// overwriting the original references with ones pointing to copies of
// them: enforceIsNew, newRevision, loadedRevisionId, fields, entityKeys,
// translatableEntityKeys, values and isDefaultRevision.
// translatableEntityKeys, values, isDefaultRevision and
// enforceRevisionTranslationAffected.
$enforce_is_new = $this->enforceIsNew;
$this->enforceIsNew = &$enforce_is_new;
@@ -1129,6 +1210,9 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
$default_revision = $this->isDefaultRevision;
$this->isDefaultRevision = &$default_revision;
$is_revision_translation_affected_enforced = $this->enforceRevisionTranslationAffected;
$this->enforceRevisionTranslationAffected = &$is_revision_translation_affected_enforced;
foreach ($this->fields as $name => $fields_by_langcode) {
$this->fields[$name] = [];
// Untranslatable fields may have multiple references for the same field
@@ -1293,17 +1377,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
* An array of field names.
*/
protected function getFieldsToSkipFromTranslationChangesCheck() {
/** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */
$entity_type = $this->getEntityType();
// A list of known revision metadata fields which should be skipped from
// the comparision.
$fields = [
$entity_type->getKey('revision'),
'revision_translation_affected',
];
$fields = array_merge($fields, array_values($entity_type->getRevisionMetadataKeys()));
return $fields;
return $this->traitGetFieldsToSkipFromTranslationChangesCheck($this);
}
/**
@@ -1343,10 +1417,15 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
// The list of fields to skip from the comparision.
$skip_fields = $this->getFieldsToSkipFromTranslationChangesCheck();
// We also check untranslatable fields, so that a change to those will mark
// all translations as affected, unless they are configured to only affect
// the default translation.
$skip_untranslatable_fields = !$this->isDefaultTranslation() && $this->isDefaultTranslationAffectedOnly();
foreach ($this->getFieldDefinitions() as $field_name => $definition) {
// @todo Avoid special-casing the following fields. See
// https://www.drupal.org/node/2329253.
if (in_array($field_name, $skip_fields, TRUE)) {
if (in_array($field_name, $skip_fields, TRUE) || ($skip_untranslatable_fields && !$definition->isTranslatable())) {
continue;
}
$field = $this->get($field_name);
@@ -1367,4 +1446,14 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
return FALSE;
}
/**
* {@inheritdoc}
*/
public function isDefaultTranslationAffectedOnly() {
$bundle_name = $this->bundle();
$bundle_info = \Drupal::service('entity_type.bundle.info')
->getBundleInfo($this->getEntityTypeId());
return !empty($bundle_info[$bundle_name]['untranslatable_fields.default_translation_affected']);
}
}
@@ -7,6 +7,8 @@ use Drupal\Core\Form\FormStateInterface;
/**
* Provides a generic base class for a content entity deletion form.
*
* @internal
*
* @todo Re-evaluate and streamline the entity deletion form class hierarchy in
* https://www.drupal.org/node/2491057.
*/
@@ -127,6 +127,15 @@ class ContentEntityForm extends EntityForm implements ContentEntityFormInterface
$this->addRevisionableFormFields($form);
}
$form['footer'] = [
'#type' => 'container',
'#weight' => 99,
'#attributes' => [
'class' => ['entity-content-form-footer']
],
'#optional' => TRUE,
];
return $form;
}
@@ -2,8 +2,6 @@
namespace Drupal\Core\Entity;
use Drupal\Core\TypedData\TranslatableInterface;
/**
* Defines a common interface for all content entity objects.
*
@@ -20,35 +18,7 @@ use Drupal\Core\TypedData\TranslatableInterface;
*
* @ingroup entity_api
*/
interface ContentEntityInterface extends \Traversable, FieldableEntityInterface, RevisionableInterface, TranslatableInterface {
/**
* Determines if the current translation of the entity has unsaved changes.
*
* @return bool
* TRUE if the current translation of the entity has changes.
*/
public function hasTranslationChanges();
/**
* Marks the current revision translation as affected.
*
* @param bool|null $affected
* The flag value. A NULL value can be specified to reset the current value
* and make sure a new value will be computed by the system.
*
* @return $this
*/
public function setRevisionTranslationAffected($affected);
/**
* Checks whether the current translation is affected by the current revision.
*
* @return bool
* TRUE if the entity object is affected by the current revision, FALSE
* otherwise.
*/
public function isRevisionTranslationAffected();
interface ContentEntityInterface extends \Traversable, FieldableEntityInterface, TranslatableRevisionableInterface {
/**
* Gets the loaded Revision ID of the entity.
@@ -38,6 +38,13 @@ class ContentEntityNullStorage extends ContentEntityStorageBase {
return NULL;
}
/**
* {@inheritdoc}
*/
public function loadMultipleRevisions(array $revision_ids) {
return [];
}
/**
* {@inheritdoc}
*/
@@ -6,6 +6,7 @@ use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\TranslationStatusInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -79,6 +80,40 @@ abstract class ContentEntityStorageBase extends EntityStorageBase implements Con
return $entity;
}
/**
* {@inheritdoc}
*/
public function createWithSampleValues($bundle = FALSE, array $values = []) {
// ID and revision should never have sample values generated for them.
$forbidden_keys = [
$this->entityType->getKey('id'),
];
if ($revision_key = $this->entityType->getKey('revision')) {
$forbidden_keys[] = $revision_key;
}
if ($bundle_key = $this->entityType->getKey('bundle')) {
if (!$bundle) {
throw new EntityStorageException("No entity bundle was specified");
}
if (!array_key_exists($bundle, $this->entityManager->getBundleInfo($this->entityTypeId))) {
throw new EntityStorageException(sprintf("Missing entity bundle. The \"%s\" bundle does not exist", $bundle));
}
$values[$bundle_key] = $bundle;
// Bundle is already set
$forbidden_keys[] = $bundle_key;
}
// Forbid sample generation on any keys whose values were submitted.
$forbidden_keys = array_merge($forbidden_keys, array_keys($values));
/** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */
$entity = $this->create($values);
foreach ($entity as $field_name => $value) {
if (!in_array($field_name, $forbidden_keys, TRUE)) {
$entity->get($field_name)->generateSampleItems();
}
}
return $entity;
}
/**
* Initializes field values.
*
@@ -114,6 +149,69 @@ abstract class ContentEntityStorageBase extends EntityStorageBase implements Con
$this->invokeHook('field_values_init', $entity);
}
/**
* Checks whether any entity revision is translated.
*
* @param \Drupal\Core\Entity\EntityInterface|\Drupal\Core\Entity\TranslatableInterface $entity
* The entity object to be checked.
*
* @return bool
* TRUE if the entity has at least one translation in any revision, FALSE
* otherwise.
*
* @see \Drupal\Core\TypedData\TranslatableInterface::getTranslationLanguages()
* @see \Drupal\Core\Entity\ContentEntityStorageBase::isAnyStoredRevisionTranslated()
*/
protected function isAnyRevisionTranslated(TranslatableInterface $entity) {
return $entity->getTranslationLanguages(FALSE) || $this->isAnyStoredRevisionTranslated($entity);
}
/**
* Checks whether any stored entity revision is translated.
*
* A revisionable entity can have translations in a pending revision, hence
* the default revision may appear as not translated. This determines whether
* the entity has any translation in the storage and thus should be considered
* as multilingual.
*
* @param \Drupal\Core\Entity\EntityInterface|\Drupal\Core\Entity\TranslatableInterface $entity
* The entity object to be checked.
*
* @return bool
* TRUE if the entity has at least one translation in any revision, FALSE
* otherwise.
*
* @see \Drupal\Core\TypedData\TranslatableInterface::getTranslationLanguages()
* @see \Drupal\Core\Entity\ContentEntityStorageBase::isAnyRevisionTranslated()
*/
protected function isAnyStoredRevisionTranslated(TranslatableInterface $entity) {
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
if ($entity->isNew()) {
return FALSE;
}
if ($entity instanceof TranslationStatusInterface) {
foreach ($entity->getTranslationLanguages(FALSE) as $langcode => $language) {
if ($entity->getTranslationStatus($langcode) === TranslationStatusInterface::TRANSLATION_EXISTING) {
return TRUE;
}
}
}
$query = $this->getQuery()
->condition($this->entityType->getKey('id'), $entity->id())
->condition($this->entityType->getKey('default_langcode'), 0)
->accessCheck(FALSE)
->range(0, 1);
if ($entity->getEntityType()->isRevisionable()) {
$query->allRevisions();
}
$result = $query->execute();
return !empty($result);
}
/**
* {@inheritdoc}
*/
@@ -132,6 +230,148 @@ abstract class ContentEntityStorageBase extends EntityStorageBase implements Con
return $translation;
}
/**
* {@inheritdoc}
*/
public function createRevision(RevisionableInterface $entity, $default = TRUE, $keep_untranslatable_fields = NULL) {
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$new_revision = clone $entity;
// For translatable entities, create a merged revision of the active
// translation and the other translations in the default revision. This
// permits the creation of pending revisions that can always be saved as the
// new default revision without reverting changes in other languages.
if (!$entity->isNew() && !$entity->isDefaultRevision() && $entity->isTranslatable() && $this->isAnyRevisionTranslated($entity)) {
$active_langcode = $entity->language()->getId();
$skipped_field_names = array_flip($this->getRevisionTranslationMergeSkippedFieldNames());
// By default we copy untranslatable field values from the default
// revision, unless they are configured to affect only the default
// translation. This way we can ensure we always have only one affected
// translation in pending revisions. This constraint is enforced by
// EntityUntranslatableFieldsConstraintValidator.
if (!isset($keep_untranslatable_fields)) {
$keep_untranslatable_fields = $entity->isDefaultTranslation() && $entity->isDefaultTranslationAffectedOnly();
}
/** @var \Drupal\Core\Entity\ContentEntityInterface $default_revision */
$default_revision = $this->load($entity->id());
$translation_languages = $default_revision->getTranslationLanguages();
foreach ($translation_languages as $langcode => $language) {
if ($langcode == $active_langcode) {
continue;
}
$default_revision_translation = $default_revision->getTranslation($langcode);
$new_revision_translation = $new_revision->hasTranslation($langcode) ?
$new_revision->getTranslation($langcode) : $new_revision->addTranslation($langcode);
/** @var \Drupal\Core\Field\FieldItemListInterface[] $sync_items */
$sync_items = array_diff_key(
$keep_untranslatable_fields ? $default_revision_translation->getTranslatableFields() : $default_revision_translation->getFields(),
$skipped_field_names
);
foreach ($sync_items as $field_name => $items) {
$new_revision_translation->set($field_name, $items->getValue());
}
// Make sure the "revision_translation_affected" flag is recalculated.
$new_revision_translation->setRevisionTranslationAffected(NULL);
// No need to copy untranslatable field values more than once.
$keep_untranslatable_fields = TRUE;
}
// Make sure we do not inadvertently recreate removed translations.
foreach (array_diff_key($new_revision->getTranslationLanguages(), $translation_languages) as $langcode => $language) {
// Allow a new revision to be created for the active language.
if ($langcode !== $active_langcode) {
$new_revision->removeTranslation($langcode);
}
}
// The "original" property is used in various places to detect changes in
// field values with respect to the stored ones. If the property is not
// defined, the stored version is loaded explicitly. Since the merged
// revision generated here is not stored anywhere, we need to populate the
// "original" property manually, so that changes can be properly detected.
$new_revision->original = clone $new_revision;
}
// Eventually mark the new revision as such.
$new_revision->setNewRevision();
$new_revision->isDefaultRevision($default);
// Actually make sure the current translation is marked as affected, even if
// there are no explicit changes, to be sure this revision can be related
// to the correct translation.
$new_revision->setRevisionTranslationAffected(TRUE);
return $new_revision;
}
/**
* Returns an array of field names to skip when merging revision translations.
*
* @return array
* An array of field names.
*/
protected function getRevisionTranslationMergeSkippedFieldNames() {
/** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */
$entity_type = $this->getEntityType();
// A list of known revision metadata fields which should be skipped from
// the comparision.
$field_names = [
$entity_type->getKey('revision'),
$entity_type->getKey('revision_translation_affected'),
];
$field_names = array_merge($field_names, array_values($entity_type->getRevisionMetadataKeys()));
return $field_names;
}
/**
* {@inheritdoc}
*/
public function getLatestRevisionId($entity_id) {
if (!$this->entityType->isRevisionable()) {
return NULL;
}
$result = $this->getQuery()
->latestRevision()
->condition($this->entityType->getKey('id'), $entity_id)
->accessCheck(FALSE)
->execute();
return key($result);
}
/**
* {@inheritdoc}
*/
public function getLatestTranslationAffectedRevisionId($entity_id, $langcode) {
if (!$this->entityType->isRevisionable()) {
return NULL;
}
if (!$this->entityType->isTranslatable()) {
return $this->getLatestRevisionId($entity_id);
}
$result = $this->getQuery()
->allRevisions()
->condition($this->entityType->getKey('id'), $entity_id)
->condition($this->entityType->getKey('revision_translation_affected'), 1, '=', $langcode)
->range(0, 1)
->sort($this->entityType->getKey('revision'), 'DESC')
->accessCheck(FALSE)
->execute();
return key($result);
}
/**
* {@inheritdoc}
*/
@@ -210,15 +450,37 @@ abstract class ContentEntityStorageBase extends EntityStorageBase implements Con
* {@inheritdoc}
*/
public function loadRevision($revision_id) {
$revision = $this->doLoadRevisionFieldItems($revision_id);
$revisions = $this->loadMultipleRevisions([$revision_id]);
if ($revision) {
$entities = [$revision->id() => $revision];
return isset($revisions[$revision_id]) ? $revisions[$revision_id] : NULL;
}
/**
* {@inheritdoc}
*/
public function loadMultipleRevisions(array $revision_ids) {
$revisions = $this->doLoadMultipleRevisionsFieldItems($revision_ids);
// The hooks are executed with an array of entities keyed by the entity ID.
// As we could load multiple revisions for the same entity ID at once we
// have to build groups of entities where the same entity ID is present only
// once.
$entity_groups = [];
$entity_group_mapping = [];
foreach ($revisions as $revision) {
$entity_id = $revision->id();
$entity_group_key = isset($entity_group_mapping[$entity_id]) ? $entity_group_mapping[$entity_id] + 1 : 0;
$entity_group_mapping[$entity_id] = $entity_group_key;
$entity_groups[$entity_group_key][$entity_id] = $revision;
}
// Invoke the entity hooks for each group.
foreach ($entity_groups as $entities) {
$this->invokeStorageLoadHook($entities);
$this->postLoad($entities);
}
return $revision;
return $revisions;
}
/**
@@ -229,9 +491,33 @@ abstract class ContentEntityStorageBase extends EntityStorageBase implements Con
*
* @return \Drupal\Core\Entity\EntityInterface|null
* The specified entity revision or NULL if not found.
*
* @deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0.
* \Drupal\Core\Entity\ContentEntityStorageBase::doLoadMultipleRevisionsFieldItems()
* should be implemented instead.
*
* @see https://www.drupal.org/node/2924915
*/
abstract protected function doLoadRevisionFieldItems($revision_id);
/**
* Actually loads revision field item values from the storage.
*
* @param array $revision_ids
* An array of revision identifiers.
*
* @return \Drupal\Core\Entity\EntityInterface[]
* The specified entity revisions or an empty array if none are found.
*/
protected function doLoadMultipleRevisionsFieldItems($revision_ids) {
$revisions = [];
foreach ($revision_ids as $revision_id) {
$revisions[] = $this->doLoadRevisionFieldItems($revision_id);
}
return $revisions;
}
/**
* {@inheritdoc}
*/
@@ -254,6 +540,15 @@ abstract class ContentEntityStorageBase extends EntityStorageBase implements Con
}
$this->populateAffectedRevisionTranslations($entity);
// Populate the "revision_default" flag. We skip this when we are resaving
// the revision because this is only allowed for default revisions, and
// these cannot be made non-default.
if ($this->entityType->isRevisionable() && $entity->isNewRevision()) {
$revision_default_key = $this->entityType->getRevisionMetadataKey('revision_default');
$entity->set($revision_default_key, $entity->isDefaultRevision());
}
$this->doSaveFieldItems($entity);
return $return;
@@ -553,32 +848,41 @@ abstract class ContentEntityStorageBase extends EntityStorageBase implements Con
$languages = $entity->getTranslationLanguages();
foreach ($languages as $langcode => $language) {
$translation = $entity->getTranslation($langcode);
// Avoid populating the value if it was already manually set.
$affected = $translation->isRevisionTranslationAffected();
if (!isset($affected) && $translation->hasTranslationChanges()) {
$translation->setRevisionTranslationAffected(TRUE);
$current_affected = $translation->isRevisionTranslationAffected();
if (!isset($current_affected) || ($entity->isNewRevision() && !$translation->isRevisionTranslationAffectedEnforced())) {
// When setting the revision translation affected flag we have to
// explicitly set it to not be enforced. By default it will be
// enforced automatically when being set, which allows us to determine
// if the flag has been already set outside the storage in which case
// we should not recompute it.
// @see \Drupal\Core\Entity\ContentEntityBase::setRevisionTranslationAffected().
$new_affected = $translation->hasTranslationChanges() ? TRUE : NULL;
$translation->setRevisionTranslationAffected($new_affected);
$translation->setRevisionTranslationAffectedEnforced(FALSE);
}
}
}
}
/**
* Ensures integer entity IDs are valid.
* Ensures integer entity key values are valid.
*
* The identifier sanitization provided by this method has been introduced
* as Drupal used to rely on the database to facilitate this, which worked
* correctly with MySQL but led to errors with other DBMS such as PostgreSQL.
*
* @param array $ids
* The entity IDs to verify.
* The entity key values to verify.
* @param string $entity_key
* (optional) The entity key to sanitise values for. Defaults to 'id'.
*
* @return array
* The sanitized list of entity IDs.
* The sanitized list of entity key values.
*/
protected function cleanIds(array $ids) {
protected function cleanIds(array $ids, $entity_key = 'id') {
$definitions = $this->entityManager->getBaseFieldDefinitions($this->entityTypeId);
$id_definition = $definitions[$this->entityType->getKey('id')];
if ($id_definition->getType() == 'integer') {
$field_name = $this->entityType->getKey($entity_key);
if ($field_name && $definitions[$field_name]->getType() == 'integer') {
$ids = array_filter($ids, function ($id) {
return is_numeric($id) && $id == (int) $id;
});
@@ -5,22 +5,22 @@ namespace Drupal\Core\Entity;
/**
* A storage that supports content entity types.
*/
interface ContentEntityStorageInterface extends EntityStorageInterface {
interface ContentEntityStorageInterface extends EntityStorageInterface, TranslatableRevisionableStorageInterface {
/**
* Constructs a new entity translation object, without permanently saving it.
* Creates an entity with sample field values.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity object being translated.
* @param string $langcode
* The translation language code.
* @param string|bool $bundle
* (optional) The entity bundle.
* @param array $values
* (optional) An associative array of initial field values keyed by field
* name. If none is provided default values will be applied.
* (optional) Any default values to use during generation.
*
* @return \Drupal\Core\Entity\ContentEntityInterface
* A new entity translation object.
* @return \Drupal\Core\Entity\FieldableEntityInterface
* A fieldable content entity.
*
* @throws \Drupal\Core\Entity\EntityStorageException
* Thrown if the bundle does not exist or was needed but not specified.
*/
public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []);
public function createWithSampleValues($bundle = FALSE, array $values = []);
}
@@ -14,15 +14,46 @@ class ContentEntityType extends EntityType implements ContentEntityTypeInterface
*/
protected $revision_metadata_keys = [];
/**
* The required revision metadata keys.
*
* This property should only be filled in the constructor. This ensures that
* only new instances get newly added required revision metadata keys.
* Unserialized objects will only retrieve the keys that they already have
* been cached with.
*
* @var array
*/
protected $requiredRevisionMetadataKeys = [];
/**
* {@inheritdoc}
*/
public function __construct($definition) {
parent::__construct($definition);
$this->handlers += [
'storage' => 'Drupal\Core\Entity\Sql\SqlContentEntityStorage',
'view_builder' => 'Drupal\Core\Entity\EntityViewBuilder',
];
// Only new instances should provide the required revision metadata keys.
// The cached instances should return only what already has been stored
// under the property $revision_metadata_keys. The BC layer in
// ::getRevisionMetadataKeys() has to detect if the revision metadata keys
// have been provided by the entity type annotation, therefore we add keys
// to the property $requiredRevisionMetadataKeys only if those keys aren't
// set in the entity type annotation.
if (!isset($this->revision_metadata_keys['revision_default'])) {
$this->requiredRevisionMetadataKeys['revision_default'] = 'revision_default';
}
// Add the required revision metadata fields here instead in the getter
// method, so that they are serialized as part of the object even if the
// getter method doesn't get called. This allows the list to be further
// extended. Only new instances of the class will contain the new list,
// while the cached instances contain the previous version of the list.
$this->revision_metadata_keys += $this->requiredRevisionMetadataKeys;
}
/**
@@ -54,7 +85,7 @@ class ContentEntityType extends EntityType implements ContentEntityTypeInterface
public function getRevisionMetadataKeys($include_backwards_compatibility_field_names = TRUE) {
// Provide backwards compatibility in case the revision metadata keys are
// not defined in the entity annotation.
if (!$this->revision_metadata_keys && $include_backwards_compatibility_field_names) {
if ((!$this->revision_metadata_keys || ($this->revision_metadata_keys == $this->requiredRevisionMetadataKeys)) && $include_backwards_compatibility_field_names) {
$base_fields = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions($this->id());
if ((isset($base_fields['revision_uid']) && $revision_user = 'revision_uid') || (isset($base_fields['revision_user']) && $revision_user = 'revision_user')) {
@trigger_error('The revision_user revision metadata key is not set.', E_USER_DEPRECATED);
@@ -2,9 +2,7 @@
namespace Drupal\Core\Entity;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldDefinitionListenerInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
/**
@@ -18,27 +16,4 @@ use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
*/
interface DynamicallyFieldableEntityStorageInterface extends FieldableEntityStorageInterface, FieldStorageDefinitionListenerInterface, FieldDefinitionListenerInterface {
/**
* Purges a batch of field data.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The deleted field whose data is being purged.
* @param $batch_size
* The maximum number of field data records to purge before returning,
* relating to the count of field data records returned by
* \Drupal\Core\Entity\FieldableEntityStorageInterface::countFieldData().
*
* @return int
* The number of field data records that have been purged.
*/
public function purgeFieldData(FieldDefinitionInterface $field_definition, $batch_size);
/**
* Performs final cleanup after all data of a field has been purged.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
* The field being purged.
*/
public function finalizePurge(FieldStorageDefinitionInterface $storage_definition);
}
@@ -39,7 +39,7 @@ class EntityAutocomplete extends Textfield {
$info['#validate_reference'] = TRUE;
// IMPORTANT! This should only be set to FALSE if the #default_value
// property is processed at another level (e.g. by a Field API widget) and
// it's value is properly checked for access.
// its value is properly checked for access.
$info['#process_default_value'] = TRUE;
$info['#element_validate'] = [[$class, 'validateEntityAutocomplete']];
@@ -2,20 +2,25 @@
namespace Drupal\Core\Entity\Enhancer;
use Drupal\Core\Routing\Enhancer\RouteEnhancerInterface;
use Drupal\Core\Routing\EnhancerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\Routing\Route;
/**
* Enhances an entity form route with the appropriate controller.
*/
class EntityRouteEnhancer implements RouteEnhancerInterface {
class EntityRouteEnhancer implements EnhancerInterface {
/**
* {@inheritdoc}
*/
public function enhance(array $defaults, Request $request) {
$route = $defaults[RouteObjectInterface::ROUTE_OBJECT];
if (!$this->applies($route)) {
return $defaults;
}
if (empty($defaults['_controller'])) {
if (!empty($defaults['_entity_form'])) {
$defaults = $this->enhanceEntityForm($defaults, $request);
@@ -31,9 +36,14 @@ class EntityRouteEnhancer implements RouteEnhancerInterface {
}
/**
* {@inheritdoc}
* Returns whether the enhancer runs on the current route.
*
* @param \Symfony\Component\Routing\Route $route
* The current route.
*
* @return bool
*/
public function applies(Route $route) {
protected function applies(Route $route) {
return !$route->hasDefault('_controller') &&
($route->hasDefault('_entity_form')
|| $route->hasDefault('_entity_list')
+28 -12
View File
@@ -88,6 +88,15 @@ abstract class Entity implements EntityInterface {
return \Drupal::entityTypeManager();
}
/**
* Gets the entity type bundle info service.
*
* @return \Drupal\Core\Entity\EntityTypeBundleInfoInterface
*/
protected function entityTypeBundleInfo() {
return \Drupal::service('entity_type.bundle.info');
}
/**
* Gets the language manager.
*
@@ -198,7 +207,7 @@ abstract class Entity implements EntityInterface {
$bundle = $this->bundle();
// A bundle-specific callback takes precedence over the generic one for
// the entity type.
$bundles = $this->entityManager()->getBundleInfo($this->getEntityTypeId());
$bundles = $this->entityTypeBundleInfo()->getBundleInfo($this->getEntityTypeId());
if (isset($bundles[$bundle]['uri_callback'])) {
$uri_callback = $bundles[$bundle]['uri_callback'];
}
@@ -344,11 +353,11 @@ abstract class Entity implements EntityInterface {
*/
public function access($operation, AccountInterface $account = NULL, $return_as_object = FALSE) {
if ($operation == 'create') {
return $this->entityManager()
return $this->entityTypeManager()
->getAccessControlHandler($this->entityTypeId)
->createAccess($this->bundle(), $account, [], $return_as_object);
}
return $this->entityManager()
return $this->entityTypeManager()
->getAccessControlHandler($this->entityTypeId)
->access($this, $operation, $account, $return_as_object);
}
@@ -374,7 +383,8 @@ abstract class Entity implements EntityInterface {
* {@inheritdoc}
*/
public function save() {
return $this->entityManager()->getStorage($this->entityTypeId)->save($this);
$storage = $this->entityTypeManager()->getStorage($this->entityTypeId);
return $storage->save($this);
}
/**
@@ -382,7 +392,7 @@ abstract class Entity implements EntityInterface {
*/
public function delete() {
if (!$this->isNew()) {
$this->entityManager()->getStorage($this->entityTypeId)->delete([$this->id() => $this]);
$this->entityTypeManager()->getStorage($this->entityTypeId)->delete([$this->id() => $this]);
}
}
@@ -407,7 +417,7 @@ abstract class Entity implements EntityInterface {
* {@inheritdoc}
*/
public function getEntityType() {
return $this->entityManager()->getDefinition($this->getEntityTypeId());
return $this->entityTypeManager()->getDefinition($this->getEntityTypeId());
}
/**
@@ -508,24 +518,30 @@ abstract class Entity implements EntityInterface {
* {@inheritdoc}
*/
public static function load($id) {
$entity_manager = \Drupal::entityManager();
return $entity_manager->getStorage($entity_manager->getEntityTypeFromClass(get_called_class()))->load($id);
$entity_type_repository = \Drupal::service('entity_type.repository');
$entity_type_manager = \Drupal::entityTypeManager();
$storage = $entity_type_manager->getStorage($entity_type_repository->getEntityTypeFromClass(get_called_class()));
return $storage->load($id);
}
/**
* {@inheritdoc}
*/
public static function loadMultiple(array $ids = NULL) {
$entity_manager = \Drupal::entityManager();
return $entity_manager->getStorage($entity_manager->getEntityTypeFromClass(get_called_class()))->loadMultiple($ids);
$entity_type_repository = \Drupal::service('entity_type.repository');
$entity_type_manager = \Drupal::entityTypeManager();
$storage = $entity_type_manager->getStorage($entity_type_repository->getEntityTypeFromClass(get_called_class()));
return $storage->loadMultiple($ids);
}
/**
* {@inheritdoc}
*/
public static function create(array $values = []) {
$entity_manager = \Drupal::entityManager();
return $entity_manager->getStorage($entity_manager->getEntityTypeFromClass(get_called_class()))->create($values);
$entity_type_repository = \Drupal::service('entity_type.repository');
$entity_type_manager = \Drupal::entityTypeManager();
$storage = $entity_type_manager->getStorage($entity_type_repository->getEntityTypeFromClass(get_called_class()));
return $storage->create($values);
}
/**
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Core\Entity\Entity\Access;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Provides an entity access control handler for form displays.
*/
class EntityFormDisplayAccessControlHandler extends EntityAccessControlHandler {
/**
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
/** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $entity */
return parent::checkAccess($entity, $operation, $account)
->orIf(AccessResult::allowedIfHasPermission($account, 'administer ' . $entity->getTargetEntityTypeId() . ' form display'));
}
}
@@ -0,0 +1,24 @@
<?php
namespace Drupal\Core\Entity\Entity\Access;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Provides an entity access control handler for displays.
*/
class EntityViewDisplayAccessControlHandler extends EntityAccessControlHandler {
/**
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
/** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $entity */
return parent::checkAccess($entity, $operation, $account)
->orIf(AccessResult::allowedIfHasPermission($account, 'administer ' . $entity->getTargetEntityTypeId() . ' display'));
}
}
@@ -23,6 +23,9 @@ use Symfony\Component\Validator\ConstraintViolationListInterface;
* "id" = "id",
* "status" = "status"
* },
* handlers = {
* "access" = "\Drupal\Core\Entity\Entity\Access\EntityFormDisplayAccessControlHandler",
* },
* config_export = {
* "id",
* "targetEntityType",
@@ -8,7 +8,7 @@ use Drupal\Core\Entity\EntityDisplayPluginCollection;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Entity\EntityDisplayBase;
use Drupal\Core\TypedData\TranslatableInterface;
use Drupal\Core\TypedData\TranslatableInterface as TranslatableDataInterface;
/**
* Configuration entity that contains display options for all components of a
@@ -21,6 +21,9 @@ use Drupal\Core\TypedData\TranslatableInterface;
* "id" = "id",
* "status" = "status"
* },
* handlers = {
* "access" = "\Drupal\Core\Entity\Entity\Access\EntityViewDisplayAccessControlHandler",
* },
* config_export = {
* "id",
* "targetEntityType",
@@ -250,7 +253,7 @@ class EntityViewDisplay extends EntityDisplayBase implements EntityViewDisplayIn
// those values using:
// - the entity language if the entity is translatable,
// - the current "content language" otherwise.
if ($entity instanceof TranslatableInterface && $entity->isTranslatable()) {
if ($entity instanceof TranslatableDataInterface && $entity->isTranslatable()) {
$view_langcode = $entity->language()->getId();
}
else {
@@ -158,7 +158,7 @@ class EntityAccessControlHandler extends EntityHandlerBase implements EntityAcce
return AccessResult::forbidden()->addCacheableDependency($entity);
}
if ($admin_permission = $this->entityType->getAdminPermission()) {
return AccessResult::allowedIfHasPermission($account, $this->entityType->getAdminPermission());
return AccessResult::allowedIfHasPermission($account, $admin_permission);
}
else {
// No opinion.
@@ -316,13 +316,17 @@ class EntityAccessControlHandler extends EntityHandlerBase implements EntityAcce
$default = $items ? $items->defaultAccess($operation, $account) : AccessResult::allowed();
// Explicitly disallow changing the entity ID and entity UUID.
if ($operation === 'edit') {
$entity = $items ? $items->getEntity() : NULL;
if ($operation === 'edit' && $entity) {
if ($field_definition->getName() === $this->entityType->getKey('id')) {
return $return_as_object ? AccessResult::forbidden('The entity ID cannot be changed') : FALSE;
// String IDs can be set when creating the entity.
if (!($entity->isNew() && $field_definition->getType() === 'string')) {
return $return_as_object ? AccessResult::forbidden('The entity ID cannot be changed')->addCacheableDependency($entity) : FALSE;
}
}
elseif ($field_definition->getName() === $this->entityType->getKey('uuid')) {
// UUIDs can be set when creating an entity.
if ($items && ($entity = $items->getEntity()) && !$entity->isNew()) {
if (!$entity->isNew()) {
return $return_as_object ? AccessResult::forbidden('The entity UUID cannot be changed')->addCacheableDependency($entity) : FALSE;
}
}
@@ -0,0 +1,36 @@
<?php
namespace Drupal\Core\Entity;
/**
* Provides helper methods to detect changes in an entity object.
*
* @internal This may be replaced by a proper entity comparison handler.
*/
trait EntityChangesDetectionTrait {
/**
* Returns an array of field names to skip when checking for changes.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* A content entity object.
*
* @return string[]
* An array of field names.
*/
protected function getFieldsToSkipFromTranslationChangesCheck(ContentEntityInterface $entity) {
/** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */
$entity_type = $entity->getEntityType();
// A list of known revision metadata fields which should be skipped from
// the comparision.
$fields = [
$entity_type->getKey('revision'),
$entity_type->getKey('revision_translation_affected'),
];
$fields = array_merge($fields, array_values($entity_type->getRevisionMetadataKeys()));
return $fields;
}
}
@@ -311,7 +311,8 @@ class EntityDefinitionUpdateManager implements EntityDefinitionUpdateManagerInte
}
// @todo Support deleting entity definitions when we support base field
// purging. See https://www.drupal.org/node/2282119.
// purging.
// @see https://www.drupal.org/node/2907779
$this->entityManager->useCaches(TRUE);
@@ -9,6 +9,8 @@ use Drupal\Core\Form\FormStateInterface;
* Provides a generic base class for an entity deletion form.
*
* @ingroup entity_api
*
* @internal
*/
class EntityDeleteForm extends EntityConfirmFormBase {

Some files were not shown because too many files have changed in this diff Show More