Skip to content
Snippets Groups Projects
class.forms.php 158 KiB
Newer Older
  • Learn to ignore specific revisions
  • Peter Rotich's avatar
    Peter Rotich committed
            if ($timestamp <= 0)
                return '';
    
            return $this->format($timestamp);
        }
    
        function asVar($value, $id=false) {
    
    Jared Hancock's avatar
    Jared Hancock committed
            global $cfg;
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
    Peter Rotich's avatar
    Peter Rotich committed
                return null;
    
            $datetime = $this->getDateTime($value);
            $config = $this->getConfiguration();
            if (!$config['gmt'] || !$config['time'])
                $timezone  = $datetime->getTimezone()->getName();
    
    Peter Rotich's avatar
    Peter Rotich committed
                $timezone  = false;
    
            return  new FormattedDate($value, array(
                        'timezone'  =>  $timezone,
                        'format'    =>  $config['time'] ? 'long' : 'short'
                        )
                    );
        }
    
        function asVarType() {
            return 'FormattedDate';
    
    Jared Hancock's avatar
    Jared Hancock committed
        function getConfigurationOptions() {
            return array(
                'time' => new BooleanField(array(
    
                    'id'=>1, 'label'=>__('Time'), 'required'=>false, 'default'=>false,
    
    Jared Hancock's avatar
    Jared Hancock committed
                    'configuration'=>array(
    
                        '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
                    ),
                    )),
    
    Jared Hancock's avatar
    Jared Hancock committed
                'gmt' => new BooleanField(array(
    
                    'id'=>3, 'label'=>__('Timezone Aware'), 'required'=>false,
    
    Jared Hancock's avatar
    Jared Hancock committed
                    'configuration'=>array(
    
                        'desc'=>__("Show date/time relative to user's timezone")))),
    
    Jared Hancock's avatar
    Jared Hancock committed
                'min' => new DatetimeField(array(
    
                    'id'=>4, 'label'=>__('Earliest'), 'required'=>false,
    
                    'hint'=>__('Earliest date selectable'))),
    
    Jared Hancock's avatar
    Jared Hancock committed
                'max' => new DatetimeField(array(
    
                    'id'=>5, 'label'=>__('Latest'), 'required'=>false,
    
                    'default'=>null, 'hint'=>__('Latest date selectable'))),
    
    Jared Hancock's avatar
    Jared Hancock committed
                'future' => new BooleanField(array(
    
                    'id'=>6, 'label'=>__('Allow Future Dates'), 'required'=>false,
    
    Jared Hancock's avatar
    Jared Hancock committed
                    'default'=>true, 'configuration'=>array(
    
                        'desc'=>__('Allow entries into the future' /* Used in the date field */)),
                )),
    
    Jared Hancock's avatar
    Jared Hancock committed
            );
        }
    
        function validateEntry($value) {
    
    Peter Rotich's avatar
    Peter Rotich committed
            global $cfg;
    
    
    Jared Hancock's avatar
    Jared Hancock committed
            $config = $this->getConfiguration();
            parent::validateEntry($value);
    
    Peter Rotich's avatar
    Peter Rotich committed
            if (!$value || !($datetime = Format::parseDatetime($value)))
                return;
    
            // Parse value to DateTime object
            $val = Format::parseDatetime($value);
            // Get configured min/max (if any)
            $min = $this->getMinDatetime();
            $max = $this->getMaxDatetime();
    
            if (!$val) {
    
                $this->_errors[] = __('Enter a valid date');
    
    Peter Rotich's avatar
    Peter Rotich committed
            } 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'),
    
                'nequal' =>     __('not 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;
    
            $nday_form = function() {
                $intervals = array(
                    'i' => _N('minute', 'minutes', 5),
                    'h' => _N('hour', 'hours', 5),
                    'd' => _N('day','days', 5),
                    'w' => _N('week', 'weeks', 5),
                    'm' => _N('month', 'months', 5),
                );
                return array(
                    'until' => new TextboxField(array(
                        'configuration' => array('validator'=>'number', 'size'=>4))
                    ),
                    'int' => new ChoiceField(array(
                        'default' => 'd',
                        'choices' => $intervals,
                    )),
                );
            };
    
            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();
    
            $value = is_int($value)
    
                ? DateTime::createFromFormat('U', !$config['gmt'] ? Misc::gmtime($value) : $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 'after':
                return new Q(array("{$name}__gte" => $value));
    
            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]
    
                        : $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),
    
                $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) {
            if ($method === 'between') {
                $l = $this->toString($value['left']);
                $r = $this->toString($value['right']);
                $desc = $this->describeSearchMethod($method);
                return sprintf($desc, $name, $l, $r);
            }
            return parent::describeSearch($method, $value, $name);
        }
    
    
        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;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getConfiguration() {
            global $cfg;
            $config = parent::getConfiguration();
    
            $config['html'] = (bool) ($cfg && $cfg->isRichTextEnabled());
    
    Peter Rotich's avatar
    Peter Rotich committed
            return $config;
        }
    
    
        function getConfigurationOptions() {
            global $cfg;
    
            $attachments = new FileUploadField();
    
            $fileupload_config = $attachments->getConfigurationOptions();
    
    Peter Rotich's avatar
    Peter Rotich committed
            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(),
    
    Peter Rotich's avatar
    Peter Rotich committed
                        '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'];
        }
    
    Peter Rotich's avatar
    Peter Rotich committed
    
        function getWidget($widgetClass=false) {
    
            if ($hint = $this->getLocal('hint'))
    
    Peter Rotich's avatar
    Peter Rotich committed
                $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() {
    
        return array(
    
            'priority' => array(__('Priority Level'), PriorityField),
    
    Peter Rotich's avatar
    Peter Rotich committed
    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;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function whatChanged($before, $after) {
            return FormField::whatChanged($before, $after);
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        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) {
    
            global $cfg;
    
            $choices = array();
            if (($depts = Dept::getDepartments()))
                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($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),
        );
    });
    
    
    
    Peter Rotich's avatar
    Peter Rotich committed
    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)
                ? array($sla->getName(), $slas->getId())
                : $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 {
    
    Peter Rotich's avatar
    Peter Rotich committed
        var $_choices = null;
    
    Peter Rotich's avatar
    Peter Rotich committed
        var $_criteria = null;
    
    
        function getWidget($widgetClass=false) {
            $widget = parent::getWidget($widgetClass);
    
            if (is_object($widget->value))
                $widget->value = $widget->value->getId();
            return $widget;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        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) {
    
            global $cfg;
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
            if (!isset($this->_choices)) {
    
    Peter Rotich's avatar
    Peter Rotich committed
                $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()))
    
    Peter Rotich's avatar
    Peter Rotich committed
                        foreach ($members as $member)
                            $agents[$member->getId()] = $member;
                } else {
                    $agents = Staff::getStaffMembers($criteria);
                }
    
    
                foreach ($agents as $id => $name)
    
    Peter Rotich's avatar
    Peter Rotich committed
                next($choices);
                $T = current($choices);
    
                if (($teams = Team::getActiveTeams()))
    
    Peter Rotich's avatar
    Peter Rotich committed
                    foreach ($teams as $id => $name)
                        $T['t'.$id] = $name;
    
    Peter Rotich's avatar
    Peter Rotich committed
                $this->_choices = $choices;
            }
    
            return $this->_choices;
    
    Peter Rotich's avatar
    Peter Rotich committed
        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) {
    
    Peter Rotich's avatar
    Peter Rotich committed
            $type = '';
    
            if (is_array($id)) {
                reset($id);
                $id = key($id);
    
    Peter Rotich's avatar
    Peter Rotich committed
                $type = $id[0];
                $id = substr($id, 1);
    
    Peter Rotich's avatar
    Peter Rotich committed
            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() {
            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 {
    
    
        static $_states = array(
    
                'open' => array(
    
                    'name' => /* @trans, @context "ticket state name" */ 'Open',
                    'verb' => /* @trans, @context "ticket state action" */ 'Open'
    
                    ),
                'closed' => array(
    
                    'name' => /* @trans, @context "ticket state name" */ 'Closed',
                    'verb' => /* @trans, @context "ticket state action" */ 'Close'
    
        // Private states
        static $_privatestates = array(
    
                'archived' => array(
    
                    'name' => /* @trans, @context "ticket state name" */ 'Archived',
                    'verb' => /* @trans, @context "ticket state action" */ 'Archive'
    
                    ),
                'deleted'  => array(
    
                    '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) {
    
            static $_choices;
    
    
            $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']);
    
                $this->ht['default'] =  '';
            }
    
            return $_choices;
        }
    
        function getChoice($state) {
    
            if ($state && is_array($state))
                $state = key($state);
    
            if (isset(static::$_states[$state]))
    
                return _P('ticket state name', static::$_states[$state]['name']);
    
    
            if (isset(static::$_privatestates[$state]))
    
                return _P('ticket state name', static::$_privatestates[$state]['name']);
    
            return $state;
    
        }
    
        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),
                )),
            );
        }
    
    
        static function getVerb($state) {
    
            if (isset(static::$_states[$state]))
    
                return _P('ticket state action', static::$_states[$state]['verb']);
    
    
            if (isset(static::$_privatestates[$state]))
    
                return _P('ticket state action', static::$_privatestates[$state]['verb']);
    
    }
    FormField::addFieldTypes('Dynamic Fields', function() {
        return array(
            'state' => array('Ticket State', TicketStateField, false),
        );
    });
    
    class TicketFlagField extends ChoiceField {
    
        // Supported flags (TODO: move to configurable custom list)
        static $_flags = array(
                'onhold' => array(
                    'flag' => 1,
                    'name' => 'Onhold',
                    'states' => array('open'),
                    ),
                'overdue' => array(
                    'flag' => 2,
                    'name' => 'Overdue',
                    'states' => array('open'),
                    ),
                'answered' => array(
                    'flag' => 4,
                    'name' => 'Answered',
                    'states' => array('open'),
                    )
                );
    
        var $_choices;
    
        function hasIdValue() {
            return true;
        }
    
        function isChangeable() {
            return true;
        }
    
    
        function getChoices($verbose=false) {
    
            $this->ht['default'] =  '';
    
            if (!$this->_choices) {
                foreach (static::$_flags as $k => $v)
                    $this->_choices[$k] = $v['name'];
            }
    
            return $this->_choices;
        }
    
        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('Dynamic Fields', function() {
        return array(
            'flags' => array('Ticket Flags', TicketFlagField, false),
        );
    });
    
    
    class FileUploadField extends FormField {
        static $widget = 'FileUploadWidget';
    
        protected $attachments;