diff --git a/forms/TreeDropdownField.php b/forms/TreeDropdownField.php index 3dcff4899..7baa0bd9e 100755 --- a/forms/TreeDropdownField.php +++ b/forms/TreeDropdownField.php @@ -1,148 +1,181 @@ '$Action' + ); + + public static $allowed_actions = array ( + 'tree' + ); /** - * Create a new tree dropdown field. - * @param name The name of the field. - * @param title The label of the field. - * @param sourceObject The object-type to list in the tree. Must be a 'hierachy' object. - * @param keyField The column of that object-type to return as the field value. Defaults to ID - * @param labelField The column to show as the human-readable value in the tree. Defaults to Title + * @ignore */ - function __construct($name, $title, $sourceObject = "Group", $keyField = "ID", $labelField = "Title") { + protected $sourceObject, $keyField, $labelField, $filterCallback, $baseID = 0; + + /** + * @param string $name the field name + * @param string $title the field label + * @param string $souceClass the class to display in the tree, must have the "Hierachy" extension. + * @param string $keyField to field on the source class to save as the field value (default ID). + * @param string $labelField the field name to show as the human-readable value on the tree (default Title). + */ + public function __construct($name, $title = null, $sourceObject = 'Group', $keyField = 'ID', $labelField = 'Title') { $this->sourceObject = $sourceObject; - $this->keyField = $keyField; - $this->labelField = $labelField; + $this->keyField = $keyField; + $this->labelField = $labelField; + + if(!Object::has_extension($this->sourceObject, 'Hierarchy')) { + throw new Exception ( + "TreeDropdownField: the source class '$this->sourceObject' must have the Hierarchy extension applied" + ); + } + + Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); + + Requirements::javascript(THIRDPARTY_DIR . '/behaviour.js'); + Requirements::javascript(THIRDPARTY_DIR . '/tree/tree.js'); + Requirements::javascript(SAPPHIRE_DIR . '/javascript/TreeSelectorField.js'); + + Requirements::css(THIRDPARTY_DIR . '/tree/tree.css'); + Requirements::css(SAPPHIRE_DIR . '/css/TreeDropdownField.css'); + parent::__construct($name, $title); } - function setFilterFunction($filterFunc) { - $this->filterFunc = $filterFunc; - } /** - * Set the root node of the tree. Defaults to 0, ie, the whole tree + * Set the ID of the root node of the tree. This defaults to 0 - i.e. displays the whole tree. + * + * @param int $ID */ - function setTreeBaseID($treeBaseID) { - $this->treeBaseID = $treeBaseID; + public function setTreeBaseID($ID) { + $this->baseID = (int) $ID; } - function Field() { - Requirements::css(SAPPHIRE_DIR . '/css/TreeDropdownField.css'); - Requirements::javascript(THIRDPARTY_DIR . "/tree/tree.js"); - Requirements::css(THIRDPARTY_DIR . "/tree/tree.css"); - Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); - Requirements::javascript(SAPPHIRE_DIR . "/javascript/TreeSelectorField.js"); + /** + * Set a callback used to filter the values of the tree before displaying to the user. + * + * @param callback $callback + */ + public function setFilterFunction($callback) { + if(!is_callable($callback, true)) { + throw new InvalidArgumentException('TreeDropdownField->setFilterCallback(): not passed a valid callback'); + } - if($this->value) { - $record = $this->getByKey($this->value); - $title = ($record) ? $record->Title : _t('DropdownField.CHOOSE', "(Choose)", PR_MEDIUM, 'Start-value of a dropdown'); + $this->filterCallback = $callback; + } + + /** + * @return string + */ + public function Field() { + if($this->Value() && $record = $this->objectForKey($this->Value())) { + $title = $record->{$this->labelField}; } else { - $title = _t('DropdownField.CHOOSE', "(Choose)", PR_MEDIUM, 'Start-value of a dropdown'); + $title = _t('DropdownField.CHOOSE', '(Choose)', PR_MEDIUM, 'start value of a dropdown'); } - $id = $this->id(); - - $classes = "TreeDropdownField single"; - if($this->extraClass()) $classes .= ' ' . $this->extraClass(); - - return <<$title  -HTML; + return $this->createTag ( + 'div', + array ( + 'id' => "TreeDropdownField_{$this->id()}", + 'class' => 'TreeDropdownField single' . ($this->extraClass() ? " {$this->extraClass()}" : '') + ), + $this->createTag ( + 'input', + array ( + 'id' => $this->id(), + 'type' => 'hidden', + 'name' => $this->name, + 'value' => $this->value + ) + ) . $this->createTag ( + 'span', + array ( + 'class' => 'items' + ), + $title + ) . $this->createTag ( + 'a', + array ( + 'href' => '#', + 'title' => 'open', + 'class' => 'editLink' + ), + ' ' + ) + ); } - - /** - * Return the site tree + * Get the whole tree of a part of the tree via an AJAX request. + * + * @param HTTPRequest $request + * @return string */ - function gettree() { - if($this->treeBaseID) $obj = DataObject::get_by_id($this->sourceObject, $this->treeBaseID); - else $obj = singleton($this->sourceObject); + public function tree(HTTPRequest $request) { + $isSubTree = false; - if($this->filterFunc) $obj->setMarkingFilterFunction($this->filterFunc); - else if($this->sourceObject == 'Folder') $obj->setMarkingFilter('ClassName', 'Folder'); - $obj->markPartialTree(); - - // If we've already got values selected, make sure that we've got them in our tree - if($_REQUEST['forceValues']) { - $forceValues = preg_split("/ *, */", trim($_REQUEST['forceValues'])); - foreach($forceValues as $value) { - $obj->markToExpose($this->getByKey($value)); - } + if($ID = (int) $request->param('ID')) { + $obj = DataObject::get_by_id($this->sourceObject, $ID); + $isSubTree = true; + + if(!$obj) { + throw new Exception ( + "TreeDropdownField->tree(): the object #$ID of type $this->sourceObject could not be found" + ); + } + } else { + if($this->baseID) { + $obj = DataObject::get_by_id($this->sourceObject, $this->baseID); + } + + if(!$this->baseID || !$obj) $obj = singleton($this->sourceObject); + } + + if($this->filterCallback) { + $obj->setMarkingFilterFunction($this->filterCallback); + } elseif($this->sourceObject == 'Folder') { + $obj->setMarkingFilter('ClassName', 'Folder'); } - $eval = '"
  • name . '-$child->' . $this->keyField . '\" class=\"$child->class" . $child->markingClasses() . "\">" . $child->' . $this->labelField . ' . ""'; - echo $obj->getChildrenAsUL("class=\"tree\"", $eval, null, true); - } - - /** - * Return a subtree via Ajax - */ - public function getsubtree() { - $obj = $this->getByKey($_REQUEST['SubtreeRootID']); - - if(!$obj) user_error("Can't find database record $this->sourceObject with $this->keyField = $_REQUEST[SubtreeRootID]", E_USER_ERROR); - - if($this->filterFunc) $obj->setMarkingFilterFunction($this->filterFunc); - else if($this->sourceObject == 'Folder') $obj->setMarkingFilter('ClassName', 'Folder'); $obj->markPartialTree(); - - $eval = '"
  • name . '-$child->' . $this->keyField . '\" class=\"$child->class" . $child->markingClasses() . "\">" . $child->' . $this->labelField . ' . ""'; - $tree = $obj->getChildrenAsUL("", $eval, null, true); - echo substr(trim($tree), 4,-5); + + if($forceValues = $this->value) { + if(($values = preg_split('/,\s*/', $forceValues)) && count($values)) foreach($values as $value) { + $obj->markToExpose($this->objectForKey($value)); + } + } + + $eval = '"
  • Name() . '-{$child->' . $this->keyField . '}\" class=\"$child->class"' . + ' . $child->markingClasses() . "\">ID\">" . $child->' . $this->labelField . ' . ""'; + + if($isSubTree) { + return substr(trim($obj->getChildrenAsUL('', $eval, null, true)), 4, -5); + } + + return $obj->getChildrenAsUL('class="tree"', $eval, null, true); } /** + * Get the object where the $keyField is equal to a certain value + * + * @param string|int $key * @return DataObject */ - public function getByKey($key) { + protected function objectForKey($key) { if($this->keyField == 'ID') { return DataObject::get_by_id($this->sourceObject, $key); } else { - $SQL_key = Convert::raw2sql($key); - return DataObject::get_one($this->sourceObject, "\"$this->keyField\" = '$SQL_key'"); + return DataObject::get_one($this->sourceObject, "\"{$this->keyField}\" = '" . Convert::raw2sql($key) . "'"); } } - /** - * Return the stack of values to be traversed to find the given key in the database - */ - public function getstack() { - $page = $this->getByKey($_REQUEST['SubtreeRootID']); - - while($page->ParentID) { - echo $ids[] = $page->ID; - $page = $page->Parent; - } - $ids[] = $page->ID; - echo implode(",", array_reverse($ids)); - } - - function performReadonlyTransformation() { - $fieldName = $this->labelField; - if($this->value) { - $obj = ($this->getByKey($this->value)) ? $this->getByKey($this->value)->$fieldName : ''; - } else { - $obj = null; - } - $source = array( - $this->value => $obj - ); - $field = new LookupField($this->name, $this->title, $source); - $field->setValue($this->value); - $field->setForm($this->form); - $field->setReadonly(true); - return $field; - } - - } - -?> \ No newline at end of file diff --git a/javascript/TreeSelectorField.js b/javascript/TreeSelectorField.js index 1ead82b34..80655aa86 100755 --- a/javascript/TreeSelectorField.js +++ b/javascript/TreeSelectorField.js @@ -142,10 +142,9 @@ TreeDropdownField.prototype = { }, ajaxGetTree: function(after) { - var ajaxURL = this.helperURLBase() + 'gettree?forceValues=' + this.inputTag.value; + var ajaxURL = this.helperURLBase() + 'tree/'; ajaxURL += $('SecurityID') ? '&SecurityID=' + $('SecurityID').value : ''; if($('Form_EditForm_Locale')) ajaxURL += "&locale=" + $('Form_EditForm_Locale').value; - new Ajax.Request(ajaxURL, { method : 'get', onSuccess : after, @@ -165,7 +164,7 @@ TreeDropdownField.prototype = { this.tree = Tree.create(this.itemTree.getElementsByTagName('ul')[0], { ajaxExpansion: this.ajaxExpansion, getIdx: function() { - return this.id.replace(this.options.idxBase,''); + return this.getElementsByTagName('a')[0].getAttribute('rel'); }, idxBase : 'selector-' + this.inputTag.name + '-', dropdownField : this, @@ -190,7 +189,7 @@ TreeDropdownField.prototype = { var ul = this.treeNodeHolder(); ul.innerHTML = ss.i18n._t('LOADING', 'Loading...'); - var ajaxURL = this.options.dropdownField.helperURLBase() + 'getsubtree?&SubtreeRootID=' + this.getIdx(); + var ajaxURL = this.options.dropdownField.helperURLBase() + 'tree/' + this.getIdx(); ajaxURL += $('SecurityID') ? '&SecurityID=' + $('SecurityID').value : ''; if($('Form_EditForm_Locale')) ajaxURL += "&locale=" + $('Form_EditForm_Locale').value; @@ -230,12 +229,6 @@ TreeDropdownField.prototype = { } else { this.humanItems.innerHTML = this.inputTag.value ? this.inputTag.value : '(Choose)'; - /* - new Ajax.Request(this.options.dropdownField.helperURLBase() + '&methodName=findsubtreefor&ID=' + this.getIdx(), { - onSuccess : this.installSubtree.bind(this), - onFailure : function(response) { errorMessage('error loading subtree', response); } - }); - */ } }, setValueFromTree: function(treeID, title) { @@ -302,4 +295,4 @@ TreeMultiselectField.prototype = { } TreeMultiselectField.applyTo('div.TreeDropdownField.multiple'); -TreeDropdownField.applyTo('div.TreeDropdownField.single'); \ No newline at end of file +TreeDropdownField.applyTo('div.TreeDropdownField.single');