diff --git a/WHATSNEW.md b/WHATSNEW.md index 8e4aa862036de3cfb56c46d19dab9a11717edb22..13f764878f5feb5fe7a9377bafb469d565e917ce 100644 --- a/WHATSNEW.md +++ b/WHATSNEW.md @@ -185,6 +185,22 @@ database engines other than MySQL. The ORM was originally introduced in osTicket v1.8.0, but has seen the greatest boost in capability in this release. About 47% of the SQL queries are removed between v1.9.7 and v1.10 +osTicket v1.9.9 +=============== +### Enhancements + * Properly balance stripped and invalid HTML (#2145) + * Add MANIFEST file to deployment process and retire duplicate code for packaging (#2052) + +### Improvements + * Fix inability to configure LDAP and S3 plugins (*regression*) (59337b3) + * Fix incorrect whitespace in search indexed HTML content (#2111) + * Add support for invalid `multipart/relative` content type (aaf1b74) + * Force line breaks for very long HTML lines (56cc709) + +### Performance and Security + * Fix slow query for ticket counts for large datasets (c4ace2d) + * Fix slow thread load query (thanks @torohill) (7b7e855) + osTicket v1.9.8.1 ================= ### Enhancements diff --git a/include/class.cli.php b/include/class.cli.php index 01f829dddb05dfaa1820b5e4b91a76da21f22e63..a4e3cfc16ab619d32fd854778b09dab81c4569fd 100644 --- a/include/class.cli.php +++ b/include/class.cli.php @@ -272,7 +272,7 @@ class Module { continue; } // Allow multiple simple args like -Dvt - if ($arg[0] == '-' && strlen($arg) > 2) { + if ($arg[0] == '-' && $arg[1] != '-' && strlen($arg) > 2) { foreach (str_split(substr($arg, 2)) as $O) array_unshift($argv, "-{$O}"); $arg = substr($arg, 0, 2); diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php index 479e94736fd3c59e263e0ab017ad968837e94589..8af03362e532eb8a797ed3f52064ab5165d21d6f 100644 --- a/include/class.dynamic_forms.php +++ b/include/class.dynamic_forms.php @@ -1320,6 +1320,8 @@ class DynamicFormEntry extends VerySimpleModel { static function create($ht=false, $data=null) { $inst = parent::create($ht); $inst->set('created', new SqlFunction('NOW')); + if ($data) + $inst->setSource($data); foreach ($inst->getDynamicFields() as $field) { if (!($impl = $field->getImpl($field))) continue; diff --git a/include/class.filter_action.php b/include/class.filter_action.php index 4cdac2e7a636ca4eecdb0f4b1a59186bd9bb423d..577a07d834dce6714b653ae83b69a889232d395d 100644 --- a/include/class.filter_action.php +++ b/include/class.filter_action.php @@ -414,7 +414,7 @@ class FA_AssignTopic extends TriggerAction { } function getConfigurationOptions() { - $choices = HelpTopic::getAllHelpTopics(); + $choices = Topic::getHelpTopics(false, Topic::DISPLAY_DISABLED); return array( 'topic_id' => new ChoiceField(array( 'configuration' => array('prompt' => __('Unchanged')), diff --git a/include/class.format.php b/include/class.format.php index 85c54d234e02fb02f0aa2cb8bc91437a3127240b..4e5bb04b832d597104de8f287d495e14b953bf0f 100644 --- a/include/class.format.php +++ b/include/class.format.php @@ -165,7 +165,7 @@ class Format { $html = $doc->saveHTML(); $html = preg_replace('`^<!DOCTYPE.+?>|<\?xml .+?>|</?html>|</?body>|</?head>|<meta .+?/?>`', '', $html); # <?php } - return preg_replace('`^<div>|</div>$`', '', $html); + return preg_replace('`^<div>|</div>$`', '', trim($html)); } function html($html, $config=array()) { diff --git a/include/class.search.php b/include/class.search.php index 109d67a9c9f7b766e46b032cdd3d6f37d87738e3..efc9bc4a188ebe2253431359f8c919c7a17a34f9 100644 --- a/include/class.search.php +++ b/include/class.search.php @@ -389,7 +389,7 @@ class MysqlSearchBackend extends SearchBackend { $galera = db_result(db_query($sql)); if ($galera && !$mysql56) - throw new Exception('Galera cannot be used with MyISAM tables'); + throw new Exception('Galera cannot be used with MyISAM tables. Upgrade to MariaDB 10 / MySQL 5.6 is required'); $engine = $galera ? 'InnodB' : ($mysql56 ? '' : 'MyISAM'); if ($engine) $engine = 'ENGINE='.$engine; @@ -402,12 +402,17 @@ class MysqlSearchBackend extends SearchBackend { primary key `object` (`object_type`, `object_id`), fulltext key `search` (`title`, `content`) ) $engine CHARSET=utf8"; - return db_query($sql); + if (!db_query($sql)) + return false; + + // Start rebuilding the index + $this->getConfig()->set('reindex', 1); + return true; } /** * Cooperates with the cron system to automatically find content that is - * not index in the _search table and add it to the index. + * not indexed in the _search table and add it to the index. */ function IndexOldStuff() { $class = get_class(); diff --git a/include/class.signal.php b/include/class.signal.php index a98d4cf63a39979382bd668d857994db0afbeb12..f1c46e903f7aabd7cec620540d2699ad698a471c 100644 --- a/include/class.signal.php +++ b/include/class.signal.php @@ -95,7 +95,7 @@ class Signal { continue; elseif ($check && !call_user_func_array($check, array($object, $data))) continue; - call_user_func_array($callable, array($object, $data)); + call_user_func_array($callable, array($object, &$data)); } } } diff --git a/include/class.ticket.php b/include/class.ticket.php index f13fc2c5855bf2e0c51b812603733c4f234321c4..f0e7936558c91151ca37d00b015492efbe0af2f2 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -1361,31 +1361,36 @@ implements RestrictedAccess, Threadable { $this->lastupdate = SqlFunction::NOW(); $this->save(); - // Auto-assign to closing staff or last respondent - // If the ticket is closed and auto-claim is not enabled then put the - // ticket back to unassigned pool. - if ($this->isClosed() && !$cfg->autoClaimTickets()) { - $this->setStaffId(0); - } elseif(!($staff=$this->getStaff()) || !$staff->isAvailable()) { - // Ticket has no assigned staff - if auto-claim is enabled then - // try assigning it to the last respondent (if available) - // otherwise leave the ticket unassigned. - if ($cfg->autoClaimTickets() //Auto claim is enabled. - && ($lastrep=$this->getLastRespondent()) - && $lastrep->isAvailable()) { - $this->setStaffId($lastrep->getId()); //direct assignment; - } else { - $this->setStaffId(0); //unassign - last respondent is not available. - } - } // Reopen if closed AND reopenable // We're also checking autorespond flag because we don't want to // reopen closed tickets on auto-reply from end user. This is not to // confused with autorespond on new message setting - if ($autorespond && $this->isClosed() && $this->isReopenable()) + if ($autorespond && $this->isClosed() && $this->isReopenable()) { $this->reopen(); + // Auto-assign to closing staff or last respondent + // If the ticket is closed and auto-claim is not enabled then put the + // ticket back to unassigned pool. + if (!$cfg->autoClaimTickets()) { + $this->setStaffId(0); + } + elseif (!($staff = $this->getStaff()) || !$staff->isAvailable()) { + // Ticket has no assigned staff - if auto-claim is enabled then + // try assigning it to the last respondent (if available) + // otherwise leave the ticket unassigned. + if (($lastrep = $this->getLastRespondent()) + && $lastrep->isAvailable() + ) { + $this->setStaffId($lastrep->getId()); //direct assignment; + } + else { + // unassign - last respondent is not available. + $this->setStaffId(0); + } + } + } + // Figure out the user if ($this->getOwnerId() == $message->getUserId()) $user = new TicketOwner( diff --git a/include/class.topic.php b/include/class.topic.php index 776f99e896905946fb5d973b2fc8b930e1104f5a..77e85ec442243fec663224d944c02fc90a8a8c58 100644 --- a/include/class.topic.php +++ b/include/class.topic.php @@ -343,7 +343,7 @@ implements TemplateVariable { if (!$disabled && $info['disabled']) continue; if ($disabled === self::DISPLAY_DISABLED && $info['disabled']) - $n .= " — ".__("(disabled)"); + $n .= " - ".__("(disabled)"); $requested_names[$id] = $n; } diff --git a/include/class.variable.php b/include/class.variable.php index 76acb1d0f2d2d789e312ee4554db8c40b0e4b630..da249610c2de7482cc09b6a4ee283334296a85ac 100644 --- a/include/class.variable.php +++ b/include/class.variable.php @@ -27,7 +27,7 @@ class VariableReplacer { var $errors; - function VariableReplacer($start_delim='%{', $end_delim='}') { + function VariableReplacer($start_delim='(?:%{|%%7B)', $end_delim='(?:}|%7D)') { $this->start_delim = $start_delim; $this->end_delim = $end_delim; @@ -164,7 +164,8 @@ class VariableReplacer { $vars = array(); foreach($result[0] as $k => $v) { if(isset($vars[$v])) continue; - $val=$this->_resolveVar($result[1][$k]); + // Format::html_balance() may urlencode() the contents here + $val=$this->_resolveVar(rawurldecode($result[1][$k])); if($val!==false) $vars[$v] = $val; } diff --git a/include/cli/modules/deploy.php b/include/cli/modules/deploy.php index 7a795e6630a2e7a521c250fb4a71f0dd54b7db5e..74a11f02c3b103a106ac495eb225f86f7acad818 100644 --- a/include/cli/modules/deploy.php +++ b/include/cli/modules/deploy.php @@ -201,7 +201,7 @@ class Deployment extends Unpacker { if ($dryrun) continue; if (!is_dir(dirname($dst))) - mkdir(dirname($dst), 0751, true); + mkdir(dirname($dst), 0755, true); $this->copyFile($src, $dst, $hash, octdec($mode)); } } diff --git a/include/cli/modules/unpack.php b/include/cli/modules/unpack.php index add4abf5c9d68145aa7187c998ee77ebbb98b022..5635a03c98726ad7232ba066ac384b75d2ae80f6 100644 --- a/include/cli/modules/unpack.php +++ b/include/cli/modules/unpack.php @@ -171,7 +171,7 @@ class Unpacker extends Module { if ($dryrun) continue; if (!is_dir($destination)) - mkdir($destination, 0751, true); + mkdir($destination, 0755, true); $this->copyFile($file, $target, $hash); } } diff --git a/scp/tickets.php b/scp/tickets.php index f3bbe8d7c00f6409974251bf5326aa7c9ec670ff..ae4f15349c25064c85de275a402908741cc74ab6 100644 --- a/scp/tickets.php +++ b/scp/tickets.php @@ -173,7 +173,7 @@ if($_POST && !$errors): $errors['assignId']= sprintf('%s - %s', __('Invalid assignee'), __('get technical support')); - elseif ($_POST['assignId'][0]!='s' + elseif ($_POST['assignId'][0]=='s' && $dept->assignMembersOnly() && !$dept->isMember($id)) { $errors['assignId'] = sprintf('%s. %s',