Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • project/hierarchy_manager
  • issue/hierarchy_manager-3188871
  • issue/hierarchy_manager-3188833
  • issue/hierarchy_manager-3191599
  • issue/hierarchy_manager-3191605
  • issue/hierarchy_manager-3205538
  • issue/hierarchy_manager-3217994
  • issue/hierarchy_manager-3230813
  • issue/hierarchy_manager-3241543
  • issue/hierarchy_manager-3243559
  • issue/hierarchy_manager-3243579
  • issue/hierarchy_manager-3278219
  • issue/hierarchy_manager-3341369
  • issue/hierarchy_manager-3344493
  • issue/hierarchy_manager-3343978
  • issue/hierarchy_manager-3347488
  • issue/hierarchy_manager-3347499
  • issue/hierarchy_manager-3343297
  • issue/hierarchy_manager-3451974
  • issue/hierarchy_manager-3467198
20 results
Show changes
Commits on Source (38)
Showing
with 835 additions and 375 deletions
hmconfig
hmsetup
Jsoneditor
jsoneditor
Jstree
jstree
Mingsong
pluginable
\ No newline at end of file
################
# GitLabCI template for Drupal projects.
#
# This template is designed to give any Contrib maintainer everything they need to test, without requiring modification.
# It is also designed to keep up to date with Core Development automatically through the use of include files that can be centrally maintained.
# As long as you include the project, ref and three files below, any future updates added by the Drupal Association will be used in your
# pipelines automatically. However, you can modify this template if you have additional needs for your project.
# The full documentation is on https://project.pages.drupalcode.org/gitlab_templates/
################
# For information on alternative values for 'ref' see https://project.pages.drupalcode.org/gitlab_templates/info/templates-version/
# To test a Drupal 7 project, change the first include filename from .main.yml to .main-d7.yml
include:
- project: $_GITLAB_TEMPLATES_REPO
ref: $_GITLAB_TEMPLATES_REF
file:
- "/includes/include.drupalci.main.yml"
- "/includes/include.drupalci.variables.yml"
- "/includes/include.drupalci.workflows.yml"
################
# Pipeline configuration variables are defined with default values and descriptions in the file
# https://git.drupalcode.org/project/gitlab_templates/-/blob/main/includes/include.drupalci.variables.yml
# Uncomment the lines below if you want to override any of the variables. The following is just an example.
################
variables:
SKIP_ESLINT: '1'
SKIP_PHPSTAN: '1'
# OPT_IN_TEST_NEXT_MAJOR: '1'
# _CURL_TEMPLATES_REF: 'main'
...@@ -10,18 +10,33 @@ CONTENTS OF THIS FILE ...@@ -10,18 +10,33 @@ CONTENTS OF THIS FILE
INTRODUCTION INTRODUCTION
------------ ------------
Drupal provides a draggable table to manage the hierarchy of menu links and taxonomy terms. The Drupal draggable table is not able to present a massive hierarchy in one page. Drupal provides a draggable table to manage the hierarchy of menu links
and taxonomy terms.
The Drupal draggable table is not able to present a massive hierarchy
in one page.
This module provides a plugin architecture to delivery a flexibility of managing hierarchy for taxonomy terms, menu links and others. There are two out of box plugins, taxonomy hierarchy management plugin and menu hierarchy plugin. The front-end JavaScript libraries is also pluginable. The out of box display plugin using jsTree to render the hierarchy tree with filter. The hierarchy tree is draggable which means you can update the hierarchy by dragging a node in the tree. This module provides a plugin architecture to delivery a flexibility
of managing hierarchy for taxonomy terms,
menu links and others. There are two out of box plugins,
taxonomy hierarchy management plugin and menu hierarchy plugin.
The front-end JavaScript libraries is also pluginable.
The out of box display plugin using jsTree to render
the hierarchy tree with filter.
The hierarchy tree is draggable which means you can update
the hierarchy by dragging a node in the tree.
Other modules can define their own management plugin to manage hierarchy for any other entities or display plugin to render the hierarchy tree by a JavaScript library other than jsTree. Other modules can define their own management plugin to manage
hierarchy for any other entities or display plugin to render
the hierarchy tree by a JavaScript library other than jsTree.
REQUIREMENTS REQUIREMENTS
------------ ------------
This module requires the following library: This module requires the following library:
* jsTree JS (This module will automatically load this library from romte CDN if it wasn't hosted locally under /libraries/jquery.jstree/3.3.8/ folder) * jsTree JS (This module will automatically load this library
from remote CDN if it wasn't hosted locally under
/libraries/jquery.jstree/3.3.8/ folder)
INSTALLATION INSTALLATION
------------ ------------
...@@ -31,13 +46,23 @@ INSTALLATION ...@@ -31,13 +46,23 @@ INSTALLATION
CONFIGURATION CONFIGURATION
------------- -------------
* Go the hierarchy manage display management page (/admin/structure/hm_display_profile) under the Structure menu to create a display profile * Go the hierarchy manage display management page
(/admin/structure/hm_display_profile) under
the Structure menu to create a display profile
* Go to the hierarchy management configuration page (/admin/config/user-interface/hierarchy_manager/config) to enable hierarchy management plugins, such as taxonomy plugin, and specify a display profile created in step above. * Go to the hierarchy management configuration page
(/admin/config/user-interface/hierarchy_manager/config) to
enable hierarchy management plugins, such as taxonomy plugin,
and specify a display profile created in step above.
* Once a hierarchy mange plugin is enabled, the related edit form should be replaced with a hierarchy tree form. For instance, the taxonomy term edit form (/admin/structure/taxonomy/manage/{tid}/overview) will be replaced with a hierarchy tree implemented by the taxonomy hierarchy manage plugin. * Once a hierarchy mange plugin is enabled, the related edit form
should be replaced with a hierarchy tree form.
For instance, the taxonomy term edit form
(/admin/structure/taxonomy/manage/{tid}/overview)
will be replaced with a hierarchy tree implemented by
the taxonomy hierarchy manage plugin.
MAINTAINERS MAINTAINERS
----------- -----------
Mingsong Hu (Mingsong) - https://www.drupal.org/u/mingsong Mingsong Hu (Mingsong) - https://www.drupal.org/u/mingsong
\ No newline at end of file
hierarchy_manager:
hierarchy_manager.hmconfig:
type: config_object
label: 'Hierarchy Manager Configuration'
mapping:
allowed_setup_plugins:
type: sequence
label: 'Allowed Setup Plugins'
sequence:
type: string
label: 'Setup Plugin ID'
setup_plugin_settings:
label: 'TFA validation plugin configuration'
type: sequence
sequence:
type: hierarchy_manager.HmSetupPlugin.plugin.config.[%key]
hierarchy_manager.HmSetupPlugin.plugin.config.hm_setup_taxonomy:
type: mapping
label: 'HM Setup Taxonomy Settings'
mapping:
display_profile:
type: string
label: 'Display Profile'
bundle:
type: sequence
label: 'Bundle'
sequence:
type: string
label: 'Bundle Name'
hierarchy_manager.HmSetupPlugin.plugin.config.hm_setup_menu:
type: mapping
label: 'HM Setup Menu Settings'
mapping:
display_profile:
type: string
label: 'Display Profile'
bundle:
type: sequence
label: 'Bundle'
sequence:
type: string
label: 'Bundle Item'
hierarchy_manager.hm_display_profile.*:
type: config_entity
label: 'Hierarchy Manager Display Profile'
mapping:
id:
type: string
label: 'ID'
label:
type: string
label: 'Label'
plugin:
type: string
label: 'Plugin'
config:
type: string
label: 'Config'
confirm:
type: boolean
label: 'Confirm'
hierarchy_manager.hm_display_profile.*:
type: config_entity
label: 'HM Display Profile Entity config'
mapping:
id:
type: string
label: 'ID'
label:
type: label
label: 'Label'
uuid:
type: string
plugin:
type: string
label: 'Display plugin'
config:
type: text
label: 'Configuration'
/* HM Tree item status (unpublished/disabled) */
.jstree-default .jstree-node.hm-tree-node-disabled > a {
color: gray;
background-color: #fff4f4;
}
/* HM menu labeling */
.hm-tree-label {
font-style: italic;
}
name: Hierarchy Manager name: Hierarchy Manager
description: Provides API and plugins to build hierarchy views for entites such as taxonomy or menu. description: Provides API and plugins to build hierarchy views for entities such as taxonomy or menu.
package: Administration package: Administration
type: module type: module
core: 8.x core_version_requirement: ^9.2.0 || ^10 || ^11
\ No newline at end of file
configure: hierarchy_manager.hm_config_form
<?php
/**
* @file
* Install, update and uninstall functions for the Hierarchy Manager module.
*/
use Drupal\system\Entity\Menu;
/**
* Enable hierarchy manager for all bundles of active setup plugins.
*/
function hierarchy_manager_update_8001() {
if ($config = \Drupal::configFactory()->getEditable('hierarchy_manager.hmconfig')) {
if ($allowed_setup_plugins = $config->get('allowed_setup_plugins')) {
if (!empty($allowed_setup_plugins['hm_setup_menu'])) {
$menus = Menu::loadMultiple();
$bundles = [];
/** @var \Drupal\system\Entity\Menu $menu */
foreach ($menus as $menu) {
$id = $menu->id();
$bundles[$id] = $id;
}
$config->set('setup_plugin_settings.hm_setup_menu.bundle', $bundles);
$config->save();
}
if (!empty($allowed_setup_plugins['hm_setup_taxonomy'])) {
$vocabularies = \Drupal::service('entity_type.bundle.info')->getBundleInfo('taxonomy_term');
$bundles = [];
foreach ($vocabularies as $key => $value) {
$bundles[$key] = $key;
}
$config->set('setup_plugin_settings.hm_setup_taxonomy.bundle', $bundles);
$config->save();
}
drupal_flush_all_caches();
}
}
}
...@@ -3,10 +3,15 @@ ...@@ -3,10 +3,15 @@
feature.hm.jstree: feature.hm.jstree:
js: js:
js/Plugin/jstree/hm.jstree.js: {} js/Plugin/jstree/hm.jstree.js: {}
css:
theme:
css/Plugin/jstree/hm.jstree.css: {}
dependencies: dependencies:
- hierarchy_manager/libraries.jquery.jstree - hierarchy_manager/libraries.jquery.jstree
- core/drupalSettings - core/drupalSettings
- core/drupal.message
- core/once
feature.hm.jsoneditor: feature.hm.jsoneditor:
js: js:
js/Plugin/jsoneditor/hm.jsoneditor.js: {} js/Plugin/jsoneditor/hm.jsoneditor.js: {}
...@@ -17,65 +22,65 @@ feature.hm.jsoneditor: ...@@ -17,65 +22,65 @@ feature.hm.jsoneditor:
libraries.jquery.jstree: libraries.jquery.jstree:
remote: https://github.com/vakata/jstree remote: https://github.com/vakata/jstree
version: '3.3.8' version: '3.3.15'
license: license:
name: MIT name: MIT
url: https://github.com/vakata/jstree/blob/master/LICENSE-MIT url: https://github.com/vakata/jstree/blob/master/LICENSE-MIT
gpl-compatible: true gpl-compatible: true
cdn: cdn:
https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.8/ https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.15/
js: js:
/libraries/jquery.jstree/3.3.8/jstree.min.js: {minified: true} /libraries/jquery.jstree/3.3.15/jstree.min.js: {minified: true}
dependencies: dependencies:
- core/jquery - core/jquery
libraries.jquery.jstree.default: libraries.jquery.jstree.default:
remote: https://github.com/vakata/jstree remote: https://github.com/vakata/jstree
version: '3.3.8' version: '3.3.15'
license: license:
name: MIT name: MIT
url: https://github.com/vakata/jstree/blob/master/LICENSE-MIT url: https://github.com/vakata/jstree/blob/master/LICENSE-MIT
gpl-compatible: true gpl-compatible: true
cdn: cdn:
https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.8/themes/default/ https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.15/themes/default/
css: css:
component: component:
/libraries/jquery.jstree/3.3.8/themes/default/style.min.css: {} /libraries/jquery.jstree/3.3.15/themes/default/style.min.css: {}
libraries.jquery.jstree.default-dark: libraries.jquery.jstree.default-dark:
remote: https://github.com/vakata/jstree remote: https://github.com/vakata/jstree
version: '3.3.8' version: '3.3.15'
license: license:
name: MIT name: MIT
url: https://github.com/vakata/jstree/blob/master/LICENSE-MIT url: https://github.com/vakata/jstree/blob/master/LICENSE-MIT
gpl-compatible: true gpl-compatible: true
cdn: cdn:
https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.8/themes/default-dark/ https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.15/themes/default-dark/
css: css:
component: component:
/libraries/jquery.jstree/3.3.8/themes/default-dark/style.min.css: {} /libraries/jquery.jstree/3.3.15/themes/default-dark/style.min.css: {}
libraries.jsoneditor: libraries.jsoneditor:
remote: https://github.com/josdejong/jsoneditor remote: https://github.com/josdejong/jsoneditor
version: '7.0.4' version: '9.9.2'
license: license:
name: Apache License 2.0 name: Apache License 2.0
url: https://github.com/josdejong/jsoneditor/blob/develop/LICENSE url: https://github.com/josdejong/jsoneditor/blob/develop/LICENSE
gpl-compatible: true gpl-compatible: true
cdn: cdn:
https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/7.0.4/ https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/9.9.2/
js: js:
/libraries/jsoneditor/7.0.4/jsoneditor.min.js: {minified: true} /libraries/jsoneditor/9.9.2/jsoneditor.min.js: {minified: true}
libraries.jsoneditor.default-theme: libraries.jsoneditor.default-theme:
remote: https://github.com/josdejong/jsoneditor remote: https://github.com/josdejong/jsoneditor
version: '7.0.4' version: '9.9.2'
license: license:
name: Apache License 2.0 name: Apache License 2.0
url: https://github.com/josdejong/jsoneditor/blob/develop/LICENSE url: https://github.com/josdejong/jsoneditor/blob/develop/LICENSE
gpl-compatible: true gpl-compatible: true
cdn: cdn:
https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/7.0.4/ https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/9.9.2/
css: css:
component: component:
/libraries/jsoneditor/7.0.4/jsoneditor.min.css: {minified: true} /libraries/jsoneditor/9.9.2/jsoneditor.min.css: {minified: true}
\ No newline at end of file
...@@ -5,6 +5,22 @@ ...@@ -5,6 +5,22 @@
* General functions and hook implementations for Hierarchy Manager module. * General functions and hook implementations for Hierarchy Manager module.
*/ */
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Implements hook_help().
*/
function hierarchy_manager_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
// Main module help for the hierarchy_manager module.
case 'help.page.hierarchy_manager':
$output = '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('Hierarchy Manager module provides a flexible solution for managing hierarchies of menu links and taxonomy terms. Unlike the default Drupal draggable table, this module supports massive hierarchies and offers a plugin architecture for customization.') . '</p>';
$output .= '<p>' . t('Out of the box, the module comes with two plugins: a taxonomy hierarchy management plugin and a menu hierarchy plugin. The front-end JavaScript libraries are also pluginable, with an out-of-the-box display plugin using jsTree to render the hierarchy tree with a filter. The hierarchy tree is draggable, allowing you to easily update the hierarchy by dragging a node in the tree.') . '</p>';
return $output;
}
}
/** /**
* Implements hook_library_info_alter(). * Implements hook_library_info_alter().
*/ */
...@@ -26,12 +42,12 @@ function hierarchy_manager_library_info_alter(array &$libraries, $module) { ...@@ -26,12 +42,12 @@ function hierarchy_manager_library_info_alter(array &$libraries, $module) {
if ($cdn_library) { if ($cdn_library) {
$libraries['libraries.jquery.jstree.default-dark']['css']['component'] = $cdn_library; $libraries['libraries.jquery.jstree.default-dark']['css']['component'] = $cdn_library;
} }
// jsoneditor min js. // Jsoneditor min js.
$cdn_library = _hierarchy_manager_use_cdn($libraries, 'libraries.jsoneditor', 'js'); $cdn_library = _hierarchy_manager_use_cdn($libraries, 'libraries.jsoneditor', 'js');
if ($cdn_library) { if ($cdn_library) {
$libraries['libraries.jsoneditor']['js'] = $cdn_library; $libraries['libraries.jsoneditor']['js'] = $cdn_library;
} }
// jsoneditor default theme. // Jsoneditor default theme.
$cdn_library = _hierarchy_manager_use_cdn($libraries, 'libraries.jsoneditor.default-theme', 'css'); $cdn_library = _hierarchy_manager_use_cdn($libraries, 'libraries.jsoneditor.default-theme', 'css');
if ($cdn_library) { if ($cdn_library) {
$libraries['libraries.jsoneditor.default-theme']['css']['component'] = $cdn_library; $libraries['libraries.jsoneditor.default-theme']['css']['component'] = $cdn_library;
...@@ -40,15 +56,12 @@ function hierarchy_manager_library_info_alter(array &$libraries, $module) { ...@@ -40,15 +56,12 @@ function hierarchy_manager_library_info_alter(array &$libraries, $module) {
} }
/** /**
* Implement hook_entity_type_alter(). * Implements hook_entity_type_alter().
*
* @param array $entity_types
* Entity type information array.
*/ */
function hierarchy_manager_entity_type_alter(array &$entity_types) { function hierarchy_manager_entity_type_alter(array &$entity_types) {
// Override the menu edit form. // Override the menu edit form.
$entity_types['menu'] $entity_types['menu']
->setFormClass('edit', 'Drupal\hierarchy_manager\Form\HmMenuForm'); ->setFormClass('edit', 'Drupal\hierarchy_manager\Form\HmMenuForm');
} }
/** /**
......
...@@ -25,7 +25,7 @@ hierarchy_manager.taxonomy.tree.json: ...@@ -25,7 +25,7 @@ hierarchy_manager.taxonomy.tree.json:
_title: 'Taxonomy tree' _title: 'Taxonomy tree'
_controller: '\Drupal\hierarchy_manager\Controller\HmTaxonomyController::taxonomyTreeJson' _controller: '\Drupal\hierarchy_manager\Controller\HmTaxonomyController::taxonomyTreeJson'
requirements: requirements:
_permission: 'administer taxonomy' _custom_access: '\Drupal\hierarchy_manager\Controller\HmTaxonomyController::access'
options: options:
_admin_route: TRUE _admin_route: TRUE
hierarchy_manager.taxonomy.tree.update: hierarchy_manager.taxonomy.tree.update:
...@@ -34,7 +34,7 @@ hierarchy_manager.taxonomy.tree.update: ...@@ -34,7 +34,7 @@ hierarchy_manager.taxonomy.tree.update:
_title: 'Taxonomy tree' _title: 'Taxonomy tree'
_controller: '\Drupal\hierarchy_manager\Controller\HmTaxonomyController::updateTerms' _controller: '\Drupal\hierarchy_manager\Controller\HmTaxonomyController::updateTerms'
requirements: requirements:
_permission: 'administer taxonomy' _custom_access: '\Drupal\hierarchy_manager\Controller\HmTaxonomyController::access'
options: options:
_admin_route: TRUE _admin_route: TRUE
......
...@@ -5,20 +5,19 @@ ...@@ -5,20 +5,19 @@
// Codes run both on normal page loads and when data is loaded by AJAX (or BigPipe!) // Codes run both on normal page loads and when data is loaded by AJAX (or BigPipe!)
// @See https://www.drupal.org/docs/8/api/javascript-api/javascript-api-overview // @See https://www.drupal.org/docs/8/api/javascript-api/javascript-api-overview
(function($, Drupal) { (function($, Drupal, once) {
Drupal.behaviors.hmJSTree = { Drupal.behaviors.hmJSTree = {
attach: function(context, settings) { attach: function(context, settings) {
$(".hm-jstree", context) const hmJstree = once('hmJSTree', '.hm-jstree', context);
.once("jstreeBehavior") // Render all trees.
.each(function() { hmJstree.forEach(function(hmJstree) {
const treeContainer = $(this); const treeContainer = $(hmJstree);
const parentID = treeContainer.attr('parent-id'); const parentID = treeContainer.attr('parent-id');
const searchTextID = (parentID) ? '#hm-jstree-search-' + parentID : '#hm-jstree-search'; const searchTextID = (parentID) ? '#hm-jstree-search-' + parentID : '#hm-jstree-search';
const optionsJson = treeContainer.attr("options"); const optionsJson = treeContainer.attr("options");
const dataURL = treeContainer.attr('data-source') + '&parent=0'; const dataURL = treeContainer.attr('data-source') + '&parent=0';
const updateURL = treeContainer.attr('url-update'); const updateURL = treeContainer.attr('url-update');
const newWindow = false; const confirm = treeContainer.attr("confirm");
let $popDialog = [];
let reload = true; let reload = true;
let rollback = false; let rollback = false;
let themes = { let themes = {
...@@ -26,8 +25,7 @@ ...@@ -26,8 +25,7 @@
name: 'default' name: 'default'
}; };
let options; let options;
var offset = 0;
if (optionsJson) { if (optionsJson) {
options = JSON.parse(optionsJson); options = JSON.parse(optionsJson);
if (options.theme) { if (options.theme) {
...@@ -39,6 +37,9 @@ ...@@ -39,6 +37,9 @@
// Build the tree. // Build the tree.
treeContainer.jstree({ treeContainer.jstree({
core: { core: {
'check_callback' : function (operation, node, node_parent, node_position, more) {
return true;
},
data: { data: {
url: function(node) { url: function(node) {
return node.id === '#' ? return node.id === '#' ?
...@@ -50,92 +51,141 @@ ...@@ -50,92 +51,141 @@
} }
}, },
themes: themes, themes: themes,
"check_callback" : true,
"multiple": false, "multiple": false,
}, },
'dnd' : {
'copy': false,
'is_draggable' : function(node) {
let can_drag = node[0].data.draggable;
if (can_drag) {
return true;
}
else {
let drupalMessages = new Drupal.Message();
drupalMessages.clear();
drupalMessages.add(Drupal.t("Cannot drag this item, possibly because it has multiple parents or ancestors."), {type: 'warning'});
return false;
}
}
},
search: { search: {
show_only_matches: true show_only_matches: true,
"search_callback": function(str, node) {
//search for any of the words entered
var word, words = [];
var searchFor = str.toLowerCase().replace(/^\s+/g, '').replace(/\s+$/g, '');
if (searchFor.indexOf(' ') >= 0) {
words = searchFor.split(' ');
} else {
words = [searchFor];
}
for (var i = 0; i < words.length; i++) {
word = words[i];
if ((node.text || "").toLowerCase().indexOf(word) >= 0) {
return true;
}
}
return false;
}
}, },
plugins: ["search", "dnd"] 'sort' : function(a, b) {
return parseInt(this.get_node(a).data.weight) > parseInt(this.get_node(b).data.weight) ? 1 : -1;
},
plugins: ["search", "dnd", "sort"]
}); });
// Node move event. // Node move event.
treeContainer.on("move_node.jstree", function(event, data) { treeContainer.on("move_node.jstree", function(event, data) {
const thisTree = data.instance; const thisTree = data.instance;
const movedNode = data.node; const movedNode = data.node;
const parent = data.parent === '#' ? 0 : data.parent;
const parent_node = thisTree.get_node(data.parent);
const old_parent = data.old_parent === '#' ? 0 : data.old_parent;
const drupalMessages = new Drupal.Message();
if (!rollback) { if (!rollback) {
let list = thisTree.get_node(data.parent).children; let parentText = Drupal.t('root');
let before = ''; if (parent !== 0) {
let after = ''; parentText = $("<div/>").html(thisTree.get_node(parent).text);
if (data.position > 0) { parentText.find("span").remove();
before = list[data.position - 1]; parentText = parentText.text();
} }
// Function to move the tree item.
if (data.position < list.length - 1) { function moveTreeItem() {
after = list[data.position + 1]; // Update the data on server side.
} $.post(updateURL, {
keys: [movedNode.id],
let parent = data.parent === '#' ? 0 : data.parent; target: data.position,
// Update the data on server side. parent: parent,
$.post(updateURL, { old_parent: old_parent,
keys: [movedNode.id], old_position: data.old_position
target: data.position, })
parent: parent, .done(function(response) {
after: after, if (response.result !== "success") {
before: before alert("Server error:" + response.result);
}) rollback = true;
.done(function(response) { thisTree.move_node(movedNode, data.old_parent, data.old_position);
if (response.result !== "success") { }
alert("Server error:" + response.result); else {
if (parent_node.data && !parent_node.data.draggable) {
// The parent node is not draggable.
// We have to update all duplicated nodes
// by refreshing the whole tree.
thisTree.refresh();
}
else {
// Update the nodes changed in the server side.
if (response.updated_nodes) {
let update_nodes = response.updated_nodes;
for (const id in update_nodes) {
let node = thisTree.get_node(id);
if (node) {
node.data.weight = update_nodes[id];
}
}
//Refresh the tree without reloading data from server.
thisTree.sort(parent_node, true);
thisTree.redraw(true);
}
}
let message = Drupal.t('@node is moved to position @position under @parent', {'@node': data.node.text, '@parent': parentText, '@position': data.position + 1});
// Inform user the movement.
drupalMessages.clear();
drupalMessages.add(message);
}
})
.fail(function() {
drupalMessages.clear();
drupalMessages.add(Drupal.t("Can't connect to the server."), {type: 'error'});
rollback = true; rollback = true;
thisTree.move_node(movedNode, data.old_parent, data.old_position); thisTree.move_node(movedNode, data.old_parent, data.old_position);
} });
}) }
.fail(function() {
alert("Error: Can't connect to the server."); // Check if confirmation dialog is enabled.
rollback = true; if (typeof confirm !== 'undefined' && confirm !== false) {
thisTree.move_node(movedNode, data.old_parent, data.old_position); // Confirmation dialog enabled.
}); let modalTitle = Drupal.t('Confirm move?');
let modalMessage = Drupal.t('Move <em class="placeholder">@node</em> to position @position under <em class="placeholder">@parent</em>?', { '@node': data.node.text, '@parent': parentText, '@position': data.position + 1 });
modalConfirmation(modalTitle, modalMessage, moveTreeItem, function () {
// Callback when confirmation is denied.
rollback = true;
thisTree.move_node(movedNode, data.old_parent, data.old_position);
});
} else {
// Confirmation dialog disabled.
moveTreeItem()
}
} }
else { else {
rollback = false; rollback = false;
} }
}); });
// Node selected event. treeContainer.on('ready.jstree open_node.jstree move_node.jstree search.jstree clear_search.jstree redraw.jstree', function (event, data) {
treeContainer.on("select_node.jstree", function(event, data) { Drupal.attachBehaviors(event.target);
var href = data.node.a_attr.href;
if (newWindow) {
window.open(href, "_self");
}
else {
Drupal.ajax({
url: href,
success: function(response) {
response.forEach(function(element) {
if (element.command && element.data) {
if (element.command === 'insert' && element.selector === null) {
$popDialog[offset] = $('<div>' + element.data + '</div>').appendTo('body');
}
}
});
if ($popDialog[offset]) {
let margin = parseInt(offset * 10 % 40);
let options = {
title: 'Edit ' + data.node.text,
minWidth: 600,
draggable: true,
resizable: true,
autoResize: false,
position: {'my': 'right bottom', 'at':'right-' + margin + ' bottom-' + margin},
};
Drupal.dialog($popDialog[offset++], options).show();
}
}
}).execute();
}
}); });
// Search filter box. // Search filter box.
...@@ -154,4 +204,51 @@ ...@@ -154,4 +204,51 @@
}); });
} }
}; };
})(jQuery, Drupal);
/**
* Generic modal helper function.
*
* @param {string} title - The title for the confirm dialog.
* @param {string} message - The main message for the confirm dialog.
* @param {function} accept - Callback fired when the user answers positive.
* @param {function} deny - Callback fired when the user answers negative.
* @returns {Object} - A jQuery dialog object.
*/
function modalConfirmation(title, message, accept, deny) {
let proceed = false;
let modalConfirmationForm = $('<div></div>').appendTo('body')
.html(message)
.dialog({
modal: true,
title: title,
autoOpen: false,
width: 400,
resizable: false,
sticky: true,
closeOnEscape: true,
dialogClass: "hm-confirm",
buttons: [
{
class: 'button button--primary',
text: Drupal.t('Yes'),
click: function () {
proceed = true;
$(this).dialog('close');
}
},
{
class: 'button',
text: Drupal.t('No'),
click: function () {
$(this).dialog('close');
}
}
],
close: function () {
proceed ? accept() : deny();
}
});
return modalConfirmationForm.dialog('open');
}
})(jQuery, Drupal, once);
...@@ -2,74 +2,86 @@ ...@@ -2,74 +2,86 @@
namespace Drupal\hierarchy_manager\Controller; namespace Drupal\hierarchy_manager\Controller;
use Drupal\Core\Url;
use Drupal\Core\Access\CsrfTokenGenerator; use Drupal\Core\Access\CsrfTokenGenerator;
use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityRepository;
use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Menu\MenuLinkTreeInterface;
use Drupal\Core\Menu\MenuLinkManagerInterface; use Drupal\Core\Menu\MenuLinkManagerInterface;
use Drupal\Core\Menu\MenuLinkTreeInterface;
use Drupal\Core\Menu\MenuTreeParameters; use Drupal\Core\Menu\MenuTreeParameters;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
/**
* Menu item feeding controller.
*/
class HmMenuController extends ControllerBase { class HmMenuController extends ControllerBase {
/** /**
* CSRF Token. * CSRF Token.
* *
* @var \Drupal\Core\Access\CsrfTokenGenerator * @var \Drupal\Core\Access\CsrfTokenGenerator
*/ */
protected $csrfToken; protected $csrfToken;
/** /**
* The menu_link_content storage handler. * The menu_link_content storage handler.
* *
* @var \Drupal\menu_link_content\MenuLinkContentStorageInterface * @var \Drupal\menu_link_content\MenuLinkContentStorageInterface
*/ */
protected $storageController; protected $storageController;
/** /**
* The hierarchy manager plugin type manager. * The hierarchy manager plugin type manager.
* *
* @var \Drupal\hierarchy_manager\PluginTypeManager * @var \Drupal\hierarchy_manager\PluginTypeManager
*/ */
protected $hmPluginTypeManager; protected $hmPluginTypeManager;
/** /**
* The menu tree service. * The menu tree service.
* *
* @var \Drupal\Core\Menu\MenuLinkTreeInterface * @var \Drupal\Core\Menu\MenuLinkTreeInterface
*/ */
protected $menuTree; protected $menuTree;
/** /**
* The menu tree array. * The menu tree array.
* *
* @var array * @var array
*/ */
protected $overviewTree = []; protected $overviewTree = [];
/** /**
* The menu link manager. * The menu link manager.
* *
* @var \Drupal\Core\Menu\MenuLinkManagerInterface * @var \Drupal\Core\Menu\MenuLinkManagerInterface
*/ */
protected $menuLinkManager; protected $menuLinkManager;
/**
* The entity repository object.
*
* @var \Drupal\Core\Entity\EntityRepository
*/
protected $entityRepository;
/** /**
* {@inheritdoc} * {@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, EntityRepository $entity_repository) {
$this->csrfToken = $csrfToken; $this->csrfToken = $csrfToken;
$this->entityTypeManager = $entity_type_manager; $this->entityTypeManager = $entity_type_manager;
$this->storageController = $entity_type_manager->getStorage('menu_link_content'); $this->storageController = $entity_type_manager->getStorage('menu_link_content');
$this->hmPluginTypeManager = $plugin_type_manager; $this->hmPluginTypeManager = $plugin_type_manager;
$this->menuTree = $menu_tree; $this->menuTree = $menu_tree;
$this->menuLinkManager = $menu_link_manager; $this->menuLinkManager = $menu_link_manager;
$this->entityRepository = $entity_repository;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -79,10 +91,11 @@ class HmMenuController extends ControllerBase { ...@@ -79,10 +91,11 @@ class HmMenuController extends ControllerBase {
$container->get('entity_type.manager'), $container->get('entity_type.manager'),
$container->get('hm.plugin_type_manager'), $container->get('hm.plugin_type_manager'),
$container->get('menu.link_tree'), $container->get('menu.link_tree'),
$container->get('plugin.manager.menu.link') $container->get('plugin.manager.menu.link'),
$container->get('entity.repository')
); );
} }
/** /**
* Callback for menu tree json. * Callback for menu tree json.
* *
...@@ -94,42 +107,42 @@ class HmMenuController extends ControllerBase { ...@@ -94,42 +107,42 @@ class HmMenuController extends ControllerBase {
public function menuTreeJson(Request $request, string $mid) { public function menuTreeJson(Request $request, string $mid) {
// Access token. // Access token.
$token = $request->get('token'); $token = $request->get('token');
if (empty($token) || !$this->csrfToken->validate($token, $mid)) { if (empty($token) || !$this->csrfToken->validate($token, $mid)) {
return new Response($this->t('Access denied!')); return new Response($this->t('Access denied!'));
} }
$parent = $request->get('parent'); $parent = $request->get('parent');
$depth = $request->get('depth'); $depth = $request->get('depth');
$destination = $request->get('destination'); $destination = $request->get('destination');
if (empty($depth)) { if (empty($depth)) {
$depth = 0; $depth = 0;
} }
else { else {
$depth = intval($depth); $depth = intval($depth);
} }
if (empty($parent)) { if (empty($parent)) {
$parent = ''; $parent = '';
} }
// We indicate that a menu administrator is running the menu access check. // We indicate that a menu administrator is running the menu access check.
$request->attributes->set('_menu_admin', TRUE); $request->attributes->set('_menu_admin', TRUE);
$tree = $this->loadMenuTree($mid, $parent, $depth, $destination); $tree = $this->loadMenuTree($mid, $parent, $depth, $destination);
// menu access check done. // Menu access check done.
$request->attributes->set('_menu_admin', FALSE); $request->attributes->set('_menu_admin', FALSE);
if ($tree) { if ($tree) {
// Display plugin instance. // Display plugin instance.
$display_plugin = $this->getDisplayPlugin(); $display_plugin = $this->getDisplayPlugin();
if (empty($display_plugin)) { if (empty($display_plugin)) {
return new JsonResponse(['result' => 'Display profile has not been set up.']); return new JsonResponse(['result' => 'Display profile has not been set up.']);
} }
if (method_exists($display_plugin, 'treeData')) { if (method_exists($display_plugin, 'treeData')) {
// Transform the tree data to the structure // Transform the tree data to the structure
// that display plugin accepts. // that display plugin accepts.
...@@ -138,20 +151,20 @@ class HmMenuController extends ControllerBase { ...@@ -138,20 +151,20 @@ class HmMenuController extends ControllerBase {
else { else {
$tree_data = $tree; $tree_data = $tree;
} }
return new JsonResponse($tree_data); return new JsonResponse($tree_data);
} }
return new JsonResponse([]); return new JsonResponse([]);
} }
/** /**
* Callback for taxonomy tree json. * Callback for taxonomy tree json.
* *
* @param \Symfony\Component\HttpFoundation\Request $request * @param \Symfony\Component\HttpFoundation\Request $request
* Http request object. * Http request object.
* @param string $vid * @param string $mid
* Vocabulary ID. * Menu ID.
*/ */
public function updateMenuLinks(Request $request, string $mid) { public function updateMenuLinks(Request $request, string $mid) {
// Access token. // Access token.
...@@ -159,31 +172,27 @@ class HmMenuController extends ControllerBase { ...@@ -159,31 +172,27 @@ class HmMenuController extends ControllerBase {
if (empty($token) || !$this->csrfToken->validate($token, $mid)) { if (empty($token) || !$this->csrfToken->validate($token, $mid)) {
return new Response($this->t('Access denied!')); return new Response($this->t('Access denied!'));
} }
$old_position = (int) $request->get('old_position');
$target_position = $request->get('target'); $target_position = $request->get('target');
$parent = $request->get('parent'); $parent = $request->get('parent');
$updated_links = $request->get('keys'); $updated_links = $request->get('keys');
//$after = $request->get('after');
$before = $request->get('before');
$all_siblings = []; $all_siblings = [];
$insert_after = TRUE;
if (is_array($updated_links) && !empty($updated_links)) { if (is_array($updated_links) && !empty($updated_links)) {
if (empty($parent)) { if (empty($parent)) {
// Root is the parent. // Root is the parent.
$parent = ''; $parent = '';
// All children menu links (depth = 1).
$parent_links = $children = $this->loadMenuLinkObjs($mid, $parent, 1); $parent_links = $children = $this->loadMenuLinkObjs($mid, $parent, 1);
} }
else { else {
// All children menu links (depth = 1).
$parent_links = $this->loadMenuLinkObjs($mid, $parent, 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.
if (empty($parent_links)) { if (empty($parent_links)) {
// The parent menu doesn't exist. // The parent menu doesn't exist.
return new JsonResponse(['result' => 'fail']); return new JsonResponse(['result' => 'fail']);
} }
if (empty($children)) { if (empty($children)) {
...@@ -195,59 +204,57 @@ class HmMenuController extends ControllerBase { ...@@ -195,59 +204,57 @@ class HmMenuController extends ControllerBase {
// The parent menu has children. // The parent menu has children.
$target_position = intval($target_position); $target_position = intval($target_position);
$position = 0;
foreach ($children as $child) { foreach ($children as $child) {
$link = $child->link; $link = $child->link;
$link_id = $link->getPLuginId(); $link_id = $link->getPluginId();
// Figure out if the new links are inserted $all_siblings[$link_id] = $link->getWeight();
// after the target position.
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. // 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);
$new_hierarchy = $this->hmPluginTypeManager->updateHierarchy($target_position, $all_siblings, $updated_links, $insert_after);
// Update all links need to update. // Update all links need to update.
foreach ($new_hierarchy as $link_id => $link_weight) { foreach ($new_hierarchy as $link_id => $link_weight) {
$this->menuLinkManager->updateDefinition($link_id, ['weight' => $link_weight, 'parent' => $parent]); $this->menuLinkManager->updateDefinition($link_id, ['weight' => $link_weight, 'parent' => $parent]);
} }
return new JsonResponse(['result' => 'success']); $result = [
'result' => 'success',
'updated_nodes' => $new_hierarchy,
];
return new JsonResponse($result);
} }
return new JsonResponse(['result' => 'fail']); return new JsonResponse(['result' => 'fail']);
} }
/** /**
* Get a display plugin instance. * Get a display plugin instance.
* *
* @return NULL|object * @return null|object
* The display plugin instance.
*/ */
protected function getDisplayPlugin() { protected function getDisplayPlugin() {
$display_profile = $this->hmPluginTypeManager->getDisplayProfile('hm_setup_menu'); $display_profile = $this->hmPluginTypeManager->getDisplayProfile('hm_setup_menu');
return $this->hmPluginTypeManager->getDisplayPluginInstance($display_profile); return $this->hmPluginTypeManager->getDisplayPluginInstance($display_profile);
} }
/** /**
* Load menu links into one array. * Load menu links into one array.
* *
* @param string $mid * @param string $mid
* The menu ID. * The menu ID.
* @param string $parent * @param string $parent
* parent id * Parent id.
* @param int $depth * @param int $depth
* The max depth loaded. * The max depth loaded.
* @param string $destination * @param string $destination
* The destination of edit link. * The destination of edit link.
*/ */
protected function loadMenuTree(string $mid, string $parent, int $depth = 0, string $destination = '') { protected function loadMenuTree(string $mid, string $parent, int $depth = 0, string $destination = '') {
$tree = $this->loadMenuLinkObjs($mid, $parent, $depth); $tree = $this->loadMenuLinkObjs($mid, $parent, $depth);
// Load all menu links into one array. // Load all menu links into one array.
$tree = $this->buildMenuLinkArray($tree); $tree = $this->buildMenuLinkArray($tree);
...@@ -257,28 +264,29 @@ class HmMenuController extends ControllerBase { ...@@ -257,28 +264,29 @@ class HmMenuController extends ControllerBase {
$element['url'] = $element['url'] . '?destination=' . $destination; $element['url'] = $element['url'] . '?destination=' . $destination;
} }
$links[] = $this->hmPluginTypeManager->buildHierarchyItem( $links[] = $this->hmPluginTypeManager->buildHierarchyItem(
$element['id'], $element['id'],
$element['title'], $element['title'],
$element['parent'], $element['parent'],
$element['url']); $element['url'],
$element['status'],
$element['weight']
);
} }
return $links; return $links;
} }
/** /**
* Load menu links into one array. * Load menu links into one array.
* *
* @param string $mid * @param string $mid
* The menu ID. * The menu ID.
* @param string $parent * @param string $parent
* parent id * Parent id.
* @param int $depth * @param int $depth
* The max depth loaded. * The max depth loaded.
* @param string $destination
* The destination of edit link.
*/ */
protected function loadMenuLinkObjs(string $mid, string $parent, int $depth = 0) { protected function loadMenuLinkObjs(string $mid, string $parent, int $depth = 0) {
$menu_para = new MenuTreeParameters(); $menu_para = new MenuTreeParameters();
if (!empty($depth)) { if (!empty($depth)) {
$menu_para->setMaxDepth($depth); $menu_para->setMaxDepth($depth);
...@@ -291,10 +299,10 @@ class HmMenuController extends ControllerBase { ...@@ -291,10 +299,10 @@ class HmMenuController extends ControllerBase {
['callable' => 'menu.default_tree_manipulators:checkAccess'], ['callable' => 'menu.default_tree_manipulators:checkAccess'],
['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'], ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
]; ];
return $tree = $this->menuTree->transform($tree, $manipulators); return $tree = $this->menuTree->transform($tree, $manipulators);
} }
/** /**
* Recursive helper function for loadMenuTree(). * Recursive helper function for loadMenuTree().
* *
...@@ -305,37 +313,38 @@ class HmMenuController extends ControllerBase { ...@@ -305,37 +313,38 @@ class HmMenuController extends ControllerBase {
* The menu links array. * The menu links array.
*/ */
protected function buildMenuLinkArray($tree) { protected function buildMenuLinkArray($tree) {
// $tree_access_cacheability = new CacheableMetadata(); // $tree_access_cacheability = new CacheableMetadata();
foreach ($tree as $element) { foreach ($tree as $element) {
// $tree_access_cacheability = $tree_access_cacheability->merge(CacheableMetadata::createFromObject($element->access)); // $tree_access_cacheability = $tree_access_cacheability->merge(CacheableMetadata::createFromObject($element->access));
// Only load accessible links. // Only load accessible links.
if (!$element->access->isAllowed()) { if (!$element->access->isAllowed()) {
continue; continue;
} }
/** @var \Drupal\Core\Menu\MenuLinkInterface $link */ /** @var \Drupal\Core\Menu\MenuLinkInterface $link */
$link = $element->link; $link = $element->link;
if ($link) { if ($link) {
// The id consistes of plugin ID and link ID.
$id = $link->getPluginId(); $id = $link->getPluginId();
$this->overviewTree[$id]['id'] = $id; $this->overviewTree[$id]['id'] = $id;
$this->overviewTree[$id]['status'] = $link->isEnabled();
if (!$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. // @todo Remove this in https://www.drupal.org/node/2568785.
elseif ($id === 'user.logout') { 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. // @todo Remove this in https://www.drupal.org/node/2568785.
elseif (($url = $link->getUrlObject()) && $url->isRouted() && $url->getRouteName() == 'user.page') { 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 { else {
$this->overviewTree[$id]['title'] = $link->getTitle(); $this->overviewTree[$id]['title'] = $link->getTitle();
} }
$this->overviewTree[$id]['parent'] = $link->getParent(); $parent_id = $link->getParent();
$this->overviewTree[$id]['parent'] = $parent_id;
$this->overviewTree[$id]['weight'] = $link->getWeight();
// Build the edit url. // Build the edit url.
// Allow for a custom edit link per plugin. // Allow for a custom edit link per plugin.
$edit_route = $link->getEditRoute(); $edit_route = $link->getEditRoute();
...@@ -347,17 +356,13 @@ class HmMenuController extends ControllerBase { ...@@ -347,17 +356,13 @@ class HmMenuController extends ControllerBase {
$this->overviewTree[$id]['url'] = Url::fromRoute('menu_ui.link_edit', ['menu_link_plugin' => $link->getPluginId()])->toString(); $this->overviewTree[$id]['url'] = Url::fromRoute('menu_ui.link_edit', ['menu_link_plugin' => $link->getPluginId()])->toString();
} }
} }
if ($element->subtree) { if ($element->subtree) {
$this->buildMenuLinkArray($element->subtree); $this->buildMenuLinkArray($element->subtree);
} }
} }
/* $tree_access_cacheability
->merge(CacheableMetadata::createFromRenderArray($this->overviewTree))
->applyTo($form); */
return $this->overviewTree; return $this->overviewTree;
} }
}
}
...@@ -2,18 +2,20 @@ ...@@ -2,18 +2,20 @@
namespace Drupal\hierarchy_manager\Controller; namespace Drupal\hierarchy_manager\Controller;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\CsrfTokenGenerator; use Drupal\Core\Access\CsrfTokenGenerator;
use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\taxonomy\VocabularyInterface; use Drupal\Core\Session\AccountInterface;
use Drupal\taxonomy\Entity\Term; use Drupal\taxonomy\Entity\Term;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
/** /**
* Taxononmy controller class. * Taxonomy feeding controller class.
*/ */
class HmTaxonomyController extends ControllerBase { class HmTaxonomyController extends ControllerBase {
...@@ -30,7 +32,7 @@ class HmTaxonomyController extends ControllerBase { ...@@ -30,7 +32,7 @@ class HmTaxonomyController extends ControllerBase {
* @var \Drupal\taxonomy\TermStorageInterface * @var \Drupal\taxonomy\TermStorageInterface
*/ */
protected $storageController; protected $storageController;
/** /**
* The hierarchy manager plugin type manager. * The hierarchy manager plugin type manager.
* *
...@@ -38,13 +40,21 @@ class HmTaxonomyController extends ControllerBase { ...@@ -38,13 +40,21 @@ class HmTaxonomyController extends ControllerBase {
*/ */
protected $hmPluginTypeManager; protected $hmPluginTypeManager;
/**
* The entity repository.
*
* @var \Drupal\Core\Entity\EntityRepositoryInterface
*/
protected $entityRepository;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function __construct(CsrfTokenGenerator $csrfToken, EntityTypeManagerInterface $entity_type_manager, $plugin_type_manager) { public function __construct(CsrfTokenGenerator $csrfToken, EntityTypeManagerInterface $entity_type_manager, EntityRepositoryInterface $entity_repository, $plugin_type_manager) {
$this->csrfToken = $csrfToken; $this->csrfToken = $csrfToken;
$this->entityTypeManager = $entity_type_manager; $this->entityTypeManager = $entity_type_manager;
$this->entityRepository = $entity_repository;
$this->storageController = $entity_type_manager->getStorage('taxonomy_term'); $this->storageController = $entity_type_manager->getStorage('taxonomy_term');
$this->hmPluginTypeManager = $plugin_type_manager; $this->hmPluginTypeManager = $plugin_type_manager;
} }
...@@ -56,10 +66,26 @@ class HmTaxonomyController extends ControllerBase { ...@@ -56,10 +66,26 @@ class HmTaxonomyController extends ControllerBase {
return new static( return new static(
$container->get('csrf_token'), $container->get('csrf_token'),
$container->get('entity_type.manager'), $container->get('entity_type.manager'),
$container->get('entity.repository'),
$container->get('hm.plugin_type_manager') $container->get('hm.plugin_type_manager')
); );
} }
/**
* Access check callback for taxonomy tree json.
*
* @param \Drupal\Core\Session\AccountInterface $account
* User account.
* @param string $vid
* Vocabulary ID.
*/
public function access(AccountInterface $account, string $vid) {
if ($account->hasPermission('administer taxonomy')) {
return AccessResult::allowed();
}
return AccessResult::allowedIfHasPermission($account, "edit terms in {$vid}");
}
/** /**
* Callback for taxonomy tree json. * Callback for taxonomy tree json.
* *
...@@ -73,6 +99,12 @@ class HmTaxonomyController extends ControllerBase { ...@@ -73,6 +99,12 @@ class HmTaxonomyController extends ControllerBase {
$token = $request->get('token'); $token = $request->get('token');
// The term array will be returned. // The term array will be returned.
$term_array = []; $term_array = [];
// Store the number of each term id present.
$ids = [];
// Store terms that have ambiguous parents.
$am_terms = [];
// Store all terms only have single ancestor.
$single_parent = [];
if (empty($token) || !$this->csrfToken->validate($token, $vid)) { if (empty($token) || !$this->csrfToken->validate($token, $vid)) {
return new Response($this->t('Access denied!')); return new Response($this->t('Access denied!'));
...@@ -80,44 +112,131 @@ class HmTaxonomyController extends ControllerBase { ...@@ -80,44 +112,131 @@ class HmTaxonomyController extends ControllerBase {
$parent = $request->get('parent') ?: 0; $parent = $request->get('parent') ?: 0;
$depth = $request->get('depth'); $depth = $request->get('depth');
$destination = $request->get('destination'); $destination = $request->get('destination');
if(!empty($depth)) { if (!empty($depth)) {
$depth = intval($depth); $depth = intval($depth);
} }
$vocabulary_hierarchy = $this->storageController->getVocabularyHierarchyType($vid); $tree = $this->storageController->loadTree($vid, $parent, $depth, TRUE);
// Taxonomy tree must not be multiple parent tree. $access_control_handler = $this->entityTypeManager->getAccessControlHandler('taxonomy_term');
if ($vocabulary_hierarchy !== VocabularyInterface::HIERARCHY_MULTIPLE) {
$tree = $this->storageController->loadTree($vid, $parent, $depth, TRUE);
$access_control_handler = $this->entityTypeManager->getAccessControlHandler('taxonomy_term'); foreach ($tree as $term) {
if ($term instanceof Term) {
foreach ($tree as $term) { $term = $this->entityRepository->getTranslationFromContext($term);
if ($term instanceof Term) { // User can only access the terms that they can update.
// User can only access the terms that they can update. if ($access_control_handler->access($term, 'update')) {
if ($access_control_handler->access($term, 'update')) { if (empty($destination)) {
if (empty($destination)) { $url = $term->toUrl('edit-form')->toString();
$url = $term->toUrl('edit-form')->toString(); }
else {
$url = $term->toUrl('edit-form', ['query' => ['destination' => $destination]])->toString();
}
$term_parent = $term->parents;
$id = $term->id();
$count_parent = count($term_parent);
if (isset($ids[$id])) {
if ($ids[$id] === 0 && isset($single_parent[$id])) {
// Update previous term in the term array
// which has the same ID. Make it not draggable.
$term_array[$single_parent[$id]]['draggable'] = FALSE;
}
$ids[$id]++;
$term_id = $id . '_' . $ids[$id];
}
else {
$ids[$id] = 0;
$term_id = $id;
}
// If a taxonomy term has multiple parents,
// It will present multiple times under different parents.
// So the term id will be duplicated.
// The solution is to format the term id as following,
// {term_id}_{parent_index}.
if ($count_parent > 1) {
$draggable = FALSE;
// This term has an ancestor with multiple parents.
if ($ids[$id] === $count_parent) {
// Put into the ambiguous array.
// Will solve it later.
$am_terms[] = [
'solved' => FALSE,
'id' => $term_id,
'label' => $term->label(),
'parent' => $term_parent,
'url' => $url,
'publish' => $term->isPublished(),
'weight' => $term->getWeight(),
'draggable' => FALSE,
];
continue;
}
$parent_id = $term_parent[$ids[$id]];
}
else {
if ($ids[$id]) {
// The parent has multiple grandparent.
$parent_id = $term_parent[0] . '_' . $ids[$id];
$draggable = FALSE;
} }
else { else {
$url = $term->toUrl('edit-form', ['query' => ['destination' => $destination]])->toString(); // The parent doesn't have multiple grandparent.
$parent_id = $term_parent[0];
$draggable = TRUE;
// At this point, we still don't know
// if this term has multiple ancestors or not.
// So keep the index of term array for later update
// if needed.
$single_parent[$id] = count($term_array);
} }
}
$term_array[] = $this->hmPluginTypeManager->buildHierarchyItem(
$term_id,
$term->label(),
$parent_id,
$url,
$term->isPublished(),
$term->getWeight(),
$draggable
);
}
}
}
// Figure out the parent id for terms in the ambiguous term array.
do {
$found = FALSE;
foreach ($am_terms as $key => $term) {
if ($term['solved']) {
continue;
}
$parent_ids = $term['parent'];
foreach ($parent_ids as $id) {
if ($ids[$id]) {
// Found the parent with multiple grandparent.
$term_array[] = $this->hmPluginTypeManager->buildHierarchyItem( $term_array[] = $this->hmPluginTypeManager->buildHierarchyItem(
$term->id(), $term['id'],
$term->label(), $term['label'],
$term->parents[0], $id . '_' . $ids[$id],
$url); $term['url'],
$term['publish'],
$term['weight'],
$term['draggable']
);
$found = TRUE;
// Remove this parent from ids array.
$ids[$id]--;
$am_terms[$key]['solved'] = TRUE;
continue 2;
} }
} }
} }
} } while ($found);
// Display profile. // Display profile.
$display_profile = $this->hmPluginTypeManager->getDisplayProfile('hm_setup_taxonomy'); $display_profile = $this->hmPluginTypeManager->getDisplayProfile('hm_setup_taxonomy');
// Display plugin instance. // Display plugin instance.
$display_plugin = $this->hmPluginTypeManager->getDisplayPluginInstance($display_profile); $display_plugin = $this->hmPluginTypeManager->getDisplayPluginInstance($display_profile);
if (empty($display_plugin)) { if (empty($display_plugin)) {
return new JsonResponse(['result' => 'Display profile has not been set up.']); return new JsonResponse(['result' => 'Display profile has not been set up.']);
} }
...@@ -150,65 +269,76 @@ class HmTaxonomyController extends ControllerBase { ...@@ -150,65 +269,76 @@ class HmTaxonomyController extends ControllerBase {
} }
$target_position = $request->get('target'); $target_position = $request->get('target');
$old_position = (int) $request->get('old_position');
$old_parent_id = $request->get('old_parent');
// Remove the parent index from the parent id.
$old_parent_id = explode('_', $old_parent_id)[0];
$parent_id = $request->get('parent'); $parent_id = $request->get('parent');
// Remove the parent index from the parent id.
$parent_id = explode('_', $parent_id)[0];
$updated_terms = $request->get('keys'); $updated_terms = $request->get('keys');
//$after = $request->get('after');
$before = $request->get('before');
$success = FALSE; $success = FALSE;
$insert_after = TRUE;
$all_siblings = []; $all_siblings = [];
if (is_array($updated_terms) && !empty($updated_terms)) { if (is_array($updated_terms) && !empty($updated_terms)) {
// Remove the parent index from the term id.
for ($i = 0; $i < count($updated_terms); $i++) {
$updated_terms[$i] = explode('_', $updated_terms[$i])[0];
}
// Taxonomy access control. // Taxonomy access control.
$access_control_handler = $this->entityTypeManager->getAccessControlHandler('taxonomy_term'); $access_control_handler = $this->entityTypeManager->getAccessControlHandler('taxonomy_term');
// Children of the parent term in weight and name alphabetically order. // Children of the parent term in weight and name alphabetically order.
$children = $this->storageController->loadTree($vid, $parent_id, 1); $children = $this->storageController->loadTree($vid, $parent_id, 1);
if (empty($children)) { if (!empty($children)) {
if (Term::load($parent_id)) {
// The parent term hasn't had any children.
}
else {
// The parent term doesn't exist.
return new JsonResponse(['result' => 'fail']);
}
}
else {
// The parent term has children. // The parent term has children.
$target_position = intval($target_position); $target_position = intval($target_position);
$position = 0;
foreach ($children as $child) { foreach ($children as $child) {
// Figure out if the new links are inserted
// after the target position.
if ($position++ == $target_position && $child->tid !== $before) {
$insert_after = FALSE;
}
$all_siblings[$child->tid] = (int) $child->weight; $all_siblings[$child->tid] = (int) $child->weight;
} }
} }
$new_hierarchy = $this->hmPluginTypeManager->updateHierarchy($target_position, $all_siblings, $updated_terms, $insert_after); $new_hierarchy = $this->hmPluginTypeManager->updateHierarchy($target_position, $all_siblings, $updated_terms, $old_position);
$tids = array_keys($new_hierarchy); $tids = array_keys($new_hierarchy);
// Load all terms needed to update. // Load all terms needed to update.
$terms = Term::loadMultiple($tids); $terms = Term::loadMultiple($tids);
// Update all terms. // Update all terms.
foreach ($terms as $term) { foreach ($terms as $term) {
if ($access_control_handler->access($term, 'update')) { if ($access_control_handler->access($term, 'update')) {
$term->set('parent', ['target_id' => $parent_id]);
$term->setWeight($new_hierarchy[$term->id()]); $term->setWeight($new_hierarchy[$term->id()]);
// Update the parent IDs.
if (in_array($term->id(), $updated_terms)) {
$parents = [];
$same_parent = $old_parent_id === $parent_id;
// Update the parent only if it is changed.
if (!$same_parent) {
foreach ($term->get('parent') as $parent) {
$tid = $parent->get('target_id')->getValue();
if ($tid === $old_parent_id) {
$tid = $parent_id;
}
elseif ($tid === $parent_id) {
continue;
}
$parents[] = ['target_id' => $tid];
}
// Set the new parent.
$term->set('parent', $parents);
}
}
$success = $term->save(); $success = $term->save();
} }
} }
} }
if ($success) { $result = [
return new JsonResponse(['result' => 'success']); 'result' => $success ? 'success' : 'fail',
} 'updated_nodes' => $new_hierarchy,
];
return new JsonResponse(['result' => 'fail']); return new JsonResponse($result);
} }
} }
...@@ -34,6 +34,7 @@ use Drupal\Core\Config\Entity\ConfigEntityBase; ...@@ -34,6 +34,7 @@ use Drupal\Core\Config\Entity\ConfigEntityBase;
* "label", * "label",
* "plugin", * "plugin",
* "config", * "config",
* "confirm",
* }, * },
* links = { * links = {
* "canonical" = "/admin/structure/hm_display_profile/{hm_display_profile}", * "canonical" = "/admin/structure/hm_display_profile/{hm_display_profile}",
...@@ -66,11 +67,19 @@ class HmDisplayProfile extends ConfigEntityBase implements HmDisplayProfileInter ...@@ -66,11 +67,19 @@ class HmDisplayProfile extends ConfigEntityBase implements HmDisplayProfileInter
* @var string * @var string
*/ */
protected $plugin; protected $plugin;
/** /**
* The configurations * The configurations.
* *
* @var string * @var string
*/ */
protected $config; protected $config;
/**
* The confirmation option.
*
* @var bool
*/
protected $confirm = FALSE;
} }
...@@ -2,14 +2,12 @@ ...@@ -2,14 +2,12 @@
namespace Drupal\hierarchy_manager\Form; namespace Drupal\hierarchy_manager\Form;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\hierarchy_manager\Plugin\HmSetupPluginManager;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Class HMConfigForm. * Hierarchy Manager configuration form.
*/ */
class HMConfigForm extends ConfigFormBase { class HMConfigForm extends ConfigFormBase {
...@@ -20,25 +18,13 @@ class HMConfigForm extends ConfigFormBase { ...@@ -20,25 +18,13 @@ class HMConfigForm extends ConfigFormBase {
*/ */
protected $pluginManagerHmSetup; protected $pluginManagerHmSetup;
/**
* Constructs a new HMConfigForm object.
*/
public function __construct(
ConfigFactoryInterface $config_factory,
HmSetupPluginManager $plugin_manager_hm_hmsetup
) {
parent::__construct($config_factory);
$this->pluginManagerHmSetup = $plugin_manager_hm_hmsetup;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public static function create(ContainerInterface $container) { public static function create(ContainerInterface $container) {
return new static( $instance = parent::create($container);
$container->get('config.factory'), $instance->pluginManagerHmSetup = $container->get('plugin.manager.hm.hmsetup');
$container->get('plugin.manager.hm.hmsetup') return $instance;
);
} }
/** /**
...@@ -90,7 +76,7 @@ class HMConfigForm extends ConfigFormBase { ...@@ -90,7 +76,7 @@ class HMConfigForm extends ConfigFormBase {
$form['setup_plugin_settings'] = [ $form['setup_plugin_settings'] = [
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => $this->t('Setup Plugin Settings'), '#title' => $this->t('Setup Plugin Settings'),
'#descrption' => $this->t('Setup plugin advanced settings.'), '#description' => $this->t('Setup plugin advanced settings.'),
'#tree' => TRUE, '#tree' => TRUE,
]; ];
foreach ($setup_plugins_labels as $key => $val) { foreach ($setup_plugins_labels as $key => $val) {
......
...@@ -5,11 +5,11 @@ namespace Drupal\hierarchy_manager\Form; ...@@ -5,11 +5,11 @@ namespace Drupal\hierarchy_manager\Form;
use Drupal\Component\Utility\Xss; use Drupal\Component\Utility\Xss;
use Drupal\Core\Entity\EntityForm; use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\hierarchy_manager\Plugin\HmDisplayPluginManager; use Drupal\hierarchy_manager\Plugin\HmDisplayPluginManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Class HmDisplayProfileForm. * HmDisplayProfile entity form class.
*/ */
class HmDisplayProfileForm extends EntityForm { class HmDisplayProfileForm extends EntityForm {
...@@ -24,8 +24,8 @@ class HmDisplayProfileForm extends EntityForm { ...@@ -24,8 +24,8 @@ class HmDisplayProfileForm extends EntityForm {
* Constructs a new HmDisplayProfileForm object. * Constructs a new HmDisplayProfileForm object.
*/ */
public function __construct( public function __construct(
HmDisplayPluginManager $plugin_manager_hm_display HmDisplayPluginManager $plugin_manager_hm_display,
) { ) {
$this->pluginManagerHmDisplay = $plugin_manager_hm_display; $this->pluginManagerHmDisplay = $plugin_manager_hm_display;
} }
...@@ -83,14 +83,14 @@ class HmDisplayProfileForm extends EntityForm { ...@@ -83,14 +83,14 @@ class HmDisplayProfileForm extends EntityForm {
'#description' => $this->t('Display plugin that is in charge of rendering the hierarchy view.'), '#description' => $this->t('Display plugin that is in charge of rendering the hierarchy view.'),
'#required' => TRUE, '#required' => TRUE,
]; ];
$form['config'] = array( $form['config'] = [
'#type' => 'hidden', '#type' => 'hidden',
'#value' => $hm_display_profile->get('config'), '#value' => $hm_display_profile->get('config'),
'#attributes' => [ '#attributes' => [
'id' => 'config-value', 'id' => 'config-value',
], ],
); ];
$form['json_editor'] = [ $form['json_editor'] = [
'#type' => 'html_tag', '#type' => 'html_tag',
'#tag' => 'div', '#tag' => 'div',
...@@ -103,7 +103,7 @@ class HmDisplayProfileForm extends EntityForm { ...@@ -103,7 +103,7 @@ class HmDisplayProfileForm extends EntityForm {
'hierarchy_manager/libraries.jsoneditor', 'hierarchy_manager/libraries.jsoneditor',
'hierarchy_manager/feature.hm.jsoneditor', 'hierarchy_manager/feature.hm.jsoneditor',
'hierarchy_manager/libraries.jsoneditor.default-theme', 'hierarchy_manager/libraries.jsoneditor.default-theme',
] ],
], ],
]; ];
} }
...@@ -114,6 +114,13 @@ class HmDisplayProfileForm extends EntityForm { ...@@ -114,6 +114,13 @@ class HmDisplayProfileForm extends EntityForm {
]; ];
} }
$form['confirm'] = [
'#type' => 'checkbox',
'#title' => $this->t('Confirm drag&drop'),
'#default_value' => $hm_display_profile->get("confirm"),
'#description' => $this->t('Displays a dialog when changing the hierarchy.'),
];
return $form; return $form;
} }
...@@ -124,7 +131,7 @@ class HmDisplayProfileForm extends EntityForm { ...@@ -124,7 +131,7 @@ class HmDisplayProfileForm extends EntityForm {
$hm_display_profile = $this->entity; $hm_display_profile = $this->entity;
// User input. // User input.
$input = $form_state->getUserInput(); $input = $form_state->getUserInput();
if (isset($input['config'])) { if (isset($input['config'])) {
// Sanitize the user input. // Sanitize the user input.
$input['config'] = Xss::filter($input['config']); $input['config'] = Xss::filter($input['config']);
...@@ -132,7 +139,10 @@ class HmDisplayProfileForm extends EntityForm { ...@@ -132,7 +139,10 @@ class HmDisplayProfileForm extends EntityForm {
$hm_display_profile->set('config', $input['config']); $hm_display_profile->set('config', $input['config']);
} }
if (isset($input['confirm'])) {
$hm_display_profile->set('confirm', $input['confirm']);
}
$status = $hm_display_profile->save(); $status = $hm_display_profile->save();
switch ($status) { switch ($status) {
......
...@@ -2,51 +2,54 @@ ...@@ -2,51 +2,54 @@
namespace Drupal\hierarchy_manager\Form; namespace Drupal\hierarchy_manager\Form;
use Drupal\Core\Url;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\menu_ui\MenuForm; use Drupal\menu_ui\MenuForm;
use Drupal\system\MenuInterface;
/**
* Hierarchy manager menu plugin configuration form.
*/
class HmMenuForm extends MenuForm { class HmMenuForm extends MenuForm {
/** /**
* The indicator if the menu hierarchy manager is enabled. * The indicator if the menu hierarchy manager is enabled.
* *
* @var bool|NULL * @var bool|null
*/ */
private $isEnabled = NULL; private $isEnabled = NULL;
/** /**
* The hierarchy manager plugin type manager. * The hierarchy manager plugin type manager.
* *
* @var \Drupal\hierarchy_manager\PluginTypeManager * @var \Drupal\hierarchy_manager\PluginTypeManager
*/ */
private $hmPluginTypeManager = NULL; private $hmPluginTypeManager = NULL;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function form(array $form, FormStateInterface $form_state) { public function form(array $form, FormStateInterface $form_state) {
$form = parent::form($form, $form_state); $form = parent::form($form, $form_state);
// If the menu hierarchy manager plugin is enabled. $menu = $this->entity;
// If the menu hierarchy manager plugin is enabled for this menu.
// Override the menu overview form. // Override the menu overview form.
if ($this->isMenuPluginEnabled() && $this->loadPluginManager()) { if ($this->isMenuPluginEnabled($menu) && $this->loadPluginManager()) {
$menu = $this->entity; // Add menu links administration form for existing menus.
if (!$menu->isNew() || $menu->isLocked()) {
// Add menu links administration form for existing menus. // We are removing the menu link overview form
if (!$menu->isNew() || $menu->isLocked()) { // and using our own hierarchy manager tree instead.
// We are removing the menu link overview form // The overview form implemented by Drupal Menu UI module.
// and using our own hierarchy manager tree instead. // @see \Drupal\menu_ui\MenuForm::form()
// The overview form implemented by Drupal Menu UI module unset($form['links']);
// @see \Drupal\menu_ui\MenuForm::form() $form['hm_links'] = $this->buildOverviewTree([], $form_state);
unset($form['links']); }
$form['hm_links'] = $this->buildOverviewTree([], $form_state);
}
} }
return $form; return $form;
} }
/** /**
* Submit handler for the menu overview form. * Submit handler for the menu overview form.
* *
...@@ -56,30 +59,32 @@ class HmMenuForm extends MenuForm { ...@@ -56,30 +59,32 @@ class HmMenuForm extends MenuForm {
* if the menu hierarchy plugin is enabled. * if the menu hierarchy plugin is enabled.
*/ */
protected function submitOverviewForm(array $complete_form, FormStateInterface $form_state) { protected function submitOverviewForm(array $complete_form, FormStateInterface $form_state) {
if (!$this->isMenuPluginEnabled()) { if (!$this->isMenuPluginEnabled($this->entity)) {
parent::submitOverviewForm($complete_form, $form_state); parent::submitOverviewForm($complete_form, $form_state);
} }
} }
/** /**
* Build a menu links overview tree element. * Build a menu links overview tree element.
* *
* @param array $form * @param array $form
* Parent form array. * Parent form array.
* @param FormStateInterface $form_state * @param \Drupal\Core\Form\FormStateInterface $form_state
* Form state object. * Form state object.
* @return NULL|array *
* @return null|array
* The tree elements.
*/ */
protected function buildOverviewTree(array $form, FormStateInterface $form_state) { protected function buildOverviewTree(array $form, FormStateInterface $form_state) {
$display_profile = $this->hmPluginTypeManager->getDisplayProfile('hm_setup_menu'); $display_profile = $this->hmPluginTypeManager->getDisplayProfile('hm_setup_menu');
if (empty($display_profile)) { if (empty($display_profile)) {
return []; return [];
} }
$display_plugin_instance = $this->hmPluginTypeManager->getDisplayPluginInstance($display_profile); $display_plugin_instance = $this->hmPluginTypeManager->getDisplayPluginInstance($display_profile);
if (!empty($display_plugin_instance)) { if (!empty($display_plugin_instance)) {
if (method_exists($display_plugin_instance, 'getForm')) { if (method_exists($display_plugin_instance, 'getForm')) {
// Menu ID. // Menu ID.
...@@ -94,43 +99,65 @@ class HmMenuForm extends MenuForm { ...@@ -94,43 +99,65 @@ class HmMenuForm extends MenuForm {
else { else {
$destination = '/'; $destination = '/';
} }
// Urls // Urls.
$source_url = Url::fromRoute('hierarchy_manager.menu.tree.json', ['mid' => $mid], ['query' => ['token' => $token, 'destination' => $destination]])->toString(); $source_url = Url::fromRoute('hierarchy_manager.menu.tree.json',
$update_url = Url::fromRoute('hierarchy_manager.menu.tree.update', ['mid' => $mid], ['query' => ['token' => $token]])->toString(); ['mid' => $mid],
[
'query' =>
[
'token' => $token,
'destination' => $destination,
],
])->toString();
$update_url = Url::fromRoute('hierarchy_manager.menu.tree.update',
['mid' => $mid],
['query' => ['token' => $token]]
)->toString();
$config = $display_profile->get("config"); $config = $display_profile->get("config");
return $display_plugin_instance->getForm($source_url, $update_url, $form, $form_state, $config); $confirm = $display_profile->get("confirm");
return $display_plugin_instance->getForm($source_url, $update_url, $form, $form_state, $config, $confirm);
} }
} }
return []; return [];
} }
/** /**
* Create a hierarchy manager plugin manager. * Create a hierarchy manager plugin manager.
* *
* @return \Drupal\hierarchy_manager\PluginTypeManager * @return \Drupal\hierarchy_manager\PluginTypeManager
* The plugin manager instance.
*/ */
protected function loadPluginManager() { protected function loadPluginManager() {
if (empty($this->hmPluginTypeManager)) { if (empty($this->hmPluginTypeManager)) {
$this->hmPluginTypeManager = \Drupal::service('hm.plugin_type_manager'); $this->hmPluginTypeManager = \Drupal::service('hm.plugin_type_manager');
} }
return $this->hmPluginTypeManager; return $this->hmPluginTypeManager;
} }
/** /**
* Check if the menu hierarchy plugin is enabled. * Check if the menu hierarchy plugin is enabled.
* *
* @return boolean|NULL * @param \Drupal\system\MenuInterface $menu
* The menu entity.
*
* @return bool|null
* Return TRUE if the menu plugin is enabled, * Return TRUE if the menu plugin is enabled,
* otherwise return FALSE. * otherwise return FALSE.
*/ */
protected function isMenuPluginEnabled() { protected function isMenuPluginEnabled(MenuInterface $menu) {
if ($this->isEnabled === NULL) { if ($this->isEnabled === NULL) {
if ($config = \Drupal::config('hierarchy_manager.hmconfig')) { if ($config = \Drupal::config('hierarchy_manager.hmconfig')) {
if ($allowed_setup_plugins = $config->get('allowed_setup_plugins')) { if ($allowed_setup_plugins = $config->get('allowed_setup_plugins')) {
if (!empty($allowed_setup_plugins['hm_setup_menu'])) { if (!empty($allowed_setup_plugins['hm_setup_menu'])) {
$this->isEnabled = TRUE; $plugin_settings = $config->get('setup_plugin_settings');
if (!empty($plugin_settings['hm_setup_menu'])) {
$enabled_bundles = array_keys(array_filter($plugin_settings['hm_setup_menu']['bundle']));
if (in_array($menu->id(), $enabled_bundles)) {
$this->isEnabled = TRUE;
}
}
} }
else { else {
$this->isEnabled = FALSE; $this->isEnabled = FALSE;
...@@ -138,8 +165,8 @@ class HmMenuForm extends MenuForm { ...@@ -138,8 +165,8 @@ class HmMenuForm extends MenuForm {
} }
} }
} }
return $this->isEnabled; return $this->isEnabled;
} }
}
}
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
namespace Drupal\hierarchy_manager\Form; namespace Drupal\hierarchy_manager\Form;
use Drupal\Core\Url;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\taxonomy\VocabularyInterface; use Drupal\Core\Url;
use Drupal\taxonomy\Form\OverviewTerms; use Drupal\taxonomy\Form\OverviewTerms;
use Drupal\taxonomy\VocabularyInterface;
/** /**
* Taxonomy overview form class. * Taxonomy overview form class.
...@@ -38,11 +38,13 @@ class HmOverviewTerms extends OverviewTerms { ...@@ -38,11 +38,13 @@ class HmOverviewTerms extends OverviewTerms {
// Hierarchy Manager setup plugin configuration. // Hierarchy Manager setup plugin configuration.
$plugin_settings = $config->get('setup_plugin_settings'); $plugin_settings = $config->get('setup_plugin_settings');
if (!empty($plugin_settings['hm_setup_taxonomy'])) { if (!empty($plugin_settings['hm_setup_taxonomy'])) {
// Enabled bundles.
$enabled_bundles = array_keys(array_filter($plugin_settings['hm_setup_taxonomy']['bundle']));
// Display profile ID. // Display profile ID.
$display_profile_id = $plugin_settings['hm_setup_taxonomy']['display_profile']; $display_profile_id = $plugin_settings['hm_setup_taxonomy']['display_profile'];
// Display profile. // Display profile.
$display_profile = $this->entityTypeManager->getStorage('hm_display_profile')->load($display_profile_id); $display_profile = $this->entityTypeManager->getStorage('hm_display_profile')->load($display_profile_id);
if (!empty($display_profile)) { if (!empty($display_profile) && in_array($taxonomy_vocabulary->id(), $enabled_bundles)) {
// Display plugin instance. // Display plugin instance.
$instance = \Drupal::service('plugin.manager.hm.display_plugin')->createInstance($display_profile->get("plugin")); $instance = \Drupal::service('plugin.manager.hm.display_plugin')->createInstance($display_profile->get("plugin"));
if (method_exists($instance, 'getForm')) { if (method_exists($instance, 'getForm')) {
...@@ -59,10 +61,22 @@ class HmOverviewTerms extends OverviewTerms { ...@@ -59,10 +61,22 @@ class HmOverviewTerms extends OverviewTerms {
$destination = '/'; $destination = '/';
} }
// Urls. // Urls.
$source_url = Url::fromRoute('hierarchy_manager.taxonomy.tree.json', ['vid' => $vid], ['query' => ['token' => $token, 'destination' => $destination]])->toString(); $source_url = Url::fromRoute('hierarchy_manager.taxonomy.tree.json',
$update_url = Url::fromRoute('hierarchy_manager.taxonomy.tree.update', ['vid' => $vid], ['query' => ['token' => $token]])->toString(); ['vid' => $vid],
[
'query' => [
'token' => $token,
'destination' => $destination,
],
]
)->toString();
$update_url = Url::fromRoute('hierarchy_manager.taxonomy.tree.update',
['vid' => $vid],
['query' => ['token' => $token]]
)->toString();
$config = $display_profile->get("config"); $config = $display_profile->get("config");
return $instance->getForm($source_url, $update_url, $form, $form_state, $config); $confirm = $display_profile->get('confirm');
return $instance->getForm($source_url, $update_url, $form, $form_state, $config, $confirm);
} }
} }
} }
...@@ -93,9 +107,14 @@ class HmOverviewTerms extends OverviewTerms { ...@@ -93,9 +107,14 @@ class HmOverviewTerms extends OverviewTerms {
// If the taxonomy setup plugin is enabled, // If the taxonomy setup plugin is enabled,
// override the submitForm function. // override the submitForm function.
if (!empty($allowed_setup_plugins['hm_setup_taxonomy'])) { if (!empty($allowed_setup_plugins['hm_setup_taxonomy'])) {
// We don't need to do anything here, $plugin_settings = $config->get('setup_plugin_settings');
// as the taxonomy plugin take it over. $enabled_bundles = array_keys(array_filter($plugin_settings['hm_setup_taxonomy']['bundle']));
return; $vocabulary = $form_state->get(['taxonomy', 'vocabulary']);
if (in_array($vocabulary->id(), $enabled_bundles)) {
// We don't need to do anything here,
// as the taxonomy plugin take it over.
return;
}
} }
} }
} }
......