From aa6e74e9afc805d2d8b54c506058ace1ab63ed91 Mon Sep 17 00:00:00 2001 From: Thomas Gauges <tgauges@dev-specialists.com> Date: Wed, 17 Jan 2024 13:20:45 +0100 Subject: [PATCH] feat(#2960456): apply the adjusted patch for 8.x-1.x to 2.0.x --- src/Element/CheckboxTree.php | 48 ++++++++++++++++--- .../Field/FieldWidget/TermReferenceTree.php | 35 ++++++++++++++ term_reference_tree.module | 35 ++++++++------ 3 files changed, 98 insertions(+), 20 deletions(-) diff --git a/src/Element/CheckboxTree.php b/src/Element/CheckboxTree.php index d79be89..e25cecf 100644 --- a/src/Element/CheckboxTree.php +++ b/src/Element/CheckboxTree.php @@ -5,6 +5,7 @@ namespace Drupal\term_reference_tree\Element; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element\CompositeFormElementTrait; use Drupal\Core\Render\Element\FormElement; +use Drupal\term_reference_tree\Plugin\Field\FieldWidget\TermReferenceTree; /** * Provides a form element for term reference tree. @@ -66,19 +67,54 @@ class CheckboxTree extends FormElement { if (empty($element['#options'])) { $options_tree = []; foreach ($element['#vocabularies'] as $vocabulary) { - $options = _term_reference_tree_get_term_hierarchy(0, $vocabulary->id(), $allowed, $filter, '', $value); - $options_tree = array_merge($options_tree, $options); + $options_tree[$vocabulary->id()] = [ + 'name' => $vocabulary->label(), + 'terms' => _term_reference_tree_get_term_hierarchy(0, $vocabulary->id(), $allowed, $filter, '', $value), + ]; } $element['#options_tree'] = $options_tree; $element['#options'] = _term_reference_tree_get_options($element['#options_tree'], $allowed, $filter); } - $terms = !empty($element['#options_tree']) ? $element['#options_tree'] : []; + if (count($element['#vocabularies']) > 1 && $element['#multiple_vocabularies'] === TermReferenceTree::MULTIPLE_VOCABULARIES_TREE_ROOT) { + $tree = new \stdClass(); + $tree->children = []; + foreach ($options_tree as $vocabulary_id => $data) { + $root = new \stdClass(); + $root->vid = $vocabulary_id; + $root->name = $data['name']; + $root->children = $data['terms']; + $tree->children[] = $root; + $element['#options'] = array_merge($element['#options'], _term_reference_tree_get_options($data['terms'], $allowed, NULL)); + } + $element[] = _term_reference_tree_build_level($element, $tree, $form_state, $value, $element['#max_choices'], [], 1); + } + elseif (count($element['#vocabularies']) > 1 && $element['#multiple_vocabularies'] === TermReferenceTree::MULTIPLE_VOCABULARIES_CLONED) { + foreach ($options_tree as $vocabulary_id => $data) { + $tree = new \stdClass(); + $tree->vid = $vocabulary_id; + $tree->name = $data['name']; + $tree->children = $data['terms']; + $element[$vocabulary_id] = [ + '#type' => 'details', + '#title' => $data['name'], + 'tree' => _term_reference_tree_build_level($element, $tree, $form_state, $value, $element['#max_choices'], [], 1), + ]; + $element['#options'] = array_merge($element['#options'], _term_reference_tree_get_options($data['terms'], $allowed, NULL)); + } + } + // If there is only one vocabulary or the "merged" mode is selected. + else { + $tree = new \stdClass(); + $tree->children = []; + foreach ($options_tree as $vocabulary_id => $data) { + $tree->children = array_merge($tree->children, $data['terms']); + } + $element['#options'] = _term_reference_tree_get_options($tree->children, $allowed, NULL); + $element[] = _term_reference_tree_build_level($element, $tree, $form_state, $value, $element['#max_choices'], [], 1); + } - $tree = new \stdClass(); - $tree->children = $terms; unset($element['#needs_validation']); - $element[] = _term_reference_tree_build_level($element, $tree, $form_state, $value, $element['#max_choices'], [], 1); return $element; } diff --git a/src/Plugin/Field/FieldWidget/TermReferenceTree.php b/src/Plugin/Field/FieldWidget/TermReferenceTree.php index 06cb955..830855c 100644 --- a/src/Plugin/Field/FieldWidget/TermReferenceTree.php +++ b/src/Plugin/Field/FieldWidget/TermReferenceTree.php @@ -28,6 +28,12 @@ class TermReferenceTree extends WidgetBase { const CASCADING_SELECTION_DESELECT = '3'; + const MULTIPLE_VOCABULARIES_MERGED = 'merged'; + + const MULTIPLE_VOCABULARIES_TREE_ROOT = 'tree_root'; + + const MULTIPLE_VOCABULARIES_CLONED = 'cloned'; + /** * {@inheritdoc} */ @@ -37,6 +43,7 @@ class TermReferenceTree extends WidgetBase { 'leaves_only' => FALSE, 'select_parents' => FALSE, 'cascading_selection' => self::CASCADING_SELECTION_NONE, + 'multiple_vocabularies' => self::MULTIPLE_VOCABULARIES_MERGED, 'max_depth' => 0, ] + parent::defaultSettings(); } @@ -94,6 +101,20 @@ class TermReferenceTree extends WidgetBase { $form['cascading_selection']['#description'] .= ' <em>' . $this->t("This option is only valid if an unlimited number of values can be selected.") . '</em>'; } + if (count($this->fieldDefinition->getSettings()['handler_settings']['target_bundles']) > 1) { + $form['multiple_vocabularies'] = [ + '#type' => 'select', + '#title' => $this->t('Multiple vocabularies handling'), + '#description' => $this->t('Select how terms from multiple vocabularies are displayed.'), + '#default_value' => $this->getSetting('multiple_vocabularies'), + '#options' => [ + self::MULTIPLE_VOCABULARIES_MERGED => $this->t('Terms from all vocabularies are merged in the same tree'), + self::MULTIPLE_VOCABULARIES_TREE_ROOT => $this->t('Each vocabulary is a root node of the tree'), + self::MULTIPLE_VOCABULARIES_CLONED => $this->t('Each vocabulary has its own widget'), + ], + ]; + } + $form['max_depth'] = [ '#type' => 'number', '#title' => $this->t('Maximum Depth'), @@ -134,6 +155,19 @@ class TermReferenceTree extends WidgetBase { $summary[] = sprintf('%s (%s)', $this->t('Cascading selection'), $this->t('Only deselect')); } + switch ($this->getSetting('multiple_vocabularies')) { + case self::MULTIPLE_VOCABULARIES_TREE_ROOT: + $summary[] = $this->t('Each vocabulary is a root node of the tree'); + break; + case self::MULTIPLE_VOCABULARIES_CLONED: + $summary[] = $this->t('Each vocabulary has its own widget'); + break; + case self::MULTIPLE_VOCABULARIES_MERGED: + default: + $summary[] = $this->t('Terms from all vocabularies are merged in the same tree'); + break; + } + if ($this->getSetting('max_depth')) { $summary[] = $this->formatPlural($this->getSetting('max_depth'), 'Maximum Depth: @count level', 'Maximum Depth: @count levels'); } @@ -156,6 +190,7 @@ class TermReferenceTree extends WidgetBase { $element['#leaves_only'] = $this->getSetting('leaves_only'); $element['#select_parents'] = $this->getSetting('select_parents'); $element['#cascading_selection'] = $this->getSetting('cascading_selection'); + $element['#multiple_vocabularies'] = $this->getSetting('multiple_vocabularies'); $element['#value_key'] = 'target_id'; $element['#max_depth'] = $this->getSetting('max_depth'); $element['#start_minimized'] = $this->getSetting('start_minimized'); diff --git a/term_reference_tree.module b/term_reference_tree.module index 9e5e870..9ac57c0 100644 --- a/term_reference_tree.module +++ b/term_reference_tree.module @@ -350,7 +350,7 @@ function _term_reference_tree_get_options(&$terms, &$allowed, $filter) { if (is_array($terms) && count($terms) > 0) { foreach ($terms as $term) { - if (!$filter || (is_array($allowed) && $allowed[$term->tid])) { + if (is_object($term) && (!$filter || (is_array($allowed) && $allowed[$term->tid]))) { $options[$term->tid] = $term->name; $options += _term_reference_tree_get_options($term->children, $allowed, $filter); } @@ -377,10 +377,11 @@ function _term_reference_tree_build_level($element, $term, $form_state, $value, '#depth' => $depth, ]; - $container['#level_start_minimized'] = $depth > 1 && $element['#start_minimized'] && !($term->children_selected); + $container['#level_start_minimized'] = $depth > 1 && $element['#start_minimized'] && empty($term->children_selected); foreach ($term->children as $child) { - $container[$child->tid] = _term_reference_tree_build_item($element, $child, $form_state, $value, $max_choices, $parent_tids, $container, $depth); + $key = _term_reference_tree_item_id($child); + $container[$key] = _term_reference_tree_build_item($element, $child, $form_state, $value, $max_choices, $parent_tids, $container, $depth); } return $container; @@ -421,7 +422,7 @@ function _term_reference_tree_build_item($element, $term, $form_state, $value, $ '#depth' => $depth, ]; - if (!$element['#leaves_only'] || count($term->children) == 0) { + if ((!$element['#leaves_only'] || count($term->children) == 0) && !empty($term->tid)) { $e = [ '#type' => ($max_choices == 1) ? 'radio' : 'checkbox', '#title' => $term_name, @@ -434,12 +435,10 @@ function _term_reference_tree_build_item($element, $term, $form_state, $value, $ '#ajax' => $element['#ajax'] ?? NULL, ]; - if (is_array($e)) { - if ($e['#type'] == 'radio') { - $parents_for_id = array_merge($element['#parents'], [$term->tid]); - $e['#id'] = Html::getId('edit-' . implode('-', $parents_for_id)); - $e['#parents'] = array_merge($element['#parents'], ['wiget']); - } + if ($e['#type'] == 'radio') { + $parents_for_id = array_merge($element['#parents'], [$term->tid]); + $e['#id'] = Html::getId('edit-' . implode('-', $parents_for_id)); + $e['#parents'] = array_merge($element['#parents'], ['wiget']); } } else { @@ -449,18 +448,26 @@ function _term_reference_tree_build_item($element, $term, $form_state, $value, $ ]; } - $container[$term->tid] = $e; + $key = _term_reference_tree_item_id($term); + $container[$key] = $e; if (($depth + 1 <= $element['#max_depth'] || !$element['#max_depth']) && property_exists($term, 'children') && count($term->children) > 0) { $parents = $parent_tids; - $parents[] = $term->tid; - $container[$term->tid . '-children'] = _term_reference_tree_build_level($element, $term, $form_state, $value, $max_choices, $parents, $depth + 1); - $container['#level_start_minimized'] = $container[$term->tid . '-children']['#level_start_minimized']; + $parents[] = $key; + $container[$key . '-children'] = _term_reference_tree_build_level($element, $term, $form_state, $value, $max_choices, $parents, $depth + 1); + $container['#level_start_minimized'] = $container[$key . '-children']['#level_start_minimized']; } return $container; } +/** + * + */ +function _term_reference_tree_item_id($item) { + return !empty($item->tid) ? $item->tid : $item->vid; +} + /** * Implements hook_preprocess_HOOK() for term tree display templates. * -- GitLab