diff --git a/include/class.client.php b/include/class.client.php index 2d157ede502923d5e88fcbfa90f79924346c30b7..2d1f2930c12b2cc8201fc8313b54656836625fac 100644 --- a/include/class.client.php +++ b/include/class.client.php @@ -457,7 +457,6 @@ class ClientAccount extends UserAccount { if ($errors) return false; $this->set('timezone', $vars['timezone']); - $this->set('dst', isset($vars['dst']) ? 1 : 0); // Change language $this->set('lang', $vars['lang'] ?: null); Internationalization::setCurrentLanguage(null); diff --git a/include/class.filter.php b/include/class.filter.php index 05f93c7845b7c713eac3690684d1e5815f022918..360d5bc791d2a1ab75f61dd3ece23163a82fdf94 100644 --- a/include/class.filter.php +++ b/include/class.filter.php @@ -153,6 +153,10 @@ class Filter { $this->update($this->ht, $errors); } + function hasFlag($flag) { + return 0 !== ($this->ht['flags'] & $flag); + } + function stopOnMatch() { return ($this->ht['stop_onmatch']); } @@ -504,6 +508,10 @@ class Filter { $vars['target'] = 'Email'; } + //Note: this will be set when validating filters + if ($vars['email_id']) + $emailId = $vars['email_id']; + $sql=' updated=NOW() ' .',isactive='.db_input($vars['isactive']) .',flags='.db_input($vars['flags']) diff --git a/include/class.filter_action.php b/include/class.filter_action.php index 3adcac43c07b53a2d1a88bcdb3354ed49da44825..88aa0d091a0499d38a6bd711f20c381cba12a5ae 100644 --- a/include/class.filter_action.php +++ b/include/class.filter_action.php @@ -82,10 +82,10 @@ class FilterAction extends VerySimpleModel { function setFilterFlag($actions, $flag, $bool) { foreach ($actions as $action) { $filter = Filter::lookup($action->filter_id); - if ($filter) { - if ($flag == 'dept') $filter->setFlag(Filter::FLAG_INACTIVE_DEPT, $bool); - if ($flag == 'topic') $filter->setFlag(Filter::FLAG_INACTIVE_HT, $bool); - } + if ($filter && ($flag == 'dept') && ($filter->hasFlag(Filter::FLAG_INACTIVE_DEPT) != $bool)) + $filter->setFlag(Filter::FLAG_INACTIVE_DEPT, $bool); + if ($filter && ($flag == 'topic') && ($filter->hasFlag(Filter::FLAG_INACTIVE_HT) != $bool)) + $filter->setFlag(Filter::FLAG_INACTIVE_HT, $bool); } } diff --git a/include/class.forms.php b/include/class.forms.php index 2c3ec0da755cbfac1bd14b502c09c3ffa224a31f..0b54f965ed71154c1606305cc00ef0537ca2be69 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -1478,6 +1478,11 @@ class TextboxField extends FormField { function parse($value) { return Format::striptags($value); } + + function display($value) { + $value = $value ?: $this->value; + return ($value == 0) ? '0' : Format::htmlchars($this->toString($value)); + } } class PasswordField extends TextboxField { diff --git a/include/class.queue.php b/include/class.queue.php index e434661d2d8beb58c0751f76619d5202ad3539bc..effe927a899438313a9b001639c80c326596aea6 100644 --- a/include/class.queue.php +++ b/include/class.queue.php @@ -1141,9 +1141,6 @@ class CustomQueue extends VerySimpleModel { return false; $order = array_keys($fields); - // Filter exportable fields - if (!($fields = array_intersect_key($this->getExportableFields(), $fields))) - return false; $new = $fields; foreach ($this->exports as $f) { diff --git a/include/class.staff.php b/include/class.staff.php index 3833c63533505f740f8ca1da3ed13d494e9fec96..c9aa07dac6c2b09169c9fddbcd1f832d66e791ad 100644 --- a/include/class.staff.php +++ b/include/class.staff.php @@ -91,6 +91,7 @@ implements AuthenticatedUser, EmailContact, TemplateVariable, Searchable { 'datetime_format' => '', 'thread_view_order' => '', 'default_ticket_queue_id' => 0, + 'reply_redirect' => 'Ticket', )); $this->_config = $_config->getInfo(); } @@ -347,6 +348,10 @@ implements AuthenticatedUser, EmailContact, TemplateVariable, Searchable { return $this->default_paper_size; } + function getReplyRedirect() { + return $this->reply_redirect; + } + function forcePasswdChange() { return $this->change_passwd; } @@ -744,6 +749,7 @@ implements AuthenticatedUser, EmailContact, TemplateVariable, Searchable { 'default_from_name' => $vars['default_from_name'], 'thread_view_order' => $vars['thread_view_order'], 'default_ticket_queue_id' => $vars['default_ticket_queue_id'], + 'reply_redirect' => ($vars['reply_redirect'] == 'Queue') ? 'Queue' : 'Ticket', ) ); $this->_config = $_config->getInfo(); diff --git a/include/class.task.php b/include/class.task.php index 55f0152896da820a1897076c884596659bc462e4..fe1e71921766846433ec0e245adcc2ce1ae7690a 100644 --- a/include/class.task.php +++ b/include/class.task.php @@ -1189,7 +1189,7 @@ class Task extends TaskModel implements RestrictedAccess, Threadable { global $cfg; if (!$entry instanceof ThreadEntry - || !($recipients=$this->getThread()->getParticipants()) + || !($recipients=$this->getThread()->getRecipients()) || !($dept=$this->getDept()) || !($tpl=$dept->getTemplate()) || !($msg=$tpl->getTaskActivityNoticeMsgTemplate()) diff --git a/include/class.thread.php b/include/class.thread.php index 343b3e9005689e01a11ef5611e3a00179b776f82..a9485d0b85477e339e6d4fc3cbe4ae978a15abb6 100644 --- a/include/class.thread.php +++ b/include/class.thread.php @@ -263,6 +263,17 @@ implements Searchable { return $this->_participants; } + // MailingList of recipients (collaborators) + function getRecipients() { + $list = new MailingList(); + if ($collabs = $this->getActiveCollaborators()) { + foreach ($collabs as $c) + $list->addCc($c); + } + + return $list; + } + function getReferral($id, $type) { return $this->referrals->findFirst(array( diff --git a/include/class.util.php b/include/class.util.php index a3bc2305d130c8b7bf5885cc4df99f4dcf1cae22..e78c00d1d56347ccacdc1c0ca0702bc91ea65ba1 100644 --- a/include/class.util.php +++ b/include/class.util.php @@ -5,6 +5,7 @@ require_once INCLUDE_DIR . 'class.variable.php'; // Used by the email system interface EmailContact { function getId(); + function getUserId(); function getName(); function getEmail(); } @@ -28,6 +29,10 @@ implements EmailContact { return $this->contact->getId(); } + function getUserId() { + return $this->contact->getUserId(); + } + function getEmail() { return $this->contact->getEmail(); } diff --git a/include/staff/dashboard.inc.php b/include/staff/dashboard.inc.php index 3c91e3c5fd9135cb550a2c39de52c4a0411445db..c921b2b40964040ac66147f21114e7155e8b1b31 100644 --- a/include/staff/dashboard.inc.php +++ b/include/staff/dashboard.inc.php @@ -180,4 +180,16 @@ foreach ($groups as $g=>$desc) { </form> <script> $.drawPlots(<?php echo JsonDataEncoder::encode($report->getPlotData()); ?>); + // Set Selected Period For Dashboard Stats and Export + <?php if ($report && $report->end) { ?> + $("div#basic_search select option").each(function(){ + // Remove default selection + if ($(this)[0].selected) + $(this).removeAttr('selected'); + // Set the selected period by the option's value (periods equal + // option's values) + if ($(this).val() == "<?php echo $report->end; ?>") + $(this).attr("selected","selected"); + }); + <?php } ?> </script> diff --git a/include/staff/profile.inc.php b/include/staff/profile.inc.php index 78bab30bf2c38abdd2638522e96cc6526fa6cae1..1acb99a263f6dd060bd834d5084f364ee7e80d49 100644 --- a/include/staff/profile.inc.php +++ b/include/staff/profile.inc.php @@ -300,6 +300,23 @@ if ($avatar->isChangeable()) { ?> <div class="error"><?php echo $errors['default_paper_size']; ?></div> </td> </tr> + <tr> + <td><?php echo __('Reply Redirect'); ?>: + <div class="faded"><?php echo __('Redirect URL used after replying to a ticket.');?></div> + </td> + <td> + <select name="reply_redirect"> + <?php + $options=array('Queue'=>__('Queue'),'Ticket'=>__('Ticket')); + foreach($options as $key=>$opt) { + echo sprintf('<option value="%s" %s>%s</option>', + $key,($staff->reply_redirect==$key)?'selected="selected"':'',$opt); + } + ?> + </select> + <div class="error"><?php echo $errors['reply_redirect']; ?></div> + </td> + </tr> </tbody> <tbody> <tr class="header"> diff --git a/include/staff/templates/collaborators.tmpl.php b/include/staff/templates/collaborators.tmpl.php index d25d10a83fb72bd3c84797137503ac74d89f8f9c..b2545daa315c3f889f1150267c8736eccea54401 100644 --- a/include/staff/templates/collaborators.tmpl.php +++ b/include/staff/templates/collaborators.tmpl.php @@ -81,10 +81,10 @@ if(($users=$thread->getCollaborators())) {?> if ($_POST && $thread && $thread->getNumCollaborators()) { - $collaborators = sprintf('Participants (%d)', + $collaborators = sprintf('Collaborators (%d)', $thread->getNumCollaborators()); - $recipients = sprintf(__('Recipients (%d of %d)'), + $recipients = sprintf(__('Collaborators (%d of %d)'), $thread->getNumActiveCollaborators(), $thread->getNumCollaborators()); ?> diff --git a/include/staff/templates/queue-quickfilter.tmpl.php b/include/staff/templates/queue-quickfilter.tmpl.php index 9e4d64850a5ec3ea3e7afac9d17e179c62edd7de..d7919963d54237f9e12186e217b0905c379cccc4 100644 --- a/include/staff/templates/queue-quickfilter.tmpl.php +++ b/include/staff/templates/queue-quickfilter.tmpl.php @@ -36,7 +36,7 @@ $.pjax({ url: '?' + query, timeout: 2000, container: '#pjax-container'});"> - <ul> + <ul <?php if (count($choices) > 20) echo 'style="height:500px;overflow-x:hidden;overflow-y:scroll;"'; ?>> <?php foreach ($choices as $k=>$desc) { $selected = isset($quick_filter) && $quick_filter == $k; ?> diff --git a/include/staff/templates/queue-tickets.tmpl.php b/include/staff/templates/queue-tickets.tmpl.php index 41ffef2a6303925094eece20861e9b420d2c308d..dba05dea8c7278d6e9dbeb01af4af0e589bba1e8 100644 --- a/include/staff/templates/queue-tickets.tmpl.php +++ b/include/staff/templates/queue-tickets.tmpl.php @@ -77,16 +77,21 @@ $page = ($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1; $pageNav = new Pagenate(PHP_INT_MAX, $page, PAGE_LIMIT); $tickets = $pageNav->paginateSimple($tickets); -// Creative twist here. Create a new query copying the query criteria, sort, limit, -// and offset. Then join this new query to the $tickets query and clear the -// criteria, sort, limit, and offset from the outer query. -$criteria = clone $tickets; -$criteria->annotations = $criteria->related = $criteria->aggregated = []; -$tickets->constraints = $tickets->extra = []; -$tickets = $tickets->filter(['ticket_id__in' => $criteria->values_flat('ticket_id')]) - ->limit(false)->offset(false)->order_by(false); -# Index hint should be used on the $criteria query only -$tickets->clearOption(QuerySet::OPT_INDEX_HINT); +if (isset($tickets->extra['tables'])) { + // Creative twist here. Create a new query copying the query criteria, sort, limit, + // and offset. Then join this new query to the $tickets query and clear the + // criteria, sort, limit, and offset from the outer query. + $criteria = clone $tickets; + $criteria->limit(500); + $criteria->annotations = $criteria->related = $criteria->aggregated = + $criteria->annotations = $criteria->ordering = []; + $tickets->constraints = $tickets->extra = []; + $tickets = $tickets->filter(['ticket_id__in' => + $criteria->values_flat('ticket_id')]); + # Index hint should be used on the $criteria query only + $tickets->clearOption(QuerySet::OPT_INDEX_HINT); + $tickets->distinct('ticket_id'); +} $count = $queue->getCount($thisstaff) ?: (PAGE_LIMIT*3); $pageNav->setTotal($count, true); diff --git a/include/staff/templates/task-view.tmpl.php b/include/staff/templates/task-view.tmpl.php index 6153c8f20da3987fecebbdedd08ed20783dd9faa..d56b230c2e96f26cb789b4a954ea3ee41777715a 100644 --- a/include/staff/templates/task-view.tmpl.php +++ b/include/staff/templates/task-view.tmpl.php @@ -146,7 +146,7 @@ if ($task->isOverdue()) <ul> <?php - if ($task->isOpen()) { ?> + if (!$task->isOpen()) { ?> <li> <a class="no-pjax task-action" href="#tasks/<?php echo $task->getId(); ?>/reopen"><i @@ -372,9 +372,9 @@ if (!$ticket) { ?> <th><?php echo __('Collaborators');?>:</th> <td> <?php - $collaborators = __('Add Participants'); + $collaborators = __('Collaborators'); if ($task->getThread()->getNumCollaborators()) - $collaborators = sprintf(__('Participants (%d)'), + $collaborators = sprintf(__('Collaborators (%d)'), $task->getThread()->getNumCollaborators()); echo sprintf('<span><a class="collaborators preview" @@ -486,9 +486,9 @@ else style="display:<?php echo $thread->getNumCollaborators() ? 'inline-block': 'none'; ?>;" > <?php - $recipients = __('Add Participants'); + $recipients = __('Collaborators'); if ($thread->getNumCollaborators()) - $recipients = sprintf(__('Recipients (%d of %d)'), + $recipients = sprintf(__('Collaborators (%d of %d)'), $thread->getNumActiveCollaborators(), $thread->getNumCollaborators()); @@ -664,7 +664,10 @@ $(function() { .slideUp(); } }) - .done(function() { }) + .done(function() { + $('#loading').hide(); + $.toggleOverlay(false); + }) .fail(function() { }); }); <?php diff --git a/include/staff/templates/tasks-actions.tmpl.php b/include/staff/templates/tasks-actions.tmpl.php index dc59eae4fc5827a7231c39dbdbb4a7fc2f9c0d2e..e323e30602aee51bd9bd7efe14a3879610a163a5 100644 --- a/include/staff/templates/tasks-actions.tmpl.php +++ b/include/staff/templates/tasks-actions.tmpl.php @@ -7,57 +7,23 @@ if ($agent->hasPerm(Task::PERM_CLOSE, false)) { if (isset($options['status'])) { $status = $options['status']; - ?> - <span - class="action-button" - data-dropdown="#action-dropdown-tasks-status"> - <i class="icon-caret-down pull-right"></i> - <a class="tasks-status-action" - href="#statuses" - data-placement="bottom" - data-toggle="tooltip" - title="<?php echo __('Change Status'); ?>"><i - class="icon-flag"></i></a> - </span> - <div id="action-dropdown-tasks-status" - class="action-dropdown anchor-right"> - <ul> - <?php - if (!$status || !strcasecmp($status, 'closed')) { ?> - <li> - <a class="no-pjax tasks-action" - href="#tasks/mass/reopen"><i - class="icon-fixed-width icon-undo"></i> <?php - echo __('Reopen');?> </a> - </li> - <?php - } - if (!$status || !strcasecmp($status, 'open')) { - ?> - <li> - <a class="no-pjax tasks-action" - href="#tasks/mass/close"><i - class="icon-fixed-width icon-ok-circle"></i> <?php - echo __('Close');?> </a> - </li> - <?php - } ?> - </ul> - </div> -<?php - } else { - $actions += array( - 'reopen' => array( - 'icon' => 'icon-undo', - 'action' => __('Reopen') - )); + if (strpos($status, 'closed') !== false) { + $actions += array( + 'reopen' => array( + 'icon' => 'icon-undo', + 'action' => __('Reopen') + )); + } - $actions += array( - 'close' => array( - 'icon' => 'icon-ok-circle', - 'action' => __('Close') - )); + + if (strpos($status, 'open') !== false) { + $actions += array( + 'close' => array( + 'icon' => 'icon-ok-circle', + 'action' => __('Close') + )); + } } } @@ -95,7 +61,7 @@ if ($agent->hasPerm(Task::PERM_DELETE, false)) { 'action' => __('Delete') )); } -if ($actions && !isset($options['status'])) { +if ($actions && isset($options['status'])) { $more = $options['morelabel'] ?: __('More'); ?> <span diff --git a/include/staff/templates/user-import.tmpl.php b/include/staff/templates/user-import.tmpl.php index a1c47340b131e4487dbcae4e21451c381175f84d..3fc7d763f85974f0942fd0819bc7eec0920ac722 100644 --- a/include/staff/templates/user-import.tmpl.php +++ b/include/staff/templates/user-import.tmpl.php @@ -34,7 +34,7 @@ if ($org_id) { ?> 'To import more other fields, use the Upload tab.'); ?></em> </p> <textarea name="pasted" style="display:block;width:100%;height:8em" - placeholder="<?php echo __('e.g. John Doe, john.doe@osticket.com'); ?>"> + placeholder="<?php echo __('Name, Email'. PHP_EOL. ' John Doe, john.doe@osticket.com'); ?>"> <?php echo $info['pasted']; ?> </textarea> </div> diff --git a/include/staff/ticket-tasks.inc.php b/include/staff/ticket-tasks.inc.php index ec19431ac78f1bd772377e31212e1d31be32bab0..8c0bc5db4ac54a322cd0d288efdef2d31a713f8f 100644 --- a/include/staff/ticket-tasks.inc.php +++ b/include/staff/ticket-tasks.inc.php @@ -40,12 +40,17 @@ $showing = $pageNav->showing().' '._N('task', 'tasks', $count); print __('Add New Task'); ?></a> <?php } + foreach ($tasks as $task) + $taskStatus .= $task->isOpen() ? 'open' : 'closed'; + if ($count) Task::getAgentActions($thisstaff, array( 'container' => '#tasks_content', 'callback_url' => sprintf('ajax.php/tickets/%d/tasks', $ticket->getId()), - 'morelabel' => __('Options'))); + 'morelabel' => __('Options'), + 'status' => $taskStatus ? $taskStatus : '') + ); ?> </div> <div class="clear"></div> diff --git a/scp/filters.php b/scp/filters.php index c83f9eac074175b908b3cea6cf3eaaa75778f64a..25f22df4c2b9557ec061a954749dd5fc1da4162d 100644 --- a/scp/filters.php +++ b/scp/filters.php @@ -31,8 +31,6 @@ if($_POST){ if(!$filter){ $errors['err']=sprintf(__('%s: Unknown or invalid'), __('ticket filter')); }elseif($filter->update($_POST,$errors)){ - $filter->setFlag(Filter::FLAG_INACTIVE_DEPT, false); - $filter->setFlag(Filter::FLAG_INACTIVE_HT, false); $msg=sprintf(__('Successfully updated %s.'), __('this ticket filter')); }elseif(!$errors['err']){ $errors['err']=sprintf('%s %s', diff --git a/scp/login.php b/scp/login.php index 6e1854456c5757f4fdab2b3289728b3cc24a34fc..4853f7e31f028cd84f31cca74ddc833631a4d105 100644 --- a/scp/login.php +++ b/scp/login.php @@ -64,6 +64,8 @@ if ($_POST) { $redirect($_SERVER['REQUEST_URI']); } +} +if ($_POST && isset($_POST['userid'])) { // Lookup support backends for this staff $username = trim($_POST['userid']); if ($user = StaffAuthenticationBackend::process($username, @@ -94,8 +96,11 @@ elseif ($_GET['do']) { // Consider single sign-on authentication backends elseif (!$thisstaff || !($thisstaff->getId() || $thisstaff->isValid())) { if (($user = StaffAuthenticationBackend::processSignOn($errors, false)) - && ($user instanceof StaffSession)) - Http::redirect($dest); + && ($user instanceof StaffSession)) { + Http::redirect($dest); + } else if (isset($_SESSION['_staff']['auth']['msg'])) { + $msg = $_SESSION['_staff']['auth']['msg']; + } } // Browsers shouldn't suggest saving that username/password diff --git a/scp/tickets.php b/scp/tickets.php index 8881874e897217bbea32e02ba8735e482ee937ab..27179c0befe768bcfbfea7654772b45d16b12b3e 100644 --- a/scp/tickets.php +++ b/scp/tickets.php @@ -214,7 +214,7 @@ if($_POST && !$errors): $ticket = null; $redirect = 'tickets.php'; - if ($ticket) + if ($ticket && $thisstaff->getReplyRedirect() == 'Ticket') $redirect = 'tickets.php?id='.$ticket->getId(); } elseif (!$errors['err']) {