diff --git a/account.php b/account.php index 416cd4761dda9d5b9f851dd40b58a8877bf7c598..2f2914a4a2893f439e16f4a1ad95fa437d16c609 100644 --- a/account.php +++ b/account.php @@ -81,7 +81,7 @@ elseif ($_POST) { elseif (!$user_form->getField('name')->getClean()) $errors['name'] = sprintf(__('%s is a required field'), $user_form->getField('name')->getLocal('label')); // Registration for existing users - elseif ($addr && !($user = User::lookupByEmail($addr))) + elseif ($addr && ($user = User::lookupByEmail($addr)) && !$user->updateInfo($_POST, $errors)) $errors['err'] = __('Unable to register account. See messages below'); // Users created from ClientCreateRequest elseif (isset($_POST['backend']) && !($user = User::fromVars($user_form->getClean()))) diff --git a/include/ajax.tickets.php b/include/ajax.tickets.php index 68ff2fbacfb26e92f2e9a27c8958cf9b7e08adbd..4f5e7a62bcc55939dd2c788b86ee880a4fe37489 100644 --- a/include/ajax.tickets.php +++ b/include/ajax.tickets.php @@ -443,6 +443,9 @@ function refer($tid, $target=null) { switch ($_POST['do']) { case 'refer': if ($form->isValid() && $ticket->refer($form, $errors)) { + $clean = $form->getClean(); + if ($clean['comments']) + $ticket->logNote('Referral', $clean['comments'], $thisstaff); $_SESSION['::sysmsgs']['msg'] = sprintf( __('%s successfully'), sprintf( diff --git a/include/class.collaborator.php b/include/class.collaborator.php index dc35a436f06c3ee2f5b2e0c1c9cb5847be864970..65deaf8e4ec06c79d77293847467d8a65ab6e4d2 100644 --- a/include/class.collaborator.php +++ b/include/class.collaborator.php @@ -41,7 +41,7 @@ implements EmailContact, ITicketUser { return Format::htmlchars($this->toString()); } function toString() { - return sprintf('%s <%s>', $this->getName(), $this->getEmail()); + return sprintf('"%s" <%s>', $this->getName(), $this->getEmail()); } function getId() { diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php index 6d654d8a300805b084bc22389a537f870654cbef..9e5ece6717585e697b13df71265dfec22bcaff05 100644 --- a/include/class.dynamic_forms.php +++ b/include/class.dynamic_forms.php @@ -1822,7 +1822,9 @@ class SelectionField extends FormField { function getSearchQ($method, $value, $name=false) { $name = $name ?: $this->get('name'); - $val = '"?'.implode('("|,|$)|"?', array_keys($value)).'("|,|$)'; + $val = $value; + if ($value && is_array($value)) + $val = '"?'.implode('("|,|$)|"?', array_keys($value)).'("|,|$)'; switch ($method) { case '!includes': return Q::not(array("{$name}__regex" => $val)); diff --git a/include/class.filter.php b/include/class.filter.php index 4a66737c11bb468c6184c1c1b8431e97d93a09fd..08b42338bba5c5ca7f7bba5f69370dc314a37d3d 100644 --- a/include/class.filter.php +++ b/include/class.filter.php @@ -369,6 +369,7 @@ class Filter { $sql='DELETE FROM '.FILTER_TABLE.' WHERE id='.db_input($id).' LIMIT 1'; if(db_query($sql) && ($num=db_affected_rows())) { db_query('DELETE FROM '.FILTER_RULE_TABLE.' WHERE filter_id='.db_input($id)); + db_query('DELETE FROM '.FILTER_ACTION_TABLE.' WHERE filter_id='.db_input($id)); } return $num; diff --git a/include/class.filter_action.php b/include/class.filter_action.php index 181935af263d4ea863b6b36b8a1da25adc1a3406..3adcac43c07b53a2d1a88bcdb3354ed49da44825 100644 --- a/include/class.filter_action.php +++ b/include/class.filter_action.php @@ -561,8 +561,9 @@ class FA_SendEmail extends TriggerAction { } function getConfigurationOptions() { - $choices = array('' => __('Default System Email')); - $choices += Email::getAddresses(); + global $cfg; + + $choices = Email::getAddresses(); return array( 'recipients' => new TextboxField(array( @@ -608,7 +609,7 @@ class FA_SendEmail extends TriggerAction { 'from' => new ChoiceField(array( 'label' => __('From Email'), 'choices' => $choices, - 'default' => '', + 'default' => $cfg->getDefaultEmail()->getId(), )), ); } diff --git a/include/class.forms.php b/include/class.forms.php index 7d7c585e9e8629ab2d09f74c0f50158dbbab0006..980f67c56ea9b991e67de2cc7c9cfaf5afb3539b 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -4395,32 +4395,21 @@ class FileUploadWidget extends Widget { $config = $this->field->getConfiguration(); $name = $this->field->getFormName(); $id = substr(md5(spl_object_hash($this)), 10); - $attachments = $this->field->getAttachments(); $mimetypes = array_filter($config['__mimetypes'], function($t) { return strpos($t, '/') !== false; } ); $maxfilesize = ($config['size'] ?: 1048576) / 1048576; $files = array(); - $new = array_fill_keys($this->field->getClean(), 1); + $new = array_flip($this->field->getClean()); //get file ids stored in session when creating tickets/tasks from thread + //XXX: This shouldn't be here!! if (!$new && is_array($_SESSION[':form-data']) && array_key_exists($this->field->get('name'), $_SESSION[':form-data'])) - $new = array_fill_keys($_SESSION[':form-data'][$this->field->get('name')], 1); + $new = $_SESSION[':form-data'][$this->field->get('name')]; - foreach ($attachments as $a) { - unset($new[$a->file_id]); - } - // Add in newly added files not yet saved (if redisplaying after an - // error) - if ($new) { - $attachments = array_merge($attachments, GenericAttachment::objects() - ->filter(array('file_id__in' => array_keys($new))) - ->all() - ); - } - - foreach ($attachments as $att) { + foreach ($this->field->getAttachments() as $att) { + unset($new[$att->file_id]); $files[] = array( 'id' => $att->file->getId(), 'name' => $att->getFilename(), @@ -4429,6 +4418,25 @@ class FileUploadWidget extends Widget { 'download_url' => $att->file->getDownloadUrl(), ); } + + // Add in newly added files not yet saved (if redisplaying after an + // error) + if ($new) { + $F = AttachmentFile::objects() + ->filter(array('id__in' => array_keys($new))) + ->all(); + foreach ($F as $f) { + $f->tmp_name = $new[$f->getId()]; + $files[] = array( + 'id' => $f->getId(), + 'name' => $f->getName(), + 'type' => $f->getType(), + 'size' => $f->getSize(), + 'download_url' => $f->getDownloadUrl(), + ); + } + } + ?><div id="<?php echo $id; ?>" class="filedrop"><div class="files"></div> <div class="dropzone"><i class="icon-upload"></i> @@ -4493,7 +4501,7 @@ class FileUploadWidget extends Widget { // Files attached to threads where we are creating tasks/tickets are allowed if (isset($_SESSION[':form-data'][$this->field->get('name')])) { foreach ($_SESSION[':form-data'][$this->field->get('name')] as $key => $value) - $allowed[$value] = 1; + $allowed[$key] = $value; } // Canned attachments initiated by this session diff --git a/include/class.orm.php b/include/class.orm.php index 1325bbab7b23a6a55db15c02a70168a55b7e1771..473be838ceba796105db5770b71d1f620f895f5e 100644 --- a/include/class.orm.php +++ b/include/class.orm.php @@ -2110,6 +2110,14 @@ extends ModelResultSet { return $object; } + + function merge(InstrumentedList $list, $save=false) { + foreach ($list as $object) + $this->add($object, $save); + + return $this; + } + function remove($object, $delete=true) { if ($delete) $object->delete(); diff --git a/include/class.queue.php b/include/class.queue.php index 6b4c1adc1cab13613ffd170f80a6d568e4cf5518..b4343d43eb17a622c1af41083ed79fa4cb819b3e 100644 --- a/include/class.queue.php +++ b/include/class.queue.php @@ -253,6 +253,7 @@ class CustomQueue extends VerySimpleModel { 'topic_id', 'created', 'est_duedate', + 'duedate', ) ); @@ -2825,7 +2826,8 @@ extends QueueColumnFilter { static $desc = /* @trans */ "Date and Time"; function filter($text, $row) { - return $text->changeTo(Format::datetime($text->value)); + return $text ? + $text->changeTo(Format::datetime($text->value)) : ''; } } diff --git a/include/class.thread.php b/include/class.thread.php index 2aa82cd494a89ba6f3a6c8f2adf73b519dea6bf2..b499b95ed59c497d37d70955079f9501e3b58e80 100644 --- a/include/class.thread.php +++ b/include/class.thread.php @@ -848,20 +848,24 @@ implements TemplateVariable { } function getBody() { - if (!isset($this->_body)) { $body = $this->body; if ($body == null && $this->getNumAttachments()) { - foreach ($this->attachments as $a) + $attachments = Attachment::objects() + ->filter(array( + 'inline' => 1, + 'object_id' => $this->getId(), + 'type' => ObjectModel::OBJECT_TYPE_THREAD, + 'file__type__in' => array('text/html','text/plain')) + ); + foreach ($attachments as $a) if ($a->inline && ($f=$a->getFile())) $body .= $f->getData(); } - $this->_body = ThreadEntryBody::fromFormattedText($body, $this->format, array('balanced' => $this->hasFlag(self::FLAG_BALANCED)) ); } - return $this->_body; } diff --git a/include/class.thread_actions.php b/include/class.thread_actions.php index 351f66370fa9fd09090f4f5bf12934cbeb961a9c..2bccfd311586643492e48b0b987f6d33c0a95d2d 100644 --- a/include/class.thread_actions.php +++ b/include/class.thread_actions.php @@ -524,7 +524,7 @@ JS unset($_SESSION[':form-data'][$k]); foreach ($this->entry->getAttachments() as $a) if (!$a->inline && $a->file) { - $_SESSION[':form-data'][$k][] = $a->file->getId(); + $_SESSION[':form-data'][$k][$a->file->getId()] = $a->getFilename(); } } diff --git a/include/class.ticket.php b/include/class.ticket.php index 409804f865aa6009d67c42a864e748192ea1905e..ede3112f149acde3ba2a6a781d07929cdde5ac70 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -3708,15 +3708,15 @@ implements RestrictedAccess, Threadable, Searchable { $errors += $form->errors(); if ($vars['topicId']) { - if ($topic=Topic::lookup($vars['topicId'])) { + if (($topic=Topic::lookup($vars['topicId'])) + && $topic->isActive()) { foreach ($topic_forms as $topic_form) { $TF = $topic_form->getForm($vars); if (!$TF->isValid($field_filter('topic'))) $errors = array_merge($errors, $TF->errors()); } - } - else { - $errors['topicId'] = 'Invalid help topic selected'; + } else { + $vars['topicId'] = 0; } } @@ -4134,10 +4134,14 @@ implements RestrictedAccess, Threadable, Searchable { $attachments = array(); $message = $ticket->getLastMessage(); if ($cfg->emailAttachments()) { - if ($message) - $attachments = $message->getAttachments(); - if ($response && $response->getNumAttachments()) - $attachments = $attachments->merge($response->getAttachments()); + if ($message && $message->getNumAttachments()) { + foreach ($message->getNumAttachments() as $attachment) + $attachments[] = $attachment; + } + if ($response && $response->getNumAttachments()) { + foreach ($response->getAttachments() as $attachment) + $attachments[] = $attachment; + } } if ($vars['signature']=='mine') diff --git a/include/class.user.php b/include/class.user.php index 53a8627c3bd4d95e319d86e26d9e1f8498f5a4d4..0fa35e4a14d62514632bcf163f80fc25e216b49c 100644 --- a/include/class.user.php +++ b/include/class.user.php @@ -678,7 +678,7 @@ implements TemplateVariable { $this->getDomain()); if ($this->getName()) - $this->address = sprintf('%s <%s>', + $this->address = sprintf('"%s" <%s>', $this->getName(), $this->email); } diff --git a/include/client/footer.inc.php b/include/client/footer.inc.php index 37369a7d3e131766679266cb346f8bcc83bb9931..6ff20f28a519799dcd852a85a1bc1fc4f21b708e 100644 --- a/include/client/footer.inc.php +++ b/include/client/footer.inc.php @@ -1,7 +1,8 @@ </div> </div> <div id="footer"> - <p><?php echo __('Copyright ©'); ?> <?php echo date('Y'); ?> <?php echo (string) $ost->company ?: 'osTicket.com'; ?> - <?php echo __('All rights reserved.'); ?></p> + <p><?php echo __('Copyright ©'); ?> <?php echo date('Y'); ?> <?php + echo Format::htmlchars((string) $ost->company ?: 'osTicket.com'); ?> - <?php echo __('All rights reserved.'); ?></p> <a id="poweredBy" href="http://osticket.com" target="_blank"><?php echo __('Helpdesk software - powered by osTicket'); ?></a> </div> <div id="overlay"></div> diff --git a/include/client/open.inc.php b/include/client/open.inc.php index 4011d14038391c6fad5e4d291a9f1818de36f736..9900e2fe272e71cdc45afda0723f62f660d1504c 100644 --- a/include/client/open.inc.php +++ b/include/client/open.inc.php @@ -26,7 +26,7 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) { $F = $F->instanciate(); $F->isValidForClient(); } - $forms[] = $F; + $forms[] = $F->getForm(); } } diff --git a/include/staff/footer.inc.php b/include/staff/footer.inc.php index 8ab46960a950cf9d116ff66db72b0b72846663fe..2376cf96ec2947289b2bb5bfbfe04355288d2494 100644 --- a/include/staff/footer.inc.php +++ b/include/staff/footer.inc.php @@ -2,7 +2,8 @@ </div> <?php if (!isset($_SERVER['HTTP_X_PJAX'])) { ?> <div id="footer"> - <?php echo __('Copyright ©') ?> 2006-<?php echo date('Y'); ?> <?php echo (string) $ost->company ?: 'osTicket.com'; ?> <?php echo __('All Rights Reserved.'); ?> + <?php echo __('Copyright ©') ?> 2006-<?php echo date('Y'); ?> <?php + echo Format::htmlchars((string) $ost->company ?: 'osTicket.com'); ?> <?php echo __('All Rights Reserved.'); ?> </div> <?php if(is_object($thisstaff) && $thisstaff->isStaff()) { ?> diff --git a/include/staff/tasks.inc.php b/include/staff/tasks.inc.php index 8fc2b0534807a8a48386a33cd45f7f4de266dc48..7bdc4a3d9e176cc167c6f78aa64c4a7a18e99ecc 100644 --- a/include/staff/tasks.inc.php +++ b/include/staff/tasks.inc.php @@ -122,6 +122,11 @@ if ($filters) $visibility = Q::any( new Q(array('flags__hasbit' => TaskModel::ISOPEN, 'staff_id' => $thisstaff->getId())) ); +// -- Task for tickets assigned to me +$visibility->add(new Q( array( + 'ticket__staff_id' => $thisstaff->getId(), + 'ticket__status__state' => 'open')) + ); // -- Routed to a department of mine if (!$thisstaff->showAssignedOnly() && ($depts=$thisstaff->getDepts())) $visibility->add(new Q(array('dept_id__in' => $depts))); diff --git a/include/staff/templates/queue-navigation.tmpl.php b/include/staff/templates/queue-navigation.tmpl.php index 3e40918fd8f94efdd80c27d285b626c5e3aff58f..d1061c8d43da152d5686b428fe6ee0b50943f8f1 100644 --- a/include/staff/templates/queue-navigation.tmpl.php +++ b/include/staff/templates/queue-navigation.tmpl.php @@ -45,15 +45,6 @@ $selected = (!isset($_REQUEST['a']) && $_REQUEST['queue'] == $this_queue->getId } include 'queue-subnavigation.tmpl.php'; } ?> - <!-- Personal Queues --> - <?php - $queues = $this_queue->getMyChildren(); - if (count($queues)) { ?> - <li class="personalQ"></li> - <?php foreach ($queues as $q) { - include 'queue-subnavigation.tmpl.php'; - } - }?> </ul> <!-- Add Queue button sticky at the bottom --> <div class="add-queue"> diff --git a/include/staff/templates/status-options.tmpl.php b/include/staff/templates/status-options.tmpl.php index 16d0f885bf3bb4813865d8ebbdc3171cf5be49b1..1aee4879007fd77f571e5efeeb90b15dd3ff9483 100644 --- a/include/staff/templates/status-options.tmpl.php +++ b/include/staff/templates/status-options.tmpl.php @@ -41,6 +41,7 @@ if (!$nextStatuses) data-dropdown="#action-dropdown-statuses" data-placement="bottom" data-toggle="tooltip" title="<?php echo __('Change Status'); ?>"> <i class="icon-caret-down pull-right"></i> <a class="tickets-action" + aria-label="<?php echo __('Change Status'); ?>" href="#statuses"><i class="icon-flag"></i></a> </span> diff --git a/include/staff/templates/tickets-actions.tmpl.php b/include/staff/templates/tickets-actions.tmpl.php index 2735fc70ea690421fd24aa066e927829cd8792fa..ff186a4be2d858884c6943c0f91ab1d7be957234 100644 --- a/include/staff/templates/tickets-actions.tmpl.php +++ b/include/staff/templates/tickets-actions.tmpl.php @@ -14,6 +14,7 @@ if ($agent->hasPerm(Ticket::PERM_ASSIGN, false)) {?> echo __('Assign'); ?>"> <i class="icon-caret-down pull-right"></i> <a class="tickets-action" id="tickets-assign" + aria-label="<?php echo __('Assign'); ?>" href="#tickets/mass/assign"><i class="icon-user"></i></a> </span> <div id="action-dropdown-assign" class="action-dropdown anchor-right"> diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php index 96ee98bbdeac440975ad94301eae6b2959c16569..62a0c60fc0f0c30833e05b6f5619ac3d2717245f 100644 --- a/include/staff/ticket-open.inc.php +++ b/include/staff/ticket-open.inc.php @@ -30,7 +30,7 @@ if (!$user && $_GET['tid'] && ($entry = ThreadEntry::lookup($_GET['tid']))) { unset($_SESSION[':form-data'][$k]); foreach ($entry->getAttachments() as $a) { if (!$a->inline && $a->file) { - $_SESSION[':form-data'][$k][] = $a->file->getId(); + $_SESSION[':form-data'][$k][$a->file->getId()] = $a->getFilename(); } } } diff --git a/include/staff/ticket-view.inc.php b/include/staff/ticket-view.inc.php index a9752bd7492c27ed8a2479556b2f39a6ef0c8324..9fd5835b562c73b6ec5ce5e7e9f8b42b5cfc93f5 100644 --- a/include/staff/ticket-view.inc.php +++ b/include/staff/ticket-view.inc.php @@ -76,7 +76,7 @@ if($ticket->isOverdue()) } ?> <span class="action-button pull-right" data-placement="bottom" data-dropdown="#action-dropdown-print" data-toggle="tooltip" title="<?php echo __('Print'); ?>"> <i class="icon-caret-down pull-right"></i> - <a id="ticket-print" href="tickets.php?id=<?php echo $ticket->getId(); ?>&a=print"><i class="icon-print"></i></a> + <a id="ticket-print" aria-label="<?php echo __('Print'); ?>" href="tickets.php?id=<?php echo $ticket->getId(); ?>&a=print"><i class="icon-print"></i></a> </span> <div id="action-dropdown-print" class="action-dropdown anchor-right"> <ul> diff --git a/js/osticket.js b/js/osticket.js index 3d82e2ba1152960450a75e0f2c3ccdd428af7edb..4e0d512de80aaa87a35deb5a0165b4da86ce3b82 100644 --- a/js/osticket.js +++ b/js/osticket.js @@ -45,6 +45,9 @@ $(document).ready(function(){ $('form').submit(function() { $(window).unbind('beforeunload'); + // Disable client-side Post Reply/Create Ticket buttons to help + // prevent duplicate POST + $(':submit', $(this)).attr('disabled', true); $('#overlay, #loading').show(); return true; }); diff --git a/scp/forms.php b/scp/forms.php index da5663a817df06f5068a69e083ddaca6e172a653..46bd619315ad9e55dfc91b4a39f4bbcb6d8fd826 100644 --- a/scp/forms.php +++ b/scp/forms.php @@ -8,6 +8,7 @@ if($_REQUEST['id'] && !($form=DynamicForm::lookup($_REQUEST['id']))) if($_POST) { $_POST = Format::htmlchars($_POST, true); + $_POST['instructions'] = Format::htmldecode($_POST['instructions']); $fields = array('title', 'notes', 'instructions'); $required = array('title'); $max_sort = 0; diff --git a/scp/js/scp.js b/scp/js/scp.js index 6f30efe1e371ac4f59d66d980c7723b2a1c71fee..c83673cd3ce944df8139dcef8f3561e7f6707b0c 100644 --- a/scp/js/scp.js +++ b/scp/js/scp.js @@ -164,7 +164,11 @@ var scp_prep = function() { $('form.save, form:has(table.list)').submit(function() { $(window).unbind('beforeunload'); $.toggleOverlay(true); - $('#loading').show(); + // Disable staff-side Post Reply/Open buttons to help prevent + // duplicate POST + $(':submit', $(this)).attr('disabled', true); + $('#overlay, #loading').show(); + return true; }); $('select#tpl_options').change(function() { diff --git a/scp/queues.php b/scp/queues.php index ba5f33b78baf9bdaec51b1da074993030703030c..f02f4efc8a5c3faa6d047d57d9962fa5fd0e86db 100644 --- a/scp/queues.php +++ b/scp/queues.php @@ -42,7 +42,6 @@ if ($_POST) { case 'create': $queue = CustomQueue::create(array( - 'flags' => CustomQueue::FLAG_PUBLIC, 'staff_id' => 0, 'title' => $_POST['queue-name'], 'root' => $_POST['root'] ?: 'T'