Skip to content
Snippets Groups Projects
class.task.php 44.5 KiB
Newer Older
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,
            ),
            'entries' => array(
                'constraint' => array(
                    "'A'" => 'DynamicFormEntry.object_type',
                    'id' => 'DynamicFormEntry.object_id',
                ),
                'list' => true,
            ),

            '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_REPLY    = 'task.reply';
    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_REPLY => array(
                'title' =>
                /* @trans */ 'Post Reply',
                'desc'  =>
                /* @trans */ 'Ability to post task update'),
            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;
    }

Peter Rotich's avatar
Peter Rotich committed
    function getCloseDate() {
Peter Rotich's avatar
Peter Rotich committed
        return $this->isClosed() ? $this->closed : '';
Peter Rotich's avatar
Peter Rotich committed
    function isOpen() {
        return $this->hasFlag(self::ISOPEN);
    }

    function isClosed() {
        return !$this->isOpen();
    }

    function isCloseable() {

        if ($this->isClosed())
            return true;

        $warning = null;
        if ($this->getMissingRequiredFields()) {
            $warning = sprintf(
                    __( '%1$s is missing data on %2$s one or more required fields %3$s and cannot be closed'),
                    __('This task'),
                    '', '');
        }

        return $warning ?: true;
    }

Peter Rotich's avatar
Peter Rotich committed
    protected function close() {
Peter Rotich's avatar
Peter Rotich committed
        return $this->clearFlag(self::ISOPEN);
    }

Peter Rotich's avatar
Peter Rotich committed
    protected function reopen() {
Peter Rotich's avatar
Peter Rotich committed
        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 RestrictedAccess, Threadable {
Peter Rotich's avatar
Peter Rotich committed
    var $form;
    var $entry;

Peter Rotich's avatar
Peter Rotich committed
    var $_thread;
    var $_entries;
    var $_answers;

    var $lastrespondent;

    function __onload() {
        $this->loadDynamicData();
    }

    function loadDynamicData() {
        if (!isset($this->_answers)) {
            $this->_answers = array();
            foreach (DynamicFormEntryAnswer::objects()
                ->filter(array(
                    'entry__object_id' => $this->getId(),
                    'entry__object_type' => ObjectModel::OBJECT_TYPE_TASK
                )) as $answer
            ) {
                $tag = mb_strtolower($answer->field->name)
                    ?: 'field.' . $answer->field->id;
                    $this->_answers[$tag] = $answer;
            }
        }
        return $this->_answers;
    }
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 getLastRespondent() {

        if (!isset($this->lastrespondent)) {
            $this->lastrespondent = Staff::objects()
                ->filter(array(
                'staff_id' => static::objects()
                    ->filter(array(
                        'thread__entries__type' => 'R',
                        'thread__entries__staff_id__gt' => 0
                    ))
                    ->values_flat('thread__entries__staff_id')
                    ->order_by('-thread__entries__id')
                    ->limit(1)
                ))
                ->first()
                ?: false;
        }

        return $this->lastrespondent;
    }

Peter Rotich's avatar
Peter Rotich committed
    function getDynamicFields($criteria=array()) {

        $fields = DynamicFormField::objects()->filter(array(
Peter Rotich's avatar
Peter Rotich committed
                    'id__in' => $this->entries
                    ->filter($criteria)
                ->values_flat('answers__field_id')));

        return ($fields && count($fields)) ? $fields : array();
Peter Rotich's avatar
Peter Rotich committed
    }

    function getMissingRequiredFields() {
Peter Rotich's avatar
Peter Rotich committed
        return $this->getDynamicFields(array(
                    'answers__field__flags__hasbit' => DynamicFormField::FLAG_ENABLED,
                    'answers__field__flags__hasbit' => DynamicFormField::FLAG_CLOSE_REQUIRED,
                    'answers__value__isnull' => true,
                    ));
Peter Rotich's avatar
Peter Rotich committed
    function getParticipants() {
        $participants = array();
        foreach ($this->getThread()->collaborators as $c)
            $participants[] = $c->getName();

        return $participants ? implode(', ', $participants) : ' ';
    }

    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;
    }

Loading
Loading full blame...