Newer
Older
if (!isset($this->value))
$this->value = $this->field->get('default');
$config = $this->field->getConfiguration();
$type = $config['multiple'] ? 'checkbox' : 'radio';
$classes = array('checkbox');
$classes = array_merge($classes, (array) $config['classes']);
foreach ($choices as $k => $v) {
if (is_array($v)) {
$this->renderSectionBreak($k);
$this->emitChoices($v);
continue;
}
<label class="<?php echo implode(' ', $classes); ?>"
for="<?php echo $id; ?>">
<input id="<?php echo $id; ?>" type="<?php echo $type; ?>"
name="<?php echo $this->name; ?>[]" <?php
if ($this->value[$k]) echo 'checked="checked"'; ?> value="<?php
echo Format::htmlchars($k); ?>"/>
<?php
if ($v) {
echo Format::viewableImages($v);
} ?>
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
</label>
<?php }
}
function renderSectionBreak($label) { ?>
<div><?php echo Format::htmlchars($label); ?></div>
<?php
}
function getValue() {
$data = $this->field->getSource();
if (count($data)) {
if (!isset($data[$this->name]))
return array();
return $this->collectValues($data[$this->name], $this->field->getChoices());
}
return parent::getValue();
}
function collectValues($data, $choices) {
$value = array();
foreach ($choices as $k => $v) {
if (is_array($v))
$value = array_merge($value, $this->collectValues($data, $v));
elseif (@in_array($k, $data))
$value[$k] = $v;
}
return $value;
}
}
/**
* An extension to the BoxChoicesWidget which will render complex choices in
* tabs.
*/
class TabbedBoxChoicesWidget extends BoxChoicesWidget {
function render($options=array()) {
$tabs = array();
foreach ($this->field->getChoices() as $label=>$group) {
if (is_array($group)) {
$tabs[$label] = $group;
}
else {
$this->emitChoices(array($label=>$group));
}
}
if ($tabs) {
?>
<div>
<ul class="alt tabs">
<?php $i = 0;
foreach ($tabs as $label => $group) {
$active = $i++ == 0; ?>
<li <?php if ($active) echo 'class="active"';
?>><a href="#<?php echo sprintf('%s-%s', $this->name, Format::slugify($label));
?>"><?php echo Format::htmlchars($label); ?></a></li>
<?php } ?>
</ul>
<?php $i = 0;
foreach ($tabs as $label => $group) {
$first = $i++ == 0; ?>
<div class="tab_content <?php if (!$first) echo 'hidden'; ?>" id="<?php
echo sprintf('%s-%s', $this->name, Format::slugify($label));?>">
<?php $this->emitChoices($group); ?>
</div>
<?php } ?>
</div>
<?php }
}
}
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
/**
* TimezoneWidget extends ChoicesWidget to add auto-detect and select2 search
* options
*
**/
class TimezoneWidget extends ChoicesWidget {
function render($options=array()) {
parent::render($options);
$config = $this->field->getConfiguration();
if (@$config['autodetect']) {
?>
<button type="button" class="action-button" onclick="javascript:
$('head').append($('<script>').attr('src', '<?php
echo ROOT_PATH; ?>js/jstz.min.js'));
var recheck = setInterval(function() {
if (window.jstz !== undefined) {
clearInterval(recheck);
var zone = jstz.determine();
$('#<?php echo $this->id; ?>').val(zone.name()).trigger('change');
}
}, 100);
return false;"
style="vertical-align:middle">
<i class="icon-map-marker"></i> <?php echo __('Auto Detect'); ?>
</button>
<?php
} ?>
<script type="text/javascript">
$(function() {
$('#<?php echo $this->id; ?>').select2({
allowClear: true,
width: '300px'
});
});
</script>
<?php
}
}
class CheckboxWidget extends Widget {
function __construct($field) {
parent::__construct($field);
$this->name = '_field-checkboxes';
}
if (!isset($this->value))
$this->value = $this->field->get('default');
$classes = array('checkbox');
$classes = array_merge($classes, (array) $config['classes']);
<label class="<?php echo implode(' ', $classes); ?>">
type="checkbox" name="<?php echo $this->name; ?>[]" <?php
if ($this->value) echo 'checked="checked"'; ?> value="<?php
echo $this->field->get('id'); ?>"/>
<?php
if ($config['desc']) {
echo Format::viewableImages($config['desc']);
$data = $this->field->getSource();
if (count($data)) {
if (!isset($data[$this->name]))
return @in_array($this->field->get('id'), $data[$this->name]);
function getJsValueGetter() {
return '%s.is(":checked")';
}
class DatetimePickerWidget extends Widget {
$timezone = $this->field->getTimezone();
if (!isset($this->value) && ($default=$this->field->get('default')))
$this->value = $default;
if (is_int($this->value))
// Assuming UTC timezone.
$datetime = DateTime::createFromFormat('U', $this->value);
else {
$datetime = Format::parseDateTime($this->value);
if ($config['time']) {
// Convert to user's timezone for update.
$datetime->setTimezone($timezone);
}
$this->value = Format::date($datetime->getTimestamp(), false,
false, $timezone ? $timezone->getName() : 'UTC');
} else {
$datetime = new DateTime('now');
$datetime->setTimezone($timezone);
}
?>
<input type="text" name="<?php echo $this->name; ?>"
id="<?php echo $this->id; ?>" style="display:inline-block;width:auto"
value="<?php echo Format::htmlchars($this->value ?: ''); ?>" size="12"
autocomplete="off" class="dp" />
<script type="text/javascript">
$(function() {
$('input[name="<?php echo $this->name; ?>"]').datepicker({
<?php
if ($dt=$this->field->getMinDateTime())
echo sprintf("minDate: new Date(%s),\n", $dt->format('U')*1000);
if ($dt=$this->field->getMaxDateTime())
echo sprintf("maxDate: new Date(%s),\n", $dt->format('U')*1000);
?>
numberOfMonths: 2,
showButtonPanel: true,
buttonImage: './images/cal.png',
dateFormat: $.translate_format('<?php echo $cfg->getDateFormat(true); ?>')
if ($config['time']) {
list($hr, $min) = explode(':', $datetime ?
$datetime->format('H:i') : '');
// TODO: Add time picker -- requires time picker or selection with
// Misc::timeDropdown
echo ' ' . Misc::timeDropdown($hr, $min, $this->name . ':time');
echo sprintf(' <span class="faded">(<a href="#"
data-placement="top" data-toggle="tooltip"
title="%s">%s</a>)</span>',
$datetime->getTimezone()->getName(),
}
/**
* Function: getValue
* Combines the datepicker date value and the time dropdown selected
* time value into a single date and time string value in DateTime::W3C
if ($value = parent::getValue()) {
// Effective timezone for the selection
$timezone = $this->field->getTimezone();
// See if we have time
$data = $this->field->getSource();
if ($value && isset($data[$this->name . ':time']))
$value .=' '.$data[$this->name . ':time'];
$dt = new DateTime($value, $timezone);
class SectionBreakWidget extends Widget {
?><div class="form-header section-break"><h3><?php
echo Format::htmlchars($this->field->getLocal('label'));
?></h3><em><?php echo Format::display($this->field->getLocal('hint'));
?></em></div>
<?php
}
}
class ThreadEntryWidget extends Widget {
$object_id = false;
if ($options['client']) {
$namespace = $options['draft-namespace']
?: 'ticket.client';
$object_id = substr(session_id(), -12);
} else {
$namespace = $options['draft-namespace'] ?: 'ticket.staff';
list($draft, $attrs) = Draft::getDraftAndDataAttrs($namespace, $object_id, $this->value);
<textarea style="width:100%;" name="<?php echo $this->field->get('name'); ?>"
placeholder="<?php echo Format::htmlchars($this->field->get('placeholder')); ?>"
class="<?php if ($config['html']) echo 'richtext';
?> draft draft-delete" <?php echo $attrs; ?>
cols="21" rows="8" style="width:80%;"><?php echo
Format::htmlchars($this->value) ?: $draft; ?></textarea>
if (!$config['attachments'])
return;
$attachments = $this->getAttachments($config);
print $attachments->render($options);
foreach ($attachments->getMedia() as $type=>$urls) {
foreach ($urls as $url)
Form::emitMedia($url, $type);
function getAttachments($config=false) {
if (!$config)
$config = $this->field->getConfiguration();
$field = new FileUploadField(array(
'id'=>'attach',
'name'=>'attach:' . $this->field->get('id'),
'configuration'=>$config)
);
$field->setForm($this->field->getForm());
return $field;
function parseValue() {
parent::parseValue();
if (isset($this->value)) {
$value = $this->value;
$config = $this->field->getConfiguration();
// Trim 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 FileUploadWidget extends Widget {
static $media = array(
'css' => array(
'/css/filedrop.css',
),
);
$config = $this->field->getConfiguration();
$name = $this->field->getFormName();
$id = substr(md5(spl_object_hash($this)), 10);
$attachments = $this->field->getFiles();
$mimetypes = array_filter($config['__mimetypes'],
function($t) { return strpos($t, '/') !== false; }
);
$maxfilesize = ($config['size'] ?: 1048576) / 1048576;
$files = $F = array();
$new = array_fill_keys($this->field->getClean(), 1);
foreach ($attachments as $a) {
$F[] = $a->file;
unset($new[$a->file_id]);
}
// Add in newly added files not yet saved (if redisplaying after an
// error)
if ($new) {
$F = array_merge($F, AttachmentFile::objects()
->filter(array('id__in' => array_keys($new)))
foreach ($F as $file) {
$files[] = array(
'id' => $file->getId(),
'name' => $file->getName(),
'type' => $file->getType(),
'size' => $file->getSize(),
'download_url' => $file->getDownloadUrl(),
);
?><div id="<?php echo $id;
?>" class="filedrop"><div class="files"></div>
<div class="dropzone"><i class="icon-upload"></i>
<?php echo sprintf(
__('Drop files here or %s choose them %s'),
'<a href="#" class="manual">', '</a>'); ?>
<input type="file" multiple="multiple"
id="file-<?php echo $id; ?>" style="display:none;"
accept="<?php echo implode(',', $config['__mimetypes']); ?>"/>
$(function(){$('#<?php echo $id; ?> .dropzone').filedropbox({
url: 'ajax.php/form/upload/<?php echo $this->field->get('id') ?>',
link: $('#<?php echo $id; ?>').find('a.manual'),
fallback_id: 'file-<?php echo $id; ?>',
allowedfileextensions: <?php echo JsonDataEncoder::encode(
$config['__extensions'] ?: array()); ?>,
allowedfiletypes: <?php echo JsonDataEncoder::encode(
maxfiles: <?php echo $config['max'] ?: 20; ?>,
maxfilesize: <?php echo $maxfilesize; ?>,
name: '<?php echo $name; ?>[]',
files: <?php echo JsonDataEncoder::encode($files); ?>
});});
</script>
<?php
}
function getValue() {
$ids = array();
// Handle manual uploads (IE<10)
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES[$this->name])) {
foreach (AttachmentFile::format($_FILES[$this->name]) as $file) {
$F = $this->field->uploadFile($file);
$ids[] = $F->getId();
// Files uploaded here MUST have been uploaded by this user and
// identified in the session
//
// If no value was sent, assume an empty list
if (!($files = parent::getValue()))
$allowed = array();
// Files already attached to the field are allowed
foreach ($this->field->getFiles() as $F) {
// FIXME: This will need special porting in v1.10
$allowed[$F->id] = 1;
// New files uploaded in this session are allowed
if (isset($_SESSION[':uploadedFiles']))
$allowed += $_SESSION[':uploadedFiles'];
// Canned attachments initiated by this session
if (isset($_SESSION[':cannedFiles']))
$allowed += $_SESSION[':cannedFiles'];
// Parse the files and make sure it's allowed.
foreach ($files as $info) {
@list($id, $name) = explode(',', $info, 2);
if (!isset($allowed[$id]))
continue;
// Keep the values as the IDs
if ($name)
$ids[$name] = $id;
else
$ids[] = $id;
}
class FileUploadError extends Exception {}
class FreeTextField extends FormField {
static $widget = 'FreeTextWidget';
function getConfigurationOptions() {
return array(
'content' => new TextareaField(array(
'configuration' => array('html' => true, 'size'=>'large'),
'label'=>__('Content'), 'required'=>true, 'default'=>'',
'hint'=>__('Free text shown in the form, such as a disclaimer'),
)),
'attachments' => new FileUploadField(array(
'id'=>'attach',
'name'=>'files',
'configuration' => array('extensions'=>'')
)),
);
}
function hasData() {
return false;
}
function isBlockLevel() {
return true;
}
/* utils */
function to_config($config) {
if ($config && isset($config['attachments']))
$keepers = $config['attachments'] = array_values($config['attachments']);
$this->getAttachments()->keepOnlyFileIds($keepers);
return $config;
}
function db_cleanup($field=false) {
$this->getAttachments()->deleteAll();
}
function getAttachments() {
if (!isset($this->attachments))
$this->attachments = GenericAttachments::forIdAndType($this->get('id'), 'I');
return $this->attachments;
}
function getFiles() {
if (!($attachments = $this->getAttachments()))
return array();
return $attachments->all();
}
}
class FreeTextWidget extends Widget {
$class = $config['classes'] ?: 'thread-body bleed';
?><div class="<?php echo $class; ?>"><?php
if ($label = $this->field->getLocal('label')) { ?>
<h3><?php
echo Format::htmlchars($label);
?></h3><?php
}
if ($hint = $this->field->getLocal('hint')) { ?>
<em><?php
echo Format::htmlchars($hint);
?></em><?php
} ?>
<div><?php
echo Format::viewableImages($config['content']); ?></div>
</div>
<?php
if (($attachments = $this->field->getFiles()) && count($attachments)) { ?>
<section class="freetext-files">
<div class="title"><?php echo __('Related Resources'); ?></div>
<?php foreach ($attachments as $attach) {
$filename = Format::htmlchars($attach->getFilename());
?>
<a href="<?php echo $attach->file->getDownloadUrl(); ?>"
target="_blank" download="<?php echo $filename; ?>"
class="truncate no-pjax">
</a>
</div>
<?php } ?>
</section>
<?php }
class ColorPickerWidget extends Widget {
static $media = array(
'css' => array(
'css/spectrum.css',
),
'js' => array(
'js/spectrum.js',
),
);
function render($options=array()) {
?><input type="color"
id="<?php echo $this->id; ?>"
name="<?php echo $this->name; ?>"
value="<?php echo Format::htmlchars($this->value); ?>"/><?php
}
}
static $operators = array(
'eq' => 1,
);
const HIDDEN = 0x0001;
const VISIBLE = 0x0002;
var $initial;
var $constraint;
function __construct($constraint, $initial=self::VISIBLE) {
$this->constraint = $constraint;
$this->initial = $initial;
}
function emitJavascript($field) {
if (!$this->constraint->constraints)
return;
$func = 'recheck';
$form = $field->getForm();
?>
<script type="text/javascript">
!(function() {
var <?php echo $func; ?> = function() {
var target = $('#field<?php echo $field->getWidget()->id; ?>');
<?php $fields = $this->getAllFields($this->constraint);
foreach ($fields as $f) {
$field = $form->getField($f);
echo sprintf('var %1$s = $("#%1$s");',
}
$expression = $this->compileQ($this->constraint, $form);
?>
if (<?php echo $expression; ?>)
$(this).trigger('show');
});
else
target.slideUp('fast', function (){
$(this).trigger('hide');
});
};
<?php foreach ($fields as $f) {
$w = $form->getField($f)->getWidget();
?>
$('#<?php echo $w->id; ?>').on('change', <?php echo $func; ?>);
$('#field<?php echo $w->id; ?>').on('show hide', <?php
echo $func; ?>);
<?php } ?>
})();
</script><?php
}
/**
* Determines if the field was visible when the form was submitted
*/
function isVisible($field) {
// Assume initial visibility if constraint is not provided.
if (!$this->constraint->constraints)
return $this->initial == self::VISIBLE;
return $this->compileQPhp($this->constraint, $field);
}
static function splitFieldAndOp($field) {
if (false !== ($last = strrpos($field, '__'))) {
$op = substr($field, $last + 2);
if (isset(static::$operators[$op]))
$field = substr($field, 0, strrpos($field, '__'));
}
return array($field, $op);
}
function compileQPhp(Q $Q, $field) {
if (!($form = $field->getForm())) {
return $this->initial == self::VISIBLE;
}
$expr = array();
foreach ($Q->constraints as $c=>$value) {
if ($value instanceof Q) {
$expr[] = $this->compileQPhp($value, $field);
}
else {
@list($f, $op) = self::splitFieldAndOp($c);
$wval = $field ? $field->getClean() : null;
switch ($op) {
case 'eq':
case null:
$expr[] = ($wval == $value && $field->isVisible());
}
}
}
$glue = $Q->isOred()
? function($a, $b) { return $a || $b; }
: function($a, $b) { return $a && $b; };
$initial = !$Q->isOred();
$expression = array_reduce($expr, $glue, $initial);
if ($Q->isNegated)
$expression = !$expression;
return $expression;
}
function getAllFields(Q $Q, &$fields=array()) {
foreach ($Q->constraints as $c=>$value) {
if ($c instanceof Q) {
$this->getAllFields($c, $fields);
}
else {
@list($f) = self::splitFieldAndOp($c);
$fields[$f] = true;
}
}
return array_keys($fields);
}
function compileQ($Q, $form) {
$expr = array();
foreach ($Q->constraints as $c=>$value) {
if ($value instanceof Q) {
$expr[] = $this->compileQ($value, $form);
}
else {
list($f, $op) = self::splitFieldAndOp($c);
$widget = $form->getField($f)->getWidget();
$id = $widget->id;
$expr[] = sprintf('(%s.is(":visible") && %s)',
$id,
sprintf('%s == %s',
sprintf($widget->getJsValueGetter(), $id),
JsonDataEncoder::encode($value))
);
}
}
}
$glue = $Q->isOred() ? ' || ' : ' && ';
$expression = implode($glue, $expr);
if (count($expr) > 1)
$expression = '('.$expression.')';
if ($Q->isNegated)
$expression = '!'.$expression;
return $expression;
}
}
class AssignmentForm extends Form {
static $id = 'assign';
var $_assignee = null;
function getFields() {
if ($this->fields)
return $this->fields;
$fields = array(
'assignee' => new AssigneeField(array(
'id'=>1, 'label' => __('Assignee'),
'flags' => hexdec(0X450F3), 'required' => true,
'validator-error' => __('Assignee selection required'),
'configuration' => array(
'criteria' => array(
'available' => true,
),
),
'refer' => new BooleanField(array(
'id'=>2, 'label'=>'', 'required'=>false,
'default'=>false,
'configuration'=>array(
'desc' => 'Maintain referral access to current assignees')
)
),
'id' => 3, 'label'=> '', 'required'=>false, 'default'=>'',
'placeholder' => __('Optional reason for the assignment'),
),
)
),
);
$fields['assignee']->setChoices($this->_assignees);
$this->setFields($fields);
return $this->fields;
}
function getField($name) {
if (($fields = $this->getFields())
&& isset($fields[$name]))
return $fields[$name];
}
function isValid($include=false) {
if (!parent::isValid($include) || !($f=$this->getField('assignee')))
return false;
// Do additional assignment validation
if (!($assignee = $this->getAssignee())) {
$f->addError(__('Unknown assignee'));
} elseif ($assignee instanceof Staff) {
// Make sure the agent is available
if (!$assignee->isAvailable())
$f->addError(__('Agent is unavailable for assignment'));
} elseif ($assignee instanceof Team) {
// Make sure the team is active and has members
if (!$assignee->isActive())
$f->addError(__('Team is disabled'));
elseif (!$assignee->getNumMembers())
$f->addError(__('Team does not have members'));
}
return !$this->errors();
}
function render($options) {
switch(strtolower($options['template'])) {
case 'simple':
$inc = STAFFINC_DIR . 'templates/dynamic-form-simple.tmpl.php';
break;
default:
throw new Exception(sprintf(__('%s: Unknown template style %s'),
'FormUtils', $options['template']));
}
$form = $this;
include $inc;
}
function setAssignees($assignees) {
$this->_assignees = $assignees;
$this->_fields = array();
}
function getAssignees() {
return $this->_assignees;
}
if (!isset($this->_assignee))
$this->_assignee = $this->getField('assignee')->getClean();
function getComments() {
return $this->getField('comments')->getClean();
function refer() {
return $this->getField('refer')->getClean();
}
}
class ClaimForm extends AssignmentForm {
var $_fields;
function setFields($fields) {
$this->_fields = $fields;
parent::setFields($fields);
}
function getFields() {
if ($this->_fields)
return $this->_fields;
// Disable && hide assignee field selection
if (isset($fields['assignee'])) {
$visibility = new VisibilityConstraint(
new Q(array()), VisibilityConstraint::HIDDEN);
$fields['assignee']->set('visibility', $visibility);
}
// Change coments placeholder to reflect claim
if (isset($fields['comments'])) {
$fields['comments']->configure('placeholder',
__('Optional reason for the claim'));
}
$this->setFields($fields);
return $this->fields;
class ReferralForm extends Form {
static $id = 'refer';
var $_target = null;
var $_choices = null;
var $_prompt = '';
function getFields() {
if ($this->fields)
return $this->fields;
$fields = array(
'target' => new ChoiceField(array(
'id'=>1,
'label' => __('Referee'),
'flags' => hexdec(0X450F3),
'required' => true,
'validator-error' => __('Selection required'),
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
'choices' => array(
'agent' => __('Agent'),
'team' => __('Team'),
'dept' => __('Department'),
),
)
),
'agent' => new ChoiceField(array(
'id'=>2,
'label' => '',
'flags' => hexdec(0X450F3),
'required' => true,
'configuration'=>array('prompt'=>__('Select Agent')),
'validator-error' => __('Agent selection required'),
'visibility' => new VisibilityConstraint(
new Q(array('target__eq'=>'agent')),
VisibilityConstraint::HIDDEN
),
)
),
'team' => new ChoiceField(array(
'id'=>3,
'label' => '',
'flags' => hexdec(0X450F3),
'required' => true,
'validator-error' => __('Team selection required'),
'configuration'=>array('prompt'=>__('Select Team')),
'visibility' => new VisibilityConstraint(
new Q(array('target__eq'=>'team')),
VisibilityConstraint::HIDDEN
),
)
),
'dept' => new DepartmentField(array(
'id'=>4,
'label' => '',
'flags' => hexdec(0X450F3),
'required' => true,
'validator-error' => __('Dept. selection required'),
'configuration'=>array('prompt'=>__('Select Department')),
'visibility' => new VisibilityConstraint(
new Q(array('target__eq'=>'dept')),
VisibilityConstraint::HIDDEN
),
)
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
'label'=> '',
'required'=>false,
'default'=>'',
'configuration' => array(
'html' => true,
'size' => 'small',
'placeholder' => __('Optional reason for the referral'),
),
)
),
);
$this->setFields($fields);
return $this->fields;
}
function getField($name) {
if (($fields = $this->getFields())
&& isset($fields[$name]))
return $fields[$name];
}