Skip to content
Snippets Groups Projects
class.user.php 38.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • <?php
    /*********************************************************************
        class.user.php
    
        External end-user identification for osTicket
    
        Peter Rotich <peter@osticket.com>
        Jared Hancock <jared@osticket.com>
        Copyright (c)  2006-2013 osTicket
        http://www.osticket.com
    
        Released under the GNU General Public License WITHOUT ANY WARRANTY.
        See LICENSE.TXT for details.
    
        vim: expandtab sw=4 ts=4 sts=4:
    **********************************************************************/
    
    require_once INCLUDE_DIR . 'class.orm.php';
    require_once INCLUDE_DIR . 'class.util.php';
    
    require_once INCLUDE_DIR . 'class.variable.php';
    
    require_once INCLUDE_DIR . 'class.search.php';
    require_once INCLUDE_DIR . 'class.organization.php';
    
    
    class UserEmailModel extends VerySimpleModel {
        static $meta = array(
            'table' => USER_EMAIL_TABLE,
            'pk' => array('id'),
            'joins' => array(
                'user' => array(
                    'constraint' => array('user_id' => 'UserModel.id')
                )
            )
        );
    
    
        function __toString() {
    
            return (string) $this->address;
    
    
        static function getIdByEmail($email) {
            $row = UserEmailModel::objects()
                ->filter(array('address'=>$email))
                ->values_flat('user_id')
                ->first();
    
            return $row ? $row[0] : 0;
        }
    
    }
    
    class UserModel extends VerySimpleModel {
        static $meta = array(
            'table' => USER_TABLE,
            'pk' => array('id'),
    
            'select_related' => array('default_email', 'org', 'account'),
    
            'joins' => array(
                'emails' => array(
                    'reverse' => 'UserEmailModel.user',
    
                'tickets' => array(
    
                    'null' => true,
    
                    'reverse' => 'Ticket.user',
    
                'account' => array(
                    'list' => false,
    
                    'null' => true,
    
                    'reverse' => 'ClientAccount.user',
    
                    'null' => true,
    
                    'constraint' => array('org_id' => 'Organization.id')
                ),
    
                'default_email' => array(
                    'null' => true,
                    'constraint' => array('default_email_id' => 'UserEmailModel.id')
                ),
    
                'cdata' => array(
                    'constraint' => array('id' => 'UserCdata.user_id'),
                    'null' => true,
                ),
    
                    'constraint' => array(
                        'id' => 'DynamicFormEntry.object_id',
                        "'U'" => 'DynamicFormEntry.object_type',
                    ),
    
        const PRIMARY_ORG_CONTACT   = 0x0001;
    
    
        const PERM_CREATE =     'user.create';
        const PERM_EDIT =       'user.edit';
        const PERM_DELETE =     'user.delete';
        const PERM_MANAGE =     'user.manage';
        const PERM_DIRECTORY =  'user.dir';
    
        static protected $perms = array(
            self::PERM_CREATE => array(
                'title' => /* @trans */ 'Create',
                'desc' => /* @trans */ 'Ability to add new users',
    
                'primary' => true,
    
            ),
            self::PERM_EDIT => array(
                'title' => /* @trans */ 'Edit',
                'desc' => /* @trans */ 'Ability to manage user information',
    
                'primary' => true,
    
            ),
            self::PERM_DELETE => array(
                'title' => /* @trans */ 'Delete',
                'desc' => /* @trans */ 'Ability to delete users',
    
                'primary' => true,
    
            ),
            self::PERM_MANAGE => array(
                'title' => /* @trans */ 'Manage Account',
                'desc' => /* @trans */ 'Ability to manage active user accounts',
    
                'primary' => true,
    
            ),
            self::PERM_DIRECTORY => array(
                'title' => /* @trans */ 'User Directory',
                'desc' => /* @trans */ 'Ability to access the user directory',
    
                'primary' => true,
    
        function getId() {
            return $this->id;
        }
    
        function getDefaultEmailAddress() {
            return $this->getDefaultEmail()->address;
        }
    
        function getDefaultEmail() {
            return $this->default_email;
        }
    
        function hasAccount() {
            return !is_null($this->account);
        }
    
        function getAccount() {
            return $this->account;
        }
    
        function getOrgId() {
             return $this->get('org_id');
        }
    
        function getOrganization() {
            return $this->org;
        }
    
    
        function setOrganization($org, $save=true) {
    
            $this->set('org', $org);
    
            if ($save)
                $this->save();
    
        public function setFlag($flag, $val) {
            if ($val)
                $this->status |= $flag;
            else
                $this->status &= ~$flag;
        }
    
    
        protected function hasStatus($flag) {
            return $this->get('status') & $flag !== 0;
        }
    
        protected function clearStatus($flag) {
            return $this->set('status', $this->get('status') & ~$flag);
        }
    
        protected function setStatus($flag) {
            return $this->set('status', $this->get('status') | $flag);
        }
    
        function isPrimaryContact() {
            return $this->hasStatus(User::PRIMARY_ORG_CONTACT);
        }
    
        function setPrimaryContact($flag) {
            if ($flag)
                $this->setStatus(User::PRIMARY_ORG_CONTACT);
            else
                $this->clearStatus(User::PRIMARY_ORG_CONTACT);
        }
    
    
        static function getPermissions() {
            return self::$perms;
        }
    
    include_once INCLUDE_DIR.'class.role.php';
    RolePermission::register(/* @trans */ 'Users', UserModel::getPermissions());
    
    class UserCdata extends VerySimpleModel {
        static $meta = array(
    
            'table' => USER_CDATA_TABLE,
    
            'pk' => array('user_id'),
    
            'joins' => array(
                'user' => array(
                    'constraint' => array('user_id' => 'UserModel.id'),
                ),
            ),
    
    class User extends UserModel
    
    implements TemplateVariable, Searchable {
    
    Peter Rotich's avatar
    Peter Rotich committed
        var $_forms;
    
        static function fromVars($vars, $create=true, $update=false) {
    
            // Try and lookup by email address
    
            $user = static::lookupByEmail($vars['email']);
    
            if (!$user && $create) {
    
                $name = $vars['name'];
    
                if (is_array($name))
                    $name = implode(', ', $name);
                elseif (!$name)
    
                    list($name) = explode('@', $vars['email'], 2);
    
    
                $user = new User(array(
    
                    'name' => Format::htmldecode(Format::sanitize($name, false)),
    
                    'created' => new SqlFunction('NOW'),
                    'updated' => new SqlFunction('NOW'),
    
                    //XXX: Do plain create once the cause
                    // of the detached emails is fixed.
    
                    'default_email' => UserEmail::ensure($vars['email'])
    
                // Is there an organization registered for this domain
                list($mailbox, $domain) = explode('@', $vars['email'], 2);
    
                if (isset($vars['org_id']))
                    $user->set('org_id', $vars['org_id']);
                elseif ($org = Organization::forDomain($domain))
                    $user->setOrganization($org, false);
    
                try {
                    $user->save(true);
                    $user->emails->add($user->default_email);
                    // Attach initial custom fields
                    $user->addDynamicData($vars);
                }
                catch (OrmException $e) {
                    return null;
                }
    
                Signal::send('user.created', $user);
    
            elseif ($update) {
    
                $errors = array();
                $user->updateInfo($vars, $errors, true);
    
        static function fromForm($form, $create=true) {
    
            global $thisstaff;
    
    Peter Rotich's avatar
    Peter Rotich committed
    
            if(!$form) return null;
    
            //Validate the form
            $valid = true;
    
            $filter = function($f) use ($thisstaff) {
                return !isset($thisstaff) || $f->isRequiredForStaff();
            };
            if (!$form->isValid($filter))
    
    Peter Rotich's avatar
    Peter Rotich committed
                $valid  = false;
    
            //Make sure the email is not in-use
            if (($field=$form->getField('email'))
                    && $field->getClean()
                    && User::lookup(array('emails__address'=>$field->getClean()))) {
    
                $field->addError(__('Email is assigned to another user'));
    
    Peter Rotich's avatar
    Peter Rotich committed
                $valid = false;
            }
    
    
            return $valid ? self::fromVars($form->getClean(), $create) : null;
    
        function getEmail() {
    
    Peter Rotich's avatar
    Peter Rotich committed
    
            if (!isset($this->_email))
                $this->_email = new EmailAddress(sprintf('"%s" <%s>',
                        $this->getName(),
                        $this->default_email->address));
    
            return $this->_email;
    
        function getAvatar($size=null) {
    
            global $cfg;
            $source = $cfg->getClientAvatarSource();
    
            $avatar = $source->getAvatar($this);
            if (isset($size))
                $avatar->setSize($size);
            return $avatar;
    
    
        function getFullName() {
            return $this->name;
        }
    
    
        function getPhoneNumber() {
            foreach ($this->getDynamicData() as $e)
                if ($a = $e->getAnswer('phone'))
                    return $a;
        }
    
    
        function getName() {
    
            if (!$this->name)
                list($name) = explode('@', $this->getDefaultEmailAddress(), 2);
            else
                $name = $this->name;
    
            return new UsersName($name);
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getUpdateDate() {
            return $this->updated;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getCreateDate() {
            return $this->created;
        }
    
    
        function getTimezone() {
            global $cfg;
    
            if (($acct = $this->getAccount()) && ($tz = $acct->getTimezone())) {
                return $tz;
            }
            return $cfg->getDefaultTimezone();
        }
    
    
        function addForm($form, $sort=1, $data=null) {
    
            $entry = $form->instanciate($sort, $data);
            $entry->set('object_type', 'U');
            $entry->set('object_id', $this->getId());
            $entry->save();
            return $entry;
    
        function getLanguage($flags=false) {
            if ($acct = $this->getAccount())
                return $acct->getLanguage($flags);
    
        function to_json() {
    
            $info = array(
                    'id'  => $this->getId(),
    
                    'name' => Format::htmlchars($this->getName()),
    
                    'email' => (string) $this->getEmail(),
                    'phone' => (string) $this->getPhoneNumber());
    
    
            return JsonDataEncoder::encode($info);
        }
    
    
        function __toString() {
            return $this->asVar();
        }
    
    
        function asVar() {
            return (string) $this->getName();
        }
    
        function getVar($tag) {
    
            $tag = mb_strtolower($tag);
    
            foreach ($this->getDynamicData() as $e)
    
                if ($a = $e->getAnswer($tag))
                    return $a;
    
        static function getVarScope() {
            $base = array(
    
                'email' => array(
                    'class' => 'EmailAddress', 'desc' => __('Default email address')
                ),
    
                'name' => array(
                    'class' => 'PersonsName', 'desc' => 'User name, default format'
                ),
    
                'organization' => array('class' => 'Organization', 'desc' => __('Organization')),
    
            );
            $extra = VariableReplacer::compileFormScope(UserForm::getInstance());
            return $base + $extra;
        }
    
    
        static function getSearchableFields() {
    
            $base = array();
    
            $uform = UserForm::getUserForm();
    
            $base = array();
    
            foreach ($uform->getFields() as $F) {
                $fname = $F->get('name') ?: ('field_'.$F->get('id'));
    
                # XXX: email in the model corresponds to `emails__address` ORM path
                if ($fname == 'email')
                    $fname = 'emails__address';
    
                if (!$F->hasData() || $F->isPresentationOnly())
                    continue;
                if (!$F->isStorable())
                    $base[$fname] = $F;
                else
                    $base["cdata__{$fname}"] = $F;
            }
            return $base;
        }
    
    
        static function supportsCustomData() {
            return true;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function addDynamicData($data) {
    
            return $this->addForm(UserForm::objects()->one(), 1, $data);
    
        function getDynamicData($create=true) {
    
            if (!isset($this->_entries)) {
    
                $this->_entries = DynamicFormEntry::forObject($this->id, 'U')->all();
    
                if (!$this->_entries && $create) {
    
                    $g = UserForm::getNewInstance();
    
                    $g->setClientId($this->id);
    
    Peter Rotich's avatar
    Peter Rotich committed
                    $g->save();
    
                    $this->_entries[] = $g;
                }
    
            return $this->_entries ?: array();
    
        function getFilterData() {
            $vars = array();
            foreach ($this->getDynamicData() as $entry) {
    
                $vars += $entry->getFilterData();
    
                // Add in special `name` and `email` fields
    
                if ($entry->getDynamicForm()->get('type') != 'U')
                    continue;
    
    
                foreach (array('name', 'email') as $name) {
    
                    if ($f = $entry->getField($name))
    
                        $vars['field.'.$f->get('id')] =
                            $name == 'name' ? $this->getName() : $this->getEmail();
    
        function getForms($data=null) {
    
    Peter Rotich's avatar
    Peter Rotich committed
    
            if (!isset($this->_forms)) {
                $this->_forms = array();
    
                foreach ($this->getDynamicData() as $entry) {
                    $entry->addMissingFields();
    
                    if(!$data
    
                            && ($form = $entry->getDynamicForm())
    
    Peter Rotich's avatar
    Peter Rotich committed
                            && $form->get('type') == 'U' ) {
    
                        foreach ($entry->getFields() as $f) {
    
    Peter Rotich's avatar
    Peter Rotich committed
                            if ($f->get('name') == 'name')
                                $f->value = $this->getFullName();
                            elseif ($f->get('name') == 'email')
                                $f->value = $this->getEmail();
                        }
                    }
    
    
                    $this->_forms[] = $entry;
    
    Peter Rotich's avatar
    Peter Rotich committed
                }
            }
    
            return $this->_forms;
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getAccountStatus() {
    
    
    Peter Rotich's avatar
    Peter Rotich committed
            if (!($account=$this->getAccount()))
    
                return __('Guest');
    
            return (string) $account->getStatus();
    
        function canSeeOrgTickets() {
            return $this->org && (
                    $this->org->shareWithEverybody()
                || ($this->isPrimaryContact() && $this->org->shareWithPrimaryContacts()));
        }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
        function register($vars, &$errors) {
    
            // user already registered?
            if ($this->getAccount())
                return true;
    
    
    Peter Rotich's avatar
    Peter Rotich committed
            return UserAccount::register($this, $vars, $errors);
    
        static function importCsv($stream, $defaults=array()) {
    
            require_once INCLUDE_DIR . 'class.import.php';
    
            $importer = new CsvImporter($stream);
            $imported = 0;
            try {
                db_autocommit(false);
    
                $records = $importer->importCsv(UserForm::getUserForm()->getFields(), $defaults);
    
                foreach ($records as $data) {
    
    JediKev's avatar
    JediKev committed
                    if (!Validator::is_email($data['email']) || empty($data['name']))
    
                        throw new ImportError('Both `name` and `email` fields are required');
                    if (!($user = static::fromVars($data, true, true)))
                        throw new ImportError(sprintf(__('Unable to import user: %s'),
    
                            print_r($data, true)));
    
                db_autocommit(true);
    
            catch (Exception $ex) {
    
                return $ex->getMessage();
    
        function importFromPost($stream, $extra=array()) {
    
            return User::importCsv($stream, $extra);
        }
    
    
        function updateInfo($vars, &$errors, $staff=false) {
    
    Peter Rotich's avatar
    Peter Rotich committed
    
            $valid = true;
    
            foreach ($forms as $entry) {
                $entry->setSource($vars);
                if ($staff && !$entry->isValidForStaff())
    
                elseif (!$staff && !$entry->isValidForClient())
    
    Peter Rotich's avatar
    Peter Rotich committed
                    $valid = false;
    
    Peter Rotich's avatar
    Peter Rotich committed
                elseif ($entry->getDynamicForm()->get('type') == 'U'
                        && ($f=$entry->getField('email'))
                        &&  $f->getClean()
                        && ($u=User::lookup(array('emails__address'=>$f->getClean())))
                        && $u->id != $this->getId()) {
    
    Peter Rotich's avatar
    Peter Rotich committed
                    $valid = false;
    
                    $f->addError(__('Email is assigned to another user'));
    
    Peter Rotich's avatar
    Peter Rotich committed
                }
    
    Peter Rotich's avatar
    Peter Rotich committed
    
                if (!$valid)
                    $errors = array_merge($errors, $entry->errors());
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
    Peter Rotich's avatar
    Peter Rotich committed
            if (!$valid)
                return false;
    
    Peter Rotich's avatar
    Peter Rotich committed
            // Save the entries
    
            foreach ($forms as $entry) {
    
    Peter Rotich's avatar
    Peter Rotich committed
                if ($entry->getDynamicForm()->get('type') == 'U') {
                    //  Name field
                    if (($name = $entry->getField('name'))) {
    
                        $name = $name->getClean();
                        if (is_array($name))
                            $name = implode(', ', $name);
                        $this->name = $name;
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
    Peter Rotich's avatar
    Peter Rotich committed
                    // Email address field
                    if (($email = $entry->getField('email'))) {
    
    Peter Rotich's avatar
    Peter Rotich committed
                        $this->default_email->address = $email->getClean();
    
    Peter Rotich's avatar
    Peter Rotich committed
                        $this->default_email->save();
                    }
                }
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
                // DynamicFormEntry::save returns the number of answers updated
    
                    $this->updated = SqlFunction::NOW();
                }
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
            return $this->save();
    
    Peter Rotich's avatar
    Peter Rotich committed
    
    
        function save($refetch=false) {
            // Drop commas and reorganize the name without them
            $parts = array_map('trim', explode(',', $this->name));
            switch (count($parts)) {
                case 2:
                    // Assume last, first --or-- last suff., first
                    $this->name = $parts[1].' '.$parts[0];
                    // XXX: Consider last, first suff.
                    break;
                case 3:
                    // Assume last, first, suffix, write 'first last suffix'
                    $this->name = $parts[1].' '.$parts[0].' '.$parts[2];
                    break;
            }
    
    
            // Handle email addresses -- use the box name
            if (Validator::is_email($this->name)) {
                list($box, $domain) = explode('@', $this->name, 2);
                if (strpos($box, '.') !== false)
                    $this->name = str_replace('.', ' ', $box);
                else
                    $this->name = $box;
                $this->name = mb_convert_case($this->name, MB_CASE_TITLE);
            }
    
    
    Peter Rotich's avatar
    Peter Rotich committed
            if (count($this->dirty)) //XXX: doesn't work??
    
                $this->set('updated', new SqlFunction('NOW'));
    
            return parent::save($refetch);
        }
    
    
        function delete() {
    
    
            // Refuse to delete a user with tickets
            if ($this->tickets->count())
    
            // Delete account record (if any)
            if ($this->getAccount())
                $this->getAccount()->delete();
    
            // Delete emails.
            $this->emails->expunge();
    
            // Drop dynamic data
    
            foreach ($this->getDynamicData() as $entry) {
                $entry->delete();
    
    Peter Rotich's avatar
    Peter Rotich committed
            // Delete user
            return parent::delete();
    
        function deleteAllTickets() {
    
    JediKev's avatar
    JediKev committed
            $status_id = TicketStatus::lookup(array('state' => 'deleted'));
    
            foreach($this->tickets as $ticket) {
                if (!$T = Ticket::lookup($ticket->getId()))
                    continue;
    
    JediKev's avatar
    JediKev committed
                if (!$T->setStatus($status_id))
    
                    return false;
            }
            $this->tickets->reset();
            return true;
        }
    
    
        static function lookupByEmail($email) {
    
            return static::lookup(array('emails__address'=>$email));
    
    
        static function getNameById($id) {
            if ($user = static::lookup($id))
                return $user->getName();
        }
    
    
        static function getLink($id) {
            global $thisstaff;
    
            if (!$id || !$thisstaff)
                return false;
    
    
            return ROOT_PATH . sprintf('scp/users.php?id=%s', $id);
    
    class EmailAddress
    implements TemplateVariable {
    
    Peter Rotich's avatar
    Peter Rotich committed
        var $email;
    
    Peter Rotich's avatar
    Peter Rotich committed
        protected $_info;
    
    
        function __construct($address) {
    
    Peter Rotich's avatar
    Peter Rotich committed
            $this->_info = self::parse($address);
            $this->email = sprintf('%s@%s',
                    $this->getMailbox(),
                    $this->getDomain());
    
            if ($this->getName())
    
    aydreeihn's avatar
    aydreeihn committed
                $this->address = sprintf('"%s" <%s>',
    
    Peter Rotich's avatar
    Peter Rotich committed
                        $this->getName(),
                        $this->email);
    
    Peter Rotich's avatar
    Peter Rotich committed
            return (string) $this->email;
    
    Peter Rotich's avatar
    Peter Rotich committed
            if (!$this->_info)
    
    Peter Rotich's avatar
    Peter Rotich committed
            case 'host':
    
    Peter Rotich's avatar
    Peter Rotich committed
                return $this->_info->host;
    
    Peter Rotich's avatar
    Peter Rotich committed
                return trim($this->_info->personal, '"');
    
    Peter Rotich's avatar
    Peter Rotich committed
                return $this->_info->mailbox;
    
    Peter Rotich's avatar
    Peter Rotich committed
        function getAddress() {
            return $this->address ?: $this->email;
        }
    
        function getHost() {
            return $this->getVar('host');
        }
    
        function getDomain() {
            return $this->getHost();
        }
    
        function getName() {
            return $this->getVar('personal');
        }
    
        function getMailbox() {
            return $this->getVar('mailbox');
        }
    
        // Parse and email adddress (RFC822) into it's parts.
        // @address - one address is expected
        static function parse($address) {
            require_once PEAR_DIR . 'Mail/RFC822.php';
            require_once PEAR_DIR . 'PEAR.php';
            if (($parts = Mail_RFC822::parseAddressList($address))
                    && !PEAR::isError($parts))
                return current($parts);
        }
    
    
        static function getVarScope() {
            return array(
                'domain' => __('Domain'),
                'mailbox' => __('Mailbox'),
                'personal' => __('Personal name'),
            );
        }
    }
    
    
    class PersonsName
    implements TemplateVariable {
    
        static $formats = array(
    
            'first' => array(     /*@trans*/ "First", 'getFirst'),
            'last' => array(      /*@trans*/ "Last", 'getLast'),
            'full' => array(      /*@trans*/ "First Last", 'getFull'),
            'legal' => array(     /*@trans*/ "First M. Last", 'getLegal'),
            'lastfirst' => array( /*@trans*/ "Last, First", 'getLastFirst'),
            'formal' => array(    /*@trans*/ "Mr. Last", 'getFormal'),
            'short' => array(     /*@trans*/ "First L.", 'getShort'),
            'shortformal' => array(/*@trans*/ "F. Last", 'getShortFormal'),
            'complete' => array(  /*@trans*/ "Mr. First M. Last Sr.", 'getComplete'),
            'original' => array(  /*@trans*/ '-- As Entered --', 'getOriginal'),
    
        function __construct($name, $format=null) {
            global $cfg;
    
    
            if ($format && isset(static::$formats[$format]))
    
                $this->format = $format;
    
            else
                $this->format = 'original';
    
            if (!is_array($name)) {
                $this->parts = static::splitName($name);
                $this->name = $name;
            }
            else {
                $this->parts = $name;
                $this->name = implode(' ', $name);
            }
    
        }
    
        function getFirst() {
            return $this->parts['first'];
        }
    
        function getLast() {
            return $this->parts['last'];
        }
    
    
        function getMiddle() {
            return $this->parts['middle'];
        }
    
        function getMiddleInitial() {
            return mb_substr($this->parts['middle'],0,1).'.';
        }
    
    
        function getFormal() {
            return trim($this->parts['salutation'].' '.$this->parts['last']);
        }
    
        function getFull() {
            return trim($this->parts['first'].' '.$this->parts['last']);
        }
    
    
        function getLegal() {
            $parts = array(
                $this->parts['first'],
                mb_substr($this->parts['middle'],0,1),
                $this->parts['last'],
            );
            if ($parts[1]) $parts[1] .= '.';
            return implode(' ', array_filter($parts));
        }
    
    
        function getComplete() {
    
            $parts = array(
                $this->parts['salutation'],
                $this->parts['first'],
                mb_substr($this->parts['middle'],0,1),
                $this->parts['last'],
                $this->parts['suffix']
            );
            if ($parts[2]) $parts[2] .= '.';
            return implode(' ', array_filter($parts));
    
        }
    
        function getLastFirst() {
    
            $name = $this->parts['last'].', '.$this->parts['first'];
            if ($this->parts['suffix'])
                $name .= ', '.$this->parts['suffix'];
            return $name;
    
        function getShort() {
            return $this->parts['first'].' '.mb_substr($this->parts['last'],0,1).'.';
        }
    
        function getShortFormal() {
            return mb_substr($this->parts['first'],0,1).'. '.$this->parts['last'];
        }
    
        function getOriginal() {
            return $this->name;
        }
    
    
        function getInitials() {
            $names = array($this->parts['first']);
            $names = array_merge($names, explode(' ', $this->parts['middle']));
            $names[] = $this->parts['last'];
            $initials = '';
            foreach (array_filter($names) as $n)
                $initials .= mb_substr($n,0,1);
            return mb_convert_case($initials, MB_CASE_UPPER);
        }
    
    
        function getNameFormats($user, $type) {
          $nameFormats = array();
    
          foreach (PersonsName::allFormats() as $format => $func) {
              $nameFormats[$type . '.name.' . $format] = $user->getName()->$func[1]();
          }
    
          return $nameFormats;
        }
    
    
        function asVar() {
            return $this->__toString();
        }
    
    
        static function getVarScope() {
            $formats = array();
            foreach (static::$formats as $name=>$info) {
                if (in_array($name, array('original', 'complete')))
                    continue;
                $formats[$name] = $info[0];
            }
            return $formats;
        }
    
    
        function __toString() {
    
    Peter Rotich's avatar
    Peter Rotich committed
    
            @list(, $func) = static::$formats[$this->format];
    
            if (!$func) $func = 'getFull';
    
            return (string) call_user_func(array($this, $func));
    
        }
    
        static function allFormats() {
            return static::$formats;
    
        }
    
        /**
         * Thanks, http://stackoverflow.com/a/14420217
         */
        static function splitName($name) {
            $results = array();
    
            $r = explode(' ', $name);
            $size = count($r);
    
    Andrés's avatar
    Andrés committed
            //check if name is bad format (ex: J.Everybody), and fix them
    
            if($size==1 && mb_strpos($r[0], '.') !== false)
    
    Andrés's avatar
    Andrés committed
            {
                $r = explode('.', $name);
                $size = count($r);
            }
    
    
            //check first for period, assume salutation if so
            if (mb_strpos($r[0], '.') === false)
            {
                $results['salutation'] = '';
                $results['first'] = $r[0];
            }
            else
            {
                $results['salutation'] = $r[0];
                $results['first'] = $r[1];
            }
    
            //check last for period, assume suffix if so
            if (mb_strpos($r[$size - 1], '.') === false)
            {
                $results['suffix'] = '';
            }
            else
            {
                $results['suffix'] = $r[$size - 1];
            }
    
            //combine remains into last
            $start = ($results['salutation']) ? 2 : 1;
            $end = ($results['suffix']) ? $size - 2 : $size - 1;
    
    
            for ($i = $start; $i <= $end; $i++)
            {
    
                $middle[] = $r[$i];
            }
            if (count($middle) > 1) {
                $results['last'] = array_pop($middle);
                $results['middle'] = implode(' ', $middle);
            }
            else {
                $results['last'] = $middle[0];
                $results['middle'] = '';
    
    class AgentsName extends PersonsName {
        function __construct($name, $format=null) {
            global $cfg;
    
            if (!$format && $cfg)
                $format = $cfg->getAgentNameFormat();
    
            parent::__construct($name, $format);
        }
    }
    
    class UsersName extends PersonsName {
        function __construct($name, $format=null) {
            global $cfg;
            if (!$format && $cfg)
                $format = $cfg->getClientNameFormat();
    
            parent::__construct($name, $format);
        }
    }
    
    
    
    class UserEmail extends UserEmailModel {
        static function ensure($address) {
            $email = static::lookup(array('address'=>$address));
            if (!$email) {
    
                $email = new static(array('address'=>$address));
    
                $email->save();
            }
            return $email;
        }
    }
    
    class UserAccount extends VerySimpleModel {
    
    Peter Rotich's avatar
    Peter Rotich committed
        static $meta = array(
            'table' => USER_ACCOUNT_TABLE,
            'pk' => array('id'),
            'joins' => array(
                'user' => array(
                    'null' => false,
    
                    'constraint' => array('user_id' => 'User.id')
    
    Peter Rotich's avatar
    Peter Rotich committed
                ),