Skip to content
Snippets Groups Projects
class.staff.php 49 KiB
Newer Older
  • Learn to ignore specific revisions
  • Jared Hancock's avatar
    Jared Hancock committed
    <?php
    /*********************************************************************
        class.staff.php
    
        Everything about staff.
    
        Peter Rotich <peter@osticket.com>
    
        Copyright (c)  2006-2013 osTicket
    
    Jared Hancock's avatar
    Jared Hancock committed
        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.ticket.php');
    
    Jared Hancock's avatar
    Jared Hancock committed
    include_once(INCLUDE_DIR.'class.dept.php');
    
    include_once(INCLUDE_DIR.'class.error.php');
    
    Jared Hancock's avatar
    Jared Hancock committed
    include_once(INCLUDE_DIR.'class.team.php');
    
    include_once(INCLUDE_DIR.'class.role.php');
    
    Jared Hancock's avatar
    Jared Hancock committed
    include_once(INCLUDE_DIR.'class.passwd.php');
    
    include_once(INCLUDE_DIR.'class.user.php');
    
    Jared Hancock's avatar
    Jared Hancock committed
    include_once(INCLUDE_DIR.'class.auth.php');
    
    Jared Hancock's avatar
    Jared Hancock committed
    class Staff extends VerySimpleModel
    
    implements AuthenticatedUser, EmailContact, TemplateVariable, Searchable {
    
        static $meta = array(
            'table' => STAFF_TABLE,
            'pk' => array('staff_id'),
            'joins' => array(
                'dept' => array(
    
                    'constraint' => array('dept_id' => 'Dept.id'),
    
                'role' => array(
                    'constraint' => array('role_id' => 'Role.id'),
                ),
    
    Jared Hancock's avatar
    Jared Hancock committed
                'dept_access' => array(
                    'reverse' => 'StaffDeptAccess.staff',
    
    Jared Hancock's avatar
    Jared Hancock committed
                'teams' => array(
    
    Peter Rotich's avatar
    Peter Rotich committed
                    'reverse' => 'TeamMember.staff',
    
    Jared Hancock's avatar
    Jared Hancock committed
                ),
    
    Jared Hancock's avatar
    Jared Hancock committed
        var $authkey;
    
    Peter Rotich's avatar
    Peter Rotich committed
        var $departments;
    
    Jared Hancock's avatar
    Jared Hancock committed
        var $stats = array();
        var $_extra;
    
    Jared Hancock's avatar
    Jared Hancock committed
        var $passwd_change;
    
        var $_roles = null;
    
    Peter Rotich's avatar
    Peter Rotich committed
        var $_teams = null;
    
    Peter Rotich's avatar
    Peter Rotich committed
        var $_config = null;
    
    Jared Hancock's avatar
    Jared Hancock committed
        var $_perm;
    
    Jared Hancock's avatar
    Jared Hancock committed
        function __onload() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            // WE have to patch info here to support upgrading from old versions.
    
            $time = null;
            if (isset($this->passwdreset) && $this->passwdreset)
                $time=strtotime($this->passwdreset);
            elseif (isset($this->added) && $this->added)
                $time=strtotime($this->added);
    
            if ($time)
    
    Jared Hancock's avatar
    Jared Hancock committed
                $this->passwd_change = time()-$time; //XXX: check timezone issues.
    
    Peter Rotich's avatar
    Peter Rotich committed
        function get($field, $default=false) {
    
            // Autoload config if not loaded already
            if (!isset($this->_config))
                $this->getConfig();
    
            if (isset($this->_config[$field]))
                return $this->_config[$field];
    
    
    aydreeihn's avatar
    aydreeihn committed
            try {
                return parent::get($field, $default);
            } catch (Exception $e) {}
    
    Peter Rotich's avatar
    Peter Rotich committed
        }
    
        function getConfig() {
    
            if (!isset($this->_config) && $this->getId()) {
                $_config = new Config('staff.'.$this->getId(),
                        // Defaults
                        array(
                            'default_from_name' => '',
    
    Peter Rotich's avatar
    Peter Rotich committed
                            'datetime_format'   => '',
                            'thread_view_order' => '',
    
                            'default_ticket_queue_id' => 0,
    
    Peter Rotich's avatar
    Peter Rotich committed
                            ));
                $this->_config = $_config->getInfo();
            }
    
            return $this->_config;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function __toString() {
            return (string) $this->getName();
        }
    
    
        function asVar() {
    
    Peter Rotich's avatar
    Peter Rotich committed
            return $this->__toString();
    
        static function getVarScope() {
          return array(
    
            'dept' => array('class' => 'Dept', 'desc' => __('Department')),
            'email' => __('Email Address'),
    
            'name' => array(
    
              'class' => 'PersonsName', 'desc' => __('Agent name'),
    
            'mobile' => __('Mobile Number'),
            'phone' => __('Phone Number'),
            'signature' => __('Signature'),
    
            'timezone' => "Agent's configured timezone",
            'username' => 'Access username',
    
        function getVar($tag) {
            switch ($tag) {
            case 'mobile':
                return Format::phone($this->ht['mobile']);
            case 'phone':
                return Format::phone($this->ht['phone']);
            }
        }
    
    
        static function getSearchableFields() {
            return array(
                'email' => new TextboxField(array(
                    'label' => __('Email Address'),
                )),
            );
        }
    
    
        static function supportsCustomData() {
            return false;
        }
    
    
        function getHashtable() {
    
            $base = $this->ht;
    
            unset($base['teams']);
    
    Jared Hancock's avatar
    Jared Hancock committed
            unset($base['dept_access']);
    
    Peter Rotich's avatar
    Peter Rotich committed
    
            if ($this->getConfig())
                $base += $this->getConfig();
    
    
            return $base;
    
        function getInfo() {
    
            return $this->getHashtable();
    
        // AuthenticatedUser implementation...
        // TODO: Move to an abstract class that extends Staff
    
        function getUserType() {
    
            list($bk, ) = explode(':', $this->getAuthKey());
    
            // If administering a user other than yourself, fallback to the
            // agent's declared backend, if any
            if (!$bk && $this->backend)
                $bk = $this->backend;
    
    
            return StaffAuthenticationBackend::getBackend($bk);
    
    Jared Hancock's avatar
    Jared Hancock committed
        function setAuthKey($key) {
            $this->authkey = $key;
        }
    
        function getAuthKey() {
            return $this->authkey;
        }
    
        // logOut the user
        function logOut() {
    
            if ($bk = $this->getAuthBackend())
                return $bk->signOut($this);
    
            return false;
        }
    
    
    Jared Hancock's avatar
    Jared Hancock committed
        /*compares user password*/
    
        function check_passwd($password, $autoupdate=true) {
    
    Jared Hancock's avatar
    Jared Hancock committed
    
            /*bcrypt based password match*/
    
            if(Passwd::cmp($password, $this->getPasswd()))
    
    Jared Hancock's avatar
    Jared Hancock committed
                return true;
    
    
            //Fall back to MD5
            if(!$password || strcmp($this->getPasswd(), MD5($password)))
                return false;
    
            //Password is a MD5 hash: rehash it (if enabled) otherwise force passwd change.
    
    Jared Hancock's avatar
    Jared Hancock committed
            $this->passwd = Passwd::hash($password);
    
    Jared Hancock's avatar
    Jared Hancock committed
            if(!$autoupdate || !$this->save())
    
    Jared Hancock's avatar
    Jared Hancock committed
                $this->forcePasswdRest();
    
    
        function cmp_passwd($password) {
            return $this->check_passwd($password, false);
    
        function hasPassword() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return (bool) $this->passwd;
    
    Jared Hancock's avatar
    Jared Hancock committed
        function forcePasswdRest() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            $this->change_passwd = 1;
    
            return $this->save();
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        /* check if passwd reset is due. */
    
        function isPasswdResetDue() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            global $cfg;
    
            return ($cfg && $cfg->getPasswdResetPeriod()
    
    Jared Hancock's avatar
    Jared Hancock committed
                        && $this->passwd_change>($cfg->getPasswdResetPeriod()*30*24*60*60));
    
        function setPassword($new, $current=false) {
            // Allow the backend to update the password. This is the preferred
            // method as it allows for integration with password policies and
            // also allows for remotely updating the password where possible and
            // supported.
            if (!($bk = $this->getAuthBackend())
                || !$bk instanceof AuthBackend
            ) {
                // Fallback to osTicket authentication token udpates
                $bk = new osTicketAuthentication();
            }
    
            // And now for the magic
            if (!$bk->supportsPasswordChange()) {
                throw new PasswordUpdateFailed(
                    __('Authentication backend does not support password updates'));
            }
    
            // Backend should throw PasswordUpdateFailed directly
            $rv = $bk->setPassword($this, $new, $current);
    
    
            // Successfully updated authentication tokens
            $this->change_passwd = 0;
            $this->cancelResetTokens();
            $this->passwdreset = SqlFunction::NOW();
    
    
            return $rv;
    
        function canAccess($something) {
            if ($something instanceof RestrictedAccess)
                return $something->checkStaffPerm($this);
    
            return true;
        }
    
    
    Jared Hancock's avatar
    Jared Hancock committed
        function isPasswdChangeDue() {
            return $this->isPasswdResetDue();
        }
    
    
        function getRefreshRate() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->auto_refresh_rate;
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        function getPageLimit() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->max_page_size;
    
        function getId() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->staff_id;
    
        function getUserId() {
            return $this->getId();
        }
    
        function getEmail() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->email;
    
    
        function getAvatar($size=null) {
            global $cfg;
            $source = $cfg->getStaffAvatarSource();
            $avatar = $source->getAvatar($this);
            if (isset($size))
                $avatar->setSize($size);
            return $avatar;
    
        function getUserName() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->username;
    
        function getPasswd() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->passwd;
    
        function getName() {
    
            return new AgentsName(array('first' => $this->ht['firstname'], 'last' => $this->ht['lastname']));
    
        function getAvatarAndName() {
            return $this->getAvatar().Format::htmlchars((string) $this->getName());
        }
    
    
        function getFirstName() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->firstname;
    
        function getLastName() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->lastname;
    
        function getSignature() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->signature;
    
        function getDefaultTicketQueueId() {
            return $this->default_ticket_queue_id;
        }
    
    
    Jared Hancock's avatar
    Jared Hancock committed
        function getDefaultSignatureType() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->default_signature_type;
    
        function getReplyFromNameType() {
            return $this->default_from_name;
        }
    
    
        function getDefaultPaperSize() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->default_paper_size;
    
        function forcePasswdChange() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->change_passwd;
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getDepartments() {
    
            // TODO: Cache this in the agent's session as it is unlikely to
            //       change while logged in
    
            if (!isset($this->departments)) {
    
                // Departments the staff is "allowed" to access...
                // based on the group they belong to + user's primary dept + user's managed depts.
    
                $sql='SELECT DISTINCT d.id FROM '.STAFF_TABLE.' s '
    
    Jared Hancock's avatar
    Jared Hancock committed
                    .' LEFT JOIN '.STAFF_DEPT_TABLE.' g ON (s.staff_id=g.staff_id) '
    
                    .' INNER JOIN '.DEPT_TABLE.' d ON (LOCATE(CONCAT("/", s.dept_id, "/"), d.path) OR d.manager_id=s.staff_id OR LOCATE(CONCAT("/", g.dept_id, "/"), d.path)) '
                    .' WHERE s.staff_id='.db_input($this->getId());
                $depts = array();
                if (($res=db_query($sql)) && db_num_rows($res)) {
                    while(list($id)=db_fetch_row($res))
    
                        $depts[] = (int) $id;
    
                }
    
                /* ORM method — about 2.0ms slower
                $q = Q::any(array(
                    'path__contains' => '/'.$this->dept_id.'/',
                    'manager_id' => $this->getId(),
                ));
    
    Jared Hancock's avatar
    Jared Hancock committed
                // Add in extended access
                foreach ($this->dept_access->depts->values_flat('dept_id') as $row) {
    
                    // Skip primary dept
                    if ($row[0] == $this->dept_id)
                        continue;
                    $q->add(new Q(array('path__contains'=>'/'.$row[0].'/')));
                }
    
                $dept_ids = Dept::objects()
                    ->filter($q)
                    ->distinct('id')
    
                    ->values_flat('id');
    
    
                foreach ($dept_ids as $row)
                    $depts[] = $row[0];
                */
    
                $this->departments = $depts;
    
    
            return $this->departments;
    
        function getDepts() {
    
    Peter Rotich's avatar
    Peter Rotich committed
            return $this->getDepartments();
        }
    
    
        function getManagedDepartments() {
    
            return ($depts=Dept::getDepartments(
                        array('manager' => $this->getId())
                        ))?array_keys($depts):array();
        }
    
        function getDeptId() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->dept_id;
    
        function getDept() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->dept;
        }
    
    
        function setDepartmentId($dept_id, $eavesdrop=false) {
            // Grant access to the current department
            $old = $this->dept_id;
            if ($eavesdrop) {
                $da = StaffDeptAccess::create(array(
                    'dept_id' => $old,
                    'role_id' => $this->role_id,
                ));
                $da->setAlerts(true);
                $this->dept_access->add($da);
            }
    
            // Drop extended access to new department
            $this->dept_id = $dept_id;
            if ($da = $this->dept_access->findFirst(array(
                'dept_id' => $dept_id))
            ) {
                $this->dept_access->remove($da);
            }
        }
    
    
        function usePrimaryRoleOnAssignment() {
            return $this->getExtraAttr('def_assn_role', true);
        }
    
    
        function getLanguage() {
    
            return (isset($this->lang)) ? $this->lang : false;
    
        function getTimezone() {
    
            if (isset($this->timezone))
                return $this->timezone;
    
        }
    
        function getLocale() {
    
            //XXX: isset is required here to avoid possible crash when upgrading
            // installation where locale column doesn't exist yet.
            return isset($this->locale) ? $this->locale : 0;
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getRoles() {
            if (!isset($this->_roles)) {
                $this->_roles = array($this->dept_id => $this->role);
                foreach($this->dept_access as $da)
                    $this->_roles[$da->dept_id] = $da->role;
            }
    
            return $this->_roles;
        }
    
        function getRole($dept=null, $assigned=false) {
    
            if (is_null($dept))
                return $this->role;
    
            if ((!$dept instanceof Dept) && !($dept=Dept::lookup($dept)))
                return null;
    
            $deptId = $dept->getId();
    
    Peter Rotich's avatar
    Peter Rotich committed
            $roles = $this->getRoles();
            if (isset($roles[$deptId]))
                return $roles[$deptId];
    
            // Default to primary role.
            if ($assigned && $this->usePrimaryRoleOnAssignment())
    
    Peter Rotich's avatar
    Peter Rotich committed
                return $this->role;
    
            // Ticket Create & View only access
            $perms = JSONDataEncoder::encode(array(
                        Ticket::PERM_CREATE => 1));
            return new Role(array('permissions' => $perms));
    
        function hasPerm($perm, $global=true) {
            if ($global)
                return $this->getPermission()->has($perm);
            if ($this->getRole()->hasPerm($perm))
                return true;
            foreach ($this->dept_access as $da)
                if ($da->role->hasPerm($perm))
                    return true;
            return false;
    
        function canSearchEverything() {
            return $this->hasPerm(SearchBackend::PERM_EVERYTHING);
        }
    
    
        function canManageTickets() {
    
            return $this->hasPerm(Ticket::PERM_DELETE, false)
                    || $this->hasPerm(Ticket::PERM_TRANSFER, false)
                    || $this->hasPerm(Ticket::PERM_ASSIGN, false)
                    || $this->hasPerm(Ticket::PERM_CLOSE, false);
    
        function isManager() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return (($dept=$this->getDept()) && $dept->getManagerId()==$this->getId());
        }
    
    
        function isStaff() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return TRUE;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function isActive() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->isactive;
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getStatus() {
            return $this->isActive() ? __('Active') : __('Locked');
        }
    
    
        function isVisible() {
    
    Jared Hancock's avatar
    Jared Hancock committed
             return $this->isvisible;
    
        function onVacation() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->onvacation;
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        function isAvailable() {
    
    Peter Rotich's avatar
    Peter Rotich committed
            return ($this->isActive() && !$this->onVacation());
    
        function showAssignedOnly() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->assigned_only;
    
    
        function isAccessLimited() {
            return $this->showAssignedOnly();
        }
    
        function isAdmin() {
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->isadmin;
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        function isTeamMember($teamId) {
    
            return ($teamId && in_array($teamId, $this->getTeams()));
    
        function canAccessDept($dept) {
    
            if (!$dept instanceof Dept)
                return false;
    
            return (!$this->isAccessLimited()
                    && in_array($dept->getId(), $this->getDepts()));
    
        function getTeams() {
    
    Peter Rotich's avatar
    Peter Rotich committed
            if (!isset($this->_teams)) {
                $this->_teams = array();
                foreach ($this->teams as $team)
    
                     $this->_teams[] = (int) $team->team_id;
    
    Peter Rotich's avatar
    Peter Rotich committed
            return $this->_teams;
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getTicketsVisibility() {
    
            // -- Open and assigned to me
            $assigned = Q::any(array(
                'staff_id' => $this->getId(),
            ));
    
            $assigned->add(array('thread__referrals__agent__staff_id' => $this->getId()));
    
            // -- Open and assigned to a team of mine
    
    Peter Rotich's avatar
    Peter Rotich committed
            if (($teams = array_filter($this->getTeams()))) {
    
    Peter Rotich's avatar
    Peter Rotich committed
                $assigned->add(array('team_id__in' => $teams));
                $assigned->add(array('thread__referrals__team__team_id__in' => $teams));
            }
    
            $visibility = Q::any(new Q(array('status__state'=>'open', $assigned)));
    
            // -- Routed to a department of mine
            if (!$this->showAssignedOnly() && ($depts=$this->getDepts())) {
                $visibility->add(array('dept_id__in' => $depts));
                $visibility->add(array('thread__referrals__dept__id__in' => $depts));
            }
    
            return $visibility;
        }
    
    
        function applyVisibility($query) {
            return $query->filter($this->getTicketsVisibility());
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        /* stats */
    
    Jared Hancock's avatar
    Jared Hancock committed
        function resetStats() {
            $this->stats = array();
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getTasksStats() {
    
            if (!$this->stats['tasks'])
                $this->stats['tasks'] = Task::getStaffStats($this);
    
            return  $this->stats['tasks'];
        }
    
        function getNumAssignedTasks() {
            return ($stats=$this->getTasksStats()) ? $stats['assigned'] : 0;
        }
    
        function getNumClosedTasks() {
            return ($stats=$this->getTasksStats()) ? $stats['closed'] : 0;
        }
    
    
        function getExtraAttr($attr=false, $default=null) {
    
            if (!isset($this->_extra) && isset($this->extra))
    
    Jared Hancock's avatar
    Jared Hancock committed
                $this->_extra = JsonDataParser::decode($this->extra);
    
            return $attr
                ? (isset($this->_extra[$attr]) ? $this->_extra[$attr] : $default)
                : $this->_extra;
    
        }
    
        function setExtraAttr($attr, $value, $commit=true) {
            $this->getExtraAttr();
    
    Jared Hancock's avatar
    Jared Hancock committed
            $this->_extra[$attr] = $value;
    
            $this->extra = JsonDataEncoder::encode($this->_extra);
    
    Jared Hancock's avatar
    Jared Hancock committed
                $this->save();
    
    Jared Hancock's avatar
    Jared Hancock committed
        function getPermission() {
            if (!isset($this->_perm)) {
                $this->_perm = new RolePermission($this->permissions);
            }
            return $this->_perm;
        }
    
        function getPermissionInfo() {
            return $this->getPermission()->getInfo();
        }
    
    
        function onLogin($bk) {
            // Update last apparent language preference
            $this->setExtraAttr('browser_lang',
                Internationalization::getCurrentLanguage(),
                false);
    
    
    Jared Hancock's avatar
    Jared Hancock committed
            $this->lastlogin = SqlFunction::NOW();
            $this->save();
    
    Jared Hancock's avatar
    Jared Hancock committed
        //Staff profile update...unfortunately we have to separate it from admin update to avoid potential issues
    
        function updateProfile($vars, &$errors) {
    
            global $cfg;
    
    Jared Hancock's avatar
    Jared Hancock committed
    
            $vars['firstname']=Format::striptags($vars['firstname']);
            $vars['lastname']=Format::striptags($vars['lastname']);
    
    
    Jared Hancock's avatar
    Jared Hancock committed
            if (isset($this->staff_id) && $this->getId() != $vars['id'])
    
                $errors['err']=__('Internal error occurred');
    
    Jared Hancock's avatar
    Jared Hancock committed
    
            if(!$vars['firstname'])
    
                $errors['firstname']=__('First name is required');
    
    Jared Hancock's avatar
    Jared Hancock committed
            if(!$vars['lastname'])
    
                $errors['lastname']=__('Last name is required');
    
            if(!$vars['email'] || !Validator::is_valid_email($vars['email']))
    
                $errors['email']=__('Valid email is required');
    
    Jared Hancock's avatar
    Jared Hancock committed
            elseif(Email::getIdByEmail($vars['email']))
    
                $errors['email']=__('Already in-use as system email');
    
    Jared Hancock's avatar
    Jared Hancock committed
            elseif (($uid=static::getIdByEmail($vars['email']))
                    && (!isset($this->staff_id) || $uid!=$this->getId()))
    
                $errors['email']=__('Email already in use by another agent');
    
    Jared Hancock's avatar
    Jared Hancock committed
    
            if($vars['phone'] && !Validator::is_phone($vars['phone']))
    
                $errors['phone']=__('Valid phone number is required');
    
    Jared Hancock's avatar
    Jared Hancock committed
    
            if($vars['mobile'] && !Validator::is_phone($vars['mobile']))
    
                $errors['mobile']=__('Valid phone number is required');
    
    Jared Hancock's avatar
    Jared Hancock committed
    
            if($vars['default_signature_type']=='mine' && !$vars['signature'])
    
                $errors['default_signature_type'] = __("You don't have a signature");
    
            // Update the user's password if requested
            if ($vars['passwd1']) {
                try {
                    $this->setPassword($vars['passwd1'], $vars['cpasswd']);
                }
                catch (BadPassword $ex) {
                    $errors['passwd1'] = $ex->getMessage();
                }
                catch (PasswordUpdateFailed $ex) {
                    // TODO: Add a warning banner or crash the update
                }
            }
    
    
    Jared Hancock's avatar
    Jared Hancock committed
            $this->firstname = $vars['firstname'];
            $this->lastname = $vars['lastname'];
            $this->email = $vars['email'];
            $this->phone = Format::phone($vars['phone']);
            $this->phone_ext = $vars['phone_ext'];
            $this->mobile = Format::phone($vars['mobile']);
            $this->signature = Format::sanitize($vars['signature']);
            $this->timezone = $vars['timezone'];
            $this->locale = $vars['locale'];
            $this->max_page_size = $vars['max_page_size'];
            $this->auto_refresh_rate = $vars['auto_refresh_rate'];
            $this->default_signature_type = $vars['default_signature_type'];
            $this->default_paper_size = $vars['default_paper_size'];
            $this->lang = $vars['lang'];
    
    Peter Rotich's avatar
    Peter Rotich committed
            $this->onvacation = isset($vars['onvacation']) ? 1 : 0;
    
            if (isset($vars['avatar_code']))
              $this->setExtraAttr('avatar', $vars['avatar_code']);
    
    
            if ($errors)
                return false;
    
    
            $_SESSION['::lang'] = null;
            TextDomain::configureForUser($this);
    
    
    Peter Rotich's avatar
    Peter Rotich committed
            // Update the config information
            $_config = new Config('staff.'.$this->getId());
            $_config->updateAll(array(
                        'datetime_format' => $vars['datetime_format'],
                        'default_from_name' => $vars['default_from_name'],
    
    Peter Rotich's avatar
    Peter Rotich committed
                        'thread_view_order' => $vars['thread_view_order'],
    
                        'default_ticket_queue_id' => $vars['default_ticket_queue_id'],
    
    Peter Rotich's avatar
    Peter Rotich committed
                        )
                    );
            $this->_config = $_config->getInfo();
    
    
    Jared Hancock's avatar
    Jared Hancock committed
            return $this->save();
        }
    
    
    Jared Hancock's avatar
    Jared Hancock committed
        function updateTeams($membership, &$errors) {
            $dropped = array();
            foreach ($this->teams as $TM)
                $dropped[$TM->team_id] = 1;
    
    Jared Hancock's avatar
    Jared Hancock committed
            reset($membership);
            while(list(, list($team_id, $alerts)) = each($membership)) {
                $member = $this->teams->findFirst(array('team_id' => $team_id));
                if (!$member) {
    
                    $this->teams->add($member = new TeamMember(array(
    
    Jared Hancock's avatar
    Jared Hancock committed
                        'team_id' => $team_id,
                    )));
    
    Jared Hancock's avatar
    Jared Hancock committed
                $member->setAlerts($alerts);
                if (!$errors)
                    $member->save();
                unset($dropped[$member->team_id]);
            }
            if (!$errors && $dropped) {
                $member = $this->teams
                    ->filter(array('team_id__in' => array_keys($dropped)))
    
    Peter Rotich's avatar
    Peter Rotich committed
                    ->delete();
    
                $this->teams->reset();
    
    Jared Hancock's avatar
    Jared Hancock committed
            }
            return true;
        }
    
    
    Jared Hancock's avatar
    Jared Hancock committed
        function delete() {
            global $thisstaff;
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
    Jared Hancock's avatar
    Jared Hancock committed
            if (!$thisstaff || $this->getId() == $thisstaff->getId())
    
    Jared Hancock's avatar
    Jared Hancock committed
                return false;
    
    
    Jared Hancock's avatar
    Jared Hancock committed
            if (!parent::delete())
                return false;
    
    Jared Hancock's avatar
    Jared Hancock committed
            // DO SOME HOUSE CLEANING
            //Move remove any ticket assignments...TODO: send alert to Dept. manager?
    
    Jared Hancock's avatar
    Jared Hancock committed
            Ticket::objects()
                ->filter(array('staff_id' => $this->getId()))
                ->update(array('staff_id' => 0));
    
    Jared Hancock's avatar
    Jared Hancock committed
            //Update the poster and clear staff_id on ticket thread table.
    
    Jared Hancock's avatar
    Jared Hancock committed
            ThreadEntry::objects()
                ->filter(array('staff_id' => $this->getId()))
                ->update(array(
                    'staff_id' => 0,
                    'poster' => $this->getName()->getOriginal(),
                ));
    
    Jared Hancock's avatar
    Jared Hancock committed
            // Cleanup Team membership table.
    
    Jared Hancock's avatar
    Jared Hancock committed
            TeamMember::objects()
                ->filter(array('staff_id'=>$this->getId()))
                ->delete();
    
            // Cleanup staff dept access
            StaffDeptAccess::objects()
                ->filter(array('staff_id'=>$this->getId()))
                ->delete();
    
    
    Jared Hancock's avatar
    Jared Hancock committed
            return true;
    
    Jared Hancock's avatar
    Jared Hancock committed
        }
    
        /**** Static functions ********/
    
    Jared Hancock's avatar
    Jared Hancock committed
        static function lookup($var) {
            if (is_array($var))
                return parent::lookup($var);
            elseif (is_numeric($var))
                return parent::lookup(array('staff_id'=>$var));
            elseif (Validator::is_email($var))
                return parent::lookup(array('email'=>$var));
            elseif (is_string($var))
                return parent::lookup(array('username'=>$var));
            else
                return null;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        static function getStaffMembers($criteria=array()) {
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
            $members = static::objects();
    
    Peter Rotich's avatar
    Peter Rotich committed
            if (isset($criteria['available'])) {
    
    Jared Hancock's avatar
    Jared Hancock committed
                $members = $members->filter(array(
                    'onvacation' => 0,
                    'isactive' => 1,
                ));
    
    Peter Rotich's avatar
    Peter Rotich committed
            $members = self::nsort($members);
    
    Jared Hancock's avatar
    Jared Hancock committed
            $users=array();
    
    Jared Hancock's avatar
    Jared Hancock committed
            foreach ($members as $M) {
    
                $users[$M->getId()] = $M->getName();
    
        static function getAvailableStaffMembers() {
    
    Peter Rotich's avatar
    Peter Rotich committed
            return self::getStaffMembers(array('available'=>true));
    
        static function getsortby($path='', $format=null) {
    
    Peter Rotich's avatar
    Peter Rotich committed
            global $cfg;
    
            $format = $format ?: $cfg->getAgentNameFormat();
            switch ($format) {
            case 'last':
            case 'lastfirst':
            case 'legal':
    
                $fields = array("{$path}lastname", "{$path}firstname");
    
    Peter Rotich's avatar
    Peter Rotich committed
                break;
            default:
    
                $fields = array("${path}firstname", "${path}lastname");
    
            return $fields;
        }
    
        static function nsort(QuerySet $qs, $path='', $format=null) {
            $fields = self::getsortby($path, $format);
            $qs->order_by($fields);
    
    Peter Rotich's avatar
    Peter Rotich committed
            return $qs;
        }
    
    
    Jared Hancock's avatar
    Jared Hancock committed
        static function getIdByUsername($username) {
            $row = static::objects()->filter(array('username' => $username))
                ->values_flat('staff_id')->first();
            return $row ? $row[0] : 0;
    
        static function getIdByEmail($email) {
    
    Jared Hancock's avatar
    Jared Hancock committed
            $row = static::objects()->filter(array('email' => $email))
                ->values_flat('staff_id')->first();
            return $row ? $row[0] : 0;
    
    Jared Hancock's avatar
    Jared Hancock committed
        static function create($vars=false) {
    
            $staff = new static($vars);
    
    Jared Hancock's avatar
    Jared Hancock committed
            $staff->created = SqlFunction::NOW();
            return $staff;
    
        function cancelResetTokens() {
            // TODO: Drop password-reset tokens from the config table for
            //       this user id
            $sql = 'DELETE FROM '.CONFIG_TABLE.' WHERE `namespace`="pwreset"
                AND `value`='.db_input($this->getId());
    
            db_query($sql, false);
    
            unset($_SESSION['_staff']['reset-token']);
        }
    
    
        function sendResetEmail($template='pwreset-staff', $log=true) {
    
            global $ost, $cfg;
    
    
            $content = Page::lookupByType($template);
    
            $token = Misc::randCode(48); // 290-bits
    
                return new BaseError(/* @trans */ 'Unable to retrieve password reset email template');
    
            $vars = array(
    
                'url' => $ost->getConfig()->getBaseUrl(),
                'token' => $token,
    
                'recipient' => $this,
    
                'reset_link' => sprintf(
                    "%s/scp/pwreset.php?token=%s",
                    $ost->getConfig()->getBaseUrl(),
                    $token),
    
            $vars['link'] = &$vars['reset_link'];
    
            if (!($email = $cfg->getAlertEmail()))
    
                $email = $cfg->getDefaultEmail();
    
    
            $info = array('email' => $email, 'vars' => &$vars, 'log'=>$log);
    
            Signal::send('auth.pwreset.email', $this, $info);
    
    
            if ($info['log'])
    
                $ost->logWarning(_S('Agent Password Reset'), sprintf(
                 _S('Password reset was attempted for agent: %1$s<br><br>
    
                    Requested-User-Id: %2$s<br>
                    Source-Ip: %3$s<br>
                    Email-Sent-To: %4$s<br>
                    Email-Sent-Via: %5$s'),
    
                    $this->getName(),
                    $_POST['userid'],
                    $_SERVER['REMOTE_ADDR'],
                    $this->getEmail(),
                    $email->getEmail()
                ), false);
    
    
    Jared Hancock's avatar
    Jared Hancock committed
            $lang = $this->lang ?: $this->getExtraAttr('browser_lang');
    
            $msg = $ost->replaceTemplateVariables(array(
    
                'subj' => $content->getLocalName($lang),
                'body' => $content->getLocalBody($lang),
    
    
            $_config = new Config('pwreset');
    
            $_config->set($vars['token'], $this->getId());
    
            $email->send($this->getEmail(), Format::striptags($msg['subj']),
    
        static function importCsv($stream, $defaults=array(), $callback=false) {
            require_once INCLUDE_DIR . 'class.import.php';
    
            $importer = new CsvImporter($stream);
            $imported = 0;
            $fields = array(
                'firstname' => new TextboxField(array(
    
                    'label' => __('First Name'),
    
                )),
                'lastname' => new TextboxField(array(
    
                    'label' => __('Last Name'),
    
                )),
                'email' => new TextboxField(array(
                    'label' => __('Email Address'),
                    'configuration' => array(
                        'validator' => 'email',
                    ),
                )),
                'username' => new TextboxField(array(
                    'label' => __('Username'),
                    'validators' => function($self, $value) {
                        if (!Validator::is_username($value))
                            $self->addError('Not a valid username');
                    },
                )),
            );
            $form = new SimpleForm($fields);
    
            try {
                db_autocommit(false);
    
    Peter Rotich's avatar
    Peter Rotich committed
                $errors = array();
    
                $records = $importer->importCsv($form->getFields(), $defaults);
                foreach ($records as $data) {
                    if (!isset($data['email']) || !isset($data['username']))
                        throw new ImportError('Both `username` and `email` fields are required');
    
                    if ($agent = self::lookup(array('username' => $data['username']))) {
                        // TODO: Update the user
                    }
                    elseif ($agent = self::create($data, $errors)) {
                        if ($callback)
                            $callback($agent, $data);
                        $agent->save();
                    }