From dd0da7a5ce22ac8ebd06b286f291cd4fbb62dcbf Mon Sep 17 00:00:00 2001
From: Peter Rotich <peter@enhancesoft.com>
Date: Wed, 25 Feb 2015 07:41:38 +0000
Subject: [PATCH] forms: Add Textbox Selection Widget

Add a text box widget to selection field. It functions like typeahead and
drop down widgets with the exception that the user doesn't get the hint. This will
be useful when a list needs to be used to validate user's input.
---
 include/class.dynamic_forms.php | 38 ++++++++++++++++++++++++++-------
 include/class.forms.php         | 24 +++++++++++++++++++++
 2 files changed, 54 insertions(+), 8 deletions(-)

diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php
index 8baf336e4..9e6142a51 100644
--- a/include/class.dynamic_forms.php
+++ b/include/class.dynamic_forms.php
@@ -1235,6 +1235,9 @@ class SelectionField extends FormField {
         $widgetClass = false;
         if ($config['widget'] == 'typeahead' && $config['multiselect'] == false)
             $widgetClass = 'TypeaheadSelectionWidget';
+        elseif ($config['widget'] == 'textbox')
+            $widgetClass = 'TextboxSelectionWidget';
+
         return parent::getWidget($widgetClass);
     }
 
@@ -1250,9 +1253,12 @@ class SelectionField extends FormField {
             foreach ($value as $k=>$v) {
                 if (($i=$list->getItem((int) $k)))
                     $selection[$i->getId()] = $i->getValue();
-                elseif (isset($choices[$v]))
-                    $selection[$v] = $choices[$v];
+                elseif (isset($choices[$k]))
+                    $selection[$k] = $choices[$k];
             }
+        } elseif($value) {
+            //Assume invalid textbox input to be validated
+            $selection[] = $value;
         }
 
         return $selection;
@@ -1317,7 +1323,12 @@ class SelectionField extends FormField {
         parent::validateEntry($entry);
         if (!$this->errors()) {
             $config = $this->getConfiguration();
-            if ($config['typeahead']
+            if ($config['widget'] == 'textbox') {
+                if (!$entry
+                        || !($k=key($entry))
+                        || !($i=$this->getList()->getItem((int) $k)))
+                    $this->_errors[] = __('Unknown or invalid input');
+            } elseif ($config['typeahead']
                     && ($entered = $this->getWidget()->getEnteredValue())
                     && !in_array($entered, $entry))
                 $this->_errors[] = __('Select a value from the list');
@@ -1339,7 +1350,8 @@ class SelectionField extends FormField {
                 'required'=>false, 'default' => 'dropdown',
                 'choices'=>array(
                     'dropdown' => __('Drop Down'),
-                    'typeahead' =>__('Typeahead'),
+                    'typeahead' => __('Typeahead'),
+                    'textbox' => __('Text Input'),
                 ),
                 'configuration'=>array(
                     'multiselect' => false,
@@ -1381,21 +1393,27 @@ class SelectionField extends FormField {
 
     function getChoices($verbose=false) {
         if (!$this->_choices || $verbose) {
-            $this->_choices = array();
+            $choices = array();
             foreach ($this->getList()->getItems() as $i)
-                $this->_choices[$i->getId()] = $i->getValue();
+                $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 (!isset($choices[$k])) {
                         if ($verbose) $v .= ' '.__('(retired)');
-                        $this->_choices[$k] = $v;
+                        $choices[$k] = $v;
                     }
                 }
             }
+
+            if ($verbose) // Don't cache
+                return $choices;
+
+            $this->_choices = $choices;
         }
+
         return $this->_choices;
     }
 
@@ -1525,6 +1543,10 @@ class TypeaheadSelectionWidget extends ChoicesWidget {
         <?php
     }
 
+    function parsedValue() {
+        return array($this->getValue() => $this->getEnteredValue());
+    }
+
     function getValue() {
         $data = $this->field->getSource();
         if (isset($data[$this->name]))
diff --git a/include/class.forms.php b/include/class.forms.php
index 3345669da..0f89971ec 100644
--- a/include/class.forms.php
+++ b/include/class.forms.php
@@ -2274,6 +2274,30 @@ class TextboxWidget extends Widget {
     }
 }
 
+
+class TextboxSelectionWidget extends TextboxWidget {
+    //TODO: Support multi-input e.g comma separated inputs
+    function render($options=array()) {
+
+        if ($this->value && is_array($this->value))
+            $this->value = current($this->value);
+
+        parent::render($options);
+    }
+
+    function getValue() {
+
+        $value = parent::getValue();
+        if (($i=$this->field->getList()->getItem((string) $value)))
+            $value = array($i->getId() => $i->getValue());
+        elseif (($choices=$this->field->getChoices())
+                && ($k=array_search($value, $choices)))
+            $value = array($k => $choices[$k]);
+
+        return $value;
+    }
+}
+
 class PasswordWidget extends TextboxWidget {
     static $input_type = 'password';
 
-- 
GitLab