Newer
Older
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) {
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
$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);
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
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
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->minus(SqlInterval::DAY(7))),
]);
case 'n30':
return $query->filter([
"{$name}__range" => array($now, $now->minus(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 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);
}
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
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);
$addNew = true;
}
}
$active_depts = array();
if($current_id)
$active_depts = Dept::objects()
->filter(array('flags__hasbit' => Dept::FLAG_ACTIVE))
->values('id', '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
$active[$current_id] = $current_name;
aydreeihn
committed
foreach ($depts as $id => $name) {
$choices[$id] = $name;
if(!array_key_exists($id, $active) && $current_id)
unset($choices[$id]);
}
}
if($addNew)
$choices[':new:'] = '— '.__('Add New').' —';
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
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($dept) {
return ($dept instanceof Dept)
? array($dept->getName(), $dept->getId())
: $dept;
}
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(
'department' => array(__('Department'), DepartmentField),
);
});
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
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)
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
: $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;
}
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
}
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']);