Total rewrite of search system.
Search queries, elements, and virtual folders broken into separate
classes for easier generation and better encapsulation of the data.
Also allows for more complex search types - for example, the "To:"
header search has been replaced by the Recipients search, which searches
all of To/Cc/Bcc. Another example: the bulk search query has been
created (although it is not yet implemented in the UI).
Virtual folder maintenance is now handled within a prefs groups to
consolidate duplicate code in both imp and dimp.
Internal storage of virtual folders has changed. IMP 4 virtual folders
will be automatically converted, but any virtual folders created using
git code in the last year or so is now invalid and no upgrade path is
available.
Removed the use_vinbox, use_vtrash, vinbox_id, and vtrash_id prefs.
'label' => _("Server and Folder Information"),
'desc' => _("Change mail server and folder settings."),
'members' => array(
- 'use_vinbox', 'subscribe', 'draftsselect', 'trashselect', 'spamselect'
+ 'subscribe', 'draftsselect', 'trashselect', 'spamselect'
)
);
-// display Virtual INBOX?
-$_prefs['use_vinbox'] = array(
- 'value' => 1,
- 'type' => 'checkbox',
- 'desc' => _("Display Virtual Inbox?")
-);
-
-// virtual inbox identifier
-$_prefs['vinbox_id'] = array(
- 'value' => ''
-);
-
// use IMAP subscribe?
$_prefs['subscribe'] = array(
'value' => 1,
// 'value' => Horde_String::convertCharset('Spam', null, 'UTF7-IMAP')
);
-$_prefs['vfolder'] = array(
- 'value' => ''
-);
-
// *** ACL Preferences ***
+// *** Saved Searches Preferences ***
+
+$prefGroups['searches'] = array(
+ 'column' => _("General Preferences"),
+ 'label' => _("Saved Searches"),
+ 'desc' => _("Manage your saved searches"),
+ 'members' => array(
+ 'searchesmanagement'
+ )
+);
+
+// UI for saved searches management.
+$_prefs['searchesmanagement'] = array(
+ 'type' => 'special'
+);
+
+$_prefs['vfolder'] = array(
+ // 'value' => serialize(array(
+ // // Virtual Inbox, enabled by default.
+ // new IMP_Search_Vfolder_Vinbox(array(
+ // 'disabled' => false
+ // )),
+ // // Virtual Trash, disabled by default.
+ // new IMP_Search_Vfolder_Vtrash(array(
+ // 'disabled' => true
+ // ))
+ // ))
+ '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;}}}'
+);
+
+
+
// *** Compose Preferences ***
$prefGroups['compose'] = array(
'desc' => _("When deleting messages, move them to your Trash folder instead of marking them as deleted?")
);
-// use Virtual Trash folder
-$_prefs['use_vtrash'] = array(
- 'value' => 0
-);
-
-// virtual trash folder identifier
-$_prefs['vtrash_id'] = array(
- 'value' => ''
-);
-
// display the 'Empty Trash' link in the menubar?
$_prefs['empty_trash_menu'] = array(
'value' => 0,
filter_on_sidebar
forward_bodytext
nav_expanded_sidebar
+ use_vinbox
+ use_vtrash
+ vinbox_id
+ vtrash_id
Hooks
}
break;
-case 'delete_search_query':
- if ($vars->queryid) {
- $imp_search = $injector->getInstance('IMP_Search');
- $notification->push(sprintf(_("Deleted Virtual Folder \"%s\"."), $imp_search->getLabel($vars->queryid)), 'horde.success');
- $imp_search->deleteSearchQuery($vars->queryid);
- }
- break;
-
case 'download_folder':
case 'download_folder_zip':
if (!empty($folder_list)) {
/* If switching from options, we need to reload page to pick up any
* prefs changes. */
- if (this.folder === null &&
- loc != 'options' &&
- $('appoptions') &&
- $('appoptions').hasClassName('on')) {
+ if (loc != 'prefs' &&
+ $('appprefs') &&
+ $('appprefs').hasClassName('on')) {
$('dimpPage').hide();
$('dimpLoading').show();
- return DimpCore.redirect(DIMP.conf.URI_DIMP + '#' + loc, true);
+ return DimpCore.redirect(DIMP.conf.URI_DIMP + '#' + escape(loc), true);
}
if (loc.startsWith('compose:')) {
if (loc.startsWith('folder:')) {
f = loc.substring(7);
+ if (f.empty()) {
+ f = this.folder;
+ }
+
if (this.folder != f || !$('dimpmain_folder').visible()) {
this.highlightSidebar(this.getFolderId(f));
if (!$('dimpmain_folder').visible()) {
}
f = this.folder;
- this.folder = null;
$('dimpmain_folder').hide();
$('dimpmain_portal').update(DIMP.text.loading).show();
DimpCore.doAction('showPortal', {}, { callback: this._portalCallback.bind(this) });
break;
- case 'options':
- this.highlightSidebar('appoptions');
+ case 'prefs':
+ // data: Extra parameters to add to prefs URL.
+ this.highlightSidebar('appprefs');
this.setHash(loc);
DimpCore.setTitle(DIMP.text.prefs);
- this.iframeContent(loc, DIMP.conf.URI_PREFS_IMP);
+ this.iframeContent(loc, DimpCore.addURLParam(DIMP.conf.URI_PREFS_IMP, data));
break;
}
},
this.go('search', tmp);
break;
+ case 'ctx_vcontainer_edit':
+ this.go('prefs', { group: 'searches' });
+ break;
+
case 'ctx_qsearchby_all':
case 'ctx_qsearchby_body':
case 'ctx_qsearchby_from':
- case 'ctx_qsearchby_to':
+ case 'ctx_qsearchby_recip':
case 'ctx_qsearchby_subject':
DIMP.conf.qsearchfield = id.substring(14);
this._updatePrefs('dimp_qsearch_field', DIMP.conf.qsearchfield);
return;
case 'appportal':
- case 'appoptions':
+ case 'appprefs':
this.go(id.substring(3));
e.stop();
return;
switch (li.retrieve('ftype')) {
case 'container':
case 'scontainer':
+ case 'vcontainer':
e.stop();
break;
case 'folder':
case 'special':
- case 'virtual':
+ case 'vfolder':
e.stop();
return this.go('folder:' + li.retrieve('mbox'));
}
}
if (ob.v) {
- ftype = ob.co ? 'scontainer' : 'virtual';
+ ftype = ob.co ? 'vcontainer' : 'vfolder';
title = label;
} else if (ob.co) {
if (ob.n) {
case 'container':
case 'folder':
new Drag(li, this._folderDragConfig);
- DimpCore.addContextMenu({
- id: fid,
- type: ftype
- });
break;
case 'scontainer':
- case 'virtual':
- DimpCore.addContextMenu({
- id: fid,
- type: (ob.v == 2) ? 'vfolder' : 'noactions'
- });
+ ftype = 'noactions';
+ break;
+
+ case 'vfolder':
+ if (ob.v == 1) {
+ ftype = 'noactions';
+ }
break;
}
+
+ DimpCore.addContextMenu({
+ id: fid,
+ type: ftype
+ });
},
deleteFolder: function(folder)
var ImpSearch = {
// The following variables are defined in search.php:
- // data, text
+ // data, i_criteria, recent, selected, text
criteria: {},
saved_searches: {},
this.resetCriteria();
criteria.each(function(c) {
- if (c.t == 'or') {
- this.insertOr();
- return;
- }
+ var crit = c.criteria;
- switch (this.data.types[c.t]) {
- case 'header':
- case 'body':
- case 'text':
- this.insertText(c.t, c.v, c.n);
+ switch (c.element) {
+ case 'IMP_Search_Element_Date':
+ this.insertDate(this.data.constants.index(crit.t), new Date(crit.d));
break;
- case 'customhdr':
- this.insertCustomHdr(c.v, c.n);
+ case 'IMP_Search_Element_Flag':
+ this.insertFlag(crit.f, !crit.s);
break;
- case 'size':
- this.insertSize(c.t, c.v);
+ case 'IMP_Search_Element_Header':
+ switch (crit.h) {
+ case 'from':
+ case 'to':
+ case 'cc':
+ case 'bcc':
+ case 'subject':
+ this.insertText(crit.h.capitalize(), crit.t, crit.n);
+ break;
+
+ default:
+ this.insertCustomHdr({ h: crit.h.capitalize(), s: crit.t }, crit.n);
+ break;
+ }
break;
- case 'date':
- this.insertDate(c.t, c.v);
+ case 'IMP_Search_Element_Or':
+ this.insertOr();
break;
- case 'within':
- this.insertWithin(c.t, c.v);
+ case 'IMP_Search_Element_Recipient':
+ this.insertText('recip', crit.t, crit.n);
break;
- case 'flag':
- this.insertFlag(c.v);
+ case 'IMP_Search_Element_Size':
+ this.insertSize(crit.s ? 'size_larger' : 'size_smaller', crit.l);
+ break;
+
+ case 'IMP_Search_Element_Text':
+ this.insertText(crit.b ? 'body' : 'text', crit.t, crit.n);
+ break;
+
+ case 'IMP_Search_Element_Within':
+ this.insertWithin(crit.o ? 'older' : 'younger', { l: this.data.constants.index(crit.t), v: crit.v });
break;
}
}, this);
});
},
- updateSavedSearches: function(label, type)
- {
- $('search_label').setValue(label);
- // TODO: type
- },
-
changeHandler: function(e)
{
var elt = e.element(), val = $F(elt);
switch (this.data.types[val]) {
case 'header':
- case 'body':
case 'text':
this.insertText(val);
break;
insertDate: function(id, data)
{
- var d = (data ? new Date(data.y, data.m, data.d) : new Date()),
- tmp = [
+ if (!data) {
+ data = new Date();
+ }
+
+ var tmp = [
new Element('EM').insert(this.getLabel(id)),
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, d);
+ this.replaceDate(this.insertCriteria(tmp), id, data);
},
replaceDate: function(id, type, d)
tmp[1].activate();
},
- insertFlag: function(id)
+ insertFlag: function(id, not)
{
var tmp = [
new Element('EM').insert(this.text.flag),
- this.getLabel(id).slice(0, -2)
+ this.getLabel(id).slice(0, -2),
+ 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 };
},
switch (this.data.types[this.criteria[c].t]) {
case 'header':
- case 'body':
case 'text':
this.criteria[c].n = Number(Boolean($F($(c).down('INPUT[type=checkbox]'))));
this.criteria[c].v = $F($(c).down('INPUT[type=text]'));
break;
case 'flag':
- data.push({ t: 'flag', v: this.criteria[c].t });
+ this.criteria[c].n = Number(Boolean($F($(c).down('INPUT[type=checkbox]'))));
+ data.push({
+ n: this.criteria[c].n,
+ t: 'flag',
+ v: this.criteria[c].t
+ });
break;
}
}, this);
e.stop();
return;
+ case 'search_edit_query_cancel':
+ e.stop();
+ if (this.data.dimp) {
+ window.parent.DimpBase.go('folder:');
+ } else {
+ document.location.href = this.prefsurl;
+ }
+ return;
+
default:
if (elt.hasClassName('arrowExpanded') ||
elt.hasClassName('arrowCollapsed')) {
{
var id = e.findElement('TR').identify();
this.replaceDate(id, this.criteria[id].t, e.memo);
+ },
+
+ onDomLoad: function()
+ {
+ if (!this.data) {
+ this.onDomLoad.bind(this).defer();
+ return;
+ }
+
+ this.data.constants.date = $H(this.data.constants.date);
+ this.data.constants.within = $H(this.data.constants.within);
+
+ if (this.recent) {
+ this.updateRecentSearches(this.recent);
+ this.recent = null;
+ }
+
+ if (this.selected) {
+ this.updateSelectedFolders(this.selected);
+ this.selected = null;
+ }
+
+ if (this.i_criteria) {
+ this.updateSearchCriteria(this.i_criteria);
+ this.i_criteria = null;
+ }
}
};
document.observe('change', ImpSearch.changeHandler.bindAsEventListener(ImpSearch));
document.observe('click', ImpSearch.clickHandler.bindAsEventListener(ImpSearch));
+document.observe('dom:loaded', ImpSearch.onDomLoad.bindAsEventListener(ImpSearch));
document.observe('Horde_Calendar:select', ImpSearch.calendarSelectHandler.bindAsEventListener(ImpSearch));
--- /dev/null
+/**
+ * Provides the javascript for managing saved searches.
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ */
+
+var ImpSearchesPrefs = {
+ // Variables set by other code: confirm_delete_vfolder, mailboxids
+
+ clickHandler: function(e)
+ {
+ if (e.isRightClick()) {
+ return;
+ }
+
+ var elt = e.element();
+
+ while (Object.isElement(elt)) {
+ if (elt.hasClassName('vfolderdelete')) {
+ if (window.confirm(this.confirm_delete_vfolder)) {
+ this._sendData('delete', elt.up().previous('.enabled').down('INPUT').readAttribute('name'));
+ }
+ e.stop();
+ return;
+ } else if (elt.match('SPAN.vfolderenabled')) {
+ e.stop();
+ window.parent.DimpBase.go('folder:' + this.mailboxids[elt.up().next('.enabled').down('INPUT').readAttribute('name')]);
+ return;
+ }
+
+ elt = elt.up();
+ }
+ },
+
+ _sendData: function(a, d)
+ {
+ $('searches_action').setValue(a)
+ $('searches_data').setValue(d);
+ $('prefs').submit();
+ }
+
+};
+
+document.observe('click', ImpSearchesPrefs.clickHandler.bindAsEventListener(ImpSearchesPrefs));
$imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
- if ($imp_search->isEditableVFolder($this->_vars->mbox)) {
- $GLOBALS['notification']->push(sprintf(_("Deleted Virtual Folder \"%s\"."), $imp_search->getLabel($this->_vars->mbox)), 'horde.success');
- $imp_search->deleteSearchQuery($this->_vars->mbox);
+ if ($imp_search->isVFolder($this->_vars->mbox, true)) {
+ $GLOBALS['notification']->push(sprintf(_("Deleted Virtual Folder \"%s\"."), $imp_search[$this->_vars->mbox]->label), 'horde.success');
+ unset($imp_search[$this->_vars->mbox]);
$result = true;
} else {
$result = $GLOBALS['injector']->getInstance('IMP_Folder')->delete(array($this->_vars->mbox));
$ob->po = 1;
}
if ($elt->vfolder) {
- $ob->v = $GLOBALS['injector']->getInstance('IMP_Search')->isEditableVFolder($elt->value) ? 2 : 1;
+ $ob->v = $GLOBALS['injector']->getInstance('IMP_Search')->isVFolder($elt->value, true) ? 2 : 1;
}
if (!$elt->sub) {
$ob->un = 1;
*/
public function searchMailbox($mailbox, $query)
{
- $results = $GLOBALS['injector']->getInstance('IMP_Search')->runSearchQuery($query, $mailbox)->indices();
+ $results = $GLOBALS['injector']->getInstance('IMP_Search')->runQuery($query, $mailbox)->indices();
return isset($results[$mailbox])
? $results[$mailbox]
: array();
if ($_SESSION['imp']['protocol'] != 'pop') {
if ($prefs->getValue('use_trash') &&
+ ($trash_folder = $prefs->getValue('trash_folder')) &&
$prefs->getValue('empty_trash_menu')) {
- $mailbox = null;
- if ($prefs->getValue('use_vtrash')) {
- $mailbox = $injector->getInstance('IMP_Search')->createSearchID($prefs->getValue('vtrash_id'));
- } else {
- $trash_folder = IMP::folderPref($prefs->getValue('trash_folder'), true);
- if (!is_null($trash_folder)) {
- $mailbox = $trash_folder;
- }
- }
+ $imp_search = $injector->getInstance('IMP_Search');
+ $trash_folder = IMP::folderPref($trash_folder, true);
- if (!empty($mailbox) &&
- !$injector->getInstance('IMP_Imap')->getOb()->isReadOnly($mailbox)) {
+ if ($injector->getInstance('IMP_Search')->isVTrash($trash_folder) ||
+ !$injector->getInstance('IMP_Imap')->getOb()->isReadOnly($trash_folder)) {
$menu->addArray(array(
'class' => '__noselection',
'icon' => 'empty_trash.png',
'onclick' => 'return window.confirm(' . Horde_Serialize::serialize(_("Are you sure you wish to empty your trash folder?"), Horde_Serialize::JSON, $registry->getCharset()) . ')',
'text' => _("Empty _Trash"),
- 'url' => IMP::generateIMPUrl($menu_mailbox_url, $mailbox)->add(array('actionID' => 'empty_mailbox', 'mailbox_token' => Horde::getRequestToken('imp.mailbox')))
+ 'url' => IMP::generateIMPUrl($menu_mailbox_url, $trash_folder)->add(array('actionID' => 'empty_mailbox', 'mailbox_token' => Horde::getRequestToken('imp.mailbox')))
));
}
}
public function mailboxesChanged()
{
$GLOBALS['injector']->getInstance('IMP_Imap_Tree')->init();
- $GLOBALS['injector']->getInstance('IMP_Search')->init(true);
}
}
: $GLOBALS['prefs']->getValue('initial_page');
$imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
-
- if (!$GLOBALS['prefs']->getValue('use_vinbox') &&
- $imp_search->isVINBOXFolder($init_url)) {
- $init_url = 'folders.php';
- } elseif (($imp_search->createSearchId($init_url) == $init_url) &&
- !$imp_search->isVFolder($init_url)) {
+ if ($imp_search->isSearchMbox($init_url) &&
+ (!$imp_search[$init_url]->enabled)) {
$init_url = 'INBOX';
- if (!$GLOBALS['prefs']->isLocked('initial_page')) {
- $GLOBALS['prefs']->setValue('initial_page', $init_url);
- }
}
switch ($init_url) {
$query = new Horde_Imap_Client_Search_Query();
$query->flag('\\seen', false);
- $ids = $GLOBALS['injector']->getInstance('IMP_Search')->runSearchQuery($query, 'INBOX', Horde_Imap_Client::SORT_SEQUENCE, 1);
+ $ids = $GLOBALS['injector']->getInstance('IMP_Search')->runQuery($query, 'INBOX', Horde_Imap_Client::SORT_SEQUENCE, 1);
$indices = reset($ids);
$html = '<table cellspacing="0" width="100%">';
return $ret;
}
+ /**
+ * Return to main dimp mailbox page from within IFRAME.
+ *
+ * @var string $mailbox The mailbox to load.
+ */
+ static public function returnToDimp($mailbox = '')
+ {
+ print '<html><head>' .
+ Horde::wrapInlineScript(array('window.parent.DimpBase.go(' . Horde_Serialize::serialize('folder:' . strval($mailbox), Horde_Serialize::JSON, $GLOBALS['registry']->getCharset()) . ')')) .
+ '</head></html>';
+ exit;
+ }
+
}
$imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
$mbox_list = $imp_search->isSearchMbox($mbox)
- ? $imp_search->getSearchFolders($mbox)
+ ? $imp_search->getSearchMailboxes($mbox)
: array($mbox);
foreach ($mbox_list as $val) {
*/
protected function _onDelete($deleted)
{
- /* Recreate Virtual Folders. */
- $GLOBALS['injector']->getInstance('IMP_Search')->init(true);
-
/* Clear the folder from the sort prefs. */
foreach ($deleted as $val) {
IMP::setSort(null, null, $val, true);
/* Update the mailbox tree. */
$GLOBALS['injector']->getInstance('IMP_Imap_Tree')->insert($folder);
- /* Recreate Virtual Folders. */
- $GLOBALS['injector']->getInstance('IMP_Search')->init(true);
-
return true;
}
*/
static public function getLabel($mbox)
{
- if (!($label = $GLOBALS['injector']->getInstance('IMP_Search')->getLabel($mbox))) {
- $label = self::displayFolder($mbox);
- }
+ $imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
+
+ $label = ($ob = $imp_search[$mbox])
+ ? $ob->label
+ : self::displayFolder($mbox);
try {
return Horde::callHook('mbox_label', array($mbox, $label), 'imp');
*/
static public function hideDeletedMsgs($mbox, $force = false)
{
+ global $injector, $prefs;
+
$delhide = &self::$_delhide;
if (is_null($delhide) || $force) {
- if ($GLOBALS['prefs']->getValue('use_vtrash')) {
- $delhide = !$GLOBALS['injector']->getInstance('IMP_Search')->isVTrashFolder($mbox);
+ $imp_search = $injector->getInstance('IMP_Search');
+ $use_trash = $prefs->getValue('use_trash');
+
+ if ($use_trash &&
+ $imp_search->isVTrash($prefs->getValue('trash_folder'))) {
+ $delhide = !$imp_search->isVTrash($mbox);
} else {
$sortpref = self::getSort();
- $delhide = ($GLOBALS['prefs']->getValue('delhide') &&
- !$GLOBALS['prefs']->getValue('use_trash') &&
- ($GLOBALS['injector']->getInstance('IMP_Search')->isSearchMbox($mbox) ||
+ $delhide = ($prefs->getValue('delhide') &&
+ !$use_trash &&
+ ($injector->getInstance('IMP_Search')->isSearchMbox($mbox) ||
($sortpref['by'] != Horde_Imap_Client::SORT_THREAD)));
}
}
* Horde_Imap_Client_Socket has a built-in ORDEREDSUBJECT
* implementation. We will always prefer REFERENCES, but will fallback
* to ORDEREDSUBJECT if the server doesn't support THREAD sorting. */
- return ($_SESSION['imp']['protocol'] == 'imap') &&
- !$GLOBALS['injector']->getInstance('IMP_Search')->isSearchMbox($mbox) &&
- (!$GLOBALS['prefs']->getValue('use_trash') ||
- !$GLOBALS['prefs']->getValue('use_vtrash') ||
- $GLOBALS['injector']->getInstance('IMP_Search')->isVTrashFolder($mbox));
+ return (($_SESSION['imp']['protocol'] == 'imap') &&
+ !$GLOBALS['injector']->getInstance('IMP_Search')->isSearchMbox($mbox));
}
/**
}
$t->set('folders', $folders);
+ $imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
if (($_SESSION['imp']['protocol'] != 'pop') &&
- $GLOBALS['prefs']->getValue('use_vinbox') &&
- ($vinbox_id = $GLOBALS['prefs']->getValue('vinbox_id'))) {
- $t->set('vinbox', Horde::link(self::generateIMPUrl('mailbox.php', $GLOBALS['injector']->getInstance('IMP_Search')->createSearchId($vinbox_id))));
+ ($vinbox = $imp_search['vinbox']) &&
+ $vinbox->enabled) {
+ $t->set('vinbox', self::generateIMPUrl('mailbox.php', strval($vinbox))->link());
}
} else {
$t->set('msg', ($var == 1) ? _("You have 1 new message.") : sprintf(_("You have %s new messages."), $var));
}
/**
+ * Given a flag/set combo, returns the text label.
+ *
+ * @param string $name Flag name.
+ * @param boolean $set Search for set flag?
+ *
+ * @return string The text label.
+ */
+ public function getLabel($name, $set)
+ {
+ $flist = $this->getList();
+
+ if (!isset($flist[$name])) {
+ return '';
+ }
+
+ if (!empty($flist[$name]['n'])) {
+ $set = !$set;
+ }
+
+ return $set
+ ? $flist[$name]['l']
+ : sprintf(_("Not %s"), $flist[$name]['l']);
+ }
+
+ /**
* Determines the colors for an entry.
*
* @param string $key The flag key.
$this->_insert($this->_getList($this->_showunsub), $this->_showunsub ? null : true);
/* Add virtual folders to the tree. */
- $this->insertVFolders($GLOBALS['injector']->getInstance('IMP_Search')->listQueries(IMP_Search::LIST_VFOLDER));
+ $imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
+ $imp_search->setIteratorFilter(IMP_Search::LIST_VFOLDER);
+ $this->updateVFolders(iterator_to_array($imp_search));
}
-
/**
* Returns the list of mailboxes on the server.
*
protected function _updatePollList()
{
$GLOBALS['prefs']->setValue('nav_poll', serialize($this->_cache['poll']));
- $GLOBALS['injector']->getInstance('IMP_Search')->createVInbox();
$this->changed = true;
}
}
/**
- * Inserts virtual folders into the tree.
+ * Updates the virtual folder list in the tree.
*
- * @param array $id_list An array with the folder IDs to add as the key
- * and the labels as the value.
+ * @param array $vfolders A list of IMP_Search_VFolder objects.
*/
- public function insertVFolders($id_list)
+ public function updateVFolders($vfolders)
{
- if (empty($id_list) ||
- empty($GLOBALS['conf']['user']['allow_folders'])) {
- return;
- }
-
- $adds = $id = array();
-
- foreach ($id_list as $key => $val) {
- $id[$GLOBALS['injector']->getInstance('IMP_Search')->createSearchId($key)] = $val;
- }
-
- foreach (array_keys($id) as $key) {
- $id_key = self::VFOLDER_KEY . $this->_delimiter . $key;
- if (!isset($this->_tree[$id_key])) {
- $adds[] = $id_key;
+ /* Clear old entries. */
+ if (isset($this->_parent[self::VFOLDER_KEY])) {
+ foreach ($this->_parent[self::VFOLDER_KEY] as $key) {
+ unset($this->_tree[$key]);
}
+ unset($this->_parent[self::VFOLDER_KEY]);
+ $this->changed = true;
}
- if (empty($adds)) {
+ if (empty($GLOBALS['conf']['user']['allow_folders'])) {
return;
}
- $this->insert($adds);
-
- foreach ($id as $key => $val) {
- $this->_tree[$key]['l'] = $val;
+ foreach ($vfolders as $val) {
+ if ($val->enabled) {
+ $key = strval($val);
+ $this->insert(self::VFOLDER_KEY . $this->_delimiter . $key);
+ $this->_tree[$key]['l'] = $val->label;
+ }
}
/* Sort the Virtual Folder list in the object, if necessary. */
- if (!$this->_needSort($this->_tree[self::VFOLDER_KEY])) {
- return;
- }
-
- $vsort = array();
- foreach ($this->_parent[self::VFOLDER_KEY] as $val) {
- $vsort[$val] = $this->_tree[$val]['l'];
+ if (isset($this->_tree[self::VFOLDER_KEY]) &&
+ $this->_needSort($this->_tree[self::VFOLDER_KEY])) {
+ $vsort = array();
+ foreach ($this->_parent[self::VFOLDER_KEY] as $val) {
+ $vsort[$val] = $this->_tree[$val]['l'];
+ }
+ natcasesort($vsort);
+ $this->_parent[self::VFOLDER_KEY] = array_keys($vsort);
+ $this->_setNeedSort($this->_tree[self::VFOLDER_KEY], false);
}
- natcasesort($vsort);
- $this->_parent[self::VFOLDER_KEY] = array_keys($vsort);
- $this->_setNeedSort($this->_tree[self::VFOLDER_KEY], false);
- $this->changed = true;
}
/**
if ($val->vfolder) {
$checkbox .= ' disabled="disabled"';
- if (!empty($opts['editvfolder']) && $val->editvfolder) {
- $imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
+ if (!empty($opts['editvfolder']) && $val->container) {
$after = ' [' .
- $imp_search->deleteUrl($val->value)->link(array('title' => _("Delete Virtual Folder"))) . _("Delete") . '</a>'.
- '] | |[' .
- $imp_search->editUrl($val->value)->link(array('title' => _("Edit Virtual Folder"))) . _("Edit") . '</a>'.
+ Horde::getServiceLink('prefs', 'imp')->add('group', 'searches')->link(array('title' => _("Edit Virtual Folder"))) . _("Edit") . '</a>'.
']';
}
}
: IMP::displayFolder($this->value);
case 'editvfolder':
- return ($this->vfolder &&
- $GLOBALS['injector']->getInstance('IMP_Search')->isEditableVFolder($this->value));
+ return $GLOBALS['injector']->getInstance('IMP_Search')->isVFolder($this->value, true);
case 'is_open':
return $this->_treeob->isOpen($this->_mbox);
case 'INBOX':
case $this->_eltCache['draft']:
case $this->_eltCache['spam']:
- return true;
-
case $this->_eltCache['trash']:
- return (!$GLOBALS['prefs']->getValue('use_vtrash'));
+ return true;
default:
return in_array($this->value, $this->_eltCache['sent']);
return false;
case 'specialvfolder':
- if (!$this->vfolder) {
- return false;
- }
- $imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
- return ($imp_search->isVTrashFolder($this->value) ||
- $imp_search->isVINBOXFolder($this->value));
+ return !$GLOBALS['injector']->getInstance('IMP_Search')->isVFolder($this->value, true);
case 'sub':
return $this->_treeob->isSubscribed($this->_mbox);
break;
case $this->_eltCache['trash']:
- if ($GLOBALS['prefs']->getValue('use_vtrash')) {
- $info->alt = _("Mailbox");
- $info->icon = $this->is_open
- ? 'folders/open.png'
- : 'folders/folder.png';
- } else {
- $info->alt = _("Trash folder");
- $info->class = 'trashImg';
- $info->icon = 'folders/trash.png';
- }
+ $info->alt = _("Trash folder");
+ $info->class = 'trashImg';
+ $info->icon = 'folders/trash.png';
break;
case $this->_eltCache['draft']:
/* Virtual folders. */
if ($this->vfolder) {
$imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
- if ($imp_search->isVTrashFolder($this->_mbox['v'])) {
- $info->alt = _("Virtual Trash Folder");
+ if ($imp_search->isVTrash($this->_mbox['v'])) {
+ $info->alt = $imp_search[$this->_mbox['v']]->label;
$info->class = 'trashImg';
$info->icon = 'folders/trash.png';
- } elseif ($imp_search->isVINBOXFolder($this->_mbox['v'])) {
- $info->alt = _("Virtual INBOX Folder");
+ } elseif ($imp_search->isVinbox($this->_mbox['v'])) {
+ $info->alt = $imp_search[$this->_mbox['v']]->label;
$info->class = 'inboxImg';
$info->icon = 'folders/inbox.png';
}
$sortpref = @unserialize($GLOBALS['prefs']->getValue('sortpref'));
foreach (array_keys($sortpref) as $key) {
- if ($imp_search->isSearchMbox($key) &&
- !$imp_search->isEditableVFolder($key)) {
+ if (!$imp_search[$key]) {
unset($sortpref[$key]);
$update = true;
}
*/
protected function _upgradeVirtualFolders()
{
- if ($GLOBALS['prefs']->isDefault('vfolder')) {
- return;
- }
+ global $prefs;
- $vfolders = $GLOBALS['prefs']->getValue('vfolder');
+ $use_vinbox = $prefs->getValue('use_vinbox');
+ $use_vtrash = $prefs->getValue('use_vtrash');
+
+ $vfolders = $prefs->getValue('vfolder');
if (!empty($vfolders)) {
$vfolders = @unserialize($vfolders);
}
return;
}
- $imp_ui_search = new IMP_Ui_Search();
-
- foreach ($vfolders as $id => $vfolder) {
- /* If this is already a stdClass object, we have already
- * upgraded. */
- if (is_object($vfolder)) {
- return;
+ if ($prefs->isDefault('vfolder') || is_object(reset($vfolders))) {
+ foreach ($vfolders as $val) {
+ if (!is_null($use_vinbox) &&
+ ($val instanceof IMP_Search_Vfolder_Vinbox)) {
+ $val->enabled = (bool)$use_vinbox;
+ } elseif (!is_null($use_vtrash) &&
+ ($val instanceof IMP_Search_Vfolder_Vtrash)) {
+ $val->enabled = (bool)$use_vtrash;
+ $prefs->setValue('trash_folder', strval($val));
+ }
}
+ $prefs->setValue('vfolder', serialize($vfolders));
+ return;
+ }
+
+ $new_vfolders = array();
+ if ($use_vinbox) {
+ $new_vfolders[] = new IMP_Search_Vfolder_Vinbox();
+ }
+ if ($use_vtrash) {
+ $vtrash = $new_vfolders[] = new IMP_Search_Vfolder_Vtrash();
+ $prefs->setValue('trash_folder', strval($vtrash));
+ }
+ foreach ($vfolders as $id => $vfolder) {
$ui = $vfolder['uiinfo'];
$or_match = ($ui['match'] == 'or');
}
}
- $rules = array();
-
foreach ($ui['field'] as $key => $val) {
- $tmp = new stdClass;
+ $ob = new IMP_Search_Vfolder(array(
+ 'enabled' => true,
+ 'label' => $ui['vfolder_label'],
+ 'mboxes' => $ui['folders']
+ ));
switch ($val) {
case 'from':
case 'cc':
case 'bcc':
case 'subject':
+ $ob->add(new IMP_Search_Element_Header(
+ $ui['text'][$key],
+ $val,
+ !empty($ui['text_not'][$key])
+ ));
+ break;
+
case 'body':
case 'text':
- $tmp->t = $val;
- $tmp->v = $ui['text'][$key];
- $tmp->n = !empty($ui['text_not'][$key]);
+ $ob->add(new IMP_Search_Element_Text(
+ $ui['text'][$key],
+ ($val == 'body'),
+ !empty($ui['text_not'][$key])
+ ));
break;
case 'date_on':
case 'date_until':
case 'date_since':
- $tmp->t = $val;
- $tmp->v = new stdClass;
- $tmp->v->y = $ui['date'][$key]['year'];
- $tmp->v->m = $ui['date'][$key]['month'] - 1;
- $tmp->v->d = $ui['date'][$key]['day'];
+ if ($val == 'date_on') {
+ $type = IMP_Search_Element_Date::DATE_ON;
+ } elseif ($val == 'date_until') {
+ $type = IMP_Search_Element_Date::DATE_BEFORE;
+ } else {
+ $type = IMP_Search_Element_Date::DATE_SINCE;
+ }
+ $ob->add(new IMP_Search_Element_Date(
+ new DateTime($ui['date'][$key]['year'] . '-' . $ui['date'][$key]['month'] . '-' . $ui['date'][$key]['day']),
+ $type
+ ));
break;
case 'size_smaller':
case 'size_larger':
- $tmp->t = $val;
- $tmp->v = $ui['text'][$key];
+ $ob->add(new IMP_Search_Element_Size(
+ $ui['text'][$key],
+ $val == 'size_larger'
+ ));
break;
case 'seen':
case 'unflagged':
case 'deleted':
case 'undeleted':
- $tmp->t = 'flag';
- $tmp->v = (strpos($val, 'un') === false)
- ? '\\' . $val
- : '0\\\\' . substr($val, 2);
+ if (strpos($val, 'un') === false) {
+ $ob->add(new IMP_Search_Element_Flag(
+ $val,
+ true
+ ));
+ } else {
+ $ob->add(new IMP_Search_Element_Flag(
+ substr($val, 2),
+ false
+ ));
+ }
break;
}
- $rules[] = $tmp;
-
if ($or_match) {
- $tmp = new stdClass;
- $tmp->t = 'or';
- $rules[] = $tmp;
+ $ob->add(new IMP_Search_Element_Or());
}
}
- /* This will overwrite the existing entry. */
- $query = $imp_ui_search->createQuery($rules);
- $GLOBALS['injector']->getInstance('IMP_Search')->addVFolder($query, $ui['folders'], $rules, $ui['vfolder_label'], $id);
+ $new_vfolders[] = $ob;
}
+
+ $GLOBALS['injector']->getInstance('IMP_Search')->setVFolders($new_vfolders);
}
}
* than 'purge_sentmail_keep' days. */
$query = new Horde_Imap_Client_Search_Query();
$query->dateSearch($del_time, Horde_Imap_Client_Search_Query::DATE_BEFORE);
- $msg_ids = $GLOBALS['injector']->getInstance('IMP_Search')->runSearchQuery($query, $mbox);
+ $msg_ids = $GLOBALS['injector']->getInstance('IMP_Search')->runQuery($query, $mbox);
/* Go through the message list and delete the messages. */
if ($imp_message->delete($msg_ids, array('nuke' => true))) {
/* Get the list of messages older than 'purge_spam_keep' days. */
$query = new Horde_Imap_Client_Search_Query();
$query->dateSearch($del_time, Horde_Imap_Client_Search_Query::DATE_BEFORE);
- $msg_ids = $GLOBALS['injector']->getInstance('IMP_Search')->runSearchQuery($query, $spam_folder);
+ $msg_ids = $GLOBALS['injector']->getInstance('IMP_Search')->runQuery($query, $spam_folder);
/* Go through the message list and delete the messages. */
if ($GLOBALS['injector']->getInstance('IMP_Message')->delete($msg_ids, array('nuke' => true))) {
*/
public function execute()
{
+ global $injector, $notification, $prefs;
+
/* If we aren't using a Trash folder or if there is no Trash
folder set, just return. */
- $trash_folder = IMP::folderPref($GLOBALS['prefs']->getValue('trash_folder'), true);
- if (!$GLOBALS['prefs']->getValue('use_trash') || !$trash_folder) {
+ if (!$prefs->getValue('use_trash') ||
+ !($trash_folder = $prefs->getValue('trash_folder'))) {
return false;
}
+ $trash_folder = IMP::folderPref($trash_folder, true);
/* Make sure the Trash folder exists. */
- if (!$GLOBALS['injector']->getInstance('IMP_Folder')->exists($trash_folder)) {
+ $imp_search = $injector->getInstance('IMP_Search');
+ if ($imp_search->isVTrash($trash_folder) ||
+ !$injector->getInstance('IMP_Folder')->exists($trash_folder)) {
return false;
}
/* Get the current UNIX timestamp minus the number of days
specified in 'purge_trash_keep'. If a message has a
timestamp prior to this value, it will be deleted. */
- $del_time = new Horde_Date(time() - ($GLOBALS['prefs']->getValue('purge_trash_keep') * 86400));
+ $del_time = new Horde_Date(time() - ($prefs->getValue('purge_trash_keep') * 86400));
/* Get the list of messages older than 'purge_trash_keep' days. */
$query = new Horde_Imap_Client_Search_Query();
$query->dateSearch($del_time, Horde_Imap_Client_Search_Query::DATE_BEFORE);
- $msg_ids = $GLOBALS['injector']->getInstance('IMP_Search')->runSearchQuery($query, $trash_folder);
+ $msg_ids = $imp_search->runQuery($query, $trash_folder);
/* Go through the message list and delete the messages. */
- if ($GLOBALS['injector']->getInstance('IMP_Message')->delete($msg_ids, array('nuke' => true))) {
- $msgcount = count($msg_ids);
- $GLOBALS['notification']->push(sprintf(ngettext("Purging %d message from Trash folder.", "Purging %d messages from Trash folder.", $msgcount), $msgcount), 'horde.message');
- return true;
+ if (!$injector->getInstance('IMP_Message')->delete($msg_ids, array('nuke' => true))) {
+ return false;
}
- return false;
+ $msgcount = count($msg_ids);
+ $notification->push(sprintf(ngettext("Purging %d message from Trash folder.", "Purging %d messages from Trash folder.", $msgcount), $msgcount), 'horde.message');
+ return true;
}
/**
if ($count &&
$this->_searchmbox &&
($type == 'unseen') &&
- $GLOBALS['injector']->getInstance('IMP_Search')->isVINBOXFolder($this->_mailbox)) {
+ $GLOBALS['injector']->getInstance('IMP_Search')->isVinbox($this->_mailbox)) {
return count($this);
}
$trash = IMP::folderPref($prefs->getValue('trash_folder'), true);
$use_trash = $prefs->getValue('use_trash');
- $use_vtrash = $prefs->getValue('use_vtrash');
- if ($use_trash && !$use_vtrash && empty($trash)) {
+ if ($use_trash && empty($trash)) {
$notification->push(_("Cannot move messages to Trash - no Trash mailbox set in preferences."), 'horde.error');
return false;
}
- $return_value = 0;
+ $imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
$maillog_update = (empty($options['keeplog']) && !empty($conf['maillog']['use_maillog']));
+ $return_value = 0;
/* Check for Trash folder. */
- $use_trash_folder = !$this->_usepop && empty($options['nuke']) && !$use_vtrash && $use_trash;
+ $use_trash_folder = $use_vtrash = false;
+ if (!$this->_usepop && empty($options['nuke']) && $use_trash) {
+ $use_vtrash = $imp_search->isVTrash($trash);
+ $use_trash_folder = !$use_vtrash;
+ }
+
if ($use_trash_folder) {
$imp_folder = $GLOBALS['injector']->getInstance('IMP_Folder');
if ($this->_usepop ||
!empty($options['nuke']) ||
($use_trash && ($mbox == $trash)) ||
- ($use_vtrash && ($GLOBALS['injector']->getInstance('IMP_Search')->isVTrashFolder($mbox)))) {
+ ($imp_search->isVTrash($mbox))) {
/* Purge messages immediately. */
$expunge_now = true;
- } else {
+ } elseif ($use_vtrash) {
/* If we are using virtual trash, we must mark the message
- * as seen or else it will appear as an 'unseen' message for
- * purposes of new message counts. */
- if ($use_vtrash) {
- $del_flags[] = '\\seen';
- }
+ * as seen or else it will appear as an 'unseen' message
+ * for purposes of new message counts. */
+ $del_flags[] = '\\seen';
}
try {
- $imp_imap->store($mbox, array('add' => array('\\deleted'), 'ids' => $msgIndices));
+ $imp_imap->store($mbox, array('add' => $del_flags, 'ids' => $msgIndices));
if ($expunge_now) {
$this->expungeMailbox(
$imp_indices->indices(),
/* If in Virtual Inbox, we need to reset flag to unseen so that it
* appears again in the mailbox list. */
- if ($GLOBALS['injector']->getInstance('IMP_Search')->isVINBOXFolder($mbox) &&
+ if ($GLOBALS['injector']->getInstance('IMP_Search')->isVinbox($mbox) &&
($pos = array_search('\\seen', $res['flags']))) {
unset($res['flags'][$pos]);
}
foreach (array_keys($mbox_list) as $key) {
if (!$imp_imap->isReadOnly($key)) {
if ($imp_search->isSearchMbox($key)) {
- foreach ($imp_search->getSearchFolders($key) as $skey) {
+ foreach ($imp_search->getSearchMailboxes($key) as $skey) {
$process_list[$skey] = $mbox_list[$key];
}
} else {
continue;
}
- if ($imp_search->isVTrashFolder($mbox)) {
- $this->expungeMailbox(array_flip($imp_search->getSearchFolders($mbox)));
+ if ($imp_search->isVTrash($mbox)) {
+ $this->expungeMailbox(array_flip($imp_search->getSearchMailboxes($mbox)));
$notification->push(_("Emptied all messages from Virtual Trash Folder."), 'horde.success');
continue;
}
/* Perform the search to find the other parts of the message. */
$query = new Horde_Imap_Client_Search_Query();
$query->headerText('Content-Type', $id);
- $indices = $GLOBALS['injector']->getInstance('IMP_Search')->runSearchQuery($query, $this->getConfigParam('imp_contents')->getMailbox());
+ $indices = $GLOBALS['injector']->getInstance('IMP_Search')->runQuery($query, $this->getConfigParam('imp_contents')->getMailbox());
/* If not able to find the other parts of the message, prepare a
* status message. */
const PREF_FOLDER_PAGE = 'folders.php';
const PREF_NO_FOLDER = "nofolder\0";
const PREF_SPECIALUSE = "specialuse\0";
- const PREF_VTRASH = "vtrash\0";
/**
* Cached folder list.
}
break;
+ case 'searches':
+ if ($prefs->isLocked('vfolder')) {
+ $ui->suppress[] = 'searchesmanagement';
+ } else {
+ Horde::addScriptFile('searchesprefs.js', 'imp');
+ }
+ break;
+
case 'server':
$code = array();
$code['spam'] = _("Enter the name for your new spam folder.");
}
- if (!$prefs->isLocked('trash_folder') &&
- !$prefs->isLocked('use_vtrash')) {
+ if (!$prefs->isLocked('trash_folder')) {
$code['trash'] = _("Enter the name for your new trash folder.");
} else {
$ui->suppress[] = 'trashselect';
/* Hide appropriate prefGroups. */
if ($pop3) {
- $ui->suppressGroups[] = 'server';
$ui->suppressGroups[] = 'flags';
+ $ui->suppressGroups[] = 'searches';
+ $ui->suppressGroups[] = 'server';
}
try {
$ui->suppressGroups[] = 'smime';
}
+ if (empty($conf['user']['allow_folders'])) {
+ $ui->suppressGroups[] = 'searches';
+ }
+
// TODO: For now, disable this group since accounts code has not
// yet been fully written.
$ui->suppressGroups[] = 'accounts';
case 'pgppublickey':
return $this->_pgpPublicKey($ui);
+ case 'searchesmanagement':
+ return $this->_searchesManagement();
+
case 'sentmailselect':
return $this->_sentmail();
$this->_updatePgpPublicKey($ui);
return false;
+ case 'searchesmanagement':
+ $this->_updateSearchesManagement($ui);
+ return false;
+
case 'sentmailselect':
return $this->_updateSentmail($ui);
* trash is active. */
if (($prefs->isDirty('use_trash') || $prefs->isDirty('trash_folder')) &&
$prefs->getValue('use_trash') &&
- !$prefs->getValue('trash_folder') &&
- !$prefs->getValue('use_vtrash')) {
+ !$prefs->getValue('trash_folder')) {
$GLOBALS['notification']->push(_("You have activated move to Trash but no Trash folder is defined. You will be unable to delete messages until you set a Trash folder in the preferences."), 'horde.warning');
}
if ($prefs->isDirty('use_trash')) {
$ui->suppress = array_diff($ui->suppress, array('trashselect', 'empty_trash_menu'));
}
- if ($prefs->isDirty('use_vtrash')) {
- $GLOBALS['injector']->getInstance('IMP_Search')->init(true);
- }
break;
case 'dimp':
break;
case 'server':
- if ($prefs->isDirty('use_vinbox')) {
- $GLOBALS['injector']->getInstance('IMP_Search')->init(true);
- }
-
if ($prefs->isDirty('subscribe')) {
$GLOBALS['registry']->getApiInstance('imp', 'application')->mailboxesChanged();
}
}
}
+ /* Saved Searches management. */
+
+ /**
+ * Create code for saved searches management.
+ *
+ * @return string HTML UI code.
+ */
+ protected function _searchesManagement()
+ {
+ $t = $GLOBALS['injector']->createInstance('Horde_Template');
+ $t->setOption('gettext', true);
+
+ $imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
+ $mailboxids = $out = array();
+ $view_mode = IMP::getViewMode();
+
+ $imp_search->setIteratorFilter(IMP_Search::LIST_VFOLDER | IMP_Search::LIST_DISABLED);
+ foreach ($imp_search as $key => $val) {
+ if (!$val->prefDisplay) {
+ continue;
+ }
+
+ $editable = $imp_search->isVFolder($val, true);
+ $m_url = ($val->enabled && ($view_mode == 'imp'))
+ ? IMP::generateIMPUrl('mailbox.php', strval($val))->link(array('class' => 'vfolderenabled'))
+ : null;
+
+ if ($view_mode == 'dimp') {
+ $mailboxids['enable_' . $key] = strval($val);
+ }
+
+ $out[] = array(
+ 'description' => Horde_String::truncate($val->querytext, 200),
+ 'edit' => ($editable ? $imp_search->editUrl($val) : null),
+ 'enabled' => $val->enabled,
+ 'key' => $key,
+ 'label' => htmlspecialchars($val->label),
+ 'm_url' => $m_url
+ );
+
+ }
+ $t->set('vfolders', $out);
+
+ Horde::addInlineJsVars(array(
+ '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');
+ }
+
+ /**
+ * Update Saved Searches related preferences.
+ *
+ * @param Horde_Core_Prefs_Ui $ui The UI object.
+ */
+ protected function _updateSearchesManagement($ui)
+ {
+ $imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
+
+ switch ($ui->vars->searches_action) {
+ case 'delete':
+ /* 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');
+ unset($imp_search[$key]);
+ }
+ break;
+
+ default:
+ /* Update enabled status. */
+ $imp_search->setIteratorFilter(IMP_Search::LIST_VFOLDER | IMP_Search::LIST_DISABLED);
+ $vfolders = array();
+
+ foreach ($imp_search as $key => $val) {
+ $form_key = 'enable_' . $key;
+ $val->enabled = !empty($ui->vars->$form_key);
+ $vfolders[$key] = $val;
+ }
+ $imp_search->setVFolders($vfolders);
+ break;
+ }
+ }
+
/* Sentmail selection. */
/**
*/
protected function _trash()
{
- $t = $GLOBALS['injector']->createInstance('Horde_Template');
- $t->setOption('gettext', true);
+ global $injector, $prefs;
- $use_vtrash = $GLOBALS['prefs']->getValue('use_vtrash');
+ $imp_search = $injector->getInstance('IMP_Search');
+ $trash_folder = IMP::folderPref($prefs->getValue('trash_folder'), true);
+
+ $t = $injector->createInstance('Horde_Template');
+ $t->setOption('gettext', true);
$t->set('label', Horde::label('trash', _("Trash folder:")));
$t->set('nofolder', IMP::formMbox(self::PREF_NO_FOLDER, true));
- $t->set('vtrash', IMP::formMbox(self::PREF_VTRASH, true));
- $t->set('vtrash_select', $use_vtrash);
$t->set('flist', IMP::flistSelect(array(
'filter' => array('INBOX'),
'new_folder' => true,
- 'selected' => ($use_vtrash ? null : IMP::folderPref($GLOBALS['prefs']->getValue('trash_folder'), true))
+ 'selected' => $trash_folder
)));
$t->set('special_use', $this->_getSpecialUse(IMP_Folder::$specialUse['trash']));
+ if (!$prefs->isLocked('vfolder') || $imp_search['vtrash']->enabled) {
+ $t->set('vtrash', IMP::formMbox($imp_search->createSearchId('vtrash'), true));
+ $t->set('vtrash_select', $imp_search->isVTrash($trash_folder));
+ }
+
return $t->fetch(IMP_TEMPLATES . '/prefs/trash.html');
}
*/
protected function _updateTrash($ui)
{
- global $prefs;
-
+ $imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
$trash = IMP::formMbox($ui->vars->trash, false);
- if ($trash == self::PREF_VTRASH) {
- if (!$prefs->isLocked('use_vtrash')) {
- $prefs->setValue('use_vtrash', 1);
- $prefs->setValue('trash_folder', '');
- return true;
- }
- } elseif ($this->_updateSpecialFolders('trash_folder', $trash, $ui->vars->trash_new, 'trash', $ui)) {
- $prefs->setValue('use_vtrash', 0);
- $prefs->setDirty('trash_folder', true);
- return true;
+ if (!$GLOBALS['prefs']->isLocked('vfolder')) {
+ $vtrash = $imp_search['vtrash'];
+ $vtrash->enabled = $imp_search->isVTrash($trash);
+ $imp_search['vtrash'] = $vtrash;
}
- return false;
+ return $this->_updateSpecialFolders('trash_folder', $trash, $ui->vars->trash_new, 'trash', $ui);
}
/* Utility functions. */
* @license http://www.fsf.org/copyleft/gpl.html GPL
* @package IMP
*/
-class IMP_Search implements Serializable
+class IMP_Search implements ArrayAccess, Iterator, Serializable
{
/* The mailbox search prefix. */
const MBOX_PREFIX = "impsearch\0";
const DIMP_FILTERSEARCH = 'dimpfsearch';
const DIMP_QUICKSEARCH = 'dimpqsearch';
- /* Bitmask constants for listQueries(). */
+ /* Bitmask filters for iterator. */
const LIST_SEARCH = 1;
const LIST_VFOLDER = 2;
- const NO_BASIC_SEARCH = 4;
+ const LIST_DISABLED = 4;
+
+ /* Query creation types. */
+ const CREATE_QUERY = 1;
+ const CREATE_VFOLDER = 2;
/**
* Has the object data changed?
public $changed = false;
/**
- * Save Virtual Folder information when adding entries?
+ * Iterator filter mask.
*
- * @var boolean
+ * @var integer
*/
- protected $_saveVFolder = true;
+ protected $_filter = 0;
/**
- * Cached data.
+ * Iterator pointer.
*
* @var array
*/
- protected $_cache = array();
+ protected $_iteratorPtr;
/**
* Search queries.
*
- * Format:
- * <pre>
- * 'id' => array(
- * '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 (label) of search,
- * 'q' => (Horde_Imap_Client_Search_Query) [serialized],
- * 'v' => (boolean) True if this is a Virtual Folder
- * )
- * </pre>
- *
- * The object properties for the 'c' (search criteria) object:
- * <pre>
- * 't' - (string) 'Type' - The criteria type
- * Values: Keys from self::searchFields(), 'flag', and 'or'.
- * 'v' - (mixed) 'Value' - The data used to build the search
- * 'header' - (string) The value to search for in the header
- * 'customhdr' - (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' - (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
- * 'within' - (object) Contains 2 elements:
- * 'l' - (string) The length of time. Either 'y'
- * (years), 'm' (months), or 'd' (days)
- * 'v' - (integer) The length of time
- * '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
- * </pre>
+ * Each subarray contains:
+ * Keys: mailbox IDs.
+ * Values: IMP_Search_Query objects.
*
* @var array
*/
- protected $_search = array();
+ protected $_search = array(
+ 'query' => array(),
+ 'vfolders' => array()
+ );
/**
- * Serialize.
- *
- * @return string Serialized representation of this object.
+ * Initialize session search data.
*/
- public function serialize()
+ public function init()
{
- return serialize($this->_search);
- }
-
- /**
- * Unserialize.
- *
- * @param string $data Serialized data.
- *
- * @throws Exception
- */
- public function unserialize($data)
- {
- $data = @unserialize($data);
- if (!is_array($data)) {
- throw new Exception('Cache version change');
- }
-
- $this->_search = $data;
- $this->changed = true;
- }
-
- /**
- * Initialize search data for a session.
- *
- * @param boolean $no_vf Don't readd the Virtual Folders.
- */
- public function init($no_vf = false)
- {
- if (!$no_vf) {
- $imaptree = $GLOBALS['injector']->getInstance('IMP_Imap_Tree');
- foreach ($this->_getVFolderList() as $key => $val) {
- if (!empty($val['v']) &&
- !$this->isEditableVFolder($key)) {
- $imaptree->insertVFolders(array($key => $val['l']));
- unset($val['c']);
- $this->_search[$key] = $val;
- $this->changed = true;
- }
- }
- }
-
- $this->createVINBOXFolder();
- $this->createVTrashFolder();
- }
-
- /**
- * Return the base search fields.
- *
- * @return array The base search fields.
- */
- public function searchFields()
- {
- return array(
- 'from' => array(
- 'label' => _("From"),
- 'type' => 'header',
- 'not' => true
- ),
- 'to' => array(
- 'label' => _("To"),
- 'type' => 'header',
- 'not' => true
- ),
- 'cc' => array(
- 'label' => _("Cc"),
- 'type' => 'header',
- 'not' => true
- ),
- 'bcc' => array(
- 'label' => _("Bcc"),
- 'type' => 'header',
- 'not' => true
- ),
- 'subject' => array(
- 'label' => _("Subject"),
- 'type' => 'header',
- 'not' => true
- ),
- 'customhdr' => array(
- 'label' => _("Custom Header"),
- 'type' => 'customhdr',
- 'not' => true
- ),
- 'body' => array(
- 'label' => _("Body"),
- 'type' => 'body',
- 'not' => true
- ),
- 'text' => array(
- 'label' => _("Entire Message"),
- 'type' => 'text',
- 'not' => true
- ),
- 'date_on' => array(
- 'label' => _("Date Equals (=)"),
- 'type' => 'date',
- 'not' => true
- ),
- 'date_until' => array(
- 'label' => _("Date Until (<)"),
- 'type' => 'date',
- 'not' => true
- ),
- 'date_since' => array(
- 'label' => _("Date Since (>=)"),
- 'type' => 'date',
- 'not' => true
- ),
- 'older' => array(
- 'label' => _("Older Than"),
- 'type' => 'within',
- 'not' => true
- ),
- 'younger' => array(
- 'label' => _("Younger Than"),
- 'type' => 'within',
- 'not' => true
- ),
- // Displayed in KB, but stored internally in bytes
- 'size_smaller' => array(
- 'label' => _("Size (KB) <"),
- 'type' => 'size',
- 'not' => false
- ),
- // Displayed in KB, but stored internally in bytes
- 'size_larger' => array(
- 'label' => _("Size (KB) >"),
- 'type' => 'size',
- 'not' => false
- ),
- );
- }
-
- /**
- * Return the base flag fields.
- *
- * @return array The base flag fields.
- */
- public function flagFields()
- {
- $flags = array();
- $flist = $GLOBALS['injector']->getInstance('IMP_Imap_Flags')->getFlagList(null);
-
- for ($i = 0, $cnt = count($flist['set']); $i < $cnt; ++$i) {
- $flags[$flist['set'][$i]['f']] = $flist['set'][$i]['l'];
- $flags[$flist['unset'][$i]['f']] = sprintf(_("Not %s"), $flist['unset'][$i]['l']);
- }
-
- return $flags;
+ $this->setVFolders($this->getVFolders(), false);
}
/**
$mbox = '';
$sorted = new IMP_Indices();
- if (empty($this->_search[$id])) {
+ $query = $this[$id]->query;
+ if (!$query) {
return $sorted;
}
/* Prepare the search query. */
- $query = unserialize($this->_search[$id]['q']);
if (!empty($ob)) {
$query->andSearch(array($ob));
}
$sortpref['by'] = $GLOBALS['prefs']->getValue('sortdate');
}
- foreach ($this->_search[$id]['f'] as $val) {
+ foreach ($this[$id]->mboxes as $val) {
$results = $this->imapSearch($val, $query, array('reverse' => $sortpref['dir'], 'sort' => array($sortpref['by'])));
$sorted->add($val, $results['sort']);
}
*
* @return IMP_Indices An indices object.
*/
- public function runSearchQuery($query, $mailbox, $sortby = null,
- $sortdir = null)
+ public function runQuery($query, $mailbox, $sortby = null,
+ $sortdir = null)
{
try {
$results = $this->imapSearch($mailbox, $query, array('reverse' => $sortdir, 'sort' => is_null($sortby) ? null : array($sortby)));
/**
* Creates the IMAP search query in the IMP session.
*
- * @param object $query The search query object
- * (Horde_Imap_Client_Search_Query).
- * @param array $folders The list of folders to search.
* @param array $criteria The search criteria array.
+ * @param array $mboxes The list of mailboxes to search.
* @param string $label The label to use for the search results.
- * @param string $id The query id (otherwise, one is
- * automatically generated).
+ * @param integer $type Query type.
+ * @param string $id Use as the mailbox ID.
*
- * @return string Returns the search query id.
+ * @return IMP_Search_Query Returns the query object.
*/
- public function createSearchQuery($query, $folders, $criteria, $label,
- $id = null)
+ public function createQuery($criteria, $mboxes, $label = null,
+ $type = self::CREATE_QUERY, $id = null)
{
- $id = is_null($id)
- ? strval(new Horde_Support_Randomid())
- : $this->_strip($id);
-
- $this->_search[$id] = array(
- 'c' => $criteria,
- 'f' => $folders,
- 'l' => $label,
- 'q' => serialize($query),
- 'v' => false
- );
+ if (!is_null($id)) {
+ $id = $this->_strip($id);
+ }
+
+ switch ($type) {
+ case self::CREATE_QUERY:
+ $cname = 'IMP_Search_Query';
+ break;
+
+ case self::CREATE_VFOLDER:
+ $cname = 'IMP_Search_Vfolder';
+ break;
+ }
+
+ $ob = new $cname(array_filter(array(
+ 'add' => $criteria,
+ 'id' => $id,
+ 'label' => $label,
+ 'mboxes' => $mboxes
+ )));
+
+ switch ($type) {
+ case self::CREATE_QUERY:
+ $this->_search['query'][$ob->id] = $ob;
+ break;
+
+ case self::CREATE_VFOLDER:
+ /* This will overwrite previous value, if it exists. */
+ $this->_search['vfolders'][$ob->id] = $ob;
+ $this->setVFolders($this->_search['vfolders']);
+ break;
+ }
+
$this->changed = true;
- return $id;
+ return $ob;
}
/**
- * Deletes an IMAP search query.
- *
- * @param string $id The search query id.
- * @param boolean $no_delete Don't delete the entry in the tree object.
+ * Obtains the list of virtual folders for the current user.
*
- * @return string Returns the search query id.
+ * @return array The list of virtual folders. Keys are mailbox IDs,
+ * values are IMP_Search_Vfolder objects.
*/
- public function deleteSearchQuery($id, $no_delete = false)
+ public function getVFolders()
{
- $id = $this->_strip($id);
- $is_vfolder = $this->isVFolder($id);
- unset($this->_search[$id]);
- $this->changed = true;
-
- if ($is_vfolder) {
- $vfolders = $this->_getVFolderList();
- unset($vfolders[$id]);
- $this->_saveVFolderList($vfolders);
+ if ($pref_vf = $GLOBALS['prefs']->getValue('vfolder')) {
+ $pref_vf = @unserialize($pref_vf);
+ }
- if (!$no_delete) {
- $GLOBALS['injector']->getInstance('IMP_Imap_Tree')->delete($id);
+ $has_vinbox = $has_vtrash = false;
+ $vf = array();
+
+ if (is_array($pref_vf)) {
+ foreach ($pref_vf as $val) {
+ if ($val instanceof IMP_Search_Vfolder) {
+ $vf[$val->id] = $val;
+
+ if (!$has_vinbox &&
+ ($val instanceof IMP_Search_Vfolder_Vinbox)) {
+ $has_vinbox = true;
+ } elseif (!$has_vtrash &&
+ ($val instanceof IMP_Search_Vfolder_Vtrash)) {
+ $has_vtrash = true;
+ }
+ }
}
}
- }
- /**
- * Retrieves the previously stored search criteria information.
- *
- * @param string $id The search query id.
- *
- * @return array The array necessary to rebuild the search UI page.
- */
- public function getCriteria($id)
- {
- $id = $this->_strip($id);
- if (isset($this->_search[$id]['c'])) {
- return $this->_search[$id]['c'];
+ if (!$has_vtrash) {
+ $ob = new IMP_Search_Vfolder_Vtrash(array(
+ 'disable' => true
+ ));
+ $vf = array($ob->id => $ob) + $vf;
}
- if ($this->isVFolder($id)) {
- $vlist = $this->_getVFolderList();
- return $vlist[$id]['c'];
+ if (!$has_vinbox) {
+ $ob = new IMP_Search_Vfolder_Vinbox(array(
+ 'disable' => true
+ ));
+ $vf = array($ob->id => $ob) + $vf;
}
- return array();
+ return $vf;
}
/**
- * Generates the label to use for search results.
- *
- * @param string $id The search query id.
+ * Saves the list of virtual folders for the current user.
*
- * @return string The search results label.
+ * @param array $vfolders The virtual folder list.
+ * @param boolean $save Save the virtual folder list to the preference
+ * backend?
*/
- public function getLabel($id)
+ public function setVFolders($vfolders, $save = true)
{
- $id = $this->_strip($id);
- return isset($this->_search[$id]['l'])
- ? $this->_search[$id]['l']
- : '';
+ if ($save) {
+ $GLOBALS['prefs']->setValue('vfolder', serialize(array_values($vfolders)));
+ }
+
+ $GLOBALS['injector']->getInstance('IMP_Imap_Tree')->updateVFolders($vfolders);
+ $this->_search['vfolders'] = $vfolders;
+ $this->changed = true;
}
/**
- * Obtains the list of virtual folders for the current user.
+ * Is a mailbox a virtual folder?
*
- * @return array The list of virtual folders.
+ * @param string $id The mailbox ID.
+ * @param boolean $editable Is this an editable (i.e. not built-in)
+ * virtual folder?
+ *
+ * @return boolean Whether the mailbox ID is a virtual folder.
*/
- protected function _getVFolderList()
+ public function isVFolder($id, $editable = false)
{
- if (!isset($this->_cache['vfolder'])) {
- if ($vf = $GLOBALS['prefs']->getValue('vfolder')) {
- $vf = @unserialize($vf);
- }
-
- if (empty($vf) || !is_array($vf)) {
- $vf = array();
- }
-
- $this->_cache['vfolder'] = $vf;
- }
-
- return $this->_cache['vfolder'];
+ return (isset($this->_search['vfolders'][$this->_strip($id)]) &&
+ (!$editable || $this[$id]->canEdit));
}
/**
- * Saves the list of virtual folders for the current user.
+ * Determines whether a mailbox ID is the Virtual Trash Folder.
*
- * @param array The virtual folder list.
+ * @param string $id The mailbox id.
+ *
+ * @return boolean True if the ID is the Virtual Trash folder.
*/
- protected function _saveVFolderList($vfolder)
+ public function isVTrash($id)
{
- $GLOBALS['prefs']->setValue('vfolder', serialize($vfolder));
- $this->_cache['vfolder'] = $vfolder;
+ return (($this->isVFolder($id)) &&
+ ($this[$id] instanceof IMP_Search_Vfolder_Vtrash));
}
/**
- * Add a virtual folder for the current user.
+ * Determines whether a mailbox ID is the Virtual INBOX Folder.
*
- * @param object $query The search query object
- * (Horde_Imap_Client_Search_Query).
- * @param array $folders The list of folders to search.
- * @param array $search The search array used to build the search UI
- * screen.
- * @param string $label The label to use for the search results.
- * @param string $id The virtual folder id.
+ * @param string $id The mailbox id.
*
- * @return string The virtual folder ID.
+ * @return boolean True if the ID is the Virtual INBOX folder.
*/
- public function addVFolder($query, $folders, $search, $label, $id = null)
+ public function isVinbox($id)
{
- $id = $this->createSearchQuery($query, $folders, $search, $label, $id);
- $this->_search[$id]['v'] = true;
- $this->changed = true;
-
- if ($this->_saveVFolder) {
- $vfolders = $this->_getVFolderList();
- $vfolders[$id] = $this->_search[$id];
- $this->_saveVFolderList($vfolders);
- }
-
- $GLOBALS['injector']->getInstance('IMP_Imap_Tree')->insertVFolders(array($id => $label));
-
- return $id;
+ return (($this->isVFolder($id)) &&
+ ($this[$id] instanceof IMP_Search_Vfolder_Vinbox));
}
/**
- * Add a virtual trash folder for the current user.
+ * Is a mailbox a search query?
+ *
+ * @param string $id The mailbox ID.
+ * @param boolean $editable Is this an editable (i.e. not built-in)
+ * search query?
*/
- public function createVTrashFolder()
+ public function isQuery($id, $editable = false)
{
- /* Delete the current Virtual Trash folder, if it exists. */
- $vtrash_id = $GLOBALS['prefs']->getValue('vtrash_id');
- if (!empty($vtrash_id)) {
- $this->deleteSearchQuery($vtrash_id, true);
- }
-
- if (!$GLOBALS['prefs']->getValue('use_vtrash')) {
- return;
- }
-
- /* Create Virtual Trash with new folder list. */
- $flist = array_keys(iterator_to_array($GLOBALS['injector']->getInstance('IMP_Imap_Tree')));
-
- $query = new Horde_Imap_Client_Search_Query();
- $query->flag('\\deleted', true);
- $label = _("Virtual Trash");
-
- $this->_saveVFolder = false;
- if (empty($vtrash_id)) {
- $vtrash_id = $this->addVFolder($query, $flist, array(), $label);
- $GLOBALS['prefs']->setValue('vtrash_id', $vtrash_id);
- } else {
- $this->addVFolder($query, $flist, array(), $label, $vtrash_id);
- }
- $this->_saveVFolder = true;
+ return (isset($this->_search['query'][$this->_strip($id)]) &&
+ (!$editable ||
+ !in_array($this[$id]->id, array(self::BASIC_SEARCH, self::DIMP_FILTERSEARCH, self::DIMP_QUICKSEARCH))));
}
/**
- * Determines whether a virtual folder ID is the Virtual Trash Folder.
+ * Get the list of searchable folders for the given search query.
*
* @param string $id The search query id.
*
- * @return boolean True if the ID is the Virtual Trash folder.
+ * @return array The list of searchable folders.
*/
- public function isVTrashFolder($id)
+ public function getSearchMailboxes($id)
{
- $vtrash_id = $GLOBALS['prefs']->getValue('vtrash_id');
- return (!empty($vtrash_id) && ($this->_strip($id) == $vtrash_id));
+ return isset($this[$id])
+ ? $this[$id]->mboxes
+ : array();
}
/**
- * Add a virtual INBOX folder for the current user.
+ * Returns a link to edit a given search query.
+ *
+ * @param string $id The search query id.
+ *
+ * @return Horde_Url The URL to the search page.
*/
- public function createVINBOXFolder()
+ public function editUrl($id)
{
- /* Delete the current Virtual Inbox folder, if it exists. */
- $vinbox_id = $GLOBALS['prefs']->getValue('vinbox_id');
- if (!empty($vinbox_id)) {
- $this->deleteSearchQuery($vinbox_id, true);
- }
-
- if (!$GLOBALS['prefs']->getValue('use_vinbox')) {
- return;
- }
-
- /* Create Virtual INBOX with nav_poll list. */
- $flist = $GLOBALS['injector']->getInstance('IMP_Imap_Tree')->getPollList();
-
- $query = new Horde_Imap_Client_Search_Query();
- $query->flag('\\seen', false);
- $query->flag('\\deleted', false);
- $label = _("Virtual INBOX");
-
- $this->_saveVFolder = false;
- if (empty($vinbox_id)) {
- $vinbox_id = $this->addVFolder($query, $flist, array(), $label);
- $GLOBALS['prefs']->setValue('vinbox_id', $vinbox_id);
- } else {
- $this->addVFolder($query, $flist, array(), $label, $vinbox_id);
- }
- $this->_saveVFolder = true;
+ return Horde::url('search.php')->add(array('edit_query' => $this->createSearchId($id)));
}
/**
- * Determines whether a virtual folder ID is the Virtual INBOX Folder.
+ * Is the given mailbox a search mailbox?
*
- * @param string $id The search query id.
+ * @param string $id The mailbox name.
*
- * @return boolean True if the ID is the Virtual INBOX folder.
+ * @return boolean Whether the given mailbox name is a search mailbox.
*/
- public function isVINBOXFolder($id)
+ public function isSearchMbox($id)
{
- $vinbox_id = $GLOBALS['prefs']->getValue('vinbox_id');
- return (!empty($vinbox_id) && ($this->_strip($id) == $vinbox_id));
+ return (strpos($id, self::MBOX_PREFIX) === 0);
}
/**
- * Is a mailbox an editable Virtual Folder?
+ * Strip the identifying label from a mailbox ID.
*
- * @param string $id The search query id.
+ * @param string $id The mailbox query ID.
*
- * @return boolean True if the mailbox is both a virtual folder and can
- * be edited.
+ * @return string The virtual folder ID, with any IMP specific
+ * identifying information stripped off.
*/
- public function isEditableVFolder($id)
+ protected function _strip($id)
{
- $id = $this->_strip($id);
- return ($this->isVFolder($id) &&
- !$this->isVTrashFolder($id) &&
- !$this->isVINBOXFolder($id));
+ return $this->isSearchMbox($id)
+ ? substr($id, strlen(self::MBOX_PREFIX))
+ : $id;
}
/**
- * Return a list of queryies.
+ * Create the canonical search ID for a given search query.
*
- * @param integer $mask A bitmask of the query types to return.
- * IMP_Search::LIST_SEARCH,
- * IMP_Search::LIST_VFOLDER, and/or
- * IMP_Search::NO_BASIC_SEARCH.
- * @param boolean $label If true, returns the label. Otherwise, returns
- * a textual representation.
+ * @param string $id The mailbox query ID.
*
- * @return array An array with the folder IDs as the key and the labels
- * as the value.
+ * @return string The canonical search query ID.
*/
- public function listQueries($mask = null, $label = true)
+ public function createSearchId($id)
{
- $folders = array();
-
- if (empty($this->_search)) {
- return $folders;
- }
+ return self::MBOX_PREFIX . $this->_strip($id);
+ }
- if (is_null($mask)) {
- $mask = self::LIST_SEARCH | self::LIST_VFOLDER;
- }
+ /* ArrayAccess methods. */
- foreach ($this->_search as $key => $val) {
- if ((($mask & self::LIST_VFOLDER) && !empty($val['v'])) ||
- (($mask & self::LIST_SEARCH) && empty($val['v'])) &&
- (!($mask & self::NO_BASIC_SEARCH) ||
- ($key != self::BASIC_SEARCH))) {
- $folders[$key] = $label
- ? $this->getLabel($key)
- : $this->searchQueryText($key);
- }
- }
+ public function offsetExists($offset)
+ {
+ $id = $this->_strip($offset);
+ return (isset($this->_search['query'][$id]) ||
+ isset($this->_search['vfolders'][$id]));
+ }
- if ($label) {
- natcasesort($folders);
- return $folders;
+ 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];
}
- return array_reverse($folders, true);
+ return null;
}
/**
- * Get the list of searchable folders for the given search query.
+ * Alter the current IMAP search query.
*
- * @param string $id The search query id.
+ * @param string $id The search query id.
+ * @param IMP_Search_Query $value The query object.
*
- * @return array The list of searchable folders.
+ * @throws InvalidArgumentException
*/
- public function getSearchFolders($id)
+ public function offsetSet($offset, $value)
{
- $id = $this->_strip($id);
- return isset($this->_search[$id]['f'])
- ? $this->_search[$id]['f']
- : array();
+ if (!($value instanceof IMP_Search_Query)) {
+ throw new InvalidArgumentException('$value must be a query object.');
+ }
+
+ $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;
+ }
+
+ throw new InvalidArgumentException('Creating search queries by array index is not supported. Use createQuery() instead.');
}
/**
- * Return search query text representation for a given search ID.
+ * Deletes an IMAP search query.
*
* @param string $id The search query id.
- *
- * @return array The textual description of the search.
*/
- public function searchQueryText($id)
+ public function offsetUnset($offset)
{
- $id = $this->_strip($id);
+ $id = $this->_strip($offset);
- if (empty($this->_search[$id])) {
- return '';
- } elseif ($this->isVINBOXFolder($id) || $this->isVTrashFolder($id)) {
- return $this->_search[$id]['l'];
- }
-
- $flagfields = $this->flagFields();
- $searchfields = $this->searchFields();
- $text = '';
- $criteria = $this->getCriteria($id);
+ foreach (array_keys($this->_search) as $val) {
+ if (isset($this->_search[$val][$id])) {
+ unset($this->_search[$val][$id]);
+ $this->changed = true;
- $text = _("Search") . ' ';
- $text_array = array();
- foreach ($criteria as $rule) {
- $field = $rule->t;
-
- switch ($field) {
- case 'flag':
- if (isset($flagfields[$rule->v])) {
- $text_array[] = sprintf(_("flagged \"%s\""), $flagfields[$rule->v]);
+ if ($val == 'vfolders') {
+ $this->setVFolders($this->_search['vfolders']);
}
break;
+ }
+ }
+ }
- case 'or':
- $text .= implode(' ' . _("and") . ' ', $text_array) . ' ' . _("OR") . ' ';
- $text_array = array();
- break;
-
- default:
- switch ($searchfields[$field]['type']) {
- case 'customhdr':
- $text_array[] = sprintf("%s for '%s'", $rule->v->h, ((!empty($rule->n)) ? _("not") . ' ' : '') . $rule->v->s);
- break;
-
- case 'date':
- $date_ob = new Horde_Date($rule->v);
- $text_array[] = sprintf("%s '%s'", $searchfields[$field]['label'], $date_ob->strftime("%x"));
- break;
+ /* Iterator methods. */
- case 'within':
- $text_array[] = sprintf("%s %u %s", $searchfields[$field]['label'], $rule->v->v, $rule->v->l == 'y' ? _("years") : ($rule->v->l == 'm' ? _("months") : _("days")));
- break;
+ public function current()
+ {
+ return (($key = key($this->_search)) !== null)
+ ? current($this->_search[$key])
+ : null;
+ }
- case 'size':
- $text_array[] = $searchfields[$field]['label'] . ' ' . ($rule->v / 1024);
- break;
+ public function key()
+ {
+ return (($key = key($this->_search)) !== null)
+ ? key($this->_search[$key])
+ : null;
+ }
- default:
- $text_array[] = sprintf("%s for '%s'", $searchfields[$field]['label'], ((!empty($rule->n)) ? _("not") . ' ' : '') . $rule->v);
- break;
+ public function next()
+ {
+ $curr = null;
+
+ while (($skey = key($this->_search)) !== null) {
+ /* When switching between search types, need to catch the first
+ * element of the new array. */
+ $curr = is_null($curr)
+ ? next($this->_search[$skey])
+ : current($this->_search[$skey]);
+
+ while ($curr !== false) {
+ if ($this->_currValid()) {
+ return;
}
+ $curr = next($this->_search[$skey]);
}
- }
- return $text . implode(' ' . _("and") . ' ', $text_array) . ' ' . _("in") . ' ' . implode(', ', $this->getSearchFolders($id));
+ next($this->_search);
+ }
}
- /**
- * Returns a link to edit a given search query.
- *
- * @param string $id The search query id.
- *
- * @return Horde_Url The URL to the search page.
- */
- public function editUrl($id)
+ public function rewind()
{
- return Horde::url('search.php')->add(array('edit_query' => $this->createSearchId($id)));
+ foreach (array_keys($this->_search) as $key) {
+ reset($this->_search[$key]);
+ }
+ reset($this->_search);
+
+ if ($this->valid() && !$this->_currValid()) {
+ $this->next();
+ }
}
- /**
- * Returns a link to delete a given search query.
- *
- * @param string $id The search query id.
- *
- * @return Horde_Url The URL to allow deletion of the search query.
- */
- public function deleteUrl($id)
+ public function valid()
{
- return Horde::url('folders.php')->add(array(
- 'actionID' => 'delete_search_query',
- 'folders_token' => Horde::getRequestToken('imp.folders'),
- 'queryid' => $this->createSearchId($id)
- ));
+ return (key($this->_search) !== null);
}
+ /* Helper functions for Iterator methods. */
+
/**
- * Is the given mailbox a search mailbox?
- *
- * @param string $id The mailbox name.
+ * Set the current iterator filter and reset the internal pointer.
*
- * @return boolean Whether the given mailbox name is a search mailbox.
+ * @param integer $mask A mask with the following possible elements:
+ * <pre>
+ * IMP_Search::LIST_SEARCH
+ * IMP_Search::LIST_VFOLDER
+ * IMP_Search::NO_BASIC_SEARCH.
+ * </pre>
*/
- public function isSearchMbox($id)
+ public function setIteratorFilter($mask = 0)
{
- return (strpos($id, self::MBOX_PREFIX) === 0);
+ $this->_filter = $mask;
+ reset($this);
}
/**
- * Is the given mailbox a virtual folder?
- *
- * @param string $id The search query id.
+ * Returns true if the current object is valid given the current filter.
*
- * @return boolean Whether the given mailbox name is a virtual folder.
+ * @return boolean True if the object should be displayed.
*/
- public function isVFolder($id)
+ protected function _currValid()
{
- return !empty($this->_search[$this->_strip($id)]['v']);
+ if (!($ob = $this->current())) {
+ return false;
+ }
+
+ if ($ob->enabled ||
+ ($this->_filter & self::LIST_DISABLED)) {
+ if (!$this->_filter) {
+ return true;
+ }
+
+ if (($this->_filter & self::LIST_VFOLDER) &&
+ $this->isVfolder($ob)) {
+ return true;
+ }
+
+ if (($this->_filter & self::LIST_SEARCH) &&
+ !$this->isVfolder($ob)) {
+ return true;
+ }
+
+ if ($this->isQuery($ob, true)) {
+ return true;
+ }
+ }
+
+ return false;
}
+ /* Serializable methods. */
+
/**
- * Strip the identifying label from a mailbox ID.
- *
- * @param string $id The mailbox query ID.
+ * Serialize.
*
- * @return string The virtual folder ID, with any IMP specific
- * identifying information stripped off.
+ * @return string Serialized representation of this object.
*/
- protected function _strip($id)
+ public function serialize()
{
- return $this->isSearchMbox($id)
- ? substr($id, strlen(self::MBOX_PREFIX))
- : $id;
+ return serialize($this->_search);
}
/**
- * Create the canonical search ID for a given search query.
+ * Unserialize.
*
- * @param string $id The mailbox query ID.
+ * @param string $data Serialized data.
*
- * @return string The canonical search query ID.
+ * @throws Exception
*/
- public function createSearchId($id)
+ public function unserialize($data)
{
- return self::MBOX_PREFIX . $this->_strip($id);
+ $data = @unserialize($data);
+ if (!is_array($data)) {
+ throw new Exception('Cache version change');
+ }
+
+ $this->_search = $data;
}
}
--- /dev/null
+<?php
+/**
+ * This class provides the framework for a search query element.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/gpl.html GPL
+ * @package IMP
+ */
+abstract class IMP_Search_Element implements Serializable
+{
+ /* Serialized version. */
+ const VERSION = 1;
+
+ /**
+ * Allow NOT search on this element?
+ *
+ * @var boolean
+ */
+ public $not = true;
+
+ /**
+ * Data for this element.
+ *
+ * @var object
+ */
+ protected $_data;
+
+ /**
+ * Adds the current query item to the query object.
+ *
+ * @param Horde_Imap_Client_Search_Query The query object.
+ */
+ abstract public function createQuery($queryob);
+
+ /**
+ * Return search query text representation.
+ *
+ * @return array The textual description of this search element.
+ */
+ abstract public function queryText();
+
+ /**
+ * Returns the criteria data for the element.
+ *
+ * @return object The criteria (see each class for the available
+ * properties).
+ */
+ public function getCriteria()
+ {
+ return $this->_data;
+ }
+
+ /* Serializable methods. */
+
+ /**
+ * Serialization.
+ *
+ * @return string Serialized data.
+ */
+ public function serialize()
+ {
+ return empty($this->_data)
+ ? null
+ : json_encode(array(
+ self::VERSION,
+ $this->_data
+ ));
+ }
+
+ /**
+ * Unserialization.
+ *
+ * @param string $data Serialized data.
+ *
+ * @throws Exception
+ */
+ public function unserialize($data)
+ {
+ if (empty($data)) {
+ return;
+ }
+
+ $data = json_decode($data);
+ if (!is_array($data) ||
+ !isset($data[0]) ||
+ ($data[0] != self::VERSION)) {
+ throw new Exception('Cache version change');
+ }
+
+ $this->_data = $data[1];
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * This class handles the bulk message search query.
+ *
+ * Precedence is a non-standard, discouraged header pursuant to RFC 2076
+ * [3.9]. However, it is widely used and may be useful in sorting out
+ * unwanted e-mail.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/gpl.html GPL
+ * @package IMP
+ */
+class IMP_Search_Element_Bulk extends IMP_Search_Element
+{
+ /**
+ * Constructor.
+ *
+ * @param boolean $not If true, do a 'NOT' search of $text.
+ */
+ public function __construct($not = false)
+ {
+ /* Data element: (integer) Do a NOT search? */
+ $this->_data = intval($not);
+ }
+
+ /**
+ * Adds the current query item to the query object.
+ *
+ * @param Horde_Imap_Client_Search_Query The query object.
+ *
+ * @return Horde_Imap_Client_Search_Query The query object.
+ */
+ public function createQuery($queryob)
+ {
+ $queryob->headerText('precedence', 'bulk', $this->_data);
+
+ return $queryob;
+ }
+
+ /**
+ * Return search query text representation.
+ *
+ * @return array The textual description of this search element.
+ */
+ public function queryText()
+ {
+ return ($this->_data ? _("not") . ' ' : '') . _("Bulk Messages");
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * This class handles date-related search queries.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/gpl.html GPL
+ * @package IMP
+ */
+class IMP_Search_Element_Date extends IMP_Search_Element
+{
+ /* Date types. */
+ const DATE_ON = 1;
+ const DATE_BEFORE = 2;
+ const DATE_SINCE = 3;
+
+ /**
+ * Constructor.
+ *
+ * @param DateTime $date Date object.
+ * @param integer $type Either:
+ * <pre>
+ * IMP_Search_Element_Date::DATE_ON
+ * IMP_Search_Element_Date::DATE_BEFORE
+ * IMP_Search_Element_Date::DATE_SINCE
+ * </pre>
+ */
+ public function __construct(DateTime $date, $type)
+ {
+ /* Data element:
+ * d = (integer) UNIX timestamp.
+ * t = (integer) Type: one of the self::DATE_* constants. */
+ $this->_data = new stdClass;
+ $this->_data->d = $date->format('U');
+ $this->_data->t = $type;
+ }
+
+ /**
+ * Adds the current query item to the query object.
+ *
+ * @param Horde_Imap_Client_Search_Query The query object.
+ *
+ * @return Horde_Imap_Client_Search_Query The query object.
+ */
+ public function createQuery($queryob)
+ {
+ $date = new DateTime($this->_data->d);
+ $queryob->dateSearch($date, ($this->_data->t == self::DATE_ON) ? Horde_Imap_Client_Search_Query::DATE_ON : (($this->_data->t == self::DATE_BEFORE) ? Horde_Imap_Client_Search_Query::DATE_BEFORE : Horde_Imap_Client_Search_Query::DATE_SINCE));
+
+ return $queryob;
+ }
+
+ /**
+ * Return search query text representation.
+ *
+ * @return array The textual description of this search element.
+ */
+ public function queryText()
+ {
+ switch ($this->_data->t) {
+ case self::DATE_ON:
+ $label = _("Date Equals (=)");
+ break;
+
+ case self::DATE_BEFORE:
+ $label = _("Date Until (<)");
+ break;
+
+ case self::DATE_SINCE:
+ $label = _("Date Since (>=)");
+ break;
+ }
+
+ return sprintf("%s '%s'", $label, strftime('%x', $this->_data->d));
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * This class handles flag/keyword search queries.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/gpl.html GPL
+ * @package IMP
+ */
+class IMP_Search_Element_Flag extends IMP_Search_Element
+{
+ /**
+ * Allow NOT search on this element?
+ *
+ * @var boolean
+ */
+ public $not = false;
+
+ /**
+ * Constructor.
+ *
+ * @param string $name The flag or keyword name.
+ * @param boolean $set If true, search for messages that have the flag
+ * set. If false, search for messages that do not
+ * have the flag set.
+ */
+ public function __construct($name, $set = true)
+ {
+ /* Data element:
+ * f = (string) Flag/keyword name.
+ * s = (integer) Search for set flag? */
+ $this->_data = new stdClass;
+ $this->_data->f = $name;
+ $this->_data->s = intval($set);
+ }
+
+ /**
+ * Adds the current query item to the query object.
+ *
+ * @param Horde_Imap_Client_Search_Query The query object.
+ *
+ * @return Horde_Imap_Client_Search_Query The query object.
+ */
+ public function createQuery($queryob)
+ {
+ $queryob->flag($this->_data->f, $this->_data->s);
+
+ return $queryob;
+ }
+
+ /**
+ * Return search query text representation.
+ *
+ * @return array The textual description of this search element.
+ */
+ public function queryText()
+ {
+ return sprintf(_("flagged \"%s\""), $GLOBALS['injector']->getInstance('IMP_Imap_Flags')->getLabel($this->_data->f, $this->_data->s));
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * This class handles header-related search queries.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/gpl.html GPL
+ * @package IMP
+ */
+class IMP_Search_Element_Header extends IMP_Search_Element
+{
+ /**
+ * Constructor.
+ *
+ * @param string $text The search text.
+ * @param string $header The header field.
+ * @param boolean $not If true, do a 'NOT' search of $text.
+ */
+ public function __construct($text, $header, $not = false)
+ {
+ /* Data element:
+ * h = (string) Header name (lower case).
+ * n = (integer) Do a NOT search?
+ * t = (string) The search text. */
+ $this->_data = new stdClass;
+ $this->_data->h = trim(Horde_String::lower($header));
+ $this->_data->n = intval($not);
+ $this->_data->t = $text;
+ }
+
+ /**
+ * Adds the current query item to the query object.
+ *
+ * @param Horde_Imap_Client_Search_Query The query object.
+ *
+ * @return Horde_Imap_Client_Search_Query The query object.
+ */
+ public function createQuery($queryob)
+ {
+ $queryob->headerText($this->_data->h, $this->_data->t, $this->_data->n);
+
+ return $queryob;
+ }
+
+ /**
+ * Return search query text representation.
+ *
+ * @return array The textual description of this search element.
+ */
+ public function queryText()
+ {
+ return sprintf("%s (Header) for '%s'", Horde_String::ucfirst($this->_data->h), ($this->_data->n ? _("not") . ' ' : '') . $this->_data->t);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * This class handles an OR clause in a search query.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/gpl.html GPL
+ * @package IMP
+ */
+class IMP_Search_Element_Or extends IMP_Search_Element
+{
+ /**
+ * Adds the current query item to the query object.
+ *
+ * @param Horde_Imap_Client_Search_Query The query object.
+ *
+ * @return Horde_Imap_Client_Search_Query The query object.
+ *
+ */
+ public function createQuery($queryob)
+ {
+ $ob = new Horde_Imap_Client_Search_Query();
+ $ob->orSearch(array($queryob));
+
+ return $ob;
+ }
+
+ /**
+ * Return search query text representation.
+ *
+ * @return array The textual description of this search element.
+ */
+ public function queryText()
+ {
+ return _("OR");
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * This class handles the recipient (To/Cc/Bcc) search query.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/gpl.html GPL
+ * @package IMP
+ */
+class IMP_Search_Element_Recipient extends IMP_Search_Element
+{
+ /**
+ * Constructor.
+ *
+ * @param string $text The search text.
+ * @param boolean $not If true, do a 'NOT' search of $text.
+ */
+ public function __construct($text, $not = false)
+ {
+ /* Data element:
+ * n = (integer) Do a NOT search?
+ * t = (string) The search text. */
+ $this->_data = new stdClass;
+ $this->_data->n = intval($not);
+ $this->_data->t = $text;
+ }
+
+ /**
+ * Adds the current query item to the query object.
+ *
+ * @param Horde_Imap_Client_Search_Query The query object.
+ *
+ * @return Horde_Imap_Client_Search_Query The query object.
+ */
+ public function createQuery($queryob)
+ {
+ $and_ob = new Horde_Imap_Client_Search_Query();
+
+ $ob = new Horde_Imap_Client_Search_Query();
+ $ob->headerText('to', $this->_data->t, $this->_data->n);
+ $and_ob->orSearch(array($ob));
+
+ $ob = new Horde_Imap_Client_Search_Query();
+ $ob->headerText('cc', $this->_data->t, $this->_data->n);
+ $and_ob->orSearch(array($ob));
+
+ $ob = new Horde_Imap_Client_Search_Query();
+ $ob->headerText('bcc', $this->_data->t, $this->_data->n);
+ $and_ob->orSearch(array($ob));
+
+ $queryob->andSearch(array($and_ob));
+
+ return $queryob;
+ }
+
+ /**
+ * Return search query text representation.
+ *
+ * @return array The textual description of this search element.
+ */
+ public function queryText()
+ {
+ return sprintf("Recipients (To/Cc/Bcc) for '%s'", ($this->_data->n ? _("not") . ' ' : '') . $this->_data->t);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * This class handles size-related search queries.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/gpl.html GPL
+ * @package IMP
+ */
+class IMP_Search_Element_Size extends IMP_Search_Element
+{
+ /**
+ * Allow NOT search on this element?
+ *
+ * @var boolean
+ */
+ public $not = false;
+
+ /**
+ * Constructor.
+ *
+ * @param integer $size The size (in bytes).
+ * @param boolean $larger Search for messages larger than $size?
+ */
+ public function __construct($size, $larger = false)
+ {
+ /* Data element:
+ * l = (integer) Larger if non-zero, smaller if zero.
+ * s = (integer) Size (in bytes). */
+ $this->_data = new stdClass;
+ $this->_data->s = intval($size);
+ $this->_data->l = intval($larger);
+ }
+
+ /**
+ * Adds the current query item to the query object.
+ *
+ * @param Horde_Imap_Client_Search_Query The query object.
+ *
+ * @return Horde_Imap_Client_Search_Query The query object.
+ */
+ public function createQuery($queryob)
+ {
+ $queryob->size($this->_data->s, $this->_data->l);
+
+ return $queryob;
+ }
+
+ /**
+ * Return search query text representation.
+ *
+ * @return array The textual description of this search element.
+ */
+ public function queryText()
+ {
+ $label = $this->_data->l
+ ? _("Size (KB) >")
+ : _("Size (KB) <");
+
+ return $label . ' ' . ($rule->v / 1024);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * This class handles text-related search queries.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/gpl.html GPL
+ * @package IMP
+ */
+class IMP_Search_Element_Text extends IMP_Search_Element
+{
+ /**
+ * Constructor.
+ *
+ * @param string $text The search text.
+ * @param string $bodyonly If true, only search in the body of the
+ * message. If false, also search in the headers.
+ * @param boolean $not If true, do a 'NOT' search of $text.
+ */
+ public function __construct($text, $bodyonly = true, $not = false)
+ {
+ /* Data element:
+ * b = (integer) Search in body only?
+ * n = (integer) Do a NOT search?
+ * t = (string) The search text. */
+ $this->_data = new stdClass;
+ $this->_data->b = intval($bodyonly);
+ $this->_data->n = intval($not);
+ $this->_data->t = $text;
+ }
+
+ /**
+ * Adds the current query item to the query object.
+ *
+ * @param Horde_Imap_Client_Search_Query The query object.
+ *
+ * @return Horde_Imap_Client_Search_Query The query object.
+ */
+ public function createQuery($queryob)
+ {
+ $queryob->text($this->_data->t, $this->_data->b, $this->_data->n);
+
+ return $queryob;
+ }
+
+ /**
+ * Return search query text representation.
+ *
+ * @return array The textual description of this search element.
+ */
+ public function queryText()
+ {
+ $label = $this->_data->b
+ ? _("Message Body")
+ : _("Entire Message (including Headers)");
+
+ return sprintf("%s for '%s'", $label, ((!empty($this->_data->n)) ? _("not") . ' ' : '') . $this->_data->t);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * This class handles within (date) search queries.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/gpl.html GPL
+ * @package IMP
+ */
+class IMP_Search_Element_Within extends IMP_Search_Element
+{
+ /* Interval types. */
+ const INTERVAL_DAYS = 1;
+ const INTERVAL_MONTHS = 2;
+ const INTERVAL_YEARS = 3;
+
+ /**
+ * Constructor.
+ *
+ * @param integer $interval Interval value.
+ * @param integer $type Interval type. Either:
+ * <pre>
+ * IMP_Search_Element_Within::INTERVAL_DAYS
+ * IMP_Search_Element_Within::INTERVAL_MONTHS
+ * IMP_Search_Element_Within::INTERVAL_YEARS
+ * </pre>
+ * @param boolean $older Do an older search?
+ */
+ public function __construct($interval, $type, $older = true)
+ {
+ /* Data element:
+ * o = (integer) Do an older search?
+ * t = (integer) Interval type.
+ * v = (integer) Interval value. */
+ $this->_data = new stdClass;
+ $this->_data->o = intval($older);
+ $this->_data->t = $type;
+ $this->_data->v = $interval;
+ }
+
+ /**
+ * Adds the current query item to the query object.
+ *
+ * @param Horde_Imap_Client_Search_Query The query object.
+ *
+ * @return Horde_Imap_Client_Search_Query The query object.
+ */
+ public function createQuery($queryob)
+ {
+ /* Limited to day granularity because that is the technical
+ * limit for IMAP servers without 'WITHIN' extension. */
+ $secs = $this->_data->v * 60 * 60 * 24;
+ switch ($this->_data->t) {
+ case self::INTERVAL_YEARS:
+ $secs *= 365;
+ break;
+
+ case self::INTERVAL_MONTHS:
+ $secs *= 30;
+ break;
+ }
+
+ $queryob->intervalSearch($secs, $this->_data->o ? Horde_Imap_Client_Search_Query::INTERVAL_OLDER : Horde_Imap_Client_Search_Query::INTERVAL_YOUNGER);
+
+ return $queryob;
+ }
+
+ /**
+ * Return search query text representation.
+ *
+ * @return array The textual description of this search element.
+ */
+ public function queryText()
+ {
+ $label = $this->_data->o
+ ? _("Older Than")
+ : _("Younger Than");
+
+ switch ($this->_data->t) {
+ case self::INTERVAL_YEARS:
+ $term = _("years");
+ break;
+
+ case self::INTERVAL_MONTHS:
+ $term = _("months");
+ break;
+
+ case self::INTERVAL_DAYS:
+ $term = _("days");
+ break;
+ }
+
+ return sprintf("%s %u %s", $label, $this->_data->v, $term);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * This class provides a data structure for a search query.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/gpl.html GPL
+ * @package IMP
+ */
+class IMP_Search_Query implements Serializable
+{
+ /* Serialized version. */
+ const VERSION = 1;
+
+ /**
+ * Is this query enabled?
+ *
+ * @var boolean
+ */
+ public $enabled = true;
+
+ /**
+ * Can this query be edited?
+ *
+ * @var boolean
+ */
+ protected $_canEdit = true;
+
+ /**
+ * The search criteria (IMP_Search_Element objects).
+ *
+ * @var array
+ */
+ protected $_criteria = array();
+
+ /**
+ * The search ID.
+ *
+ * @var string
+ */
+ protected $_id;
+
+ /**
+ * The virtual folder label.
+ *
+ * @var string
+ */
+ protected $_label;
+
+ /**
+ * The mailbox list.
+ *
+ * @var array
+ */
+ protected $_mboxes = array();
+
+ /**
+ * List of serialize entries not to save.
+ *
+ * @var array
+ */
+ protected $_nosave = array();
+
+ /**
+ * Constructor.
+ *
+ * @var array $opts Options:
+ * <pre>
+ * 'add' - (array) A list of criteria to add (Horde_Search_Element
+ * objects).
+ * DEFAULT: No criteria explicitly added.
+ * 'disable' - (boolean) Disable this query?
+ * DEFAULT: false
+ * 'id' - (string) Use this ID.
+ * DEFAULT: ID automatically generated.
+ * 'label' - (string) The label for this query.
+ * DEFAULT: Search Results
+ * 'mboxes' - (array) The list of mailboxes to search.
+ * DEFAULT: None
+ * </pre>
+ */
+ public function __construct(array $opts = array())
+ {
+ $this->enabled = empty($opts['disable']);
+ if (isset($opts['add'])) {
+ foreach ($opts['add'] as $val) {
+ $this->add($val);
+ }
+ }
+
+ $this->_id = isset($opts['id'])
+ ? $opts['id']
+ : strval(new Horde_Support_Randomid());
+
+ $this->_label = isset($opts['label'])
+ ? $opts['label']
+ : _("Search Results");
+
+ if (isset($opts['mboxes'])) {
+ $this->_mboxes = $opts['mboxes'];
+ }
+ }
+
+ /**
+ * Get object properties.
+ *
+ * @param string $name Available properties:
+ * <pre>
+ * 'canEdit' - (boolean)
+ * 'id' - (string)
+ * 'label' - (string)
+ * 'mboxes' - (array)
+ * 'mid' - (string)
+ * 'query' - (Horde_Imap_Client_Search_Query)
+ * 'querytext' - (string)
+ * </pre>
+ *
+ * @return mixed Property value.
+ */
+ public function __get($name)
+ {
+ switch ($name) {
+ case 'canEdit':
+ return $this->_canEdit;
+
+ case 'criteria':
+ $out = array();
+ foreach ($this->_criteria as $elt) {
+ $out[] = array(
+ 'criteria' => $elt->getCriteria(),
+ 'element' => get_class($elt)
+ );
+ }
+ return $out;
+
+ case 'id':
+ return $this->_id;
+
+ case 'label':
+ return $this->_label;
+
+ case 'mboxes':
+ return $this->_mboxes;
+
+ case 'mid':
+ return IMP_Search::MBOX_PREFIX . $this->_id;
+
+ case 'query':
+ $query = new Horde_Imap_Client_Search_Query();
+ foreach ($this->_criteria as $elt) {
+ $query = $elt->createQuery($query);
+ }
+ return $query;
+
+ 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) . ' ' . _("in") . ' [' . implode(', ', $this->mboxes) . ']';
+ }
+ }
+
+ /**
+ * String representation of this object: the mailbox ID.
+ *
+ * @return string Mailbox ID.
+ */
+ public function __toString()
+ {
+ return $this->mid;
+ }
+
+ /**
+ * Add a search query element.
+ *
+ * @param IMP_Search_Element $elt The search element to add.
+ */
+ public function add(IMP_Search_Element $elt)
+ {
+ $this->_criteria[] = $elt;
+ }
+
+ /* Serializable methods. */
+
+ /**
+ * Serialization.
+ *
+ * @return string Serialized data.
+ */
+ public function serialize()
+ {
+ $data = array_filter(array(
+ 'c' => $this->_criteria,
+ 'e' => intval($this->enabled),
+ 'i' => $this->_id,
+ 'l' => $this->_label,
+ 'm' => $this->_mboxes,
+ 'v' => self::VERSION
+ ));
+
+ foreach ($this->_nosave as $val) {
+ unset($data[$val]);
+ }
+
+ return serialize($data);
+ }
+
+ /**
+ * Unserialization.
+ *
+ * @param string $data Serialized data.
+ *
+ * @throws Exception
+ */
+ public function unserialize($data)
+ {
+ $data = unserialize($data);
+ if (!is_array($data) ||
+ !isset($data['v']) ||
+ ($data['v'] != self::VERSION)) {
+ throw new Exception('Cache version change');
+ }
+
+ if (isset($data['c'])) {
+ $this->_criteria = $data['c'];
+ }
+ $this->enabled = !empty($data['e']);
+ if (isset($data['i'])) {
+ $this->_id = $data['i'];
+ }
+ if (isset($data['l'])) {
+ $this->_label = $data['l'];
+ }
+ $this->_mboxes = $data['m'];
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * This class provides a data structure for storing a virtual folder.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/gpl.html GPL
+ * @package IMP
+ */
+class IMP_Search_Vfolder extends IMP_Search_Query
+{
+ /**
+ * Display this virtual folder in the preferences screen?
+ *
+ * @var boolean
+ */
+ public $prefDisplay = true;
+
+}
--- /dev/null
+<?php
+/**
+ * This class provides a data structure for storing the virtual inbox.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/gpl.html GPL
+ * @package IMP
+ */
+class IMP_Search_Vfolder_Vinbox extends IMP_Search_Vfolder
+{
+ /**
+ * Can this query be edited?
+ *
+ * @var boolean
+ */
+ protected $_canEdit = false;
+
+ /**
+ * List of serialize entries not to save.
+ *
+ * @var array
+ */
+ protected $_nosave = array('i', 'l', 'm');
+
+ /**
+ * Constructor.
+ *
+ * The 'add', 'id', 'label', and 'mboxes' parameters are not honored.
+ *
+ * @see parent::__construct()
+ */
+ public function __construct(array $opts = array())
+ {
+ $this->enabled = empty($opts['disable']);
+
+ $this->add(new IMP_Search_Element_Flag(
+ '\\seen',
+ false
+ ));
+ $this->add(new IMP_Search_Element_Flag(
+ '\\deleted',
+ false
+ ));
+
+ $this->_init();
+ }
+
+ /**
+ * Initialization tasks.
+ */
+ protected function _init()
+ {
+ $this->_id = 'vinbox';
+ $this->_label = _("Virtual Inbox");
+ }
+
+ /**
+ * Get object properties.
+ * Only create mailbox list on demand.
+ *
+ * @see parent::__get()
+ */
+ public function __get($name)
+ {
+ switch ($name) {
+ case 'mboxes':
+ return $GLOBALS['injector']->getInstance('IMP_Imap_Tree')->getPollList(true, true);
+ }
+
+ return parent::__get($name);
+ }
+
+ /**
+ * Unserialization.
+ *
+ * @param string $data Serialized data.
+ *
+ * @throws Exception
+ */
+ public function unserialize($data)
+ {
+ parent::unserialize($data);
+ $this->_init();
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * This class provides a data structure for storing the virtual trash.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license http://www.fsf.org/copyleft/gpl.html GPL
+ * @package IMP
+ */
+class IMP_Search_Vfolder_Vtrash extends IMP_Search_Vfolder
+{
+ /**
+ * Can this query be edited?
+ *
+ * @var boolean
+ */
+ public $canEdit = false;
+
+ /**
+ * Display this virtual folder in the preferences screen?
+ *
+ * @var boolean
+ */
+ public $prefDisplay = false;
+
+ /**
+ * List of serialize entries not to save.
+ *
+ * @var array
+ */
+ protected $_nosave = array('i', 'l', 'm');
+
+ /**
+ * Constructor.
+ *
+ * The 'add', 'id', 'label', and 'mboxes' parameters are not honored.
+ *
+ * @see parent::__construct()
+ */
+ public function __construct(array $opts = array())
+ {
+ $this->enabled = empty($opts['disable']);
+
+ $this->add(new IMP_Search_Element_Flag(
+ '\\deleted',
+ true
+ ));
+
+ $this->_init();
+ }
+
+ /**
+ * Initialization tasks.
+ */
+ protected function _init()
+ {
+ $this->_id = 'vtrash';
+ $this->_label = _("Virtual Trash");
+ }
+
+ /**
+ * Get object properties.
+ * Only create mailbox list on demand.
+ *
+ * @see parent::__get()
+ */
+ public function __get($name)
+ {
+ switch ($name) {
+ case 'mboxes':
+ return array_keys(iterator_to_array($GLOBALS['injector']->getInstance('IMP_Imap_Tree')));
+ }
+
+ return parent::__get($name);
+ }
+
+ /**
+ * Unserialization.
+ *
+ * @param string $data Serialized data.
+ *
+ * @throws Exception
+ */
+ public function unserialize($data)
+ {
+ parent::unserialize($data);
+ $this->_init();
+ }
+
+}
$this->_buildIndents($this->_root_nodes);
+ $filter = $injector->createInstance('Horde_Text_Filter');
$t = $injector->createInstance('Horde_Template');
$t->setOption('gettext', true);
/* Virtual folders. */
if ($this->getOption('inc_vfolder')) {
$imp_search = $injector->getInstance('IMP_Search');
- $vfolders = $imp_search->listQueries(IMP_Search::LIST_VFOLDER);
- if (!empty($vfolders)) {
- $vfolder_list = array();
- foreach ($vfolders as $id => $val) {
- $vfolder_list[] = array(
- 'l' => $injector->getInstance('Horde_Text_Filter')->filter($val, 'space2html', array('encode' => true)),
- 'sel' => (IMP::$mailbox == $id),
- 'v' => IMP::formMbox($imp_search->createSearchId($id), true)
- );
- }
+ $vfolder_list = array();
+
+ $imp_search->setIteratorFilter(IMP_Search::LIST_VFOLDER);
+ foreach ($imp_search as $val) {
+ $vfolder_list[] = array(
+ 'l' => $filter->filter($val->label, 'space2html', array('encode' => true)),
+ 'sel' => (IMP::$mailbox == strval($val)),
+ 'v' => IMP::formMbox(strval($val), true)
+ );
+ }
+
+ if (!empty($vfolder_list)) {
$t->set('vfolder', $vfolder_list);
}
}
$tasklist_list = array();
foreach ($tasklists as $id => $tasklist) {
$tasklist_list[] = array(
- 'l' => $injector->getInstance('Horde_Text_Filter')->filter($tasklist->get('name'), 'space2html', array('encode' => true)),
+ 'l' => $filter->filter($tasklist->get('name'), 'space2html', array('encode' => true)),
'v' => IMP::formMbox(IMP::TASKLIST_EDIT . $id, true)
);
}
$notepad_list[] = array();
foreach ($notepads as $id => $notepad) {
$notepad_list[] = array(
- 'l' => $injector->getInstance('Horde_Text_Filter')->filter($notepad->get('name'), 'space2html', array('encode' => true)),
+ 'l' => $filter->filter($notepad->get('name'), 'space2html', array('encode' => true)),
'v' => IMP::formMbox(IMP::NOTEPAD_EDIT . $id, true)
);
}
+++ /dev/null
-<?php
-/**
- * The IMP_Ui_Search:: class is designed to provide a place to store common
- * code shared among IMP's various UI views for the search page.
- *
- * Copyright 2009-2010 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (GPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @category Horde
- * @license http://www.fsf.org/copyleft/gpl.html GPL
- * @package IMP
- */
-class IMP_Ui_Search
-{
- /**
- * Creates a search query.
- *
- * @param array $search The list of search criteria.
- *
- * @return object A search object (Horde_Imap_Client_Search_Query).
- */
- public function createQuery($search)
- {
- $query = new Horde_Imap_Client_Search_Query();
-
- $search_array = array();
- $search_fields = $GLOBALS['injector']->getInstance('IMP_Search')->searchFields();
- $flag_fields = $GLOBALS['injector']->getInstance('IMP_Search')->flagFields();
- $imp_flags = $GLOBALS['injector']->getInstance('IMP_Imap_Flags');
- $or_search = false;
-
- foreach ($search as $rule) {
- $ob = new Horde_Imap_Client_Search_Query();
-
- $type = isset($search_fields[$rule->t]['type'])
- ? $search_fields[$rule->t]['type']
- : $rule->t;
-
- switch ($type) {
- case 'or':
- $query->orSearch($search_array);
- $search_array = array();
- $or_search = true;
- break;
-
- 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->v, $search_fields[$rule->t]['type'] == 'body', !empty($rule->n));
- $search_array[] = $ob;
- }
- break;
-
- case 'date':
- if (!empty($rule->v)) {
- $date = new Horde_Date($rule->v);
- $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 'within':
- /* Limited to day granularity because that is the technical
- * limit for IMAP servers without 'WITHIN' extension. */
- if (!empty($rule->v)) {
- $secs = $rule->v->v * 60 * 60 * 24;
-
- switch ($rule->v->l) {
- case 'y':
- $secs *= 365;
- break;
-
- case 'm':
- $secs *= 30;
- break;
- }
-
- $ob->intervalSearch($secs, $rule->t == 'older' ? Horde_Imap_Client_Search_Query::INTERVAL_OLDER : Horde_Imap_Client_Search_Query::INTERVAL_YOUNGER);
- $search_array[] = $ob;
- }
- break;
-
- case 'size':
- if (!empty($rule->v)) {
- $ob->size(intval($rule->v), $rule->t == 'size_larger');
- $search_array[] = $ob;
- }
- break;
- }
- }
-
- if ($or_search) {
- $query->orSearch($search_array);
- } else {
- $query->andSearch($search_array);
- }
-
- return $query;
- }
-
- /**
- * Create a search query from input gathered from the basic search script
- * (imp/search-basic.php).
- *
- * @param string $mbox The mailbox to search.
- * @param string $criteria The criteria to search.
- * @param string $text The criteria text.
- * @param boolean $not Should the criteria search be a not search?
- * @param string $flag A flag to search for.
- *
- * @return string The search query ID.
- */
- public function processBasicSearch($mbox, $criteria, $text, $not, $flag)
- {
- $c_list = array();
-
- if ($criteria) {
- $search_fields = $GLOBALS['injector']->getInstance('IMP_Search')->searchFields();
- $tmp = new stdClass;
- $tmp->t = $criteria;
- $tmp->v = ($search_fields[$criteria]['type'] == 'size')
- ? floatval($text) * 1024
- : $text;
- if ($search_fields[$criteria]['not']) {
- $tmp->n = (bool)$not;
- }
- $c_list[] = $tmp;
- }
-
- if ($flag) {
- $tmp = new stdClass;
- $tmp->t = 'flag';
- $tmp->v = $flag;
- $c_list[] = $tmp;
- }
-
- /* Set the search in the IMP session. */
- return $GLOBALS['injector']->getInstance('IMP_Search')->createSearchQuery($this->createQuery($c_list), array($mbox), $c_list, _("Search Results"), IMP_Search::MBOX_PREFIX . IMP_Search::BASIC_SEARCH);
- }
-
-}
/* Check for quicksearch request. */
if (strlen($args['qsearchmbox'])) {
/* Create the search query. */
- $query = new Horde_Imap_Client_Search_Query();
+ $c_list = array();
if (strlen($args['qsearchflag'])) {
- $query->flag($args['qsearchflag'], empty($args['qsearchflagnot']));
- $tmp = new stdClass;
- $tmp->t = 'flag';
- $tmp->v = $args['qsearchflag'];
- $criteria = array($tmp);
+ $c_list[] = new IMP_Search_Element_Flag(
+ $args['qsearchflag'],
+ empty($args['qsearchflagnot'])
+ );
$is_search = true;
} elseif (strlen($args['qsearch'])) {
$is_search = true;
switch ($field) {
+ case 'all':
case 'body':
- $query->text($args['qsearch'], true);
-
- $tmp = new stdClass;
- $tmp->t = 'body';
- $tmp->v = $args['qsearch'];
- $criteria = array($tmp);
+ $c_list[] = new IMP_Search_Element_Text(
+ $args['qsearch'],
+ ($field == 'body')
+ );
break;
case 'from':
case 'subject':
- $query->headerText($field, $args['qsearch']);
-
- $tmp = new stdClass;
- $tmp->t = $field;
- $tmp->v = $args['qsearch'];
- $criteria = array($tmp);
- break;
-
- case 'to':
- $query2 = new Horde_Imap_Client_Search_Query();
- $query2->headerText('cc', $args['qsearch']);
-
- $query3 = new Horde_Imap_Client_Search_Query();
- $query3->headerText('bcc', $args['qsearch']);
-
- $query->headerText('to', $args['qsearch']);
- $query->orSearch(array($query2, $query3));
-
- $tmp = new stdClass;
- $tmp->t = 'to';
- $tmp->v = $args['qsearch'];
- $criteria = array($tmp);
-
- $tmp = new stdClass;
- $tmp->t = 'or';
- $criteria[] = $tmp;
-
- $tmp = new stdClass;
- $tmp->t = 'cc';
- $tmp->v = $args['qsearch'];
- $criteria[] = $tmp;
-
- $tmp = new stdClass;
- $tmp->t = 'or';
- $criteria[] = $tmp;
-
- $tmp = new stdClass;
- $tmp->t = 'bcc';
- $tmp->v = $args['qsearch'];
- $criteria[] = $tmp;
+ $c_list[] = new IMP_Search_Element_Header(
+ $args['qsearch'],
+ $field
+ );
break;
- case 'all':
- $query->text($args['qsearch'], false);
-
- $tmp = new stdClass;
- $tmp->t = 'text';
- $tmp->v = $args['qsearch'];
- $criteria = array($tmp);
+ case 'recip':
+ $c_list[] = new IMP_Search_Element_Recipient(
+ $args['qsearch']
+ );
break;
default:
}
}
- /* Set the search in the IMP session. */
+ /* Store the search in the session. */
if ($is_search) {
$imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
- $imp_search->createSearchQuery($query, array($args['qsearchmbox']), $criteria, _("Search Results"), $mbox);
+ $imp_search->createQuery(
+ $c_list,
+ array($args['qsearchmbox']),
+ null,
+ false,
+ $mbox
+ );
}
} else {
$imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
/* The search query may have changed. */
if ($is_search &&
($args['initial'] || strlen($args['qsearchmbox']))) {
- $md->slabel = $imp_search->searchQueryText($mbox);
+ $md->slabel = $imp_search[$mbox]->querytext;
if ($imp_search->isVFolder($mbox)) {
$md->vfolder = 1;
- if (!$imp_search->isEditableVFolder($mbox)) {
+ if (!$imp_search->isVFolder($mbox, true)) {
$md->noedit = 1;
}
}
case 'rs':
if (!empty($vars->search) &&
($_SESSION['imp']['protocol'] == 'imap')) {
- $query = new Horde_Imap_Client_Search_Query();
- $query->text($vars->search, false);
-
/* Create the search query and reset the global mailbox variable. */
- $sq = $imp_search->createSearchQuery($query, array(IMP::$mailbox), array(), _("Search Results"));
- IMP::setCurrentMailboxInfo($imp_search->createSearchId($sq));
+ $q_ob = $imp_search->createQuery(
+ array(new IMP_Search_Element_Text($vars->search, false)),
+ array(IMP::$mailbox)
+ );
+ IMP::setCurrentMailboxInfo(strval($q_ob));
/* Need to re-calculate these values. */
$readonly = $imp_imap->isReadOnly(IMP::$mailbox);
/* Determine if we are going to show the Purge Deleted link. */
if (!$readonly &&
!$prefs->getValue('use_trash') &&
- !$imp_search->isVINBOXFolder(IMP::$mailbox)) {
+ !$imp_search->isVinbox(IMP::$mailbox)) {
$menu[] = array(_("Purge Deleted"), $mailbox->copy()->add('a', 'e'));
}
/* Add search link. */
if ($_SESSION['imp']['protocol'] == 'imap') {
if ($search_mbox) {
- $orig_mbox = reset($imp_search->getSearchFolders(IMP::$mailbox));
+ $orig_mbox = reset($imp_search->getSearchMailboxes(IMP::$mailbox));
$menu[] = array(sprintf(_("New Search in %s"), IMP::getLabel($orig_mbox)), IMP::generateIMPUrl('mailbox-mimp.php', $orig_mbox)->add('a', 's'));
} else {
$menu[] = array(_("Search"), $mailbox_url->copy()->add('a', 's'));
$imp_search = $injector->getInstance('IMP_Search');
$search_mbox = $imp_search->isSearchMbox(IMP::$mailbox);
$vars = Horde_Variables::getDefaultVariables();
-$vfolder = $imp_search->isVFolder(IMP::$mailbox);
/* There is a chance that this page is loaded directly via message.php. If so,
* don't re-include config files, and the following variables will already be
/* Determine if we are going to show the Hide/Purge Deleted Message links. */
if (!$prefs->getValue('use_trash') &&
- !$imp_search->isVINBOXFolder(IMP::$mailbox)) {
+ !$imp_search->isVinbox(IMP::$mailbox)) {
$showdelete = array('hide' => ($sortpref['by'] != Horde_Imap_Client::SORT_THREAD), 'purge' => true);
} else {
$showdelete = array('hide' => false, 'purge' => false);
}
$unread = $imp_mailbox->unseenMessages(Horde_Imap_Client::SORT_RESULTS_COUNT);
-$vtrash = $imp_search->isVTrashFolder(IMP::$mailbox)
- ? $imp_search->createSearchId($search_mbox)
- : null;
+$vtrash = $imp_search->isVTrash(IMP::$mailbox);
Horde::addInlineScript(array(
'ImpMailbox.unread = ' . intval($unread)
$pagetitle = $title .= ' (' . $unread . ')';
}
-if ($vfolder ||
- ($search_mbox && (IMP::$mailbox != IMP_Search::BASIC_SEARCH))) {
- $query_text = wordwrap($imp_search->searchQueryText(IMP::$mailbox));
- if ($vfolder) {
- $pagetitle .= ' [' . Horde::linkTooltip('#', $query_text, '', '', '', $query_text) . _("Virtual Folder") . '</a>]';
- $title .= ' [' . _("Virtual Folder") . ']';
- } else {
- $pagetitle = Horde::linkTooltip('#', $query_text, '', '', '', $query_text) . $pagetitle . '</a>';
- }
+if ($imp_search->isVFolder(IMP::$mailbox, true)) {
+ $query_text = wordwrap($imp_search[IMP::$mailbox]->querytext);
+ $pagetitle .= ' [' . Horde::linkTooltip('#', $query_text, '', '', '', $query_text) . _("Virtual Folder") . '</a>]';
+ $title .= ' [' . _("Virtual Folder") . ']';
+} elseif ($imp_search->isQuery(IMP::$mailbox, true)) {
+ $query_text = wordwrap($imp_search[IMP::$mailbox]->querytext);
+ $pagetitle = Horde::linkTooltip('#', $query_text, '', '', '', $query_text) . $pagetitle . '</a>';
} else {
$pagetitle = $title = htmlspecialchars($title);
}
$hdr_template->set('empty_img', Horde::img('empty_spam.png', _("Empty folder")));
}
} else {
- if ($imp_search->isEditableVFolder(IMP::$mailbox)) {
+ if ($imp_search->isVFolder(IMP::$mailbox, true)) {
$edit_search = _("Edit Virtual Folder");
- $hdr_template->set('delete_vfolder_url', $imp_search->deleteUrl(IMP::$mailbox));
- $hdr_template->set('delete_vfolder_img', Horde::img('delete.png', _("Delete Virtual Folder")));
- } elseif ($search_mbox && !isset($query_text)) {
- /* Mini search results. */
- $search_mailbox = reset($imp_search->getSearchFolders(IMP::$mailbox));
- $hdr_template->set('search_url', Horde::url('search-basic.php')->add('search_mailbox', $search_mailbox));
- $hdr_template->set('searchclose', IMP::generateIMPUrl('mailbox.php', $search_mailbox));
- } elseif (!$vfolder) {
- $edit_search = _("Edit Search Query");
+ } elseif ($imp_search->isQuery(IMP::$mailbox)) {
+ if ($imp_search->isQuery(IMP::$mailbox, true)) {
+ $edit_search = _("Edit Search Query");
+ } else {
+ /* Basic search results. */
+ $search_mailbox = reset($imp_search->getSearchMailboxes(IMP::$mailbox));
+ $hdr_template->set('search_url', Horde::url('search-basic.php')->add('search_mailbox', $search_mailbox));
+ $hdr_template->set('searchclose', IMP::generateIMPUrl('mailbox.php', $search_mailbox));
+ }
}
if (isset($edit_search)) {
}
}
}
+
/* Generate mailbox summary string. */
if (empty($pageOb['end'])) {
$hdr_template->set('msgcount', _("No Messages"));
/* Prepare the actions template. */
$a_template = $injector->createInstance('Horde_Template');
if (!$readonly) {
- $del_class = ($use_trash && ((IMP::$mailbox == (IMP::folderPref($prefs->getValue('trash_folder'), true))) || !is_null($vtrash)))
+ $del_class = ($use_trash && ($vtrash || (IMP::$mailbox == (IMP::folderPref($prefs->getValue('trash_folder'), true)))))
? 'permdeleteAction'
: 'deleteAction';
$a_template->set('delete', Horde::widget('#', _("Delete"), 'widget ' . $del_class, '', '', _("_Delete")));
/* Obtain some information describing the mailbox state. */
$total_num = count($imp_mailbox);
-$unseen_num = ($imp_search->isVINBOXFolder($mailbox))
+$unseen_num = ($imp_search->isVinbox($mailbox))
? $total_num
: $imp_mailbox->unseenMessages(Horde_Imap_Client::SORT_RESULTS_COUNT);
if ($new_mail) {
$query->flag('\\seen', false);
}
-$ids = $imp_search->runSearchQuery($query, $mailbox, Horde_Imap_Client::SORT_ARRIVAL, 1);
+$ids = $imp_search->runQuery($query, $mailbox, Horde_Imap_Client::SORT_ARRIVAL, 1);
if (count($ids)) {
$imp_ui = new IMP_Ui_Mailbox(IMP::$mailbox);
$vars = Horde_Variables::getDefaultVariables();
/* If search_basic_mbox is set, we are processing the search query. */
-if ($vars->search_basic_mailbox) {
- $imp_ui_search = new IMP_Ui_Search();
- $id = $imp_ui_search->processBasicSearch($vars->search_mailbox, $vars->search_criteria, $vars->search_criteria_text, $vars->search_criteria_not, $vars->search_flags);
+if ($vars->search_basic_mbox) {
+ $c_list = array();
- /* Redirect to the mailbox screen. */
- Horde::url('mailbox.php', true)->add('mailbox', $imp_search->createSearchId($id))->redirect();
-}
+ if ($vars->search_criteria_text) {
+ switch ($vars->search_criteria) {
+ case 'from':
+ case 'subject':
+ $c_list[] = new IMP_Search_Element_Header(
+ $vars->search_criteria_text,
+ $vars->search_criteria,
+ $vars->search_criteria_not
+ );
+ break;
-$f_fields = $s_fields = array();
+ case 'recip':
+ $c_list[] = new IMP_Search_Element_Recipient(
+ $vars->search_criteria_text,
+ $vars->search_criteria_not
+ );
+ break;
+
+ case 'body':
+ case 'text':
+ $c_list[] = new IMP_Search_Element_Text(
+ $vars->search_criteria_text,
+ ($vars->search_criteria == 'body'),
+ $vars->search_criteria_not
+ );
+ break;
+ }
+ }
-foreach ($imp_search->searchFields() as $key => $val) {
- if (!in_array($val['type'], array('customhdr', 'date', 'within'))) {
- $s_fields[] = array(
- 'val' => $key,
- 'label' => $val['label']
+ if ($vars->search_criteria_flag) {
+ $formdata = $injector->getInstance('IMP_Imap_Flags')->parseFormId($vars->search_criteria_flag);
+ $c_list[] = new IMP_Search_Element_Flag(
+ $formdata['flag'],
+ ($formdata['set'] && !$vars->search_criteria_flag_not)
);
}
+
+ /* Store the search in the session. */
+ $q_ob = $imp_search->createQuery(
+ $c_list,
+ array($vars->search_basic_mbox),
+ null,
+ IMP_Search::CREATE_QUERY,
+ IMP_Search::BASIC_SEARCH
+ );
+
+ /* Redirect to the mailbox screen. */
+ Horde::url('mailbox.php', true)->add('mailbox', strval($q_ob))->redirect();
}
-foreach ($imp_search->flagFields() as $key => $val) {
- $f_fields[] = array(
- 'val' => $key,
- 'label' => $val
+$flist = $injector->getInstance('IMP_Imap_Flags')->getFlagList($vars->search_mailbox);
+$flag_set = array();
+foreach ($flist['set'] as $val) {
+ $flag_set[] = array(
+ 'val' => $val['f'],
+ 'label' => $val['l']
);
}
$t->set('action', Horde::url('search-basic.php'));
$t->set('mbox', htmlspecialchars($vars->search_mailbox));
$t->set('search_title', sprintf(_("Search %s"), htmlspecialchars(IMP::displayFolder($vars->search_mailbox))));
-$t->set('s_fields', $s_fields);
-$t->set('f_fields', $f_fields);
+$t->set('flist', $flag_set);
$title = _("Search");
$menu = IMP::menu();
require_once dirname(__FILE__) . '/lib/Application.php';
Horde_Registry::appInit('imp');
-/* Load basic search if javascript is not enabled or searching is not
- * allowed (basic page will do the required redirection in the latter case). */
+/* Define the criteria list. */
+$criteria = array(
+ 'from' => array(
+ 'label' => _("From"),
+ 'type' => 'header'
+ ),
+ 'recip' => array(
+ 'label' => _("Recipients (To/Cc/Bcc)"),
+ 'type' => 'header'
+ ),
+ 'to' => array(
+ 'label' => _("To"),
+ 'type' => 'header'
+ ),
+ 'cc' => array(
+ 'label' => _("Cc"),
+ 'type' => 'header'
+ ),
+ 'bcc' => array(
+ 'label' => _("Bcc"),
+ 'type' => 'header'
+ ),
+ 'subject' => array(
+ 'label' => _("Subject"),
+ 'type' => 'header'
+ ),
+ 'customhdr' => array(
+ 'label' => _("Custom Header"),
+ 'type' => 'customhdr'
+ ),
+ 'body' => array(
+ 'label' => _("Body"),
+ 'type' => 'text'
+ ),
+ 'text' => array(
+ 'label' => _("Entire Message"),
+ 'type' => 'text'
+ ),
+ 'date_on' => array(
+ 'label' => _("Date Equals (=)"),
+ 'type' => 'date'
+ ),
+ 'date_until' => array(
+ 'label' => _("Date Until (<)"),
+ 'type' => 'date'
+ ),
+ 'date_since' => array(
+ 'label' => _("Date Since (>=)"),
+ 'type' => 'date'
+ ),
+ 'older' => array(
+ 'label' => _("Older Than"),
+ 'type' => 'within'
+ ),
+ 'younger' => array(
+ 'label' => _("Younger Than"),
+ 'type' => 'within'
+ ),
+ // Displayed in KB, but stored internally in bytes
+ 'size_smaller' => array(
+ 'label' => _("Size (KB) <"),
+ 'type' => 'size'
+ ),
+ // Displayed in KB, but stored internally in bytes
+ 'size_larger' => array(
+ 'label' => _("Size (KB) >"),
+ 'type' => 'size'
+ ),
+);
+
+/* Define some constants. */
+$constants = array(
+ 'date' => array(
+ 'date_on' => IMP_Search_Element_Date::DATE_ON,
+ 'date_until' => IMP_Search_Element_Date::DATE_BEFORE,
+ 'date_since' => IMP_Search_Element_Date::DATE_SINCE
+ ),
+ 'within' => array(
+ 'd' => IMP_Search_Element_Within::INTERVAL_DAYS,
+ 'm' => IMP_Search_Element_Within::INTERVAL_MONTHS,
+ 'y' => IMP_Search_Element_Within::INTERVAL_YEARS
+ )
+);
+
+/* Load basic search if javascript is not enabled or searching is not allowed
+ * (basic page will do the required redirection in the latter case). */
if (!$browser->hasFeature('javascript') ||
($_SESSION['imp']['protocol'] == 'pop')) {
require IMP_BASE . '/search-basic.php';
exit;
}
+$imp_flags = $injector->getInstance('IMP_Imap_Flags');
$imp_search = $injector->getInstance('IMP_Search');
$vars = Horde_Variables::getDefaultVariables();
-$charset = $registry->getCharset();
$dimp_view = ($_SESSION['imp']['view'] == 'dimp');
-$js_load = array();
-$search_fields = $imp_search->searchFields();
+$js_vars = array();
$search_mailbox = isset($vars->search_mailbox)
? $vars->search_mailbox
: 'INBOX';
+$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_list = array();
+
+ foreach ($criteria as $val) {
+ switch ($val->t) {
+ case 'from':
+ case 'to':
+ case 'cc':
+ case 'bcc':
+ case 'subject':
+ $c_list[] = new IMP_Search_Element_Header(
+ $val->v,
+ $val->t,
+ $val->n
+ );
+ break;
+
+ case 'recip':
+ $c_list[] = new IMP_Search_Element_Recipient(
+ $val->v,
+ $val->n
+ );
+ break;
+
+ case 'customhdr':
+ $c_list[] = new IMP_Search_Element_Header(
+ $val->v->s,
+ $val->v->h,
+ $val->n
+ );
+ break;
+
+ case 'body':
+ case 'text':
+ $c_list[] = new IMP_Search_Element_Text(
+ $val->v,
+ ($val->t == 'body'),
+ $val->n
+ );
+ break;
+
+ case 'date_on':
+ case 'date_until':
+ case 'date_since':
+ $c_list[] = new IMP_Search_Element_Date(
+ new DateTime($val->v),
+ $constants['date'][$val->t]
+ );
+ break;
+
+ case 'older':
+ case 'younger':
+ $c_list[] = new IMP_Search_Element_Within(
+ $val->v,
+ $constants['within'][$val->l]
+ ($val->t == 'older')
+ );
+ break;
+
+ case 'size_smaller':
+ case 'size_larger':
+ $c_list[] = new IMP_Search_Element_Size(
+ $val->v,
+ ($val->t == 'size_larger')
+ );
+ break;
+
+ case 'or':
+ $c_list[] = new IMP_Search_Element_Or();
+ break;
- /* Create the search query. */
- $imp_ui_search = new IMP_Ui_Search();
- $query = $imp_ui_search->createQuery($criteria);
+ case 'flag':
+ /* Flag search. */
+ $formdata = $imp_flags->parseFormId($val->v);
+ $c_list[] = new IMP_Search_Element_Flag(
+ $formdata['flag'],
+ ($formdata['set'] && !$val->n)
+ );
+ break;
+ }
+ }
/* Save the search if requested. */
if ($vars->search_save) {
switch ($vars->search_type) {
case 'vfolder':
- $id = $imp_search->addVFolder($query, $vars->folder_list, $criteria, $vars->search_label, $vars->edit_query_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');
+ if ($dimp_view) {
+ IMP_Dimp::returnToDimp(strval($q_ob));
+ }
+ Horde::getServiceLink('prefs', 'imp')->add('group', 'searches')->redirect();
+ exit;
+ }
+
$notification->push(sprintf(_("Virtual Folder \"%s\" created succesfully."), $vars->search_label), 'horde.success');
break;
}
} else {
/* Set the search in the session. */
- $id = $imp_search->createSearchQuery($query, $vars->folder_list, $criteria, _("Search Results"));
+ $q_ob = $imp_search->createQuery(
+ $c_list,
+ $vars->folder_list
+ );
}
/* Redirect to the mailbox page. */
- $id = $imp_search->createSearchId($id);
if ($dimp_view) {
- /* Output javascript code to close the IFRAME and load the search
- * mailbox in DIMP. */
- print '<html><head>' .
- Horde::wrapInlineScript(array('window.parent.DimpBase.go(' . Horde_Serialize::serialize('folder:' . $id, Horde_Serialize::JSON, $charset) . ')')) .
- '</head></html>';
- exit;
+ IMP_Dimp::returnToDimp(strval($q_ob));
}
- Horde::url('mailbox.php', true)->add('mailbox', $id)->redirect();
-}
-
-/* Generate master folder list. */
-$tree = $injector->getInstance('IMP_Imap_Tree')->createTree('imp_search', array(
- 'checkbox' => true,
-));
-
-/* Process list of recent searches. */
-$recent_searches = $imp_search->listQueries(IMP_Search::LIST_SEARCH | IMP_Search::NO_BASIC_SEARCH, false);
-if (!empty($recent_searches)) {
- $rs = array();
- foreach ($recent_searches as $key => $val) {
- $rs[$key] = array(
- 'c' => $imp_search->getCriteria($key),
- 'l' => Horde_String::truncate($val),
- 'v' => $key
- );
- }
- $js_load[] = 'ImpSearch.updateRecentSearches(' . Horde_Serialize::serialize($rs, Horde_Serialize::JSON, $charset) . ')';
+ Horde::url('mailbox.php', true)->add('mailbox', strval($q_ob))->redirect();
+ exit;
}
/* Preselect mailboxes. */
-$js_load[] = 'ImpSearch.updateSelectedFolders(' . Horde_Serialize::serialize(array($search_mailbox), Horde_Serialize::JSON, $charset) . ')';
+$js_vars['ImpSearch.selected'] = array($search_mailbox);
/* Prepare the search template. */
$t = $injector->createInstance('Horde_Template');
$t->set('action', Horde::url('search.php'));
$t->set('virtualfolder', $_SESSION['imp']['protocol'] != 'pop');
-/* Determine if we are editing a current search folder. */
+/* Determine if we are editing a search query. */
if ($vars->edit_query && $imp_search->isSearchMbox($vars->edit_query)) {
- if ($imp_search->isVFolder($vars->edit_query)) {
- if (!$imp_search->isEditableVFolder($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');
- Horde::url('mailbox.php', true)->redirect();
+ Horde::getServiceLink('prefs', 'imp')->add('group', 'searches')->redirect();
}
- $t->set('edit_query_vfolder', htmlspecialchars($vars->edit_query));
+ $t->set('edit_query_vfolder', IMP::formMbox($q_ob, true));
+ $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);
+ foreach ($imp_search as $val) {
+ $rs[$val->id] = array(
+ 'c' => $val->criteria,
+ 'l' => Horde_String::truncate($val->querytext),
+ 'v' => $val->id
+ );
}
- $js_load[] = 'ImpSearch.updateSearchCriteria(' . Horde_Serialize::serialize($imp_search->getCriteria($vars->edit_query), Horde_Serialize::JSON, $charset) . ')';
- $js_load[] = 'ImpSearch.updateSavedSearches(' . Horde_Serialize::serialize($imp_search->getLabel($vars->edit_query), Horde_Serialize::JSON, $charset) . ')';
-}
-$f_fields = $s_fields = $types = array();
+ if (!empty($rs)) {
+ $js_vars['ImpSearch.recent'] = $rs;
+ }
+}
-/* Process the list of fields. */
-foreach ($search_fields as $key => $val) {
- $s_fields[] = array(
+$c_list = $types = array();
+foreach ($criteria as $key => $val) {
+ $c_list[] = array(
'val' => $key,
- 'label' => $val['label']
+ 'label' => htmlspecialchars($val['label'])
);
$types[$key] = $val['type'];
}
-$t->set('s_fields', $s_fields);
+$t->set('clist', $c_list);
-foreach ($imp_search->flagFields() as $key => $val) {
- $f_fields[] = array(
- 'val' => $key,
- 'label' => $val
+/* Create the flag_list. */
+$flag_set = array();
+foreach ($flist['set'] as $val) {
+ $flag_set[] = array(
+ 'val' => rawurlencode($val['f']),
+ 'label' => htmlspecialchars($val['l'])
);
- $types[$key] = 'flag';
+ $types[rawurlencode($val['f'])] = 'flag';
}
-$t->set('f_fields', $f_fields);
-$t->set('tree', $tree->getTree());
+$t->set('flist', $flag_set);
-Horde_Core_Ui_JsCalendar::init();
-
-/* Gettext strings for this page. */
-$gettext_strings = array(
- 'and' => _("and"),
- '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_label' => _("Saved searches require a label."),
- 'not_match' => _("Do NOT Match"),
- 'or' => _("OR"),
- 'search_term' => _("Search Term:")
-);
-
-/* Javascript data for this page. */
-$js_data = array(
- 'months' => Horde_Core_Ui_JsCalendar::months(),
- 'searchmbox' => $search_mailbox,
- 'types' => $types
-);
-
-Horde::addInlineJsVars(array(
- 'ImpSearch.data' => $js_data,
- 'ImpSearch.text' => $gettext_strings,
+/* Generate master folder list. */
+$tree = $injector->getInstance('IMP_Imap_Tree')->createTree('imp_search', array(
+ 'checkbox' => true,
));
-Horde::addInlineScript($js_load, 'dom');
+$t->set('tree', $tree->getTree());
-$title = _("Search");
+Horde_Core_Ui_JsCalendar::init();
Horde::addScriptFile('horde.js', 'horde');
Horde::addScriptFile('stripe.js', 'horde');
Horde::addScriptFile('search.js', 'imp');
+Horde::addInlineJsVars(array_merge($js_vars, array(
+ /* Javascript data for this page. */
+ 'ImpSearch.data' => array(
+ 'constants' => $constants,
+ 'dimp' => $dimp_view,
+ 'months' => Horde_Core_Ui_JsCalendar::months(),
+ 'searchmbox' => $search_mailbox,
+ 'types' => $types
+ ),
+ /* Gettext strings for this page. */
+ 'ImpSearch.text' => array(
+ 'and' => _("and"),
+ '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_label' => _("Saved searches require a label."),
+ 'not_match' => _("Do NOT Match"),
+ 'or' => _("OR"),
+ 'search_term' => _("Search Term:")
+ )
+)), false, 'dom');
+
if ($dimp_view) {
- $t->set('return_mailbox_val', sprintf(_("Return to %s"), htmlspecialchars($search_mailbox)));
+ if (!$vars->edit_query) {
+ $t->set('return_mailbox_val', sprintf(_("Return to %s"), htmlspecialchars(IMP::displayFolder($search_mailbox))));
+ }
} else {
$menu = IMP::menu();
}
+
+$title = _("Search");
require IMP_TEMPLATES . '/common-header.inc';
if (!$dimp_view) {
echo $menu;
<?php echo _simpleButton('appportal', _("_Portal"), 'hordeIcon') ?>
<?php endif; ?>
<?php if (Horde_Menu::showService('prefs')): ?>
- <?php echo _simpleButton('appoptions', _("_Options"), 'prefsIcon') ?>
+ <?php echo _simpleButton('appprefs', _("Preferences"), 'prefsIcon') ?>
<?php endif; ?>
<?php if (Horde_Menu::showService('logout')): ?>
<?php echo _simpleButton('applogout', _("_Log Out"), 'logoutIcon') ?>
<div><?php echo _("No actions available") ?></div>
</div>
+<div class="context" id="ctx_vcontainer" style="display:none">
+ <a id="ctx_vcontainer_edit"><span class="contextImg"></span><?php echo _("Edit Virtual Folders") ?></a>
+</div>
+
<div class="context" id="ctx_vfolder" style="display:none">
<a id="ctx_vfolder_edit"><span class="contextImg"></span><?php echo _("Edit Virtual Folder") ?></a>
<a id="ctx_vfolder_delete"><span class="contextImg"></span><?php echo _("Delete Virtual Folder") ?></a>
<a id="ctx_qsearchby_all"><?php echo _("Entire Message") ?></a>
<a id="ctx_qsearchby_body"><?php echo _("Body") ?></a>
<a id="ctx_qsearchby_from"><?php echo _("From") ?></a>
- <a id="ctx_qsearchby_to"><?php echo _("To") ?></a>
+ <a id="ctx_qsearchby_recip"><?php echo _("Recipients (To/Cc/Bcc)") ?></a>
<a id="ctx_qsearchby_subject"><?php echo _("Subject") ?></a>
</div>
<?php endif; ?>
<if:empty>
<a href="<tag:empty />" id="empty_mailbox" title="<gettext>Empty Mailbox</gettext>"><tag:empty_img /></a>
</if:empty>
-<if:delete_vfolder_url>
- <a href="<tag:delete_vfolder_url />" id="delete_vfolder" title="<gettext>Delete Virtual Folder</gettext>"><tag:delete_vfolder_img /></a>
-</if:delete_vfolder_url>
</div>
<div class="clear"></div>
</div>
<select name="search_criteria">
<option value=""><gettext>None</gettext></option>
<option value="" disabled="disabled">- - - - - - - - - -</option>
-<loop:s_fields>
- <option value="<tag:s_fields.val />"><tag:s_fields.label /></option>
-</loop:s_fields>
+ <option value="from"><gettext>From</gettext></option>
+ <option value="recip"><gettext>Recipients (To/Cc/Bcc)</gettext></option>
+ <option value="subject"><gettext>Subject</gettext></option>
+ <option value="body"><gettext>Message Body</gettext></option>
+ <option value="text"><gettext>Entire Message</gettext></option>
</select>
- <span<if:hide_criteria> style="display:none"</if:hide_criteria>>
- <input type="text" name="search_criteria_text" size="30" />
- <input type="checkbox" class="checkbox" name="search_criteria_not" />
- <label for="search_criteria_not"><gettext>Do NOT Match</gettext></label>
- </span>
+ <input type="text" name="search_criteria_text" size="30" />
+ <input type="checkbox" class="checkbox" name="search_criteria_not" />
+ <label for="search_criteria_not"><gettext>Do NOT Match</gettext></label>
</td>
</tr>
<tr>
<td class="searchUILabel"><gettext>Search Flags:</gettext></td>
<td>
- <select name="search_flags">
+ <select name="search_criteria_flag">
<option value=""><gettext>None</gettext></option>
<option value="" disabled="disabled">- - - - - - - - - -</option>
-<loop:f_fields>
- <option value="<tag:f_fields.val />"><tag:f_fields.label /></option>
-</loop:f_fields>
+<loop:flist>
+ <option value="<tag:flist.val />"><tag:flist.label /></option>
+</loop:flist>
</select>
+ <input type="checkbox" class="checkbox" name="search_criteria_flag_not" />
+ <label for="search_criteria_flag_not"><gettext>Do NOT Match</gettext></label>
</td>
</tr>
<if:advsearch>
<option value="" disabled="disabled">- - - - - - - - -</option>
<option value="or" style="display:none"><gettext>Add OR clause</gettext></option>
<option value="" disabled="disabled" style="display:none">- - - - - - - - -</option>
-<loop:s_fields>
- <option value="<tag:s_fields.val />"><tag:s_fields.label /></option>
-</loop:s_fields>
+<loop:clist>
+ <option value="<tag:clist.val />"><tag:clist.label /></option>
+</loop:clist>
<option value="" disabled="disabled">- - - - - - - - -</option>
-<loop:f_fields>
- <option value="<tag:f_fields.val />"><tag:f_fields.label /></option>
-</loop:f_fields>
+<loop:flist>
+ <option value="<tag:flist.val />"><tag:flist.label /></option>
+</loop:flist>
</select>
</div>
</div>
<span class="searchuiImg arrowCollapsed"></span>
<gettext>Search Folders</gettext>
<span class="searchuiFoldersActions" style="display:none">
- <a id="link_sel_all" href="#"><gettext>Select all</gettext></a> |
- <a id="link_sel_none" href="#"><gettext>Select none</gettext></a>
+ [ <a id="link_sel_all" href="#"><gettext>Select all</gettext></a> |
+ <a id="link_sel_none" href="#"><gettext>Select none</gettext></a> ]
</span>
</div>
<div style="display:none">
<if:edit_query_vfolder>
- <input type="hidden" name="edit_query_vfolder" value="" />
+ <input type="hidden" name="search_save" id="search_save" value="1" />
+ <input type="hidden" name="search_type" value="vfolder" />
+ <input type="hidden" name="edit_query_vfolder" value="<tag:edit_query_vfolder />" />
<else:edit_query_vfolder>
<div class="item">
<input type="checkbox" class="checkbox" id="search_save" name="search_save" /> <label for="search_save"><gettext>Save search?</gettext></label>
</div>
</else:edit_query_vfolder></if:edit_query_vfolder>
<div class="item">
- <label for="search_label"><gettext>Label:</gettext></label> <input type="text" name="search_label" id="search_label" />
+ <label for="search_label"><gettext>Label:</gettext></label> <input type="text" name="search_label" id="search_label"<if:search_label> value="<tag:search_label />"</if:search_label> />
</div>
</div>
</if:virtualfolder>
<div class="searchuiButtons">
- <input type="button" id="search_submit" class="button" value="<if:edit_query_vfolder><gettext>Save</gettext><else:edit_query_vfolder><gettext>Submit</gettext></else:edit_query_vfolder></if:edit_query_vfolder>" />
+<if:edit_query_vfolder>
+ <input type="button" id="search_submit" class="button" value="<gettext>Save</gettext>" />
+ <input type="button" id="search_edit_query_cancel" class="button" value="<gettext>Cancel</gettext>" />
+<else:edit_query_vfolder>
+ <input type="button" id="search_submit" class="button" value="<gettext>Submit</gettext>" />
<input type="button" id="search_reset" class="button" value="<gettext>Reset</gettext>" />
+</else:edit_query_vfolder></if:edit_query_vfolder>
<if:return_mailbox_val>
<input type="button" id="search_dimp_return" class="button" value="<tag:return_mailbox_val />" />
</if:return_mailbox_val>
--- /dev/null
+<input type="hidden" name="searches_action" id="searches_action" />
+<input type="hidden" name="searches_data" id="searches_data" />
+<table class="searchesmanagement">
+ <thead>
+ <tr>
+ <td><gettext>Virtual Folder</gettext></td>
+ <td><gettext>Enabled?</gettext></td>
+ <td><gettext>Actions</gettext></td>
+ </tr>
+ </thead>
+ <tbody>
+<loop:vfolders>
+ <tr>
+ <td>
+<if:vfolders.enabled>
+<if:vfolders.m_url>
+ <tag:vfolders.m_url /><tag:vfolders.label /></a>
+<else:vfolders.m_url>
+ <span class="vfolderenabled"><tag:vfolders.label /></span>
+</else:vfolders.m_url></if:vfolders.m_url>
+<else:vfolders.enabled>
+ <tag:vfolders.label />
+</else:vfolders.enabled></if:vfolders.enabled>
+ </td>
+ <td class="enabled">
+ <input class="checkbox" type="checkbox" name="enable_<tag:vfolders.key />"<if:vfolders.enabled> checked="checked"</if:vfolders.enabled> />
+ </td>
+ <td>
+<if:vfolders.edit>
+ <a class="vfolderedit" href="<tag:vfolders.edit />"><span class="editImg"></span></a>
+ <a class="vfolderdelete" href="#"><span class="deleteImg"></span></a>
+<else:vfolders.edit>
+ <gettext>No Actions Available</gettext>
+</else:vfolders.edit></if:vfolders.edit>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3" class="fixed vfolderdescription">
+ <tag:vfolders.description />
+ </td>
+</loop:vfolders>
+ </tbody>
+</table>
<div>
<select id="trash" name="trash">
<option value="<tag:nofolder />"><gettext>None</gettext></option>
+<if:vtrash>
<option value="<tag:vtrash />"<if:vtrash_select> selected="selected"</if:vtrash_select>><gettext>Use Virtual Trash</gettext></option>
+</if:vtrash>
<tag:special_use />
<tag:flist />
</select>
#ctx_folder_create span.contextImg, #ctx_container_create span.contextImg, #ctx_folderopts_new span.contextImg {
background-image: url("../graphics/folders/create.png");
}
-#ctx_folder_rename span.contextImg, #ctx_container_rename span.contextImg, #ctx_vfolder_edit span.contextImg {
+#ctx_folder_rename span.contextImg, #ctx_container_rename span.contextImg, #ctx_vcontainer_edit span.contextImg, #ctx_vfolder_edit span.contextImg {
background-image: url("../graphics/folders/edit.png");
}
#ctx_folder_delete span.contextImg, #ctx_vfolder_delete span.contextImg {
background-image: url("graphics/mail_deleted.png");
}
-/* Accounts/flag management (prefs) styles. */
-table.accountsmanagement, table.flagmanagement {
+/* Prefs management styles. */
+table.accountsmanagement, table.flagmanagement, table.searchesmanagement {
padding-bottom: 10px;
}
-table.accountsmanagement td, table.flagmanagement td {
+table.accountsmanagement td, table.flagmanagement td, table.searchesmanagement td {
padding-right: 12px;
}
-table.accountsmanagement thead td, table.flagmanagement thead td {
+table.accountsmanagement thead td, table.flagmanagement thead td, table.searchesmanagement thead td {
font-weight: bold;
text-decoration: underline;
}
-table.flagmanagement div.flagUser {
- border: 1px solid gray;
- margin-right: 2px;
-}
-
table.accountsmanagement td.required, .accountsNotSecure {
color: red;
}
+table.accountsmanagement td.noneconfigured {
+ font-style: italic;
+}
.accountsSecure {
color: green;
}
+
+table.flagmanagement div.flagUser {
+ border: 1px solid gray;
+ margin-right: 2px;
+}
table.flagmanagement tbody td.flagicon {
text-align: center;
}
-table.accountsmanagement td.noneconfigured {
- font-style: italic;
+
+table.searchesmanagement td.vfolderdescription {
+ background-color: #ffa;
+ width: 500px;
+}
+table.searchesmanagement .vfolderenabled {
+ color: blue;
+ cursor: pointer;
+ text-decoration: underline;
}
/* Prefs styles. */
}
/* Other images. */
-.downloadAtc, .downloadZipAtc, .saveImgAtc, .printAtc, .closeImg, .deleteImg, .folderImg, .foldersImg, .searchuiImg, .reloadImg {
+.downloadAtc, .downloadZipAtc, .saveImgAtc, .printAtc, .closeImg, .deleteImg, .folderImg, .foldersImg, .searchuiImg, .reloadImg, .editImg {
display: -moz-inline-stack;
display: inline-block;
height: 16px;
.reloadImg {
background-image: url("graphics/reload.png");
}
+.editImg {
+ background-image: url("graphics/edit.png");
+}
/* iTip styles. */
#itipconflicts {
#ctx_folder_create span.contextImg, #ctx_container_create span.contextImg, #ctx_folderopts_new span.contextImg {
background-image: url("../graphics/folders/create.png");
}
-#ctx_folder_rename span.contextImg, #ctx_container_rename span.contextImg, #ctx_vfolder_edit span.contextImg {
+#ctx_folder_rename span.contextImg, #ctx_container_rename span.contextImg, #ctx_vcontainer_edit span.contextImg, #ctx_vfolder_edit span.contextImg {
background-image: url("../graphics/folders/edit.png");
}
#ctx_folder_delete span.contextImg, #ctx_vfolder_delete span.contextImg {