Skip to content
Snippets Groups Projects
Commit c9c61082 authored by Mingsong Hu's avatar Mingsong Hu
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
Showing
with 1238 additions and 0 deletions
CONTENTS OF THIS FILE
---------------------
* Introduction
* Requirements
* Installation
* Configuration
* Maintainers
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.
This module provides a plugin mechanism to manage 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 Francytree to render the hierarchy tree with filter. The hierarchy tree is draggable which means you can drag and drop a taxonomy term 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 other JavaScript libraries.
REQUIREMENTS
------------
This module requires the following library:
* Fancytree JS (This module will automatically load this library from romte CDN if it wasn't hosted locally under /libraries/jquery.fancytree/ folder)
INSTALLATION
------------
* Install as you would normally install a contributed Drupal module.
CONFIGURATION
-------------
* 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.
* 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
-----------
Mingsong Hu (Mingsong) - https://www.drupal.org/u/mingsong
\ No newline at end of file
hierarchy_manager:
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'
name: Hierarchy Manager
description: Provides API and plugins to build hierarchy views for entites such as taxonomy or menu.
package: Administration
type: module
core: 8.x
\ No newline at end of file
# Feature libraries.
feature.hm.fancytree:
js:
js/Plugin/fancytree/hm.fancytree.js: {}
dependencies:
- hierarchy_manager/libraries.jquery.fancytree
# External libraries.
libraries.jquery.fancytree:
remote: https://github.com/mar10/fancytree
version: 'v2.31.0'
license:
name: MIT
url: https://github.com/mar10/fancytree/blob/master/LICENSE.txt
gpl-compatible: true
cdn:
https://unpkg.com/jquery.fancytree@2.31.0/dist/
js:
/libraries/jquery.fancytree/jquery.fancytree-all-deps.min.js: {minified: true}
dependencies:
- core/jquery
libraries.jquery.fancytree.skin-win8:
remote: https://github.com/mar10/fancytree
version: 'v2.31.0'
license:
name: MIT
url: https://github.com/mar10/fancytree/blob/master/LICENSE.txt
gpl-compatible: true
cdn:
https://unpkg.com/jquery.fancytree@2.31.0/dist/skin-win8/
css:
component:
/libraries/jquery.fancytree/skin-win8/ui.fancytree.min.css: {}
libraries.jquery.fancytree.skin-bootstrap:
remote: https://github.com/mar10/fancytree
version: 'v2.31.0'
license:
name: MIT
url: https://github.com/mar10/fancytree/blob/master/LICENSE.txt
gpl-compatible: true
cdn:
https://unpkg.com/jquery.fancytree@2.31.0/dist/skin-bootstrap/
css:
component:
/libraries/jquery.fancytree/skin-bootstrap/ui.fancytree.min.css: {}
\ No newline at end of file
entity.hm_display_profile.add_form:
route_name: entity.hm_display_profile.add_form
title: 'Add HM Display Profile Entity'
appears_on:
- entity.hm_display_profile.collection
hierarchy_manager.hm_config_form:
title: 'Hierarchy Manager'
route_name: hierarchy_manager.hm_config_form
description: 'Hierarchy Manager Config'
parent: system.admin_config_ui
weight: 99
# HM Display Profile Entity menu items definition
entity.hm_display_profile.collection:
title: 'HM Display Profile Entity'
route_name: entity.hm_display_profile.collection
description: 'List HM Display Profile Entity (bundles)'
parent: system.admin_structure
weight: 99
<?php
/**
* @file
* General functions and hook implementations for Hierarchy Manager module.
*/
/**
* Implements hook_library_info_alter().
*/
function hierarchy_manager_library_info_alter(array &$libraries, $module) {
if ('hierarchy_manager' == $module) {
// Use CDN instead of all local missing libraries.
// Fancytree min js.
$cdn_library = _hierarchy_manager_use_cdn($libraries, 'libraries.jquery.fancytree', 'js');
if ($cdn_library) {
$libraries['libraries.jquery.fancytree']['js'] = $cdn_library;
}
// Fancytree drag and drop for html 5 js.
$cdn_library = _hierarchy_manager_use_cdn($libraries, 'libraries.jquery.fancytree.dnd5', 'js');
if ($cdn_library) {
$libraries['libraries.jquery.fancytree.dnd5']['js'] = $cdn_library;
}
// Fancytree win-8 theme.
$cdn_library = _hierarchy_manager_use_cdn($libraries, 'libraries.jquery.fancytree.skin-win8', 'css');
if ($cdn_library) {
$libraries['libraries.jquery.fancytree.skin-win8']['css']['component'] = $cdn_library;
}
// Fancytree win-8 theme.
$cdn_library = _hierarchy_manager_use_cdn($libraries, 'libraries.jquery.fancytree.skin-bootstrap', 'css');
if ($cdn_library) {
$libraries['libraries.jquery.fancytree.skin-bootstrap']['css']['component'] = $cdn_library;
}
}
}
/**
* Replace local library with CDN.
*
* @param array $libraries
* The libraries array.
* @param string $library_name
* The library name.
* @param string $type
* The library type.
* @param bool $replace_local
* Force to replace local libraries with CDN.
*
* @return array
* The new library array (CDN)
*/
function _hierarchy_manager_use_cdn(array $libraries, $library_name, $type, $replace_local = FALSE) {
if (isset($libraries[$library_name])) {
if (isset($libraries[$library_name][$type]) && isset($libraries[$library_name]['cdn'])) {
$library_array = [];
$updated = FALSE;
// CSS library has a sub-array called component.
if ($type === 'css') {
if (isset($libraries[$library_name][$type]['component'])) {
$local_library = $libraries[$library_name][$type]['component'];
}
else {
return FALSE;
}
}
else {
// Local js library.
$local_library = $libraries[$library_name][$type];
}
foreach ($local_library as $key => $value) {
if (!file_exists(DRUPAL_ROOT . $key) || $replace_local) {
// The js file doesn't exist.
// Replace it with remote cdn.
$library_array[$libraries[$library_name]['cdn'] . basename($key)] = $value;
$updated = TRUE;
}
else {
$library_array[$key] = $value;
}
}
}
}
return empty($updated) ? FALSE : $library_array;
}
# Hierarchy Manager Configuration
hierarchy_manager.hm_config_form:
path: '/admin/config/user-interface/hierarchy_manager/config'
defaults:
_form: '\Drupal\hierarchy_manager\Form\HMConfigForm'
_title: 'HMConfigForm'
requirements:
_permission: 'access administration pages'
options:
_admin_route: TRUE
# Taxonomy display plugin.
hierarchy_manager.taxonomy.tree.json:
path: '/admin/hierarchy_manager/taxonomy/json/{vid}'
defaults:
_title: 'Taxonomy tree'
_controller: '\Drupal\hierarchy_manager\Controller\HmTaxonomyController::taxonomyTreeJson'
requirements:
_permission: 'administer taxonomy'
options:
_admin_route: TRUE
hierarchy_manager.taxonomy.tree.update:
path: '/admin/hierarchy_manager/taxonomy/update/{vid}'
defaults:
_title: 'Taxonomy tree'
_controller: '\Drupal\hierarchy_manager\Controller\HmTaxonomyController::updateTerms'
requirements:
_permission: 'administer taxonomy'
options:
_admin_route: TRUE
services:
plugin.manager.hm.hmsetup:
class: Drupal\hierarchy_manager\Plugin\HmSetupPluginManager
parent: default_plugin_manager
plugin.manager.hm.display_plugin:
class: Drupal\hierarchy_manager\Plugin\HmDisplayPluginManager
parent: default_plugin_manager
hm.route_subscriber:
class: Drupal\hierarchy_manager\Routing\HmRouteSubscriber
tags:
- { name: event_subscriber }
(function($, Drupal) {
$(document).ready(() => {
$(".fancytree").each(function(index) {
const $treeElement = $(this);
const sourceURL = $treeElement.attr("data-source");
const updateURL = $treeElement.attr("url-update");
$treeElement.fancytree({
extensions: ["dnd5", "filter"],
source: {
url: sourceURL
},
// Event handler
dblclick: function(event, data) {
const node = data.node;
if (node.data.edit_url) {
event.stopPropagation();
event.preventDefault();
// Todo: make the target of the new window configurable.
window.open(node.data.edit_url, "_self");
}
},
// Called when a lazy node is expanded for the first time:
lazyLoad: function(event, data) {
const node = data.node;
// Load child nodes via Ajax GET sourceURL?depth=1&parent={node.key}
data.result = {
url: sourceURL,
data: { depth: 1, parent: node.key }
};
},
filter: {
autoApply: true, // Re-apply last filter if lazy data is loaded
autoExpand: true, // Expand all branches that contain matches while filtered
counter: true, // Show a badge with number of matching child nodes near parent icons
fuzzy: false, // Match single characters in order, e.g. 'fb' will match 'FooBar'
hideExpandedCounter: false, // Hide counter badge if parent is expanded
hideExpanders: false, // Hide expanders if all child nodes are hidden by filter
highlight: true, // Highlight matches by wrapping inside <mark> tags
leavesOnly: false, // Match end nodes only
nodata: true, // Display a 'no data' status node if result is empty
mode: "hide" // Grayout unmatched nodes (pass "hide" to remove unmatched node instead)
},
dnd5: {
// autoExpandMS: 400,
preventForeignNodes: true,
preventNonNodes: true,
preventRecursion: true, // Prevent dropping nodes on own descendants
// preventSameParent: true,
preventVoidMoves: true, // Prevent moving nodes 'before self', etc.
// effectAllowed: "all",
// dropEffectDefault: "move", // "auto",
// --- Drag-support:
dragStart: function(node, data) {
/* This function MUST be defined to enable dragging for the tree.
*
* Return false to cancel dragging of node.
* data.dataTransfer.setData() and .setDragImage() is available
* here.
*/
// Set the allowed effects (i.e. override the 'effectAllowed' option)
data.effectAllowed = "all";
// Set a drop effect (i.e. override the 'dropEffectDefault' option)
// data.dropEffect = "link";
data.dropEffect = "copy";
// We could use a custom image here:
// data.dataTransfer.setDragImage($("<div>TEST</div>").appendTo("body")[0], -10, -10);
// data.useDefaultImage = false;
// Return true to allow the drag operation
return true;
},
dragEnter: function(node, data) {
// data.dropEffect = "copy";
return true;
},
dragOver: function(node, data) {
// Assume typical mapping for modifier keys
data.dropEffect = data.dropEffectSuggested;
// data.dropEffect = "move";
},
dragDrop: function(node, data) {
/* This function MUST be defined to enable dropping of items on
* the tree.
*/
const mode = data.dropEffect;
if (data.otherNode) {
// Drop another Fancytree node from same frame (maybe a different tree however)
// var sameTree = (data.otherNode.tree === data.tree);
if (mode === "move") {
let parentKey;
const childrenNodeIDs = [];
let i = 0;
const hitMode =
data.hitMode === "over" ? "firstChild" : data.hitMode;
// Get all nodes moving.
data.otherNodeList.forEach(element => {
childrenNodeIDs[i++] = element.key;
});
// The parent key of the target.
parentKey = node.parent.key;
// For drupal, the ID of the root node is 0.
if (parentKey === "root_1") {
parentKey = 0;
}
// Update the data on server side.
$.post(updateURL, {
keys: childrenNodeIDs,
target: node.key,
parent: parentKey,
mode: hitMode
})
.done(response => {
if (response.result === "success") {
// Move the nodes.
data.otherNode.moveTo(node, hitMode, affectedNodes => {
affectedNodes.parent.folder = true;
});
} else {
alert("Server error:" + response.result);
}
})
.fail(() => {
alert("Error: Can't connect to the server.");
});
} else {
/* Todo: duplicate nodes
*/
}
} else if (data.otherNodeData) {
// Drop Fancytree node from different frame or window, so we only have
// JSON representation available
/* Todo: move node from different tree
node.addChild(data.otherNodeData, data.hitMode);
*/
}
node.setExpanded();
}
},
activate: function(event, data) {
// alert("activate " + data.node);
}
});
});
// Event handlers.
$("input[name=fancytree-search]")
.on("keyup", function(e) {
// Todo: Deal with multiple tree and search text field.
const tree = $.ui.fancytree.getTree();
const match = $(this).val();
if ((e && e.which === $.ui.keyCode.ESCAPE) || $.trim(match) === "") {
tree.clearFilter();
return;
}
// Pass a string to perform case insensitive matching
tree.filterBranches(match);
})
.focus();
});
})(jQuery, Drupal);
<?php
namespace Drupal\hierarchy_manager\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a Hierarchy manager display plugin item annotation object.
*
* @see \Drupal\hierarchy_manager\Plugin\HmDisplayPluginManager
* @see plugin_api
*
* @Annotation
*/
class HmDisplayPlugin extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The label of the plugin.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $label;
}
<?php
namespace Drupal\hierarchy_manager\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a Hierarchy Manager Setup Plugin item annotation object.
*
* @see \Drupal\hierarchy_manager\Plugin\HmSetupPluginManager
* @see plugin_api
*
* @Annotation
*/
class HmSetupPlugin extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The label of the plugin.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $label;
}
<?php
namespace Drupal\hierarchy_manager\Controller;
use Drupal\Core\Access\CsrfTokenGenerator;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\taxonomy\VocabularyInterface;
use Drupal\taxonomy\Entity\Term;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
/**
* Taxononmy controller class.
*/
class HmTaxonomyController extends ControllerBase {
/**
* CSRF Token.
*
* @var \Drupal\Core\Access\CsrfTokenGenerator
*/
protected $csrfToken;
/**
* The term storage handler.
*
* @var \Drupal\taxonomy\TermStorageInterface
*/
protected $storageController;
/**
* Display plugin manager.
*
* @var \Drupal\hierarchy_manager\Plugin\HmDisplayPluginInterface
*/
protected $displayManager;
/**
* Setup plugin manager.
*
* @var \Drupal\hierarchy_manager\Plugin\HmSetupPluginManager
*/
protected $setupManager;
/**
* {@inheritdoc}
*/
public function __construct(CsrfTokenGenerator $csrfToken, EntityTypeManagerInterface $entity_type_manager, $display_manager, $setup_manager) {
$this->csrfToken = $csrfToken;
$this->entityTypeManager = $entity_type_manager;
$this->storageController = $entity_type_manager->getStorage('taxonomy_term');
$this->displayManager = $display_manager;
$this->setupManager = $setup_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('csrf_token'),
$container->get('entity_type.manager'),
$container->get('plugin.manager.hm.display_plugin'),
$container->get('plugin.manager.hm.hmsetup')
);
}
/**
* Callback for taxonomy tree json.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* Http request object.
* @param string $vid
* Vocabulary ID.
*/
public function taxonomyTreeJson(Request $request, string $vid) {
// Access token.
$token = $request->get('token');
// The term array will be returned.
$term_array = [];
if (empty($token) || !$this->csrfToken->validate($token, $vid)) {
return new Response($this->t('Access denied!'));
}
$parent = $request->get('parent') ?: 0;
$depth = $request->get('depth') ?: 1;
$vocabulary_hierarchy = $this->storageController->getVocabularyHierarchyType($vid);
// Taxonomy tree must not be multiple parent tree.
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) {
// User can only access the terms that they can update.
if ($access_control_handler->access($term, 'update')) {
// Find children of this term.
$query = \Drupal::entityQuery('taxonomy_term')
->condition('parent', $term
->id());
$has_children = empty($query->execute()) ? FALSE : TRUE;
$term_array[] = [
'id' => $term->id(),
'title' => $term->label(),
'has_children' => $has_children,
'edit_url' => $term->toUrl('edit-form')->toString(),
];
}
}
}
}
// Taxonomy setup plugin instance.
$taxonomy_setup_plugin = $this->setupManager->createInstance('hm_setup_taxonomy');
// Display profile.
$display_profile = $this->entityTypeManager->getStorage('hm_display_profile')->load($taxonomy_setup_plugin->getDispalyProfileId());
// Display plugin ID.
$display_plugin_id = $display_profile->get("plugin");
// Display plugin instance.
$display_plugin = $this->displayManager->createInstance($display_plugin_id);
if (method_exists($display_plugin, 'treeData')) {
// Convert the tree data to the structure
// that display plugin accepts.
$tree_data = $display_plugin->treeData($term_array);
}
else {
$tree_data = $term_array;
}
return new JsonResponse($tree_data);
}
/**
* Callback for taxonomy tree json.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* Http request object.
* @param string $vid
* Vocabulary ID.
*/
public function updateTerms(Request $request, string $vid) {
// Access token.
$token = $request->get('token');
if (empty($token) || !$this->csrfToken->validate($token, $vid)) {
return new Response($this->t('Access denied!'));
}
$target_id = $request->get('target');
$parent_id = intval($request->get('parent'));
$mode = $request->get('mode');
$updated_terms = $request->get('keys');
$success = FALSE;
if (is_array($updated_terms) && !empty($updated_terms) && !empty($target_id)) {
// Taxonomy access control.
$access_control_handler = $this->entityTypeManager->getAccessControlHandler('taxonomy_term');
/*
* Mode firstChild: Insert terms as first children of the target term.
* Mode before: Insert terms before the target term as siblings.
* Mode after: Insert terms after the target term as siblings.
*/
if ($mode === 'firstChild') {
// The target is the parent.
$parent_id = intval($target_id);
// All children of the parent term.
$children = $this->storageController->loadTree($vid, $parent_id, 1);
// Figure out the weight of the first child.
if (is_array($children) && !empty($children)) {
$child = reset($children);
// Make sure the weight is less than the first child,
// so that the terms will be inserted before the first child.
$weight = $child->weight - count($updated_terms);
}
}
// Insert before or after the target.
else {
$step = 0;
// Children of the parent term in weight and name alphabetically order.
$children = $this->storageController->loadTree($vid, $parent_id, 1, TRUE);
// Loop the children array to move other terms after the target term,
// include the target term if the mode is 'before'.
foreach ($children as $child) {
// Identify the target term.
if (($step === 0) && ((string) $child->id() === $target_id)) {
if ($mode === 'before') {
// Updated terms will be insert into the positoin of target term.
$weight = $child->getWeight();
$step = count($updated_terms) + $weight;
}
else {
// Updated terms will be insert after the positoin of target term.
$weight = $child->getWeight() + 1;
}
}
// Still haven't reached the target term,
// move the point forward.
elseif (!isset($weight)) {
continue;
}
// Start moving the terms after this point,
// if the step has been set.
if ($step) {
if ($child->getWeight() <= $step) {
$child->setWeight($step++);
$child->save();
}
else {
// The rest of children don't need to move,
// as their weight is greater then the gap.
break;
}
}
else {
$step = count($updated_terms) + $weight;
}
}
}
if (!isset($weight)) {
// Set the weight to 0 as default.
$weight = 0;
}
$terms = Term::loadMultiple($updated_terms);
// Update all terms, the weight will be increased by 1,
// after inserting.
foreach ($terms as $term) {
if ($access_control_handler->access($term, 'update')) {
$term->set('parent', ['target_id' => $parent_id]);
$term->setWeight($weight++);
$success = $term->save();
}
}
}
if ($success) {
return new JsonResponse(['result' => 'success']);
}
return new JsonResponse(['result' => 'fail']);
}
}
<?php
namespace Drupal\hierarchy_manager\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBase;
/**
* Defines the HM Display Profile Entity entity.
*
* @ConfigEntityType(
* id = "hm_display_profile",
* label = @Translation("HM Display Profile Entity"),
* handlers = {
* "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
* "list_builder" = "Drupal\hierarchy_manager\HmDisplayProfileListBuilder",
* "form" = {
* "add" = "Drupal\hierarchy_manager\Form\HmDisplayProfileForm",
* "edit" = "Drupal\hierarchy_manager\Form\HmDisplayProfileForm",
* "delete" = "Drupal\hierarchy_manager\Form\HmDisplayProfileDeleteForm"
* },
* "route_provider" = {
* "html" = "Drupal\hierarchy_manager\HmDisplayProfileHtmlRouteProvider",
* },
* },
* config_prefix = "hm_display_profile",
* admin_permission = "administer site configuration",
* entity_keys = {
* "id" = "id",
* "label" = "label",
* "uuid" = "uuid"
* },
* config_export = {
* "id",
* "label",
* "plugin",
* },
* links = {
* "canonical" = "/admin/structure/hm_display_profile/{hm_display_profile}",
* "add-form" = "/admin/structure/hm_display_profile/add",
* "edit-form" = "/admin/structure/hm_display_profile/{hm_display_profile}/edit",
* "delete-form" = "/admin/structure/hm_display_profile/{hm_display_profile}/delete",
* "collection" = "/admin/structure/hm_display_profile"
* }
* )
*/
class HmDisplayProfile extends ConfigEntityBase implements HmDisplayProfileInterface {
/**
* The HM Display Profile Entity ID.
*
* @var string
*/
protected $id;
/**
* The HM Display Profile Entity label.
*
* @var string
*/
protected $label;
/**
* The display plugin machine name.
*
* @var string
*/
protected $plugin;
}
<?php
namespace Drupal\hierarchy_manager\Entity;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
/**
* Provides an interface for defining HM Display Profile Entity entities.
*/
interface HmDisplayProfileInterface extends ConfigEntityInterface {
// Add get/set methods for your configuration properties here.
}
<?php
namespace Drupal\hierarchy_manager\Form;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\hierarchy_manager\Plugin\HmSetupPluginManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Class HMConfigForm.
*/
class HMConfigForm extends ConfigFormBase {
/**
* Setup plugin manager.
*
* @var \Drupal\hierarchy_manager\Plugin\HmSetupPluginManager
*/
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}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.factory'),
$container->get('plugin.manager.hm.hmsetup')
);
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return [
'hierarchy_manager.hmconfig',
];
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'hm_config_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('hierarchy_manager.hmconfig');
// Setup plugins.
$setup_plugins = $this->pluginManagerHmSetup->getDefinitions();
// Get setup plugin labels.
$setup_plugins_labels = [];
foreach ($setup_plugins as $key => $plugin) {
$setup_plugins_labels[$plugin['id']] = $plugin['label']->render();
}
if (count($setup_plugins)) {
$form['hm_allowed_setup_plugins'] = [
'#type' => 'checkboxes',
'#title' => $this->t('Enabled setup plugins'),
'#options' => $setup_plugins_labels,
'#default_value' => $config->get('allowed_setup_plugins') ?: ['hm_setup_taxonomy'],
'#description' => $this->t('Plugins that presents the hierarchy manager button in the edit form.'),
];
}
else {
$form['no_setup_plugin'] = [
'#value' => 'markup',
'#markup' => $this->t('No available setup plugins available.'),
];
}
// Setup plugin advanced settings.
$form['setup_plugin_settings'] = [
'#type' => 'fieldset',
'#title' => $this->t('Setup Plugin Settings'),
'#descrption' => $this->t('Setup plugin advanced settings.'),
'#tree' => TRUE,
];
foreach ($setup_plugins_labels as $key => $val) {
$instance = $this->pluginManagerHmSetup->createInstance($key);
if (method_exists($instance, 'buildConfigurationForm')) {
$setup_enabled_state = [
'visible' => [
[
':input[name="hm_allowed_setup_plugins[' . $key . ']"]' => ['checked' => TRUE],
],
],
];
$form['setup_plugin_settings'][$key . '_container'] = [
'#type' => 'container',
'#states' => $setup_enabled_state,
'title' => [
'#type' => 'html_tag',
'#tag' => 'h3',
'#value' => $val,
],
];
$container_name = $key . '_container';
// Plugin settings.
$form['setup_plugin_settings'][$container_name]['form'] = $instance->buildConfigurationForm($config, $setup_enabled_state);
$form['setup_plugin_settings'][$container_name]['form']['#parents'] = ['setup_plugin_settings', $key];
}
}
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
$setup_plugin_settings = $form_state->getValue('setup_plugin_settings');
if (empty($setup_plugin_settings)) {
$setup_plugin_settings = [];
}
$this->config('hierarchy_manager.hmconfig')
->set('allowed_setup_plugins', $form_state->getValue('hm_allowed_setup_plugins'))
->set('setup_plugin_settings', $setup_plugin_settings)
->save();
}
}
<?php
namespace Drupal\hierarchy_manager\Form;
use Drupal\Core\Entity\EntityConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
/**
* Builds the form to delete HM Display Profile Entity entities.
*/
class HmDisplayProfileDeleteForm extends EntityConfirmFormBase {
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you want to delete %name?', ['%name' => $this->entity->label()]);
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('entity.hm_display_profile.collection');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Delete');
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->entity->delete();
$this->messenger()->addMessage(
$this->t('content @type: deleted @label.', [
'@type' => $this->entity->bundle(),
'@label' => $this->entity->label(),
])
);
$form_state->setRedirectUrl($this->getCancelUrl());
}
}
<?php
namespace Drupal\hierarchy_manager\Form;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\hierarchy_manager\Plugin\HmDisplayPluginManager;
/**
* Class HmDisplayProfileForm.
*/
class HmDisplayProfileForm extends EntityForm {
/**
* Display plugin manager.
*
* @var \Drupal\hierarchy_manager\Plugin\HmDisplayPluginInterface
*/
protected $pluginManagerHmDisplay;
/**
* Constructs a new HmDisplayProfileForm object.
*/
public function __construct(
HmDisplayPluginManager $plugin_manager_hm_display
) {
$this->pluginManagerHmDisplay = $plugin_manager_hm_display;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.hm.display_plugin')
);
}
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$form = parent::form($form, $form_state);
// Setup plugins.
$display_plugins = $this->pluginManagerHmDisplay->getDefinitions();
// Get display plugin labels.
$display_plugin_labels = [];
foreach ($display_plugins as $key => $plugin) {
$display_plugin_labels[$plugin['id']] = $plugin['label']->render();
}
$hm_display_profile = $this->entity;
$form['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#maxlength' => 255,
'#default_value' => $hm_display_profile->label(),
'#description' => $this->t("Label for the HM Display Profile Entity."),
'#required' => TRUE,
];
$form['id'] = [
'#type' => 'machine_name',
'#default_value' => $hm_display_profile->id(),
'#machine_name' => [
'exists' => '\Drupal\hierarchy_manager\Entity\HmDisplayProfile::load',
],
'#disabled' => !$hm_display_profile->isNew(),
];
// Display plugin.
if (count($display_plugins)) {
$form['plugin'] = [
'#type' => 'radios',
'#title' => $this->t('Display plugin'),
'#options' => $display_plugin_labels,
'#default_value' => $hm_display_profile->get("plugin") ?: ['hm_display_jstree'],
'#description' => $this->t('Display plugin that is in charge of rendering the hierarchy view.'),
'#required' => TRUE,
];
}
else {
$form['no_setup_plugin'] = [
'#value' => 'markup',
'#markup' => $this->t('No available setup plugins available.'),
];
}
return $form;
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
$hm_display_profile = $this->entity;
$status = $hm_display_profile->save();
switch ($status) {
case SAVED_NEW:
$this->messenger()->addMessage($this->t('Created the %label HM Display Profile Entity.', [
'%label' => $hm_display_profile->label(),
]));
break;
default:
$this->messenger()->addMessage($this->t('Saved the %label HM Display Profile Entity.', [
'%label' => $hm_display_profile->label(),
]));
}
$form_state->setRedirectUrl($hm_display_profile->toUrl('collection'));
}
}
<?php
namespace Drupal\hierarchy_manager\Form;
use Drupal\Core\Form\FormStateInterface;
use Drupal\taxonomy\VocabularyInterface;
use Drupal\taxonomy\Form\OverviewTerms;
/**
* Taxonomy overview form class.
*/
class HmOverviewTerms extends OverviewTerms {
/**
* Form constructor.
*
* Override the form submit method to avoid the parent one from running,
* If the hierarchy manager taxonomy plugin is enabled.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param \Drupal\taxonomy\VocabularyInterface $taxonomy_vocabulary
* The vocabulary to display the overview form for.
*
* @return array
* The form structure.
*/
public function buildForm(array $form, FormStateInterface $form_state, VocabularyInterface $taxonomy_vocabulary = NULL) {
global $base_path;
// Override the form if the taxonomy hierarchy manager has been set up.
if (!empty($taxonomy_vocabulary) && $config = \Drupal::config('hierarchy_manager.hmconfig')) {
if ($allowed_setup_plugins = $config->get('allowed_setup_plugins')) {
// If the taxonomy setup plugin is enabled.
// Override the taxonomy overview form.
if (!empty($allowed_setup_plugins['hm_setup_taxonomy'])) {
// Hierarchy Manager setup plugin configuration.
$plugin_settings = $config->get('setup_plugin_settings');
if (!empty($plugin_settings['hm_setup_taxonomy'])) {
// Display profile ID.
$display_profile_id = $plugin_settings['hm_setup_taxonomy']['display_profile'];
// Display profile.
$display_profile = $this->entityTypeManager->getStorage('hm_display_profile')->load($display_profile_id);
if (!empty($display_profile)) {
// Display plugin instance.
$instance = \Drupal::service('plugin.manager.hm.display_plugin')->createInstance($display_profile->get("plugin"));
if (method_exists($instance, 'getForm')) {
// Vocabulary ID.
$vid = $taxonomy_vocabulary->id();
// CSRF token.
$token = \Drupal::csrfToken()->get($vid);
// Get current language.
$language = \Drupal::languageManager()->getCurrentLanguage();
if ($language->isDefault()) {
$source_url = $base_path . 'admin/hierarchy_manager/taxonomy/json/' . $vid . '?parent=0&depth=1&token=' . $token;
$update_url = $base_path . 'admin/hierarchy_manager/taxonomy/update/' . $vid . '?token=' . $token;
}
else {
$source_url = $base_path . $language->getId() . '/admin/hierarchy_manager/taxonomy/json/' . $vid . '?parent=0&depth=1&token=' . $token;
$update_url = $base_path . $language->getId() . '/admin/hierarchy_manager/taxonomy/update/' . $vid . '?token=' . $token;
}
return $instance->getForm($source_url, $update_url);
}
}
}
}
}
}
// The taxonomy setup plugin is not enabled.
return parent::buildForm($form, $form_state, $taxonomy_vocabulary);
}
/**
* Form submission handler.
*
* Override the form submit method to avoid the parent one from running,
* If the hierarchy manager taxonomy plugin is enabled.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment