Skip to content
Snippets Groups Projects
class.task.php 47.2 KiB
Newer Older
  • Learn to ignore specific revisions
  •             return call_user_func(array($this, 'get'.ucfirst($tag)));
    
            switch(mb_strtolower($tag)) {
            case 'phone':
            case 'phone_number':
                return $this->getPhoneNumber();
    
            case 'ticket_link':
                if ($ticket = $this->ticket) {
                    return sprintf('%s/scp/tickets.php?id=%d#tasks',
                        $cfg->getBaseUrl(), $ticket->getId());
                }
    
            case 'staff_link':
                return sprintf('%s/scp/tasks.php?id=%d', $cfg->getBaseUrl(), $this->getId());
            case 'create_date':
                return new FormattedDate($this->getCreateDate());
             case 'due_date':
                if ($due = $this->getEstDueDate())
                    return new FormattedDate($due);
                break;
            case 'close_date':
                if ($this->isClosed())
                    return new FormattedDate($this->getCloseDate());
                break;
            case 'last_update':
                return new FormattedDate($this->last_update);
            default:
                if (isset($this->_answers[$tag]))
                    // The answer object is retrieved here which will
                    // automatically invoke the toString() method when the
                    // answer is coerced into text
                    return $this->_answers[$tag];
            }
            return false;
        }
    
        static function getVarScope() {
            $base = array(
    
                'assigned' => __('Assigned Agent / Team'),
    
                'close_date' => array(
                    'class' => 'FormattedDate', 'desc' => __('Date Closed'),
                ),
                'create_date' => array(
    
                    'class' => 'FormattedDate', 'desc' => __('Date Created'),
    
                ),
                'dept' => array(
                    'class' => 'Dept', 'desc' => __('Department'),
                ),
                'due_date' => array(
                    'class' => 'FormattedDate', 'desc' => __('Due Date'),
                ),
    
                'number' => __('Task Number'),
    
                'recipients' => array(
                    'class' => 'UserList', 'desc' => __('List of all recipient names'),
                ),
                'status' => __('Status'),
                'staff' => array(
                    'class' => 'Staff', 'desc' => __('Assigned/closing agent'),
                ),
                'subject' => 'Subject',
                'team' => array(
                    'class' => 'Team', 'desc' => __('Assigned/closing team'),
                ),
                'thread' => array(
                    'class' => 'TaskThread', 'desc' => __('Task Thread'),
                ),
    
                'staff_link' => __('Link to view the task'),
                'ticket_link' => __('Link to view the task inside the ticket'),
    
                'last_update' => array(
                    'class' => 'FormattedDate', 'desc' => __('Time of last update'),
                ),
            );
    
            $extra = VariableReplacer::compileFormScope(TaskForm::getInstance());
            return $base + $extra;
        }
    
        function onActivity($vars, $alert=true) {
            global $cfg, $thisstaff;
    
            if (!$alert // Check if alert is enabled
                || !$cfg->alertONTaskActivity()
                || !($dept=$this->getDept())
                || !($email=$cfg->getAlertEmail())
                || !($tpl = $dept->getTemplate())
                || !($msg=$tpl->getTaskActivityAlertMsgTemplate())
            ) {
                return;
            }
    
            // Alert recipients
            $recipients = array();
            //Last respondent.
            if ($cfg->alertLastRespondentONTaskActivity())
                $recipients[] = $this->getLastRespondent();
    
            // Assigned staff / team
            if ($cfg->alertAssignedONTaskActivity()) {
                if (isset($vars['assignee'])
                        && $vars['assignee'] instanceof Staff)
                     $recipients[] = $vars['assignee'];
                elseif ($this->isOpen() && ($assignee = $this->getStaff()))
                    $recipients[] = $assignee;
    
                if ($team = $this->getTeam())
                    $recipients = array_merge($recipients, $team->getMembers());
            }
    
            // Dept manager
            if ($cfg->alertDeptManagerONTaskActivity() && $dept && $dept->getManagerId())
                $recipients[] = $dept->getManager();
    
            $options = array();
            $staffId = $thisstaff ? $thisstaff->getId() : 0;
            if ($vars['threadentry'] && $vars['threadentry'] instanceof ThreadEntry) {
    
                $options = array('thread' => $vars['threadentry']);
    
    
                // Activity details
                if (!$vars['message'])
                    $vars['message'] = $vars['threadentry'];
    
                // Staff doing the activity
                $staffId = $vars['threadentry']->getStaffId() ?: $staffId;
            }
    
            $msg = $this->replaceVars($msg->asArray(),
                    array(
                        'note' => $vars['threadentry'], // For compatibility
                        'activity' => $vars['activity'],
                        'message' => $vars['message']));
    
            $isClosed = $this->isClosed();
            $sentlist=array();
            foreach ($recipients as $k=>$staff) {
                if (!is_object($staff)
                    // Don't bother vacationing staff.
                    || !$staff->isAvailable()
                    // No need to alert the poster!
                    || $staffId == $staff->getId()
                    // No duplicates.
                    || isset($sentlist[$staff->getEmail()])
                    // Make sure staff has access to task
                    || ($isClosed && !$this->checkStaffPerm($staff))
                ) {
                    continue;
                }
                $alert = $this->replaceVars($msg, array('recipient' => $staff));
                $email->sendAlert($staff, $alert['subj'], $alert['body'], null, $options);
                $sentlist[$staff->getEmail()] = 1;
            }
    
        }
    
        /*
         * Notify collaborators on response or new message
         *
         */
        function  notifyCollaborators($entry, $vars = array()) {
            global $cfg;
    
            if (!$entry instanceof ThreadEntry
                || !($recipients=$this->getThread()->getParticipants())
                || !($dept=$this->getDept())
                || !($tpl=$dept->getTemplate())
                || !($msg=$tpl->getTaskActivityNoticeMsgTemplate())
                || !($email=$dept->getEmail())
            ) {
                return;
            }
    
            // Who posted the entry?
            $skip = array();
    
    Peter Rotich's avatar
    Peter Rotich committed
            if ($entry instanceof MessageThreadEntry) {
    
                $poster = $entry->getUser();
                // Skip the person who sent in the message
                $skip[$entry->getUserId()] = 1;
                // Skip all the other recipients of the message
                foreach ($entry->getAllEmailRecipients() as $R) {
                    foreach ($recipients as $R2) {
                        if (0 === strcasecmp($R2->getEmail(), $R->mailbox.'@'.$R->host)) {
                            $skip[$R2->getUserId()] = true;
                            break;
                        }
                    }
                }
            } else {
                $poster = $entry->getStaff();
            }
    
            $vars = array_merge($vars, array(
                'message' => (string) $entry,
                'poster' => $poster ?: _S('A collaborator'),
                )
            );
    
            $msg = $this->replaceVars($msg->asArray(), $vars);
    
            $attachments = $cfg->emailAttachments()?$entry->getAttachments():array();
    
            $options = array('thread' => $entry);
    
    
            foreach ($recipients as $recipient) {
                // Skip folks who have already been included on this part of
                // the conversation
                if (isset($skip[$recipient->getUserId()]))
                    continue;
                $notice = $this->replaceVars($msg, array('recipient' => $recipient));
                $email->send($recipient, $notice['subj'], $notice['body'], $attachments,
                    $options);
            }
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function update($forms, $vars, &$errors) {
            global $thisstaff;
    
    
            if (!$forms || !$this->checkStaffPerm($thisstaff, Task::PERM_EDIT))
                return false;
    
    
            foreach ($forms as $form) {
                $form->setSource($vars);
                if (!$form->isValid(function($f) {
                    return $f->isVisibleToStaff() && $f->isEditableToStaff();
                }, array('mode'=>'edit'))) {
                    $errors = array_merge($errors, $form->errors());
                }
            }
    
            if ($errors)
                return false;
    
            // Update dynamic meta-data
            $changes = array();
            foreach ($forms as $f) {
                $changes += $f->getChanges();
                $f->save();
            }
    
    
            if ($vars['note']) {
                $_errors = array();
                $this->postNote(array(
                            'note' => $vars['note'],
    
                            'title' => _S('Task Updated'),
    
    Peter Rotich's avatar
    Peter Rotich committed
                            ),
                        $_errors,
                        $thisstaff);
            }
    
    
    JediKev's avatar
    JediKev committed
            $this->updated = SqlFunction::NOW();
    
    
    Peter Rotich's avatar
    Peter Rotich committed
            if ($changes)
                $this->logEvent('edited', array('fields' => $changes));
    
            Signal::send('model.updated', $this);
            return $this->save();
        }
    
    
        /* static routines */
    
    Peter Rotich's avatar
    Peter Rotich committed
        static function lookupIdByNumber($number) {
    
    
    Peter Rotich's avatar
    Peter Rotich committed
            if (($task = self::lookup(array('number' => $number))))
                return $task->getId();
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        }
    
        static function isNumberUnique($number) {
            return !self::lookupIdByNumber($number);
        }
    
    
        static function create($vars=false) {
    
    Peter Rotich's avatar
    Peter Rotich committed
            global $thisstaff, $cfg;
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
    Peter Rotich's avatar
    Peter Rotich committed
            if (!is_array($vars)
                    || !$thisstaff
                    || !$thisstaff->hasPerm(Task::PERM_CREATE, false))
    
            $task = new static(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'),
            ));
    
            if ($vars['internal_formdata']['dept_id'])
                $task->dept_id = $vars['internal_formdata']['dept_id'];
            if ($vars['internal_formdata']['duedate'])
    
    Kevin Thorne's avatar
    Kevin Thorne committed
    	    $task->duedate = date('Y-m-d G:i', Misc::dbtime($vars['internal_formdata']['duedate']));
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
    Peter Rotich's avatar
    Peter Rotich committed
            if (!$task->save(true))
                return false;
    
    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);
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
            $task->logEvent('created', null, $thisstaff);
    
            // Get role for the dept
            $role = $thisstaff->getRole($task->dept_id);
            // Assignment
    
            $assignee = $vars['internal_formdata']['assignee'];
            if ($assignee
    
    Peter Rotich's avatar
    Peter Rotich committed
                    // skip assignment if the user doesn't have perm.
                    && $role->hasPerm(Task::PERM_ASSIGN)) {
                $_errors = array();
    
                $assigneeId = sprintf('%s%d',
                        ($assignee  instanceof Staff) ? 's' : 't',
                        $assignee->getId());
    
                $form = AssignmentForm::instantiate(array('assignee' => $assigneeId));
    
    
    Peter Rotich's avatar
    Peter Rotich committed
                $task->assign($form, $_errors);
            }
    
    
            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 tasks.
    
    Peter Rotich's avatar
    Peter Rotich committed
                $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
    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',
    
    Peter Rotich's avatar
    Peter Rotich committed
                'object_type' => ObjectModel::OBJECT_TYPE_TASK,
    
    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];
    
            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, $options=array()) {
    
            if (!isset(static::$internalForm))
    
                static::$internalForm = new TaskInternalForm($source, $options);
    
    
            return static::$internalForm;
        }
    
    }
    
    class TaskInternalForm
    extends AbstractForm {
        static $layout = 'GridFormLayout';
    
        function buildFields() {
    
            $fields = array(
    
                    'dept_id' => new DepartmentField(array(
                        'id'=>1,
                        'label' => __('Department'),
                        'required' => true,
    
    Peter Rotich's avatar
    Peter Rotich committed
                        'layout' => new GridFluidCell(6),
    
    Peter Rotich's avatar
    Peter Rotich committed
                    'assignee' => new AssigneeField(array(
    
                        'id'=>2,
                        'label' => __('Assignee'),
                        'required' => false,
    
    Peter Rotich's avatar
    Peter Rotich committed
                        'layout' => new GridFluidCell(6),
    
                        )),
                    'duedate'  =>  new DatetimeField(array(
                        'id' => 3,
                        'label' => __('Due Date'),
                        'required' => false,
                        'configuration' => array(
                            'min' => Misc::gmtime(),
                            'time' => true,
    
    Peter Rotich's avatar
    Peter Rotich committed
                            'gmt' => false,
    
                            'future' => true,
                            ),
                        )),
    
                );
    
    
            $mode = @$this->options['mode'];
            if ($mode && $mode == 'edit') {
                unset($fields['dept_id']);
    
    Peter Rotich's avatar
    Peter Rotich committed
                unset($fields['assignee']);
    
    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']);
    
    Peter Rotich's avatar
    Peter Rotich committed
            return MessageThreadEntry::add($vars, $errors);
    
        static function create($task=false) {
            assert($task !== false);
    
    
    Peter Rotich's avatar
    Peter Rotich committed
            $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;