Newer
Older
// Sort the form the way it is declared to be sorted
usort($this->_fields,
function($a, $b) {
return $a->get('sort') - $b->get('sort');
}
}
function save() {
if (count($this->dirty))
$this->set('updated', new SqlFunction('NOW'));
parent::save();
foreach ($this->getFields() as $field) {
// Set the entry ID here so that $field->getClean() can use the
// entry-id if necessary
$a->set('entry_id', $this->get('id'));
$val = $field->to_database($field->getClean());
if (is_array($val)) {
$a->set('value', $val[0]);
$a->set('value_id', $val[1]);
}
else
$a->set('value', $val);
// Don't save answers for presentation-only fields
if ($field->hasData() && !$field->isPresentationOnly())
$a->save();
function delete() {
foreach ($this->getAnswers() as $a)
$a->delete();
return parent::delete();
}
static function create($ht=false) {
$inst = parent::create($ht);
$inst->set('created', new SqlFunction('NOW'));
foreach ($inst->getForm()->getDynamicFields() as $f) {
$a = DynamicFormEntryAnswer::create(
array('field_id'=>$f->get('id')));
$a->field = $f;
$inst->_values[] = $a;
}
return $inst;
}
}
/**
* Represents a single answer to a single field on a dynamic form. The
* data / answer to the field is linked back to the form and field which was
* originally used for the submission.
*/
class DynamicFormEntryAnswer extends VerySimpleModel {
static $meta = array(
'table' => FORM_ANSWER_TABLE,
'ordering' => array('field__sort'),
'pk' => array('entry_id', 'field_id'),
'select_related' => array('field'),
'fields' => array('entry_id', 'field_id', 'value', 'value_id'),
'joins' => array(
'field' => array(
'constraint' => array('field_id' => 'DynamicFormField.id'),
),
'entry' => array(
'constraint' => array('entry_id' => 'DynamicFormEntry.id'),
),
),
);
var $deleted = false;
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;
if (!$this->_value && isset($this->value))
$this->_value = $this->getField()->to_php(
$this->get('value'), $this->get('value_id'));
return $this->_value;
function getLocal($tag) {
return $this->field->getLocal($tag);
}
function getIdValue() {
return $this->get('value_id');
}
function isDeleted() {
return $this->deleted;
}
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())
);
}
function getSearchKeys() {
$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 (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();
}
class SelectionField extends FormField {
static $widget = 'ChoicesWidget';
function getListId() {
list(,$list_id) = explode('-', $this->get('type'));
return $list_id ?: $this->get('list_id');
if (!$this->_list)
$this->_list = DynamicList::lookup($this->getListId());
function getWidget() {
$config = $this->getConfiguration();
$widgetClass = false;
if ($config['widget'] == 'typeahead' && $config['multiselect'] == false)
$widgetClass = 'TypeaheadSelectionWidget';
return parent::getWidget($widgetClass);
}
if (!($list=$this->getList()))
return null;
$config = $this->getConfiguration();
$choices = $this->getChoices();
$selection = array();
if ($value && is_array($value)) {
foreach ($value as $k=>$v) {
if (($i=$list->getItem((int) $k)))
$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);
}
if ($value && is_array($value))
$value = JsonDataEncoder::encode($value);
return $value;
function to_php($value, $id=false) {
if (is_string($value))
$value = JsonDataParser::parse($value) ?: $value;
if (!is_array($value)) {
$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.
return $this->getList()->getForm();
$form = $this->getList()->getForm();
if ($form)
return $form->getFields();
return array();
function toString($items) {
return ($items && is_array($items))
? implode(', ', $items) : (string) $items;
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');
}
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')),
)),
'id'=>1,
'label'=>__('Widget'),
'required'=>false, 'default' => 'dropdown',
'dropdown' => __('Drop Down'),
'typeahead' =>__('Typeahead'),
),
'configuration'=>array(
'multiselect' => false,
),
'visibility' => new VisibilityConstraint(
new Q(array('multiselect__eq'=>false)),
VisibilityConstraint::HIDDEN
),
'hint'=>__('Typeahead will work better for large lists')
'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')),
)),
function getConfiguration() {
$config = parent::getConfiguration();
if ($config['widget'])
$config['typeahead'] = $config['widget'] == 'typeahead';
// Drop down list does not support multiple selections
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->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)');
}
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() {
$data = array(parent::getFilterData());
if (($v = $this->getClean()) instanceof DynamicListItem) {
$data = array_merge($data, $v->getFilterData());
}
return $data;
}
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
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($options=array()) {
if ($options['mode'] == 'search')
return parent::render($options);
$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);
}
$source = array();
foreach ($this->field->getList()->getItems() as $i)
$source[] = array(
'value' => $i->getValue(), 'id' => $i->getId(),
'info' => sprintf('%s %s',
$i->getValue(),
(($extra= $i->getAbbrev()) ? "-- $extra" : '')),
<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); ?>"/>
<script type="text/javascript">
$(function() {
$('input#<?php echo $this->name; ?>').typeahead({
source: <?php echo JsonDataEncoder::encode($source); ?>,
$('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']);
}
});
});
</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();
}