From 5aa230e09318fbd6a894f7a064fda23a665fb55e Mon Sep 17 00:00:00 2001 From: Mingsong <amds@2986445.no-reply.drupal.org> Date: Mon, 2 May 2022 00:37:54 +0000 Subject: [PATCH] Issue #3205538: Add (optional) confirmation dialog after drag&drop --- config/schema/hm_display_profile.schema.yml | 3 + js/Plugin/jstree/hm.jstree.js | 163 ++++++++++++------ src/Entity/HmDisplayProfile.php | 8 + src/Form/HmDisplayProfileForm.php | 10 ++ src/Form/HmMenuForm.php | 3 +- src/Form/HmOverviewTerms.php | 3 +- .../HmDisplayPlugin/HmDisplayJstree.php | 3 +- 7 files changed, 142 insertions(+), 51 deletions(-) diff --git a/config/schema/hm_display_profile.schema.yml b/config/schema/hm_display_profile.schema.yml index 6f91e0f..6be45fe 100644 --- a/config/schema/hm_display_profile.schema.yml +++ b/config/schema/hm_display_profile.schema.yml @@ -16,3 +16,6 @@ hierarchy_manager.hm_display_profile.*: config: type: text label: 'Configuration' + confirm: + type: boolean + label: 'Confirm' diff --git a/js/Plugin/jstree/hm.jstree.js b/js/Plugin/jstree/hm.jstree.js index 15c2bb7..ed9b21b 100644 --- a/js/Plugin/jstree/hm.jstree.js +++ b/js/Plugin/jstree/hm.jstree.js @@ -17,6 +17,7 @@ const optionsJson = treeContainer.attr("options"); const dataURL = treeContainer.attr('data-source') + '&parent=0'; const updateURL = treeContainer.attr('url-update'); + const confirm = treeContainer.attr("confirm"); let reload = true; let rollback = false; let themes = { @@ -103,61 +104,80 @@ const drupalMessages = new Drupal.Message(); if (!rollback) { - // Update the data on server side. - $.post(updateURL, { - keys: [movedNode.id], - target: data.position, - parent: parent, - old_parent: old_parent, - old_position: data.old_position - }) - .done(function(response) { - if (response.result !== "success") { - alert("Server error:" + response.result); - rollback = true; - thisTree.move_node(movedNode, data.old_parent, data.old_position); - } - else { - if (parent === 0) { - var parentText = Drupal.t('root'); - } - else { - var parentText = parent_node.text; - } - 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(); + let parentText = Drupal.t('root'); + if (parent !== 0) { + parentText = $("<div/>").html(thisTree.get_node(parent).text); + parentText.find("span").remove(); + parentText = parentText.text(); + } + // Function to move the tree item. + function moveTreeItem() { + // Update the data on server side. + $.post(updateURL, { + keys: [movedNode.id], + target: data.position, + parent: parent, + old_parent: old_parent, + old_position: data.old_position + }) + .done(function(response) { + if (response.result !== "success") { + alert("Server error:" + response.result); + rollback = true; + thisTree.move_node(movedNode, data.old_parent, data.old_position); } 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]; + 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); } - //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); } - - 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. + }) + .fail(function() { drupalMessages.clear(); - drupalMessages.add(message); - } - }) - .fail(function() { - drupalMessages.clear(); - drupalMessages.add(Drupal.t("Can't connect to the server."), {type: 'error'}); - rollback = true; - thisTree.move_node(movedNode, data.old_parent, data.old_position); - }); + drupalMessages.add(Drupal.t("Can't connect to the server."), {type: 'error'}); + rollback = true; + thisTree.move_node(movedNode, data.old_parent, data.old_position); + }); + } + + // Check if confirmation dialog is enabled. + if (typeof confirm !== 'undefined' && confirm !== false) { + // 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 { rollback = false; @@ -184,4 +204,51 @@ }); } }; + + /** + * 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); diff --git a/src/Entity/HmDisplayProfile.php b/src/Entity/HmDisplayProfile.php index 56d40b3..32f1f0d 100644 --- a/src/Entity/HmDisplayProfile.php +++ b/src/Entity/HmDisplayProfile.php @@ -34,6 +34,7 @@ use Drupal\Core\Config\Entity\ConfigEntityBase; * "label", * "plugin", * "config", + * "confirm", * }, * links = { * "canonical" = "/admin/structure/hm_display_profile/{hm_display_profile}", @@ -73,4 +74,11 @@ class HmDisplayProfile extends ConfigEntityBase implements HmDisplayProfileInter * @var string */ protected $config; + + /** + * The confirmation option. + * + * @var bool + */ + protected $confirm = FALSE; } diff --git a/src/Form/HmDisplayProfileForm.php b/src/Form/HmDisplayProfileForm.php index 2979542..c85a30f 100644 --- a/src/Form/HmDisplayProfileForm.php +++ b/src/Form/HmDisplayProfileForm.php @@ -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; } @@ -132,6 +139,9 @@ class HmDisplayProfileForm extends EntityForm { $hm_display_profile->set('config', $input['config']); } + if (isset($input['confirm'])) { + $hm_display_profile->set('confirm', $input['confirm']); + } $status = $hm_display_profile->save(); diff --git a/src/Form/HmMenuForm.php b/src/Form/HmMenuForm.php index 6ff2eb9..428dd66 100644 --- a/src/Form/HmMenuForm.php +++ b/src/Form/HmMenuForm.php @@ -98,7 +98,8 @@ class HmMenuForm extends MenuForm { $source_url = Url::fromRoute('hierarchy_manager.menu.tree.json', ['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"); - 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); } } diff --git a/src/Form/HmOverviewTerms.php b/src/Form/HmOverviewTerms.php index 829551e..3c7bc87 100644 --- a/src/Form/HmOverviewTerms.php +++ b/src/Form/HmOverviewTerms.php @@ -64,7 +64,8 @@ class HmOverviewTerms extends OverviewTerms { $source_url = Url::fromRoute('hierarchy_manager.taxonomy.tree.json', ['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"); - 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); } } } diff --git a/src/Plugin/HmDisplayPlugin/HmDisplayJstree.php b/src/Plugin/HmDisplayPlugin/HmDisplayJstree.php index 80c6a97..8dba5c0 100644 --- a/src/Plugin/HmDisplayPlugin/HmDisplayJstree.php +++ b/src/Plugin/HmDisplayPlugin/HmDisplayJstree.php @@ -23,7 +23,7 @@ class HmDisplayJstree extends HmDisplayPluginBase implements HmDisplayPluginInte /* * Build the tree form. */ - public function getForm(string $url_source, string $url_update, array &$form = [], FormStateInterface &$form_state = NULL, $options = NULL) { + public function getForm(string $url_source, string $url_update, array &$form = [], FormStateInterface &$form_state = NULL, $options = NULL, $confirm = FALSE) { if (!empty($url_source)) { if (!empty(($form_state))) { $parent_formObj = $form_state->getFormObject(); @@ -70,6 +70,7 @@ class HmDisplayJstree extends HmDisplayPluginBase implements HmDisplayPluginInte 'id' => isset($parent_id) ? 'hm-jstree-' . $parent_id : 'hm-jstree', 'parent-id' => isset($parent_id) ? $parent_id : '', 'options' => $options, + 'confirm' => $confirm, 'data-source' => $url_source, 'url-update' => $url_update, ], -- GitLab