diff --git a/core/modules/menu/lib/Drupal/menu/Controller/MenuController.php b/core/modules/menu/lib/Drupal/menu/Controller/MenuController.php new file mode 100644 index 0000000..f707dfb --- /dev/null +++ b/core/modules/menu/lib/Drupal/menu/Controller/MenuController.php @@ -0,0 +1,101 @@ +menuLinkStorage = $menu_link_storage; + $this->entityManager = $entity_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.entity')->getStorageController('menu_link'), + $container->get('plugin.manager.entity') + ); + } + + /** + * Gets all the available menus and menu items as a JavaScript array. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The request of the page. + * + * @return \Symfony\Component\HttpFoundation\JsonResponse + * The available menu and menu items. + */ + public function getParentOptions(Request $request) { + $available_menus = array(); + if ($menus = $request->request->get('menus')) { + foreach ($menus as $menu) { + $available_menus[$menu] = $menu; + } + } + $options = _menu_get_options(menu_get_menus(), $available_menus, array('mlid' => 0)); + + return new JsonResponse($options); + } + + /** + * Provides the menu link submission form. + * + * @param \Drupal\system\MenuInterface $menu + * An entity representing a custom menu. + * + * @return array + * Returns the menu link submission form. + */ + public function addLink(MenuInterface $menu) { + // @todo Remove this when https://drupal.org/node/1981644 is in. + drupal_set_title(t('Add menu link')); + $menu_link = $this->menuLinkStorage->create(array( + 'mlid' => 0, + 'plid' => 0, + 'menu_name' => $menu->id(), + )); + return $this->entityManager->getForm($menu_link); + } + +} diff --git a/core/modules/menu/lib/Drupal/menu/MenuFormController.php b/core/modules/menu/lib/Drupal/menu/MenuFormController.php index a709d2e..a8b4ff6 100644 --- a/core/modules/menu/lib/Drupal/menu/MenuFormController.php +++ b/core/modules/menu/lib/Drupal/menu/MenuFormController.php @@ -2,14 +2,18 @@ /** * @file - * Contains Drupal\menu\MenuFormController. + * Contains \Drupal\menu\MenuFormController. */ namespace Drupal\menu; +use Drupal\Component\Utility\NestedArray; use Drupal\Core\Entity\EntityControllerInterface; use Drupal\Core\Entity\EntityFormController; +use Drupal\Core\Entity\Query\QueryFactory; +use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Language\Language; +use Drupal\menu_link\MenuLinkStorageControllerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -18,12 +22,71 @@ class MenuFormController extends EntityFormController implements EntityControllerInterface { /** + * The module handler to invoke hooks on. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + + /** + * The factory for entity queries. + * + * @var \Drupal\Core\Entity\Query\QueryFactory + */ + protected $entityQueryFactory; + + /** + * The menu link storage controller. + * + * @var \Drupal\menu_link\MenuLinkStorageControllerInterface + */ + protected $storageController; + + /** + * The overview tree form. + * + * @var array + */ + protected $overviewTreeForm = array('#tree' => TRUE); + + /** + * Constructs a MenuFormController object. + * + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler to invoke hooks on. + * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query_factory + * The factory for entity queries. + * @param \Drupal\menu_link\MenuLinkStorageControllerInterface $storage_controller + * The menu link storage controller. + */ + public function __construct(ModuleHandlerInterface $module_handler, QueryFactory $entity_query_factory, MenuLinkStorageControllerInterface $storage_controller) { + $this->moduleHandler = $module_handler; + $this->entityQueryFactory = $entity_query_factory; + $this->storageController = $storage_controller; + } + + /** + * {@inheritdoc} + */ + public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) { + return new static( + $container->get('module_handler'), + $container->get('entity.query'), + $container->get('plugin.manager.entity')->getStorageController('menu_link') + ); + } + + /** * Overrides Drupal\Core\Entity\EntityFormController::form(). */ public function form(array $form, array &$form_state) { $menu = $this->entity; + + if ($this->operation == 'edit') { + drupal_set_title(t('Edit menu %label', array('%label' => $menu->label())), PASS_THROUGH); + } + $system_menus = menu_list_system_menus(); - $form_state['menu'] = &$menu; $form['label'] = array( '#type' => 'textfield', @@ -39,7 +102,7 @@ public function form(array $form, array &$form_state) { '#description' => t('A unique name to construct the URL for the menu. It must only contain lowercase letters, numbers and hyphens.'), '#field_prefix' => $menu->isNew() ? 'menu-' : '', '#machine_name' => array( - 'exists' => 'menu_edit_menu_name_exists', + 'exists' => array($this, 'menuNameExists'), 'source' => array('label'), 'replace_pattern' => '[^a-z0-9-]+', 'replace' => '-', @@ -83,17 +146,36 @@ public function form(array $form, array &$form_state) { // within forms, but does not allow to handle the form section's submission // equally separated yet. Therefore, we use a $form_state key to point to // the parents of the form section. - // @see menu_overview_form_submit() + // @see self::submitOverviewForm() $form_state['menu_overview_form_parents'] = array('links'); $form['links'] = array(); - $form['links'] = menu_overview_form($form['links'], $form_state); + $form['links'] = $this->buildOverviewForm($form['links'], $form_state); } return parent::form($form, $form_state); } /** - * Overrides Drupal\Core\Entity\EntityFormController::actions(). + * Returns whether a menu name already exists. + * + * @param string $value + * The name of the menu. + * + * @return bool + * Returns TRUE if the menu already exists. + * + * @see form_validate_machine_name() + */ + public function menuNameExists($value) { + $custom_exists = $this->entityQueryFactory->get('menu')->condition('id', $value)->range(0, 1)->count()->execute(); + // 'menu-' is added to the menu name to avoid name-space conflicts. + $link_exists = $this->entityQueryFactory->get('menu_link')->condition('menu_name', 'menu-' . $value)->range(0, 1)->count()->execute(); + + return $custom_exists || $link_exists; + } + + /** + * {@inheritdoc} */ protected function actions(array $form, array &$form_state) { $actions = parent::actions($form, $form_state); @@ -142,14 +224,14 @@ public function languageConfigurationSubmit(array &$form, array &$form_state) { } /** - * Overrides Drupal\Core\Entity\EntityFormController::save(). + * {@inheritdoc} */ public function save(array $form, array &$form_state) { $menu = $this->entity; $system_menus = menu_list_system_menus(); if (!$menu->isNew() || isset($system_menus[$menu->id()])) { - menu_overview_form_submit($form, $form_state); + $this->submitOverviewForm($form, $form_state); } $status = $menu->save(); @@ -168,11 +250,201 @@ public function save(array $form, array &$form_state) { } /** - * Overrides Drupal\Core\Entity\EntityFormController::delete(). + * {@inheritdoc} */ public function delete(array $form, array &$form_state) { - $menu = $this->entity; - $form_state['redirect'] = 'admin/structure/menu/manage/' . $menu->id() . '/delete'; + $form_state['redirect'] = 'admin/structure/menu/manage/' . $this->entity->id() . '/delete'; + } + + /** + * Form constructor to edit an entire menu tree at once. + * + * Shows for one menu the menu links accessible to the current user and + * relevant operations. + * + * This form constructor can be integrated as a section into another form. It + * relies on the following keys in $form_state: + * - menu: A loaded menu definition, as returned by menu_load(). + * - menu_overview_form_parents: An array containing the parent keys to this + * form. + * Forms integrating this section should call menu_overview_form_submit() from + * their form submit handler. + */ + protected function buildOverviewForm(array &$form, array &$form_state) { + global $menu_admin; + + // Ensure that menu_overview_form_submit() knows the parents of this form + // section. + $form['#tree'] = TRUE; + $form['#theme'] = 'menu_overview_form'; + $form_state += array('menu_overview_form_parents' => array()); + + $form['#attached']['css'] = array(drupal_get_path('module', 'menu') . '/css/menu.admin.css'); + + $links = array(); + $query = $this->entityQueryFactory->get('menu_link') + ->condition('menu_name', $this->entity->id()); + for ($i = 1; $i <= MENU_MAX_DEPTH; $i++) { + $query->sort('p' . $i, 'ASC'); + } + $result = $query->execute(); + + if (!empty($result)) { + $links = $this->storageController->loadMultiple($result); + } + + $delta = max(count($links), 50); + $tree = menu_tree_data($links); + $node_links = array(); + menu_tree_collect_node_links($tree, $node_links); + // We indicate that a menu administrator is running the menu access check. + $menu_admin = TRUE; + menu_tree_check_access($tree, $node_links); + $menu_admin = FALSE; + + $form = array_merge($form, $this->buildOverviewTreeForm($tree, $delta)); + $form['#empty_text'] = t('There are no menu links yet. Add link.', array('@link' => url('admin/structure/menu/manage/' . $this->entity->id() .'/add'))); + + return $form; + } + + /** + * Recursive helper function for buildOverviewForm(). + * + * @param $tree + * The menu_tree retrieved by menu_tree_data. + * @param $delta + * The default number of menu items used in the menu weight selector is 50. + * + * @return array + * The overview tree form. + */ + protected function buildOverviewTreeForm($tree, $delta) { + $form = &$this->overviewTreeForm; + foreach ($tree as $data) { + $item = $data['link']; + // Don't show callbacks; these have $item['hidden'] < 0. + if ($item && $item['hidden'] >= 0) { + $mlid = 'mlid:' . $item['mlid']; + $form[$mlid]['#item'] = $item; + $form[$mlid]['#attributes'] = $item['hidden'] ? array('class' => array('menu-disabled')) : array('class' => array('menu-enabled')); + $form[$mlid]['title']['#markup'] = l($item['title'], $item['href'], $item['localized_options']); + if ($item['hidden']) { + $form[$mlid]['title']['#markup'] .= ' (' . t('disabled') . ')'; + } + elseif ($item['link_path'] == 'user' && $item['module'] == 'system') { + $form[$mlid]['title']['#markup'] .= ' (' . t('logged in users only') . ')'; + } + + $form[$mlid]['hidden'] = array( + '#type' => 'checkbox', + '#title' => t('Enable @title menu link', array('@title' => $item['title'])), + '#title_display' => 'invisible', + '#default_value' => !$item['hidden'], + ); + $form[$mlid]['weight'] = array( + '#type' => 'weight', + '#delta' => $delta, + '#default_value' => $item['weight'], + '#title_display' => 'invisible', + '#title' => t('Weight for @title', array('@title' => $item['title'])), + ); + $form[$mlid]['mlid'] = array( + '#type' => 'hidden', + '#value' => $item['mlid'], + ); + $form[$mlid]['plid'] = array( + '#type' => 'hidden', + '#default_value' => $item['plid'], + ); + // Build a list of operations. + $operations = array(); + $links = array(); + $links['edit'] = array( + 'title' => t('Edit'), + 'href' => 'admin/structure/menu/item/' . $item['mlid'] . '/edit', + ); + $operations['edit'] = array('#type' => 'link', '#title' => t('Edit'), '#href' => 'admin/structure/menu/item/' . $item['mlid'] . '/edit'); + // Only items created by the menu module can be deleted. + if ($item['module'] == 'menu' || $item['updated'] == 1) { + $links['delete'] = array( + 'title' => t('Delete'), + 'href' => 'admin/structure/menu/item/' . $item['mlid'] . '/delete', + ); + $operations['delete'] = array('#type' => 'link', '#title' => t('Delete'), '#href' => 'admin/structure/menu/item/' . $item['mlid'] . '/delete'); + } + // Set the reset column. + elseif ($item['module'] == 'system' && $item['customized']) { + $links['reset'] = array( + 'title' => t('Reset'), + 'href' => 'admin/structure/menu/item/' . $item['mlid'] . '/reset', + ); + $operations['reset'] = array('#type' => 'link', '#title' => t('Reset'), '#href' => 'admin/structure/menu/item/' . $item['mlid'] . '/reset'); + } + $form[$mlid]['operations'] = array( + '#type' => 'operations', + '#links' => $links, + ); + } + + if ($data['below']) { + $this->buildOverviewTreeForm($data['below'], $delta); + } + } + return $form; + } + + /** + * Submit handler for the menu overview form. + * + * This function takes great care in saving parent items first, then items + * underneath them. Saving items in the incorrect order can break the menu tree. + */ + protected function submitOverviewForm(array $complete_form, array &$form_state) { + // Form API supports constructing and validating self-contained sections + // within forms, but does not allow to handle the form section's submission + // equally separated yet. Therefore, we use a $form_state key to point to + // the parents of the form section. + $parents = $form_state['menu_overview_form_parents']; + $input = NestedArray::getValue($form_state['input'], $parents); + $form = &NestedArray::getValue($complete_form, $parents); + + // When dealing with saving menu items, the order in which these items are + // saved is critical. If a changed child item is saved before its parent, + // the child item could be saved with an invalid path past its immediate + // parent. To prevent this, save items in the form in the same order they + // are sent, ensuring parents are saved first, then their children. + // See http://drupal.org/node/181126#comment-632270 + $order = is_array($input) ? array_flip(array_keys($input)) : array(); + // Update our original form with the new order. + $form = array_intersect_key(array_merge($order, $form), $form); + + $updated_items = array(); + $fields = array('weight', 'plid'); + foreach (element_children($form) as $mlid) { + if (isset($form[$mlid]['#item'])) { + $element = $form[$mlid]; + // Update any fields that have changed in this menu item. + foreach ($fields as $field) { + if ($element[$field]['#value'] != $element[$field]['#default_value']) { + $element['#item'][$field] = $element[$field]['#value']; + $updated_items[$mlid] = $element['#item']; + } + } + // Hidden is a special case, the value needs to be reversed. + if ($element['hidden']['#value'] != $element['hidden']['#default_value']) { + // Convert to integer rather than boolean due to PDO cast to string. + $element['#item']['hidden'] = $element['hidden']['#value'] ? 0 : 1; + $updated_items[$mlid] = $element['#item']; + } + } + } + + // Save all our changed items to the database. + foreach ($updated_items as $item) { + $item['customized'] = 1; + $item->save(); + } } } diff --git a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php index b914837..48e57eb 100644 --- a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php +++ b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php @@ -188,7 +188,7 @@ function deleteCustomMenu($menu) { $this->assertFalse($result, 'All menu links associated to the custom menu were deleted.'); // Make sure there's no delete button on system menus. - $this->drupalGet('admin/structure/menu/manage/main/edit'); + $this->drupalGet('admin/structure/menu/manage/main'); $this->assertNoRaw('edit-delete', 'The delete button was not found'); // Try to delete the main menu. @@ -375,7 +375,7 @@ function testSystemMenuRename() { $edit = array( 'label' => $this->randomName(16), ); - $this->drupalPost('admin/structure/menu/manage/main/edit', $edit, t('Save')); + $this->drupalPost('admin/structure/menu/manage/main', $edit, t('Save')); // Make sure menu shows up with new name in block addition. $default_theme = variable_get('theme_default', 'stark'); diff --git a/core/modules/menu/menu.admin.inc b/core/modules/menu/menu.admin.inc index 7346712..2fe9455 100644 --- a/core/modules/menu/menu.admin.inc +++ b/core/modules/menu/menu.admin.inc @@ -5,254 +5,6 @@ * Administrative page callbacks for menu module. */ -use Drupal\menu_link\Plugin\Core\Entity\MenuLink; -use Drupal\system\Plugin\Core\Entity\Menu; -use Drupal\Component\Utility\NestedArray; -use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; - -/** - * Menu callback which shows an overview page of all the custom menus and their descriptions. - */ -function menu_overview_page() { - return Drupal::entityManager() - ->getListController('menu') - ->render(); -} - -/** - * Page callback: Presents the menu creation form. - * - * @return array - * A form array as expected by drupal_render(). - * - * @see menu_menu() - */ -function menu_menu_add() { - $menu = entity_create('menu', array()); - return Drupal::entityManager()->getForm($menu); -} - -/** - * Page callback: Presents the menu edit form. - * - * @param \Drupal\system\Plugin\Core\Entity\Menu $menu - * The menu to edit. - * - * @return array - * A form array as expected by drupal_render(). - * - * @see menu_menu() - */ -function menu_menu_edit(Menu $menu) { - drupal_set_title(t('Edit menu %label', array('%label' => $menu->label())), PASS_THROUGH); - return Drupal::entityManager()->getForm($menu); -} - -/** - * Form constructor to edit an entire menu tree at once. - * - * Shows for one menu the menu links accessible to the current user and - * relevant operations. - * - * This form constructor can be integrated as a section into another form. It - * relies on the following keys in $form_state: - * - menu: A loaded menu definition, as returned by menu_load(). - * - menu_overview_form_parents: An array containing the parent keys to this - * form. - * Forms integrating this section should call menu_overview_form_submit() from - * their form submit handler. - */ -function menu_overview_form($form, &$form_state) { - global $menu_admin; - - // Ensure that menu_overview_form_submit() knows the parents of this form - // section. - $form['#tree'] = TRUE; - $form['#theme'] = 'menu_overview_form'; - $form_state += array('menu_overview_form_parents' => array()); - - $form['#attached']['css'] = array(drupal_get_path('module', 'menu') . '/css/menu.admin.css'); - - $links = array(); - $query = Drupal::entityQuery('menu_link') - ->condition('menu_name', $form_state['menu']->id()); - for ($i = 1; $i <= MENU_MAX_DEPTH; $i++) { - $query->sort('p' . $i, 'ASC'); - } - $result = $query->execute(); - - if (!empty($result)) { - $links = menu_link_load_multiple($result); - } - - $delta = max(count($links), 50); - $tree = menu_tree_data($links); - $node_links = array(); - menu_tree_collect_node_links($tree, $node_links); - // We indicate that a menu administrator is running the menu access check. - $menu_admin = TRUE; - menu_tree_check_access($tree, $node_links); - $menu_admin = FALSE; - - // Inline the "Add link" action so it displays right above the table of - // links. No access check needed, since this form has the same access - // restriction as adding menu items to the menu. - $form['inline_actions'] = array( - '#prefix' => '', - ); - $form['inline_actions']['add'] = array( - '#theme' => 'menu_local_action', - '#link' => array( - 'href' => 'admin/structure/menu/manage/' . $form_state['menu']->id() . '/add', - 'title' => t('Add link'), - ), - ); - $form = array_merge($form, _menu_overview_tree_form($tree, $delta)); - $form['#empty_text'] = t('There are no menu links yet. Add link.', array('@link' => url('admin/structure/menu/manage/' . $form_state['menu']->id() .'/add'))); - - return $form; -} - -/** - * Recursive helper function for menu_overview_form(). - * - * @param $tree - * The menu_tree retrieved by menu_tree_data. - * @param $delta - * The default number of menu items used in the menu weight selector is 50. - */ -function _menu_overview_tree_form($tree, $delta = 50) { - $form = &drupal_static(__FUNCTION__, array('#tree' => TRUE)); - foreach ($tree as $data) { - $title = ''; - $item = $data['link']; - // Don't show callbacks; these have $item['hidden'] < 0. - if ($item && $item['hidden'] >= 0) { - $mlid = 'mlid:' . $item['mlid']; - $form[$mlid]['#item'] = $item; - $form[$mlid]['#attributes'] = $item['hidden'] ? array('class' => array('menu-disabled')) : array('class' => array('menu-enabled')); - $form[$mlid]['title']['#markup'] = l($item['title'], $item['href'], $item['localized_options']); - if ($item['hidden']) { - $form[$mlid]['title']['#markup'] .= ' (' . t('disabled') . ')'; - } - elseif ($item['link_path'] == 'user' && $item['module'] == 'system') { - $form[$mlid]['title']['#markup'] .= ' (' . t('logged in users only') . ')'; - } - - $form[$mlid]['hidden'] = array( - '#type' => 'checkbox', - '#title' => t('Enable @title menu link', array('@title' => $item['title'])), - '#title_display' => 'invisible', - '#default_value' => !$item['hidden'], - ); - $form[$mlid]['weight'] = array( - '#type' => 'weight', - '#delta' => $delta, - '#default_value' => $item['weight'], - '#title_display' => 'invisible', - '#title' => t('Weight for @title', array('@title' => $item['title'])), - ); - $form[$mlid]['mlid'] = array( - '#type' => 'hidden', - '#value' => $item['mlid'], - ); - $form[$mlid]['plid'] = array( - '#type' => 'hidden', - '#default_value' => $item['plid'], - ); - // Build a list of operations. - $operations = array(); - $links = array(); - $links['edit'] = array( - 'title' => t('Edit'), - 'href' => 'admin/structure/menu/item/' . $item['mlid'] . '/edit', - ); - $operations['edit'] = array('#type' => 'link', '#title' => t('Edit'), '#href' => 'admin/structure/menu/item/' . $item['mlid'] . '/edit'); - // Only items created by the menu module can be deleted. - if ($item['module'] == 'menu' || $item['updated'] == 1) { - $links['delete'] = array( - 'title' => t('Delete'), - 'href' => 'admin/structure/menu/item/' . $item['mlid'] . '/delete', - ); - $operations['delete'] = array('#type' => 'link', '#title' => t('Delete'), '#href' => 'admin/structure/menu/item/' . $item['mlid'] . '/delete'); - } - // Set the reset column. - elseif ($item['module'] == 'system' && $item['customized']) { - $links['reset'] = array( - 'title' => t('Reset'), - 'href' => 'admin/structure/menu/item/' . $item['mlid'] . '/reset', - ); - $operations['reset'] = array('#type' => 'link', '#title' => t('Reset'), '#href' => 'admin/structure/menu/item/' . $item['mlid'] . '/reset'); - } - $form[$mlid]['operations'] = array( - '#type' => 'operations', - '#links' => $links, - ); - } - - if ($data['below']) { - _menu_overview_tree_form($data['below'], $delta); - } - } - return $form; -} - -/** - * Submit handler for the menu overview form. - * - * This function takes great care in saving parent items first, then items - * underneath them. Saving items in the incorrect order can break the menu tree. - * - * @see menu_overview_form() - */ -function menu_overview_form_submit($complete_form, &$form_state) { - // Form API supports constructing and validating self-contained sections - // within forms, but does not allow to handle the form section's submission - // equally separated yet. Therefore, we use a $form_state key to point to - // the parents of the form section. - $parents = $form_state['menu_overview_form_parents']; - $input = NestedArray::getValue($form_state['input'], $parents); - $form = &NestedArray::getValue($complete_form, $parents); - - // When dealing with saving menu items, the order in which these items are - // saved is critical. If a changed child item is saved before its parent, - // the child item could be saved with an invalid path past its immediate - // parent. To prevent this, save items in the form in the same order they - // are sent, ensuring parents are saved first, then their children. - // See http://drupal.org/node/181126#comment-632270 - $order = is_array($input) ? array_flip(array_keys($input)) : array(); - // Update our original form with the new order. - $form = array_intersect_key(array_merge($order, $form), $form); - - $updated_items = array(); - $fields = array('weight', 'plid'); - foreach (element_children($form) as $mlid) { - if (isset($form[$mlid]['#item'])) { - $element = $form[$mlid]; - // Update any fields that have changed in this menu item. - foreach ($fields as $field) { - if ($element[$field]['#value'] != $element[$field]['#default_value']) { - $element['#item'][$field] = $element[$field]['#value']; - $updated_items[$mlid] = $element['#item']; - } - } - // Hidden is a special case, the value needs to be reversed. - if ($element['hidden']['#value'] != $element['hidden']['#default_value']) { - // Convert to integer rather than boolean due to PDO cast to string. - $element['#item']['hidden'] = $element['hidden']['#value'] ? 0 : 1; - $updated_items[$mlid] = $element['#item']; - } - } - } - - // Save all our changed items to the database. - foreach ($updated_items as $item) { - $item['customized'] = 1; - menu_link_save($item); - } -} - /** * Returns HTML for the menu overview form into a table. * @@ -323,69 +75,3 @@ function theme_menu_overview_form($variables) { $output .= drupal_render_children($form); return $output; } - -/** - * Returns whether a menu name already exists. - * - * @see menu_edit_menu() - * @see form_validate_machine_name() - */ -function menu_edit_menu_name_exists($value) { - $custom_exists = entity_load('menu', $value); - // 'menu-' is added to the menu name to avoid name-space conflicts. - $value = 'menu-' . $value; - $link_exists = Drupal::entityQuery('menu_link')->condition('menu_name', $value)->range(0,1)->count()->execute(); - - return $custom_exists || $link_exists; -} - -/** - * Submit function for adding or editing a custom menu. - */ -function menu_edit_menu_submit($form, &$form_state) { - $menu = $form_state['values']; - $path = 'admin/structure/menu/manage/'; - if ($form['#insert']) { - // Add 'menu-' to the menu name to help avoid name-space conflicts. - $menu['id'] = 'menu-' . $menu['id']; - $system_link = entity_load_multiple_by_properties('menu_link', array('link_path' => 'admin/structure/menu', 'module' => 'system')); - $system_link = reset($system_link); - $menu_link = entity_create('menu_link', array( - 'link_title' => $menu['label'], - 'link_path' => $path . $menu['id'], - 'router_path' => $path . '%', - 'plid' => $system_link->id(), - )); - $menu_link->save(); - menu_save($menu); - } - else { - menu_save($menu); - $menu_links = entity_load_multiple_by_properties('menu_link', array('link_path' => $path . $menu['id'])); - foreach ($menu_links as $menu_link) { - $menu_link->link_title = $menu['label']; - $menu_link->save(); - } - } - drupal_set_message(t('Your configuration has been saved.')); - $form_state['redirect'] = $path . $menu['id']; -} - -/** - * Menu callback: Provides the menu link submission form. - * - * @param \Drupal\system\Plugin\Core\Entity\Menu $menu - * An entity representing a custom menu. - * - * @return - * Returns the menu link submission form. - */ -function menu_link_add(Menu $menu) { - $menu_link = entity_create('menu_link', array( - 'mlid' => 0, - 'plid' => 0, - 'menu_name' => $menu->id(), - )); - drupal_set_title(t('Add menu link')); - return Drupal::entityManager()->getForm($menu_link); -} diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module index 850feed..7f760ea 100644 --- a/core/modules/menu/menu.module +++ b/core/modules/menu/menu.module @@ -68,52 +68,32 @@ function menu_menu() { $items['admin/structure/menu'] = array( 'title' => 'Menus', 'description' => 'Add new menus to your site, edit existing menus, and rename and reorganize menu links.', - 'page callback' => 'menu_overview_page', - 'access callback' => 'user_access', - 'access arguments' => array('administer menu'), - 'file' => 'menu.admin.inc', - ); - $items['admin/structure/menu/parents'] = array( - 'title' => 'Parent menu items', - 'page callback' => 'menu_parent_options_js', - 'type' => MENU_CALLBACK, - 'access arguments' => array(TRUE), + 'route_name' => 'menu_overview_page', ); $items['admin/structure/menu/list'] = array( 'title' => 'List menus', 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items['admin/structure/menu/add'] = array( - 'title' => 'Add menu', - 'page callback' => 'menu_menu_add', - 'access arguments' => array('administer menu'), - 'type' => MENU_LOCAL_ACTION, - 'file' => 'menu.admin.inc', + 'route_name' => 'menu_menu_add', + 'type' => MENU_SIBLING_LOCAL_TASK, ); $items['admin/structure/menu/settings'] = array( 'title' => 'Settings', 'route_name' => 'menu_settings', - 'access arguments' => array('administer menu'), 'type' => MENU_LOCAL_TASK, 'weight' => 100, ); $items['admin/structure/menu/manage/%menu'] = array( 'title' => 'Edit menu', - 'page callback' => 'menu_menu_edit', - 'page arguments' => array(4), + 'route_name' => 'menu_menu_edit', 'title callback' => 'entity_page_label', 'title arguments' => array(4), - 'access arguments' => array('administer menu'), - 'file' => 'menu.admin.inc', ); - // Not officially a local action, but displayed as such in - // menu_overview_form(). $items['admin/structure/menu/manage/%menu/add'] = array( - 'title' => 'Add menu link', - 'page callback' => 'menu_link_add', - 'page arguments' => array(4), - 'access arguments' => array('administer menu'), - 'file' => 'menu.admin.inc', + 'title' => 'Add link', + 'route_name' => 'menu_link_add', + 'type' => MENU_SIBLING_LOCAL_TASK, ); $items['admin/structure/menu/manage/%menu/edit'] = array( 'title' => 'Edit menu', @@ -127,9 +107,7 @@ function menu_menu() { ); $items['admin/structure/menu/item/%menu_link/edit'] = array( 'title' => 'Edit menu link', - 'page callback' => 'entity_get_form', - 'page arguments' => array(4), - 'access arguments' => array('administer menu'), + 'route_name' => 'menu_link_edit', ); $items['admin/structure/menu/item/%menu_link/reset'] = array( 'title' => 'Reset menu link', @@ -143,6 +121,28 @@ function menu_menu() { } /** + * Implements hook_local_actions(). + */ +function menu_local_actions() { + return array( + array( + 'route_name' => 'menu_menu_add', + 'title' => t('Add menu'), + 'appears_on' => array( + 'menu_overview_page', + ), + ), + array( + 'route_name' => 'menu_link_add', + 'title' => t('Add link'), + 'appears_on' => array( + 'menu_menu_edit', + ), + ), + ); +} + +/** * Implements hook_entity_info(). */ function menu_entity_info(&$entity_info) { @@ -150,7 +150,8 @@ function menu_entity_info(&$entity_info) { $entity_info['menu']['controllers']['access'] = 'Drupal\menu\MenuAccessController'; $entity_info['menu']['uri_callback'] = 'menu_uri'; $entity_info['menu']['controllers']['form'] = array( - 'default' => 'Drupal\menu\MenuFormController', + 'add' => 'Drupal\menu\MenuFormController', + 'edit' => 'Drupal\menu\MenuFormController', 'delete' => 'Drupal\menu\Form\MenuDeleteForm', ); @@ -211,7 +212,7 @@ function menu_enable() { $base_link = entity_create('menu_link', array( 'menu_name' => $system_link->menu_name, - 'router_path' => 'admin/structure/menu/manage/%', + 'router_path' => 'admin/structure/menu/manage/%menu', 'module' => 'menu', )); @@ -221,6 +222,7 @@ function menu_enable() { $link->plid = $system_link->id(); $link->link_title = $menu->label(); $link->link_path = 'admin/structure/menu/manage/' . $menu->id(); + $link->route_name = 'menu_menu_edit'; $query = Drupal::entityQuery('menu_link') ->condition('link_path', $link->link_path) @@ -359,23 +361,6 @@ function menu_parent_options(array $menus, MenuLink $menu_link = NULL, $type = N } /** - * Page callback. - * Get all the available menus and menu items as a JavaScript array. - */ -function menu_parent_options_js() { - $available_menus = array(); - $menus = Drupal::request()->request->get('menus'); - if (count($menus)) { - foreach ($menus as $menu) { - $available_menus[$menu] = $menu; - } - } - $options = _menu_get_options(menu_get_menus(), $available_menus, array('mlid' => 0)); - - return new JsonResponse($options); -} - -/** * Helper function to get the items of the given menu. */ function _menu_get_options($menus, $available_menus, $item) { diff --git a/core/modules/menu/menu.routing.yml b/core/modules/menu/menu.routing.yml index 62c54f2..7c3979f 100644 --- a/core/modules/menu/menu.routing.yml +++ b/core/modules/menu/menu.routing.yml @@ -5,6 +5,34 @@ menu_settings: requirements: _permission: 'administer menu' +menu_overview_page: + pattern: '/admin/structure/menu' + defaults: + _entity_list: 'menu' + requirements: + _permission: 'administer menu' + +menu_parent_options_js: + pattern: '/admin/structure/menu/parents' + defaults: + _controller: '\Drupal\menu\Controller\MenuController::getParentOptions' + requirements: + _access: 'TRUE' + +menu_link_add: + pattern: '/admin/structure/menu/manage/{menu}/add' + defaults: + _content: '\Drupal\menu\Controller\MenuController::addLink' + requirements: + _permission: 'administer menu' + +menu_link_edit: + pattern: '/admin/structure/menu/item/{menu_link}/edit' + defaults: + _entity_form: 'menu_link' + requirements: + _permission: 'administer menu' + menu_link_reset: pattern: 'admin/structure/menu/item/{menu_link}/reset' defaults: @@ -19,6 +47,20 @@ menu_link_delete: requirements: _access_menu_delete_link: 'TRUE' +menu_menu_add: + pattern: '/admin/structure/menu/add' + defaults: + _entity_form: 'menu.add' + requirements: + _permission: 'administer menu' + +menu_menu_edit: + pattern: '/admin/structure/menu/manage/{menu}' + defaults: + _entity_form: 'menu.edit' + requirements: + _permission: 'administer menu' + menu_delete_menu: pattern: 'admin/structure/menu/manage/{menu}/delete' defaults: diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php index fd1c08f..f003d9c 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php @@ -114,7 +114,6 @@ function testBreadCrumbs() { 'admin/structure/menu' => t('Menus'), ); $this->assertBreadcrumb('admin/structure/menu/manage/tools', $trail); - $this->assertBreadcrumb('admin/structure/menu/manage/tools/edit', $trail); $mlid_node_add = db_query('SELECT mlid FROM {menu_links} WHERE link_path = :href AND module = :module', array( ':href' => 'node/add',