Skip to content
Snippets Groups Projects
class.dynamic_forms.php 45.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • Jared Hancock's avatar
    Jared Hancock committed
                'field' => array(
                    'constraint' => array('field_id' => 'DynamicFormField.id'),
                ),
                'entry' => array(
                    'constraint' => array('entry_id' => 'DynamicFormEntry.id'),
                ),
            ),
        );
    
    
    Jared Hancock's avatar
    Jared Hancock committed
        var $form;
        var $entry;
    
        var $deleted = false;
    
    Jared Hancock's avatar
    Jared Hancock committed
        var $_value;
    
        function getEntry() {
            return $this->entry;
        }
    
        function getForm() {
            if (!$this->form)
                $this->form = $this->getEntry()->getForm();
            return $this->form;
        }
    
        function getField() {
    
            if (!isset($this->_field)) {
                $this->_field = $this->field->getImpl($this->field);
                $this->_field->setAnswer($this);
    
            return $this->_field;
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        function getValue() {
    
            if (!$this->_value && isset($this->value))
                $this->_value = $this->getField()->to_php(
                    $this->get('value'), $this->get('value_id'));
            return $this->_value;
    
        function getIdValue() {
            return $this->get('value_id');
        }
    
    
        function isDeleted() {
            return $this->deleted;
        }
    
    
    Jared Hancock's avatar
    Jared Hancock committed
        function toString() {
            return $this->getField()->toString($this->getValue());
        }
    
        function display() {
            return $this->getField()->display($this->getValue());
        }
    
    
        function getSearchable($include_label=false) {
            if ($include_label)
                $label = Format::searchable($this->getField()->getLabel()) . " ";
            return sprintf("%s%s", $label,
                $this->getField()->searchable($this->getValue())
            );
        }
    
    
            $val = $this->getField()->to_php(
                $this->get('value'), $this->get('value_id'));
    
            if (is_array($val))
                return array_keys($val);
    
            elseif (is_object($val) && method_exists($val, 'getId'))
    
                return array($val->getId());
    
            return array($val);
    
        function asVar() {
    
            return (is_object($this->getValue()))
                ? $this->getValue() : $this->toString();
        }
    
        function getVar($tag) {
            if (is_object($this->getValue()) && method_exists($this->getValue(), 'getVar'))
                return $this->getValue()->getVar($tag);
    
        function __toString() {
    
            $v = $this->toString();
            return is_string($v) ? $v : (string) $this->getValue();
    
    Jared Hancock's avatar
    Jared Hancock committed
    }
    
    class SelectionField extends FormField {
    
        static $widget = 'ChoicesWidget';
    
        function getListId() {
            list(,$list_id) = explode('-', $this->get('type'));
    
            return $list_id ?: $this->get('list_id');
    
    Jared Hancock's avatar
    Jared Hancock committed
        function getList() {
    
            if (!$this->_list)
                $this->_list = DynamicList::lookup($this->getListId());
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->_list;
        }
    
    
        function getWidget() {
            $config = $this->getConfiguration();
            $widgetClass = false;
    
            if ($config['widget'] == 'typeahead' && $config['multiselect'] == false)
    
                $widgetClass = 'TypeaheadSelectionWidget';
            return parent::getWidget($widgetClass);
        }
    
    
        function parse($value) {
    
    Peter Rotich's avatar
    Peter Rotich committed
    
            if (!($list=$this->getList()))
                return null;
    
    
            $config = $this->getConfiguration();
    
    Peter Rotich's avatar
    Peter Rotich committed
            $choices = $this->getChoices();
            $selection = array();
    
            if ($value && is_array($value)) {
                foreach ($value as $k=>$v) {
                    if (($i=$list->getItem((int) $k)))
    
    Peter Rotich's avatar
    Peter Rotich committed
                        $selection[$i->getId()] = $i->getValue();
                    elseif (isset($choices[$v]))
                        $selection[$v] = $choices[$v];
                }
            }
    
            return $selection;
        }
    
        function to_database($value) {
    
            if (is_array($value)) {
                reset($value);
            }
    
    Peter Rotich's avatar
    Peter Rotich committed
            if ($value && is_array($value))
                $value = JsonDataEncoder::encode($value);
    
    
        function to_php($value, $id=false) {
    
            if (is_string($value))
                $value = JsonDataParser::parse($value) ?: $value;
    
    
            if (!is_array($value)) {
    
                $config = $this->getConfiguration();
                if (!$config['multiselect']) {
                    // CDATA may be built with comma-list
                    list($value,) = explode(',', $value, 2);
                }
    
                $choices = $this->getChoices();
                if (isset($choices[$value]))
    
                    $value = array($value => $choices[$value]);
                elseif ($id && isset($choices[$id]))
                    $value = array($id => $choices[$id]);
    
            // Don't set the ID here as multiselect prevents using exactly one
            // ID value. Instead, stick with the JSON value only.
    
        function hasSubFields() {
    
            return $this->getList()->getForm();
    
        }
        function getSubFields() {
    
            $form = $this->getList()->getForm();
            if ($form)
                return $form->getFields();
            return array();
    
    Peter Rotich's avatar
    Peter Rotich committed
        function toString($items) {
            return ($items && is_array($items))
    
                ? implode(', ', $items) : (string) $items;
    
    Peter Rotich's avatar
    Peter Rotich committed
        function validateEntry($entry) {
            parent::validateEntry($entry);
            if (!$this->errors()) {
                $config = $this->getConfiguration();
    
                if ($config['typeahead']
                        && ($entered = $this->getWidget()->getEnteredValue())
                        && !in_array($entered, $entry))
    
                    $this->_errors[] = __('Select a value from the list');
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        function getConfigurationOptions() {
            return array(
    
                'multiselect' => new BooleanField(array(
                    'id'=>2,
                    'label'=>__(/* Type of widget allowing multiple selections */ 'Multiselect'),
                    'required'=>false, 'default'=>false,
                    'configuration'=>array(
                        'desc'=>__('Allow multiple selections')),
                )),
    
    Peter Rotich's avatar
    Peter Rotich committed
                'widget' => new ChoiceField(array(
    
                    'id'=>1,
                    'label'=>__('Widget'),
                    'required'=>false, 'default' => 'dropdown',
    
    Peter Rotich's avatar
    Peter Rotich committed
                    'choices'=>array(
    
                        'dropdown' => __('Drop Down'),
                        'typeahead' =>__('Typeahead'),
    
    Peter Rotich's avatar
    Peter Rotich committed
                    ),
                    'configuration'=>array(
                        'multiselect' => false,
                    ),
    
                    'visibility' => new VisibilityConstraint(
    
                        new Q(array('multiselect__eq'=>false)),
    
                        VisibilityConstraint::HIDDEN
                    ),
    
                    'hint'=>__('Typeahead will work better for large lists')
    
    Peter Rotich's avatar
    Peter Rotich committed
                )),
    
                'prompt' => new TextboxField(array(
    
                    'id'=>3,
                    'label'=>__('Prompt'), 'required'=>false, 'default'=>'',
    
                    'hint'=>__('Leading text shown before a value is selected'),
    
                    'configuration'=>array('size'=>40, 'length'=>40,
                        'translatable'=>$this->getTranslateTag('prompt'),
                    ),
    
                'default' => new SelectionField(array(
                    'id'=>4, 'label'=>__('Default'), 'required'=>false, 'default'=>'',
                    'list_id'=>$this->getListId(),
                    'configuration' => array('prompt'=>__('Select a Default')),
                )),
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getConfiguration() {
    
            $config = parent::getConfiguration();
            if ($config['widget'])
    
                $config['typeahead'] = $config['widget'] == 'typeahead';
    
            // Drop down list does not support multiple selections
    
    Peter Rotich's avatar
    Peter Rotich committed
            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)
    
    Peter Rotich's avatar
    Peter Rotich committed
                    $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)');
    
    Peter Rotich's avatar
    Peter Rotich committed
                            $this->_choices[$k] = $v;
                        }
                    }
    
            }
            return $this->_choices;
        }
    
        function getChoice($value) {
            $choices = $this->getChoices();
            if ($value && is_array($value)) {
                $selection = $value;
            } elseif (isset($choices[$value]))
                $selection[] = $choices[$value];
            elseif ($this->get('default'))
                $selection[] = $choices[$this->get('default')];
    
            return $selection;
        }
    
    
        function export($value) {
            if ($value && is_numeric($value)
                    && ($item = DynamicListItem::lookup($value)))
                return $item->toString();
            return $value;
        }
    
    
        function getFilterData() {
            $data = array(parent::getFilterData());
            if (($v = $this->getClean()) instanceof DynamicListItem) {
                $data = array_merge($data, $v->getFilterData());
            }
            return $data;
        }
    
    
        function getSearchMethods() {
            return array(
                'set' =>        __('has a value'),
                'notset' =>     __('does not have a value'),
                'includes' =>   __('includes'),
                '!includes' =>  __('does not include'),
            );
        }
    
        function getSearchMethodWidgets() {
            return array(
                'set' => null,
                'notset' => null,
                'includes' => array('ChoiceField', array(
                    'choices' => $this->getChoices(),
                    'configuration' => array('multiselect' => true),
                )),
                '!includes' => array('ChoiceField', array(
                    'choices' => $this->getChoices(),
                    'configuration' => array('multiselect' => true),
                )),
            );
        }
    
        function getSearchQ($method, $value, $name=false) {
            $name = $name ?: $this->get('name');
            switch ($method) {
            case '!includes':
    
                return Q::not(array("{$name}__intersect" => array_keys($value)));
    
                return new Q(array("{$name}__intersect" => array_keys($value)));
    
            default:
                return parent::getSearchQ($method, $value, $name);
            }
        }
    
    class TypeaheadSelectionWidget extends ChoicesWidget {
        function render($how) {
            if ($how == 'search')
                return parent::render($how);
    
            $name = $this->getEnteredValue();
    
            $config = $this->field->getConfiguration();
    
            if (is_array($this->value)) {
                $name = $name ?: current($this->value);
                $value = key($this->value);
    
            else {
                // Pull configured default (if configured)
                $def_key = $this->field->get('default');
                if (!$def_key && $config['default'])
                    $def_key = $config['default'];
                if (is_array($def_key))
                    $name = current($def_key);
            }
    
    Jared Hancock's avatar
    Jared Hancock committed
            $source = array();
            foreach ($this->field->getList()->getItems() as $i)
                $source[] = array(
    
    Peter Rotich's avatar
    Peter Rotich committed
                    'value' => $i->getValue(), 'id' => $i->getId(),
                    'info' => sprintf('%s %s',
                        $i->getValue(),
                        (($extra= $i->getAbbrev()) ? "-- $extra" : '')),
    
    Jared Hancock's avatar
    Jared Hancock committed
            ?>
            <span style="display:inline-block">
    
            <input type="text" size="30" name="<?php echo $this->name; ?>_name"
                id="<?php echo $this->name; ?>" value="<?php echo Format::htmlchars($name); ?>"
    
                placeholder="<?php echo $config['prompt'];
                ?>" autocomplete="off" />
    
            <input type="hidden" name="<?php echo $this->name;
    
                ?>[<?php echo $value; ?>]" id="<?php echo $this->name;
                ?>_id" value="<?php echo Format::htmlchars($name); ?>"/>
    
    Jared Hancock's avatar
    Jared Hancock committed
            <script type="text/javascript">
            $(function() {
    
                $('input#<?php echo $this->name; ?>').typeahead({
    
    Jared Hancock's avatar
    Jared Hancock committed
                    source: <?php echo JsonDataEncoder::encode($source); ?>,
    
                    property: 'info',
    
    Jared Hancock's avatar
    Jared Hancock committed
                    onselect: function(item) {
    
                        $('input#<?php echo $this->name; ?>_name').val(item['value'])
                        $('input#<?php echo $this->name; ?>_id')
                          .attr('name', '<?php echo $this->name; ?>[' + item['id'] + ']')
                          .val(item['value']);
    
    Jared Hancock's avatar
    Jared Hancock committed
                    }
                });
            });
            </script>
            </span>
            <?php
        }
    
        function getValue() {
            $data = $this->field->getSource();
            if (isset($data[$this->name]))
                return $data[$this->name];
            return parent::getValue();
        }
    
    
        function getEnteredValue() {
            // Used to verify typeahead fields
    
            $data = $this->field->getSource();
            if (isset($data[$this->name.'_name']))
                return trim($data[$this->name.'_name']);
    
            return parent::getValue();
        }