Skip to content
Snippets Groups Projects
class.dynamic_forms.php 45.2 KiB
Newer Older
Jared Hancock's avatar
Jared Hancock committed
            'entry' => array(
                'constraint' => array('entry_id' => 'DynamicFormEntry.id'),
            ),
        ),
    );

    var $field;
    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)) {
            $f = DynamicFormField::lookup($this->get('field_id'));
            $this->field = $f->getImpl($f);
            $this->field->setAnswer($this);
Jared Hancock's avatar
Jared Hancock committed
        }
        return $this->field;
    }

    function getValue() {
        if (!isset($this->_value) && isset($this->value)) {
            //XXX: We're settting the value here to avoid infinite loop
            $this->_value = false;
            $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')
            $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)) {
            $values = array();
            $choices = $this->getChoices();
            foreach (explode(',', $value) as $V) {
                if (isset($choices[$V]))
                    $values[$V] = $choices[$V];
            }
            if ($id && isset($choices[$id]))
                $values[$id] = $choices[$id];

            if ($values)
                return $values;
            // else return $value unchanged
        // 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() {
        $fields = new ListObject(array(
            new TextboxField(array(
                // XXX: i18n: Change to a better word when the UI changes
                'label' => '['.__('Abbrev').']',
                'id' => 'abb',
            ))
        ));
        $form = $this->getList()->getForm();
        if ($form && ($F = $form->getFields()))
            $fields->extend($F);
        return $fields;
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(
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,
                ),
                'hint'=>__('Typeahead will work better for large lists')
Peter Rotich's avatar
Peter Rotich committed
            'multiselect' => new BooleanField(array(
                'id'=>2,
                'label'=>__(/* Type of widget allowing multiple selections */ 'Multiselect'),
                'required'=>false, 'default'=>false,
Peter Rotich's avatar
Peter Rotich committed
                'configuration'=>array(
                    'desc'=>__('Allow multiple selections')),
                'visibility' => new VisibilityConstraint(
                    new Q(array('widget__eq'=>'dropdown')),
                    VisibilityConstraint::HIDDEN
                ),
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),
            )),
            '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';
Peter Rotich's avatar
Peter Rotich committed

        //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)
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 getFilterData() {
        // Start with the filter data for the list item as the [0] index
        $data = array(parent::getFilterData());
        if (($v = $this->getClean())) {
            // Add in the properties for all selected list items in sub
            // labeled by their field id
            foreach ($v as $id=>$L) {
                if (!($li = DynamicListItem::lookup($id)))
                    continue;
                foreach ($li->getFilterData() as $prop=>$value) {
                    if (!isset($data[$prop]))
                        $data[$prop] = $value;
                    else
                        $data[$prop] .= " $value";
                }
            }
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',
Peter Rotich's avatar
Peter Rotich committed
                    $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'])) {
            // Drop the extra part, if any
            $v = $data[$this->name.'_name'];
            $v = substr($v, 0, strrpos($v, ' — '));
            return trim($v);
        }
        return parent::getValue();
    }