Issue #1893772 by chx, fubhy, slashrsm: Move entity-type specific storage logic into entity classes.
parent
d9f7b3a35b
commit
1648a479c4
|
@ -278,6 +278,7 @@ class ConfigStorageController extends EntityStorageControllerBase {
|
|||
*/
|
||||
public function create(array $values) {
|
||||
$class = $this->entityInfo['class'];
|
||||
$class::preCreate($this, $values);
|
||||
|
||||
// Set default language to site default if not provided.
|
||||
$values += array('langcode' => language_default()->langcode);
|
||||
|
@ -292,6 +293,7 @@ class ConfigStorageController extends EntityStorageControllerBase {
|
|||
$uuid = new Uuid();
|
||||
$entity->{$this->uuidKey} = $uuid->generate();
|
||||
}
|
||||
$entity->postCreate($this);
|
||||
|
||||
// Modules might need to add or change the data initially held by the new
|
||||
// entity object, for instance to fill-in default values.
|
||||
|
@ -314,7 +316,8 @@ class ConfigStorageController extends EntityStorageControllerBase {
|
|||
return;
|
||||
}
|
||||
|
||||
$this->preDelete($entities);
|
||||
$entity_class = $this->entityInfo['class'];
|
||||
$entity_class::preDelete($this, $entities);
|
||||
foreach ($entities as $id => $entity) {
|
||||
$this->invokeHook('predelete', $entity);
|
||||
}
|
||||
|
@ -324,7 +327,7 @@ class ConfigStorageController extends EntityStorageControllerBase {
|
|||
$config->delete();
|
||||
}
|
||||
|
||||
$this->postDelete($entities);
|
||||
$entity_class::postDelete($this, $entities);
|
||||
foreach ($entities as $id => $entity) {
|
||||
$this->invokeHook('delete', $entity);
|
||||
}
|
||||
|
@ -367,7 +370,7 @@ class ConfigStorageController extends EntityStorageControllerBase {
|
|||
$this->configFactory->rename($prefix . $id, $prefix . $entity->id());
|
||||
}
|
||||
|
||||
$this->preSave($entity);
|
||||
$entity->preSave($this);
|
||||
$this->invokeHook('presave', $entity);
|
||||
|
||||
// Retrieve the desired properties and set them in config.
|
||||
|
@ -378,7 +381,7 @@ class ConfigStorageController extends EntityStorageControllerBase {
|
|||
if (!$is_new) {
|
||||
$return = SAVED_UPDATED;
|
||||
$config->save();
|
||||
$this->postSave($entity, TRUE);
|
||||
$entity->postSave($this, TRUE);
|
||||
$this->invokeHook('update', $entity);
|
||||
|
||||
// Immediately update the original ID.
|
||||
|
@ -388,7 +391,7 @@ class ConfigStorageController extends EntityStorageControllerBase {
|
|||
$return = SAVED_NEW;
|
||||
$config->save();
|
||||
$entity->enforceIsNew(FALSE);
|
||||
$this->postSave($entity, FALSE);
|
||||
$entity->postSave($this, FALSE);
|
||||
$this->invokeHook('insert', $entity);
|
||||
}
|
||||
|
||||
|
@ -397,45 +400,6 @@ class ConfigStorageController extends EntityStorageControllerBase {
|
|||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts on an entity before the presave hook is invoked.
|
||||
*
|
||||
* Used before the entity is saved and before invoking the presave hook.
|
||||
*/
|
||||
protected function preSave(EntityInterface $entity) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts on a saved entity before the insert or update hook is invoked.
|
||||
*
|
||||
* Used after the entity is saved, but before invoking the insert or update
|
||||
* hook.
|
||||
*
|
||||
* @param EntityInterface $entity
|
||||
* The entity to act on.
|
||||
* @param $update
|
||||
* (bool) TRUE if the entity has been updated, or FALSE if it has been
|
||||
* inserted.
|
||||
*/
|
||||
protected function postSave(EntityInterface $entity, $update) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts on entities before they are deleted.
|
||||
*
|
||||
* Used before the entities are deleted and before invoking the delete hook.
|
||||
*/
|
||||
protected function preDelete($entities) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts on deleted entities before the delete hook is invoked.
|
||||
*
|
||||
* Used after the entities are deleted but before invoking the delete hook.
|
||||
*/
|
||||
protected function postDelete($entities) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Entity\EntityStorageControllerInterface::getFieldDefinitions().
|
||||
*/
|
||||
|
|
|
@ -370,15 +370,17 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
* Implements \Drupal\Core\Entity\EntityStorageControllerInterface::create().
|
||||
*/
|
||||
public function create(array $values) {
|
||||
$class = $this->entityInfo['class'];
|
||||
$entity_class = $this->entityInfo['class'];
|
||||
$entity_class::preCreate($this, $values);
|
||||
|
||||
$entity = new $class($values, $this->entityType);
|
||||
$entity = new $entity_class($values, $this->entityType);
|
||||
|
||||
// Assign a new UUID if there is none yet.
|
||||
if ($this->uuidKey && !isset($entity->{$this->uuidKey})) {
|
||||
$uuid = new Uuid();
|
||||
$entity->{$this->uuidKey} = $uuid->generate();
|
||||
}
|
||||
$entity->postCreate($this);
|
||||
|
||||
// Modules might need to add or change the data initially held by the new
|
||||
// entity object, for instance to fill-in default values.
|
||||
|
@ -398,7 +400,8 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
$transaction = $this->database->startTransaction();
|
||||
|
||||
try {
|
||||
$this->preDelete($entities);
|
||||
$entity_class = $this->entityInfo['class'];
|
||||
$entity_class::preDelete($this, $entities);
|
||||
foreach ($entities as $id => $entity) {
|
||||
$this->invokeHook('predelete', $entity);
|
||||
}
|
||||
|
@ -417,7 +420,7 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
// Reset the cache as soon as the changes have been applied.
|
||||
$this->resetCache($ids);
|
||||
|
||||
$this->postDelete($entities);
|
||||
$entity_class::postDelete($this, $entities);
|
||||
foreach ($entities as $id => $entity) {
|
||||
$this->invokeHook('delete', $entity);
|
||||
}
|
||||
|
@ -442,7 +445,7 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
$entity->original = entity_load_unchanged($this->entityType, $entity->id());
|
||||
}
|
||||
|
||||
$this->preSave($entity);
|
||||
$entity->preSave($this);
|
||||
$this->invokeHook('presave', $entity);
|
||||
|
||||
if (!$entity->isNew()) {
|
||||
|
@ -458,7 +461,7 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
$this->saveRevision($entity);
|
||||
}
|
||||
$this->resetCache(array($entity->id()));
|
||||
$this->postSave($entity, TRUE);
|
||||
$entity->postSave($this, TRUE);
|
||||
$this->invokeHook('update', $entity);
|
||||
}
|
||||
else {
|
||||
|
@ -470,7 +473,7 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
$this->resetCache(array());
|
||||
|
||||
$entity->enforceIsNew(FALSE);
|
||||
$this->postSave($entity, FALSE);
|
||||
$entity->postSave($this, FALSE);
|
||||
$this->invokeHook('insert', $entity);
|
||||
}
|
||||
|
||||
|
@ -507,7 +510,7 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
// Cast to object as preSaveRevision() expects one to be compatible with the
|
||||
// upcoming NG storage controller.
|
||||
$record = (object) $record;
|
||||
$this->preSaveRevision($record, $entity);
|
||||
$entity->preSaveRevision($this, $record);
|
||||
$record = (array) $record;
|
||||
|
||||
if ($entity->isNewRevision()) {
|
||||
|
@ -527,49 +530,6 @@ class DatabaseStorageController extends EntityStorageControllerBase {
|
|||
$entity->{$this->revisionKey} = $record[$this->revisionKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts on an entity before the presave hook is invoked.
|
||||
*
|
||||
* Used before the entity is saved and before invoking the presave hook.
|
||||
*/
|
||||
protected function preSave(EntityInterface $entity) { }
|
||||
|
||||
/**
|
||||
* Acts on a saved entity before the insert or update hook is invoked.
|
||||
*
|
||||
* Used after the entity is saved, but before invoking the insert or update
|
||||
* hook.
|
||||
*
|
||||
* @param $update
|
||||
* (bool) TRUE if the entity has been updated, or FALSE if it has been
|
||||
* inserted.
|
||||
*/
|
||||
protected function postSave(EntityInterface $entity, $update) { }
|
||||
|
||||
/**
|
||||
* Acts on entities before they are deleted.
|
||||
*
|
||||
* Used before the entities are deleted and before invoking the delete hook.
|
||||
*/
|
||||
protected function preDelete($entities) { }
|
||||
|
||||
/**
|
||||
* Acts on deleted entities before the delete hook is invoked.
|
||||
*
|
||||
* Used after the entities are deleted but before invoking the delete hook.
|
||||
*/
|
||||
protected function postDelete($entities) { }
|
||||
|
||||
/**
|
||||
* Act on a revision before being saved.
|
||||
*
|
||||
* @param \stdClass $record
|
||||
* The revision object.
|
||||
* @param Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity object.
|
||||
*/
|
||||
protected function preSaveRevision(\stdClass $record, EntityInterface $entity) { }
|
||||
|
||||
/**
|
||||
* Invokes a hook on behalf of the entity.
|
||||
*
|
||||
|
|
|
@ -98,6 +98,9 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
|
|||
* A new entity object.
|
||||
*/
|
||||
public function create(array $values) {
|
||||
$entity_class = $this->entityClass;
|
||||
$entity_class::preCreate($this, $values);
|
||||
|
||||
// We have to determine the bundle first.
|
||||
$bundle = FALSE;
|
||||
if ($this->bundleKey) {
|
||||
|
@ -118,6 +121,7 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
|
|||
$uuid = new Uuid();
|
||||
$entity->{$this->uuidKey} = $uuid->generate();
|
||||
}
|
||||
$entity->postCreate($this);
|
||||
|
||||
// Modules might need to add or change the data initially held by the new
|
||||
// entity object, for instance to fill-in default values.
|
||||
|
@ -354,7 +358,7 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
|
|||
$entity->original = entity_load_unchanged($this->entityType, $entity->id());
|
||||
}
|
||||
|
||||
$this->preSave($entity);
|
||||
$entity->preSave($this);
|
||||
$this->invokeHook('presave', $entity);
|
||||
|
||||
// Create the storage record to be saved.
|
||||
|
@ -376,7 +380,7 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
|
|||
$this->savePropertyData($entity);
|
||||
}
|
||||
$this->resetCache(array($entity->id()));
|
||||
$this->postSave($entity, TRUE);
|
||||
$entity->postSave($this, TRUE);
|
||||
$this->invokeHook('update', $entity);
|
||||
}
|
||||
else {
|
||||
|
@ -394,7 +398,7 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
|
|||
$this->resetCache(array());
|
||||
|
||||
$entity->enforceIsNew(FALSE);
|
||||
$this->postSave($entity, FALSE);
|
||||
$entity->postSave($this, FALSE);
|
||||
$this->invokeHook('insert', $entity);
|
||||
}
|
||||
|
||||
|
@ -445,7 +449,7 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
|
|||
$record->{$this->revisionKey} = NULL;
|
||||
}
|
||||
|
||||
$this->preSaveRevision($record, $entity);
|
||||
$entity->preSaveRevision($this, $record);
|
||||
|
||||
if ($entity->isNewRevision()) {
|
||||
drupal_write_record($this->revisionTable, $record);
|
||||
|
@ -596,12 +600,14 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
|
|||
|
||||
$transaction = $this->database->startTransaction();
|
||||
try {
|
||||
$entity_class = $this->entityClass;
|
||||
$entity_class::preDelete($this, $entities);
|
||||
|
||||
// Ensure we are dealing with the actual entities.
|
||||
foreach ($entities as $id => $entity) {
|
||||
$entities[$id] = $entity->getNGEntity();
|
||||
}
|
||||
|
||||
$this->preDelete($entities);
|
||||
foreach ($entities as $id => $entity) {
|
||||
$this->invokeHook('predelete', $entity);
|
||||
}
|
||||
|
@ -626,7 +632,7 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
|
|||
// Reset the cache as soon as the changes have been applied.
|
||||
$this->resetCache($ids);
|
||||
|
||||
$this->postDelete($entities);
|
||||
$entity_class::postDelete($this, $entities);
|
||||
foreach ($entities as $id => $entity) {
|
||||
$this->invokeHook('delete', $entity);
|
||||
}
|
||||
|
|
|
@ -531,4 +531,52 @@ class Entity implements IteratorAggregate, EntityInterface {
|
|||
return !empty($bundles[$this->bundle()]['translatable']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageControllerInterface $storage_controller) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postCreate(EntityStorageControllerInterface $storage_controller) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postLoad(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSaveRevision(EntityStorageControllerInterface $storage_controller, \stdClass $record) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -533,4 +533,53 @@ class EntityBCDecorator implements IteratorAggregate, EntityInterface {
|
|||
public function isTranslatable() {
|
||||
return $this->decorated->isTranslatable();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageControllerInterface $storage_controller) {
|
||||
$this->decorated->preSave($storage_controller);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSaveRevision(EntityStorageControllerInterface $storage_controller, \stdClass $record) {
|
||||
$this->decorated->preSave($storage_controller, $record);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
$this->decorated->postSave($storage_controller, $update);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
|
||||
}
|
||||
|
||||
public function postCreate(EntityStorageControllerInterface $storage_controller) {
|
||||
$this->decorated->postCreate($storage_controller);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postLoad(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -162,6 +162,94 @@ interface EntityInterface extends ComplexDataInterface, AccessibleInterface, Tra
|
|||
*/
|
||||
public function delete();
|
||||
|
||||
/**
|
||||
* Acts on an entity before the presave hook is invoked.
|
||||
*
|
||||
* Used before the entity is saved and before invoking the presave hook.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller
|
||||
* The entity storage controller object.
|
||||
*/
|
||||
public function preSave(EntityStorageControllerInterface $storage_controller);
|
||||
|
||||
/**
|
||||
* Acts on a revision before it gets saved.
|
||||
*
|
||||
* @param EntityStorageControllerInterface $storage_controller
|
||||
* The entity storage controller object.
|
||||
* @param \stdClass $record
|
||||
* The revision object.
|
||||
*/
|
||||
public function preSaveRevision(EntityStorageControllerInterface $storage_controller, \stdClass $record);
|
||||
|
||||
/**
|
||||
* Acts on a saved entity before the insert or update hook is invoked.
|
||||
*
|
||||
* Used after the entity is saved, but before invoking the insert or update
|
||||
* hook.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller
|
||||
* The entity storage controller object.
|
||||
* @param bool $update
|
||||
* TRUE if the entity has been updated, or FALSE if it has been inserted.
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE);
|
||||
|
||||
/**
|
||||
* Changes the values of an entity before it is created.
|
||||
*
|
||||
* Load defaults for example.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller
|
||||
* The entity storage controller object.
|
||||
* @param array $values
|
||||
* An array of values to set, keyed by property name. If the entity type has
|
||||
* bundles the bundle key has to be specified.
|
||||
*/
|
||||
public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values);
|
||||
|
||||
/**
|
||||
* Acts on an entity after it is created but before hooks are invoked.
|
||||
*
|
||||
* @param EntityStorageControllerInterface $storage_controller
|
||||
* The entity storage controller object.
|
||||
*/
|
||||
public function postCreate(EntityStorageControllerInterface $storage_controller);
|
||||
|
||||
/**
|
||||
* Acts on entities before they are deleted and before hooks are invoked.
|
||||
*
|
||||
* Used before the entities are deleted and before invoking the delete hook.
|
||||
*
|
||||
* @param EntityStorageControllerInterface $storage_controller
|
||||
* The entity storage controller object.
|
||||
* @param array $entities
|
||||
* An array of entities.
|
||||
*/
|
||||
public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities);
|
||||
|
||||
/**
|
||||
* Acts on deleted entities before the delete hook is invoked.
|
||||
*
|
||||
* Used after the entities are deleted but before invoking the delete hook.
|
||||
*
|
||||
* @param EntityStorageControllerInterface $storage_controller
|
||||
* The entity storage controller object.
|
||||
* @param array $entities
|
||||
* An array of entities.
|
||||
*/
|
||||
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities);
|
||||
|
||||
/**
|
||||
* Acts on loaded entities before the load hook is invoked.
|
||||
*
|
||||
* @param EntityStorageControllerInterface $storage_controller
|
||||
* The entity storage controller object.
|
||||
* @param array $entities
|
||||
* An array of entities.
|
||||
*/
|
||||
public static function postLoad(EntityStorageControllerInterface $storage_controller, array $entities);
|
||||
|
||||
/**
|
||||
* Creates a duplicate of the entity.
|
||||
*
|
||||
|
|
|
@ -33,7 +33,7 @@ interface EntityStorageControllerInterface {
|
|||
* Loads one or more entities.
|
||||
*
|
||||
* @param $ids
|
||||
* An array of entity IDs, or FALSE to load all entities.
|
||||
* An array of entity IDs, or NULL to load all entities.
|
||||
*
|
||||
* @return
|
||||
* An array of entity objects indexed by their ids.
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\aggregator;
|
||||
|
||||
use Drupal\Core\Entity\DatabaseStorageControllerNG;
|
||||
use Drupal\aggregator\Plugin\Core\Entity\Feed;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
|
@ -16,101 +17,14 @@ use Drupal\Core\Entity\EntityInterface;
|
|||
* This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
|
||||
* required special handling for feed entities.
|
||||
*/
|
||||
class FeedStorageController extends DatabaseStorageControllerNG {
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DataBaseStorageController::create().
|
||||
*/
|
||||
public function create(array $values) {
|
||||
$values += array(
|
||||
'link' => '',
|
||||
'description' => '',
|
||||
'image' => '',
|
||||
);
|
||||
return parent::create($values);
|
||||
}
|
||||
class FeedStorageController extends DatabaseStorageControllerNG implements FeedStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DataBaseStorageController::attachLoad().
|
||||
*/
|
||||
protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
|
||||
parent::attachLoad($queried_entities, $load_revision);
|
||||
foreach ($queried_entities as $item) {
|
||||
$item->categories = db_query('SELECT c.cid, c.title FROM {aggregator_category} c JOIN {aggregator_category_feed} f ON c.cid = f.cid AND f.fid = :fid ORDER BY title', array(':fid' => $item->id()))->fetchAllKeyed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DataBaseStorageController::preDelete().
|
||||
*/
|
||||
protected function preDelete($entities) {
|
||||
parent::preDelete($entities);
|
||||
|
||||
// Invalidate the block cache to update aggregator feed-based derivatives.
|
||||
if (module_exists('block')) {
|
||||
\Drupal::service('plugin.manager.block')->clearCachedDefinitions();
|
||||
}
|
||||
foreach ($entities as $entity) {
|
||||
// Notify processors to remove stored items.
|
||||
$manager = \Drupal::service('plugin.manager.aggregator.processor');
|
||||
foreach ($manager->getDefinitions() as $id => $definition) {
|
||||
$manager->createInstance($id)->remove($entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DataBaseStorageController::postDelete().
|
||||
*/
|
||||
protected function postDelete($entities) {
|
||||
parent::postDelete($entities);
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
// Make sure there is no active block for this feed.
|
||||
$block_configs = config_get_storage_names_with_prefix('plugin.core.block');
|
||||
foreach ($block_configs as $config_id) {
|
||||
$config = config($config_id);
|
||||
if ($config->get('id') == 'aggregator_feed_block:' . $entity->id()) {
|
||||
$config->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DataBaseStorageController::preSave().
|
||||
*/
|
||||
protected function preSave(EntityInterface $entity) {
|
||||
parent::preSave($entity);
|
||||
|
||||
// Invalidate the block cache to update aggregator feed-based derivatives.
|
||||
if (module_exists('block')) {
|
||||
drupal_container()->get('plugin.manager.block')->clearCachedDefinitions();
|
||||
}
|
||||
// An existing feed is being modified, delete the category listings.
|
||||
db_delete('aggregator_category_feed')
|
||||
->condition('fid', $entity->id())
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DataBaseStorageController::postSave().
|
||||
*/
|
||||
protected function postSave(EntityInterface $entity, $update) {
|
||||
parent::postSave($entity, $update);
|
||||
|
||||
if (!empty($entity->categories)) {
|
||||
foreach ($entity->categories as $cid => $value) {
|
||||
if ($value) {
|
||||
db_insert('aggregator_category_feed')
|
||||
->fields(array(
|
||||
'fid' => $entity->id(),
|
||||
'cid' => $cid,
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->loadCategories($queried_entities);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -191,4 +105,39 @@ class FeedStorageController extends DatabaseStorageControllerNG {
|
|||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadCategories(array $feeds) {
|
||||
foreach ($feeds as $feed) {
|
||||
$feed->categories = $this->database->query('SELECT c.cid, c.title FROM {aggregator_category} c JOIN {aggregator_category_feed} f ON c.cid = f.cid AND f.fid = :fid ORDER BY title', array(':fid' => $feed->id()))->fetchAllKeyed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveCategories(Feed $feed, array $categories) {
|
||||
foreach ($categories as $cid => $value) {
|
||||
if ($value) {
|
||||
$this->database->insert('aggregator_category_feed')
|
||||
->fields(array(
|
||||
'fid' => $feed->id(),
|
||||
'cid' => $cid,
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteCategories(array $feeds) {
|
||||
// An existing feed is being modified, delete the category listings.
|
||||
$this->database->delete('aggregator_category_feed')
|
||||
->condition('fid', array_keys($feeds))
|
||||
->execute();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\aggregator\FeedStorageControllerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\aggregator;
|
||||
|
||||
use Drupal\aggregator\Plugin\Core\Entity\Feed;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
|
||||
/**
|
||||
* Defines a common interface for aggregator feed entity controller classes.
|
||||
*/
|
||||
interface FeedStorageControllerInterface extends EntityStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Loads the categories of a feed.
|
||||
*
|
||||
* @param array $entities
|
||||
* A list of feed entities keyed by feed id. Each entity will get a
|
||||
* categories property added.
|
||||
*/
|
||||
public function loadCategories(array $feeds);
|
||||
|
||||
/**
|
||||
* Saves the categories of a feed.
|
||||
*
|
||||
* @param Feed $feed
|
||||
* The feed entity.
|
||||
* @param array $categories
|
||||
* The array of categories.
|
||||
*/
|
||||
public function saveCategories(Feed $feed, array $categories);
|
||||
|
||||
/**
|
||||
* Deletes the categories of a feed.
|
||||
*
|
||||
* @param array $feeds
|
||||
* A list of feed entities keyed by feed id.
|
||||
*/
|
||||
public function deleteCategories(array $feeds);
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\aggregator;
|
||||
|
||||
use Drupal\Core\Entity\DatabaseStorageControllerNG;
|
||||
use Drupal\aggregator\Plugin\Core\Entity\Item;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
|
@ -16,55 +17,14 @@ use Drupal\Core\Entity\EntityInterface;
|
|||
* This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
|
||||
* required special handling for feed item entities.
|
||||
*/
|
||||
class ItemStorageController extends DatabaseStorageControllerNG {
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DataBaseStorageController::create().
|
||||
*/
|
||||
public function create(array $values) {
|
||||
$entity = parent::create($values);
|
||||
|
||||
// Set an initial timestamp, this will be overwritten if known.
|
||||
$entity->timestamp->value = REQUEST_TIME;
|
||||
return $entity;
|
||||
}
|
||||
class ItemStorageController extends DatabaseStorageControllerNG implements ItemStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DataBaseStorageController::attachLoad().
|
||||
*/
|
||||
protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
|
||||
parent::attachLoad($queried_entities, $load_revision);
|
||||
foreach ($queried_entities as $item) {
|
||||
$item->categories = db_query('SELECT c.title, c.cid FROM {aggregator_category_item} ci LEFT JOIN {aggregator_category} c ON ci.cid = c.cid WHERE ci.iid = :iid ORDER BY c.title', array(':iid' => $item->id()))->fetchAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DataBaseStorageController::preDelete().
|
||||
*/
|
||||
protected function preDelete($entities) {
|
||||
parent::preDelete($entities);
|
||||
|
||||
db_delete('aggregator_category_item')
|
||||
->condition('iid', array_keys($entities), 'IN')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DataBaseStorageController::postSave().
|
||||
*/
|
||||
protected function postSave(EntityInterface $entity, $update) {
|
||||
parent::postSave($entity, $update);
|
||||
|
||||
$result = db_query('SELECT cid FROM {aggregator_category_feed} WHERE fid = :fid', array(':fid' => $entity->fid->value));
|
||||
foreach ($result as $category) {
|
||||
db_merge('aggregator_category_item')
|
||||
->key(array(
|
||||
'iid' => $entity->id(),
|
||||
'cid' => $category->cid,
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
$this->loadCategories($queried_entities);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,4 +80,36 @@ class ItemStorageController extends DatabaseStorageControllerNG {
|
|||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadCategories(array $entities) {
|
||||
foreach ($entities as $item) {
|
||||
$item->categories = db_query('SELECT c.title, c.cid FROM {aggregator_category_item} ci LEFT JOIN {aggregator_category} c ON ci.cid = c.cid WHERE ci.iid = :iid ORDER BY c.title', array(':iid' => $item->id()))->fetchAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteCategories(array $entities) {
|
||||
$this->database->delete('aggregator_category_item')
|
||||
->condition('iid', array_keys($entities))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function saveCategories(Item $item) {
|
||||
$result = $this->database->query('SELECT cid FROM {aggregator_category_feed} WHERE fid = :fid', array(':fid' => $item->fid->value));
|
||||
foreach ($result as $category) {
|
||||
$this->database->merge('aggregator_category_item')
|
||||
->key(array(
|
||||
'iid' => $item->id(),
|
||||
'cid' => $category->cid,
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains Drupal\aggregator\ItemStorageControllerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\aggregator;
|
||||
|
||||
use Drupal\aggregator\Plugin\Core\Entity\Item;
|
||||
use Drupal\core\Entity\EntityStorageControllerInterface;
|
||||
|
||||
/**
|
||||
* Defines a common interface for aggregator item entity controller classes.
|
||||
*/
|
||||
interface ItemStorageControllerInterface extends EntityStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Load the categories for aggregator items.
|
||||
*
|
||||
* @param array $entities
|
||||
* An array of aggregator item objects, keyed by the item id. Each object
|
||||
* will get a categories property added.
|
||||
*/
|
||||
public function loadCategories(array $entities);
|
||||
|
||||
/**
|
||||
* Delete the categories for aggregator items.
|
||||
*
|
||||
* @param array $entities
|
||||
* An array of aggregator item objects, keyed by the item id being
|
||||
* deleted. The storage backend should delete the category data of the
|
||||
* items.
|
||||
*/
|
||||
public function deleteCategories(array $entities);
|
||||
|
||||
/**
|
||||
* Store the categories for aggregator items.
|
||||
*
|
||||
* @param Item $item
|
||||
* The storage backend should save the categories of this item.
|
||||
*/
|
||||
public function saveCategories(Item $item);
|
||||
}
|
|
@ -8,6 +8,8 @@
|
|||
namespace Drupal\aggregator\Plugin\Core\Entity;
|
||||
|
||||
use Drupal\Core\Entity\EntityNG;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\aggregator\FeedInterface;
|
||||
|
@ -157,7 +159,6 @@ class Feed extends EntityNG implements FeedInterface {
|
|||
unset($this->etag);
|
||||
unset($this->modified);
|
||||
unset($this->block);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -173,4 +174,75 @@ class Feed extends EntityNG implements FeedInterface {
|
|||
public function label($langcode = NULL) {
|
||||
return $this->get('title')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
|
||||
$values += array(
|
||||
'link' => '',
|
||||
'description' => '',
|
||||
'image' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
// Invalidate the block cache to update aggregator feed-based derivatives.
|
||||
if (module_exists('block')) {
|
||||
\Drupal::service('plugin.manager.block')->clearCachedDefinitions();
|
||||
}
|
||||
$storage_controller->deleteCategories($entities);
|
||||
foreach ($entities as $entity) {
|
||||
// Notify processors to remove stored items.
|
||||
$manager = \Drupal::service('plugin.manager.aggregator.processor');
|
||||
foreach ($manager->getDefinitions() as $id => $definition) {
|
||||
$manager->createInstance($id)->remove($entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
foreach ($entities as $entity) {
|
||||
// Make sure there is no active block for this feed.
|
||||
$block_configs = config_get_storage_names_with_prefix('plugin.core.block');
|
||||
foreach ($block_configs as $config_id) {
|
||||
$config = config($config_id);
|
||||
if ($config->get('id') == 'aggregator_feed_block:' . $entity->id()) {
|
||||
$config->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageControllerInterface $storage_controller) {
|
||||
$this->clearBlockCacheDefinitions();
|
||||
$storage_controller->deleteCategories(array($this->id() => $this));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = FALSE) {
|
||||
if (!empty($this->categories)) {
|
||||
$storage_controller->saveCategories($this, $this->categories);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the block cache to update aggregator feed-based derivatives.
|
||||
*/
|
||||
protected function clearBlockCacheDefinitions() {
|
||||
if ($block_manager = \Drupal::getContainer()->get('plugin.manager.block', Container::NULL_ON_INVALID_REFERENCE)) {
|
||||
$block_manager->clearCachedDefinitions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\aggregator\Plugin\Core\Entity;
|
||||
|
||||
use Drupal\Core\Entity\EntityNG;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\aggregator\ItemInterface;
|
||||
|
@ -130,4 +131,25 @@ class Item extends EntityNG implements ItemInterface {
|
|||
public function label($langcode = NULL) {
|
||||
return $this->get('title')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postCreate(EntityStorageControllerInterface $storage_controller) {
|
||||
$this->timestamp->value = REQUEST_TIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
$storage_controller->saveCategories($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
$storage_controller->deleteCategories($entities);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,41 +8,15 @@
|
|||
namespace Drupal\custom_block;
|
||||
|
||||
use Drupal\Core\Entity\DatabaseStorageControllerNG;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
|
||||
/**
|
||||
* Controller class for custom blocks.
|
||||
*
|
||||
* This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
|
||||
* required special handling for custom block entities.
|
||||
* This extends the Drupal\Core\Entity\DatabaseStorageControllerNG class,
|
||||
* adding required special handling for custom block entities.
|
||||
*/
|
||||
class CustomBlockStorageController extends DatabaseStorageControllerNG {
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\Entity\DatabaseStorageController::preSaveRevision().
|
||||
*/
|
||||
protected function preSaveRevision(\stdClass $record, EntityInterface $entity) {
|
||||
if ($entity->isNewRevision()) {
|
||||
// When inserting either a new custom block or a new custom_block
|
||||
// revision, $entity->log must be set because {block_custom_revision}.log
|
||||
// is a text column and therefore cannot have a default value. However,
|
||||
// it might not be set at this point (for example, if the user submitting
|
||||
// the form does not have permission to create revisions), so we ensure
|
||||
// that it is at least an empty string in that case.
|
||||
// @todo: Make the {block_custom_revision}.log column nullable so that we
|
||||
// can remove this check.
|
||||
if (!isset($record->log)) {
|
||||
$record->log = '';
|
||||
}
|
||||
}
|
||||
elseif (isset($entity->original) && (!isset($record->log) || $record->log === '')) {
|
||||
// If we are updating an existing custom_block without adding a new
|
||||
// revision, we need to make sure $entity->log is reset whenever it is
|
||||
// empty. Therefore, this code allows us to avoid clobbering an existing
|
||||
// log entry with an empty one.
|
||||
$record->log = $entity->original->log->value;
|
||||
}
|
||||
}
|
||||
class CustomBlockStorageController extends DatabaseStorageControllerNG implements EntityStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\Entity\DatabaseStorageController::attachLoad().
|
||||
|
@ -62,14 +36,6 @@ class CustomBlockStorageController extends DatabaseStorageControllerNG {
|
|||
parent::attachLoad($blocks, $load_revision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\Entity\DatabaseStorageController::postSave().
|
||||
*/
|
||||
protected function postSave(EntityInterface $block, $update) {
|
||||
// Invalidate the block cache to update custom block-based derivatives.
|
||||
drupal_container()->get('plugin.manager.block')->clearCachedDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Entity\DataBaseStorageControllerNG::basePropertyDefinitions().
|
||||
*/
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\custom_block\CustomBlockTypeStorageController.
|
||||
*/
|
||||
|
||||
namespace Drupal\custom_block;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigStorageController;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* Controller class for custom block types.
|
||||
*/
|
||||
class CustomBlockTypeStorageController extends ConfigStorageController {
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\Config\Entity\ConfigStorageController::postSave().
|
||||
*/
|
||||
protected function postSave(EntityInterface $entity, $update) {
|
||||
parent::postSave($entity, $update);
|
||||
|
||||
if (!$update) {
|
||||
entity_invoke_bundle_hook('create', 'custom_block', $entity->id());
|
||||
custom_block_add_body_field($entity->id());
|
||||
}
|
||||
elseif ($entity->original->id() != $entity->id()) {
|
||||
entity_invoke_bundle_hook('rename', 'custom_block', $entity->original->id(), $entity->id());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\Config\Entity\ConfigStorageController::postDelete().
|
||||
*/
|
||||
protected function postDelete($entities) {
|
||||
parent::postDelete($entities);
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
entity_invoke_bundle_hook('delete', 'custom_block', $entity->id());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\custom_block\Plugin\Core\Entity;
|
||||
|
||||
use Drupal\Core\Entity\EntityNG;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\custom_block\CustomBlockInterface;
|
||||
|
@ -181,6 +182,14 @@ class CustomBlock extends EntityNG implements CustomBlockInterface {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
// Invalidate the block cache to update custom block-based derivatives.
|
||||
\Drupal::service('plugin.manager.block')->clearCachedDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -188,6 +197,30 @@ class CustomBlock extends EntityNG implements CustomBlockInterface {
|
|||
return entity_load_multiple_by_properties('block', array('plugin' => 'custom_block:' . $this->uuid->value));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSaveRevision(EntityStorageControllerInterface $storage_controller, \stdClass $record) {
|
||||
if ($this->isNewRevision()) {
|
||||
// When inserting either a new custom block or a new custom_block
|
||||
// revision, $entity->log must be set because {block_custom_revision}.log
|
||||
// is a text column and therefore cannot have a default value. However,
|
||||
// it might not be set at this point (for example, if the user submitting
|
||||
// the form does not have permission to create revisions), so we ensure
|
||||
// that it is at least an empty string in that case.
|
||||
// @todo: Make the {block_custom_revision}.log column nullable so that we
|
||||
// can remove this check.
|
||||
if (!isset($record->log)) {
|
||||
$record->log = '';
|
||||
}
|
||||
}
|
||||
elseif (isset($this->original) && (!isset($record->log) || $record->log === '')) {
|
||||
// If we are updating an existing custom_block without adding a new
|
||||
// revision and the user did not supply a log, keep the existing one.
|
||||
$record->log = $this->original->log->value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace Drupal\custom_block\Plugin\Core\Entity;
|
|||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\custom_block\CustomBlockTypeInterface;
|
||||
|
||||
/**
|
||||
|
@ -20,7 +21,7 @@ use Drupal\custom_block\CustomBlockTypeInterface;
|
|||
* label = @Translation("Custom block type"),
|
||||
* module = "custom_block",
|
||||
* controllers = {
|
||||
* "storage" = "Drupal\custom_block\CustomBlockTypeStorageController",
|
||||
* "storage" = "Drupal\Core\Config\Entity\ConfigStorageController",
|
||||
* "form" = {
|
||||
* "default" = "Drupal\custom_block\CustomBlockTypeFormController"
|
||||
* },
|
||||
|
@ -83,4 +84,26 @@ class CustomBlockType extends ConfigEntityBase implements CustomBlockTypeInterfa
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
if (!$update) {
|
||||
entity_invoke_bundle_hook('create', 'custom_block', $this->id());
|
||||
custom_block_add_body_field($this->id);
|
||||
}
|
||||
elseif ($this->originalID != $this->id) {
|
||||
entity_invoke_bundle_hook('rename', 'custom_block', $this->originalID, $this->id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
foreach ($entities as $entity) {
|
||||
entity_invoke_bundle_hook('delete', 'custom_block', $entity->id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,13 +26,4 @@ class BlockStorageController extends ConfigStorageController {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function preSave(EntityInterface $entity) {
|
||||
parent::preSave($entity);
|
||||
|
||||
$entity->set('settings', $entity->getPlugin()->getConfig());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ use Drupal\Core\Entity\Annotation\EntityType;
|
|||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\block\BlockPluginBag;
|
||||
use Drupal\block\BlockInterface;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
|
||||
/**
|
||||
* Defines a Block configuration entity class.
|
||||
|
@ -163,4 +164,11 @@ class Block extends ConfigEntityBase implements BlockInterface {
|
|||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageControllerInterface $storage_controller) {
|
||||
$this->set('settings', $this->getPlugin()->getConfig());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,14 +18,15 @@ use LogicException;
|
|||
* This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
|
||||
* required special handling for comment entities.
|
||||
*/
|
||||
class CommentStorageController extends DatabaseStorageControllerNG {
|
||||
class CommentStorageController extends DatabaseStorageControllerNG implements CommentStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* The thread for which a lock was acquired.
|
||||
*/
|
||||
protected $threadLock = '';
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::buildQuery().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildQuery($ids, $revision_id = FALSE) {
|
||||
$query = parent::buildQuery($ids, $revision_id);
|
||||
|
@ -39,7 +40,7 @@ class CommentStorageController extends DatabaseStorageControllerNG {
|
|||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::attachLoad().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function attachLoad(&$records, $load_revision = FALSE) {
|
||||
// Prepare standard comment fields.
|
||||
|
@ -52,156 +53,9 @@ class CommentStorageController extends DatabaseStorageControllerNG {
|
|||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageControllerNG::create().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function create(array $values) {
|
||||
if (empty($values['node_type']) && !empty($values['nid'])) {
|
||||
$node = node_load(is_object($values['nid']) ? $values['nid']->value : $values['nid']);
|
||||
$values['node_type'] = 'comment_node_' . $node->type;
|
||||
}
|
||||
return parent::create($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::preSave().
|
||||
*
|
||||
* @see comment_int_to_alphadecimal()
|
||||
* @see comment_alphadecimal_to_int()
|
||||
*/
|
||||
protected function preSave(EntityInterface $comment) {
|
||||
global $user;
|
||||
|
||||
if (!isset($comment->status->value)) {
|
||||
$comment->status->value = user_access('skip comment approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED;
|
||||
}
|
||||
// Make sure we have a proper bundle name.
|
||||
if (!isset($comment->node_type->value)) {
|
||||
$comment->node_type->value = 'comment_node_' . $comment->nid->entity->type;
|
||||
}
|
||||
if ($comment->isNew()) {
|
||||
// Add the comment to database. This next section builds the thread field.
|
||||
// Also see the documentation for comment_view().
|
||||
if (!empty($comment->thread->value)) {
|
||||
// Allow calling code to set thread itself.
|
||||
$thread = $comment->thread->value;
|
||||
}
|
||||
else {
|
||||
if ($this->threadLock) {
|
||||
// As preSave() is protected, this can only happen when this class
|
||||
// is extended in a faulty manner.
|
||||
throw new LogicException('preSave is called again without calling postSave() or releaseThreadLock()');
|
||||
}
|
||||
if ($comment->pid->target_id == 0) {
|
||||
// This is a comment with no parent comment (depth 0): we start
|
||||
// by retrieving the maximum thread level.
|
||||
$max = db_query('SELECT MAX(thread) FROM {comment} WHERE nid = :nid', array(':nid' => $comment->nid->target_id))->fetchField();
|
||||
// Strip the "/" from the end of the thread.
|
||||
$max = rtrim($max, '/');
|
||||
// We need to get the value at the correct depth.
|
||||
$parts = explode('.', $max);
|
||||
$n = comment_alphadecimal_to_int($parts[0]);
|
||||
$prefix = '';
|
||||
}
|
||||
else {
|
||||
// This is a comment with a parent comment, so increase the part of
|
||||
// the thread value at the proper depth.
|
||||
|
||||
// Get the parent comment:
|
||||
$parent = $comment->pid->entity;
|
||||
// Strip the "/" from the end of the parent thread.
|
||||
$parent->thread->value = (string) rtrim((string) $parent->thread->value, '/');
|
||||
$prefix = $parent->thread->value . '.';
|
||||
// Get the max value in *this* thread.
|
||||
$max = db_query("SELECT MAX(thread) FROM {comment} WHERE thread LIKE :thread AND nid = :nid", array(
|
||||
':thread' => $parent->thread->value . '.%',
|
||||
':nid' => $comment->nid->target_id,
|
||||
))->fetchField();
|
||||
|
||||
if ($max == '') {
|
||||
// First child of this parent. As the other two cases do an
|
||||
// increment of the thread number before creating the thread
|
||||
// string set this to -1 so it requires an increment too.
|
||||
$n = -1;
|
||||
}
|
||||
else {
|
||||
// Strip the "/" at the end of the thread.
|
||||
$max = rtrim($max, '/');
|
||||
// Get the value at the correct depth.
|
||||
$parts = explode('.', $max);
|
||||
$parent_depth = count(explode('.', $parent->thread->value));
|
||||
$n = comment_alphadecimal_to_int($parts[$parent_depth]);
|
||||
}
|
||||
}
|
||||
// Finally, build the thread field for this new comment. To avoid
|
||||
// race conditions, get a lock on the thread. If aother process already
|
||||
// has the lock, just move to the next integer.
|
||||
do {
|
||||
$thread = $prefix . comment_int_to_alphadecimal(++$n) . '/';
|
||||
} while (!lock()->acquire("comment:{$comment->nid->target_id}:$thread"));
|
||||
$this->threadLock = $thread;
|
||||
}
|
||||
if (empty($comment->created->value)) {
|
||||
$comment->created->value = REQUEST_TIME;
|
||||
}
|
||||
if (empty($comment->changed->value)) {
|
||||
$comment->changed->value = $comment->created->value;
|
||||
}
|
||||
// We test the value with '===' because we need to modify anonymous
|
||||
// users as well.
|
||||
if ($comment->uid->target_id === $user->uid && $user->uid) {
|
||||
$comment->name->value = $user->name;
|
||||
}
|
||||
// Add the values which aren't passed into the function.
|
||||
$comment->thread->value = $thread;
|
||||
$comment->hostname->value = \Drupal::request()->getClientIP();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::postSave().
|
||||
*/
|
||||
protected function postSave(EntityInterface $comment, $update) {
|
||||
$this->releaseThreadLock();
|
||||
// Update the {node_comment_statistics} table prior to executing the hook.
|
||||
$this->updateNodeStatistics($comment->nid->target_id);
|
||||
if ($comment->status->value == COMMENT_PUBLISHED) {
|
||||
module_invoke_all('comment_publish', $comment);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::postDelete().
|
||||
*/
|
||||
protected function postDelete($comments) {
|
||||
// Delete the comments' replies.
|
||||
$query = db_select('comment', 'c')
|
||||
->fields('c', array('cid'))
|
||||
->condition('pid', array(array_keys($comments)), 'IN');
|
||||
$child_cids = $query->execute()->fetchCol();
|
||||
entity_delete_multiple('comment', $child_cids);
|
||||
|
||||
foreach ($comments as $comment) {
|
||||
$this->updateNodeStatistics($comment->nid->target_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the comment statistics for a given node.
|
||||
*
|
||||
* The {node_comment_statistics} table has the following fields:
|
||||
* - last_comment_timestamp: The timestamp of the last comment for this node,
|
||||
* or the node created timestamp if no comments exist for the node.
|
||||
* - last_comment_name: The name of the anonymous poster for the last comment.
|
||||
* - last_comment_uid: The user ID of the poster for the last comment for
|
||||
* this node, or the node author's user ID if no comments exist for the
|
||||
* node.
|
||||
* - comment_count: The total number of approved/published comments on this
|
||||
* node.
|
||||
*
|
||||
* @param $nid
|
||||
* The node ID.
|
||||
*/
|
||||
protected function updateNodeStatistics($nid) {
|
||||
public function updateNodeStatistics($nid) {
|
||||
// Allow bulk updates and inserts to temporarily disable the
|
||||
// maintenance of the {node_comment_statistics} table.
|
||||
if (!variable_get('comment_maintain_node_statistics', TRUE)) {
|
||||
|
@ -247,17 +101,7 @@ class CommentStorageController extends DatabaseStorageControllerNG {
|
|||
}
|
||||
|
||||
/**
|
||||
* Release the lock acquired for the thread in preSave().
|
||||
*/
|
||||
protected function releaseThreadLock() {
|
||||
if ($this->threadLock) {
|
||||
lock()->release($this->threadLock);
|
||||
$this->threadLock = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Entity\DataBaseStorageControllerNG::basePropertyDefinitions().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function baseFieldDefinitions() {
|
||||
$properties['cid'] = array(
|
||||
|
@ -356,4 +200,32 @@ class CommentStorageController extends DatabaseStorageControllerNG {
|
|||
);
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMaxThread(EntityInterface $comment) {
|
||||
return db_query('SELECT MAX(thread) FROM {comment} WHERE nid = :nid', array(':nid' => $comment->nid->target_id))->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMaxThreadPerThread(EntityInterface $comment) {
|
||||
return $this->database->query("SELECT MAX(thread) FROM {comment} WHERE thread LIKE :thread AND nid = :nid", array(
|
||||
':thread' => rtrim($comment->pid->entity->thread->value, '/') . '.%',
|
||||
':nid' => $comment->nid->target_id,
|
||||
))->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getChildCids(array $comments) {
|
||||
return $this->database->select('comment', 'c')
|
||||
->fields('c', array('cid'))
|
||||
->condition('pid', array_keys($comments))
|
||||
->execute()
|
||||
->fetchCol();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\comment\CommentStorageControllerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\comment;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
|
||||
/**
|
||||
* Defines a common interface for comment entity controller classes.
|
||||
*/
|
||||
interface CommentStorageControllerInterface extends EntityStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Get the maximum encoded thread value for the top level comments.
|
||||
*
|
||||
* @param EntityInterface $comment
|
||||
* A comment entity.
|
||||
*
|
||||
* @return string
|
||||
* The maximum encoded thread value among the top level comments of the
|
||||
* node $comment belongs to.
|
||||
*/
|
||||
public function getMaxThread(EntityInterface $comment);
|
||||
|
||||
/**
|
||||
* Get the maximum encoded thread value for the children of this comment.
|
||||
*
|
||||
* @param EntityInterface $comment
|
||||
* A comment entity.
|
||||
*
|
||||
* @return string
|
||||
* The maximum encoded thread value among all replies of $comment.
|
||||
*/
|
||||
public function getMaxThreadPerThread(EntityInterface $comment);
|
||||
|
||||
/**
|
||||
* Gets the comment ids of the passed comment entities' children.
|
||||
*
|
||||
* @param array $comments
|
||||
* An array of comment entities keyed by their ids.
|
||||
* @return array
|
||||
* The entity ids of the passed comment entities' children as an array.
|
||||
*/
|
||||
public function getChildCids(array $comments);
|
||||
|
||||
/**
|
||||
* Updates the comment statistics for a given node.
|
||||
*
|
||||
* The {node_comment_statistics} table has the following fields:
|
||||
* - last_comment_timestamp: The timestamp of the last comment for this node,
|
||||
* or the node created timestamp if no comments exist for the node.
|
||||
* - last_comment_name: The name of the anonymous poster for the last comment.
|
||||
* - last_comment_uid: The user ID of the poster for the last comment for
|
||||
* this node, or the node author's user ID if no comments exist for the
|
||||
* node.
|
||||
* - comment_count: The total number of approved/published comments on this
|
||||
* node.
|
||||
*
|
||||
* @param $nid
|
||||
* The node ID.
|
||||
*/
|
||||
public function updateNodeStatistics($nid);
|
||||
|
||||
}
|
|
@ -11,6 +11,7 @@ use Drupal\Core\Entity\EntityNG;
|
|||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\comment\CommentInterface;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\Core\Language\Language;
|
||||
|
||||
/**
|
||||
|
@ -224,6 +225,139 @@ class Comment extends EntityNG implements CommentInterface {
|
|||
return $this->get('cid')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
|
||||
if (empty($values['node_type']) && !empty($values['nid'])) {
|
||||
$node = node_load(is_object($values['nid']) ? $values['nid']->value : $values['nid']);
|
||||
$values['node_type'] = 'comment_node_' . $node->type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageControllerInterface $storage_controller) {
|
||||
global $user;
|
||||
|
||||
if (!isset($this->status->value)) {
|
||||
$this->status->value = user_access('skip comment approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED;
|
||||
}
|
||||
// Make sure we have a proper bundle name.
|
||||
if (!isset($this->node_type->value)) {
|
||||
$this->node_type->value = 'comment_node_' . $this->nid->entity->type;
|
||||
}
|
||||
if ($this->isNew()) {
|
||||
// Add the comment to database. This next section builds the thread field.
|
||||
// Also see the documentation for comment_view().
|
||||
if (!empty($this->thread->value)) {
|
||||
// Allow calling code to set thread itself.
|
||||
$thread = $this->thread->value;
|
||||
}
|
||||
else {
|
||||
if ($this->threadLock) {
|
||||
// As preSave() is protected, this can only happen when this class
|
||||
// is extended in a faulty manner.
|
||||
throw new \LogicException('preSave is called again without calling postSave() or releaseThreadLock()');
|
||||
}
|
||||
if ($this->pid->target_id == 0) {
|
||||
// This is a comment with no parent comment (depth 0): we start
|
||||
// by retrieving the maximum thread level.
|
||||
$max = $storage_controller->getMaxThread($this);
|
||||
// Strip the "/" from the end of the thread.
|
||||
$max = rtrim($max, '/');
|
||||
// We need to get the value at the correct depth.
|
||||
$parts = explode('.', $max);
|
||||
$n = comment_alphadecimal_to_int($parts[0]);
|
||||
$prefix = '';
|
||||
}
|
||||
else {
|
||||
// This is a comment with a parent comment, so increase the part of
|
||||
// the thread value at the proper depth.
|
||||
|
||||
// Get the parent comment:
|
||||
$parent = $this->pid->entity;
|
||||
// Strip the "/" from the end of the parent thread.
|
||||
$parent->thread->value = (string) rtrim((string) $parent->thread->value, '/');
|
||||
$prefix = $parent->thread->value . '.';
|
||||
// Get the max value in *this* thread.
|
||||
$max = $storage_controller->getMaxThreadPerThread($this);
|
||||
|
||||
if ($max == '') {
|
||||
// First child of this parent. As the other two cases do an
|
||||
// increment of the thread number before creating the thread
|
||||
// string set this to -1 so it requires an increment too.
|
||||
$n = -1;
|
||||
}
|
||||
else {
|
||||
// Strip the "/" at the end of the thread.
|
||||
$max = rtrim($max, '/');
|
||||
// Get the value at the correct depth.
|
||||
$parts = explode('.', $max);
|
||||
$parent_depth = count(explode('.', $parent->thread->value));
|
||||
$n = comment_alphadecimal_to_int($parts[$parent_depth]);
|
||||
}
|
||||
}
|
||||
// Finally, build the thread field for this new comment. To avoid
|
||||
// race conditions, get a lock on the thread. If aother process already
|
||||
// has the lock, just move to the next integer.
|
||||
do {
|
||||
$thread = $prefix . comment_int_to_alphadecimal(++$n) . '/';
|
||||
} while (!lock()->acquire("comment:{$this->nid->target_id}:$thread"));
|
||||
$this->threadLock = $thread;
|
||||
}
|
||||
if (empty($this->created->value)) {
|
||||
$this->created->value = REQUEST_TIME;
|
||||
}
|
||||
if (empty($this->changed->value)) {
|
||||
$this->changed->value = $this->created->value;
|
||||
}
|
||||
// We test the value with '===' because we need to modify anonymous
|
||||
// users as well.
|
||||
if ($this->uid->target_id === $user->uid && $user->uid) {
|
||||
$this->name->value = $user->name;
|
||||
}
|
||||
// Add the values which aren't passed into the function.
|
||||
$this->thread->value = $thread;
|
||||
$this->hostname->value = \Drupal::request()->getClientIP();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
$this->releaseThreadLock();
|
||||
// Update the {node_comment_statistics} table prior to executing the hook.
|
||||
$storage_controller->updateNodeStatistics($this->nid->target_id);
|
||||
if ($this->status->value == COMMENT_PUBLISHED) {
|
||||
module_invoke_all('comment_publish', $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the lock acquired for the thread in preSave().
|
||||
*/
|
||||
protected function releaseThreadLock() {
|
||||
if ($this->threadLock) {
|
||||
lock()->release($this->threadLock);
|
||||
$this->threadLock = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
$child_cids = $storage_controller->getChildCids($entities);
|
||||
entity_delete_multiple('comment', $child_cids);
|
||||
|
||||
foreach ($entities as $id => $entity) {
|
||||
$storage_controller->updateNodeStatistics($entity->nid->target_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -8,36 +8,10 @@
|
|||
namespace Drupal\contact;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigStorageController;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* Controller class for contact categories.
|
||||
*/
|
||||
class CategoryStorageController extends ConfigStorageController {
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\Config\Entity\ConfigStorageController::postSave().
|
||||
*/
|
||||
protected function postSave(EntityInterface $entity, $update) {
|
||||
parent::postSave($entity, $update);
|
||||
|
||||
if (!$update) {
|
||||
entity_invoke_bundle_hook('create', 'contact_message', $entity->id());
|
||||
}
|
||||
elseif ($entity->original->id() != $entity->id()) {
|
||||
entity_invoke_bundle_hook('rename', 'contact_message', $entity->original->id(), $entity->id());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\Config\Entity\ConfigStorageController::postDelete().
|
||||
*/
|
||||
protected function postDelete($entities) {
|
||||
parent::postDelete($entities);
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
entity_invoke_bundle_hook('delete', 'contact_message', $entity->id());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\contact\Plugin\Core\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\contact\CategoryInterface;
|
||||
|
@ -81,4 +82,25 @@ class Category extends ConfigEntityBase implements CategoryInterface {
|
|||
*/
|
||||
public $weight = 0;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
if (!$update) {
|
||||
entity_invoke_bundle_hook('create', 'contact_message', $this->id());
|
||||
}
|
||||
elseif ($this->original->id() != $this->id()) {
|
||||
entity_invoke_bundle_hook('rename', 'contact_message', $this->original->id(), $this->id());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
foreach ($entities as $entity) {
|
||||
entity_invoke_bundle_hook('delete', 'contact_message', $entity->id());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ use Drupal\Core\Annotation\Translation;
|
|||
* label = @Translation("Test Entity with a bundle key"),
|
||||
* module = "field_test",
|
||||
* controllers = {
|
||||
* "storage" = "Drupal\field_test\TestEntityController",
|
||||
* "storage" = "Drupal\Core\Entity\DatabaseStorageController",
|
||||
* "form" = {
|
||||
* "default" = "Drupal\field_test\TestEntityFormController"
|
||||
* }
|
||||
|
|
|
@ -18,7 +18,7 @@ use Drupal\Core\Annotation\Translation;
|
|||
* label = @Translation("Test Entity with a specified bundle"),
|
||||
* module = "field_test",
|
||||
* controllers = {
|
||||
* "storage" = "Drupal\field_test\TestEntityController",
|
||||
* "storage" = "Drupal\Core\Entity\DatabaseStorageController",
|
||||
* "form" = {
|
||||
* "default" = "Drupal\field_test\TestEntityFormController"
|
||||
* }
|
||||
|
|
|
@ -18,7 +18,7 @@ use Drupal\Core\Annotation\Translation;
|
|||
* label = @Translation("Test Entity, cacheable"),
|
||||
* module = "field_test",
|
||||
* controllers = {
|
||||
* "storage" = "Drupal\field_test\TestEntityController"
|
||||
* "storage" = "Drupal\Core\Entity\DatabaseStorageController"
|
||||
* },
|
||||
* field_cache = TRUE,
|
||||
* base_table = "test_entity",
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace Drupal\field_test\Plugin\Core\Entity;
|
|||
use Drupal\Core\Entity\Entity;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
|
||||
/**
|
||||
* Test entity class.
|
||||
|
@ -19,7 +20,7 @@ use Drupal\Core\Annotation\Translation;
|
|||
* label = @Translation("Test Entity"),
|
||||
* module = "field_test",
|
||||
* controllers = {
|
||||
* "storage" = "Drupal\field_test\TestEntityController",
|
||||
* "storage" = "Drupal\Core\Entity\DatabaseStorageController",
|
||||
* "render" = "Drupal\Core\Entity\EntityRenderController",
|
||||
* "form" = {
|
||||
* "default" = "Drupal\field_test\TestEntityFormController"
|
||||
|
@ -86,5 +87,16 @@ class TestEntity extends Entity {
|
|||
public function bundle() {
|
||||
return !empty($this->fttype) ? $this->fttype : $this->entityType();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSaveRevision(EntityStorageControllerInterface $storage_controller, \stdClass $record) {
|
||||
// Allow for predefined revision ids.
|
||||
if (!empty($record->use_provided_revision_id)) {
|
||||
$record->ftvid = $record->use_provided_revision_id;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\field_test\TestEntityController.
|
||||
*/
|
||||
|
||||
namespace Drupal\field_test;
|
||||
|
||||
use Drupal\Core\Entity\DatabaseStorageController;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* Controller class for the test entity entity types.
|
||||
*/
|
||||
class TestEntityController extends DatabaseStorageController {
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::preSaveRevision().
|
||||
*/
|
||||
public function preSaveRevision(\stdClass $record, EntityInterface $entity) {
|
||||
// Allow for predefined revision ids.
|
||||
if (!empty($record->use_provided_revision_id)) {
|
||||
$record->ftvid = $record->use_provided_revision_id;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -8,76 +8,17 @@
|
|||
namespace Drupal\file;
|
||||
|
||||
use Drupal\Core\Entity\DatabaseStorageControllerNG;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Language\Language;
|
||||
|
||||
/**
|
||||
* File storage controller for files.
|
||||
*/
|
||||
class FileStorageController extends DatabaseStorageControllerNG {
|
||||
class FileStorageController extends DatabaseStorageControllerNG implements FileStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::create().
|
||||
*/
|
||||
public function create(array $values) {
|
||||
// Automatically detect filename if not set.
|
||||
if (!isset($values['filename']) && isset($values['uri'])) {
|
||||
$values['filename'] = drupal_basename($values['uri']);
|
||||
}
|
||||
|
||||
// Automatically detect filemime if not set.
|
||||
if (!isset($values['filemime']) && isset($values['uri'])) {
|
||||
$values['filemime'] = file_get_mimetype($values['uri']);
|
||||
}
|
||||
return parent::create($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::presave().
|
||||
*/
|
||||
protected function preSave(EntityInterface $entity) {
|
||||
$entity->timestamp = REQUEST_TIME;
|
||||
$entity->setSize(filesize($entity->getFileUri()));
|
||||
if (!$entity->langcode->value) {
|
||||
// Default the file's language code to none, because files are language
|
||||
// neutral more often than language dependent. Until we have better
|
||||
// flexible settings.
|
||||
// @todo See http://drupal.org/node/258785 and followups.
|
||||
$entity->langcode = Language::LANGCODE_NOT_SPECIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::preDelete().
|
||||
*/
|
||||
public function preDelete($entities) {
|
||||
foreach ($entities as $entity) {
|
||||
// Delete the actual file. Failures due to invalid files and files that
|
||||
// were already deleted are logged to watchdog but ignored, the
|
||||
// corresponding file entity will be deleted.
|
||||
file_unmanaged_delete($entity->getFileUri());
|
||||
}
|
||||
// Delete corresponding file usage entries.
|
||||
db_delete('file_usage')
|
||||
->condition('fid', array_keys($entities), 'IN')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines total disk space used by a single user or the whole filesystem.
|
||||
*
|
||||
* @param int $uid
|
||||
* Optional. A user id, specifying NULL returns the total space used by all
|
||||
* non-temporary files.
|
||||
* @param $status
|
||||
* Optional. The file status to consider. The default is to only
|
||||
* consider files in status FILE_STATUS_PERMANENT.
|
||||
*
|
||||
* @return int
|
||||
* An integer containing the number of bytes used.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function spaceUsed($uid = NULL, $status = FILE_STATUS_PERMANENT) {
|
||||
$query = db_select($this->entityInfo['base_table'], 'f')
|
||||
$query = $this->database->select($this->entityInfo['base_table'], 'f')
|
||||
->condition('f.status', $status);
|
||||
$query->addExpression('SUM(f.filesize)', 'filesize');
|
||||
if (isset($uid)) {
|
||||
|
@ -87,15 +28,12 @@ class FileStorageController extends DatabaseStorageControllerNG {
|
|||
}
|
||||
|
||||
/**
|
||||
* Retrieve temporary files that are older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
|
||||
*
|
||||
* @return
|
||||
* A list of files to be deleted.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function retrieveTemporaryFiles() {
|
||||
// Use separate placeholders for the status to avoid a bug in some versions
|
||||
// of PHP. See http://drupal.org/node/352956.
|
||||
return db_query('SELECT fid FROM {' . $this->entityInfo['base_table'] . '} WHERE status <> :permanent AND timestamp < :timestamp', array(
|
||||
return $this->database->query('SELECT fid FROM {' . $this->entityInfo['base_table'] . '} WHERE status <> :permanent AND timestamp < :timestamp', array(
|
||||
':permanent' => FILE_STATUS_PERMANENT,
|
||||
':timestamp' => REQUEST_TIME - DRUPAL_MAXIMUM_TEMP_FILE_AGE
|
||||
));
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\file\FileStorageControllerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\file;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
|
||||
/**
|
||||
* Defines a common interface for file entity controller classes.
|
||||
*/
|
||||
interface FileStorageControllerInterface extends EntityStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Determines total disk space used by a single user or the whole filesystem.
|
||||
*
|
||||
* @param int $uid
|
||||
* Optional. A user id, specifying NULL returns the total space used by all
|
||||
* non-temporary files.
|
||||
* @param int $status
|
||||
* (Optional) The file status to consider. The default is to only
|
||||
* consider files in status FILE_STATUS_PERMANENT.
|
||||
*
|
||||
* @return int
|
||||
* An integer containing the number of bytes used.
|
||||
*/
|
||||
public function spaceUsed($uid = NULL, $status = FILE_STATUS_PERMANENT);
|
||||
|
||||
/**
|
||||
* Retrieve temporary files that are older than DRUPAL_MAXIMUM_TEMP_FILE_AGE.
|
||||
*
|
||||
* @return array
|
||||
* A list of files to be deleted.
|
||||
*/
|
||||
public function retrieveTemporaryFiles();
|
||||
|
||||
}
|
|
@ -9,6 +9,7 @@ namespace Drupal\file\Plugin\Core\Entity;
|
|||
|
||||
use Drupal\Core\Entity\EntityNG;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\file\FileInterface;
|
||||
|
@ -158,4 +159,53 @@ class File extends EntityNG implements FileInterface {
|
|||
$this->get('status')->value = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
|
||||
// Automatically detect filename if not set.
|
||||
if (!isset($values['filename']) && isset($values['uri'])) {
|
||||
$values['filename'] = drupal_basename($values['uri']);
|
||||
}
|
||||
|
||||
// Automatically detect filemime if not set.
|
||||
if (!isset($values['filemime']) && isset($values['uri'])) {
|
||||
$values['filemime'] = file_get_mimetype($values['uri']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageControllerInterface $storage_controller) {
|
||||
$this->timestamp = REQUEST_TIME;
|
||||
$this->setSize(filesize($this->getFileUri()));
|
||||
if (!isset($this->langcode->value)) {
|
||||
// Default the file's language code to none, because files are language
|
||||
// neutral more often than language dependent. Until we have better
|
||||
// flexible settings.
|
||||
// @todo See http://drupal.org/node/258785 and followups.
|
||||
$this->langcode = Language::LANGCODE_NOT_SPECIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
foreach ($entities as $entity) {
|
||||
// Delete all remaining references to this file.
|
||||
$file_usage = file_usage()->listUsage($entity);
|
||||
if (!empty($file_usage)) {
|
||||
foreach ($file_usage as $module => $usage) {
|
||||
file_usage()->delete($entity, $module);
|
||||
}
|
||||
}
|
||||
// Delete the actual file. Failures due to invalid files and files that
|
||||
// were already deleted are logged to watchdog but ignored, the
|
||||
// corresponding file entity will be deleted.
|
||||
file_unmanaged_delete($entity->getFileUri());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\filter\FilterFormatStorageController.
|
||||
*/
|
||||
|
||||
namespace Drupal\filter;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigStorageController;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* Defines the storage controller class for Filter Format entities.
|
||||
*/
|
||||
class FilterFormatStorageController extends ConfigStorageController {
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\Config\Entity\ConfigStorageController::preSave().
|
||||
*/
|
||||
protected function preSave(EntityInterface $entity) {
|
||||
parent::preSave($entity);
|
||||
|
||||
$entity->name = trim($entity->label());
|
||||
|
||||
// @todo Do not save disabled filters whose properties are identical to
|
||||
// all default properties.
|
||||
|
||||
// Determine whether the format can be cached.
|
||||
// @todo This is a derived/computed definition, not configuration.
|
||||
$entity->cache = TRUE;
|
||||
foreach ($entity->filters() as $filter) {
|
||||
if ($filter->status && !$filter->cache) {
|
||||
$entity->cache = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\Config\Entity\ConfigStorageController::postSave().
|
||||
*/
|
||||
protected function postSave(EntityInterface $entity, $update) {
|
||||
parent::postSave($entity, $update);
|
||||
|
||||
// Clear the static caches of filter_formats() and others.
|
||||
filter_formats_reset();
|
||||
|
||||
if ($update) {
|
||||
// Clear the filter cache whenever a text format is updated.
|
||||
cache('filter')->deleteTags(array('filter_format' => $entity->id()));
|
||||
}
|
||||
else {
|
||||
// Default configuration of modules and installation profiles is allowed
|
||||
// to specify a list of user roles to grant access to for the new format;
|
||||
// apply the defined user role permissions when a new format is inserted
|
||||
// and has a non-empty $roles property.
|
||||
// Note: user_role_change_permissions() triggers a call chain back into
|
||||
// filter_permission() and lastly filter_formats(), so its cache must be
|
||||
// reset upfront.
|
||||
if (($roles = $entity->get('roles')) && $permission = filter_permission_name($entity)) {
|
||||
foreach (user_roles() as $rid => $name) {
|
||||
$enabled = in_array($rid, $roles, TRUE);
|
||||
user_role_change_permissions($rid, array($permission => $enabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -10,6 +10,7 @@ namespace Drupal\filter\Plugin\Core\Entity;
|
|||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\filter\FilterFormatInterface;
|
||||
use Drupal\filter\FilterBag;
|
||||
|
||||
|
@ -21,7 +22,7 @@ use Drupal\filter\FilterBag;
|
|||
* label = @Translation("Text format"),
|
||||
* module = "filter",
|
||||
* controllers = {
|
||||
* "storage" = "Drupal\filter\FilterFormatStorageController"
|
||||
* "storage" = "Drupal\Core\Config\Entity\ConfigStorageController"
|
||||
* },
|
||||
* config_prefix = "filter.format",
|
||||
* entity_keys = {
|
||||
|
@ -179,4 +180,50 @@ class FilterFormat extends ConfigEntityBase implements FilterFormatInterface {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageControllerInterface $storage_controller) {
|
||||
$this->name = trim($this->label());
|
||||
|
||||
// @todo Do not save disabled filters whose properties are identical to
|
||||
// all default properties.
|
||||
|
||||
// Determine whether the format can be cached.
|
||||
// @todo This is a derived/computed definition, not configuration.
|
||||
$this->cache = TRUE;
|
||||
foreach ($this->filters() as $filter) {
|
||||
if ($filter->status && !$filter->cache) {
|
||||
$this->cache = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
// Clear the static caches of filter_formats() and others.
|
||||
filter_formats_reset();
|
||||
|
||||
if ($update) {
|
||||
// Clear the filter cache whenever a text format is updated.
|
||||
cache('filter')->deleteTags(array('filter_format' => $this->id()));
|
||||
}
|
||||
else {
|
||||
// Default configuration of modules and installation profiles is allowed
|
||||
// to specify a list of user roles to grant access to for the new format;
|
||||
// apply the defined user role permissions when a new format is inserted
|
||||
// and has a non-empty $roles property.
|
||||
// Note: user_role_change_permissions() triggers a call chain back into
|
||||
// filter_permission() and lastly filter_formats(), so its cache must be
|
||||
// reset upfront.
|
||||
if (($roles = $this->get('roles')) && $permission = filter_permission_name($this)) {
|
||||
foreach (user_roles() as $rid => $name) {
|
||||
$enabled = in_array($rid, $roles, TRUE);
|
||||
user_role_change_permissions($rid, array($permission => $enabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@ namespace Drupal\image;
|
|||
|
||||
use Drupal\Core\Config\Entity\ConfigStorageController;
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\image\Plugin\Core\Entity\ImageStyle;
|
||||
|
||||
/**
|
||||
* Defines a controller class for image styles.
|
||||
|
@ -35,73 +33,4 @@ class ImageStyleStorageController extends ConfigStorageController {
|
|||
parent::attachLoad($queried_entities, $revision_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\Config\Entity\ConfigStorageController::postSave().
|
||||
*/
|
||||
protected function postSave(EntityInterface $entity, $update) {
|
||||
if ($update && !empty($entity->original) && $entity->{$this->idKey} !== $entity->original->{$this->idKey}) {
|
||||
// The old image style name needs flushing after a rename.
|
||||
image_style_flush($entity->original);
|
||||
// Update field instance settings if necessary.
|
||||
$this->replaceImageStyle($entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\Config\Entity\ConfigStorageController::postDelete().
|
||||
*/
|
||||
protected function postDelete($entities) {
|
||||
foreach ($entities as $style) {
|
||||
// Flush cached media for the deleted style.
|
||||
image_style_flush($style);
|
||||
// Check whether field instance settings need to be updated.
|
||||
// In case no replacement style was specified, all image fields that are
|
||||
// using the deleted style are left in a broken state.
|
||||
if ($new_id = $style->get('replacementID')) {
|
||||
// The deleted ID is still set as originalID.
|
||||
$style->set('name', $new_id);
|
||||
$this->replaceImageStyle($style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update field instance settings if the image style name is changed.
|
||||
*
|
||||
* @param ImageStyle $style
|
||||
* The image style.
|
||||
*/
|
||||
protected function replaceImageStyle(ImageStyle $style) {
|
||||
if ($style->id() != $style->getOriginalID()) {
|
||||
$instances = field_read_instances();
|
||||
// Loop through all fields searching for image fields.
|
||||
foreach ($instances as $instance) {
|
||||
if ($instance->getField()->type == 'image') {
|
||||
$view_modes = entity_get_view_modes($instance['entity_type']);
|
||||
$view_modes = array('default') + array_keys($view_modes);
|
||||
foreach ($view_modes as $view_mode) {
|
||||
$display = entity_get_display($instance['entity_type'], $instance['bundle'], $view_mode);
|
||||
$display_options = $display->getComponent($instance['field_name']);
|
||||
|
||||
// Check if the formatter involves an image style.
|
||||
if ($display_options && $display_options['type'] == 'image' && $display_options['settings']['image_style'] == $style->getOriginalID()) {
|
||||
// Update display information for any instance using the image
|
||||
// style that was just deleted.
|
||||
$display_options['settings']['image_style'] = $style->id();
|
||||
$display->setComponent($instance['field_name'], $display_options)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
$entity_form_display = entity_get_form_display($instance['entity_type'], $instance['bundle'], 'default');
|
||||
$widget_configuration = $entity_form_display->getComponent($instance['field_name']);
|
||||
if ($widget_configuration['settings']['preview_image_style'] == $style->getOriginalID()) {
|
||||
$widget_options['settings']['preview_image_style'] = $style->id();
|
||||
$entity_form_display->setComponent($instance['field_name'], $widget_options)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace Drupal\image\Plugin\Core\Entity;
|
|||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\image\ImageStyleInterface;
|
||||
|
||||
/**
|
||||
|
@ -68,4 +69,73 @@ class ImageStyle extends ConfigEntityBase implements ImageStyleInterface {
|
|||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
if ($update && !empty($this->original) && $this->id() !== $this->original->id()) {
|
||||
// The old image style name needs flushing after a rename.
|
||||
image_style_flush($this->original);
|
||||
// Update field instance settings if necessary.
|
||||
static::replaceImageStyle($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
foreach ($entities as $style) {
|
||||
// Flush cached media for the deleted style.
|
||||
image_style_flush($style);
|
||||
// Check whether field instance settings need to be updated.
|
||||
// In case no replacement style was specified, all image fields that are
|
||||
// using the deleted style are left in a broken state.
|
||||
if ($new_id = $style->get('replacementID')) {
|
||||
// The deleted ID is still set as originalID.
|
||||
$style->set('name', $new_id);
|
||||
static::replaceImageStyle($style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update field instance settings if the image style name is changed.
|
||||
*
|
||||
* @param \Drupal\image\Plugin\Core\Entity\ImageStyle $style
|
||||
* The image style.
|
||||
*/
|
||||
protected static function replaceImageStyle(ImageStyle $style) {
|
||||
if ($style->id() != $style->getOriginalID()) {
|
||||
$instances = field_read_instances();
|
||||
// Loop through all fields searching for image fields.
|
||||
foreach ($instances as $instance) {
|
||||
if ($instance->getField()->type == 'image') {
|
||||
$view_modes = entity_get_view_modes($instance['entity_type']);
|
||||
$view_modes = array('default') + array_keys($view_modes);
|
||||
foreach ($view_modes as $view_mode) {
|
||||
$display = entity_get_display($instance['entity_type'], $instance['bundle'], $view_mode);
|
||||
$display_options = $display->getComponent($instance['field_name']);
|
||||
|
||||
// Check if the formatter involves an image style.
|
||||
if ($display_options && $display_options['type'] == 'image' && $display_options['settings']['image_style'] == $style->getOriginalID()) {
|
||||
// Update display information for any instance using the image
|
||||
// style that was just deleted.
|
||||
$display_options['settings']['image_style'] = $style->id();
|
||||
$display->setComponent($instance['field_name'], $display_options)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
$entity_form_display = entity_get_form_display($instance['entity_type'], $instance['bundle'], 'default');
|
||||
$widget_configuration = $entity_form_display->getComponent($instance['field_name']);
|
||||
if ($widget_configuration['settings']['preview_image_style'] == $style->getOriginalID()) {
|
||||
$widget_options['settings']['preview_image_style'] = $style->id();
|
||||
$entity_form_display->setComponent($instance['field_name'], $widget_options)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ namespace Drupal\menu_link;
|
|||
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a menu link entity.
|
||||
|
@ -53,4 +55,44 @@ interface MenuLinkInterface extends ContentEntityInterface {
|
|||
*/
|
||||
public static function buildFromRouterItem(array $item);
|
||||
|
||||
/**
|
||||
* Returns the route_name matching a URL.
|
||||
*
|
||||
* @param string $link_path
|
||||
* The link path to find a route name for.
|
||||
*
|
||||
* @return string
|
||||
* The route name.
|
||||
*/
|
||||
public static function findRouteName($link_path);
|
||||
|
||||
/**
|
||||
* Sets the p1 through p9 properties for a menu link entity being saved.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $parent
|
||||
* A menu link entity.
|
||||
*/
|
||||
public function setParents(EntityInterface $parent);
|
||||
|
||||
/**
|
||||
* Finds a possible parent for a given menu link entity.
|
||||
*
|
||||
* Because the parent of a given link might not exist anymore in the database,
|
||||
* we apply a set of heuristics to determine a proper parent:
|
||||
*
|
||||
* - use the passed parent link if specified and existing.
|
||||
* - else, use the first existing link down the previous link hierarchy
|
||||
* - else, for system menu links (derived from hook_menu()), reparent
|
||||
* based on the path hierarchy.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller
|
||||
* Storage controller object.
|
||||
* @param array $parent_candidates
|
||||
* An array of menu link entities keyed by mlid.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface|false
|
||||
* A menu link entity structure of the possible parent or FALSE if no valid
|
||||
* parent has been found.
|
||||
*/
|
||||
public function findParent(EntityStorageControllerInterface $storage_controller, array $parent_candidates = array());
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ use Drupal\Core\Entity\EntityStorageException;
|
|||
use Drupal\Core\Database\Connection;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Cmf\Component\Routing\RouteProviderInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Controller class for menu links.
|
||||
|
@ -21,7 +20,7 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
* This extends the Drupal\entity\DatabaseStorageController class, adding
|
||||
* required special handling for menu_link entities.
|
||||
*/
|
||||
class MenuLinkStorageController extends DatabaseStorageController {
|
||||
class MenuLinkStorageController extends DatabaseStorageController implements MenuLinkStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Indicates whether the delete operation should re-parent children items.
|
||||
|
@ -144,6 +143,8 @@ class MenuLinkStorageController extends DatabaseStorageController {
|
|||
* Overrides DatabaseStorageController::save().
|
||||
*/
|
||||
public function save(EntityInterface $entity) {
|
||||
$entity_class = $this->entityInfo['class'];
|
||||
|
||||
// We return SAVED_UPDATED by default because the logic below might not
|
||||
// update the entity if its values haven't changed, so returning FALSE
|
||||
// would be confusing in that situation.
|
||||
|
@ -165,7 +166,7 @@ class MenuLinkStorageController extends DatabaseStorageController {
|
|||
// 'presave' hook first because we want to allow modules to alter the
|
||||
// entity before all the logic from our preSave() method.
|
||||
$this->invokeHook('presave', $entity);
|
||||
$this->preSave($entity);
|
||||
$entity->preSave($this);
|
||||
|
||||
// If every value in $entity->original is the same in the $entity, there
|
||||
// is no reason to run the update queries or clear the caches. We use
|
||||
|
@ -178,7 +179,7 @@ class MenuLinkStorageController extends DatabaseStorageController {
|
|||
if ($return) {
|
||||
if (!$entity->isNew()) {
|
||||
$this->resetCache(array($entity->{$this->idKey}));
|
||||
$this->postSave($entity, TRUE);
|
||||
$entity->postSave($this, TRUE);
|
||||
$this->invokeHook('update', $entity);
|
||||
}
|
||||
else {
|
||||
|
@ -186,7 +187,7 @@ class MenuLinkStorageController extends DatabaseStorageController {
|
|||
$this->resetCache();
|
||||
|
||||
$entity->enforceIsNew(FALSE);
|
||||
$this->postSave($entity, FALSE);
|
||||
$entity->postSave($this, FALSE);
|
||||
$this->invokeHook('insert', $entity);
|
||||
}
|
||||
}
|
||||
|
@ -206,178 +207,21 @@ class MenuLinkStorageController extends DatabaseStorageController {
|
|||
}
|
||||
|
||||
/**
|
||||
* Overrides DatabaseStorageController::preSave().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function preSave(EntityInterface $entity) {
|
||||
// This is the easiest way to handle the unique internal path '<front>',
|
||||
// since a path marked as external does not need to match a router path.
|
||||
$entity->external = (url_is_external($entity->link_path) || $entity->link_path == '<front>') ? 1 : 0;
|
||||
|
||||
// Try to find a parent link. If found, assign it and derive its menu.
|
||||
$parent_candidates = !empty($entity->parentCandidates) ? $entity->parentCandidates : array();
|
||||
$parent = $this->findParent($entity, $parent_candidates);
|
||||
if ($parent) {
|
||||
$entity->plid = $parent->id();
|
||||
$entity->menu_name = $parent->menu_name;
|
||||
}
|
||||
// If no corresponding parent link was found, move the link to the top-level.
|
||||
else {
|
||||
$entity->plid = 0;
|
||||
}
|
||||
|
||||
// Directly fill parents for top-level links.
|
||||
if ($entity->plid == 0) {
|
||||
$entity->p1 = $entity->id();
|
||||
for ($i = 2; $i <= MENU_MAX_DEPTH; $i++) {
|
||||
$parent_property = "p$i";
|
||||
$entity->$parent_property = 0;
|
||||
}
|
||||
$entity->depth = 1;
|
||||
}
|
||||
// Otherwise, ensure that this link's depth is not beyond the maximum depth
|
||||
// and fill parents based on the parent link.
|
||||
else {
|
||||
if ($entity->has_children && $entity->original) {
|
||||
$limit = MENU_MAX_DEPTH - $this->findChildrenRelativeDepth($entity->original) - 1;
|
||||
}
|
||||
else {
|
||||
$limit = MENU_MAX_DEPTH - 1;
|
||||
}
|
||||
if ($parent->depth > $limit) {
|
||||
return FALSE;
|
||||
}
|
||||
$entity->depth = $parent->depth + 1;
|
||||
$this->setParents($entity, $parent);
|
||||
}
|
||||
|
||||
// Need to check both plid and menu_name, since plid can be 0 in any menu.
|
||||
if (isset($entity->original) && ($entity->plid != $entity->original->plid || $entity->menu_name != $entity->original->menu_name)) {
|
||||
$this->moveChildren($entity, $entity->original);
|
||||
}
|
||||
// Find the router_path.
|
||||
if (empty($entity->router_path) || empty($entity->original) || (isset($entity->original) && $entity->original->link_path != $entity->link_path)) {
|
||||
if ($entity->external) {
|
||||
$entity->router_path = '';
|
||||
}
|
||||
else {
|
||||
// Find the router path which will serve this path.
|
||||
$entity->parts = explode('/', $entity->link_path, MENU_MAX_PARTS);
|
||||
$entity->router_path = _menu_find_router_path($entity->link_path);
|
||||
}
|
||||
}
|
||||
// Find the route_name.
|
||||
if (!isset($entity->route_name)) {
|
||||
$entity->route_name = $this->findRouteName($entity->link_path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the route_name matching a URL.
|
||||
*
|
||||
* @param string $link_path
|
||||
* The link path to find a route name for.
|
||||
*
|
||||
* @return string
|
||||
* The route name.
|
||||
*/
|
||||
protected function findRouteName($link_path) {
|
||||
// Look up the route_name used for the given path.
|
||||
$request = Request::create('/' . $link_path);
|
||||
$request->attributes->set('system_path', $link_path);
|
||||
try {
|
||||
// Use router.dynamic instead of router, because router will call the
|
||||
// legacy router which will call hook_menu() and you will get back to
|
||||
// this method.
|
||||
$result = \Drupal::service('router.dynamic')->matchRequest($request);
|
||||
return isset($result['_route']) ? $result['_route'] : '';
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
return '';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* DatabaseStorageController::postSave().
|
||||
*/
|
||||
function postSave(EntityInterface $entity, $update) {
|
||||
// Check the has_children status of the parent.
|
||||
$this->updateParentalStatus($entity);
|
||||
|
||||
menu_cache_clear($entity->menu_name);
|
||||
if (isset($entity->original) && $entity->menu_name != $entity->original->menu_name) {
|
||||
menu_cache_clear($entity->original->menu_name);
|
||||
}
|
||||
|
||||
// Now clear the cache.
|
||||
_menu_clear_page_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an internal flag that allows us to prevent the reparenting operations
|
||||
* executed during deletion.
|
||||
*
|
||||
* @param bool $value
|
||||
*/
|
||||
public function preventReparenting($value = FALSE) {
|
||||
public function setPreventReparenting($value = FALSE) {
|
||||
$this->preventReparenting = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides DatabaseStorageController::preDelete().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function preDelete($entities) {
|
||||
// Nothing to do if we don't want to reparent children.
|
||||
if ($this->preventReparenting) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
// Children get re-attached to the item's parent.
|
||||
if ($entity->has_children) {
|
||||
$children = $this->loadByProperties(array('plid' => $entity->plid));
|
||||
foreach ($children as $child) {
|
||||
$child->plid = $entity->plid;
|
||||
$this->save($child);
|
||||
}
|
||||
}
|
||||
}
|
||||
public function getPreventReparenting() {
|
||||
return $this->preventReparenting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides DatabaseStorageController::postDelete().
|
||||
*/
|
||||
protected function postDelete($entities) {
|
||||
$affected_menus = array();
|
||||
// Update the has_children status of the parent.
|
||||
foreach ($entities as $entity) {
|
||||
if (!$this->preventReparenting) {
|
||||
$this->updateParentalStatus($entity);
|
||||
}
|
||||
|
||||
// Store all menu names for which we need to clear the cache.
|
||||
if (!isset($affected_menus[$entity->menu_name])) {
|
||||
$affected_menus[$entity->menu_name] = $entity->menu_name;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($affected_menus as $menu_name) {
|
||||
menu_cache_clear($menu_name);
|
||||
}
|
||||
_menu_clear_page_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads updated and customized menu links for specific router paths.
|
||||
*
|
||||
* Note that this is a low-level method and it doesn't return fully populated
|
||||
* menu link entities. (e.g. no fields are attached)
|
||||
*
|
||||
* @param array $router_paths
|
||||
* An array of router paths.
|
||||
*
|
||||
* @return array
|
||||
* An array of menu link objects indexed by their ids.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadUpdatedCustomized(array $router_paths) {
|
||||
$query = parent::buildQuery(NULL);
|
||||
|
@ -403,10 +247,7 @@ class MenuLinkStorageController extends DatabaseStorageController {
|
|||
}
|
||||
|
||||
/**
|
||||
* Loads system menu link as needed by system_get_module_admin_tasks().
|
||||
*
|
||||
* @return array
|
||||
* An array of menu link entities indexed by their IDs.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadModuleAdminTasks() {
|
||||
$query = $this->buildQuery(NULL);
|
||||
|
@ -422,12 +263,9 @@ class MenuLinkStorageController extends DatabaseStorageController {
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks and updates the 'has_children' property for the parent of a link.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* A menu link entity.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function updateParentalStatus(EntityInterface $entity, $exclude = FALSE) {
|
||||
public function updateParentalStatus(EntityInterface $entity, $exclude = FALSE) {
|
||||
// If plid == 0, there is nothing to update.
|
||||
if ($entity->plid) {
|
||||
// Check if at least one visible child exists in the table.
|
||||
|
@ -451,125 +289,7 @@ class MenuLinkStorageController extends DatabaseStorageController {
|
|||
}
|
||||
|
||||
/**
|
||||
* Finds a possible parent for a given menu link entity.
|
||||
*
|
||||
* Because the parent of a given link might not exist anymore in the database,
|
||||
* we apply a set of heuristics to determine a proper parent:
|
||||
*
|
||||
* - use the passed parent link if specified and existing.
|
||||
* - else, use the first existing link down the previous link hierarchy
|
||||
* - else, for system menu links (derived from hook_menu()), reparent
|
||||
* based on the path hierarchy.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* A menu link entity.
|
||||
* @param array $parent_candidates
|
||||
* An array of menu link entities keyed by mlid.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface|false
|
||||
* A menu link entity structure of the possible parent or FALSE if no valid
|
||||
* parent has been found.
|
||||
*/
|
||||
protected function findParent(EntityInterface $entity, array $parent_candidates = array()) {
|
||||
$parent = FALSE;
|
||||
|
||||
// This item is explicitely top-level, skip the rest of the parenting.
|
||||
if (isset($entity->plid) && empty($entity->plid)) {
|
||||
return $parent;
|
||||
}
|
||||
|
||||
// If we have a parent link ID, try to use that.
|
||||
$candidates = array();
|
||||
if (isset($entity->plid)) {
|
||||
$candidates[] = $entity->plid;
|
||||
}
|
||||
|
||||
// Else, if we have a link hierarchy try to find a valid parent in there.
|
||||
if (!empty($entity->depth) && $entity->depth > 1) {
|
||||
for ($depth = $entity->depth - 1; $depth >= 1; $depth--) {
|
||||
$parent_property = "p$depth";
|
||||
$candidates[] = $entity->$parent_property;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($candidates as $mlid) {
|
||||
if (isset($parent_candidates[$mlid])) {
|
||||
$parent = $parent_candidates[$mlid];
|
||||
}
|
||||
else {
|
||||
$parent = $this->load(array($mlid));
|
||||
$parent = reset($parent);
|
||||
}
|
||||
if ($parent) {
|
||||
return $parent;
|
||||
}
|
||||
}
|
||||
|
||||
// If everything else failed, try to derive the parent from the path
|
||||
// hierarchy. This only makes sense for links derived from menu router
|
||||
// items (ie. from hook_menu()).
|
||||
if ($entity->module == 'system') {
|
||||
// Find the parent - it must be unique.
|
||||
$parent_path = $entity->link_path;
|
||||
do {
|
||||
$parent = FALSE;
|
||||
$parent_path = substr($parent_path, 0, strrpos($parent_path, '/'));
|
||||
|
||||
$query = \Drupal::entityQuery($this->entityType);
|
||||
$query
|
||||
->condition('mlid', $entity->id(), '<>')
|
||||
->condition('module', 'system')
|
||||
// We always respect the link's 'menu_name'; inheritance for router
|
||||
// items is ensured in _menu_router_build().
|
||||
->condition('menu_name', $entity->menu_name)
|
||||
->condition('link_path', $parent_path);
|
||||
|
||||
$result = $query->execute();
|
||||
// Only valid if we get a unique result.
|
||||
if (count($result) == 1) {
|
||||
$parent = $this->load($result);
|
||||
$parent = reset($parent);
|
||||
}
|
||||
} while ($parent === FALSE && $parent_path);
|
||||
}
|
||||
|
||||
return $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the p1 through p9 properties for a menu link entity being saved.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* A menu link entity.
|
||||
* @param \Drupal\Core\Entity\EntityInterface $parent
|
||||
* A menu link entity.
|
||||
*/
|
||||
protected function setParents(EntityInterface $entity, EntityInterface $parent) {
|
||||
$i = 1;
|
||||
while ($i < $entity->depth) {
|
||||
$p = 'p' . $i++;
|
||||
$entity->{$p} = $parent->{$p};
|
||||
}
|
||||
$p = 'p' . $i++;
|
||||
// The parent (p1 - p9) corresponding to the depth always equals the mlid.
|
||||
$entity->{$p} = $entity->id();
|
||||
while ($i <= MENU_MAX_DEPTH) {
|
||||
$p = 'p' . $i++;
|
||||
$entity->{$p} = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the depth of an item's children relative to its depth.
|
||||
*
|
||||
* For example, if the item has a depth of 2 and the maximum of any child in
|
||||
* the menu link tree is 5, the relative depth is 3.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* A menu link entity.
|
||||
*
|
||||
* @return int
|
||||
* The relative depth, or zero.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function findChildrenRelativeDepth(EntityInterface $entity) {
|
||||
// @todo Since all we need is a specific field from the base table, does it
|
||||
|
@ -593,15 +313,9 @@ class MenuLinkStorageController extends DatabaseStorageController {
|
|||
}
|
||||
|
||||
/**
|
||||
* Updates the children of a menu link that is being moved.
|
||||
*
|
||||
* The menu name, parents (p1 - p6), and depth are updated for all children of
|
||||
* the link, and the has_children status of the previous parent is updated.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* A menu link entity.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function moveChildren(EntityInterface $entity) {
|
||||
public function moveChildren(EntityInterface $entity) {
|
||||
$query = $this->database->update($this->entityInfo['base_table']);
|
||||
|
||||
$query->fields(array('menu_name' => $entity->menu_name));
|
||||
|
@ -645,10 +359,7 @@ class MenuLinkStorageController extends DatabaseStorageController {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the number of menu links from a menu.
|
||||
*
|
||||
* @param string $menu_name
|
||||
* The unique name of a menu.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function countMenuLinks($menu_name) {
|
||||
$query = \Drupal::entityQuery($this->entityType);
|
||||
|
@ -657,4 +368,34 @@ class MenuLinkStorageController extends DatabaseStorageController {
|
|||
->count();
|
||||
return $query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParentFromHierarchy(EntityInterface $entity) {
|
||||
$parent_path = $entity->link_path;
|
||||
do {
|
||||
$parent = FALSE;
|
||||
$parent_path = substr($parent_path, 0, strrpos($parent_path, '/'));
|
||||
|
||||
$query = \Drupal::entityQuery($this->entityType);
|
||||
$query
|
||||
->condition('mlid', $entity->id(), '<>')
|
||||
->condition('module', 'system')
|
||||
// We always respect the link's 'menu_name'; inheritance for router
|
||||
// items is ensured in _menu_router_build().
|
||||
->condition('menu_name', $entity->menu_name)
|
||||
->condition('link_path', $parent_path);
|
||||
|
||||
$result = $query->execute();
|
||||
// Only valid if we get a unique result.
|
||||
if (count($result) == 1) {
|
||||
$parent = $this->load($result);
|
||||
$parent = reset($parent);
|
||||
}
|
||||
} while ($parent === FALSE && $parent_path);
|
||||
|
||||
return $parent;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\menu_link\MenuLinkStorageControllerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
|
||||
/**
|
||||
* Defines a common interface for menu link entity controller classes.
|
||||
*/
|
||||
interface MenuLinkStorageControllerInterface extends EntityStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Sets an internal flag that allows us to prevent the reparenting operations
|
||||
* executed during deletion.
|
||||
*
|
||||
* @param bool $value
|
||||
* TRUE if reparenting should be allowed, FALSE if it should be prevented.
|
||||
*/
|
||||
public function setPreventReparenting($value = FALSE);
|
||||
|
||||
/**
|
||||
* Gets value of internal flag that allows/prevents reparenting operations
|
||||
* executed during deletion.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if reparenting is allowed, FALSE if it is prevented.
|
||||
*/
|
||||
public function getPreventReparenting();
|
||||
|
||||
/**
|
||||
* Loads updated and customized menu links for specific router paths.
|
||||
*
|
||||
* Note that this is a low-level method and it doesn't return fully populated
|
||||
* menu link entities. (e.g. no fields are attached)
|
||||
*
|
||||
* @param array $router_paths
|
||||
* An array of router paths.
|
||||
*
|
||||
* @return array
|
||||
* An array of menu link objects indexed by their ids.
|
||||
*/
|
||||
public function loadUpdatedCustomized(array $router_paths);
|
||||
|
||||
/**
|
||||
* Loads system menu link as needed by system_get_module_admin_tasks().
|
||||
*
|
||||
* @return array
|
||||
* An array of menu link entities indexed by their IDs.
|
||||
*/
|
||||
public function loadModuleAdminTasks();
|
||||
|
||||
/**
|
||||
* Checks and updates the 'has_children' property for the parent of a link.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* A menu link entity.
|
||||
*/
|
||||
public function updateParentalStatus(EntityInterface $entity, $exclude = FALSE);
|
||||
|
||||
/**
|
||||
* Finds the depth of an item's children relative to its depth.
|
||||
*
|
||||
* For example, if the item has a depth of 2 and the maximum of any child in
|
||||
* the menu link tree is 5, the relative depth is 3.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* A menu link entity.
|
||||
*
|
||||
* @return int
|
||||
* The relative depth, or zero.
|
||||
*/
|
||||
public function findChildrenRelativeDepth(EntityInterface $entity);
|
||||
|
||||
/**
|
||||
* Updates the children of a menu link that is being moved.
|
||||
*
|
||||
* The menu name, parents (p1 - p6), and depth are updated for all children of
|
||||
* the link, and the has_children status of the previous parent is updated.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* A menu link entity.
|
||||
*/
|
||||
public function moveChildren(EntityInterface $entity);
|
||||
|
||||
/**
|
||||
* Returns the number of menu links from a menu.
|
||||
*
|
||||
* @param string $menu_name
|
||||
* The unique name of a menu.
|
||||
*/
|
||||
public function countMenuLinks($menu_name);
|
||||
|
||||
/**
|
||||
* Tries to derive menu link's parent from the path hierarchy.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* A menu link entity.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface|false
|
||||
* A menu link entity or FALSE if not valid parent was found.
|
||||
*/
|
||||
public function getParentFromHierarchy(EntityInterface $entity);
|
||||
|
||||
}
|
|
@ -9,9 +9,13 @@ namespace Drupal\menu_link\Plugin\Core\Entity;
|
|||
|
||||
use Drupal\menu_link\MenuLinkInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\Entity;
|
||||
|
||||
|
@ -372,4 +376,217 @@ class MenuLink extends Entity implements \ArrayAccess, MenuLinkInterface {
|
|||
public function offsetUnset($offset) {
|
||||
unset($this->{$offset});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
// Nothing to do if we don't want to reparent children.
|
||||
if ($storage_controller->getPreventReparenting()) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
// Children get re-attached to the item's parent.
|
||||
if ($entity->has_children) {
|
||||
$children = $storage_controller->loadByProperties(array('plid' => $entity->plid));
|
||||
foreach ($children as $child) {
|
||||
$child->plid = $entity->plid;
|
||||
$storage_controller->save($child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
$affected_menus = array();
|
||||
// Update the has_children status of the parent.
|
||||
foreach ($entities as $entity) {
|
||||
if (!$storage_controller->getPreventReparenting()) {
|
||||
$storage_controller->updateParentalStatus($entity);
|
||||
}
|
||||
|
||||
// Store all menu names for which we need to clear the cache.
|
||||
if (!isset($affected_menus[$entity->menu_name])) {
|
||||
$affected_menus[$entity->menu_name] = $entity->menu_name;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($affected_menus as $menu_name) {
|
||||
menu_cache_clear($menu_name);
|
||||
}
|
||||
_menu_clear_page_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageControllerInterface $storage_controller) {
|
||||
// This is the easiest way to handle the unique internal path '<front>',
|
||||
// since a path marked as external does not need to match a router path.
|
||||
$this->external = (url_is_external($this->link_path) || $this->link_path == '<front>') ? 1 : 0;
|
||||
|
||||
// Try to find a parent link. If found, assign it and derive its menu.
|
||||
$parent_candidates = !empty($this->parentCandidates) ? $this->parentCandidates : array();
|
||||
$parent = $this->findParent($storage_controller, $parent_candidates);
|
||||
if ($parent) {
|
||||
$this->plid = $parent->id();
|
||||
$this->menu_name = $parent->menu_name;
|
||||
}
|
||||
// If no corresponding parent link was found, move the link to the top-level.
|
||||
else {
|
||||
$this->plid = 0;
|
||||
}
|
||||
|
||||
// Directly fill parents for top-level links.
|
||||
if ($this->plid == 0) {
|
||||
$this->p1 = $this->id();
|
||||
for ($i = 2; $i <= MENU_MAX_DEPTH; $i++) {
|
||||
$parent_property = "p$i";
|
||||
$this->{$parent_property} = 0;
|
||||
}
|
||||
$this->depth = 1;
|
||||
}
|
||||
// Otherwise, ensure that this link's depth is not beyond the maximum depth
|
||||
// and fill parents based on the parent link.
|
||||
else {
|
||||
if ($this->has_children && $this->original) {
|
||||
$limit = MENU_MAX_DEPTH - $storage_controller->findChildrenRelativeDepth($this->original) - 1;
|
||||
}
|
||||
else {
|
||||
$limit = MENU_MAX_DEPTH - 1;
|
||||
}
|
||||
if ($parent->depth > $limit) {
|
||||
return FALSE;
|
||||
}
|
||||
$this->depth = $parent->depth + 1;
|
||||
$this->setParents($parent);
|
||||
}
|
||||
|
||||
// Need to check both plid and menu_name, since plid can be 0 in any menu.
|
||||
if (isset($this->original) && ($this->plid != $this->original->plid || $this->menu_name != $this->original->menu_name)) {
|
||||
$storage_controller->moveChildren($this, $this->original);
|
||||
}
|
||||
// Find the router_path.
|
||||
if (empty($this->router_path) || empty($this->original) || (isset($this->original) && $this->original->link_path != $this->link_path)) {
|
||||
if ($this->external) {
|
||||
$this->router_path = '';
|
||||
}
|
||||
else {
|
||||
// Find the router path which will serve this path.
|
||||
$this->parts = explode('/', $this->link_path, MENU_MAX_PARTS);
|
||||
$this->router_path = _menu_find_router_path($this->link_path);
|
||||
}
|
||||
}
|
||||
// Find the route_name.
|
||||
if (!isset($this->route_name)) {
|
||||
$this->route_name = $this::findRouteName($this->link_path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
// Check the has_children status of the parent.
|
||||
$storage_controller->updateParentalStatus($this);
|
||||
|
||||
menu_cache_clear($this->menu_name);
|
||||
if (isset($this->original) && $this->menu_name != $this->original->menu_name) {
|
||||
menu_cache_clear($this->original->menu_name);
|
||||
}
|
||||
|
||||
// Now clear the cache.
|
||||
_menu_clear_page_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function findRouteName($link_path) {
|
||||
// Look up the route_name used for the given path.
|
||||
$request = Request::create('/' . $link_path);
|
||||
$request->attributes->set('system_path', $link_path);
|
||||
try {
|
||||
// Use router.dynamic instead of router, because router will call the
|
||||
// legacy router which will call hook_menu() and you will get back to
|
||||
// this method.
|
||||
$result = \Drupal::service('router.dynamic')->matchRequest($request);
|
||||
return isset($result['_route']) ? $result['_route'] : '';
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setParents(EntityInterface $parent) {
|
||||
$i = 1;
|
||||
while ($i < $this->depth) {
|
||||
$p = 'p' . $i++;
|
||||
$this->{$p} = $parent->{$p};
|
||||
}
|
||||
$p = 'p' . $i++;
|
||||
// The parent (p1 - p9) corresponding to the depth always equals the mlid.
|
||||
$this->{$p} = $this->id();
|
||||
while ($i <= MENU_MAX_DEPTH) {
|
||||
$p = 'p' . $i++;
|
||||
$this->{$p} = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function findParent(EntityStorageControllerInterface $storage_controller, array $parent_candidates = array()) {
|
||||
$parent = FALSE;
|
||||
|
||||
// This item is explicitely top-level, skip the rest of the parenting.
|
||||
if (isset($this->plid) && empty($this->plid)) {
|
||||
return $parent;
|
||||
}
|
||||
|
||||
// If we have a parent link ID, try to use that.
|
||||
$candidates = array();
|
||||
if (isset($this->plid)) {
|
||||
$candidates[] = $this->plid;
|
||||
}
|
||||
|
||||
// Else, if we have a link hierarchy try to find a valid parent in there.
|
||||
if (!empty($this->depth) && $this->depth > 1) {
|
||||
for ($depth = $this->depth - 1; $depth >= 1; $depth--) {
|
||||
$parent_property = "p$depth";
|
||||
$candidates[] = $this->$parent_property;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($candidates as $mlid) {
|
||||
if (isset($parent_candidates[$mlid])) {
|
||||
$parent = $parent_candidates[$mlid];
|
||||
}
|
||||
else {
|
||||
$parent = $storage_controller->load(array($mlid));
|
||||
$parent = reset($parent);
|
||||
}
|
||||
if ($parent) {
|
||||
return $parent;
|
||||
}
|
||||
}
|
||||
|
||||
// If everything else failed, try to derive the parent from the path
|
||||
// hierarchy. This only makes sense for links derived from menu router
|
||||
// items (ie. from hook_menu()).
|
||||
if ($this->module == 'system') {
|
||||
$parent = $storage_controller->getParentFromHierarchy($this);
|
||||
}
|
||||
|
||||
return $parent;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ function menu_link_delete_multiple(array $mlids, $force = FALSE, $prevent_repare
|
|||
else {
|
||||
$entities = $controller->load($mlids);
|
||||
}
|
||||
$controller->preventReparenting($prevent_reparenting);
|
||||
$controller->setPreventReparenting($prevent_reparenting);
|
||||
$controller->delete($entities);
|
||||
}
|
||||
|
||||
|
|
|
@ -123,63 +123,6 @@ class NodeStorageController extends DatabaseStorageControllerNG {
|
|||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::preSave().
|
||||
*/
|
||||
protected function preSave(EntityInterface $node) {
|
||||
// Before saving the node, set changed and revision times.
|
||||
$node->changed->value = REQUEST_TIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::preSaveRevision().
|
||||
*/
|
||||
protected function preSaveRevision(\stdClass $record, EntityInterface $entity) {
|
||||
if ($entity->isNewRevision()) {
|
||||
// When inserting either a new node or a new node revision, $node->log
|
||||
// must be set because {node_field_revision}.log is a text column and
|
||||
// therefore cannot have a default value. However, it might not be set at
|
||||
// this point (for example, if the user submitting a node form does not
|
||||
// have permission to create revisions), so we ensure that it is at least
|
||||
// an empty string in that case.
|
||||
// @todo Make the {node_field_revision}.log column nullable so that we
|
||||
// can remove this check.
|
||||
if (!isset($record->log)) {
|
||||
$record->log = '';
|
||||
}
|
||||
}
|
||||
elseif (isset($entity->original) && (!isset($record->log) || $record->log === '')) {
|
||||
// If we are updating an existing node without adding a new revision, we
|
||||
// need to make sure $entity->log is reset whenever it is empty.
|
||||
// Therefore, this code allows us to avoid clobbering an existing log
|
||||
// entry with an empty one.
|
||||
$record->log = $entity->original->log;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::postSave().
|
||||
*/
|
||||
public function postSave(EntityInterface $node, $update) {
|
||||
// Update the node access table for this node, but only if it is the
|
||||
// default revision. There's no need to delete existing records if the node
|
||||
// is new.
|
||||
if ($node->isDefaultRevision()) {
|
||||
node_access_acquire_grants($node->getBCEntity(), $update);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::preDelete().
|
||||
*/
|
||||
public function preDelete($entities) {
|
||||
if (module_exists('search')) {
|
||||
foreach ($entities as $id => $entity) {
|
||||
search_reindex($entity->nid->value, 'node');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::postDelete().
|
||||
*/
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\node\Plugin\Core\Entity;
|
||||
|
||||
use Drupal\Core\Entity\EntityNG;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\node\NodeInterface;
|
||||
|
@ -241,6 +242,52 @@ class Node extends EntityNG implements NodeInterface {
|
|||
return $this->get('vid')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageControllerInterface $storage_controller) {
|
||||
// Before saving the node, set changed and revision times.
|
||||
$this->changed->value = REQUEST_TIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSaveRevision(EntityStorageControllerInterface $storage_controller, \stdClass $record) {
|
||||
if ($this->newRevision) {
|
||||
// When inserting either a new node or a new node revision, $node->log
|
||||
// must be set because {node_field_revision}.log is a text column and
|
||||
// therefore cannot have a default value. However, it might not be set at
|
||||
// this point (for example, if the user submitting a node form does not
|
||||
// have permission to create revisions), so we ensure that it is at least
|
||||
// an empty string in that case.
|
||||
// @todo Make the {node_field_revision}.log column nullable so that we
|
||||
// can remove this check.
|
||||
if (!isset($record->log)) {
|
||||
$record->log = '';
|
||||
}
|
||||
}
|
||||
elseif (isset($this->original) && (!isset($record->log) || $record->log === '')) {
|
||||
// If we are updating an existing node without adding a new revision, we
|
||||
// need to make sure $entity->log is reset whenever it is empty.
|
||||
// Therefore, this code allows us to avoid clobbering an existing log
|
||||
// entry with an empty one.
|
||||
$record->log = $this->original->log;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
// Update the node access table for this node, but only if it is the
|
||||
// default revision. There's no need to delete existing records if the node
|
||||
// is new.
|
||||
if ($this->isDefaultRevision()) {
|
||||
node_access_acquire_grants($this->getBCEntity(), $update);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -252,4 +299,16 @@ class Node extends EntityNG implements NodeInterface {
|
|||
return $this->bcEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
if (module_exists('search')) {
|
||||
foreach ($entities as $id => $entity) {
|
||||
search_reindex($entity->nid->value, 'node');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node_test\NodeTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\node_test;
|
||||
|
||||
use Drupal\node\Plugin\Core\Entity\Node;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
|
||||
/**
|
||||
* Overrides the default node entity class for testing.
|
||||
*/
|
||||
class NodeTest extends Node {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageControllerInterface $storage_controller) {
|
||||
// Allow test nodes to specify their updated ('changed') time.
|
||||
}
|
||||
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\node_test\NodeTestStorageController.
|
||||
*/
|
||||
|
||||
namespace Drupal\node_test;
|
||||
|
||||
use Drupal\node\NodeStorageController;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* Provides a test storage controller for nodes.
|
||||
*/
|
||||
class NodeTestStorageController extends NodeStorageController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function preSave(EntityInterface $node) {
|
||||
// Allow test nodes to specify their updated ('changed') time.
|
||||
}
|
||||
|
||||
}
|
|
@ -186,6 +186,6 @@ function node_test_node_insert(EntityInterface $node) {
|
|||
*/
|
||||
function node_test_entity_info_alter(&$entity_info) {
|
||||
if (Drupal::state()->get('node_test.storage_controller')) {
|
||||
$entity_info['node']['controllers']['storage'] = 'Drupal\node_test\NodeTestStorageController';
|
||||
$entity_info['node']['class'] = 'Drupal\node_test\NodeTest';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ class SetDelete extends ConfirmFormBase implements ControllerInterface {
|
|||
|
||||
// Find out how many users are directly assigned to this shortcut set, and
|
||||
// make a message.
|
||||
$number = $this->database->query('SELECT COUNT(*) FROM {shortcut_set_users} WHERE set_name = :name', array(':name' => $this->shortcut->id()))->fetchField();
|
||||
$number = \Drupal::entityManager()->getStorageController('shortcut')->countAssignedUsers($shortcut);
|
||||
$info = '';
|
||||
if ($number) {
|
||||
$info .= '<p>' . format_plural($number,
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace Drupal\shortcut\Plugin\Core\Entity;
|
|||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\shortcut\ShortcutInterface;
|
||||
|
||||
/**
|
||||
|
@ -79,4 +80,65 @@ class Shortcut extends ConfigEntityBase implements ShortcutInterface {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postCreate(EntityStorageControllerInterface $storage_controller) {
|
||||
// Generate menu-compatible set name.
|
||||
if (!$this->getOriginalID()) {
|
||||
// Save a new shortcut set with links copied from the user's default set.
|
||||
$default_set = shortcut_default_set();
|
||||
// Generate a name to have no collisions with menu.
|
||||
// Size of menu_name is 32 so id could be 23 = 32 - strlen('shortcut-').
|
||||
$id = substr($this->id(), 0, 23);
|
||||
$this->set('id', $id);
|
||||
if ($default_set->id() != $id) {
|
||||
foreach ($default_set->links as $link) {
|
||||
$link = $link->createDuplicate();
|
||||
$link->enforceIsNew();
|
||||
$link->menu_name = $id;
|
||||
$link->save();
|
||||
$this->links[$link->uuid()] = $link;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageControllerInterface $storage_controller) {
|
||||
// Just store the UUIDs.
|
||||
foreach ($this->links as $uuid => $link) {
|
||||
$this->links[$uuid] = $uuid;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
foreach ($this->links as $uuid) {
|
||||
if ($menu_link = entity_load_by_uuid('menu_link', $uuid)) {
|
||||
// Do not specifically associate these links with the shortcut module,
|
||||
// since other modules may make them editable via the menu system.
|
||||
// However, we do need to specify the correct menu name.
|
||||
$menu_link->menu_name = 'shortcut-' . $this->id();
|
||||
$menu_link->plid = 0;
|
||||
$menu_link->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
foreach ($entities as $entity) {
|
||||
$storage_controller->deleteAssignedShortcutSets($entity);
|
||||
// Next, delete the menu links for this set.
|
||||
menu_delete_links('shortcut-' . $entity->id());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
namespace Drupal\shortcut;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigStorageController;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\shortcut\Plugin\Core\Entity\Shortcut;
|
||||
|
||||
/**
|
||||
* Defines a storage controller for shortcut entities.
|
||||
*/
|
||||
class ShortcutStorageController extends ConfigStorageController {
|
||||
class ShortcutStorageController extends ConfigStorageController implements ShortcutStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\config\ConfigStorageController::attachLoad().
|
||||
|
@ -30,80 +30,51 @@ class ShortcutStorageController extends ConfigStorageController {
|
|||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\config\ConfigStorageController::create().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function create(array $values) {
|
||||
$entity = parent::create($values);
|
||||
|
||||
// Generate menu-compatible set name.
|
||||
if (!$entity->getOriginalID()) {
|
||||
// Save a new shortcut set with links copied from the user's default set.
|
||||
$default_set = shortcut_default_set();
|
||||
// Generate a name to have no collisions with menu.
|
||||
// Size of menu_name is 32 so id could be 23 = 32 - strlen('shortcut-').
|
||||
$id = substr($entity->id(), 0, 23);
|
||||
$entity->set('id', $id);
|
||||
if ($default_set->id() != $id) {
|
||||
foreach ($default_set->links as $link) {
|
||||
$link = $link->createDuplicate();
|
||||
$link->enforceIsNew();
|
||||
$link->menu_name = $id;
|
||||
$link->save();
|
||||
$entity->links[$link->uuid()] = $link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $entity;
|
||||
public function deleteAssignedShortcutSets(Shortcut $entity) {
|
||||
// First, delete any user assignments for this set, so that each of these
|
||||
// users will go back to using whatever default set applies.
|
||||
db_delete('shortcut_set_users')
|
||||
->condition('set_name', $entity->id())
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\config\ConfigStorageController::preSave().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityInterface $entity) {
|
||||
// Just store the UUIDs.
|
||||
foreach ($entity->links as $uuid => $link) {
|
||||
$entity->links[$uuid] = $uuid;
|
||||
}
|
||||
|
||||
parent::preSave($entity);
|
||||
public function assignUser($shortcut_set, $account) {
|
||||
db_merge('shortcut_set_users')
|
||||
->key(array('uid' => $account->uid))
|
||||
->fields(array('set_name' => $shortcut_set->id()))
|
||||
->execute();
|
||||
drupal_static_reset('shortcut_current_displayed_set');
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\config\ConfigStorageController::postSave().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function postSave(EntityInterface $entity, $update) {
|
||||
// Process links in shortcut set.
|
||||
foreach ($entity->links as $uuid) {
|
||||
if ($menu_link = entity_load_by_uuid('menu_link', $uuid)) {
|
||||
// Do not specifically associate these links with the shortcut module,
|
||||
// since other modules may make them editable via the menu system.
|
||||
// However, we do need to specify the correct menu name.
|
||||
$menu_link->menu_name = 'shortcut-' . $entity->id();
|
||||
$menu_link->plid = 0;
|
||||
$menu_link->save();
|
||||
}
|
||||
}
|
||||
|
||||
parent::postSave($entity, $update);
|
||||
public function unassignUser($account) {
|
||||
$deleted = db_delete('shortcut_set_users')
|
||||
->condition('uid', $account->uid)
|
||||
->execute();
|
||||
return (bool) $deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\Entity\ConfigStorageController::preDelete().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function preDelete($entities) {
|
||||
foreach ($entities as $entity) {
|
||||
// First, delete any user assignments for this set, so that each of these
|
||||
// users will go back to using whatever default set applies.
|
||||
db_delete('shortcut_set_users')
|
||||
->condition('set_name', $entity->id())
|
||||
->execute();
|
||||
|
||||
// Next, delete the menu links for this set.
|
||||
menu_delete_links('shortcut-' . $entity->id());
|
||||
}
|
||||
|
||||
parent::preDelete($entities);
|
||||
public function getAssignedToUser($account) {
|
||||
$query = db_select('shortcut_set_users', 'ssu');
|
||||
$query->fields('ssu', array('set_name'));
|
||||
$query->condition('ssu.uid', $account->uid);
|
||||
return $query->execute()->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function countAssignedUsers(Shortcut $shortcut) {
|
||||
return db_query('SELECT COUNT(*) FROM {shortcut_set_users} WHERE set_name = :name', array(':name' => $shortcut->id()))->fetchField();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\shortcut\ShortcutStorageControllerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\shortcut;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\shortcut\Plugin\Core\Entity\Shortcut;
|
||||
|
||||
/**
|
||||
* Defines a common interface for shortcut entity controller classes.
|
||||
*/
|
||||
interface ShortcutStorageControllerInterface extends EntityStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Assigns a user to a particular shortcut set.
|
||||
*
|
||||
* @param \Drupal\shortcut\Plugin\Core\Entity\Shortcut $shortcut_set
|
||||
* An object representing the shortcut set.
|
||||
* @param $account
|
||||
* A user account that will be assigned to use the set.
|
||||
*/
|
||||
public function assignUser($shortcut_set, $account);
|
||||
|
||||
/**
|
||||
* Unassigns a user from any shortcut set they may have been assigned to.
|
||||
*
|
||||
* The user will go back to using whatever default set applies.
|
||||
*
|
||||
* @param $account
|
||||
* A user account that will be removed from the shortcut set assignment.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the user was previously assigned to a shortcut set and has been
|
||||
* successfully removed from it. FALSE if the user was already not assigned
|
||||
* to any set.
|
||||
*/
|
||||
public function unassignUser($account);
|
||||
|
||||
/**
|
||||
* Delete shortcut sets assigned to users.
|
||||
*
|
||||
* @param \Drupal\shortcut\Plugin\Core\Entity\Shortcut $entity
|
||||
* Delete the user assigned sets belonging to this shortcut.
|
||||
*/
|
||||
public function deleteAssignedShortcutSets(Shortcut $entity);
|
||||
|
||||
/**
|
||||
* Get the name of the set assigned to this user.
|
||||
*
|
||||
* @param \Drupal\user\Plugin\Core\Entity\User
|
||||
* The user account.
|
||||
*
|
||||
* @return string
|
||||
* The name of the shortcut set assigned to this user.
|
||||
*/
|
||||
public function getAssignedToUser($account);
|
||||
|
||||
/**
|
||||
* Get the number of users who have this set assigned to them.
|
||||
*
|
||||
* @param \Drupal\shortcut\Plugin\Core\Entity\Shortcut $shortcut
|
||||
* The shortcut to count the users assigned to.
|
||||
*
|
||||
* @return int
|
||||
* The number of users who have this set assigned to them.
|
||||
*/
|
||||
public function countAssignedUsers(Shortcut $shortcut);
|
||||
}
|
|
@ -311,11 +311,9 @@ function shortcut_set_reset_link_weights(&$shortcut_set) {
|
|||
* A user account that will be assigned to use the set.
|
||||
*/
|
||||
function shortcut_set_assign_user($shortcut_set, $account) {
|
||||
db_merge('shortcut_set_users')
|
||||
->key(array('uid' => $account->uid))
|
||||
->fields(array('set_name' => $shortcut_set->id()))
|
||||
->execute();
|
||||
drupal_static_reset('shortcut_current_displayed_set');
|
||||
Drupal::entityManager()
|
||||
->getStorageController('shortcut')
|
||||
->assignUser($shortcut_set, $account);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -332,10 +330,9 @@ function shortcut_set_assign_user($shortcut_set, $account) {
|
|||
* to any set.
|
||||
*/
|
||||
function shortcut_set_unassign_user($account) {
|
||||
$deleted = db_delete('shortcut_set_users')
|
||||
->condition('uid', $account->uid)
|
||||
->execute();
|
||||
return (bool) $deleted;
|
||||
return (bool) Drupal::entityManager()
|
||||
->getStorageController('shortcut')
|
||||
->unassignUser($account);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -362,10 +359,9 @@ function shortcut_current_displayed_set($account = NULL) {
|
|||
}
|
||||
// If none was found, try to find a shortcut set that is explicitly assigned
|
||||
// to this user.
|
||||
$query = db_select('shortcut_set_users', 'ssu');
|
||||
$query->fields('ssu', array('set_name'));
|
||||
$query->condition('ssu.uid', $account->uid);
|
||||
$shortcut_set_name = $query->execute()->fetchField();
|
||||
$shortcut_set_name = Drupal::entityManager()
|
||||
->getStorageController('shortcut')
|
||||
->getAssignedToUser($account);
|
||||
if ($shortcut_set_name) {
|
||||
$shortcut_set = shortcut_set_load($shortcut_set_name);
|
||||
}
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\ActionStorageController.
|
||||
*/
|
||||
|
||||
namespace Drupal\system;
|
||||
|
||||
use Drupal\Core\Action\ConfigurableActionInterface;
|
||||
use Drupal\Core\Config\Entity\ConfigStorageController;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* Defines the storage controller class for Action entities.
|
||||
*/
|
||||
class ActionStorageController extends ConfigStorageController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function preSave(EntityInterface $entity) {
|
||||
parent::preSave($entity);
|
||||
|
||||
$plugin = $entity->getPlugin();
|
||||
// If this plugin has any configuration, ensure that it is set.
|
||||
if ($plugin instanceof ConfigurableActionInterface) {
|
||||
$entity->set('configuration', $plugin->getConfiguration());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -10,6 +10,7 @@ namespace Drupal\system\Plugin\Core\Entity;
|
|||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\system\ActionConfigEntityInterface;
|
||||
use Drupal\Core\Action\ActionBag;
|
||||
use Drupal\Core\Action\ConfigurableActionInterface;
|
||||
|
@ -22,7 +23,7 @@ use Drupal\Core\Action\ConfigurableActionInterface;
|
|||
* label = @Translation("Action"),
|
||||
* module = "system",
|
||||
* controllers = {
|
||||
* "storage" = "Drupal\system\ActionStorageController"
|
||||
* "storage" = "Drupal\Core\Config\Entity\ConfigStorageController"
|
||||
* },
|
||||
* config_prefix = "action.action",
|
||||
* entity_keys = {
|
||||
|
@ -176,4 +177,15 @@ class Action extends ConfigEntityBase implements ActionConfigEntityInterface {
|
|||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageControllerInterface $storage_controller) {
|
||||
$plugin = $this->getPlugin();
|
||||
// If this plugin has any configuration, ensure that it is set.
|
||||
if ($plugin instanceof ConfigurableActionInterface) {
|
||||
$this->set('configuration', $plugin->getConfiguration());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Drupal\taxonomy\Plugin\Core\Entity;
|
|||
|
||||
use Drupal\Core\Entity\EntityNG;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\taxonomy\TermInterface;
|
||||
|
@ -151,4 +152,46 @@ class Term extends EntityNG implements TermInterface {
|
|||
unset($this->description);
|
||||
unset($this->parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
// See if any of the term's children are about to be become orphans.
|
||||
$orphans = array();
|
||||
foreach (array_keys($entities) as $tid) {
|
||||
if ($children = taxonomy_term_load_children($tid)) {
|
||||
foreach ($children as $child) {
|
||||
// If the term has multiple parents, we don't delete it.
|
||||
$parents = taxonomy_term_load_parents($child->id());
|
||||
// Because the parent has already been deleted, the parent count might
|
||||
// be 0.
|
||||
if (count($parents) <= 1) {
|
||||
$orphans[] = $child->id();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete term hierarchy information after looking up orphans but before
|
||||
// deleting them so that their children/parent information is consistent.
|
||||
$storage_controller->deleteTermHierarchy(array_keys($entities));
|
||||
|
||||
if (!empty($orphans)) {
|
||||
entity_delete_multiple('taxonomy_term', $orphans);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
// Only change the parents if a value is set, keep the existing values if
|
||||
// not.
|
||||
if (isset($this->parent->value)) {
|
||||
$storage_controller->deleteTermHierarchy(array($this->id()));
|
||||
$storage_controller->updateTermHierarchy($this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\taxonomy\Plugin\Core\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\taxonomy\VocabularyInterface;
|
||||
|
@ -104,4 +105,78 @@ class Vocabulary extends ConfigEntityBase implements VocabularyInterface {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
if (!$update) {
|
||||
entity_invoke_bundle_hook('create', 'taxonomy_term', $this->id());
|
||||
}
|
||||
elseif ($this->getOriginalID() != $this->id()) {
|
||||
// Reflect machine name changes in the definitions of existing 'taxonomy'
|
||||
// fields.
|
||||
$fields = field_read_fields();
|
||||
foreach ($fields as $field_name => $field) {
|
||||
$update_field = FALSE;
|
||||
if ($field['type'] == 'taxonomy_term_reference') {
|
||||
foreach ($field['settings']['allowed_values'] as $key => &$value) {
|
||||
if ($value['vocabulary'] == $this->getOriginalID()) {
|
||||
$value['vocabulary'] = $this->id();
|
||||
$update_field = TRUE;
|
||||
}
|
||||
}
|
||||
if ($update_field) {
|
||||
field_update_field($field);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update bundles.
|
||||
entity_invoke_bundle_hook('rename', 'taxonomy_term', $this->getOriginalID(), $this->id());
|
||||
}
|
||||
$storage_controller->resetCache($update ? array($this->getOriginalID()) : array());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
// Only load terms without a parent, child terms will get deleted too.
|
||||
entity_delete_multiple('taxonomy_term', $storage_controller->getToplevelTids(array_keys($entities)));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
$vocabularies = array();
|
||||
foreach ($entities as $vocabulary) {
|
||||
$vocabularies[$vocabulary->id()] = $vocabulary->id();
|
||||
}
|
||||
// Load all Taxonomy module fields and delete those which use only this
|
||||
// vocabulary.
|
||||
$taxonomy_fields = field_read_fields(array('module' => 'taxonomy'));
|
||||
foreach ($taxonomy_fields as $field_name => $taxonomy_field) {
|
||||
$modified_field = FALSE;
|
||||
// Term reference fields may reference terms from more than one
|
||||
// vocabulary.
|
||||
foreach ($taxonomy_field['settings']['allowed_values'] as $key => $allowed_value) {
|
||||
if (isset($vocabularies[$allowed_value['vocabulary']])) {
|
||||
unset($taxonomy_field['settings']['allowed_values'][$key]);
|
||||
$modified_field = TRUE;
|
||||
}
|
||||
}
|
||||
if ($modified_field) {
|
||||
if (empty($taxonomy_field['settings']['allowed_values'])) {
|
||||
field_delete_field($field_name);
|
||||
}
|
||||
else {
|
||||
// Update the field definition with the new allowed values.
|
||||
field_update_field($taxonomy_field);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Reset caches.
|
||||
$storage_controller->resetCache(array_keys($vocabularies));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use Drupal\Core\Entity\DatabaseStorageControllerNG;
|
|||
/**
|
||||
* Defines a Controller class for taxonomy terms.
|
||||
*/
|
||||
class TermStorageController extends DatabaseStorageControllerNG {
|
||||
class TermStorageController extends DatabaseStorageControllerNG implements TermStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::create().
|
||||
|
@ -43,61 +43,6 @@ class TermStorageController extends DatabaseStorageControllerNG {
|
|||
parent::buildPropertyQuery($entity_query, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::postDelete().
|
||||
*/
|
||||
protected function postDelete($entities) {
|
||||
// See if any of the term's children are about to be become orphans.
|
||||
$orphans = array();
|
||||
foreach (array_keys($entities) as $tid) {
|
||||
if ($children = taxonomy_term_load_children($tid)) {
|
||||
foreach ($children as $child) {
|
||||
// If the term has multiple parents, we don't delete it.
|
||||
$parents = taxonomy_term_load_parents($child->id());
|
||||
// Because the parent has already been deleted, the parent count might
|
||||
// be 0.
|
||||
if (count($parents) <= 1) {
|
||||
$orphans[] = $child->id();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete term hierarchy information after looking up orphans but before
|
||||
// deleting them so that their children/parent information is consistent.
|
||||
db_delete('taxonomy_term_hierarchy')
|
||||
->condition('tid', array_keys($entities))
|
||||
->execute();
|
||||
|
||||
if (!empty($orphans)) {
|
||||
entity_delete_multiple('taxonomy_term', $orphans);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::postSave().
|
||||
*/
|
||||
protected function postSave(EntityInterface $entity, $update) {
|
||||
// Only change the parents if a value is set, keep the existing values if
|
||||
// not.
|
||||
if (isset($entity->parent->value)) {
|
||||
db_delete('taxonomy_term_hierarchy')
|
||||
->condition('tid', $entity->id())
|
||||
->execute();
|
||||
|
||||
$query = db_insert('taxonomy_term_hierarchy')
|
||||
->fields(array('tid', 'parent'));
|
||||
|
||||
foreach ($entity->parent as $parent) {
|
||||
$query->values(array(
|
||||
'tid' => $entity->id(),
|
||||
'parent' => (int) $parent->value,
|
||||
));
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::resetCache().
|
||||
*/
|
||||
|
@ -167,4 +112,30 @@ class TermStorageController extends DatabaseStorageControllerNG {
|
|||
);
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteTermHierarchy($tids) {
|
||||
$this->database->delete('taxonomy_term_hierarchy')
|
||||
->condition('tid', $tids)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateTermHierarchy(EntityInterface $term) {
|
||||
$query = $this->database->insert('taxonomy_term_hierarchy')
|
||||
->fields(array('tid', 'parent'));
|
||||
|
||||
foreach ($term->parent as $parent) {
|
||||
$query->values(array(
|
||||
'tid' => $term->id(),
|
||||
'parent' => (int) $parent->value,
|
||||
));
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\TermStorageControllerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
|
||||
/**
|
||||
* Defines a common interface for taxonomy term entity controller classes.
|
||||
*/
|
||||
interface TermStorageControllerInterface extends EntityStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Removed reference to terms from term_hierarchy.
|
||||
*
|
||||
* @param array
|
||||
* Array of terms that need to be removed from hierarchy.
|
||||
*/
|
||||
public function deleteTermHierarchy($tids);
|
||||
|
||||
/**
|
||||
* Updates terms hierarchy information with the hierarchy trail of it.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $term
|
||||
* Term entity that needs to be added to term hierarchy information.
|
||||
*/
|
||||
public function updateTermHierarchy(EntityInterface $term);
|
||||
|
||||
}
|
|
@ -8,91 +8,11 @@
|
|||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigStorageController;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* Defines a controller class for taxonomy vocabularies.
|
||||
*/
|
||||
class VocabularyStorageController extends ConfigStorageController {
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Config\Entity\ConfigStorageController::postSave().
|
||||
*/
|
||||
protected function postSave(EntityInterface $entity, $update) {
|
||||
if (!$update) {
|
||||
entity_invoke_bundle_hook('create', 'taxonomy_term', $entity->id());
|
||||
}
|
||||
elseif ($entity->getOriginalID() != $entity->id()) {
|
||||
// Reflect machine name changes in the definitions of existing 'taxonomy'
|
||||
// fields.
|
||||
$fields = field_read_fields();
|
||||
foreach ($fields as $field_name => $field) {
|
||||
$update_field = FALSE;
|
||||
if ($field['type'] == 'taxonomy_term_reference') {
|
||||
foreach ($field['settings']['allowed_values'] as $key => &$value) {
|
||||
if ($value['vocabulary'] == $entity->getOriginalID()) {
|
||||
$value['vocabulary'] = $entity->id();
|
||||
$update_field = TRUE;
|
||||
}
|
||||
}
|
||||
if ($update_field) {
|
||||
field_update_field($field);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update bundles.
|
||||
entity_invoke_bundle_hook('rename', 'taxonomy_term', $entity->getOriginalID(), $entity->id());
|
||||
}
|
||||
parent::postSave($entity, $update);
|
||||
$this->resetCache($update ? array($entity->getOriginalID()) : array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Config\Entity\ConfigStorageController::preDelete().
|
||||
*/
|
||||
protected function preDelete($entities) {
|
||||
parent::preDelete($entities);
|
||||
// Only load terms without a parent, child terms will get deleted too.
|
||||
$tids = db_query('SELECT t.tid FROM {taxonomy_term_data} t INNER JOIN {taxonomy_term_hierarchy} th ON th.tid = t.tid WHERE t.vid IN (:vids) AND th.parent = 0', array(':vids' => array_keys($entities)))->fetchCol();
|
||||
entity_delete_multiple('taxonomy_term', $tids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Config\Entity\ConfigStorageController::postDelete().
|
||||
*/
|
||||
protected function postDelete($entities) {
|
||||
parent::postDelete($entities);
|
||||
|
||||
$vocabularies = array();
|
||||
foreach ($entities as $vocabulary) {
|
||||
$vocabularies[$vocabulary->id()] = $vocabulary->id();
|
||||
}
|
||||
// Load all Taxonomy module fields and delete those which use only this
|
||||
// vocabulary.
|
||||
$taxonomy_fields = field_read_fields(array('module' => 'taxonomy'));
|
||||
foreach ($taxonomy_fields as $field_name => $taxonomy_field) {
|
||||
$modified_field = FALSE;
|
||||
// Term reference fields may reference terms from more than one
|
||||
// vocabulary.
|
||||
foreach ($taxonomy_field['settings']['allowed_values'] as $key => $allowed_value) {
|
||||
if (isset($vocabularies[$allowed_value['vocabulary']])) {
|
||||
unset($taxonomy_field['settings']['allowed_values'][$key]);
|
||||
$modified_field = TRUE;
|
||||
}
|
||||
}
|
||||
if ($modified_field) {
|
||||
if (empty($taxonomy_field['settings']['allowed_values'])) {
|
||||
field_delete_field($field_name);
|
||||
}
|
||||
else {
|
||||
// Update the field definition with the new allowed values.
|
||||
field_update_field($taxonomy_field);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Reset caches.
|
||||
$this->resetCache(array_keys($vocabularies));
|
||||
}
|
||||
class VocabularyStorageController extends ConfigStorageController implements VocabularyStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Config\Entity\ConfigStorageController::resetCache().
|
||||
|
@ -104,4 +24,11 @@ class VocabularyStorageController extends ConfigStorageController {
|
|||
entity_info_cache_clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getToplevelTids($vids) {
|
||||
return db_query('SELECT t.tid FROM {taxonomy_term_data} t INNER JOIN {taxonomy_term_hierarchy} th ON th.tid = t.tid WHERE t.vid IN (:vids) AND th.parent = 0', array(':vids' => $vids))->fetchCol();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\taxonomy\VocabularyStorageControllerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\taxonomy;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
|
||||
/**
|
||||
* Defines a common interface for taxonomy vocabulary entity controller classes.
|
||||
*/
|
||||
interface VocabularyStorageControllerInterface extends EntityStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Gets top-level term IDs of vocabularies.
|
||||
*
|
||||
* @param array $vids
|
||||
* Array of vocabulary IDs.
|
||||
*
|
||||
* @return array
|
||||
* Array of top-level term IDs.
|
||||
*/
|
||||
public function getToplevelTids($vids);
|
||||
|
||||
}
|
|
@ -10,6 +10,7 @@ namespace Drupal\user\Plugin\Core\Entity;
|
|||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
|
@ -78,4 +79,23 @@ class Role extends ConfigEntityBase implements RoleInterface {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageControllerInterface $storage_controller) {
|
||||
if (!isset($this->weight) && ($roles = $storage_controller->load())) {
|
||||
// Set a role weight to make this new role last.
|
||||
$max = array_reduce($roles, function($max, $role) {
|
||||
return $max > $role->weight ? $max : $role->weight;
|
||||
});
|
||||
$this->weight = $max + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
$storage_controller->deleteRoleReferences(array_keys($entities));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
namespace Drupal\user\Plugin\Core\Entity;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\Core\Entity\EntityMalformedException;
|
||||
use Drupal\Core\Entity\EntityNG;
|
||||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
|
@ -228,6 +230,96 @@ class User extends EntityNG implements UserInterface {
|
|||
unset($this->uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
|
||||
if (!isset($values['created'])) {
|
||||
$values['created'] = REQUEST_TIME;
|
||||
}
|
||||
// Users always have the authenticated user role.
|
||||
$values['roles'][] = DRUPAL_AUTHENTICATED_RID;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageControllerInterface $storage_controller) {
|
||||
// Update the user password if it has changed.
|
||||
if ($this->isNew() || ($this->pass->value && $this->pass->value != $this->original->pass->value)) {
|
||||
// Allow alternate password hashing schemes.
|
||||
$this->pass->value = \Drupal::service('password')->hash(trim($this->pass->value));
|
||||
// Abort if the hashing failed and returned FALSE.
|
||||
if (!$this->pass->value) {
|
||||
throw new EntityMalformedException('The entity does not have a password.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->isNew()) {
|
||||
// If the password is empty, that means it was not changed, so use the
|
||||
// original password.
|
||||
if (empty($this->pass->value)) {
|
||||
$this->pass->value = $this->original->pass->value;
|
||||
}
|
||||
}
|
||||
|
||||
// Store account cancellation information.
|
||||
foreach (array('user_cancel_method', 'user_cancel_notify') as $key) {
|
||||
if (isset($this->{$key})) {
|
||||
\Drupal::service('user.data')->set('user', $this->id(), substr($key, 5), $this->{$key});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
if ($update) {
|
||||
// If the password has been changed, delete all open sessions for the
|
||||
// user and recreate the current one.
|
||||
if ($this->pass->value != $this->original->pass->value) {
|
||||
drupal_session_destroy_uid($this->id());
|
||||
if ($this->id() == $GLOBALS['user']->uid) {
|
||||
drupal_session_regenerate();
|
||||
}
|
||||
}
|
||||
|
||||
// Update user roles if changed.
|
||||
if ($this->roles->getValue() != $this->original->roles->getValue()) {
|
||||
$storage_controller->deleteUserRoles(array($this->id()));
|
||||
$storage_controller->saveRoles($this);
|
||||
}
|
||||
|
||||
// If the user was blocked, delete the user's sessions to force a logout.
|
||||
if ($this->original->status->value != $this->status->value && $this->status->value == 0) {
|
||||
drupal_session_destroy_uid($this->id());
|
||||
}
|
||||
|
||||
// Send emails after we have the new user object.
|
||||
if ($this->status->value != $this->original->status->value) {
|
||||
// The user's status is changing; conditionally send notification email.
|
||||
$op = $this->status->value == 1 ? 'status_activated' : 'status_blocked';
|
||||
_user_mail_notify($op, $this->getBCEntity());
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Save user roles.
|
||||
if (count($this->roles) > 1) {
|
||||
$storage_controller->saveRoles($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
$uids = array_keys($entities);
|
||||
\Drupal::service('user.data')->delete(NULL, $uids);
|
||||
$storage_controller->deleteUserRoles($uids);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -8,26 +8,11 @@
|
|||
namespace Drupal\user;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigStorageController;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* Controller class for user roles.
|
||||
*/
|
||||
class RoleStorageController extends ConfigStorageController {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityInterface $entity) {
|
||||
if (!isset($entity->weight) && $roles = entity_load_multiple('user_role')) {
|
||||
// Set a role weight to make this new role last.
|
||||
$max = array_reduce($roles, function($max, $entity) {
|
||||
return $max > $entity->weight ? $max : $entity->weight;
|
||||
});
|
||||
$entity->weight = $max + 1;
|
||||
}
|
||||
parent::preSave($entity);
|
||||
}
|
||||
class RoleStorageController extends ConfigStorageController implements RoleStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -43,9 +28,7 @@ class RoleStorageController extends ConfigStorageController {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function postDelete($entities) {
|
||||
$rids = array_keys($entities);
|
||||
|
||||
public function deleteRoleReferences(array $rids) {
|
||||
// Delete permission assignments.
|
||||
db_delete('role_permission')
|
||||
->condition('rid', $rids)
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\RoleStorageControllerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\user;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
|
||||
/**
|
||||
* Defines a common interface for roel entity controller classes.
|
||||
*/
|
||||
interface RoleStorageControllerInterface extends EntityStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Delete role references.
|
||||
*
|
||||
* @param array $rids
|
||||
* The list of role IDs being deleted. The storage controller should
|
||||
* remove permission and user references to this role.
|
||||
*/
|
||||
public function deleteRoleReferences(array $rids);
|
||||
}
|
|
@ -21,7 +21,7 @@ use Drupal\Core\Entity\DatabaseStorageControllerNG;
|
|||
* This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
|
||||
* required special handling for user objects.
|
||||
*/
|
||||
class UserStorageController extends DatabaseStorageControllerNG {
|
||||
class UserStorageController extends DatabaseStorageControllerNG implements UserStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Provides the password hashing service object.
|
||||
|
@ -86,10 +86,7 @@ class UserStorageController extends DatabaseStorageControllerNG {
|
|||
}
|
||||
|
||||
// Add any additional roles from the database.
|
||||
$result = db_query('SELECT rid, uid FROM {users_roles} WHERE uid IN (:uids)', array(':uids' => array_keys($queried_users)));
|
||||
foreach ($result as $record) {
|
||||
$queried_users[$record->uid]->roles[] = $record->rid;
|
||||
}
|
||||
$this->addRoles($queried_users);
|
||||
|
||||
// Call the default attachLoad() method. This will add fields and call
|
||||
// hook_user_load().
|
||||
|
@ -97,15 +94,9 @@ class UserStorageController extends DatabaseStorageControllerNG {
|
|||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::create().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function create(array $values) {
|
||||
if (!isset($values['created'])) {
|
||||
$values['created'] = REQUEST_TIME;
|
||||
}
|
||||
// Users always have the authenticated user role.
|
||||
$values['roles'][] = DRUPAL_AUTHENTICATED_RID;
|
||||
|
||||
return parent::create($values)->getBCEntity();
|
||||
}
|
||||
|
||||
|
@ -126,106 +117,38 @@ class UserStorageController extends DatabaseStorageControllerNG {
|
|||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::preSave().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function preSave(EntityInterface $entity) {
|
||||
// Update the user password if it has changed.
|
||||
if ($entity->isNew() || ($entity->pass->value && $entity->pass->value != $entity->original->pass->value)) {
|
||||
// Allow alternate password hashing schemes.
|
||||
$entity->pass->value = $this->password->hash(trim($entity->pass->value));
|
||||
// Abort if the hashing failed and returned FALSE.
|
||||
if (!$entity->pass->value) {
|
||||
throw new EntityMalformedException('The entity does not have a password.');
|
||||
public function saveRoles(EntityInterface $user) {
|
||||
$query = $this->database->insert('users_roles')->fields(array('uid', 'rid'));
|
||||
foreach ($user->roles as $role) {
|
||||
if (!in_array($role->value, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
|
||||
$query->values(array(
|
||||
'uid' => $user->id(),
|
||||
'rid' => $role->value,
|
||||
));
|
||||
}
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
if (!$entity->isNew()) {
|
||||
// If the password is empty, that means it was not changed, so use the
|
||||
// original password.
|
||||
if (empty($entity->pass->value)) {
|
||||
$entity->pass->value = $entity->original->pass->value;
|
||||
}
|
||||
}
|
||||
|
||||
// Store account cancellation information.
|
||||
foreach (array('user_cancel_method', 'user_cancel_notify') as $key) {
|
||||
if (isset($entity->{$key})) {
|
||||
$this->userData->set('user', $entity->id(), substr($key, 5), $entity->{$key});
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addRoles(array $users) {
|
||||
$result = db_query('SELECT rid, uid FROM {users_roles} WHERE uid IN (:uids)', array(':uids' => array_keys($users)));
|
||||
foreach ($result as $record) {
|
||||
$users[$record->uid]->roles[] = $record->rid;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::postSave().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function postSave(EntityInterface $entity, $update) {
|
||||
|
||||
if ($update) {
|
||||
// If the password has been changed, delete all open sessions for the
|
||||
// user and recreate the current one.
|
||||
if ($entity->pass->value != $entity->original->pass->value) {
|
||||
drupal_session_destroy_uid($entity->id());
|
||||
if ($entity->id() == $GLOBALS['user']->uid) {
|
||||
drupal_session_regenerate();
|
||||
}
|
||||
}
|
||||
|
||||
// Update user roles if changed.
|
||||
if ($entity->roles->getValue() != $entity->original->roles->getValue()) {
|
||||
db_delete('users_roles')
|
||||
->condition('uid', $entity->id())
|
||||
->execute();
|
||||
|
||||
$query = $this->database->insert('users_roles')->fields(array('uid', 'rid'));
|
||||
foreach ($entity->roles as $role) {
|
||||
if (!in_array($role->value, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
|
||||
|
||||
$query->values(array(
|
||||
'uid' => $entity->id(),
|
||||
'rid' => $role->value,
|
||||
));
|
||||
}
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
// If the user was blocked, delete the user's sessions to force a logout.
|
||||
if ($entity->original->status->value != $entity->status->value && $entity->status->value == 0) {
|
||||
drupal_session_destroy_uid($entity->id());
|
||||
}
|
||||
|
||||
// Send emails after we have the new user object.
|
||||
if ($entity->status->value != $entity->original->status->value) {
|
||||
// The user's status is changing; conditionally send notification email.
|
||||
$op = $entity->status->value == 1 ? 'status_activated' : 'status_blocked';
|
||||
_user_mail_notify($op, $entity->getBCEntity());
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Save user roles.
|
||||
if (count($entity->roles) > 1) {
|
||||
$query = $this->database->insert('users_roles')->fields(array('uid', 'rid'));
|
||||
foreach ($entity->roles as $role) {
|
||||
if (!in_array($role->value, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
|
||||
$query->values(array(
|
||||
'uid' => $entity->id(),
|
||||
'rid' => $role->value,
|
||||
));
|
||||
}
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\Core\Entity\DatabaseStorageController::postDelete().
|
||||
*/
|
||||
protected function postDelete($entities) {
|
||||
public function deleteUserRoles(array $uids) {
|
||||
$this->database->delete('users_roles')
|
||||
->condition('uid', array_keys($entities), 'IN')
|
||||
->condition('uid', $uids)
|
||||
->execute();
|
||||
$this->userData->delete(NULL, array_keys($entities));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\UserStorageControllerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\user;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
|
||||
/**
|
||||
* Defines a common interface for user entity controller classes.
|
||||
*/
|
||||
interface UserStorageControllerInterface extends EntityStorageControllerInterface {
|
||||
|
||||
/**
|
||||
* Add any roles from the storage to the user.
|
||||
*
|
||||
* @param array $users
|
||||
*/
|
||||
public function addRoles(array $users);
|
||||
|
||||
/**
|
||||
* Save the user's roles.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $user
|
||||
*/
|
||||
public function saveRoles(EntityInterface $user);
|
||||
|
||||
/**
|
||||
* Remove the roles of a user.
|
||||
*
|
||||
* @param array $uids
|
||||
*/
|
||||
public function deleteUserRoles(array $uids);
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\views\Plugin\Core\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\views\Views;
|
||||
use Drupal\views_ui\ViewUI;
|
||||
use Drupal\views\ViewStorageInterface;
|
||||
|
@ -361,4 +362,63 @@ class View extends ConfigEntityBase implements ViewStorageInterface {
|
|||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
views_invalidate_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
|
||||
// If there is no information about displays available add at least the
|
||||
// default display.
|
||||
$values += array(
|
||||
'display' => array(
|
||||
'default' => array(
|
||||
'display_plugin' => 'default',
|
||||
'id' => 'default',
|
||||
'display_title' => 'Master',
|
||||
'position' => 0,
|
||||
'display_options' => array(),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postCreate(EntityStorageControllerInterface $storage_controller) {
|
||||
$this->mergeDefaultDisplaysOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mergeDefaultDisplaysOptions() {
|
||||
$displays = array();
|
||||
foreach ($this->get('display') as $key => $options) {
|
||||
$options += array(
|
||||
'display_options' => array(),
|
||||
'display_plugin' => NULL,
|
||||
'id' => NULL,
|
||||
'display_title' => '',
|
||||
'position' => NULL,
|
||||
);
|
||||
// Add the defaults for the display.
|
||||
$displays[$key] = $options;
|
||||
}
|
||||
// Sort the displays.
|
||||
uasort($displays, function ($display1, $display2) {
|
||||
if ($display1['position'] != $display2['position']) {
|
||||
return $display1['position'] < $display2['position'] ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
$this->set('display', $displays);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -35,72 +35,11 @@ class ViewStorageController extends ConfigStorageController {
|
|||
*/
|
||||
protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
|
||||
foreach ($queried_entities as $id => $entity) {
|
||||
$this->mergeDefaultDisplaysOptions($entity);
|
||||
$entity->mergeDefaultDisplaysOptions();
|
||||
}
|
||||
|
||||
parent::attachLoad($queried_entities, $revision_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\config\ConfigStorageController::postSave().
|
||||
*/
|
||||
protected function postSave(EntityInterface $entity, $update) {
|
||||
parent::postSave($entity, $update);
|
||||
// Clear caches.
|
||||
views_invalidate_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Drupal\config\ConfigStorageController::create().
|
||||
*/
|
||||
public function create(array $values) {
|
||||
// If there is no information about displays available add at least the
|
||||
// default display.
|
||||
$values += array(
|
||||
'display' => array(
|
||||
'default' => array(
|
||||
'display_plugin' => 'default',
|
||||
'id' => 'default',
|
||||
'display_title' => 'Master',
|
||||
'position' => 0,
|
||||
'display_options' => array(),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$entity = parent::create($values);
|
||||
|
||||
$this->mergeDefaultDisplaysOptions($entity);
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add defaults to the display options.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The view entity to attach default displays options.
|
||||
*/
|
||||
protected function mergeDefaultDisplaysOptions(EntityInterface $entity) {
|
||||
$displays = array();
|
||||
foreach ($entity->get('display') as $key => $options) {
|
||||
$options += array(
|
||||
'display_options' => array(),
|
||||
'display_plugin' => NULL,
|
||||
'id' => NULL,
|
||||
'display_title' => '',
|
||||
'position' => NULL,
|
||||
);
|
||||
// Add the defaults for the display.
|
||||
$displays[$key] = $options;
|
||||
}
|
||||
// Sort the displays.
|
||||
uasort($displays, function ($display1, $display2) {
|
||||
if ($display1['position'] != $display2['position']) {
|
||||
return $display1['position'] < $display2['position'] ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
$entity->set('display', $displays);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,4 +25,8 @@ interface ViewStorageInterface extends \IteratorAggregate, ConfigEntityInterface
|
|||
*/
|
||||
public function &getDisplay($display_id);
|
||||
|
||||
/**
|
||||
* Add defaults to the display options.
|
||||
*/
|
||||
public function mergeDefaultDisplaysOptions();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\views_ui;
|
||||
|
||||
use Drupal\views\Views;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
|
@ -1136,12 +1137,69 @@ class ViewUI implements ViewStorageInterface {
|
|||
$this->storage->onChange($property_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSave(EntityStorageControllerInterface $storage_controller) {
|
||||
$this->storage->presave($storage_controller);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
|
||||
$this->storage->postSave($storage_controller, $update);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postCreate(EntityStorageControllerInterface $storage_controller) {
|
||||
$this->storage->postCreate($storage_controller);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postLoad(EntityStorageControllerInterface $storage_controller, array $entities) {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function preSaveRevision(EntityStorageControllerInterface $storage_controller, \stdClass $record) {
|
||||
$this->storage->preSaveRevision($storage_controller, $record);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function mergeDefaultDisplaysOptions() {
|
||||
$this->storage->mergeDefaultDisplaysOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function uriRelationships() {
|
||||
return $this->storage->uriRelationships();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue