diff --git a/css/Plugin/jstree/hm.jstree.css b/css/Plugin/jstree/hm.jstree.css new file mode 100644 index 0000000000000000000000000000000000000000..90ceca98331a77ff1bf447ac0ab09466ef6ef2ab --- /dev/null +++ b/css/Plugin/jstree/hm.jstree.css @@ -0,0 +1,10 @@ + +/* HM Tree item status (unpublished/disabled) */ +.jstree-default .jstree-node.hm-tree-node-disabled { + color: grey; +} + +/* HM menu labeling */ +.hm-tree-label { + font-style: italic; +} diff --git a/hierarchy_manager.libraries.yml b/hierarchy_manager.libraries.yml index a16050518a0cbce19dd55846c5338b471373caf7..a29acdac8d889ab0f9088b8b1c94fb1225fa90e9 100644 --- a/hierarchy_manager.libraries.yml +++ b/hierarchy_manager.libraries.yml @@ -3,10 +3,13 @@ feature.hm.jstree: js: js/Plugin/jstree/hm.jstree.js: {} + css: + theme: + css/Plugin/jstree/hm.jstree.css: {} dependencies: - hierarchy_manager/libraries.jquery.jstree - core/drupalSettings - + feature.hm.jsoneditor: js: js/Plugin/jsoneditor/hm.jsoneditor.js: {} @@ -28,7 +31,7 @@ libraries.jquery.jstree: /libraries/jquery.jstree/3.3.8/jstree.min.js: {minified: true} dependencies: - core/jquery - + libraries.jquery.jstree.default: remote: https://github.com/vakata/jstree version: '3.3.8' @@ -41,7 +44,7 @@ libraries.jquery.jstree.default: css: component: /libraries/jquery.jstree/3.3.8/themes/default/style.min.css: {} - + libraries.jquery.jstree.default-dark: remote: https://github.com/vakata/jstree version: '3.3.8' @@ -54,7 +57,7 @@ libraries.jquery.jstree.default-dark: css: component: /libraries/jquery.jstree/3.3.8/themes/default-dark/style.min.css: {} - + libraries.jsoneditor: remote: https://github.com/josdejong/jsoneditor version: '7.0.4' @@ -66,7 +69,7 @@ libraries.jsoneditor: https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/7.0.4/ js: /libraries/jsoneditor/7.0.4/jsoneditor.min.js: {minified: true} - + libraries.jsoneditor.default-theme: remote: https://github.com/josdejong/jsoneditor version: '7.0.4' @@ -78,4 +81,4 @@ libraries.jsoneditor.default-theme: https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/7.0.4/ css: component: - /libraries/jsoneditor/7.0.4/jsoneditor.min.css: {minified: true} \ No newline at end of file + /libraries/jsoneditor/7.0.4/jsoneditor.min.css: {minified: true} diff --git a/src/Controller/HmMenuController.php b/src/Controller/HmMenuController.php index 77a7b568f0524b7d63ff1c166a7ad7eea5c386dd..e44a4d80e8da3353d28327a44f167600cf9706f2 100644 --- a/src/Controller/HmMenuController.php +++ b/src/Controller/HmMenuController.php @@ -15,53 +15,53 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\JsonResponse; class HmMenuController extends ControllerBase { - + /** * CSRF Token. * * @var \Drupal\Core\Access\CsrfTokenGenerator */ protected $csrfToken; - + /** * The menu_link_content storage handler. * * @var \Drupal\menu_link_content\MenuLinkContentStorageInterface */ protected $storageController; - + /** * The hierarchy manager plugin type manager. * * @var \Drupal\hierarchy_manager\PluginTypeManager */ protected $hmPluginTypeManager; - + /** * The menu tree service. * * @var \Drupal\Core\Menu\MenuLinkTreeInterface */ protected $menuTree; - + /** * The menu tree array. * * @var array */ protected $overviewTree = []; - + /** * The menu link manager. * * @var \Drupal\Core\Menu\MenuLinkManagerInterface */ protected $menuLinkManager; - + /** * {@inheritdoc} */ - public function __construct(CsrfTokenGenerator $csrfToken, EntityTypeManagerInterface $entity_type_manager, $plugin_type_manager, MenuLinkTreeInterface $menu_tree, MenuLinkManagerInterface $menu_link_manager) { + public function __construct(CsrfTokenGenerator $csrfToken, EntityTypeManagerInterface $entity_type_manager, $plugin_type_manager, MenuLinkTreeInterface $menu_tree, MenuLinkManagerInterface $menu_link_manager) { $this->csrfToken = $csrfToken; $this->entityTypeManager = $entity_type_manager; $this->storageController = $entity_type_manager->getStorage('menu_link_content'); @@ -69,7 +69,7 @@ class HmMenuController extends ControllerBase { $this->menuTree = $menu_tree; $this->menuLinkManager = $menu_link_manager; } - + /** * {@inheritdoc} */ @@ -82,7 +82,7 @@ class HmMenuController extends ControllerBase { $container->get('plugin.manager.menu.link') ); } - + /** * Callback for menu tree json. * @@ -94,42 +94,42 @@ class HmMenuController extends ControllerBase { public function menuTreeJson(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!')); } - + $parent = $request->get('parent'); $depth = $request->get('depth'); $destination = $request->get('destination'); - + if (empty($depth)) { $depth = 0; } else { $depth = intval($depth); } - + if (empty($parent)) { $parent = ''; } - + // We indicate that a menu administrator is running the menu access check. $request->attributes->set('_menu_admin', TRUE); - + $tree = $this->loadMenuTree($mid, $parent, $depth, $destination); - + // menu access check done. $request->attributes->set('_menu_admin', FALSE); - + if ($tree) { // Display plugin instance. $display_plugin = $this->getDisplayPlugin(); - + if (empty($display_plugin)) { return new JsonResponse(['result' => 'Display profile has not been set up.']); } - + if (method_exists($display_plugin, 'treeData')) { // Transform the tree data to the structure // that display plugin accepts. @@ -138,13 +138,13 @@ class HmMenuController extends ControllerBase { else { $tree_data = $tree; } - + return new JsonResponse($tree_data); } - + return new JsonResponse([]); } - + /** * Callback for taxonomy tree json. * @@ -159,7 +159,7 @@ class HmMenuController extends ControllerBase { if (empty($token) || !$this->csrfToken->validate($token, $mid)) { return new Response($this->t('Access denied!')); } - + $target_position = $request->get('target'); $parent = $request->get('parent'); $updated_links = $request->get('keys'); @@ -167,7 +167,7 @@ class HmMenuController extends ControllerBase { $before = $request->get('before'); $all_siblings = []; $insert_after = TRUE; - + if (is_array($updated_links) && !empty($updated_links)) { if (empty($parent)) { // Root is the parent. @@ -177,7 +177,7 @@ class HmMenuController extends ControllerBase { else { // All children menu links (depth = 1). $parent_links = $this->loadMenuLinkObjs($mid, $parent, 1); - } + } // 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. @@ -204,40 +204,40 @@ class HmMenuController extends ControllerBase { if ($position++ == $target_position && $link_id !== $before) { $insert_after = FALSE; } - + $all_siblings[$link_id] = (int) $link->getWeight(); } } else { // The parent link doesn't have children. - + } - + $new_hierarchy = $this->hmPluginTypeManager->updateHierarchy($target_position, $all_siblings, $updated_links, $insert_after); // Update all links need to update. foreach ($new_hierarchy as $link_id => $link_weight) { $this->menuLinkManager->updateDefinition($link_id, ['weight' => $link_weight, 'parent' => $parent]); } - + return new JsonResponse(['result' => 'success']); } - + return new JsonResponse(['result' => 'fail']); } - + /** * Get a display plugin instance. - * + * * @return NULL|object */ protected function getDisplayPlugin() { $display_profile = $this->hmPluginTypeManager->getDisplayProfile('hm_setup_menu'); return $this->hmPluginTypeManager->getDisplayPluginInstance($display_profile); } - + /** * Load menu links into one array. - * + * * @param string $mid * The menu ID. * @param string $parent @@ -257,15 +257,17 @@ class HmMenuController extends ControllerBase { $element['url'] = $element['url'] . '?destination=' . $destination; } $links[] = $this->hmPluginTypeManager->buildHierarchyItem( - $element['id'], - $element['title'], - $element['parent'], - $element['url']); + $element['id'], + $element['title'], + $element['parent'], + $element['url'], + $element['status'] + ); } - + return $links; } - + /** * Load menu links into one array. * @@ -291,10 +293,10 @@ class HmMenuController extends ControllerBase { ['callable' => 'menu.default_tree_manipulators:checkAccess'], ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'], ]; - + return $tree = $this->menuTree->transform($tree, $manipulators); } - + /** * Recursive helper function for loadMenuTree(). * @@ -308,33 +310,34 @@ class HmMenuController extends ControllerBase { // $tree_access_cacheability = new CacheableMetadata(); foreach ($tree as $element) { // $tree_access_cacheability = $tree_access_cacheability->merge(CacheableMetadata::createFromObject($element->access)); - + // Only load accessible links. if (!$element->access->isAllowed()) { continue; } - + /** @var \Drupal\Core\Menu\MenuLinkInterface $link */ $link = $element->link; if ($link) { - // The id consistes of plugin ID and link ID. + // The id consistes of plugin ID and link ID. $id = $link->getPluginId(); $this->overviewTree[$id]['id'] = $id; + $this->overviewTree[$id]['status'] = $link->isEnabled(); if (!$link->isEnabled()) { - $this->overviewTree[$id]['title'] = '(' . $this->t('disabled') . ')' . $link->getTitle(); + $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') { - $this->overviewTree[$id]['title'] = ' (' . $this->t('<q>Log in</q> for anonymous users') . ')' . $link->getTitle(); + $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') { - $this->overviewTree[$id]['title'] = ' (' . $this->t('logged in users only') . ')' . $link->getTitle(); + $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(); } - + $this->overviewTree[$id]['parent'] = $link->getParent(); // Build the edit url. // Allow for a custom edit link per plugin. @@ -347,16 +350,16 @@ class HmMenuController extends ControllerBase { $this->overviewTree[$id]['url'] = Url::fromRoute('menu_ui.link_edit', ['menu_link_plugin' => $link->getPluginId()])->toString(); } } - + if ($element->subtree) { $this->buildMenuLinkArray($element->subtree); } } - + /* $tree_access_cacheability ->merge(CacheableMetadata::createFromRenderArray($this->overviewTree)) ->applyTo($form); */ - + return $this->overviewTree; } } diff --git a/src/Controller/HmTaxonomyController.php b/src/Controller/HmTaxonomyController.php index 7d28ecef9646002ff28e40af83f1bf81385108fe..24116f466b632254c9da0d6a16d38bceddc78eaf 100644 --- a/src/Controller/HmTaxonomyController.php +++ b/src/Controller/HmTaxonomyController.php @@ -104,10 +104,12 @@ class HmTaxonomyController extends ControllerBase { } $term_array[] = $this->hmPluginTypeManager->buildHierarchyItem( - $term->id(), - $term->label(), - $term->parents[0], - $url); + $term->id(), + $term->label(), + $term->parents[0], + $url, + $term->isPublished() + ); } } } diff --git a/src/Plugin/HmDisplayPlugin/HmDisplayJstree.php b/src/Plugin/HmDisplayPlugin/HmDisplayJstree.php index 3b590176ed34b6eb03cc1dadc65b065df9e79fd7..e91b0e00ba1bea7d43f2aec571208f4b522e5ac0 100644 --- a/src/Plugin/HmDisplayPlugin/HmDisplayJstree.php +++ b/src/Plugin/HmDisplayPlugin/HmDisplayJstree.php @@ -18,7 +18,7 @@ use Drupal\hierarchy_manager\Plugin\HmDisplayPluginBase; */ class HmDisplayJstree extends HmDisplayPluginBase implements HmDisplayPluginInterface { use StringTranslationTrait; - + /* * Build the tree form. */ @@ -28,17 +28,17 @@ class HmDisplayJstree extends HmDisplayPluginBase implements HmDisplayPluginInte $parent_formObj = $form_state->getFormObject(); $parent_id = $parent_formObj->getFormId(); } - + // The jsTree default theme. $theme = 'default'; - + if (!empty($options)) { $jsonObj = json_decode($options); if (isset($jsonObj->theme) && isset($jsonObj->theme->name)) { $theme = $jsonObj->theme->name; } } - + // Search input. $form['search'] = [ '#type' => 'textfield', @@ -56,7 +56,7 @@ class HmDisplayJstree extends HmDisplayPluginBase implements HmDisplayPluginInte '#size' => 60, '#maxlength' => 128, ]; - + $form['jstree'] = [ '#type' => 'html_tag', '#suffix' => '<div class="description">' . $this->t('Click a tree node to edit it.') . '<br>' . $this->t('The tree node is draggable and droppable') . '</div>', @@ -73,22 +73,22 @@ class HmDisplayJstree extends HmDisplayPluginBase implements HmDisplayPluginInte 'url-update' => $url_update, ], ]; - + $form['#attached']['library'][] = 'hierarchy_manager/libraries.jquery.jstree.' . $theme; $form['#attached']['library'][] = 'hierarchy_manager/feature.hm.jstree'; $form['#attached']['library'][] = 'core/drupal.dialog.ajax'; } - + return $form; - + } - + /** * Build the data array that JS library accepts. */ public function treeData(array $data) { $jstree_data = []; - + // The array key of jsTree is different from the data source. // So we need to translate them. foreach ($data as $tree_node) { @@ -98,10 +98,16 @@ class HmDisplayJstree extends HmDisplayPluginBase implements HmDisplayPluginInte $jstree_node['parent'] = '#'; } + if (!$tree_node['status']) { + $jstree_node['li_attr'] = [ + 'class' => 'hm-tree-node-disabled', + ]; + } + $dialog_options = [ 'minWidth' => '300', 'width' => '960', - 'title' => $this->t('Edit') . ' ' . $tree_node['text'], + 'title' => $this->t('Edit') . ' ' . preg_replace('~<span(.*?)</span>~Usi', '', $tree_node['text']), ]; // Custom data $jstree_node['a_attr'] = [ @@ -114,7 +120,7 @@ class HmDisplayJstree extends HmDisplayPluginBase implements HmDisplayPluginInte // Add this node into the data array. $jstree_data[] = $jstree_node; } - + return $jstree_data; } } diff --git a/src/PluginTypeManager.php b/src/PluginTypeManager.php index 0f6f8912405775fea2284af44a253ff73e9e20e3..fb277d36e5003f4b8ef4b5e60680540de77542d3 100644 --- a/src/PluginTypeManager.php +++ b/src/PluginTypeManager.php @@ -50,16 +50,19 @@ class PluginTypeManager { * Parent id of the item. * @param string $edit_url * The URL where to edit this item. + * @param boolean $status + * The item status. * @return array * The hierarchy item array. */ - public function buildHierarchyItem($id, $label, $parent, $edit_url) { + public function buildHierarchyItem($id, $label, $parent, $edit_url, $status = TRUE) { return [ - 'id' => $id, - 'text' => $label, - 'parent' => $parent, - 'edit_url' => $edit_url, + 'id' => $id, + 'text' => $label, + 'parent' => $parent, + 'edit_url' => $edit_url, + 'status' => $status ]; }