mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #4568 from tractorcow/pulls/4.0/dropdown-cleanup
API Cleanup and refactor of select fields and Standardise Relation interface
This commit is contained in:
commit
1bf0605f5e
10
admin/thirdparty/chosen/chosen/chosen.jquery.js
vendored
10
admin/thirdparty/chosen/chosen/chosen.jquery.js
vendored
@ -49,7 +49,8 @@
|
|||||||
|
|
||||||
SelectParser.prototype.add_option = function(option, group_position, group_disabled) {
|
SelectParser.prototype.add_option = function(option, group_position, group_disabled) {
|
||||||
if (option.nodeName.toUpperCase() === "OPTION") {
|
if (option.nodeName.toUpperCase() === "OPTION") {
|
||||||
if (option.text !== "") {
|
// workaround for https://github.com/harvesthq/chosen/issues/2125
|
||||||
|
if (!option.text.match(/^\s*$/g)) {
|
||||||
if (group_position != null) {
|
if (group_position != null) {
|
||||||
this.parsed[group_position].children += 1;
|
this.parsed[group_position].children += 1;
|
||||||
}
|
}
|
||||||
@ -135,7 +136,12 @@ Copyright (c) 2011 by Harvest
|
|||||||
this.results_showing = false;
|
this.results_showing = false;
|
||||||
this.result_highlighted = null;
|
this.result_highlighted = null;
|
||||||
this.result_single_selected = null;
|
this.result_single_selected = null;
|
||||||
this.allow_single_deselect = (this.options.allow_single_deselect != null) && (this.form_field.options[0] != null) && this.form_field.options[0].text === "" ? this.options.allow_single_deselect : false;
|
this.allow_single_deselect = (this.options.allow_single_deselect != null)
|
||||||
|
&& (this.form_field.options[0] != null)
|
||||||
|
// workaround for https://github.com/harvesthq/chosen/issues/2125
|
||||||
|
&& this.form_field.options[0].text.match(/^\s*$/g)
|
||||||
|
? this.options.allow_single_deselect
|
||||||
|
: false;
|
||||||
this.disable_search_threshold = this.options.disable_search_threshold || 0;
|
this.disable_search_threshold = this.options.disable_search_threshold || 0;
|
||||||
this.disable_search = this.options.disable_search || false;
|
this.disable_search = this.options.disable_search || false;
|
||||||
this.search_contains = this.options.search_contains || false;
|
this.search_contains = this.options.search_contains || false;
|
||||||
|
@ -498,3 +498,18 @@ configuration customisation is done via overriding these templates.
|
|||||||
If upgrading from an existing installation, make sure to invoke ?flush=all at least once.
|
If upgrading from an existing installation, make sure to invoke ?flush=all at least once.
|
||||||
|
|
||||||
See [/developer_guides/files/file_security] for more information.
|
See [/developer_guides/files/file_security] for more information.
|
||||||
|
|
||||||
|
### `ListboxField` is now multiple-only
|
||||||
|
|
||||||
|
Previously, this field would operate as either a single select (default) or multi-select by setting
|
||||||
|
`setMultiple` to either true or false.
|
||||||
|
|
||||||
|
Now this field should only be used for multi-selection. Single-selection should be done using
|
||||||
|
a regular `DropdownField`.
|
||||||
|
|
||||||
|
### `GroupedDropdownField::setDisabled` now only accepts a list of values.
|
||||||
|
|
||||||
|
Where previously you could specify a list of grouped values in the same way as `setSource`, this
|
||||||
|
method now only accepts either a non-associative array of values (not titles) or an `SS_List`
|
||||||
|
of items to disable.
|
||||||
|
|
||||||
|
@ -36,12 +36,7 @@
|
|||||||
* @package forms
|
* @package forms
|
||||||
* @subpackage fields-basic
|
* @subpackage fields-basic
|
||||||
*/
|
*/
|
||||||
class CheckboxSetField extends OptionsetField {
|
class CheckboxSetField extends MultiSelectField {
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $defaultItems = array();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo Explain different source data that can be used with this field,
|
* @todo Explain different source data that can be used with this field,
|
||||||
@ -60,306 +55,43 @@ class CheckboxSetField extends OptionsetField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Gets the list of options to render in this formfield
|
||||||
|
*
|
||||||
* @return ArrayList
|
* @return ArrayList
|
||||||
*/
|
*/
|
||||||
public function getOptions() {
|
public function getOptions() {
|
||||||
|
$selectedValues = $this->getValueArray();
|
||||||
|
$defaultItems = $this->getDefaultItems();
|
||||||
|
|
||||||
|
// Generate list of options to display
|
||||||
$odd = 0;
|
$odd = 0;
|
||||||
|
$formID = $this->ID();
|
||||||
$source = $this->source;
|
$options = new ArrayList();
|
||||||
$values = $this->value;
|
foreach($this->getSource() as $itemValue => $title) {
|
||||||
$items = array();
|
$itemID = Convert::raw2htmlid("{$formID}_{$itemValue}");
|
||||||
|
|
||||||
// Get values from the join, if available
|
|
||||||
if(is_object($this->form)) {
|
|
||||||
$record = $this->form->getRecord();
|
|
||||||
if(!$values && $record && $record->hasMethod($this->name)) {
|
|
||||||
$funcName = $this->name;
|
|
||||||
$join = $record->$funcName();
|
|
||||||
if($join) {
|
|
||||||
foreach($join as $joinItem) {
|
|
||||||
$values[] = $joinItem->ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Source is not an array
|
|
||||||
if(!is_array($source) && !is_a($source, 'SQLMap')) {
|
|
||||||
if(is_array($values)) {
|
|
||||||
$items = $values;
|
|
||||||
} else {
|
|
||||||
// Source and values are DataObject sets.
|
|
||||||
if($values && is_a($values, 'SS_List')) {
|
|
||||||
foreach($values as $object) {
|
|
||||||
if(is_a($object, 'DataObject')) {
|
|
||||||
$items[] = $object->ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} elseif($values && is_string($values)) {
|
|
||||||
if(!empty($values)) {
|
|
||||||
$items = explode(',', $values);
|
|
||||||
$items = str_replace('{comma}', ',', $items);
|
|
||||||
} else {
|
|
||||||
$items = array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Sometimes we pass a singluar default value thats ! an array && !SS_List
|
|
||||||
if($values instanceof SS_List || is_array($values)) {
|
|
||||||
$items = $values;
|
|
||||||
} else {
|
|
||||||
if($values === null) {
|
|
||||||
$items = array();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(!empty($values)) {
|
|
||||||
$items = explode(',', $values);
|
|
||||||
$items = str_replace('{comma}', ',', $items);
|
|
||||||
} else {
|
|
||||||
$items = array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(is_array($source)) {
|
|
||||||
unset($source['']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$options = array();
|
|
||||||
|
|
||||||
if ($source == null) {
|
|
||||||
$source = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($source as $value => $item) {
|
|
||||||
if($item instanceof DataObject) {
|
|
||||||
$value = $item->ID;
|
|
||||||
$title = $item->Title;
|
|
||||||
} else {
|
|
||||||
$title = $item;
|
|
||||||
}
|
|
||||||
|
|
||||||
$itemID = $this->ID() . '_' . preg_replace('/[^a-zA-Z0-9]/', '', $value);
|
|
||||||
$odd = ($odd + 1) % 2;
|
$odd = ($odd + 1) % 2;
|
||||||
$extraClass = $odd ? 'odd' : 'even';
|
$extraClass = $odd ? 'odd' : 'even';
|
||||||
$extraClass .= ' val' . preg_replace('/[^a-zA-Z0-9\-\_]/', '_', $value);
|
$extraClass .= ' val' . preg_replace('/[^a-zA-Z0-9\-\_]/', '_', $itemValue);
|
||||||
|
|
||||||
$options[] = new ArrayData(array(
|
$itemChecked = in_array($itemValue, $selectedValues) || in_array($itemValue, $defaultItems);
|
||||||
|
$itemDisabled = $this->isDisabled() || in_array($itemValue, $defaultItems);
|
||||||
|
|
||||||
|
$options->push(new ArrayData(array(
|
||||||
'ID' => $itemID,
|
'ID' => $itemID,
|
||||||
'Class' => $extraClass,
|
'Class' => $extraClass,
|
||||||
'Name' => "{$this->name}[{$value}]",
|
'Name' => "{$this->name}[{$itemValue}]",
|
||||||
'Value' => $value,
|
'Value' => $itemValue,
|
||||||
'Title' => $title,
|
'Title' => $title,
|
||||||
'isChecked' => in_array($value, $items) || in_array($value, $this->defaultItems),
|
'isChecked' => $itemChecked,
|
||||||
'isDisabled' => $this->disabled || in_array($value, $this->disabledItems)
|
'isDisabled' => $itemDisabled,
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
$options = new ArrayList($options);
|
|
||||||
|
|
||||||
$this->extend('updateGetOptions', $options);
|
$this->extend('updateGetOptions', $options);
|
||||||
|
|
||||||
return $options;
|
return $options;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Default selections, regardless of the {@link setValue()} settings.
|
|
||||||
* Note: Items marked as disabled through {@link setDisabledItems()} can still be
|
|
||||||
* selected by default through this method.
|
|
||||||
*
|
|
||||||
* @param Array $items Collection of array keys, as defined in the $source array
|
|
||||||
*/
|
|
||||||
public function setDefaultItems($items) {
|
|
||||||
$this->defaultItems = $items;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Array
|
|
||||||
*/
|
|
||||||
public function getDefaultItems() {
|
|
||||||
return $this->defaultItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load a value into this CheckboxSetField
|
|
||||||
*/
|
|
||||||
public function setValue($value, $obj = null) {
|
|
||||||
// If we're not passed a value directly, we can look for it in a relation method on the object passed as a
|
|
||||||
// second arg
|
|
||||||
if(!$value && $obj && $obj instanceof DataObject && $obj->hasMethod($this->name)) {
|
|
||||||
$funcName = $this->name;
|
|
||||||
$value = $obj->$funcName()->getIDList();
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::setValue($value, $obj);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save the current value of this CheckboxSetField into a DataObject.
|
|
||||||
* If the field it is saving to is a has_many or many_many relationship,
|
|
||||||
* it is saved by setByIDList(), otherwise it creates a comma separated
|
|
||||||
* list for a standard DB text/varchar field.
|
|
||||||
*
|
|
||||||
* @param DataObject $record The record to save into
|
|
||||||
*/
|
|
||||||
public function saveInto(DataObjectInterface $record) {
|
|
||||||
$fieldname = $this->name;
|
|
||||||
$relation = ($fieldname && $record && $record->hasMethod($fieldname)) ? $record->$fieldname() : null;
|
|
||||||
if($fieldname && $record && $relation &&
|
|
||||||
($relation instanceof RelationList || $relation instanceof UnsavedRelationList)) {
|
|
||||||
$idList = array();
|
|
||||||
if($this->value) foreach($this->value as $id => $bool) {
|
|
||||||
if($bool) {
|
|
||||||
$idList[] = $id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$relation->setByIDList($idList);
|
|
||||||
} elseif($fieldname && $record) {
|
|
||||||
if($this->value) {
|
|
||||||
$this->value = str_replace(',', '{comma}', $this->value);
|
|
||||||
$record->$fieldname = implode(',', (array) $this->value);
|
|
||||||
} else {
|
|
||||||
$record->$fieldname = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the CheckboxSetField value as a string
|
|
||||||
* selected item keys.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function dataValue() {
|
|
||||||
if($this->value && is_array($this->value)) {
|
|
||||||
$filtered = array();
|
|
||||||
foreach($this->value as $item) {
|
|
||||||
if($item) {
|
|
||||||
$filtered[] = str_replace(",", "{comma}", $item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return implode(',', $filtered);
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function performDisabledTransformation() {
|
|
||||||
$clone = clone $this;
|
|
||||||
$clone->setDisabled(true);
|
|
||||||
|
|
||||||
return $clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms the source data for this CheckboxSetField
|
|
||||||
* into a comma separated list of values.
|
|
||||||
*
|
|
||||||
* @return ReadonlyField
|
|
||||||
*/
|
|
||||||
public function performReadonlyTransformation() {
|
|
||||||
$values = '';
|
|
||||||
$data = array();
|
|
||||||
|
|
||||||
$items = $this->value;
|
|
||||||
if($this->source) {
|
|
||||||
foreach($this->source as $source) {
|
|
||||||
if(is_object($source)) {
|
|
||||||
$sourceTitles[$source->ID] = $source->Title;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if($items) {
|
|
||||||
// Items is a DO Set
|
|
||||||
if($items instanceof SS_List) {
|
|
||||||
foreach($items as $item) {
|
|
||||||
$data[] = $item->Title;
|
|
||||||
}
|
|
||||||
if($data) $values = implode(', ', $data);
|
|
||||||
|
|
||||||
// Items is an array or single piece of string (including comma seperated string)
|
|
||||||
} else {
|
|
||||||
if(!is_array($items)) {
|
|
||||||
$items = preg_split('/ *, */', trim($items));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($items as $item) {
|
|
||||||
if(is_array($item)) {
|
|
||||||
$data[] = $item['Title'];
|
|
||||||
} elseif(is_array($this->source) && !empty($this->source[$item])) {
|
|
||||||
$data[] = $this->source[$item];
|
|
||||||
} elseif(is_a($this->source, 'SS_List')) {
|
|
||||||
$data[] = $sourceTitles[$item];
|
|
||||||
} else {
|
|
||||||
$data[] = $item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$values = implode(', ', $data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$field = $this->castedCopy('ReadonlyField');
|
|
||||||
$field->setValue($values);
|
|
||||||
|
|
||||||
return $field;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Type() {
|
public function Type() {
|
||||||
return 'optionset checkboxset';
|
return 'optionset checkboxset';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ExtraOptions() {
|
|
||||||
return FormField::ExtraOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate this field
|
|
||||||
*
|
|
||||||
* @param Validator $validator
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function validate($validator) {
|
|
||||||
$values = $this->value;
|
|
||||||
if (!$values) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$sourceArray = $this->getSourceAsArray();
|
|
||||||
if (is_array($values)) {
|
|
||||||
if (!array_intersect_key($sourceArray, $values)) {
|
|
||||||
$validator->validationError(
|
|
||||||
$this->name,
|
|
||||||
_t(
|
|
||||||
'CheckboxSetField.SOURCE_VALIDATION',
|
|
||||||
"Please select a value within the list provided. '{value}' is not a valid option",
|
|
||||||
array('value' => implode(' and ', array_diff($sourceArray, $values)))
|
|
||||||
),
|
|
||||||
"validation"
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!in_array($this->value, $sourceArray)) {
|
|
||||||
$validator->validationError(
|
|
||||||
$this->name,
|
|
||||||
_t(
|
|
||||||
'CheckboxSetField.SOURCE_VALIDATION',
|
|
||||||
"Please select a value within the list provided. '{value}' is not a valid option",
|
|
||||||
array('value' => $this->value)
|
|
||||||
),
|
|
||||||
"validation"
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,13 +11,18 @@ class CountryDropdownField extends DropdownField {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Should we default the dropdown to the region determined from the user's locale?
|
* Should we default the dropdown to the region determined from the user's locale?
|
||||||
|
*
|
||||||
|
* @config
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private static $default_to_locale = true;
|
private static $default_to_locale = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The region code to default to if default_to_locale is set to false, or we can't determine a region from a locale
|
* The region code to default to if default_to_locale is set to false, or we can't
|
||||||
* @var string
|
* determine a region from a locale.
|
||||||
|
*
|
||||||
|
* @config
|
||||||
|
* @var string
|
||||||
*/
|
*/
|
||||||
private static $default_country = 'NZ';
|
private static $default_country = 'NZ';
|
||||||
|
|
||||||
@ -28,48 +33,57 @@ class CountryDropdownField extends DropdownField {
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function locale() {
|
protected function locale() {
|
||||||
if (($member = Member::currentUser()) && $member->Locale) return $member->Locale;
|
if (($member = Member::currentUser()) && $member->Locale) {
|
||||||
|
return $member->Locale;
|
||||||
|
}
|
||||||
return i18n::get_locale();
|
return i18n::get_locale();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct($name, $title = null, $source = null, $value = "", $form=null) {
|
public function setSource($source) {
|
||||||
if(!is_array($source)) {
|
if($source) {
|
||||||
// Get a list of countries from Zend
|
return parent::setSource($source);
|
||||||
$source = Zend_Locale::getTranslationList('territory', $this->locale(), 2);
|
|
||||||
|
|
||||||
// We want them ordered by display name, not country code
|
|
||||||
|
|
||||||
// PHP 5.3 has an extension that sorts UTF-8 strings correctly
|
|
||||||
if (class_exists('Collator') && ($collator = Collator::create($this->locale()))) {
|
|
||||||
$collator->asort($source);
|
|
||||||
}
|
|
||||||
// Otherwise just put up with them being weirdly ordered for now
|
|
||||||
else {
|
|
||||||
asort($source);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't want "unknown country" as an option
|
|
||||||
unset($source['ZZ']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parent::__construct($name, ($title===null) ? $name : $title, $source, $value, $form);
|
// map empty source to country list
|
||||||
|
// Get a list of countries from Zend
|
||||||
|
$source = Zend_Locale::getTranslationList('territory', $this->locale(), 2);
|
||||||
|
|
||||||
|
// We want them ordered by display name, not country code
|
||||||
|
|
||||||
|
// PHP 5.3 has an extension that sorts UTF-8 strings correctly
|
||||||
|
if (class_exists('Collator') && ($collator = Collator::create($this->locale()))) {
|
||||||
|
$collator->asort($source);
|
||||||
|
} else {
|
||||||
|
// Otherwise just put up with them being weirdly ordered for now
|
||||||
|
asort($source);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't want "unknown country" as an option
|
||||||
|
unset($source['ZZ']);
|
||||||
|
|
||||||
|
return parent::setSource($source);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Field($properties = array()) {
|
public function Field($properties = array()) {
|
||||||
$source = $this->getSource();
|
$source = $this->getSource();
|
||||||
|
|
||||||
if (!$this->value || !isset($source[$this->value])) {
|
// Default value to best availabel locale
|
||||||
if ($this->config()->default_to_locale && $this->locale()) {
|
$value = $this->Value();
|
||||||
$locale = new Zend_Locale();
|
if ($this->config()->default_to_locale
|
||||||
$locale->setLocale($this->locale());
|
&& (!$value || !isset($source[$value]))
|
||||||
$this->value = $locale->getRegion();
|
&& $this->locale()
|
||||||
}
|
) {
|
||||||
|
$locale = new Zend_Locale();
|
||||||
|
$locale->setLocale($this->locale());
|
||||||
|
$value = $locale->getRegion();
|
||||||
|
$this->setValue($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->value || !isset($source[$this->value])) {
|
// Default to default country otherwise
|
||||||
$this->value = $this->config()->default_country;
|
if (!$value || !isset($source[$value])) {
|
||||||
|
$this->setValue($this->config()->default_country);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::Field();
|
return parent::Field($properties);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,51 +81,31 @@
|
|||||||
* @package forms
|
* @package forms
|
||||||
* @subpackage fields-basic
|
* @subpackage fields-basic
|
||||||
*/
|
*/
|
||||||
class DropdownField extends FormField {
|
class DropdownField extends SingleSelectField {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array|ArrayAccess $source Associative or numeric array of all dropdown items,
|
* Build a field option for template rendering
|
||||||
* with array key as the submitted field value, and the array value as a
|
*
|
||||||
* natural language description shown in the interface element.
|
* @param mixed $value Value of the option
|
||||||
|
* @param string $title Title of the option
|
||||||
|
* @return ArrayData Field option
|
||||||
*/
|
*/
|
||||||
protected $source;
|
protected function getFieldOption($value, $title) {
|
||||||
|
// Check selection
|
||||||
|
$selected = $this->isSelectedValue($value, $this->Value());
|
||||||
|
|
||||||
/**
|
// Check disabled
|
||||||
* @var boolean $isSelected Determines if the field was selected
|
$disabled = false;
|
||||||
* at the time it was rendered, so if {@link $value} matches on of the array
|
if($this->isDisabledValue($value) && $title != $this->getEmptyString()){
|
||||||
* values specified in {@link $source}
|
$disabled = 'disabled';
|
||||||
*/
|
}
|
||||||
protected $isSelected;
|
|
||||||
|
|
||||||
/**
|
return new ArrayData(array(
|
||||||
* @var boolean $hasEmptyDefault Show the first <option> element as
|
'Title' => $title,
|
||||||
* empty (not having a value), with an optional label defined through
|
'Value' => $value,
|
||||||
* {@link $emptyString}. By default, the <select> element will be
|
'Selected' => $selected,
|
||||||
* rendered with the first option from {@link $source} selected.
|
'Disabled' => $disabled,
|
||||||
*/
|
));
|
||||||
protected $hasEmptyDefault = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string $emptyString The title shown for an empty default selection,
|
|
||||||
* e.g. "Select...".
|
|
||||||
*/
|
|
||||||
protected $emptyString = '';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array $disabledItems The keys for items that should be disabled (greyed out) in the dropdown
|
|
||||||
*/
|
|
||||||
protected $disabledItems = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $name The field name
|
|
||||||
* @param string $title The field title
|
|
||||||
* @param array|ArrayAccess $source A map of the dropdown items
|
|
||||||
* @param string $value The current value
|
|
||||||
* @param Form $form The parent form
|
|
||||||
*/
|
|
||||||
public function __construct($name, $title=null, $source=array(), $value='', $form=null) {
|
|
||||||
$this->setSource($source);
|
|
||||||
parent::__construct($name, ($title===null) ? $name : $title, $value, $form);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,49 +113,11 @@ class DropdownField extends FormField {
|
|||||||
* @return HTMLText
|
* @return HTMLText
|
||||||
*/
|
*/
|
||||||
public function Field($properties = array()) {
|
public function Field($properties = array()) {
|
||||||
$source = $this->getSource();
|
|
||||||
$options = array();
|
$options = array();
|
||||||
|
|
||||||
if ($this->getHasEmptyDefault()) {
|
// Add all options
|
||||||
$selected = ($this->value === '' || $this->value === null);
|
foreach($this->getSourceEmpty() as $value => $title) {
|
||||||
$disabled = (in_array('', $this->disabledItems, true)) ? 'disabled' : false;
|
$options[] = $this->getFieldOption($value, $title);
|
||||||
|
|
||||||
$options[] = new ArrayData(array(
|
|
||||||
'Value' => '',
|
|
||||||
'Title' => $this->getEmptyString(),
|
|
||||||
'Selected' => $selected,
|
|
||||||
'Disabled' => $disabled
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($source) {
|
|
||||||
foreach($source as $value => $title) {
|
|
||||||
$selected = false;
|
|
||||||
if($value === '' && ($this->value === '' || $this->value === null)) {
|
|
||||||
$selected = true;
|
|
||||||
} else {
|
|
||||||
// check against value, fallback to a type check comparison when !value
|
|
||||||
if($value) {
|
|
||||||
$selected = ($value == $this->value);
|
|
||||||
} else {
|
|
||||||
$selected = ($value === $this->value) || (((string) $value) === ((string) $this->value));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->isSelected = $selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
$disabled = false;
|
|
||||||
if(in_array($value, $this->disabledItems) && $title != $this->emptyString ){
|
|
||||||
$disabled = 'disabled';
|
|
||||||
}
|
|
||||||
|
|
||||||
$options[] = new ArrayData(array(
|
|
||||||
'Title' => $title,
|
|
||||||
'Value' => $value,
|
|
||||||
'Selected' => $selected,
|
|
||||||
'Disabled' => $disabled,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$properties = array_merge($properties, array(
|
$properties = array_merge($properties, array(
|
||||||
@ -184,173 +126,4 @@ class DropdownField extends FormField {
|
|||||||
|
|
||||||
return parent::Field($properties);
|
return parent::Field($properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark certain elements as disabled, regardless of the
|
|
||||||
* {@link setDisabled()} settings.
|
|
||||||
*
|
|
||||||
* @param array $items Collection of array keys, as defined in the $source array
|
|
||||||
*/
|
|
||||||
public function setDisabledItems($items) {
|
|
||||||
$this->disabledItems = $items;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getDisabledItems() {
|
|
||||||
return $this->disabledItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getAttributes() {
|
|
||||||
return array_merge(
|
|
||||||
parent::getAttributes(),
|
|
||||||
array(
|
|
||||||
'type' => null,
|
|
||||||
'value' => null
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function isSelected() {
|
|
||||||
return $this->isSelected;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the source array including any empty default values.
|
|
||||||
*
|
|
||||||
* @return array|ArrayAccess
|
|
||||||
*/
|
|
||||||
public function getSource() {
|
|
||||||
return $this->source;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array|ArrayAccess $source
|
|
||||||
*/
|
|
||||||
public function setSource($source) {
|
|
||||||
$this->source = $source;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param boolean $bool
|
|
||||||
*/
|
|
||||||
public function setHasEmptyDefault($bool) {
|
|
||||||
$this->hasEmptyDefault = $bool;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function getHasEmptyDefault() {
|
|
||||||
return $this->hasEmptyDefault;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the default selection label, e.g. "select...".
|
|
||||||
*
|
|
||||||
* Defaults to an empty string. Automatically sets {@link $hasEmptyDefault}
|
|
||||||
* to true.
|
|
||||||
*
|
|
||||||
* @param string $str
|
|
||||||
*/
|
|
||||||
public function setEmptyString($str) {
|
|
||||||
$this->setHasEmptyDefault(true);
|
|
||||||
$this->emptyString = $str;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getEmptyString() {
|
|
||||||
return $this->emptyString;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return LookupField
|
|
||||||
*/
|
|
||||||
public function performReadonlyTransformation() {
|
|
||||||
$field = $this->castedCopy('LookupField');
|
|
||||||
$field->setSource($this->getSource());
|
|
||||||
$field->setReadonly(true);
|
|
||||||
|
|
||||||
return $field;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the source of this field as an array
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getSourceAsArray()
|
|
||||||
{
|
|
||||||
$source = $this->getSource();
|
|
||||||
if (is_array($source)) {
|
|
||||||
return $source;
|
|
||||||
} else {
|
|
||||||
$sourceArray = array();
|
|
||||||
foreach ($source as $key => $value) {
|
|
||||||
$sourceArray[$key] = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $sourceArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate this field
|
|
||||||
*
|
|
||||||
* @param Validator $validator
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function validate($validator) {
|
|
||||||
$source = $this->getSourceAsArray();
|
|
||||||
$disabled = $this->getDisabledItems();
|
|
||||||
|
|
||||||
if (!array_key_exists($this->value, $source) || in_array($this->value, $disabled)) {
|
|
||||||
if ($this->getHasEmptyDefault() && !$this->value) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$validator->validationError(
|
|
||||||
$this->name,
|
|
||||||
_t(
|
|
||||||
'DropdownField.SOURCE_VALIDATION',
|
|
||||||
"Please select a value within the list provided. {value} is not a valid option",
|
|
||||||
array('value' => $this->value)
|
|
||||||
),
|
|
||||||
"validation"
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns another instance of this field, but "cast" to a different class.
|
|
||||||
*
|
|
||||||
* @see FormField::castedCopy()
|
|
||||||
*
|
|
||||||
* @param String $classOrCopy
|
|
||||||
* @return FormField
|
|
||||||
*/
|
|
||||||
public function castedCopy($classOrCopy) {
|
|
||||||
$field = parent::castedCopy($classOrCopy);
|
|
||||||
if($field->hasMethod('setHasEmptyDefault')) {
|
|
||||||
$field->setHasEmptyDefault($this->getHasEmptyDefault());
|
|
||||||
}
|
|
||||||
return $field;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -649,7 +649,6 @@ class FormField extends RequestHandler {
|
|||||||
*/
|
*/
|
||||||
public function setValue($value) {
|
public function setValue($value) {
|
||||||
$this->value = $value;
|
$this->value = $value;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,18 +36,13 @@
|
|||||||
*
|
*
|
||||||
* <b>Disabling individual items</b>
|
* <b>Disabling individual items</b>
|
||||||
*
|
*
|
||||||
|
* Unlike the source, disabled items are specified in the same way as
|
||||||
|
* normal DropdownFields, using a single value list. Don't pass in grouped
|
||||||
|
* values here.
|
||||||
|
*
|
||||||
* <code>
|
* <code>
|
||||||
* $groupedDrDownField->setDisabledItems(
|
* // Disables first and third option in each group
|
||||||
* array(
|
* $groupedDrDownField->setDisabledItems(array("1", "3"))
|
||||||
* "numbers" => array(
|
|
||||||
* "1" => "1",
|
|
||||||
* "3" => "3"
|
|
||||||
* ),
|
|
||||||
* "letters" => array(
|
|
||||||
* "3" => "C"
|
|
||||||
* )
|
|
||||||
* )
|
|
||||||
* )
|
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
* @package forms
|
* @package forms
|
||||||
@ -55,80 +50,47 @@
|
|||||||
*/
|
*/
|
||||||
class GroupedDropdownField extends DropdownField {
|
class GroupedDropdownField extends DropdownField {
|
||||||
|
|
||||||
public function Field($properties = array()) {
|
/**
|
||||||
$options = '';
|
* Build a potentially nested fieldgroup
|
||||||
foreach($this->getSource() as $value => $title) {
|
*
|
||||||
if(is_array($title)) {
|
* @param mixed $valueOrGroup Value of item, or title of group
|
||||||
$options .= "<optgroup label=\"$value\">";
|
* @param string|array $titleOrOptions Title of item, or options in grouip
|
||||||
foreach($title as $value2 => $title2) {
|
* @return ArrayData Data for this item
|
||||||
$disabled = '';
|
*/
|
||||||
if( array_key_exists($value, $this->disabledItems)
|
protected function getFieldOption($valueOrGroup, $titleOrOptions) {
|
||||||
&& is_array($this->disabledItems[$value])
|
// Return flat option
|
||||||
&& in_array($value2, $this->disabledItems[$value]) ){
|
if(!is_array($titleOrOptions)) {
|
||||||
$disabled = 'disabled="disabled"';
|
return parent::getFieldOption($valueOrGroup, $titleOrOptions);
|
||||||
}
|
|
||||||
$selected = $value2 == $this->value ? " selected=\"selected\"" : "";
|
|
||||||
$options .= "<option$selected value=\"$value2\" $disabled>$title2</option>";
|
|
||||||
}
|
|
||||||
$options .= "</optgroup>";
|
|
||||||
} else { // Fall back to the standard dropdown field
|
|
||||||
$disabled = '';
|
|
||||||
if( in_array($value, $this->disabledItems) ){
|
|
||||||
$disabled = 'disabled="disabled"';
|
|
||||||
}
|
|
||||||
$selected = $value == $this->value ? " selected=\"selected\"" : "";
|
|
||||||
$options .= "<option$selected value=\"$value\" $disabled>$title</option>";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return FormField::create_tag('select', $this->getAttributes(), $options);
|
// Build children from options list
|
||||||
|
$options = new ArrayList();
|
||||||
|
foreach($titleOrOptions as $childValue => $childTitle) {
|
||||||
|
$options->push($this->getFieldOption($childValue, $childTitle));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ArrayData(array(
|
||||||
|
'Title' => $valueOrGroup,
|
||||||
|
'Options' => $options
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Type() {
|
public function Type() {
|
||||||
return 'groupeddropdown dropdown';
|
return 'groupeddropdown dropdown';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getSourceValues() {
|
||||||
* Validate this field
|
// Flatten values
|
||||||
*
|
$values = array();
|
||||||
* @param Validator $validator
|
$source = $this->getSource();
|
||||||
* @return bool
|
array_walk_recursive(
|
||||||
*/
|
$source,
|
||||||
public function validate($validator) {
|
// Function to extract value from array key
|
||||||
$valid = false;
|
function($title, $value) use (&$values) {
|
||||||
$source = $this->getSourceAsArray();
|
$values[] = $value;
|
||||||
$disabled = $this->getDisabledItems();
|
|
||||||
|
|
||||||
if ($this->value) {
|
|
||||||
foreach ($source as $value => $title) {
|
|
||||||
if (is_array($title) && array_key_exists($this->value, $title)) {
|
|
||||||
// Check that the set value is not in the list of disabled items
|
|
||||||
if (!isset($disabled[$value]) || !in_array($this->value, $disabled[$value])) {
|
|
||||||
$valid = true;
|
|
||||||
}
|
|
||||||
// Check that the value matches and is not disabled
|
|
||||||
} elseif($this->value == $value && !in_array($this->value, $disabled)) {
|
|
||||||
$valid = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} elseif ($this->getHasEmptyDefault()) {
|
);
|
||||||
$valid = true;
|
return $values;
|
||||||
}
|
|
||||||
|
|
||||||
if (!$valid) {
|
|
||||||
$validator->validationError(
|
|
||||||
$this->name,
|
|
||||||
_t(
|
|
||||||
'DropdownField.SOURCE_VALIDATION',
|
|
||||||
"Please select a value within the list provided. {value} is not a valid option",
|
|
||||||
array('value' => $this->value)
|
|
||||||
),
|
|
||||||
"validation"
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
* @package forms
|
* @package forms
|
||||||
* @subpackage fields-basic
|
* @subpackage fields-basic
|
||||||
*/
|
*/
|
||||||
class ListboxField extends DropdownField {
|
class ListboxField extends MultiSelectField {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The size of the field in rows.
|
* The size of the field in rows.
|
||||||
@ -33,223 +33,107 @@ class ListboxField extends DropdownField {
|
|||||||
*/
|
*/
|
||||||
protected $size;
|
protected $size;
|
||||||
|
|
||||||
/**
|
|
||||||
* Should the user be able to select multiple
|
|
||||||
* items on this dropdown field?
|
|
||||||
*
|
|
||||||
* @var boolean
|
|
||||||
*/
|
|
||||||
protected $multiple = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Array
|
* @var Array
|
||||||
*/
|
*/
|
||||||
protected $disabledItems = array();
|
protected $disabledItems = array();
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Array
|
|
||||||
*/
|
|
||||||
protected $defaultItems = array();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new dropdown field.
|
* Creates a new dropdown field.
|
||||||
*
|
*
|
||||||
* @param string $name The field name
|
* @param string $name The field name
|
||||||
* @param string $title The field title
|
* @param string $title The field title
|
||||||
* @param array $source An map of the dropdown items
|
* @param array $source An map of the dropdown items
|
||||||
* @param string|array $value You can pass an array of values or a single value like a drop down to be selected
|
* @param string|array|null $value You can pass an array of values or a single value like a drop down to be selected
|
||||||
* @param int $size Optional size of the select element
|
* @param int $size Optional size of the select element
|
||||||
* @param form The parent form
|
|
||||||
*/
|
*/
|
||||||
public function __construct($name, $title = '', $source = array(), $value = '', $size = null, $multiple = false) {
|
public function __construct($name, $title = '', $source = array(), $value = null, $size = null) {
|
||||||
if($size) $this->size = $size;
|
if($size) {
|
||||||
if($multiple) $this->multiple = $multiple;
|
$this->setSize($size);
|
||||||
|
}
|
||||||
|
|
||||||
parent::__construct($name, $title, $source, $value);
|
parent::__construct($name, $title, $source, $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a <select> tag containing all the appropriate <option> tags
|
* Returns a <select> tag containing all the appropriate <option> tags
|
||||||
|
*
|
||||||
|
* @param array $properties
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function Field($properties = array()) {
|
public function Field($properties = array()) {
|
||||||
if($this->multiple) $this->name .= '[]';
|
$properties = array_merge($properties, array(
|
||||||
$options = array();
|
'Options' => $this->getOptions()
|
||||||
|
));
|
||||||
|
return $this
|
||||||
|
->customise($properties)
|
||||||
|
->renderWith($this->getTemplates());
|
||||||
|
}
|
||||||
|
|
||||||
// We have an array of values
|
/**
|
||||||
if(is_array($this->value)){
|
* Gets the list of options to render in this formfield
|
||||||
// Loop through and figure out which values were selected.
|
*
|
||||||
foreach($this->getSource() as $value => $title) {
|
* @return ArrayList
|
||||||
$options[] = new ArrayData(array(
|
*/
|
||||||
'Title' => $title,
|
public function getOptions() {
|
||||||
'Value' => $value,
|
// Loop through and figure out which values were selected.
|
||||||
'Selected' => (in_array($value, $this->value) || in_array($value, $this->defaultItems)),
|
$options = array();
|
||||||
'Disabled' => $this->disabled || in_array($value, $this->disabledItems),
|
$selectedValue = $this->getValueArray();
|
||||||
));
|
foreach($this->getSource() as $itemValue => $title) {
|
||||||
}
|
$itemSelected = in_array($itemValue, $selectedValue)
|
||||||
} else {
|
|| in_array($itemValue, $this->getDefaultItems());
|
||||||
// Listbox was based a singlular value, so treat it like a dropdown.
|
$itemDisabled = $this->isDisabled()
|
||||||
foreach($this->getSource() as $value => $title) {
|
|| in_array($itemValue, $this->getDisabledItems());
|
||||||
$options[] = new ArrayData(array(
|
$options[] = new ArrayData(array(
|
||||||
'Title' => $title,
|
'Title' => $title,
|
||||||
'Value' => $value,
|
'Value' => $itemValue,
|
||||||
'Selected' => ($value == $this->value || in_array($value, $this->defaultItems)),
|
'Selected' => $itemSelected,
|
||||||
'Disabled' => $this->disabled || in_array($value, $this->disabledItems),
|
'Disabled' => $itemDisabled,
|
||||||
));
|
));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$properties = array_merge($properties, array(
|
$options = new ArrayList($options);
|
||||||
'Options' => new ArrayList($options)
|
$this->extend('updateGetOptions', $options);
|
||||||
));
|
return $options;
|
||||||
|
|
||||||
return $this->customise($properties)->renderWith($this->getTemplates());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAttributes() {
|
public function getAttributes() {
|
||||||
return array_merge(
|
return array_merge(
|
||||||
parent::getAttributes(),
|
parent::getAttributes(),
|
||||||
array(
|
array(
|
||||||
'multiple' => $this->multiple,
|
'multiple' => 'true',
|
||||||
'size' => $this->size
|
'size' => $this->getSize(),
|
||||||
|
'name' => $this->getName() . '[]'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the size of this dropdown in rows.
|
||||||
|
*
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
public function getSize() {
|
||||||
|
return $this->size;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the size of this dropdown in rows.
|
* Sets the size of this dropdown in rows.
|
||||||
|
*
|
||||||
* @param int $size The height in rows (e.g. 3)
|
* @param int $size The height in rows (e.g. 3)
|
||||||
|
* @return $this Self reference
|
||||||
*/
|
*/
|
||||||
public function setSize($size) {
|
public function setSize($size) {
|
||||||
$this->size = $size;
|
$this->size = $size;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets this field to have a muliple select attribute
|
|
||||||
* @param boolean $bool
|
|
||||||
*/
|
|
||||||
public function setMultiple($bool) {
|
|
||||||
$this->multiple = $bool;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setSource($source) {
|
|
||||||
if($source) {
|
|
||||||
$hasCommas = array_filter(array_keys($source),
|
|
||||||
create_function('$key', 'return strpos($key, ",") !== FALSE;'));
|
|
||||||
if($hasCommas) {
|
|
||||||
throw new InvalidArgumentException('No commas allowed in $source keys');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::setSource($source);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the CheckboxSetField value as a string
|
|
||||||
* selected item keys.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function dataValue() {
|
|
||||||
if($this->value && is_array($this->value) && $this->multiple) {
|
|
||||||
$filtered = array();
|
|
||||||
foreach($this->value as $item) {
|
|
||||||
if($item) {
|
|
||||||
$filtered[] = str_replace(",", "{comma}", $item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return implode(',', $filtered);
|
|
||||||
} else {
|
|
||||||
return parent::dataValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save the current value of this field into a DataObject.
|
|
||||||
* If the field it is saving to is a has_many or many_many relationship,
|
|
||||||
* it is saved by setByIDList(), otherwise it creates a comma separated
|
|
||||||
* list for a standard DB text/varchar field.
|
|
||||||
*
|
|
||||||
* @param DataObject $record The record to save into
|
|
||||||
*/
|
|
||||||
public function saveInto(DataObjectInterface $record) {
|
|
||||||
if($this->multiple) {
|
|
||||||
$fieldname = $this->name;
|
|
||||||
$relation = ($fieldname && $record && $record->hasMethod($fieldname)) ? $record->$fieldname() : null;
|
|
||||||
if($fieldname && $record && $relation &&
|
|
||||||
($relation instanceof RelationList || $relation instanceof UnsavedRelationList)) {
|
|
||||||
$idList = (is_array($this->value)) ? array_values($this->value) : array();
|
|
||||||
if(!$record->ID) {
|
|
||||||
$record->write(); // record needs to have an ID in order to set relationships
|
|
||||||
$relation = ($fieldname && $record && $record->hasMethod($fieldname))
|
|
||||||
? $record->$fieldname()
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
$relation->setByIDList($idList);
|
|
||||||
} elseif($fieldname && $record) {
|
|
||||||
if($this->value) {
|
|
||||||
$this->value = str_replace(',', '{comma}', $this->value);
|
|
||||||
$record->$fieldname = implode(",", $this->value);
|
|
||||||
} else {
|
|
||||||
$record->$fieldname = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parent::saveInto($record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load a value into this ListboxField
|
|
||||||
*/
|
|
||||||
public function setValue($val, $obj = null) {
|
|
||||||
// If we're not passed a value directly,
|
|
||||||
// we can look for it in a relation method on the object passed as a second arg
|
|
||||||
if(!$val && $obj && $obj instanceof DataObject && $obj->hasMethod($this->name)) {
|
|
||||||
$funcName = $this->name;
|
|
||||||
$val = array_values($obj->$funcName()->getIDList());
|
|
||||||
}
|
|
||||||
|
|
||||||
if($val) {
|
|
||||||
if(!$this->multiple && is_array($val)) {
|
|
||||||
throw new InvalidArgumentException('Array values are not allowed (when multiple=false).');
|
|
||||||
}
|
|
||||||
|
|
||||||
if($this->multiple) {
|
|
||||||
$parts = (is_array($val)) ? $val : preg_split("/ *, */", trim($val));
|
|
||||||
if(ArrayLib::is_associative($parts)) {
|
|
||||||
// This is due to the possibility of accidentally passing an array of values (as keys) and titles (as values) when only the keys were intended to be saved.
|
|
||||||
throw new InvalidArgumentException('Associative arrays are not allowed as values (when multiple=true), only indexed arrays.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Doesn't check against unknown values in order to allow for less rigid data handling.
|
|
||||||
// They're silently ignored and overwritten the next time the field is saved.
|
|
||||||
parent::setValue($parts);
|
|
||||||
} else {
|
|
||||||
if(!in_array($val, array_keys($this->getSource()))) {
|
|
||||||
throw new InvalidArgumentException(sprintf(
|
|
||||||
'Invalid value "%s" for multiple=false',
|
|
||||||
Convert::raw2xml($val)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::setValue($val);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parent::setValue($val);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark certain elements as disabled,
|
* Mark certain elements as disabled,
|
||||||
* regardless of the {@link setDisabled()} settings.
|
* regardless of the {@link setDisabled()} settings.
|
||||||
*
|
*
|
||||||
* @param array $items Collection of array keys, as defined in the $source array
|
* @param array $items Collection of array keys, as defined in the $source array
|
||||||
|
* @return $this Self reference
|
||||||
*/
|
*/
|
||||||
public function setDisabledItems($items) {
|
public function setDisabledItems($items) {
|
||||||
$this->disabledItems = $items;
|
$this->disabledItems = $items;
|
||||||
@ -257,70 +141,10 @@ class ListboxField extends DropdownField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getDisabledItems() {
|
public function getDisabledItems() {
|
||||||
return $this->disabledItems;
|
return $this->disabledItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Default selections, regardless of the {@link setValue()} settings.
|
|
||||||
* Note: Items marked as disabled through {@link setDisabledItems()} can still be
|
|
||||||
* selected by default through this method.
|
|
||||||
*
|
|
||||||
* @param Array $items Collection of array keys, as defined in the $source array
|
|
||||||
*/
|
|
||||||
public function setDefaultItems($items) {
|
|
||||||
$this->defaultItems = $items;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Array
|
|
||||||
*/
|
|
||||||
public function getDefaultItems() {
|
|
||||||
return $this->defaultItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate this field
|
|
||||||
*
|
|
||||||
* @param Validator $validator
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function validate($validator) {
|
|
||||||
$values = $this->value;
|
|
||||||
if (!$values) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$source = $this->getSourceAsArray();
|
|
||||||
if (is_array($values)) {
|
|
||||||
if (!array_intersect_key($source,array_flip($values))) {
|
|
||||||
$validator->validationError(
|
|
||||||
$this->name,
|
|
||||||
_t(
|
|
||||||
"Please select a value within the list provided. {value} is not a valid option",
|
|
||||||
array('value' => $this->value)
|
|
||||||
),
|
|
||||||
"validation"
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!array_key_exists($this->value, $source)) {
|
|
||||||
$validator->validationError(
|
|
||||||
$this->name,
|
|
||||||
_t(
|
|
||||||
'ListboxField.SOURCE_VALIDATION',
|
|
||||||
"Please select a value within the list provided. %s is not a valid option",
|
|
||||||
array('value' => $this->value)
|
|
||||||
),
|
|
||||||
"validation"
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
* @package forms
|
* @package forms
|
||||||
* @subpackage fields-basic
|
* @subpackage fields-basic
|
||||||
*/
|
*/
|
||||||
class LookupField extends DropdownField {
|
class LookupField extends MultiSelectField {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var boolean $readonly
|
* @var boolean $readonly
|
||||||
@ -24,36 +24,20 @@ class LookupField extends DropdownField {
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function Field($properties = array()) {
|
public function Field($properties = array()) {
|
||||||
$source = $this->getSource();
|
$source = ArrayLib::flatten($this->getSource());
|
||||||
|
$values = $this->getValueArray();
|
||||||
// Normalize value to array to simplify further processing
|
|
||||||
if(is_array($this->value) || is_object($this->value)) {
|
|
||||||
$values = $this->value;
|
|
||||||
} else {
|
|
||||||
$values = array(trim($this->value));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Get selected values
|
||||||
$mapped = array();
|
$mapped = array();
|
||||||
|
foreach($values as $value) {
|
||||||
if($source instanceof SQLMap) {
|
if(isset($source[$value])) {
|
||||||
foreach($values as $value) {
|
$mapped[] = $source[$value];
|
||||||
$mapped[] = $source->getItem($value);
|
|
||||||
}
|
}
|
||||||
} else if($source instanceof ArrayAccess || is_array($source)) {
|
|
||||||
$source = ArrayLib::flatten($source);
|
|
||||||
|
|
||||||
foreach($values as $value) {
|
|
||||||
if(isset($source[$value])) {
|
|
||||||
$mapped[] = $source[$value];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$mapped = array();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't check if string arguments are matching against the source,
|
// Don't check if string arguments are matching against the source,
|
||||||
// as they might be generated HTML diff views instead of the actual values
|
// as they might be generated HTML diff views instead of the actual values
|
||||||
if($this->value && !is_array($this->value) && !$mapped) {
|
if($this->value && is_string($this->value) && empty($mapped)) {
|
||||||
$mapped = array(trim($this->value));
|
$mapped = array(trim($this->value));
|
||||||
$values = array();
|
$values = array();
|
||||||
}
|
}
|
||||||
@ -102,10 +86,13 @@ class LookupField extends DropdownField {
|
|||||||
*/
|
*/
|
||||||
public function performReadonlyTransformation() {
|
public function performReadonlyTransformation() {
|
||||||
$clone = clone $this;
|
$clone = clone $this;
|
||||||
|
|
||||||
return $clone;
|
return $clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getHasEmptyDefault() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
@ -5,63 +5,87 @@
|
|||||||
*/
|
*/
|
||||||
class MemberDatetimeOptionsetField extends OptionsetField {
|
class MemberDatetimeOptionsetField extends OptionsetField {
|
||||||
|
|
||||||
|
const CUSTOM_OPTION = '__custom__';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Non-ambiguous date to use for the preview.
|
||||||
|
* Must be in 'y-MM-dd HH:mm:ss' format
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private static $preview_date = '25-12-2011 17:30:00';
|
||||||
|
|
||||||
public function Field($properties = array()) {
|
public function Field($properties = array()) {
|
||||||
Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/MemberDatetimeOptionsetField.js');
|
Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/MemberDatetimeOptionsetField.js');
|
||||||
|
$options = array();
|
||||||
|
$odd = false;
|
||||||
|
|
||||||
$options = '';
|
// Add all options striped
|
||||||
$odd = 0;
|
$anySelected = false;
|
||||||
$source = $this->getSource();
|
foreach($this->getSourceEmpty() as $value => $title) {
|
||||||
|
$odd = !$odd;
|
||||||
foreach($source as $key => $value) {
|
if(!$anySelected) {
|
||||||
// convert the ID to an HTML safe value (dots are not replaced, as they are valid in an ID attribute)
|
$anySelected = $this->isSelectedValue($value, $this->Value());
|
||||||
$itemID = $this->id() . '_' . preg_replace('/[^\.a-zA-Z0-9\-\_]/', '_', $key);
|
|
||||||
if($key == $this->value) {
|
|
||||||
$useValue = false;
|
|
||||||
$checked = " checked=\"checked\"";
|
|
||||||
} else {
|
|
||||||
$checked = "";
|
|
||||||
}
|
}
|
||||||
|
$options[] = $this->getFieldOption($value, $title, $odd);
|
||||||
$odd = ($odd + 1) % 2;
|
|
||||||
$extraClass = $odd ? "odd" : "even";
|
|
||||||
$extraClass .= " val" . preg_replace('/[^a-zA-Z0-9\-\_]/', '_', $key);
|
|
||||||
$disabled = ($this->disabled || in_array($key, $this->disabledItems)) ? "disabled=\"disabled\"" : "";
|
|
||||||
$ATT_key = Convert::raw2att($key);
|
|
||||||
|
|
||||||
$options .= "<li class=\"".$extraClass."\">"
|
|
||||||
. "<input id=\"$itemID\" name=\"$this->name\" type=\"radio\" value=\"$key\"$checked $disabled"
|
|
||||||
. " class=\"radio\" /> <label title=\"$ATT_key\" for=\"$itemID\">$value</label></li>\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add "custom" input field
|
// Add "custom" input field option
|
||||||
$value = ($this->value && !array_key_exists($this->value, $this->source)) ? $this->value : null;
|
$options[] = $this->getCustomFieldOption(!$anySelected, !$odd);
|
||||||
$checked = ($value) ? " checked=\"checked\"" : '';
|
|
||||||
$options .= "<li class=\"valCustom\">"
|
|
||||||
. sprintf(
|
|
||||||
"<input id=\"%s_custom\" name=\"%s\" type=\"radio\" value=\"__custom__\" class=\"radio\" %s />",
|
|
||||||
$itemID, $this->name,
|
|
||||||
$checked
|
|
||||||
)
|
|
||||||
. sprintf(
|
|
||||||
'<label for="%s_custom">%s:</label>',
|
|
||||||
$itemID, _t('MemberDatetimeOptionsetField.Custom', 'Custom')
|
|
||||||
)
|
|
||||||
. sprintf(
|
|
||||||
"<input class=\"customFormat cms-help cms-help-tooltip\" name=\"%s_custom\" value=\"%s\" />\n",
|
|
||||||
$this->name, Convert::raw2xml($value)
|
|
||||||
)
|
|
||||||
. sprintf(
|
|
||||||
"<input type=\"hidden\" class=\"formatValidationURL\" value=\"%s\" />",
|
|
||||||
$this->Link() . '/validate'
|
|
||||||
);
|
|
||||||
$options .= ($value) ? sprintf(
|
|
||||||
'<span class="preview">(%s: "%s")</span>',
|
|
||||||
_t('MemberDatetimeOptionsetField.Preview', 'Preview'),
|
|
||||||
Convert::raw2xml(Zend_Date::now()->toString($value))
|
|
||||||
) : '';
|
|
||||||
|
|
||||||
$id = $this->id();
|
// Build fieldset
|
||||||
return "<ul id=\"$id\" class=\"optionset {$this->extraClass()}\">\n$options</ul>\n";
|
$properties = array_merge($properties, array(
|
||||||
|
'Options' => new ArrayList($options)
|
||||||
|
));
|
||||||
|
|
||||||
|
return $this->customise($properties)->renderWith(
|
||||||
|
$this->getTemplates()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the "custom" selection field option
|
||||||
|
*
|
||||||
|
* @param bool $isChecked True if this is checked
|
||||||
|
* @param bool $odd Is odd striped
|
||||||
|
* @return ArrayData
|
||||||
|
*/
|
||||||
|
protected function getCustomFieldOption($isChecked, $odd) {
|
||||||
|
// Add "custom" input field
|
||||||
|
$option = $this->getFieldOption(
|
||||||
|
self::CUSTOM_OPTION,
|
||||||
|
_t('MemberDatetimeOptionsetField.Custom', 'Custom'),
|
||||||
|
$odd
|
||||||
|
);
|
||||||
|
$option->setField('isChecked', $isChecked);
|
||||||
|
$option->setField('CustomName', $this->getName().'[Custom]');
|
||||||
|
$option->setField('CustomValue', $this->Value());
|
||||||
|
if($this->Value()) {
|
||||||
|
$preview = Convert::raw2xml($this->previewFormat($this->Value()));
|
||||||
|
$option->setField('CustomPreview', $preview);
|
||||||
|
$option->setField('CustomPreviewLabel', _t('MemberDatetimeOptionsetField.Preview', 'Preview'));
|
||||||
|
}
|
||||||
|
return $option;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For a given format, generate a preview for the date
|
||||||
|
*
|
||||||
|
* @param string $format Date format
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function previewFormat($format) {
|
||||||
|
$date = $this->config()->preview_date;
|
||||||
|
$zendDate = new Zend_Date($date, 'y-MM-dd HH:mm:ss');
|
||||||
|
return $zendDate->toString($format);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOptionName() {
|
||||||
|
return parent::getOptionName() . '[Options]';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Type() {
|
||||||
|
return 'optionset memberdatetimeoptionset';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,13 +134,20 @@ class MemberDatetimeOptionsetField extends OptionsetField {
|
|||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function setValue($value) {
|
public function setValue($value) {
|
||||||
if($value == '__custom__') {
|
// Extract custom option from postback
|
||||||
$value = isset($_REQUEST[$this->name . '_custom']) ? $_REQUEST[$this->name . '_custom'] : null;
|
if(is_array($value)) {
|
||||||
}
|
if(empty($value['Options'])) {
|
||||||
if($value) {
|
$value = '';
|
||||||
parent::setValue($value);
|
} elseif($value['Options'] === self::CUSTOM_OPTION) {
|
||||||
|
$value = $value['Custom'];
|
||||||
|
} else {
|
||||||
|
$value = $value['Options'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return parent::setValue($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,8 +157,10 @@ class MemberDatetimeOptionsetField extends OptionsetField {
|
|||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function validate($validator) {
|
public function validate($validator) {
|
||||||
$value = isset($_POST[$this->name . '_custom']) ? $_POST[$this->name . '_custom'] : null;
|
$value = $this->Value();
|
||||||
if(!$value) return true; // no custom value, don't validate
|
if(!$value) {
|
||||||
|
return true; // no custom value, don't validate
|
||||||
|
}
|
||||||
|
|
||||||
// Check that the current date with the date format is valid or not
|
// Check that the current date with the date format is valid or not
|
||||||
require_once 'Zend/Date.php';
|
require_once 'Zend/Date.php';
|
||||||
@ -135,12 +168,17 @@ class MemberDatetimeOptionsetField extends OptionsetField {
|
|||||||
$valid = Zend_Date::isDate($date, $value);
|
$valid = Zend_Date::isDate($date, $value);
|
||||||
if($valid) {
|
if($valid) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
if($validator) {
|
|
||||||
$validator->validationError($this->name,
|
|
||||||
_t('MemberDatetimeOptionsetField.DATEFORMATBAD',"Date format is invalid"), "validation", false);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fail
|
||||||
|
$validator->validationError(
|
||||||
|
$this->getName(),
|
||||||
|
_t(
|
||||||
|
'MemberDatetimeOptionsetField.DATEFORMATBAD',
|
||||||
|
"Date format is invalid"
|
||||||
|
),
|
||||||
|
"validation"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
219
forms/MultiSelectField.php
Normal file
219
forms/MultiSelectField.php
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\Model\Relation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a SelectField that may potentially have multiple selections, and may have
|
||||||
|
* a {@link ManyManyList} as a data source.
|
||||||
|
*/
|
||||||
|
abstract class MultiSelectField extends SelectField {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of items to mark as checked, and may not be unchecked
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $defaultItems = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the value of this field, normalised as an array.
|
||||||
|
* Scalar values will return a single length array, even if empty
|
||||||
|
*
|
||||||
|
* @return array List of values as an array
|
||||||
|
*/
|
||||||
|
public function getValueArray() {
|
||||||
|
return $this->getListValues($this->Value());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default selections, regardless of the {@link setValue()} settings.
|
||||||
|
* Note: Items marked as disabled through {@link setDisabledItems()} can still be
|
||||||
|
* selected by default through this method.
|
||||||
|
*
|
||||||
|
* @param array $items Collection of array keys, as defined in the $source array
|
||||||
|
* @return $this Self reference
|
||||||
|
*/
|
||||||
|
public function setDefaultItems($items) {
|
||||||
|
$this->defaultItems = $this->getListValues($items);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default selections, regardless of the {@link setValue()} settings.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getDefaultItems() {
|
||||||
|
return $this->defaultItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a value into this MultiSelectField
|
||||||
|
*/
|
||||||
|
public function setValue($val, $obj = null) {
|
||||||
|
// If we're not passed a value directly,
|
||||||
|
// we can look for it in a relation method on the object passed as a second arg
|
||||||
|
if($obj instanceof DataObject) {
|
||||||
|
$this->loadFrom($obj);
|
||||||
|
} else {
|
||||||
|
return parent::setValue($val);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the value from the dataobject into this field
|
||||||
|
*
|
||||||
|
* @param DataObjectInterface $record
|
||||||
|
*/
|
||||||
|
public function loadFrom(DataObjectInterface $record) {
|
||||||
|
$fieldName = $this->getName();
|
||||||
|
if(empty($fieldName) || empty($record)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$relation = $record->hasMethod($fieldName)
|
||||||
|
? $record->$fieldName()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
// Detect DB relation or field
|
||||||
|
if($relation instanceof Relation) {
|
||||||
|
// Load ids from relation
|
||||||
|
$value = array_values($relation->getIDList());
|
||||||
|
parent::setValue($value);
|
||||||
|
} elseif($record->hasField($fieldName)) {
|
||||||
|
$value = $this->stringDecode($record->$fieldName);
|
||||||
|
parent::setValue($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the current value of this MultiSelectField into a DataObject.
|
||||||
|
* If the field it is saving to is a has_many or many_many relationship,
|
||||||
|
* it is saved by setByIDList(), otherwise it creates a comma separated
|
||||||
|
* list for a standard DB text/varchar field.
|
||||||
|
*
|
||||||
|
* @param DataObject $record The record to save into
|
||||||
|
*/
|
||||||
|
public function saveInto(DataObjectInterface $record) {
|
||||||
|
$fieldName = $this->getName();
|
||||||
|
if(empty($fieldName) || empty($record)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$relation = $record->hasMethod($fieldName)
|
||||||
|
? $record->$fieldName()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
// Detect DB relation or field
|
||||||
|
$items = $this->getValueArray();
|
||||||
|
if($relation instanceof Relation) {
|
||||||
|
// Save ids into relation
|
||||||
|
$relation->setByIDList($items);
|
||||||
|
} elseif($record->hasField($fieldName)) {
|
||||||
|
// Save dataValue into field
|
||||||
|
$record->$fieldName = $this->stringEncode($items);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a list of values into a string, or null if empty (to simplify empty checks)
|
||||||
|
*
|
||||||
|
* @param array $value
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function stringEncode($value) {
|
||||||
|
return $value
|
||||||
|
? json_encode(array_values($value))
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a string value into an array of values
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function stringDecode($value) {
|
||||||
|
// Handle empty case
|
||||||
|
if(empty($value)) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If json deserialisation fails, then fallover to legacy format
|
||||||
|
$result = json_decode($value, true);
|
||||||
|
if($result !== false) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException("Invalid string encoded value for multi select field");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate this field
|
||||||
|
*
|
||||||
|
* @param Validator $validator
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function validate($validator) {
|
||||||
|
$values = $this->getValueArray();
|
||||||
|
$validValues = $this->getValidValues();
|
||||||
|
|
||||||
|
// Filter out selected values not in the data source
|
||||||
|
$self = $this;
|
||||||
|
$invalidValues = array_filter(
|
||||||
|
$values,
|
||||||
|
function($userValue) use ($self, $validValues) {
|
||||||
|
foreach($validValues as $formValue) {
|
||||||
|
if($self->isSelectedValue($formValue, $userValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if(empty($invalidValues)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// List invalid items
|
||||||
|
$validator->validationError(
|
||||||
|
$this->getName(),
|
||||||
|
_t(
|
||||||
|
'MultiSelectField.SOURCE_VALIDATION',
|
||||||
|
"Please select values within the list provided. Invalid option(s) {value} given",
|
||||||
|
array('value' => implode(',', $invalidValues))
|
||||||
|
),
|
||||||
|
"validation"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms the source data for this CheckboxSetField
|
||||||
|
* into a comma separated list of values.
|
||||||
|
*
|
||||||
|
* @return ReadonlyField
|
||||||
|
*/
|
||||||
|
public function performReadonlyTransformation() {
|
||||||
|
$source = $this->getSource();
|
||||||
|
|
||||||
|
// Map selected values to titles
|
||||||
|
$data = array();
|
||||||
|
foreach($this->getValueArray() as $value) {
|
||||||
|
if(array_key_exists($value, $source)) {
|
||||||
|
$data[] = $source[$value];
|
||||||
|
} else {
|
||||||
|
$data[] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$values = implode(', ', $data);
|
||||||
|
|
||||||
|
$field = $this->castedCopy('ReadonlyField');
|
||||||
|
$field->setValue($values);
|
||||||
|
|
||||||
|
return $field;
|
||||||
|
}
|
||||||
|
}
|
@ -50,33 +50,69 @@
|
|||||||
* @package forms
|
* @package forms
|
||||||
* @subpackage fields-basic
|
* @subpackage fields-basic
|
||||||
*/
|
*/
|
||||||
class OptionsetField extends DropdownField {
|
class OptionsetField extends SingleSelectField {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* Build a field option for template rendering
|
||||||
|
*
|
||||||
|
* @param mixed $value Value of the option
|
||||||
|
* @param string $title Title of the option
|
||||||
|
* @param boolean $odd True if this should be striped odd. Otherwise it should be striped even
|
||||||
|
* @return ArrayData Field option
|
||||||
*/
|
*/
|
||||||
|
protected function getFieldOption($value, $title, $odd) {
|
||||||
|
return new ArrayData(array(
|
||||||
|
'ID' => $this->getOptionID($value),
|
||||||
|
'Class' => $this->getOptionClass($value, $odd),
|
||||||
|
'Name' => $this->getOptionName(),
|
||||||
|
'Value' => $value,
|
||||||
|
'Title' => $title,
|
||||||
|
'isChecked' => $this->isSelectedValue($value, $this->Value()),
|
||||||
|
'isDisabled' => $this->isDisabledValue($value)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate an ID property for a single option
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getOptionID($value) {
|
||||||
|
return $this->ID() . '_' . Convert::raw2htmlid($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the "name" property for each item in the list
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getOptionName() {
|
||||||
|
return $this->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get extra classes for each item in the list
|
||||||
|
*
|
||||||
|
* @param string $value Value of this item
|
||||||
|
* @param bool $odd If this item is odd numbered in the list
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getOptionClass($value, $odd) {
|
||||||
|
$oddClass = $odd ? 'odd' : 'even';
|
||||||
|
$valueClass = ' val' . Convert::raw2htmlid($value);
|
||||||
|
return $oddClass . $valueClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function Field($properties = array()) {
|
public function Field($properties = array()) {
|
||||||
$source = $this->getSource();
|
|
||||||
$odd = 0;
|
|
||||||
$options = array();
|
$options = array();
|
||||||
|
$odd = false;
|
||||||
|
|
||||||
if($source) {
|
// Add all options striped
|
||||||
foreach($source as $value => $title) {
|
foreach($this->getSourceEmpty() as $value => $title) {
|
||||||
$itemID = $this->ID() . '_' . preg_replace('/[^a-zA-Z0-9]/', '', $value);
|
$odd = !$odd;
|
||||||
$odd = ($odd + 1) % 2;
|
$options[] = $this->getFieldOption($value, $title, $odd);
|
||||||
$extraClass = $odd ? 'odd' : 'even';
|
|
||||||
$extraClass .= ' val' . preg_replace('/[^a-zA-Z0-9\-\_]/', '_', $value);
|
|
||||||
|
|
||||||
$options[] = new ArrayData(array(
|
|
||||||
'ID' => $itemID,
|
|
||||||
'Class' => $extraClass,
|
|
||||||
'Name' => $this->name,
|
|
||||||
'Value' => $value,
|
|
||||||
'Title' => $title,
|
|
||||||
'isChecked' => $value == $this->value,
|
|
||||||
'isDisabled' => $this->disabled || in_array($value, $this->disabledItems),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$properties = array_merge($properties, array(
|
$properties = array_merge($properties, array(
|
||||||
@ -92,17 +128,13 @@ class OptionsetField extends DropdownField {
|
|||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function validate($validator) {
|
public function validate($validator) {
|
||||||
if (!$this->value) {
|
if (!$this->Value()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::validate($validator);
|
return parent::validate($validator);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ExtraOptions() {
|
|
||||||
return new ArrayList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAttributes() {
|
public function getAttributes() {
|
||||||
$attributes = parent::getAttributes();
|
$attributes = parent::getAttributes();
|
||||||
unset($attributes['name']);
|
unset($attributes['name']);
|
||||||
|
225
forms/SelectField.php
Normal file
225
forms/SelectField.php
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a field that allows users to select one or more items from a list
|
||||||
|
*/
|
||||||
|
abstract class SelectField extends FormField {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associative or numeric array of all dropdown items,
|
||||||
|
* with array key as the submitted field value, and the array value as a
|
||||||
|
* natural language description shown in the interface element.
|
||||||
|
*
|
||||||
|
* @var array|ArrayAccess
|
||||||
|
*/
|
||||||
|
protected $source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The values for items that should be disabled (greyed out) in the dropdown.
|
||||||
|
* This is a non-associative array
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $disabledItems = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name The field name
|
||||||
|
* @param string $title The field title
|
||||||
|
* @param array|ArrayAccess $source A map of the dropdown items
|
||||||
|
* @param mixed $value The current value
|
||||||
|
*/
|
||||||
|
public function __construct($name, $title = null, $source = array(), $value = null) {
|
||||||
|
$this->setSource($source);
|
||||||
|
if(!isset($title)) {
|
||||||
|
$title = $name;
|
||||||
|
}
|
||||||
|
parent::__construct($name, $title, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark certain elements as disabled,
|
||||||
|
* regardless of the {@link setDisabled()} settings.
|
||||||
|
*
|
||||||
|
* These should be items that appear in the source list, not in addition to them.
|
||||||
|
*
|
||||||
|
* @param array|SS_List $items Collection of values or items
|
||||||
|
*/
|
||||||
|
public function setDisabledItems($items){
|
||||||
|
$this->disabledItems = $this->getListValues($items);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Non-associative list of disabled item values
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getDisabledItems(){
|
||||||
|
return $this->disabledItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given value is disabled
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function isDisabledValue($value) {
|
||||||
|
if($this->isDisabled()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return in_array($value, $this->getDisabledItems());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAttributes() {
|
||||||
|
return array_merge(
|
||||||
|
parent::getAttributes(),
|
||||||
|
array('type' => null, 'value' => null)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve all values in the source array
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getSourceValues() {
|
||||||
|
return array_keys($this->getSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all valid values for this field.
|
||||||
|
*
|
||||||
|
* Does not include "empty" value if specified
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getValidValues() {
|
||||||
|
$valid = array_diff($this->getSourceValues(), $this->getDisabledItems());
|
||||||
|
// Renumber indexes from 0
|
||||||
|
return array_values($valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the source array not including any empty default values.
|
||||||
|
*
|
||||||
|
* @return array|ArrayAccess
|
||||||
|
*/
|
||||||
|
public function getSource() {
|
||||||
|
return $this->source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the source for this list
|
||||||
|
*
|
||||||
|
* @param mixed $source
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setSource($source) {
|
||||||
|
$this->source = $this->getListMap($source);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a list of values, extract the associative map of id => title
|
||||||
|
*
|
||||||
|
* @param mixed $source
|
||||||
|
* @return array Associative array of ids and titles
|
||||||
|
*/
|
||||||
|
protected function getListMap($source) {
|
||||||
|
// Extract source as an array
|
||||||
|
if($source instanceof SS_List) {
|
||||||
|
$source = $source->map();
|
||||||
|
}
|
||||||
|
if($source instanceof SS_Map) {
|
||||||
|
$source = $source->toArray();
|
||||||
|
}
|
||||||
|
if(!is_array($source) && !($source instanceof ArrayAccess)) {
|
||||||
|
user_error('$source passed in as invalid type', E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a non-array collection, extract the non-associative list of ids
|
||||||
|
* If passing as array, treat the array values (not the keys) as the ids
|
||||||
|
*
|
||||||
|
* @param mixed $values
|
||||||
|
* @return array Non-associative list of values
|
||||||
|
*/
|
||||||
|
protected function getListValues($values) {
|
||||||
|
// Empty values
|
||||||
|
if(empty($values)) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direct array
|
||||||
|
if(is_array($values)) {
|
||||||
|
return array_values($values);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract lists
|
||||||
|
if($values instanceof SS_List) {
|
||||||
|
return $values->column('ID');
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(trim($values));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the current value of this field matches the given option value
|
||||||
|
*
|
||||||
|
* @param mixed $dataValue The value as extracted from the source of this field (or empty value if available)
|
||||||
|
* @param mixed $userValue The value as submitted by the user
|
||||||
|
* @return boolean True if the selected value matches the given option value
|
||||||
|
*/
|
||||||
|
public function isSelectedValue($dataValue, $userValue) {
|
||||||
|
if($dataValue === $userValue) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow null to match empty strings
|
||||||
|
if($dataValue === '' && $userValue === null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For non-falsey values do loose comparison
|
||||||
|
if($dataValue) {
|
||||||
|
return $dataValue == $userValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For empty values, use string comparison to perform visible value match
|
||||||
|
return ((string) $dataValue) === ((string) $userValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function performReadonlyTransformation() {
|
||||||
|
$field = $this->castedCopy('LookupField');
|
||||||
|
$field->setSource($this->getSource());
|
||||||
|
$field->setReadonly(true);
|
||||||
|
|
||||||
|
return $field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function performDisabledTransformation() {
|
||||||
|
$clone = clone $this;
|
||||||
|
$clone->setDisabled(true);
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns another instance of this field, but "cast" to a different class.
|
||||||
|
*
|
||||||
|
* @see FormField::castedCopy()
|
||||||
|
*
|
||||||
|
* @param String $classOrCopy
|
||||||
|
* @return FormField
|
||||||
|
*/
|
||||||
|
public function castedCopy($classOrCopy) {
|
||||||
|
$field = parent::castedCopy($classOrCopy);
|
||||||
|
if($field instanceof SelectField) {
|
||||||
|
$field->setSource($this->getSource());
|
||||||
|
}
|
||||||
|
return $field;
|
||||||
|
}
|
||||||
|
}
|
121
forms/SingleSelectField.php
Normal file
121
forms/SingleSelectField.php
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the base class for a single-select field
|
||||||
|
*/
|
||||||
|
abstract class SingleSelectField extends SelectField {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the first <option> element as empty (not having a value),
|
||||||
|
* with an optional label defined through {@link $emptyString}.
|
||||||
|
* By default, the <select> element will be rendered with the
|
||||||
|
* first option from {@link $source} selected.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $hasEmptyDefault = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The title shown for an empty default selection,
|
||||||
|
* e.g. "Select...".
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $emptyString = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param boolean $bool
|
||||||
|
* @return self Self reference
|
||||||
|
*/
|
||||||
|
public function setHasEmptyDefault($bool) {
|
||||||
|
$this->hasEmptyDefault = $bool;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getHasEmptyDefault() {
|
||||||
|
return $this->hasEmptyDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the default selection label, e.g. "select...".
|
||||||
|
* Defaults to an empty string. Automatically sets
|
||||||
|
* {@link $hasEmptyDefault} to true.
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
*/
|
||||||
|
public function setEmptyString($string) {
|
||||||
|
$this->setHasEmptyDefault(true);
|
||||||
|
$this->emptyString = $string;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getEmptyString() {
|
||||||
|
return $this->emptyString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the source array, including the empty string, if present
|
||||||
|
*
|
||||||
|
* @return array|ArrayAccess
|
||||||
|
*/
|
||||||
|
public function getSourceEmpty() {
|
||||||
|
// Inject default option
|
||||||
|
if($this->getHasEmptyDefault()) {
|
||||||
|
return array('' => $this->getEmptyString()) + $this->getSource();
|
||||||
|
} else {
|
||||||
|
return $this->getSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate this field
|
||||||
|
*
|
||||||
|
* @param Validator $validator
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function validate($validator) {
|
||||||
|
// Check if valid value is given
|
||||||
|
$selected = $this->Value();
|
||||||
|
if(strlen($selected)) {
|
||||||
|
// Use selection rules to check which are valid
|
||||||
|
foreach($this->getValidValues() as $formValue) {
|
||||||
|
if($this->isSelectedValue($formValue, $selected)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($this->getHasEmptyDefault()) {
|
||||||
|
// Check empty value
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$selected = '(none)';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail
|
||||||
|
$validator->validationError(
|
||||||
|
$this->name,
|
||||||
|
_t(
|
||||||
|
'DropdownField.SOURCE_VALIDATION',
|
||||||
|
"Please select a value within the list provided. {value} is not a valid option",
|
||||||
|
array('value' => $selected)
|
||||||
|
),
|
||||||
|
"validation"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function castedCopy($classOrCopy) {
|
||||||
|
$field = parent::castedCopy($classOrCopy);
|
||||||
|
if($field instanceof SingleSelectField && $this->getHasEmptyDefault()) {
|
||||||
|
$field->setEmptyString($this->getEmptyString());
|
||||||
|
}
|
||||||
|
return $field;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
43
model/Relation.php
Normal file
43
model/Relation.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Model;
|
||||||
|
|
||||||
|
use DBField;
|
||||||
|
use SS_Filterable;
|
||||||
|
use SS_Limitable;
|
||||||
|
use SS_List;
|
||||||
|
use SS_Sortable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract representation of a DB relation field, either saved or in memory
|
||||||
|
*
|
||||||
|
* @package framework
|
||||||
|
* @subpackage model
|
||||||
|
*/
|
||||||
|
interface Relation extends SS_List, SS_Filterable, SS_Sortable, SS_Limitable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the ComponentSet to be the given ID list.
|
||||||
|
* Records will be added and deleted as appropriate.
|
||||||
|
*
|
||||||
|
* @param array $idList List of IDs.
|
||||||
|
*/
|
||||||
|
public function setByIDList($idList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array with both the keys and values set to the IDs of the records in this list.
|
||||||
|
*
|
||||||
|
* Does not return the IDs for unsaved DataObjects
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getIDList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the DBField object that represents the given field on the related class.
|
||||||
|
*
|
||||||
|
* @param string $fieldName Name of the field
|
||||||
|
* @return DBField The field as a DBField object
|
||||||
|
*/
|
||||||
|
public function dbObject($fieldName);
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\Model\Relation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A DataList that represents a relation.
|
* A DataList that represents a relation.
|
||||||
*
|
*
|
||||||
@ -8,7 +10,7 @@
|
|||||||
* @package framework
|
* @package framework
|
||||||
* @subpackage model
|
* @subpackage model
|
||||||
*/
|
*/
|
||||||
abstract class RelationList extends DataList {
|
abstract class RelationList extends DataList implements Relation {
|
||||||
|
|
||||||
public function getForeignID() {
|
public function getForeignID() {
|
||||||
return $this->dataQuery->getQueryParam('Foreign.ID');
|
return $this->dataQuery->getQueryParam('Foreign.ID');
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\Model\Relation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link ArrayList} that represents an unsaved relation.
|
* An {@link ArrayList} that represents an unsaved relation.
|
||||||
*
|
*
|
||||||
@ -16,7 +18,7 @@
|
|||||||
* @package framework
|
* @package framework
|
||||||
* @subpackage model
|
* @subpackage model
|
||||||
*/
|
*/
|
||||||
class UnsavedRelationList extends ArrayList {
|
class UnsavedRelationList extends ArrayList implements Relation {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The DataObject class name that this relation is on
|
* The DataObject class name that this relation is on
|
||||||
@ -151,21 +153,6 @@ class UnsavedRelationList extends ArrayList {
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the given column can be used to filter the records.
|
|
||||||
*/
|
|
||||||
public function canFilterBy($by) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the given column can be used to sort the records.
|
|
||||||
*/
|
|
||||||
public function canSortBy($by) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all items from this relation.
|
* Remove all items from this relation.
|
||||||
*/
|
*/
|
||||||
@ -177,7 +164,7 @@ class UnsavedRelationList extends ArrayList {
|
|||||||
/**
|
/**
|
||||||
* Remove the items from this list with the given IDs
|
* Remove the items from this list with the given IDs
|
||||||
*
|
*
|
||||||
* @param array $idList
|
* @param array $items
|
||||||
*/
|
*/
|
||||||
public function removeMany($items) {
|
public function removeMany($items) {
|
||||||
$this->items = array_diff($this->items, $items);
|
$this->items = array_diff($this->items, $items);
|
||||||
@ -283,156 +270,4 @@ class UnsavedRelationList extends ArrayList {
|
|||||||
public function dbObject($fieldName) {
|
public function dbObject($fieldName) {
|
||||||
return singleton($this->dataClass)->dbObject($fieldName);
|
return singleton($this->dataClass)->dbObject($fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**#@+
|
|
||||||
* Prevents calling DataList methods that rely on the objects being saved
|
|
||||||
*/
|
|
||||||
public function addFilter() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function alterDataQuery() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function avg() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function byIDs() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function byID($id) {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function dataQuery() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function exclude() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function filter() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRange($offset, $length) {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function innerJoin() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function insertFirst() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function join() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function leftJoin() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function limit($length, $offset = 0) {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function map($keyField = 'ID', $titleField = 'Title') {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function max() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function merge($with) {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function min() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function newObject() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function offsetExists($offset) {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function offsetGet($offset) {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function offsetSet($offset, $value) {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function offsetUnset($offset) {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function pop() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function relation() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removeByFilter() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removeByID() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reverse() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setDataModel() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setDataQuery() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setQueriedColumns() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function shift() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function sql() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function subtract() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function sum() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function unshift($item) {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function where() {
|
|
||||||
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
|
|
||||||
}
|
|
||||||
/**#@-*/
|
|
||||||
}
|
}
|
||||||
|
@ -202,7 +202,6 @@ class Group extends DataObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$rolesField = ListboxField::create('Roles', false, $allRoles->map()->toArray())
|
$rolesField = ListboxField::create('Roles', false, $allRoles->map()->toArray())
|
||||||
->setMultiple(true)
|
|
||||||
->setDefaultItems($groupRoleIDs)
|
->setDefaultItems($groupRoleIDs)
|
||||||
->setAttribute('data-placeholder', _t('Group.AddRole', 'Add a role for this group'))
|
->setAttribute('data-placeholder', _t('Group.AddRole', 'Add a role for this group'))
|
||||||
->setDisabledItems($inheritedRoleIDs);
|
->setDisabledItems($inheritedRoleIDs);
|
||||||
|
@ -1342,7 +1342,6 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
|||||||
asort($groupsMap);
|
asort($groupsMap);
|
||||||
$fields->addFieldToTab('Root.Main',
|
$fields->addFieldToTab('Root.Main',
|
||||||
ListboxField::create('DirectGroups', singleton('Group')->i18n_plural_name())
|
ListboxField::create('DirectGroups', singleton('Group')->i18n_plural_name())
|
||||||
->setMultiple(true)
|
|
||||||
->setSource($groupsMap)
|
->setSource($groupsMap)
|
||||||
->setAttribute(
|
->setAttribute(
|
||||||
'data-placeholder',
|
'data-placeholder',
|
||||||
|
12
templates/Includes/GroupedDropdownFieldOption.ss
Normal file
12
templates/Includes/GroupedDropdownFieldOption.ss
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<% if $Options %>
|
||||||
|
<optgroup label="$Title.ATT">
|
||||||
|
<% loop $Options %>
|
||||||
|
<% include GroupedDropdownFieldOption %>
|
||||||
|
<% end_loop %>
|
||||||
|
</optgroup>
|
||||||
|
<% else %>
|
||||||
|
<option value="$Value.ATT"
|
||||||
|
<% if $Selected %> selected="selected"<% end_if %>
|
||||||
|
<% if $Disabled %> disabled="disabled"<% end_if %>
|
||||||
|
><% if $Title %>$Title.XML<% else %> <% end_if %></option>
|
||||||
|
<% end_if %>
|
@ -1,5 +1,9 @@
|
|||||||
<select $AttributesHTML>
|
<select $AttributesHTML>
|
||||||
<% loop $Options %>
|
<% loop $Options %>
|
||||||
<option value="$Value.XML"<% if $Selected %> selected="selected"<% end_if %><% if $Disabled %> disabled="disabled"<% end_if %>><% if Title %>$Title.XML<% else %> <% end_if %></option>
|
<option value="$Value.XML"
|
||||||
|
<% if $Selected %> selected="selected"<% end_if %>
|
||||||
|
<% if $Disabled %> disabled="disabled"<% end_if %>
|
||||||
|
><% if $Title %>$Title.XML<% else %> <% end_if %>
|
||||||
|
</option>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</select>
|
</select>
|
||||||
|
5
templates/forms/GroupedDropdownField.ss
Normal file
5
templates/forms/GroupedDropdownField.ss
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<select $AttributesHTML>
|
||||||
|
<% loop $Options %>
|
||||||
|
<% include GroupedDropdownFieldOption %>
|
||||||
|
<% end_loop %>
|
||||||
|
</select>
|
5
templates/forms/ListboxField.ss
Normal file
5
templates/forms/ListboxField.ss
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<select $AttributesHTML>
|
||||||
|
<% loop $Options %>
|
||||||
|
<option value="$Value.XML"<% if $Selected %> selected="selected"<% end_if %><% if $Disabled %> disabled="disabled"<% end_if %>>$Title.XML</option>
|
||||||
|
<% end_loop %>
|
||||||
|
</select>
|
14
templates/forms/MemberDatetimeOptionsetField.ss
Normal file
14
templates/forms/MemberDatetimeOptionsetField.ss
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<ul $AttributesHTML>
|
||||||
|
<% loop $Options %>
|
||||||
|
<li class="$Class">
|
||||||
|
<input id="$ID" class="radio" name="$Name" type="radio" value="$Value.ATT" <% if $isChecked %> checked<% end_if %><% if $isDisabled %> disabled<% end_if %> />
|
||||||
|
<label for="$ID">$Title.XML</label>
|
||||||
|
<% if $CustomName %>
|
||||||
|
<input class="customFormat cms-help cms-help-tooltip" name="$CustomName" value="$CustomValue.ATT">
|
||||||
|
<% if $CustomPreview %>
|
||||||
|
<span class="preview">({$CustomPreviewLabel.XML}: "{$CustomPreview.XML}")</span>
|
||||||
|
<% end_if %>
|
||||||
|
<% end_if %>
|
||||||
|
</li>
|
||||||
|
<% end_loop %>
|
||||||
|
</ul>
|
@ -49,6 +49,39 @@ class CheckboxSetFieldTest extends SapphireTest {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test different data sources
|
||||||
|
*/
|
||||||
|
public function testSources() {
|
||||||
|
// Array
|
||||||
|
$items = array('a' => 'Apple', 'b' => 'Banana', 'c' => 'Cranberry');
|
||||||
|
$field = new CheckboxSetField('Field', null, $items);
|
||||||
|
$this->assertEquals($items, $field->getSource());
|
||||||
|
|
||||||
|
// SS_List
|
||||||
|
$list = new ArrayList(array(
|
||||||
|
new ArrayData(array(
|
||||||
|
'ID' => 'a',
|
||||||
|
'Title' => 'Apple'
|
||||||
|
)),
|
||||||
|
new ArrayData(array(
|
||||||
|
'ID' => 'b',
|
||||||
|
'Title' => 'Banana'
|
||||||
|
)),
|
||||||
|
new ArrayData(array(
|
||||||
|
'ID' => 'c',
|
||||||
|
'Title' => 'Cranberry'
|
||||||
|
))
|
||||||
|
));
|
||||||
|
$field2 = new CheckboxSetField('Field', null, $list);
|
||||||
|
$this->assertEquals($items, $field2->getSource());
|
||||||
|
|
||||||
|
$field3 = new CheckboxSetField('Field', null, $list->map());
|
||||||
|
$this->assertEquals($items, $field3->getSource());
|
||||||
|
}
|
||||||
|
|
||||||
public function testSaveWithNothingSelected() {
|
public function testSaveWithNothingSelected() {
|
||||||
$article = $this->objFromFixture('CheckboxSetFieldTest_Article', 'articlewithouttags');
|
$article = $this->objFromFixture('CheckboxSetFieldTest_Article', 'articlewithouttags');
|
||||||
|
|
||||||
@ -73,11 +106,11 @@ class CheckboxSetFieldTest extends SapphireTest {
|
|||||||
$tag1 = $this->objFromFixture('CheckboxSetFieldTest_Tag', 'tag1');
|
$tag1 = $this->objFromFixture('CheckboxSetFieldTest_Tag', 'tag1');
|
||||||
$tag2 = $this->objFromFixture('CheckboxSetFieldTest_Tag', 'tag2');
|
$tag2 = $this->objFromFixture('CheckboxSetFieldTest_Tag', 'tag2');
|
||||||
|
|
||||||
/* Create a CheckboxSetField with 2 items selected. Note that the array is in the format (key) => (selected) */
|
/* Create a CheckboxSetField with 2 items selected. Note that the array is a list of values */
|
||||||
$field = new CheckboxSetField("Tags", "Test field", DataObject::get("CheckboxSetFieldTest_Tag")->map());
|
$field = new CheckboxSetField("Tags", "Test field", DataObject::get("CheckboxSetFieldTest_Tag")->map());
|
||||||
$field->setValue(array(
|
$field->setValue(array(
|
||||||
$tag1->ID => true,
|
$tag1->ID,
|
||||||
$tag2->ID => true
|
$tag2->ID
|
||||||
));
|
));
|
||||||
|
|
||||||
/* Saving should work */
|
/* Saving should work */
|
||||||
@ -115,12 +148,14 @@ class CheckboxSetFieldTest extends SapphireTest {
|
|||||||
new FieldList()
|
new FieldList()
|
||||||
);
|
);
|
||||||
$form->loadDataFrom($articleWithTags);
|
$form->loadDataFrom($articleWithTags);
|
||||||
|
$value = $field->Value();
|
||||||
|
sort($value);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
array(
|
array(
|
||||||
$tag1->ID => $tag1->ID,
|
$tag1->ID,
|
||||||
$tag2->ID => $tag2->ID
|
$tag2->ID
|
||||||
),
|
),
|
||||||
$field->Value(),
|
$value,
|
||||||
'CheckboxSetField loads data from a manymany relationship in an object through Form->loadDataFrom()'
|
'CheckboxSetField loads data from a manymany relationship in an object through Form->loadDataFrom()'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -141,33 +176,36 @@ class CheckboxSetFieldTest extends SapphireTest {
|
|||||||
$article->ID
|
$article->ID
|
||||||
))->value();
|
))->value();
|
||||||
|
|
||||||
$this->assertEquals('Test,Another', $dbValue);
|
// JSON encoded values
|
||||||
|
$this->assertEquals('["Test","Another"]', $dbValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testValidationWithArray() {
|
public function testValidationWithArray() {
|
||||||
//test with array input
|
// Test with array input
|
||||||
$field = CheckboxSetField::create('Test', 'Testing', array(
|
$field = CheckboxSetField::create('Test', 'Testing', array(
|
||||||
"One" => "One",
|
"One" => "One",
|
||||||
"Two" => "Two",
|
"Two" => "Two",
|
||||||
"Three" => "Three"
|
"Three" => "Three"
|
||||||
));
|
));
|
||||||
$validator = new RequiredFields();
|
$validator = new RequiredFields();
|
||||||
$field->setValue(array("One" => "One", "Two" => "Two"));
|
$field->setValue(array("One", "Two"));
|
||||||
$this->assertTrue(
|
$this->assertTrue(
|
||||||
$field->validate($validator),
|
$field->validate($validator),
|
||||||
'Field validates values within source array'
|
'Field validates values within source array'
|
||||||
);
|
);
|
||||||
//non valid value should fail
|
|
||||||
|
// Non valid value should fail
|
||||||
$field->setValue(array("Four" => "Four"));
|
$field->setValue(array("Four" => "Four"));
|
||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
$field->validate($validator),
|
$field->validate($validator),
|
||||||
'Field does not validate values outside of source array'
|
'Field does not validate values outside of source array'
|
||||||
);
|
);
|
||||||
//non valid value included with valid options should succeed
|
|
||||||
$field->setValue(array("One" => "One", "Two" => "Two", "Four" => "Four"));
|
// Non valid value, even if included with valid options, should fail
|
||||||
$this->assertTrue(
|
$field->setValue(array("One", "Two", "Four"));
|
||||||
|
$this->assertFalse(
|
||||||
$field->validate($validator),
|
$field->validate($validator),
|
||||||
'Field validates when presented with mixed valid and invalid values'
|
'Field does not validate when presented with mixed valid and invalid values'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,9 +238,9 @@ class CheckboxSetFieldTest extends SapphireTest {
|
|||||||
$tag2->ID => $tag2->ID,
|
$tag2->ID => $tag2->ID,
|
||||||
$tag3->ID => $tag3->ID
|
$tag3->ID => $tag3->ID
|
||||||
));
|
));
|
||||||
$this->assertTrue(
|
$this->assertFalse(
|
||||||
$field->validate($validator),
|
$field->validate($validator),
|
||||||
'Validates when presented with mixed valid and invalid values'
|
'Field does not validate when presented with mixed valid and invalid values'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,37 @@ class DropdownFieldTest extends SapphireTest {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test different data sources
|
||||||
|
*/
|
||||||
|
public function testSources() {
|
||||||
|
// Array
|
||||||
|
$items = array('a' => 'Apple', 'b' => 'Banana', 'c' => 'Cranberry');
|
||||||
|
$field = new DropdownField('Field', null, $items);
|
||||||
|
$this->assertEquals($items, $field->getSource());
|
||||||
|
|
||||||
|
// SS_List
|
||||||
|
$list = new ArrayList(array(
|
||||||
|
new ArrayData(array(
|
||||||
|
'ID' => 'a',
|
||||||
|
'Title' => 'Apple'
|
||||||
|
)),
|
||||||
|
new ArrayData(array(
|
||||||
|
'ID' => 'b',
|
||||||
|
'Title' => 'Banana'
|
||||||
|
)),
|
||||||
|
new ArrayData(array(
|
||||||
|
'ID' => 'c',
|
||||||
|
'Title' => 'Cranberry'
|
||||||
|
))
|
||||||
|
));
|
||||||
|
$field2 = new DropdownField('Field', null, $list);
|
||||||
|
$this->assertEquals($items, $field2->getSource());
|
||||||
|
|
||||||
|
$field3 = new DropdownField('Field', null, $list->map());
|
||||||
|
$this->assertEquals($items, $field3->getSource());
|
||||||
|
}
|
||||||
|
|
||||||
public function testReadonlyField() {
|
public function testReadonlyField() {
|
||||||
$field = new DropdownField('FeelingOk', 'Are you feeling ok?', array(0 => 'No', 1 => 'Yes'));
|
$field = new DropdownField('FeelingOk', 'Are you feeling ok?', array(0 => 'No', 1 => 'Yes'));
|
||||||
$field->setEmptyString('(Select one)');
|
$field->setEmptyString('(Select one)');
|
||||||
|
@ -18,6 +18,8 @@ class GroupedDropdownFieldTest extends SapphireTest {
|
|||||||
)
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
$this->assertEquals(array("1", "2", "3", "4"), $field->getValidValues());
|
||||||
|
|
||||||
$validator = new RequiredFields();
|
$validator = new RequiredFields();
|
||||||
|
|
||||||
$field->setValue("1");
|
$field->setValue("1");
|
||||||
@ -43,11 +45,10 @@ class GroupedDropdownFieldTest extends SapphireTest {
|
|||||||
//disabled items shouldn't validate
|
//disabled items shouldn't validate
|
||||||
$field->setDisabledItems(array('1'));
|
$field->setDisabledItems(array('1'));
|
||||||
$field->setValue('1');
|
$field->setValue('1');
|
||||||
$this->assertFalse($field->validate($validator));
|
|
||||||
|
|
||||||
//grouped disabled items shouldn't validate
|
$this->assertEquals(array("2", "3", "4"), $field->getValidValues());
|
||||||
$field->setDisabledItems(array("Group One" => array("2")));
|
$this->assertEquals(array("1"), $field->getDisabledItems());
|
||||||
$field->setValue('2');
|
|
||||||
$this->assertFalse($field->validate($validator));
|
$this->assertFalse($field->validate($validator));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ class ListboxFieldTest extends SapphireTest {
|
|||||||
$tag2 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag2');
|
$tag2 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag2');
|
||||||
$tag3 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag3');
|
$tag3 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag3');
|
||||||
$field = new ListboxField("Tags", "Test field", DataObject::get("ListboxFieldTest_Tag")->map()->toArray());
|
$field = new ListboxField("Tags", "Test field", DataObject::get("ListboxFieldTest_Tag")->map()->toArray());
|
||||||
$field->setMultiple(true);
|
|
||||||
$field->setValue(null, $articleWithTags);
|
$field->setValue(null, $articleWithTags);
|
||||||
|
|
||||||
$p = new CSSContentParser($field->Field());
|
$p = new CSSContentParser($field->Field());
|
||||||
@ -35,7 +34,6 @@ class ListboxFieldTest extends SapphireTest {
|
|||||||
$tag2 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag2');
|
$tag2 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag2');
|
||||||
$tag3 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag3');
|
$tag3 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag3');
|
||||||
$field = new ListboxField("Tags", "Test field", DataObject::get("ListboxFieldTest_Tag")->map()->toArray());
|
$field = new ListboxField("Tags", "Test field", DataObject::get("ListboxFieldTest_Tag")->map()->toArray());
|
||||||
$field->setMultiple(true);
|
|
||||||
$field->setValue(null, $articleWithTags);
|
$field->setValue(null, $articleWithTags);
|
||||||
$field->setDisabledItems(array($tag1->ID, $tag3->ID));
|
$field->setDisabledItems(array($tag1->ID, $tag3->ID));
|
||||||
|
|
||||||
@ -54,7 +52,6 @@ class ListboxFieldTest extends SapphireTest {
|
|||||||
public function testSaveIntoNullValueWithMultipleOff() {
|
public function testSaveIntoNullValueWithMultipleOff() {
|
||||||
$choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value');
|
$choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value');
|
||||||
$field = new ListboxField('Choices', 'Choices', $choices);
|
$field = new ListboxField('Choices', 'Choices', $choices);
|
||||||
$field->multiple = true;
|
|
||||||
|
|
||||||
$obj = new ListboxFieldTest_DataObject();
|
$obj = new ListboxFieldTest_DataObject();
|
||||||
$field->setValue('a');
|
$field->setValue('a');
|
||||||
@ -67,10 +64,9 @@ class ListboxFieldTest extends SapphireTest {
|
|||||||
public function testSaveIntoNullValueWithMultipleOn() {
|
public function testSaveIntoNullValueWithMultipleOn() {
|
||||||
$choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value');
|
$choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value');
|
||||||
$field = new ListboxField('Choices', 'Choices', $choices);
|
$field = new ListboxField('Choices', 'Choices', $choices);
|
||||||
$field->multiple = true;
|
|
||||||
|
|
||||||
$obj = new ListboxFieldTest_DataObject();
|
$obj = new ListboxFieldTest_DataObject();
|
||||||
$field->setValue('a,c');
|
$field->setValue(array('a', 'c'));
|
||||||
$field->saveInto($obj);
|
$field->saveInto($obj);
|
||||||
$field->setValue('');
|
$field->setValue('');
|
||||||
$field->saveInto($obj);
|
$field->saveInto($obj);
|
||||||
@ -80,30 +76,30 @@ class ListboxFieldTest extends SapphireTest {
|
|||||||
public function testSaveInto() {
|
public function testSaveInto() {
|
||||||
$choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value');
|
$choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value');
|
||||||
$field = new ListboxField('Choices', 'Choices', $choices);
|
$field = new ListboxField('Choices', 'Choices', $choices);
|
||||||
$field->multiple = false;
|
|
||||||
|
|
||||||
$obj = new ListboxFieldTest_DataObject();
|
$obj = new ListboxFieldTest_DataObject();
|
||||||
$field->setValue('a');
|
$field->setValue('a');
|
||||||
$field->saveInto($obj);
|
$field->saveInto($obj);
|
||||||
$this->assertEquals('a', $obj->Choices);
|
$this->assertEquals('["a"]', $obj->Choices);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSaveIntoMultiple() {
|
public function testSaveIntoMultiple() {
|
||||||
$choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value');
|
$choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value');
|
||||||
$field = new ListboxField('Choices', 'Choices', $choices);
|
$field = new ListboxField('Choices', 'Choices', $choices);
|
||||||
$field->multiple = true;
|
|
||||||
|
|
||||||
// As array
|
// As array
|
||||||
$obj1 = new ListboxFieldTest_DataObject();
|
$obj1 = new ListboxFieldTest_DataObject();
|
||||||
$field->setValue(array('a', 'c'));
|
$field->setValue(array('a', 'c'));
|
||||||
$field->saveInto($obj1);
|
$field->saveInto($obj1);
|
||||||
$this->assertEquals('a,c', $obj1->Choices);
|
$this->assertEquals('["a","c"]', $obj1->Choices);
|
||||||
|
|
||||||
// As string
|
// As string
|
||||||
$obj2 = new ListboxFieldTest_DataObject();
|
$obj2 = new ListboxFieldTest_DataObject();
|
||||||
$field->setValue('a,c');
|
$obj2->Choices = '["a","c"]';
|
||||||
|
$field->setValue(null, $obj2);
|
||||||
|
$this->assertEquals(array('a', 'c'), $field->Value());
|
||||||
$field->saveInto($obj2);
|
$field->saveInto($obj2);
|
||||||
$this->assertEquals('a,c', $obj2->Choices);
|
$this->assertEquals('["a","c"]', $obj2->Choices);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSaveIntoManyManyRelation() {
|
public function testSaveIntoManyManyRelation() {
|
||||||
@ -112,7 +108,6 @@ class ListboxFieldTest extends SapphireTest {
|
|||||||
$tag1 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag1');
|
$tag1 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag1');
|
||||||
$tag2 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag2');
|
$tag2 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag2');
|
||||||
$field = new ListboxField("Tags", "Test field", DataObject::get("ListboxFieldTest_Tag")->map()->toArray());
|
$field = new ListboxField("Tags", "Test field", DataObject::get("ListboxFieldTest_Tag")->map()->toArray());
|
||||||
$field->setMultiple(true);
|
|
||||||
|
|
||||||
// Save new relations
|
// Save new relations
|
||||||
$field->setValue(array($tag1->ID,$tag2->ID));
|
$field->setValue(array($tag1->ID,$tag2->ID));
|
||||||
@ -133,37 +128,9 @@ class ListboxFieldTest extends SapphireTest {
|
|||||||
$this->assertEquals(array(), $article->Tags()->sort('ID')->column('ID'));
|
$this->assertEquals(array(), $article->Tags()->sort('ID')->column('ID'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @expectedException InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function testSetValueFailsOnArrayIfMultipleIsOff() {
|
|
||||||
$choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value');
|
|
||||||
$field = new ListboxField('Choices', 'Choices', $choices);
|
|
||||||
$field->multiple = false;
|
|
||||||
|
|
||||||
// As array (type error)
|
|
||||||
$failsOnArray = false;
|
|
||||||
$obj = new ListboxFieldTest_DataObject();
|
|
||||||
$field->setValue(array('a', 'c'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @expectedException InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function testSetValueFailsOnStringIfChoiceInvalidAndMultipleIsOff() {
|
|
||||||
$choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value');
|
|
||||||
$field = new ListboxField('Choices', 'Choices', $choices);
|
|
||||||
$field->multiple = false;
|
|
||||||
|
|
||||||
// As string (invalid choice as comma is regarded literal)
|
|
||||||
$obj = new ListboxFieldTest_DataObject();
|
|
||||||
$field->setValue('invalid');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testFieldRenderingMultipleOff() {
|
public function testFieldRenderingMultipleOff() {
|
||||||
$choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value');
|
$choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value');
|
||||||
$field = new ListboxField('Choices', 'Choices', $choices);
|
$field = new ListboxField('Choices', 'Choices', $choices);
|
||||||
$field->multiple = true;
|
|
||||||
$field->setValue('a');
|
$field->setValue('a');
|
||||||
$parser = new CSSContentParser($field->Field());
|
$parser = new CSSContentParser($field->Field());
|
||||||
$optEls = $parser->getBySelector('option');
|
$optEls = $parser->getBySelector('option');
|
||||||
@ -176,8 +143,7 @@ class ListboxFieldTest extends SapphireTest {
|
|||||||
public function testFieldRenderingMultipleOn() {
|
public function testFieldRenderingMultipleOn() {
|
||||||
$choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value');
|
$choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value');
|
||||||
$field = new ListboxField('Choices', 'Choices', $choices);
|
$field = new ListboxField('Choices', 'Choices', $choices);
|
||||||
$field->multiple = true;
|
$field->setValue(array('a', 'c'));
|
||||||
$field->setValue('a,c');
|
|
||||||
$parser = new CSSContentParser($field->Field());
|
$parser = new CSSContentParser($field->Field());
|
||||||
$optEls = $parser->getBySelector('option');
|
$optEls = $parser->getBySelector('option');
|
||||||
$this->assertEquals(3, count($optEls));
|
$this->assertEquals(3, count($optEls));
|
||||||
@ -186,14 +152,6 @@ class ListboxFieldTest extends SapphireTest {
|
|||||||
$this->assertEquals('selected', (string)$optEls[2]['selected']);
|
$this->assertEquals('selected', (string)$optEls[2]['selected']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @expectedException InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function testCommasInSourceKeys() {
|
|
||||||
$choices = array('a' => 'a value', 'b,with,comma' => 'b value,with,comma',);
|
|
||||||
$field = new ListboxField('Choices', 'Choices', $choices);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testValidationWithArray() {
|
public function testValidationWithArray() {
|
||||||
//test with array input
|
//test with array input
|
||||||
$field = ListboxField::create('Test', 'Testing', array(
|
$field = ListboxField::create('Test', 'Testing', array(
|
||||||
@ -208,7 +166,6 @@ class ListboxFieldTest extends SapphireTest {
|
|||||||
$field->validate($validator),
|
$field->validate($validator),
|
||||||
'Validates values in source map'
|
'Validates values in source map'
|
||||||
);
|
);
|
||||||
$field->setMultiple(true);
|
|
||||||
$field->setValue(array(1));
|
$field->setValue(array(1));
|
||||||
$this->assertTrue(
|
$this->assertTrue(
|
||||||
$field->validate($validator),
|
$field->validate($validator),
|
||||||
@ -247,7 +204,6 @@ class ListboxFieldTest extends SapphireTest {
|
|||||||
// $field->validate($validator),
|
// $field->validate($validator),
|
||||||
// 'Field does not validate values outside of source map'
|
// 'Field does not validate values outside of source map'
|
||||||
// );
|
// );
|
||||||
$field->setMultiple(true);
|
|
||||||
$field->setValue(false, new ArrayData(array(
|
$field->setValue(false, new ArrayData(array(
|
||||||
$tag1->ID => $tag1->ID,
|
$tag1->ID => $tag1->ID,
|
||||||
$tag2->ID => $tag2->ID
|
$tag2->ID => $tag2->ID
|
||||||
|
@ -59,7 +59,7 @@ class MemberDatetimeOptionsetFieldTest extends SapphireTest {
|
|||||||
$field->setForm(new Form(new MemberDatetimeOptionsetFieldTest_Controller(), 'Form', new FieldList(),
|
$field->setForm(new Form(new MemberDatetimeOptionsetFieldTest_Controller(), 'Form', new FieldList(),
|
||||||
new FieldList())); // fake form
|
new FieldList())); // fake form
|
||||||
$parser = new CSSContentParser($field->Field());
|
$parser = new CSSContentParser($field->Field());
|
||||||
$xmlArr = $parser->getBySelector('#Form_Form_TimeFormat_h_mm_ss_a');
|
$xmlArr = $parser->getBySelector('#Form_Form_TimeFormat_h:mm:ss_a');
|
||||||
$this->assertEquals('checked', (string) $xmlArr[0]['checked']);
|
$this->assertEquals('checked', (string) $xmlArr[0]['checked']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,8 +81,7 @@ class MemberDatetimeOptionsetFieldTest extends SapphireTest {
|
|||||||
$field->setForm(new Form(new MemberDatetimeOptionsetFieldTest_Controller(), 'Form', new FieldList(),
|
$field->setForm(new Form(new MemberDatetimeOptionsetFieldTest_Controller(), 'Form', new FieldList(),
|
||||||
new FieldList())); // fake form
|
new FieldList())); // fake form
|
||||||
$parser = new CSSContentParser($field->Field());
|
$parser = new CSSContentParser($field->Field());
|
||||||
$xmlInputArr = $parser->getBySelector('.valCustom input');
|
$xmlInputArr = $parser->getBySelector('.valcustom input');
|
||||||
$xmlPreview = $parser->getBySelector('.preview');
|
|
||||||
$this->assertEquals('checked', (string) $xmlInputArr[0]['checked']);
|
$this->assertEquals('checked', (string) $xmlInputArr[0]['checked']);
|
||||||
$this->assertEquals('dd MM yy', (string) $xmlInputArr[1]['value']);
|
$this->assertEquals('dd MM yy', (string) $xmlInputArr[1]['value']);
|
||||||
}
|
}
|
||||||
@ -91,9 +90,15 @@ class MemberDatetimeOptionsetFieldTest extends SapphireTest {
|
|||||||
$field = new MemberDatetimeOptionsetField('DateFormat', 'DateFormat');
|
$field = new MemberDatetimeOptionsetField('DateFormat', 'DateFormat');
|
||||||
$validator = new RequiredFields();
|
$validator = new RequiredFields();
|
||||||
$this->assertTrue($field->validate($validator));
|
$this->assertTrue($field->validate($validator));
|
||||||
$_POST['DateFormat_custom'] = 'dd MM yyyy';
|
$field->setValue(array(
|
||||||
|
'Options' => '__custom__',
|
||||||
|
'Custom' => 'dd MM yyyy'
|
||||||
|
));
|
||||||
$this->assertTrue($field->validate($validator));
|
$this->assertTrue($field->validate($validator));
|
||||||
$_POST['DateFormat_custom'] = 'sdfdsfdfd1244';
|
$field->setValue(array(
|
||||||
|
'Options' => '__custom__',
|
||||||
|
'Custom' => 'sdfdsfdfd1244'
|
||||||
|
));
|
||||||
$this->assertFalse($field->validate($validator));
|
$this->assertFalse($field->validate($validator));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user