diff --git a/core/includes/menu.inc b/core/includes/menu.inc index 94d0e57..d7ac705 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -2273,20 +2273,19 @@ function menu_secondary_local_tasks() { function menu_get_local_actions() { $links = menu_local_tasks(); $router_item = menu_get_item(); - // @todo Consider storing the results of hook_local_actions() in a static. - foreach (Drupal::moduleHandler()->invokeAll('local_actions') as $route_info) { - if (in_array($router_item['route_name'], $route_info['appears_on'])) { - $route_path = _menu_router_translate_route($route_info['route_name']); - $action_router_item = menu_get_item($route_path); - $links['actions'][$route_path] = array( - '#theme' => 'menu_local_action', - '#link' => array( - 'title' => $route_info['title'], - 'href' => $route_path, - ), - '#access' => $action_router_item['access'], - ); - } + $manager = Drupal::service('plugin.manager.system.menu_local_action'); + $local_actions = $manager->getActionsForRoute($router_item['route_name']); + foreach ($local_actions as $plugin) { + $route_path = $manager->getPath($plugin); + $action_router_item = menu_get_item($route_path); + $links['actions'][$route_path] = array( + '#theme' => 'menu_local_action', + '#link' => array( + 'title' => $manager->getTitle($plugin), + 'href' => $route_path, + ), + '#access' => $action_router_item['access'], + ); } return $links['actions']; } diff --git a/core/modules/config/tests/config_test/config_test.module b/core/modules/config/tests/config_test/config_test.module index 5367d64..41e7c62 100644 --- a/core/modules/config/tests/config_test/config_test.module +++ b/core/modules/config/tests/config_test/config_test.module @@ -58,21 +58,6 @@ function config_test_menu() { } /** - * Implements hook_local_actions() - */ -function config_test_local_actions() { - return array( - array( - 'route_name' => 'config_test_entity_add', - 'title' => t('Add test configuration'), - 'appears_on' => array( - 'config_test_list_page', - ), - ), - ); -} - -/** * Loads a ConfigTest object. * * @param string $id diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/MenuLocalAction/AddConfigTestEntityLocalAction.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/MenuLocalAction/AddConfigTestEntityLocalAction.php new file mode 100644 index 0000000..a274751 --- /dev/null +++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/MenuLocalAction/AddConfigTestEntityLocalAction.php @@ -0,0 +1,24 @@ + 'filter_format_add', - 'title' => t('Add text format'), - 'appears_on' => array( - 'filter_admin_overview', - ), - ), - ); -} - -/** * Loads a text format object from the database. * * @param $format_id diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/MenuLocalAction/AddFilterFormatLocalAction.php b/core/modules/filter/lib/Drupal/filter/Plugin/MenuLocalAction/AddFilterFormatLocalAction.php new file mode 100644 index 0000000..8d77bee --- /dev/null +++ b/core/modules/filter/lib/Drupal/filter/Plugin/MenuLocalAction/AddFilterFormatLocalAction.php @@ -0,0 +1,24 @@ + 'shortcut_set_add', - 'title' => t('Add shortcut set'), - 'appears_on' => array( - 'shortcut_set_admin', - ), - ), - ); -} diff --git a/core/modules/system/lib/Drupal/system/Annotation/MenuLocalAction.php b/core/modules/system/lib/Drupal/system/Annotation/MenuLocalAction.php new file mode 100644 index 0000000..1a09a16 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Annotation/MenuLocalAction.php @@ -0,0 +1,49 @@ +get('string_translation'), + $container->get('url_generator'), + $configuration, + $plugin_id, + $plugin_definition + ); + } + + /** + * Constructs a Drupal\system\Plugin\MenuLocalActionBase object. + * + * @param \Drupal\Core\StringTranslation\Translator\TranslatorInterface $string_translation + * A translator object for use by subclasses generating localized titles. + * @param \Symfony\Component\Routing\Generator\UrlGeneratorInterface $generator + * A URL generator object used to get the path from the route. + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin_id for the plugin instance. + * @param array $plugin_definition + * The plugin implementation definition. + */ + public function __construct(TranslatorInterface $string_translation, UrlGeneratorInterface $generator, array $configuration, $plugin_id, array $plugin_definition) { + // This is available for subclasses that need to translate + // a dynamic title. + $this->t = $string_translation; + $this->generator = $generator; + parent::__construct($configuration, $plugin_id, $plugin_definition); + } + + /** + * {@inheritdoc} + */ + public function getRouteName() { + return $this->pluginDefinition['route_name']; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + // Subclasses may pull in the request or specific attributes as parameters. + return $this->pluginDefinition['title']; + } + + /** + * {@inheritdoc} + */ + public function getPath() { + // Subclasses may set a request into the generator or + // use any desired method to generate the path. + // @todo - use the new method from https://drupal.org/node/2031353 + $path = $this->generator->generate($this->getRouteName()); + // In order to get the Drupal path the base URL has + // to be stripped off. + $base_url = $this->generator->getContext()->getBaseUrl(); + if (!empty($base_url) && strpos($path, $base_url) === 0) { + $path = substr($path, strlen($base_url)); + } + return trim($path, '/'); + } +} diff --git a/core/modules/system/lib/Drupal/system/Plugin/MenuLocalActionInterface.php b/core/modules/system/lib/Drupal/system/Plugin/MenuLocalActionInterface.php new file mode 100644 index 0000000..6c5b179 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Plugin/MenuLocalActionInterface.php @@ -0,0 +1,43 @@ + $namespaces['Drupal\system']); + parent::__construct('MenuLocalAction', $namespaces, $annotation_namespaces, 'Drupal\system\Annotation\MenuLocalAction'); + $this->controllerResolver = $controller_resolver; + $this->request = $request; + } + + /** + * Gets the title for a local action. + * + * @param \Drupal\system\Plugin\MenuLocalActionInterface $local_action + * An object to get the title from. + * + * @return string + * The title (already localized). + * + * @throws \BadMethodCallException + * If the plugin does not implement the getTitle() method. + */ + public function getTitle(MenuLocalActionInterface $local_action) { + $controller = array($local_action, 'getTitle'); + $arguments = $this->controllerResolver->getArguments($this->request, $controller); + return call_user_func_array($controller, $arguments); + } + + /** + * Gets the Drupal path for a local action. + * + * @param \Drupal\system\Plugin\MenuLocalActionInterface $local_action + * An object to get the path from. + * + * @return string + * The path. + * + * @throws \BadMethodCallException + * If the plugin does not implement the getPath() method. + */ + public function getPath(MenuLocalActionInterface $local_action) { + $controller = array($local_action, 'getPath'); + $arguments = $this->controllerResolver->getArguments($this->request, $controller); + return call_user_func_array($controller, $arguments); + } + + /** + * Finds all local actions that appear on a named route. + * + * @param string $route_name + * The route for which to find local actions. + * + * @return array + * Objects implementing MenuLocalActionInterface that appear on the route + * path. + */ + public function getActionsForRoute($route_name) { + if (!isset($this->instances[$route_name])) { + $this->instances[$route_name] = array(); + // @todo - optimize this lookup by compiling or caching. + foreach ($this->getDefinitions() as $plugin_id => $action_info) { + if (in_array($route_name, $action_info['appears_on'])) { + $plugin = $this->createInstance($plugin_id); + $this->instances[$route_name][$plugin_id] = $plugin; + } + } + } + return $this->instances[$route_name]; + } +} diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index 1e1bae3..ebdf862 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -821,31 +821,6 @@ function hook_menu() { } /** - * Define route-based local actions. - * - * Instead of using MENU_LOCAL_ACTION in hook_menu(), implement - * hook_local_actions(). - * - * @return array - * An associative array containing the following keys: - * - route_name: The machine name of the local action route. - * - title: The title of the local action. - * - appears_on: An array of route names for this action to be display on. - */ -function hook_local_actions() { - return array( - array( - 'route_name' => 'mymodule.route.action', - 'title' => t('Perform local action'), - 'appears_on' => array( - 'mymodule.other_route', - 'mymodule.other_other_route', - ), - ), - ); -} - -/** * Alter the data being saved to the {menu_router} table after hook_menu is invoked. * * This hook is invoked by menu_router_build(). The menu definitions are passed diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml index 6aefa00..a68b788 100644 --- a/core/modules/system/system.services.yml +++ b/core/modules/system/system.services.yml @@ -6,6 +6,9 @@ services: plugin.manager.system.plugin_ui: class: Drupal\system\Plugin\Type\PluginUIManager arguments: ['@container.namespaces'] + plugin.manager.system.menu_local_action: + class: Drupal\system\Plugin\Type\MenuLocalActionManager + arguments: ['@container.namespaces', '@controller_resolver', '@request'] system.manager: class: Drupal\system\SystemManager arguments: ['@module_handler', '@database'] diff --git a/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Plugin/MenuLocalAction/MenuTestLocalAction.php b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Plugin/MenuLocalAction/MenuTestLocalAction.php new file mode 100644 index 0000000..45fd22c --- /dev/null +++ b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Plugin/MenuLocalAction/MenuTestLocalAction.php @@ -0,0 +1,24 @@ + 'menu_test_local_action3', - 'title' => t('My routing action'), - 'appears_on' => array( - 'menu_test_local_action1', - ), - ), - ); -} - -/** * Implements hook_menu_local_tasks(). * * If the menu_test.settings configuration 'tasks.add' has been set, adds diff --git a/core/modules/views_ui/lib/Drupal/views_ui/Plugin/MenuLocalAction/AddViewLocalAction.php b/core/modules/views_ui/lib/Drupal/views_ui/Plugin/MenuLocalAction/AddViewLocalAction.php new file mode 100644 index 0000000..76652e9 --- /dev/null +++ b/core/modules/views_ui/lib/Drupal/views_ui/Plugin/MenuLocalAction/AddViewLocalAction.php @@ -0,0 +1,24 @@ + 'views_ui.add', - 'title' => t('Add new view'), - 'appears_on' => array( - 'views_ui.list', - ), - ), - ); -} - -/** * Implements hook_theme(). */ function views_ui_theme() {