Update to Drupal 8.0.0. For more information, see https://www.drupal.org/node/2619030

This commit is contained in:
Pantheon Automation 2015-11-19 07:04:44 -08:00 committed by Greg Anderson
parent 7784f4c23d
commit 25a6735fb3
49 changed files with 1394 additions and 281 deletions

View file

@ -511,8 +511,6 @@ class ConfigImporter {
* If the configuration is already importing.
*/
public function initialize() {
$this->createExtensionChangelist();
// Ensure that the changes have been validated.
$this->validate();
@ -710,8 +708,10 @@ class ConfigImporter {
* @throws \Drupal\Core\Config\ConfigImporterException
* Exception thrown if the validate event logged any errors.
*/
protected function validate() {
public function validate() {
if (!$this->validated) {
// Create the list of installs and uninstalls.
$this->createExtensionChangelist();
// Validate renames.
foreach ($this->getUnprocessedConfiguration('rename') as $name) {
$names = $this->storageComparer->extractRenameNames($name);

View file

@ -69,9 +69,6 @@ class ConfigEntityType extends EntityType implements ConfigEntityTypeInterface {
// Always add a default 'uuid' key.
$this->entity_keys['uuid'] = 'uuid';
$this->entity_keys['langcode'] = 'langcode';
if (isset($this->handlers['storage'])) {
$this->checkStorageClass($this->handlers['storage']);
}
$this->handlers += array(
'storage' => 'Drupal\Core\Config\Entity\ConfigEntityStorage',
);
@ -135,23 +132,12 @@ class ConfigEntityType extends EntityType implements ConfigEntityTypeInterface {
/**
* {@inheritdoc}
*
* @see \Drupal\Core\Config\Entity\ConfigEntityStorage.
*
* @throws \Drupal\Core\Config\Entity\Exception\ConfigEntityStorageClassException
* Exception thrown when the provided class is not an instance of
* \Drupal\Core\Config\Entity\ConfigEntityStorage.
*/
public function setStorageClass($class) {
$this->checkStorageClass($class);
parent::setStorageClass($class);
}
/**
* Checks that the provided class is an instance of ConfigEntityStorage.
*
* @param string $class
* The class to check.
*
* @see \Drupal\Core\Config\Entity\ConfigEntityStorage.
*/
protected function checkStorageClass($class) {
if (!is_a($class, 'Drupal\Core\Config\Entity\ConfigEntityStorage', TRUE)) {
throw new ConfigEntityStorageClassException("$class is not \\Drupal\\Core\\Config\\Entity\\ConfigEntityStorage or it does not extend it");

View file

@ -808,6 +808,13 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
return !empty($this->translations[$langcode]['status']);
}
/**
* {@inheritdoc}
*/
public function isNewTranslation() {
return $this->translations[$this->activeLangcode]['status'] == static::TRANSLATION_CREATED;
}
/**
* {@inheritdoc}
*/
@ -822,37 +829,11 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
throw new \InvalidArgumentException("The entity cannot be translated since it is language neutral ({$this->defaultLangcode}).");
}
// Instantiate a new empty entity so default values will be populated in the
// specified language.
$entity_type = $this->getEntityType();
$default_values = array(
$entity_type->getKey('bundle') => $this->bundle(),
$this->langcodeKey => $langcode,
);
$entity = $this->entityManager()
->getStorage($this->getEntityTypeId())
->create($default_values);
foreach ($entity as $name => $field) {
if (!isset($values[$name]) && !$field->isEmpty()) {
$values[$name] = $field->getValue();
}
}
$values[$this->langcodeKey] = $langcode;
$values[$this->defaultLangcodeKey] = FALSE;
// Initialize the translation object.
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
$storage = $this->entityManager()->getStorage($this->getEntityTypeId());
$this->translations[$langcode]['status'] = static::TRANSLATION_CREATED;
$translation = $this->getTranslation($langcode);
$definitions = $translation->getFieldDefinitions();
foreach ($values as $name => $value) {
if (isset($definitions[$name]) && $definitions[$name]->isTranslatable()) {
$translation->values[$name][$langcode] = $value;
}
}
return $translation;
return $storage->createTranslation($this, $langcode, $values);
}
/**

View file

@ -13,7 +13,10 @@ use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
abstract class ContentEntityStorageBase extends EntityStorageBase implements DynamicallyFieldableEntityStorageInterface {
/**
* Base class for content entity storage handlers.
*/
abstract class ContentEntityStorageBase extends EntityStorageBase implements ContentEntityStorageInterface, DynamicallyFieldableEntityStorageInterface {
/**
* The entity bundle key.
@ -87,13 +90,32 @@ abstract class ContentEntityStorageBase extends EntityStorageBase implements Dyn
$bundle = $values[$this->bundleKey];
}
$entity = new $this->entityClass(array(), $this->entityTypeId, $bundle);
$this->initFieldValues($entity, $values);
return $entity;
}
/**
* Initializes field values.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* An entity object.
* @param array $values
* (optional) An associative array of initial field values keyed by field
* name. If none is provided default values will be applied.
* @param array $field_names
* (optional) An associative array of field names to be initialized. If none
* is provided all fields will be initialized.
*/
protected function initFieldValues(ContentEntityInterface $entity, array $values = [], array $field_names = []) {
// Populate field values.
foreach ($entity as $name => $field) {
if (isset($values[$name])) {
$entity->$name = $values[$name];
}
elseif (!array_key_exists($name, $values)) {
$entity->get($name)->applyDefaultValue();
if (!$field_names || isset($field_names[$name])) {
if (isset($values[$name])) {
$entity->$name = $values[$name];
}
elseif (!array_key_exists($name, $values)) {
$entity->get($name)->applyDefaultValue();
}
}
unset($values[$name]);
}
@ -102,7 +124,23 @@ abstract class ContentEntityStorageBase extends EntityStorageBase implements Dyn
foreach ($values as $name => $value) {
$entity->$name = $value;
}
return $entity;
// Make sure modules can alter field initial values.
$this->invokeHook('field_values_init', $entity);
}
/**
* {@inheritdoc}
*/
public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []) {
$translation = $entity->getTranslation($langcode);
$definitions = array_filter($translation->getFieldDefinitions(), function(FieldDefinitionInterface $definition) { return $definition->isTranslatable(); });
$field_names = array_map(function(FieldDefinitionInterface $definition) { return $definition->getName(); }, $definitions);
$values[$this->langcodeKey] = $langcode;
$values[$this->getEntityType()->getKey('default_langcode')] = FALSE;
$this->initFieldValues($translation, $values, $field_names);
$this->invokeHook('translation_create', $entity);
return $translation;
}
/**

View file

@ -0,0 +1,31 @@
<?php
/**
* @file
* Contains \Drupal\Core\Entity\ContentEntityStorageInterface.
*/
namespace Drupal\Core\Entity;
/**
* A storage that supports content entity types.
*/
interface ContentEntityStorageInterface extends EntityStorageInterface {
/**
* Constructs a new entity translation object, without permanently saving it.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity object being translated.
* @param string $langcode
* The translation language code.
* @param array $values
* (optional) An associative array of initial field values keyed by field
* name. If none is provided default values will be applied.
*
* @return \Drupal\Core\Entity\ContentEntityInterface
* A new entity translation object.
*/
public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []);
}

View file

@ -30,4 +30,20 @@ class ContentEntityType extends EntityType implements ContentEntityTypeInterface
return 'content';
}
/**
* {@inheritdoc}
*
* @see \Drupal\Core\Entity\ContentEntityStorageInterface.
*
* @throws \InvalidArgumentException
* If the provided class does not implement
* \Drupal\Core\Entity\ContentEntityStorageInterface.
*/
protected function checkStorageClass($class) {
$required_interface = ContentEntityStorageInterface::class;
if (!is_subclass_of($class, $required_interface)) {
throw new \InvalidArgumentException("$class does not implement $required_interface");
}
}
}

View file

@ -276,6 +276,9 @@ class EntityType implements EntityTypeInterface {
$this->handlers += array(
'access' => 'Drupal\Core\Entity\EntityAccessControlHandler',
);
if (isset($this->handlers['storage'])) {
$this->checkStorageClass($this->handlers['storage']);
}
// Automatically add the EntityChanged constraint if the entity type tracks
// the changed time.
@ -459,9 +462,20 @@ class EntityType implements EntityTypeInterface {
* {@inheritdoc}
*/
public function setStorageClass($class) {
$this->checkStorageClass($class);
$this->handlers['storage'] = $class;
}
/**
* Checks that the provided class is an instance of ConfigEntityStorage.
*
* @param string $class
* The class to check.
*/
protected function checkStorageClass($class) {
// Nothing to check by default.
}
/**
* {@inheritdoc}
*/

View file

@ -0,0 +1,25 @@
<?php
/**
* @file
* Contains \Drupal\Core\Entity\KeyValueStore\KeyValueContentEntityStorage.
*/
namespace Drupal\Core\Entity\KeyValueStore;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\ContentEntityStorageInterface;
/**
* Provides a key value backend for content entities.
*/
class KeyValueContentEntityStorage extends KeyValueEntityStorage implements ContentEntityStorageInterface {
/**
* {@inheritdoc}
*/
public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []) {
// @todo
}
}

View file

@ -780,10 +780,9 @@ function hook_entity_bundle_delete($entity_type_id, $bundle) {
}
/**
* Act on a newly created entity.
* Acts when creating a new entity.
*
* This hook runs after a new entity object has just been instantiated. It can
* be used to set initial values, e.g. to provide defaults.
* This hook runs after a new entity object has just been instantiated.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity object.
@ -792,16 +791,13 @@ function hook_entity_bundle_delete($entity_type_id, $bundle) {
* @see hook_ENTITY_TYPE_create()
*/
function hook_entity_create(\Drupal\Core\Entity\EntityInterface $entity) {
if ($entity instanceof FieldableEntityInterface && !$entity->foo->value) {
$entity->foo->value = 'some_initial_value';
}
\Drupal::logger('example')->info('Entity created: @label', ['@label' => $entity->label()]);
}
/**
* Act on a newly created entity of a specific type.
* Acts when creating a new entity of a specific type.
*
* This hook runs after a new entity object has just been instantiated. It can
* be used to set initial values, e.g. to provide defaults.
* This hook runs after a new entity object has just been instantiated.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity object.
@ -810,9 +806,7 @@ function hook_entity_create(\Drupal\Core\Entity\EntityInterface $entity) {
* @see hook_entity_create()
*/
function hook_ENTITY_TYPE_create(\Drupal\Core\Entity\EntityInterface $entity) {
if (!$entity->foo->value) {
$entity->foo->value = 'some_initial_value';
}
\Drupal::logger('example')->info('ENTITY_TYPE created: @label', ['@label' => $entity->label()]);
}
/**
@ -1011,6 +1005,38 @@ function hook_ENTITY_TYPE_update(Drupal\Core\Entity\EntityInterface $entity) {
->execute();
}
/**
* Acts when creating a new entity translation.
*
* This hook runs after a new entity translation object has just been
* instantiated.
*
* @param \Drupal\Core\Entity\EntityInterface $translation
* The entity object.
*
* @ingroup entity_crud
* @see hook_ENTITY_TYPE_translation_create()
*/
function hook_entity_translation_create(\Drupal\Core\Entity\EntityInterface $translation) {
\Drupal::logger('example')->info('Entity translation created: @label', ['@label' => $translation->label()]);
}
/**
* Acts when creating a new entity translation of a specific type.
*
* This hook runs after a new entity translation object has just been
* instantiated.
*
* @param \Drupal\Core\Entity\EntityInterface $translation
* The entity object.
*
* @ingroup entity_crud
* @see hook_entity_translation_create()
*/
function hook_ENTITY_TYPE_translation_create(\Drupal\Core\Entity\EntityInterface $translation) {
\Drupal::logger('example')->info('ENTITY_TYPE translation created: @label', ['@label' => $translation->label()]);
}
/**
* Respond to creation of a new entity translation.
*
@ -1886,6 +1912,44 @@ function hook_entity_field_access_alter(array &$grants, array $context) {
}
}
/**
* Acts when initializing a fieldable entity object.
*
* This hook runs after a new entity object or a new entity translation object
* has just been instantiated. It can be used to set initial values, e.g. to
* provide defaults.
*
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
* The entity object.
*
* @ingroup entity_crud
* @see hook_ENTITY_TYPE_field_values_init()
*/
function hook_entity_field_values_init(\Drupal\Core\Entity\FieldableEntityInterface $entity) {
if ($entity instanceof \Drupal\Core\Entity\ContentEntityInterface && !$entity->foo->value) {
$entity->foo->value = 'some_initial_value';
}
}
/**
* Acts when initializing a fieldable entity object.
*
* This hook runs after a new entity object or a new entity translation object
* has just been instantiated. It can be used to set initial values, e.g. to
* provide defaults.
*
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
* The entity object.
*
* @ingroup entity_crud
* @see hook_entity_field_values_init()
*/
function hook_ENTITY_TYPE_field_values_init(\Drupal\Core\Entity\FieldableEntityInterface $entity) {
if (!$entity->foo->value) {
$entity->foo->value = 'some_initial_value';
}
}
/**
* Exposes "pseudo-field" components on content entities.
*

View file

@ -28,6 +28,14 @@ interface TranslatableInterface {
*/
public function isDefaultTranslation();
/**
* Checks whether the translation is new.
*
* @return bool
* TRUE if the translation is new, FALSE otherwise.
*/
public function isNewTranslation();
/**
* Returns the languages the data is translated to.
*