From 6b3b61873a3d514f073b970cedea014de454abd2 Mon Sep 17 00:00:00 2001 From: Loz Calver Date: Thu, 30 May 2013 12:45:10 +0100 Subject: [PATCH] FEATURE: Disable specific tree nodes in TreeDropdownField DO NOT MERGE: to be reviewed. This feature request was born out of wanting the ability to disable (for example) a top level page from being selected, while still being able to select a child page. Using setFilterFunction() simply removes the node and its children. Extra styling for disabled nodes Disable ability to select a disabled node for TreeDropdownField Disable hover CSS changes Fixing merge conflict during rebase Return a boolean for nodeIsDisabled() --- admin/css/screen.css | 2 ++ admin/images/sitetree_ss_default_icons.png | Bin 6929 -> 2528 bytes admin/scss/_tree.scss | 11 +++++- forms/TreeDropdownField.php | 37 +++++++++++++++++---- javascript/TreeDropdownField.js | 20 ++++++++++- 5 files changed, 62 insertions(+), 8 deletions(-) diff --git a/admin/css/screen.css b/admin/css/screen.css index ff33a4c9d..ddd577c97 100644 --- a/admin/css/screen.css +++ b/admin/css/screen.css @@ -747,6 +747,7 @@ form.import-form label.left { width: 250px; } .cms .jstree li.jstree-open > ul, .TreeDropdownField .treedropdownfield-panel .jstree li.jstree-open > ul { display: block; } .cms .jstree li.jstree-closed > ul, .TreeDropdownField .treedropdownfield-panel .jstree li.jstree-closed > ul { display: none; } .cms .jstree li.disabled > a, .TreeDropdownField .treedropdownfield-panel .jstree li.disabled > a { color: #aaaaaa; } +.cms .jstree li.disabled > a:hover, .TreeDropdownField .treedropdownfield-panel .jstree li.disabled > a:hover { background: transparent; cursor: default; } .cms .jstree li.edit-disabled > a, .TreeDropdownField .treedropdownfield-panel .jstree li.edit-disabled > a { color: #aaaaaa; } .cms .jstree li > .jstree-icon, .TreeDropdownField .treedropdownfield-panel .jstree li > .jstree-icon { cursor: pointer; } .cms .jstree ins, .TreeDropdownField .treedropdownfield-panel .jstree ins { display: inline-block; text-decoration: none; width: 18px; height: 18px; margin: 0 0 0 0; padding: 0; float: left; } @@ -817,6 +818,7 @@ form.import-form label.left { width: 250px; } .tree-holder.jstree-apple li.Root > a .jstree-icon, .cms-tree.jstree-apple li.Root > a .jstree-icon { background-position: -56px -36px; } .tree-holder.jstree-apple li.status-deletedonlive .text, .cms-tree.jstree-apple li.status-deletedonlive .text { text-decoration: line-through; } .tree-holder.jstree-apple li.jstree-checked > a, .tree-holder.jstree-apple li.jstree-checked > a:link, .cms-tree.jstree-apple li.jstree-checked > a, .cms-tree.jstree-apple li.jstree-checked > a:link { background-color: #efe999; } +.tree-holder.jstree-apple li.disabled > a > .jstree-checkbox, .cms-tree.jstree-apple li.disabled > a > .jstree-checkbox { background-position: -57px -54px; } .tree-holder.jstree-apple li.readonly, .cms-tree.jstree-apple li.readonly { color: #aaaaaa; padding-left: 18px; } .tree-holder.jstree-apple li.readonly a, .tree-holder.jstree-apple li.readonly a:link, .cms-tree.jstree-apple li.readonly a, .cms-tree.jstree-apple li.readonly a:link { margin: 0; padding: 0; } .tree-holder.jstree-apple li.readonly .jstree-icon, .cms-tree.jstree-apple li.readonly .jstree-icon { display: none; } diff --git a/admin/images/sitetree_ss_default_icons.png b/admin/images/sitetree_ss_default_icons.png index 101351db99d711002e90ab83ced11a548e183863..783fd22689c2baa35406ce25c079438b73807924 100644 GIT binary patch literal 2528 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 X a { color: #aaaaaa; + &:hover { + background: transparent; + cursor: default; + } } &.edit-disabled > a { color: #aaaaaa; @@ -438,7 +442,12 @@ > a, > a:link{ background-color: $color-cms-batchactions-menu-selected-background; } - } + } + &.disabled { + > a > .jstree-checkbox { + background-position: -57px -54px; + } + } &.readonly { color: $color-text-disabled; padding-left: 18px; diff --git a/forms/TreeDropdownField.php b/forms/TreeDropdownField.php index d9a57c954..c56f575b7 100644 --- a/forms/TreeDropdownField.php +++ b/forms/TreeDropdownField.php @@ -53,7 +53,8 @@ class TreeDropdownField extends FormField { /** * @ignore */ - protected $sourceObject, $keyField, $labelField, $filterCallback, $searchCallback, $baseID = 0; + protected $sourceObject, $keyField, $labelField, $filterCallback, + $disableCallback, $searchCallback, $baseID = 0; /** * @var string default child method in Hierarcy->getChildrenAsUL */ @@ -122,6 +123,20 @@ class TreeDropdownField extends FormField { $this->filterCallback = $callback; return $this; } + + /** + * Set a callback used to disable checkboxes for some items in the tree + * + * @param callback $callback + */ + public function setDisableFunction($callback) { + if(!is_callable($callback, true)) { + throw new InvalidArgumentException('TreeDropdownField->setDisableFunction(): not passed a valid callback'); + } + + $this->disableCallback = $callback; + return $this; + } /** * Set a callback used to search the hierarchy globally, even before @@ -181,11 +196,11 @@ class TreeDropdownField extends FormField { if($record) { $title = $record->{$this->labelField}; } else { - if($this->showSearch){ + if($this->showSearch) { $title = _t('DropdownField.CHOOSESEARCH', '(Choose or Search)', 'start value of a dropdown'); - }else{ - $title = _t('DropdownField.CHOOSE', '(Choose)', 'start value of a dropdown'); - } + } else { + $title = _t('DropdownField.CHOOSE', '(Choose)', 'start value of a dropdown'); + } } // TODO Implement for TreeMultiSelectField @@ -276,12 +291,13 @@ class TreeDropdownField extends FormField { $keyField = $self->keyField; $labelField = $self->labelField; return sprintf( - '
  • %s', + '
  • %s', Convert::raw2xml($self->getName()), Convert::raw2xml($child->$keyField), Convert::raw2xml($child->$keyField), Convert::raw2xml($child->class), Convert::raw2xml($child->markingClasses()), + ($self->nodeIsDisabled($child)) ? 'disabled' : '', (int)$child->ID, $escapeLabelField ? Convert::raw2xml($child->$labelField) : $child->$labelField ); @@ -351,6 +367,15 @@ class TreeDropdownField extends FormField { return true; } + /** + * Marking a specific node in the tree as disabled + * @param $node + * @return boolean + */ + public function nodeIsDisabled($node) { + return ($this->disableCallback && call_user_func($this->disableCallback, $node)); + } + /** * @param String $field */ diff --git a/javascript/TreeDropdownField.js b/javascript/TreeDropdownField.js index 4f687b719..7c05f33e4 100644 --- a/javascript/TreeDropdownField.js +++ b/javascript/TreeDropdownField.js @@ -228,7 +228,25 @@ 'themes': { 'theme': 'apple' }, - 'plugins': ['html_data', 'ui', 'themes'] + 'types' : { + 'types' : { + 'default': { + 'check_node': function(node) { + return ( ! node.hasClass('disabled')); + }, + 'uncheck_node': function(node) { + return ( ! node.hasClass('disabled')); + }, + 'select_node': function(node) { + return ( ! node.hasClass('disabled')); + }, + 'deselect_node': function(node) { + return ( ! node.hasClass('disabled')); + } + } + } + }, + 'plugins': ['html_data', 'ui', 'themes', 'types'] }; }, /**