Skip to content
Snippets Groups Projects
class.list.php 37.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • <?php
    /*********************************************************************
        class.list.php
    
        Custom List utils
    
        Jared Hancock <jared@osticket.com>
        Peter Rotich <peter@osticket.com>
        Copyright (c)  2014 osTicket
        http://www.osticket.com
    
        Released under the GNU General Public License WITHOUT ANY WARRANTY.
        See LICENSE.TXT for details.
    
        vim: expandtab sw=4 ts=4 sts=4:
    **********************************************************************/
    require_once(INCLUDE_DIR .'class.dynamic_forms.php');
    
    require_once(INCLUDE_DIR .'class.variable.php');
    
    
    /**
     * Interface for Custom Lists
     *
     * Custom lists are used to represent list of arbitrary data that can be
     * used as dropdown or typeahead selections in dynamic forms. This model
     * defines a list. The individual items are stored in the "Item" model.
     *
     */
    
    interface CustomList {
    
        function getId();
        function getName();
        function getPluralName();
    
        function getNumItems();
        function getAllItems();
        function getItems($criteria);
    
    
        function getItem($id);
        function addItem($vars, &$errors);
    
    
        function getForm(); // Config form
        function hasProperties();
    
        function getSortModes();
    
        function getSortMode();
    
        function getListOrderBy();
    
    
        function allowAdd();
        function hasAbbrev();
    
    
        function update($vars, &$errors);
        function delete();
    
        static function create($vars, &$errors);
    
        static function lookup($id);
    }
    
    /*
     * Custom list item interface
     */
    interface CustomListItem {
        function getId();
        function getValue();
        function getAbbrev();
        function getSortOrder();
    
        function getConfiguration();
    
        function getConfigurationForm($source=null);
    
    
    
        function isEnabled();
        function isDeletable();
        function isEnableable();
        function isInternal();
    
        function enable();
        function disable();
    
        function update($vars, &$errors);
        function delete();
    
     * Base class for Custom List handlers
    
     * Custom list handler extends custom list and might store data outside the
     * typical dynamic list store.
    
        function __construct($list) {
            $this->_list = $list;
    
            $rv = null;
            if ($this->_list && is_callable(array($this->_list, $name)))
                $rv = $args
                    ? call_user_func_array(array($this->_list, $name), $args)
                    : call_user_func(array($this->_list, $name));
    
        function __get($field) {
            return $this->_list->{$field};
        }
    
    
        function update($vars, &$errors) {
            return $this->_list->update($vars, $errors);
    
        abstract function getListOrderBy();
    
        abstract function getNumItems();
        abstract function getAllItems();
        abstract function getItems($criteria);
        abstract function getItem($id);
        abstract function addItem($vars, &$errors);
    
    }
    
    /**
     * Dynamic lists are Custom Lists solely defined by the user.
     *
     */
    class DynamicList extends VerySimpleModel implements CustomList {
    
        static $meta = array(
            'table' => LIST_TABLE,
            'ordering' => array('name'),
            'pk' => array('id'),
        );
    
        // Required fields
        static $fields = array('name', 'name_plural', 'sort_mode', 'notes');
    
    
        // Supported masks
        const MASK_EDIT     = 0x0001;
        const MASK_ADD      = 0x0002;
        const MASK_DELETE   = 0x0004;
        const MASK_ABBREV   = 0x0008;
    
    
        var $_items;
        var $_form;
    
    
        function getId() {
            return $this->get('id');
    
        }
    
        function getInfo() {
            return $this->ht;
        }
    
        function hasProperties() {
            return ($this->getForm() && $this->getForm()->getFields());
        }
    
        function getSortModes() {
    
            return array(
                'Alpha'     => __('Alphabetical'),
                '-Alpha'    => __('Alphabetical (Reversed)'),
                'SortCol'   => __('Manually Sorted')
            );
    
        function getSortMode() {
            return $this->sort_mode;
        }
    
    
        function getListOrderBy() {
    
            switch ($this->getSortMode()) {
    
                case 'Alpha':   return 'value';
                case '-Alpha':  return '-value';
                case 'SortCol': return 'sort';
            }
        }
    
        function getName() {
    
            return $this->getLocal('name');
    
        }
    
        function getPluralName() {
    
            if ($name = $this->getLocal('name_plural'))
    
                return $name;
            else
    
                return $this->getName() . 's';
    
        }
    
        function getItemCount() {
            return DynamicListItem::objects()->filter(array('list_id'=>$this->id))
                ->count();
        }
    
        function getNumItems() {
            return $this->getItemCount();
        }
    
        function getAllItems() {
             return DynamicListItem::objects()->filter(
                    array('list_id'=>$this->get('id')))
                    ->order_by($this->getListOrderBy());
        }
    
        function getItems($limit=false, $offset=false) {
            if (!$this->_items) {
                $this->_items = DynamicListItem::objects()->filter(
                    array('list_id'=>$this->get('id'),
                          'status__hasbit'=>DynamicListItem::ENABLED))
                    ->order_by($this->getListOrderBy());
                if ($limit)
                    $this->_items->limit($limit);
                if ($offset)
                    $this->_items->offset($offset);
            }
            return $this->_items;
        }
    
    
        function getItem($val, $extra=false) {
    
            $items = DynamicListItem::objects()->filter(
                    array('list_id' => $this->getId()));
    
    
            if (is_int($val))
    
                $items->filter(array('id' => $val));
    
            elseif ($extra)
                $items->filter(array('extra' => $val));
    
                $items->filter(array('value' => $val));
    
    
            return $items->first();
    
        }
    
        function addItem($vars, &$errors) {
    
            if (($item=$this->getItem($vars['value'])))
                return $item;
    
    
            $item = DynamicListItem::create(array(
    
    Peter Rotich's avatar
    Peter Rotich committed
                'status' => 1,
    
                'list_id' => $this->getId(),
                'sort'  => $vars['sort'],
                'value' => $vars['value'],
                'extra' => $vars['abbrev']
            ));
    
            $item->save();
    
            $this->_items = false;
    
            return $item;
        }
    
    
        function getConfigurationForm($autocreate=false) {
    
            if (!$this->_form) {
    
                $this->_form = DynamicForm::lookup(array('type'=>'L'.$this->getId()));
                if (!$this->_form
                        && $autocreate
                        && $this->createConfigurationForm())
                    return $this->getConfigurationForm(false);
    
            return $this->_form;
        }
    
    
        function isDeleteable() {
            return !$this->hasMask(static::MASK_DELETE);
        }
    
        function isEditable() {
            return !$this->hasMask(static::MASK_EDIT);
        }
    
        function allowAdd() {
            return !$this->hasMask(static::MASK_ADD);
        }
    
        function hasAbbrev() {
            return !$this->hasMask(static::MASK_ABBREV);
        }
    
        protected function hasMask($mask) {
            return 0 !== ($this->get('masks') & $mask);
        }
    
        protected function clearMask($mask) {
            return $this->set('masks', $this->get('masks') & ~$mask);
        }
    
        protected function setFlag($mask) {
            return $this->set('mask', $this->get('mask') | $mask);
        }
    
    
        private function createConfigurationForm() {
    
            $form = DynamicForm::create(array(
                        'type' => 'L'.$this->getId(),
                        'title' => $this->getName() . ' Properties'
            ));
    
            return $form->save(true);
        }
    
        function getForm($autocreate=true) {
            return $this->getConfigurationForm($autocreate);
    
            return JsonDataParser::parse($this->configuration);
    
        function getTranslateTag($subtag) {
            return _H(sprintf('list.%s.%s', $subtag, $this->id));
        }
        function getLocal($subtag) {
            $tag = $this->getTranslateTag($subtag);
            $T = CustomDataTranslation::translate($tag);
            return $T != $tag ? $T : $this->get($subtag);
        }
    
    
        function update($vars, &$errors) {
    
    
            $required = array();
            if ($this->isEditable())
                $required = array('name');
    
    
            foreach (static::$fields as $f) {
                if (in_array($f, $required) && !$vars[$f])
    
                    $errors[$f] = sprintf(__('%s is required'), mb_convert_case($f, MB_CASE_TITLE));
    
                elseif (isset($vars[$f]))
                    $this->set($f, $vars[$f]);
            }
    
            if ($errors)
                return false;
    
            return $this->save(true);
        }
    
        function save($refetch=false) {
            if (count($this->dirty))
                $this->set('updated', new SqlFunction('NOW'));
            if (isset($this->dirty['notes']))
                $this->notes = Format::sanitize($this->notes);
    
            return parent::save($refetch);
        }
    
        function delete() {
            $fields = DynamicFormField::objects()->filter(array(
                'type'=>'list-'.$this->id))->count();
    
    
            // Refuse to delete lists that are in use by fields
            if ($fields != 0)
                return false;
    
            if (!parent::delete())
    
                return false;
    
    
            if (($form = $this->getForm(false))) {
                $form->delete(false);
                $form->fields->delete();
            }
    
            return true;
    
        private function createForm() {
    
            $form = DynamicForm::create(array(
                        'type' => 'L'.$this->getId(),
                        'title' => $this->getName() . ' Properties'
            ));
    
            return $form->save(true);
        }
    
        static function add($vars, &$errors) {
    
            $required = array('name');
            $ht = array();
            foreach (static::$fields as $f) {
                if (in_array($f, $required) && !$vars[$f])
    
                    $errors[$f] = sprintf(__('%s is required'), mb_convert_case($f, MB_CASE_TITLE));
    
                elseif(isset($vars[$f]))
                    $ht[$f] = $vars[$f];
            }
    
            if (!$ht || $errors)
                return false;
    
            // Create the list && form
            if (!($list = self::create($ht))
                    || !$list->save(true)
                    || !$list->createConfigurationForm())
                return false;
    
            return $list;
        }
    
    
        static function create($ht=false, &$errors=array()) {
    
            if (isset($ht['configuration'])) {
                $ht['configuration'] = JsonDataEncoder::encode($ht['configuration']);
            }
    
    
            $inst = parent::create($ht);
            $inst->set('created', new SqlFunction('NOW'));
    
    
            if (isset($ht['properties'])) {
                $inst->save();
                $ht['properties']['type'] = 'L'.$inst->getId();
                $form = DynamicForm::create($ht['properties']);
                $form->save();
            }
    
            if (isset($ht['items'])) {
                $inst->save();
                foreach ($ht['items'] as $i) {
                    $i['list_id'] = $inst->getId();
                    $item = DynamicListItem::create($i);
                    $item->save();
                }
            }
    
    
        static function lookup($id) {
    
            if (!($list = parent::lookup($id)))
                return null;
    
            if (($config = $list->getConfiguration())) {
                if (($lh=$config['handler']) && class_exists($lh))
                    $list = new $lh($list);
            }
    
            return $list;
        }
    
    
        static function getSelections() {
            $selections = array();
            foreach (DynamicList::objects() as $list) {
                $selections['list-'.$list->id] =
                    array($list->getPluralName(),
                        SelectionField, $list->get('id'));
            }
            return $selections;
        }
    
    
        function importCsv($stream, $defaults=array()) {
            //Read the header (if any)
            $headers = array('value' => __('Value'), 'abbrev' => __('Abbreviation'));
            $form = $this->getConfigurationForm();
            $named_fields = $fields = array(
                'value' => new TextboxField(array(
                    'label' => __('Value'),
                    'name' => 'value',
                    'configuration' => array(
                        'length' => 0,
                    ),
                )),
                'abbrev' => new TextboxField(array(
                    'name' => 'abbrev',
                    'label' => __('Abbreviation'),
                    'configuration' => array(
                        'length' => 0,
                    ),
                )),
            );
            $all_fields = $form->getFields();
            $has_header = false;
            foreach ($all_fields as $f)
                if ($f->get('name'))
                    $named_fields[] = $f;
    
            if (!($data = fgetcsv($stream, 1000, ",")))
                return __('Whoops. Perhaps you meant to send some CSV records');
    
            foreach ($data as $D) {
                if (strcasecmp($D, 'value') === 0)
                    $has_header = true;
            }
            if ($has_header) {
                foreach ($data as $h) {
                    $found = false;
                    foreach ($all_fields as $f) {
                        if (in_array(mb_strtolower($h), array(
                                mb_strtolower($f->get('name')), mb_strtolower($f->get('label'))))) {
                            $found = true;
                            if (!$f->get('name'))
                                return sprintf(__(
                                    '%s: Field must have `variable` set to be imported'), $h);
                            $headers[$f->get('name')] = $f->get('label');
                            break;
                        }
                    }
                    if (!$found) {
                        $has_header = false;
                        if (count($data) == count($named_fields)) {
                            // Number of fields in the user form matches the number
                            // of fields in the data. Assume things line up
                            $headers = array();
                            foreach ($named_fields as $f)
                                $headers[$f->get('name')] = $f->get('label');
                            break;
                        }
                        else {
                            return sprintf(__('%s: Unable to map header to a property'), $h);
                        }
                    }
                }
            }
    
    
            // 'value' MUST be in the headers
    
            if (!isset($headers['value']))
                return __('CSV file must include `value` column');
    
            if (!$has_header)
                fseek($stream, 0);
    
            $items = array();
            $keys = array('value', 'abbrev');
            foreach ($headers as $h => $label) {
                if (!($f = $form->getField($h)))
                    continue;
    
                $name = $keys[] = $f->get('name');
                $fields[$name] = $f->getImpl();
            }
    
            // Add default fields (org_id, etc).
            foreach ($defaults as $key => $val) {
                // Don't apply defaults which are also being imported
                if (isset($header[$key]))
                    unset($defaults[$key]);
                $keys[] = $key;
            }
    
            while (($data = fgetcsv($stream, 1000, ",")) !== false) {
                if (count($data) == 1 && $data[0] == null)
                    // Skip empty rows
                    continue;
                elseif (count($data) != count($headers))
                    return sprintf(__('Bad data. Expected: %s'), implode(', ', $headers));
                // Validate according to field configuration
                $i = 0;
                foreach ($headers as $h => $label) {
                    $f = $fields[$h];
                    $T = $f->parse($data[$i]);
                    if ($f->validateEntry($T) && $f->errors())
                        return sprintf(__(
                            /* 1 will be a field label, and 2 will be error messages */
                            '%1$s: Invalid data: %2$s'),
                            $label, implode(', ', $f->errors()));
                    // Convert to database format
                    $data[$i] = $f->to_database($T);
                    $i++;
                }
                // Add default fields
                foreach ($defaults as $key => $val)
                    $data[] = $val;
    
                $items[] = $data;
            }
    
            foreach ($items as $u) {
                $vars = array_combine($keys, $u);
    
                $errors = array();
                $item = $this->addItem($vars, $errors);
                if (!$item || !$item->setConfiguration($vars, $errors))
    
                    return sprintf(__('Unable to import item: %s'),
                        print_r($vars, true));
            }
    
            return count($items);
        }
    
        function importFromPost($stuff, $extra=array()) {
            if (is_array($stuff) && !$stuff['error']) {
                // Properly detect Macintosh style line endings
                ini_set('auto_detect_line_endings', true);
                $stream = fopen($stuff['tmp_name'], 'r');
            }
            elseif ($stuff) {
                $stream = fopen('php://temp', 'w+');
                fwrite($stream, $stuff);
                rewind($stream);
            }
            else {
                return __('Unable to parse submitted items');
            }
    
            return self::importCsv($stream, $extra);
        }
    
    FormField::addFieldTypes(/* @trans */ 'Custom Lists', array('DynamicList', 'getSelections'));
    
    
    /**
     * Represents a single item in a dynamic list
     *
     * Fields:
     * value - (char * 255) Actual list item content
     * extra - (char * 255) Other values that represent the same item in the
     *      list, such as an abbreviation. In practice, should be a
     *      space-separated list of tokens which should hit this list item in a
     *      search
     * sort - (int) If sorting by this field, represents the numeric sort order
     *      that this item should come in the dropdown list
     */
    
    class DynamicListItem extends VerySimpleModel implements CustomListItem {
    
    
        static $meta = array(
            'table' => LIST_ITEM_TABLE,
            'pk' => array('id'),
            'joins' => array(
                'list' => array(
                    'null' => true,
                    'constraint' => array('list_id' => 'DynamicList.id'),
                ),
            ),
        );
    
        var $_config;
        var $_form;
    
    
        const ENABLED   = 0x0001;
        const INTERNAL  = 0x0002;
    
    
        protected function hasStatus($flag) {
            return 0 !== ($this->get('status') & $flag);
        }
    
        protected function clearStatus($flag) {
            return $this->set('status', $this->get('status') & ~$flag);
        }
    
        protected function setStatus($flag) {
            return $this->set('status', $this->get('status') | $flag);
        }
    
    
        function isInternal() {
    
            return  $this->hasStatus(self::INTERNAL);
    
        }
    
        function isEnableable() {
            return true;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function isDisableable() {
            return !$this->isInternal();
        }
    
    
        function isDeletable() {
            return !$this->isInternal();
        }
    
    
        function isEnabled() {
            return $this->hasStatus(self::ENABLED);
        }
    
        function enable() {
            $this->setStatus(self::ENABLED);
        }
        function disable() {
            $this->clearStatus(self::ENABLED);
        }
    
        function getId() {
            return $this->get('id');
        }
    
    
        function getListId() {
            return $this->get('list_id');
        }
    
    
        function getValue() {
    
            return $this->getLocal('value');
    
        }
    
        function getAbbrev() {
            return $this->get('extra');
        }
    
        function getSortOrder() {
            return $this->get('sort');
        }
    
        function getConfiguration() {
            if (!$this->_config) {
                $this->_config = $this->get('properties');
                if (is_string($this->_config))
                    $this->_config = JsonDataParser::parse($this->_config);
                elseif (!$this->_config)
                    $this->_config = array();
            }
            return $this->_config;
        }
    
    
        function setConfiguration($vars, &$errors=array()) {
    
            $config = array();
    
            foreach ($this->getConfigurationForm($vars)->getFields() as $field) {
    
                $config[$field->get('id')] = $field->to_php($field->getClean());
    
                $errors = array_merge($errors, $field->errors());
            }
    
    
            if ($errors)
                return false;
    
            $this->set('properties', JsonDataEncoder::encode($config));
    
            return $this->save();
    
        function getConfigurationForm($source=null) {
    
            if (!$this->_form) {
    
                $config = $this->getConfiguration();
    
                $this->_form = DynamicForm::lookup(
    
                    array('type'=>'L'.$this->get('list_id')))->getForm($source);
                if (!$source && $config) {
    
                    $fields = $this->_form->getFields();
                    foreach ($fields as $f) {
    
                        $name = $f->get('id');
                        if (isset($config[$name]))
                            $f->value = $f->to_php($config[$name]);
                        else if ($f->get('default'))
                            $f->value = $f->get('default');
                    }
                }
    
            return $this->_form;
        }
    
    
        function getForm() {
            return $this->getConfigurationForm();
        }
    
    
        function getVar($name) {
            $config = $this->getConfiguration();
            $name = mb_strtolower($name);
            foreach ($this->getConfigurationForm()->getFields() as $field) {
                if (mb_strtolower($field->get('name')) == $name)
    
                    return $field->asVar($config[$field->get('id')]);
    
        function getFilterData() {
            $data = array();
            foreach ($this->getConfigurationForm()->getFields() as $F) {
                $data['.'.$F->get('id')] = $F->toString($F->value);
            }
    
            $data['.abb'] = (string) $this->get('extra');
    
        function getTranslateTag($subtag) {
            return _H(sprintf('listitem.%s.%s', $subtag, $this->id));
        }
        function getLocal($subtag) {
            $tag = $this->getTranslateTag($subtag);
            $T = CustomDataTranslation::translate($tag);
            return $T != $tag ? $T : $this->get($subtag);
        }
    
    
        function toString() {
            return $this->get('value');
        }
    
        function __toString() {
            return $this->toString();
        }
    
    
        function update($vars, &$errors=array()) {
    
            if (!$vars['value']) {
    
                $errors['value-'.$this->getId()] = __('Value required');
    
                return false;
            }
    
    
            foreach (array(
                        'sort' => 'sort',
                        'value' => 'value',
                        'abbrev' => 'extra') as $k => $v) {
                if (isset($vars[$k]))
                    $this->set($v, $vars[$k]);
            }
    
    
            return $this->save();
    
        }
    
        function delete() {
            # Don't really delete, just unset the list_id to un-associate it with
            # the list
            $this->set('list_id', null);
            return $this->save();
        }
    
    
        static function create($ht=false, &$errors=array()) {
    
            if (isset($ht['properties']) && is_array($ht['properties']))
                $ht['properties'] = JsonDataEncoder::encode($ht['properties']);
    
            $inst = parent::create($ht);
            $inst->save(true);
    
            // Auto-config properties if any
            if ($ht['configuration'] && is_array($ht['configuration'])) {
                $config = $inst->getConfiguration();
                if (($form = $inst->getConfigurationForm())) {
                    foreach ($form->getFields() as $f) {
                        if (!isset($ht['configuration'][$f->get('name')]))
                            continue;
    
    
                        $config[$f->get('id')] =
                            $ht['configuration'][$f->get('name')];
    
                    }
                }
    
                $inst->set('properties', JsonDataEncoder::encode($config));
            }
    
            return $inst;
        }
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    /*
     * Ticket status List
     *
     */
    
    
    class TicketStatusList extends CustomListHandler {
    
    Peter Rotich's avatar
    Peter Rotich committed
        // Fields of interest we need to store
        static $config_fields = array('sort_mode', 'notes');
    
    
        var $_items;
        var $_form;
    
    
        function getListOrderBy() {
            switch ($this->getSortMode()) {
                case 'Alpha':   return 'name';
                case '-Alpha':  return '-name';
                case 'SortCol': return 'sort';
            }
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getNumItems() {
    
    Peter Rotich's avatar
    Peter Rotich committed
            return TicketStatus::objects()->count();
    
        function getAllItems() {
            if (!$this->_items)
                $this->_items = TicketStatus::objects()->order_by($this->getListOrderBy());
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
            return $this->_items;
    
        function getItems($criteria = array()) {
    
            // Default to only enabled items
            if (!isset($criteria['enabled']))
                $criteria['enabled'] = true;
    
            $filters =  array();
            if ($criteria['enabled'])
                $filters['mode__hasbit'] = TicketStatus::ENABLED;
            if ($criteria['states'] && is_array($criteria['states']))
                $filters['state__in'] = $criteria['states'];
    
            else
                $filters['state__isnull'] = false;
    
    
            $items = TicketStatus::objects();
            if ($filters)
                $items->filter($filters);
            if ($criteria['limit'])
                $items->limit($criteria['limit']);
            if ($criteria['offset'])
                $items->offset($criteria['offset']);
    
            $items->order_by($this->getListOrderBy());
    
            return $items;
    
        function getItem($val) {
    
            if (!is_int($val))
                $val = array('name' => $val);
    
    
             return TicketStatus::lookup($val, $this);
    
    Peter Rotich's avatar
    Peter Rotich committed
        }
    
        function addItem($vars, &$errors) {
    
    Peter Rotich's avatar
    Peter Rotich committed
            $item = TicketStatus::create(array(
    
    Peter Rotich's avatar
    Peter Rotich committed
                'sort'  => $vars['sort'],
                'name' => $vars['value'],
            ));
            $item->save();
    
            $this->_items = false;
    
            return $item;
        }
    
    
        static function getStatuses($criteria=array()) {
    
    Peter Rotich's avatar
    Peter Rotich committed
    
            $statuses = array();
            if (($list = DynamicList::lookup(
                            array('type' => 'ticket-status'))))
    
                $statuses = $list->getItems($criteria);
    
    Peter Rotich's avatar
    Peter Rotich committed
    
            return $statuses;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        static function __load() {
            require_once(INCLUDE_DIR.'class.i18n.php');
    
            $i18n = new Internationalization();
    
    Peter Rotich's avatar
    Peter Rotich committed
            $tpl = $i18n->getTemplate('list.yaml');
    
    Peter Rotich's avatar
    Peter Rotich committed
            foreach ($tpl->getData() as $f) {
    
    Peter Rotich's avatar
    Peter Rotich committed
                if ($f['type'] == 'ticket-status') {
                    $list = DynamicList::create($f);
                    $list->save();
    
    Peter Rotich's avatar
    Peter Rotich committed
            if (!$list || !($o=DynamicForm::objects()->filter(
                            array('type'=>'L'.$list->getId()))))
    
    Peter Rotich's avatar
    Peter Rotich committed
                return false;
    
            // Create default statuses
            if (($statuses = $i18n->getTemplate('ticket_status.yaml')->getData()))
                foreach ($statuses as $status)
                    TicketStatus::__create($status);
    
            return $o[0];
        }
    
    Peter Rotich's avatar
    Peter Rotich committed
    }
    
    class TicketStatus
    extends VerySimpleModel
    implements CustomListItem, TemplateVariable {
    
    Peter Rotich's avatar
    Peter Rotich committed
    
        static $meta = array(
            'table' => TICKET_STATUS_TABLE,
            'ordering' => array('name'),
            'pk' => array('id'),
    
            'joins' => array(
                'tickets' => array(
                    'reverse' => 'TicketModel.status',
                    )
            )
    
    Peter Rotich's avatar
    Peter Rotich committed
        var $_form;
        var $_settings;
    
        var $_properties;
    
        const ENABLED   = 0x0001;
        const INTERNAL  = 0x0002; // Forbid deletion or name and status change.
    
    Peter Rotich's avatar
    Peter Rotich committed
    
        protected function hasFlag($field, $flag) {
            return 0 !== ($this->get($field) & $flag);
        }
    
        protected function clearFlag($field, $flag) {
            return $this->set($field, $this->get($field) & ~$flag);
        }
    
        protected function setFlag($field, $flag) {
            return $this->set($field, $this->get($field) | $flag);
        }
    
    
            return ($this->get('properties'));
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getForm() {
    
            if (!$this->_form && $this->_list) {
                $this->_form = DynamicForm::lookup(
    
                    array('type'=>'L'.$this->_list->getId()));
    
    Peter Rotich's avatar
    Peter Rotich committed
            }
            return $this->_form;
        }
    
    
        function getExtraConfigOptions($source=null) {