Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 23 |
CRAP | |
0.00% |
0 / 139 |
MenuLinkManager | |
0.00% |
0 / 1 |
|
0.00% |
0 / 23 |
2256 | |
0.00% |
0 / 139 |
__construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 4 |
|||
processDefinition | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 4 |
|||
getDiscovery | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 8 |
|||
getFactory | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 5 |
|||
getDefinitions | |
0.00% |
0 / 1 |
30 | |
0.00% |
0 / 13 |
|||
rebuild | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 9 |
|||
getDefinition | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 6 |
|||
hasDefinition | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
createInstance | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
getInstance | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 4 |
|||
deleteLinksInMenu | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 11 |
|||
deleteInstance | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 11 |
|||
removeDefinition | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 6 |
|||
menuNameInUse | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
countMenuLinks | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
getParentIds | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 5 |
|||
getChildIds | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 5 |
|||
loadLinksByRoute | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 7 |
|||
addDefinition | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 10 |
|||
updateDefinition | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 8 |
|||
resetLink | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 4 |
|||
resetInstance | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 9 |
|||
resetDefinitions | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\Core\Menu\MenuLinkManager. | |
*/ | |
namespace Drupal\Core\Menu; | |
use Drupal\Component\Plugin\Exception\PluginException; | |
use Drupal\Component\Plugin\Exception\PluginNotFoundException; | |
use Drupal\Component\Utility\NestedArray; | |
use Drupal\Core\Extension\ModuleHandlerInterface; | |
use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator; | |
use Drupal\Core\Plugin\Discovery\YamlDiscovery; | |
use Drupal\Core\Plugin\Factory\ContainerFactory; | |
/** | |
* Manages discovery, instantiation, and tree building of menu link plugins. | |
* | |
* This manager finds plugins that are rendered as menu links. | |
*/ | |
class MenuLinkManager implements MenuLinkManagerInterface { | |
/** | |
* Provides some default values for the definition of all menu link plugins. | |
* | |
* @todo Decide how to keep these field definitions in sync. | |
* https://www.drupal.org/node/2302085 | |
* | |
* @var array | |
*/ | |
protected $defaults = array( | |
// (required) The name of the menu for this link. | |
'menu_name' => 'tools', | |
// (required) The name of the route this links to, unless it's external. | |
'route_name' => '', | |
// Parameters for route variables when generating a link. | |
'route_parameters' => array(), | |
// The external URL if this link has one (required if route_name is empty). | |
'url' => '', | |
// The static title for the menu link. If this came from a YAML definition | |
// or other safe source this may be a TranslatableMarkup object. | |
'title' => '', | |
// The description. If this came from a YAML definition or other safe source | |
// this may be be a TranslatableMarkup object. | |
'description' => '', | |
// The plugin ID of the parent link (or NULL for a top-level link). | |
'parent' => '', | |
// The weight of the link. | |
'weight' => 0, | |
// The default link options. | |
'options' => array(), | |
'expanded' => 0, | |
'enabled' => 1, | |
// The name of the module providing this link. | |
'provider' => '', | |
'metadata' => array(), | |
// Default class for local task implementations. | |
'class' => 'Drupal\Core\Menu\MenuLinkDefault', | |
'form_class' => 'Drupal\Core\Menu\Form\MenuLinkDefaultForm', | |
// The plugin ID. Set by the plugin system based on the top-level YAML key. | |
'id' => '', | |
); | |
/** | |
* The object that discovers plugins managed by this manager. | |
* | |
* @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface | |
*/ | |
protected $discovery; | |
/** | |
* The object that instantiates plugins managed by this manager. | |
* | |
* @var \Drupal\Component\Plugin\Factory\FactoryInterface | |
*/ | |
protected $factory; | |
/** | |
* The menu link tree storage. | |
* | |
* @var \Drupal\Core\Menu\MenuTreeStorageInterface | |
*/ | |
protected $treeStorage; | |
/** | |
* Service providing overrides for static links. | |
* | |
* @var \Drupal\Core\Menu\StaticMenuLinkOverridesInterface | |
*/ | |
protected $overrides; | |
/** | |
* The module handler. | |
* | |
* @var \Drupal\Core\Extension\ModuleHandlerInterface | |
*/ | |
protected $moduleHandler; | |
/** | |
* Constructs a \Drupal\Core\Menu\MenuLinkManager object. | |
* | |
* @param \Drupal\Core\Menu\MenuTreeStorageInterface $tree_storage | |
* The menu link tree storage. | |
* @param \Drupal\Core\Menu\StaticMenuLinkOverridesInterface $overrides | |
* The service providing overrides for static links. | |
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler | |
* The module handler. | |
*/ | |
public function __construct(MenuTreeStorageInterface $tree_storage, StaticMenuLinkOverridesInterface $overrides, ModuleHandlerInterface $module_handler) { | |
$this->treeStorage = $tree_storage; | |
$this->overrides = $overrides; | |
$this->moduleHandler = $module_handler; | |
} | |
/** | |
* Performs extra processing on plugin definitions. | |
* | |
* By default we add defaults for the type to the definition. If a type has | |
* additional processing logic, the logic can be added by replacing or | |
* extending this method. | |
* | |
* @param array $definition | |
* The definition to be processed and modified by reference. | |
* @param $plugin_id | |
* The ID of the plugin this definition is being used for. | |
*/ | |
protected function processDefinition(array &$definition, $plugin_id) { | |
$definition = NestedArray::mergeDeep($this->defaults, $definition); | |
// Typecast so NULL, no parent, will be an empty string since the parent ID | |
// should be a string. | |
$definition['parent'] = (string) $definition['parent']; | |
$definition['id'] = $plugin_id; | |
} | |
/** | |
* Gets the plugin discovery. | |
* | |
* @return \Drupal\Component\Plugin\Discovery\DiscoveryInterface | |
*/ | |
protected function getDiscovery() { | |
if (!isset($this->discovery)) { | |
$yaml_discovery = new YamlDiscovery('links.menu', $this->moduleHandler->getModuleDirectories()); | |
$yaml_discovery->addTranslatableProperty('title', 'title_context'); | |
$yaml_discovery->addTranslatableProperty('description', 'description_context'); | |
$this->discovery = new ContainerDerivativeDiscoveryDecorator($yaml_discovery); | |
} | |
return $this->discovery; | |
} | |
/** | |
* Gets the plugin factory. | |
* | |
* @return \Drupal\Component\Plugin\Factory\FactoryInterface | |
*/ | |
protected function getFactory() { | |
if (!isset($this->factory)) { | |
$this->factory = new ContainerFactory($this); | |
} | |
return $this->factory; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getDefinitions() { | |
// Since this function is called rarely, instantiate the discovery here. | |
$definitions = $this->getDiscovery()->getDefinitions(); | |
$this->moduleHandler->alter('menu_links_discovered', $definitions); | |
foreach ($definitions as $plugin_id => &$definition) { | |
$definition['id'] = $plugin_id; | |
$this->processDefinition($definition, $plugin_id); | |
} | |
// If this plugin was provided by a module that does not exist, remove the | |
// plugin definition. | |
// @todo Address what to do with an invalid plugin. | |
// https://www.drupal.org/node/2302623 | |
foreach ($definitions as $plugin_id => $plugin_definition) { | |
if (!empty($plugin_definition['provider']) && !$this->moduleHandler->moduleExists($plugin_definition['provider'])) { | |
unset($definitions[$plugin_id]); | |
} | |
} | |
return $definitions; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function rebuild() { | |
$definitions = $this->getDefinitions(); | |
// Apply overrides from config. | |
$overrides = $this->overrides->loadMultipleOverrides(array_keys($definitions)); | |
foreach ($overrides as $id => $changes) { | |
if (!empty($definitions[$id])) { | |
$definitions[$id] = $changes + $definitions[$id]; | |
} | |
} | |
$this->treeStorage->rebuild($definitions); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getDefinition($plugin_id, $exception_on_invalid = TRUE) { | |
$definition = $this->treeStorage->load($plugin_id); | |
if (empty($definition) && $exception_on_invalid) { | |
throw new PluginNotFoundException($plugin_id); | |
} | |
return $definition; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function hasDefinition($plugin_id) { | |
return (bool) $this->getDefinition($plugin_id, FALSE); | |
} | |
/** | |
* Returns a pre-configured menu link plugin instance. | |
* | |
* @param string $plugin_id | |
* The ID of the plugin being instantiated. | |
* @param array $configuration | |
* An array of configuration relevant to the plugin instance. | |
* | |
* @return \Drupal\Core\Menu\MenuLinkInterface | |
* A menu link instance. | |
* | |
* @throws \Drupal\Component\Plugin\Exception\PluginException | |
* If the instance cannot be created, such as if the ID is invalid. | |
*/ | |
public function createInstance($plugin_id, array $configuration = array()) { | |
return $this->getFactory()->createInstance($plugin_id, $configuration); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getInstance(array $options) { | |
if (isset($options['id'])) { | |
return $this->createInstance($options['id']); | |
} | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function deleteLinksInMenu($menu_name) { | |
foreach ($this->treeStorage->loadByProperties(array('menu_name' => $menu_name)) as $plugin_id => $definition) { | |
$instance = $this->createInstance($plugin_id); | |
if ($instance->isDeletable()) { | |
$this->deleteInstance($instance, TRUE); | |
} | |
elseif ($instance->isResettable()) { | |
$new_instance = $this->resetInstance($instance); | |
$affected_menus[$new_instance->getMenuName()] = $new_instance->getMenuName(); | |
} | |
} | |
} | |
/** | |
* Deletes a specific instance. | |
* | |
* @param \Drupal\Core\Menu\MenuLinkInterface $instance | |
* The plugin instance to be deleted. | |
* @param bool $persist | |
* If TRUE, calls MenuLinkInterface::deleteLink() on the instance. | |
* | |
* @throws \Drupal\Component\Plugin\Exception\PluginException | |
* If the plugin instance does not support deletion. | |
*/ | |
protected function deleteInstance(MenuLinkInterface $instance, $persist) { | |
$id = $instance->getPluginId(); | |
if ($instance->isDeletable()) { | |
if ($persist) { | |
$instance->deleteLink(); | |
} | |
} | |
else { | |
throw new PluginException("Menu link plugin with ID '$id' does not support deletion"); | |
} | |
$this->treeStorage->delete($id); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function removeDefinition($id, $persist = TRUE) { | |
$definition = $this->treeStorage->load($id); | |
// It's possible the definition has already been deleted, or doesn't exist. | |
if ($definition) { | |
$instance = $this->createInstance($id); | |
$this->deleteInstance($instance, $persist); | |
} | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function menuNameInUse($menu_name) { | |
$this->treeStorage->menuNameInUse($menu_name); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function countMenuLinks($menu_name = NULL) { | |
return $this->treeStorage->countMenuLinks($menu_name); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getParentIds($id) { | |
if ($this->getDefinition($id, FALSE)) { | |
return $this->treeStorage->getRootPathIds($id); | |
} | |
return NULL; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getChildIds($id) { | |
if ($this->getDefinition($id, FALSE)) { | |
return $this->treeStorage->getAllChildIds($id); | |
} | |
return NULL; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function loadLinksByRoute($route_name, array $route_parameters = array(), $menu_name = NULL) { | |
$instances = array(); | |
$loaded = $this->treeStorage->loadByRoute($route_name, $route_parameters, $menu_name); | |
foreach ($loaded as $plugin_id => $definition) { | |
$instances[$plugin_id] = $this->createInstance($plugin_id); | |
} | |
return $instances; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function addDefinition($id, array $definition) { | |
if ($this->treeStorage->load($id)) { | |
throw new PluginException("The menu link ID $id already exists as a plugin definition"); | |
} | |
elseif ($id === '') { | |
throw new PluginException("The menu link ID cannot be empty"); | |
} | |
// Add defaults, so there is no requirement to specify everything. | |
$this->processDefinition($definition, $id); | |
// Store the new link in the tree. | |
$this->treeStorage->save($definition); | |
return $this->createInstance($id); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function updateDefinition($id, array $new_definition_values, $persist = TRUE) { | |
$instance = $this->createInstance($id); | |
if ($instance) { | |
$new_definition_values['id'] = $id; | |
$changed_definition = $instance->updateLink($new_definition_values, $persist); | |
$this->treeStorage->save($changed_definition); | |
} | |
return $instance; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function resetLink($id) { | |
$instance = $this->createInstance($id); | |
$new_instance = $this->resetInstance($instance); | |
return $new_instance; | |
} | |
/** | |
* Resets the menu link to its default settings. | |
* | |
* @param \Drupal\Core\Menu\MenuLinkInterface $instance | |
* The menu link which should be reset. | |
* | |
* @return \Drupal\Core\Menu\MenuLinkInterface | |
* The reset menu link. | |
* | |
* @throws \Drupal\Component\Plugin\Exception\PluginException | |
* Thrown when the menu link is not resettable. | |
*/ | |
protected function resetInstance(MenuLinkInterface $instance) { | |
$id = $instance->getPluginId(); | |
if (!$instance->isResettable()) { | |
throw new PluginException("Menu link $id is not resettable"); | |
} | |
// Get the original data from disk, reset the override and re-save the menu | |
// tree for this link. | |
$definition = $this->getDefinitions()[$id]; | |
$this->overrides->deleteOverride($id); | |
$this->treeStorage->save($definition); | |
return $this->createInstance($id); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function resetDefinitions() { | |
$this->treeStorage->resetDefinitions(); | |
} | |
} |