Newer
Older
}
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) { ?>
<a href="<?php echo $attach->file->getDownloadUrl(); ?>"
target="_blank" download="<?php echo $attach->file->getDownloadUrl();
?>" class="truncate no-pjax">
<?php echo Format::htmlchars($attach->getFilename()); ?>
</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,
),
),
)
),
'comments' => new TextareaField(array(
'id' => 2,
'label'=> '',
'required'=>false,
'default'=>'',
'configuration' => array(
'html' => true,
'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();
}
}
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 TransferForm extends Form {
static $id = 'transfer';
var $_dept = null;
function __construct($source=null, $options=array()) {
parent::__construct($source, $options);
function getFields() {
if ($this->fields)
return $this->fields;
$fields = array(
'dept' => new DepartmentField(array(
'id'=>1,
'label' => __('Department'),
'flags' => hexdec(0X450F3),
'required' => true,
'validator-error' => __('Department selection is required'),
)
),
'comments' => new TextareaField(array(
'id' => 2,
'label'=> '',
'required'=>false,
'default'=>'',
'configuration' => array(
'html' => true,
'placeholder' => __('Optional reason for the transfer'),
),
)
),
);
$this->setFields($fields);
return $this->fields;
function isValid($include=false) {
if (!parent::isValid($include))
return false;
// Do additional validations
if (!($dept = $this->getDept()))
$this->getField('dept')->addError(
__('Unknown department'));
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'),
get_class(), $options['template']));
}
$form = $this;
include $inc;
function getDept() {
if (!isset($this->_dept)) {
if (($id = $this->getField('dept')->getClean()))
$this->_dept = Dept::lookup($id);
}
return $this->_dept;
/**
* FieldUnchanged
*
* Thrown in the to_database() method to indicate the value should not be
* saved in the database (it wasn't changed in the request)
*/
class FieldUnchanged extends Exception {}