Newer
Older
function supportsQuickFilter() {
return true;
}
function getQuickFilterChoices() {
return $this->getChoices();
}
function applyQuickFilter($query, $qf_value, $name=false) {
global $thisstaff;
//special assignment quick filters
switch (true) {
case ($qf_value == 'assigned'):
case ($qf_value == '!assigned'):
$result = AssigneeChoiceField::getSearchQ($qf_value, $qf_value);
return $query->filter($result);
case (strpos($qf_value, 's') !== false):
case (strpos($qf_value, 't') !== false):
case ($qf_value == 'M'):
case ($qf_value == 'T'):
$value = array($qf_value => $qf_value);
$result = AssigneeChoiceField::getSearchQ('includes', $value);
return $query->filter($result);
break;
}
return $query->filter(array(
$name ?: $this->get('name') => $qf_value,
));
}
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
class NumericField extends FormField {
function getSearchMethods() {
return array(
'equal' => __('Equal'),
'greater' => __('Greater Than'),
'less' => __('Less Than'),
);
}
function getSearchMethodWidgets() {
return array(
'equal' => array('TextboxField', array(
'configuration' => array(
'validator' => 'number',
'size' => 6
),
)),
'greater' => array('TextboxField', array(
'configuration' => array(
'validator' => 'number',
'size' => 6
),
)),
'less' => array('TextboxField', array(
'configuration' => array(
'validator' => 'number',
'size' => 6
),
)),
);
}
}
static $widget = 'DatetimePickerWidget';
static function intervals($count=2, $i='') {
$intervals = array(
'i' => _N('minute', 'minutes', $count),
'h' => _N('hour', 'hours', $count),
'd' => _N('day','days', $count),
'w' => _N('week', 'weeks', $count),
'm' => _N('month', 'months', $count),
);
return $i ? $intervals[$i] : $intervals;
}
static function periods($period='') {
$periods = array(
'td' => __('Today'),
'yd' => __('Yesterday'),
'tw' => __('This Week'),
'tm' => __('This Month'),
'ty' => __('This Year'),
'lw' => __('Last Week'),
'lm' => __('Last Month'),
'ly' => __('Last Year'),
);
return $period ? $periods[$period] : $periods;
}
// Get php DatateTime object of the field - null if value is empty
function getDateTime($value=null) {
return Format::parseDateTime($value ?: $this->value);
}
// Get effective timezone for the field
function getTimeZone() {
global $cfg;
$config = $this->getConfiguration();
$timezone = new DateTimeZone($config['timezone'] ?:
$cfg->getTimezone());
return $timezone;
}
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
function getMinDateTime() {
if (!isset($this->min)) {
$config = $this->getConfiguration();
$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 (!is_numeric($value) && strtotime($value) <= 0)
if (!$value || !($datetime = Format::parseDateTime($value)))
return $this->format((int) $datetime->format('U'), $format);
// Force timezone if field has one.
if ($config['timezone']) {
$timezone = new DateTimezone($config['timezone']);
$datetime->setTimezone($timezone);
}
$datetime->getTimezone()->getName(),
$format);
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, $format=false) {
if (!$timestamp || $timestamp <= 0)
return '';
$config = $this->getConfiguration();
if ($config['time'])
$formatted = Format::datetime($timestamp, false, $format, $timezone);
$formatted = Format::date($timestamp, false, $format, $timezone);
$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 $datetime < $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 $datetime > $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(),
),
)),
'period' => array('ChoiceField', array(
'choices' => self::periods(),
)),
'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) && strlen($value) > 2)
$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':
$left = Format::parseDateTime($value['left']);
$right = Format::parseDateTime($value['right']);
// TODO: allow time selection for between
$left = $left->setTime(00, 00, 00);
$right = $right->setTime(23, 59, 59);
// Convert time to db timezone
$dbtz = new DateTimeZone($cfg->getDbTimezone());
$left->setTimezone($dbtz);
$right->setTimezone($dbtz);
"{$name}__gte" => $left->format('Y-m-d H:i:s'),
"{$name}__lte" => $right->format('Y-m-d H:i:s'),
));
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),
$tz = new DateTimeZone($cfg->getTimezone());
// Get the period range boundaries in user's tz
$period = Misc::date_range($value, Misc::gmtime('now'), $tz);
// Convert boundaries to db time
$dbtz = new DateTimeZone($cfg->getDbTimezone());
$start = $period->start->setTimezone($dbtz);
$end = $period->end->setTimezone($dbtz);
// Set the range
return new Q(array(
"{$name}__range" => array(
$start->format('Y-m-d H:i:s'),
$end->format('Y-m-d H:i:s')
)
));
break;
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');
case 'period':
return __('%1$s is %2$s');
default:
return parent::describeSearchMethod($method);
}
}
function describeSearch($method, $value, $name=false) {
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
$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));
case 'period':
return sprintf($desc, $name, self::periods($value) ?: $value);
default:
return parent::describeSearch($method, $value, $name);
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
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 {
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
var $priorities;
var $_choices;
function getPriorities() {
if (!isset($this->priorities))
$this->priorities = Priority::objects();
return $this->priorities;
}
function getPriority($id) {
if ($this->getPriorities() &&
($p=$this->priorities->findFirst(array('priority_id' =>
$id))))
return $p;
return Priority::lookup($id);
}
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) {
if (!isset($this->_choices)) {
$choices = array('' => '— '.__('Default').' —');
foreach ($this->getPriorities() as $p)
$choices[$p->getId()] = $p->getDesc();
$this->_choices = $choices;
}
return $this->_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;
return $this->getPriority($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);
}
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
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'),