-
Notifications
You must be signed in to change notification settings - Fork 821
/
TreeMultiselectField.php
executable file
·190 lines (167 loc) · 6.32 KB
/
TreeMultiselectField.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
<?php
/**
* This formfield represents many-many joins using a tree selector shown in a dropdown styled element
* which can be added to any form usually in the CMS.
*
* This form class allows you to represent Many-Many Joins in a handy single field. The field has javascript which generates a AJAX tree of the site structure allowing you to save selected options to a component set on a given {@link DataObject}.
*
* <b>Saving</b>
*
* This field saves a {@link ComponentSet} object which is present on the {@link DataObject} passed by the form, returned by calling a function with the same name as the field. The Join is updated by running setByIDList on the {@link ComponentSet}
*
* <b>Customizing Save Behaviour</b>
*
* Before the data is saved, you can modify the ID list sent to the {@link ComponentSet} by specifying a function on the {@link DataObject} called "onChange[fieldname](&items)". This will be passed by reference the IDlist (an array of ID's) from the Treefield to be saved to the component set.
* Returning false on this method will prevent treemultiselect from saving to the {@link ComponentSet} of the given {@link DataObject}
*
* <code>
* // Called when we try and set the Parents() component set
* // by Tree Multiselect Field in the administration.
* function onChangeParents(&$items) {
* // This ensures this DataObject can never be a parent of itself
* if($items){
* foreach($items as $k => $id){
* if($id == $this->ID){
* unset($items[$k]);
* }
* }
* }
* return true;
* }
* </code>
*
* @see TreeDropdownField for the sample implementation, but only allowing single selects
*
* @package forms
* @subpackage fields-relational
*/
class TreeMultiselectField extends TreeDropdownField {
function __construct($name, $title, $sourceObject = "Group", $keyField = "ID", $labelField = "Title") {
parent::__construct($name, $title, $sourceObject, $keyField, $labelField);
$this->value = 'unchanged';
}
/**
* Return this field's linked items
*/
function getItems() {
// If the value has been set, use that
if($this->value != 'unchanged' && is_array($this->sourceObject)) {
$items = array();
$values = is_array($this->value) ? $this->value : preg_split('/ *, */', trim($this->value));
foreach($values as $value) {
$item = new stdClass;
$item->ID = $value;
$item->Title = $this->sourceObject[$value];
$items[] = $item;
}
return $items;
// Otherwise, look data up from the linked relation
} if($this->value != 'unchanged' && is_string($this->value)) {
$items = new DataObjectSet();
$ids = explode(',', $this->value);
foreach($ids as $id) {
if(!is_numeric($id)) continue;
$item = DataObject::get_by_id($this->sourceObject, $id);
if($item) $items->push($item);
}
return $items;
} else if($this->form) {
$fieldName = $this->name;
$record = $this->form->getRecord();
if(is_object($record) && $record->hasMethod($fieldName))
return $record->$fieldName();
}
}
/**
* We overwrite the field attribute to add our hidden fields, as this
* formfield can contain multiple values.
*/
function Field() {
$value = '';
$itemList = '';
Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/prototype/prototype.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/behaviour/behaviour.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/tree/tree.js');
// needed for errorMessage()
Requirements::javascript(SAPPHIRE_DIR . '/javascript/LeftAndMain.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/TreeSelectorField.js');
Requirements::css(SAPPHIRE_DIR . '/javascript/tree/tree.css');
Requirements::css(SAPPHIRE_DIR . '/css/TreeDropdownField.css');
$items = $this->getItems();
if($items && count($items)) {
foreach($items as $id => $item) {
$titleArray[] = $item->Title;
$idArray[] = $item->ID;
}
if(isset($titleArray)) {
$itemList = implode(", ", $titleArray);
$value = implode(",", $idArray);
}
}
$id = $this->id();
return <<<HTML
<div class="TreeDropdownField multiple" href="{$this->Link()}"><input id="$id" type="hidden" name="$this->name" value="$value" /><span class="items">$itemList</span><a href="#" title="open" class="editLink"> </a></div>
HTML;
}
/**
* Save the results into the form
* Calls function $record->onChange($items) before saving to the assummed
* Component set.
*/
function saveInto(DataObject $record) {
// Detect whether this field has actually been updated
if($this->value !== 'unchanged') {
$items = array();
$fieldName = $this->name;
$saveDest = $record->$fieldName();
if(!$saveDest) user_error("TreeMultiselectField::saveInto() Field '$fieldName' not found on $record->class.$record->ID", E_USER_ERROR);
if($this->value) {
$items = preg_split("/ *, */", trim($this->value));
}
// Allows you to modify the items on your object before save
$funcName = "onChange$fieldName";
if($record->hasMethod($funcName)){
$result = $record->$funcName($items);
if(!$result){
return;
}
}
$saveDest->setByIDList($items);
}
}
/**
* Changes this field to the readonly field.
*/
function performReadonlyTransformation() {
$field = new TreeMultiselectField_Readonly($this->name, $this->title, $this->sourceObject, $this->keyField, $this->labelField);
$field->addExtraClass($this->extraClass());
$field->setForm($this->form);
$field->setValue($this->value);
return $field;
}
}
/**
* @package forms
* @subpackage fields-relational
*/
class TreeMultiselectField_Readonly extends TreeMultiselectField {
protected $readonly = true;
function Field() {
$titleArray = $itemIDs = array();
$titleList = $itemIDsList = "";
if($items = $this->getItems()) {
foreach($items as $item) $titleArray[] = $item->Title;
foreach($items as $item) $itemIDs[] = $item->ID;
if($titleArray) $titleList = implode(", ", $titleArray);
if($itemIDs) $itemIDsList = implode(",", $itemIDs);
}
$field = new ReadonlyField($this->name.'_ReadonlyValue', $this->title);
$field->setValue($titleList);
$field->setForm($this->form);
$valueField = new HiddenField($this->name);
$valueField->setValue($itemIDsList);
$valueField->setForm($this->form);
return $field->Field().$valueField->Field();
}
}