From 9436536d33cd6df191c5096130411fefd05101df Mon Sep 17 00:00:00 2001 From: Jared Hancock <jared@osticket.com> Date: Sat, 31 Dec 2016 17:00:59 -0600 Subject: [PATCH] queue: Add conditions to whole row in queue This allows a queue to define conditions which apply to the entire row, so that all the columns in a row can react to a particular condition. For instance, rows in the ticket queue for tickets due in the near future or assigned to a particular team could be formatted a certain way. --- include/ajax.search.php | 11 +- include/class.queue.php | 143 ++++++++++++------ include/staff/queue.inc.php | 60 +++++++- .../templates/queue-column-condition.tmpl.php | 6 +- include/staff/templates/queue-column.tmpl.php | 5 +- 5 files changed, 168 insertions(+), 57 deletions(-) diff --git a/include/ajax.search.php b/include/ajax.search.php index 8d596e4fe..ccd861d1b 100644 --- a/include/ajax.search.php +++ b/include/ajax.search.php @@ -291,8 +291,13 @@ class SearchAjaxAPI extends AjaxController { if (!$thisstaff) { Http::response(403, 'Agent login is required'); } - elseif (!isset($_GET['field']) || !isset($_GET['id']) || !isset($_GET['colid'])) { - Http::response(400, '`field`, `id`, and `colid` parameters required'); + elseif (!isset($_GET['field']) || !isset($_GET['id']) + || !isset($_GET['object_id']) + ) { + Http::response(400, '`field`, `id`, and `object_id` parameters required'); + } + elseif (!is_numeric($_GET['object_id'])) { + Http::response(400, '`object_id` should be an integer'); } $fields = SavedSearch::getSearchableFields('Ticket'); if (!isset($fields[$_GET['field']])) { @@ -304,7 +309,7 @@ class SearchAjaxAPI extends AjaxController { // Ensure `name` is preserved $field_name = $_GET['field']; $id = $_GET['id']; - $column = new QueueColumn(array('id' => $_GET['colid'])); + $object_id = $_GET['object_id']; $condition = new QueueColumnCondition(); include STAFFINC_DIR . 'templates/queue-column-condition.tmpl.php'; } diff --git a/include/class.queue.php b/include/class.queue.php index f0994e7b7..e63dbf9e7 100644 --- a/include/class.queue.php +++ b/include/class.queue.php @@ -63,6 +63,7 @@ class CustomQueue extends VerySimpleModel { const FLAG_INHERIT_EVERYTHING = 0x78; // Maskf or all INHERIT flags var $criteria; + var $_conditions; static function queues() { return parent::objects()->filter(array( @@ -108,13 +109,19 @@ class CustomQueue extends VerySimpleModel { $this->criteria = is_string($this->config) ? JsonDataParser::decode($this->config) : $this->config; - // Auto-upgrade criteria to new format - if ($old) { + // Auto-upgrade v1.10 saved-search criteria to new format + // But support new style with `conditions` support + if ($old && is_array($this->criteria) + && !isset($this->criteria['conditions']) + ) { // TODO: Upgrade old ORM path names $this->criteria = $this->isolateCriteria($this->criteria); } } $criteria = $this->criteria ?: array(); + // Support new style with `conditions` support + if (isset($criteria['criteria'])) + $criteria = $criteria['criteria']; if ($include_parent && $this->parent_id && $this->parent) { $criteria = array_merge($this->parent->getCriteria(true), $criteria); @@ -479,6 +486,22 @@ class CustomQueue extends VerySimpleModel { return $items; } + function getConditions() { + if (!isset($this->_conditions)) { + $this->getCriteria(); + $conds = array(); + if (is_array($this->criteria) + && isset($this->criteria['conditions']) + ) { + $conds = $this->criteria['conditions']; + } + foreach ($conds as $C) + if ($T = QueueColumnCondition::fromJson($C)) + $this->_conditions[] = $T; + } + return $this->_conditions; + } + function getColumns($use_template=false) { if ($this->columns_id && ($q = CustomQueue::lookup($this->columns_id)) @@ -920,15 +943,23 @@ class CustomQueue extends VerySimpleModel { } } + list($this->_conditions, $conditions) + = QueueColumn::getConditionsFromPost($vars, $this->id, $this->getRoot()); + // TODO: Move this to SavedSearch::update() and adjust // AjaxSearch::_saveSearch() $form = $form ?: $this->getForm($vars); - if (!$vars || !$form->isValid()) { + if (!$vars) { + $errors['criteria'] = __('No criteria specified'); + } + elseif (!$form->isValid()) { $errors['criteria'] = __('Validation errors exist on criteria'); } else { - $this->config = JsonDataEncoder::encode( - $this->isolateCriteria($form->getClean())); + $this->config = JsonDataEncoder::encode([ + 'criteria' => $this->isolateCriteria($form->getClean()), + 'conditions' => $conditions, + ]); } return 0 === count($errors); @@ -1436,8 +1467,7 @@ class QueueColumnCondition { } function render($row, $text, &$styles=array()) { - $annotation = $this->getAnnotationName(); - if ($V = $row[$annotation]) { + if ($V = $row[$this->getAnnotationName()]) { foreach ($this->getProperties() as $css=>$value) { $field = QueueColumnConditionProperty::getField($css); $field->value = $value; @@ -1817,7 +1847,7 @@ extends VerySimpleModel { return $this->_annotations; } - function getConditions() { + function getConditions($include_queue=true) { if (!isset($this->_conditions)) { $this->_conditions = array(); if ($this->conditions @@ -1827,6 +1857,12 @@ extends VerySimpleModel { if ($T = QueueColumnCondition::fromJson($C)) $this->_conditions[] = $T; } + // Support row-spanning conditions + if ($include_queue && ($q = $this->getQueue()) + && ($q_conds = $q->getConditions()) + ) { + $this->_conditions = array_merge($this->_conditions, $q_conds); + } } return $this->_conditions; } @@ -1863,52 +1899,63 @@ extends VerySimpleModel { // Do the conditions $this->_conditions = $conditions = array(); if (isset($vars['conditions'])) { - foreach (@$vars['conditions'] as $i=>$id) { - if ($vars['condition_column'][$i] != $this->id) - // Not a condition for this column - continue; - // Determine the criteria - $name = $vars['condition_field'][$i]; - $fields = CustomQueue::getSearchableFields($root); - if (!isset($fields[$name])) - // No such field exists for this queue root type - continue; - $parts = CustomQueue::getSearchField($fields[$name], $name); - $search_form = new SimpleForm($parts, $vars, array('id' => $id)); - $search_form->getField("{$name}+search")->value = true; - $crit = $search_form->getClean(); - // Check the box to enable searching on the field - $crit["{$name}+search"] = true; - - // Isolate only the critical parts of the criteria - $crit = QueueColumnCondition::isolateCriteria($crit); - - // Determine the properties - $props = array(); - foreach ($vars['properties'] as $i=>$cid) { - if ($cid != $id) - // Not a property for this condition - continue; - - // Determine the property configuration - $prop = $vars['property_name'][$i]; - if (!($F = QueueColumnConditionProperty::getField($prop))) { - // Not a valid property - continue; - } - $prop_form = new SimpleForm(array($F), $vars, array('id' => $cid)); - $props[$prop] = $prop_form->getField($prop)->getClean(); - } - $json = array('crit' => $crit, 'prop' => $props); - $this->_conditions[] = QueueColumnCondition::fromJson($json); - $conditions[] = $json; - } + list($this->_conditions, $conditions) + = self::getConditionsFromPost($vars, $this->id, $root); } // Store as JSON array $this->annotations = JsonDataEncoder::encode($annotations); $this->conditions = JsonDataEncoder::encode($conditions); } + + static function getConditionsFromPost(array $vars, $myid, $root='Ticket') { + $condition_objects = $conditions = array(); + + if (!isset($vars['conditions'])) + return array($condition_objects, $conditions); + + foreach (@$vars['conditions'] as $i=>$id) { + if ($vars['condition_column'][$i] != $myid) + // Not a condition for this column + continue; + // Determine the criteria + $name = $vars['condition_field'][$i]; + $fields = CustomQueue::getSearchableFields($root); + if (!isset($fields[$name])) + // No such field exists for this queue root type + continue; + $parts = CustomQueue::getSearchField($fields[$name], $name); + $search_form = new SimpleForm($parts, $vars, array('id' => $id)); + $search_form->getField("{$name}+search")->value = true; + $crit = $search_form->getClean(); + // Check the box to enable searching on the field + $crit["{$name}+search"] = true; + + // Isolate only the critical parts of the criteria + $crit = QueueColumnCondition::isolateCriteria($crit); + + // Determine the properties + $props = array(); + foreach ($vars['properties'] as $i=>$cid) { + if ($cid != $id) + // Not a property for this condition + continue; + + // Determine the property configuration + $prop = $vars['property_name'][$i]; + if (!($F = QueueColumnConditionProperty::getField($prop))) { + // Not a valid property + continue; + } + $prop_form = new SimpleForm(array($F), $vars, array('id' => $cid)); + $props[$prop] = $prop_form->getField($prop)->getClean(); + } + $json = array('crit' => $crit, 'prop' => $props); + $condition_objects[] = QueueColumnCondition::fromJson($json); + $conditions[] = $json; + } + return array($condition_objects, $conditions); + } } class QueueColumnGlue diff --git a/include/staff/queue.inc.php b/include/staff/queue.inc.php index 25ef7faa9..ea01dd558 100644 --- a/include/staff/queue.inc.php +++ b/include/staff/queue.inc.php @@ -45,6 +45,8 @@ else { <?php echo __('Columns'); ?></a></li> <li><a href="#sorting-tab"><i class="icon-sort-by-attributes"></i> <?php echo __('Sort'); ?></a></li> + <li><a href="#conditions-tab"><i class="icon-exclamation-sign"></i> + <?php echo __('Conditions'); ?></a></li> <li><a href="#preview-tab"><i class="icon-eye-open"></i> <?php echo __('Preview'); ?></a></li> </ul> @@ -311,7 +313,8 @@ var Q = setInterval(function() { $(function() { $('#preview-tab').on('afterShow', function() { $.ajax({ - url: 'ajax.php/queue/preview', + url: 'ajax.php/queue<?php + if (isset($queue->id)) echo "/{$queue->id}"; ?>/preview', type: 'POST', data: $('#save').serializeArray(), success: function(html) { @@ -321,6 +324,61 @@ var Q = setInterval(function() { }); }); </script> + </div> + + <div class="hidden tab_content" id="conditions-tab"> + <div style="margin-bottom: 15px"><?php echo __("Conditions are used to change the view of the data in a row based on some conditions of the data. For instance, a column might be shown bold if some condition is met."); + ?> <?php echo __("These conditions apply to an entire row in the queue."); + ?></div> + <div class="conditions"> +<?php +if ($queue->getConditions()) { + $fields = CustomQueue::getSearchableFields($queue->getRoot()); + foreach ($queue->getConditions() as $i=>$condition) { + $id = QueueColumnCondition::getUid(); + list($label, $field) = $condition->getField(); + $field_name = $condition->getFieldName(); + $object_id = $queue->id; + include STAFFINC_DIR . 'templates/queue-column-condition.tmpl.php'; + } +} ?> + + <div style="margin-top: 10px; padding-top: 10px; border-top: 1px solid #bbb"> + <i class="icon-plus-sign"></i> + <select class="add-condition"> + <option value="0">— <?php echo __("Add a condition"); ?> —</option> +<?php + foreach (CustomQueue::getSearchableFields('Ticket') as $path=>$f) { + list($label) = $f; + echo sprintf('<option value="%s">%s</option>', $path, Format::htmlchars($label)); + } +?> + </select> + <script> + $(function() { + var queueid = <?php echo $queue->id ?: 0; ?>, + nextid = <?php echo 1000 + QueueColumnCondition::getUid(); ?>; + $('#conditions-tab select.add-condition').change(function() { + var $this = $(this), + container = $this.closest('div'), + selected = $this.find(':selected'); + if (selected.val() <= 0) + return; + $.ajax({ + url: 'ajax.php/queue/condition/add', + data: { field: selected.val(), object_id: queueid, id: nextid }, + dataType: 'html', + success: function(html) { + $(html).insertBefore(container); + $this.find('[value=0]').select(); + nextid++; + } + }); + }); + }); + </script> + </div> + </div> </div> diff --git a/include/staff/templates/queue-column-condition.tmpl.php b/include/staff/templates/queue-column-condition.tmpl.php index bc69978c8..59421ed46 100644 --- a/include/staff/templates/queue-column-condition.tmpl.php +++ b/include/staff/templates/queue-column-condition.tmpl.php @@ -3,13 +3,13 @@ // // $field - field for the condition (Ticket / Last Update) // $condition - <QueueColumnCondition> instance for this condition -// $column - <QueueColumn> to which the condition belongs +// $object_id - ID# of the object to which the condition belongs // $id - temporary ID number for the condition // $field_name - search path / name for the field ?> <div class="condition"> <input name="conditions[]" value="<?php echo $id; ?>" type="hidden" /> - <input name="condition_column[]" value="<?php echo $column->getId(); ?>" + <input name="condition_column[]" value="<?php echo $object_id; ?>" type="hidden" /> <input name="condition_field[]" value="<?php echo $field_name; ?>" type="hidden" /> <div class="pull-right"> @@ -17,7 +17,7 @@ return false; "><i class="icon-trash"></i></a> </div> - <?php echo $label ?: $field->getLabel(); ?> + <div><strong><?php echo $label ?: $field->getLabel(); ?></div></strong> <div class="advanced-search"> <?php $parts = CustomQueue::getSearchField(array($label, $field), $field_name); diff --git a/include/staff/templates/queue-column.tmpl.php b/include/staff/templates/queue-column.tmpl.php index 851d374cd..806825ad4 100644 --- a/include/staff/templates/queue-column.tmpl.php +++ b/include/staff/templates/queue-column.tmpl.php @@ -117,12 +117,13 @@ foreach (Internationalization::sortKeyedList($annotations) as $class=>$desc) { ?></div> <div class="conditions"> <?php -if ($column->getConditions()) { +if ($column->getConditions(false)) { $fields = CustomQueue::getSearchableFields($root); foreach ($column->getConditions() as $i=>$condition) { $id = QueueColumnCondition::getUid(); list($label, $field) = $condition->getField(); $field_name = $condition->getFieldName(); + $object_id = $column->getId(); include STAFFINC_DIR . 'templates/queue-column-condition.tmpl.php'; } } ?> @@ -147,7 +148,7 @@ if ($column->getConditions()) { selected = $this.find(':selected'); $.ajax({ url: 'ajax.php/queue/condition/add', - data: { field: selected.val(), colid: colid, id: nextid }, + data: { field: selected.val(), object_id: colid, id: nextid }, dataType: 'html', success: function(html) { $(html).insertBefore(container); -- GitLab