Newer
Older
$this->min = $config['min']
? Format::parseDateTime($config['min']) : false;
}
return $this->min;
}
function getMaxDateTime() {
if (!isset($this->max)) {
$config = $this->getConfiguration();
$this->max = $config['max']
? Format::parseDateTime($config['max']) : false;
}
return $this->max;
}
function getPastPresentLabels() {
return array(__('Create Date'), __('Reopen Date'),
__('Close Date'), __('Last Update'));
}
// Store time in format given by Date Picker (DateTime::W3C)
return $value;
if (strtotime($value) <= 0)
return 0;
return $value;
if (!$value || !($datetime = Format::parseDateTime($value)))
return '';
$config = $this->getConfiguration();
if ($config['gmt'])
return $this->format((int) $datetime->format('U'));
// Force timezone if field has one.
if ($config['timezone']) {
$timezone = new DateTimezone($config['timezone']);
$datetime->setTimezone($timezone);
}
$value = $this->format($datetime->format('U'),
$datetime->getTimezone()->getName());
// No need to show timezone
if (!$config['time'])
return $value;
// Display is NOT timezone aware show entry's timezone.
return sprintf('%s (%s)',
$value, $datetime->format('T'));
function from_query($row, $name=false) {
$value = parent::from_query($row, $name);
$timestamp = is_int($value) ? $value : (int) strtotime($value);
return ($timestamp > 0) ? $timestamp : '';
function format($timestamp, $timezone=false) {
if (!$timestamp || $timestamp <= 0)
return '';
$config = $this->getConfiguration();
if ($config['time'])
$formatted = Format::datetime($timestamp, false, $timezone);
else
$formatted = Format::date($timestamp, false, false, $timezone);
return $formatted;
$timestamp = is_int($value) ? $value : (int) strtotime($value);
if ($timestamp <= 0)
return '';
return $this->format($timestamp);
}
function asVar($value, $id=false) {
return null;
$datetime = $this->getDateTime($value);
$config = $this->getConfiguration();
if (!$config['gmt'] || !$config['time'])
$timezone = $datetime->getTimezone()->getName();
$timezone = false;
return new FormattedDate($value, array(
'timezone' => $timezone,
'format' => $config['time'] ? 'long' : 'short'
)
);
}
function asVarType() {
return 'FormattedDate';
function getConfigurationOptions() {
return array(
'time' => new BooleanField(array(
'id'=>1, 'label'=>__('Time'), 'required'=>false, 'default'=>false,
'desc'=>__('Show time selection with date picker')))),
'timezone' => new TimezoneField(array(
'id'=>2, 'label'=>__('Timezone'), 'required'=>false,
'hint'=>__('Timezone of the date time selection'),
'configuration' => array('autodetect'=>false,
'prompt' => __("User's timezone")),
'visibility' => new VisibilityConstraint(
new Q(array('time__eq'=> true)),
VisibilityConstraint::HIDDEN
),
)),
'id'=>3, 'label'=>__('Timezone Aware'), 'required'=>false,
'desc'=>__("Show date/time relative to user's timezone")))),
'id'=>4, 'label'=>__('Earliest'), 'required'=>false,
'hint'=>__('Earliest date selectable'))),
'id'=>5, 'label'=>__('Latest'), 'required'=>false,
'default'=>null, 'hint'=>__('Latest date selectable'))),
'id'=>6, 'label'=>__('Allow Future Dates'), 'required'=>false,
'desc'=>__('Allow entries into the future' /* Used in the date field */)),
)),
);
}
function validateEntry($value) {
$config = $this->getConfiguration();
parent::validateEntry($value);
if (!$value || !($datetime = Format::parseDateTime($value)))
$min = $this->getMinDateTime();
$max = $this->getMaxDateTime();
$this->_errors[] = __('Enter a valid date');
} elseif ($min and $val < $min) {
$this->_errors[] = sprintf('%s (%s)',
__('Selected date is earlier than permitted'),
Format::date($min->getTimestamp(), false, false,
$min->getTimezone()->getName() ?: 'UTC')
);
} elseif ($max and $val > $max) {
$this->_errors[] = sprintf('%s (%s)',
__('Selected date is later than permitted'),
Format::date($max->getTimestamp(), false, false,
$max->getTimezone()->getName() ?: 'UTC')
);
}
// SearchableField interface ------------------------------
function getSearchMethods() {
return array(
'set' => __('has a value'),
'nset' => __('does not have a value'),
'equal' => __('on'),
'before' => __('before'),
'after' => __('after'),
'between' => __('between'),
'ndaysago' => __('in the last n days'),
'ndays' => __('in the next n days'),
'future' => __('in the future'),
'past' => __('in the past'),
'distfut' => __('more than n days from now'),
'distpast' => __('more than n days ago'),
);
}
function getSearchMethodWidgets() {
$config_notime = $config = $this->getConfiguration();
$config_notime['time'] = false;
return array(
'until' => new TextboxField(array(
'configuration' => array('validator'=>'number', 'size'=>4))
),
'int' => new ChoiceField(array(
'default' => 'd',
'choices' => self::intervals($x),
)),
);
};
return array(
'set' => null,
'past' => null,
'future' => null,
'equal' => array('DatetimeField', array(
'configuration' => $config_notime,
'nequal' => array('DatetimeField', array(
'configuration' => $config_notime,
)),
'before' => array('DatetimeField', array(
'configuration' => $config,
)),
'after' => array('DatetimeField', array(
'configuration' => $config,
)),
'between' => array('InlineformField', array(
'form' => array(
'left' => new DatetimeField(),
'text' => new FreeTextField(array(
'configuration' => array('content' => 'and'))
),
'right' => new DatetimeField(),
),
)),
'ndaysago' => array('InlineformField', array('form'=>$nday_form())),
'ndays' => array('InlineformField', array('form'=>$nday_form())),
'distfut' => array('InlineformField', array('form'=>$nday_form())),
'distpast' => array('InlineformField', array('form'=>$nday_form())),
);
}
function getSearchQ($method, $value, $name=false) {
static $intervals = array(
'm' => 'MONTH',
'w' => 'WEEK',
'd' => 'DAY',
'h' => 'HOUR',
'i' => 'MINUTE',
);
$name = $name ?: $this->get('name');
$now = SqlFunction::NOW();
$config = $this->getConfiguration();
if (is_int($value))
$value = DateTime::createFromFormat('U', !$config['gmt'] ? Misc::gmtime($value) : $value) ?: $value;
elseif (is_string($value))
$value = Format::parseDateTime($value) ?: $value;
case 'equal':
$l = clone $value;
$r = $value->add(new DateInterval('P1D'));
return new Q(array(
"{$name}__gte" => $l,
"{$name}__lt" => $r
));
case 'nequal':
$l = clone $value;
$r = $value->add(new DateInterval('P1D'));
return Q::any(array(
"{$name}__lt" => $l,
"{$name}__gte" => $r,
));
case 'future':
$value = $now;
case 'after':
return new Q(array("{$name}__gte" => $value));
case 'past':
$value = $now;
case 'before':
return new Q(array("{$name}__lt" => $value));
case 'between':
foreach (array('left', 'right') as $side) {
$value[$side] = is_int($value[$side])
? DateTime::createFromFormat('U', !$config['gmt']
? Misc::gmtime($value[$side]) : $value[$side]) ?: $value[$side]
return new Q(array(
"{$name}__gte" => $value['left'],
"{$name}__lte" => $value['right'],
));
case 'ndaysago':
$int = $intervals[$value['int'] ?: 'd'] ?: 'DAY';
$interval = new SqlInterval($int, $value['until']);
"{$name}__range" => array($now->minus($interval), $now),
));
case 'ndays':
$int = $intervals[$value['int'] ?: 'd'] ?: 'DAY';
$interval = new SqlInterval($int, $value['until']);
"{$name}__range" => array($now, $now->plus($interval)),
));
// Distant past and future ranges
case 'distpast':
$int = $intervals[$value['int'] ?: 'd'] ?: 'DAY';
$interval = new SqlInterval($int, $value['until']);
return new Q(array(
"{$name}__lte" => $now->minus($interval),
));
case 'distfut':
$int = $intervals[$value['int'] ?: 'd'] ?: 'DAY';
$interval = new SqlInterval($int, $value['until']);
return new Q(array(
"{$name}__gte" => $now->plus($interval),
));
default:
return parent::getSearchQ($method, $value, $name);
}
}
function describeSearchMethod($method) {
switch ($method) {
case 'before':
return __('%1$s before %2$s' /* occurs before a date and time */);
case 'after':
return __('%1$s after %2$s' /* occurs after a date and time */);
case 'ndays':
return __('%1$s in the next %2$s' /* occurs within a window (like 3 days) */);
case 'ndaysago':
return __('%1$s in the last %2$s' /* occurs within a recent window (like 3 days) */);
case 'distfut':
return __('%1$s after %2$s from now' /* occurs after a window (like 3 days) */);
case 'distpast':
return __('%1$s before %2$s ago' /* occurs previous to a window (like 3 days) */);
case 'between':
return __('%1$s between %2$s and %3$s');
case 'future':
return __('%1$s is in the future');
case 'past':
return __('%1$s is in the past');
default:
return parent::describeSearchMethod($method);
}
}
function describeSearch($method, $value, $name=false) {
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
$name = $name ?: $this->get('name');
$desc = $this->describeSearchMethod($method);
switch ($method) {
case 'between':
return sprintf($desc, $name,
$this->toString($value['left']),
$this->toString($value['right']));
case 'ndays':
case 'ndaysago':
case 'distfut':
case 'distpast':
$interval = sprintf('%s %s', $value['until'],
self::intervals($value['until'], $value['int']));
return sprintf($desc, $name, $interval);
break;
case 'future':
case 'past':
return sprintf($desc, $name);
case 'before':
case 'after':
return sprintf($desc, $name, $this->toString($value));
default:
return parent::describeSearch($method, $value, $name);
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
function supportsQuickFilter() {
return true;
}
function getQuickFilterChoices() {
return array(
'h' => __('Today'),
'm' => __('Tomorrow'),
'g' => __('Yesterday'),
'l7' => __('Last 7 days'),
'l30' => __('Last 30 days'),
'n7' => __('Next 7 days'),
'n30' => __('Next 30 days'),
/* Ugh. These boundaries are so difficult in SQL
'w' => __('This Week'),
'm' => __('This Month'),
'lw' => __('Last Week'),
'lm' => __('Last Month'),
'nw' => __('Next Week'),
'nm' => __('Next Month'),
*/
);
}
function applyQuickFilter($query, $qf_value, $name=false) {
$name = $name ?: $this->get('name');
$now = SqlFunction::NOW();
$midnight = Misc::dbtime(time() - (time() % 86400));
switch ($qf_value) {
case 'l7':
return $query->filter([
"{$name}__range" => array($now->minus(SqlInterval::DAY(7)), $now),
]);
case 'l30':
return $query->filter([
"{$name}__range" => array($now->minus(SqlInterval::DAY(30)), $now),
]);
case 'n7':
return $query->filter([
"{$name}__range" => array($now, $now->plus(SqlInterval::DAY(7))),
]);
case 'n30':
return $query->filter([
"{$name}__range" => array($now, $now->plus(SqlInterval::DAY(30))),
]);
case 'g':
$midnight -= 86400;
// Fall through to the today case
case 'm':
if ($qf_value === 'm') $midnight += 86400;
// Fall through to the today case
case 'h':
$midnight = DateTime::createFromFormat('U', $midnight);
return $query->filter([
"{$name}__range" => array($midnight,
SqlExpression::plus($midnight, SqlInterval::DAY(1))),
]);
}
}
/**
* This is kind-of a special field that doesn't have any data. It's used as
* a field to provide a horizontal section break in the display of a form
*/
class SectionBreakField extends FormField {
static $widget = 'SectionBreakWidget';
function hasData() {
return false;
}
function isBlockLevel() {
return true;
}
}
class ThreadEntryField extends FormField {
static $widget = 'ThreadEntryWidget';
function isChangeable() {
return false;
}
function isBlockLevel() {
return true;
}
function isPresentationOnly() {
return true;
}
function getMedia() {
$config = $this->getConfiguration();
$media = parent::getMedia() ?: array();
if ($config['attachments'])
$media = array_merge_recursive($media, FileUploadWidget::$media);
return $media;
}
function getConfiguration() {
global $cfg;
$config = parent::getConfiguration();
$config['html'] = (bool) ($cfg && $cfg->isRichTextEnabled());
function getConfigurationOptions() {
global $cfg;
$attachments = new FileUploadField();
$fileupload_config = $attachments->getConfigurationOptions();
if ($cfg->getAllowedFileTypes())
$fileupload_config['extensions']->set('default', $cfg->getAllowedFileTypes());
foreach ($fileupload_config as $C) {
$C->set('visibility', new VisibilityConstraint(new Q(array(
'attachments__eq'=>true,
)), VisibilityConstraint::HIDDEN));
}
return array(
'attachments' => new BooleanField(array(
'label'=>__('Enable Attachments'),
'default'=>$cfg->allowAttachments(),
'configuration'=>array(
'desc'=>__('Enables attachments, regardless of channel'),
'validators' => function($self, $value) {
if (!ini_get('file_uploads'))
$self->addError(__('The "file_uploads" directive is disabled in php.ini'));
}
+ $fileupload_config;
function isAttachmentsEnabled() {
$config = $this->getConfiguration();
return $config['attachments'];
}
if ($hint = $this->getLocal('hint'))
$this->set('placeholder', $hint);
$this->set('hint', null);
$widget = parent::getWidget($widgetClass);
return $widget;
}
}
class PriorityField extends ChoiceField {
function getWidget($widgetClass=false) {
$widget = parent::getWidget($widgetClass);
if ($widget->value instanceof Priority)
$widget->value = $widget->value->getId();
return $widget;
}
function hasIdValue() {
return true;
}
function getChoices($verbose=false) {
$sql = 'SELECT priority_id, priority_desc FROM '.PRIORITY_TABLE
.' ORDER BY priority_urgency DESC';
$choices = array('' => '— '.__('Default').' —');
if (!($res = db_query($sql)))
return $choices;
while ($row = db_fetch_row($res))
$choices[$row[0]] = $row[1];
return $choices;
}
function parse($id) {
return $this->to_php(null, $id);
}
function to_php($value, $id=false) {
if ($value instanceof Priority)
return $value;
if (is_array($id)) {
reset($id);
$id = key($id);
}
elseif (is_array($value))
list($value, $id) = $value;
elseif ($id === false)
$id = $value;
if ($id)
return Priority::lookup($id);
}
function to_database($prio) {
return ($prio instanceof Priority)
? array($prio->getDesc(), $prio->getId())
: $prio;
}
function display($prio, &$styles=null) {
if (!$prio instanceof Priority)
return parent::display($prio);
if (is_array($styles))
$styles += array(
'background-color' => $prio->getColor()
);
return Format::htmlchars($prio->getDesc());
function toString($value) {
return ($value instanceof Priority) ? $value->getDesc() : $value;
}
function whatChanged($before, $after) {
return FormField::whatChanged($before, $after);
}
function searchable($value) {
// Priority isn't searchable this way
return null;
}
function getKeys($value) {
return ($value instanceof Priority) ? array($value->getId()) : null;
}
function asVar($value, $id=false) {
return $this->to_php($value, $id);
}
function getConfigurationOptions() {
$choices = $this->getChoices();
$choices[''] = __('System Default');
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),
)),
'default' => new ChoiceField(array(
'id'=>3, 'label'=>__('Default'), 'required'=>false, 'default'=>'',
'choices' => $choices,
'hint'=>__('Default selection for this field'),
'configuration'=>array('size'=>20, 'length'=>40),
)),
function getConfiguration() {
global $cfg;
$config = parent::getConfiguration();
if (!isset($config['default']))
$config['default'] = $cfg->getDefaultPriorityId();
return $config;
}
function applyOrderBy($query, $reverse=false, $name=false) {
if ($query->model == 'Ticket' && $name == 'cdata__priority') {
// Order by the priority urgency field
$col = 'cdata__:priority__priority_urgency';
$reverse = !$reverse;
}
else {
$col = $name ?: CustomQueue::getOrmPath($this->get('name'), $query);
}
if ($reverse)
$col = "-$col";
return $query->order_by($col);
}
FormField::addFieldTypes(/*@trans*/ 'Dynamic Fields', function() {
'priority' => array(__('Priority Level'), 'PriorityField'),
class TimezoneField extends ChoiceField {
static $widget = 'TimezoneWidget';
function hasIdValue() {
return false;
}
function getChoices($verbose=false) {
global $cfg;
$choices = array();
foreach (DateTimeZone::listIdentifiers() as $zone)
$choices[$zone] = str_replace('/',' / ',$zone);
return $choices;
}
function whatChanged($before, $after) {
return FormField::whatChanged($before, $after);
}
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
function searchable($value) {
return null;
}
function getConfigurationOptions() {
return array(
'autodetect' => new BooleanField(array(
'id'=>1, 'label'=>__('Auto Detect'), 'required'=>false, 'default'=>true,
'configuration'=>array(
'desc'=>__('Add Auto Detect Button'))
)),
'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),
)),
);
}
}
class DepartmentField extends ChoiceField {
function getWidget($widgetClass=false) {
$widget = parent::getWidget($widgetClass);
if ($widget->value instanceof Dept)
$widget->value = $widget->value->getId();
return $widget;
}
function hasIdValue() {
return true;
}
function getChoices($verbose=false) {
$selected = self::getWidget();
aydreeihn
committed
if($selected && $selected->value) {
if(is_array($selected->value)) {
foreach ($selected->value as $k => $v) {
$current_id = $k;
$current_name = $v;
}
}
else {
$current_id = $selected->value;
$current_name = Dept::getNameById($current_id);
}
}
$active_depts = Dept::objects()
->filter(array('flags__hasbit' => Dept::FLAG_ACTIVE))
->values('id', 'name')
->order_by('name');
aydreeihn
committed
if ($depts = Dept::getDepartments(null, true, Dept::DISPLAY_DISABLED)) {
//create array w/queryset
$active = array();
foreach ($active_depts as $dept)
$active[$dept['id']] = $dept['name'];
//add selected dept to list
if($current_id)
$active[$current_id] = $current_name;
else
return $active;
aydreeihn
committed
foreach ($depts as $id => $name) {
$choices[$id] = $name;
if(!array_key_exists($id, $active) && $current_id)
unset($choices[$id]);
return $choices;
}
function parse($id) {
return $this->to_php(null, $id);
}
function to_php($value, $id=false) {
if ($id) {
if (is_array($id)) {
reset($id);
$id = key($id);
}
return $id;
} else {
return $value;
}
}
function to_database($dept) {
if ($dept instanceof Dept)
return array($dept->getName(), $dept->getId());
if (!is_array($dept)) {
$choices = $this->getChoices();
if (isset($choices[$dept]))
$dept = array($choices[$dept], $dept);
}
if (!$dept)
$dept = array();
return $dept;
if (!is_array($value))
$value = $this->getChoice($value);
if (is_array($value))
return implode(', ', $value);
function getChoice($value) {
$choices = $this->getChoices();
$selection = array();
if ($value && is_array($value)) {
$selection = $value;
} elseif (isset($choices[$value])) {
$selection[] = $choices[$value];
}
return $selection;
}
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(
'department' => array(__('Department'), 'DepartmentField'),
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
class SLAField extends ChoiceField {
function getWidget($widgetClass=false) {
$widget = parent::getWidget($widgetClass);
if ($widget->value instanceof SLA)
$widget->value = $widget->value->getId();
return $widget;
}
function hasIdValue() {
return true;
}
function getChoices($verbose=false) {
global $cfg;
$choices = array();
if (($depts = SLA::getSLAs()))
foreach ($depts as $id => $name)
$choices[$id] = $name;
return $choices;
}
function parse($id) {
return $this->to_php(null, $id);
}
function to_php($value, $id=false) {
if (is_array($id)) {
reset($id);
$id = key($id);
}
return $id;
}
function to_database($sla) {
return ($sla instanceof SLA)
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
: $sla;
}
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),
)),
);
}
}
class AssigneeField extends ChoiceField {
function getWidget($widgetClass=false) {
$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;
}
}
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() {