Skip to content
Snippets Groups Projects
class.task.php 23.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • Peter Rotich's avatar
    Peter Rotich committed
    <?php
    
    /*********************************************************************
        class.task.php
    
        Task
    
        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:
    **********************************************************************/
    
    include_once INCLUDE_DIR.'class.role.php';
    
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    class TaskModel extends VerySimpleModel {
        static $meta = array(
            'table' => TASK_TABLE,
            'pk' => array('id'),
            'joins' => array(
    
                'dept' => array(
                    'constraint' => array('dept_id' => 'Dept.id'),
    
    Peter Rotich's avatar
    Peter Rotich committed
                ),
    
                'lock' => array(
                    'constraint' => array('lock_id' => 'Lock.lock_id'),
                    'null' => true,
                ),
    
                'staff' => array(
                    'constraint' => array('staff_id' => 'Staff.staff_id'),
                    'null' => true,
                ),
    
    Peter Rotich's avatar
    Peter Rotich committed
                'team' => array(
                    'constraint' => array('team_id' => 'Team.team_id'),
                    'null' => true,
                ),
                'thread' => array(
                    'constraint' => array(
    
    Peter Rotich's avatar
    Peter Rotich committed
                        'id'  => 'TaskThread.object_id',
                        "'A'" => 'TaskThread.object_type',
    
    Peter Rotich's avatar
    Peter Rotich committed
                    ),
                    'list' => false,
                    'null' => false,
                ),
                'cdata' => array(
                    'constraint' => array('id' => 'TaskCData.task_id'),
                    'list' => false,
                ),
    
                'ticket' => array(
                    'constraint' => array(
                        'object_type' => "'T'",
                        'object_id' => 'Ticket.ticket_id',
                    ),
                    'null' => true,
                ),
    
    Peter Rotich's avatar
    Peter Rotich committed
        );
    
    
        const PERM_CREATE   = 'task.create';
        const PERM_EDIT     = 'task.edit';
        const PERM_ASSIGN   = 'task.assign';
        const PERM_TRANSFER = 'task.transfer';
        const PERM_CLOSE    = 'task.close';
        const PERM_DELETE   = 'task.delete';
    
        static protected $perms = array(
                self::PERM_CREATE    => array(
    
    Peter Rotich's avatar
    Peter Rotich committed
                    'title' =>
    
                    /* @trans */ 'Create',
    
    Peter Rotich's avatar
    Peter Rotich committed
                    'desc'  =>
    
                    /* @trans */ 'Ability to create tasks'),
                self::PERM_EDIT      => array(
    
    Peter Rotich's avatar
    Peter Rotich committed
                    'title' =>
    
                    /* @trans */ 'Edit',
    
    Peter Rotich's avatar
    Peter Rotich committed
                    'desc'  =>
    
                    /* @trans */ 'Ability to edit tasks'),
                self::PERM_ASSIGN    => array(
    
    Peter Rotich's avatar
    Peter Rotich committed
                    'title' =>
    
                    /* @trans */ 'Assign',
    
    Peter Rotich's avatar
    Peter Rotich committed
                    'desc'  =>
    
                    /* @trans */ 'Ability to assign tasks to agents or teams'),
                self::PERM_TRANSFER  => array(
    
    Peter Rotich's avatar
    Peter Rotich committed
                    'title' =>
    
                    /* @trans */ 'Transfer',
    
    Peter Rotich's avatar
    Peter Rotich committed
                    'desc'  =>
    
                    /* @trans */ 'Ability to transfer tasks between departments'),
                self::PERM_CLOSE     => array(
    
    Peter Rotich's avatar
    Peter Rotich committed
                    'title' =>
    
                    /* @trans */ 'Close',
    
    Peter Rotich's avatar
    Peter Rotich committed
                    'desc'  =>
    
                    /* @trans */ 'Ability to close tasks'),
                self::PERM_DELETE    => array(
    
    Peter Rotich's avatar
    Peter Rotich committed
                    'title' =>
    
                    /* @trans */ 'Delete',
    
    Peter Rotich's avatar
    Peter Rotich committed
                    'desc'  =>
    
                    /* @trans */ 'Ability to delete tasks'),
                );
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        const ISOPEN    = 0x0001;
        const ISOVERDUE = 0x0002;
    
    
        protected function hasFlag($flag) {
            return ($this->get('flags') & $flag) !== 0;
        }
    
        protected function clearFlag($flag) {
            return $this->set('flags', $this->get('flags') & ~$flag);
        }
    
        protected function setFlag($flag) {
            return $this->set('flags', $this->get('flags') | $flag);
        }
    
        function getId() {
            return $this->id;
        }
    
        function getNumber() {
            return $this->number;
        }
    
        function getStaffId() {
            return $this->staff_id;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getStaff() {
            return $this->staff;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getTeamId() {
            return $this->team_id;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getTeam() {
            return $this->team;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getDeptId() {
            return $this->dept_id;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getDept() {
            return $this->dept;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getCreateDate() {
            return $this->created;
        }
    
        function getDueDate() {
            return $this->duedate;
        }
    
        function isOpen() {
            return $this->hasFlag(self::ISOPEN);
        }
    
        function isClosed() {
            return !$this->isOpen();
        }
    
        function close() {
            return $this->clearFlag(self::ISOPEN);
        }
    
        function reopen() {
            return $this->setFlag(self::ISOPEN);
        }
    
        function isAssigned() {
            return ($this->isOpen() && ($this->getStaffId() || $this->getTeamId()));
        }
    
        function isOverdue() {
            return $this->hasFlag(self::ISOVERDUE);
        }
    
    
        static function getPermissions() {
            return self::$perms;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
    }
    
    
    RolePermission::register(/* @trans */ 'Tasks', TaskModel::getPermissions());
    
    
    
    class Task extends TaskModel implements Threadable {
    
    Peter Rotich's avatar
    Peter Rotich committed
        var $form;
        var $entry;
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        var $_thread;
    
        var $_entries;
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getStatus() {
    
    Peter Rotich's avatar
    Peter Rotich committed
            return $this->isOpen() ? __('Open') : __('Completed');
    
    Peter Rotich's avatar
    Peter Rotich committed
        }
    
        function getTitle() {
            return $this->__cdata('title', ObjectModel::OBJECT_TYPE_TASK);
        }
    
    
        function checkStaffPerm($staff, $perm=null) {
    
            // Must be a valid staff
            if (!$staff instanceof Staff && !($staff=Staff::lookup($staff)))
                return false;
    
            // Check access based on department or assignment
            if (!$staff->canAccessDept($this->getDeptId())
                    && $this->isOpen()
                    && $staff->getId() != $this->getStaffId()
                    && !$staff->isTeamMember($this->getTeamId()))
                return false;
    
            // At this point staff has access unless a specific permission is
            // requested
            if ($perm === null)
                return true;
    
            // Permission check requested -- get role.
            if (!($role=$staff->getRole($this->getDeptId())))
                return false;
    
            // Check permission based on the effective role
            return $role->hasPerm($perm);
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getAssignee() {
    
            if (!$this->isOpen() || !$this->isAssigned())
                return false;
    
            if ($this->staff)
                return $this->staff;
    
            if ($this->team)
                return $this->team;
    
            return null;
        }
    
        function getAssigneeId() {
    
            if (!($assignee=$this->getAssignee()))
                return null;
    
            $id = '';
            if ($assignee instanceof Staff)
                $id = 's'.$assignee->getId();
            elseif ($assignee instanceof Team)
                $id = 't'.$assignee->getId();
    
            return $id;
        }
    
    
        function getAssignees() {
    
            $assignees=array();
            if ($this->staff)
                $assignees[] = $this->staff->getName();
    
            //Add team assignment
    
    Peter Rotich's avatar
    Peter Rotich committed
            if ($this->team)
    
                $assignees[] = $this->team->getName();
    
            return $assignees;
        }
    
        function getAssigned($glue='/') {
            $assignees = $this->getAssignees();
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
            return $assignees ? implode($glue, $assignees):'';
    
    Peter Rotich's avatar
    Peter Rotich committed
        }
    
    
        function getThreadId() {
            return $this->thread->getId();
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getThread() {
    
    Peter Rotich's avatar
    Peter Rotich committed
            return $this->thread;
    
    Peter Rotich's avatar
    Peter Rotich committed
        }
    
        function getThreadEntry($id) {
            return $this->getThread()->getEntry($id);
        }
    
    
        function getThreadEntries($type=false) {
            $thread = $this->getThread()->getEntries();
            if ($type && is_array($type))
                $thread->filter(array('type__in' => $type));
            return $thread;
    
    Peter Rotich's avatar
    Peter Rotich committed
        }
    
    
        function postThreadEntry($type, $vars, $options=array()) {
    
            $errors = array();
    
            $poster = isset($options['poster']) ? $options['poster'] : null;
            $alert = isset($options['alert']) ? $options['alert'] : true;
    
            switch ($type) {
            case 'N':
            default:
    
                return $this->postNote($vars, $errors, $poster, $alert);
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getForm() {
            if (!isset($this->form)) {
                // Look for the entry first
                if ($this->form = DynamicFormEntry::lookup(
                            array('object_type' => ObjectModel::OBJECT_TYPE_TASK))) {
                    return $this->form;
                }
                // Make sure the form is in the database
                elseif (!($this->form = DynamicForm::lookup(
                                array('type' => ObjectModel::OBJECT_TYPE_TASK)))) {
                    $this->__loadDefaultForm();
                    return $this->getForm();
                }
                // Create an entry to be saved later
                $this->form = $this->form->instanciate();
                $this->form->object_type = ObjectModel::OBJECT_TYPE_TASK;
            }
    
            return $this->form;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getAssignmentForm($source=null) {
    
            if (!$source)
                $source = array('assignee' => array($this->getAssigneeId()));
    
            return AssignmentForm::instantiate($source,
                    array('dept' => $this->getDept()));
        }
    
        function getTransferForm($source=null) {
    
            if (!$source)
                $source = array('dept' => array($this->getDeptId()));
    
            return TransferForm::instantiate($source);
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function addDynamicData($data) {
    
            $tf = TaskForm::getInstance($this->id, true);
            foreach ($tf->getFields() as $f)
                if (isset($data[$f->get('name')]))
                    $tf->setAnswer($f->get('name'), $data[$f->get('name')]);
    
            $tf->save();
    
            return $tf;
        }
    
        function getDynamicData($create=true) {
            if (!isset($this->_entries)) {
                $this->_entries = DynamicFormEntry::forObject($this->id,
                        ObjectModel::OBJECT_TYPE_TASK)->all();
                if (!$this->_entries && $create) {
                    $f = TaskForm::getInstance($this->id, true);
                    $f->save();
                    $this->_entries[] = $f;
                }
            }
    
            return $this->_entries ?: array();
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function setStatus($status, $comments='') {
            global $thisstaff;
    
            switch($status) {
            case 'open':
                if ($this->isOpen())
                    return false;
    
                $this->reopen();
                break;
            case 'closed':
                if ($this->isClosed())
                    return false;
                $this->close();
                break;
            default:
                return false;
            }
    
            $this->save(true);
            if ($comments) {
                $errors = array();
                $this->postNote(array(
                            'note' => $comments,
                            'title' => sprintf(
                                __('Status changed to %s'),
                                $this->getStatus())
                            ),
                        $errors,
                        $thisstaff);
            }
    
            return true;
        }
    
    Peter Rotich's avatar
    Peter Rotich committed
    
        function to_json() {
    
            $info = array(
                    'id'  => $this->getId(),
                    'title' => $this->getTitle()
                    );
    
            return JsonDataEncoder::encode($info);
        }
    
        function __cdata($field, $ftype=null) {
    
            foreach ($this->getDynamicData() as $e) {
                // Make sure the form type matches
    
    Peter Rotich's avatar
    Peter Rotich committed
                if (!$e->form
                        || ($ftype && $ftype != $e->form->get('type')))
    
    Peter Rotich's avatar
    Peter Rotich committed
                    continue;
    
                // Get the named field and return the answer
    
    Peter Rotich's avatar
    Peter Rotich committed
                if ($a = $e->getAnswer($field))
                    return $a;
    
    Peter Rotich's avatar
    Peter Rotich committed
            }
    
            return null;
        }
    
        function __toString() {
            return (string) $this->getTitle();
        }
    
        /* util routines */
    
    Peter Rotich's avatar
    Peter Rotich committed
        function assign(AssignmentForm $form, &$errors, $alert=true) {
    
            $assignee = $form->getAssignee();
            if ($assignee instanceof Staff) {
                if ($this->getStaffId() == $assignee->getId()) {
                    $errors['assignee'] = sprintf(__('%s already assigned to %s'),
                            __('Task'),
                            __('the agent')
                            );
                } elseif(!$assignee->isAvailable()) {
                    $errors['assignee'] = __('Agent is unavailable for assignment');
                } else {
                    $this->staff_id = $assignee->getId();
                }
            } elseif ($assignee instanceof Team) {
                if ($this->getTeamId() == $assignee->getId()) {
                    $errors['assignee'] = sprintf(__('%s already assigned to %s'),
                            __('Task'),
                            __('the team')
                            );
                } else {
                    $this->team_id = $assignee->getId();
    
    Peter Rotich's avatar
    Peter Rotich committed
                }
            } else {
                $errors['assignee'] = __('Unknown assignee');
            }
    
    Peter Rotich's avatar
    Peter Rotich committed
            if ($errors || !$this->save(true))
    
                return false;
    
    
    Peter Rotich's avatar
    Peter Rotich committed
            $this->onAssignment($assignee,
                    $form->getField('comments')->getClean(),
                    $alert);
    
            return true;
        }
    
        function onAssignment($assignee, $note='', $alert=true) {
            global $thisstaff;
    
            if (!is_object($assignee))
                return false;
    
            $assigner = $thisstaff ?: __('SYSTEM (Auto Assignment)');
            //Assignment completed... post internal note.
    
            $title = sprintf(__('Task assigned to %s'),
    
    Peter Rotich's avatar
    Peter Rotich committed
                    (string) $assignee);
    
            if (!$note) {
    
                $note = $title;
                $title = '';
            }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
            $errors = array();
            $note = $this->postNote(
    
                    array('note' => $note, 'title' => $title),
                    $errors,
    
    Peter Rotich's avatar
    Peter Rotich committed
                    $assigner,
                    false);
    
            // Send alerts out
            if (!$alert)
                return false;
    
    Peter Rotich's avatar
    Peter Rotich committed
        function transfer(TransferForm $form, &$errors, $alert=true) {
    
            global $thisstaff;
    
    
    Peter Rotich's avatar
    Peter Rotich committed
            $dept = $form->getDept();
            if (!$dept || !($dept instanceof Dept))
                $errors['dept'] = __('Department selection required');
    
            elseif ($dept->getid() == $this->getDeptId())
    
    Peter Rotich's avatar
    Peter Rotich committed
                $errors['dept'] = __('Task already in the department');
    
            else
                $this->dept_id = $dept->getId();
    
            if ($errors || !$this->save())
                return false;
    
            // Transfer completed... post internal note.
    
    Peter Rotich's avatar
    Peter Rotich committed
            $title = sprintf(__('%s transferred to %s department'),
                    __('Task'),
    
                    $dept->getName());
    
    Peter Rotich's avatar
    Peter Rotich committed
    
            $note = $form->getField('comments')->getClean();
            if (!$note) {
    
                $note = $title;
                $title = '';
            }
    
    Peter Rotich's avatar
    Peter Rotich committed
            $_errors = array();
            $note = $this->postNote(
    
                    array('note' => $note, 'title' => $title),
    
    Peter Rotich's avatar
    Peter Rotich committed
                    $_errors, $thisstaff, false);
    
            // Send alerts if requested && enabled.
            if (!$alert)
                return true;
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function postNote($vars, &$errors, $poster='', $alert=true) {
            global $cfg, $thisstaff;
    
            $vars['staffId'] = 0;
            $vars['poster'] = 'SYSTEM';
            if ($poster && is_object($poster)) {
                $vars['staffId'] = $poster->getId();
                $vars['poster'] = $poster->getName();
            } elseif ($poster) { //string
                $vars['poster'] = $poster;
            }
    
            if (!($note=$this->getThread()->addNote($vars, $errors)))
                return null;
    
    
    Peter Rotich's avatar
    Peter Rotich committed
            if (isset($vars['task_status']))
                $this->setStatus($vars['task_status']);
    
    Peter Rotich's avatar
    Peter Rotich committed
    
            return $note;
        }
    
        static function lookupIdByNumber($number) {
            $sql = 'SELECT id FROM '.TASK_TABLE
                  .' WHERE `number`='.db_input($number);
            list($id) = db_fetch_row(db_query($sql));
    
            return $id;
        }
    
        static function isNumberUnique($number) {
            return !self::lookupIdByNumber($number);
        }
    
    
        static function create($vars=false) {
    
            global $cfg;
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
            if (!is_array($vars))
                return null;
    
    
            $task = parent::create(array(
                'flags' => self::ISOPEN,
                'object_id' => $vars['object_id'],
                'object_type' => $vars['object_type'],
                'number' => $cfg->getNewTaskNumber(),
                'created' => new SqlFunction('NOW'),
                'updated' => new SqlFunction('NOW'),
            ));
            // Save internal fields.
            if ($vars['internal_formdata']['staff_id'])
                $task->staff_id = $vars['internal_formdata']['staff_id'];
            if ($vars['internal_formdata']['dept_id'])
                $task->dept_id = $vars['internal_formdata']['dept_id'];
            if ($vars['internal_formdata']['duedate'])
                $task->duedate = $vars['internal_formdata']['duedate'];
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
            $task->save(true);
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
            // Add dynamic data
            $task->addDynamicData($vars['default_formdata']);
    
    Peter Rotich's avatar
    Peter Rotich committed
    
            // Create a thread + message.
            $thread = TaskThread::create($task);
            $thread->addDescription($vars);
    
            Signal::send('task.created', $task);
    
    Peter Rotich's avatar
    Peter Rotich committed
    
            return $task;
        }
    
    
        function delete($comments='') {
            global $ost, $thisstaff;
    
            $thread = $this->getThread();
    
            if (!parent::delete())
                return false;
    
            $thread->delete();
    
            Draft::deleteForNamespace('task.%.' . $this->getId());
    
            foreach (DynamicFormEntry::forObject($this->getId(), ObjectModel::OBJECT_TYPE_TASK) as $form)
                $form->delete();
    
            // Log delete
            $log = sprintf(__('Task #%1$s deleted by %2$s'),
                    $this->getNumber(),
                    $thisstaff ? $thisstaff->getName() : __('SYSTEM'));
    
            if ($comments)
                $log .= sprintf('<hr>%s', $comments);
    
            $ost->logDebug(
                    sprintf( __('Task #%s deleted'), $this->getNumber()),
                    $log);
    
            return true;
    
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        static function __loadDefaultForm() {
    
            require_once INCLUDE_DIR.'class.i18n.php';
    
            $i18n = new Internationalization();
            $tpl = $i18n->getTemplate('form.yaml');
            foreach ($tpl->getData() as $f) {
                if ($f['type'] == ObjectModel::OBJECT_TYPE_TASK) {
                    $form = DynamicForm::create($f);
                    $form->save();
                    break;
                }
            }
        }
    
    Peter Rotich's avatar
    Peter Rotich committed
    
        /* Quick staff's stats */
        static function getStaffStats($staff) {
            global $cfg;
    
            /* Unknown or invalid staff */
            if (!$staff
                    || (!is_object($staff) && !($staff=Staff::lookup($staff)))
                    || !$staff->isStaff())
                return null;
    
            $where = array('(task.staff_id='.db_input($staff->getId())
                        .sprintf(' AND task.flags & %d != 0 ', TaskModel::ISOPEN)
                        .') ');
            $where2 = '';
    
            if(($teams=$staff->getTeams()))
    
    Peter Rotich's avatar
    Peter Rotich committed
                $where[] = ' ( task.team_id IN('.implode(',', db_input(array_filter($teams)))
    
    Peter Rotich's avatar
    Peter Rotich committed
                            .') AND '
                            .sprintf('task.flags & %d != 0 ', TaskModel::ISOPEN)
                            .')';
    
            if(!$staff->showAssignedOnly() && ($depts=$staff->getDepts())) //Staff with limited access just see Assigned tickets.
                $where[] = 'task.dept_id IN('.implode(',', db_input($depts)).') ';
    
            $where = implode(' OR ', $where);
            if ($where) $where = 'AND ( '.$where.' ) ';
    
            $sql =  'SELECT \'open\', count(task.id ) AS tasks '
                    .'FROM ' . TASK_TABLE . ' task '
                    . sprintf(' WHERE task.flags & %d != 0 ', TaskModel::ISOPEN)
                    . $where . $where2
    
                    .'UNION SELECT \'overdue\', count( task.id ) AS tasks '
                    .'FROM ' . TASK_TABLE . ' task '
                    . sprintf(' WHERE task.flags & %d != 0 ', TaskModel::ISOPEN)
                    . sprintf(' AND task.flags & %d != 0 ', TaskModel::ISOVERDUE)
                    . $where
    
                    .'UNION SELECT \'assigned\', count( task.id ) AS tasks '
                    .'FROM ' . TASK_TABLE . ' task '
                    . sprintf(' WHERE task.flags & %d != 0 ', TaskModel::ISOPEN)
                    .'AND task.staff_id = ' . db_input($staff->getId()) . ' '
                    . $where
    
                    .'UNION SELECT \'closed\', count( task.id ) AS tasks '
                    .'FROM ' . TASK_TABLE . ' task '
                    . sprintf(' WHERE task.flags & %d = 0 ', TaskModel::ISOPEN)
                    . $where;
    
            $res = db_query($sql);
            $stats = array();
            while ($row = db_fetch_row($res))
                $stats[$row[0]] = $row[1];
    
            return $stats;
        }
    
    Peter Rotich's avatar
    Peter Rotich committed
    
        static function getAgentActions($agent, $options=array()) {
            if (!$agent)
                return;
    
            require STAFFINC_DIR.'templates/tasks-actions.tmpl.php';
        }
    
    Peter Rotich's avatar
    Peter Rotich committed
    }
    
    
    class TaskCData extends VerySimpleModel {
        static $meta = array(
            'pk' => array('task_id'),
            'table' => TASK_CDATA_TABLE,
            'joins' => array(
                'task' => array(
                    'constraint' => array('task_id' => 'TaskModel.task_id'),
                ),
            ),
        );
    
    Peter Rotich's avatar
    Peter Rotich committed
    }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
    Peter Rotich's avatar
    Peter Rotich committed
    class TaskForm extends DynamicForm {
        static $instance;
    
        static $defaultForm;
        static $internalForm;
    
        static $forms;
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        static $cdata = array(
                'table' => TASK_CDATA_TABLE,
                'object_id' => 'task_id',
                'object_type' => 'A',
            );
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        static function objects() {
            $os = parent::objects();
            return $os->filter(array('type'=>ObjectModel::OBJECT_TYPE_TASK));
        }
    
        static function getDefaultForm() {
    
            if (!isset(static::$defaultForm)) {
    
    Peter Rotich's avatar
    Peter Rotich committed
                if (($o = static::objects()) && $o[0])
    
                    static::$defaultForm = $o[0];
    
    Peter Rotich's avatar
    Peter Rotich committed
            }
    
    
            return static::$defaultForm;
    
    Peter Rotich's avatar
    Peter Rotich committed
        }
    
        static function getInstance($object_id=0, $new=false) {
            if ($new || !isset(static::$instance))
                static::$instance = static::getDefaultForm()->instanciate();
    
            static::$instance->object_type = ObjectModel::OBJECT_TYPE_TASK;
    
            if ($object_id)
                static::$instance->object_id = $object_id;
    
            return static::$instance;
        }
    
    
        static function getInternalForm($source=null) {
            if (!isset(static::$internalForm))
    
                static::$internalForm = new SimpleForm(self::getInternalFields(), $source);
    
    
            return static::$internalForm;
        }
    
        static function getInternalFields() {
            return array(
                    'dept_id' => new DepartmentField(array(
                        'id'=>1,
                        'label' => __('Department'),
                        'flags' => hexdec(0X450F3),
                        'required' => true,
                        )),
                    'staff_id' => new AssigneeField(array(
                        'id'=>2,
                        'label' => __('Assignee'),
                        'flags' => hexdec(0X450F3),
                        'required' => false,
                        )),
                    'duedate'  =>  new DatetimeField(array(
                        'id' => 3,
                        'label' => __('Due Date'),
                        'flags' => hexdec(0X450B3),
                        'required' => false,
                        'configuration' => array(
                            'min' => Misc::gmtime(),
                            'time' => true,
                            'gmt' => true,
                            'future' => true,
                            ),
                        )),
    
                );
        }
    
    Peter Rotich's avatar
    Peter Rotich committed
    }
    
    // Task thread class
    class TaskThread extends ObjectThread {
    
        function addDescription($vars, &$errors=array()) {
    
            $vars['threadId'] = $this->getId();
            $vars['message'] = $vars['description'];
            unset($vars['description']);
    
            return MessageThreadEntry::create($vars, $errors);
        }
    
        static function create($task) {
            $id = is_object($task) ? $task->getId() : $task;
    
            $thread = parent::create(array(
    
    Peter Rotich's avatar
    Peter Rotich committed
                        'object_id' => $id,
                        'object_type' => ObjectModel::OBJECT_TYPE_TASK
                        ));
    
            if ($thread->save())
                return $thread;