diff --git a/bootstrap.php b/bootstrap.php index c5a03fe2ab45d9bbe2982b95e4d4b6444caec192..424f3637c1bc5cc9ecde66edd30fd07555490562 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -80,8 +80,9 @@ class Bootstrap { define('TEAM_TABLE',$prefix.'team'); define('TEAM_MEMBER_TABLE',$prefix.'team_member'); define('DEPT_TABLE',$prefix.'department'); - define('GROUP_TABLE',$prefix.'groups'); + define('GROUP_TABLE', $prefix.'group'); define('GROUP_DEPT_TABLE', $prefix.'group_dept_access'); + define('ROLE_TABLE', $prefix.'role'); define('FAQ_TABLE',$prefix.'faq'); define('FAQ_TOPIC_TABLE',$prefix.'faq_topic'); diff --git a/include/ajax.draft.php b/include/ajax.draft.php index 5fc24e77a9c5e08d3e8607a6613c4e1a7fdaec6b..8d264ff486639b94df5622028ea611c656f130a0 100644 --- a/include/ajax.draft.php +++ b/include/ajax.draft.php @@ -291,7 +291,7 @@ class DraftAjaxAPI extends AjaxController { $draft = Draft::create(array( 'staff_id' => $thisstaff->getId(), - 'namespace' => $namepace + 'namespace' => $namespace )); if (!$draft->save()) Http::response(500, 'Unable to create draft'); diff --git a/include/ajax.i18n.php b/include/ajax.i18n.php index 38793902e4405e5451c27da173a808f0bda69532..9d3e76b6025600129519afff79d97d551ac02ee5 100644 --- a/include/ajax.i18n.php +++ b/include/ajax.i18n.php @@ -67,11 +67,11 @@ class i18nAjaxAPI extends AjaxController { } foreach ($_POST as $lang => $phrase) { if (isset($phrases[$lang])) { + $p = $phrases[$lang]; if (!$phrase) { $p->delete(); } else { - $p = $phrases[$lang]; // Avoid XSS injection $p->text = trim(Format::striptags($phrase)); $p->agent_id = $thisstaff->getId(); diff --git a/include/ajax.reports.php b/include/ajax.reports.php index e9e660a8c69b36f0c876ca63724b8735789aae94..bdaa7d9fe3fb828fe7428ed7a2c4cb03101d328d 100644 --- a/include/ajax.reports.php +++ b/include/ajax.reports.php @@ -42,11 +42,11 @@ class OverviewReportAjaxAPI extends AjaxController { $groups = array( "dept" => array( "table" => DEPT_TABLE, - "pk" => "dept_id", - "sort" => 'T1.dept_name', - "fields" => 'T1.dept_name', + "pk" => "id", + "sort" => 'T1.name', + "fields" => 'T1.name', "headers" => array(__('Department')), - "filter" => ('T1.dept_id IN ('.implode(',', db_input($thisstaff->getDepts())).')') + "filter" => ('T1.id IN ('.implode(',', db_input($thisstaff->getDepts())).')') ), "topic" => array( "table" => TOPIC_TABLE, @@ -70,7 +70,7 @@ class OverviewReportAjaxAPI extends AjaxController { (T1.staff_id='.db_input($thisstaff->getId()) .(($depts=$thisstaff->getManagedDepartments())? (' OR T1.dept_id IN('.implode(',', db_input($depts)).')'):'') - .(($thisstaff->canViewStaffStats())? + .(($thisstaff->getRole()->canViewStaffStats())? (' OR T1.dept_id IN('.implode(',', db_input($thisstaff->getDepts())).')'):'') .')' ) diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index d8fccf5dc451aa81a673e56627f00e7c82e25854..196bcc390e7147479bc5fd073dfaef3ec25e331b 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -536,6 +536,8 @@ class TicketsAjaxAPI extends AjaxController { || !$ticket->checkStaffAccess($thisstaff)) Http::response(404, 'Unknown ticket #'); + $role = $thisstaff->getRole($ticket->getDeptId()); + $info = array(); $state = null; switch($status) { @@ -544,12 +546,12 @@ class TicketsAjaxAPI extends AjaxController { $state = 'open'; break; case 'close': - if (!$thisstaff->canCloseTickets()) + if (!$role->canCloseTickets()) Http::response(403, 'Access denied'); $state = 'closed'; break; case 'delete': - if (!$thisstaff->canDeleteTickets()) + if (!$role->canDeleteTickets()) Http::response(403, 'Access denied'); $state = 'deleted'; break; @@ -584,20 +586,21 @@ class TicketsAjaxAPI extends AjaxController { __($status->getName())); else { // Make sure the agent has permission to set the status + $role = $thisstaff->getRole($ticket->getDeptId()); switch(mb_strtolower($status->getState())) { case 'open': - if (!$thisstaff->canCloseTickets() - && !$thisstaff->canCreateTickets()) + if (!$role->canCloseTickets() + && !$role->canCreateTickets()) $errors['err'] = sprintf(__('You do not have permission %s.'), __('to reopen tickets')); break; case 'closed': - if (!$thisstaff->canCloseTickets()) + if (!$role->canCloseTickets()) $errors['err'] = sprintf(__('You do not have permission %s.'), __('to resolve/close tickets')); break; case 'deleted': - if (!$thisstaff->canDeleteTickets()) + if (!$role->canDeleteTickets()) $errors['err'] = sprintf(__('You do not have permission %s.'), __('to archive/delete tickets')); break; diff --git a/include/class.auth.php b/include/class.auth.php index e6fed10c5089badd4597d9ab220964b717964281..b115258952323bd069ece521c8643b7211f5fa45 100644 --- a/include/class.auth.php +++ b/include/class.auth.php @@ -4,7 +4,7 @@ interface AuthenticatedUser { // Get basic information function getId(); function getUsername(); - function getRole(); + function getUserType(); //Backend used to authenticate the user function getAuthBackend(); @@ -30,7 +30,7 @@ implements AuthenticatedUser { // Get basic information abstract function getId(); abstract function getUsername(); - abstract function getRole(); + abstract function getUserType(); //Backend used to authenticate the user abstract function getAuthBackend(); diff --git a/include/class.client.php b/include/class.client.php index a8b39d1e5135bc69e01fccf9ef711bfbf32b613b..39b8241c9da43a839e70770f5632a8d236b94abf 100644 --- a/include/class.client.php +++ b/include/class.client.php @@ -232,7 +232,7 @@ class EndUser extends BaseAuthenticatedUser { return $this->user->getEmail(); } - function getRole() { + function getUserType() { return $this->isOwner() ? 'owner' : 'collaborator'; } diff --git a/include/class.config.php b/include/class.config.php index 164171a920a964493fbd24ebe969f814709c65ee..6fd4208b64e2f1e9a0f8fbc3aa5d44a8fb161704 100644 --- a/include/class.config.php +++ b/include/class.config.php @@ -39,6 +39,10 @@ class Config { if (!isset($_SESSION['cfg:'.$this->section])) $_SESSION['cfg:'.$this->section] = array(); $this->session = &$_SESSION['cfg:'.$this->section]; + $this->load(); + } + + function load() { $sql='SELECT id, `key`, value, `updated` FROM '.$this->table .' WHERE `'.$this->section_column.'` = '.db_input($this->section); diff --git a/include/class.dept.php b/include/class.dept.php index d6d1fd9248d2aac08dd6bc6f2de3f786c6610dad..a3129c909dfe991fe80b3588a5d098fa1d35a997 100644 --- a/include/class.dept.php +++ b/include/class.dept.php @@ -18,22 +18,35 @@ class Dept extends VerySimpleModel { static $meta = array( 'table' => DEPT_TABLE, - 'pk' => array('dept_id'), + 'pk' => array('id'), 'joins' => array( + 'email' => array( + 'constraint' => array('email_id' => 'EmailModel.email_id'), + 'null' => true, + ), 'sla' => array( 'constraint' => array('sla_id' => 'SLA.sla_id'), 'null' => true, ), 'manager' => array( + 'null' => true, 'constraint' => array('manager_id' => 'Staff.staff_id'), ), + 'members' => array( + 'null' => true, + 'list' => true, + 'reverse' => 'Staff.dept', + ), 'groups' => array( + 'null' => true, + 'list' => true, 'reverse' => 'GroupDeptAccess.dept' ), ), ); - var $members; + var $_members; + var $_groupids; var $config; var $template; @@ -55,17 +68,17 @@ class Dept extends VerySimpleModel { } function getId() { - return $this->dept_id; + return $this->id; } function getName() { - return $this->dept_name; + return $this->name; } function getLocalName($locale=false) { $tag = $this->getTranslateTag(); $T = CustomDataTranslation::translate($tag); - return $T != $tag ? $T : $this->dept_name; + return $T != $tag ? $T : $this->name; } static function getLocalById($id, $subtag, $default) { $tag = _H(sprintf('dept.%s.%s', $subtag, $id)); @@ -82,11 +95,12 @@ class Dept extends VerySimpleModel { } function getEmail() { - if(!$this->email) - if(!($this->email = Email::lookup($this->getEmailId())) && $cfg) - $this->email = $cfg->getDefaultEmail(); + global $cfg; + + if ($this->email) + return $this->email; - return $this->email; + return $cfg? $cfg->getDefaultEmail() : null; } function getNumMembers() { @@ -94,12 +108,12 @@ class Dept extends VerySimpleModel { } function getMembers($criteria=null) { - if (!$this->members || $criteria) { + if (!$this->_members || $criteria) { $members = Staff::objects() ->filter(Q::any(array( 'dept_id' => $this->getId(), new Q(array( - 'group__depts__dept_id' => $this->getId(), + 'group__depts__id' => $this->getId(), 'group__depts__group_membership' => self::ALERTS_DEPT_AND_GROUPS, )), 'staff_id' => $this->manager_id @@ -112,14 +126,14 @@ class Dept extends VerySimpleModel { 'onvacation' => 0, )); - $qs->order_by('lastname', 'firstname'); + $members->order_by('lastname', 'firstname'); if ($criteria) - return $qs->all(); + return $members->all(); - $this->members = $qs->all(); + $this->_members = $members->all(); } - return $this->members; + return $this->_members; } function getAvailableMembers() { @@ -177,7 +191,7 @@ class Dept extends VerySimpleModel { } function getSignature() { - return $this->dept_signature; + return $this->signature; } function canAppendSignature() { @@ -225,7 +239,12 @@ class Dept extends VerySimpleModel { } function getHashtable() { - return $this->ht; + $ht = $this->ht; + if (static::$meta['joins']) + foreach (static::$meta['joins'] as $k => $v) + unset($ht[$k]); + + return $ht; } function getInfo() { @@ -233,42 +252,54 @@ class Dept extends VerySimpleModel { } function getAllowedGroups() { - if ($this->groups) - return $this->groups; - $groups = GroupDept::object() - ->filter(array('dept_id' => $this->getId())) - ->values_flat('group_id'); + if (!isset($this->_groupids)) { + $this->_groupids = array(); + + $groups = GroupDeptAccess::objects() + ->filter(array('dept_id' => $this->getId())) + ->values_flat('group_id'); - foreach ($groups as $row) { - list($id) = $row; - $this->groups[] = $id; + foreach ($groups as $row) + $this->_groupids[] = $row[0]; } - return $this->groups; + + return $this->_groupids; } - function updateSettings($vars) { + function updateGroups($groups_ids, $vars) { - // Groups allowes to access department - if($vars['groups'] && is_array($vars['groups'])) { - $groups = GroupDept::object() + // Groups allowed to access department + if (is_array($groups_ids)) { + $groups = GroupDeptAccess::objects() ->filter(array('dept_id' => $this->getId())); foreach ($groups as $group) { - if ($idx = array_search($group->group_id, $vars['groups'])) - unset($vars['groups'][$idx]); - else + if ($idx = array_search($group->group_id, $groups_ids)) { + unset($groups_ids[$idx]); + $roleId = $vars['group'.$group->group_id.'_role_id']; + if ($roleId != $group->role_id) { + $group->set('role_id', $roleId ?: 0); + $group->save(); + } + } else { $group->delete(); + } } - foreach ($vars['groups'] as $id) { - GroupDept::create(array( - 'dept_id'=>$this->getId(), 'group_id'=>$id + foreach ($groups_ids as $id) { + $roleId = $vars['group'.$id.'_role_id']; + GroupDeptAccess::create(array( + 'dept_id' => $this->getId(), + 'group_id' => $id, + 'role_id' => $roleId ?: 0, ))->save(); } } - // Misc. config settings - $this->getConfig()->set('assign_members_only', $vars['assign_members_only']); + } + function updateSettings($vars) { + $this->updateGroups($vars['groups'] ?: array(), $vars); + $this->getConfig()->set('assign_members_only', $vars['assign_members_only']); return true; } @@ -279,16 +310,14 @@ class Dept extends VerySimpleModel { // Default department cannot be deleted || $this->getId()==$cfg->getDefaultDeptId() // Department with users cannot be deleted - || Staff::objects() - ->filter(array('dept_id'=>$this->getId())) - ->count() + || $this->members->count() ) { return 0; } parent::delete(); $id = $this->getId(); - $sql='DELETE FROM '.DEPT_TABLE.' WHERE dept_id='.db_input($id).' LIMIT 1'; + $sql='DELETE FROM '.DEPT_TABLE.' WHERE id='.db_input($id).' LIMIT 1'; if(db_query($sql) && ($num=db_affected_rows())) { // DO SOME HOUSE CLEANING //Move tickets to default Dept. TODO: Move one ticket at a time and send alerts + log notes. @@ -318,8 +347,8 @@ class Dept extends VerySimpleModel { /*----Static functions-------*/ static function getIdByName($name) { $row = static::objects() - ->filter(array('dept_name'=>$name)) - ->values_flat('dept_id') + ->filter(array('name'=>$name)) + ->values_flat('id') ->first(); return $row ? $row[0] : 0; @@ -339,38 +368,59 @@ class Dept extends VerySimpleModel { } static function getDepartments( $criteria=null) { + static $depts = null; + + if (!isset($depts) || $criteria) { + $depts = array(); + $query = self::objects(); + if (isset($criteria['publiconly'])) + $query->filter(array( + 'ispublic' => ($criteria['publiconly'] ? 1 : 0))); + + if ($manager=$criteria['manager']) + $query->filter(array( + 'manager_id' => is_object($manager)?$manager->getId():$manager)); + + $query->order_by('name') + ->values_flat('id', 'name'); + + $names = array(); + foreach ($query as $row) + $names[$row[0]] = $row[1]; + + // Fetch local names + foreach (CustomDataTranslation::getDepartmentNames(array_keys($names)) as $id=>$name) { + // Translate the department + $names[$id] = $name; + } - $depts = self::objects(); - if ($criteria['publiconly']) - $depts->filter(array('public' => 1)); - - if ($manager=$criteria['manager']) - $depts->filter(array('manager_id' => is_object($manager)?$manager->getId():$manager)); - - $depts->order_by('dept_name') - ->values_flat('dept_id', 'dept_name'); + if ($criteria) + return $names; - $names = array(); - foreach ($depts as $row) { - list($id, $name) = $row; - $names[$id] = $name; + $depts = $names; } - // Fetch local names - foreach (CustomDataTranslation::getDepartmentNames(array_keys($names)) as $id=>$name) { - // Translate the department - $names[$id] = $name; - } - return $names; + return $depts; } - function getPublicDepartments() { - return self::getDepartments(array('publiconly'=>true)); + static function getPublicDepartments() { + static $depts =null; + + if (!$depts) + $depts = self::getDepartments(array('publiconly'=>true)); + + return $depts; } - static function create($vars, &$errors) { + static function create($vars=false, &$errors=array()) { $dept = parent::create($vars); - $dept->create = SqlFunction::NOW(); + $dept->created = SqlFunction::NOW(); + return $dept; + } + + static function __create($vars, &$errors) { + $dept = self::create($vars); + $dept->save(); return $dept; } @@ -383,7 +433,7 @@ class Dept extends VerySimpleModel { function update($vars, &$errors) { global $cfg; - if (isset($this->dept_id) && $this->getId() != $vars['id']) + if (isset($this->id) && $this->getId() != $vars['id']) $errors['err']=__('Missing or invalid Dept ID (internal error).'); if (!$vars['name']) { @@ -391,7 +441,7 @@ class Dept extends VerySimpleModel { } elseif (strlen($vars['name'])<4) { $errors['name']=__('Name is too short.'); } elseif (($did=static::getIdByName($vars['name'])) - && (!isset($this->dept_id) || $did!=$this->getId())) { + && (!isset($this->id) || $did!=$this->getId())) { $errors['name']=__('Department already exists'); } @@ -408,8 +458,8 @@ class Dept extends VerySimpleModel { $this->sla_id = isset($vars['sla_id'])?$vars['sla_id']:0; $this->autoresp_email_id = isset($vars['autoresp_email_id'])?$vars['autoresp_email_id']:0; $this->manager_id = $vars['manager_id']?$vars['manager_id']:0; - $this->dept_name = Format::striptags($vars['name']); - $this->dept_signature = Format::sanitize($vars['signature']); + $this->name = Format::striptags($vars['name']); + $this->signature = Format::sanitize($vars['signature']); $this->group_membership = $vars['group_membership']; $this->ticket_auto_response = isset($vars['ticket_auto_response'])?$vars['ticket_auto_response']:1; $this->message_auto_response = isset($vars['message_auto_response'])?$vars['message_auto_response']:1; @@ -417,7 +467,7 @@ class Dept extends VerySimpleModel { if ($this->save()) return $this->updateSettings($vars); - if (isset($this->dept_id)) + if (isset($this->id)) $errors['err']=sprintf(__('Unable to update %s.'), __('this department')) .' '.__('Internal error occurred'); else @@ -435,10 +485,13 @@ class GroupDeptAccess extends VerySimpleModel { 'pk' => array('dept_id', 'group_id'), 'joins' => array( 'dept' => array( - 'constraint' => array('dept_id' => 'Dept.dept_id'), + 'constraint' => array('dept_id' => 'Dept.id'), ), 'group' => array( - 'constraint' => array('group_id' => 'Group.group_id'), + 'constraint' => array('group_id' => 'Group.id'), + ), + 'role' => array( + 'constraint' => array('role_id' => 'Role.id'), ), ), ); diff --git a/include/class.email.php b/include/class.email.php index d38806f75f668c558872f2035208edeab924f2b2..53a966d71e23e8f5f6f5d01c09b570247c1d3948 100644 --- a/include/class.email.php +++ b/include/class.email.php @@ -11,10 +11,41 @@ vim: expandtab sw=4 ts=4 sts=4: **********************************************************************/ - include_once(INCLUDE_DIR.'class.dept.php'); include_once(INCLUDE_DIR.'class.mailfetch.php'); +class EmailModel extends VerySimpleModel { + static $meta = array( + 'table' => EMAIL_TABLE, + 'pk' => array('email_id'), + 'joins' => array( + 'priority' => array( + 'constraint' => array('priority_id' => 'Priority.priority_id'), + 'null' => true, + ), + 'dept' => array( + 'constraint' => array('dept_id' => 'Dept.id'), + 'null' => true, + ), + 'topic' => array( + 'constraint' => array('topic_id' => 'Topic.topic_id'), + 'null' => true, + ), + ) + ); + + function getId() { + return $this->email_id; + } + + function __toString() { + if ($this->name) + return sprintf('%s <%s>', $this->name, $this->email); + + return $this->email; + } +} + class Email { var $id; var $address; diff --git a/include/class.format.php b/include/class.format.php index 186a02d0e4016766d3d94ab3d5b0ff1572f0f85f..e6761abfea7a62b8f7b19487f202f10f6e19dad4 100644 --- a/include/class.format.php +++ b/include/class.format.php @@ -513,7 +513,7 @@ class Format { $format = self::getStrftimeFormat($format); // TODO: Properly convert to local time $time = DateTime::createFromFormat('U', $timestamp, new DateTimeZone('UTC')); - $time->setTimeZone(new DateTimeZone($cfg->getTimeZone())); + $time->setTimeZone(new DateTimeZone($cfg->getTimezone())); $timestamp = $time->getTimestamp(); return strftime($format ?: $strftimeFallback, $timestamp); } @@ -547,7 +547,7 @@ class Format { return self::__formatDate($timestamp, $format ?: $cfg->getTimeFormat(), $fromDb, IntlDateFormatter::NONE, IntlDateFormatter::SHORT, - '%x', $timezone ?: $cfg->getTimeZone()); + '%x', $timezone ?: $cfg->getTimezone()); } function date($timestamp, $fromDb=true, $format=false, $timezone=false) { @@ -556,25 +556,25 @@ class Format { return self::__formatDate($timestamp, $format ?: $cfg->getDateFormat(), $fromDb, IntlDateFormatter::SHORT, IntlDateFormatter::NONE, - '%X', $timezone ?: $cfg->getTimeZone()); + '%X', $timezone ?: $cfg->getTimezone()); } function datetime($timestamp, $fromDb=true, $timezone=false) { global $cfg; return self::__formatDate($timestamp, - $format ?: $cfg->getDateTimeFormat(), $fromDb, - IntlDateFormatter::SHORT, IntlDateFormatter::SHORT, - '%X %x', $timezone ?: $cfg->getTimeZone()); + $cfg->getDateTimeFormat(), $fromDb, + IntlDateFormatter::SHORT, IntlDateFormatter::SHORT, + '%X %x', $timezone ?: $cfg->getTimezone()); } function daydatetime($timestamp, $fromDb=true, $timezone=false) { global $cfg; return self::__formatDate($timestamp, - $format ?: $cfg->getDayDateTimeFormat(), $fromDb, - IntlDateFormatter::FULL, IntlDateFormatter::SHORT, - '%X %x', $timezone ?: $cfg->getTimeZone()); + $cfg->getDayDateTimeFormat(), $fromDb, + IntlDateFormatter::FULL, IntlDateFormatter::SHORT, + '%X %x', $timezone ?: $cfg->getTimezone()); } function getStrftimeFormat($format) { @@ -742,7 +742,7 @@ class Format { if (class_exists('IntlBreakIterator')) { // Split by word boundaries if ($tokenizer = IntlBreakIterator::createWordInstance( - $lang ?: ($cfg ? $cfg->getSystemLanguage() : 'en_US')) + $lang ?: ($cfg ? $cfg->getPrimaryLanguage() : 'en_US')) ) { $tokenizer->setText($text); $tokens = array(); diff --git a/include/class.group.php b/include/class.group.php index 6352289749de218a7841c40fa1cc4f8daf966d37..3bbf296f5ecbfaf2173311ac1f23a4124b31746b 100644 --- a/include/class.group.php +++ b/include/class.group.php @@ -18,37 +18,79 @@ class Group extends VerySimpleModel { static $meta = array( 'table' => GROUP_TABLE, - 'pk' => array('group_id'), + 'pk' => array('id'), + 'joins' => array( + 'members' => array( + 'null' => true, + 'list' => true, + 'reverse' => 'Staff.group', + ), + 'depts' => array( + 'null' => true, + 'list' => true, + 'reverse' => 'GroupDeptAccess.group', + ), + 'role' => array( + 'constraint' => array('role_id' => 'Role.id') + ), + ), ); - var $members; + const FLAG_ENABLED = 0X0001; + var $departments; function getHashtable() { $base = $this->ht; - $base['name'] = $base['group_name']; - $base['isactive'] = $base['group_enabled']; + $base['name'] = $base['name']; + $base['isactive'] = $base['flags']; return $base; } - function getInfo(){ + function getInfo() { return $this->getHashtable(); } - function getId(){ - return $this->group_id; + function getId() { + return $this->id; + } + + function getRoleId() { + return $this->role_id; + } + + function getRole($deptId=0) { + + if ($deptId // Department specific role. + && ($roles=$this->getDepartmentsAccess()) + && isset($roles[$deptId]) + && $roles[$deptId] + && ($role=Role::lookup($roles[$deptId])) + && $role->isEnabled()) + return $role; + + // Default role for this group. + return $this->role; + } + + function getName() { + return $this->name; } - function getName(){ - return $this->group_name; + function getCreateDate() { + return $this->created; } - function getNumUsers(){ - return Staff::objects()->filter(array('group_id'=>$this->getId()))->count(); + function getUpdateDate() { + return $this->updated; } - function isEnabled(){ - return $this->group_enabled; + function getNumMembers() { + return $this->members ? $this->members->count() : 0; + } + + function isEnabled() { + $this->get('flags') & self::FLAG_ENABLED !== 0; } function isActive(){ @@ -81,34 +123,47 @@ class Group extends VerySimpleModel { return $this->members; } - //Get departments the group is allowed to access. + //Get departments & roles the group is allowed to access. function getDepartments() { + return array_keys($this->getDepartmentsAccess()); + } + + function getDepartmentsAccess() { if (!isset($this->departments)) { $this->departments = array(); foreach (GroupDeptAccess::objects() ->filter(array('group_id'=>$this->getId())) - ->values_flat('dept_id') as $gda + ->values_flat('dept_id', 'role_id') as $gda ) { - $this->departments[] = $gda[0]; + $this->departments[$gda[0]] = $gda[1]; } } + return $this->departments; } - - function updateDeptAccess($dept_ids) { + function updateDeptAccess($dept_ids, $vars=array()) { if (is_array($dept_ids)) { $groups = GroupDeptAccess::objects() ->filter(array('group_id' => $this->getId())); foreach ($groups as $group) { - if ($idx = array_search($group->dept_id, $dept_ids)) + if ($idx = array_search($group->dept_id, $dept_ids)) { unset($dept_ids[$idx]); - else + $roleId = $vars['dept'.$group->dept_id.'_role_id']; + if ($roleId != $group->role_id) { + $group->set('role_id', $roleId ?: 0); + $group->save(); + } + } else { $group->delete(); + } } foreach ($dept_ids as $id) { + $roleId = $vars['dept'.$id.'_role_id']; GroupDeptAccess::create(array( - 'group_id'=>$this->getId(), 'dept_id'=>$id + 'group_id' => $this->getId(), + 'dept_id' => $id, + 'role_id' => $roleId ?: 0 ))->save(); } } @@ -118,7 +173,7 @@ class Group extends VerySimpleModel { function delete() { // Can't delete with members - if ($this->getNumUsers()) + if ($this->getNumMembers()) return false; if (!parent::delete()) @@ -132,36 +187,8 @@ class Group extends VerySimpleModel { return true; } - /*** Static functions ***/ - static function getIdByName($name){ - $id = static::objects()->filter(array('group_name'=>trim($name))) - ->values_flat('group_id')->first(); - - return $id ? $id[0] : 0; - } - - static function getGroupNames($localize=true) { - static $groups=array(); - - if (!$groups) { - $query = static::objects() - ->values_flat('group_id', 'group_name', 'group_enabled') - ->order_by('group_name'); - foreach ($query as $row) { - list($id, $name, $enabled) = $row; - $groups[$id] = sprintf('%s%s', - self::getLocalById($id, 'name', $name), - $enabled ? '' : ' ' . __('(disabled)')); - } - } - // TODO: Sort groups if $localize - return $groups; - } - - static function create($vars=false) { - $group = parent::create($vars); - $group->created = SqlFunction::NOW(); - return $group; + function __toString() { + return $this->getName(); } function save($refetch=false) { @@ -172,40 +199,37 @@ class Group extends VerySimpleModel { } function update($vars,&$errors) { - if (isset($this->group_id) && $this->getId() != $vars['id']) - $errors['err']=__('Missing or invalid group ID'); + if (isset($this->id) && $this->getId() != $vars['id']) + $errors['err'] = __('Missing or invalid group ID'); if (!$vars['name']) { - $errors['name']=__('Group name required'); + $errors['name'] = __('Group name required'); } elseif(strlen($vars['name'])<3) { - $errors['name']=__('Group name must be at least 3 chars.'); + $errors['name'] = __('Group name must be at least 3 chars.'); } elseif (($gid=static::getIdByName($vars['name'])) - && (!isset($this->group_id) || $gid!=$this->getId())) { - $errors['name']=__('Group name already exists'); + && (!isset($this->id) || $gid!=$this->getId())) { + $errors['name'] = __('Group name already exists'); } + if (!$vars['role_id']) + $errors['role_id'] = __('Role selection required'); + if ($errors) return false; - $this->group_name=Format::striptags($vars['name']); - $this->group_enabled=$vars['isactive']; - $this->can_create_tickets=$vars['can_create_tickets']; - $this->can_delete_tickets=$vars['can_delete_tickets']; - $this->can_edit_tickets=$vars['can_edit_tickets']; - $this->can_assign_tickets=$vars['can_assign_tickets']; - $this->can_transfer_tickets=$vars['can_transfer_tickets']; - $this->can_close_tickets=$vars['can_close_tickets']; - $this->can_ban_emails=$vars['can_ban_emails']; - $this->can_manage_premade=$vars['can_manage_premade']; - $this->can_manage_faq=$vars['can_manage_faq']; - $this->can_post_ticket_reply=$vars['can_post_ticket_reply']; - $this->can_view_staff_stats=$vars['can_view_staff_stats']; - $this->notes=Format::sanitize($vars['notes']); + $this->name = Format::striptags($vars['name']); + $this->role_id = $vars['role_id']; + $this->notes = Format::sanitize($vars['notes']); + + if ($vars['isactive']) + $this->flags = ($this->flags | self::FLAG_ENABLED); + else + $this->flags = ($this->flags & ~self::FLAG_ENABLED); if ($this->save()) - return $this->updateDeptAccess($vars['depts'] ?: array()); + return $this->updateDeptAccess($vars['depts'] ?: array(), $vars); - if (isset($this->group_id)) { + if (isset($this->id)) { $errors['err']=sprintf(__('Unable to update %s.'), __('this group')) .' '.__('Internal error occurred'); } @@ -215,5 +239,72 @@ class Group extends VerySimpleModel { } return false; } + + /*** Static functions ***/ + static function getIdByName($name){ + $id = static::objects()->filter(array('name'=>trim($name))) + ->values_flat('id')->first(); + + return $id ? $id[0] : 0; + } + + static function create($vars=false) { + $group = parent::create($vars); + $group->created = SqlFunction::NOW(); + return $group; + } + + static function __create($vars, &$errors) { + $g = self::create($vars); + $g->save(); + if ($vars['depts']) + $g->updateDeptAccess($vars['depts'], $vars); + + return $g; + } + + static function getGroups($criteria=array()) { + static $groups = null; + if (!isset($groups) || $criteria) { + $groups = array(); + $query = static::objects() + ->values_flat('id', 'name', 'flags') + ->order_by('name'); + + $filters = array(); + if (isset($criteria['active'])) + $filters += array( + 'isactive' => $criteria['active'] ? 1 : 0); + + if ($filters) + $query->filter($filters); + + $names = array(); + foreach ($query as $row) { + list($id, $name, $flags) = $row; + $names[$id] = sprintf('%s%s', + self::getLocalById($id, 'name', $name), + $flags ? '' : ' ' . __('(disabled)')); + } + + //TODO: sort if $criteria['localize']; + if ($criteria) + return $names; + + $groups = $names; + } + + return $groups; + } + + static function getActiveGroups() { + static $groups = null; + + if (!isset($groups)) + $groups = self::getGroups(array('active'=>true)); + + return $groups; + } + } ?> diff --git a/include/class.i18n.php b/include/class.i18n.php index d5d524a9782d7d6ecd97d2e912937a5318476a0d..4b764717a82cdc7a51651f7f6eb3bc6f11c8d333 100644 --- a/include/class.i18n.php +++ b/include/class.i18n.php @@ -49,21 +49,23 @@ class Internationalization { function loadDefaultData() { # notrans -- do not translate the contents of this array $models = array( - 'department.yaml' => 'Dept::create', - 'sla.yaml' => 'SLA::create', + 'department.yaml' => 'Dept::__create', + 'sla.yaml' => 'SLA::__create', 'form.yaml' => 'DynamicForm::create', 'list.yaml' => 'DynamicList::create', // Note that department, sla, and forms are required for // help_topic 'help_topic.yaml' => 'Topic::__create', 'filter.yaml' => 'Filter::create', - 'team.yaml' => 'Team::create', + 'team.yaml' => 'Team::__create', // Organization 'organization.yaml' => 'Organization::__create', // Ticket - 'ticket_status.yaml' => 'TicketStatus::__create', + 'ticket_status.yaml' => 'TicketStatus::__create', + // Role + 'role.yaml' => 'Role::__create', // Note that group requires department - 'group.yaml' => 'Group::create', + 'group.yaml' => 'Group::__create', 'file.yaml' => 'AttachmentFile::create', 'sequence.yaml' => 'Sequence::__create', ); @@ -384,10 +386,8 @@ class Internationalization { return $thisstaff->getLocale() ?: self::getCurrentLanguage(); } - if (!$locale) - $locale = $cfg->getDefaultLocale(); - if (!$locale) + if (!($locale = $cfg->getDefaultLocale())) $locale = self::getCurrentLanguage(); return $locale; diff --git a/include/class.lock.php b/include/class.lock.php index c61771e67f5a3b166734e41d8c6ee3ebd03c977c..6e0588978e29b71d125b0602388923b50edc6898 100644 --- a/include/class.lock.php +++ b/include/class.lock.php @@ -71,6 +71,7 @@ class TicketLock extends VerySimpleModel { //Renew existing lock. function renew($lockTime=0) { + global $cfg; if(!$lockTime || !is_numeric($lockTime)) //XXX: test to make it works. $lockTime = $cfg->getLockTime(); diff --git a/include/class.nav.php b/include/class.nav.php index bc4956f084b4b810b17d242637f38bcc365bbee8..551d6265be765983d602570863eda20733500dca 100644 --- a/include/class.nav.php +++ b/include/class.nav.php @@ -161,9 +161,9 @@ class StaffNav { case 'kbase': $subnav[]=array('desc'=>__('FAQs'),'href'=>'kb.php', 'urls'=>array('faq.php'), 'iconclass'=>'kb'); if($staff) { - if($staff->canManageFAQ()) + if($staff->getRole()->canManageFAQ()) $subnav[]=array('desc'=>__('Categories'),'href'=>'categories.php','iconclass'=>'faq-categories'); - if($staff->canManageCannedResponses()) + if($staff->getRole()->canManageCannedResponses()) $subnav[]=array('desc'=>__('Canned Responses'),'href'=>'canned.php','iconclass'=>'canned'); } break; @@ -260,6 +260,7 @@ class AdminNav extends StaffNav{ $subnav[]=array('desc'=>__('Agents'),'href'=>'staff.php','iconclass'=>'users'); $subnav[]=array('desc'=>__('Teams'),'href'=>'teams.php','iconclass'=>'teams'); $subnav[]=array('desc'=>__('Groups'),'href'=>'groups.php','iconclass'=>'groups'); + $subnav[]=array('desc'=>__('Roles'),'href'=>'roles.php','iconclass'=>'lists'); $subnav[]=array('desc'=>__('Departments'),'href'=>'departments.php','iconclass'=>'departments'); break; case 'apps': diff --git a/include/class.orm.php b/include/class.orm.php index f9e51f55332c15e6546cee9cec5688a29d19e10b..a1328a6fdb03e1b4c7ab8d2bd76aa30112840da2 100644 --- a/include/class.orm.php +++ b/include/class.orm.php @@ -491,6 +491,24 @@ class SqlFunction { } } +class SqlExpr extends SqlFunction { + function __construct($args) { + $this->args = $args; + } + + function toSql($compiler, $model=false, $alias=false) { + $O = array(); + foreach ($this->args as $field=>$value) { + list($field, $op) = $compiler->getField($field, $model); + if (is_callable($op)) + $O[] = call_user_func($op, $field, $value, $model); + else + $O[] = sprintf($op, $field, $compiler->input($value)); + } + return implode(' ', $O) . ($alias ? ' AS ' . $alias : ''); + } +} + class SqlExpression extends SqlFunction { var $operator; var $operands; @@ -515,6 +533,10 @@ class SqlExpression extends SqlFunction { $operator = '+'; break; case 'times': $operator = '*'; break; + case 'bitand': + $operator = '&'; break; + case 'bitor': + $operator = '|'; break; default: throw new InvalidArgumentException('Invalid operator specified'); } @@ -566,7 +588,7 @@ class SqlCode extends SqlFunction { } } -class Aggregate extends SqlFunction { +class SqlAggregate extends SqlFunction { var $func; var $expr; @@ -590,7 +612,10 @@ class Aggregate extends SqlFunction { } function toSql($compiler, $model=false, $alias=false) { - $options = array('constraint'=>$this->constraint); + $options = array( + 'constraint' => $this->constraint, + 'distinct' => $this->distinct); + list($field) = $compiler->getField($this->expr, $model, $options); return sprintf('%s(%s)%s', $this->func, $field, $alias && $this->alias ? ' AS '.$compiler->quote($this->alias) : ''); @@ -781,12 +806,12 @@ class QuerySet implements IteratorAggregate, ArrayAccess, Serializable { if (!is_array($annotations)) $annotations = func_get_args(); foreach ($annotations as $name=>$A) { - if ($A instanceof Aggregate) { + if ($A instanceof SqlAggregate) { if (is_int($name)) $name = $A->getFieldName(); $A->setAlias($name); - $this->annotations[$name] = $A; } + $this->annotations[$name] = $A; } return $this; } @@ -991,7 +1016,7 @@ class ModelInstanceManager extends ResultSet { * The annotated fields are in the AnnotatedModel instance and the * database-backed fields are managed by the Model instance. */ - function getOrBuild($modelClass, $fields) { + function getOrBuild($modelClass, $fields, $cache=true) { // Check for NULL primary key, used with related model fetching. If // the PK is NULL, then consider the object to also be NULL foreach ($modelClass::$meta['pk'] as $pkf) { @@ -1006,8 +1031,7 @@ class ModelInstanceManager extends ResultSet { // be the root model's fields. The annotated fields will be wrapped // using an AnnotatedModel instance. if ($annotations && $modelClass == $this->model) { - foreach ($annotations as $A) { - $name = $A->getAlias(); + foreach ($annotations as $name=>$A) { if (isset($fields[$name])) { $extras[$name] = $fields[$name]; unset($fields[$name]); @@ -1312,7 +1336,8 @@ class SqlCompiler { // Call pushJoin for each segment in the join path. A new JOIN // fragment will need to be emitted and/or cached - $push = function($p, $path, $extra=false) use (&$model) { + $self = $this; + $push = function($p, $path, $extra=false) use (&$model, $self) { $model::_inspect(); if (!($info = $model::$meta['joins'][$p])) { throw new OrmException(sprintf( @@ -1322,7 +1347,7 @@ class SqlCompiler { $crumb = implode('__', $path); $path[] = $p; $tip = implode('__', $path); - $alias = $this->pushJoin($crumb, $tip, $model, $info, $extra); + $alias = $self->pushJoin($crumb, $tip, $model, $info, $extra); // Roll to foreign model foreach ($info['constraint'] as $local => $foreign) { list($model, $f) = explode('.', $foreign); @@ -1351,6 +1376,10 @@ class SqlCompiler { $field = $alias.'.'.$this->quote($field); else $field = $this->quote($field); + + if (isset($options['distinct']) && $options['distinct']) + $field = " DISTINCT $field"; + if (isset($options['model']) && $options['model']) $operator = $model; return array($field, $operator); @@ -1775,6 +1804,9 @@ class MySqlCompiler extends SqlCompiler { } // TODO: Throw exception if $field can be indentified as // invalid + if ($field instanceof SqlFunction) + $field = $field->toSql($this, $model); + $orders[] = $field.' '.$dir; } $sort = ' ORDER BY '.implode(', ', $orders); @@ -1857,10 +1889,11 @@ class MySqlCompiler extends SqlCompiler { } } $fields = array_keys($fields); + $group_by = array(); // Add in annotations if ($queryset->annotations) { - foreach ($queryset->annotations as $A) { - $fields[] = $T = $A->toSql($this, $model, true); + foreach ($queryset->annotations as $alias=>$A) { + $fields[] = $T = $A->toSql($this, $model, $alias); // TODO: Add to last fieldset in fieldMap if ($fieldMap) $fieldMap[0][0][] = $A->getAlias(); diff --git a/include/class.priority.php b/include/class.priority.php index 67bb0d8e33c87ea1a6e1a17bf9c16ba130c7c21e..ac6976d29bb53517b41ee82d5dc4bf536e124bf0 100644 --- a/include/class.priority.php +++ b/include/class.priority.php @@ -59,8 +59,7 @@ class Priority extends VerySimpleModel { $objects->filter(array('ispublic'=>1)); foreach ($objects as $row) { - list($id, $name) = $row; - $priorities[$id] = $name; + $priorities[$row[0]] = $row[1]; } return $priorities; diff --git a/include/class.role.php b/include/class.role.php new file mode 100644 index 0000000000000000000000000000000000000000..36ea3f9a5a74eac80e077ef46221b6e300f8c9b3 --- /dev/null +++ b/include/class.role.php @@ -0,0 +1,348 @@ +<?php +/********************************************************************* + class.role.php + + Role-based access + + 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: +**********************************************************************/ + +class RoleModel extends VerySimpleModel { + static $meta = array( + 'table' => ROLE_TABLE, + 'pk' => array('id'), + 'joins' => array( + 'groups' => array( + 'null' => true, + 'list' => true, + 'reverse' => 'Group.role', + ), + ), + ); + + // Flags + const FLAG_ENABLED = 0x0001; + + 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 getName() { + return $this->name; + } + + function getCreateDate() { + return $this->created; + } + + function getUpdateDate() { + return $this->updated; + } + + function getInfo() { + return $this->ht; + } + + function isEnabled() { + return $this->hasFlag(self::FLAG_ENABLED); + } + + function isDeleteable() { + return ($this->groups->count() == 0); + } + +} + +class Role extends RoleModel { + var $form; + var $entry; + + var $_perm; + + function getPermission() { + if (!$this->_perm) + $this->_perm = new RolePermission('role.'.$this->getId()); + + return $this->_perm; + } + + function getPermissionInfo() { + return $this->getPermission()->getInfo(); + } + + function to_json() { + + $info = array( + 'id' => $this->getId(), + 'name' => $this->getName() + ); + + return JsonDataEncoder::encode($info); + } + + function __toString() { + return (string) $this->getName(); + } + + function __call($what, $args) { + $rv = null; + if($this->getPermission() && is_callable(array($this->_perm, $what))) + $rv = $args + ? call_user_func_array(array($this->_perm, $what), $args) + : call_user_func(array($this->_perm, $what)); + + return $rv; + } + + private function updatePerms($vars, &$errors=array()) { + + $config = array(); + foreach (RolePermission::allPermissions() as $g => $perms) { + foreach($perms as $k => $v) + $config[$k] = in_array($k, $vars) ? 1 : 0; + } + + $this->getPermission()->updateAll($config); + $this->getPermission()->load(); + } + + function update($vars, &$errors) { + + if (!$vars['name']) + $errors['name'] = __('Name required'); + elseif (($r=Role::lookup(array('name'=>$vars['name']))) + && $r->getId() != $vars['id']) + $errors['name'] = __('Name already in-use'); + elseif (!$vars['perms'] || !count($vars['perms'])) + $errors['err'] = __('Must check at least one permission for the role'); + + if ($errors) + return false; + + $this->name = $vars['name']; + $this->notes = $vars['notes']; + if (!$this->save(true)) + return false; + + $this->updatePerms($vars['perms'], $errors); + + return true; + } + + function save($refetch=false) { + if (count($this->dirty)) + $this->set('updated', new SqlFunction('NOW')); + if (isset($this->dirty['notes'])) + $this->notes = Format::sanitize($this->notes); + + return parent::save($refetch | $this->dirty); + } + + function delete() { + + if (!$this->isDeleteable()) + return false; + + if (!parent::delete()) + return false; + + // Remove dept access entries + GroupDeptAccess::objects() + ->filter(array('role_id'=>$this->getId())) + ->update(array('role_id' => 0)); + + // Delete permission settings + $this->getPermission()->destroy(); + + return true; + } + + static function create($vars=false) { + $role = parent::create($vars); + $role->created = SqlFunction::NOW(); + return $role; + } + + static function __create($vars, &$errors) { + $role = self::create($vars); + $role->save(); + if ($vars['permissions']) + $role->updatePerms($vars['permissions']); + + return $role; + } + + static function getRoles($criteria=null) { + static $roles = null; + + if (!isset($roles) || $criteria) { + + $filters = array(); + if (isset($criteria['enabled'])) { + if ($criteria['enabled']) + $filters += array( + 'flags__hasbit' => self::FLAG_ENABLED); + else + $filters [] = Q::not(array( + 'flags__hasbit' => self::FLAG_ENABLED)); + } + $query = self::objects() + ->order_by('name') + ->values_flat('id', 'name'); + + if ($filters) + $query->filter($filters); + + $names = array(); + foreach ($query as $row) + $names[$row[0]] = $row[1]; + + // TODO: Localize + + if ($criteria) return $names; + + $roles = $names; + } + + return $roles; + } + + static function getActiveRoles() { + static $roles = null; + + if (!isset($roles)) + $roles = self::getRoles(array('enabled' => true)); + + return $roles; + } +} + + +class RolePermission extends Config { + + static $_permissions = array( + /* @trans */ 'Tickets' => array( + 'ticket.create' => array( + /* @trans */ 'Create', + /* @trans */ 'Ability to open tickets on behalf of users'), + 'ticket.edit' => array( + /* @trans */ 'Edit', + /* @trans */ 'Ability to edit tickets'), + 'ticket.assign' => array( + /* @trans */ 'Assign', + /* @trans */ 'Ability to assign tickets to agents or teams'), + 'ticket.transfer' => array( + /* @trans */ 'Transfer', + /* @trans */ 'Ability to transfer tickets between departments'), + 'ticket.reply' => array( + /* @trans */ 'Post Reply', + /* @trans */ 'Ability to post a ticket reply'), + 'ticket.close' => array( + /* @trans */ 'Close', + /* @trans */ 'Ability to close tickets'), + 'ticket.delete' => array( + /* @trans */ 'Delete', + /* @trans */ 'Ability to delete tickets'), + ), + /* @trans */ 'Knowledgebase' => array( + 'kb.premade' => array( + /* @trans */ 'Premade', + /* @trans */ 'Ability to add/update/disable/delete canned responses'), + 'kb.faq' => array( + /* @trans */ 'FAQ', + /* @trans */ 'Ability to add/update/disable/delete knowledgebase categories and FAQs'), + ), + /* @trans */ 'Misc.' => array( + 'stats.agents' => array( + /* @trans */ 'Stats', + /* @trans */ 'Ability to view stats of other agents in allowed departments'), + 'emails.banlist' => array( + /* @trans */ 'Banlist', + /* @trans */ 'Ability to add/remove emails from banlist via ticket interface'), + ), + ); + + static function allPermissions() { + return static::$_permissions; + } + + function get($var) { + return (bool) parent::get($var); + } + + /* tickets */ + function canCreateTickets() { + return ($this->get('ticket.create')); + } + + function canEditTickets() { + return ($this->get('ticket.edit')); + } + + function canAssignTickets() { + return ($this->get('ticket.assign')); + } + + function canTransferTickets() { + return ($this->get('ticket.transfer')); + } + + function canPostReply() { + return ($this->get('ticket.reply')); + } + + function canCloseTickets() { + return ($this->get('ticket.close')); + } + + function canDeleteTickets() { + return ($this->get('ticket.delete')); + } + + /* Knowledge base */ + function canManagePremade() { + return ($this->get('kb.premade')); + } + + function canManageCannedResponses() { + return ($this->canManagePremade()); + } + + function canManageFAQ() { + return ($this->get('kb.faq')); + } + + function canManageFAQs() { + return ($this->canManageFAQ()); + } + + /* stats */ + function canViewStaffStats() { + return ($this->get('stats.agents')); + } + + /* email */ + function canBanEmails() { + return ($this->get('emails.banlist')); + } +} +?> diff --git a/include/class.search.php b/include/class.search.php index 68076e619847a82a9ee9bba0736b98fe3cdd0623..ff2ccbdcac8c87a13e1f3774cd85696da9dd54be 100644 --- a/include/class.search.php +++ b/include/class.search.php @@ -317,38 +317,13 @@ class MysqlSearchBackend extends SearchBackend { $criteria->filter(array('ticket_id'=>new SqlCode($key))); $criteria->distinct('ticket_id'); } - /* - case 'email': - case 'org_id': - case 'form_id': - default: - if (strpos($name, 'cdata.') === 0) { - // Search ticket CDATA table - $cdata_search = true; - $name = substr($name, 6); - if (is_array($value)) { - $where[] = '(' . implode(' OR ', array_map( - function($k) use ($name) { - return sprintf('FIND_IN_SET(%s, cdata.`%s`)', - db_input($k), $name); - }, $value) - ) . ')'; - } - else { - $where[] = sprintf("cdata.%s = %s", $name, db_input($value)); - } - } - } - } - */ - // TODO: Consider sorting preferences } // TODO: Ensure search table exists; if (false) { - // Create the search table automatically - $class::createSearchTable(); + // TODO: Create the search table automatically + // $class::createSearchTable(); } return $criteria; } diff --git a/include/class.sla.php b/include/class.sla.php index 490508660f54d2e1303bf391d48a8082eb758e05..083663695f4d245710bc6742b4650bc8fedad9b0 100644 --- a/include/class.sla.php +++ b/include/class.sla.php @@ -3,7 +3,6 @@ class.sla.php SLA - Peter Rotich <peter@osticket.com> Copyright (c) 2006-2013 osTicket http://www.osticket.com @@ -14,75 +13,54 @@ vim: expandtab sw=4 ts=4 sts=4: **********************************************************************/ -class SlaModel extends VerySimpleModel { +class SLA extends VerySimpleModel { + static $meta = array( 'table' => SLA_TABLE, - 'pk' => array('sla_id'), + 'pk' => array('id'), ); -} - -class SLA { - - var $id; - - var $info; - var $config; - - function SLA($id) { - $this->id=0; - $this->load($id); - } - function load($id=0) { + //TODO: Use flags - if(!$id && !($id=$this->getId())) - return false; - - $sql='SELECT * FROM '.SLA_TABLE.' WHERE id='.db_input($id); - if(!($res=db_query($sql)) || !db_num_rows($res)) - return false; - - $this->ht=db_fetch_array($res); - $this->id=$this->ht['id']; - return true; - } - - function reload() { - return $this->load(); - } + var $_config; function getId() { return $this->id; } function getName() { - return $this->ht['name']; + return $this->name; } function getGracePeriod() { - return $this->ht['grace_period']; - } - - function getNotes() { - return $this->ht['notes']; + return $this->grace_period; } function getHashtable() { - return array_merge($this->getConfig()->getInfo(), $this->ht); + $this->getHashtable(); } function getInfo() { - return $this->getHashtable(); + return array_merge($this->getConfig()->getInfo(), $this->ht); } function getConfig() { - if (!isset($this->config)) - $this->config = new SlaConfig($this->getId()); - return $this->config; + if (!isset($this->_config)) + $this->_config = new SlaConfig($this->getId()); + + return $this->_config; + } + + function getCreateDate() { + return $this->created; + } + + function getUpdateDate() { + return $this->updated; } function isActive() { - return ($this->ht['isactive']); + return ($this->isactive); } function isTransient() { @@ -90,7 +68,7 @@ class SLA { } function sendAlerts() { - return (!$this->ht['disable_overdue_alerts']); + return $this->disable_overdue_alerts; } function alertOnOverdue() { @@ -98,32 +76,68 @@ class SLA { } function priorityEscalation() { - return ($this->ht['enable_priority_escalation']); + return ($this->enable_priority_escalation); } function getTranslateTag($subtag) { - return _H(sprintf('sla.%s.%s', $subtag, $this->id)); + return _H(sprintf('sla.%s.%s', $subtag, $this->getId())); } + function getLocal($subtag) { $tag = $this->getTranslateTag($subtag); $T = CustomDataTranslation::translate($tag); return $T != $tag ? $T : $this->ht[$subtag]; } + static function getLocalById($id, $subtag, $default) { $tag = _H(sprintf('sla.%s.%s', $subtag, $id)); $T = CustomDataTranslation::translate($tag); return $T != $tag ? $T : $default; } - function update($vars,&$errors) { + function update($vars, &$errors) { + + if (!$vars['grace_period']) + $errors['grace_period'] = __('Grace period required'); + elseif (!is_numeric($vars['grace_period'])) + $errors['grace_period'] = __('Numeric value required (in hours)'); - if(!SLA::save($this->getId(),$vars,$errors)) + if (!$vars['name']) + $errors['name'] = __('Name is required'); + elseif (($sid=SLA::getIdByName($vars['name'])) && $sid!=$vars['id']) + $errors['name'] = __('Name already exists'); + + if ($errors) return false; - $this->reload(); - $this->getConfig()->set('transient', isset($vars['transient']) ? 1 : 0); + $this->isactive = $vars['isactive']; + $this->name = $vars['name']; + $this->grace_period = $vars['grace_period']; + $this->disable_overdue_alerts = isset($vars['disable_overdue_alerts']) ? 1 : 0; + $this->enable_priority_escalation = isset($vars['enable_priority_escalation'])? 1: 0; + $this->notes = Format::sanitize($vars['notes']); + + if ($this->save()) { + $this->getConfig()->set('transient', isset($vars['transient']) ? 1 : 0); + return true; + } - return true; + if (isset($this->id)) { + $errors['err']=sprintf(__('Unable to update %s.'), __('this SLA plan')) + .' '.__('Internal error occurred'); + } else { + $errors['err']=sprintf(__('Unable to add %s.'), __('this SLA plan')) + .' '.__('Internal error occurred'); + } + + return false; + } + + function save($refetch=false) { + if ($this->dirty) + $this->updated = SqlFunction::NOW(); + + return parent::save($refetch || $this->dirty); } function delete() { @@ -132,6 +146,7 @@ class SLA { if(!$cfg || $cfg->getDefaultSLAId()==$this->getId()) return false; + //TODO: Use ORM to delete & update $id=$this->getId(); $sql='DELETE FROM '.SLA_TABLE.' WHERE id='.db_input($id).' LIMIT 1'; if(db_query($sql) && ($num=db_affected_rows())) { @@ -144,86 +159,43 @@ class SLA { } /** static functions **/ - function create($vars,&$errors) { - if (($id = SLA::save(0,$vars,$errors)) && ($sla = self::lookup($id))) - $sla->getConfig()->set('transient', - isset($vars['transient']) ? 1 : 0); - return $id; - } - - function getSLAs() { + static function getSLAs($criteria=array()) { - $slas=array(); + $slas = self::objects() + ->order_by('name') + ->values_flat('id', 'name', 'isactive', 'grace_period'); - $sql='SELECT id, name, isactive, grace_period FROM '.SLA_TABLE.' ORDER BY name'; - if(($res=db_query($sql)) && db_num_rows($res)) { - while($row=db_fetch_array($res)) - $slas[$row['id']] = sprintf(__('%s (%d hours - %s)' + $entries = array(); + foreach ($slas as $row) { + $entries[$row[0]] = sprintf(__('%s (%d hours - %s)' /* Tokens are <name> (<#> hours - <Active|Disabled>) */), - self::getLocalById($row['id'], 'name', $row['name']), - $row['grace_period'], - $row['isactive']?__('Active'):__('Disabled')); + self::getLocalById($row[0], 'name', $row[1]), + $row[3], + $row[2] ? __('Active') : __('Disabled')); } - return $slas; + return $entries; } + static function getIdByName($name) { + $row = static::objects() + ->filter(array('name'=>$name)) + ->values_flat('id') + ->first(); - function getIdByName($name) { - - $sql='SELECT id FROM '.SLA_TABLE.' WHERE name='.db_input($name); - if(($res=db_query($sql)) && db_num_rows($res)) - list($id)=db_fetch_row($res); - - return $id; + return $row ? $row[0] : 0; } - function lookup($id) { - return ($id && is_numeric($id) && ($sla= new SLA($id)) && $sla->getId()==$id)?$sla:null; + static function create($vars=false, &$errors=array()) { + $sla = parent::create($vars); + $sla->created = SqlFunction::NOW(); + return $sla; } - function save($id,$vars,&$errors) { - - if(!$vars['grace_period']) - $errors['grace_period']=__('Grace period required'); - elseif(!is_numeric($vars['grace_period'])) - $errors['grace_period']=__('Numeric value required (in hours)'); - - if(!$vars['name']) - $errors['name']=__('Name is required'); - elseif(($sid=SLA::getIdByName($vars['name'])) && $sid!=$id) - $errors['name']=__('Name already exists'); - - if($errors) return false; - - $sql=' updated=NOW() '. - ',isactive='.db_input($vars['isactive']). - ',name='.db_input($vars['name']). - ',grace_period='.db_input($vars['grace_period']). - ',disable_overdue_alerts='.db_input(isset($vars['disable_overdue_alerts'])?1:0). - ',enable_priority_escalation='.db_input(isset($vars['enable_priority_escalation'])?1:0). - ',notes='.db_input(Format::sanitize($vars['notes'])); - - if($id) { - $sql='UPDATE '.SLA_TABLE.' SET '.$sql.' WHERE id='.db_input($id); - if(db_query($sql)) - return true; - - $errors['err']=sprintf(__('Unable to update %s.'), __('this SLA plan')) - .' '.__('Internal error occurred'); - }else{ - if (isset($vars['id'])) - $sql .= ', id='.db_input($vars['id']); - - $sql='INSERT INTO '.SLA_TABLE.' SET '.$sql.',created=NOW() '; - if(db_query($sql) && ($id=db_insert_id())) - return $id; - - $errors['err']=sprintf(__('Unable to add %s.'), __('this SLA plan')) - .' '.__('Internal error occurred'); - } - - return false; + static function __create($vars, &$errors=array()) { + $sla = self::create($vars); + $sla->save(); + return $sla; } } @@ -231,8 +203,8 @@ require_once(INCLUDE_DIR.'class.config.php'); class SlaConfig extends Config { var $table = CONFIG_TABLE; - function SlaConfig($id) { - parent::Config("sla.$id"); + function __construct($id) { + parent::__construct("sla.$id"); } } ?> diff --git a/include/class.staff.php b/include/class.staff.php index 57b35bd02a22ef325a5ab4f6352cb627a4425a86..63b87d9884d3e1b532c5b1fe3dbca171552d3a57 100644 --- a/include/class.staff.php +++ b/include/class.staff.php @@ -17,6 +17,7 @@ include_once(INCLUDE_DIR.'class.ticket.php'); include_once(INCLUDE_DIR.'class.dept.php'); include_once(INCLUDE_DIR.'class.error.php'); include_once(INCLUDE_DIR.'class.team.php'); +include_once(INCLUDE_DIR.'class.role.php'); include_once(INCLUDE_DIR.'class.group.php'); include_once(INCLUDE_DIR.'class.passwd.php'); include_once(INCLUDE_DIR.'class.user.php'); @@ -31,24 +32,27 @@ implements AuthenticatedUser { 'select_related' => array('group'), 'joins' => array( 'dept' => array( - 'constraint' => array('dept_id' => 'Dept.dept_id'), + 'constraint' => array('dept_id' => 'Dept.id'), ), 'group' => array( - 'constraint' => array('group_id' => 'Group.group_id'), + 'constraint' => array('group_id' => 'Group.id'), ), 'teams' => array( - 'constraint' => array('staff_id' => 'StaffTeamMember.staff_id'), + 'null' => true, + 'list' => true, + 'reverse' => 'TeamMember.staff', ), ), ); var $authkey; var $departments; - var $teams; var $timezone; var $stats = array(); var $_extra; var $passwd_change; + var $_roles = null; + var $_teams = null; function __onload() { // WE have to patch info here to support upgrading from old versions. @@ -76,7 +80,7 @@ implements AuthenticatedUser { // AuthenticatedUser implementation... // TODO: Move to an abstract class that extends Staff - function getRole() { + function getUserType() { return 'staff'; } @@ -200,28 +204,30 @@ implements AuthenticatedUser { function getDepartments() { - if (isset($this->departments)) - return $this->departments; - - // Departments the staff is "allowed" to access... - // based on the group they belong to + user's primary dept + user's managed depts. - $dept_ids = array(); - $depts = Dept::objects() - ->filter(Q::any(array( - 'dept_id' => $this->dept_id, - 'groups__group_id' => $this->group_id, - 'manager_id' => $this->getId(), - ))) - ->values_flat('dept_id'); - - foreach ($depts as $row) { - list($id) = $row; - $dept_ids[] = $id; - } - if (!$dept_ids) { //Neptune help us! (fallback) - $dept_ids = array_merge($this->getGroup()->getDepartments(), array($this->getDeptId())); + 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. + $dept_ids = array(); + $depts = Dept::objects() + ->filter(Q::any(array( + 'id' => $this->dept_id, + 'groups__group_id' => $this->group_id, + 'manager_id' => $this->getId(), + ))) + ->values_flat('id'); + + foreach ($depts as $row) + $dept_ids[] = $row[0]; + + if (!$dept_ids) { //Neptune help us! (fallback) + $dept_ids = array_merge($this->getGroup()->getDepartments(), array($this->getDeptId())); + } + + $this->departments = array_filter(array_unique($dept_ids)); } - return $this->departments = array_filter(array_unique($dept_ids)); + + return $this->departments; } function getDepts() { @@ -263,6 +269,57 @@ implements AuthenticatedUser { return $this->locale; } + function getRole($dept=null) { + + if ($dept) { + $deptId = is_object($dept) ? $dept->getId() : $dept; + if (isset($this->_roles[$deptId])) + return $this->_roles[$deptId]; + + if (($role=$this->group->getRole($deptId))) + return $this->_roles[$deptId] = $role; + } + + return $this->group->getRole(); + } + + function hasPermission($perm) { + static $perms = null; + if (!isset($perms[$perm])) { + $perms[$perm] = false; + foreach($this->getDepartments() as $deptId) { + if (($role=$this->getRole($deptId)) + && $role->getPermission() + && $role->getPermission()->get($perm)) + $perms[$perm] = true; + } + } + + return $perms[$perm]; + } + + function canCreateTickets() { + return $this->hasPermission('ticket.create'); + } + + function canAssignTickets() { + return $this->hasPermission('ticket.create'); + } + + function canCloseTickets() { + return $this->hasPermission('ticket.close'); + } + + function canDeleteTickets() { + return $this->hasPermission('ticket.delete'); + } + + function canManageTickets() { + return ($this->isAdmin() + || $this->canDeleteTickets() + || $this->canCloseTickets()); + } + function isManager() { return (($dept=$this->getDept()) && $dept->getManagerId()==$this->getId()); } @@ -272,7 +329,7 @@ implements AuthenticatedUser { } function isGroupActive() { - return $this->group->group_enabled; + return $this->group->isEnabled(); } function isactive() { @@ -311,80 +368,15 @@ implements AuthenticatedUser { return ($deptId && in_array($deptId, $this->getDepts()) && !$this->isAccessLimited()); } - function canCreateTickets() { - return $this->group->can_create_tickets; - } - - function canEditTickets() { - return $this->group->can_edit_tickets; - } - - function canDeleteTickets() { - return $this->group->can_delete_tickets; - } - - function canCloseTickets() { - return $this->group->can_close_tickets; - } - - function canPostReply() { - return $this->group->can_post_ticket_reply; - } - - function canViewStaffStats() { - return $this->group->can_view_staff_stats; - } - - function canAssignTickets() { - return $this->group->can_assign_tickets; - } - - function canTransferTickets() { - return $this->group->can_transfer_tickets; - } - - function canBanEmails() { - return $this->group->can_ban_emails; - } - - function canManageTickets() { - return ($this->isAdmin() - || $this->canDeleteTickets() - || $this->canCloseTickets()); - } - - function canManagePremade() { - return $this->group->can_manage_premade; - } - - function canManageCannedResponses() { - return $this->canManagePremade(); - } - - function canManageFAQ() { - return $this->group->can_manage_faq; - } - - function canManageFAQs() { - return $this->canManageFAQ(); - } - - function showAssignedTickets() { - return $this->show_assigned_tickets; - } - function getTeams() { - if (!isset($this->teams)) { - $this->teams = array(); - $sql='SELECT team_id FROM '.TEAM_MEMBER_TABLE - .' WHERE staff_id='.db_input($this->getId()); - if(($res=db_query($sql)) && db_num_rows($res)) - while(list($id)=db_fetch_row($res)) - $this->teams[] = $id; + if (!isset($this->_teams)) { + $this->_teams = array(); + foreach ($this->teams as $team) + $this->_teams[] = $team->team_id; } - return $this->teams; + return $this->_teams; } /* stats */ @@ -530,27 +522,30 @@ implements AuthenticatedUser { } function updateTeams($team_ids) { - if ($team_ids && is_array($team_ids)) { - $teams = StaffTeamMember::objects() + + if (is_array($team_ids)) { + $members = TeamMember::objects() ->filter(array('staff_id' => $this->getId())); - foreach ($teams as $member) { + foreach ($members as $member) { if ($idx = array_search($member->team_id, $team_ids)) { - // XXX: Do we really need to track the time of update? - $member->updated = SqlFunction::NOW(); - $member->save(); unset($team_ids[$idx]); - } - else { + } else { $member->delete(); } } + foreach ($team_ids as $id) { - StaffTeamMember::create(array( - 'updated'=>SqlFunction::NOW(), - 'staff_id'=>$this->getId(), 'team_id'=>$id + TeamMember::create(array( + 'staff_id'=>$this->getId(), + 'team_id'=>$id ))->save(); } + } else { + TeamMember::objects() + ->filter(array('staff_id'=>$this->getId())) + ->delete(); } + return true; } @@ -573,9 +568,7 @@ implements AuthenticatedUser { .' WHERE staff_id='.db_input($this->getId())); // Cleanup Team membership table. - TeamMember::objects() - ->filter(array('staff_id'=>$this->getId())) - ->delete(); + $this->updateTeams(array()); return true; } @@ -600,7 +593,7 @@ implements AuthenticatedUser { if ($availableonly) { $members = $members->filter(array( - 'group__group_enabled' => 1, + 'group__flags__hasbit' => Group::FLAG_ENABLED, 'onvacation' => 0, 'isactive' => 1, )); @@ -608,7 +601,7 @@ implements AuthenticatedUser { $users=array(); foreach ($members as $M) { - $users[$S->id] = $M->getName(); + $users[$M->getId()] = $M->getName(); } return $users; @@ -741,14 +734,14 @@ implements AuthenticatedUser { if($vars['mobile'] && !Validator::is_phone($vars['mobile'])) $errors['mobile']=__('Valid phone number is required'); - if($vars['passwd1'] || $vars['passwd2'] || !$id) { + if($vars['passwd1'] || $vars['passwd2'] || !$vars['id']) { if($vars['passwd1'] && strcmp($vars['passwd1'], $vars['passwd2'])) { $errors['passwd2']=__('Passwords do not match'); } elseif ($vars['backend'] != 'local' || $vars['welcome_email']) { // Password can be omitted } - elseif(!$vars['passwd1'] && !$id) { + elseif(!$vars['passwd1'] && !$vars['id']) { $errors['passwd1']=__('Temporary password is required'); $errors['temppasswd']=__('Required'); } elseif($vars['passwd1'] && strlen($vars['passwd1'])<6) { @@ -809,19 +802,4 @@ implements AuthenticatedUser { return false; } } - -class StaffTeamMember extends VerySimpleModel { - static $meta = array( - 'table' => TEAM_MEMBER_TABLE, - 'pk' => array('staff_id', 'team_id'), - 'joins' => array( - 'staff' => array( - 'constraint' => array('staff_id' => 'Staff.staff_id'), - ), - 'team' => array( - 'constraint' => array('team_id' => 'Team.team_id'), - ), - ), - ); -} ?> diff --git a/include/class.team.php b/include/class.team.php index 8ba31af52bc5ee2800dae7936875149df6003e5f..b7bd6a5026bb1991f04e8e58f55dd55208d660f9 100644 --- a/include/class.team.php +++ b/include/class.team.php @@ -20,16 +20,19 @@ class Team extends VerySimpleModel { 'table' => TEAM_TABLE, 'pk' => array('team_id'), 'joins' => array( - 'staffmembers' => array( - 'reverse' => 'StaffTeamMember.team' - ), 'lead' => array( + 'null' => true, 'constraint' => array('lead_id' => 'Staff.staff_id'), ), + 'members' => array( + 'null' => true, + 'list' => true, + 'reverse' => 'TeamMember.team', + ), ), ); - var $members; + var $_members; function asVar() { return $this->__toString(); @@ -52,16 +55,18 @@ class Team extends VerySimpleModel { } function getMembers() { - if (!isset($this->members)) { - $this->members = Staff::objects() - ->filter(array('teams__team_id'=>$this->getId())) - ->order_by('lastname', 'firstname'); + + if (!isset($this->_members)) { + $this->_members = array(); + foreach ($this->members as $m) + $this->_members[] = $m->staff; } - return $this->members; + + return $this->_members; } function hasMember($staff) { - return $this->getMembers() + return $this->members ->filter(array('staff_id'=>$staff->getId())) ->count() !== 0; } @@ -114,16 +119,58 @@ class Team extends VerySimpleModel { return $T != $tag ? $T : $default; } - function updateMembership($vars) { + function update($vars, &$errors=array()) { - // Delete staff marked for removal... - if ($vars['remove']) { - $this->staffmembers - ->filter(array( - 'staff_id__in' => $vars['remove'])) - ->delete(); + if (!$vars['name']) { + $errors['name']=__('Team name is required'); + } elseif(($tid=self::getIdByName($vars['name'])) && $tid!=$vars['id']) { + $errors['name']=__('Team name already exists'); } - return true; + + if ($errors) + return false; + + // Reset team lead if they're getting removed + if (isset($this->lead_id) + && $this->lead_id == $vars['lead_id'] + && $vars['remove'] + && in_array($this->lead_id, $vars['remove'])) + $vars['lead_id'] =0 ; + + $this->isenabled = $vars['isenabled']; + $this->noalerts = isset($vars['noalerts']) ? $vars['noalerts'] : 0; + $this->lead_id = $vars['lead_id'] ?: 0; + $this->name = $vars['name']; + $this->notes = Format::sanitize($vars['notes']); + + if ($this->save()) { + // Remove checked members + if ($vars['remove'] && is_array($vars['remove'])) { + TeamMember::objects() + ->filter(array( + 'staff_id__in' => $vars['remove'])) + ->delete(); + } + + return true; + } + + if (isset($this->team_id)) { + $errors['err']=sprintf(__('Unable to update %s.'), __('this team')) + .' '.__('Internal error occurred'); + } else { + $errors['err']=sprintf(__('Unable to create %s.'), __('this team')) + .' '.__('Internal error occurred'); + } + + return false; + } + + function save($refetch=false) { + if ($this->dirty) + $this->updated = SqlFunction::NOW(); + + return parent::save($refetch || $this->dirty); } function delete() { @@ -146,106 +193,89 @@ class Team extends VerySimpleModel { return true; } - function save($refetch=false) { - if ($this->dirty) - $this->updated = SqlFunction::NOW(); - return parent::save($refetch || $this->dirty); - } - /* ----------- Static function ------------------*/ + static function getIdByName($name) { - static function getIdbyName($name) { - $row = static::objects() - ->filter(array('name'=>$name)) + $row = self::objects() + ->filter(array('name'=>trim($name))) ->values_flat('team_id') ->first(); - return $row ? $row[0] : null; + return $row ? $row[0] : 0; } - static function getTeams( $availableOnly=false ) { - static $names; - - if (isset($names)) - return $names; - - $names = array(); - $teams = static::objects() - ->values_flat('team_id', 'name', 'isenabled'); + static function getTeams($criteria=array()) { + static $teams = null; + if (!$teams || $criteria) { + $teams = array(); + $query = static::objects() + ->values_flat('team_id', 'name', 'isenabled') + ->order_by('name'); - if ($availableOnly) { - //Make sure the members are active...TODO: include group check!! - $teams->annotate(array('members'=>Aggregate::COUNT('staffmembers'))) + if (isset($criteria['active']) && $criteria['active']) { + $query->annotate(array('members_count'=>Aggregate::COUNT('members'))) ->filter(array( 'isenabled'=>1, - 'staffmembers__staff__isactive'=>1, - 'staffmembers__staff__onvacation'=>0, - 'staffmembers__staff__group__group_enabled'=>1, + 'members__staff__isactive'=>1, + 'members__staff__onvacation'=>0, + 'members__staff__group__group_enabled'=>1, )) - ->filter(array('members__gt'=>0)) - ->order_by('name'); - } - - foreach ($teams as $row) { - list($id, $name, $isenabled) = $row; - $names[$id] = self::getLocalById($id, 'name', $name); - if (!$isenabled) - $names[$id] .= ' ' . __('(disabled)'); + ->filter(array('members_count__gt'=>0)); + } + + $items = array(); + foreach ($query as $row) { + //TODO: Fix enabled - flags is a bit field. + list($id, $name, $enabled) = $row; + $items[$id] = sprintf('%s%s', + self::getLocalById($id, 'name', $name), + ($enabled || isset($criteria['active'])) + ? '' : ' ' . __('(disabled)')); + } + + //TODO: sort if $criteria['localize']; + if ($criteria) + return $items; + + $teams = $items; } - return $names; + return $teams; } static function getActiveTeams() { - return self::getTeams(true); + static $teams = null; + + if (!isset($teams)) + $teams = self::getTeams(array('active'=>true)); + + return $teams; } - static function create($vars=array()) { + static function create($vars=false) { $team = parent::create($vars); $team->created = SqlFunction::NOW(); return $team; } - function update($vars, &$errors) { - if (isset($this->team_id) && $this->getId() != $vars['id']) - $errors['err']=__('Missing or invalid team'); - - if(!$vars['name']) { - $errors['name']=__('Team name is required'); - } elseif(strlen($vars['name'])<3) { - $errors['name']=__('Team name must be at least 3 chars.'); - } elseif(($tid=static::getIdByName($vars['name'])) - && (!isset($this->team_id) || $tid!=$this->getId())) { - $errors['name']=__('Team name already exists'); - } - - if ($errors) - return false; - - $this->isenabled = $vars['isenabled']; - $this->name = $vars['name']; - $this->noalerts = isset($vars['noalerts'])?$vars['noalerts']:0; - $this->notes = Format::sanitize($vars['notes']); - if (isset($vars['lead_id'])) - $this->lead_id = $vars['lead_id']; - - // reset team lead if they're being removed from the team - if ($this->getLeadId() == $vars['lead_id'] - && $vars['remove'] && in_array($this->getLeadId(), $vars['remove'])) - $this->lead_id = 0; + static function __create($vars, &$errors) { + return self::create($vars)->save(); + } - if ($this->save()) - return $this->updateMembership($vars); +} - if ($this->__new__) { - $errors['err']=sprintf(__('Unable to create %s.'), __('this team')) - .' '.__('Internal error occurred'); - } - else { - $errors['err']=sprintf(__('Unable to update %s.'), __('this team')) - .' '.__('Internal error occurred'); - } - return false; - } +class TeamMember extends VerySimpleModel { + static $meta = array( + 'table' => TEAM_MEMBER_TABLE, + 'pk' => array('team_id', 'staff_id'), + 'joins' => array( + 'team' => array( + 'constraint' => array('team_id' => 'Team.team_id'), + ), + 'staff' => array( + 'constraint' => array('staff_id' => 'Staff.staff_id'), + ), + ), + ); } ?> diff --git a/include/class.ticket.php b/include/class.ticket.php index 7b95d9b82dc6c128db158958ce572fec58bde560..1e3c1eb64240842ab2d5867bc9fcf837ceeea220 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -51,7 +51,7 @@ class TicketModel extends VerySimpleModel { 'null' => true, ), 'dept' => array( - 'constraint' => array('dept_id' => 'Dept.dept_id'), + 'constraint' => array('dept_id' => 'Dept.id'), ), 'sla' => array( 'constraint' => array('sla_id' => 'SlaModel.id'), @@ -182,10 +182,10 @@ class Ticket { if(!$id && !($id=$this->getId())) return false; - $sql='SELECT ticket.*, lock_id, dept_name ' + $sql='SELECT ticket.*, lock_id, dept.name as dept_name ' .' ,count(distinct attach.attach_id) as attachments' .' FROM '.TICKET_TABLE.' ticket ' - .' LEFT JOIN '.DEPT_TABLE.' dept ON (ticket.dept_id=dept.dept_id) ' + .' LEFT JOIN '.DEPT_TABLE.' dept ON (ticket.dept_id=dept.id) ' .' LEFT JOIN '.SLA_TABLE.' sla ON (ticket.sla_id=sla.id AND sla.isactive=1) ' .' LEFT JOIN '.TICKET_LOCK_TABLE.' tlock ON ( ticket.ticket_id=tlock.ticket_id AND tlock.expire>NOW()) ' @@ -321,7 +321,7 @@ class Ticket { //Collaborator? // 1) If the user was authorized via this ticket. if ($user->getTicketId() == $this->getId() - && !strcasecmp($user->getRole(), 'collaborator')) + && !strcasecmp($user->getUserType(), 'collaborator')) return true; // 2) Query the database to check for expanded access... @@ -980,15 +980,29 @@ class Ticket { function setStatus($status, $comments='', &$errors=array()) { global $thisstaff; + if (!$thisstaff || !($role=$thisstaff->getRole($this->getDeptId()))) + return false; + if ($status && is_numeric($status)) $status = TicketStatus::lookup($status); if (!$status || !$status instanceof TicketStatus) return false; - // XXX: intercept deleted status and do hard delete - if (!strcasecmp($status->getState(), 'deleted')) - return $this->delete($comments); + // Double check permissions + switch ($status->getState()) { + case 'closed': + if (!($role->canCloseTickets())) + return false; + break; + case 'deleted': + // XXX: intercept deleted status and do hard delete + if ($role->canDeleteTickets()) + return $this->delete($comments); + // Agent doesn't have permission to delete tickets + return false; + break; + } if ($this->getStatusId() == $status->getId()) return true; @@ -996,6 +1010,7 @@ class Ticket { $sql = 'UPDATE '.TICKET_TABLE.' SET updated=NOW() '. ' ,status_id='.db_input($status->getId()); + //TODO: move this up. $ecb = null; switch($status->getState()) { case 'closed': @@ -1593,7 +1608,9 @@ class Ticket { global $cfg, $thisstaff; - if(!$thisstaff || !$thisstaff->canTransferTickets()) + if(!$thisstaff + || !($role=$thisstaff->getRole($this->getDeptId())) + || !$role->canTransferTickets()) return false; $currentDept = $this->getDeptName(); //Current department @@ -1745,7 +1762,8 @@ class Ticket { if (!$user || ($user->getId() == $this->getOwnerId()) - || !$thisstaff->canEditTickets()) + || !($role=$thisstaff->getRole($this->getDeptId())) + || !$role->canEditTickets()) return false; $sql ='UPDATE '.TICKET_TABLE.' SET updated = NOW() ' @@ -2224,7 +2242,10 @@ class Ticket { global $cfg, $thisstaff; - if(!$cfg || !$thisstaff || !$thisstaff->canEditTickets()) + if (!$cfg + || !$thisstaff + || !($role=$thisstaff->getRole($this->getDeptId())) + || !$role->canEditTickets()) return false; $fields=array(); @@ -2870,7 +2891,7 @@ class Ticket { } // Update the estimated due date in the database - $this->updateEstDueDate(); + $ticket->updateEstDueDate(); /********** double check auto-response ************/ //Override auto responder if the FROM email is one of the internal emails...loop control. @@ -2926,7 +2947,7 @@ class Ticket { static function open($vars, &$errors) { global $thisstaff, $cfg; - if(!$thisstaff || !$thisstaff->canCreateTickets()) return false; + if (!$thisstaff || !$thisstaff->canCreateTickets()) return false; if($vars['source'] && !in_array(strtolower($vars['source']),array('email','phone','other'))) $errors['source']=sprintf(__('Invalid source given - %s'),Format::htmlchars($vars['source'])); @@ -2943,6 +2964,9 @@ class Ticket { if (!$thisstaff->canAssignTickets()) unset($vars['assignId']); + //TODO: Deny action based on selected department. + + $create_vars = $vars; $tform = TicketForm::objects()->one()->getForm($create_vars); $create_vars['cannedattachments'] @@ -2953,16 +2977,19 @@ class Ticket { $vars['msgId']=$ticket->getLastMsgId(); + // Effective role for the department + $role = $thisstaff->getRole($ticket->getDeptId()); + // post response - if any $response = null; - if($vars['response'] && $thisstaff->canPostReply()) { + if($vars['response'] && $role->canPostReply()) { $vars['response'] = $ticket->replaceVars($vars['response']); // $vars['cannedatachments'] contains the attachments placed on // the response form. if(($response=$ticket->postReply($vars, $errors, false))) { //Only state supported is closed on response - if(isset($vars['ticket_state']) && $thisstaff->canCloseTickets()) + if(isset($vars['ticket_state']) && $role->canCloseTickets()) $ticket->setState($vars['ticket_state']); } } diff --git a/include/class.topic.php b/include/class.topic.php index ba9e1a82788c2d83c345f28f4b5f52b52de8ffe5..8bc814c8dd9afe512db91c7c4020f7b7ba09a3c9 100644 --- a/include/class.topic.php +++ b/include/class.topic.php @@ -40,6 +40,18 @@ class Topic extends VerySimpleModel { 'page_id' => 'Page.id', ), ), + 'dept' => array( + 'null' => true, + 'constraint' => array( + 'dept_id' => 'Dept.id', + ), + ), + 'priority' => array( + 'null' => true, + 'constraint' => array( + 'priority_id' => 'Priority.priority_id', + ), + ), ), ); @@ -52,15 +64,6 @@ class Topic extends VerySimpleModel { const FLAG_CUSTOM_NUMBERS = 0x0001; - function __onload() { - global $cfg; - - // Handle upgrade case where sort has not yet been defined - if (!$this->ht['sort'] && $cfg->getTopicSortMode() == 'a') { - static::updateSortOrder(); - } - } - function asVar() { return $this->getName(); } @@ -123,9 +126,6 @@ class Topic extends VerySimpleModel { } function getPage() { - if(!$this->page && $this->getPageId()) - $this->page = Page::lookup($this->getPageId()); - return $this->page; } @@ -260,7 +260,7 @@ class Topic extends VerySimpleModel { static function __create($vars, &$errors) { $topic = self::create(); - $topic->save($vars, $errors); + $topic->update($vars, $errors); return $topic; } diff --git a/include/client/faq-category.inc.php b/include/client/faq-category.inc.php index 80168a07c83a0c1758da2304225e9e66b880a345..eb6606af8b261f9daf9d3e8fed1d17c345423a90 100644 --- a/include/client/faq-category.inc.php +++ b/include/client/faq-category.inc.php @@ -14,7 +14,7 @@ if(!defined('OSTCLIENTINC') || !$category || !$category->isPublic()) die('Access $faqs = FAQ::objects() ->filter(array('category'=>$category)) ->exclude(array('ispublished'=>false)) - ->annotate(array('has_attachments'=>Aggregate::COUNT('attachments', false, + ->annotate(array('has_attachments'=>SqlAggregate::COUNT('attachments', false, array('attachments__inline'=>0)))) ->order_by('-ispublished', 'question'); @@ -55,7 +55,7 @@ foreach (Topic::objects() ->filter(array('faqs__faq__category__category_id'=>$category->getId())) as $t) { ?> <a href="?topicId=<?php echo urlencode($t->getId()); ?>" - ><?php echo $t->getFullname(); ?></a> + ><?php echo $t->getFullName(); ?></a> <?php } ?> </section> </div> diff --git a/include/client/kb-categories.inc.php b/include/client/kb-categories.inc.php index 27f41475eb61fa7f859fb5cd100a7bce6a29132a..c6cc4e930aad3076b4237db87f8d932d6aac1df6 100644 --- a/include/client/kb-categories.inc.php +++ b/include/client/kb-categories.inc.php @@ -3,7 +3,7 @@ <?php $categories = Category::objects() ->exclude(Q::any(array('ispublic'=>false, 'faqs__ispublished'=>false))) - ->annotate(array('faq_count'=>Aggregate::count('faqs'))) + ->annotate(array('faq_count'=>SqlAggregate::COUNT('faqs'))) ->filter(array('faq_count__gt'=>0)); if ($categories->all()) { ?> <div><?php echo __('Click on the category to browse FAQs.'); ?></div> @@ -45,7 +45,7 @@ <option value="">— Browse by Topic —</option> <?php $topics = Topic::objects() - ->annotate(array('has_faqs'=>Aggregate::COUNT('faqs'))) + ->annotate(array('has_faqs'=>SqlAggregate::COUNT('faqs'))) ->filter(array('has_faqs__gt'=>0)); foreach ($topics as $T) { ?> <option value="<?php echo $T->getId(); ?>"><?php echo $T->getFullName(); diff --git a/include/client/kb-search.inc.php b/include/client/kb-search.inc.php index ee8f51f87e4132a9923387bcc21212b50fddd794..5166a6616fbd5ee2ff677124e46aeaaae15b0a2c 100644 --- a/include/client/kb-search.inc.php +++ b/include/client/kb-search.inc.php @@ -34,7 +34,7 @@ <div class="header"><?php echo __('Help Topics'); ?></div> <?php foreach (Topic::objects() - ->annotate(array('faqs_count'=>Aggregate::count('faqs'))) + ->annotate(array('faqs_count'=>SqlAggregate::count('faqs'))) ->filter(array('faqs_count__gt'=>0)) as $t) { ?> <div><a href="?topicId=<?php echo urlencode($t->getId()); ?>" @@ -45,7 +45,7 @@ foreach (Topic::objects() <div class="header"><?php echo __('Categories'); ?></div> <?php foreach (Category::objects() - ->annotate(array('faqs_count'=>Aggregate::count('faqs'))) + ->annotate(array('faqs_count'=>SqlAggregate::count('faqs'))) ->filter(array('faqs_count__gt'=>0)) as $C) { ?> <div><a href="?cid=<?php echo urlencode($C->getId()); ?>" diff --git a/include/client/knowledgebase.inc.php b/include/client/knowledgebase.inc.php index fa9ba29cc185d435dabaa85daad6d1f79d28a47a..ac4a82f6941f7263b97bef48974865457fbf9fe1 100644 --- a/include/client/knowledgebase.inc.php +++ b/include/client/knowledgebase.inc.php @@ -6,8 +6,8 @@ if(!defined('OSTCLIENTINC')) die('Access Denied'); if($_REQUEST['q'] || $_REQUEST['cid'] || $_REQUEST['topicId']) { //Search $faqs = FAQ::allPublic() ->annotate(array( - 'attachment_count'=>Aggregate::COUNT('attachments'), - 'topic_count'=>Aggregate::COUNT('topics') + 'attachment_count'=>SqlAggregate::COUNT('attachments'), + 'topic_count'=>SqlAggregate::COUNT('topics') )) ->order_by('question'); diff --git a/include/client/tickets.inc.php b/include/client/tickets.inc.php index 58ac63af651345554c0f0ab120727179ad80af0c..8ebae5be121324e0a2221a51d66c0c65e6684b96 100644 --- a/include/client/tickets.inc.php +++ b/include/client/tickets.inc.php @@ -25,7 +25,7 @@ if(isset($_REQUEST['status'])) { //Query string status has nothing to do with th } $sortOptions=array('id'=>'`number`', 'subject'=>'cdata.subject', - 'status'=>'status.name', 'dept'=>'dept_name','date'=>'ticket.created'); + 'status'=>'status.name', 'dept'=>'dept.name','date'=>'ticket.created'); $orderWays=array('DESC'=>'DESC','ASC'=>'ASC'); //Sorting options... $order_by=$order=null; @@ -46,7 +46,7 @@ $$x=' class="'.strtolower($order).'" '; $qselect='SELECT ticket.ticket_id,ticket.`number`,ticket.dept_id,isanswered, ' .'dept.ispublic, cdata.subject,' - .'dept_name, status.name as status, status.state, ticket.source, ticket.created '; + .'dept.name as dept_name, status.name as status, status.state, ticket.source, ticket.created '; $qfrom='FROM '.TICKET_TABLE.' ticket ' .' LEFT JOIN '.TICKET_STATUS_TABLE.' status diff --git a/include/i18n/en_US/group.yaml b/include/i18n/en_US/group.yaml index bc65037e180922fbf221261ae4a9e2035a5cd681..c48f1c2e577da9a1c5575b122e48bf00a78ec6fc 100644 --- a/include/i18n/en_US/group.yaml +++ b/include/i18n/en_US/group.yaml @@ -2,29 +2,11 @@ # Default groups defined for the system # # Fields: -# isactive - (bool:0|1) true or false if the group should be initially -# usable +# id - Primary id for the group +# role_id - (int) default role for the group +# flags - (bit mask) group flags # name - (string) descriptive name for the group # notes - (string) administrative notes (viewable internally only) -# can_create_tickets - (bool:0|1) true or false if users of the group can -# create new tickets -# can_edit_tickets - (bool:0|1) true or false if users of the group can -# modify and update existing tickets -# can_delete_tickets - (bool:0|1) true or false if members of the group can -# delete tickets (permanently) -# can_close_tickets - (bool:0|1) true or false if members of the group can -# close active tickets -# can_assign_ticets - (bool:0|1) true or false if members of the group can -# assign tickets to staff -# can_transfer_tickets - (bool:0|1) true or false if members of the group -# can change the department tickets are assigne dto -# can_ban_emails - (bool:0|1) true or false if members of the group can add -# emails to the system ban list -# can_manage_premade - (bool:0|1) true or false if members of the group can -# create, modify, and delete canned responses -# can_manage_faq - (bool:0|1) true or false if members of the group can -# manage the customer-facing and internal knowledgebase -# # depts: (list<Department<id>>) id's of the departments to which the group # should initially have access # @@ -32,54 +14,30 @@ # The very first group listed in this document will be the primary group of # the initial staff member -- the administrator. --- -- isactive: 1 +- id: 1 + role_id: 1 + flags: 1 name: Lion Tamers notes: | System overlords. These folks (initially) have full control to all the departments they have access to. - can_create_tickets: 1 - can_edit_tickets: 1 - can_delete_tickets: 1 - can_close_tickets: 1 - can_assign_tickets: 1 - can_transfer_tickets: 1 - can_ban_emails: 1 - can_manage_premade: 1 - can_manage_faq: 1 - can_post_ticket_reply: 1 depts: [1, 2, 3] -- isactive: 1 +- id: 2 + role_id: 2 + flags: 1 name: Elephant Walkers notes: | Inhabitants of the ivory tower - can_create_tickets: 1 - can_edit_tickets: 1 - can_delete_tickets: 1 - can_close_tickets: 1 - can_assign_tickets: 1 - can_transfer_tickets: 1 - can_ban_emails: 1 - can_manage_premade: 1 - can_manage_faq: 1 - can_post_ticket_reply: 1 depts: [1, 2, 3] -- isactive: 1 +- id: 3 + role_id: 2 + flags: 1 name: Flea Trainers notes: | Lowly staff members - can_create_tickets: 1 - can_edit_tickets: 1 - can_delete_tickets: 0 - can_close_tickets: 1 - can_assign_tickets: 1 - can_transfer_tickets: 1 - can_ban_emails: 0 - can_manage_premade: 0 - can_manage_faq: 0 - can_post_ticket_reply: 1 depts: [1, 2, 3] diff --git a/include/i18n/en_US/role.yaml b/include/i18n/en_US/role.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f3def31e27e96f965ff5aa4fc8c3325ea225d81c --- /dev/null +++ b/include/i18n/en_US/role.yaml @@ -0,0 +1,60 @@ +# +# Default roles defined for the system +# +# Fields: +# id - Primary id for the role +# flags - (bit mask) role flags +# name - (string) descriptive name for the role +# notes - (string) internal notes +# permissions: (list<keys>) +# +# NOTE: ------------------------------------ +# --- +- id: 1 + flags: 1 + name: All Access + notes: | + Role with unlimited access + + permissions: [ + ticket.create, + ticket.edit, + ticket.assign, + ticket.transfer, + ticket.reply, + ticket.close, + ticket.delete, + kb.premade, + kb.faq, + stats.agents, + emails.banlist] + +- id: 2 + flags: 1 + name: Expanded Access + notes: | + Role with expanded access + + permissions: [ + ticket.create, + ticket.edit, + ticket.assign, + ticket.transfer, + ticket.reply, + ticket.close, + kb.premade, + kb.faq, + stats.agents, + emails.banlist] + +- id: 3 + flags: 1 + name: Limited Access + notes: | + Role with limited access + + permissions: [ + ticket.create, + ticket.assign, + ticket.transfer, + ticket.reply] diff --git a/include/staff/cannedresponse.inc.php b/include/staff/cannedresponse.inc.php index ad0d784fb5e0babcce5366938c7d0d5516dbe0fa..57276a6df94fbb504869a5213631005a515a108b 100644 --- a/include/staff/cannedresponse.inc.php +++ b/include/staff/cannedresponse.inc.php @@ -56,9 +56,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <select name="dept_id"> <option value="0">— <?php echo __('All Departments');?> —</option> <?php - $sql='SELECT dept_id, dept_name FROM '.DEPT_TABLE.' dept ORDER by dept_name'; - if(($res=db_query($sql)) && db_num_rows($res)) { - while(list($id,$name)=db_fetch_row($res)) { + if (($depts=Dept::getDepartments())) { + foreach($depts as $id => $name) { $selected=($info['dept_id'] && $id==$info['dept_id'])?'selected="selected"':''; echo sprintf('<option value="%d" %s>%s</option>',$id,$selected,$name); } diff --git a/include/staff/cannedresponses.inc.php b/include/staff/cannedresponses.inc.php index fe63868ba71655d7f596961a52abcf2e33593c66..8f729bb0cb68f5fb03680e62326b229cb77c5b4a 100644 --- a/include/staff/cannedresponses.inc.php +++ b/include/staff/cannedresponses.inc.php @@ -2,7 +2,7 @@ if(!defined('OSTSCPINC') || !$thisstaff) die('Access Denied'); $qstr=''; -$sql='SELECT canned.*, count(attach.file_id) as files, dept.dept_name as department '. +$sql='SELECT canned.*, count(attach.file_id) as files, dept.name as department '. ' FROM '.CANNED_TABLE.' canned '. ' LEFT JOIN '.DEPT_TABLE.' dept ON (dept.dept_id=canned.dept_id) '. ' LEFT JOIN '.ATTACHMENT_TABLE.' attach diff --git a/include/staff/categories.inc.php b/include/staff/categories.inc.php index c88237571ff1f37e786e78b475049d4ddff1a76a..3708e09ca1a897b42137d0fe5c42c0e48c05c999 100644 --- a/include/staff/categories.inc.php +++ b/include/staff/categories.inc.php @@ -3,7 +3,7 @@ if(!defined('OSTSCPINC') || !$thisstaff) die('Access Denied'); $qstr=''; $categories = Category::objects() - ->annotate(array('faq_count'=>Aggregate::COUNT('faqs'))); + ->annotate(array('faq_count'=>SqlAggregate::COUNT('faqs'))); $sortOptions=array('name'=>'name','type'=>'ispublic','faqs'=>'faq_count','updated'=>'updated'); $orderWays=array('DESC'=>'-','ASC'=>''); $sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'name'; diff --git a/include/staff/category.inc.php b/include/staff/category.inc.php index f96fc6019d9563ed94e72706fd41042f44719c3c..dc0eaf086d51d997ba2659f985d93b873d408b61 100644 --- a/include/staff/category.inc.php +++ b/include/staff/category.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->canManageFAQ()) die('Access Denied'); +if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->getRole()->canManageFAQ()) die('Access Denied'); $info=array(); $qstr=''; if($category && $_REQUEST['a']!='add'){ diff --git a/include/staff/department.inc.php b/include/staff/department.inc.php index 7681645200ae9f88d6e3171cf987bc91342ba8f5..6828b06925d9bdee216ed73825707b5e8eafeae7 100644 --- a/include/staff/department.inc.php +++ b/include/staff/department.inc.php @@ -7,10 +7,9 @@ if($dept && $_REQUEST['a']!='add') { $title=__('Update Department'); $action='update'; $submit_text=__('Save Changes'); - $info=$dept->getInfo(); - $info['id']=$dept->getId(); + $info = $dept->getInfo(); + $info['id'] = $dept->getId(); $info['groups'] = $dept->getAllowedGroups(); - $qstr.='&id='.$dept->getId(); } else { $title=__('Add New Department'); @@ -23,8 +22,10 @@ if($dept && $_REQUEST['a']!='add') { $info['group_membership'] = 1; $qstr.='&a='.$_REQUEST['a']; + } -$info=Format::htmlchars(($errors && $_POST)?$_POST:$info); + +$info = Format::htmlchars(($errors && $_POST) ? $_POST : $info); ?> <form action="departments.php?<?php echo $qstr; ?>" method="post" id="save"> <?php csrf_token(); ?> @@ -32,6 +33,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>"> <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> <h2><?php echo __('Department');?></h2> +<br> +<ul class="tabs"> + <li class="active"><a href="#settings"> + <i class="icon-file"></i> <?php echo __('Settings'); ?></a></li> + <li><a href="#access"> + <i class="icon-lock"></i> <?php echo __('Access'); ?></a></li> +</ul> +<div id="settings" class="tab_content"> <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> <thead> <tr> @@ -47,7 +56,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <?php echo __('Name');?>: </td> <td> - <input data-translate-tag="<?php echo $dept ? $dept->getTranslationTag() : ''; + <input data-translate-tag="<?php echo $dept ? $dept->getTranslateTag() : ''; ?>" type="text" size="30" name="name" value="<?php echo $info['name']; ?>"> <span class="error">* <?php echo $errors['name']; ?></span> </td> @@ -250,30 +259,6 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </span> </td> </tr> - <tr> - <th colspan="2"> - <em><strong><?php echo __('Group Access'); ?></strong>: - <?php echo __('Check all groups allowed to access this department.'); ?> - <i class="help-tip icon-question-sign" href="#department_access"></i></em> - </th> - </tr> - <?php - $sql='SELECT group_id, group_name, count(staff.staff_id) as members ' - .' FROM '.GROUP_TABLE.' grp ' - .' LEFT JOIN '.STAFF_TABLE. ' staff USING(group_id) ' - .' GROUP by grp.group_id ' - .' ORDER BY group_name'; - if(($res=db_query($sql)) && db_num_rows($res)){ - while(list($id, $name, $members) = db_fetch_row($res)) { - if($members>0) - $members=sprintf('<a href="staff.php?a=filter&gid=%d">%d</a>', $id, $members); - - $ck=($info['groups'] && in_array($id,$info['groups']))?'checked="checked"':''; - echo sprintf('<tr><td colspan=2> <label><input type="checkbox" name="groups[]" value="%d" %s> %s</label> (%s)</td></tr>', - $id, $ck, $name, $members); - } - } - ?> <tr> <th colspan="2"> <em><strong><?php echo __('Department Signature'); ?></strong>: @@ -289,9 +274,92 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </tr> </tbody> </table> +</div> +<div id="access" class="tab_content" style="display:none"> + <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <thead> + <tr> + <th colspan=2> + <em><?php echo __('Primary department members have access to this department by default'); ?></em> + </th> + </tr> + <tr> + <th width="40%"><?php echo __('Group'); ?></th> + <th><?php echo __('Role'); ?></th> + </tr> + </thead> + <tbody> + <?php + $deptId = $dept ? $dept->getId() : 0; + $roles = Role::getRoles(); + $groups = Group::objects() + ->annotate(array( + 'isenabled'=>new SqlExpr(array( + 'flags__hasbit' => Group::FLAG_ENABLED)) + )) + ->order_by('name'); + foreach ($groups as $group) { + $DeptAccess = $group->getDepartmentsAccess(); + ?> + <tr> + <td> + + <label> + <?php + $ck = ($info['groups'] && in_array($group->getId(), $info['groups'])) ? 'checked="checked"' : ''; + echo sprintf('%s %s', + sprintf('<input type="checkbox" class="grp-ckb" + name="groups[]" value="%s" %s />', + $group->getId(), $ck), + Format::htmlchars($group->getName())); + ?> + </label> + </td> + <td> + <?php + $_name = 'group'.$group->getId().'_role_id'; + ?> + <select name="<?php echo $_name; ?>"> + <option value="0">— <?php + echo sprintf('%s (%s)', + __('Group Default'), + $group->getRole()); + ?> + —</option> + <?php + foreach ($roles as $rid => $role) { + $sel = ''; + if (isset($info[$_name])) + $sel = ($info[$_name] == $rid) ? 'selected="selected"' : ''; + elseif ($deptId && isset($DeptAccess[$deptId])) + $sel = ($DeptAccess[$deptId] == $rid) ? 'selected="selected"' : ''; + + echo sprintf('<option value="%d" %s>%s</option>', + $rid, $sel, $role); + } ?> + </select> + <i class="help-tip icon-question-sign" href="#dept-role"></i> + </td> + </tr> + <?php + } ?> + </tbody> + <tfoot> + <tr> + <td colspan="2"> + <?php echo __('Select');?>: + <a id="selectAll" href="#grp-ckb"><?php echo __('All');?></a> + <a id="selectNone" href="#grp-ckb"><?php echo __('None');?></a> + <a id="selectToggle" href="#grp-ckb"><?php echo __('Toggle');?></a> + </td> + </tr> + </tfoot> + </table> +</div> <p style="text-align:center"> <input type="submit" name="submit" value="<?php echo $submit_text; ?>"> <input type="reset" name="reset" value="<?php echo __('Reset');?>"> - <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="departments.php"'> + <input type="button" name="cancel" value="<?php echo __('Cancel');?>" + onclick='window.location.href="?"'> </p> </form> diff --git a/include/staff/departments.inc.php b/include/staff/departments.inc.php index ba2d04707bc23d6c699951a048ad75d6fe07dcc1..20018b8dabde7baa4df3475a0951cd8681dca5f6 100644 --- a/include/staff/departments.inc.php +++ b/include/staff/departments.inc.php @@ -1,50 +1,47 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); +if (!defined('OSTADMININC') || !$thisstaff->isAdmin()) + die('Access Denied'); $qstr=''; -$sql='SELECT dept.dept_id,dept_name,email.email_id,email.email,email.name as email_name,ispublic,count(staff.staff_id) as users '. - ',CONCAT_WS(" ",mgr.firstname,mgr.lastname) as manager,mgr.staff_id as manager_id,dept.created,dept.updated FROM '.DEPT_TABLE.' dept '. - ' LEFT JOIN '.STAFF_TABLE.' mgr ON dept.manager_id=mgr.staff_id '. - ' LEFT JOIN '.EMAIL_TABLE.' email ON dept.email_id=email.email_id '. - ' LEFT JOIN '.STAFF_TABLE.' staff ON dept.dept_id=staff.dept_id '; +$sortOptions=array( + 'name' => 'name', + 'type' => 'ispublic', + 'members'=> 'members_count', + 'email'=> 'email__name', + 'manager'=>'manager__lastname' + ); -$sql.=' WHERE 1'; -$sortOptions=array('name'=>'dept.dept_name','type'=>'ispublic','users'=>'users','email'=>'email_name, email.email','manager'=>'manager'); -$orderWays=array('DESC'=>'DESC','ASC'=>'ASC'); -$sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'name'; -//Sorting options... -if($sort && $sortOptions[$sort]) { - $order_column =$sortOptions[$sort]; +$orderWays = array('DESC'=>'DESC', 'ASC'=>'ASC'); +$sort = ($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])]) ? strtolower($_REQUEST['sort']) : 'name'; +if ($sort && $sortOptions[$sort]) { + $order_column = $sortOptions[$sort]; } -$order_column=$order_column?$order_column:'dept.dept_name'; -if($_REQUEST['order'] && $orderWays[strtoupper($_REQUEST['order'])]) { - $order=$orderWays[strtoupper($_REQUEST['order'])]; +$order_column = $order_column ? $order_column : 'name'; + +if ($_REQUEST['order'] && isset($orderWays[strtoupper($_REQUEST['order'])])) { + $order = $orderWays[strtoupper($_REQUEST['order'])]; +} else { + $order = 'ASC'; } -$order=$order?$order:'ASC'; -if($order_column && strpos($order_column,',')){ +if ($order_column && strpos($order_column,',')) { $order_column=str_replace(','," $order,",$order_column); } $x=$sort.'_sort'; $$x=' class="'.strtolower($order).'" '; -$order_by="$order_column $order "; - +$page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1; +$count = Dept::objects()->count(); +$pageNav = new Pagenate($count, $page, PAGE_LIMIT); +$_qstr = $qstr.'&sort='.urlencode($_REQUEST['sort']).'&order='.urlencode($_REQUEST['order']); +$pageNav->setURL('departments.php', $_qstr); +$showing = $pageNav->showing().' '._N('department', 'departments', $count); $qstr.='&order='.($order=='DESC'?'ASC':'DESC'); - -$query="$sql GROUP BY dept.dept_id ORDER BY $order_by"; -$res=db_query($query); -if($res && ($num=db_num_rows($res))) - $showing=sprintf(_N('Showing %d department', 'Showing %d departments', - $num),$num); -else - $showing=__('No departments found!'); - ?> <div class="pull-left" style="width:700px;padding-top:5px;"> <h2><?php echo __('Departments');?></h2> </div> -<div class="pull-left flush-right" style="padding-top:5px;padding-right:5px;"> +<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;"> <b><a href="departments.php?a=add" class="Icon newDepartment"><?php echo __('Add New Department');?></a></b></div> <div class="clear"></div> <form action="departments.php" method="POST" name="depts"> @@ -56,61 +53,76 @@ else <thead> <tr> <th width="7px"> </th> - <th width="180"><a <?php echo $name_sort; ?> href="departments.php?<?php echo $qstr; ?>&sort=name"><?php echo __('Name');?></a></th> + <th width="200"><a <?php echo $name_sort; ?> href="departments.php?<?php echo $qstr; ?>&sort=name"><?php echo __('Name');?></a></th> <th width="80"><a <?php echo $type_sort; ?> href="departments.php?<?php echo $qstr; ?>&sort=type"><?php echo __('Type');?></a></th> - <th width="70"><a <?php echo $users_sort; ?>href="departments.php?<?php echo $qstr; ?>&sort=users"><?php echo __('Users');?></a></th> + <th width="70"><a <?php echo $members_sort; ?>href="departments.php?<?php echo $qstr; ?>&sort=members"><?php echo __('Members');?></a></th> <th width="300"><a <?php echo $email_sort; ?> href="departments.php?<?php echo $qstr; ?>&sort=email"><?php echo __('Email Address');?></a></th> - <th width="200"><a <?php echo $manager_sort; ?> href="departments.php?<?php echo $qstr; ?>&sort=manager"><?php echo __('Department Manager');?></a></th> + <th width="180"><a <?php echo $manager_sort; ?> href="departments.php?<?php echo $qstr; ?>&sort=manager"><?php echo __('Manager');?></a></th> </tr> </thead> <tbody> <?php - $total=0; - $ids=($errors && is_array($_POST['ids']))?$_POST['ids']:null; - if($res && db_num_rows($res)): + $ids= ($errors && is_array($_POST['ids'])) ? $_POST['ids'] : null; + if ($count) { + $depts = Dept::objects() + ->annotate(array( + 'members_count' => SqlAggregate::COUNT('members', true), + )) + ->order_by(sprintf('%s%s', + strcasecmp($order, 'DESC') ? '' : '-', + $order_column)) + ->limit($pageNav->getLimit()) + ->offset($pageNav->getStart()); $defaultId=$cfg->getDefaultDeptId(); $defaultEmailId = $cfg->getDefaultEmail()->getId(); $defaultEmailAddress = (string) $cfg->getDefaultEmail(); - while ($row = db_fetch_array($res)) { + foreach ($depts as $dept) { + $id = $dept->getId(); $sel=false; - if($ids && in_array($row['dept_id'],$ids)) + if($ids && in_array($dept->getId(), $ids)) $sel=true; - if ($row['email_id']) - $row['email']=$row['email_name']?($row['email_name'].' <'.$row['email'].'>'):$row['email']; - elseif($defaultEmailId) { - $row['email_id'] = $defaultEmailId; - $row['email'] = $defaultEmailAddress; + if ($dept->email) { + $email = (string) $dept->email; + $emailId = $dept->email->getId(); + } else { + $emailId = $defaultEmailId; + $email = $defaultEmailAddress; } - $default=($defaultId==$row['dept_id'])?' <small>'.__('(Default)').'</small>':''; + $default= ($defaultId == $dept->getId()) ?' <small>'.__('(Default)').'</small>' : ''; ?> - <tr id="<?php echo $row['dept_id']; ?>"> + <tr id="<?php echo $id; ?>"> <td width=7px> - <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['dept_id']; ?>" - <?php echo $sel?'checked="checked"':''; ?> <?php echo $default?'disabled="disabled"':''; ?> > + <input type="checkbox" class="ckb" name="ids[]" + value="<?php echo $id; ?>" + <?php echo $sel? 'checked="checked"' : ''; ?> + <?php echo $default? 'disabled="disabled"' : ''; ?> > </td> - <td><a href="departments.php?id=<?php echo $row['dept_id']; ?>"><?php echo $row['dept_name']; ?></a> <?php echo $default; ?></td> - <td><?php echo $row['ispublic']?__('Public'):'<b>'.__('Private').'</b>'; ?></td> + <td><a href="departments.php?id=<?php echo $id; ?>"><?php + echo $dept->getName(); ?></a> <?php echo $default; ?></td> + <td><?php echo $dept->isPublic() ? __('Public') :'<b>'.__('Private').'</b>'; ?></td> <td> <b> - <?php if($row['users']>0) { ?> - <a href="staff.php?did=<?php echo $row['dept_id']; ?>"><?php echo $row['users']; ?></a> + <?php if ($dept->members_count) { ?> + <a href="staff.php?did=<?php echo $id; ?>"><?php echo $dept->members_count; ?></a> <?php }else{ ?> 0 <?php } ?> </b> </td> - <td><span class="ltr"><a href="emails.php?id=<?php echo $row['email_id']; ?>"><?php - echo Format::htmlchars($row['email']); ?></a></span></td> - <td><a href="staff.php?id=<?php echo $row['manager_id']; ?>"><?php echo $row['manager']; ?> </a></td> + <td><span class="ltr"><a href="emails.php?id=<?php echo $emailId; ?>"><?php + echo Format::htmlchars($email); ?></a></span></td> + <td><a href="staff.php?id=<?php echo $dept->manager_id; ?>"><?php + echo $dept->manager_id ? $dept->manager : ''; ?> </a></td> </tr> <?php - } //end of while. - endif; ?> + } //end of foreach. + } ?> <tfoot> <tr> <td colspan="6"> - <?php if($res && $num){ ?> + <?php + if ($count) { ?> <?php echo __('Select');?>: <a id="selectAll" href="#ckb"><?php echo __('All');?></a> <a id="selectNone" href="#ckb"><?php echo __('None');?></a> @@ -123,18 +135,18 @@ else </tfoot> </table> <?php -if($res && $num): //Show options.. -?> -<p class="centered" id="actions"> - <input class="button" type="submit" name="make_public" value="<?php echo __('Make Public');?>" > - <input class="button" type="submit" name="make_private" value="<?php echo __('Make Private');?>" > - <input class="button" type="submit" name="delete" value="<?php echo __('Delete Dept(s)');?>" > -</p> +if ($count): //Show options.. + echo '<div> '.__('Page').':'.$pageNav->getPageLinks().' </div>'; + ?> + <p class="centered" id="actions"> + <input class="button" type="submit" name="delete" value="<?php echo + __('Delete'); + ?>" > + </p> <?php endif; ?> </form> - <div style="display:none;" class="dialog" id="confirm-action"> <h3><?php echo __('Please Confirm');?></h3> <a class="close" href=""><i class="icon-remove-circle"></i></a> diff --git a/include/staff/directory.inc.php b/include/staff/directory.inc.php index 6d127962a2f9d30a9b5ed655333f170cac453ce9..cb212ec6505cc2469f2bf3771e351f138ab6ddf9 100644 --- a/include/staff/directory.inc.php +++ b/include/staff/directory.inc.php @@ -1,7 +1,7 @@ <?php if(!defined('OSTSTAFFINC') || !$thisstaff || !$thisstaff->isStaff()) die('Access Denied'); $qstr=''; -$select='SELECT staff.*,CONCAT_WS(" ",firstname,lastname) as name,dept.dept_name as dept '; +$select='SELECT staff.*,CONCAT_WS(" ",firstname,lastname) as name,dept.name as dept '; $from='FROM '.STAFF_TABLE.' staff '. 'LEFT JOIN '.DEPT_TABLE.' dept ON(staff.dept_id=dept.dept_id) '; $where='WHERE staff.isvisible=1 '; @@ -28,7 +28,7 @@ if($_REQUEST['did'] && is_numeric($_REQUEST['did'])) { $qstr.='&did='.urlencode($_REQUEST['did']); } -$sortOptions=array('name'=>'staff.firstname,staff.lastname','email'=>'staff.email','dept'=>'dept.dept_name', +$sortOptions=array('name'=>'staff.firstname,staff.lastname','email'=>'staff.email','dept'=>'dept.name', 'phone'=>'staff.phone','mobile'=>'staff.mobile','ext'=>'phone_ext', 'created'=>'staff.created','login'=>'staff.lastlogin'); $orderWays=array('DESC'=>'DESC','ASC'=>'ASC'); @@ -68,10 +68,10 @@ $query="$select $from $where GROUP BY staff.staff_id ORDER BY $order_by LIMIT ". <select name="did" id="did"> <option value="0">— <?php echo __('All Departments');?> —</option> <?php - $sql='SELECT dept.dept_id, dept.dept_name,count(staff.staff_id) as users '. + $sql='SELECT dept.dept_id, dept.name as dept,count(staff.staff_id) as users '. 'FROM '.DEPT_TABLE.' dept '. 'INNER JOIN '.STAFF_TABLE.' staff ON(staff.dept_id=dept.dept_id AND staff.isvisible=1) '. - 'GROUP By dept.dept_id HAVING users>0 ORDER BY dept_name'; + 'GROUP By dept.dept_id HAVING users>0 ORDER BY dept.name'; if(($res=db_query($sql)) && db_num_rows($res)){ while(list($id,$name, $users)=db_fetch_row($res)){ $sel=($_REQUEST['did'] && $_REQUEST['did']==$id)?'selected="selected"':''; diff --git a/include/staff/email.inc.php b/include/staff/email.inc.php index 9d2e3f89c566b28d2df5eae293b548de28732bf0..e556d39b643f874d21a79e50be6c02fcf9fbf0e3 100644 --- a/include/staff/email.inc.php +++ b/include/staff/email.inc.php @@ -84,12 +84,11 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <option value="0" selected="selected">— <?php echo __('System Default'); ?> —</option> <?php - $sql='SELECT dept_id, dept_name FROM '.DEPT_TABLE.' dept ORDER by dept_name'; - if(($res=db_query($sql)) && db_num_rows($res)){ - while(list($id,$name)=db_fetch_row($res)){ - $selected=($info['dept_id'] && $id==$info['dept_id'])?'selected="selected"':''; - echo sprintf('<option value="%d" %s>%s</option>',$id,$selected,$name); - } + if (($depts=Dept::getDepartments())) { + foreach ($depts as $id => $name) { + $selected=($info['dept_id'] && $id==$info['dept_id'])?'selected="selected"':''; + echo sprintf('<option value="%d" %s>%s</option>',$id,$selected,$name); + } } ?> </select> diff --git a/include/staff/emails.inc.php b/include/staff/emails.inc.php index 6eb1faf3cf23a978e9e8f7ca975a76d9753e7cf8..7b772d3e0f173802cabf564fdad4a35aedc32f6b 100644 --- a/include/staff/emails.inc.php +++ b/include/staff/emails.inc.php @@ -2,49 +2,42 @@ if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); $qstr=''; -$sql='SELECT email.*,dept.dept_name as department,priority_desc as priority '. - ' FROM '.EMAIL_TABLE.' email '. - ' LEFT JOIN '.DEPT_TABLE.' dept ON (dept.dept_id=email.dept_id) '. - ' LEFT JOIN '.TICKET_PRIORITY_TABLE.' pri ON (pri.priority_id=email.priority_id) '; -$sql.=' WHERE 1'; -$sortOptions=array('email'=>'email.email','dept'=>'department','priority'=>'priority','created'=>'email.created','updated'=>'email.updated'); -$orderWays=array('DESC'=>'DESC','ASC'=>'ASC'); -$sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'email'; -//Sorting options... -if($sort && $sortOptions[$sort]) { - $order_column =$sortOptions[$sort]; -} -$order_column=$order_column?$order_column:'email.email'; +$sortOptions = array( + 'email' => 'email', + 'dept' => 'dept__name', + 'priority' => 'priority__priority_desc', + 'created' => 'created', + 'updated' => 'updated'); + -if($_REQUEST['order'] && $orderWays[strtoupper($_REQUEST['order'])]) { - $order=$orderWays[strtoupper($_REQUEST['order'])]; +$orderWays = array('DESC'=>'DESC', 'ASC'=>'ASC'); +$sort = ($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])]) ? strtolower($_REQUEST['sort']) : 'email'; +if ($sort && $sortOptions[$sort]) { + $order_column = $sortOptions[$sort]; } -$order=$order?$order:'ASC'; -if($order_column && strpos($order_column,',')){ - $order_column=str_replace(','," $order,",$order_column); +$order_column = $order_column ? $order_column : 'email'; + +if ($_REQUEST['order'] && isset($orderWays[strtoupper($_REQUEST['order'])])) +{ + $order = $orderWays[strtoupper($_REQUEST['order'])]; +} else { + $order = 'ASC'; } + $x=$sort.'_sort'; $$x=' class="'.strtolower($order).'" '; -$order_by="$order_column $order "; - -$total=db_count('SELECT count(*) FROM '.EMAIL_TABLE.' email '); -$page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1; -$pageNav=new Pagenate($total, $page, PAGE_LIMIT); -$pageNav->setURL('emails.php',$qstr.'&sort='.urlencode($_REQUEST['sort']).'&order='.urlencode($_REQUEST['order'])); -//Ok..lets roll...create the actual query +$page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1; +$count = EmailModel::objects()->count(); +$pageNav = new Pagenate($count, $page, PAGE_LIMIT); +$_qstr = $qstr.'&sort='.urlencode($_REQUEST['sort']).'&order='.urlencode($_REQUEST['order']); +$pageNav->setURL('emails.php', $_qstr); +$showing = $pageNav->showing().' '._N('email', 'emails', $count); $qstr.='&order='.($order=='DESC'?'ASC':'DESC'); -$query="$sql GROUP BY email.email_id ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit(); -$res=db_query($query); -if($res && ($num=db_num_rows($res))) - $showing=$pageNav->showing().' '.__('emails'); -else - $showing=__('No emails found!'); $def_dept_id = $cfg->getDefaultDeptId(); $def_dept_name = $cfg->getDefaultDept()->getName(); $def_priority = $cfg->getDefaultPriority()->getDesc(); - ?> <div class="pull-left" style="width:700px;padding-top:5px;"> <h2><?php echo __('Email Addresses');?></h2> @@ -70,30 +63,37 @@ $def_priority = $cfg->getDefaultPriority()->getDesc(); </thead> <tbody> <?php - $total=0; - $ids=($errors && is_array($_POST['ids']))?$_POST['ids']:null; - if($res && db_num_rows($res)): + $ids = ($errors && is_array($_POST['ids'])) ? $_POST['ids'] : null; + if ($count): $defaultId=$cfg->getDefaultEmailId(); - while ($row = db_fetch_array($res)) { + $emails = EmailModel::objects() + ->order_by(sprintf('%s%s', + strcasecmp($order, 'DESC') ? '' : '-', + $order_column)) + ->limit($pageNav->getLimit()) + ->offset($pageNav->getStart()); + + foreach ($emails as $email) { + $id = $email->getId(); $sel=false; - if($ids && in_array($row['email_id'],$ids)) + if ($ids && in_array($email, $ids)) $sel=true; - $default=($row['email_id']==$defaultId); - $email=$row['email']; - if($row['name']) - $email=$row['name'].' <'.$row['email'].'>'; + $default=($id==$defaultId); ?> - <tr id="<?php echo $row['email_id']; ?>"> + <tr id="<?php echo $id; ?>"> <td width=7px> - <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['email_id']; ?>" - <?php echo $sel?'checked="checked"':''; ?> <?php echo $default?'disabled="disabled"':''; ?>> + <input type="checkbox" class="ckb" name="ids[]" + value="<?php echo $id; ?>" + <?php echo $sel ? 'checked="checked"' : ''; ?> + <?php echo $default?'disabled="disabled"':''; ?>> </td> - <td><span class="ltr"><a href="emails.php?id=<?php echo $row['email_id']; ?>"><?php echo Format::htmlchars($email); ?></a></span></td> - <td><?php echo $row['priority'] ?: $def_priority; ?></td> - <td><a href="departments.php?id=<?php $row['dept_id'] ?: $def_dept_id; ?>"><?php - echo $row['department'] ?: $def_dept_name; ?></a></td> - <td> <?php echo Format::date($row['created']); ?></td> - <td> <?php echo Format::datetime($row['updated']); ?></td> + <td><span class="ltr"><a href="emails.php?id=<?php echo $id; ?>"><?php + echo Format::htmlchars((string) $email); ?></a></span></td> + <td><?php echo $email->priority ?: $def_priority; ?></td> + <td><a href="departments.php?id=<?php $email->dept_id ?: $def_dept_id; ?>"><?php + echo $email->dept ?: $def_dept_name; ?></a></td> + <td> <?php echo Format::date($email->created); ?></td> + <td> <?php echo Format::datetime($email->updated); ?></td> </tr> <?php } //end of while. @@ -101,7 +101,7 @@ $def_priority = $cfg->getDefaultPriority()->getDesc(); <tfoot> <tr> <td colspan="6"> - <?php if($res && $num){ ?> + <?php if ($count){ ?> <?php echo __('Select');?>: <a id="selectAll" href="#ckb"><?php echo __('All');?></a> <a id="selectNone" href="#ckb"><?php echo __('None');?></a> @@ -114,7 +114,7 @@ $def_priority = $cfg->getDefaultPriority()->getDesc(); </tfoot> </table> <?php -if($res && $num): //Show options.. +if ($count): echo '<div> '.__('Page').':'.$pageNav->getPageLinks().' </div>'; ?> <p class="centered" id="actions"> diff --git a/include/staff/faq-categories.inc.php b/include/staff/faq-categories.inc.php index 0019380d022a5e9fd00d7fa87b74785c41854c0b..d66c24470c1475e5d3b05697f1d23ae1ebcfb796 100644 --- a/include/staff/faq-categories.inc.php +++ b/include/staff/faq-categories.inc.php @@ -11,7 +11,7 @@ if(!defined('OSTSTAFFINC') || !$thisstaff) die('Access Denied'); <option value="">— <?php echo __('All Categories');?> —</option> <?php $categories = Category::objects() - ->annotate(array('faq_count'=>Aggregate::COUNT('faqs'))) + ->annotate(array('faq_count'=>SqlAggregate::COUNT('faqs'))) ->filter(array('faq_count__gt'=>0)) ->order_by('name'); print $categories; @@ -31,7 +31,7 @@ print $categories; <option value="">— <?php echo __('All Help Topics');?> —</option> <?php $topics = Topic::objects() - ->annotate(array('faq_count'=>Aggregate::COUNT('faqs'))) + ->annotate(array('faq_count'=>SqlAggregate::COUNT('faqs'))) ->filter(array('faq_count__gt'=>0)) ->all(); usort($topics, function($a, $b) { @@ -53,8 +53,8 @@ print $categories; if($_REQUEST['q'] || $_REQUEST['cid'] || $_REQUEST['topicId']) { //Search. $faqs = FAQ::objects() ->annotate(array( - 'attachment_count'=>Aggregate::COUNT('attachments'), - 'topic_count'=>Aggregate::COUNT('topics') + 'attachment_count'=>SqlAggregate::COUNT('attachments'), + 'topic_count'=>SqlAggregate::COUNT('topics') )) ->order_by('question'); @@ -89,7 +89,7 @@ if($_REQUEST['q'] || $_REQUEST['cid'] || $_REQUEST['topicId']) { //Search. } } else { //Category Listing. $categories = Category::objects() - ->annotate(array('faq_count'=>Aggregate::COUNT('faqs'))) + ->annotate(array('faq_count'=>SqlAggregate::COUNT('faqs'))) ->all(); if (count($categories)) { diff --git a/include/staff/faq-category.inc.php b/include/staff/faq-category.inc.php index 8ff80af13b9a0be313d561ea17af13a4bf14cd65..f4edf2d9a1f33f088e5d8c48ce23890deeefcaa1 100644 --- a/include/staff/faq-category.inc.php +++ b/include/staff/faq-category.inc.php @@ -17,7 +17,7 @@ if(!defined('OSTSTAFFINC') || !$category || !$thisstaff) die('Access Denied'); <?php echo Format::display($category->getDescription()); ?> </div> <?php -if($thisstaff->canManageFAQ()) { +if($thisstaff->getRole()->canManageFAQ()) { echo sprintf('<div class="cat-manage-bar"><a href="categories.php?id=%d" class="Icon editCategory">'.__('Edit Category').'</a> <a href="categories.php" class="Icon deleteCategory">'.__('Delete Category').'</a> <a href="faq.php?cid=%d&a=add" class="Icon newFAQ">'.__('Add New FAQ').'</a></div>', diff --git a/include/staff/faq-view.inc.php b/include/staff/faq-view.inc.php index 7b120f0d21aab98d411e024015ec1892ad26de40..2c20b6dcb793727089e49db063fa4afab3417fed 100644 --- a/include/staff/faq-view.inc.php +++ b/include/staff/faq-view.inc.php @@ -79,7 +79,7 @@ $query = http_build_query($query); ?> echo __('Print'); ?> </a></button> <?php -if ($thisstaff->canManageFAQ()) { ?> +if ($thisstaff->getRole()->canManageFAQ()) { ?> <button> <i class="icon-edit"></i> <a href="faq.php?id=<?php echo $faq->getId(); ?>&a=edit"><?php @@ -104,7 +104,7 @@ if ($thisstaff->canManageFAQ()) { ?> <hr> <?php -if ($thisstaff->canManageFAQ()) { ?> +if ($thisstaff->getRole()->canManageFAQ()) { ?> <form action="faq.php?id=<?php echo $faq->getId(); ?>" method="post"> <?php csrf_token(); ?> <input type="hidden" name="do" value="manage-faq"> diff --git a/include/staff/faq.inc.php b/include/staff/faq.inc.php index 5e32630d014942c0a4d2cad5b6a5629371dac19f..01b404b3fc6115970a46e332797164635813b618 100644 --- a/include/staff/faq.inc.php +++ b/include/staff/faq.inc.php @@ -1,5 +1,5 @@ <?php -if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->canManageFAQ()) die('Access Denied'); +if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->getRole()->canManageFAQ()) die('Access Denied'); $info=array(); $qstr=''; if($faq){ diff --git a/include/staff/group.inc.php b/include/staff/group.inc.php index 74cc64cb479f4e450df44b8c60ebf8e3645419ab..d471b791d8da636d5897153fd8e886bcf6332af4 100644 --- a/include/staff/group.inc.php +++ b/include/staff/group.inc.php @@ -1,50 +1,58 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); + $info=array(); -$qstr=''; -if($group && $_REQUEST['a']!='add'){ - $title=__('Update Group'); - $action='update'; - $submit_text=__('Save Changes'); - $info=$group->getInfo(); - $info['id']=$group->getId(); - $info['depts']=$group->getDepartments(); +if ($group) { + $title = __('Update Group'); + $action = 'update'; + $submit_text = __('Save Changes'); + $info = $group->getInfo(); + $info['id'] = $group->getId(); + $info['depts'] = $group->getDepartments(); $trans['name'] = $group->getTranslateTag('name'); - $qstr.='&id='.$group->getId(); -}else { - $title=__('Add New Group'); - $action='create'; - $submit_text=__('Create Group'); - $info['isactive']=isset($info['isactive'])?$info['isactive']:1; - $info['can_create_tickets']=isset($info['can_create_tickets'])?$info['can_create_tickets']:1; - $qstr.='&a='.$_REQUEST['a']; +} else { + $title = __('Add New Group'); + $action = 'add'; + $submit_text = __('Create Group'); + $info['isactive'] = isset($info['isactive']) ? $info['isactive'] : 1; } -$info=Format::htmlchars(($errors && $_POST)?$_POST:$info); + +$info = Format::htmlchars(($errors && $_POST) ? array_merge($info, $_POST) : $info); +$roles = Role::getActiveRoles(); + ?> -<form action="groups.php?<?php echo $qstr; ?>" method="post" id="save" name="group"> - <?php csrf_token(); ?> - <input type="hidden" name="do" value="<?php echo $action; ?>"> - <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>"> - <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> - <h2><?php echo __('Group Access and Permissions');?></h2> - <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> +<form action="" method="post" id="save"> + <?php csrf_token(); ?> + <input type="hidden" name="do" value="<?php echo $action; ?>"> + <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>"> + <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> +<h2> <?php echo $group ?: __('New Group'); ?></h2> +<br> +<ul class="tabs"> + <li class="active"><a href="#group"> + <i class="icon-file"></i> <?php echo __('Group'); ?></a></li> + <li><a href="#departments"> + <i class="icon-lock"></i> <?php echo __('Departments Access'); ?></a></li> +</ul> +<div id="group" class="tab_content"> + <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> <thead> <tr> <th colspan="2"> <h4><?php echo $title; ?></h4> - <em><strong><?php echo __('Group Information');?></strong>: <?php echo __("Disabled group will limit agents' access. Admins are exempted.");?></em> + <em><?php echo __( + 'Roles are used to define agents\' permissions' + ); ?> <i class="help-tip icon-question-sign" + href="#roles"></i></em> </th> </tr> </thead> <tbody> <tr> - <td width="180" class="required"> - <?php echo __('Name');?>: - </td> + <td width="180" class="required"><?php echo __('Name'); ?>:</td> <td> - <input type="text" size="30" name="name" value="<?php echo $info['name']; ?>" + <input size="50" type="text" name="name" value="<?php echo $info['name']; ?>" data-translate-tag="<?php echo $trans['name']; ?>"/> - <span class="error">* <?php echo $errors['name']; ?></span> + <span class="error">* <?php echo $errors['name']; ?></span> </td> </tr> <tr> @@ -52,137 +60,121 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <?php echo __('Status');?>: </td> <td> - <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><strong><?php echo __('Active');?></strong> + <input type="radio" name="isactive" value="1" <?php + echo $info['isactive'] ? 'checked="checked"' : ''; ?>><strong><?php echo __('Active');?></strong> - <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>><strong><?php echo __('Disabled');?></strong> + <input type="radio" name="isactive" value="0" <?php + echo !$info['isactive'] ? 'checked="checked"' : ''; ?>><strong><?php echo __('Disabled');?></strong> <span class="error">* <?php echo $errors['status']; ?></span> <i class="help-tip icon-question-sign" href="#status"></i> </td> </tr> <tr> - <th colspan="2"> - <em><strong><?php echo __('Group Permissions');?></strong>: <?php echo __('Applies to all group members');?> </em> - </th> - </tr> - <tr><td><?php echo __('Can <b>Create</b> Tickets');?></td> - <td> - <input type="radio" name="can_create_tickets" value="1" <?php echo $info['can_create_tickets']?'checked="checked"':''; ?> /><?php echo __('Yes');?> - - <input type="radio" name="can_create_tickets" value="0" <?php echo !$info['can_create_tickets']?'checked="checked"':''; ?> /><?php echo __('No');?> - <i><?php echo __('Ability to open tickets on behalf of users.');?></i> - </td> - </tr> - <tr><td><?php echo __('Can <b>Edit</b> Tickets</td>');?> - <td> - <input type="radio" name="can_edit_tickets" value="1" <?php echo $info['can_edit_tickets']?'checked="checked"':''; ?> /><?php echo __('Yes');?> - - <input type="radio" name="can_edit_tickets" value="0" <?php echo !$info['can_edit_tickets']?'checked="checked"':''; ?> /><?php echo __('No');?> - <i><?php echo __('Ability to edit tickets.');?></i> - </td> - </tr> - <tr><td><?php echo __('Can <b>Post Reply</b>');?></td> - <td> - <input type="radio" name="can_post_ticket_reply" value="1" <?php echo $info['can_post_ticket_reply']?'checked="checked"':''; ?> /><?php echo __('Yes');?> - - <input type="radio" name="can_post_ticket_reply" value="0" <?php echo !$info['can_post_ticket_reply']?'checked="checked"':''; ?> /><?php echo __('No');?> - <i><?php echo __('Ability to post a ticket reply.');?></i> - </td> - </tr> - <tr><td><?php echo __('Can <b>Close</b> Tickets');?></td> - <td> - <input type="radio" name="can_close_tickets" value="1" <?php echo $info['can_close_tickets']?'checked="checked"':''; ?> /><?php echo __('Yes');?> - - <input type="radio" name="can_close_tickets" value="0" <?php echo !$info['can_close_tickets']?'checked="checked"':''; ?> /><?php echo __('No');?> - <i><?php echo __('Ability to close tickets. Agents can still post a response.');?></i> - </td> - </tr> - <tr><td><?php echo __('Can <b>Assign</b> Tickets');?></td> - <td> - <input type="radio" name="can_assign_tickets" value="1" <?php echo $info['can_assign_tickets']?'checked="checked"':''; ?> /><?php echo __('Yes');?> - - <input type="radio" name="can_assign_tickets" value="0" <?php echo !$info['can_assign_tickets']?'checked="checked"':''; ?> /><?php echo __('No');?> - <i><?php echo __('Ability to assign tickets to agents.');?></i> - </td> - </tr> - <tr><td><?php echo __('Can <b>Transfer</b> Tickets');?></td> - <td> - <input type="radio" name="can_transfer_tickets" value="1" <?php echo $info['can_transfer_tickets']?'checked="checked"':''; ?> /><?php echo __('Yes');?> - - <input type="radio" name="can_transfer_tickets" value="0" <?php echo !$info['can_transfer_tickets']?'checked="checked"':''; ?> /><?php echo __('No');?> - <i><?php echo __('Ability to transfer tickets between departments.');?></i> - </td> - </tr> - <tr><td><?php echo __('Can <b>Delete</b> Tickets');?></td> - <td> - <input type="radio" name="can_delete_tickets" value="1" <?php echo $info['can_delete_tickets']?'checked="checked"':''; ?> /><?php echo __('Yes');?> - - <input type="radio" name="can_delete_tickets" value="0" <?php echo !$info['can_delete_tickets']?'checked="checked"':''; ?> /><?php echo __('No');?> - <i><?php echo __("Ability to delete tickets (Deleted tickets can't be recovered!)");?></i> - </td> - </tr> - <tr><td><?php echo __('Can Ban Emails');?></td> - <td> - <input type="radio" name="can_ban_emails" value="1" <?php echo $info['can_ban_emails']?'checked="checked"':''; ?> /><?php echo __('Yes');?> - - <input type="radio" name="can_ban_emails" value="0" <?php echo !$info['can_ban_emails']?'checked="checked"':''; ?> /><?php echo __('No');?> - <i><?php echo __('Ability to add/remove emails from banlist via ticket interface.');?></i> + <td width="180" class="required"> + <?php echo __('Default Role');?>: </td> - </tr> - <tr><td><?php echo __('Can Manage Premade');?></td> <td> - <input type="radio" name="can_manage_premade" value="1" <?php echo $info['can_manage_premade']?'checked="checked"':''; ?> /><?php echo __('Yes');?> - - <input type="radio" name="can_manage_premade" value="0" <?php echo !$info['can_manage_premade']?'checked="checked"':''; ?> /><?php echo __('No');?> - <i><?php echo __('Ability to add/update/disable/delete canned responses and attachments.');?></i> + <select name="role_id"> + <option value="0">Select One</option> + <?php + foreach ($roles as $id => $role) { + $sel = ($info['role_id'] == $id) ? 'selected="selected"' : ''; + echo sprintf('<option value="%d" %s>%s</option>', + $id, $sel, $role); + } ?> + </select> + <span class="error">* <?php echo $errors['role_id']; ?></span> + <i class="help-tip icon-question-sign" href="#role"></i> </td> </tr> - <tr><td><?php echo __('Can Manage FAQ');?></td> - <td> - <input type="radio" name="can_manage_faq" value="1" <?php echo $info['can_manage_faq']?'checked="checked"':''; ?> /><?php echo __('Yes');?> - - <input type="radio" name="can_manage_faq" value="0" <?php echo !$info['can_manage_faq']?'checked="checked"':''; ?> /><?php echo __('No');?> - <i><?php echo __('Ability to add/update/disable/delete knowledgebase categories and FAQs.');?></i> - </td> + </tbody> + <tbody> + <tr> + <th colspan="7"> + <em><strong><?php echo __('Internal Notes'); ?></strong> </em> + </th> </tr> - <tr><td><?php echo __('Can View Agent Stats');?></td> - <td> - <input type="radio" name="can_view_staff_stats" value="1" <?php echo $info['can_view_staff_stats']?'checked="checked"':''; ?> /><?php echo __('Yes');?> - - <input type="radio" name="can_view_staff_stats" value="0" <?php echo !$info['can_view_staff_stats']?'checked="checked"':''; ?> /><?php echo __('No');?> - <i><?php echo __('Ability to view stats of other agents in allowed departments.');?></i> + <tr> + <td colspan="7"><textarea name="notes" class="richtext no-bar" + rows="6" cols="80"><?php + echo $info['notes']; ?></textarea> </td> </tr> + </tbody> + </table> +</div> +<div id="departments" class="tab_content" style="display:none"> + <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <thead> <tr> - <th colspan="2"> - <em><strong><?php echo __('Department Access');?></strong>: - <i class="help-tip icon-question-sign" href="#department_access"></i> - <a id="selectAll" href="#deptckb"><?php echo __('Select All');?></a> - - <a id="selectNone" href="#deptckb"><?php echo __('Select None');?></a></em> + <th colspan=2> + <em><?php echo __('Check departments the group is allowed to access and optionally select an effective role.') ?></em> </th> </tr> - <?php - foreach (Dept::getDepartments() as $id=>$name) { - $ck=($info['depts'] && in_array($id,$info['depts']))?'checked="checked"':''; - echo sprintf('<tr><td colspan=2> <input type="checkbox" class="deptckb" name="depts[]" value="%d" %s> %s</td></tr>',$id,$ck,$name); - } - ?> <tr> - <th colspan="2"> - <em><strong><?php echo __('Admin Notes');?></strong>: <?php echo __('Internal notes viewable by all admins.');?> </em> - </th> + <th width="40%"><?php echo __('Department'); ?></th> + <th><?php echo __('Group Role'); ?></th> </tr> - <tr> - <td colspan=2> - <textarea class="richtext no-bar" name="notes" cols="21" - rows="8" style="width: 80%;"><?php echo $info['notes']; ?></textarea> + </thead> + <tbody> + <?php + foreach (Dept::getDepartments() as $deptId => $name) { ?> + <tr> + <td> + + <label> + <?php + $ck = ($info['depts'] && in_array($deptId, $info['depts'])) ? 'checked="checked"' : ''; + echo sprintf('%s %s', + sprintf('<input type="checkbox" class="dept-ckb" + name="depts[]" value="%s" %s />', + $deptId, $ck), + Format::htmlchars($name)); + ?> + </label> </td> - </tr> + <td> + <?php + $DeptAccess = $group ? $group->getDepartmentsAccess() : array(); + $_name = 'dept'.$deptId.'_role_id'; + ?> + <select name="<?php echo $_name; ?>"> + <option value="0">— <?php + echo __('Group Default'); ?> —</option> + <?php + foreach ($roles as $rid => $role) { + $sel = ''; + if (isset($info[$_name])) + $sel = ($info[$_name] == $rid) ? 'selected="selected"' : ''; + elseif ($DeptAccess && isset($DeptAccess[$deptId])) + $sel = ($DeptAccess[$deptId] == $rid) ? 'selected="selected"' : ''; + + echo sprintf('<option value="%d" %s>%s</option>', + $rid, $sel, $role); + } ?> + </select> + <i class="help-tip icon-question-sign" href="#dept-role"></i> + </td> + </tr> + <?php + } ?> </tbody> -</table> -<p style="text-align:center"> + <tfoot> + <tr> + <td colspan="2"> + <?php echo __('Select');?>: + <a id="selectAll" href="#dept-ckb"><?php echo __('All');?></a> + <a id="selectNone" href="#dept-ckb"><?php echo __('None');?></a> + <a id="selectToggle" href="#dept-ckb"><?php echo __('Toggle');?></a> + </td> + </tr> + </tfoot> + </table> +</div> +<p class="centered"> <input type="submit" name="submit" value="<?php echo $submit_text; ?>"> - <input type="reset" name="reset" value="<?php echo __('Reset');?>"> - <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="groups.php"'> + <input type="reset" name="reset" value="<?php echo __('Reset'); ?>"> + <input type="button" name="cancel" value="<?php echo __('Cancel'); ?>" + onclick='window.location.href="?"'> </p> </form> diff --git a/include/staff/groups.inc.php b/include/staff/groups.inc.php index 326400daa0ba12932943c96934a7783ffb13af92..ac3f438956466eaa2ba695303e8ddae8152ac076 100644 --- a/include/staff/groups.inc.php +++ b/include/staff/groups.inc.php @@ -1,43 +1,44 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); +if (!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) + die('Access Denied'); -$qstr=''; +$qstr = ''; +$sortOptions = array( + 'name' => 'name', + 'users' => 'members_count', + 'depts' => 'depts_count', + 'status' => 'isenabled', + 'created'=> 'created', + 'updated'=> 'updated'); + +$orderWays = array('DESC'=>'DESC', 'ASC'=>'ASC'); +$sort = ($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])]) ? strtolower($_REQUEST['sort']) : 'name'; -$sql='SELECT grp.*,count(DISTINCT staff.staff_id) as users, count(DISTINCT dept.dept_id) as depts ' - .' FROM '.GROUP_TABLE.' grp ' - .' LEFT JOIN '.STAFF_TABLE.' staff ON(staff.group_id=grp.group_id) ' - .' LEFT JOIN '.GROUP_DEPT_TABLE.' dept ON(dept.group_id=grp.group_id) ' - .' WHERE 1'; -$sortOptions=array('name'=>'grp.group_name','status'=>'grp.group_enabled', - 'users'=>'users', 'depts'=>'depts', 'created'=>'grp.created','updated'=>'grp.updated'); -$orderWays=array('DESC'=>'DESC','ASC'=>'ASC'); -$sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'name'; //Sorting options... -if($sort && $sortOptions[$sort]) { - $order_column =$sortOptions[$sort]; +if ($sort && $sortOptions[$sort]) { + $order_column = $sortOptions[$sort]; } -$order_column=$order_column?$order_column:'grp.group_name'; -if($_REQUEST['order'] && $orderWays[strtoupper($_REQUEST['order'])]) { - $order=$orderWays[strtoupper($_REQUEST['order'])]; +$order_column = $order_column ? $order_column : 'name'; + +if ($_REQUEST['order'] && isset($orderWays[strtoupper($_REQUEST['order'])])) { + $order = $orderWays[strtoupper($_REQUEST['order'])]; +} else { + $order = 'ASC'; } -$order=$order?$order:'ASC'; -if($order_column && strpos($order_column,',')){ +if ($order_column && strpos($order_column,',')) { $order_column=str_replace(','," $order,",$order_column); } $x=$sort.'_sort'; $$x=' class="'.strtolower($order).'" '; -$order_by="$order_column $order "; - +$page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1; +$count = Group::objects()->count(); +$pageNav = new Pagenate($count, $page, PAGE_LIMIT); +$_qstr = $qstr.'&sort='.urlencode($_REQUEST['sort']).'&order='.urlencode($_REQUEST['order']); +$pageNav->setURL('groups.php', $_qstr); +$showing = $pageNav->showing().' '._N('group', 'groups', $count); $qstr.='&order='.($order=='DESC'?'ASC':'DESC'); -$query="$sql GROUP BY grp.group_id ORDER BY $order_by"; -$res=db_query($query); -if($res && ($num=db_num_rows($res))) - $showing=sprintf(__('Showing 1-%1$d of %2$d groups'), $num, $num); -else - $showing=__('No groups found!'); - ?> <div class="pull-left" style="width:700px;padding-top:5px;"> <h2><?php echo __('Agent Groups');?> @@ -67,31 +68,47 @@ else <tbody> <?php $total=0; - $ids=($errors && is_array($_POST['ids']))?$_POST['ids']:null; - if($res && db_num_rows($res)) { - while ($row = db_fetch_array($res)) { + $ids = ($errors && is_array($_POST['ids'])) ? $_POST['ids'] : null; + if ($count) { + $groups= Group::objects() + ->annotate(array( + 'members_count'=>SqlAggregate::COUNT('members', true), + 'depts_count'=>SqlAggregate::COUNT('depts', true), + 'isenabled'=>new SqlExpr(array( + 'flags__hasbit' => Group::FLAG_ENABLED)) + )) + ->order_by(sprintf('%s%s', + strcasecmp($order, 'DESC') ? '' : '-', + $order_column)) + ->limit($pageNav->getLimit()) + ->offset($pageNav->getStart()); + + foreach ($groups as $group) { $sel=false; - if($ids && in_array($row['group_id'],$ids)) + $id = $group->getId(); + if($ids && in_array($id, $ids)) $sel=true; ?> - <tr id="<?php echo $row['group_id']; ?>"> + <tr id="<?php echo $id; ?>"> <td width=7px> - <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['group_id']; ?>" - <?php echo $sel?'checked="checked"':''; ?>> </td> - <td><a href="groups.php?id=<?php echo $row['group_id']; ?>"><?php echo $row['group_name']; ?></a> </td> - <td> <?php echo $row['group_enabled']?__('Active'):'<b>'.__('Disabled').'</b>'; ?></td> + <input type="checkbox" class="ckb" name="ids[]" + value="<?php echo $id; ?>" + <?php echo $sel?'checked="checked"':''; ?>> </td> + <td><a href="groups.php?id=<?php echo $id; ?>"><?php echo + $group->getName(); ?></a> </td> + <td> <?php echo $group->isenabled ? __('Active') : '<b>'.__('Disabled').'</b>'; ?></td> <td style="text-align:right;padding-right:30px"> - <?php if($row['users']>0) { ?> - <a href="staff.php?gid=<?php echo $row['group_id']; ?>"><?php echo $row['users']; ?></a> - <?php }else{ ?> 0 + <?php if ($num=$group->members_count) { ?> + <a href="staff.php?gid=<?php echo $id; ?>"><?php echo $num; ?></a> + <?php } else { ?> 0 <?php } ?> </td> <td style="text-align:right;padding-right:30px"> - <?php echo $row['depts']; ?> + <?php echo $group->depts_count; ?> </td> - <td><?php echo Format::date($row['created']); ?> </td> - <td><?php echo Format::datetime($row['updated']); ?> </td> + <td><?php echo Format::date($group->getCreateDate()); ?> </td> + <td><?php echo Format::datetime($group->getUpdateDate()); ?> </td> </tr> <?php } //end of while. @@ -99,7 +116,7 @@ else <tfoot> <tr> <td colspan="7"> - <?php if($res && $num){ ?> + <?php if ($count) { ?> <?php echo __('Select');?>: <a id="selectAll" href="#ckb"><?php echo __('All');?></a> <a id="selectNone" href="#ckb"><?php echo __('None');?></a> @@ -112,7 +129,8 @@ else </tfoot> </table> <?php -if($res && $num): //Show options.. +if ($count): + echo '<div> '.__('Page').':'.$pageNav->getPageLinks().' </div>'; ?> <p class="centered" id="actions"> <input class="button" type="submit" name="enable" value="<?php echo __('Enable');?>" > diff --git a/include/staff/helptopic.inc.php b/include/staff/helptopic.inc.php index 73db1e02235e16af3f67678d704d41a98c03908d..21dd951f3fa1d6f1ab48343775ab53f79b1c688c 100644 --- a/include/staff/helptopic.inc.php +++ b/include/staff/helptopic.inc.php @@ -118,11 +118,11 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"'; <select name="dept_id"> <option value="0">— <?php echo __('System Default'); ?> —</option> <?php - $sql='SELECT dept_id,dept_name FROM '.DEPT_TABLE.' dept ORDER by dept_name'; - if(($res=db_query($sql)) && db_num_rows($res)){ - while(list($id,$name)=db_fetch_row($res)){ + if (($depts=Dept::getDepartments())) { + foreach ($depts as $id => $name) { $selected=($info['dept_id'] && $id==$info['dept_id'])?'selected="selected"':''; - echo sprintf('<option value="%d" %s>%s</option>',$id,$selected,$name); + echo sprintf('<option value="%d" %s>%s</option>', + $id, $selected, $name); } } ?> @@ -169,11 +169,10 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"'; <select name="priority_id"> <option value="">— <?php echo __('System Default'); ?> —</option> <?php - $sql='SELECT priority_id,priority_desc FROM '.PRIORITY_TABLE.' pri ORDER by priority_urgency DESC'; - if(($res=db_query($sql)) && db_num_rows($res)){ - while(list($id,$name)=db_fetch_row($res)){ + if (($priorities=Priority::getPriorities())) { + foreach ($priorities as $id => $name) { $selected=($info['priority_id'] && $id==$info['priority_id'])?'selected="selected"':''; - echo sprintf('<option value="%d" %s>%s</option>',$id,$selected,$name); + echo sprintf('<option value="%d" %s>%s</option>', $id, $selected, $name); } } ?> @@ -231,9 +230,9 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"'; <option value="0">— <?php echo __('Unassigned'); ?> —</option> <?php if (($users=Staff::getStaffMembers())) { - echo sprintf('<OPTGROUP label="%s">', sprintf(__('Agents (%d)'), count($user))); + echo sprintf('<OPTGROUP label="%s">', + sprintf(__('Agents (%d)'), count($users))); foreach ($users as $id => $name) { - $name = new PersonsName($name); $k="s$id"; $selected = ($info['assign']==$k || $info['staff_id']==$id)?'selected="selected"':''; ?> @@ -243,15 +242,12 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"'; } echo '</OPTGROUP>'; } - $sql='SELECT team_id, name, isenabled FROM '.TEAM_TABLE.' ORDER BY name'; - if(($res=db_query($sql)) && ($cteams = db_num_rows($res))) { - echo sprintf('<OPTGROUP label="%s">', sprintf(__('Teams (%d)'), $cteams)); - while (list($id, $name, $isenabled) = db_fetch_row($res)){ + if (($teams=Team::getTeams())) { + echo sprintf('<OPTGROUP label="%s">', + sprintf(__('Teams (%d)'), count($teams))); + foreach ($teams as $id => $name) { $k="t$id"; - $selected = ($info['assign']==$k || $info['team_id']==$id)?'selected="selected"':''; - - if (!$isenabled) - $name .= ' '.__('(disabled)'); + $selected = ($info['assign']==$k || $info['team_id']==$id) ? 'selected="selected"' : ''; ?> <option value="<?php echo $k; ?>"<?php echo $selected; ?>><?php echo $name; ?></option> <?php diff --git a/include/staff/helptopics.inc.php b/include/staff/helptopics.inc.php index 1c9d3fd649344453cd9d32282f39ca257a60d2d2..681efbbe0acbc34ba12ff908e862a8242b5a5032 100644 --- a/include/staff/helptopics.inc.php +++ b/include/staff/helptopics.inc.php @@ -1,31 +1,14 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); +if (!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); -$sql='SELECT topic.* ' - .', dept.dept_name as department ' - .', priority_desc as priority ' - .' FROM '.TOPIC_TABLE.' topic ' - .' LEFT JOIN '.DEPT_TABLE.' dept ON (dept.dept_id=topic.dept_id) ' - .' LEFT JOIN '.TICKET_PRIORITY_TABLE.' pri ON (pri.priority_id=topic.priority_id) '; -$sql.=' WHERE 1'; -$order_by = '`sort`'; -$page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1; -//Ok..lets roll...create the actual query -$query="$sql ORDER BY $order_by"; -$res=db_query($query); -if($res && ($num=db_num_rows($res))) - $showing=sprintf(_N('Showing %d help topic', 'Showing %d help topics', $num), $num); -else - $showing=__('No help topics found!'); +$page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1; +$count = Topic::objects()->count(); +$pageNav = new Pagenate($count, $page, PAGE_LIMIT); +$pageNav->setURL('helptopics.php', $_qstr); +$showing = $pageNav->showing().' '._N('help topic', 'help topics', $count); -// Get the full names and filter for this page -$topics = array(); -while ($row = db_fetch_array($res)) - $topics[] = $row; - -foreach ($topics as &$t) - $t['name'] = Topic::getTopicName($t['topic_id']); +$order_by = ($cfg->getTopicSortMode() == 'm') ? 'sort' : 'topic'; ?> <div class="pull-left" style="width:700px;padding-top:5px;"> @@ -67,52 +50,67 @@ foreach ($topics as &$t) <tbody class="<?php if ($cfg->getTopicSortMode() == 'm') echo 'sortable-rows'; ?>" data-sort="sort-"> <?php - $total=0; - $ids=($errors && is_array($_POST['ids']))?$_POST['ids']:null; - if (count($topics)): + $ids= ($errors && is_array($_POST['ids'])) ? $_POST['ids'] : null; + if ($count) { + $topics = Topic::objects() + ->order_by(sprintf('%s%s', + strcasecmp($order, 'DESC') ? '' : '-', + $order_by)) + ->limit($pageNav->getLimit()) + ->offset($pageNav->getStart()); + $defaultDept = $cfg->getDefaultDept(); $defaultPriority = $cfg->getDefaultPriority(); $sort = 0; - foreach($topics as $row) { + foreach($topics as $topic) { + $id = $topic->getId(); $sort++; // Track initial order for transition $sel=false; - if($ids && in_array($row['topic_id'],$ids)) + if ($ids && in_array($id, $ids)) $sel=true; - if (!$row['dept_id'] && $defaultDept) { - $row['dept_id'] = $defaultDept->getId(); - $row['department'] = (string) $defaultDept; + if ($topic->dept_id) { + $deptId = $topic->dept_id; + $dept = (string) $topic->dept; + } elseif ($defaultDept) { + $deptId = $defaultDept->getId(); + $dept = (string) $defaultDept; + } else { + $deptId = 0; + $dept = ''; } - - if (!$row['priority'] && $defaultPriority) - $row['priority'] = (string) $defaultPriority; - + $priority = $team->priority ?: $defaultPriority; ?> - <tr id="<?php echo $row['topic_id']; ?>"> + <tr id="<?php echo $id; ?>"> <td width=7px> - <input type="hidden" name="sort-<?php echo $row['topic_id']; ?>" value="<?php - echo $row['sort'] ?: $sort; ?>"/> - <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['topic_id']; ?>" - <?php echo $sel?'checked="checked"':''; ?>> + <input type="hidden" name="sort-<?php echo $id; ?>" value="<?php + echo $topic->sort ?: $sort; ?>"/> + <input type="checkbox" class="ckb" name="ids[]" + value="<?php echo $id; ?>" <?php + echo $sel ? 'checked="checked"' : ''; ?>> </td> <td> -<?php if ($cfg->getTopicSortMode() == 'm') { ?> - <i class="icon-sort"></i> -<?php } ?> -<a href="helptopics.php?id=<?php echo $row['topic_id']; ?>"><?php echo $row['name']; ?></a> </td> - <td><?php echo $row['isactive']?__('Active'):'<b>'.__('Disabled').'</b>'; ?></td> - <td><?php echo $row['ispublic']?__('Public'):'<b>'.__('Private').'</b>'; ?></td> - <td><?php echo $row['priority']; ?></td> - <td><a href="departments.php?id=<?php echo $row['dept_id']; ?>"><?php echo $row['department']; ?></a></td> - <td> <?php echo Format::datetime($row['updated']); ?></td> + <?php + if ($cfg->getTopicSortMode() == 'm') { ?> + <i class="icon-sort"></i> + <?php } ?> + <a href="helptopics.php?id=<?php echo $id; ?>"><?php + echo Topic::getTopicName($id); ?></a> + </td> + <td><?php echo $topic->isactive ? __('Active') : '<b>'.__('Disabled').'</b>'; ?></td> + <td><?php echo $topic->ispublic ? __('Public') : '<b>'.__('Private').'</b>'; ?></td> + <td><?php echo $priority; ?></td> + <td><a href="departments.php?id=<?php echo $deptId; + ?>"><?php echo $dept; ?></a></td> + <td> <?php echo Format::datetime($team->updated); ?></td> </tr> <?php - } //end of while. - endif; ?> + } //end of foreach. + }?> <tfoot> <tr> <td colspan="7"> - <?php if($res && $num){ ?> + <?php if ($count) { ?> <?php echo __('Select');?>: <a id="selectAll" href="#ckb"><?php echo __('All');?></a> <a id="selectNone" href="#ckb"><?php echo __('None');?></a> @@ -125,7 +123,8 @@ foreach ($topics as &$t) </tfoot> </table> <?php -if($res && $num): //Show options.. +if ($count): //Show options.. + echo '<div> '.__('Page').':'.$pageNav->getPageLinks().' </div>'; ?> <p class="centered" id="actions"> <?php if ($cfg->getTopicSortMode() != 'a') { ?> diff --git a/include/staff/pages.inc.php b/include/staff/pages.inc.php index 6fbd8af00f883083023792023ccb8f896dc6f848..5f364a768592f2c1fb5cf3b61bac752eb019392e 100644 --- a/include/staff/pages.inc.php +++ b/include/staff/pages.inc.php @@ -3,7 +3,7 @@ if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); $pages = Page::objects() ->filter(array('type__in'=>array('other','landing','thank-you','offline'))) - ->annotate(array('topics'=>Aggregate::count('topics'))); + ->annotate(array('topics'=>SqlAggregate::COUNT('topics'))); $qstr=''; $sortOptions=array( 'name'=>'name', 'status'=>'isactive', diff --git a/include/staff/role.inc.php b/include/staff/role.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..49e1fff6a49fe46fc5d8e9dbe51c1e207db6711d --- /dev/null +++ b/include/staff/role.inc.php @@ -0,0 +1,118 @@ +<?php + +$info=array(); +if ($role) { + $title = __('Update Role'); + $action = 'update'; + $submit_text = __('Save Changes'); + $info = $role->getInfo(); + $newcount=2; +} else { + $title = __('Add New Role'); + $action = 'add'; + $submit_text = __('Add Role'); + $newcount=4; +} + +$info = Format::htmlchars(($errors && $_POST) ? array_merge($info, $_POST) : $info); + +?> +<form action="" method="post" id="save"> + <?php csrf_token(); ?> + <input type="hidden" name="do" value="<?php echo $action; ?>"> + <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>"> + <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> +<h2> <?php echo $role ?: __('New Role'); ?></h2> +<br> +<ul class="tabs"> + <li class="active"><a href="#definition"> + <i class="icon-file"></i> <?php echo __('Definition'); ?></a></li> + <li><a href="#permissions"> + <i class="icon-lock"></i> <?php echo __('Permissions'); ?></a></li> +</ul> +<div id="definition" class="tab_content"> + <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <thead> + <tr> + <th colspan="2"> + <h4><?php echo $title; ?></h4> + <em><?php echo __( + 'Roles are used to define agents\' permissions' + ); ?> <i class="help-tip icon-question-sign" + href="#roles"></i></em> + </th> + </tr> + </thead> + <tbody> + <tr> + <td width="180" class="required"><?php echo __('Name'); ?>:</td> + <td> + <input size="50" type="text" name="name" value="<?php echo + $info['name']; ?>"/> + <span class="error">* <?php echo $errors['name']; ?></span> + </td> + </tr> + </tbody> + <tbody> + <tr> + <th colspan="7"> + <em><strong><?php echo __('Internal Notes'); ?></strong> </em> + </th> + </tr> + <tr> + <td colspan="7"><textarea name="notes" class="richtext no-bar" + rows="6" cols="80"><?php + echo $info['notes']; ?></textarea> + </td> + </tr> + </tbody> + </table> +</div> +<div id="permissions" class="tab_content" style="display:none"> + <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <thead> + <tr> + <th> + <em><?php echo __('Check all permissions applicable to this role.') ?></em> + </th> + </tr> + </thead> + <tbody> + <?php + + $setting = $role ? $role->getPermissionInfo() : array(); + foreach (RolePermission::allPermissions() as $g => $perms) { ?> + <tr><th><?php + echo Format::htmlchars(__($g)); ?></th></tr> + <?php + foreach($perms as $k => $v) { ?> + <tr> + <td> + <label> + <?php + echo sprintf('<input type="checkbox" name="perms[]" value="%s" %s />', + $k, + (isset($setting[$k]) && $setting[$k]) ? 'checked="checked"' : ''); + ?> + + <?php + echo sprintf('%s - <em>%s</em>', + Format::htmlchars(__($v[0])), + Format::htmlchars(__($v[1]))); + ?> + </label> + </td> + </tr> + <?php + } + } ?> + </tbody> + </table> +</div> +<p class="centered"> + <input type="submit" name="submit" value="<?php echo $submit_text; ?>"> + <input type="reset" name="reset" value="<?php echo __('Reset'); ?>"> + <input type="button" name="cancel" value="<?php echo __('Cancel'); ?>" + onclick='window.location.href="?"'> +</p> +</form> diff --git a/include/staff/roles.inc.php b/include/staff/roles.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..17723f19df1f776006a3a36858faf10ade560b28 --- /dev/null +++ b/include/staff/roles.inc.php @@ -0,0 +1,125 @@ +<div class="pull-left" style="width:700;padding-top:5px;"> + <h2><?php echo __('Roles'); ?></h2> +</div> +<div class="pull-right flush-right" style="padding-top:5px;padding-right:5px;"> + <b><a href="roles.php?a=add" class="Icon list-add"><?php + echo __('Add New Role'); ?></a></b></div> +<div class="clear"></div> + +<?php +$page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1; +$count = Role::objects()->count(); +$pageNav = new Pagenate($count, $page, PAGE_LIMIT); +$pageNav->setURL('roles.php'); +$showing=$pageNav->showing().' '._N('role', 'roles', $count); + +?> +<form action="roles.php" method="POST" name="roles"> +<?php csrf_token(); ?> +<input type="hidden" name="do" value="mass_process" > +<input type="hidden" id="action" name="a" value="" > +<table class="list" border="0" cellspacing="1" cellpadding="0" width="940"> + <caption><?php echo $showing; ?></caption> + <thead> + <tr> + <th width="7"> </th> + <th><?php echo __('Name'); ?></th> + <th width="100"><?php echo __('Status'); ?></th> + <th width="200"><?php echo __('Created On') ?></th> + <th width="250"><?php echo __('Last Updated'); ?></th> + </tr> + </thead> + <tbody> + <?php foreach (Role::objects()->order_by('name') + ->limit($pageNav->getLimit()) + ->offset($pageNav->getStart()) as $role) { + $id = $role->getId(); + $sel = false; + if ($ids && in_array($id, $ids)) + $sel = true; ?> + <tr> + <td> + <?php + if ($role->isDeleteable()) { ?> + <input width="7" type="checkbox" class="ckb" name="ids[]" + value="<?php echo $id; ?>" + <?php echo $sel?'checked="checked"':''; ?>> + <?php + } else { + echo ' '; + } + ?> + </td> + <td><a href="?id=<?php echo $id; ?>"><?php echo + $role->getName(); ?></a></td> + <td> <?php echo $role->isEnabled() ? __('Active') : + '<b>'.__('Disabled').'</b>'; ?></td> + <td><?php echo Format::date($role->getCreateDate()); ?></td> + <td><?php echo Format::datetime($role->getUpdateDate()); ?></td> + </tr> + <?php } + ?> + </tbody> + <tfoot> + <tr> + <td colspan="4"> + <?php if($count){ ?> + <?php echo __('Select'); ?>: + <a id="selectAll" href="#ckb"><?php echo __('All'); ?></a> + <a id="selectNone" href="#ckb"><?php echo __('None'); ?></a> + <a id="selectToggle" href="#ckb"><?php echo __('Toggle'); ?></a> + <?php } else { + echo sprintf(__('No roles defined yet — %s add one %s!'), + '<a href="roles.php?a=add">','</a>'); + } ?> + </td> + </tr> + </tfoot> +</table> +<?php +if ($count) //Show options.. + echo '<div> '.__('Page').':'.$pageNav->getPageLinks().' </div>'; +?> + +<p class="centered" id="actions"> + <input class="button" type="submit" name="enable" value="<?php echo + __('Enable'); ?>"> + + <input class="button" type="submit" name="disable" value="<?php echo + __('Disable'); ?>"> + + <input class="button" type="submit" name="delete" value="<?php echo + __('Delete'); ?>"> +</p> +</form> + +<div style="display:none;" class="dialog" id="confirm-action"> + <h3><?php echo __('Please Confirm'); ?></h3> + <a class="close" href=""><i class="icon-remove-circle"></i></a> + <hr/> + <p class="confirm-action" style="display:none;" id="enable-confirm"> + <?php echo sprintf(__('Are you sure want to <b>enable</b> %s?'), + _N('selected role', 'selected roles', 2));?> + </p> + <p class="confirm-action" style="display:none;" id="disable-confirm"> + <?php echo sprintf(__('Are you sure want to <b>disable</b> %s?'), + _N('selected role', 'selected roles', 2));?> + </p> + <p class="confirm-action" style="display:none;" id="delete-confirm"> + <font color="red"><strong><?php echo sprintf( + __('Are you sure you want to DELETE %s?'), + _N('selected role', 'selected roles', 2)); ?></strong></font> + <br><br><?php echo __('Deleted roles CANNOT be recovered.'); ?> + </p> + <div><?php echo __('Please confirm to continue.'); ?></div> + <hr style="margin-top:1em"/> + <p class="full-width"> + <span class="buttons pull-left"> + <input type="button" value="<?php echo __('No, Cancel'); ?>" class="close"> + </span> + <span class="buttons pull-right"> + <input type="button" value="<?php echo __('Yes, Do it!'); ?>" class="confirm"> + </span> + </p> + <div class="clear"></div> +</div> diff --git a/include/staff/settings-system.inc.php b/include/staff/settings-system.inc.php index a095fbf6ab63ef1ddb4bb98caf7ee369fd8bf5f0..956d1555c3a0ba30df719fd496a54cefddb3533f 100644 --- a/include/staff/settings-system.inc.php +++ b/include/staff/settings-system.inc.php @@ -50,9 +50,8 @@ $gmtime = Misc::gmtime(); <select name="default_dept_id"> <option value="">— <?php echo __('Select Default Department');?> —</option> <?php - $sql='SELECT dept_id,dept_name FROM '.DEPT_TABLE.' WHERE ispublic=1'; - if(($res=db_query($sql)) && db_num_rows($res)){ - while (list($id, $name) = db_fetch_row($res)){ + if (($depts=Dept::getPublicDepartments())) { + foreach ($depts as $id => $name) { $selected = ($config['default_dept_id']==$id)?'selected="selected"':''; ?> <option value="<?php echo $id; ?>"<?php echo $selected; ?>><?php echo $name; ?> <?php echo __('Dept');?></option> <?php @@ -110,12 +109,12 @@ $gmtime = Misc::gmtime(); <td width="180"><?php echo __('Default Name Formatting'); ?>:</td> <td> <select name="name_format"> -<?php foreach (PersonsName::allFormats() as $n=>$f) { - list($desc, $func) = $f; - $selected = ($config['name_format'] == $n) ? 'selected="selected"' : ''; ?> - <option value="<?php echo $n; ?>" <?php echo $selected; - ?>><?php echo __($desc); ?></option> -<?php } ?> + <?php foreach (PersonsName::allFormats() as $n=>$f) { + list($desc, $func) = $f; + $selected = ($config['name_format'] == $n) ? 'selected="selected"' : ''; ?> + <option value="<?php echo $n; ?>" <?php echo $selected; + ?>><?php echo __($desc); ?></option> + <?php } ?> </select> <i class="help-tip icon-question-sign" href="#default_name_formatting"></i> </td> @@ -131,24 +130,30 @@ $gmtime = Misc::gmtime(); <td> <select name="default_locale"> <option value=""><?php echo __('Use Language Preference'); ?></option> -<?php foreach (Internationalization::allLocales() as $code=>$name) { ?> + <?php + foreach (Internationalization::allLocales() as $code=>$name) { ?> <option value="<?php echo $code; ?>" <?php if ($code == $config['default_locale']) echo 'selected="selected"'; ?>><?php echo $name; ?></option> -<?php } ?> + + <?php + } ?> </select> </td> </tr> <tr><td width="220" class="required"><?php echo __('Default Time Zone');?>:</td> <td> <select name="default_timezone" id="timezone-dropdown"> -<?php foreach (DateTimeZone::listIdentifiers() as $zone) { ?> + <?php + foreach (DateTimeZone::listIdentifiers() as $zone) { ?> <option value="<?php echo $zone; ?>" <?php if ($config['default_timezone'] == $zone) echo 'selected="selected"'; ?>><?php echo str_replace('/',' / ',$zone); ?></option> -<?php } ?> + + <?php + } ?> </select> <button class="action-button" onclick="javascript: $('head').append($('<script>').attr('src', '<?php diff --git a/include/staff/slaplans.inc.php b/include/staff/slaplans.inc.php index 1eaf4b24e1d0150a869455c23e2c19d160fc4d7a..3565ea7605554c3e828071386e68e60458d5ac60 100644 --- a/include/staff/slaplans.inc.php +++ b/include/staff/slaplans.inc.php @@ -2,45 +2,41 @@ if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); $qstr=''; -$sql='SELECT * FROM '.SLA_TABLE.' sla WHERE 1'; -$sortOptions=array('name'=>'sla.name','status'=>'sla.isactive','period'=>'sla.grace_period','date'=>'sla.created','updated'=>'sla.updated'); -$orderWays=array('DESC'=>'DESC','ASC'=>'ASC'); -$sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'name'; -//Sorting options... -if($sort && $sortOptions[$sort]) { - $order_column =$sortOptions[$sort]; +$sortOptions=array( + 'name' => 'name', + 'status' => 'isactive', + 'period' => 'grace_period', + 'created' => 'created', + 'updated' => 'updated' + ); + +$orderWays = array('DESC'=>'DESC', 'ASC'=>'ASC'); +$sort = ($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])]) ? strtolower($_REQUEST['sort']) : 'name'; +if ($sort && $sortOptions[$sort]) { + $order_column = $sortOptions[$sort]; } -$order_column=$order_column?$order_column:'sla.name'; -if($_REQUEST['order'] && $orderWays[strtoupper($_REQUEST['order'])]) { - $order=$orderWays[strtoupper($_REQUEST['order'])]; +$order_column = $order_column ? $order_column : 'name'; + +if ($_REQUEST['order'] && isset($orderWays[strtoupper($_REQUEST['order'])])) { + $order = $orderWays[strtoupper($_REQUEST['order'])]; +} else { + $order = 'ASC'; } -$order=$order?$order:'ASC'; -if($order_column && strpos($order_column,',')){ +if ($order_column && strpos($order_column,',')) { $order_column=str_replace(','," $order,",$order_column); } $x=$sort.'_sort'; $$x=' class="'.strtolower($order).'" '; -$order_by="$order_column $order "; - -$total=db_count('SELECT count(*) FROM '.SLA_TABLE.' sla '); -$page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1; -$pageNav=new Pagenate($total, $page, PAGE_LIMIT); -$pageNav->setURL('slas.php',$qstr.'&sort='.urlencode($_REQUEST['sort']).'&order='.urlencode($_REQUEST['order'])); -//Ok..lets roll...create the actual query +$page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1; +$count = SLA::objects()->count(); +$pageNav = new Pagenate($count, $page, PAGE_LIMIT); +$_qstr = $qstr.'&sort='.urlencode($_REQUEST['sort']).'&order='.urlencode($_REQUEST['order']); +$pageNav->setURL('slas.php', $_qstr); +$showing = $pageNav->showing().' '._N('SLA plan', 'SLA plans', $count); $qstr.='&order='.($order=='DESC'?'ASC':'DESC'); -$query="$sql ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit(); -$res=db_query($query); -if($res && ($num=db_num_rows($res))) - $showing=$pageNav->showing().' '._N('SLA plan', - 'SLA plans' /* SLA is abbreviation for Service Level Agreement */, - $total); -else - $showing=__('No SLA plans found!' /* SLA is abbreviation for Service Level Agreement */); - ?> - <div class="pull-left" style="width:700px;padding-top:5px;"> <h2><?php echo __('Service Level Agreements');?></h2> </div> @@ -66,38 +62,46 @@ else <tbody> <?php $total=0; - $ids=($errors && is_array($_POST['ids']))?$_POST['ids']:null; - if($res && db_num_rows($res)): + $ids = ($errors && is_array($_POST['ids'])) ? $_POST['ids'] : null; + if ($count) { + $slas = SLA::objects() + ->order_by(sprintf('%s%s', + strcasecmp($order, 'DESC') ? '' : '-', + $order_column)) + ->limit($pageNav->getLimit()) + ->offset($pageNav->getStart()); + $defaultId = $cfg->getDefaultSLAId(); - while ($row = db_fetch_array($res)) { + foreach ($slas as $sla) { $sel=false; - if($ids && in_array($row['id'],$ids)) + $id = $sla->getId(); + if($ids && in_array($id, $ids)) $sel=true; $default = ''; - if ($row['id'] == $defaultId) + if ($id == $defaultId) $default = '<small><em>(Default)</em></small>'; ?> - <tr id="<?php echo $row['id']; ?>"> + <tr id="<?php echo $id; ?>"> <td width=7px> - <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['id']; ?>" - <?php echo $sel?'checked="checked"':''; ?>> + <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $id; ?>" + <?php echo $sel ? 'checked="checked"' :'' ; ?>> </td> - <td> <a href="slas.php?id=<?php echo $row['id']; - ?>"><?php echo Format::htmlchars($row['name']); + <td> <a href="slas.php?id=<?php echo $id; + ?>"><?php echo Format::htmlchars($sla->getName()); ?></a> <?php echo $default; ?></td> - <td><?php echo $row['isactive']?__('Active'):'<b>'.__('Disabled').'</b>'; ?></td> - <td style="text-align:right;padding-right:35px;"><?php echo $row['grace_period']; ?> </td> - <td> <?php echo Format::date($row['created']); ?></td> - <td> <?php echo Format::datetime($row['updated']); ?></td> + <td><?php echo $sla->isActive() ? __('Active') : '<b>'.__('Disabled').'</b>'; ?></td> + <td style="text-align:right;padding-right:35px;"><?php echo $sla->getGracePeriod(); ?> </td> + <td> <?php echo Format::date($sla->getCreateDate()); ?></td> + <td> <?php echo Format::datetime($sla->getUpdateDate()); ?></td> </tr> <?php - } //end of while. - endif; ?> + } //end of foreach. + } ?> <tfoot> <tr> <td colspan="6"> - <?php if($res && $num){ ?> + <?php if ($count) { ?> <?php echo __('Select');?>: <a id="selectAll" href="#ckb"><?php echo __('All');?></a> <a id="selectNone" href="#ckb"><?php echo __('None');?></a> @@ -110,7 +114,7 @@ else </tfoot> </table> <?php -if($res && $num): //Show options.. +if ($count): //Show options.. echo '<div> '.__('Page').':'.$pageNav->getPageLinks().' </div>'; ?> <p class="centered" id="actions"> diff --git a/include/staff/staff.inc.php b/include/staff/staff.inc.php index 665797a0cc6e8343cfb6949853dae92c68b19c89..1bc7ca1d675f14a7be2c5346d14f173c9a1e3949 100644 --- a/include/staff/staff.inc.php +++ b/include/staff/staff.inc.php @@ -234,7 +234,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <select name="group_id" id="group_id"> <option value="0">— <?php echo __('Select Group');?> —</option> <?php - foreach (Group::getGroupNames() as $id=>$name) { + foreach (Group::getGroups() as $id=>$name) { $sel=($info['group_id']==$id)?'selected="selected"':''; echo sprintf('<option value="%d" %s>%s</option>', $id, $sel, $name); diff --git a/include/staff/staffmembers.inc.php b/include/staff/staffmembers.inc.php index 9ea2607a6db6099cdef1d28ae85ce8ac321eefe9..fe312a01f043c3918cfd9d547dee5d5f397e99fe 100644 --- a/include/staff/staffmembers.inc.php +++ b/include/staff/staffmembers.inc.php @@ -1,74 +1,92 @@ <?php -if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); +if (!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) + die('Access Denied'); + $qstr=''; -$select='SELECT staff.*,CONCAT_WS(" ",firstname,lastname) as name, grp.group_name, dept.dept_name as dept,count(m.team_id) as teams '; -$from='FROM '.STAFF_TABLE.' staff '. - 'LEFT JOIN '.GROUP_TABLE.' grp ON(staff.group_id=grp.group_id) '. - 'LEFT JOIN '.DEPT_TABLE.' dept ON(staff.dept_id=dept.dept_id) '. - 'LEFT JOIN '.TEAM_MEMBER_TABLE.' m ON(m.staff_id=staff.staff_id) '; -$where='WHERE 1 '; - -if($_REQUEST['did'] && is_numeric($_REQUEST['did'])) { - $where.=' AND staff.dept_id='.db_input($_REQUEST['did']); - $qstr.='&did='.urlencode($_REQUEST['did']); -} +$sortOptions = array( + 'name' => 'lastname', + 'username' => 'username', + 'status' => 'isactive', + 'group' => 'group__name', + 'dept' => 'dept__name', + 'created' => 'created', + 'login' => 'lastlogin' + ); -if($_REQUEST['gid'] && is_numeric($_REQUEST['gid'])) { - $where.=' AND staff.group_id='.db_input($_REQUEST['gid']); - $qstr.='&gid='.urlencode($_REQUEST['gid']); -} +$orderWays = array('DESC'=>'DESC', 'ASC'=>'ASC'); +$sort = ($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])]) ? strtolower($_REQUEST['sort']) : 'name'; -if($_REQUEST['tid'] && is_numeric($_REQUEST['tid'])) { - $where.=' AND m.team_id='.db_input($_REQUEST['tid']); - $qstr.='&tid='.urlencode($_REQUEST['tid']); +if ($sort && $sortOptions[$sort]) { + $order_column = $sortOptions[$sort]; } -$sortOptions=array('name'=>'staff.firstname,staff.lastname','username'=>'staff.username','status'=>'isactive', - 'group'=>'grp.group_name','dept'=>'dept.dept_name','created'=>'staff.created','login'=>'staff.lastlogin'); -$orderWays=array('DESC'=>'DESC','ASC'=>'ASC'); -$sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'name'; -//Sorting options... -if($sort && $sortOptions[$sort]) { - $order_column =$sortOptions[$sort]; -} -$order_column=$order_column?$order_column:'staff.firstname,staff.lastname'; +$order_column = $order_column ? $order_column : 'lastname'; -if($_REQUEST['order'] && $orderWays[strtoupper($_REQUEST['order'])]) { - $order=$orderWays[strtoupper($_REQUEST['order'])]; +if ($_REQUEST['order'] && isset($orderWays[strtoupper($_REQUEST['order'])])) { + $order = $orderWays[strtoupper($_REQUEST['order'])]; +} else { + $order = 'ASC'; } -$order=$order?$order:'ASC'; -if($order_column && strpos($order_column,',')){ +if ($order_column && strpos($order_column,',')) { $order_column=str_replace(','," $order,",$order_column); } $x=$sort.'_sort'; $$x=' class="'.strtolower($order).'" '; -$order_by="$order_column $order "; -$total=db_count('SELECT count(DISTINCT staff.staff_id) '.$from.' '.$where); -$page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1; -$pageNav=new Pagenate($total,$page,PAGE_LIMIT); -$pageNav->setURL('staff.php',$qstr.'&sort='.urlencode($_REQUEST['sort']).'&order='.urlencode($_REQUEST['order'])); -//Ok..lets roll...create the actual query +//Filers +$filters = array(); +if ($_REQUEST['did'] && is_numeric($_REQUEST['did'])) { + $filters += array('dept_id' => $_REQUEST['did']); + $qstr.='&did='.urlencode($_REQUEST['did']); +} + +if ($_REQUEST['gid'] && is_numeric($_REQUEST['gid'])) { + $filters += array('group_id' => $_REQUEST['gid']); + $qstr.='&gid='.urlencode($_REQUEST['gid']); +} + +if ($_REQUEST['tid'] && is_numeric($_REQUEST['tid'])) { + $filters += array('teams__team_id' => $_REQUEST['tid']); + $qstr.='&tid='.urlencode($_REQUEST['tid']); +} + +//agents objects +$agents = Staff::objects() + ->annotate(array( + 'teams_count'=>SqlAggregate::COUNT('teams', true), + )) + ->order_by(sprintf('%s%s', + strcasecmp($order, 'DESC') ? '' : '-', + $order_column)); + +if ($filters) + $agents->filter($filters); + +// paginate +$page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1; +$count = $agents->count(); +$pageNav = new Pagenate($count, $page, PAGE_LIMIT); +$_qstr = $qstr.'&sort='.urlencode($_REQUEST['sort']).'&order='.urlencode($_REQUEST['order']); +$pageNav->setURL('staff.php', $_qstr); +$showing = $pageNav->showing().' '._N('agent', 'agents', $count); $qstr.='&order='.($order=='DESC'?'ASC':'DESC'); -$query="$select $from $where GROUP BY staff.staff_id ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit(); -//echo $query; + +// add limits. +$agents->limit($pageNav->getLimit())->offset($pageNav->getStart()); ?> <h2><?php echo __('Agents');?></h2> + <div class="pull-left" style="width:700px;"> <form action="staff.php" method="GET" name="filter"> <input type="hidden" name="a" value="filter" > <select name="did" id="did"> <option value="0">— <?php echo __('All Department');?> —</option> <?php - $sql='SELECT dept.dept_id, dept.dept_name,count(staff.staff_id) as users '. - 'FROM '.DEPT_TABLE.' dept '. - 'INNER JOIN '.STAFF_TABLE.' staff ON(staff.dept_id=dept.dept_id) '. - 'GROUP By dept.dept_id HAVING users>0 ORDER BY dept_name'; - if(($res=db_query($sql)) && db_num_rows($res)){ - while(list($id,$name, $users)=db_fetch_row($res)){ + if (($depts=Dept::getDepartments())) { + foreach ($depts as $id => $name) { $sel=($_REQUEST['did'] && $_REQUEST['did']==$id)?'selected="selected"':''; - echo sprintf('<option value="%d" %s>%s (%s)</option>',$id,$sel,$name,$users); + echo sprintf('<option value="%d" %s>%s</option>',$id,$sel,$name); } } ?> @@ -76,14 +94,10 @@ $query="$select $from $where GROUP BY staff.staff_id ORDER BY $order_by LIMIT ". <select name="gid" id="gid"> <option value="0">— <?php echo __('All Groups');?> —</option> <?php - $sql='SELECT grp.group_id, group_name,count(staff.staff_id) as users '. - 'FROM '.GROUP_TABLE.' grp '. - 'INNER JOIN '.STAFF_TABLE.' staff ON(staff.group_id=grp.group_id) '. - 'GROUP BY grp.group_id ORDER BY group_name'; - if(($res=db_query($sql)) && db_num_rows($res)){ - while(list($id,$name,$users)=db_fetch_row($res)){ + if (($groups=Group::getGroups())) { + foreach ($groups as $id => $name) { $sel=($_REQUEST['gid'] && $_REQUEST['gid']==$id)?'selected="selected"':''; - echo sprintf('<option value="%d" %s>%s (%s)</option>',$id,$sel,$name,$users); + echo sprintf('<option value="%d" %s>%s</option>',$id,$sel,$name); } } ?> @@ -91,13 +105,10 @@ $query="$select $from $where GROUP BY staff.staff_id ORDER BY $order_by LIMIT ". <select name="tid" id="tid"> <option value="0">— <?php echo __('All Teams');?> —</option> <?php - $sql='SELECT team.team_id, team.name, count(member.staff_id) as users FROM '.TEAM_TABLE.' team '. - 'INNER JOIN '.TEAM_MEMBER_TABLE.' member ON(member.team_id=team.team_id) '. - 'GROUP BY team.team_id ORDER BY team.name'; - if(($res=db_query($sql)) && db_num_rows($res)){ - while(list($id,$name,$users)=db_fetch_row($res)){ + if (($teams=Team::getTeams())) { + foreach ($teams as $id => $name) { $sel=($_REQUEST['tid'] && $_REQUEST['tid']==$id)?'selected="selected"':''; - echo sprintf('<option value="%d" %s>%s (%s)</option>',$id,$sel,$name,$users); + echo sprintf('<option value="%d" %s>%s</option>',$id,$sel,$name); } } ?> @@ -108,13 +119,6 @@ $query="$select $from $where GROUP BY staff.staff_id ORDER BY $order_by LIMIT ". </div> <div class="pull-right flush-right" style="padding-right:5px;"><b><a href="staff.php?a=add" class="Icon newstaff"><?php echo __('Add New Agent');?></a></b></div> <div class="clear"></div> -<?php -$res=db_query($query); -if($res && ($num=db_num_rows($res))) - $showing=$pageNav->showing() . ' ' . _N('agent', 'agents', $num); -else - $showing=__('No agents found!'); -?> <form action="staff.php" method="POST" name="staff" > <?php csrf_token(); ?> <input type="hidden" name="do" value="mass_process" > @@ -135,31 +139,38 @@ else </thead> <tbody> <?php - if($res && db_num_rows($res)): - $ids=($errors && is_array($_POST['ids']))?$_POST['ids']:null; - while ($row = db_fetch_array($res)) { + if ($count): + $ids = ($errors && is_array($_POST['ids'])) ? $_POST['ids'] : null; + foreach ($agents as $agent) { + $id = $agent->getId(); $sel=false; - if($ids && in_array($row['staff_id'],$ids)) + if ($ids && in_array($id, $ids)) $sel=true; ?> - <tr id="<?php echo $row['staff_id']; ?>"> + <tr id="<?php echo $id; ?>"> <td width=7px> - <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['staff_id']; ?>" <?php echo $sel?'checked="checked"':''; ?> > - <td><a href="staff.php?id=<?php echo $row['staff_id']; ?>"><?php echo Format::htmlchars($row['name']); ?></a> </td> - <td><?php echo $row['username']; ?></td> - <td><?php echo $row['isactive']?__('Active'):'<b>'.__('Locked').'</b>'; ?> <?php echo $row['onvacation']?'<small>(<i>'.__('vacation').'</i>)</small>':''; ?></td> - <td><a href="groups.php?id=<?php echo $row['group_id']; ?>"><?php echo Format::htmlchars($row['group_name']); ?></a></td> - <td><a href="departments.php?id=<?php echo $row['dept_id']; ?>"><?php echo Format::htmlchars($row['dept']); ?></a></td> - <td><?php echo Format::date($row['created']); ?></td> - <td><?php echo Format::datetime($row['lastlogin']); ?> </td> + <input type="checkbox" class="ckb" name="ids[]" + value="<?php echo $id; ?>" <?php echo $sel ? 'checked="checked"' : ''; ?> > + <td><a href="staff.php?id=<?php echo $id; ?>"><?php echo + Format::htmlchars($agent->getName()); ?></a> </td> + <td><?php echo $agent->getUserName(); ?></td> + <td><?php echo $agent->isActive() ? __('Active') :'<b>'.__('Locked').'</b>'; ?> <?php + echo $agent->onvacation ? '<small>(<i>'.__('vacation').'</i>)</small>' : ''; ?></td> + <td><a href="groups.php?id=<?php echo $agent->group_id; ?>"><?php + echo Format::htmlchars('FIXME'/*$agent->group->getName()*/); ?></a></td> + <td><a href="departments.php?id=<?php echo + $agent->getDeptId(); ?>"><?php + echo Format::htmlchars((string) $agent->dept); ?></a></td> + <td><?php echo Format::date($agent->created); ?></td> + <td><?php echo Format::datetime($agent->lastlogin); ?> </td> </tr> <?php - } //end of while. + } //end of foreach endif; ?> <tfoot> <tr> <td colspan="8"> - <?php if($res && $num){ ?> + <?php if ($count) { ?> <?php echo __('Select');?>: <a id="selectAll" href="#ckb"><?php echo __('All');?></a> <a id="selectNone" href="#ckb"><?php echo __('None');?></a> @@ -172,7 +183,7 @@ else </tfoot> </table> <?php -if($res && $num): //Show options.. +if ($count): //Show options.. echo '<div> '.__('Page').':'.$pageNav->getPageLinks().' </div>'; ?> <p class="centered" id="actions"> diff --git a/include/staff/team.inc.php b/include/staff/team.inc.php index 0d5fdd2d37e231408761e92d70adcd41b9dd531b..ec651205c6a9bf6e4529de325ba2c921e125c47d 100644 --- a/include/staff/team.inc.php +++ b/include/staff/team.inc.php @@ -1,8 +1,8 @@ <?php if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); -$info=array(); +$info=$members=array(); $qstr=''; -if($team && $_REQUEST['a']!='add'){ +if ($team && $_REQUEST['a']!='add') { //Editing Team $title=__('Update Team'); $action='update'; @@ -11,7 +11,8 @@ if($team && $_REQUEST['a']!='add'){ $info['id']=$team->getId(); $trans['name'] = $team->getTranslateTag('name'); $qstr.='&id='.$team->getId(); -}else { + $members = $team->getMembers(); +} else { $title=__('Add New Team'); $action='create'; $submit_text=__('Create Team'); @@ -19,16 +20,29 @@ if($team && $_REQUEST['a']!='add'){ $info['noalerts']=0; $qstr.='&a='.$_REQUEST['a']; } -$info=Format::htmlchars(($errors && $_POST)?$_POST:$info); + +$info = Format::htmlchars(($errors && $_POST) ? $_POST : $info); ?> <form action="teams.php?<?php echo $qstr; ?>" method="post" id="save"> <?php csrf_token(); ?> <input type="hidden" name="do" value="<?php echo $action; ?>"> <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>"> <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> - <h2><?php echo __('Team');?> + <h2><?php echo __('Team');?> <i class="help-tip icon-question-sign" href="#teams"></i> </h2> +<br> +<ul class="tabs"> + <li class="active"><a href="#team"> + <i class="icon-file"></i> <?php echo __('Team'); ?></a></li> + <?php + if ($members) { ?> + <li><a href="#members"> + <i class="icon-group"></i> <?php echo __('Members'); ?></a></li> + <?php + } ?> +</ul> +<div id="team" class="tab_content"> <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> <thead> <tr> @@ -71,9 +85,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <span> <select name="lead_id"> <option value="0">— <?php echo __('None');?> —</option> - <option value="" disabled="disabled"><?php echo __('Select Team Lead (Optional)');?></option> <?php - if($team && ($members=$team->getMembers())){ + if ($members) { foreach($members as $k=>$staff){ $selected=($info['lead_id'] && $staff->getId()==$info['lead_id'])?'selected="selected"':''; echo sprintf('<option value="%d" %s>%s</option>',$staff->getId(),$selected,$staff->getName()); @@ -96,25 +109,6 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <i class="help-tip icon-question-sign" href="#assignment_alert"></i> </td> </tr> - <?php - if($team && ($members=$team->getMembers())){ ?> - <tr> - <th colspan="2"> - <em><strong><?php echo __('Team Members'); ?></strong>: - <i class="help-tip icon-question-sign" href="#members"></i> -</em> - </th> - </tr> - <?php - foreach($members as $k=>$staff){ - echo sprintf('<tr><td colspan=2><span style="width:350px;padding-left:5px; display:block;" class="pull-left"> - <b><a href="staff.php?id=%d">%s</a></span></b> - <input type="checkbox" name="remove[]" value="%d"><i>'.__('Remove').'</i></td></tr>', - $staff->getId(),$staff->getName(),$staff->getId()); - - - } - } ?> <tr> <th colspan="2"> <em><strong><?php echo __('Admin Notes');?></strong>: <?php echo __('Internal notes viewable by all admins.');?> </em> @@ -128,9 +122,39 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </tr> </tbody> </table> +</div> +<?php +if ($members) { ?> +<div id="members" class="tab_content" style="display:none"> + <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> + <thead> + <tr> + <th> + <em><?php echo __('Agents who are members of this team'); ?><i + class="help-tip icon-question-sign" href="#members"></i></em> + </th> + </tr> + </thead> + <tbody> + <?php + foreach($members as $k=>$staff) { + echo sprintf('<tr><td colspan=2><span style="width:350px;padding-left:5px; display:block;" class="pull-left"> + <b><a href="staff.php?id=%d">%s</a></span></b> + <input type="checkbox" name="remove[]" value="%d"><i>'.__('Remove').'</i></td></tr>', + $staff->getId() , + $staff->getName(), + $staff->getId()); + + } + ?> + </tbody> + </table> +</div> +<?php +} ?> <p style="text-align:center"> <input type="submit" name="submit" value="<?php echo $submit_text; ?>"> <input type="reset" name="reset" value="<?php echo __('Reset');?>"> - <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="teams.php"'> + <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="?"'> </p> </form> diff --git a/include/staff/teams.inc.php b/include/staff/teams.inc.php index d05c371d7a3e20ad36543d6a21e072b20b8273f9..22c08c2073dbf428d406df8320620d6cc7383615 100644 --- a/include/staff/teams.inc.php +++ b/include/staff/teams.inc.php @@ -2,40 +2,44 @@ if(!defined('OSTADMININC') || !$thisstaff || !$thisstaff->isAdmin()) die('Access Denied'); $qstr=''; -$sql='SELECT team.*,count(m.staff_id) as members,CONCAT_WS(" ",lead.firstname,lead.lastname) as team_lead '. - ' FROM '.TEAM_TABLE.' team '. - ' LEFT JOIN '.TEAM_MEMBER_TABLE.' m ON(m.team_id=team.team_id) '. - ' LEFT JOIN '.STAFF_TABLE.' lead ON(lead.staff_id=team.lead_id) '; -$sql.=' WHERE 1'; -$sortOptions=array('name'=>'team.name','status'=>'team.isenabled','members'=>'members','lead'=>'team_lead','created'=>'team.created'); -$orderWays=array('DESC'=>'DESC','ASC'=>'ASC'); -$sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'name'; +$sortOptions=array( + 'name' => 'name', + 'status' => 'isenabled', + 'members' => 'members_count', + 'lead' => 'lead__lastname', + 'created' => 'created', + 'updated' => 'updated', + ); + +$orderWays = array('DESC'=>'DESC', 'ASC'=>'ASC'); +$sort = ($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])]) ? strtolower($_REQUEST['sort']) : 'name'; + //Sorting options... -if($sort && $sortOptions[$sort]) { - $order_column =$sortOptions[$sort]; +if ($sort && $sortOptions[$sort]) { + $order_column = $sortOptions[$sort]; } -$order_column=$order_column?$order_column:'team.name'; -if($_REQUEST['order'] && $orderWays[strtoupper($_REQUEST['order'])]) { - $order=$orderWays[strtoupper($_REQUEST['order'])]; +$order_column = $order_column ? $order_column : 'name'; + +if ($_REQUEST['order'] && isset($orderWays[strtoupper($_REQUEST['order'])])) { + $order = $orderWays[strtoupper($_REQUEST['order'])]; +} else { + $order = 'ASC'; } -$order=$order?$order:'ASC'; -if($order_column && strpos($order_column,',')){ +if ($order_column && strpos($order_column,',')) { $order_column=str_replace(','," $order,",$order_column); } $x=$sort.'_sort'; $$x=' class="'.strtolower($order).'" '; -$order_by="$order_column $order "; - +$page = ($_GET['p'] && is_numeric($_GET['p'])) ? $_GET['p'] : 1; +$count = Team::objects()->count(); +$pageNav = new Pagenate($count, $page, PAGE_LIMIT); +$_qstr = $qstr.'&sort='.urlencode($_REQUEST['sort']).'&order='.urlencode($_REQUEST['order']); +$pageNav->setURL('teams.php', $_qstr); +$showing = $pageNav->showing().' '._N('team', 'teams', $count); $qstr.='&order='.($order=='DESC'?'ASC':'DESC'); -$query="$sql GROUP BY team.team_id ORDER BY $order_by"; -$res=db_query($query); -if($res && ($num=db_num_rows($res))) - $showing=sprintf(__('Showing 1-%1$d of %2$d teams'), $num, $num); -else - $showing=__('No teams found!'); ?> <div class="pull-left" style="width:700px;padding-top:5px;"> @@ -65,38 +69,52 @@ else </thead> <tbody> <?php - $total=0; - $ids=($errors && is_array($_POST['ids']))?$_POST['ids']:null; - if($res && db_num_rows($res)): - while ($row = db_fetch_array($res)) { + $ids= ($errors && is_array($_POST['ids'])) ? $_POST['ids'] : null; + if ($count) { + $teams = Team::objects() + ->annotate(array( + 'members_count'=>SqlAggregate::COUNT('members', true), + )) + ->order_by(sprintf('%s%s', + strcasecmp($order, 'DESC') ? '' : '-', + $order_column)) + ->limit($pageNav->getLimit()) + ->offset($pageNav->getStart()); + + foreach ($teams as $team) { + $id = $team->getId(); $sel=false; - if($ids && in_array($row['team_id'],$ids)) + if ($ids && in_array($id, $ids)) $sel=true; ?> - <tr id="<?php echo $row['team_id']; ?>"> + <tr id="<?php echo $id; ?>"> <td width=7px> - <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['team_id']; ?>" - <?php echo $sel?'checked="checked"':''; ?>> </td> - <td><a href="teams.php?id=<?php echo $row['team_id']; ?>"><?php echo $row['name']; ?></a> </td> - <td> <?php echo $row['isenabled']?__('Active'):'<b>'.__('Disabled').'</b>'; ?></td> + <input type="checkbox" class="ckb" name="ids[]" + value="<?php echo $id; ?>" + <?php echo $sel ? 'checked="checked"' : ''; ?>> </td> + <td><a href="teams.php?id=<?php echo $id; ?>"><?php echo + $team->getName(); ?></a> </td> + <td> <?php echo $team->isActive() ? __('Active') : '<b>'.__('Disabled').'</b>'; ?></td> <td style="text-align:right;padding-right:25px"> - <?php if($row['members']>0) { ?> - <a href="staff.php?tid=<?php echo $row['team_id']; ?>"><?php echo $row['members']; ?></a> - <?php }else{ ?> 0 + <?php if ($team->members_count > 0) { ?> + <a href="staff.php?tid=<?php echo $id; ?>"><?php + echo $team->members_count; ?></a> + <?php } else { ?> 0 <?php } ?> </td> - <td><a href="staff.php?id=<?php echo $row['lead_id']; ?>"><?php echo $row['team_lead']; ?> </a></td> - <td><?php echo Format::date($row['created']); ?> </td> - <td><?php echo Format::datetime($row['updated']); ?> </td> + <td><a href="staff.php?id=<?php + echo $team->getLeadId(); ?>"><?php echo $team->lead ?: ''; ?> </a></td> + <td><?php echo Format::date($team->created); ?> </td> + <td><?php echo Format::datetime($team->updated); ?> </td> </tr> <?php - } //end of while. - endif; ?> + } //end of foreach + }?> <tfoot> <tr> <td colspan="7"> - <?php if($res && $num){ ?> + <?php if ($count){ ?> <?php echo __('Select');?>: <a id="selectAll" href="#ckb"><?php echo __('All');?></a> <a id="selectNone" href="#ckb"><?php echo __('None');?></a> @@ -109,7 +127,8 @@ else </tfoot> </table> <?php -if($res && $num): //Show options.. +if ($count): //Show options.. + echo '<div> '.__('Page').':'.$pageNav->getPageLinks().' </div>'; ?> <p class="centered" id="actions"> <input class="button" type="submit" name="enable" value="<?php echo __('Enable');?>" > diff --git a/include/staff/templates/ticket-preview.tmpl.php b/include/staff/templates/ticket-preview.tmpl.php index 4d6e301961aa4bdccc56b78e01577ad91eb94759..55276c6d62b9d01aad30773a532d672295eb805a 100644 --- a/include/staff/templates/ticket-preview.tmpl.php +++ b/include/staff/templates/ticket-preview.tmpl.php @@ -6,6 +6,7 @@ $staff=$ticket->getStaff(); $lock=$ticket->getLock(); +$role=$thisstaff->getRole($ticket->getDeptId()); $error=$msg=$warn=null; if($lock && $lock->getStaffId()==$thisstaff->getId()) @@ -154,15 +155,15 @@ if($ticket->getNumNotes()) if($ticket->isOpen()) $options[]=array('action'=>__('Reply'),'url'=>"tickets.php?id=$tid#reply"); -if($thisstaff->canAssignTickets()) +if ($role->canAssignTickets()) $options[]=array('action'=>($ticket->isAssigned()?__('Reassign'):__('Assign')),'url'=>"tickets.php?id=$tid#assign"); -if($thisstaff->canTransferTickets()) +if ($role->canTransferTickets()) $options[]=array('action'=>__('Transfer'),'url'=>"tickets.php?id=$tid#transfer"); $options[]=array('action'=>__('Post Note'),'url'=>"tickets.php?id=$tid#note"); -if($thisstaff->canEditTickets()) +if ($role->canEditTickets()) $options[]=array('action'=>__('Edit Ticket'),'url'=>"tickets.php?id=$tid&a=edit"); if($options) { diff --git a/include/staff/templates/tickets.tmpl.php b/include/staff/templates/tickets.tmpl.php index 67f59864dd13692813ea64850ae5a87842397bed..31872def97e03e8c9b94616ce57587d964bf4a6e 100644 --- a/include/staff/templates/tickets.tmpl.php +++ b/include/staff/templates/tickets.tmpl.php @@ -1,7 +1,7 @@ <?php $select ='SELECT ticket.ticket_id,ticket.`number`,ticket.dept_id,ticket.staff_id,ticket.team_id, ticket.user_id ' - .' ,dept.dept_name,status.name as status,ticket.source,ticket.isoverdue,ticket.isanswered,ticket.created ' + .' ,dept.name as department, status.name as status,ticket.source,ticket.isoverdue,ticket.isanswered,ticket.created ' .' ,CAST(GREATEST(IFNULL(ticket.lastmessage, 0), IFNULL(ticket.reopened, 0), ticket.created) as datetime) as effective_date ' .' ,CONCAT_WS(" ", staff.firstname, staff.lastname) as staff, team.name as team ' .' ,IF(staff.staff_id IS NULL,team.name,CONCAT_WS(" ", staff.lastname, staff.firstname)) as assigned ' @@ -156,7 +156,7 @@ if ($results) { ?> </td> <?php if ($user) { ?> - <td><?php echo Format::truncate($row['dept_name'], 40); ?></td> + <td><?php echo Format::truncate($row['department'], 40); ?></td> <td> <?php echo $assigned; ?></td> <?php } else { ?> diff --git a/include/staff/ticket-edit.inc.php b/include/staff/ticket-edit.inc.php index ca63955bbf3970a0cfb6bf2b5e82613f3c5ff48e..5b464cdca0a2b2997e37125b4b810026ffaae02e 100644 --- a/include/staff/ticket-edit.inc.php +++ b/include/staff/ticket-edit.inc.php @@ -1,5 +1,10 @@ <?php -if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->canEditTickets() || !$ticket) die('Access Denied'); +if (!defined('OSTSCPINC') + || !$thisstaff + || !$ticket + || !($role=$thisstaff->getRole($ticket->getDeptId())) + || !$role->canEditTickets()) + die('Access Denied'); $info=Format::htmlchars(($errors && $_POST)?$_POST:$ticket->getUpdateInfo()); if ($_POST) diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php index 0c0c6dfe2b61199b0ac658ef82cc2588910ec0a5..9af71ea08c13084ff2aa55c4e25d8f6c87ac4ddc 100644 --- a/include/staff/ticket-open.inc.php +++ b/include/staff/ticket-open.inc.php @@ -272,7 +272,7 @@ if ($_POST) <tbody> <?php //is the user allowed to post replies?? - if($thisstaff->canPostReply()) { ?> + if($thisstaff->getRole()->canPostReply()) { ?> <tr> <th colspan="2"> <em><strong><?php echo __('Response');?></strong>: <?php echo __('Optional response to the above issue.');?></em> diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index 207d573c5da4c312a1f7ba9448167cfb2a7dec72..f3ab38354b3c3b186fa0f04260df8e3342046fb3 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -14,6 +14,7 @@ if($cfg->getLockTime() && !$ticket->acquireLock($thisstaff->getId(),$cfg->getLoc //Get the goodies. $dept = $ticket->getDept(); //Dept +$role = $thisstaff->getRole($dept); $staff = $ticket->getStaff(); //Assigned or closed by.. $user = $ticket->getOwner(); //Ticket User (EndUser) $team = $ticket->getTeam(); //Assigned team. @@ -59,8 +60,8 @@ if($ticket->isOverdue()) </td> <td width="auto" class="flush-right has_bottom_border"> <?php - if ($thisstaff->canBanEmails() - || $thisstaff->canEditTickets() + if ($role->canBanEmails() + || $role->canEditTickets() || ($dept && $dept->isManager($thisstaff))) { ?> <span class="action-button pull-right" data-dropdown="#action-dropdown-more"> <i class="icon-caret-down pull-right"></i> @@ -71,12 +72,12 @@ if($ticket->isOverdue()) // Status change options echo TicketStatus::status_options(); - if ($thisstaff->canEditTickets()) { ?> + if ($role->canEditTickets()) { ?> <a class="action-button pull-right" href="tickets.php?id=<?php echo $ticket->getId(); ?>&a=edit"><i class="icon-edit"></i> <?php echo __('Edit'); ?></a> <?php } - if ($ticket->isOpen() && !$ticket->isAssigned() && $thisstaff->canAssignTickets()) {?> + if ($ticket->isOpen() && !$ticket->isAssigned() && $role->canAssignTickets()) {?> <a id="ticket-claim" class="action-button pull-right confirm-action" href="#claim"><i class="icon-user"></i> <?php echo __('Claim'); ?></a> @@ -98,13 +99,13 @@ if($ticket->isOverdue()) <div id="action-dropdown-more" class="action-dropdown anchor-right"> <ul> <?php - if($thisstaff->canEditTickets()) { ?> + if ($role->canEditTickets()) { ?> <li><a class="change-user" href="#tickets/<?php echo $ticket->getId(); ?>/change-user"><i class="icon-user"></i> <?php echo __('Change Owner'); ?></a></li> <?php } - if($thisstaff->canDeleteTickets()) { + if ($role->canDeleteTickets()) { ?> <li><a class="ticket-action" href="#tickets/<?php echo $ticket->getId(); ?>/status/delete" @@ -142,7 +143,7 @@ if($ticket->isOverdue()) return false" ><i class="icon-paste"></i> <?php echo __('Manage Forms'); ?></a></li> -<?php if($thisstaff->canBanEmails()) { +<?php if ($role->canBanEmails()) { if(!$emailBanned) {?> <li><a class="confirm-action" id="ticket-banemail" href="#banemail"><i class="icon-ban-circle"></i> <?php echo sprintf( @@ -446,24 +447,24 @@ $tcount+= $ticket->getNumNotes(); <div id="response_options"> <ul class="tabs"> <?php - if($thisstaff->canPostReply()) { ?> + if ($role->canPostReply()) { ?> <li><a id="reply_tab" href="#reply"><?php echo __('Post Reply');?></a></li> <?php } ?> <li><a id="note_tab" href="#note"><?php echo __('Post Internal Note');?></a></li> <?php - if($thisstaff->canTransferTickets()) { ?> + if ($role->canTransferTickets()) { ?> <li><a id="transfer_tab" href="#transfer"><?php echo __('Department Transfer');?></a></li> <?php } - if($thisstaff->canAssignTickets()) { ?> + if ($role->canAssignTickets()) { ?> <li><a id="assign_tab" href="#assign"><?php echo $ticket->isAssigned()?__('Reassign Ticket'):__('Assign Ticket'); ?></a></li> <?php } ?> </ul> <?php - if($thisstaff->canPostReply()) { ?> + if ($role->canPostReply()) { ?> <form id="reply" class="tab_content" action="tickets.php?id=<?php echo $ticket->getId(); ?>#reply" name="reply" method="post" enctype="multipart/form-data"> <?php csrf_token(); ?> <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>"> @@ -621,7 +622,7 @@ print $response_form->getField('attachments')->render(); <?php $statusId = $info['reply_status_id'] ?: $ticket->getStatusId(); $states = array('open'); - if ($thisstaff->canCloseTickets() && !$outstanding) + if ($role->canCloseTickets() && !$outstanding) $states = array_merge($states, array('closed')); foreach (TicketStatusList::getStatuses( @@ -704,7 +705,7 @@ print $note_form->getField('attachments')->render(); <?php $statusId = $info['note_status_id'] ?: $ticket->getStatusId(); $states = array('open'); - if ($thisstaff->canCloseTickets()) + if ($role->canCloseTickets()) $states = array_merge($states, array('closed')); foreach (TicketStatusList::getStatuses( array('states' => $states)) as $s) { @@ -730,7 +731,7 @@ print $note_form->getField('attachments')->render(); </p> </form> <?php - if($thisstaff->canTransferTickets()) { ?> + if ($role->canTransferTickets()) { ?> <form id="transfer" class="tab_content" action="tickets.php?id=<?php echo $ticket->getId(); ?>#transfer" name="transfer" method="post" enctype="multipart/form-data"> <?php csrf_token(); ?> <input type="hidden" name="ticket_id" value="<?php echo $ticket->getId(); ?>"> @@ -790,7 +791,7 @@ print $note_form->getField('attachments')->render(); <?php } ?> <?php - if($thisstaff->canAssignTickets()) { ?> + if ($role->canAssignTickets()) { ?> <form id="assign" class="tab_content" action="tickets.php?id=<?php echo $ticket->getId(); ?>#assign" name="assign" method="post" enctype="multipart/form-data"> <?php csrf_token(); ?> <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>"> diff --git a/include/staff/tickets.inc.php b/include/staff/tickets.inc.php index 5f852956dd1f570195bf3efb7f6d480df7ace91d..a1591aa36ea3f763601b59a3b824ee34c095bc9a 100644 --- a/include/staff/tickets.inc.php +++ b/include/staff/tickets.inc.php @@ -75,7 +75,7 @@ $tickets->filter(Q::any($visibility)); // Select pertinent columns // ------------------------------------------------------------ -$tickets->values('lock__lock_id', 'staff_id', 'isoverdue', 'team_id', 'ticket_id', 'number', 'cdata__subject', 'user__default_email__address', 'source', 'cdata__:priority__priority_color', 'cdata__:priority__priority_desc', 'status__name', 'status__state', 'dept_id', 'dept__dept_name', 'user__name', 'lastupdate'); +$tickets->values('lock__lock_id', 'staff_id', 'isoverdue', 'team_id', 'ticket_id', 'number', 'cdata__subject', 'user__default_email__address', 'source', 'cdata__:priority__priority_color', 'cdata__:priority__priority_desc', 'status__name', 'status__state', 'dept_id', 'dept__name', 'user__name', 'lastupdate'); // Apply requested quick filter @@ -240,7 +240,7 @@ $_SESSION[':Q:tickets'] = $tickets; $flag='overdue'; $lc=''; - $dept = Dept::getLocalById($T['dept_id'], 'name', $T['dept__dept_name']); + $dept = Dept::getLocalById($T['dept_id'], 'name', $T['dept__name']); if($showassigned) { if($T['staff_id']) $lc=sprintf('<span class="Icon staffAssigned">%s</span>',Format::truncate((string) new PersonsName($T['staff__firstname'], $T['staff__lastname']),40)); diff --git a/include/upgrader/streams/core.sig b/include/upgrader/streams/core.sig index 776ffc839a61c0b88c0387008deb5ea18c453bce..ffd1538e853ca6f5780cc554c6aaf40d9b013416 100644 --- a/include/upgrader/streams/core.sig +++ b/include/upgrader/streams/core.sig @@ -1 +1 @@ -1ee831c854fe9f35115a3e672916bb91 +c7c828356c88b462ba2e3e1437dca0df diff --git a/include/upgrader/streams/core/1ee831c8-c7c82835.cleanup.sql b/include/upgrader/streams/core/1ee831c8-c7c82835.cleanup.sql new file mode 100644 index 0000000000000000000000000000000000000000..0355df4757646be61acf5e727ed2761f233c38a1 --- /dev/null +++ b/include/upgrader/streams/core/1ee831c8-c7c82835.cleanup.sql @@ -0,0 +1,17 @@ +-- drop old permissions from group table +ALTER TABLE `%TABLE_PREFIX%group` + DROP `group_enabled`, + DROP `can_create_tickets`, + DROP `can_edit_tickets`, + DROP `can_post_ticket_reply`, + DROP `can_delete_tickets`, + DROP `can_close_tickets`, + DROP `can_assign_tickets`, + DROP `can_transfer_tickets`, + DROP `can_ban_emails`, + DROP `can_manage_premade`, + DROP `can_manage_faq`, + DROP `can_view_staff_stats`; + +-- drop useless updated column +ALTER TABLE `%TABLE_PREFIX%team_member` DROP `updated`; diff --git a/include/upgrader/streams/core/1ee831c8-c7c82835.patch.sql b/include/upgrader/streams/core/1ee831c8-c7c82835.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..4c886287491690966973a793386ffc19262b6e74 --- /dev/null +++ b/include/upgrader/streams/core/1ee831c8-c7c82835.patch.sql @@ -0,0 +1,41 @@ +/** + * @signature c7c828356c88b462ba2e3e1437dca0df + * @version v1.9.6 + * @title Add role-based access + * + * This patch adds support for role based access to group and departments + * + */ + +CREATE TABLE `%TABLE_PREFIX%role` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `flags` int(10) unsigned NOT NULL DEFAULT '1', + `name` varchar(64) DEFAULT NULL, + `notes` text, + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) DEFAULT CHARSET=utf8; + +ALTER TABLE `%TABLE_PREFIX%group_dept_access` + ADD `role_id` INT UNSIGNED NOT NULL DEFAULT '0'; + +ALTER TABLE `%TABLE_PREFIX%groups` + ADD `role_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `group_id` , + ADD `flags` INT UNSIGNED NOT NULL DEFAULT '1' AFTER `role_id`, + CHANGE `group_name` `name` VARCHAR(120) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', + CHANGE `group_id` `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + ADD INDEX (`role_id`); + +RENAME TABLE `%TABLE_PREFIX%groups` TO `%TABLE_PREFIX%group`; + +-- department changes +ALTER TABLE `%TABLE_PREFIX%department` + CHANGE `dept_id` `id` INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT, + CHANGE `dept_signature` `signature` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + CHANGE `dept_name` `name` VARCHAR( 128 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT ''; + +-- Finished with patch +UPDATE `%TABLE_PREFIX%config` + SET `schema_signature`='c7c828356c88b462ba2e3e1437dca0df'; diff --git a/include/upgrader/streams/core/1ee831c8-c7c82835.task.php b/include/upgrader/streams/core/1ee831c8-c7c82835.task.php new file mode 100644 index 0000000000000000000000000000000000000000..09bf7a6def9a4abc5f762c59d0b0fe8a16e2a3f4 --- /dev/null +++ b/include/upgrader/streams/core/1ee831c8-c7c82835.task.php @@ -0,0 +1,43 @@ +<?php +class GroupRoles extends MigrationTask { + + var $pmap = array( + 'can_create_tickets' => 'ticket.create', + 'can_edit_tickets' => 'ticket.edit', + 'can_post_ticket_reply' => 'ticket.reply', + 'can_delete_tickets' => 'ticket.delete', + 'can_close_tickets' => 'ticket.close', + 'can_assign_tickets' => 'ticket.assign', + 'can_transfer_tickets' => 'ticket.transfer', + 'can_ban_emails' => 'emails.banlist', + 'can_manage_premade' => 'kb.premade', + 'can_manage_faq' => 'kb.faq', + 'can_view_staff_stats' => 'stats.agents'); + + function run($max_time) { + global $cfg; + // Select existing groups and create roles matching the current + // settings + foreach (Group::objects() as $group) { + $ht=array( + 'flags=1', + 'name' => sprintf('%s %s', $group->getName(), + __('Role')), + 'notes' => $group->getName() + ); + $perms = array(); + foreach (self::$pmap as $k => $v) { + if ($group->{$k}) + $perms[] = $v; + } + + $ht['permissions'] = $perms; + + $role = Role::__create($ht); + $group->role_id = $role->getId(); + $group->save(); + } + } +} + +return 'GroupRoles'; diff --git a/scp/canned.php b/scp/canned.php index 5de2f87b50e128d08c615275f9c6e2e25b5d14e8..d6a9ffa9af4858d4a0c8585d09daf4575ea85007 100644 --- a/scp/canned.php +++ b/scp/canned.php @@ -17,7 +17,7 @@ require('staff.inc.php'); include_once(INCLUDE_DIR.'class.canned.php'); /* check permission */ -if(!$thisstaff || !$thisstaff->canManageCannedResponses()) { +if(!$thisstaff || !$thisstaff->getRole()->canManageCannedResponses()) { header('Location: kb.php'); exit; } @@ -35,7 +35,7 @@ $canned_form = new Form(array( )), )); -if($_POST && $thisstaff->canManageCannedResponses()) { +if($_POST && $thisstaff->getRole()->canManageCannedResponses()) { switch(strtolower($_POST['do'])) { case 'update': if(!$canned) { diff --git a/scp/categories.php b/scp/categories.php index 422ec184135fe7a1dc6af185d02aa3a8b6a135cb..059e52d8064cfd12b6df0167e17ae299bcd2eede 100644 --- a/scp/categories.php +++ b/scp/categories.php @@ -17,7 +17,7 @@ require('staff.inc.php'); include_once(INCLUDE_DIR.'class.category.php'); /* check permission */ -if(!$thisstaff || !$thisstaff->canManageFAQ()) { +if(!$thisstaff || !$thisstaff->getRole()->canManageFAQ()) { header('Location: kb.php'); exit; } diff --git a/scp/css/scp.css b/scp/css/scp.css index b97e1cc2bcfdc37b4a840095165b790d043311dc..add15fde893acb7e58be9e9a6f639e2492f61424 100644 --- a/scp/css/scp.css +++ b/scp/css/scp.css @@ -489,8 +489,12 @@ table.list tbody tr:hover td, table.list tbody tr.highlight td { background: #F table.list tbody tr:hover td.nohover, table.list tbody tr.highlight td.nohover {} -table.list tfoot td { +table tfoot td { background:#eee; + padding: 1px; +} + +table.list tfoot td { padding: 2px; } @@ -1952,7 +1956,7 @@ tr.disabled th { .tab_content { position: relative; - padding: 10px; + padding: 5px 0; } .left-tabs { margin-left: 48px; diff --git a/scp/faq.php b/scp/faq.php index 196b0862b1a2f7bb1e0b89e58bc099c40e634b22..322420fd796e389b8a7f1b19b5203ae1332db4e0 100644 --- a/scp/faq.php +++ b/scp/faq.php @@ -153,11 +153,11 @@ else { $inc='faq-categories.inc.php'; //FAQs landing page. if($faq) { $inc='faq-view.inc.php'; - if($_REQUEST['a']=='edit' && $thisstaff->canManageFAQ()) + if($_REQUEST['a']=='edit' && $thisstaff->getRole()->canManageFAQ()) $inc='faq.inc.php'; elseif ($_REQUEST['a'] == 'print') return $faq->printPdf(); -}elseif($_REQUEST['a']=='add' && $thisstaff->canManageFAQ()) { +}elseif($_REQUEST['a']=='add' && $thisstaff->getRole()->canManageFAQ()) { $inc='faq.inc.php'; } elseif($category && $_REQUEST['a']!='search') { $inc='faq-category.inc.php'; diff --git a/scp/groups.php b/scp/groups.php index 8f273e1b11db8bf69320f2f94f4d209f0f9d5af0..494824bb108c2c83b5477ab56ed95d073f772361 100644 --- a/scp/groups.php +++ b/scp/groups.php @@ -22,39 +22,52 @@ if($_REQUEST['id'] && !($group=Group::lookup($_REQUEST['id']))) if($_POST){ switch(strtolower($_POST['do'])){ case 'update': - if(!$group){ + if (!$group) { $errors['err']=sprintf(__('%s: Unknown or invalid'), __('group')); - }elseif($group->update($_POST,$errors)){ + } elseif (!$_POST['isactive'] + && ($thisstaff->getGroupId() == $group->getId())) { + $errors['err'] = sprintf( + __("As an admin, you cannot %s a group you belong to - you might lockout all admins!"), + __('disable')); + } elseif ($group->update($_POST, $errors)) { $msg=sprintf(__('Successfully updated %s'), __('this group')); - }elseif(!$errors['err']){ + } elseif (!$errors['err']) { $errors['err']=sprintf(__('Unable to update %s. Correct error(s) below and try again!'), __('this group')); } break; - case 'create': - $group = Group::create(); - if(($group->update($_POST,$errors))){ + case 'add': + $_group = Group::create(); + if (($_group->update($_POST,$errors))) { $msg=sprintf(__('Successfully added %s'),Format::htmlchars($_POST['name'])); $_REQUEST['a']=null; - }elseif(!$errors['err']){ + } elseif(!$errors['err']) { $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'), __('this group')); } break; case 'mass_process': - if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) { + $action = strtolower($_POST['a']); + if (!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) { $errors['err'] = sprintf(__('You must select at least %s.'), __('one group')); - } elseif(in_array($thisstaff->getGroupId(), $_POST['ids'])) { - $errors['err'] = __("As an admin, you cannot disable/delete a group you belong to - you might lockout all admins!"); + } elseif(in_array($thisstaff->getGroupId(), $_POST['ids']) + && in_array($action, array('disable', 'delete'))) { + $errors['err'] = sprintf( + __("As an admin, you cannot %s a group you belong to - you might lockout all admins!"), + __('disable or delete')); } else { - $count=count($_POST['ids']); - switch(strtolower($_POST['a'])) { + $count = count($_POST['ids']); + switch($action) { case 'enable': - $sql='UPDATE '.GROUP_TABLE.' SET group_enabled=1, updated=NOW() ' - .' WHERE group_id IN ('.implode(',', db_input($_POST['ids'])).')'; - - if(db_query($sql) && ($num=db_affected_rows())){ + $num = Group::objects()->filter(array( + 'id__in' => $_POST['ids'] + ))->update(array( + 'flags'=> SqlExpression::bitor( + new SqlField('flags'), + Group::FLAG_ENABLED) + )); + if ($num) { if($num==$count) $msg = sprintf(__('Successfully activated %s'), _N('selected group', 'selected groups', $count)); @@ -67,9 +80,15 @@ if($_POST){ } break; case 'disable': - $sql='UPDATE '.GROUP_TABLE.' SET group_enabled=0, updated=NOW() ' - .' WHERE group_id IN ('.implode(',', db_input($_POST['ids'])).')'; - if(db_query($sql) && ($num=db_affected_rows())) { + $num = Group::objects()->filter(array( + 'id__in' => $_POST['ids'] + ))->update(array( + 'flags'=> SqlExpression::bitand( + new SqlField('flags'), + (~Group::FLAG_ENABLED)) + )); + + if ($num) { if($num==$count) $msg = sprintf(__('Successfully disabled %s'), _N('selected group', 'selected groups', $count)); diff --git a/scp/roles.php b/scp/roles.php new file mode 100644 index 0000000000000000000000000000000000000000..43c097f5c09c96400feae5b41d02ac8c240053fd --- /dev/null +++ b/scp/roles.php @@ -0,0 +1,139 @@ +<?php +/********************************************************************* + roles.php + + Agent's roles + + 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: +**********************************************************************/ + +require('admin.inc.php'); + +$errors = array(); +$role=null; +if ($_REQUEST['id'] && !($role = Role::lookup($_REQUEST['id']))) + $errors['err'] = sprintf(__('%s: Unknown or invalid ID.'), + __('Role')); + +if ($_POST) { + switch (strtolower($_POST['do'])) { + case 'update': + if (!$role) { + $errors['err'] = sprintf(__('%s: Unknown or invalid ID.'), + __('Role')); + } elseif ($role->update($_POST, $errors)) { + $msg = __('Role updated successfully'); + } elseif ($errors) { + $errors['err'] = $errors['err'] ?: + sprintf(__('Unable to update %s. Correct error(s) below and try again!'), + __('this role')); + } else { + $errors['err'] = sprintf(__('Unable to update %s.'), __('this role')) + .' '.__('Internal error occurred'); + } + break; + case 'add': + $_role = Role::create(); + if ($_role->update($_POST, $errors)) { + $msg = sprintf(__('Successfully added %s'), + __('role')); + } elseif ($errors) { + $errors['err'] = $errors['err'] ?: + sprintf(__('Unable to add %s. Correct error(s) below and try again.'), + __('role')); + } else { + $errors['err'] = sprintf(__('Unable to add %s.'), __('role')) + .' '.__('Internal error occurred'); + } + break; + case 'mass_process': + if (!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) { + $errors['err'] = sprintf(__('You must select at least %s'), + __('one role')); + } else { + $count = count($_POST['ids']); + switch(strtolower($_POST['a'])) { + case 'enable': + $num = Role::objects()->filter(array( + 'id__in' => $_POST['ids'] + ))->update(array( + 'flags'=> SqlExpression::bitor( + new SqlField('flags'), + Role::FLAG_ENABLED) + )); + if ($num) { + if($num==$count) + $msg = sprintf(__('Successfully enabled %s'), + _N('selected role', 'selected roles', $count)); + else + $warn = sprintf(__('%1$d of %2$d %3$s enabled'), $num, $count, + _N('selected role', 'selected roles', $count)); + } else { + $errors['err'] = sprintf(__('Unable to enable %s'), + _N('selected role', 'selected roles', $count)); + } + break; + case 'disable': + $num = Role::objects()->filter(array( + 'id__in' => $_POST['ids'] + ))->update(array( + 'flags'=> SqlExpression::bitand( + new SqlField('flags'), + (~Role::FLAG_ENABLED)) + )); + + if ($num) { + if($num==$count) + $msg = sprintf(__('Successfully disabled %s'), + _N('selected role', 'selected roles', $count)); + else + $warn = sprintf(__('%1$d of %2$d %3$s disabled'), $num, $count, + _N('selected role', 'selected roles', $count)); + } else { + $errors['err'] = sprintf(__('Unable to disable %s'), + _N('selected role', 'selected roles', $count)); + } + break; + case 'delete': + $i=0; + foreach ($_POST['ids'] as $k=>$v) { + if (($r=Role::lookup($v)) && $r->isDeleteable() && $r->delete()) + $i++; + } + if ($i && $i==$count) + $msg = sprintf(__('Successfully deleted %s'), + _N('selected role', 'selected roles', $count)); + elseif ($i > 0) + $warn = sprintf(__('%1$d of %2$d %3$s deleted'), $i, $count, + _N('selected role', 'selected roles', $count)); + elseif (!$errors['err']) + $errors['err'] = sprintf(__('Unable to delete %s — they may be in use.'), + _N('selected role', 'selected roles', $count)); + break; + default: + $errors['err'] = __('Unknown action'); + } + } + break; + } +} + +$page='roles.inc.php'; +if($role || ($_REQUEST['a'] && !strcasecmp($_REQUEST['a'], 'add'))) { + $page='role.inc.php'; + $ost->addExtraHeader('<meta name="tip-namespace" content="agents.role" />', + "$('#content').data('tipNamespace', 'agents.role');"); +} + +$nav->setTabActive('staff'); +require(STAFFINC_DIR.'header.inc.php'); +require(STAFFINC_DIR.$page); +include(STAFFINC_DIR.'footer.inc.php'); +?> diff --git a/scp/slas.php b/scp/slas.php index b47c73092e21344b8b81dceba0f3f2036a1ad697..f57ba70e7266952253b442695cb56dc6c8e128aa 100644 --- a/scp/slas.php +++ b/scp/slas.php @@ -36,11 +36,12 @@ if($_POST){ } break; case 'add': - if(($id=SLA::create($_POST,$errors))){ + $_sla = SLA::create(); + if (($_sla->update($_POST, $errors))) { $msg=sprintf(__('Successfully added %s'), __('a SLA plan')); $_REQUEST['a']=null; - }elseif(!$errors['err']){ + } elseif (!$errors['err']) { $errors['err']=sprintf(__('Unable to add %s. Correct error(s) below and try again.'), __('this SLA plan')); } @@ -53,10 +54,12 @@ if($_POST){ $count=count($_POST['ids']); switch(strtolower($_POST['a'])) { case 'enable': - $sql='UPDATE '.SLA_TABLE.' SET isactive=1 ' - .' WHERE id IN ('.implode(',', db_input($_POST['ids'])).')'; - - if(db_query($sql) && ($num=db_affected_rows())) { + $num = SLA::objects()->filter(array( + 'id__in' => $_POST['ids'] + ))->update(array( + 'isactive' => 1 + )); + if ($num) { if($num==$count) $msg = sprintf(__('Successfully enabled %s'), _N('selected SLA plan', 'selected SLA plans', $count)); @@ -69,9 +72,13 @@ if($_POST){ } break; case 'disable': - $sql='UPDATE '.SLA_TABLE.' SET isactive=0 ' - .' WHERE id IN ('.implode(',', db_input($_POST['ids'])).')'; - if(db_query($sql) && ($num=db_affected_rows())) { + $num = SLA::objects()->filter(array( + 'id__in' => $_POST['ids'] + ))->update(array( + 'isactive' => 0 + )); + + if ($num) { if($num==$count) $msg = sprintf(__('Successfully disabled %s'), _N('selected SLA plan', 'selected SLA plans', $count)); @@ -85,7 +92,7 @@ if($_POST){ break; case 'delete': $i=0; - foreach($_POST['ids'] as $k=>$v) { + foreach ($_POST['ids'] as $k => $v) { if (($p=SLA::lookup($v)) && $p->getId() != $cfg->getDefaultSLAId() && $p->delete()) diff --git a/scp/staff.php b/scp/staff.php index 384af7df0771efdeafd05a8684aa03114950b899..17a106e93c41cbe16da59755ffb0b7fdefd9563b 100644 --- a/scp/staff.php +++ b/scp/staff.php @@ -52,10 +52,13 @@ if($_POST){ $count=count($_POST['ids']); switch(strtolower($_POST['a'])) { case 'enable': - $sql='UPDATE '.STAFF_TABLE.' SET isactive=1 ' - .' WHERE staff_id IN ('.implode(',', db_input($_POST['ids'])).')'; + $num = Staff::objects()->filter(array( + 'staff_id__in' => $_POST['ids'] + ))->update(array( + 'isactive' => 1 + )); - if(db_query($sql) && ($num=db_affected_rows())) { + if ($num) { if($num==$count) $msg = sprintf('Successfully activated %s', _N('selected agent', 'selected agents', $count)); @@ -68,10 +71,13 @@ if($_POST){ } break; case 'disable': - $sql='UPDATE '.STAFF_TABLE.' SET isactive=0 ' - .' WHERE staff_id IN ('.implode(',', db_input($_POST['ids'])).') AND staff_id!='.db_input($thisstaff->getId()); + $num = Staff::objects()->filter(array( + 'staff_id__in' => $_POST['ids'] + ))->update(array( + 'isactive' => 0 + )); - if(db_query($sql) && ($num=db_affected_rows())) { + if ($num) { if($num==$count) $msg = sprintf('Successfully disabled %s', _N('selected agent', 'selected agents', $count)); @@ -84,6 +90,7 @@ if($_POST){ } break; case 'delete': + $i = 0; foreach($_POST['ids'] as $k=>$v) { if($v!=$thisstaff->getId() && ($s=Staff::lookup($v)) && $s->delete()) $i++; diff --git a/scp/teams.php b/scp/teams.php index 42fb2cbdd1bd8d38b6b215f4220db5c395558ff8..28e72adbf8e850a6c59a3f85b846514b2222f2d2 100644 --- a/scp/teams.php +++ b/scp/teams.php @@ -33,8 +33,8 @@ if($_POST){ } break; case 'create': - $team = Team::create(); - if ($team->update($_POST,$errors)) { + $_team = Team::create(); + if (($_team->update($_POST, $errors))){ $msg=sprintf(__('Successfully added %s'),Format::htmlchars($_POST['team'])); $_REQUEST['a']=null; }elseif(!$errors['err']){ @@ -49,10 +49,13 @@ if($_POST){ $count=count($_POST['ids']); switch(strtolower($_POST['a'])) { case 'enable': - $sql='UPDATE '.TEAM_TABLE.' SET isenabled=1 ' - .' WHERE team_id IN ('.implode(',', db_input($_POST['ids'])).')'; + $num = Team::objects()->filter(array( + 'team_id__in' => $_POST['ids'] + ))->update(array( + 'isenabled' => 1 + )); - if(db_query($sql) && ($num=db_affected_rows())) { + if ($num) { if($num==$count) $msg = sprintf(__('Successfully activated %s'), _N('selected team', 'selected teams', $count)); @@ -65,10 +68,13 @@ if($_POST){ } break; case 'disable': - $sql='UPDATE '.TEAM_TABLE.' SET isenabled=0 ' - .' WHERE team_id IN ('.implode(',', db_input($_POST['ids'])).')'; + $num = Team::objects()->filter(array( + 'team_id__in' => $_POST['ids'] + ))->update(array( + 'isenabled' => 0 + )); - if(db_query($sql) && ($num=db_affected_rows())) { + if ($num) { if($num==$count) $msg = sprintf(__('Successfully disabled %s'), _N('selected team', 'selected teams', $count)); diff --git a/scp/tickets.php b/scp/tickets.php index 36f075bc15c125cbb4ab2332ab9a7f1e340c8e80..f65cfd4c189bfd828d4531c5a4d1a7c25c80c9b2 100644 --- a/scp/tickets.php +++ b/scp/tickets.php @@ -58,9 +58,10 @@ if($_POST && !$errors): //More coffee please. $errors=array(); $lock=$ticket->getLock(); //Ticket lock if any + $role = $thistaff->getRole($ticket->getDeptId); switch(strtolower($_POST['a'])): case 'reply': - if(!$thisstaff->canPostReply()) + if(!$role || !$role->canPostReply()) $errors['err'] = __('Action denied. Contact admin for access'); else { @@ -108,7 +109,7 @@ if($_POST && !$errors): break; case 'transfer': /** Transfer ticket **/ //Check permission - if(!$thisstaff->canTransferTickets()) + if(!$role->canTransferTickets()) $errors['err']=$errors['transfer'] = __('Action Denied. You are not allowed to transfer tickets.'); else { @@ -141,7 +142,7 @@ if($_POST && !$errors): break; case 'assign': - if(!$thisstaff->canAssignTickets()) + if(!$role->canAssignTickets()) $errors['err']=$errors['assign'] = __('Action Denied. You are not allowed to assign/reassign tickets.'); else { @@ -212,7 +213,7 @@ if($_POST && !$errors): break; case 'edit': case 'update': - if(!$ticket || !$thisstaff->canEditTickets()) + if(!$ticket || !$role->canEditTickets()) $errors['err']=__('Permission Denied. You are not allowed to edit tickets'); elseif($ticket->update($_POST,$errors)) { $msg=__('Ticket updated successfully'); @@ -240,7 +241,7 @@ if($_POST && !$errors): } break; case 'claim': - if(!$thisstaff->canAssignTickets()) { + if(!$role->canAssignTickets()) { $errors['err'] = __('Permission Denied. You are not allowed to assign/claim tickets.'); } elseif(!$ticket->isOpen()) { $errors['err'] = __('Only open tickets can be assigned'); @@ -286,7 +287,7 @@ if($_POST && !$errors): } break; case 'banemail': - if(!$thisstaff->canBanEmails()) { + if(!$role->canBanEmails()) { $errors['err']=__('Permission Denied. You are not allowed to ban emails'); } elseif(BanList::includes($ticket->getEmail())) { $errors['err']=__('Email already in banlist'); @@ -297,7 +298,7 @@ if($_POST && !$errors): } break; case 'unbanemail': - if(!$thisstaff->canBanEmails()) { + if(!$role->canBanEmails()) { $errors['err'] = __('Permission Denied. You are not allowed to remove emails from banlist.'); } elseif(Banlist::remove($ticket->getEmail())) { $msg = __('Email removed from banlist'); @@ -308,7 +309,7 @@ if($_POST && !$errors): } break; case 'changeuser': - if (!$thisstaff->canEditTickets()) { + if (!$role->canEditTickets()) { $errors['err']=__('Permission Denied. You are not allowed to edit tickets'); } elseif (!$_POST['user_id'] || !($user=User::lookup($_POST['user_id']))) { $errors['err'] = __('Unknown user selected'); @@ -471,7 +472,8 @@ if($ticket) { $ost->setPageTitle(sprintf(__('Ticket #%s'),$ticket->getNumber())); $nav->setActiveSubMenu(-1); $inc = 'ticket-view.inc.php'; - if($_REQUEST['a']=='edit' && $thisstaff->canEditTickets()) { + if ($_REQUEST['a']=='edit' + && $thisstaff->getRole($ticket->getDeptId())->canEditTickets()) { $inc = 'ticket-edit.inc.php'; if (!$forms) $forms=DynamicFormEntry::forTicket($ticket->getId()); // Auto add new fields to the entries diff --git a/setup/inc/class.installer.php b/setup/inc/class.installer.php index 0c7ea1f5b2a7029062e40969e3312cccd4a8be08..7831cb65d953118e04d98edec18aac9708a30512 100644 --- a/setup/inc/class.installer.php +++ b/setup/inc/class.installer.php @@ -159,25 +159,22 @@ class Installer extends SetupWizard { Signal::send('system.install', $this); - $sql='SELECT `id` FROM '.TABLE_PREFIX.'sla ORDER BY `id` LIMIT 1'; + $sql='SELECT `id` FROM `'.TABLE_PREFIX.'sla` ORDER BY `id` LIMIT 1'; $sla_id_1 = db_result(db_query($sql, false)); - $sql='SELECT `dept_id` FROM '.TABLE_PREFIX.'department ORDER BY `dept_id` LIMIT 1'; + $sql='SELECT `id` FROM `'.TABLE_PREFIX.'department` ORDER BY `id` LIMIT 1'; $dept_id_1 = db_result(db_query($sql, false)); - $sql='SELECT `tpl_id` FROM '.TABLE_PREFIX.'email_template_group ORDER BY `tpl_id` LIMIT 1'; + $sql='SELECT `tpl_id` FROM `'.TABLE_PREFIX.'email_template_group` ORDER BY `tpl_id` LIMIT 1'; $template_id_1 = db_result(db_query($sql, false)); - $sql='SELECT `group_id` FROM '.TABLE_PREFIX.'groups ORDER BY `group_id` LIMIT 1'; + $sql='SELECT `id` FROM `'.TABLE_PREFIX.'group` ORDER BY `id` LIMIT 1'; $group_id_1 = db_result(db_query($sql, false)); - $sql='SELECT `value` FROM '.TABLE_PREFIX.'config WHERE namespace=\'core\' and `key`=\'default_timezone_id\' LIMIT 1'; - $default_timezone = db_result(db_query($sql, false)); - //Create admin user. $sql='INSERT INTO '.TABLE_PREFIX.'staff SET created=NOW() ' .", isactive=1, isadmin=1, group_id='$group_id_1', dept_id='$dept_id_1'" - .", timezone_id='$default_timezone', max_page_size=25" + .', max_page_size=25' .', email='.db_input($vars['admin_email']) .', firstname='.db_input($vars['fname']) .', lastname='.db_input($vars['lname']) diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql index f32ff6d74962ad72ed72886ef9fcefb145d1e039..e35b6a364b6e6de4ee4af0ff4ed01eda630f50ee 100644 --- a/setup/inc/streams/core/install-mysql.sql +++ b/setup/inc/streams/core/install-mysql.sql @@ -197,22 +197,22 @@ CREATE TABLE `%TABLE_PREFIX%list_items` ( DROP TABLE IF EXISTS `%TABLE_PREFIX%department`; CREATE TABLE `%TABLE_PREFIX%department` ( - `dept_id` int(11) unsigned NOT NULL auto_increment, + `id` int(11) unsigned NOT NULL auto_increment, `tpl_id` int(10) unsigned NOT NULL default '0', `sla_id` int(10) unsigned NOT NULL default '0', `email_id` int(10) unsigned NOT NULL default '0', `autoresp_email_id` int(10) unsigned NOT NULL default '0', `manager_id` int(10) unsigned NOT NULL default '0', - `dept_name` varchar(128) NOT NULL default '', - `dept_signature` text NOT NULL, + `name` varchar(128) NOT NULL default '', + `signature` text NOT NULL, `ispublic` tinyint(1) unsigned NOT NULL default '1', `group_membership` tinyint(1) NOT NULL default '0', `ticket_auto_response` tinyint(1) NOT NULL default '1', `message_auto_response` tinyint(1) NOT NULL default '0', `updated` datetime NOT NULL, `created` datetime NOT NULL, - PRIMARY KEY (`dept_id`), - UNIQUE KEY `dept_name` (`dept_name`), + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`), KEY `manager_id` (`manager_id`), KEY `autoresp_email_id` (`autoresp_email_id`), KEY `tpl_id` (`tpl_id`) @@ -388,35 +388,39 @@ CREATE TABLE `%TABLE_PREFIX%file_chunk` ( PRIMARY KEY (`file_id`, `chunk_id`) ) DEFAULT CHARSET=utf8; -DROP TABLE IF EXISTS `%TABLE_PREFIX%groups`; -CREATE TABLE `%TABLE_PREFIX%groups` ( - `group_id` int(10) unsigned NOT NULL auto_increment, - `group_enabled` tinyint(1) unsigned NOT NULL default '1', - `group_name` varchar(50) NOT NULL default '', - `can_create_tickets` tinyint(1) unsigned NOT NULL default '1', - `can_edit_tickets` tinyint(1) unsigned NOT NULL default '1', - `can_post_ticket_reply` tinyint( 1 ) unsigned NOT NULL DEFAULT '1', - `can_delete_tickets` tinyint(1) unsigned NOT NULL default '0', - `can_close_tickets` tinyint(1) unsigned NOT NULL default '1', - `can_assign_tickets` tinyint(1) unsigned NOT NULL default '1', - `can_transfer_tickets` tinyint(1) unsigned NOT NULL default '1', - `can_ban_emails` tinyint(1) unsigned NOT NULL default '0', - `can_manage_premade` tinyint(1) unsigned NOT NULL default '0', - `can_manage_faq` tinyint(1) unsigned NOT NULL default '0', - `can_view_staff_stats` tinyint( 1 ) unsigned NOT NULL DEFAULT '0', +DROP TABLE IF EXISTS `%TABLE_PREFIX%group`; +CREATE TABLE `%TABLE_PREFIX%group` ( + `id` int(10) unsigned NOT NULL auto_increment, + `role_id` int(11) unsigned NOT NULL, + `flags` int(11) unsigned NOT NULL default '1', + `name` varchar(120) NOT NULL default '', `notes` text, `created` datetime NOT NULL, `updated` datetime NOT NULL, - PRIMARY KEY (`group_id`), - KEY `group_active` (`group_enabled`) + PRIMARY KEY (`id`), + KEY `role_id` (`role_id`) +) DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `%TABLE_PREFIX%role`; +CREATE TABLE `%TABLE_PREFIX%role` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `flags` int(10) unsigned NOT NULL DEFAULT '1', + `name` varchar(64) DEFAULT NULL, + `notes` text, + `created` datetime NOT NULL, + `updated` datetime NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) ) DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `%TABLE_PREFIX%group_dept_access`; CREATE TABLE `%TABLE_PREFIX%group_dept_access` ( `group_id` int(10) unsigned NOT NULL default '0', `dept_id` int(10) unsigned NOT NULL default '0', + `role_id` int(10) unsigned NOT NULL default '0', UNIQUE KEY `group_dept` (`group_id`,`dept_id`), - KEY `dept_id` (`dept_id`) + KEY `dept_id` (`dept_id`), + KEY `role_id` (`role_id`) ) DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `%TABLE_PREFIX%help_topic`; diff --git a/setup/test/tests/stubs.php b/setup/test/tests/stubs.php index 5e3904e62d1ba00ccf14b1537d851bb8a81f6d6c..dbd057d515db677b957cd3d8345ad26872ecebd8 100644 --- a/setup/test/tests/stubs.php +++ b/setup/test/tests/stubs.php @@ -30,6 +30,7 @@ class mysqli_stmt { class mysqli_result { function free() {} function free_result() {} + function fetch_fields() {} } class ReflectionClass { @@ -122,4 +123,35 @@ class IntlBreakIterator { class SqlFunction { static function NOW() {} } + +class SqlExpression { + static function plus() {} + static function times() {} + static function bitor() {} + static function bitand() {} +} + +class SqlInterval { + static function MINUTE() {} + static function DAY() {} +} + +class SqlAggregate { + static function COUNT() {} +} + +class Q { + static function ANY() {} +} + +class IntlDateFormatter { + function setPattern() {} + function getPattern() {} + function parse() {} +} + +class ResourceBundle { + function getLocales() {} +} + ?>