updated core

This commit is contained in:
Bachir Soussi Chiadmi
2018-01-24 13:36:32 +01:00
parent cd7dea45a9
commit f9374cf96d
1997 changed files with 5175 additions and 127715 deletions
@@ -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() {
}
}
+4 -3
View File
@@ -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;
+44 -37
View File
@@ -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 = []);
+4
View File
@@ -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.
+6 -6
View File
@@ -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;
@@ -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;
}
/**
+28 -26
View File
@@ -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 -3
View File
@@ -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;
}
}
@@ -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);
}
+1 -1
View File
@@ -32,7 +32,7 @@ class GeneratedLink extends BubbleableMetadata implements MarkupInterface, \Coun
* @return string
*/
public function getGeneratedLink() {
return $this->generatedLink ;
return $this->generatedLink;
}
/**
+1 -1
View File
@@ -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;
+7 -1
View File
@@ -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.
*
+11 -5
View File
@@ -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();
+1 -1
View File
@@ -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;
}
}