From 8267407b9326e82ce455e88ebd87d5841e0c3268 Mon Sep 17 00:00:00 2001 From: Michael M Slusarz Date: Mon, 20 Sep 2010 00:38:18 -0600 Subject: [PATCH] Create search filters UI in IMP. These are generic search queries that can be applied to any mailbox. Presently, these filters are only usable in IMP. The dimp interface has not yet been written. See, e.g., Request #8659. --- imp/config/prefs.php.dist | 10 ++ imp/docs/CHANGES | 2 + imp/js/mailbox.js | 29 +++++- imp/js/search.js | 13 ++- imp/js/searchesprefs.js | 11 ++- imp/lib/Prefs/Ui.php | 75 +++++++++++---- imp/lib/Search.php | 155 +++++++++++++++++++++++++----- imp/lib/Search/Filter.php | 58 +++++++++++ imp/lib/Search/Filter/Bulk.php | 69 +++++++++++++ imp/mailbox.php | 30 +++++- imp/search.php | 122 +++++++++++++++-------- imp/templates/imp/mailbox/form_start.html | 1 + imp/templates/imp/mailbox/navbar.html | 37 ++++--- imp/templates/imp/search/search.html | 24 +++-- imp/templates/prefs/searches.html | 49 +++++++++- imp/themes/screen.css | 2 +- 16 files changed, 571 insertions(+), 116 deletions(-) create mode 100644 imp/lib/Search/Filter.php create mode 100644 imp/lib/Search/Filter/Bulk.php diff --git a/imp/config/prefs.php.dist b/imp/config/prefs.php.dist index bae00245e..512f8abf1 100644 --- a/imp/config/prefs.php.dist +++ b/imp/config/prefs.php.dist @@ -397,6 +397,16 @@ $_prefs['vfolder'] = array( 'value' => 'a:1:{i:0;C:25:"IMP_Search_Vfolder_Vinbox":175:{a:3:{s:1:"c";a:2:{i:0;C:23:"IMP_Search_Element_Flag":24:{[1,{"f":"\\seen","s":0}]}i:1;C:23:"IMP_Search_Element_Flag":27:{[1,{"f":"\\deleted","s":0}]}}s:1:"e";i:1;s:1:"v";i:1;}}}' ); +$_prefs['filter'] = array( + // 'value' => serialize(array( + // // Bulk filter, enabled by default + // new IMP_Search_Filter_Bulk(array( + // 'disable' => false + // )) + // )) + 'value' => 'a:1:{i:0;C:22:"IMP_Search_Filter_Bulk":88:{a:3:{s:1:"c";a:1:{i:0;C:23:"IMP_Search_Element_Bulk":5:{[1,0]}}s:1:"e";i:1;s:1:"v";i:1;}}}' +); + // *** Compose Preferences *** diff --git a/imp/docs/CHANGES b/imp/docs/CHANGES index a3a05ec23..53c6ba861 100644 --- a/imp/docs/CHANGES +++ b/imp/docs/CHANGES @@ -2,6 +2,8 @@ v5.0-git -------- +[mms] Add ability to define search criteria to be applied to any mailbox + (Request #8659). [mms] Use recipient search (To/Cc/Bcc) by default instead of To search. [mms] Virtual folders now editable via a preferences group. [mms] Search code has been entirely rewritten. diff --git a/imp/js/mailbox.js b/imp/js/mailbox.js index 074464556..fb7c66ee9 100644 --- a/imp/js/mailbox.js +++ b/imp/js/mailbox.js @@ -24,9 +24,17 @@ var ImpMailbox = { submit: function(actID) { - if (!this.anySelected()) { - alert(IMP.text.mailbox_submit); - return; + switch (actID) { + case 'filter_messages': + // No-op + break; + + default: + if (!this.anySelected()) { + alert(IMP.text.mailbox_submit); + return; + } + break; } switch (actID) { @@ -160,6 +168,17 @@ var ImpMailbox = { } }, + filterMessages: function(form) + { + var f1 = $('filter1'), f2 = $('filter2'); + + if ((form == 1 && $F(f1) != "") || + (form == 2 && $F(f2) != "")) { + $('messages').down('[name=filter]').setValue((form == 1) ? $F(f1) : $F(f2)); + this.submit('filter_messages'); + } + }, + getMessage: function(id, offset) { if (!offset) { @@ -186,6 +205,8 @@ var ImpMailbox = { if (id) { if (id.startsWith('flag')) { this.flagMessages(id.substring(4)); + } else if (id.startsWith('filter')) { + this.filterMessages(id.substring(6)); } else if (id.startsWith('targetMailbox')) { this.updateFolders(id.substring(13)); } @@ -365,7 +386,7 @@ var ImpMailbox = { submitHandler: function(e) { - if (e.element().readAttribute('id').startsWith('select')) { + if (e.element().hasClassName('navbarselect')) { e.stop(); } } diff --git a/imp/js/search.js b/imp/js/search.js index 1e13264e4..f804b18d2 100644 --- a/imp/js/search.js +++ b/imp/js/search.js @@ -101,7 +101,13 @@ var ImpSearch = { updateSelectedFolders: function(folders) { - var tmp = $('search_folders_hdr').next(); + var tmp = $('search_folders_hdr'); + + if (!tmp) { + return; + } + + tmp = tmp.next(); this.selectFolders(false); folders.each(function(f) { var i = tmp.down('INPUT[value=' + f + ']'); @@ -313,9 +319,10 @@ var ImpSearch = { { var data = [], tmp; - if (!this._getAll().findAll(function(i) { return i.checked; }).size()) { + if ($('search_folders_hdr') && + !this._getAll().findAll(function(i) { return i.checked; }).size()) { alert(this.text.need_folder); - } else if ($F('search_save') && !$('search_label').present()) { + } else if ($F('search_type') && !$('search_label').present()) { alert(this.text.need_label); } else { tmp = $('search_criteria_table').childElements().pluck('id'); diff --git a/imp/js/searchesprefs.js b/imp/js/searchesprefs.js index 9153b850f..1e1fac1f2 100644 --- a/imp/js/searchesprefs.js +++ b/imp/js/searchesprefs.js @@ -6,7 +6,8 @@ */ var ImpSearchesPrefs = { - // Variables set by other code: confirm_delete_vfolder, mailboxids + // Variables set by PHP script: confirm_delete_filter, + // confirm_delete_vfolder, mailboxids clickHandler: function(e) { @@ -17,7 +18,13 @@ var ImpSearchesPrefs = { var elt = e.element(); while (Object.isElement(elt)) { - if (elt.hasClassName('vfolderdelete')) { + if (elt.hasClassName('filterdelete')) { + if (window.confirm(this.confirm_delete_filter)) { + this._sendData('delete', elt.up().previous('.enabled').down('INPUT').readAttribute('name')); + } + e.stop(); + return; + } else if (elt.hasClassName('vfolderdelete')) { if (window.confirm(this.confirm_delete_vfolder)) { this._sendData('delete', elt.up().previous('.enabled').down('INPUT').readAttribute('name')); } diff --git a/imp/lib/Prefs/Ui.php b/imp/lib/Prefs/Ui.php index cf47d7494..719052b35 100644 --- a/imp/lib/Prefs/Ui.php +++ b/imp/lib/Prefs/Ui.php @@ -235,11 +235,7 @@ class IMP_Prefs_Ui break; case 'searches': - if ($prefs->isLocked('vfolder')) { - $ui->suppress[] = 'searchesmanagement'; - } else { - Horde::addScriptFile('searchesprefs.js', 'imp'); - } + Horde::addScriptFile('searchesprefs.js', 'imp'); break; case 'server': @@ -1256,20 +1252,24 @@ class IMP_Prefs_Ui */ protected function _searchesManagement() { - $t = $GLOBALS['injector']->createInstance('Horde_Template'); + global $injector, $prefs; + + $t = $injector->createInstance('Horde_Template'); $t->setOption('gettext', true); - $imp_search = $GLOBALS['injector']->getInstance('IMP_Search'); - $mailboxids = $out = array(); + $imp_search = $injector->getInstance('IMP_Search'); + $fout = $mailboxids = $vout = array(); $view_mode = IMP::getViewMode(); $imp_search->setIteratorFilter(IMP_Search::LIST_VFOLDER | IMP_Search::LIST_DISABLED); + $vfolder_locked = $prefs->isLocked('vfolder'); + foreach ($imp_search as $key => $val) { if (!$val->prefDisplay) { continue; } - $editable = $imp_search->isVFolder($val, true); + $editable = !$vfolder_locked && $imp_search->isVFolder($val, true); $m_url = ($val->enabled && ($view_mode == 'imp')) ? IMP::generateIMPUrl('mailbox.php', strval($val))->link(array('class' => 'vfolderenabled')) : null; @@ -1278,22 +1278,48 @@ class IMP_Prefs_Ui $mailboxids['enable_' . $key] = strval($val); } - $out[] = array( + $vout[] = array( 'description' => Horde_String::truncate($val->querytext, 200), 'edit' => ($editable ? $imp_search->editUrl($val) : null), 'enabled' => $val->enabled, + 'enabled_locked' => $vfolder_locked, 'key' => $key, 'label' => htmlspecialchars($val->label), 'm_url' => $m_url ); + } + $t->set('vfolders', $vout); + + $imp_search->setIteratorFilter(IMP_Search::LIST_FILTER | IMP_Search::LIST_DISABLED); + $filter_locked = $prefs->isLocked('filter'); + + foreach ($imp_search as $key => $val) { + $editable = !$filter_locked && $imp_search->isFilter($val, true); + + if ($view_mode == 'dimp') { + $mailboxids['enable_' . $key] = strval($val); + } + $fout[] = array( + 'description' => Horde_String::truncate($val->querytext, 200), + 'edit' => ($editable ? $imp_search->editUrl($val) : null), + 'enabled' => $val->enabled, + 'enabled_locked' => $filter_locked, + 'key' => $key, + 'label' => htmlspecialchars($val->label) + ); } - $t->set('vfolders', $out); + $t->set('filters', $fout); - Horde::addInlineJsVars(array( - 'ImpSearchesPrefs.confirm_delete_vfolder' => _("Are you sure you want to delete this virtual folder?"), - 'ImpSearchesPrefs.mailboxids' => $mailboxids - )); + if (empty($fout) && empty($vout)) { + $t->set('nosearches', true); + } else { + Horde::addInlineJsVars(array( + 'ImpSearchesPrefs.confirm_delete_filter' => _("Are you sure you want to delete this filter?"), + 'ImpSearchesPrefs.confirm_delete_vfolder' => _("Are you sure you want to delete this virtual folder?"), + 'ImpSearchesPrefs.mailboxids' => $mailboxids + )); + } return $t->fetch(IMP_TEMPLATES . '/prefs/searches.html'); } @@ -1312,13 +1338,17 @@ class IMP_Prefs_Ui /* Remove 'enable_' prefix. */ $key = substr($ui->vars->searches_data, 7); if ($ob = $imp_search[$key]) { - $GLOBALS['notification']->push(sprintf(_("Virtual Folder \"%s\" deleted."), $ob->label), 'horde.success'); + if ($imp_search->isVFolder($ob)) { + $GLOBALS['notification']->push(sprintf(_("Virtual Folder \"%s\" deleted."), $ob->label), 'horde.success'); + } elseif ($imp_search->isFilter($ob)) { + $GLOBALS['notification']->push(sprintf(_("Filter \"%s\" deleted."), $ob->label), 'horde.success'); + } unset($imp_search[$key]); } break; default: - /* Update enabled status. */ + /* Update enabled status for Virtual Folders. */ $imp_search->setIteratorFilter(IMP_Search::LIST_VFOLDER | IMP_Search::LIST_DISABLED); $vfolders = array(); @@ -1333,6 +1363,17 @@ class IMP_Prefs_Ui } } $imp_search->setVFolders($vfolders); + + /* Update enabled status for Filters. */ + $imp_search->setIteratorFilter(IMP_Search::LIST_FILTER | IMP_Search::LIST_DISABLED); + $filters = array(); + + foreach ($imp_search as $key => $val) { + $form_key = 'enable_' . $key; + $val->enabled = !empty($ui->vars->$form_key); + $filters[$key] = $val; + } + $imp_search->setFilters($filters); break; } } diff --git a/imp/lib/Search.php b/imp/lib/Search.php index 5c17a8f0e..0b94593af 100644 --- a/imp/lib/Search.php +++ b/imp/lib/Search.php @@ -24,13 +24,15 @@ class IMP_Search implements ArrayAccess, Iterator, Serializable const DIMP_QUICKSEARCH = 'dimpqsearch'; /* Bitmask filters for iterator. */ - const LIST_SEARCH = 1; - const LIST_VFOLDER = 2; - const LIST_DISABLED = 4; + const LIST_FILTER = 1; + const LIST_QUERY = 2; + const LIST_VFOLDER = 4; + const LIST_DISABLED = 8; /* Query creation types. */ - const CREATE_QUERY = 1; - const CREATE_VFOLDER = 2; + const CREATE_FILTER = 1; + const CREATE_QUERY = 2; + const CREATE_VFOLDER = 3; /** * Has the object data changed? @@ -63,6 +65,7 @@ class IMP_Search implements ArrayAccess, Iterator, Serializable * @var array */ protected $_search = array( + 'filters' => array(), 'query' => array(), 'vfolders' => array() ); @@ -72,6 +75,7 @@ class IMP_Search implements ArrayAccess, Iterator, Serializable */ public function init() { + $this->setFilters($this->getFilters(), false); $this->setVFolders($this->getVFolders(), false); } @@ -197,8 +201,9 @@ class IMP_Search implements ArrayAccess, Iterator, Serializable * @param string $id Use as the mailbox ID. * * @return IMP_Search_Query Returns the query object. + * @throws InvalidArgumentException */ - public function createQuery($criteria, $mboxes, $label = null, + public function createQuery($criteria, $mboxes = array(), $label = null, $type = self::CREATE_QUERY, $id = null) { if (!is_null($id)) { @@ -206,12 +211,22 @@ class IMP_Search implements ArrayAccess, Iterator, Serializable } switch ($type) { + case self::CREATE_FILTER: + $cname = 'IMP_Search_Filter'; + break; + case self::CREATE_QUERY: $cname = 'IMP_Search_Query'; + if (empty($mboxes)) { + throw new InvalidArgumentException('Search query requires at least one mailbox.'); + } break; case self::CREATE_VFOLDER: $cname = 'IMP_Search_Vfolder'; + if (empty($mboxes)) { + throw new InvalidArgumentException('Search query requires at least one mailbox.'); + } break; } @@ -223,6 +238,12 @@ class IMP_Search implements ArrayAccess, Iterator, Serializable ))); switch ($type) { + case self::CREATE_FILTER: + /* This will overwrite previous value, if it exists. */ + $this->_search['filters'][$ob->id] = $ob; + $this->setFilters($this->_search['filters']); + break; + case self::CREATE_QUERY: $this->_search['query'][$ob->id] = $ob; break; @@ -240,6 +261,83 @@ class IMP_Search implements ArrayAccess, Iterator, Serializable } /** + * Obtains the list of filters for the current user. + * + * @return array The list of filters. Keys are mailbox IDs, values are + * IMP_Search_Filter objects. + */ + public function getFilters() + { + if ($f_list = $GLOBALS['prefs']->getValue('filter')) { + $f_list = @unserialize($f_list); + } + + $filters = array(); + + if (is_array($f_list)) { + foreach ($f_list as $val) { + if ($val instanceof IMP_Search_Filter) { + $filters[$val->id] = $val; + } + } + } + + return $filters; + } + + /** + * Saves the list of filters for the current user. + * + * @param array $filters The filter list. + * @param boolean $save Save the filter list to the preference backend? + */ + public function setFilters($filters, $save = true) + { + if ($save) { + $GLOBALS['prefs']->setValue('filter', serialize(array_values($filters))); + } + + $this->_search['filters'] = $filters; + $this->changed = true; + } + + /** + * Is a mailbox a filter query? + * + * @param string $id The mailbox ID. + * @param boolean $editable Is this an editable (i.e. not built-in) + * filter query? + */ + public function isFilter($id, $editable = false) + { + return (isset($this->_search['filters'][$this->_strip($id)]) && + (!$editable || $this[$id]->canEdit)); + } + + /** + * Converts a filter to a search query and stores it in the local + * session. + * + * @param string $id The mailbox ID of the filter. + * @param array $mboxes The list of mailboxes to apply the filter on. + * + * @return IMP_Search_Query The created query object. + * @throws InvalidArgumentException + */ + public function applyFilter($id, array $mboxes) + { + if (!$this->isFilter($id)) { + throw new InvalidArgumentException('Invalid filter ID given.'); + } + + $q_ob = $this[$id]->toQuery($mboxes); + $this->_search['query'][$q_ob->id] = $q_ob; + $this->changed = true; + + return $q_ob; + } + + /** * Obtains the list of virtual folders for the current user. * * @return array The list of virtual folders. Keys are mailbox IDs, @@ -416,17 +514,24 @@ class IMP_Search implements ArrayAccess, Iterator, Serializable public function offsetExists($offset) { $id = $this->_strip($offset); - return (isset($this->_search['query'][$id]) || - isset($this->_search['vfolders'][$id])); + + foreach (array_keys($this->_search) as $key) { + if (isset($this->_search[$key][$id])) { + return true; + } + } + + return false; } public function offsetGet($offset) { $id = $this->_strip($offset); - if (isset($this->_search['query'][$id])) { - return $this->_search['query'][$id]; - } elseif (isset($this->_search['vfolders'][$id])) { - return $this->_search['vfolders'][$id]; + + foreach (array_keys($this->_search) as $key) { + if (isset($this->_search[$key][$id])) { + return $this->_search[$key][$id]; + } } return null; @@ -447,12 +552,12 @@ class IMP_Search implements ArrayAccess, Iterator, Serializable } $id = $this->_strip($offset); - if (isset($this->_search['query'][$id])) { - $this->_search['query'][$id] = $value; - return; - } elseif (isset($this->_search['vfolders'][$id])) { - $this->_search['vfolders'][$id] = $value; - return; + + foreach (array_keys($this->_search) as $key) { + if (isset($this->_search[$key][$id])) { + $this->_search[$key][$id] = $value; + return; + } } throw new InvalidArgumentException('Creating search queries by array index is not supported. Use createQuery() instead.'); @@ -542,7 +647,8 @@ class IMP_Search implements ArrayAccess, Iterator, Serializable * * @param integer $mask A mask with the following possible elements: *
-     * IMP_Search::LIST_SEARCH
+     * IMP_Search::LIST_FILTER
+     * IMP_Search::LIST_QUERY
      * IMP_Search::LIST_VFOLDER
      * 
*/ @@ -569,17 +675,18 @@ class IMP_Search implements ArrayAccess, Iterator, Serializable return true; } - if (($this->_filter & self::LIST_VFOLDER) && - $this->isVfolder($ob)) { + if (($this->_filter & self::LIST_FILTER) && + $this->isFilter($ob)) { return true; } - if (($this->_filter & self::LIST_SEARCH) && - !$this->isVfolder($ob)) { + if (($this->_filter & self::LIST_QUERY) && + $this->isQuery($ob)) { return true; } - if ($this->isQuery($ob, true)) { + if (($this->_filter & self::LIST_VFOLDER) && + $this->isVfolder($ob)) { return true; } } diff --git a/imp/lib/Search/Filter.php b/imp/lib/Search/Filter.php new file mode 100644 index 000000000..70f43deb8 --- /dev/null +++ b/imp/lib/Search/Filter.php @@ -0,0 +1,58 @@ + + * @category Horde + * @license http://www.fsf.org/copyleft/gpl.html GPL + * @package IMP + */ +class IMP_Search_Filter extends IMP_Search_Query +{ + /** + * Get object properties. + * + * @see parent::__get() + */ + public function __get($name) + { + switch ($name) { + case 'querytext': + $text = array(_("Search")); + + foreach ($this->_criteria as $elt) { + $text[] = $elt->queryText(); + if (!($elt instanceof IMP_Search_Element_Or)) { + $text[] = _("and"); + } + } + array_pop($text); + + return implode(' ', $text); + } + + return parent::__get($name); + } + + /** + * Creates a query object from this filter. + * + * @param array $mboxes The list of mailboxes to apply the filter to. + * + * @return IMP_Search_Query A query object. + */ + public function toQuery(array $mboxes) + { + return new IMP_Search_Query(array( + 'add' => $this->_criteria, + 'label' => $this->label, + 'mboxes' => $mboxes + )); + } + +} diff --git a/imp/lib/Search/Filter/Bulk.php b/imp/lib/Search/Filter/Bulk.php new file mode 100644 index 000000000..4fde711f5 --- /dev/null +++ b/imp/lib/Search/Filter/Bulk.php @@ -0,0 +1,69 @@ + + * @category Horde + * @license http://www.fsf.org/copyleft/gpl.html GPL + * @package IMP + */ +class IMP_Search_Filter_Bulk extends IMP_Search_Filter +{ + /** + * Can this query be edited? + * + * @var boolean + */ + protected $_canEdit = false; + + /** + * List of serialize entries not to save. + * + * @var array + */ + protected $_nosave = array('i', 'l'); + + /** + * Constructor. + * + * The 'add', 'id', 'label', and 'mboxes' parameters are ignored. + * + * @see parent::__construct() + */ + public function __construct(array $opts = array()) + { + $this->enabled = empty($opts['disable']); + + $this->add(new IMP_Search_Element_Bulk()); + + $this->_init(); + } + + /** + * Initialization tasks. + */ + protected function _init() + { + $this->_id = 'filter_bulk'; + $this->_label = _("Bulk Messages"); + } + + /** + * Unserialization. + * + * @param string $data Serialized data. + * + * @throws Exception + */ + public function unserialize($data) + { + parent::unserialize($data); + $this->_init(); + } + +} diff --git a/imp/mailbox.php b/imp/mailbox.php index 14ea04b08..4d57ac812 100644 --- a/imp/mailbox.php +++ b/imp/mailbox.php @@ -90,7 +90,7 @@ if (!$search_mbox) { /* Determine if mailbox is readonly. */ $readonly = $imp_imap->isReadOnly(IMP::$mailbox); if ($readonly && - in_array($actionID, array('delete_messages', 'undelete_messages', 'move_messages', 'flag_messages', 'empty_mailbox', 'filter'))) { + in_array($actionID, array('delete_messages', 'undelete_messages', 'move_messages', 'flag_messages', 'filter_messages', 'empty_mailbox', 'filter'))) { $actionID = null; } @@ -160,13 +160,21 @@ case 'copy_messages': break; case 'flag_messages': - $flag = Horde_Util::getPost('flag'); - if ($flag && count($indices)) { - $flag = $imp_flags->parseFormId($flag); + if ($vars->flag && count($indices)) { + $flag = $imp_flags->parseFormId($vars->flag); $injector->getInstance('IMP_Message')->flag(array($flag['flag']), $indices, $flag['set']); } break; +case 'filter_messages': + $filter = IMP::formMbox($vars->filter, false); + try { + $q_ob = $imp_search->applyFilter($filter, array(IMP::$mailbox)); + Horde::url('mailbox.php', true)->add('mailbox', strval($q_ob))->redirect(); + exit; + } catch (InvalidArgumentException $e) {} + break; + case 'hide_deleted': $prefs->setValue('delhide', !$prefs->getValue('delhide')); IMP::hideDeletedMsgs(IMP::$mailbox, true); @@ -467,6 +475,20 @@ if ($pageOb['msgcount']) { $n_template->set('flaglist_set', $tmp['set']); $n_template->set('flaglist_unset', $tmp['unset']); + if (!$search_mbox) { + $filters = array(); + $imp_search->setIteratorFilter(IMP_Search::LIST_FILTER); + foreach ($imp_search as $val) { + $filters[] = array( + 'l' => htmlspecialchars($val->label), + 'v' => IMP::formMbox(strval($val), true) + ); + } + if (!empty($filters)) { + $n_template->set('filters', $filters); + } + } + if ($n_template->get('use_folders')) { $n_template->set('move', Horde::widget('#', _("Move to folder"), 'widget moveAction', '', '', _("Move"), true)); $n_template->set('copy', Horde::widget('#', _("Copy to folder"), 'widget copyAction', '', '', _("Copy"), true)); diff --git a/imp/search.php b/imp/search.php index 84e66bc5e..36183f8c7 100644 --- a/imp/search.php +++ b/imp/search.php @@ -8,13 +8,15 @@ * --------------- * 'criteria_form' - (string) JSON representation of the search query. * 'edit_query' - (string) The search query to edit. - * 'edit_query_vfolder' - (string) The name of the vfolder being edited. + * 'edit_query_filter' - (string) The name of the filter being edited. + * 'edit_query_vfolder' - (string) The name of the virtual folder being + * edited. * 'folder_list' - (array) The list of folders to add to the query. * 'search_label' - (string) The label to use when saving the search. * 'search_mailbox' - (string) Use this mailbox as the default value. * DEFAULT: INBOX - * 'search_save' - (integer) If set, save search. - * 'search_type' - (string) The type of saved search ('vfolder'). + * 'search_type' - (string) The type of saved search ('filter', 'vfolder'). + * If empty, the search should not be saved. * * Copyright 1999-2010 The Horde Project (http://www.horde.org/) * @@ -145,10 +147,10 @@ $flist = $imp_flags->getFlagList($search_mailbox); /* Generate the search query if 'criteria_form' is present in the form * data. */ if ($vars->criteria_form) { - $criteria = Horde_Serialize::unserialize($vars->criteria_form, Horde_Serialize::JSON); + $c_data = Horde_Serialize::unserialize($vars->criteria_form, Horde_Serialize::JSON); $c_list = array(); - foreach ($criteria as $val) { + foreach ($c_data as $val) { switch ($val->t) { case 'from': case 'to': @@ -233,45 +235,74 @@ if ($vars->criteria_form) { } } - /* Save the search if requested. */ - if ($vars->search_save) { - switch ($vars->search_type) { - case 'vfolder': - $q_ob = $imp_search->createQuery( - $c_list, - $vars->folder_list, - $vars->search_label, - IMP_Search::CREATE_VFOLDER, - IMP::formMbox($vars->edit_query_vfolder, false) - ); + $redirect_dimp = true; + $redirect_target = false; + + switch ($vars->search_type) { + case 'filter': + $q_ob = $imp_search->createQuery( + $c_list, + array(), + $vars->search_label, + IMP_Search::CREATE_FILTER, + IMP::formMbox($vars->edit_query_filter, false) + ); - if ($vars->edit_query_vfolder) { - $notification->push(sprintf(_("Virtual Folder \"%s\" edited successfully."), $vars->search_label), 'horde.success'); - if ($dimp_view) { - IMP_Dimp::returnToDimp(strval($q_ob)); - } - Horde::getServiceLink('prefs', 'imp')->add('group', 'searches')->redirect(); - exit; - } + if ($vars->edit_query_filter) { + $notification->push(sprintf(_("Filter \"%s\" edited successfully."), $vars->search_label), 'horde.success'); + $redirect_dimp = false; + $redirect_target = 'prefs'; + } else { + $notification->push(sprintf(_("Filter \"%s\" created succesfully."), $vars->search_label), 'horde.success'); + } + break; + case 'vfolder': + $q_ob = $imp_search->createQuery( + $c_list, + $vars->folder_list, + $vars->search_label, + IMP_Search::CREATE_VFOLDER, + IMP::formMbox($vars->edit_query_vfolder, false) + ); + + if ($vars->edit_query_vfolder) { + $notification->push(sprintf(_("Virtual Folder \"%s\" edited successfully."), $vars->search_label), 'horde.success'); + $redirect_target = 'prefs'; + } else { $notification->push(sprintf(_("Virtual Folder \"%s\" created succesfully."), $vars->search_label), 'horde.success'); - break; + $redirect_target = 'mailbox'; } - } else { - /* Set the search in the session. */ + break; + + default: $q_ob = $imp_search->createQuery( $c_list, $vars->folder_list ); + $redirect_target = 'mailbox'; + break; } /* Redirect to the mailbox page. */ - if ($dimp_view) { - IMP_Dimp::returnToDimp(strval($q_ob)); - } + if ($redirect_target) { + if ($dimp_view && $redirect_dimp) { + IMP_Dimp::returnToDimp(strval($q_ob)); + exit; + } - Horde::url('mailbox.php', true)->add('mailbox', strval($q_ob))->redirect(); - exit; + switch ($redirect_target) { + case 'mailbox': + Horde::url('mailbox.php', true)->add('mailbox', strval($q_ob))->redirect(); + break; + + case 'prefs': + Horde::getServiceLink('prefs', 'imp')->add('group', 'searches')->redirect(); + break; + } + + exit; + } } /* Preselect mailboxes. */ @@ -288,19 +319,30 @@ if ($vars->edit_query && $imp_search->isSearchMbox($vars->edit_query)) { $q_ob = $imp_search[$vars->edit_query]; if ($imp_search->isVFolder($q_ob)) { if (!$imp_search->isVFolder($q_ob, true)) { - $notification->push(_("Special Virtual Folders cannot be edited."), 'horde.error'); + $notification->push(_("Built-in Virtual Folders cannot be edited."), 'horde.error'); Horde::getServiceLink('prefs', 'imp')->add('group', 'searches')->redirect(); } + $t->set('edit_query', true); $t->set('edit_query_vfolder', IMP::formMbox($q_ob, true)); - $t->set('search_label', htmlspecialchars($q_ob->label)); + } elseif ($imp_search->isFilter($q_ob)) { + if (!$imp_search->isFilter($q_ob, true)) { + $notification->push(_("Built-in Filters cannot be edited."), 'horde.error'); + Horde::getServiceLink('prefs', 'imp')->add('group', 'searches')->redirect(); + } + $t->set('edit_query', true); + $t->set('edit_query_filter', IMP::formMbox($q_ob, true)); + } + if ($t->get('edit_query')) { + $t->set('search_label', htmlspecialchars($q_ob->label)); $js_vars['ImpSearch.prefsurl'] = strval(Horde::getServiceLink('prefs', 'imp')->add('group', 'searches')->setRaw(true)); } + $js_vars['ImpSearch.i_criteria'] = $q_ob->criteria; } else { /* Process list of recent searches. */ $rs = array(); - $imp_search->setIteratorFilter(IMP_Search::LIST_SEARCH); + $imp_search->setIteratorFilter(IMP_Search::LIST_QUERY); foreach ($imp_search as $val) { $rs[$val->id] = array( 'c' => $val->criteria, @@ -348,10 +390,12 @@ foreach ($flist['set'] as $val) { $t->set('flist', $flag_set); /* Generate master folder list. */ -$tree = $injector->getInstance('IMP_Imap_Tree')->createTree('imp_search', array( - 'checkbox' => true, -)); -$t->set('tree', $tree->getTree()); +if (!$t->get('edit_query_filter')) { + $tree = $injector->getInstance('IMP_Imap_Tree')->createTree('imp_search', array( + 'checkbox' => true, + )); + $t->set('tree', $tree->getTree()); +} Horde_Core_Ui_JsCalendar::init(); Horde::addScriptFile('horde.js', 'horde'); diff --git a/imp/templates/imp/mailbox/form_start.html b/imp/templates/imp/mailbox/form_start.html index 903a32a2b..6700b43c1 100644 --- a/imp/templates/imp/mailbox/form_start.html +++ b/imp/templates/imp/mailbox/form_start.html @@ -7,3 +7,4 @@ + diff --git a/imp/templates/imp/mailbox/navbar.html b/imp/templates/imp/mailbox/navbar.html index dc211fb97..54ccd3ce3 100644 --- a/imp/templates/imp/mailbox/navbar.html +++ b/imp/templates/imp/mailbox/navbar.html @@ -4,24 +4,37 @@
-
- - - + + + - + - - + + - + - + +
+
+ +
+
+
diff --git a/imp/templates/imp/search/search.html b/imp/templates/imp/search/search.html index 7b2708c22..c553b94d3 100644 --- a/imp/templates/imp/search/search.html +++ b/imp/templates/imp/search/search.html @@ -6,7 +6,11 @@ Edit Virtual Folder + + Edit Filter + Search + @@ -53,6 +57,7 @@
+
@@ -66,6 +71,7 @@ +
@@ -76,20 +82,22 @@
- - + -
- -
+ + + +
+
value="" /> @@ -98,13 +106,13 @@
- + - + - + diff --git a/imp/templates/prefs/searches.html b/imp/templates/prefs/searches.html index 98d8501af..99282195a 100644 --- a/imp/templates/prefs/searches.html +++ b/imp/templates/prefs/searches.html @@ -1,5 +1,7 @@ + + @@ -23,7 +25,7 @@ -
- checked="checked" /> + checked="checked" disabled="disabled" /> @@ -35,9 +37,52 @@
+
+
+ + + + + + + + + + + + + + + + + + + + + +
FilterEnabled?Actions
+ + + checked="checked" disabled="disabled" /> + + + + + + No Actions Available + +
+ +
+
+ + +
+ No Saved Searches Defined. +
+
diff --git a/imp/themes/screen.css b/imp/themes/screen.css index 51bb6b06c..f474867c6 100644 --- a/imp/themes/screen.css +++ b/imp/themes/screen.css @@ -401,7 +401,7 @@ table.flagmanagement tbody td.flagicon { text-align: center; } -table.searchesmanagement td.vfolderdescription { +table.searchesmanagement td.searchdescription { background-color: #ffa; width: 500px; } -- 2.11.0