From b089b7affd4a0723846073e1d44f0cc82aad7cf9 Mon Sep 17 00:00:00 2001
From: Peter Rotich <peter@enhancesoft.com>
Date: Wed, 21 Mar 2018 20:22:49 +0000
Subject: [PATCH] Saved Searches : Save old records

Commit 5be0de0d introduced the idea of moving-up old saved searches to make
room for Custom Queues but failed to account for cases where the system
has more than 30 saved searches.

This commit addresses the issue by just re-inserting the old records after
queues are imported. It also adds validates & reformat the search criteria
to make the expected format.
---
 include/class.json.php                        | 32 +++++++++++++------
 include/class.queue.php                       | 24 ++++++++------
 include/mysqli.php                            |  2 +-
 .../streams/core/934b8db8-ad9d0a5f.task.php   | 24 ++++++++++++--
 4 files changed, 59 insertions(+), 23 deletions(-)

diff --git a/include/class.json.php b/include/class.json.php
index ad5ac65c1..66edb7623 100644
--- a/include/class.json.php
+++ b/include/class.json.php
@@ -21,25 +21,39 @@
 include_once "JSON.php";
 
 class JsonDataParser {
-    function parse($stream) {
+    function parse($stream, $tidy=false) {
         if (is_resource($stream)) {
             $contents = '';
             while (!feof($stream))
                 $contents .= fread($stream, 8192);
         } else
             $contents = $stream;
+
+        if ($contents && $tidy)
+            $contents = self::tidy($contents);
+
         return self::decode($contents);
     }
 
-    function decode($contents) {
-        if (function_exists("json_decode")) {
-            return json_decode($contents, true);
-        } else {
-            # Create associative arrays rather than 'objects'
-            $decoder = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
-            return $decoder->decode($contents);
-        }
+    static function decode($contents, $assoc=true) {
+        if (function_exists("json_decode"))
+            return json_decode($contents, $assoc);
+
+        $decoder = new Services_JSON($assoc ? SERVICES_JSON_LOOSE_TYPE : 0);
+        return $decoder->decode($contents);
     }
+
+    static function tidy($content) {
+
+        // Clean up doubly quoted JSON
+        $content = str_replace(
+                array(':"{', '}"', '\"'),
+                array(':{', '}', '"'),
+                $content);
+        // return trimmed content.
+        return trim($content);
+    }
+
     function lastError() {
         if (function_exists("json_last_error")) {
             $errors = array(
diff --git a/include/class.queue.php b/include/class.queue.php
index b037d4eee..284853a44 100644
--- a/include/class.queue.php
+++ b/include/class.queue.php
@@ -123,7 +123,9 @@ class CustomQueue extends VerySimpleModel {
                 && !isset($this->criteria['conditions'])
             ) {
                 // TODO: Upgrade old ORM path names
-                $this->criteria = $this->isolateCriteria($this->criteria);
+                // Parse criteria out of JSON if any.
+                $this->criteria = self::isolateCriteria($this->criteria,
+                        $this->getRoot());
             }
         }
         $criteria = $this->criteria ?: array();
@@ -460,11 +462,13 @@ class CustomQueue extends VerySimpleModel {
      * field name being search, the method used for searhing, and the method-
      * specific data entered in the UI.
      */
-    function isolateCriteria($criteria, $root=null) {
-        $searchable = static::getSearchableFields($root ?: $this->getRoot());
-        $items = array();
+    static function isolateCriteria($criteria, $base='Ticket') {
+
         if (!is_array($criteria))
             return null;
+
+        $items = array();
+        $searchable = static::getSearchableFields($base);
         foreach ($criteria as $k=>$v) {
             if (substr($k, -7) === '+method') {
                 list($name,) = explode('+', $k, 2);
@@ -477,9 +481,8 @@ class CustomQueue extends VerySimpleModel {
 
                 // Lookup the field to search this condition
                 list($label, $field) = $searchable[$name];
-
-                // Get the search method and value
-                $method = $v;
+                // Get the search method
+                $method = is_array($v) ? key($v) : $v;
                 // Not all search methods require a value
                 $value = $criteria["{$name}+{$method}"];
 
@@ -1154,7 +1157,8 @@ class CustomQueue extends VerySimpleModel {
         }
         else {
             $this->config = JsonDataEncoder::encode([
-                'criteria' => $this->isolateCriteria($form->getClean()),
+                'criteria' => self::isolateCriteria($form->getClean(),
+                    $this->getRoot()),
                 'conditions' => $conditions,
             ]);
             // Clear currently set criteria.and conditions.
@@ -1650,8 +1654,8 @@ class QueueColumnCondition {
      * field name being search, the method used for searhing, and the method-
      * specific data entered in the UI.
      */
-    static function isolateCriteria($criteria, $root='Ticket') {
-        $searchable = CustomQueue::getSearchableFields($root);
+    static function isolateCriteria($criteria, $base='Ticket') {
+        $searchable = CustomQueue::getSearchableFields($base);
         foreach ($criteria as $k=>$v) {
             if (substr($k, -7) === '+method') {
                 list($name,) = explode('+', $k, 2);
diff --git a/include/mysqli.php b/include/mysqli.php
index 998d28659..efb459f33 100644
--- a/include/mysqli.php
+++ b/include/mysqli.php
@@ -234,7 +234,7 @@ function db_fetch_field($res) {
     return ($res) ? $res->fetch_field() : NULL;
 }
 
-function db_assoc_array($res, $mode=false) {
+function db_assoc_array($res, $mode=MYSQLI_ASSOC) {
     $result = array();
     if($res && db_num_rows($res)) {
         while ($row=db_fetch_array($res, $mode))
diff --git a/include/upgrader/streams/core/934b8db8-ad9d0a5f.task.php b/include/upgrader/streams/core/934b8db8-ad9d0a5f.task.php
index 29a3a4b8c..886bcf6eb 100644
--- a/include/upgrader/streams/core/934b8db8-ad9d0a5f.task.php
+++ b/include/upgrader/streams/core/934b8db8-ad9d0a5f.task.php
@@ -9,14 +9,32 @@ class QueueSortCreator extends MigrationTask {
         foreach ($columns as $C) {
             QueueColumn::__create($C);
         }
-        // Make room for the new queues starting at ID 1
-        $id = new SqlField('id');
-        CustomQueue::objects()->update(['id' => $id->plus(30)]);
+
+        // Save old records
+        $old = db_assoc_array(db_query('SELECT * FROM '.QUEUE_TABLE));
+        // Truncate Queue table - make room for the new queues starting at ID 1
+        db_query('TRUNCATE TABLE '.QUEUE_TABLE);
         $queues = $i18n->getTemplate('queue.yaml')->getData();
         foreach ($queues as $C) {
             CustomQueue::__create($C);
         }
 
+        // Re-insert old saved searches
+        foreach ($old ?: array() as $row) {
+            // Only save entries with "valid" criteria
+            if (!$row['title']
+                    || !($config = JsonDataParser::parse($row['config'], true))
+                    || !($criteria = CustomQueue::isolateCriteria($criteria)))
+                continue;
+
+            $row['config'] = JsonDataEncoder::encode(array(
+                        'criteria' => $criteria, 'conditions' => array()));
+            $row['root'] = 'T';
+            CustomQueue::__create(array_intersect_key($row, array_flip(
+                            array('staff_id', 'title', 'config', 'flags',
+                                'root', 'created', 'updated'))));
+        }
+
         $columns = $i18n->getTemplate('queue_sort.yaml')->getData();
         foreach ($columns as $C) {
             QueueSort::__create($C);
-- 
GitLab