Request #6875: More work on advanced search screen
authorMichael M Slusarz <slusarz@curecanti.org>
Wed, 16 Sep 2009 05:10:11 +0000 (23:10 -0600)
committerMichael M Slusarz <slusarz@curecanti.org>
Wed, 16 Sep 2009 05:10:11 +0000 (23:10 -0600)
Add ability to search by a custom defined header.
Add documentation for criteria UI object
Clean up the way we store flag searches in criteria UI object to be more
consistent with other criteria
Cleanup the way we output gettext strings on search page
Move dimp quicksearch folder ID to IMP_Search::DIMP_QUICKSEARCH

imp/docs/CHANGES
imp/js/search.js
imp/lib/Search.php
imp/lib/UI/Search.php
imp/search.php
imp/templates/javascript_defs_dimp.php

index 84bfcad..2c6b254 100644 (file)
@@ -2,6 +2,7 @@
 v5.0-git
 --------
 
+[mms] Add ability to search by a custom defined header (Request #6875).
 [mms] Improved advanced search query interface (Request #6875).
 [mms] More intelligent ordering of autocomplete search results.
 [mms] Add a mini search query interface (Request #6875).
index 8a49427..705387e 100644 (file)
@@ -7,8 +7,7 @@
 
 var ImpSearch = {
     // The following variables are defined in search.php:
-    //   loading, months, need_criteria, need_folder, need_vfolder_label,
-    //   types
+    //   months, text, types
     criteria: {},
     saved_searches: {},
     show_unsub: false,
@@ -68,6 +67,10 @@ var ImpSearch = {
                 this.insertText(c.t, c.v, c.n);
                 break;
 
+            case 'customhdr':
+                this.insertCustomHdr(c.v, c.n);
+                break;
+
             case 'size':
                 this.insertSize(c.t, c.v);
                 break;
@@ -77,7 +80,7 @@ var ImpSearch = {
                 break;
 
             case 'flag':
-                this.insertFlag(c.t);
+                this.insertFlag(c.v);
                 break;
             }
         }, this);
@@ -118,6 +121,10 @@ var ImpSearch = {
                 this.insertText(val);
                 break;
 
+            case 'customhdr':
+                this.insertCustomHdr();
+                break;
+
             case 'size':
                 this.insertSize(val);
                 break;
@@ -158,11 +165,24 @@ var ImpSearch = {
         var tmp = [
             new Element('EM').insert(this.getLabel(id)),
             new Element('INPUT', { type: 'text', size: 25 }),
-            new Element('SPAN').insert(new Element('INPUT', { checked: Boolean(not), className: 'checkbox', type: 'checkbox' })).insert(this.not_match)
+            new Element('SPAN').insert(new Element('INPUT', { checked: Boolean(not), className: 'checkbox', type: 'checkbox' })).insert(this.text.not_match)
         ];
         this.criteria[this.insertCriteria(tmp)] = { t: id };
     },
 
+    insertCustomHdr: function(text, not)
+    {
+        text = text || { h: '', s: '' };
+
+        var tmp = [
+            new Element('EM').insert(this.text.customhdr),
+            new Element('INPUT', { type: 'text', size: 25 }).setValue(text.h),
+            new Element('SPAN').insert(new Element('EM').insert(this.text.search_term + ' ')).insert(new Element('INPUT', { type: 'text', size: 25 }).setValue(text.s)),
+            new Element('SPAN').insert(new Element('INPUT', { checked: Boolean(not), className: 'checkbox', type: 'checkbox' })).insert(this.text.not_match)
+        ];
+        this.criteria[this.insertCriteria(tmp)] = { t: 'customhdr' };
+    },
+
     insertSize: function(id, size)
     {
         var tmp = [
@@ -178,7 +198,7 @@ var ImpSearch = {
         var d = (data ? new Date(data.y, data.m, data.d) : new Date()),
             tmp = [
                 new Element('EM').insert(this.getLabel(id)),
-                new Element('SPAN').insert(new Element('SPAN')).insert(new Element('A', { href: '#', className: 'calendarPopup', title: this.dateselection }).insert(new Element('SPAN', { className: 'searchuiImg searchuiCalendar' })))
+                new Element('SPAN').insert(new Element('SPAN')).insert(new Element('A', { href: '#', className: 'calendarPopup', title: this.text.dateselection }).insert(new Element('SPAN', { className: 'searchuiImg searchuiCalendar' })))
             ];
         this.replaceDate(this.insertCriteria(tmp), id, { y: d.getFullYear(), m: d.getMonth(), d: d.getDate() });
     },
@@ -195,7 +215,7 @@ var ImpSearch = {
     insertFlag: function(id)
     {
         var tmp = [
-            new Element('EM').insert(this.flag),
+            new Element('EM').insert(this.text.flag),
             this.getLabel(id).slice(0, -2)
         ];
         this.criteria[this.insertCriteria(tmp)] = { t: id };
@@ -206,7 +226,9 @@ var ImpSearch = {
         var data = [], tmp;
 
         if (!this._getAll().findAll(function(i) { return i.checked; }).size()) {
-            alert(this.need_folder);
+            alert(this.text.need_folder);
+        } else if ($F('vfolder_save') && $F('vfolder_label').empty()) {
+            alert(this.text.need_vfolder_label);
         } else {
             tmp = $('search_criteria_table').childElements().pluck('id');
             if (tmp.size()) {
@@ -222,6 +244,11 @@ var ImpSearch = {
                         data.push(this.criteria[c]);
                         break;
 
+                    case 'customhdr':
+                        this.criteria[c].v = { h: $F($(c).down('INPUT')), s: $F($(c).down('INPUT', 1)) };
+                        data.push(this.criteria[c]);
+                        break;
+
                     case 'size':
                         tmp2 = Number($F($(c).down('INPUT')));
                         if (!isNaN(tmp2)) {
@@ -243,7 +270,7 @@ var ImpSearch = {
                 $('criteria_form').setValue(data.toJSON());
                 $('search_form').submit();
             } else {
-                alert(this.need_criteria);
+                alert(this.text.need_criteria);
             }
         }
     },
@@ -280,7 +307,7 @@ var ImpSearch = {
             case 'link_sub':
                 tmp = this._getAll();
                 this.show_unsub = !this.show_unsub;
-                $('search_folders_hdr').next('DIV').update(this.loading);
+                $('search_folders_hdr').next('DIV').update(this.text.loading);
                 new Ajax.Request($('search_form').readAttribute('action'), {
                     parameters: { show_unsub: Number(this.show_unsub) },
                     onComplete: this._showFoldersCallback.bind(this, tmp)
index 33e35f1..07f9b76 100644 (file)
@@ -8,16 +8,45 @@
  *
  * $_SESSION['imp']['search'] = array(
  *     'id_1' => array(
- *         'c' => (array) List of search criteria. For virtual folders, this
- *                data is stored in the preferences,
+ *         'c' => (array) List of search criteria (the IMP-specific data
+ *                structure that allows recreation of the search query on the
+ *                search page). For virtual folders, this data is stored in
+ *                the preferences,
  *         'f' => (array) List of folders to search,
- *         'l' => (string) Description of search,
+ *         'l' => (string) Description (label) of search,
  *         'q' => (Horde_Imap_Client_Search_Query) [serialized],
  *         'v' => (boolean) True if this is a Virtual Folder
  *     ),
  *     ....
  * );
  *
+ * The format of the 'c' (search criteria) array is as folows:
+ * array(
+ *     stdClass object {
+ *         't' => (string) 'Type' - The criteria type
+ *                Values: header, customhdr, body, text, date, size, flag
+ *         'v' => (mixed) 'Value' - The data used to build the search
+ *                'header' - (string) The value to search for in the header
+ *                'customhdr' - (stdClass object) Contains 2 elements:
+ *                         'h' - (string) The header name
+ *                         's' - (string) The search string
+ *                'body' - (string) The value to search for in the body
+ *                'text' - (string) The value to search for in the entire
+ *                         message
+ *                'date' - (stdClass object) Contains 3 elements:
+ *                         'y' - (integer) The search year
+ *                         'm' - (integer) The search month (is 1 less than
+ *                               the actual month)
+ *                         'd' - (integer) The search day
+ *                'size' - (integer) The search size in bytes
+ *                'flag' - (string) The flag to search for
+ *         'n' => (boolean) 'Not' - Should we do a not search?
+ *                Only used for the following types: header, customhdr, body,
+ *                text
+ *     },
+ *     ...
+ * )
+ *
  * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
  *
  * See the enclosed file COPYING for license information (GPL). If you
@@ -31,8 +60,9 @@ class IMP_Search
     /* The mailbox search prefix. */
     const MBOX_PREFIX = 'impsearch\0';
 
-    /* The basic search mailbox name. */
+    /* The special search mailbox names. */
     const BASIC_SEARCH = 'impbsearch';
+    const DIMP_QUICKSEARCH = 'dimpqsearch';
 
     /* Bitmask constants for listQueries(). */
     const LIST_SEARCH = 1;
@@ -130,7 +160,7 @@ class IMP_Search
                 'type' => 'header',
                 'not' => true
             ),
-            'custom' => array(
+            'customhdr' => array(
                 'label' => _("Custom Header"),
                 'type' => 'customhdr',
                 'not' => true
@@ -629,23 +659,32 @@ class IMP_Search
         $text_array = array();
         foreach ($criteria as $rule) {
             $field = $rule->t;
-
-            if (isset($flagfields[$field])) {
-                $text_array[] = sprintf(_("flagged \"%s\""), $flagfields[$field]);
-            } else {
-                switch ($searchfields[$field]['type']) {
-                case 'date':
-                    $text_array[] = sprintf("%s '%s'", $searchfields[$field]['label'], strftime("%x", mktime(0, 0, 0, $rule->v->m + 1, $rule->v->d, $rule->v->y)));
-                    break;
-
-                case 'size':
-                    $text_array[] = $searchfields[$field]['label'] . ' ' . ($rule->v / 1024);
-                    break;
-
-                default:
-                    $text_array[] = sprintf("%s for '%s'", $searchfields[$field]['label'], ((!empty($rule->n)) ? _("not") . ' ' : '') . $rule->v);
-                    break;
+            $type = isset($searchfields[$field]['type'])
+                ? $searchfields[$field]['type']
+                : $field;
+
+            switch ($searchfields[$field]['type']) {
+            case 'flag':
+                if (isset($flagfields[$rule->v])) {
+                    $text_array[] = sprintf(_("flagged \"%s\""), $flagfields[$field]);
                 }
+                break;
+
+            case 'date':
+                $text_array[] = sprintf("%s '%s'", $searchfields[$field]['label'], strftime("%x", mktime(0, 0, 0, $rule->v->m + 1, $rule->v->d, $rule->v->y)));
+                break;
+
+            case 'size':
+                $text_array[] = $searchfields[$field]['label'] . ' ' . ($rule->v / 1024);
+                break;
+
+            case 'customhdr':
+                $text_array[] = sprintf("%s for '%s'", $rule->v->h, ((!empty($rule->n)) ? _("not") . ' ' : '') . $rule->v->s);
+                break;
+
+            default:
+                $text_array[] = sprintf("%s for '%s'", $searchfields[$field]['label'], ((!empty($rule->n)) ? _("not") . ' ' : '') . $rule->v);
+                break;
             }
         }
 
index 34feb3a..7989172 100644 (file)
@@ -32,43 +32,55 @@ class IMP_UI_Search
         foreach ($search as $rule) {
             $ob = new Horde_Imap_Client_Search_Query();
 
-            if (isset($flag_fields[$rule->t])) {
-                $val = $imp_flags->parseFormId($rule->t);
-                $ob->flag($val['flag'], $val['set']);
-                $search_array[] = $ob;
-            } else {
-                /* Ignore unknown types. */
-                switch ($search_fields[$rule->t]['type']) {
-                case 'header':
-                    if (!empty($rule->v)) {
-                        $ob->headerText($rule->t, $rule->v, !empty($rule->n));
-                        $search_array[] = $ob;
-                    }
-                    break;
-
-                case 'body':
-                case 'text':
-                    if (!empty($rule->v)) {
-                        $ob->text($rule->c, $search_fields[$rule->t]['type'] == 'body', !empty($rule->n));
-                        $search_array[] = $ob;
-                    }
-                    break;
-
-                case 'date':
-                    if (!empty($rule->v)) {
-                        $date = new Horde_Date(array('year' => $rule->v->y, 'month' => $rule->v->m + 1, 'mday' => $rule->v->d));
-                        $ob->dateSearch($date, ($rule->t == 'date_on') ? Horde_Imap_Client_Search_Query::DATE_ON : (($rule->t == 'date_until') ? Horde_Imap_Client_Search_Query::DATE_BEFORE : Horde_Imap_Client_Search_Query::DATE_SINCE));
-                        $search_array[] = $ob;
-                    }
-                    break;
-
-                case 'size':
-                    if (!empty($rule->v)) {
-                        $ob->size(intval($rule->v), $rule->t == 'size_larger');
-                        $search_array[] = $ob;
-                    }
-                    break;
+            $type = isset($search_fields[$rule->t]['type'])
+                ? $search_fields[$rule->t]['type']
+                : $rule->t;
+
+            switch ($type) {
+            case 'flag':
+                if (isset($flag_fields[$rule->v])) {
+                    $val = $imp_flags->parseFormId($rule->t);
+                    $ob->flag($val['flag'], $val['set']);
+                    $search_array[] = $ob;
+                }
+                break;
+
+            case 'header':
+                if (!empty($rule->v)) {
+                    $ob->headerText($rule->t, $rule->v, !empty($rule->n));
+                    $search_array[] = $ob;
+                }
+                break;
+
+            case 'customhdr':
+                if (!empty($rule->v)) {
+                    $ob->headerText($rule->v->h, $rule->v->s, !empty($rule->n));
+                    $search_array[] = $ob;
+                }
+                break;
+
+            case 'body':
+            case 'text':
+                if (!empty($rule->v)) {
+                    $ob->text($rule->c, $search_fields[$rule->t]['type'] == 'body', !empty($rule->n));
+                    $search_array[] = $ob;
+                }
+                break;
+
+            case 'date':
+                if (!empty($rule->v)) {
+                    $date = new Horde_Date(array('year' => $rule->v->y, 'month' => $rule->v->m + 1, 'mday' => $rule->v->d));
+                    $ob->dateSearch($date, ($rule->t == 'date_on') ? Horde_Imap_Client_Search_Query::DATE_ON : (($rule->t == 'date_until') ? Horde_Imap_Client_Search_Query::DATE_BEFORE : Horde_Imap_Client_Search_Query::DATE_SINCE));
+                    $search_array[] = $ob;
+                }
+                break;
+
+            case 'size':
+                if (!empty($rule->v)) {
+                    $ob->size(intval($rule->v), $rule->t == 'size_larger');
+                    $search_array[] = $ob;
                 }
+                break;
             }
         }
 
@@ -108,7 +120,8 @@ class IMP_UI_Search
 
         if ($flag) {
             $tmp = new stdClass;
-            $tmp->t = $flag;
+            $tmp->t = 'flag';
+            $tmp->v = $flag;
             $c_list[] = $tmp;
         }
 
index 54f45e0..a9b8a0a 100644 (file)
@@ -151,15 +151,22 @@ $t->set('f_fields', $f_fields);
 
 Horde_UI_JsCalendar::init();
 
+/* Gettext strings for this page. */
+$gettext_strings = array(
+    'customhdr' => _("Custom Header:"),
+    'dateselection' => _("Date Selection"),
+    'flag' => _("Flag:"),
+    'loading' => _("Loading..."),
+    'need_criteria' => _("Please select at least one search criteria."),
+    'need_folder' => _("Please select at least one folder to search."),
+    'need_vfolder_label' => _("Virtual Folders require a label."),
+    'not_match' => _("Do NOT Match"),
+    'search_term' => _("Search Term:")
+);
+
 Horde::addInlineScript(array(
-    'ImpSearch.dateselection = ' . Horde_Serialize::serialize(_("Date Selection"), Horde_Serialize::JSON, $charset),
-    'ImpSearch.flag = ' . Horde_Serialize::serialize(_("Flag:"), Horde_Serialize::JSON, $charset),
-    'ImpSearch.loading = ' . Horde_Serialize::serialize(_("Loading..."), Horde_Serialize::JSON, $charset),
     'ImpSearch.months = ' . Horde_Serialize::serialize(Horde_UI_JsCalendar::months(), Horde_Serialize::JSON, $charset),
-    'ImpSearch.need_criteria = ' . Horde_Serialize::serialize(_("Please select at least one search criteria."), Horde_Serialize::JSON, $charset),
-    'ImpSearch.need_folder = ' . Horde_Serialize::serialize(_("Please select at least one folder to search."), Horde_Serialize::JSON, $charset),
-    'ImpSearch.need_vfolder_label = ' . Horde_Serialize::serialize(_("Virtual Folders require a label."), Horde_Serialize::JSON, $charset),
-    'ImpSearch.not_match = ' . Horde_Serialize::serialize(_("Do NOT Match"), Horde_Serialize::JSON, $charset),
+    'ImpSearch.text = ' . Horde_Serialize::serialize($gettext_strings, Horde_Serialize::JSON, $charset),
     'ImpSearch.types = ' . Horde_Serialize::serialize($types, Horde_Serialize::JSON, $charset)
 ));
 Horde::addInlineScript($on_domload, 'dom');
index 2de28ce..e1ae82a 100644 (file)
@@ -71,7 +71,7 @@ $code['conf'] = array_filter(array(
     'popup_height' => 610,
     'popup_width' => 820,
     'preview_pref' => intval($GLOBALS['prefs']->getValue('dimp_show_preview')),
-    'qsearchid' => IMP_Search::MBOX_PREFIX . 'dimpqsearch',
+    'qsearchid' => IMP_Search::MBOX_PREFIX . IMP_Search::DIMP_QUICKSEARCH,
     'qsearchfield' => $GLOBALS['prefs']->getValue('dimp_qsearch_field'),
     'refresh_time' => intval($GLOBALS['prefs']->getValue('refresh_time')),
     'searchprefix' => IMP_Search::MBOX_PREFIX,