From dd1d66384d941a80058ed96200dc1ed2ba673b11 Mon Sep 17 00:00:00 2001 From: Peter Rotich <peter@osticket.com> Date: Wed, 9 Jul 2014 20:05:38 +0000 Subject: [PATCH] Add multiselect support Add multiselect option to selection and choices fields. --- include/class.dynamic_forms.php | 155 +++++++++++++++++++++----------- include/class.forms.php | 52 ++++++----- include/class.list.php | 6 +- 3 files changed, 135 insertions(+), 78 deletions(-) diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php index 2d685db89..f097a2e4d 100644 --- a/include/class.dynamic_forms.php +++ b/include/class.dynamic_forms.php @@ -920,66 +920,90 @@ class SelectionField extends FormField { function getList() { if (!$this->_list) $this->_list = DynamicList::lookup($this->getListId()); + return $this->_list; } function parse($value) { + + if (!($list=$this->getList())) + return null; + $config = $this->getConfiguration(); - if (is_int($value)) - return $this->to_php($this->getWidget()->getEnteredValue(), (int) $value); - elseif (!$config['typeahead']) - return $this->to_php(null, (int) $value); - else - return $this->to_php($value); + $choices = $this->getChoices(); + $selection = array(); + if ($config['typeahead']) { + // Entered value + $val = $this->getWidget()->getEnteredValue(); + if (($i=$list->getItem($val)) && $i->getId() == $value) + $selection[$i->getId()] = $i->getValue(); + elseif ($val && isset($choices[$value])) //perhaps old deleted item... + $selection[$value] = $choices[$value]; + } elseif ($value && is_array($value)) { + foreach ($value as $v) { + if (($i=$list->getItem((int) $v))) + $selection[$i->getId()] = $i->getValue(); + elseif (isset($choices[$v])) + $selection[$v] = $choices[$v]; + } + } + + return $selection; + } + + function to_database($value) { + if ($value && is_array($value)) + $value = JsonDataEncoder::encode($value); + + return $value; } function to_php($value, $id=false) { - if ($value === null && $id === null) - return null; - if ($id && is_int($id)) - $item = DynamicListItem::lookup($id); - # Attempt item lookup by name too - if (!$item || ($value !== null && $value != $item->get('value'))) { - $item = DynamicListItem::lookup(array( - 'value'=>$value, - 'list_id'=>$this->getListId())); - } - return ($item) ? $item : $value; + return ($value && !is_array($value)) + ? JsonDataParser::parse($value) : $value; } function hasIdValue() { return true; } - function to_database($item) { - if ($item instanceof DynamicListItem) - return array($item->value, $item->id); - return null; - } - - function toString($item) { - return ($item instanceof DynamicListItem) - ? $item->toString() : (string) $item; + function toString($items) { + return ($items && is_array($items)) + ? explode(', ', $items) : (string) $items; } - function validateEntry($item) { - $config = $this->getConfiguration(); - parent::validateEntry($item); - if ($item && !$item instanceof DynamicListItem) - $this->_errors[] = 'Select a value from the list'; - elseif ($item && $config['typeahead'] - && $this->getWidget()->getEnteredValue() != $item->get('value')) - $this->_errors[] = 'Select a value from the list'; + function validateEntry($entry) { + parent::validateEntry($entry); + if (!$this->errors()) { + $config = $this->getConfiguration(); + if (!$entry || count($entry) == 0) + $this->_errors[] = 'Select a value from the list'; + elseif ($config['typeahead'] + && !in_array($this->getWidget()->getEnteredValue(), $entry)) + $this->_errors[] = 'Select a value from the list'; + } } function getConfigurationOptions() { return array( - 'typeahead' => new ChoiceField(array( + 'widget' => new ChoiceField(array( 'id'=>1, 'label'=>'Widget', 'required'=>false, - 'default'=>false, - 'choices'=>array(false=>'Drop Down', true=>'Typeahead'), + 'default' => 'dropdown', + 'choices'=>array( + 'dropdown' => 'Drop Down', + 'typeahead' =>'Typeahead', + ), + 'configuration'=>array( + 'multiselect' => false, + ), 'hint'=>'Typeahead will work better for large lists' )), + 'multiselect' => new BooleanField(array( + 'id'=>1, 'label'=>'Multiselect', 'required'=>false, 'default'=>false, + 'configuration'=>array( + 'desc'=>'Allow multiple selections'), + 'hint' => 'Dropdown only', + )), 'prompt' => new TextboxField(array( 'id'=>2, 'label'=>'Prompt', 'required'=>false, 'default'=>'', 'hint'=>'Leading text shown before a value is selected', @@ -988,14 +1012,34 @@ class SelectionField extends FormField { ); } - function getChoices() { - if (!$this->_choices) { + function getConfiguration() { + + $config = parent::getConfiguration(); + if ($config['widget']) + $config['typeahead'] = isset($config['widget']['typeahead']); + + //Typeahed doesn't support multiselect for now TODO: Add! + if ($config['typeahead']) + $config['multiselect'] = false; + + return $config; + } + + function getChoices($verbose=false) { + if (!$this->_choices || $verbose) { $this->_choices = array(); foreach ($this->getList()->getItems() as $i) - $this->_choices[$i->get('id')] = $i->get('value'); - if ($this->value && !isset($this->_choices[$this->value])) { - $v = DynamicListItem::lookup($this->value); - $this->_choices[$v->get('id')] = $v->get('value').' (Disabled)'; + $this->_choices[$i->getId()] = $i->getValue(); + + // Retired old selections + $values = ($a=$this->getAnswer()) ? $a->getValue() : array(); + if ($values && is_array($values)) { + foreach ($values as $k => $v) { + if (!isset($this->_choices[$k])) { + if ($verbose) $v .= ' (retired)'; + $this->_choices[$k] = $v; + } + } } } return $this->_choices; @@ -1019,27 +1063,30 @@ class SelectionField extends FormField { class SelectionWidget extends ChoicesWidget { function render($mode=false) { + $config = $this->field->getConfiguration(); - $value = false; - if ($this->value instanceof DynamicListItem) { - // Loaded from database - $value = $this->value->get('id'); - $name = $this->value->get('value'); - } elseif ($this->value) { - // Loaded from POST + if (($value=$this->getValue())) + $value = $this->field->parse($value); + elseif ($this->value) $value = $this->value; - $name = $this->getEnteredValue(); - } + if (!$config['typeahead'] || $mode=='search') { $this->value = $value; return parent::render($mode); } + if ($value && is_array($value)) { + $name = current($value); + $value = key($value); + } + $source = array(); foreach ($this->field->getList()->getItems() as $i) $source[] = array( - 'value' => $i->get('value'), 'id' => $i->get('id'), - 'info' => $i->get('value')." -- ".$i->get('extra'), + 'value' => $i->getValue(), 'id' => $i->getId(), + 'info' => sprintf('%s %s', + $i->getValue(), + (($extra= $i->getAbbrev()) ? "-- $extra" : '')), ); ?> <span style="display:inline-block"> diff --git a/include/class.forms.php b/include/class.forms.php index c5cb405a6..246aa0169 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -751,9 +751,9 @@ class ChoiceField extends FormField { if (is_array($value)) { foreach($value as $k => $v) if (isset($choices[$v])) - $values[] = $v; + $values[$v] = $choices[$v]; } elseif(isset($choices[$value])) { - $values[] = $value; + $values[$value] = $choices[$value]; } return $values ?: null; @@ -761,13 +761,14 @@ class ChoiceField extends FormField { function to_database($value) { if ($value && is_array($value)) - $value = implode(',', $value); + $value = JsonDataEncoder::encode($value); return $value; } function to_php($value) { - return $value ? explode(',', $value) : $value; + return ($value && !is_array($value)) + ? JsonDataParser::parse($value) : $value; } function toString($value) { @@ -775,9 +776,7 @@ class ChoiceField extends FormField { $choices = $this->getChoices(); $selection = array(); if ($value && is_array($value)) { - foreach ($value as $v) - if (isset($choices[$v])) - $selection[] = $choices[$v]; + $selection = $value; } elseif (isset($choices[$value])) $selection[] = $choices[$value]; elseif ($this->get('default')) @@ -786,8 +785,8 @@ class ChoiceField extends FormField { return $selection ? implode(', ', array_filter($selection)) : ''; } - function getChoices() { - if ($this->_choices === null) { + function getChoices($verbose=false) { + if ($this->_choices === null || $verbose) { // Allow choices to be set in this->ht (for configurationOptions) $this->_choices = $this->get('choices'); if (!$this->_choices) { @@ -801,6 +800,18 @@ class ChoiceField extends FormField { $val = $key; $this->_choices[trim($key)] = trim($val); } + // Add old selections if nolonger available + // This is necessary so choices made previously can be + // retained + $values = ($a=$this->getAnswer()) ? $a->getValue() : array(); + if ($values && is_array($values)) { + foreach ($values as $k => $v) { + if (!isset($this->_choices[$k])) { + if ($verbose) $v .= ' (retired)'; + $this->_choices[$k] = $v; + } + } + } } } return $this->_choices; @@ -1121,7 +1132,7 @@ class ChoicesWidget extends Widget { $config = $this->field->getConfiguration(); // Determine the value for the default (the one listed if nothing is // selected) - $choices = $this->field->getChoices(); + $choices = $this->field->getChoices(true); $prompt = $config['prompt'] ?: 'Select'; $have_def = false; @@ -1135,16 +1146,15 @@ class ChoicesWidget extends Widget { $have_def = isset($choices[$def_key]); $def_val = $have_def ? $choices[$def_key] : $prompt; } - $value = $this->value; - if ($value === null && $have_def) - $value = $def_key; - if ($value && is_array($value)) - $values = $value; - elseif ($value !== null) // Assume multiselect (comma delimited values) - $values = explode(',', $value); - else - $values = array(); + if (($value=$this->getValue())) + $values = $this->field->parse($value); + elseif ($this->value) + $values = $this->value; + + if ($values === null) + $values = $have_def ? array($def_key => $choices[$def_key]) : array(); + ?> <span style="display:inline-block"> <select name="<?php echo $this->name; ?>[]" @@ -1156,11 +1166,11 @@ class ChoicesWidget extends Widget { <option value="<?php echo $def_key; ?>">— <?php echo $def_val; ?> —</option> <?php } - foreach ($choices as $key=>$name) { + foreach ($choices as $key => $name) { if (!$have_def && $key == $def_key) continue; ?> <option value="<?php echo $key; ?>" <?php - if (in_array($key, $values)) echo 'selected="selected"'; + if (isset($values[$key])) echo 'selected="selected"'; ?>><?php echo $name; ?></option> <?php } ?> </select> diff --git a/include/class.list.php b/include/class.list.php index d6080304a..89564ad62 100644 --- a/include/class.list.php +++ b/include/class.list.php @@ -892,11 +892,10 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { function setConfiguration(&$errors=array()) { $properties = array(); foreach ($this->getConfigurationForm()->getFields() as $f) { - $val = $f->to_database($f->getClean()); + $val = $f->getClean(); $name = mb_strtolower($f->get('name')); switch ($name) { case 'flags': - $val = $f->getClean(); if ($val && is_array($val)) { $flags = 0; foreach ($val as $v) { @@ -911,13 +910,14 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { } break; case 'state': + $val = $f->to_database($val); if ($val && in_array($val, static::$_states)) $this->set('state', $val); else $f->addError('Unknown or invalid state', $name); break; default: //Custom properties the user might add. - $properties[$f->get('id')] = $val; + $properties[$f->get('id')] = $f->to_php($val); } $errors = array_merge($errors, $f->errors()); } -- GitLab