/** * Loads the whole menu tree. */ public function expandAll($tree) { foreach ($tree as $key => $element) { if ($element->hasChildren && null !== $element->link && !$element->link instanceof InaccessibleMenuLink) { $menu_tree = \Drupal::menuTree(); $parameters = new MenuTreeParameters(); $parameters->setRoot($element->link->getPluginId())->excludeRoot()->setMaxDepth(1)->onlyEnabledLinks(); $subtree = $menu_tree->load(NULL, $parameters); if ($subtree) { $tree[$key]->subtree = $this->expandAll($subtree); } } } return $tree; }
/** * Responds to GET requests. * * Returns a menu tree for the specified menu name. * * @param int $menu_name * The machine name of the Drupal menu. * * @return \Drupal\rest\ResourceResponse * The response containing the menu tree. * * @throws \Symfony\Component\HttpKernel\Exception\HttpException */ public function get($menu_name = NULL) { if ($menu_name) { // Get menu tree resource config, set at /admin/config/services/menutree. $config = \Drupal::config('menutree_resource.services_settings'); $services_menus = $config->get('services_menus'); // Only allow a response if the menu is in config if (in_array($menu_name, array_filter(array_values($services_menus)))) { $menu_tree = \Drupal::menuTree(); $parameters = new MenuTreeParameters(); $parameters->onlyEnabledLinks(); $tree = $menu_tree->load($menu_name, $parameters); if (!empty($tree)) { $manipulators = array(array('callable' => 'menu.default_tree_manipulators:checkAccess'), array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort')); $tree = $menu_tree->transform($tree, $manipulators); $build = $menu_tree->build($tree); // Clean the menu tree so it's ready for serialisation in a resource response. $items = $this->clean_tree($build['#items']); return new ResourceResponse($items); } throw new NotFoundHttpException(t('Menu with name @menu_name was not found', array('@menu_name' => $menu_name))); } throw new NotFoundHttpException(t('Menu tree @menu_name not allowed.', array('@menu_name' => $menu_name))); } throw new HttpException(t('No menu name was provided')); }
/** * {@inheritdoc} */ public function getCurrentRouteMenuTreeParameters($menu_name) { $active_trail = $this->menuActiveTrail->getActiveTrailIds($menu_name); $parameters = new MenuTreeParameters(); $parameters->setActiveTrail($active_trail)->addExpandedParents($active_trail)->addExpandedParents($this->treeStorage->getExpanded($menu_name, $active_trail)); return $parameters; }
/** * @return array * Array containing Devel Menu links */ protected function develMenuLinks() { $parameters = new MenuTreeParameters(); $parameters->setMaxDepth(1)->onlyEnabledLinks(); $tree = $this->menuLinkTree->load('devel', $parameters); $manipulators = array(array('callable' => 'menu.default_tree_manipulators:checkAccess'), array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort')); $tree = $this->menuLinkTree->transform($tree, $manipulators); $links = array(); foreach ($tree as $item) { if ($item->access->isAllowed()) { $links[] = array('title' => $item->link->getTitle(), 'description' => $item->link->getDescription(), 'url' => $item->link->getUrlObject()->toString()); } } return $links; }
/** * {@inheritdoc} */ public function getParentSelectOptions($id = '', array $menus = NULL) { if (!isset($menus)) { $menus = $this->getMenuOptions(); } $options = array(); $depth_limit = $this->getParentDepthLimit($id); foreach ($menus as $menu_name => $menu_title) { $options[$menu_name . ':'] = '<' . $menu_title . '>'; $parameters = new MenuTreeParameters(); $parameters->setMaxDepth($depth_limit); $tree = $this->menuLinkTree->load($menu_name, $parameters); $manipulators = array(array('callable' => 'menu.default_tree_manipulators:checkAccess'), array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort')); $tree = $this->menuLinkTree->transform($tree, $manipulators); $this->parentSelectOptionsTreeWalk($tree, $menu_name, '--', $options, $id, $depth_limit); } return $options; }
/** * Tests the loadTreeData method. */ public function testLoadTree() { $this->addMenuLink('test1', ''); $this->addMenuLink('test2', 'test1'); $this->addMenuLink('test3', 'test2'); $this->addMenuLink('test4'); $this->addMenuLink('test5', 'test4'); $data = $this->treeStorage->loadTreeData('tools', new MenuTreeParameters()); $tree = $data['tree']; $this->assertEqual(count($tree['test1']['subtree']), 1); $this->assertEqual(count($tree['test1']['subtree']['test2']['subtree']), 1); $this->assertEqual(count($tree['test1']['subtree']['test2']['subtree']['test3']['subtree']), 0); $this->assertEqual(count($tree['test4']['subtree']), 1); $this->assertEqual(count($tree['test4']['subtree']['test5']['subtree']), 0); $parameters = new MenuTreeParameters(); $parameters->setActiveTrail(array('test4', 'test5')); $data = $this->treeStorage->loadTreeData('tools', $parameters); $tree = $data['tree']; $this->assertEqual(count($tree['test1']['subtree']), 1); $this->assertFalse($tree['test1']['in_active_trail']); $this->assertEqual(count($tree['test1']['subtree']['test2']['subtree']), 1); $this->assertFalse($tree['test1']['subtree']['test2']['in_active_trail']); $this->assertEqual(count($tree['test1']['subtree']['test2']['subtree']['test3']['subtree']), 0); $this->assertFalse($tree['test1']['subtree']['test2']['subtree']['test3']['in_active_trail']); $this->assertEqual(count($tree['test4']['subtree']), 1); $this->assertTrue($tree['test4']['in_active_trail']); $this->assertEqual(count($tree['test4']['subtree']['test5']['subtree']), 0); $this->assertTrue($tree['test4']['subtree']['test5']['in_active_trail']); // Add some conditions to ensure that conditions work as expected. $parameters = new MenuTreeParameters(); $parameters->addCondition('parent', 'test1'); $data = $this->treeStorage->loadTreeData('tools', $parameters); $this->assertEqual(count($data['tree']), 1); $this->assertEqual($data['tree']['test2']['definition']['id'], 'test2'); $this->assertEqual($data['tree']['test2']['subtree'], []); // Test for only enabled links. $link = $this->treeStorage->load('test3'); $link['enabled'] = FALSE; $this->treeStorage->save($link); $link = $this->treeStorage->load('test4'); $link['enabled'] = FALSE; $this->treeStorage->save($link); $link = $this->treeStorage->load('test5'); $link['enabled'] = FALSE; $this->treeStorage->save($link); $parameters = new MenuTreeParameters(); $parameters->onlyEnabledLinks(); $data = $this->treeStorage->loadTreeData('tools', $parameters); $this->assertEqual(count($data['tree']), 1); $this->assertEqual($data['tree']['test1']['definition']['id'], 'test1'); $this->assertEqual(count($data['tree']['test1']['subtree']), 1); $this->assertEqual($data['tree']['test1']['subtree']['test2']['definition']['id'], 'test2'); $this->assertEqual($data['tree']['test1']['subtree']['test2']['subtree'], []); }
/** * {@inheritdoc} */ public function loadAllChildren($id, $max_relative_depth = NULL) { $parameters = new MenuTreeParameters(); $parameters->setRoot($id)->excludeRoot()->setMaxDepth($max_relative_depth)->onlyEnabledLinks(); $links = $this->loadLinks(NULL, $parameters); foreach ($links as $id => $link) { $links[$id] = $this->prepareLink($link); } return $links; }
/** * Tests creating links with an expected tree structure. */ public function testCreateLinksInMenu() { // This creates a tree with the following structure: // - 1 // - 2 // - 3 // - 4 // - 5 // - 7 // - 6 // - 8 // With link 6 being the only external link. $links = array(1 => MenuLinkMock::create(array('id' => 'test.example1', 'route_name' => 'example1', 'title' => 'foo', 'parent' => '')), 2 => MenuLinkMock::create(array('id' => 'test.example2', 'route_name' => 'example2', 'title' => 'bar', 'parent' => 'test.example1', 'route_parameters' => array('foo' => 'bar'))), 3 => MenuLinkMock::create(array('id' => 'test.example3', 'route_name' => 'example3', 'title' => 'baz', 'parent' => 'test.example2', 'route_parameters' => array('baz' => 'qux'))), 4 => MenuLinkMock::create(array('id' => 'test.example4', 'route_name' => 'example4', 'title' => 'qux', 'parent' => 'test.example3')), 5 => MenuLinkMock::create(array('id' => 'test.example5', 'route_name' => 'example5', 'title' => 'foofoo', 'parent' => '')), 6 => MenuLinkMock::create(array('id' => 'test.example6', 'route_name' => '', 'url' => 'https://drupal.org/', 'title' => 'barbar', 'parent' => '')), 7 => MenuLinkMock::create(array('id' => 'test.example7', 'route_name' => 'example7', 'title' => 'bazbaz', 'parent' => '')), 8 => MenuLinkMock::create(array('id' => 'test.example8', 'route_name' => 'example8', 'title' => 'quxqux', 'parent' => ''))); foreach ($links as $instance) { $this->menuLinkManager->addDefinition($instance->getPluginId(), $instance->getPluginDefinition()); } $parameters = new MenuTreeParameters(); $tree = $this->linkTree->load('mock', $parameters); $count = function (array $tree) { $sum = function ($carry, MenuLinkTreeElement $item) { return $carry + $item->count(); }; return array_reduce($tree, $sum); }; $this->assertEqual($count($tree), 8); $parameters = new MenuTreeParameters(); $parameters->setRoot('test.example2'); $tree = $this->linkTree->load($instance->getMenuName(), $parameters); $top_link = reset($tree); $this->assertEqual(count($top_link->subtree), 1); $child = reset($top_link->subtree); $this->assertEqual($child->link->getPluginId(), $links[3]->getPluginId()); $height = $this->linkTree->getSubtreeHeight('test.example2'); $this->assertEqual($height, 3); }
/** * Tests the regression in https://www.drupal.org/node/2532490. */ public function testDefaultMenuTabRegression() { $this->container->get('module_installer')->install(['menu_ui', 'menu_link_content', 'toolbar', 'system']); $admin_user = $this->drupalCreateUser(['administer views', 'administer blocks', 'bypass node access', 'access user profiles', 'view all revisions', 'administer permissions', 'administer menu', 'link to any page', 'access toolbar']); $this->drupalLogin($admin_user); $edit = ['title[0][value]' => 'Menu title', 'link[0][uri]' => '/admin/foo', 'menu_parent' => 'admin:system.admin']; $this->drupalPostForm('admin/structure/menu/manage/admin/add', $edit, t('Save')); $menu_items = \Drupal::entityManager()->getStorage('menu_link_content')->getQuery()->sort('id', 'DESC')->pager(1)->execute(); $menu_item = end($menu_items); /** @var \Drupal\menu_link_content\MenuLinkContentInterface $menu_link_content */ $menu_link_content = MenuLinkContent::load($menu_item); $edit = []; $edit['label'] = $this->randomMachineName(16); $view_id = $edit['id'] = strtolower($this->randomMachineName(16)); $edit['description'] = $this->randomMachineName(16); $edit['page[create]'] = TRUE; $edit['page[path]'] = 'admin/foo'; $this->drupalPostForm('admin/structure/views/add', $edit, t('Save and edit')); $parameters = new MenuTreeParameters(); $parameters->addCondition('id', $menu_link_content->getPluginId()); $result = \Drupal::menuTree()->load('admin', $parameters); $plugin_definition = end($result)->link->getPluginDefinition(); $this->assertEqual('view.' . $view_id . '.page_1', $plugin_definition['route_name']); $this->clickLink(t('No menu')); $this->drupalPostForm(NULL, ['menu[type]' => 'default tab', 'menu[title]' => 'Menu title'], t('Apply')); $this->assertText('Default tab options'); $this->drupalPostForm(NULL, ['tab_options[type]' => 'normal', 'tab_options[title]' => 'Parent title'], t('Apply')); $this->drupalPostForm(NULL, [], t('Save')); // Assert that saving the view will not cause an exception. $this->assertResponse(200); }
/** * Tests excludeRoot(). * * @covers ::excludeRoot */ public function testExcludeRoot() { $parameters = new MenuTreeParameters(); $parameters->excludeRoot(); $this->assertEquals(1, $parameters->minDepth); }
/** * Provide the administration overview page. * * @param string $link_id * The ID of the administrative path link for which to display child links. * * @return array * A renderable array of the administration overview page. */ public function overview($link_id) { // Check for status report errors. if ($this->systemManager->checkRequirements() && $this->currentUser()->hasPermission('administer site configuration')) { drupal_set_message($this->t('One or more problems were detected with your Drupal installation. Check the <a href="@status">status report</a> for more information.', array('@status' => $this->url('system.status'))), 'error'); } // Load all menu links below it. $parameters = new MenuTreeParameters(); $parameters->setRoot($link_id)->excludeRoot()->setTopLevelOnly()->onlyEnabledLinks(); $tree = $this->menuLinkTree->load(NULL, $parameters); $manipulators = array(array('callable' => 'menu.default_tree_manipulators:checkAccess'), array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort')); $tree = $this->menuLinkTree->transform($tree, $manipulators); $tree_access_cacheability = new CacheableMetadata(); $blocks = array(); foreach ($tree as $key => $element) { $tree_access_cacheability = $tree_access_cacheability->merge(CacheableMetadata::createFromObject($element->access)); // Only render accessible links. if (!$element->access->isAllowed()) { continue; } $link = $element->link; $block['title'] = $link->getTitle(); $block['description'] = $link->getDescription(); $block['content'] = array('#theme' => 'admin_block_content', '#content' => $this->systemManager->getAdminBlock($link)); if (!empty($block['content']['#content'])) { $blocks[$key] = $block; } } if ($blocks) { ksort($blocks); $build = ['#theme' => 'admin_page', '#blocks' => $blocks]; $tree_access_cacheability->applyTo($build); return $build; } else { $build = ['#markup' => $this->t('You do not have any administrative items.')]; $tree_access_cacheability->applyTo($build); return $build; } }
/** * Generates menu links in a tree structure. */ protected function generateLinks($num_links, $menus, $title_length, $link_types, $max_depth, $max_width) { $links = array(); $menus = array_keys(array_filter($menus)); $link_types = array_keys(array_filter($link_types)); $nids = array(); for ($i = 1; $i <= $num_links; $i++) { // Pick a random menu. $menu_name = $menus[array_rand($menus)]; // Build up our link. $link_title = $this->getRandom()->word(mt_rand(2, max(2, $title_length))); $link = $this->menuLinkContentStorage->create(array('menu_name' => $menu_name, 'weight' => mt_rand(-50, 50), 'title' => $link_title, 'bundle' => 'menu_link_content', 'description' => $this->t('Description of @title.', array('@title' => $link_title)))); $link->link->options = array('devel' => TRUE); // For the first $max_width items, make first level links. if ($i <= $max_width) { $depth = 0; } else { // Otherwise, get a random parent menu depth. $depth = mt_rand(1, max(1, $max_depth - 1)); } // Get a random parent link from the proper depth. do { $parameters = new MenuTreeParameters(); $parameters->setMinDepth($depth); $parameters->setMaxDepth($depth); $tree = $this->menuLinkTree->load($menu_name, $parameters); if ($tree) { $link->parent = array_rand($tree); } $depth--; } while (!$link->parent && $depth > 0); $link_type = array_rand($link_types); switch ($link_types[$link_type]) { case 'node': // Grab a random node ID. $select = db_select('node_field_data', 'n')->fields('n', array('nid', 'title'))->condition('n.status', 1)->range(0, 1)->orderRandom(); // Don't put a node into the menu twice. if (!empty($nids[$menu_name])) { $select->condition('n.nid', $nids[$menu_name], 'NOT IN'); } $node = $select->execute()->fetchAssoc(); if (isset($node['nid'])) { $nids[$menu_name][] = $node['nid']; $link->link->uri = 'entity:node/' . $node['nid']; $link->title = $node['title']; break; } case 'external': $link->link->uri = 'http://www.example.com/'; break; case 'front': $link->link->uri = 'internal:/<front>'; break; default: $link->devel_link_type = $link_type; break; } $link->save(); $links[$link->id()] = $link->link_title; } return $links; }
/** * Provide a single block on the administration overview page. * * @param \Drupal\Core\Menu\MenuLinkInterface $instance * The menu item to be displayed. * * @return array * An array of menu items, as expected by admin-block-content.html.twig. */ public function getAdminBlock(MenuLinkInterface $instance) { $content = array(); // Only find the children of this link. $link_id = $instance->getPluginId(); $parameters = new MenuTreeParameters(); $parameters->setRoot($link_id)->excludeRoot()->setTopLevelOnly()->onlyEnabledLinks(); $tree = $this->menuTree->load(NULL, $parameters); $manipulators = array(array('callable' => 'menu.default_tree_manipulators:checkAccess'), array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort')); $tree = $this->menuTree->transform($tree, $manipulators); foreach ($tree as $key => $element) { // Only render accessible links. if (!$element->access->isAllowed()) { // @todo Bubble cacheability metadata of both accessible and // inaccessible links. Currently made impossible by the way admin // blocks are rendered. continue; } /** @var $link \Drupal\Core\Menu\MenuLinkInterface */ $link = $element->link; $content[$key]['title'] = $link->getTitle(); $content[$key]['options'] = $link->getOptions(); $content[$key]['description'] = $link->getDescription(); $content[$key]['url'] = $link->getUrlObject(); } ksort($content); return $content; }
/** * Returns all top level menu links. * * @return \Drupal\Core\Menu\MenuLinkInterface[] */ protected function getTopLevelMenuLinks() { $menu_tree = \Drupal::menuTree(); // The system.admin link is normally the parent of all top-level admin links. $parameters = new MenuTreeParameters(); $parameters->setRoot('system.admin')->excludeRoot()->setTopLevelOnly()->excludeHiddenLinks(); $tree = $menu_tree->load(NULL, $parameters); $manipulators = array(array('callable' => 'menu.default_tree_manipulators:checkAccess'), array('callable' => 'menu.default_tree_manipulators:flatten')); $tree = $menu_tree->transform($tree, $manipulators); // Transform the tree to a list of menu links. $menu_links = array(); foreach ($tree as $element) { $menu_links[] = $element->link; } return $menu_links; }
/** * {@inheritdoc} */ public function getCurrentRouteMenuTreeParameters($menu_name) { $route_parameters = $this->routeMatch->getRawParameters()->all(); ksort($route_parameters); $cid = 'current-route-parameters:' . $menu_name . ':route:' . $this->routeMatch->getRouteName() . ':route_parameters:' . serialize($route_parameters); if (!isset($this->cachedCurrentRouteParameters[$menu_name])) { $cache = $this->cache->get($cid); if ($cache && $cache->data) { $parameters = $cache->data; } else { $active_trail = $this->menuActiveTrail->getActiveTrailIds($menu_name); $parameters = new MenuTreeParameters(); $parameters->setActiveTrail($active_trail)->addExpandedParents($active_trail)->addExpandedParents($this->treeStorage->getExpanded($menu_name, $active_trail)); $this->cache->set($cid, $parameters, CacheBackendInterface::CACHE_PERMANENT, array('menu' => $menu_name)); } $this->cachedCurrentRouteParameters[$menu_name] = $parameters; } return $this->cachedCurrentRouteParameters[$menu_name]; }
/** * Provide a single block on the administration overview page. * * @param \Drupal\Core\Menu\MenuLinkInterface $instance * The menu item to be displayed. * * @return array * An array of menu items, as expected by theme_admin_block_content(). */ public function getAdminBlock(MenuLinkInterface $instance) { $content = array(); // Only find the children of this link. $link_id = $instance->getPluginId(); $parameters = new MenuTreeParameters(); $parameters->setRoot($link_id)->excludeRoot()->setTopLevelOnly()->excludeHiddenLinks(); $tree = $this->menuTree->load(NULL, $parameters); $manipulators = array(array('callable' => 'menu.default_tree_manipulators:checkAccess'), array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort')); $tree = $this->menuTree->transform($tree, $manipulators); foreach ($tree as $key => $element) { /** @var $link \Drupal\Core\Menu\MenuLinkInterface */ $link = $element->link; $content[$key]['title'] = $link->getTitle(); $content[$key]['options'] = $link->getOptions(); $content[$key]['description'] = $link->getDescription(); $content[$key]['url'] = $link->getUrlObject(); } ksort($content); return $content; }
/** * Tests the loadTreeData method. */ public function testLoadTree() { $this->addMenuLink('test1', ''); $this->addMenuLink('test2', 'test1'); $this->addMenuLink('test3', 'test2'); $this->addMenuLink('test4'); $this->addMenuLink('test5', 'test4'); $data = $this->treeStorage->loadTreeData('tools', new MenuTreeParameters()); $tree = $data['tree']; $this->assertEqual(count($tree['test1']['subtree']), 1); $this->assertEqual(count($tree['test1']['subtree']['test2']['subtree']), 1); $this->assertEqual(count($tree['test1']['subtree']['test2']['subtree']['test3']['subtree']), 0); $this->assertEqual(count($tree['test4']['subtree']), 1); $this->assertEqual(count($tree['test4']['subtree']['test5']['subtree']), 0); $parameters = new MenuTreeParameters(); $parameters->setActiveTrail(array('test4', 'test5')); $data = $this->treeStorage->loadTreeData('tools', $parameters); $tree = $data['tree']; $this->assertEqual(count($tree['test1']['subtree']), 1); $this->assertFalse($tree['test1']['in_active_trail']); $this->assertEqual(count($tree['test1']['subtree']['test2']['subtree']), 1); $this->assertFalse($tree['test1']['subtree']['test2']['in_active_trail']); $this->assertEqual(count($tree['test1']['subtree']['test2']['subtree']['test3']['subtree']), 0); $this->assertFalse($tree['test1']['subtree']['test2']['subtree']['test3']['in_active_trail']); $this->assertEqual(count($tree['test4']['subtree']), 1); $this->assertTrue($tree['test4']['in_active_trail']); $this->assertEqual(count($tree['test4']['subtree']['test5']['subtree']), 0); $this->assertTrue($tree['test4']['subtree']['test5']['in_active_trail']); }