Commit d443c10c authored by catch's avatar catch

Issue #2021779 by amateescu, tim.plunkett: Decouple shortcuts from menu links.

parent a0fb280d
......@@ -534,7 +534,7 @@ function drupal_schema_get_field_value(array $info, $value) {
elseif ($info['type'] == 'float') {
$value = (float) $value;
}
else {
elseif (!is_array($value)) {
$value = (string) $value;
}
}
......
<?php
/**
* @file
* Contains \Drupal\Core\Field\Plugin\Field\FieldType\MapItem.
*/
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldItemBase;
/**
* Defines the 'map' entity field type.
*
* @FieldType(
* id = "map",
* label = @Translation("Map"),
* description = @Translation("An entity field containing a map value."),
* configurable = FALSE
* )
*/
class MapItem extends FieldItemBase {
/**
* {@inheritdoc}
*/
public function setValue($values, $notify = TRUE) {
$this->values = array();
if (!isset($values)) {
return;
}
if (!is_array($values)) {
if ($values instanceof MapItem) {
$values = $values->getValue();
}
else {
$values = unserialize($values);
}
}
$this->values = $values;
// Notify the parent of any changes.
if ($notify && isset($this->parent)) {
$this->parent->onChange($this->name);
}
}
/**
* {@inheritdoc}
*/
public function __get($name) {
if (!isset($this->values[$name])) {
$this->values[$name] = array();
}
return $this->values[$name];
}
/**
* {@inheritdoc}
*/
public function __set($name, $value) {
if (isset($value)) {
$this->values[$name] = $value;
}
else {
unset($this->values[$name]);
}
}
}
......@@ -33,4 +33,33 @@ public function finalMatch(RouteCollection $collection, Request $request) {
return $this->match('/' . $request->attributes->get('_system_path'));
}
/**
* Returns the route_name and route parameters matching a system path.
*
* @todo Find a better place for this method in
* https://drupal.org/node/2153891.
*
* @param string $link_path
* The link path to find a route name for.
*
* @return array
* Returns an array with both the route name and parameters, or an empty
* array if no route was matched.
*/
public function findRouteNameParameters($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 {
$result = \Drupal::service('router')->matchRequest($request);
$return = array();
$return[] = isset($result['_route']) ? $result['_route'] : '';
$return[] = $result['_raw_variables']->all();
return $return;
}
catch (\Exception $e) {
return array();
}
}
}
......@@ -7,16 +7,12 @@
namespace Drupal\menu_link\Entity;
use Drupal\Core\Language\Language;
use Drupal\menu_link\MenuLinkInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\Entity;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\Entity;
use Drupal\Core\Routing\UrlMatcher;
use Drupal\menu_link\MenuLinkInterface;
use Symfony\Component\Routing\Route;
/**
* Defines the menu link entity class.
......@@ -547,7 +543,7 @@ public function preSave(EntityStorageControllerInterface $storage_controller) {
}
// Find the route_name.
if (!isset($this->route_name)) {
if ($result = static::findRouteNameParameters($this->link_path)) {
if ($result = \Drupal::service('router.matcher.final_matcher')->findRouteNameParameters($this->link_path)) {
list($this->route_name, $this->route_parameters) = $result;
}
else {
......@@ -611,25 +607,6 @@ public static function postLoad(EntityStorageControllerInterface $storage_contro
}
}
/**
* {@inheritdoc}
*/
public static function findRouteNameParameters($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 {
$result = \Drupal::service('router')->matchRequest($request);
$return = array();
$return[] = isset($result['_route']) ? $result['_route'] : '';
$return[] = $result['_raw_variables']->all();
return $return;
}
catch (\Exception $e) {
return array();
}
}
/**
* {@inheritdoc}
*/
......
......@@ -55,18 +55,6 @@ public function reset();
*/
public static function buildFromRouterItem(array $item);
/**
* Returns the route_name and route parameters matching a system path.
*
* @param string $link_path
* The link path to find a route name for.
*
* @return array
* Returns an array with both the route name and parameters, or an empty
* array if no route was matched.
*/
public static function findRouteNameParameters($link_path);
/**
* Sets the p1 through p9 properties for a menu link entity being saved.
*
......
<?php
/**
* @file
* Contains Drupal\shortcut\Access\LinkAccessCheck.
*/
namespace Drupal\shortcut\Access;
use Drupal\Core\Access\StaticAccessCheckInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\HttpFoundation\Request;
/**
* Provides an access check for shortcut link delete routes.
*/
class LinkAccessCheck implements StaticAccessCheckInterface {
/**
* {@inheritdoc}
*/
public function appliesTo() {
return array('_access_shortcut_link');
}
/**
* {@inheritdoc}
*/
public function access(Route $route, Request $request, AccountInterface $account) {
$menu_link = $request->attributes->get('menu_link');
$set_name = str_replace('shortcut-', '', $menu_link['menu_name']);
if ($shortcut_set = shortcut_set_load($set_name)) {
return shortcut_set_edit_access($shortcut_set) ? static::ALLOW : static::DENY;
}
return static::DENY;
}
}
<?php
/**
* @file
* Contains \Drupal\shortcut\Controller\ShortcutController.
*/
namespace Drupal\shortcut\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\shortcut\ShortcutSetInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides route responses for taxonomy.module.
*/
class ShortcutController extends ControllerBase {
/**
* Returns a rendered edit form to create a new shortcut associated to the
* given shortcut set.
*
* @param \Drupal\shortcut\ShortcutSetInterface $shortcut_set
* The shortcut set this shortcut will be added to.
*
* @return array
* The shortcut add form.
*/
public function addForm(ShortcutSetInterface $shortcut_set) {
$shortcut = $this->entityManager()->getStorageController('shortcut')->create(array('shortcut_set' => $shortcut_set->id()));
if ($this->moduleHandler()->moduleExists('language')) {
$shortcut->langcode = language_get_default_langcode('shortcut', $shortcut_set->id());
}
return $this->entityManager()->getForm($shortcut, 'add');
}
}
......@@ -34,21 +34,22 @@ class ShortcutSetController extends ControllerBase {
*/
public function addShortcutLinkInline(ShortcutSetInterface $shortcut_set, Request $request) {
$link = $request->query->get('link');
$name = $request->query->get('name');
if (shortcut_valid_link($link)) {
$item = menu_get_item($link);
$title = ($item && $item['title']) ? $item['title'] : $link;
$link = array(
'link_title' => $title,
'link_path' => $link,
);
$this->moduleHandler()->loadInclude('shortcut', 'admin.inc');
shortcut_admin_add_link($link, $shortcut_set);
if ($shortcut_set->save() == SAVED_UPDATED) {
drupal_set_message(t('Added a shortcut for %title.', array('%title' => $link['link_title'])));
$shortcut = $this->entityManager()->getStorageController('shortcut')->create(array(
'title' => $name,
'shortcut_set' => $shortcut_set->id(),
'path' => $link,
));
try {
$shortcut->save();
drupal_set_message($this->t('Added a shortcut for %title.', array('%title' => $shortcut->label())));
}
else {
drupal_set_message(t('Unable to add a shortcut for %title.', array('%title' => $link['link_title'])));
catch (\Exception $e) {
drupal_set_message($this->t('Unable to add a shortcut for %title.', array('%title' => $shortcut->label())));
}
return $this->redirect('<front>');
}
......
<?php
/**
* @file
* Contains \Drupal\shortcut\Plugin\Core\Entity\Shortcut.
*/
namespace Drupal\shortcut\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Field\FieldDefinition;
use Drupal\Core\Routing\UrlMatcher;
use Drupal\shortcut\ShortcutInterface;
/**
* Defines the shortcut entity class.
*
* @EntityType(
* id = "shortcut",
* label = @Translation("Shortcut link"),
* module = "shortcut",
* controllers = {
* "storage" = "Drupal\Core\Entity\FieldableDatabaseStorageController",
* "access" = "Drupal\shortcut\ShortcutAccessController",
* "form" = {
* "default" = "Drupal\shortcut\ShortcutFormController",
* "add" = "Drupal\shortcut\ShortcutFormController",
* "delete" = "Drupal\shortcut\Form\ShortcutDeleteForm"
* },
* "translation" = "Drupal\content_translation\ContentTranslationController"
* },
* base_table = "shortcut",
* data_table = "shortcut_field_data",
* translatable = TRUE,
* entity_keys = {
* "id" = "id",
* "uuid" = "uuid",
* "bundle" = "shortcut_set",
* "label" = "title"
* },
* links = {
* "edit-form" = "/admin/config/user-interface/shortcut/link/{shortcut}"
* }
* )
*/
class Shortcut extends ContentEntityBase implements ShortcutInterface {
/**
* {@inheritdoc}
*/
public function getTitle() {
return $this->get('title')->value;
}
/**
* {@inheritdoc}
*/
public function setTitle($link_title) {
$this->set('title', $link_title);
return $this;
}
/**
* {@inheritdoc}
*/
public function getWeight() {
return $this->get('weight')->value;
}
/**
* {@inheritdoc}
*/
public function setWeight($weight) {
$this->set('weight', $weight);
return $this;
}
/**
* {@inheritdoc}
*/
public function getRouteName() {
return $this->get('route_name')->value;
}
/**
* {@inheritdoc}
*/
public function setRouteName($route_name) {
$this->set('route_name', $route_name);
return $this;
}
/**
* {@inheritdoc}
*/
public function getRouteParams() {
$value = $this->get('route_parameters')->getValue();
return reset($value);
}
/**
* {@inheritdoc}
*/
public function setRouteParams($route_parameters) {
$this->set('route_parameters', array('value' => $route_parameters));
return $this;
}
/**
* {@inheritdoc}
*/
public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
parent::preCreate($storage_controller, $values);
if (!isset($values['shortcut_set'])) {
$values['shortcut_set'] = 'default';
}
}
/**
* {@inheritdoc}
*/
public function preSave(EntityStorageControllerInterface $storage_controller) {
parent::preSave($storage_controller);
if ($route_info = \Drupal::service('router.matcher.final_matcher')->findRouteNameParameters($this->path->value)) {
$this->setRouteName($route_info[0]);
$this->setRouteParams($route_info[1]);
}
}
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions($entity_type) {
$fields['id'] = FieldDefinition::create('integer')
->setLabel(t('ID'))
->setDescription(t('The ID of the shortcut.'))
->setReadOnly(TRUE);
$fields['uuid'] = FieldDefinition::create('uuid')
->setLabel(t('UUID'))
->setDescription(t('The UUID of the shortcut.'))
->setReadOnly(TRUE);
$fields['shortcut_set'] = FieldDefinition::create('entity_reference')
->setLabel(t('Shortcut set'))
->setDescription(t('The bundle of the shortcut.'))
->setSetting('target_type', 'shortcut_set')
->setRequired(TRUE);
$fields['title'] = FieldDefinition::create('string')
->setLabel(t('Title'))
->setDescription(t('The name of the shortcut.'))
->setTranslatable(TRUE);
$fields['weight'] = FieldDefinition::create('integer')
->setLabel(t('Weight'))
->setDescription(t('Weight among shortcuts in the same shortcut set.'));
$fields['route_name'] = FieldDefinition::create('string')
->setLabel(t('Route name'))
->setDescription(t('The machine name of a defined Route this shortcut represents.'));
$fields['route_parameters'] = FieldDefinition::create('map')
->setLabel(t('Route parameters'))
->setDescription(t('A serialized array of route parameters of this shortcut.'));
$fields['langcode'] = FieldDefinition::create('language')
->setLabel(t('Language code'))
->setDescription(t('The language code of the shortcut.'));
$fields['default_langcode'] = FieldDefinition::create('boolean')
->setLabel(t('Default language'))
->setDescription(t('Flag to indicate whether this is the default language.'));
$fields['path'] = FieldDefinition::create('string')
->setLabel(t('Path'))
->setDescription(t('The computed shortcut path.'))
->setComputed(TRUE);
$item_definition = $fields['path']->getItemDefinition();
$item_definition->setClass('\Drupal\shortcut\ShortcutPath');
$fields['path']->setItemDefinition($item_definition);
return $fields;
}
}
......@@ -8,9 +8,8 @@
namespace Drupal\shortcut\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;
use Drupal\shortcut\ShortcutSetInterface;
/**
......@@ -65,13 +64,6 @@ class ShortcutSet extends ConfigEntityBase implements ShortcutSetInterface {
*/
public $label;
/**
* An array of menu links.
*
* @var array
*/
public $links = array();
/**
* {@inheritdoc}
*/
......@@ -82,18 +74,11 @@ public function postCreate(EntityStorageControllerInterface $storage_controller)
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;
}
foreach ($default_set->getShortcuts() as $shortcut) {
$shortcut = $shortcut->createDuplicate();
$shortcut->enforceIsNew();
$shortcut->shortcut_set->target_id = $this->id();
$shortcut->save();
}
}
}
......@@ -101,57 +86,41 @@ public function postCreate(EntityStorageControllerInterface $storage_controller)
/**
* {@inheritdoc}
*/
public static function postLoad(EntityStorageControllerInterface $storage_controller, array &$entities) {
parent::postLoad($storage_controller, $entities);
foreach ($entities as $id => $entity) {
$links = menu_load_links('shortcut-' . $id);
foreach ($links as $menu_link) {
$entity->links[$menu_link->uuid()] = $menu_link;
}
}
}
public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
parent::preDelete($storage_controller, $entities);
/**
* {@inheritdoc}
*/
public function preSave(EntityStorageControllerInterface $storage_controller) {
parent::preSave($storage_controller);
foreach ($entities as $entity) {
$storage_controller->deleteAssignedShortcutSets($entity);
// Next, delete the shortcuts for this set.
$shortcut_ids = \Drupal::entityQuery('shortcut')
->condition('shortcut_set', $entity->id(), '=')
->execute();
// Just store the UUIDs.
foreach ($this->links as $uuid => $link) {
$this->links[$uuid] = $uuid;
$controller = \Drupal::entityManager()->getStorageController('shortcut');
$entities = $controller->loadMultiple($shortcut_ids);
$controller->delete($entities);
}
}
/**
* {@inheritdoc}
*/
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
parent::postSave($storage_controller, $update);
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();
}
public function resetLinkWeights() {
$weight = -50;
foreach ($this->getShortcuts() as $shortcut) {
$shortcut->weight->value = ++$weight;
$shortcut->save();
}
return $this;
}