updated core
This commit is contained in:
@@ -23,6 +23,19 @@ use Drupal\Component\Utility\ToStringTrait;
|
||||
* errors are. This is less disruptive than allowing datetime exceptions
|
||||
* to abort processing. The calling script can decide what to do about
|
||||
* errors using hasErrors() and getErrors().
|
||||
*
|
||||
* @method $this add(\DateInterval $interval)
|
||||
* @method static array getLastErrors()
|
||||
* @method $this modify(string $modify)
|
||||
* @method $this setDate(int $year, int $month, int $day)
|
||||
* @method $this setISODate(int $year, int $week, int $day = 1)
|
||||
* @method $this setTime(int $hour, int $minute, int $second = 0, int $microseconds = 0)
|
||||
* @method $this setTimestamp(int $unixtimestamp)
|
||||
* @method $this setTimezone(\DateTimeZone $timezone)
|
||||
* @method $this sub(\DateInterval $interval)
|
||||
* @method int getOffset()
|
||||
* @method int getTimestamp()
|
||||
* @method \DateTimeZone getTimezone()
|
||||
*/
|
||||
class DateTimePlus {
|
||||
|
||||
@@ -53,31 +66,43 @@ class DateTimePlus {
|
||||
|
||||
/**
|
||||
* The value of the time value passed to the constructor.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $inputTimeRaw = '';
|
||||
|
||||
/**
|
||||
* The prepared time, without timezone, for this date.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $inputTimeAdjusted = '';
|
||||
|
||||
/**
|
||||
* The value of the timezone passed to the constructor.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $inputTimeZoneRaw = '';
|
||||
|
||||
/**
|
||||
* The prepared timezone object used to construct this date.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $inputTimeZoneAdjusted = '';
|
||||
|
||||
/**
|
||||
* The value of the format passed to the constructor.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $inputFormatRaw = '';
|
||||
|
||||
/**
|
||||
* The prepared format, if provided.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $inputFormatAdjusted = '';
|
||||
|
||||
|
||||
@@ -586,8 +586,7 @@ class Container implements ContainerInterface, ResettableContainerInterface {
|
||||
/**
|
||||
* Ensure that cloning doesn't work.
|
||||
*/
|
||||
private function __clone()
|
||||
{
|
||||
private function __clone() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class PoItem {
|
||||
/**
|
||||
* The language code this translation is in.
|
||||
*
|
||||
* @car string
|
||||
* @var string
|
||||
*/
|
||||
private $_langcode;
|
||||
|
||||
@@ -27,7 +27,8 @@ class PoItem {
|
||||
/**
|
||||
* The source string or array of strings if it has plurals.
|
||||
*
|
||||
* @var string or array
|
||||
* @var string|array
|
||||
*
|
||||
* @see $_plural
|
||||
*/
|
||||
private $_source;
|
||||
@@ -49,7 +50,7 @@ class PoItem {
|
||||
/**
|
||||
* The translation string or array of strings if it has plurals.
|
||||
*
|
||||
* @var string or array
|
||||
* @var string|array
|
||||
* @see $_plural
|
||||
*/
|
||||
private $_translation;
|
||||
|
||||
@@ -138,34 +138,42 @@ EOF;
|
||||
* The directory path.
|
||||
* @param int $mode
|
||||
* The mode, permissions, the directory should have.
|
||||
* @param bool $is_backwards_recursive
|
||||
* Internal use only.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the directory exists or has been created, FALSE otherwise.
|
||||
*/
|
||||
protected function createDirectory($directory, $mode = 0777, $is_backwards_recursive = FALSE) {
|
||||
protected function createDirectory($directory, $mode = 0777) {
|
||||
// If the directory exists already, there's nothing to do.
|
||||
if (is_dir($directory)) {
|
||||
return TRUE;
|
||||
}
|
||||
// Otherwise, try to create the directory and ensure to set its permissions,
|
||||
// because mkdir() obeys the umask of the current process.
|
||||
if (is_dir($parent = dirname($directory))) {
|
||||
// If the parent directory exists, then the backwards recursion must end,
|
||||
// regardless of whether the subdirectory could be created.
|
||||
if ($status = mkdir($directory)) {
|
||||
// Only try to chmod() if the subdirectory could be created.
|
||||
$status = chmod($directory, $mode);
|
||||
}
|
||||
return $is_backwards_recursive ? TRUE : $status;
|
||||
|
||||
// If the parent directory doesn't exist, try to create it.
|
||||
$parent_exists = is_dir($parent = dirname($directory));
|
||||
if (!$parent_exists) {
|
||||
$parent_exists = $this->createDirectory($parent, $mode);
|
||||
}
|
||||
// If the parent directory and the requested directory does not exist and
|
||||
// could not be created above, walk the requested directory path back up
|
||||
// until an existing directory is hit, and from there, recursively create
|
||||
// the sub-directories. Only if that recursion succeeds, create the final,
|
||||
// originally requested subdirectory.
|
||||
return $this->createDirectory($parent, $mode, TRUE) && mkdir($directory) && chmod($directory, $mode);
|
||||
|
||||
// If parent exists, try to create the directory and ensure to set its
|
||||
// permissions, because mkdir() obeys the umask of the current process.
|
||||
if ($parent_exists) {
|
||||
// We hide warnings and ignore the return because there may have been a
|
||||
// race getting here and the directory could already exist.
|
||||
@mkdir($directory);
|
||||
// Only try to chmod() if the subdirectory could be created.
|
||||
if (is_dir($directory)) {
|
||||
// Avoid writing permissions if possible.
|
||||
if (fileperms($directory) !== $mode) {
|
||||
return chmod($directory, $mode);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
// Something failed and the directory doesn't exist.
|
||||
trigger_error('mkdir(): Permission Denied', E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -603,11 +603,13 @@ EOD;
|
||||
*
|
||||
* @param string $string
|
||||
* The header to encode.
|
||||
* @param bool $shorten
|
||||
* If TRUE, only return the first chunk of a multi-chunk encoded string.
|
||||
*
|
||||
* @return string
|
||||
* The mime-encoded header.
|
||||
*/
|
||||
public static function mimeHeaderEncode($string) {
|
||||
public static function mimeHeaderEncode($string, $shorten = FALSE) {
|
||||
if (preg_match('/[^\x20-\x7E]/', $string)) {
|
||||
// floor((75 - strlen("=?UTF-8?B??=")) * 0.75);
|
||||
$chunk_size = 47;
|
||||
@@ -616,6 +618,9 @@ EOD;
|
||||
while ($len > 0) {
|
||||
$chunk = static::truncateBytes($string, $chunk_size);
|
||||
$output .= ' =?UTF-8?B?' . base64_encode($chunk) . "?=\n";
|
||||
if ($shorten) {
|
||||
break;
|
||||
}
|
||||
$c = strlen($chunk);
|
||||
$string = substr($string, $c);
|
||||
$len -= $c;
|
||||
|
||||
@@ -38,7 +38,7 @@ interface AccessManagerInterface {
|
||||
/**
|
||||
* Execute access checks against the incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The incoming request.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* (optional) Run access checks for this account. Defaults to the current
|
||||
|
||||
@@ -119,7 +119,7 @@ class OpenDialogCommand implements CommandInterface, CommandWithAttachedAssetsIn
|
||||
* The new title of the dialog.
|
||||
*/
|
||||
public function setDialogTitle($title) {
|
||||
$this->setDialogOptions('title', $title);
|
||||
$this->setDialogOption('title', $title);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,7 +36,6 @@ class CacheContextsPass implements CompilerPassInterface {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$container->setParameter('cache_contexts', $cache_contexts);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class LanguagesCacheContext implements CalculatedCacheContextInterface {
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
|
||||
@@ -292,78 +292,85 @@ class ConfigManager implements ConfigManagerInterface {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConfigEntitiesToChangeOnDependencyRemoval($type, array $names, $dry_run = TRUE) {
|
||||
// Determine the current list of dependent configuration entities and set up
|
||||
// initial values.
|
||||
$dependency_manager = $this->getConfigDependencyManager();
|
||||
$dependents = $this->findConfigEntityDependentsAsEntities($type, $names, $dependency_manager);
|
||||
$original_dependencies = $dependents;
|
||||
$delete_uuids = [];
|
||||
|
||||
// Store the list of dependents in three separate variables. This allows us
|
||||
// to determine how the dependency graph changes as entities are fixed by
|
||||
// calling the onDependencyRemoval() method.
|
||||
|
||||
// The list of original dependents on $names. This list never changes.
|
||||
$original_dependents = $this->findConfigEntityDependentsAsEntities($type, $names, $dependency_manager);
|
||||
|
||||
// The current list of dependents on $names. This list is recalculated when
|
||||
// calling an entity's onDependencyRemoval() method results in the entity
|
||||
// changing. This list is passed to each entity's onDependencyRemoval()
|
||||
// method as the list of affected entities.
|
||||
$current_dependents = $original_dependents;
|
||||
|
||||
// The list of dependents to process. This list changes as entities are
|
||||
// processed and are either fixed or deleted.
|
||||
$dependents_to_process = $original_dependents;
|
||||
|
||||
// Initialize other variables.
|
||||
$affected_uuids = [];
|
||||
$return = [
|
||||
'update' => [],
|
||||
'delete' => [],
|
||||
'unchanged' => [],
|
||||
];
|
||||
|
||||
// Create a map of UUIDs to $original_dependencies key so that we can remove
|
||||
// fixed dependencies.
|
||||
$uuid_map = [];
|
||||
foreach ($original_dependencies as $key => $entity) {
|
||||
$uuid_map[$entity->uuid()] = $key;
|
||||
}
|
||||
|
||||
// Try to fix any dependencies and find out what will happen to the
|
||||
// dependency graph. Entities are processed in the order of most dependent
|
||||
// first. For example, this ensures that Menu UI third party dependencies on
|
||||
// node types are fixed before processing the node type's other
|
||||
// dependencies.
|
||||
while ($dependent = array_pop($dependents)) {
|
||||
// Try to fix the dependents and find out what will happen to the dependency
|
||||
// graph. Entities are processed in the order of most dependent first. For
|
||||
// example, this ensures that Menu UI third party dependencies on node types
|
||||
// are fixed before processing the node type's other dependents.
|
||||
while ($dependent = array_pop($dependents_to_process)) {
|
||||
/** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $dependent */
|
||||
if ($dry_run) {
|
||||
// Clone the entity so any changes do not change any static caches.
|
||||
$dependent = clone $dependent;
|
||||
}
|
||||
$fixed = FALSE;
|
||||
if ($this->callOnDependencyRemoval($dependent, $original_dependencies, $type, $names)) {
|
||||
if ($this->callOnDependencyRemoval($dependent, $current_dependents, $type, $names)) {
|
||||
// Recalculate dependencies and update the dependency graph data.
|
||||
$dependent->calculateDependencies();
|
||||
$dependency_manager->updateData($dependent->getConfigDependencyName(), $dependent->getDependencies());
|
||||
// Based on the updated data rebuild the list of dependents. This will
|
||||
// remove entities that are no longer dependent after the recalculation.
|
||||
$dependents = $this->findConfigEntityDependentsAsEntities($type, $names, $dependency_manager);
|
||||
// Remove any entities that we've already marked for deletion.
|
||||
$dependents = array_filter($dependents, function ($dependent) use ($delete_uuids) {
|
||||
return !in_array($dependent->uuid(), $delete_uuids);
|
||||
// Based on the updated data rebuild the list of current dependents.
|
||||
// This will remove entities that are no longer dependent after the
|
||||
// recalculation.
|
||||
$current_dependents = $this->findConfigEntityDependentsAsEntities($type, $names, $dependency_manager);
|
||||
// Rebuild the list of entities that we need to process using the new
|
||||
// list of current dependents and removing any entities that we've
|
||||
// already processed.
|
||||
$dependents_to_process = array_filter($current_dependents, function ($current_dependent) use ($affected_uuids) {
|
||||
return !in_array($current_dependent->uuid(), $affected_uuids);
|
||||
});
|
||||
// Ensure that the dependency has actually been fixed. It is possible
|
||||
// that the dependent has multiple dependencies that cause it to be in
|
||||
// the dependency chain.
|
||||
// Ensure that the dependent has actually been fixed. It is possible
|
||||
// that other dependencies cause it to still be in the list.
|
||||
$fixed = TRUE;
|
||||
foreach ($dependents as $key => $entity) {
|
||||
foreach ($dependents_to_process as $key => $entity) {
|
||||
if ($entity->uuid() == $dependent->uuid()) {
|
||||
$fixed = FALSE;
|
||||
unset($dependents[$key]);
|
||||
unset($dependents_to_process[$key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($fixed) {
|
||||
// Remove the fixed dependency from the list of original dependencies.
|
||||
unset($original_dependencies[$uuid_map[$dependent->uuid()]]);
|
||||
$affected_uuids[] = $dependent->uuid();
|
||||
$return['update'][] = $dependent;
|
||||
}
|
||||
}
|
||||
// If the entity cannot be fixed then it has to be deleted.
|
||||
if (!$fixed) {
|
||||
$delete_uuids[] = $dependent->uuid();
|
||||
$affected_uuids[] = $dependent->uuid();
|
||||
// Deletes should occur in the order of the least dependent first. For
|
||||
// example, this ensures that fields are removed before field storages.
|
||||
array_unshift($return['delete'], $dependent);
|
||||
}
|
||||
}
|
||||
// Use the lists of UUIDs to filter the original list to work out which
|
||||
// configuration entities are unchanged.
|
||||
$return['unchanged'] = array_filter($original_dependencies, function ($dependent) use ($delete_uuids) {
|
||||
return !(in_array($dependent->uuid(), $delete_uuids));
|
||||
// Use the list of affected UUIDs to filter the original list to work out
|
||||
// which configuration entities are unchanged.
|
||||
$return['unchanged'] = array_filter($original_dependents, function ($dependent) use ($affected_uuids) {
|
||||
return !(in_array($dependent->uuid(), $affected_uuids));
|
||||
});
|
||||
|
||||
return $return;
|
||||
|
||||
@@ -159,7 +159,7 @@ class DatabaseStorage implements StorageInterface {
|
||||
* @throws \Drupal\Core\Config\StorageException
|
||||
* If a database error occurs.
|
||||
*/
|
||||
protected function ensureTableExists() {
|
||||
protected function ensureTableExists() {
|
||||
try {
|
||||
if (!$this->connection->schema()->tableExists($this->table)) {
|
||||
$this->connection->schema()->createTable($this->table, static::schemaDefinition());
|
||||
|
||||
@@ -445,7 +445,7 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
|
||||
* @param bool $is_syncing
|
||||
* Is the configuration entity being created as part of a config sync.
|
||||
*
|
||||
* @return ConfigEntityInterface
|
||||
* @return \Drupal\Core\Config\ConfigEntityInterface
|
||||
* The configuration entity.
|
||||
*
|
||||
* @see \Drupal\Core\Config\Entity\ConfigEntityStorageInterface::createFromStorageRecord()
|
||||
|
||||
@@ -219,7 +219,7 @@ class QueryFactory implements QueryFactoryInterface, EventSubscriberInterface {
|
||||
/**
|
||||
* Updates configuration entity in the key store.
|
||||
*
|
||||
* @param ConfigCrudEvent $event
|
||||
* @param \Drupal\Core\Config\ConfigCrudEvent $event
|
||||
* The configuration event.
|
||||
*/
|
||||
public function onConfigSave(ConfigCrudEvent $event) {
|
||||
|
||||
@@ -124,7 +124,6 @@ class Connection extends DatabaseConnection {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
|
||||
// Create functions needed by SQLite.
|
||||
$pdo->sqliteCreateFunction('if', [__CLASS__, 'sqlFunctionIf']);
|
||||
$pdo->sqliteCreateFunction('greatest', [__CLASS__, 'sqlFunctionGreatest']);
|
||||
|
||||
@@ -19,6 +19,8 @@ class Schema extends DatabaseSchema {
|
||||
|
||||
/**
|
||||
* Override DatabaseSchema::$defaultSchema
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultSchema = 'main';
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@ abstract class Query implements PlaceholderInterface {
|
||||
|
||||
/**
|
||||
* The placeholder counter.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $nextPlaceholder = 0;
|
||||
|
||||
|
||||
@@ -113,6 +113,8 @@ class Select extends Query implements SelectInterface {
|
||||
|
||||
/**
|
||||
* The FOR UPDATE status
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $forUpdate = FALSE;
|
||||
|
||||
@@ -825,7 +827,6 @@ class Select extends Query implements SelectInterface {
|
||||
}
|
||||
$query .= implode(', ', $fields);
|
||||
|
||||
|
||||
// FROM - We presume all queries have a FROM, as any query that doesn't won't need the query builder anyway.
|
||||
$query .= "\nFROM ";
|
||||
foreach ($this->tables as $table) {
|
||||
|
||||
@@ -30,6 +30,8 @@ class SelectExtender implements SelectInterface {
|
||||
|
||||
/**
|
||||
* The placeholder counter.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $placeholder = 0;
|
||||
|
||||
|
||||
@@ -346,6 +346,8 @@ interface SelectInterface extends ConditionInterface, AlterableInterface, Extend
|
||||
* db_query('A')->rightJoin('B') is identical to
|
||||
* db_query('B')->leftJoin('A'). This functionality has been deprecated
|
||||
* because SQLite does not support it.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2765249
|
||||
*/
|
||||
public function rightJoin($table, $alias = NULL, $condition = NULL, $arguments = []);
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ abstract class Schema implements PlaceholderInterface {
|
||||
|
||||
/**
|
||||
* The placeholder counter.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $placeholder = 0;
|
||||
|
||||
@@ -30,6 +32,8 @@ abstract class Schema implements PlaceholderInterface {
|
||||
* method.
|
||||
*
|
||||
* @see DatabaseSchema::getPrefixInfo()
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultSchema = 'public';
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@ class BackendCompilerPass implements CompilerPassInterface {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach ($container->findTaggedServiceIds('backend_overridable') as $id => $attributes) {
|
||||
// If the service is already an alias it is not the original backend, so
|
||||
// we don't want to fallback to other storages any longer.
|
||||
|
||||
@@ -676,13 +676,13 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
||||
*
|
||||
* @param \Exception $e
|
||||
* An exception
|
||||
* @param Request $request
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* A Request instance
|
||||
* @param int $type
|
||||
* The type of the request (one of HttpKernelInterface::MASTER_REQUEST or
|
||||
* HttpKernelInterface::SUB_REQUEST)
|
||||
*
|
||||
* @return Response
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* A Response instance
|
||||
*
|
||||
* @throws \Exception
|
||||
@@ -1187,10 +1187,10 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
||||
/**
|
||||
* Attach synthetic values on to kernel.
|
||||
*
|
||||
* @param ContainerInterface $container
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
|
||||
* Container object
|
||||
*
|
||||
* @return ContainerInterface
|
||||
* @return \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
protected function attachSynthetic(ContainerInterface $container) {
|
||||
$persist = [];
|
||||
@@ -1213,7 +1213,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
||||
/**
|
||||
* Compiles a new service container.
|
||||
*
|
||||
* @return ContainerBuilder The compiled service container
|
||||
* @return \Drupal\Core\DependencyInjection\ContainerBuilder The compiled service container
|
||||
*/
|
||||
protected function compileContainer() {
|
||||
// We are forcing a container build so it is reasonable to assume that the
|
||||
@@ -1334,7 +1334,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
|
||||
/**
|
||||
* Gets a new ContainerBuilder instance used to build the service container.
|
||||
*
|
||||
* @return ContainerBuilder
|
||||
* @return \Drupal\Core\DependencyInjection\ContainerBuilder
|
||||
*/
|
||||
protected function getContainerBuilder() {
|
||||
return new ContainerBuilder(new ParameterBag($this->getKernelParameters()));
|
||||
|
||||
@@ -33,6 +33,8 @@ class EntityType extends Plugin {
|
||||
|
||||
/**
|
||||
* The group machine name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $group = 'default';
|
||||
|
||||
|
||||
@@ -713,6 +713,12 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
||||
elseif (isset($this->translatableEntityKeys[$key][$this->activeLangcode])) {
|
||||
unset($this->translatableEntityKeys[$key][$this->activeLangcode]);
|
||||
}
|
||||
// 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()) {
|
||||
$this->newRevision = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,11 @@ interface EntityChangedInterface {
|
||||
/**
|
||||
* Gets the timestamp of the last entity change across all translations.
|
||||
*
|
||||
* This method will return the highest timestamp across all translations. To
|
||||
* check that no translation is older than in another version of the entity
|
||||
* (e.g. to avoid overwriting newer translations with old data), compare each
|
||||
* translation to the other version individually.
|
||||
*
|
||||
* @return int
|
||||
* The timestamp of the last entity save operation across all
|
||||
* translations.
|
||||
|
||||
@@ -439,7 +439,7 @@ abstract class EntityDisplayBase extends ConfigEntityBase implements EntityDispl
|
||||
/**
|
||||
* Determines if a field has options for a given display.
|
||||
*
|
||||
* @param FieldDefinitionInterface $definition
|
||||
* @param \Drupal\Core\Field\FieldDefinitionInterface $definition
|
||||
* A field definition.
|
||||
* @return array|null
|
||||
*/
|
||||
|
||||
@@ -60,7 +60,7 @@ class EntityFieldManager implements EntityFieldManagerInterface {
|
||||
* - type: The field type.
|
||||
* - bundles: The bundles in which the field appears.
|
||||
*
|
||||
* @return array
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldMap = [];
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ class EntityViewBuilder extends EntityHandlerBase implements EntityHandlerInterf
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
@@ -66,9 +66,9 @@ class EntityViewBuilder extends EntityHandlerBase implements EntityHandlerInterf
|
||||
/**
|
||||
* The EntityViewDisplay objects created for individual field rendering.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityViewBuilder::getSingleFieldDisplay()
|
||||
* @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface[]
|
||||
*
|
||||
* @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface[]
|
||||
* @see \Drupal\Core\Entity\EntityViewBuilder::getSingleFieldDisplay()
|
||||
*/
|
||||
protected $singleFieldDisplays;
|
||||
|
||||
|
||||
+17
-4
@@ -18,10 +18,23 @@ class EntityChangedConstraintValidator extends ConstraintValidator {
|
||||
/** @var \Drupal\Core\Entity\EntityInterface $entity */
|
||||
if (!$entity->isNew()) {
|
||||
$saved_entity = \Drupal::entityManager()->getStorage($entity->getEntityTypeId())->loadUnchanged($entity->id());
|
||||
// A change to any other translation must add a violation to the current
|
||||
// translation because there might be untranslatable shared fields.
|
||||
if ($saved_entity && $saved_entity->getChangedTimeAcrossTranslations() > $entity->getChangedTimeAcrossTranslations()) {
|
||||
$this->context->addViolation($constraint->message);
|
||||
// Ensure that all the entity translations are the same as or newer
|
||||
// than their current version in the storage in order to avoid
|
||||
// reverting other changes. In fact the entity object that is being
|
||||
// saved might contain an older entity translation when different
|
||||
// translations are being concurrently edited.
|
||||
if ($saved_entity) {
|
||||
$common_translation_languages = array_intersect_key($entity->getTranslationLanguages(), $saved_entity->getTranslationLanguages());
|
||||
foreach (array_keys($common_translation_languages) as $langcode) {
|
||||
// Merely comparing the latest changed timestamps across all
|
||||
// translations is not sufficient since other translations may have
|
||||
// been edited and saved in the meanwhile. Therefore, compare the
|
||||
// changed timestamps of each entity translation individually.
|
||||
if ($saved_entity->getTranslation($langcode)->getChangedTime() > $entity->getTranslation($langcode)->getChangedTime()) {
|
||||
$this->context->addViolation($constraint->message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,8 +204,10 @@ class SqlContentEntityStorageSchema implements DynamicallyFieldableEntityStorage
|
||||
|
||||
$current_schema = $this->getSchemaFromStorageDefinition($storage_definition);
|
||||
$this->processFieldStorageSchema($current_schema);
|
||||
$installed_schema = $this->loadFieldSchemaData($original);
|
||||
$this->processFieldStorageSchema($installed_schema);
|
||||
|
||||
return $current_schema != $this->loadFieldSchemaData($original);
|
||||
return $current_schema != $installed_schema;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -287,11 +287,11 @@ use Drupal\node\Entity\NodeType;
|
||||
* out-of-the-box support for Entity API's revisioning and publishing
|
||||
* features, which will allow your entity type to be used with Drupal's
|
||||
* editorial workflow provided by the Content Moderation module.
|
||||
* - The 'id' annotation gives the entity type ID, and the 'label' annotation
|
||||
* gives the human-readable name of the entity type. If you are defining a
|
||||
* content entity type that uses bundles, the 'bundle_label' annotation gives
|
||||
* the human-readable name to use for a bundle of this entity type (for
|
||||
* example, "Content type" for the Node entity).
|
||||
* - In the annotation, the 'id' property gives the entity type ID, and the
|
||||
* 'label' property gives the human-readable name of the entity type. If you
|
||||
* are defining a content entity type that uses bundles, the 'bundle_label'
|
||||
* property gives the human-readable name to use for a bundle of this entity
|
||||
* type (for example, "Content type" for the Node entity).
|
||||
* - The annotation will refer to several handler classes, which you will also
|
||||
* need to define:
|
||||
* - list_builder: Define a class that extends
|
||||
@@ -310,16 +310,17 @@ use Drupal\node\Entity\NodeType;
|
||||
* \Drupal\Core\Entity\EntityViewBuilderInterface (usually extending
|
||||
* \Drupal\Core\Entity\EntityViewBuilder), to display a single entity.
|
||||
* - translation: For translatable content entities (if the 'translatable'
|
||||
* annotation has value TRUE), define a class that extends
|
||||
* annotation property has value TRUE), define a class that extends
|
||||
* \Drupal\content_translation\ContentTranslationHandler, to translate
|
||||
* the content. Configuration translation is handled automatically by the
|
||||
* Configuration Translation module, without the need of a handler class.
|
||||
* - access: If your configuration entity has complex permissions, you might
|
||||
* need an access control handling, implementing
|
||||
* \Drupal\Core\Entity\EntityAccessControlHandlerInterface, but most entities
|
||||
* can just use the 'admin_permission' annotation instead. Note that if you
|
||||
* are creating your own access control handler, you should override the
|
||||
* checkAccess() and checkCreateAccess() methods, not access().
|
||||
* \Drupal\Core\Entity\EntityAccessControlHandlerInterface, but most
|
||||
* entities can just use the 'admin_permission' annotation property
|
||||
* instead. Note that if you are creating your own access control handler,
|
||||
* you should override the checkAccess() and checkCreateAccess() methods,
|
||||
* not access().
|
||||
* - storage: A class implementing
|
||||
* \Drupal\Core\Entity\EntityStorageInterface. If not specified, content
|
||||
* entities will use \Drupal\Core\Entity\Sql\SqlContentEntityStorage, and
|
||||
@@ -352,25 +353,26 @@ use Drupal\node\Entity\NodeType;
|
||||
* - delete-form: Confirmation form to delete the entity.
|
||||
* - edit-form: Editing form.
|
||||
* - Other link types specific to your entity type can also be defined.
|
||||
* - If your content entity is fieldable, provide 'field_ui_base_route'
|
||||
* annotation, giving the name of the route that the Manage Fields, Manage
|
||||
* Display, and Manage Form Display pages from the Field UI module will be
|
||||
* attached to. This is usually the bundle settings edit page, or an entity
|
||||
* type settings page if there are no bundles.
|
||||
* - If your content entity is fieldable, provide the 'field_ui_base_route'
|
||||
* annotation property, giving the name of the route that the Manage Fields,
|
||||
* Manage Display, and Manage Form Display pages from the Field UI module
|
||||
* will be attached to. This is usually the bundle settings edit page, or an
|
||||
* entity type settings page if there are no bundles.
|
||||
* - If your content entity has bundles, you will also need to define a second
|
||||
* plugin to handle the bundles. This plugin is itself a configuration entity
|
||||
* type, so follow the steps here to define it. The machine name ('id'
|
||||
* annotation) of this configuration entity class goes into the
|
||||
* 'bundle_entity_type' annotation on the entity type class. For example, for
|
||||
* the Node entity, the bundle class is \Drupal\node\Entity\NodeType, whose
|
||||
* machine name is 'node_type'. This is the annotation value for
|
||||
* 'bundle_entity_type' on the \Drupal\node\Entity\Node class. Also, the
|
||||
* bundle config entity type annotation must have a 'bundle_of' entry,
|
||||
* annotation property) of this configuration entity class goes into the
|
||||
* 'bundle_entity_type' annotation property on the entity type class. For
|
||||
* example, for the Node entity, the bundle class is
|
||||
* \Drupal\node\Entity\NodeType, whose machine name is 'node_type'. This is
|
||||
* the annotation property 'bundle_entity_type' on the
|
||||
* \Drupal\node\Entity\Node class. Also, the
|
||||
* bundle config entity type annotation must have a 'bundle_of' property,
|
||||
* giving the machine name of the entity type it is acting as a bundle for.
|
||||
* These machine names are considered permanent, they may not be renamed.
|
||||
* - Additional annotations can be seen on entity class examples such as
|
||||
* \Drupal\node\Entity\Node (content) and \Drupal\user\Entity\Role
|
||||
* (configuration). These annotations are documented on
|
||||
* - Additional annotation properties can be seen on entity class examples such
|
||||
* as \Drupal\node\Entity\Node (content) and \Drupal\user\Entity\Role
|
||||
* (configuration). These annotation properties are documented on
|
||||
* \Drupal\Core\Entity\EntityType.
|
||||
*
|
||||
* @section sec_routes Entity routes
|
||||
@@ -456,8 +458,8 @@ use Drupal\node\Entity\NodeType;
|
||||
* $storage = $container->get('entity.manager')->getStorage('your_entity_type');
|
||||
* @endcode
|
||||
* Here, 'your_entity_type' is the machine name of your entity type ('id'
|
||||
* annotation on the entity class), and note that you should use dependency
|
||||
* injection to retrieve this object if possible. See the
|
||||
* annotation property on the entity class), and note that you should use
|
||||
* dependency injection to retrieve this object if possible. See the
|
||||
* @link container Services and Dependency Injection topic @endlink for more
|
||||
* about how to properly retrieve services.
|
||||
*
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Core\EventSubscriber;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
|
||||
use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
/**
|
||||
* View subscriber rendering a 406 if we could not route or render a request.
|
||||
*
|
||||
* @todo fix or replace this in https://www.drupal.org/node/2364011
|
||||
*/
|
||||
class AcceptNegotiation406 implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* Throws an HTTP 406 error if we get this far, which we normally shouldn't.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event
|
||||
* The event to process.
|
||||
*/
|
||||
public function onViewDetect406(GetResponseForControllerResultEvent $event) {
|
||||
$request = $event->getRequest();
|
||||
$result = $event->getControllerResult();
|
||||
|
||||
// If this is a render array then we assume that the router went with the
|
||||
// generic controller and not one with a format. If the format requested is
|
||||
// not HTML though we can also assume that the requested format is invalid
|
||||
// so we provide a 406 response.
|
||||
if (is_array($result) && $request->getRequestFormat() !== 'html') {
|
||||
throw new NotAcceptableHttpException('Not acceptable format: ' . $request->getRequestFormat());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
$events[KernelEvents::VIEW][] = ['onViewDetect406', -10];
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -136,7 +136,7 @@ class ConfigImportSubscriber extends ConfigImportValidateEventSubscriberBase {
|
||||
|
||||
// Ensure the profile is not changing.
|
||||
if ($install_profile !== $core_extension['profile']) {
|
||||
$config_importer->logError($this->t('Cannot change the install profile from %new_profile to %profile once Drupal is installed.', ['%profile' => $install_profile, '%new_profile' => $core_extension['profile']]));
|
||||
$config_importer->logError($this->t('Cannot change the install profile from %profile to %new_profile once Drupal is installed.', ['%profile' => $install_profile, '%new_profile' => $core_extension['profile']]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,9 +37,9 @@ class ConfigSnapshotSubscriber implements EventSubscriberInterface {
|
||||
/**
|
||||
* Constructs the ConfigSnapshotSubscriber object.
|
||||
*
|
||||
* @param StorageInterface $source_storage
|
||||
* @param \Drupal\Core\Config\StorageInterface $source_storage
|
||||
* The source storage used to discover configuration changes.
|
||||
* @param StorageInterface $snapshot_storage
|
||||
* @param \Drupal\Core\Config\StorageInterface $snapshot_storage
|
||||
* The snapshot storage used to write configuration changes.
|
||||
*/
|
||||
public function __construct(ConfigManagerInterface $config_manager, StorageInterface $source_storage, StorageInterface $snapshot_storage) {
|
||||
|
||||
@@ -1,245 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Core\EventSubscriber;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\Utility\Error;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
/**
|
||||
* Last-chance handler for exceptions.
|
||||
*
|
||||
* This handler will catch any exceptions not caught elsewhere and report
|
||||
* them as an error page.
|
||||
*/
|
||||
class DefaultExceptionSubscriber implements EventSubscriberInterface {
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* One of the error level constants defined in bootstrap.inc.
|
||||
*/
|
||||
protected $errorLevel;
|
||||
|
||||
/**
|
||||
* The config factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* Constructs a new DefaultExceptionSubscriber.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The configuration factory.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory) {
|
||||
$this->configFactory = $config_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configured error level.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorLevel() {
|
||||
if (!isset($this->errorLevel)) {
|
||||
$this->errorLevel = $this->configFactory->get('system.logging')->get('error_level');
|
||||
}
|
||||
return $this->errorLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles any exception as a generic error page for HTML.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
|
||||
* The event to process.
|
||||
*/
|
||||
protected function onHtml(GetResponseForExceptionEvent $event) {
|
||||
$exception = $event->getException();
|
||||
$error = Error::decodeException($exception);
|
||||
|
||||
// Display the message if the current error reporting level allows this type
|
||||
// of message to be displayed, and unconditionally in update.php.
|
||||
$message = '';
|
||||
if (error_displayable($error)) {
|
||||
// If error type is 'User notice' then treat it as debug information
|
||||
// instead of an error message.
|
||||
// @see debug()
|
||||
if ($error['%type'] == 'User notice') {
|
||||
$error['%type'] = 'Debug';
|
||||
}
|
||||
|
||||
// Attempt to reduce verbosity by removing DRUPAL_ROOT from the file path
|
||||
// in the message. This does not happen for (false) security.
|
||||
$root_length = strlen(DRUPAL_ROOT);
|
||||
if (substr($error['%file'], 0, $root_length) == DRUPAL_ROOT) {
|
||||
$error['%file'] = substr($error['%file'], $root_length + 1);
|
||||
}
|
||||
|
||||
unset($error['backtrace']);
|
||||
|
||||
if ($this->getErrorLevel() != ERROR_REPORTING_DISPLAY_VERBOSE) {
|
||||
// Without verbose logging, use a simple message.
|
||||
|
||||
// We call SafeMarkup::format directly here, rather than use t() since
|
||||
// we are in the middle of error handling, and we don't want t() to
|
||||
// cause further errors.
|
||||
$message = SafeMarkup::format('%type: @message in %function (line %line of %file).', $error);
|
||||
}
|
||||
else {
|
||||
// With verbose logging, we will also include a backtrace.
|
||||
|
||||
$backtrace_exception = $exception;
|
||||
while ($backtrace_exception->getPrevious()) {
|
||||
$backtrace_exception = $backtrace_exception->getPrevious();
|
||||
}
|
||||
$backtrace = $backtrace_exception->getTrace();
|
||||
// First trace is the error itself, already contained in the message.
|
||||
// While the second trace is the error source and also contained in the
|
||||
// message, the message doesn't contain argument values, so we output it
|
||||
// once more in the backtrace.
|
||||
array_shift($backtrace);
|
||||
|
||||
// Generate a backtrace containing only scalar argument values.
|
||||
$error['@backtrace'] = Error::formatBacktrace($backtrace);
|
||||
$message = SafeMarkup::format('%type: @message in %function (line %line of %file). <pre class="backtrace">@backtrace</pre>', $error);
|
||||
}
|
||||
}
|
||||
|
||||
$content = $this->t('The website encountered an unexpected error. Please try again later.');
|
||||
$content .= $message ? '</br></br>' . $message : '';
|
||||
$response = new Response($content, 500);
|
||||
|
||||
if ($exception instanceof HttpExceptionInterface) {
|
||||
$response->setStatusCode($exception->getStatusCode());
|
||||
$response->headers->add($exception->getHeaders());
|
||||
}
|
||||
else {
|
||||
$response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR, '500 Service unavailable (with message)');
|
||||
}
|
||||
|
||||
$event->setResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles any exception as a generic error page for JSON.
|
||||
*
|
||||
* @todo This should probably check the error reporting level.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
|
||||
* The event to process.
|
||||
*/
|
||||
protected function onJson(GetResponseForExceptionEvent $event) {
|
||||
$exception = $event->getException();
|
||||
$error = Error::decodeException($exception);
|
||||
|
||||
// Display the message if the current error reporting level allows this type
|
||||
// of message to be displayed,
|
||||
$data = NULL;
|
||||
if (error_displayable($error) && $message = $exception->getMessage()) {
|
||||
$data = ['message' => sprintf('A fatal error occurred: %s', $message)];
|
||||
}
|
||||
|
||||
$response = new JsonResponse($data, Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
if ($exception instanceof HttpExceptionInterface) {
|
||||
$response->setStatusCode($exception->getStatusCode());
|
||||
$response->headers->add($exception->getHeaders());
|
||||
}
|
||||
|
||||
$event->setResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an HttpExceptionInterface exception for unknown formats.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
|
||||
* The event to process.
|
||||
*/
|
||||
protected function onFormatUnknown(GetResponseForExceptionEvent $event) {
|
||||
/** @var \Symfony\Component\HttpKernel\Exception\HttpExceptionInterface|\Exception $exception */
|
||||
$exception = $event->getException();
|
||||
|
||||
$response = new Response($exception->getMessage(), $exception->getStatusCode(), $exception->getHeaders());
|
||||
$event->setResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles errors for this subscriber.
|
||||
*
|
||||
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
|
||||
* The event to process.
|
||||
*/
|
||||
public function onException(GetResponseForExceptionEvent $event) {
|
||||
$format = $this->getFormat($event->getRequest());
|
||||
$exception = $event->getException();
|
||||
|
||||
$method = 'on' . $format;
|
||||
if (!method_exists($this, $method)) {
|
||||
if ($exception instanceof HttpExceptionInterface) {
|
||||
$this->onFormatUnknown($event);
|
||||
$response = $event->getResponse();
|
||||
$response->headers->set('Content-Type', 'text/plain');
|
||||
}
|
||||
else {
|
||||
$this->onHtml($event);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->$method($event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the error-relevant format from the request.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request object.
|
||||
*
|
||||
* @return string
|
||||
* The format as which to treat the exception.
|
||||
*/
|
||||
protected function getFormat(Request $request) {
|
||||
$format = $request->query->get(MainContentViewSubscriber::WRAPPER_FORMAT, $request->getRequestFormat());
|
||||
|
||||
// These are all JSON errors for our purposes. Any special handling for
|
||||
// them can/should happen in earlier listeners if desired.
|
||||
if (in_array($format, ['drupal_modal', 'drupal_dialog', 'drupal_ajax'])) {
|
||||
$format = 'json';
|
||||
}
|
||||
|
||||
// Make an educated guess that any Accept header type that includes "json"
|
||||
// can probably handle a generic JSON response for errors. As above, for
|
||||
// any format this doesn't catch or that wants custom handling should
|
||||
// register its own exception listener.
|
||||
foreach ($request->getAcceptableContentTypes() as $mime) {
|
||||
if (strpos($mime, 'html') === FALSE && strpos($mime, 'json') !== FALSE) {
|
||||
$format = 'json';
|
||||
}
|
||||
}
|
||||
|
||||
return $format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the methods in this class that should be listeners.
|
||||
*
|
||||
* @return array
|
||||
* An array of event listener definitions.
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
$events[KernelEvents::EXCEPTION][] = ['onException', -256];
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -194,6 +194,17 @@ class ModuleInstaller implements ModuleInstallerInterface {
|
||||
// Update the kernel to include it.
|
||||
$this->updateKernel($module_filenames);
|
||||
|
||||
// Replace the route provider service with a version that will rebuild
|
||||
// if routes used during installation. This ensures that a module's
|
||||
// routes are available during installation. This has to occur before
|
||||
// any services that depend on it are instantiated otherwise those
|
||||
// services will have the old route provider injected. Note that, since
|
||||
// the container is rebuilt by updating the kernel, the route provider
|
||||
// service is the regular one even though we are in a loop and might
|
||||
// have replaced it before.
|
||||
\Drupal::getContainer()->set('router.route_provider.old', \Drupal::service('router.route_provider'));
|
||||
\Drupal::getContainer()->set('router.route_provider', \Drupal::service('router.route_provider.lazy_builder'));
|
||||
|
||||
// Allow modules to react prior to the installation of a module.
|
||||
$this->moduleHandler->invokeAll('module_preinstall', [$module]);
|
||||
|
||||
@@ -283,10 +294,6 @@ class ModuleInstaller implements ModuleInstallerInterface {
|
||||
// @see https://www.drupal.org/node/2208429
|
||||
\Drupal::service('theme_handler')->refreshInfo();
|
||||
|
||||
// In order to make uninstalling transactional if anything uses routes.
|
||||
\Drupal::getContainer()->set('router.route_provider.old', \Drupal::service('router.route_provider'));
|
||||
\Drupal::getContainer()->set('router.route_provider', \Drupal::service('router.route_provider.lazy_builder'));
|
||||
|
||||
// Allow the module to perform install tasks.
|
||||
$this->moduleHandler->invoke($module, 'install');
|
||||
|
||||
@@ -498,34 +505,30 @@ class ModuleInstaller implements ModuleInstallerInterface {
|
||||
* The name of the module for which to remove all registered cache bins.
|
||||
*/
|
||||
protected function removeCacheBins($module) {
|
||||
// Remove any cache bins defined by a module.
|
||||
$service_yaml_file = drupal_get_path('module', $module) . "/$module.services.yml";
|
||||
if (file_exists($service_yaml_file)) {
|
||||
$definitions = Yaml::decode(file_get_contents($service_yaml_file));
|
||||
if (isset($definitions['services'])) {
|
||||
foreach ($definitions['services'] as $id => $definition) {
|
||||
if (isset($definition['tags'])) {
|
||||
foreach ($definition['tags'] as $tag) {
|
||||
// This works for the default cache registration and even in some
|
||||
// cases when a non-default "super" factory is used. That should
|
||||
// be extremely rare.
|
||||
if ($tag['name'] == 'cache.bin' && isset($definition['factory_service']) && isset($definition['factory_method']) && !empty($definition['arguments'])) {
|
||||
try {
|
||||
$factory = \Drupal::service($definition['factory_service']);
|
||||
if (method_exists($factory, $definition['factory_method'])) {
|
||||
$backend = call_user_func_array([$factory, $definition['factory_method']], $definition['arguments']);
|
||||
if ($backend instanceof CacheBackendInterface) {
|
||||
$backend->removeBin();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
watchdog_exception('system', $e, 'Failed to remove cache bin defined by the service %id.', ['%id' => $id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!file_exists($service_yaml_file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$definitions = Yaml::decode(file_get_contents($service_yaml_file));
|
||||
|
||||
$cache_bin_services = array_filter(
|
||||
isset($definitions['services']) ? $definitions['services'] : [],
|
||||
function ($definition) {
|
||||
$tags = isset($definition['tags']) ? $definition['tags'] : [];
|
||||
foreach ($tags as $tag) {
|
||||
if (isset($tag['name']) && ($tag['name'] == 'cache.bin')) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
);
|
||||
|
||||
foreach (array_keys($cache_bin_services) as $service_id) {
|
||||
$backend = $this->kernel->getContainer()->get($service_id);
|
||||
if ($backend instanceof CacheBackendInterface) {
|
||||
$backend->removeBin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +265,6 @@ class ThemeInstaller implements ThemeInstallerInterface {
|
||||
$extension_config->save(TRUE);
|
||||
$this->state->set('system.theme.data', $current_theme_data);
|
||||
|
||||
|
||||
// @todo Remove system_list().
|
||||
$this->themeHandler->refreshInfo();
|
||||
$this->resetSystem();
|
||||
|
||||
@@ -7,6 +7,7 @@ use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\TypedData\DataDefinitionInterface;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\ItemList;
|
||||
|
||||
/**
|
||||
@@ -378,7 +379,6 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function equals(FieldItemListInterface $list_to_compare) {
|
||||
$columns = $this->getFieldDefinition()->getFieldStorageDefinition()->getColumns();
|
||||
$count1 = count($this);
|
||||
$count2 = count($list_to_compare);
|
||||
if ($count1 === 0 && $count2 === 0) {
|
||||
@@ -396,9 +396,13 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
|
||||
}
|
||||
// If the values are not equal ensure a consistent order of field item
|
||||
// properties and remove properties which will not be saved.
|
||||
$callback = function (&$value) use ($columns) {
|
||||
$property_definitions = $this->getFieldDefinition()->getFieldStorageDefinition()->getPropertyDefinitions();
|
||||
$non_computed_properties = array_filter($property_definitions, function (DataDefinitionInterface $property) {
|
||||
return !$property->isComputed();
|
||||
});
|
||||
$callback = function (&$value) use ($non_computed_properties) {
|
||||
if (is_array($value)) {
|
||||
$value = array_intersect_key($value, $columns);
|
||||
$value = array_intersect_key($value, $non_computed_properties);
|
||||
ksort($value);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Core\Field;
|
||||
|
||||
/**
|
||||
* Defines a item list class for map fields.
|
||||
*/
|
||||
class MapFieldItemList extends FieldItemList {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function equals(FieldItemListInterface $list_to_compare) {
|
||||
$count1 = count($this);
|
||||
$count2 = count($list_to_compare);
|
||||
if ($count1 === 0 && $count2 === 0) {
|
||||
// Both are empty we can safely assume that it did not change.
|
||||
return TRUE;
|
||||
}
|
||||
if ($count1 !== $count2) {
|
||||
// The number of items is different so they do not have the same values.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// The map field type does not have any property defined (because they are
|
||||
// dynamic), so the only way to evaluate the equality for it is to rely
|
||||
// solely on its values.
|
||||
$value1 = $this->getValue();
|
||||
$value2 = $list_to_compare->getValue();
|
||||
|
||||
return $value1 == $value2;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -82,7 +82,7 @@ class EntityReferenceEntityFormatter extends EntityReferenceFormatterBase implem
|
||||
* The view mode.
|
||||
* @param array $third_party_settings
|
||||
* Any third party settings settings.
|
||||
* @param LoggerChannelFactoryInterface $logger_factory
|
||||
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
|
||||
* The logger factory.
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
|
||||
@@ -503,19 +503,6 @@ class EntityReferenceItem extends FieldItemBase implements OptionsProviderInterf
|
||||
}
|
||||
|
||||
$bundles_changed = TRUE;
|
||||
|
||||
// In case we deleted the only target bundle allowed by the field
|
||||
// we have to log a critical message because the field will not
|
||||
// function correctly anymore.
|
||||
if ($handler_settings['target_bundles'] === []) {
|
||||
\Drupal::logger('entity_reference')->critical('The %target_bundle bundle (entity type: %target_entity_type) was deleted. As a result, the %field_name entity reference field (entity_type: %entity_type, bundle: %bundle) no longer has any valid bundle it can reference. The field is not working correctly anymore and has to be adjusted.', [
|
||||
'%target_bundle' => $bundle->label(),
|
||||
'%target_entity_type' => $bundle->getEntityType()->getBundleOf(),
|
||||
'%field_name' => $field_definition->getName(),
|
||||
'%entity_type' => $field_definition->getTargetEntityTypeId(),
|
||||
'%bundle' => $field_definition->getTargetBundle()
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ use Drupal\Core\Field\FieldItemBase;
|
||||
* id = "map",
|
||||
* label = @Translation("Map"),
|
||||
* description = @Translation("An entity field for storing a serialized array of values."),
|
||||
* no_ui = TRUE
|
||||
* no_ui = TRUE,
|
||||
* list_class = "\Drupal\Core\Field\MapFieldItemList",
|
||||
* )
|
||||
*/
|
||||
class MapItem extends FieldItemBase {
|
||||
|
||||
@@ -42,7 +42,7 @@ class Email extends FieldPluginBase {
|
||||
*/
|
||||
public function processFieldValues(MigrationInterface $migration, $field_name, $data) {
|
||||
$process = [
|
||||
'plugin' => 'iterator',
|
||||
'plugin' => 'sub_process',
|
||||
'source' => $field_name,
|
||||
'process' => [
|
||||
'value' => 'email',
|
||||
|
||||
@@ -41,7 +41,7 @@ class MimeTypeGuesser implements MimeTypeGuesserInterface {
|
||||
/**
|
||||
* Constructs a MimeTypeGuesser object.
|
||||
*
|
||||
* @param StreamWrapperManagerInterface $stream_wrapper_manager
|
||||
* @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager
|
||||
* The stream wrapper manager.
|
||||
*/
|
||||
public function __construct(StreamWrapperManagerInterface $stream_wrapper_manager) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Drupal\Core\Form\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Ajax\ReplaceCommand;
|
||||
use Drupal\Core\Ajax\PrependCommand;
|
||||
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
|
||||
use Drupal\Core\Form\Exception\BrokenPostRequestException;
|
||||
use Drupal\Core\Form\FormAjaxException;
|
||||
@@ -78,7 +78,7 @@ class FormAjaxSubscriber implements EventSubscriberInterface {
|
||||
$this->drupalSetMessage($this->t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', ['@size' => $this->formatSize($exception->getSize())]), 'error');
|
||||
$response = new AjaxResponse();
|
||||
$status_messages = ['#type' => 'status_messages'];
|
||||
$response->addCommand(new ReplaceCommand(NULL, $status_messages));
|
||||
$response->addCommand(new PrependCommand(NULL, $status_messages));
|
||||
$response->headers->set('X-Status-Code', 200);
|
||||
$event->setResponse($response);
|
||||
return;
|
||||
|
||||
@@ -557,7 +557,7 @@ abstract class FormStateDecoratorBase implements FormStateInterface {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isValueEmpty($key) {
|
||||
public function isValueEmpty($key) {
|
||||
return $this->decoratedFormState->isValueEmpty($key);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class GeneratedLink extends BubbleableMetadata implements MarkupInterface, \Coun
|
||||
* @return string
|
||||
*/
|
||||
public function getGeneratedLink() {
|
||||
return $this->generatedLink ;
|
||||
return $this->generatedLink;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,7 @@ class GeneratedUrl extends BubbleableMetadata {
|
||||
* @return string
|
||||
*/
|
||||
public function getGeneratedUrl() {
|
||||
return $this->generatedUrl ;
|
||||
return $this->generatedUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Core\Installer;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Config\ConfigFactoryOverrideInterface;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\DependencyInjection\ServiceProviderInterface;
|
||||
|
||||
/**
|
||||
* Override configuration during the installer.
|
||||
*/
|
||||
class ConfigOverride implements ServiceProviderInterface, ConfigFactoryOverrideInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function register(ContainerBuilder $container) {
|
||||
// Register this class so that it can override configuration.
|
||||
$container
|
||||
->register('core.install_config_override', static::class)
|
||||
->addTag('config.factory.override');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadOverrides($names) {
|
||||
$overrides = [];
|
||||
if (drupal_installation_attempted() && function_exists('drupal_install_profile_distribution_name')) {
|
||||
// Early in the installer the site name is unknown. In this case we need
|
||||
// to fallback to the distribution's name.
|
||||
$overrides['system.site'] = [
|
||||
'name' => drupal_install_profile_distribution_name(),
|
||||
];
|
||||
}
|
||||
return $overrides;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheSuffix() {
|
||||
return 'core.install_config_override';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata($name) {
|
||||
return new CacheableMetadata();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,7 +40,7 @@ class KeyValueFactory implements KeyValueFactoryInterface {
|
||||
protected $stores = [];
|
||||
|
||||
/**
|
||||
* var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
* @var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Drupal\Core\Mail;
|
||||
|
||||
use Drupal\Component\Render\PlainTextOutput;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
@@ -248,7 +249,12 @@ class MailManager extends DefaultPluginManager implements MailManagerInterface {
|
||||
// Return-Path headers should have a domain authorized to use the
|
||||
// originating SMTP server.
|
||||
$headers['Sender'] = $headers['Return-Path'] = $site_mail;
|
||||
$headers['From'] = $site_config->get('name') . ' <' . $site_mail . '>';
|
||||
// Headers are usually encoded in the mail plugin that implements
|
||||
// \Drupal\Core\Mail\MailInterface::mail(), for example,
|
||||
// \Drupal\Core\Mail\Plugin\Mail\PhpMail::mail(). The site name must be
|
||||
// encoded here to prevent mail plugins from encoding the email address,
|
||||
// which would break the header.
|
||||
$headers['From'] = Unicode::mimeHeaderEncode($site_config->get('name'), TRUE) . ' <' . $site_mail . '>';
|
||||
if ($reply) {
|
||||
$headers['Reply-to'] = $reply;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ class PhpassHashedPassword implements PasswordInterface {
|
||||
|
||||
/**
|
||||
* Returns a string for mapping an int to the corresponding base 64 character.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $ITOA64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class ContextDefinition implements ContextDefinitionInterface {
|
||||
/**
|
||||
* The data type of the data.
|
||||
*
|
||||
* @return string
|
||||
* @var string
|
||||
* The data type.
|
||||
*/
|
||||
protected $dataType;
|
||||
@@ -25,7 +25,7 @@ class ContextDefinition implements ContextDefinitionInterface {
|
||||
/**
|
||||
* The human-readable label.
|
||||
*
|
||||
* @return string
|
||||
* @var string
|
||||
* The label.
|
||||
*/
|
||||
protected $label;
|
||||
@@ -33,7 +33,7 @@ class ContextDefinition implements ContextDefinitionInterface {
|
||||
/**
|
||||
* The human-readable description.
|
||||
*
|
||||
* @return string|null
|
||||
* @var string|null
|
||||
* The description, or NULL if no description is available.
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
@@ -87,19 +87,19 @@ class HtmlTag extends RenderElement {
|
||||
$escaped_tag = HtmlUtility::escape($element['#tag']);
|
||||
$open_tag = '<' . $escaped_tag . $attributes;
|
||||
$close_tag = '</' . $escaped_tag . ">\n";
|
||||
$prefix = isset($element['#prefix']) ? $element['#prefix'] . $open_tag : $open_tag;
|
||||
$suffix = isset($element['#suffix']) ? $close_tag . $element['#suffix'] : $close_tag;
|
||||
// Construct a void element.
|
||||
if (in_array($element['#tag'], self::$voidElements)) {
|
||||
$prefix .= " />\n";
|
||||
$suffix = '';
|
||||
$open_tag .= ' />';
|
||||
$close_tag = "\n";
|
||||
}
|
||||
// Construct all other elements.
|
||||
else {
|
||||
$prefix .= '>';
|
||||
$open_tag .= '>';
|
||||
$markup = $element['#value'] instanceof MarkupInterface ? $element['#value'] : Xss::filterAdmin($element['#value']);
|
||||
$element['#markup'] = Markup::create($markup);
|
||||
}
|
||||
$prefix = isset($element['#prefix']) ? $element['#prefix'] . $open_tag : $open_tag;
|
||||
$suffix = isset($element['#suffix']) ? $close_tag . $element['#suffix'] : $close_tag;
|
||||
if (!empty($element['#noscript'])) {
|
||||
$prefix = '<noscript>' . $prefix;
|
||||
$suffix .= '</noscript>';
|
||||
|
||||
@@ -269,7 +269,6 @@ abstract class RenderElement extends PluginBase implements ElementInterface {
|
||||
$element['#attributes']['data-disable-refocus'] = "true";
|
||||
}
|
||||
|
||||
|
||||
// Add a reasonable default event handler if none was specified.
|
||||
if (isset($element['#ajax']) && !isset($element['#ajax']['event'])) {
|
||||
switch ($element['#type']) {
|
||||
|
||||
@@ -62,7 +62,7 @@ class DialogRenderer implements MainContentRendererInterface {
|
||||
*
|
||||
* @param array &$options
|
||||
* The 'target' option, if set, is used, and then removed from $options.
|
||||
* @param RouteMatchInterface $route_match
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* When no 'target' option is set in $options, $route_match is used instead
|
||||
* to determine the target.
|
||||
*
|
||||
|
||||
@@ -509,11 +509,17 @@ class Renderer implements RendererInterface {
|
||||
// We store the resulting output in $elements['#markup'], to be consistent
|
||||
// with how render cached output gets stored. This ensures that placeholder
|
||||
// replacement logic gets the same data to work with, no matter if #cache is
|
||||
// disabled, #cache is enabled, there is a cache hit or miss.
|
||||
$prefix = isset($elements['#prefix']) ? $this->xssFilterAdminIfUnsafe($elements['#prefix']) : '';
|
||||
$suffix = isset($elements['#suffix']) ? $this->xssFilterAdminIfUnsafe($elements['#suffix']) : '';
|
||||
|
||||
$elements['#markup'] = Markup::create($prefix . $elements['#children'] . $suffix);
|
||||
// disabled, #cache is enabled, there is a cache hit or miss. If
|
||||
// #render_children is set the #prefix and #suffix will have already been
|
||||
// added.
|
||||
if (isset($elements['#render_children'])) {
|
||||
$elements['#markup'] = Markup::create($elements['#children']);
|
||||
}
|
||||
else {
|
||||
$prefix = isset($elements['#prefix']) ? $this->xssFilterAdminIfUnsafe($elements['#prefix']) : '';
|
||||
$suffix = isset($elements['#suffix']) ? $this->xssFilterAdminIfUnsafe($elements['#suffix']) : '';
|
||||
$elements['#markup'] = Markup::create($prefix . $elements['#children'] . $suffix);
|
||||
}
|
||||
|
||||
// We've rendered this element (and its subtree!), now update the context.
|
||||
$context->update($elements);
|
||||
|
||||
@@ -613,6 +613,10 @@ function hook_preprocess_HOOK(&$variables) {
|
||||
* hook called (in this case 'node__article') is available in
|
||||
* $variables['theme_hook_original'].
|
||||
*
|
||||
* Implementations of this hook must be placed in *.module or *.theme files, or
|
||||
* must otherwise make sure that the hook implementation is available at
|
||||
* any given time.
|
||||
*
|
||||
* @todo Add @code sample.
|
||||
*
|
||||
* @param array $variables
|
||||
@@ -694,6 +698,10 @@ function hook_theme_suggestions_alter(array &$suggestions, array $variables, $ho
|
||||
* hook called (in this case 'node__article') is available in
|
||||
* $variables['theme_hook_original'].
|
||||
*
|
||||
* Implementations of this hook must be placed in *.module or *.theme files, or
|
||||
* must otherwise make sure that the hook implementation is available at
|
||||
* any given time.
|
||||
*
|
||||
* @todo Add @code sample.
|
||||
*
|
||||
* @param array $suggestions
|
||||
|
||||
@@ -141,7 +141,6 @@ class MatcherDumper implements MatcherDumperInterface {
|
||||
$insert->execute();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$transaction->rollBack();
|
||||
|
||||
@@ -67,7 +67,7 @@ class RouteMatch implements RouteMatchInterface {
|
||||
/**
|
||||
* Creates a RouteMatch from a request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* A request object.
|
||||
*
|
||||
* @return \Drupal\Core\Routing\RouteMatchInterface
|
||||
|
||||
@@ -135,7 +135,7 @@ class RouteProvider implements PreloadableRouteProviderInterface, PagedRouteProv
|
||||
* very large route sets to be filtered down to likely candidates, which
|
||||
* may then be filtered in memory more completely.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* A request against which to match.
|
||||
*
|
||||
* @return \Symfony\Component\Routing\RouteCollection
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
namespace Drupal\Core\Routing;
|
||||
|
||||
use Symfony\Cmf\Component\Routing\PagedRouteProviderInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* A Route Provider front-end for all Drupal-stored routes.
|
||||
*/
|
||||
class RouteProviderLazyBuilder implements PreloadableRouteProviderInterface, PagedRouteProviderInterface {
|
||||
class RouteProviderLazyBuilder implements PreloadableRouteProviderInterface, PagedRouteProviderInterface, EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The route provider service.
|
||||
@@ -31,6 +32,18 @@ class RouteProviderLazyBuilder implements PreloadableRouteProviderInterface, Pag
|
||||
*/
|
||||
protected $rebuilt = FALSE;
|
||||
|
||||
/**
|
||||
* Flag to determine if router is currently being rebuilt.
|
||||
*
|
||||
* Used to prevent recursive router rebuilds during module installation.
|
||||
* Recursive rebuilds can occur when route information is required by alter
|
||||
* hooks that are triggered during a rebuild, for example,
|
||||
* hook_menu_links_discovered_alter().
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $rebuilding = FALSE;
|
||||
|
||||
/**
|
||||
* RouteProviderLazyBuilder constructor.
|
||||
*
|
||||
@@ -51,7 +64,7 @@ class RouteProviderLazyBuilder implements PreloadableRouteProviderInterface, Pag
|
||||
* The route provider service.
|
||||
*/
|
||||
protected function getRouteProvider() {
|
||||
if (!$this->rebuilt) {
|
||||
if (!$this->rebuilt && !$this->rebuilding) {
|
||||
$this->routeBuilder->rebuild();
|
||||
$this->rebuilt = TRUE;
|
||||
}
|
||||
@@ -132,4 +145,27 @@ class RouteProviderLazyBuilder implements PreloadableRouteProviderInterface, Pag
|
||||
return $this->rebuilt;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
$events[RoutingEvents::DYNAMIC][] = ['routerRebuilding', 3000];
|
||||
$events[RoutingEvents::FINISHED][] = ['routerRebuildFinished', -3000];
|
||||
return $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the router rebuilding flag to TRUE.
|
||||
*/
|
||||
public function routerRebuilding() {
|
||||
$this->rebuilding = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the router rebuilding flag to FALSE.
|
||||
*/
|
||||
public function routerRebuildFinished() {
|
||||
$this->rebuilding = FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Core\TypedData;
|
||||
|
||||
/**
|
||||
* Provides common functionality for computed item lists.
|
||||
*
|
||||
* @see \Drupal\Core\TypedData\ListInterface
|
||||
* @see \Drupal\Core\TypedData\Plugin\DataType\ItemList
|
||||
* @see \Drupal\Core\Field\FieldItemListInterface
|
||||
* @see \Drupal\Core\Field\FieldItemList
|
||||
*
|
||||
* @ingroup typed_data
|
||||
*
|
||||
* @internal
|
||||
* This trait has been added in Drupal 8.4.3 as an internal helper and should
|
||||
* not be used outside of core.
|
||||
*/
|
||||
trait ComputedItemListTrait {
|
||||
|
||||
/**
|
||||
* Whether the values have already been computed or not.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $valueComputed = FALSE;
|
||||
|
||||
/**
|
||||
* Computes the values for an item list.
|
||||
*/
|
||||
abstract protected function computeValue();
|
||||
|
||||
/**
|
||||
* Ensures that values are only computed once.
|
||||
*/
|
||||
protected function ensureComputedValue() {
|
||||
if ($this->valueComputed === FALSE) {
|
||||
$this->computeValue();
|
||||
$this->valueComputed = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValue() {
|
||||
$this->ensureComputedValue();
|
||||
return parent::getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setValue($values, $notify = TRUE) {
|
||||
parent::setValue($values, $notify);
|
||||
|
||||
// Make sure that subsequent getter calls do not try to compute the values
|
||||
// again.
|
||||
$this->valueComputed = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getString() {
|
||||
$this->ensureComputedValue();
|
||||
return parent::getString();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($index) {
|
||||
if (!is_numeric($index)) {
|
||||
throw new \InvalidArgumentException('Unable to get a value with a non-numeric delta in a list.');
|
||||
}
|
||||
|
||||
// Unlike the base implementation of
|
||||
// \Drupal\Core\TypedData\ListInterface::get(), we do not add an empty item
|
||||
// automatically because computed item lists need to behave like
|
||||
// non-computed ones. For example, calling isEmpty() on a computed item list
|
||||
// should return TRUE when the values were computed and the item list is
|
||||
// truly empty.
|
||||
// @see \Drupal\Core\TypedData\Plugin\DataType\ItemList::get().
|
||||
$this->ensureComputedValue();
|
||||
|
||||
return isset($this->list[$index]) ? $this->list[$index] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($index, $value) {
|
||||
$this->ensureComputedValue();
|
||||
return parent::set($index, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function appendItem($value = NULL) {
|
||||
$this->ensureComputedValue();
|
||||
return parent::appendItem($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeItem($index) {
|
||||
$this->ensureComputedValue();
|
||||
return parent::removeItem($index);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isEmpty() {
|
||||
$this->ensureComputedValue();
|
||||
return parent::isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function offsetExists($offset) {
|
||||
$this->ensureComputedValue();
|
||||
return parent::offsetExists($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIterator() {
|
||||
$this->ensureComputedValue();
|
||||
return parent::getIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count() {
|
||||
$this->ensureComputedValue();
|
||||
return parent::count();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applyDefaultValue($notify = TRUE) {
|
||||
// Default values do not make sense for computed item lists. However, this
|
||||
// method can be overridden if needed.
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user