diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/MapItem.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/MapItem.php new file mode 100644 index 0000000..1c8a1a9 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/MapItem.php @@ -0,0 +1,49 @@ + 'map', + 'label' => t('Array values'), + ); + } + return static::$propertyDefinitions; + } + +} diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkInterface.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkInterface.php index c185dc4..999385b 100644 --- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkInterface.php +++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkInterface.php @@ -10,7 +10,6 @@ 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. @@ -85,7 +84,7 @@ public function setParents(EntityInterface $parent); * - else, for system menu links (derived from hook_menu()), reparent * based on the path hierarchy. * - * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller + * @param \Drupal\menu_link\MenuLinkStorageControllerInterface $storage_controller * Storage controller object. * @param array $parent_candidates * An array of menu link entities keyed by mlid. @@ -94,5 +93,5 @@ public function setParents(EntityInterface $parent); * 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()); + public function findParent(MenuLinkStorageControllerInterface $storage_controller, array $parent_candidates = array()); } diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php index cba2e9c..ebfd73a 100644 --- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php +++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php @@ -125,6 +125,21 @@ protected function attachLoad(&$queried_entities, $load_revision = FALSE) { } /** + * {@inheritdoc} + */ + protected function mapFromStorageRecords(array $records, $load_revision = FALSE) { + $entities = parent::mapFromStorageRecords($records, $load_revision); + + foreach ($entities as &$entity) { + foreach (static::$routerItemFields as $router_field) { + $entity->offsetSet($router_field, $records[$entity->id()]->{$router_field}); + } + } + + return $entities; + } + + /** * Overrides DatabaseStorageController::save(). */ public function save(EntityInterface $entity) { @@ -135,6 +150,12 @@ public function save(EntityInterface $entity) { $transaction = $this->database->startTransaction(); try { + // Ensure we are dealing with the actual entity. + $entity = $entity->getNGEntity(); + + // Sync the changes made in the fields array to the internal values array. + $entity->updateOriginalValues(); + // Load the stored entity, if any. if (!$entity->isNew() && !isset($entity->original)) { $entity->original = entity_load_unchanged($this->entityType, $entity->id()); @@ -158,18 +179,44 @@ public function save(EntityInterface $entity) { // $entity may have additional keys left over from building a router entry. // The intersect removes the extra keys, allowing a meaningful comparison. if ($entity->isNew() || (array_intersect_key($entity->getPropertyValues(), $entity->original->getPropertyValues()) != $entity->original->getPropertyValues())) { - $return = drupal_write_record($this->entityInfo['base_table'], $entity, $this->idKey); + // Create the storage record to be saved. + $record = $this->mapToStorageRecord($entity); + $return = drupal_write_record($this->entityInfo['base_table'], $record, $this->idKey); if ($return) { if (!$entity->isNew()) { + // @todo, should a different value be returned when saving an entity + // with $isDefaultRevision = FALSE? + if (!$entity->isDefaultRevision()) { + $return = FALSE; + } + + if ($this->revisionKey) { + $record->{$this->revisionKey} = $this->saveRevision($entity); + } + if ($this->dataTable) { + $this->savePropertyData($entity); + } $this->resetCache(array($entity->id())); $entity->postSave($this, TRUE); $this->invokeFieldMethod('update', $entity); $this->invokeHook('update', $entity); + if ($this->dataTable) { + $this->invokeTranslationHooks($entity); + } } else { $return = SAVED_NEW; - $this->resetCache(); + if ($this->revisionKey) { + $record->{$this->revisionKey} = $this->saveRevision($entity); + } + $entity->{$this->idKey}->value = $record->{$this->idKey}; + if ($this->dataTable) { + $this->savePropertyData($entity); + } + + // Reset general caches, but keep caches specific to certain entities. + $this->resetCache(array()); $entity->enforceIsNew(FALSE); $entity->postSave($this, FALSE); @@ -210,6 +257,8 @@ public function getPreventReparenting() { * {@inheritdoc} */ public function loadUpdatedCustomized(array $router_paths) { + $menu_links = array(); + // @todo This doesn't really make sense anymore with EntityNG.. and EFQ got // OR condition support in the meantime, so convert this query. $query = parent::buildQuery(NULL); @@ -222,9 +271,12 @@ public function loadUpdatedCustomized(array $router_paths) { ->condition('customized', 1) ) ); - $ids = $query->execute()->fetchCol(1); - return $this->load($ids); + if ($ids = $query->execute()->fetchCol(1)) { + $menu_links = $this->load($ids); + } + + return $menu_links; } /** @@ -248,13 +300,13 @@ public function loadModuleAdminTasks() { */ public function updateParentalStatus(EntityInterface $entity, $exclude = FALSE) { // If plid == 0, there is nothing to update. - if ($entity->plid->value) { + if ($entity->plid->target_id) { // Check if at least one visible child exists in the table. $query = \Drupal::entityQuery($this->entityType); $query ->condition('menu_name', $entity->menu_name->value) ->condition('hidden', 0) - ->condition('plid', $entity->plid->value) + ->condition('plid', $entity->plid->target_id) ->count(); if ($exclude) { @@ -264,7 +316,7 @@ public function updateParentalStatus(EntityInterface $entity, $exclude = FALSE) $parent_has_children = ((bool) $query->execute()) ? 1 : 0; $this->database->update('menu_links') ->fields(array('has_children' => $parent_has_children)) - ->condition('mlid', $entity->plid->value) + ->condition('mlid', $entity->plid->target_id) ->execute(); } } @@ -382,6 +434,11 @@ public function getParentFromHierarchy(EntityInterface $entity) { * {@inheritdoc} */ public function baseFieldDefinitions() { + $properties['menu_name'] = array( + 'label' => t('Menu name'), + 'description' => t('The menu name. All links with the same menu name (such as "tools") are part of the same menu.'), + 'type' => 'string_field', + ); $properties['mlid'] = array( 'label' => t('Menu link ID'), 'description' => t('The menu link ID.'), @@ -423,7 +480,7 @@ public function baseFieldDefinitions() { $properties['options'] = array( 'label' => t('Options'), 'description' => t('A serialized array of options to be passed to the url() or l() function, such as a query string or HTML attributes.'), - 'type' => 'map', + 'type' => 'map_field', ); $properties['module'] = array( 'label' => t('Module'), @@ -522,6 +579,26 @@ public function baseFieldDefinitions() { 'description' => t('The machine name of a defined Symfony Route this menu item represents.'), 'type' => 'string_field', ); + + // @todo Most of these should probably go away. + $properties['access'] = array( + 'label' => t('(old router) Access'), + 'description' => t(''), + 'type' => 'boolean_field', + 'computed' => TRUE, + ); + $properties['in_active_trail'] = array( + 'label' => t('In active trail'), + 'description' => t(''), + 'type' => 'boolean_field', + 'computed' => TRUE, + ); + $properties['localized_options'] = array( + 'label' => t('Localized options'), + 'description' => t(''), + 'type' => 'map_field', + 'computed' => TRUE, + ); return $properties; } diff --git a/core/modules/menu_link/lib/Drupal/menu_link/Plugin/Core/Entity/MenuLink.php b/core/modules/menu_link/lib/Drupal/menu_link/Plugin/Core/Entity/MenuLink.php index 2f4d217..f7ad7ce 100644 --- a/core/modules/menu_link/lib/Drupal/menu_link/Plugin/Core/Entity/MenuLink.php +++ b/core/modules/menu_link/lib/Drupal/menu_link/Plugin/Core/Entity/MenuLink.php @@ -14,6 +14,7 @@ use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\Core\Language\Language; use Drupal\menu_link\MenuLinkInterface; +use Drupal\menu_link\MenuLinkStorageControllerInterface; use Symfony\Component\Routing\Route; use Symfony\Component\HttpFoundation\Request; @@ -280,6 +281,31 @@ class MenuLink extends EntityNG implements \ArrayAccess, MenuLinkInterface { protected $routeObject; /** + * Crap coming from the old routing system. + * + * @todo Remove when we rip out the old routing system. + * + * @var array + */ + protected $oldRouterItem = array(); + + /** + * Properties of the old routing system. + * + * @todo Remove when we rip out the old routing system. + * + * @var array + */ + protected $oldRoutingProperties = array( + 'path', 'load_functions', 'to_arg_functions', 'access_callback', + 'access_arguments', 'page_callback', 'page_arguments', 'fit', + 'number_parts', 'context', 'tab_parent', 'tab_root', 'title', + 'title_callback', 'title_arguments', 'theme_callback', 'theme_arguments', + 'type', 'description', 'description_callback', 'description_arguments', + 'position', 'include_file', 'route_name', + ); + + /** * {@inheritdoc} */ public function id() { @@ -406,7 +432,11 @@ public static function buildFromRouterItem(array $item) { * {@inheritdoc} */ public function offsetExists($offset) { - return $this->get($offset)->offsetGet(0)->__isset(); + if (in_array($offset, $this->oldRoutingProperties)) { + return isset($this->oldRouterItem[$offset]); + } + + return isset($this->{$offset}->value); // return isset($this->{$offset}); } @@ -414,7 +444,11 @@ public function offsetExists($offset) { * {@inheritdoc} */ public function offsetGet($offset) { - return $this->get($offset)->offsetGet(0)->get('value'); + if (in_array($offset, $this->oldRoutingProperties)) { + return $this->oldRouterItem[$offset]; + } + + return $this->{$offset}->value; // return $this->{$offset}; } @@ -422,7 +456,12 @@ public function offsetGet($offset) { * {@inheritdoc} */ public function offsetSet($offset, $value) { - $this->get($offset)->offsetGet(0)->set('value', $value); + if (in_array($offset, $this->oldRoutingProperties)) { + $this->oldRouterItem[$offset] = $value; + } + else { + $this->{$offset}->value = $value; + } // $this->{$offset} = $value; } @@ -430,13 +469,27 @@ public function offsetSet($offset, $value) { * {@inheritdoc} */ public function offsetUnset($offset) { - $this->get($offset)->offsetGet(0)->set('value', NULL); + if (in_array($offset, $this->oldRoutingProperties)) { + unset($this->oldRouterItem[$offset]); + } + else { + $this->{$offset}->value = NULL; + } // unset($this->{$offset}); } /** * {@inheritdoc} */ + public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) { + if (empty($values['menu_name'])) { + $values['menu_name'] = $values['bundle'] = 'tools'; + } + } + + /** + * {@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()) { @@ -446,9 +499,9 @@ public static function preDelete(EntityStorageControllerInterface $storage_contr foreach ($entities as $entity) { // Children get re-attached to the item's parent. if ($entity->has_children->value) { - $children = $storage_controller->loadByProperties(array('plid' => $entity->plid->value)); + $children = $storage_controller->loadByProperties(array('plid' => $entity->plid->target_id)); foreach ($children as $child) { - $child->plid->value = $entity->plid->value; + $child->plid->target_id = $entity->plid->target_id; $storage_controller->save($child); } } @@ -490,16 +543,16 @@ public function preSave(EntityStorageControllerInterface $storage_controller) { $parent_candidates = !empty($this->parentCandidates) ? $this->parentCandidates : array(); $parent = $this->findParent($storage_controller, $parent_candidates); if ($parent) { - $this->plid->value = $parent->id(); + $this->plid->target_id = $parent->id(); $this->menu_name->value = $parent->menu_name->value; } // If no corresponding parent link was found, move the link to the top-level. else { - $this->plid->value = 0; + $this->plid->target_id = 0; } // Directly fill parents for top-level links. - if ($this->plid->value == 0) { + if ($this->plid->target_id == 0) { $this->p1->value = $this->id(); for ($i = 2; $i <= MENU_MAX_DEPTH; $i++) { $parent_property = "p$i"; @@ -524,7 +577,7 @@ public function preSave(EntityStorageControllerInterface $storage_controller) { } // Need to check both plid and menu_name, since plid can be 0 in any menu. - if (isset($this->original) && ($this->plid->value != $this->original->plid->value || $this->menu_name->value != $this->original->menu_name->value)) { + if (isset($this->original) && ($this->plid->target_id != $this->original->plid->target_id || $this->menu_name->value != $this->original->menu_name->value)) { $storage_controller->moveChildren($this, $this->original); } // Find the router_path. @@ -552,9 +605,9 @@ public function postSave(EntityStorageControllerInterface $storage_controller, $ // 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); + menu_cache_clear($this->menu_name->value); + if (isset($this->original) && $this->menu_name->value != $this->original->menu_name->value) { + menu_cache_clear($this->original->menu_name->value); } // Now clear the cache. @@ -585,23 +638,23 @@ public static function findRouteName($link_path) { */ public function setParents(EntityInterface $parent) { $i = 1; - while ($i < $this->depth) { + while ($i < $this->depth->value) { $p = 'p' . $i++; - $this->{$p} = $parent->{$p}; + $this->{$p}->value = $parent->{$p}->value; } $p = 'p' . $i++; // The parent (p1 - p9) corresponding to the depth always equals the mlid. - $this->{$p} = $this->id(); + $this->{$p}->value = $this->id(); while ($i <= MENU_MAX_DEPTH) { $p = 'p' . $i++; - $this->{$p} = 0; + $this->{$p}->value = 0; } } /** * {@inheritdoc} */ - public function findParent(EntityStorageControllerInterface $storage_controller, array $parent_candidates = array()) { + public function findParent(MenuLinkStorageControllerInterface $storage_controller, array $parent_candidates = array()) { $parent = FALSE; // This item is explicitely top-level, skip the rest of the parenting. @@ -638,7 +691,7 @@ public function findParent(EntityStorageControllerInterface $storage_controller, // 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') { + if ($this->module->value == 'system') { $parent = $storage_controller->getParentFromHierarchy($this); }