Newer
Older
$widget = parent::getWidget($widgetClass);
if (is_object($widget->value))
$widget->value = $widget->value->getId();
return $widget;
}
function getCriteria() {
if (!isset($this->_criteria)) {
$this->_criteria = array('available' => true);
if (($c=parent::getCriteria()))
$this->_criteria = array_merge($this->_criteria, $c);
}
return $this->_criteria;
}
function hasIdValue() {
return true;
}
function setChoices($choices) {
$this->_choices = $choices;
}
function getChoices($verbose=false) {
if (!isset($this->_choices)) {
$config = $this->getConfiguration();
$choices = array(
__('Agents') => new ArrayObject(),
__('Teams') => new ArrayObject());
$A = current($choices);
$criteria = $this->getCriteria();
$agents = array();
if (($dept=$config['dept']) && $dept->assignMembersOnly()) {
if (($members = $dept->getAvailableMembers()))
foreach ($members as $member)
$agents[$member->getId()] = $member;
} else {
$agents = Staff::getStaffMembers($criteria);
}
$A['s'.$id] = $name;
if (($teams = Team::getActiveTeams()))
foreach ($teams as $id => $name)
$T['t'.$id] = $name;
$this->_choices = $choices;
}
return $this->_choices;
function getValue() {
if (($value = parent::getValue()) && ($id=$this->getClean()))
return $value[$id];
}
function parse($id) {
return $this->to_php(null, $id);
}
function to_php($value, $id=false) {
if (is_array($id)) {
reset($id);
$id = key($id);
switch ($type) {
case 's':
return Staff::lookup($id);
case 't':
return Team::lookup($id);
case 'd':
return Dept::lookup($id);
default:
return $id;
}
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
}
function to_database($value) {
return (is_object($value))
? array($value->getName(), $value->getId())
: $value;
}
function toString($value) {
return (string) $value;
}
function searchable($value) {
return null;
}
function getConfigurationOptions() {
return array(
'prompt' => new TextboxField(array(
'id'=>2, 'label'=>__('Prompt'), 'required'=>false, 'default'=>'',
'hint'=>__('Leading text shown before a value is selected'),
'configuration'=>array('size'=>40, 'length'=>40),
)),
);
}
}
FormField::addFieldTypes(/*@trans*/ 'Dynamic Fields', function() {
return array(
'assignee' => array(__('Assignee'), 'AssigneeField'),
class TicketStateField extends ChoiceField {
'name' => /* @trans, @context "ticket state name" */ 'Open',
'verb' => /* @trans, @context "ticket state action" */ 'Open'
'name' => /* @trans, @context "ticket state name" */ 'Closed',
'verb' => /* @trans, @context "ticket state action" */ 'Close'
// Private states
static $_privatestates = array(
'name' => /* @trans, @context "ticket state name" */ 'Archived',
'verb' => /* @trans, @context "ticket state action" */ 'Archive'
'name' => /* @trans, @context "ticket state name" */ 'Deleted',
'verb' => /* @trans, @context "ticket state action" */ 'Delete'
);
function hasIdValue() {
return true;
}
function isChangeable() {
return false;
}
function getChoices($verbose=false) {
$states = static::$_states;
if ($this->options['private_too'])
$states += static::$_privatestates;
if (!isset($_choices)) {
// Translate and cache the choices
foreach ($states as $k => $v)
$_choices[$k] = _P('ticket state name', $v['name']);
$this->ht['default'] = '';
}
return $_choices;
}
function getChoice($state) {
if ($state && is_array($state))
$state = key($state);
if (isset(static::$_states[$state]))
return _P('ticket state name', static::$_states[$state]['name']);
if (isset(static::$_privatestates[$state]))
return _P('ticket state name', static::$_privatestates[$state]['name']);
}
function getConfigurationOptions() {
return array(
'prompt' => new TextboxField(array(
'id'=>2, 'label'=> __('Prompt'), 'required'=>false, 'default'=>'',
'hint'=> __('Leading text shown before a value is selected'),
'configuration'=>array('size'=>40, 'length'=>40),
)),
);
}
static function getVerb($state) {
if (isset(static::$_states[$state]))
return _P('ticket state action', static::$_states[$state]['verb']);
if (isset(static::$_privatestates[$state]))
return _P('ticket state action', static::$_privatestates[$state]['verb']);
}
FormField::addFieldTypes('Dynamic Fields', function() {
return array(
'state' => array('Ticket State', 'TicketStateField', false),
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
);
});
class TicketFlagField extends ChoiceField {
// Supported flags (TODO: move to configurable custom list)
static $_flags = array(
'onhold' => array(
'flag' => 1,
'name' => 'Onhold',
'states' => array('open'),
),
'overdue' => array(
'flag' => 2,
'name' => 'Overdue',
'states' => array('open'),
),
'answered' => array(
'flag' => 4,
'name' => 'Answered',
'states' => array('open'),
)
);
var $_choices;
function hasIdValue() {
return true;
}
function isChangeable() {
return true;
}
function getChoices($verbose=false) {
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
$this->ht['default'] = '';
if (!$this->_choices) {
foreach (static::$_flags as $k => $v)
$this->_choices[$k] = $v['name'];
}
return $this->_choices;
}
function getConfigurationOptions() {
return array(
'prompt' => new TextboxField(array(
'id'=>2, 'label'=>'Prompt', 'required'=>false, 'default'=>'',
'hint'=>'Leading text shown before a value is selected',
'configuration'=>array('size'=>40, 'length'=>40),
)),
);
}
}
FormField::addFieldTypes('Dynamic Fields', function() {
return array(
'flags' => array('Ticket Flags', 'TicketFlagField', false),
class FileUploadField extends FormField {
static $widget = 'FileUploadWidget';
protected $attachments;
static function getFileTypes() {
static $filetypes;
if (!isset($filetypes)) {
if (function_exists('apcu_fetch')) {
$key = md5(SECRET_SALT . GIT_VERSION . 'filetypes');
$filetypes = apcu_fetch($key);
}
if (!$filetypes)
$filetypes = YamlDataParser::load(INCLUDE_DIR . '/config/filetype.yaml');
if ($key)
apcu_store($key, $filetypes, 7200);
return $filetypes;
}
function getConfigurationOptions() {
// Compute size selections
$sizes = array('262144' => '— '.__('Small').' —');
$next = 512 << 10;
$max = strtoupper(ini_get('upload_max_filesize'));
$limit = (int) $max;
if (!$limit) $limit = 2 << 20; # 2M default value
elseif (strpos($max, 'K')) $limit <<= 10;
elseif (strpos($max, 'M')) $limit <<= 20;
elseif (strpos($max, 'G')) $limit <<= 30;
while ($next <= $limit) {
// Select the closest, larger value (in case the
// current value is between two)
$sizes[$next] = Format::file_size($next);
$next *= 2;
}
// Add extra option if top-limit in php.ini doesn't fall
// at a power of two
if ($next < $limit * 2)
$sizes[$limit] = Format::file_size($limit);
foreach (self::getFileTypes() as $type=>$info) {
$types[$type] = $info['description'];
}
return array(
'size' => new ChoiceField(array(
'label'=>__('Maximum File Size'),
'hint'=>__('Choose maximum size of a single file uploaded to this field'),
'default'=>$cfg->getMaxFileSize(),
'mimetypes' => new ChoiceField(array(
'label'=>__('Restrict by File Type'),
'hint'=>__('Optionally, choose acceptable file types.'),
'required'=>false,
'choices'=>$types,
'configuration'=>array('multiselect'=>true,'prompt'=>__('No restrictions'))
'extensions' => new TextareaField(array(
'label'=>__('Additional File Type Filters'),
'hint'=>__('Optionally, enter comma-separated list of additional file types, by extension. (e.g .doc, .pdf).'),
'configuration'=>array('html'=>false, 'rows'=>2),
)),
'max' => new TextboxField(array(
'label'=>__('Maximum Files'),
'hint'=>__('Users cannot upload more than this many files.'),
'default'=>false,
'required'=>false,
'validator'=>'number',
'configuration'=>array('size'=>8, 'length'=>4, 'placeholder'=>__('No limit')),
/**
* Called from the ajax handler for async uploads via web clients.
*/
function ajaxUpload($bypass=false) {
$config = $this->getConfiguration();
$files = AttachmentFile::format($_FILES['upload'],
// For numeric fields assume configuration exists
!is_numeric($this->get('id')));
if (count($files) != 1)
Http::response(400, 'Send one file at a time');
$file = array_shift($files);
$file['name'] = urldecode($file['name']);
if (!$bypass && !$this->isValidFileType($file['name'], $file['type']))
Http::response(415, 'File type is not allowed');
$config = $this->getConfiguration();
if (!$bypass && $file['size'] > $config['size'])
Http::response(413, 'File is too large');
if (!($F = AttachmentFile::upload($file)))
Http::response(500, 'Unable to store file: '. $file['error']);
// This file is allowed for attachment in this session
/**
* Called from FileUploadWidget::getValue() when manual upload is used
* for browsers which do not support the HTML5 way of uploading async.
*/
function uploadFile($file) {
if (!$this->isValidFileType($file['name'], $file['type']))
throw new FileUploadError(__('File type is not allowed'));
$config = $this->getConfiguration();
if ($file['size'] > $config['size'])
throw new FileUploadError(__('File size is too large'));
return AttachmentFile::upload($file);
}
/**
* Called from API and email routines and such to handle attachments
* sent other than via web upload
*/
function uploadAttachment(&$file) {
if (!$this->isValidFileType($file['name'], $file['type']))
throw new FileUploadError(__('File type is not allowed'));
if (is_callable($file['data']))
$file['data'] = $file['data']();
if (!isset($file['size'])) {
// bootstrap.php include a compat version of mb_strlen
if (extension_loaded('mbstring'))
$file['size'] = mb_strlen($file['data'], '8bit');
else
$file['size'] = strlen($file['data']);
}
$config = $this->getConfiguration();
if ($file['size'] > $config['size'])
throw new FileUploadError(__('File size is too large'));
if (!$F = AttachmentFile::create($file))
throw new FileUploadError(__('Unable to save file'));
return $F;
}
function isValidFileType($name, $type=false) {
$config = $this->getConfiguration();
// Check MIME type - file ext. shouldn't be solely trusted.
if ($type && $config['__mimetypes']
// Return true if all file types are allowed (.*)
if (!$config['__extensions'] || in_array('.*', $config['__extensions']))
$allowed = $config['__extensions'];
$ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
return ($ext && is_array($allowed) && in_array(".$ext", $allowed));
}
if (!isset($this->attachments) && ($a = $this->getAnswer())
&& ($e = $a->getEntry()) && ($e->get('id'))
) {
$this->attachments = GenericAttachments::forIdAndType(
// Combine the field and entry ids to make the key
sprintf('%u', abs(crc32('E'.$this->get('id').$e->get('id')))),
return $this->attachments ?: array();
function setAttachments(GenericAttachments $att) {
$this->attachments = $att;
function getFiles() {
if (!isset($this->files)) {
$files = array();
foreach ($this->getAttachments() as $a) {
if ($a && ($f=$a->getFile()))
$files[] = $f;
}
foreach ($this->getClean(false) ?: array() as $key => $value)
$files[] = array('id' => $key, 'name' => $value);
$this->files = $files;
}
return $this->files;
}
function getConfiguration() {
$config = parent::getConfiguration();
$_types = self::getFileTypes();
$mimetypes = array();
$extensions = array();
if (isset($config['mimetypes']) && is_array($config['mimetypes'])) {
foreach ($config['mimetypes'] as $type=>$desc) {
foreach ($_types[$type]['types'] as $mime=>$exts) {
$mimetypes[$mime] = true;
if (is_array($exts))
foreach ($exts as $ext)
$extensions['.'.$ext] = true;
}
}
}
if (strpos($config['extensions'], '.*') !== false)
$config['extensions'] = '';
if (is_string($config['extensions'])) {
foreach (preg_split('/\s+/', str_replace(',',' ', $config['extensions'])) as $ext) {
if (!$ext) {
continue;
}
elseif (strpos($ext, '/')) {
}
else {
if ($ext[0] != '.')
$ext = '.' . $ext;
// Ensure that the extension is lower-cased for comparison latr
$ext = strtolower($ext);
// Add this to the MIME types list so it can be exported to
// the @accept attribute
if (!isset($extensions[$ext]))
$mimetypes[$ext] = true;
$extensions[$ext] = true;
}
$config['__extensions'] = array_keys($extensions);
}
elseif (is_array($config['extensions'])) {
$config['__extensions'] = $config['extensions'];
}
// 'mimetypes' is the array represented from the user interface,
// '__mimetypes' is a complete list of supported MIME types.
$config['__mimetypes'] = array_keys($mimetypes);
return $config;
}
// When the field is saved to database, encode the ID listing as a json
// array. Then, inspect the difference between the files actually
// attached to this field
function to_database($value) {
$this->getAttachments();
if (isset($this->attachments) && $this->attachments) {
$this->attachments->keepOnlyFileIds($value);
}
return JsonDataEncoder::encode($value);
}
function parse($value) {
}
function to_php($value) {
return is_array($value) ? $value : JsonDataParser::decode($value);
function display($value) {
$links = array();
$links[] = sprintf('<a class="no-pjax" href="%s"><i class="icon-paperclip icon-flip-horizontal"></i> %s</a>',
Format::htmlchars($a->file->getDownloadUrl()),
Format::htmlchars($a->getFilename()));
}
return implode('<br/>', $links);
}
function toString($value) {
$files = array();
foreach ($this->getFiles() as $f) {
}
return implode(', ', $files);
}
function db_cleanup($field=false) {
$this->attachments->deleteAll();
}
}
function asVar($value, $id=false) {
if (($attachments = $this->getAttachments()))
$attachments = $attachments->all();
return new FileFieldAttachments($attachments ?: array());
}
function asVarType() {
return 'FileFieldAttachments';
}
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
function whatChanged($before, $after) {
$B = (array) $before;
$A = (array) $after;
$added = array_diff($A, $B);
$deleted = array_diff($B, $A);
$added = Format::htmlchars(array_keys($added));
$deleted = Format::htmlchars(array_keys($deleted));
if ($added && $deleted) {
$desc = sprintf(
__('added <strong>%1$s</strong> and removed <strong>%2$s</strong>'),
implode(', ', $added), implode(', ', $deleted));
}
elseif ($added) {
$desc = sprintf(
__('added <strong>%1$s</strong>'),
implode(', ', $added));
}
elseif ($deleted) {
$desc = sprintf(
__('removed <strong>%1$s</strong>'),
implode(', ', $deleted));
}
else {
$desc = sprintf(
__('changed from <strong>%1$s</strong> to <strong>%2$s</strong>'),
$this->display($before), $this->display($after));
}
return $desc;
}
}
class FileFieldAttachments {
function __construct($attachments) {
$this->attachments = $attachments;
}
function __toString() {
$files = array();
foreach ($this->getAttachments() as $a) {
$files[] = $a->getFilename();
}
return implode(', ', $files);
}
function getAttachments() {
return $this->attachments ?: array();
}
function getVar($tag) {
switch ($tag) {
case 'names':
return $this->__toString();
throw new OOBContent(OOBContent::FILES, $this->getAttachments());
}
}
static function getVarScope() {
return array(
'names' => __('List of file names'),
'files' => __('Attached files'),
);
}
class ColorChoiceField extends FormField {
static $widget = 'ColorPickerWidget';
}
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
class InlineFormData extends ArrayObject {
var $_form;
function __construct($form, array $data=array()) {
parent::__construct($data);
$this->_form = $form;
}
function getVar($tag) {
foreach ($this->_form->getFields() as $f) {
if ($f->get('name') == $tag)
return $this[$f->get('id')];
}
}
}
class InlineFormField extends FormField {
static $widget = 'InlineFormWidget';
var $_iform = null;
function validateEntry($value) {
if (!$this->getInlineForm()->isValid()) {
$this->_errors[] = __('Correct any errors below and try again.');
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
}
}
function parse($value) {
// The InlineFieldWidget returns an array of cleaned data
return $value;
}
function to_database($value) {
return JsonDataEncoder::encode($value);
}
function to_php($value) {
$data = JsonDataParser::decode($value);
// The InlineFormData helps with the variable replacer API
return new InlineFormData($this->getInlineForm(), $data);
}
function display($data) {
$form = $this->getInlineForm();
ob_start(); ?>
<div><?php
foreach ($form->getFields() as $field) { ?>
<span style="display:inline-block;padding:0 5px;vertical-align:top">
<strong><?php echo Format::htmlchars($field->get('label')); ?></strong>
<div><?php
$value = $data[$field->get('id')];
echo $field->display($value); ?></div>
</span><?php
} ?>
</div><?php
return ob_get_clean();
}
function getInlineForm($data=false) {
$form = $this->get('form');
if (is_array($form)) {
$form = new SimpleForm($form, $data ?: $this->value ?: $this->getSource());
// Ensure unique, but predictable form and field IDs
$form->setId(sprintf('%u', crc32($this->get('name')) >> 1));
class InlineDynamicFormField extends FormField {
function getInlineForm($data=false) {
if (!isset($this->_iform) || $data) {
$config = $this->getConfiguration();
$this->_iform = DynamicForm::lookup($config['form']);
if ($data)
$this->_iform = $this->_iform->getForm($data);
function getConfigurationOptions() {
$forms = DynamicForm::objects()->filter(array('type'=>'G'))
->values_flat('id', 'title');
$choices = array();
foreach ($forms as $row) {
list($id, $title) = $row;
$choices[$id] = $title;
return array(
'form' => new ChoiceField(array(
'id'=>2, 'label'=>'Inline Form', 'required'=>true,
'default'=>'', 'choices'=>$choices
)),
);
class InlineFormWidget extends Widget {
function render($mode=false) {
$form = $this->field->getInlineForm();
if (!$form)
return;
// Handle first-step edits -- load data from $this->value
if ($form instanceof DynamicForm && !$form->getSource())
$form = $form->getForm($this->value);
$inc = ($mode == 'client') ? CLIENTINC_DIR : STAFFINC_DIR;
include $inc . 'templates/inline-form.tmpl.php';
}
function getValue() {
$data = $this->field->getSource();
if (!$data)
return null;
$form = $this->field->getInlineForm($data);
if (!$form)
return null;
return $form->getClean();
function __construct($field) {
$this->field = $field;
$this->name = $field->getFormName();
$this->id = '_' . $this->name;
$this->value = $this->getValue();
if (!isset($this->value) && is_object($this->field->getAnswer()))
$this->value = $this->field->getAnswer()->getValue();
if (!isset($this->value) && isset($this->field->value))
$data = $this->field->getSource();
// Search for HTML form name first
if (isset($data[$this->name]))
return $data[$this->name];
elseif (isset($data[$this->field->get('name')]))
return $data[$this->field->get('name')];
elseif (isset($data[$this->field->get('id')]))
return $data[$this->field->get('id')];
/**
* getJsValueGetter
*
* Used with the dependent fields feature, this function should return a
* single javascript expression which can be used in a larger expression
* (<> == true, where <> is the result of this function). The %s token
* will be replaced with a jQuery variable representing this widget.
*/
function getJsValueGetter() {
return '%s.val()';
}
}
class TextboxWidget extends Widget {
function render($options=array(), $extraConfig=false) {
if (is_array($extraConfig)) {
foreach ($extraConfig as $k=>$v)
if (!isset($config[$k]) || !$config[$k])
$config[$k] = $v;
}
if (isset($config['size']))
$size = "size=\"{$config['size']}\"";
if (isset($config['length']) && $config['length'])
$maxlength = "maxlength=\"{$config['length']}\"";
if (isset($config['classes']))
$classes = 'class="'.$config['classes'].'"';
if (isset($config['autocomplete']))
$autocomplete = 'autocomplete="'.($config['autocomplete']?'on':'off').'"';
if (isset($config['autofocus']))
$autofocus = 'autofocus';
if (isset($config['disabled']))
$disabled = 'disabled="disabled"';
if (isset($config['translatable']) && $config['translatable'])
$translatable = 'data-translate-tag="'.$config['translatable'].'"';
$type = static::$input_type;
$types = array(
'email' => 'email',
'phone' => 'tel',
);
if ($type == 'text' && isset($types[$config['validator']]))
$type = $types[$config['validator']];
$placeholder = sprintf('placeholder="%s"', $this->field->getLocal('placeholder',
$config['placeholder']));
<input type="<?php echo $type; ?>"
id="<?php echo $this->id; ?>"
<?php echo implode(' ', array_filter(array(
$size, $maxlength, $classes, $autocomplete, $disabled,
$translatable, $placeholder, $autofocus))); ?>
name="<?php echo $this->name; ?>"
value="<?php echo Format::htmlchars($this->value); ?>"/>
<?php
}
}
class TextboxSelectionWidget extends TextboxWidget {
//TODO: Support multi-input e.g comma separated inputs
function render($options=array(), $extraConfig=array()) {
if ($this->value && is_array($this->value))
$this->value = current($this->value);
parent::render($options);
}
function getValue() {
$value = parent::getValue();
if ($value && ($item=$this->field->lookupChoice((string) $value)))
$value = $item;
return $value;
}
}
class PasswordWidget extends TextboxWidget {
static $input_type = 'password';
function render($mode=false, $extra=false) {
$extra = array();
if ($this->field->value) {
$extra['placeholder'] = '••••••••••••';
}
return parent::render($mode, $extra);
}
if ($_SERVER['REQUEST_METHOD'] != 'POST'
|| !$this->field->getForm()->isValid())
$class = $cols = $rows = $maxlength = "";
if (isset($config['rows']))
$rows = "rows=\"{$config['rows']}\"";
if (isset($config['cols']))
$cols = "cols=\"{$config['cols']}\"";
if (isset($config['length']) && $config['length'])
$maxlength = "maxlength=\"{$config['length']}\"";
if (isset($config['html']) && $config['html']) {
$class = array('richtext', 'no-bar');
$class[] = @$config['size'] ?: 'small';
$class = sprintf('class="%s"', implode(' ', $class));
$this->value = Format::viewableImages($this->value);
}
if (isset($config['context']))
$attrs['data-root-context'] = '"'.$config['context'].'"';
<span style="display:inline-block;width:100%">
<textarea <?php echo $rows." ".$cols." ".$maxlength." ".$class
.' '.Format::array_implode('=', ' ', $attrs)
.' placeholder="'.$config['placeholder'].'"'; ?>
id="<?php echo $this->id; ?>"
name="<?php echo $this->name; ?>"><?php
echo Format::htmlchars($this->value);
?></textarea>
</span>
<?php
}
function parseValue() {
parent::parseValue();
if (isset($this->value)) {
$value = $this->value;
$config = $this->field->getConfiguration();
// Trim empty spaces based on text input type.
// Preserve original input if not empty.
if ($config['html'])
$this->value = trim($value, " <>br/\t\n\r") ? $value : '';
else
$this->value = trim($value) ? $value : '';
}
}
}
class PhoneNumberWidget extends Widget {
$config = $this->field->getConfiguration();
list($phone, $ext) = explode("X", $this->value);
?>
<input id="<?php echo $this->id; ?>" type="tel" name="<?php echo $this->name; ?>" value="<?php
echo Format::htmlchars($phone); ?>"/><?php
// Allow display of extension field even if disabled if the phone
// number being edited has an extension
if ($ext || $config['ext']) { ?> <?php echo __('Ext'); ?>:
echo $this->name; ?>-ext" value="<?php echo Format::htmlchars($ext);
?>" size="5"/>
$data = $this->field->getSource();
$base = parent::getValue();
if ($base === null)
return $base;
$ext = $data["{$this->name}-ext"];
// NOTE: 'X' is significant. Don't change it
}
}
class ChoicesWidget extends Widget {
$mode = null;
if (isset($options['mode']))
$mode = $options['mode'];
elseif (isset($this->field->options['render_mode']))
$mode = $this->field->options['render_mode'];
if ($mode == 'view') {
if (!($val = (string) $this->field))
$val = sprintf('<span class="faded">%s</span>', __('None'));
echo $val;
return;
}
if ($mode == 'search') {
$config['multiselect'] = true;
}