diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index e4fdafcc25a8a72fa2d0eb0cb721eebafd5feae5..84e939550b93de56efea6f194a7ef2a3a07e2c7c 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -695,5 +695,256 @@ class TicketsAjaxAPI extends AjaxController { return $canned->getFormattedResponse($format, $varReplacer); } + + function changeTicketStatus($tid, $status) { + global $thisstaff; + + if (!$thisstaff) + Http::response(403, 'Access denied'); + elseif (!$tid + || !($ticket=Ticket::lookup($tid)) + || !$ticket->checkStaffAccess($thisstaff)) + Http::response(404, 'Unknown ticket #'); + + $info = array(); + switch($status) { + case 'open': + case 'reopen': + $state = 'open'; + break; + case 'resolve': + $state = 'resolved'; + break; + case 'close': + if (!$thisstaff->canCloseTickets()) + Http::response(403, 'Access denied'); + $state = 'closed'; + break; + case 'delete': + if (!$thisstaff->canDeleteTickets()) + Http::response(403, 'Access denied'); + $state = 'deleted'; + $info = array( + 'warn' => sprintf(__('Are you sure you want to DELETE %s?'), + __('this ticket')), + //TODO: remove message below once we ship data retention plug + 'extra' => sprintf('<strong>%s</strong>', + __('Deleted tickets CANNOT be recovered, + including any associated attachments.'))); + break; + default: + $info['warn'] = sprintf('%s %s', + __('Unknown or invalid'), __('status')); + } + + $verb = TicketStateField::getVerb($state); + + $info['action'] = sprintf('#tickets/%d/status/%s', $ticket->getId(), $status); + $info['title'] = sprintf('%s %s #%s', + $verb ?: $status, + __('Ticket'), $ticket->getNumber()); + $info['status_id'] = $_REQUEST['status_id'] ?: $ticket->getStatusId(); + + return self::_setStatus($state, $info); + } + + function setTicketStatus($tid, $state) { + global $thisstaff, $ost; + + if (!$thisstaff) + Http::response(403, 'Access denied'); + elseif (!$tid + || !($ticket=Ticket::lookup($tid)) + || !$ticket->checkStaffAccess($thisstaff)) + Http::response(404, 'Unknown ticket #'); + + $errors = $info = array(); + if (!($status= TicketStatus::lookup($_REQUEST['status_id']))) + $errors['status_id'] = sprintf('%s %s', + __('Unknown or invalid'), __('status')); + elseif (!$errors) { + // Make sure the agent has permission to set the status + switch(mb_strtolower($status->getState())) { + case 'open': + if (!$thisstaff->canCloseTickets() + && !$thisstaff->canCreateTickets()) + $errors['err'] = sprintf(__('You do not have + permission to %s.'), + __('reopen tickets')); + break; + case 'resolved': + case 'closed': + if (!$thisstaff->canCloseTickets()) + $errors['err'] = sprintf(__('You do not have + permission to %s.'), + __('resolve/close tickets')); + break; + case 'deleted': + if (!$thisstaff->canDeleteTickets()) + $errors['err'] = sprintf(__('You do not have + permission to %s.'), + __('archive/delete tickets')); + break; + default: + $errors['err'] = sprintf('%s %s', + __('Unknown or invalid'), __('status')); + } + } + + if ($ticket->setStatus($status, $_REQUEST['comments'])) { + $_SESSION['::sysmsgs']['msg'] = sprintf( + __('Successfully updated status to %s'), + $status->getName()); + Http::response(201, 'Successfully processed'); + } + + $errors['err'] = __('Error updating ticket status'); + $info['errors'] = $errors; + return self::_setStatus($state, $info); + } + + function changeTicketsStatus($status, $id=0) { + global $thisstaff, $cfg; + + if (!$thisstaff) + Http::response(403, 'Access denied'); + + $state = null; + $info = array(); + switch($status) { + case 'open': + case 'reopen': + $state = 'open'; + break; + case 'resolve': + $state = 'resolved'; + break; + case 'close': + if (!$thisstaff->canCloseTickets()) + Http::response(403, 'Access denied'); + $state = 'closed'; + break; + case 'delete': + if (!$thisstaff->canDeleteTickets()) + Http::response(403, 'Access denied'); + + $state = 'deleted'; + $info = array( + 'warn' => sprintf(__('Are you sure you want to DELETE %s?'), + _N('selected ticket', 'selected tickets', $_REQUEST['count'])), + //TODO: remove message below once we ship data retention plug + 'extra' => sprintf('<strong>%s</strong>', + __('Deleted tickets CANNOT be recovered, + including any associated attachments.'))); + break; + default: + $info['warn'] = sprintf('%s %s', + __('Unknown or invalid'), __('status')); + } + + $info['title'] = sprintf('%s %s', + TicketStateField::getVerb($state), + __('Tickets')); + + if ($_REQUEST['count']) + $info['title'] .= sprintf(' — %d %s', + $_REQUEST['count'], __('selected')); + + $info['status_id'] = $id; + + return self::_setStatus($state, $info); + } + + function setTicketsStatus($state) { + global $thisstaff, $ost; + + $errors = $info = array(); + if (!$thisstaff || !$thisstaff->canManageTickets()) + $errors['err']=__('You do not have permission to mass manage tickets. Contact admin for such access'); + elseif (!$_REQUEST['tids'] || !count($_REQUEST['tids'])) + $errors['err']=sprintf(__('You must select at least %s.'), + __('one ticket')); + elseif (!($status= TicketStatus::lookup($_REQUEST['status_id']))) + $errors['status_id'] = sprintf('%s %s', + __('Unknown or invalid'), __('status')); + elseif (!$errors) { + // Make sure the agent has permission to set the status + switch(mb_strtolower($status->getState())) { + case 'open': + if (!$thisstaff->canCloseTickets() + && !$thisstaff->canCreateTickets()) + $errors['err'] = sprintf(__('You do not have + permission to %s.'), + __('reopen tickets')); + break; + case 'resolved': + case 'closed': + if (!$thisstaff->canCloseTickets()) + $errors['err'] = sprintf(__('You do not have + permission to %s.'), + __('resolve/close tickets')); + break; + case 'deleted': + if (!$thisstaff->canDeleteTickets()) + $errors['err'] = sprintf(__('You do not have + permission to %s.'), + __('archive/delete tickets')); + break; + default: + $errors['err'] = sprintf('%s %s', + __('Unknown or invalid'), __('status')); + } + } + + if (!$errors) { + $i = 0; + $count = count($_REQUEST['tids']); + $comments = $_REQUEST['comments']; + foreach ($_REQUEST['tids'] as $tid) { + if (($ticket=Ticket::lookup($tid)) + && $ticket->getStatusId() != $status->getId() + && $ticket->checkStaffAccess($thisstaff) + && $ticket->setStatus($status, $comments)) + $i++; + } + + if (!$i) + $errors['err'] = sprintf(__('Unable to set status for %s'), + _N('selected ticket', 'selected tickets', $count)); + else { + // Assume success + if ($i==$count) { + $_SESSION['::sysmsgs']['msg'] = sprintf( + __('Successfully updated %s status to %s'), + _N('selected ticket', 'selected tickets', + $count), + $status->getName()); + } else { + $_SESSION['::sysmsgs']['warn'] = sprintf( + __('%1$d of %2$d %3$s status updated to %4$s'),$i, $count, + _N('selected ticket', 'selected tickets', + $count), + $status->getName()); + } + + Http::response(201, 'Successfully processed'); + } + } + + $info['errors'] = $errors; + return self::_setStatus($state, $info); + } + + function _setStatus($state, $info=array()) { + + $errors = array(); + if ($info && isset($info['errors'])) + $errors = $info['errors']; + + if (!$info['error'] && isset($errors['err'])) + $info['error'] = $errors['err']; + + include(STAFFINC_DIR . 'templates/ticket-status.tmpl.php'); + } } ?> diff --git a/include/class.forms.php b/include/class.forms.php index 4411290f073dbde95d745d146ae683c8680e210c..c5c93866e9fa35b9404de09a3cf54dc2fe095628 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -787,6 +787,10 @@ class ChoiceField extends FormField { } function toString($value) { + return (string) $this->getChoice($value); + } + + function getChoice($value) { $choices = $this->getChoices(); $selection = array(); @@ -1021,12 +1025,30 @@ FormField::addFieldTypes(/*trans*/ 'Dynamic Fields', function() { class TicketStateField extends ChoiceField { - static $_choices = array( - 'open' => 'Open', - 'resolved' => 'Resolved', - 'closed' => 'Closed', - 'archived' => 'Archived', - 'deleted' => 'Deleted' + static $_states = array( + 'open' => array( + 'name' => /* trans */ 'Open', + 'verb' => /* trans */ 'Open' + ), + 'resolved' => array( + 'name' => /* trans */ 'Resolved', + 'verb' => /* trans */ 'Resolve' + ), + 'closed' => array( + 'name' => /* trans */ 'Closed', + 'verb' => /* trans */ 'Close' + ) + ); + // Private states + static $_privatestates = array( + 'archived' => array( + 'name' => /* trans */ 'Archived', + 'verb' => /* trans */ 'Archive' + ), + 'deleted' => array( + 'name' => /* trans */ 'Deleted', + 'verb' => /* trans */ 'Delete' + ) ); function hasIdValue() { @@ -1038,20 +1060,51 @@ class TicketStateField extends ChoiceField { } function getChoices() { - $this->ht['default'] = ''; + static $_choices; + + if (!isset($_choices)) { + // Translate and cache the choices + foreach (static::$_states as $k => $v) + $_choices[$k] = __($v['name']); + + $this->ht['default'] = ''; + } - return static::$_choices; + return $_choices; + } + + function getChoice($state) { + + if ($state && is_array($state)) + $state = key($state); + + if (isset(static::$_states[$state])) + return __(static::$_states[$state]['name']); + + if (isset(static::$_privatestates[$state])) + return __(static::$_privatestates[$state]['name']); + + return $state; } function getConfigurationOptions() { return array( 'prompt' => new TextboxField(array( - 'id'=>2, 'label'=>'Prompt', 'required'=>false, 'default'=>'', - 'hint'=>'Leading text shown before a value is selected', + 'id'=>2, 'label'=> __('Prompt'), 'required'=>false, 'default'=>'', + 'hint'=> __('Leading text shown before a value is selected'), 'configuration'=>array('size'=>40, 'length'=>40), )), ); } + + static function getVerb($state) { + + if (isset(static::$_states[$state])) + return __(static::$_states[$state]['verb']); + + if (isset(static::$_privatestates[$state])) + return __(static::$_privatestates[$state]['verb']); + } } FormField::addFieldTypes('Dynamic Fields', function() { return array( diff --git a/include/class.list.php b/include/class.list.php index 6f322d35e88b982346f4a28d187abbcaeec27b47..31a3dd51938e8f56fd841021b7021d72a1ec3291 100644 --- a/include/class.list.php +++ b/include/class.list.php @@ -649,30 +649,38 @@ class TicketStatusList extends CustomListHandler { return TicketStatus::objects()->count(); } - function getAllItems($states=array()) { - if ($states) - $items = TicketStatus::objects()->filter( - array('state__in' => $states)) - ->order_by($this->getListOrderBy()); - else - $items = TicketStatus::objects()->order_by($this->getListOrderBy()); + function getAllItems() { + if (!$this->_items) + $this->_items = TicketStatus::objects()->order_by($this->getListOrderBy()); - return $items; + return $this->_items; } - function getItems($criteria) { + function getItems($criteria = array()) { - if (!$this->_items) { - $this->_items = TicketStatus::objects()->filter( - array('flags__hasbit' => TicketStatus::ENABLED)) - ->order_by($this->getListOrderBy()); - if ($criteria['limit']) - $this->_items->limit($criteria['limit']); - if ($criteria['offset']) - $this->_items->offset($criteria['offset']); - } + // Default to only enabled items + if (!isset($criteria['enabled'])) + $criteria['enabled'] = true; - return $this->_items; + $filters = array(); + if ($criteria['enabled']) + $filters['mode__hasbit'] = TicketStatus::ENABLED; + if ($criteria['states'] && is_array($criteria['states'])) + $filters['state__in'] = $criteria['states']; + else + $filters['state__isnull'] = false; + + $items = TicketStatus::objects(); + if ($filters) + $items->filter($filters); + if ($criteria['limit']) + $items->limit($criteria['limit']); + if ($criteria['offset']) + $items->offset($criteria['offset']); + + $items->order_by($this->getListOrderBy()); + + return $items; } function getItem($val) { @@ -686,6 +694,7 @@ class TicketStatusList extends CustomListHandler { function addItem($vars, &$errors) { $item = TicketStatus::create(array( + 'mode' => 1, 'flags' => 0, 'sort' => $vars['sort'], 'name' => $vars['value'], @@ -697,12 +706,12 @@ class TicketStatusList extends CustomListHandler { return $item; } - static function getAll($states=array()) { + static function getStatuses($criteria=array()) { $statuses = array(); if (($list = DynamicList::lookup( array('type' => 'ticket-status')))) - $statuses = $list->getAllItems($states); + $statuses = $list->getItems($criteria); return $statuses; } @@ -739,12 +748,18 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { 'table' => TICKET_STATUS_TABLE, 'ordering' => array('name'), 'pk' => array('id'), + 'joins' => array( + 'tickets' => array( + 'reverse' => 'TicketModel.status', + ) + ) ); var $_list; var $_form; var $_config; var $_settings; + var $_properties; const ENABLED = 0x0001; const INTERNAL = 0x0002; // Forbid deletion or name and status change. @@ -813,7 +828,7 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { } function isEnableable() { - return $this->hasProperties(); + return ($this->getState()); } function isDisableable() { @@ -822,11 +837,19 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { function isDeletable() { - return !($this->isInternal() || $this->isDefault()); + return !($this->isInternal() + || $this->isDefault() + || $this->getNumTickets()); } function isInternal() { - return ($this->hasFlag('mode', self::INTERNAL)); + return ($this->isDefault() + || $this->hasFlag('mode', self::INTERNAL)); + } + + + function getNumTickets() { + return $this->tickets->count(); } function getId() { @@ -853,14 +876,25 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { return $this->get('sort'); } + private function getProperties() { + + if (!isset($this->_properties)) { + $this->_properties = $this->_config->get('properties'); + if (is_string($this->_properties)) + $this->_properties = JsonDataParser::parse($this->_properties); + elseif (!$this->_properties) + $this->_properties = array(); + } + + return $this->_properties; + } + function getConfiguration() { if (!$this->_settings) { - $this->_settings = $this->_config->get('properties'); - if (is_string($this->_settings)) - $this->_settings = JsonDataParser::parse($this->_settings); - elseif (!$this->_settings) - $this->_settings = array(); + $this->_settings = $this->getProperties(); + if (!$this->_settings) + $this->_settings = array(); if ($this->getConfigurationForm()) { foreach ($this->getConfigurationForm()->getFields() as $f) { @@ -952,11 +986,11 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { function delete() { + // Statuses with tickets are not deletable if (!$this->isDeletable()) return false; - // TODO: Delete and do house cleaning (move tickets..etc) - + return parent::delete(); } function toString() { @@ -967,6 +1001,15 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { return $this->toString(); } + static function create($ht) { + + if (!isset($ht['mode'])) + $ht['mode'] = 1; + + $ht['created'] = new SqlFunction('NOW'); + + return parent::create($ht); + } static function lookup($var, $list= false) { @@ -984,7 +1027,6 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { $properties = JsonDataEncoder::encode($ht['properties']); unset($ht['properties']); - $ht['created'] = new SqlFunction('NOW'); if ($status = TicketStatus::create($ht)) { $status->save(true); $status->_config = new Config('TS.'.$status->getId()); @@ -993,9 +1035,11 @@ class TicketStatus extends VerySimpleModel implements CustomListItem { return $status; } -} - - + static function options() { + include(STAFFINC_DIR . 'templates/status-options.tmpl.php'); + } +} +TicketStatus::_inspect(); ?> diff --git a/include/class.nav.php b/include/class.nav.php index 6f72add47f346900b3230096cd5324da0e14a6eb..bc4956f084b4b810b17d242637f38bcc365bbee8 100644 --- a/include/class.nav.php +++ b/include/class.nav.php @@ -102,6 +102,8 @@ class StaffNav { function addSubMenu($item,$active=false){ + // Triger lazy loading if submenus haven't been initialized + isset($this->submenus[$this->getPanel().'.'.$this->activetab]); $this->submenus[$this->getPanel().'.'.$this->activetab][]=$item; if($active) $this->activeMenu=sizeof($this->submenus[$this->getPanel().'.'.$this->activetab]); diff --git a/include/class.orm.php b/include/class.orm.php index f46fd8ea5a37972e032d89ae52ce8198e46f95f7..264582fb6885c4dd636aaa09f9777f5c421e652d 100644 --- a/include/class.orm.php +++ b/include/class.orm.php @@ -42,10 +42,14 @@ class VerySimpleModel { elseif (isset(static::$meta['joins'][$field])) { // TODO: Support instrumented lists and such $j = static::$meta['joins'][$field]; - $class = $j['fkey'][0]; - $v = $this->ht[$field] = $class::lookup( - array($j['fkey'][1] => $this->ht[$j['local']])); - return $v; + // Make sure joins were inspected + if (isset($j['fkey']) + && ($class = $j['fkey'][0]) + && class_exists($class)) { + $v = $this->ht[$field] = $class::lookup( + array($j['fkey'][1] => $this->ht[$j['local']])); + return $v; + } } if (isset($default)) return $default; diff --git a/include/class.search.php b/include/class.search.php index e34d032248f0faec48cfcf6d83a9bb14319c30ef..1beeb2d5c26fa7b1e3aeba5dc4ed0bb2d29475df 100644 --- a/include/class.search.php +++ b/include/class.search.php @@ -382,7 +382,7 @@ class MysqlSearchBackend extends SearchBackend { } static function createSearchTable() { - $sql = 'CREATE TABLE '.TABLE_PREFIX.'_search ( + $sql = 'CREATE TABLE IF NOT EXISTS '.TABLE_PREFIX.'_search ( `object_type` varchar(8) not null, `object_id` int(11) unsigned not null, `title` text collate utf8_general_ci, @@ -406,7 +406,8 @@ class MysqlSearchBackend extends SearchBackend { return true; // Create the search table automatically - $class::createSearchTable(); + $class::__init(); + }; // THREADS ---------------------------------- @@ -558,5 +559,14 @@ class MysqlSearchBackend extends SearchBackend { return true; } + + static function __init() { + self::createSearchTable(); + } + } + +Signal::connect('system.install', + array('MysqlSearchBackend', '__init')); + MysqlSearchBackend::register(); diff --git a/include/class.ticket.php b/include/class.ticket.php index 5fff02517bc54e36c27ec9d9b1b9fcf16ab7fda1..f451de786c52c8b59ac520e8e4df7996d4fbb696 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -828,7 +828,7 @@ class Ticket { } //Status helper. - function setStatus($status) { + function setStatus($status, $comments='') { global $thisstaff; if ($status && is_numeric($status)) @@ -837,6 +837,10 @@ class Ticket { if (!$status || !$status instanceof TicketStatus) return false; + // XXX: intercept deleted status and do hard delete + if (!strcasecmp($status->getState(), 'deleted')) + return $this->delete($comments); + if ($this->getStatusId() == $status->getId()) return true; @@ -845,6 +849,7 @@ class Ticket { $ecb = null; switch($status->getState()) { + case 'resolved': case 'closed': $sql.=', closed=NOW(), duedate=NULL '; if ($thisstaff) @@ -857,6 +862,7 @@ class Ticket { }; break; case 'open': + // TODO: check current status if it allows for reopening if ($this->isClosed()) { $sql.= ',closed=NULL, reopened=NOW() '; @@ -865,6 +871,9 @@ class Ticket { }; } break; + default: + return false; + } $sql.=' WHERE ticket_id='.db_input($this->getId()); @@ -873,12 +882,19 @@ class Ticket { return false; // Log status change b4 reload - $note= sprintf('Status changed from %s to %s by %s', + $note = sprintf(__('Status changed from %s to %s by %s'), $this->getStatus(), $status, $thisstaff ?: 'SYSTEM'); - $this->logNote('Status Changed', $note, $thisstaff, false); + $alert = false; + if ($comments) { + $note .= sprintf('<hr>%s', $comments); + // Send out alerts if comments are included + $alert = true; + } + + $this->logNote(__('Status Changed'), $note, $thisstaff, $alert); // Log events via callback if ($ecb) $ecb($this); @@ -2013,7 +2029,8 @@ class Ticket { exit; } - function delete() { + function delete($comments='') { + global $ost, $thisstaff; //delete just orphaned ticket thread & associated attachments. // Fetch thread prior to removing ticket entry @@ -2030,6 +2047,18 @@ class Ticket { $this->deleteDrafts(); + // Log delete + $log = sprintf(__('Ticket #%1$s deleted by %2$s'), + $this->getNumber(), + $thisstaff ? $thisstaff->getName() : __('SYSTEM')); + + if ($comments) + $log .= sprintf('<hr>%s', $comments); + + $ost->logDebug( + sprintf( __('Ticket #%s deleted'), $this->getNumber()), + $log); + return true; } @@ -2827,13 +2856,15 @@ class Ticket { function checkOverdue() { $sql='SELECT ticket_id FROM '.TICKET_TABLE.' T1 ' + .' INNER JOIN '.TICKET_STATUS_TABLE.' status + ON (status.id=T1.status_id AND status.state="open") ' .' LEFT JOIN '.SLA_TABLE.' T2 ON (T1.sla_id=T2.id AND T2.isactive=1) ' - .' WHERE status=\'open\' AND isoverdue=0 ' + .' WHERE isoverdue=0 ' .' AND ((reopened is NULL AND duedate is NULL AND TIME_TO_SEC(TIMEDIFF(NOW(),T1.created))>=T2.grace_period*3600) ' .' OR (reopened is NOT NULL AND duedate is NULL AND TIME_TO_SEC(TIMEDIFF(NOW(),reopened))>=T2.grace_period*3600) ' .' OR (duedate is NOT NULL AND duedate<NOW()) ' .' ) ORDER BY T1.created LIMIT 50'; //Age upto 50 tickets at a time? - //echo $sql; + if(($res=db_query($sql)) && db_num_rows($res)) { while(list($id)=db_fetch_row($res)) { if(($ticket=Ticket::lookup($id)) && $ticket->markOverdue()) diff --git a/include/class.user.php b/include/class.user.php index 127a078d5067d1acf43ff136ec6d0f0d27e804b1..8130551a96b1fb7ceda79bc9c2ebf1607b7caf6c 100644 --- a/include/class.user.php +++ b/include/class.user.php @@ -39,6 +39,9 @@ class TicketModel extends VerySimpleModel { 'joins' => array( 'user' => array( 'constraint' => array('user_id' => 'UserModel.id') + ), + 'status' => array( + 'constraint' => array('status_id' => 'TicketStatus.id') ) ) ); diff --git a/include/i18n/en_US/list.yaml b/include/i18n/en_US/list.yaml index ffe4a09cb759c98c308ad12579caf1515f601fad..613ca4a2f83db4a46048c70069a9569c95791a86 100644 --- a/include/i18n/en_US/list.yaml +++ b/include/i18n/en_US/list.yaml @@ -42,15 +42,6 @@ edit_mask: 63 configuration: prompt: State of a ticket - - type: flags # notrans - name: flags # notrans - label: Flags - required: false - sort: 2 - edit_mask: 63 - configuration: - prompt: Ticket Flags - multiselect: true - type: memo # notrans name: description # notrans label: Description @@ -64,68 +55,3 @@ length: 100 configuration: handler: TicketStatusList - -# Ticket flags -- type: ticket-flag # notrans - name: Ticket Flag - name_plural: Ticket Flags - sort_mode: Alpha # notrans - masks: 15 - notes: | - Flags that can be set on tickets - properties: - title: Flag Properties - instructions: Properties that can be set on a flag. - deletable: false - fields: - - type: state # notrans - name: states # notrans - label: Allowed States - required: true - sort: 1 - edit_mask: 63 - configuration: - prompt: Allowed Ticket States - multiselect: true - - type: memo # notrans - name: description # notrans - label: Description - required: false - sort: 2 - edit_mask: 15 - configuration: - rows: 2 - cols: 40 - html: false - length: 100 - items: # Note that id doubles as a bit mask for the flag (must be preset) - - id: 1 # notrans - value: Answered - extra: answered - status: 3 - sort: 1 - configuration: - states: - open: Open - description:| - Marked as Answered - - id: 2 # notrans - value: Onhold - extra: onhold - status: 3 - sort: 2 - configuration: - states: - open: Open - description:| - Put on hold - - id: 4 # notrans - value: Overdue - extra: overdue - status: 3 - sort: 3 - configuration: - states: - open: Open - description:| - Marked overdue diff --git a/include/staff/dynamic-list.inc.php b/include/staff/dynamic-list.inc.php index e29c1afd29efd7929e6d14da6202a0aaa6c8d758..280d85eccf1a08609215299b7b1abdeb8b87224c 100644 --- a/include/staff/dynamic-list.inc.php +++ b/include/staff/dynamic-list.inc.php @@ -17,7 +17,7 @@ if ($list) { $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info); ?> -<form action="?" method="post" id="save"> +<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']); ?>"> @@ -150,16 +150,11 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info) <?php } ?> </select> <?php if ($f->isConfigurable()) { ?> - <a class="action-button" style="float:none;overflow:inherit" - href="#ajax.php/form/field-config/<?php - echo $f->get('id'); ?>" - onclick="javascript: - $('#overlay').show(); - $('#field-config .body').empty().load($(this).attr('href').substr(1)); - $('#field-config').show(); - return false; - "><i class="icon-edit"></i> <?php echo __('Config'); ?></a> - <?php } ?></td> + <a class="action-button field-config" + style="float:none;overflow:inherit" + href="#form/field-config/<?php + echo $f->get('id'); ?>"><i + class="icon-cog"></i> <?php echo __('Config'); ?></a> <?php } ?></td> <td> <input type="text" size="20" name="name-<?php echo $id; ?>" value="<?php echo Format::htmlchars($f->get('name')); @@ -258,15 +253,18 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info) <td><input type="text" size="40" name="value-<?php echo $id; ?>" value="<?php echo $i->getValue(); ?>"/> <?php if ($list->hasProperties()) { ?> - <a class="action-button" style="float:none;overflow:inherit" - href="#ajax.php/list/<?php - echo $list->getId(); ?>/item/<?php echo $id ?>/properties" - onclick="javascript: - $('#overlay').show(); - $('#field-config .body').empty().load($(this).attr('href').substr(1)); - $('#field-config').show(); - return false; - "><i class="icon-edit"></i> <?php echo __('Properties'); ?></a> + <a class="action-button field-config" + style="float:none;overflow:inherit" + href="#list/<?php + echo $list->getId(); ?>/item/<?php + echo $id ?>/properties" + id="item-<?php echo $id; ?>" + ><?php + echo sprintf('<i class="icon-edit" %s></i> ', + $i->getConfiguration() + ? '': 'style="color:red; font-weight:bold;"'); + echo __('Properties'); + ?></a> <?php } @@ -335,24 +333,16 @@ $info=Format::htmlchars(($errors && $_POST) ? array_merge($info,$_POST) : $info) </p> </form> -<div style="display:none;" class="dialog draggable" id="field-config"> - <div id="popup-loading"> - <h1><i class="icon-spinner icon-spin icon-large"></i> - <?php echo __('Loading ...');?></h1> - </div> - <div class="body"></div> -</div> - <script type="text/javascript"> $(function() { - var $this = $('#popup-loading').hide(); - $(document).ajaxStart( function(event) { - console.log(1,event); - var $h1 = $this.find('h1'); - $this.show(); - $h1.css({'margin-top':$this.height()/3-$h1.height()/3}); // show Loading Div - }).ajaxStop ( function(){ - $this.hide(); // hide loading div + $('a.field-config').click( function(e) { + e.preventDefault(); + var $id = $(this).attr('id'); + var url = 'ajax.php/'+$(this).attr('href').substr(1); + $.dialog(url, [200], function (xhr) { + $('a#'+$id+' i').removeAttr('style'); + }); + return false; }); }); </script> diff --git a/include/staff/filter.inc.php b/include/staff/filter.inc.php index e9a51705a4eb4352b73e0bd92fe91c38781ba527..3bda64e043f56e6106e758ebdcc36988a217e4ce 100644 --- a/include/staff/filter.inc.php +++ b/include/staff/filter.inc.php @@ -258,7 +258,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <select name="status_id"> <option value="">— <?php echo __('Default'); ?> —</option> <?php - foreach (TicketStatusList::getAll() as $status) { + foreach (TicketStatusList::getStatuses() as $status) { $name = $status->getName(); if (!($isenabled = $status->isEnabled())) $name.=' '.__('(disabled)'); diff --git a/include/staff/footer.inc.php b/include/staff/footer.inc.php index 68779e589ce2a3c645d71adeb32c24197da7e86f..8a6fc6852f1444cdc4e3a772c032917ab827c2d7 100644 --- a/include/staff/footer.inc.php +++ b/include/staff/footer.inc.php @@ -19,9 +19,27 @@ if(is_object($thisstaff) && $thisstaff->isStaff()) { ?> <i class="icon-spinner icon-spin icon-3x pull-left icon-light"></i> <h1><?php echo __('Loading ...');?></h1> </div> -<div class="dialog" style="display:none;width:650px;" id="popup"> +<div class="dialog draggable" style="display:none;width:650px;" id="popup"> + <div id="popup-loading"> + <h1 style="margin-bottom: 20px;"><i class="icon-spinner icon-spin icon-large"></i> + <?php echo __('Loading ...');?></h1> + </div> <div class="body"></div> </div> +<div style="display:none;" class="dialog" id="alert"> + <h3><i class="icon-warning-sign"></i> <span id="title"></span></h3> + <a class="close" href=""><i class="icon-remove-circle"></i></a> + <hr/> + <div id="body" style="min-height: 20px;"></div> + <hr style="margin-top:3em"/> + <p class="full-width"> + <span class="buttons pull-right"> + <input type="button" value="<?php echo __('OK');?>" class="close"> + </span> + </p> + <div class="clear"></div> +</div> + <script type="text/javascript"> if ($.support.pjax) { $(document).on('click', 'a', function(event) { diff --git a/include/staff/helptopic.inc.php b/include/staff/helptopic.inc.php index 0742b32ae7f82b234a66f57e60cb7db50d8031ef..c4190b76c04784287027301aeb6cc094691f17d5 100644 --- a/include/staff/helptopic.inc.php +++ b/include/staff/helptopic.inc.php @@ -138,7 +138,7 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"'; <select name="status_id"> <option value="">— <?php echo __('System Default'); ?> —</option> <?php - foreach (TicketStatusList::getAll() as $status) { + foreach (TicketStatusList::getStatuses() as $status) { $name = $status->getName(); if (!($isenabled = $status->isEnabled())) $name.=' '.__('(disabled)'); diff --git a/include/staff/settings-tickets.inc.php b/include/staff/settings-tickets.inc.php index e2a46f46717bd6be28553143edd07e987270ef80..b8526ed0c5a03c2cab1b2ed0c82f39af25601c15 100644 --- a/include/staff/settings-tickets.inc.php +++ b/include/staff/settings-tickets.inc.php @@ -61,7 +61,8 @@ if(!($maxfileuploads=ini_get('max_file_uploads'))) <span> <select name="default_ticket_status_id"> <?php - foreach (TicketStatusList::getAll(array('open')) as $status) { + $criteria = array('states' => array('open')); + foreach (TicketStatusList::getStatuses($criteria) as $status) { $name = $status->getName(); if (!($isenabled = $status->isEnabled())) $name.=' '.__('(disabled)'); diff --git a/include/staff/templates/dynamic-field-config.tmpl.php b/include/staff/templates/dynamic-field-config.tmpl.php index 9c1261e9ace8a02000fb188efb51f62900fc951d..fe4116707be969c5c5d4ab2e5ad3510646ce3f91 100644 --- a/include/staff/templates/dynamic-field-config.tmpl.php +++ b/include/staff/templates/dynamic-field-config.tmpl.php @@ -1,19 +1,8 @@ <h3><?php echo __('Field Configuration'); ?> — <?php echo $field->get('label') ?></h3> <a class="close" href=""><i class="icon-remove-circle"></i></a> <hr/> - <form method="post" action="ajax.php/form/field-config/<?php - echo $field->get('id'); ?>" onsubmit="javascript: - var form = $(this); - $.post(this.action, form.serialize(), function(data, status, xhr) { - if (!data.length) { - form.closest('.dialog').hide(); - $('#overlay').hide(); - } else { - form.closest('.dialog').empty().append(data); - } - }); - return false; - "> + <form method="post" action="#form/field-config/<?php + echo $field->get('id'); ?>"> <?php echo csrf_token(); $config = $field->getConfiguration(); diff --git a/include/staff/templates/list-item-properties.tmpl.php b/include/staff/templates/list-item-properties.tmpl.php index e105bea4fef479b32992bf4fbd28331924191adc..44ce0de055728a163cd805e59ec99a5a8e549fd9 100644 --- a/include/staff/templates/list-item-properties.tmpl.php +++ b/include/staff/templates/list-item-properties.tmpl.php @@ -1,19 +1,9 @@ <h3><?php echo __('Item Properties'); ?> — <?php echo $item->getValue() ?></h3> <a class="close" href=""><i class="icon-remove-circle"></i></a> <hr/> - <form method="post" action="ajax.php/list/<?php + <form method="post" action="#list/<?php echo $list->getId(); ?>/item/<?php - echo $item->getId(); ?>/properties" onsubmit="javascript: - var form = $(this); - $.post(this.action, form.serialize(), function(data, status, xhr) { - if (!data.length) { - form.closest('.dialog').hide(); - $('#overlay').hide(); - } else { - form.closest('.dialog .body').empty().append(data); - } - }); - return false;"> + echo $item->getId(); ?>/properties"> <?php echo csrf_token(); $config = $item->getConfiguration(); diff --git a/include/staff/templates/status-options.tmpl.php b/include/staff/templates/status-options.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..5fc39f32ad99c9f6c52b129291545a8f8d7f336d --- /dev/null +++ b/include/staff/templates/status-options.tmpl.php @@ -0,0 +1,61 @@ +<?php +$actions= array( + 'close' => array( + 'action' => __('Close'), + 'icon' => 'icon-repeat', + 'states' => array('closed') + ), + 'resolve' => array( + 'action' => __('Resolve'), + 'icon' => 'icon-ok-circle', + 'states' => array('resolved') + ), + 'reopen' => array( + 'action' => __('Reopen'), + 'icon' => 'icon-undo', + 'states' => array('open') + ), + ); + +foreach($actions as $k => $v) { + $criteria = array('states' => $v['states']); + if (!($statuses = TicketStatusList::getStatuses($criteria)->all())) + continue; + + if ($statuses && count($statuses) > 1) { + ?> + <span + class="action-button" + data-dropdown="#action-dropdown-<?php echo $k; ?>"> + <a id="tickets-<?php echo $k; ?>" + class="tickets-action" + href="#tickets/status/<?php echo $k; ?>"><i + class="<?php echo $v['icon']; ?>"></i> <?php echo $v['action']; ?></a> + <i class="icon-caret-down"></i> + </span> + <div id="action-dropdown-<?php echo $k; ?>" + class="action-dropdown anchor-right"> + <ul> + <?php + foreach ($statuses as $s) { + ?> + + <li> + <a class="no-pjax tickets-action" + href="#tickets/status/<?php echo $k; ?>/<?php + echo $s->getId(); ?>"> <i + class="icon-tag"></i> <?php echo __($s->getName()); ?></a> </li> + <?php + } ?> + </ul> + </div> + <?php + } else { + ?> + <a id="tickets-<?php echo $k; ?>" class="action-button tickets-action" + href="#tickets/status/<?php echo $k; ?>"><i + class="<?php echo $v['icon']; ?>"></i> <?php echo $v['action']; ?></a> +<?php + } +} +?> diff --git a/include/staff/templates/ticket-status.tmpl.php b/include/staff/templates/ticket-status.tmpl.php new file mode 100644 index 0000000000000000000000000000000000000000..8646a05cf439b1f550b6b992fdeef8d72d4df1ee --- /dev/null +++ b/include/staff/templates/ticket-status.tmpl.php @@ -0,0 +1,118 @@ +<?php +global $cfg; + +if (!$info['title']) + $info['title'] = 'Change Tickets Status'; + +?> +<h3><?php echo $info['title']; ?></h3> +<b><a class="close" href="#"><i class="icon-remove-circle"></i></a></b> +<div class="clear"></div> +<hr/> +<?php +if ($info['error']) { + echo sprintf('<p id="msg_error">%s</p>', $info['error']); +} elseif ($info['warn']) { + echo sprintf('<p id="msg_warning">%s</p>', $info['warn']); +} elseif ($info['msg']) { + echo sprintf('<p id="msg_notice">%s</p>', $info['msg']); +} elseif ($info['notice']) { + echo sprintf('<p id="msg_info"><i class="icon-info-sign"></i> %s</p>', + $info['notice']); +} + + +$action = $info['action'] ?: ('#tickets/status/'. $state); +?> +<div id="ticket-status" style="display:block; margin:5px;"> + <form method="post" name="status" id="status" + action="<?php echo $action; ?>"> + <table width="100%"> + <?php + if ($info['extra']) { + ?> + <tbody> + <tr><td colspan="2"><strong><?php echo $info['extra']; + ?></strong></td> </tr> + </tbody> + <?php + } + + $verb = ''; + if ($state) { + $statuses = TicketStatusList::getStatuses(array('states'=>array($state)))->all(); + $verb = TicketStateField::getVerb($state); + } + + if ($statuses) { + ?> + <tbody> + <tr> + <td colspan=2> + <span> + <?php + if (count($statuses) > 1) { ?> + <strong><?php echo __('Status') ?>: </strong> + <select name="status_id"> + <?php + foreach ($statuses as $s) { + echo sprintf('<option value="%d" %s>%s</option>', + $s->getId(), + ($info['status_id'] == $s->getId()) + ? 'selected="selected"' : '', + $s->getName() + ); + } + ?> + </select> + <font class="error">* <?php echo $errors['status_id']; ?></font> + <?php + } elseif ($statuses[0]) { + echo "<input type='hidden' name='status_id' value={$statuses[0]->getId()} />"; + } ?> + </span> + </td> + </tr> + </tbody> + <?php + } ?> + <tbody> + <tr> + <td colspan="2"> + <?php + $placeholder = __('Optional reason for status change (internal note)'); + ?> + <textarea name="comments" id="comments" + cols="50" rows="3" wrap="soft" style="width:100%" + class="richtext ifhtml no-bar" + placeholder="<?php echo $placeholder; ?>"><?php + echo $info['notes']; ?></textarea> + </td> + </tr> + </tbody> + </table> + <hr> + <p class="full-width"> + <span class="buttons" style="float:left"> + <input type="reset" value="<?php echo __('Reset'); ?>"> + <input type="button" name="cancel" class="close" + value="<?php echo __('Cancel'); ?>"> + </span> + <span class="buttons" style="float:right"> + <input type="submit" value="<?php + echo $verb ?: __('Submit'); ?>"> + </span> + </p> + </form> +</div> +<div class="clear"></div> +<script type="text/javascript"> +$(function() { + // Copy checked tickets to status form. + $('form#tickets input[name="tids[]"]:checkbox:checked') + .clone() + .prop('type', 'hidden') + .removeAttr('class') + .appendTo('form#status'); + }); +</script> diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php index f9ab0d8f23482bb4ba072ddb60187cd4962844ad..63da455a8bf70b9f52b914e3bda8e9b80517c6e5 100644 --- a/include/staff/ticket-open.inc.php +++ b/include/staff/ticket-open.inc.php @@ -330,17 +330,29 @@ if ($_POST) <?php } ?> - <?php - if($thisstaff->canCloseTickets()) { ?> - <tr> - <td width="100"><?php echo __('Ticket Status');?>:</td> - <td> - <input type="checkbox" name="ticket_state" value="closed" <?php echo $info['ticket_state']?'checked="checked"':''; ?>> - <b><?php echo __('Close On Response');?></b> <em>(<?php echo __('Only applicable if response is entered');?>)</em> - </td> - </tr> - <?php - } ?> + <tr> + <td width="100"><?php echo __('Ticket Status');?>:</td> + <td> + <select name="statusId"> + <?php + $statusId = $info['statusId'] ?: $cfg->getDefaultTicketStatusId(); + $states = array('open'); + if ($thisstaff->canCloseTickets()) + $states = array_merge($states, array('resolved', 'closed')); + foreach (TicketStatusList::getStatuses( + array('states' => $states)) as $s) { + if (!$s->isEnabled()) continue; + $selected = ($statusId == $s->getId()); + echo sprintf('<option value="%d" %s>%s</option>', + $s->getId(), + $selected + ? 'selected="selected"' : '', + __($s->getName())); + } + ?> + </select> + </td> + </tr> <tr> <td width="100"><?php echo __('Signature');?>:</td> <td> diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index c1e15703866132c3197a8413cd385fa3585b77d0..2b6bc0477cfb915c4b4426aa399d2174600a0bfb 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -56,17 +56,20 @@ if($ticket->isOverdue()) </span> <?php } - if ($thisstaff->canDeleteTickets()) { ?> - <a id="ticket-delete" class="action-button confirm-action" href="#delete"><i class="icon-trash"></i> <?php - echo __('Delete'); ?></a> - <?php - } if($thisstaff->canCloseTickets()) { if($ticket->isOpen()) {?> - <a id="ticket-close" class="action-button" href="#close"><i class="icon-remove-circle"></i> <?php echo __('Close');?></a> + <a class="action-button ticket-action" + href="#tickets/<?php echo $ticket->getId() + ?>/status/close"><i class="icon-repeat"></i> <?php echo __('Close');?></a> + <a class="action-button ticket-action" + href="#tickets/<?php echo $ticket->getId() + ?>/status/resolve"><i class="icon-ok-circle"></i> <?php echo __('Resolve');?></a> <?php } else { ?> - <a id="ticket-reopen" class="action-button" href="#reopen"><i class="icon-undo"></i> <?php echo __('Reopen');?></a> + <a class="action-button ticket-action" + href="#tickets/<?php echo $ticket->getId() + ?>/status/reopen"><i class="icon-undo"></i> <?php echo + __('Reopen');?></a> <?php } } @@ -100,7 +103,15 @@ if($ticket->isOverdue()) if($thisstaff->canEditTickets()) { ?> <li><a class="change-user" href="#tickets/<?php echo $ticket->getId(); ?>/change-user"><i class="icon-user"></i> <?php - echo __('Change Ticket Owner'); ?></a></li> + echo __('Change Owner'); ?></a></li> + <?php + } + if($thisstaff->canDeleteTickets()) { + ?> + <li><a class="ticket-action" href="#tickets/<?php + echo $ticket->getId(); ?>/status/delete" + data-href="tickets.php"><i class="icon-trash"></i> <?php + echo __('Delete Ticket'); ?></a></li> <?php } if($ticket->isOpen() && ($dept && $dept->isManager($thisstaff))) { @@ -618,18 +629,21 @@ $tcount+= $ticket->getNumNotes(); <select name="reply_status_id"> <?php $statusId = $info['reply_status_id'] ?: $ticket->getStatusId(); - $states = array('open', 'resolved'); + $states = array('open'); if ($thisstaff->canCloseTickets()) - $states = array_merge($states, - array('closed', 'archived')); + $states = array_merge($states, array('resolved', 'closed')); - foreach (TicketStatusList::getAll($states) as $s) { + foreach (TicketStatusList::getStatuses( + array('states' => $states)) as $s) { if (!$s->isEnabled()) continue; - echo sprintf('<option value="%d" %s>%s</option>', + $selected = ($statusId == $s->getId()); + echo sprintf('<option value="%d" %s>%s%s</option>', $s->getId(), - ($statusId == $s->getId()) + $selected ? 'selected="selected"' : '', - $s->getName() + __($s->getName()), + $selected + ? (' ('.__('current').')') : '' ); } ?> @@ -709,18 +723,18 @@ $tcount+= $ticket->getNumNotes(); <select name="note_status_id"> <?php $statusId = $info['note_status_id'] ?: $ticket->getStatusId(); - $states = array('open', 'resolved'); + $states = array('open'); if ($thisstaff->canCloseTickets()) - $states = array_merge($states, - array('closed', 'archived')); - foreach (TicketStatusList::getAll($states) as $s) { + $states = array_merge($states, array('resolved', 'closed')); + foreach (TicketStatusList::getStatuses( + array('states' => $states)) as $s) { if (!$s->isEnabled()) continue; - $selected = $statusId == $s->getID(); + $selected = $statusId == $s->getId(); echo sprintf('<option value="%d" %s>%s%s</option>', $s->getId(), $selected ? 'selected="selected"' : '', - $s->getName(), - $selected ? ' '.__('(current)') : '' + __($s->getName()), + $selected ? (' ('.__('current').')') : '' ); } ?> @@ -930,67 +944,6 @@ $tcount+= $ticket->getNumNotes(); </form> <div class="clear"></div> </div> -<div style="display:none;" class="dialog" id="ticket-status"> - <h3><?php echo sprintf($ticket->isClosed() ? __('Reopen Ticket #%s') : __('Close Ticket #%s'), - $ticket->getNumber()); ?></h3> - <a class="close" href=""><i class="icon-remove-circle"></i></a> - <hr/> - <?php - echo sprintf(__('Are you sure you want to <b>%s</b> this ticket?'), - $ticket->isClosed()?__('REOPEN'):__('CLOSE')); - $action = $ticket->isClosed() ? 'reopen': 'close'; - ?> - <br><br> - <form action="tickets.php?id=<?php echo $ticket->getId(); ?>" method="post" id="status-form" name="status-form"> - <?php csrf_token(); ?> - <input type="hidden" name="id" value="<?php echo $ticket->getId(); ?>"> - <input type="hidden" name="a" value="process"> - <input type="hidden" name="do" value="<?php echo $action; ?>"> - <fieldset> - <div><strong>Ticket Status </strong> - <select name="status_id"> - <?php - $statusId = $info['status_id'] ?: 0; - if (!$ticket->isOpen()) - $states = array('open'); - else - $states = array('resolved', 'closed'); - - foreach (TicketStatusList::getAll($states) as $s) { - if (!$s->isEnabled()) continue; - echo sprintf('<option value="%d" %s>%s</option>', - $s->getId(), - ($statusId == $s->getId()) - ? 'selected="selected"' : '', - $s->getName() - ); - } - ?> - </select> - <span class='error'>* <?php echo $errors['status_id']; ?></span> - </div> - </fieldset> - <fieldset> - <div style="margin-bottom:0.5em"> - <em><?php echo __('Reasons for status change (internal note). Optional but highly recommended.');?></em><br> - </div> - <textarea name="ticket_status_notes" id="ticket_status_notes" cols="50" rows="5" wrap="soft" - style="width:100%" - class="richtext ifhtml no-bar"><?php echo $info['ticket_status_notes']; ?></textarea> - </fieldset> - <hr style="margin-top:1em"/> - <p class="full-width"> - <span class="buttons pull-left"> - <input type="reset" value="<?php echo __('Reset');?>"> - <input type="button" value="<?php echo __('Cancel');?>" class="close"> - </span> - <span class="buttons pull-right"> - <input type="submit" value="<?php echo $ticket->isClosed()?__('Reopen'):__('Close'); ?>"> - </span> - </p> - </form> - <div class="clear"></div> -</div> <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/tickets.inc.php b/include/staff/tickets.inc.php index 094d28ded43cf56a50280d5e0413f682f8f9613f..5bd1df943ef8b4c71f5e5e3d7e13f7654ada63f3 100644 --- a/include/staff/tickets.inc.php +++ b/include/staff/tickets.inc.php @@ -273,7 +273,7 @@ $query="$qselect $qfrom $qwhere ORDER BY $order_by $order LIMIT ".$pageNav->getS $hash = md5($query); $_SESSION['search_'.$hash] = $query; $res = db_query($query); -$showing=db_num_rows($res)?$pageNav->showing():""; +$showing=db_num_rows($res)? ' — '.$pageNav->showing():""; if(!$results_type) $results_type = sprintf(__('%s Tickets' /* %s will be a status such as 'open' */), mb_convert_case($status, MB_CASE_TITLE)); @@ -327,15 +327,31 @@ if ($results) { </div> <!-- SEARCH FORM END --> <div class="clear"></div> -<div style="margin-bottom:20px"> -<form action="tickets.php" method="POST" name='tickets'> +<div style="margin-bottom:20px; padding-top:10px;"> +<div> + <div class="pull-left flush-left"> + <h2><a href="<?php echo Format::htmlchars($_SERVER['REQUEST_URI']); ?>" + title="<?php echo __('Refresh'); ?>"><i class="icon-refresh"></i> <?php echo + $results_type.$showing; ?></a></h2> + </div> + <div class="pull-right flush-right"> + <a id="tickets-delete" class="action-button tickets-action" + href="#tickets/status/delete"><i + class="icon-trash"></i> <?php echo __('Delete'); ?></a> + <?php + if ($res && $results && $thisstaff->canManageTickets()) { + echo TicketStatus::options(); + } + ?> + </div> +</div> +<div class="clear" style="margin-bottom:10px;"></div> +<form action="tickets.php" method="POST" name='tickets' id="tickets"> <?php csrf_token(); ?> - <a class="refresh pull-right" href="<?php echo Format::htmlchars($_SERVER['REQUEST_URI']); ?>"><?php echo __('Refresh'); ?></a> <input type="hidden" name="a" value="mass_process" > <input type="hidden" name="do" id="action" value="" > <input type="hidden" name="status" value="<?php echo Format::htmlchars($_REQUEST['status']); ?>" > <table class="list" border="0" cellspacing="1" cellpadding="2" width="940"> - <caption><?php echo $showing; ?> <?php echo $results_type; ?></caption> <thead> <tr> <?php if($thisstaff->canManageTickets()) { ?> @@ -427,7 +443,8 @@ if ($results) { $sel=true; ?> <td align="center" class="nohover"> - <input class="ckb" type="checkbox" name="tids[]" value="<?php echo $row['ticket_id']; ?>" <?php echo $sel?'checked="checked"':''; ?>> + <input class="ckb" type="checkbox" name="tids[]" + value="<?php echo $row['ticket_id']; ?>" <?php echo $sel?'checked="checked"':''; ?>> </td> <?php } ?> <td title="<?php echo $row['email']; ?>" nowrap> @@ -490,42 +507,6 @@ if ($results) { echo '<div> '.__('Page').':'.$pageNav->getPageLinks().' '; echo '<a class="export-csv no-pjax" href="?a=export&h=' .$hash.'&status='.$_REQUEST['status'] .'">'.__('Export').'</a> <i class="help-tip icon-question-sign" href="#export"></i></div>'; - ?> - <?php - if($thisstaff->canManageTickets()) { ?> - <p class="centered" id="actions"> - <?php - $status=$_REQUEST['status']?$_REQUEST['status']:$status; - switch (strtolower($status)) { - case 'closed': ?> - <input class="button" type="submit" name="reopen" value="<?php echo __('Reopen');?>" > - <?php - break; - case 'open': - case 'answered': - case 'assigned': - ?> - <input class="button" type="submit" name="mark_overdue" value="<?php echo __('Overdue');?>" > - <input class="button" type="submit" name="close" value="<?php echo __('Close');?>"> - <?php - break; - case 'overdue': - ?> - <input class="button" type="submit" name="close" value="<?php echo __('Close');?>"> - <?php - break; - default: //search?? - ?> - <input class="button" type="submit" name="close" value="<?php echo __('Close');?>" > - <input class="button" type="submit" name="reopen" value="<?php echo __('Reopen');?>"> - <?php - } - if($thisstaff->canDeleteTickets()) { ?> - <input class="button" type="submit" name="delete" value="<?php echo __('Delete');?>"> - <?php } ?> - </p> - <?php - } } ?> </form> </div> @@ -534,20 +515,9 @@ if ($results) { <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="close-confirm"> - <?php echo __('Are you sure want to <b>close</b> selected open tickets?');?> - </p> - <p class="confirm-action" style="display:none;" id="reopen-confirm"> - <?php echo __('Are you sure want to <b>reopen</b> selected closed tickets?');?> - </p> <p class="confirm-action" style="display:none;" id="mark_overdue-confirm"> <?php echo __('Are you sure want to flag the selected tickets as <font color="red"><b>overdue</b></font>?');?> </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 ticket', 'selected tickets', 2));?></strong></font> - <br><br><?php echo __('Deleted tickets CANNOT be recovered, including any associated attachments.');?> - </p> <div><?php echo __('Please confirm to continue.');?></div> <hr style="margin-top:1em"/> <p class="full-width"> @@ -683,3 +653,22 @@ if ($results) { </p> </form> </div> +<script type="text/javascript"> +$(function() { + $(document).off('.tickets'); + $(document).on('click.tickets', 'a.tickets-action', function(e) { + e.preventDefault(); + var count = checkbox_checker($('form#tickets'), 1); + if (count) { + var url = 'ajax.php/' + +$(this).attr('href').substr(1) + +'?count='+count + +'&_uid='+new Date().getTime(); + $.dialog(url, [201], function (xhr) { + window.location.href = window.location.href; + }); + } + return false; + }); +}); +</script> diff --git a/include/upgrader/streams/core.sig b/include/upgrader/streams/core.sig index 4d847feb47a8f3ef48ed15a006600ab660f6190d..30a5aca33dc5932c1e09ede64d418368d54367cb 100644 --- a/include/upgrader/streams/core.sig +++ b/include/upgrader/streams/core.sig @@ -1 +1 @@ -b38066879371ed792958b4ba9bc8de4a +03ff59bf35a58a102e9b32ad33c2839f diff --git a/include/upgrader/streams/core/8f99b8bf-03ff59bf.cleanup.sql b/include/upgrader/streams/core/8f99b8bf-03ff59bf.cleanup.sql new file mode 100644 index 0000000000000000000000000000000000000000..bccb432fbf33b17fc27d54fc90490fcfb01e7315 --- /dev/null +++ b/include/upgrader/streams/core/8f99b8bf-03ff59bf.cleanup.sql @@ -0,0 +1,7 @@ +DELETE FROM `%TABLE_PREFIX%config` + WHERE `namespace`='core' AND `key` = 'random_ticket_ids'; + +ALTER TABLE `%TABLE_PREFIX%ticket` + DROP COLUMN `status`; + +OPTIMIZE TABLE `%TABLE_PREFIX%ticket`; diff --git a/include/upgrader/streams/core/8f99b8bf-b3806687.patch.sql b/include/upgrader/streams/core/8f99b8bf-03ff59bf.patch.sql similarity index 75% rename from include/upgrader/streams/core/8f99b8bf-b3806687.patch.sql rename to include/upgrader/streams/core/8f99b8bf-03ff59bf.patch.sql index a4cc062be8e8ecd9b19fb3e28ab634b49e03411d..65a50036f8155af515f07dbbe3ba560d96065006 100644 --- a/include/upgrader/streams/core/8f99b8bf-b3806687.patch.sql +++ b/include/upgrader/streams/core/8f99b8bf-03ff59bf.patch.sql @@ -1,6 +1,6 @@ /** * @version v1.9.4 - * @signature b38066879371ed792958b4ba9bc8de4a + * @signature 03ff59bf35a58a102e9b32ad33c2839f * @title Custom Ticket Numbers and Statuses * * This patch adds support for ticket number sequences to the database @@ -26,21 +26,18 @@ CREATE TABLE `%TABLE_PREFIX%sequence` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8; SET @random = (SELECT `value` FROM `%TABLE_PREFIX%config` - WHERE `namespace` = 'core' AND `key` = 'use_random_ids'); + WHERE `namespace` = 'core' AND `key` = 'random_ticket_ids'); -- Sequence (id=1) will be loaded in the task file INSERT INTO `%TABLE_PREFIX%config` (`namespace`, `key`, `value`) VALUES - ('core', 'number_format', CASE WHEN @random THEN '######' ELSE '#' END), - ('core', 'sequence_id', CASE WHEN @random THEN 0 ELSE 1 END); - -DELETE FROM `%TABLE_PREFIX%config` -WHERE `namespace`='core' AND `key` = 'use_random_ids'; + ('core', 'number_format', IF(@random, '######', '#')), + ('core', 'sequence_id', IF(@random, 0, 1)); ALTER TABLE `%TABLE_PREFIX%help_topic` - ADD `flags` int(10) unsigned DEFAULT '0' AFTER `noautoresp`, - ADD `sequence_id` int(10) unsigned NOT NULL DEFAULT '0' AFTER `form_id`, - ADD `number_format` varchar(32) DEFAULT NULL AFTER `topic`; + ADD `flags` int(10) unsigned DEFAULT '0' AFTER `noautoresp`, + ADD `sequence_id` int(10) unsigned NOT NULL DEFAULT '0' AFTER `form_id`, + ADD `number_format` varchar(32) DEFAULT NULL AFTER `topic`; ALTER TABLE `%TABLE_PREFIX%list` ADD `masks` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `sort_mode`, @@ -50,9 +47,9 @@ ALTER TABLE `%TABLE_PREFIX%list` CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%ticket_status` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(60) NOT NULL DEFAULT '', - `state` varchar(16) NOT NULL DEFAULT 'open', + `state` varchar(16) DEFAULT NULL, `mode` int(11) unsigned NOT NULL DEFAULT '0', - `flags` int(10) unsigned NOT NULL DEFAULT '0', + `flags` int(11) unsigned NOT NULL DEFAULT '0', `sort` int(11) unsigned NOT NULL DEFAULT '0', `notes` text NOT NULL, `created` datetime NOT NULL, @@ -72,7 +69,13 @@ ALTER TABLE `%TABLE_PREFIX%ticket` ADD `status_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `user_email_id`, ADD INDEX (`status_id`); +UPDATE `%TABLE_PREFIX%ticket` SET `status_id` = 3 + WHERE `status` = 'closed'; + +UPDATE `%TABLE_PREFIX%ticket` SET `status_id` = 1 + WHERE `status` = 'open'; + -- Finished with patch UPDATE `%TABLE_PREFIX%config` - SET `value` = 'b38066879371ed792958b4ba9bc8de4a' + SET `value` = '03ff59bf35a58a102e9b32ad33c2839f' WHERE `key` = 'schema_signature' AND `namespace` = 'core'; diff --git a/include/upgrader/streams/core/8f99b8bf-b3806687.task.php b/include/upgrader/streams/core/8f99b8bf-03ff59bf.task.php similarity index 92% rename from include/upgrader/streams/core/8f99b8bf-b3806687.task.php rename to include/upgrader/streams/core/8f99b8bf-03ff59bf.task.php index 8c717837e24f0bebaf5ad5796483ba87473d15b7..f7bb36f7db2da78056a307b730543f6cc43ad5b0 100644 --- a/include/upgrader/streams/core/8f99b8bf-b3806687.task.php +++ b/include/upgrader/streams/core/8f99b8bf-03ff59bf.task.php @@ -30,6 +30,9 @@ class SequenceLoader extends MigrationTask { foreach ($statuses as $s) { TicketStatus::__create($s); } + + // Initialize MYSQL search backend + MysqlSearchBackend::__init(); } } diff --git a/main.inc.php b/main.inc.php index 4f4b01bfbe404fad9b44767eaa4b49acb9755360..026e440ca84cc904d8a5ebcf8e6b5101cd81d41e 100644 --- a/main.inc.php +++ b/main.inc.php @@ -43,4 +43,12 @@ $_POST=Format::strip_slashes($_POST); $_GET=Format::strip_slashes($_GET); $_REQUEST=Format::strip_slashes($_REQUEST); } + +// extract system messages +$errors = array(); +$msg=$warn=$sysnotice=''; +if ($_SESSION['::sysmsgs']) { + extract($_SESSION['::sysmsgs']); + unset($_SESSION['::sysmsgs']); +} ?> diff --git a/scp/ajax.php b/scp/ajax.php index 664e12566b00bbcb03ab2e33511b7396b18caa55..cdeec76d9bb1f7ef5b5e3ae3b86fb009b47e4fe9 100644 --- a/scp/ajax.php +++ b/scp/ajax.php @@ -135,11 +135,15 @@ $dispatcher = patterns('', url_get('^(?P<tid>\d+)/add-collaborator/(?P<uid>\d+)$', 'addCollaborator'), url_get('^(?P<tid>\d+)/add-collaborator/auth:(?P<bk>\w+):(?P<id>.+)$', 'addRemoteCollaborator'), url('^(?P<tid>\d+)/add-collaborator$', 'addCollaborator'), - url_get('^lookup', 'lookup'), - url_get('^search', 'search'), url_get('^(?P<tid>\d+)/forms/manage$', 'manageForms'), url_post('^(?P<tid>\d+)/forms/manage$', 'updateForms'), - url_get('^(?P<tid>\d+)/canned-resp/(?P<cid>\w+).(?P<format>json|txt)', 'cannedResponse') + url_get('^(?P<tid>\d+)/canned-resp/(?P<cid>\w+).(?P<format>json|txt)', 'cannedResponse'), + url_get('^(?P<tid>\d+)/status/(?P<status>\w+)$', 'changeTicketStatus'), + url_post('^(?P<tid>\d+)/status/(?P<status>\w+)$', 'setTicketStatus'), + url_get('^status/(?P<status>\w+)(?:/(?P<sid>\d+))?$', 'changeTicketsStatus'), + url_post('^status/(?P<state>\w+)$', 'setTicketsStatus'), + url_get('^lookup', 'lookup'), + url_get('^search', 'search') )), url('^/collaborators/', patterns('ajax.tickets.php:TicketsAjaxAPI', url_get('^(?P<cid>\d+)/view$', 'viewCollaborator'), diff --git a/scp/js/scp.js b/scp/js/scp.js index eb71febb5e78f2b9613629264d115266b39b7b70..953712f43b368377454b5ff028b553bb40330c96 100644 --- a/scp/js/scp.js +++ b/scp/js/scp.js @@ -16,18 +16,22 @@ function checkbox_checker(formObj, min, max) { msg=__("You're limited to only {0} selections.\n") .replace('{0}', max); msg=msg + __("You have made {0} selections.\n").replace('{0}', checked); msg=msg + __("Please remove {0} selection(s).").replace('{0}', checked-max); - alert(msg) + $.sysAlert(__('Alert'), msg); + return (false); } if (checked< min ){ - alert(__("Please make at least {0} selections. {1} checked so far.") - .replace('{0}', min) - .replace('{1}', checked)); + $.sysAlert( __('Alert'), + __("Please make at least {0} selections. {1} checked so far.") + .replace('{0}', min) + .replace('{1}', checked) + ); + return (false); } - return (true); + return checked; } @@ -526,12 +530,20 @@ $.dialog = function (url, codes, cb, options) { if (codes && !$.isArray(codes)) codes = [codes]; - $('.dialog#popup .body').load(url, function () { + var $popup = $('.dialog#popup'); + + $('#overlay').show(); + $('div.body', $popup).empty().hide(); + $('div#popup-loading', $popup).show(); + $popup.show(); + $('div.body', $popup).load(url, function () { + $('div#popup-loading', $popup).hide(); $('#overlay').show(); - $('.dialog#popup').show({ + $('div.body', $popup).show({ duration: 0, complete: function() { if (options.onshow) options.onshow(); } }); + $popup.show(); $(document).off('.dialog'); $(document).on('submit.dialog', '.dialog#popup form', function(e) { e.preventDefault(); @@ -563,6 +575,18 @@ $.dialog = function (url, codes, cb, options) { if (options.onload) { options.onload(); } }; +$.sysAlert = function (title, msg, cb) { + var $dialog = $('.dialog#alert'); + if ($dialog.length) { + $('#overlay').show(); + $('#title', $dialog).html(title); + $('#body', $dialog).html(msg); + $dialog.show(); + } else { + alert(msg); + } +}; + $.userLookup = function (url, cb) { $.dialog(url, 201, function (xhr) { var user = $.parseJSON(xhr.responseText); diff --git a/scp/js/ticket.js b/scp/js/ticket.js index ba70ec2df65f1a77b6f76cbf65163383c9979e19..ca5f3957e25da6f1218aa102e27725f6c51b17e3 100644 --- a/scp/js/ticket.js +++ b/scp/js/ticket.js @@ -381,7 +381,7 @@ var ticket_onload = function($) { autoLock.Init(); /*** Ticket Actions **/ - //print options + //print options TODO: move to backend $('a#ticket-print').click(function(e) { e.preventDefault(); $('#overlay').show(); @@ -389,11 +389,18 @@ var ticket_onload = function($) { return false; }); - //ticket status (close & reopen) xxx: move to backend ticket-action - $('a#ticket-close, a#ticket-reopen').click(function(e) { + + $(document).off('.ticket-action'); + $(document).on('click.ticket-action', 'a.ticket-action', function(e) { e.preventDefault(); - $('#overlay').show(); - $('.dialog#ticket-status').show(); + var url = 'ajax.php/' + +$(this).attr('href').substr(1) + +'?_uid='+new Date().getTime(); + var $redirect = $(this).data('href'); + $.dialog(url, [201], function (xhr) { + window.location.href = $redirect ? $redirect : window.location.href; + }); + return false; }); diff --git a/scp/lists.php b/scp/lists.php index 02b41d117988466a5a58f92ff31be4d409541e08..b3c424de7e15af1ecd0bbfe7428b57c013776330 100644 --- a/scp/lists.php +++ b/scp/lists.php @@ -95,9 +95,11 @@ if($_POST) { if ($errors) $errors['err'] = $errors['err'] ?: sprintf(__('Unable to update %s. Correct error(s) below and try again!'), __('custom list items')); - else + else { + $list->_items = null; $msg = sprintf(__('Successfully updated %s'), __('this custom list')); + } } elseif ($errors) $errors['err'] = $errors['err'] ?: sprintf(__('Unable to update %s. Correct error(s) below and try again!'), diff --git a/scp/staff.inc.php b/scp/staff.inc.php index 9330717e3dc86bc495d8b22e0b0c2066001247a9..d4399c74b8fa94f9e049c84bf4be855c1d44df66 100644 --- a/scp/staff.inc.php +++ b/scp/staff.inc.php @@ -110,9 +110,6 @@ $_SESSION['TZ_DST']=$thisstaff->observeDaylight(); define('PAGE_LIMIT', $thisstaff->getPageLimit()?$thisstaff->getPageLimit():DEFAULT_PAGE_LIMIT); -//Clear some vars. we use in all pages. -$errors=array(); -$msg=$warn=$sysnotice=''; $tabs=array(); $submenu=array(); $exempt = in_array(basename($_SERVER['SCRIPT_NAME']), array('logout.php', 'ajax.php', 'logs.php', 'upgrade.php')); diff --git a/scp/tickets.php b/scp/tickets.php index f3502c96a2666cfcbdd8868d0a276ad03055c4cd..17235eb62f1368744bc5a5164bb42cde496e600f 100644 --- a/scp/tickets.php +++ b/scp/tickets.php @@ -201,52 +201,6 @@ if($_POST && !$errors): break; case 'process': switch(strtolower($_POST['do'])): - case 'close': - if(!$thisstaff->canCloseTickets()) { - $errors['err'] = __('Permission Denied. You are not allowed to close tickets.'); - } elseif($ticket->isClosed()) { - $errors['err'] = __('Ticket is already closed!'); - } elseif (!($status=TicketStatus::lookup($_POST['status_id']))) { - $errors['err'] = __('Unknown status'); - } elseif (!in_array($status->getState(), array('resolved', 'closed'))) { - $errors['err'] = __('Invalid status'); - } elseif ($ticket->setStatus($status)) { - $msg=sprintf(__('Ticket #%s status set to CLOSED'),$ticket->getNumber()); - if($_POST['ticket_status_notes']) { - $ticket->logNote( - __('Ticket Closed'), - $_POST['ticket_status_notes'], - $thisstaff); - } - //Going back to main listing. - TicketLock::removeStaffLocks($thisstaff->getId(), $ticket->getId()); - $page=$ticket=null; - } else { - $errors['err']=__('Problems closing the ticket. Try again'); - } - break; - case 'reopen': - //if staff can close or create tickets ...then assume they can reopen. - if (!$thisstaff->canCloseTickets() && !$thisstaff->canCreateTickets()) { - $errors['err']=__('Permission Denied. You are not allowed to reopen tickets.'); - } elseif ($ticket->isOpen()) { - $errors['err'] = __('Ticket is already open!'); - } elseif (!($status=TicketStatus::lookup($_POST['status_id']))) { - $errors['err'] = __('Unknown status'); - } elseif (strcasecmp($status->getState(), 'open')) { - $errors['err'] = __('Invalid status'); - } elseif ($ticket->setStatus($status)) { - $msg=__('Ticket Reopened'); - if($_POST['ticket_status_notes']) { - $ticket->logNote( - __('Ticket Reopened'), - $_POST['ticket_status_notes'], - $thisstaff); - } - } else { - $errors['err']=__('Problems reopening the ticket. Try again'); - } - break; case 'release': if(!$ticket->isAssigned() || !($assigned=$ticket->getAssigned())) { $errors['err'] = __('Ticket is not assigned!'); @@ -336,21 +290,6 @@ if($_POST && !$errors): $errors['err'] = 'Unable to change tiket ownership. Try again'; } break; - case 'delete': // Dude what are you trying to hide? bad customer support?? - if(!$thisstaff->canDeleteTickets()) { - $errors['err']=__('Permission Denied. You are not allowed to DELETE tickets!!'); - } elseif($ticket->delete()) { - $msg=sprintf(__('Ticket #%s deleted successfully'),$ticket->getNumber()); - //Log a debug note - $ost->logDebug(sprintf(__('Ticket #%s deleted'),$ticket->getNumber()), - sprintf(__('Ticket #%1$s deleted by %2$s'), - $ticket->getNumber(), $thisstaff->getName()) - ); - $ticket=null; //clear the object. - } else { - $errors['err']=__('Problems deleting the ticket. Try again'); - } - break; default: $errors['err']=__('You must select action to perform'); endswitch; @@ -363,90 +302,6 @@ if($_POST && !$errors): }elseif($_POST['a']) { switch($_POST['a']) { - case 'mass_process': - if(!$thisstaff->canManageTickets()) - $errors['err']=__('You do not have permission to mass manage tickets. Contact admin for such access'); - elseif(!$_POST['tids'] || !is_array($_POST['tids'])) - $errors['err']=sprintf(__('You must select at least %s.'), - __('one ticket')); - else { - $count=count($_POST['tids']); - $i = 0; - switch(strtolower($_POST['do'])) { - case 'reopen': - if($thisstaff->canCloseTickets() || $thisstaff->canCreateTickets()) { - $note=sprintf(__('Ticket reopened by %s'),$thisstaff->getName()); - foreach($_POST['tids'] as $k=>$v) { - if(($t=Ticket::lookup($v)) && $t->isClosed() && @$t->reopen()) { - $i++; - $t->logNote(__('Ticket Reopened'), $note, $thisstaff); - } - } - - if($i==$count) - $msg = sprintf(__('Successfully reopened %s'), - _N('selected ticket', 'selected tickets', $count)); - elseif($i) - $warn = sprintf(__('%1$d of %2$d %3$s reopened'),$i, $count, - _N('selected ticket', 'selected tickets', $count)); - else - $errors['err'] = sprintf(__('Unable to reopen %s'), - _N('selected ticket', 'selected tickets', $count)); - } else { - $errors['err'] = __('You do not have permission to reopen tickets'); - } - break; - case 'close': - $errors['err'] = 'Support coming soon!'; - break; - case 'mark_overdue': - $note=sprintf(__('Ticket flagged as overdue by %s'),$thisstaff->getName()); - foreach($_POST['tids'] as $k=>$v) { - if(($t=Ticket::lookup($v)) && !$t->isOverdue() && $t->markOverdue()) { - $i++; - $t->logNote(__('Ticket Marked Overdue'), $note, $thisstaff); - } - } - - if($i==$count) - $msg = sprintf(__('Selected tickets (%d) marked overdue'), $i); - elseif($i) - $warn = sprintf(__('%1$d of %2$d selected tickets marked overdue'), $i, $count); - else - $errors['err'] = __('Unable to flag selected tickets as overdue'); - break; - case 'delete': - if($thisstaff->canDeleteTickets()) { - foreach($_POST['tids'] as $k=>$v) { - if(($t=Ticket::lookup($v)) && @$t->delete()) $i++; - } - - //Log a warning - if($i) { - $log = sprintf(_S('%1$s (%2$s) just deleted %3$d ticket(s)'), - $thisstaff->getName(), $thisstaff->getUserName(), $i); - $ost->logWarning(_S('Tickets deleted'), $log, false); - - } - - if($i==$count) - $msg = sprintf(__('Successfully deleted %s'), - _N('selected ticket', 'selected tickets', $count)); - elseif($i) - $warn = sprintf(__('%1$d of %2$d %3$s deleted'),$i, $count, - _N('selected ticket', 'selected tickets', $count)); - else - $errors['err'] = sprintf(__('Unable to delete %s'), - _N('selected ticket', 'selected tickets', $count)); - } else { - $errors['err'] = __('You do not have permission to delete tickets'); - } - break; - default: - $errors['err']=__('Unknown action - get technical help.'); - } - } - break; case 'open': $ticket=null; if(!$thisstaff || !$thisstaff->canCreateTickets()) { @@ -489,7 +344,8 @@ if($cfg->showAnsweredTickets()) { (!$_REQUEST['status'] || $_REQUEST['status']=='open')); } else { - if($stats) { + if ($stats) { + $nav->addSubMenu(array('desc'=>$open_name.' ('.number_format($stats['open']).')', 'title'=>__('Open Tickets'), 'href'=>'tickets.php', @@ -534,13 +390,14 @@ if($thisstaff->showAssignedOnly() && $stats['closed']) { ($_REQUEST['status']=='closed')); } else { - $nav->addSubMenu(array('desc'=>sprintf(__('Resolved (%s)'), number_format($stats['resolved'])), - 'title'=>__('Resolved Tickets'), - 'href'=>'tickets.php?status=resolved', - 'iconclass'=>'closedTickets'), - ($_REQUEST['status']=='resolved')); + if ($stats['resolved']) + $nav->addSubMenu(array('desc' => __('Resolved').' ('.number_format($stats['resolved']).')', + 'title'=>__('Resolved Tickets'), + 'href'=>'tickets.php?status=resolved', + 'iconclass'=>'closedTickets'), + ($_REQUEST['status']=='resolved')); - $nav->addSubMenu(array('desc'=>sprintf(__('Closed Tickets (%s)'), number_format($stats['closed'])), + $nav->addSubMenu(array('desc' => __('Closed').' ('.number_format($stats['closed']).')', 'title'=>__('Closed Tickets'), 'href'=>'tickets.php?status=closed', 'iconclass'=>'closedTickets'), diff --git a/setup/inc/class.installer.php b/setup/inc/class.installer.php index 1e3ae346e35784331d16d4ebc3d2c895c3746f58..0c7ea1f5b2a7029062e40969e3312cccd4a8be08 100644 --- a/setup/inc/class.installer.php +++ b/setup/inc/class.installer.php @@ -115,8 +115,8 @@ class Installer extends SetupWizard { /*************** We're ready to install ************************/ define('ADMIN_EMAIL',$vars['admin_email']); //Needed to report SQL errors during install. - define('PREFIX',$vars['prefix']); //Table prefix - Bootstrap::defineTables(PREFIX); + define('TABLE_PREFIX',$vars['prefix']); //Table prefix + Bootstrap::defineTables(TABLE_PREFIX); Bootstrap::loadCode(); $debug = true; // Change it to false to squelch SQL errors. @@ -157,23 +157,25 @@ class Installer extends SetupWizard { $i18n = new Internationalization($vars['lang_id']); $i18n->loadDefaultData(); - $sql='SELECT `id` FROM '.PREFIX.'sla ORDER BY `id` LIMIT 1'; + Signal::send('system.install', $this); + + $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 '.PREFIX.'department ORDER BY `dept_id` LIMIT 1'; + $sql='SELECT `dept_id` FROM '.TABLE_PREFIX.'department ORDER BY `dept_id` LIMIT 1'; $dept_id_1 = db_result(db_query($sql, false)); - $sql='SELECT `tpl_id` FROM '.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 '.PREFIX.'groups ORDER BY `group_id` LIMIT 1'; + $sql='SELECT `group_id` FROM '.TABLE_PREFIX.'groups ORDER BY `group_id` LIMIT 1'; $group_id_1 = db_result(db_query($sql, false)); - $sql='SELECT `value` FROM '.PREFIX.'config WHERE namespace=\'core\' and `key`=\'default_timezone_id\' LIMIT 1'; + $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 '.PREFIX.'staff SET created=NOW() ' + $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" .', email='.db_input($vars['admin_email']) @@ -189,14 +191,14 @@ class Installer extends SetupWizard { //Create default emails! $email = $vars['email']; list(,$domain)=explode('@',$vars['email']); - $sql='INSERT INTO '.PREFIX.'email (`name`,`email`,`created`,`updated`) VALUES ' + $sql='INSERT INTO '.TABLE_PREFIX.'email (`name`,`email`,`created`,`updated`) VALUES ' ." ('Support','$email',NOW(),NOW())" .",('osTicket Alerts','alerts@$domain',NOW(),NOW())" .",('','noreply@$domain',NOW(),NOW())"; $support_email_id = db_query($sql, false) ? db_insert_id() : 0; - $sql='SELECT `email_id` FROM '.PREFIX."email WHERE `email`='alerts@$domain' LIMIT 1"; + $sql='SELECT `email_id` FROM '.TABLE_PREFIX."email WHERE `email`='alerts@$domain' LIMIT 1"; $alert_email_id = db_result(db_query($sql, false)); //Create config settings---default settings! @@ -248,7 +250,7 @@ class Installer extends SetupWizard { /************* Make the system happy ***********************/ - $sql='UPDATE '.PREFIX."email SET dept_id=$dept_id_1"; + $sql='UPDATE '.TABLE_PREFIX."email SET dept_id=$dept_id_1"; db_query($sql, false); global $cfg; @@ -271,7 +273,7 @@ class Installer extends SetupWizard { //Log a message. $msg=__("Congratulations osTicket basic installation completed!\n\nThank you for choosing osTicket!"); - $sql='INSERT INTO '.PREFIX.'syslog SET created=NOW(), updated=NOW(), log_type="Debug" ' + $sql='INSERT INTO '.TABLE_PREFIX.'syslog SET created=NOW(), updated=NOW(), log_type="Debug" ' .', title="osTicket installed!"' .', log='.db_input($msg) .', ip_address='.db_input($_SERVER['REMOTE_ADDR']); diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql index ac5c3783f3ecd1fcb0b646f2e1e8a7d9a8c17056..4879e53ce514f2aa1f4ba813b1fd967b3ae3f2cb 100644 --- a/setup/inc/streams/core/install-mysql.sql +++ b/setup/inc/streams/core/install-mysql.sql @@ -599,7 +599,6 @@ CREATE TABLE `%TABLE_PREFIX%ticket` ( `email_id` int(11) unsigned NOT NULL default '0', `flags` int(10) unsigned NOT NULL default '0', `ip_address` varchar(64) NOT NULL default '', - `status` enum('open','closed') NOT NULL default 'open', `source` enum('Web','Email','Phone','API','Other') NOT NULL default 'Other', `isoverdue` tinyint(1) unsigned NOT NULL default '0', `isanswered` tinyint(1) unsigned NOT NULL default '0', @@ -614,7 +613,6 @@ CREATE TABLE `%TABLE_PREFIX%ticket` ( KEY `user_id` (`user_id`), KEY `dept_id` (`dept_id`), KEY `staff_id` (`staff_id`), - KEY `status` (`status`), KEY `team_id` (`team_id`), KEY `status_id` (`status_id`), KEY `created` (`created`), @@ -679,9 +677,9 @@ DROP TABLE IF EXISTS `%TABLE_PREFIX%ticket_status`; CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%ticket_status` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(60) NOT NULL DEFAULT '', - `state` varchar(16) NOT NULL DEFAULT 'open', + `state` varchar(16) DEFAULT NULL, `mode` int(11) unsigned NOT NULL DEFAULT '0', - `flags` int(10) unsigned NOT NULL DEFAULT '0', + `flags` int(11) unsigned NOT NULL DEFAULT '0', `sort` int(11) unsigned NOT NULL DEFAULT '0', `notes` text NOT NULL, `created` datetime NOT NULL,