Newer
Older
<?php
namespace Drupal\hierarchy_manager\Controller;
use Drupal\Core\Access\CsrfTokenGenerator;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Menu\MenuLinkManagerInterface;
use Drupal\Core\Menu\MenuTreeParameters;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class HmMenuController extends ControllerBase {

Denis K****
committed
/**
* CSRF Token.
*
* @var \Drupal\Core\Access\CsrfTokenGenerator
*/
protected $csrfToken;

Denis K****
committed
/**
* The menu_link_content storage handler.
*
* @var \Drupal\menu_link_content\MenuLinkContentStorageInterface
*/
protected $storageController;

Denis K****
committed
/**
* The hierarchy manager plugin type manager.
*
* @var \Drupal\hierarchy_manager\PluginTypeManager
*/
protected $hmPluginTypeManager;

Denis K****
committed
/**
* The menu tree service.
*
* @var \Drupal\Core\Menu\MenuLinkTreeInterface
*/
protected $menuTree;

Denis K****
committed
/**
* The menu tree array.
*
* @var array
*/
protected $overviewTree = [];

Denis K****
committed
/**
* The menu link manager.
*
* @var \Drupal\Core\Menu\MenuLinkManagerInterface
*/
protected $menuLinkManager;

Denis K****
committed
*
* @var \Drupal\Core\Entity\EntityRepository
*/
protected $entityRepository;
public function __construct(CsrfTokenGenerator $csrfToken, EntityTypeManagerInterface $entity_type_manager, $plugin_type_manager, MenuLinkTreeInterface $menu_tree, MenuLinkManagerInterface $menu_link_manager, EntityRepository $entity_repository) {
$this->csrfToken = $csrfToken;
$this->entityTypeManager = $entity_type_manager;
$this->storageController = $entity_type_manager->getStorage('menu_link_content');
$this->hmPluginTypeManager = $plugin_type_manager;
$this->menuTree = $menu_tree;
$this->menuLinkManager = $menu_link_manager;

Denis K****
committed
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('csrf_token'),
$container->get('entity_type.manager'),
$container->get('hm.plugin_type_manager'),
$container->get('menu.link_tree'),
$container->get('plugin.manager.menu.link'),
$container->get('entity.repository')

Denis K****
committed
/**
* Callback for menu tree json.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* Http request object.
* @param string $mid
* Menu ID.
*/
public function menuTreeJson(Request $request, string $mid) {
// Access token.
$token = $request->get('token');

Denis K****
committed
if (empty($token) || !$this->csrfToken->validate($token, $mid)) {
return new Response($this->t('Access denied!'));
}

Denis K****
committed
$parent = $request->get('parent');
$depth = $request->get('depth');
$destination = $request->get('destination');

Denis K****
committed
if (empty($depth)) {
$depth = 0;
}
else {
$depth = intval($depth);
}

Denis K****
committed
if (empty($parent)) {
$parent = '';
}

Denis K****
committed
// We indicate that a menu administrator is running the menu access check.
$request->attributes->set('_menu_admin', TRUE);

Denis K****
committed
$tree = $this->loadMenuTree($mid, $parent, $depth, $destination);

Denis K****
committed
$request->attributes->set('_menu_admin', FALSE);

Denis K****
committed
if ($tree) {
// Display plugin instance.
$display_plugin = $this->getDisplayPlugin();

Denis K****
committed
if (empty($display_plugin)) {
return new JsonResponse(['result' => 'Display profile has not been set up.']);
}

Denis K****
committed
if (method_exists($display_plugin, 'treeData')) {
// Transform the tree data to the structure
// that display plugin accepts.
$tree_data = $display_plugin->treeData($tree);
}
else {
$tree_data = $tree;
}

Denis K****
committed
return new JsonResponse($tree_data);
}

Denis K****
committed
return new JsonResponse([]);
}

Denis K****
committed
/**
* Callback for taxonomy tree json.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* Http request object.
*/
public function updateMenuLinks(Request $request, string $mid) {
// Access token.
$token = $request->get('token');
if (empty($token) || !$this->csrfToken->validate($token, $mid)) {
return new Response($this->t('Access denied!'));
}

Denis K****
committed
$old_position = (int) $request->get('old_position');
$target_position = $request->get('target');
$parent = $request->get('parent');
$updated_links = $request->get('keys');

Denis K****
committed
if (is_array($updated_links) && !empty($updated_links)) {
if (empty($parent)) {
// Root is the parent.
$parent = '';
// All children menu links (depth = 1).
$parent_links = $children = $this->loadMenuLinkObjs($mid, $parent, 1);
}
else {
$parent_links = $this->loadMenuLinkObjs($mid, $parent, 1);

Denis K****
committed
}
// The parent menu doesn't exist.
return new JsonResponse(['result' => 'fail']);
if (empty($children)) {
$parent_link = reset($parent_links);
$children = $parent_link->subtree;
}
if ($children) {
// The parent menu has children.
$target_position = intval($target_position);
foreach ($children as $child) {
$link = $child->link;

Denis K****
committed
// In order to make room for menu links inserted,
// we need to move all children links forward,
// and work out the weight for links inserted.
$new_hierarchy = $this->hmPluginTypeManager->updateHierarchy($target_position, $all_siblings, $updated_links, $old_position);
// Update all links need to update.
foreach ($new_hierarchy as $link_id => $link_weight) {
$this->menuLinkManager->updateDefinition($link_id, ['weight' => $link_weight, 'parent' => $parent]);

Denis K****
committed

Mingsong Hu
committed
$result = [
'result' => 'success',
'updated_nodes' => $new_hierarchy,
];
return new JsonResponse($result);

Denis K****
committed
return new JsonResponse(['result' => 'fail']);
}

Denis K****
committed
/**
* Get a display plugin instance.

Denis K****
committed
*
* @return null|object
* The display plugin instance.
*/
protected function getDisplayPlugin() {
$display_profile = $this->hmPluginTypeManager->getDisplayProfile('hm_setup_menu');
return $this->hmPluginTypeManager->getDisplayPluginInstance($display_profile);

Denis K****
committed
/**
* Load menu links into one array.

Denis K****
committed
*
* @param string $mid
* The menu ID.
* @param string $parent
* @param int $depth
* The max depth loaded.
* @param string $destination
* The destination of edit link.
*/
protected function loadMenuTree(string $mid, string $parent, int $depth = 0, string $destination = '') {
$tree = $this->loadMenuLinkObjs($mid, $parent, $depth);
// Load all menu links into one array.
$tree = $this->buildMenuLinkArray($tree);
$links = [];
foreach ($tree as $element) {
if (!empty($destination)) {
$element['url'] = $element['url'] . '?destination=' . $destination;
}
$links[] = $this->hmPluginTypeManager->buildHierarchyItem(

Denis K****
committed
$element['id'],
$element['title'],
$element['parent'],
$element['url'],

Denis K****
committed
);

Denis K****
committed

Denis K****
committed
/**
* Load menu links into one array.
*
* @param string $mid
* The menu ID.
* @param string $parent
* @param int $depth
* The max depth loaded.
*/
protected function loadMenuLinkObjs(string $mid, string $parent, int $depth = 0) {
$menu_para = new MenuTreeParameters();
if (!empty($depth)) {
$menu_para->setMaxDepth($depth);
}
if (!empty($parent)) {
$menu_para->setRoot($parent);
}
$tree = $this->menuTree->load($mid, $menu_para);
$manipulators = [
['callable' => 'menu.default_tree_manipulators:checkAccess'],
['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
];

Denis K****
committed
return $tree = $this->menuTree->transform($tree, $manipulators);
}

Denis K****
committed
/**
* Recursive helper function for loadMenuTree().
*
* @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
* The tree retrieved by \Drupal\Core\Menu\MenuLinkTreeInterface::load().
*
* @return array
* The menu links array.
*/
protected function buildMenuLinkArray($tree) {
// $tree_access_cacheability = new CacheableMetadata();
// $tree_access_cacheability = $tree_access_cacheability->merge(CacheableMetadata::createFromObject($element->access));
// Only load accessible links.
if (!$element->access->isAllowed()) {
continue;
}

Denis K****
committed
/** @var \Drupal\Core\Menu\MenuLinkInterface $link */
$link = $element->link;
if ($link) {
$this->overviewTree[$id]['id'] = $id;

Denis K****
committed
$this->overviewTree[$id]['status'] = $link->isEnabled();

Denis K****
committed
$this->overviewTree[$id]['title'] = $link->getTitle() . ' <span class="hm-tree-label hm-tree-label--disabled">(' . $this->t('disabled') . ')</span>';
}
// @todo Remove this in https://www.drupal.org/node/2568785.
elseif ($id === 'user.logout') {

Denis K****
committed
$this->overviewTree[$id]['title'] = $link->getTitle() . ' <span class="hm-tree-label hm-tree-label--login">(' . $this->t('<q>Log in</q> for anonymous users') . ')</span>';
}
// @todo Remove this in https://www.drupal.org/node/2568785.
elseif (($url = $link->getUrlObject()) && $url->isRouted() && $url->getRouteName() == 'user.page') {

Denis K****
committed
$this->overviewTree[$id]['title'] = $link->getTitle() . ' <span class="hm-tree-label hm-tree-label--logged-only">(' . $this->t('logged in users only') . ')</span>';
}
else {
$this->overviewTree[$id]['title'] = $link->getTitle();
}

Denis K****
committed
$this->overviewTree[$id]['parent'] = $parent_id;
$this->overviewTree[$id]['weight'] = $link->getWeight();
// Build the edit url.
// Allow for a custom edit link per plugin.
$edit_route = $link->getEditRoute();
if ($edit_route) {
$this->overviewTree[$id]['url'] = $edit_route->toString();
}
else {
// Fall back to the standard edit link.
$this->overviewTree[$id]['url'] = Url::fromRoute('menu_ui.link_edit', ['menu_link_plugin' => $link->getPluginId()])->toString();
}
}

Denis K****
committed
if ($element->subtree) {
$this->buildMenuLinkArray($element->subtree);
}
}

Denis K****
committed
return $this->overviewTree;
}