Rewrite IMAP flags handling/display code
authorMichael M Slusarz <slusarz@curecanti.org>
Wed, 15 Dec 2010 23:30:53 +0000 (16:30 -0700)
committerMichael M Slusarz <slusarz@curecanti.org>
Thu, 16 Dec 2010 20:03:01 +0000 (13:03 -0700)
Rewrite to match recent search rewrite. Biggest benefit: removes a large
amount of logic from the preferences file (and, thus, wasted storage in
the session).

Move all flags to separate objects, and use these objects to interact
with the various flag quirks.

This commit does make a fairly significant UI change: you no longer mark
a message as 'Unseen', you unmark it as 'Seen'.  Not quite as intuitive
to me, but this is the way the IMAP specs define it and the way that
other MUAs (e.g. Thunderbird) show to user. But at the internal level it
allows us to get rid of the 'inverse' flagging.

Fix bug: Allow filtering of ALL IMAP flags in dimp, not just user settable
ones.

fullmessage-dimp.js -> message-dimp.js

For those upgrading that have custom flags you don't want to lose, this
script is what I used to convert.  YMMV.

<?php
require_once dirname(__FILE__) . '/lib/Application.php';
Horde_Registry::appInit('imp', array('authentication' => 'none'));

$registry->setAuth(***USERNAME***, array());

$msgflags = array_merge(
    json_decode($prefs->getValue('msgflags_user'), true),
    json_decode($prefs->getValue('msgflags'), true)
);
$out = array();

foreach ($msgflags as $key => $val) {
    if ($val['t'] == 'imapp') {
        $out[] = new IMP_Flag_User($val['l'], $key, $val['b']);
    }
}

$prefs->setValue('msgflags', serialize($out));
$prefs->remove('msgflags_user');

51 files changed:
imp/config/prefs.php.dist
imp/js/compose-dimp.js
imp/js/dimpbase.js
imp/js/dimpcore.js
imp/js/fullmessage-dimp.js [deleted file]
imp/js/message-dimp.js [new file with mode: 0644]
imp/lib/Ajax/Application.php
imp/lib/Api.php
imp/lib/Application.php
imp/lib/Flag/Base.php [new file with mode: 0644]
imp/lib/Flag/Imap.php [new file with mode: 0644]
imp/lib/Flag/Imap/Answered.php [new file with mode: 0644]
imp/lib/Flag/Imap/Deleted.php [new file with mode: 0644]
imp/lib/Flag/Imap/Draft.php [new file with mode: 0644]
imp/lib/Flag/Imap/Flagged.php [new file with mode: 0644]
imp/lib/Flag/Imap/Forwarded.php [new file with mode: 0644]
imp/lib/Flag/Imap/Seen.php [new file with mode: 0644]
imp/lib/Flag/System.php [new file with mode: 0644]
imp/lib/Flag/System/Attachment.php [new file with mode: 0644]
imp/lib/Flag/System/Encrypted.php [new file with mode: 0644]
imp/lib/Flag/System/HighPriority.php [new file with mode: 0644]
imp/lib/Flag/System/LowPriority.php [new file with mode: 0644]
imp/lib/Flag/System/Personal.php [new file with mode: 0644]
imp/lib/Flag/System/Signed.php [new file with mode: 0644]
imp/lib/Flag/System/Unseen.php [new file with mode: 0644]
imp/lib/Flag/User.php [new file with mode: 0644]
imp/lib/Flags.php [new file with mode: 0644]
imp/lib/Imap/Flags.php [deleted file]
imp/lib/Indices.php
imp/lib/Injector/Factory/Flags.php [new file with mode: 0644]
imp/lib/Message.php
imp/lib/Prefs/Ui.php
imp/lib/Search/Element/Flag.php
imp/lib/Ui/Mailbox.php
imp/lib/Views/ListMessages.php
imp/lib/Views/ShowMessage.php
imp/mailbox-mimp.php
imp/mailbox.php
imp/message-dimp.php
imp/message-mimp.php
imp/message.php
imp/package.xml
imp/search-basic.php
imp/search.php
imp/templates/dimp/index.inc
imp/templates/dimp/javascript_defs.php
imp/templates/mobile/javascript_defs.php
imp/templates/prefs/flags.html
imp/themes/default/dimp/screen.css
imp/themes/default/screen.css
imp/themes/silver/screen.css

index 7088be3..24e780f 100644 (file)
@@ -987,149 +987,11 @@ $_prefs['flagmanagement'] = array(
     'type' => 'special'
 );
 
-// Message flags - system defaults
-// This entry should normally never be changed.  It provides the base flags
-// for all users. Flags specific to a certain user should be defined in
-// 'msgflags_user'.
-$_prefs['msgflags'] = array(
-    // Format:
-    //   KEY: Flag name
-    //   VALUE: Array with the following entries
-    //          'a' - (string) [abbreviation] The abbreviation used in
-    //                the mobile (mimp) view.
-    //                DEFAULT: Don't show flag
-    //          'b' - (string) [background] The CSS background color
-    //                DEFAULT: Use value of 'msgflags_color'
-    //          'c' - (string) [class] The CSS background class (used to
-    //                display status icon).
-    //                NO DEFAULT (entry required)
-    //          'd' - (boolean) [delete] If true, entry can be deleted.
-    //                DEFAULT: false
-    //          'l' - (string) [label] The flag text label.
-    //                NO DEFAULT (entry required)
-    //          't' - (string) [type] The flag type:
-    //                'atc' - Attachment information
-    //                'imap' - IMAP system flags (not settable by user)
-    //                'imapp' - IMAP flags (personal flags - created by user
-    //                          through the prefs interface)
-    //                'imapu' - IMAP system flags (settable by user)
-    //                'imp' - IMP defined flags
-    //                NO DEFAULT (entry required)
-    'value' => json_encode(array(
-        // Static internal imp flags (i.e. status icons)
-        // THESE ENTRIES MUST NOT BE DELETED
-        'personal' => array(
-            'a' => '+',
-            'c' => 'flagPersonal',
-            'l' => _("Personal"),
-            't' => 'imp'
-        ),
-        'highpri' => array(
-            'a' => '!',
-            'b' => '#fcc',
-            'c' => 'flagHighpriority',
-            'l' => _("High Priority"),
-            't' => 'imp'
-        ),
-        'lowpri' => array(
-            'a' => '0',
-            'c' => 'flagLowpriority',
-            'l' => _("Low Priority"),
-            't' => 'imp'
-        ),
-
-        // Attachment flags
-        // THESE ENTRIES MUST NOT BE DELETED
-        'signed' => array(
-            'c' => 'flagSignedmsg',
-            'l' => _("Message is Signed"),
-            't' => 'atc'
-        ),
-        'encrypt' => array(
-            'c' => 'flagEncryptmsg',
-            'l' => _("Message is Encrypted"),
-            't' => 'atc'
-        ),
-        'attach' => array(
-            'c' => 'flagAttachmsg',
-            'l' => _("Message has Attachments"),
-            't' => 'atc'
-        ),
-
-        // IMAP flags
-        // KEY: IMAP flag as it exists on the IMAP server
-        // VALUES (additional to base values):
-        //   'n' - (boolean) [NOT match] Don't match the flag.
-        //         DEFAULT: false
-
-        // System IMAP flags (RFC 3501 [2.3.2])
-        '\\answered' => array(
-            'a' => 'r',
-            'b' => '#cfc',
-            'c' => 'flagAnswered',
-            'l' => _("Answered"),
-            // By default, this flag is not settable by the user - it is
-            // automatically set when a message is replied to.
-            't' => 'imap'
-        ),
-        '\\draft' => array(
-            'a' => 'd',
-            'c' => 'flagDraft',
-            'l' => _("Draft"),
-            // By default, this flag is not settable by the user - it is
-            // automatically set/reset when a message is drafted/finished.
-            't' => 'imap'
-        ),
-        '\\deleted' => array(
-            'a' => 'D',
-            'b' => '#999',
-            'c' => 'flagDeleted',
-            'l' => _("Deleted"),
-            // By default, this flag is not settable by the user - it is
-            // handled via the Delete/Undelete links.
-            't' => 'imap'
-        ),
-        '\\seen' => array(
-            'a' => 'N',
-            'b' => '#eef',
-            'c' => 'flagUnseen',
-            'l' => _("Unseen"),
-            'n' => true,
-            't' => 'imapu'
-        ),
-        '\\flagged' => array(
-            'a' => '*',
-            'b' => '#fcc',
-            'c' => 'flagFlagged',
-            'l' => _("Flagged for Followup"),
-            't' => 'imapu'
-        ),
-
-        // Forwarded flag (RFC 5550 [5.9])
-        '$forwarded' => array(
-            'a' => 'F',
-            'b' => '#bfdfdf',
-            'c' => 'flagForwarded',
-            'l' => _("Forwarded"),
-            // Pursuant to RFC, this flag SHOULD NOT be changed by the user
-            't' => 'imap'
-        ),
-    ))
-);
-
-// Message flags - user specific
 // This array contains the list of flags created by the user through the
-// flags UI. Additionally, an admin can define default non-system flags
-// for a user in this preference.
-// See the 'msgflags' preference for the format of this value.
-$_prefs['msgflags_user'] = array(
-    // 'value' = json_encode(array())
-    'value' => '[]'
-);
-
-// The default color to use for flags that don't require row highlighting.
-$_prefs['msgflags_color'] = array(
-    'value' => '#ddd'
+// flags UI, and any modifications to the built-in system flags.
+$_prefs['msgflags'] = array(
+    // 'value' = serialize(array())
+    'value' => 'a:0:{}'
 );
 
 // Show all flags (including flags set by other mail programs)?
index 78aafca..51e4107 100644 (file)
@@ -281,8 +281,8 @@ var DimpCompose = {
 
             case 'sendMessage':
                 if (this.is_popup && DimpCore.base) {
-                    if (d.reply_type) {
-                        DimpCore.base.DimpBase.flag(d.reply_type == 'forward' ? '$forwarded' : '\\answered', true, { uid: d.uid, mailbox: d.reply_folder, noserver: true });
+                    if (d.flag) {
+                        DimpCore.base.DimpBase.flagCallback(d);
                     }
 
                     if (d.mailbox) {
@@ -294,7 +294,7 @@ var DimpCompose = {
                     }
 
                     if (d.log) {
-                        DimpCore.base.DimpBase.updateMsgLog(d.log, { uid: d.uid, mailbox: d.reply_folder });
+                        DimpCore.base.DimpBase.updateMsgLog(d.log, { uid: d.uid, mailbox: d.mbox });
                     }
 
                     if (!DIMP.conf_compose.qreply) {
index fe00bc7..4b82146 100644 (file)
@@ -316,7 +316,6 @@ var DimpBase = {
     // r = ViewPort row data
     msgWindow: function(r)
     {
-        this.updateSeenUID(r, 1);
         var url = DIMP.conf.URI_MESSAGE;
         url += (url.include('?') ? '&' : '?') +
                $H({ folder: r.view,
@@ -414,7 +413,7 @@ var DimpBase = {
                 if (r.flag) {
                     r.flag.each(function(a) {
                         var ptr = DIMP.conf.flags[a];
-                        if (ptr.p) {
+                        if (ptr.u) {
                             if (!ptr.elt) {
                                 /* Until text-overflow is supported on all
                                  * browsers, need to truncate label text
@@ -423,12 +422,14 @@ var DimpBase = {
                             }
                             r.subjectdata += ptr.elt;
                         } else {
-                            if (!ptr.elt) {
-                                ptr.elt = '<div class="iconImg msgflags ' + ptr.c + '" title="' + ptr.l + '"></div>';
-                            }
-                            r.status += ptr.elt;
+                            if (ptr.c) {
+                                if (!ptr.elt) {
+                                    ptr.elt = '<div class="iconImg msgflags ' + ptr.c + '" title="' + ptr.l + '"></div>';
+                                }
+                                r.status += ptr.elt;
 
-                            r.VP_bg.push(ptr.c);
+                                r.VP_bg.push(ptr.c);
+                            }
 
                             if (ptr.b) {
                                 bg = ptr.b;
@@ -505,7 +506,7 @@ var DimpBase = {
                     } else if (this.search.flag) {
                         p.update({
                             qsearchflag: this.search.flag,
-                            qsearchflagnot: Number(this.convertFlag(this.search.flag, this.search.not))
+                            qsearchflagnot: Number(this.search.not)
                         });
                     } else {
                         p.set('qsearch', $F('qsearch_input'));
@@ -675,6 +676,8 @@ var DimpBase = {
                 $('ctx_flag').childElements().each(function(c) {
                     [ c ].invoke(flags.include(c.retrieve('flag')) ? 'show' : 'hide');
                 });
+            } else {
+                $('ctx_flag').childElements().invoke('show');
             }
         }.bindAsEventListener(this));
 
@@ -780,7 +783,11 @@ var DimpBase = {
 
         case 'ctx_folder_seen':
         case 'ctx_folder_unseen':
-            this.flagAll('\\seen', id == 'ctx_folder_seen', e.findElement('LI').retrieve('mbox'));
+            DimpCore.doAction('flagAll', {
+                add: Number(id == 'ctx_folder_seen'),
+                flags: Object.toJSON([ '\\seen' ]),
+                mbox: e.findElement('LI').retrieve('mbox')
+            });
             break;
 
         case 'ctx_folder_poll':
@@ -944,7 +951,7 @@ var DimpBase = {
                 this.go('folder:' + DIMP.conf.fsearchid);
             } else if (menu.endsWith('_setflag') || menu.endsWith('_unsetflag')) {
                 flag = elt.retrieve('flag');
-                this.flag(flag, this.convertFlag(flag, menu.endsWith('_setflag')));
+                this.flag(flag, menu.endsWith('_setflag'));
             } else if (menu.endsWith('_flag') || menu.endsWith('_flagnot')) {
                 this.search = {
                     flag: elt.retrieve('flag'),
@@ -1072,18 +1079,18 @@ var DimpBase = {
         a.store('filter', filter);
     },
 
-    contextAddFlag: function(flag, f)
+    contextAddFlag: function(flag, f, id)
     {
         var a = new Element('A'),
             style = {};
 
-        if (f.p) {
+        if (f.u) {
             style.backgroundColor = f.b.escapeHTML();
         }
 
-        $('ctx_flag').insert(
+        $(id).insert(
             a.insert(
-                new Element('SPAN', { className: 'iconImg' }).addClassName(f.c.escapeHTML()).setStyle(style)
+                new Element('SPAN', { className: 'iconImg' }).addClassName(f.i ? f.i.escapeHTML() : f.c.escapeHTML()).setStyle(style)
             ).insert(
                 f.l.escapeHTML()
             )
@@ -1235,12 +1242,6 @@ var DimpBase = {
             pp_uid = this._getPPId(data.imapuid, data.view);
 
             if (this.ppfifo.indexOf(pp_uid) != -1) {
-                  // There is a chance that the message may have been marked
-                  // as unseen since first being viewed. If so, we need to
-                  // explicitly flag as seen here. TODO?
-                if (!this.hasFlag('\\seen', data)) {
-                    this.flag('\\seen', true);
-                }
                 return this._loadPreviewCallback(this.ppcache[pp_uid]);
             }
         }
@@ -1252,7 +1253,7 @@ var DimpBase = {
 
     _loadPreviewCallback: function(resp)
     {
-        var bg, ppuid, row, search, tmp,
+        var bg, ppuid, tmp, vs,
             pm = $('previewMsg'),
             r = resp.response.preview,
             t = $('msgHeadersContent').down('THEAD');
@@ -1260,14 +1261,6 @@ var DimpBase = {
         bg = (this.pp &&
               (this.pp.imapuid != r.uid || this.pp.view != r.mailbox));
 
-        if (!r.error) {
-            search = this.viewport.getSelection().search({ imapuid: { equal: [ r.uid ] }, view: { equal: [ r.mailbox ] } });
-            if (search.size()) {
-                row = search.get('dataob').first();
-                this.updateSeenUID(row, 1);
-            }
-        }
-
         if (r.error || this.viewport.getSelected().size() != 1) {
             if (!bg) {
                 if (r.error) {
@@ -1278,6 +1271,8 @@ var DimpBase = {
             return;
         }
 
+        vs = this.viewport.getSelection();
+
         // Store in cache.
         ppuid = this._getPPId(r.uid, r.mailbox);
         this._expirePPCache([ ppuid ]);
@@ -1329,7 +1324,7 @@ var DimpBase = {
         }
 
         // Toggle resume link
-        [ $('msg_resume_draft').up() ].invoke(this.viewport.getSelection().get('dataob').first().draft ? 'show' : 'hide');
+        [ $('msg_resume_draft').up() ].invoke(vs.get('dataob').first().draft ? 'show' : 'hide');
 
         $('messageBody').update(r.msgtext);
         this.loadingImg('msg', false);
@@ -1341,7 +1336,7 @@ var DimpBase = {
             eval(r.js.join(';'));
         }
 
-        this.setHash('msg:' + row.view + ':' + row.imapuid);
+        this.setHash('msg:' + r.mailbox + ':' + r.uid);
     },
 
     _stripAttachmentCallback: function(r)
@@ -1453,25 +1448,6 @@ var DimpBase = {
         return uid + '|' + mailbox;
     },
 
-    // Labeling functions
-    updateSeenUID: function(r, setflag)
-    {
-        var isunseen = !this.hasFlag('\\seen', r),
-            sel, unseen;
-
-        if ((setflag && !isunseen) || (!setflag && isunseen)) {
-            return false;
-        }
-
-        sel = this.viewport.createSelection('dataob', r);
-        unseen = this.getUnseenCount(r.view);
-
-        unseen += setflag ? -1 : 1;
-        this.updateFlag(sel, '\\seen', setflag);
-
-        this.updateUnseenStatus(r.view, unseen);
-    },
-
     // mbox = (string)
     getUnseenCount: function(mbox)
     {
@@ -2446,12 +2422,27 @@ var DimpBase = {
         }
     },
 
-    _flagAllCallback: function(r)
+    flagCallback: function(r)
     {
-        if (r.response &&
-            r.response.mbox == this.folder) {
-            r.response.flags.each(function(f) {
-                this.updateFlag(this.viewport.createSelectionBuffer(), f, r.response.set);
+        if (!r.flag ||
+            r.flag.mbox != this.folder) {
+            return;
+        }
+
+        var f = r.flag,
+            sb = f.uids
+                ? this.viewport.createSelection('uid', DimpCore.parseRangeString(f.uids)[f.mbox])
+                : this.viewport.createSelectionBuffer();
+
+        if (f.add) {
+            f.add.each(function(f) {
+                this.updateFlag(sb, f, true);
+            }, this);
+        }
+
+        if (f.remove) {
+            f.remove.each(function(f) {
+                this.updateFlag(sb, f, false);
             }, this);
         }
     },
@@ -2931,86 +2922,42 @@ var DimpBase = {
     deleteMsg: function(opts)
     {
         opts = opts || {};
-        var vs = this._getFlagSelection(opts);
-
-        // Make sure that any given row is not deleted more than once. Need to
-        // explicitly mark here because message may already be flagged deleted
-        // when we load page (i.e. switching to using trash folder).
-        vs = vs.search({ isdel: { notequal: [ true ] } });
-        if (!vs.size()) {
-            return;
-        }
-        vs.set({ isdel: true });
-
-        opts.vs = vs;
+        opts.vs = this._getFlagSelection(opts);
 
         this._doMsgAction('deleteMessages', opts, {});
-        this.updateFlag(vs, '\\deleted', true);
+        this.updateFlag(opts.vs, '\\deleted', true);
     },
 
     // flag = (string) IMAP flag name
-    // set = (boolean) True to set flag
-    // opts = (Object) 'mailbox', 'noserver', 'uid'
-    flag: function(flag, set, opts)
+    // add = (boolean) True to add flag
+    // opts = (Object) 'mailbox', 'uid'
+    flag: function(flag, add, opts)
     {
-        opts = opts || {};
-        var flags = [ (set ? '' : '-') + flag ],
-            vs = this._getFlagSelection(opts);
+        var vs = this._getFlagSelection(opts || {});
 
         if (!vs.size()) {
             return;
         }
 
-        switch (flag) {
-        case '\\answered':
-            if (set) {
-                this.updateFlag(vs, '\\flagged', false);
-                flags.push('-\\flagged');
-            }
-            break;
-
-        case '\\deleted':
-            vs.set({ isdel: false });
-            break;
+        this.updateFlag(vs, flag, add);
 
-        case '\\seen':
-            vs.get('dataob').each(function(s) {
-                this.updateSeenUID(s, set);
-            }, this);
-            break;
-        }
-
-        this.updateFlag(vs, flag, set);
-        if (!opts.noserver) {
-            DimpCore.doAction('flagMessages', this.viewport.addRequestParams({ flags: Object.toJSON(flags), view: this.folder }), { uids: vs });
-        }
-    },
-
-    // type = (string) 'seen' or 'unseen'
-    // mbox = (string) The mailbox to flag
-    flagAll: function(type, set, mbox)
-    {
-        DimpCore.doAction('flagAll', { flags: Object.toJSON([ type ]), set: Number(set), mbox: mbox }, { callback: this._flagAllCallback.bind(this) });
+        DimpCore.doAction('flagMessages', this.viewport.addRequestParams({
+            add: Number(add),
+            flags: Object.toJSON([ flag ]),
+            view: this.folder
+        }), {
+            uids: vs
+        });
     },
 
     hasFlag: function(f, r)
     {
-        return this.convertFlag(f, r.flag ? r.flag.include(f) : false);
-    },
-
-    convertFlag: function(f, set)
-    {
-        /* For some flags, we need to do an inverse match (e.g. knowing a
-         * message is SEEN is not as important as knowing the message lacks
-         * the SEEN FLAG). This function will determine if, for a given flag,
-         * the inverse action should be taken on it. */
-        return DIMP.conf.flags[f].n ? !set : set;
+        return (r.flag ? r.flag.include(f) : false);
     },
 
     updateFlag: function(vs, flag, add)
     {
         var s = {};
-        add = this.convertFlag(flag, add);
 
         vs.get('dataob').each(function(ob) {
             this._updateFlag(ob, flag, add);
@@ -3146,8 +3093,8 @@ var DimpBase = {
             });
             DM.addSubMenu('ctx_qsearchopts_by', 'ctx_qsearchby');
             DM.addSubMenu('ctx_qsearchopts_filter', 'ctx_filter');
-            DM.addSubMenu('ctx_qsearchopts_flag', 'ctx_flag');
-            DM.addSubMenu('ctx_qsearchopts_flagnot', 'ctx_flag');
+            DM.addSubMenu('ctx_qsearchopts_flag', 'ctx_flag_search');
+            DM.addSubMenu('ctx_qsearchopts_flagnot', 'ctx_flag_search');
 
             /* Create flag entries. */
             DIMP.conf.filters_o.each(function(f) {
@@ -3197,7 +3144,10 @@ var DimpBase = {
         /* Create flag entries. */
         DIMP.conf.flags_o.each(function(f) {
             if (DIMP.conf.flags[f].s) {
-                this.contextAddFlag(f, DIMP.conf.flags[f]);
+                this.contextAddFlag(f, DIMP.conf.flags[f], 'ctx_flag_search');
+            }
+            if (DIMP.conf.flags[f].a) {
+                this.contextAddFlag(f, DIMP.conf.flags[f], 'ctx_flag');
             }
         }, this);
 
@@ -3331,6 +3281,7 @@ DimpCore.onDoActionComplete = function(r) {
     if (DimpBase.viewport) {
         DimpBase.viewport.parseJSONResponse(r);
     }
+    DimpBase.flagCallback(r);
     DimpBase.pollCallback(r);
 };
 
index 362b080..9b63928 100644 (file)
@@ -471,7 +471,7 @@ var DimpCore = {
 
     reloadMessage: function(params)
     {
-        if (typeof DimpFullmessage != 'undefined') {
+        if (typeof DimpMessage != 'undefined') {
             window.location = this.addURLParam(document.location.href, params);
         } else {
             DimpBase.loadPreview(null, params);
diff --git a/imp/js/fullmessage-dimp.js b/imp/js/fullmessage-dimp.js
deleted file mode 100644 (file)
index 0779a3e..0000000
+++ /dev/null
@@ -1,253 +0,0 @@
-/**
- * Copyright 2005-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.
- */
-
-var DimpFullmessage = {
-
-    // Variables defaulting to empty/false: mailbox, uid
-    quickreply: function(type)
-    {
-        var func, ob = {};
-        ob[this.mailbox] = [ this.uid ];
-
-        switch (type) {
-        case 'reply':
-        case 'reply_all':
-        case 'reply_auto':
-        case 'reply_list':
-            $('compose').show();
-            $('redirect').hide();
-            func = 'getReplyData';
-            break;
-
-        case 'forward_auto':
-        case 'forward_attach':
-        case 'forward_body':
-        case 'forward_both':
-            $('compose').show();
-            $('redirect').hide();
-            func = 'getForwardData';
-            break;
-
-        case 'forward_redirect':
-            $('compose').hide();
-            $('redirect').show();
-            func = 'getRedirectData';
-            break;
-        }
-
-        $('msgData').hide();
-        $('qreply').show();
-
-        DimpCore.doAction(func,
-                          { imp_compose: $F('composeCache'),
-                            type: type },
-                          { uids: ob,
-                            callback: this.msgTextCallback.bind(this) });
-    },
-
-    msgTextCallback: function(result)
-    {
-        if (!result.response) {
-            return;
-        }
-
-        var i, id,
-            r = result.response;
-
-        if (r.imp_compose) {
-            $('composeCache').setValue(r.imp_compose);
-        }
-
-
-        if (r.type != 'forward_redirect') {
-            if (!r.opts) {
-                r.opts = {};
-            }
-            r.opts.noupdate = true;
-            r.opts.show_editor = (r.format == 'html');
-
-            id = (r.identity === null) ? $F('identity') : r.identity;
-            i = IMP_Compose_Base.getIdentity(id, r.opts.show_editor);
-
-            $('identity', 'last_identity').invoke('setValue', id);
-
-            DimpCompose.fillForm((i.id[2]) ? ("\n" + i.sig + r.body) : (r.body + "\n" + i.sig), r.header, r.opts);
-        }
-    },
-
-    /* Click handlers. */
-    clickHandler: function(parentfunc, e)
-    {
-        if (e.isRightClick()) {
-            return;
-        }
-
-        var elt = orig = e.element(), id;
-
-        while (Object.isElement(elt)) {
-            id = elt.readAttribute('id');
-
-            switch (id) {
-            case 'windowclose':
-                window.close();
-                e.stop();
-                return;
-
-            case 'forward_link':
-            case 'reply_link':
-                this.quickreply(id == 'reply_link' ? 'reply_auto' : 'forward_auto');
-                e.stop();
-                return;
-
-            case 'button_deleted':
-            case 'button_ham':
-            case 'button_spam':
-                if (DimpCore.base) {
-                    DimpCore.base.focus();
-                    if (id == 'button_deleted') {
-                        DimpCore.base.DimpBase.deleteMsg({ uid: this.uid, mailbox: this.mailbox });
-                    } else {
-                        DimpCore.base.DimpBase.reportSpam(id == 'button_spam', { uid: this.uid, mailbox: this.mailbox });
-                    }
-                    window.close();
-                }
-                e.stop();
-                return;
-
-            case 'msgloglist_toggle':
-            case 'partlist_toggle':
-                tmp = (id == 'partlist_toggle') ? 'partlist' : 'msgloglist';
-                $(tmp + '_col', tmp + '_exp').invoke('toggle');
-                Effect.toggle(tmp, 'blind', {
-                    afterFinish: function() {
-                        this.resizeWindow();
-                        $('msgData').down('DIV.messageBody').setStyle({ overflowY: 'auto' })
-                    }.bind(this),
-                    beforeSetup: function() {
-                        $('msgData').down('DIV.messageBody').setStyle({ overflowY: 'hidden' })
-                    },
-                    duration: 0.2,
-                    queue: {
-                        position: 'end',
-                        scope: tmp,
-                        limit: 2
-                    }
-                });
-                break;
-
-            case 'msg_view_source':
-                DimpCore.popupWindow(DimpCore.addURLParam(DIMP.conf.URI_VIEW, { uid: this.uid, mailbox: this.mailbox, actionID: 'view_source', id: 0 }, true), this.uid + '|' + this.mailbox);
-                break;
-
-            case 'qreply':
-                if (orig.match('DIV.headercloseimg IMG')) {
-                    DimpCompose.confirmCancel();
-                }
-                break;
-
-            default:
-                if (elt.hasClassName('printAtc')) {
-                    DimpCore.popupWindow(DimpCore.addURLParam(DIMP.conf.URI_VIEW, { uid: this.uid, mailbox: this.mailbox, actionID: 'print_attach', id: elt.readAttribute('mimeid') }, true), this.uid + '|' + this.mailbox + '|print', IMP.printWindow);
-                    e.stop();
-                    return;
-                } else if (elt.hasClassName('stripAtc')) {
-                    DimpCore.reloadMessage({
-                        actionID: 'strip_attachment',
-                        mailbox: this.mailbox,
-                        id: elt.readAttribute('mimeid'),
-                        uid: this.uid
-                    });
-                    e.stop();
-                    return;
-                }
-                break;
-            }
-
-            elt = elt.up();
-        }
-
-        parentfunc(e);
-    },
-
-    contextOnClick: function(parentfunc, e)
-    {
-        var id = e.memo.elt.readAttribute('id');
-
-        switch (id) {
-        case 'ctx_reply_reply':
-        case 'ctx_reply_reply_all':
-        case 'ctx_reply_reply_list':
-            this.quickreply(id.substring(10));
-            break;
-
-        case 'ctx_forward_attach':
-        case 'ctx_forward_body':
-        case 'ctx_forward_both':
-        case 'ctx_forward_redirect':
-            this.quickreply(id.substring(4));
-            break;
-
-        default:
-            parentfunc(e);
-            break;
-        }
-    },
-
-    resizeWindow: function()
-    {
-        var mb = $('msgData').down('DIV.messageBody');
-
-        mb.setStyle({ height: (document.viewport.getHeight() - mb.cumulativeOffset()[1] - parseInt(mb.getStyle('paddingTop'), 10) - parseInt(mb.getStyle('paddingBottom'), 10)) + 'px' });
-    },
-
-    onDomLoad: function()
-    {
-        DimpCore.growler_log = false;
-        DimpCore.init();
-
-        if (DIMP.conf.disable_compose) {
-            tmp = $('reply_link', 'forward_link').compact().invoke('up', 'SPAN').concat([ $('ctx_contacts_new') ]).compact().invoke('remove');
-        } else {
-            DimpCore.addPopdown('reply_link', 'replypopdown');
-            DimpCore.addPopdown('forward_link', 'forwardpopdown');
-        }
-
-        /* Set up address linking. */
-        [ 'from', 'to', 'cc', 'bcc', 'replyTo' ].each(function(a) {
-            if (this[a]) {
-                var elt = $('msgHeader' + a.charAt(0).toUpperCase() + a.substring(1)).down('TD', 1);
-                elt.replace(DimpCore.buildAddressLinks(this[a], elt.clone(false)));
-            }
-        }, this);
-
-        /* Add message information. */
-        if (this.log) {
-            $('msgLogInfo').show();
-            DimpCore.updateMsgLog(this.log);
-        }
-
-        if (this.strip && DimpCore.base) {
-            DimpCore.base.DimpBase.poll();
-        }
-
-        $('dimpLoading').hide();
-        $('pageContainer').show();
-
-        this.resizeWindow();
-    }
-
-};
-
-/* ContextSensitive functions. */
-DimpCore.contextOnClick = DimpCore.contextOnClick.wrap(DimpFullmessage.contextOnClick.bind(DimpFullmessage));
-
-/* Click handler. */
-DimpCore.clickHandler = DimpCore.clickHandler.wrap(DimpFullmessage.clickHandler.bind(DimpFullmessage));
-
-/* Attach event handlers. */
-document.observe('dom:loaded', DimpFullmessage.onDomLoad.bind(DimpFullmessage));
-Event.observe(window, 'resize', DimpFullmessage.resizeWindow.bind(DimpFullmessage));
diff --git a/imp/js/message-dimp.js b/imp/js/message-dimp.js
new file mode 100644 (file)
index 0000000..fa047f1
--- /dev/null
@@ -0,0 +1,261 @@
+/**
+ * Copyright 2005-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.
+ */
+
+var DimpMessage = {
+
+    // Variables defaulting to empty/false: mailbox, uid
+    quickreply: function(type)
+    {
+        var func, ob = {};
+        ob[this.mailbox] = [ this.uid ];
+
+        switch (type) {
+        case 'reply':
+        case 'reply_all':
+        case 'reply_auto':
+        case 'reply_list':
+            $('compose').show();
+            $('redirect').hide();
+            func = 'getReplyData';
+            break;
+
+        case 'forward_auto':
+        case 'forward_attach':
+        case 'forward_body':
+        case 'forward_both':
+            $('compose').show();
+            $('redirect').hide();
+            func = 'getForwardData';
+            break;
+
+        case 'forward_redirect':
+            $('compose').hide();
+            $('redirect').show();
+            func = 'getRedirectData';
+            break;
+        }
+
+        $('msgData').hide();
+        $('qreply').show();
+
+        DimpCore.doAction(func,
+                          { imp_compose: $F('composeCache'),
+                            type: type },
+                          { uids: ob,
+                            callback: this.msgTextCallback.bind(this) });
+    },
+
+    msgTextCallback: function(result)
+    {
+        if (!result.response) {
+            return;
+        }
+
+        var i, id,
+            r = result.response;
+
+        if (r.imp_compose) {
+            $('composeCache').setValue(r.imp_compose);
+        }
+
+
+        if (r.type != 'forward_redirect') {
+            if (!r.opts) {
+                r.opts = {};
+            }
+            r.opts.noupdate = true;
+            r.opts.show_editor = (r.format == 'html');
+
+            id = (r.identity === null) ? $F('identity') : r.identity;
+            i = IMP_Compose_Base.getIdentity(id, r.opts.show_editor);
+
+            $('identity', 'last_identity').invoke('setValue', id);
+
+            DimpCompose.fillForm((i.id[2]) ? ("\n" + i.sig + r.body) : (r.body + "\n" + i.sig), r.header, r.opts);
+        }
+    },
+
+    /* Click handlers. */
+    clickHandler: function(parentfunc, e)
+    {
+        if (e.isRightClick()) {
+            return;
+        }
+
+        var elt = orig = e.element(), id;
+
+        while (Object.isElement(elt)) {
+            id = elt.readAttribute('id');
+
+            switch (id) {
+            case 'windowclose':
+                window.close();
+                e.stop();
+                return;
+
+            case 'forward_link':
+            case 'reply_link':
+                this.quickreply(id == 'reply_link' ? 'reply_auto' : 'forward_auto');
+                e.stop();
+                return;
+
+            case 'button_deleted':
+            case 'button_ham':
+            case 'button_spam':
+                if (DimpCore.base) {
+                    DimpCore.base.focus();
+                    if (id == 'button_deleted') {
+                        DimpCore.base.DimpBase.deleteMsg({ uid: this.uid, mailbox: this.mailbox });
+                    } else {
+                        DimpCore.base.DimpBase.reportSpam(id == 'button_spam', { uid: this.uid, mailbox: this.mailbox });
+                    }
+                    window.close();
+                }
+                e.stop();
+                return;
+
+            case 'msgloglist_toggle':
+            case 'partlist_toggle':
+                tmp = (id == 'partlist_toggle') ? 'partlist' : 'msgloglist';
+                $(tmp + '_col', tmp + '_exp').invoke('toggle');
+                Effect.toggle(tmp, 'blind', {
+                    afterFinish: function() {
+                        this.resizeWindow();
+                        $('msgData').down('DIV.messageBody').setStyle({ overflowY: 'auto' })
+                    }.bind(this),
+                    beforeSetup: function() {
+                        $('msgData').down('DIV.messageBody').setStyle({ overflowY: 'hidden' })
+                    },
+                    duration: 0.2,
+                    queue: {
+                        position: 'end',
+                        scope: tmp,
+                        limit: 2
+                    }
+                });
+                break;
+
+            case 'msg_view_source':
+                DimpCore.popupWindow(DimpCore.addURLParam(DIMP.conf.URI_VIEW, { uid: this.uid, mailbox: this.mailbox, actionID: 'view_source', id: 0 }, true), this.uid + '|' + this.mailbox);
+                break;
+
+            case 'qreply':
+                if (orig.match('DIV.headercloseimg IMG')) {
+                    DimpCompose.confirmCancel();
+                }
+                break;
+
+            default:
+                if (elt.hasClassName('printAtc')) {
+                    DimpCore.popupWindow(DimpCore.addURLParam(DIMP.conf.URI_VIEW, { uid: this.uid, mailbox: this.mailbox, actionID: 'print_attach', id: elt.readAttribute('mimeid') }, true), this.uid + '|' + this.mailbox + '|print', IMP.printWindow);
+                    e.stop();
+                    return;
+                } else if (elt.hasClassName('stripAtc')) {
+                    DimpCore.reloadMessage({
+                        actionID: 'strip_attachment',
+                        mailbox: this.mailbox,
+                        id: elt.readAttribute('mimeid'),
+                        uid: this.uid
+                    });
+                    e.stop();
+                    return;
+                }
+                break;
+            }
+
+            elt = elt.up();
+        }
+
+        parentfunc(e);
+    },
+
+    contextOnClick: function(parentfunc, e)
+    {
+        var id = e.memo.elt.readAttribute('id');
+
+        switch (id) {
+        case 'ctx_reply_reply':
+        case 'ctx_reply_reply_all':
+        case 'ctx_reply_reply_list':
+            this.quickreply(id.substring(10));
+            break;
+
+        case 'ctx_forward_attach':
+        case 'ctx_forward_body':
+        case 'ctx_forward_both':
+        case 'ctx_forward_redirect':
+            this.quickreply(id.substring(4));
+            break;
+
+        default:
+            parentfunc(e);
+            break;
+        }
+    },
+
+    resizeWindow: function()
+    {
+        var mb = $('msgData').down('DIV.messageBody');
+
+        mb.setStyle({ height: (document.viewport.getHeight() - mb.cumulativeOffset()[1] - parseInt(mb.getStyle('paddingTop'), 10) - parseInt(mb.getStyle('paddingBottom'), 10)) + 'px' });
+    },
+
+    onDomLoad: function()
+    {
+        DimpCore.growler_log = false;
+        DimpCore.init();
+
+        if (DIMP.conf.disable_compose) {
+            tmp = $('reply_link', 'forward_link').compact().invoke('up', 'SPAN').concat([ $('ctx_contacts_new') ]).compact().invoke('remove');
+        } else {
+            DimpCore.addPopdown('reply_link', 'replypopdown');
+            DimpCore.addPopdown('forward_link', 'forwardpopdown');
+        }
+
+        /* Set up address linking. */
+        [ 'from', 'to', 'cc', 'bcc', 'replyTo' ].each(function(a) {
+            if (this[a]) {
+                var elt = $('msgHeader' + a.charAt(0).toUpperCase() + a.substring(1)).down('TD', 1);
+                elt.replace(DimpCore.buildAddressLinks(this[a], elt.clone(false)));
+            }
+        }, this);
+
+        /* Add message information. */
+        if (this.log) {
+            $('msgLogInfo').show();
+            DimpCore.updateMsgLog(this.log);
+        }
+
+        if (DimpCore.base) {
+            if (this.strip) {
+                DimpCore.base.DimpBase.poll();
+            } else if (this.poll) {
+                DimpCore.base.DimpBase.pollCallback({ poll: this.poll });
+            }
+
+            if (this.flag) {
+                DimpCore.base.DimpBase.flagCallback({ flag: this.flag });
+            }
+        }
+
+        $('dimpLoading').hide();
+        $('pageContainer').show();
+
+        this.resizeWindow();
+    }
+
+};
+
+/* ContextSensitive functions. */
+DimpCore.contextOnClick = DimpCore.contextOnClick.wrap(DimpMessage.contextOnClick.bind(DimpMessage));
+
+/* Click handler. */
+DimpCore.clickHandler = DimpCore.clickHandler.wrap(DimpMessage.clickHandler.bind(DimpMessage));
+
+/* Attach event handlers. */
+document.observe('dom:loaded', DimpMessage.onDomLoad.bind(DimpMessage));
+Event.observe(window, 'resize', DimpMessage.resizeWindow.bind(DimpMessage));
index bb87a47..97e8785 100644 (file)
@@ -215,42 +215,37 @@ class IMP_Ajax_Application extends Horde_Core_Ajax_Application
      *
      * Variables used:
      * <pre>
-     * 'flags' - (string) The flags to set (JSON serialized array).
+     * 'add' - (integer) Add the flags?
+     * 'flags' - (string) The IMAP flags to add/remove (JSON serialized
+     *           array).
      * 'mbox' - (string) The full malbox name.
-     * 'set' - (integer) Set the flags?
      * </pre>
      *
      * @return mixed  False on failure, or an object with the following
      *                entries:
      * <pre>
-     * 'flags' - (array) The list of flags that were set.
-     * 'mbox' - (string) The full mailbox name.
+     * 'flag' - (object) See flagEntry().
      * 'poll' - (array) Mailbox names as the keys, number of unseen messages
      *          as the values.
-     * 'set' - (integer) 1 if the flag was set. Unset otherwise.
      * </pre>
      */
     public function flagAll()
     {
         $flags = Horde_Serialize::unserialize($this->_vars->flags, Horde_Serialize::JSON);
-        if (!$this->_vars->mbox || empty($flags)) {
+        if (!$this->_vars->mbox ||
+            empty($flags) ||
+            !$GLOBALS['injector']->getInstance('IMP_Message')->flagAllInMailbox($flags, array($this->_vars->mbox), $this->_vars->add)) {
             return false;
         }
 
-        $result = $GLOBALS['injector']->getInstance('IMP_Message')->flagAllInMailbox($flags, array($this->_vars->mbox), $this->_vars->set);
+        $result = new stdClass;
 
-        if ($result) {
-            $result = new stdClass;
-            $result->flags = $flags;
-            $result->mbox = $this->_vars->mbox;
-            if ($this->_vars->set) {
-                $result->set = 1;
-            }
+        /* Get list of flags that were also affected by this flag
+         * change. */
+        $result->flag = $this->flagEntry($flags, $this->_vars->add, $this->_vars->mbox);
 
-            $poll = $this->_getPollInformation($this->_vars->mbox);
-            if (!empty($poll)) {
-                $result->poll = array($this->_vars->mbox => $poll[$this->_vars->mbox]);
-            }
+        if ($poll = $this->pollEntry($this->_vars->mbox)) {
+            $result->poll = $poll;
         }
 
         return $result;
@@ -453,11 +448,7 @@ class IMP_Ajax_Application extends Horde_Core_Ajax_Application
         $changed = false;
 
         $result = new stdClass;
-        $result->poll = array();
-
-        foreach ($GLOBALS['injector']->getInstance('IMP_Injector_Factory_Imap')->create()->statusMultiple($GLOBALS['injector']->getInstance('IMP_Imap_Tree')->getPollList(), Horde_Imap_Client::STATUS_UNSEEN) as $key => $val) {
-            $result->poll[$key] = intval($val['unseen']);
-        }
+        $result->poll = $this->pollEntry();
 
         if ($this->_vars->view &&
             ($changed = $this->_changed())) {
@@ -650,7 +641,7 @@ class IMP_Ajax_Application extends Horde_Core_Ajax_Application
             /* Update poll information for destination folder if necessary.
              * Poll information for current folder will be added by
              * _generateDeleteResult() call above. */
-            if ($poll = $this->_getPollInformation($this->_vars->mboxto)) {
+            if ($poll = $this->pollEntry($this->_vars->mboxto)) {
                 $result->poll = array_merge(isset($result->poll) ? $result->poll : array(), $poll);
             }
         } else {
@@ -687,7 +678,7 @@ class IMP_Ajax_Application extends Horde_Core_Ajax_Application
         }
 
         if ($result = $GLOBALS['injector']->getInstance('IMP_Message')->copy($this->_vars->mboxto, 'copy', $indices)) {
-            if ($poll = $this->_getPollInformation($this->_vars->mboxto)) {
+            if ($poll = $this->pollEntry($this->_vars->mboxto)) {
                 $result->poll = array_merge(isset($result->poll) ? $result->poll : array(), $poll);
             }
         } else {
@@ -703,6 +694,7 @@ class IMP_Ajax_Application extends Horde_Core_Ajax_Application
      * See the list of variables needed for _changed() and
      * _checkUidvalidity().  Additional variables used:
      * <pre>
+     * 'add' - (integer) Set the flag?
      * 'flags' - (string) The flags to set (JSON serialized array).
      * 'uid' - (string) Indices of the messages to flag (IMAP sequence
      *         string).
@@ -711,6 +703,8 @@ class IMP_Ajax_Application extends Horde_Core_Ajax_Application
      * @return mixed  False on failure, or an object with the following
      *                entries:
      * <pre>
+     * 'flag' - (object) See flagEntry().
+     * 'poll' - (array) See pollEntry().
      * 'ViewPort' - (object) See _viewPortData().
      * </pre>
      */
@@ -728,37 +722,28 @@ class IMP_Ajax_Application extends Horde_Core_Ajax_Application
         }
 
         $flags = Horde_Serialize::unserialize($this->_vars->flags, Horde_Serialize::JSON);
-        $set = $notset = array();
 
-        foreach ($flags as $val) {
-            if ($val[0] == '-') {
-                $notset[] = substr($val, 1);
-            } else {
-                $set[] = $val;
-            }
+        if (!$GLOBALS['injector']->getInstance('IMP_Message')->flag($flags, $indices, $this->_vars->add)) {
+            return $this->_checkUidvalidity();
         }
 
-        if (!empty($set)) {
-            $result = $GLOBALS['injector']->getInstance('IMP_Message')->flag($set, $indices, true);
-        }
+        $result = new stdClass;
+        list($mbox, $uids) = $indices->getSingle(true);
+        $result->flag = $this->flagEntry($flags, $this->_vars->add, $mbox, $uids);
 
-        if (!empty($notset)) {
-            $result = $GLOBALS['injector']->getInstance('IMP_Message')->flag($notset, $indices, false);
+        if (in_array('\\seen', $flags) && ($poll = $this->pollEntry($mbox))) {
+            $result->poll = $poll;
         }
 
-        if ($result) {
-            $result = new stdClass;
-            if ($change) {
-                $result->ViewPort = $this->_viewPortData(true);
-            } else {
-                $result->ViewPort = new stdClass;
-                $result->ViewPort->updatecacheid = $GLOBALS['injector']->getInstance('IMP_Injector_Factory_MailboxList')->create($this->_vars->view)->getCacheID($this->_vars->view);
-                $result->ViewPort->view = $this->_vars->view;
-            }
-            return $result;
+        if ($change) {
+            $result->ViewPort = $this->_viewPortData(true);
+        } else {
+            $result->ViewPort = new stdClass;
+            $result->ViewPort->updatecacheid = $GLOBALS['injector']->getInstance('IMP_Injector_Factory_MailboxList')->create($this->_vars->view)->getCacheID($this->_vars->view);
+            $result->ViewPort->view = $this->_vars->view;
         }
 
-        return $this->_checkUidvalidity();
+        return $result;
     }
 
     /**
@@ -965,6 +950,9 @@ class IMP_Ajax_Application extends Horde_Core_Ajax_Application
      * @return mixed  False on failure, or an object with the following
      *                entries:
      * <pre>
+     * 'flag' - (object) See flagEntry().
+     * 'poll' - (array) Mailbox names as the keys, number of unseen messages
+     *          as the values.
      * 'preview' - (object) Return from IMP_View_ShowMessage::showMessage().
      * 'ViewPort' - (object) See _viewPortData(). (Only returns updatecacheid
      *                       entry - don't do mailbox poll here).
@@ -1005,6 +993,13 @@ class IMP_Ajax_Application extends Horde_Core_Ajax_Application
                     $result->ViewPort->view = $this->_vars->view;
                 }
             }
+
+            if ($poll = $this->pollEntry($this->_vars->view)) {
+                $result->poll = $poll;
+            }
+
+            /* Add changed flag information. */
+            $result->flag = $this->flagEntry(array('\\seen'), true, $mbox, $idx);
         } catch (Horde_Imap_Client_Exception $e) {
             $result->preview->error = $e->getMessage();
             $result->preview->errortype = 'horde.error';
@@ -1641,14 +1636,14 @@ class IMP_Ajax_Application extends Horde_Core_Ajax_Application
      * 'action' - (string) The AJAX action string
      * 'draft_delete' - (integer) TODO
      * 'encryptjs' - (array) Javascript to run after encryption failure.
+     * 'flag' - (object) See flagEntry().
      * 'identity' - (integer) If set, this is the identity that is tied to
      *              the current recipient address.
      * 'log' - (array) TODO
      * 'mailbox' - (array) TODO
-     * 'reply_folder' - (string) TODO
-     * 'reply_type' - (string) TODO
+     * 'mbox' - (string) Mailbox of original message.
      * 'success' - (integer) 1 on success, 0 on failure.
-     * 'uid' - (integer) TODO
+     * 'uid' - (integer) IMAP UID of original message.
      * </pre>
      */
     public function sendMessage()
@@ -1663,13 +1658,6 @@ class IMP_Ajax_Application extends Horde_Core_Ajax_Application
 
         $headers['replyto'] = $identity->getValue('replyto_addr');
 
-        $result->uid = $imp_compose->getMetadata('uid');
-
-        if ($reply_type = $imp_compose->getMetadata('reply_type')) {
-            $result->reply_folder = $imp_compose->getMetadata('mailbox');
-            $result->reply_type = $reply_type;
-        }
-
         /* Use IMP_Tree to determine whether the sent mail folder was
          * created. */
         $imptree = $GLOBALS['injector']->getInstance('IMP_Imap_Tree');
@@ -1749,6 +1737,21 @@ class IMP_Ajax_Application extends Horde_Core_Ajax_Application
             }
         }
 
+        $result->mbox = $imp_compose->getMetadata('mailbox');
+        $result->uid = $imp_compose->getMetadata('uid');
+
+        switch ($imp_compose->getMetadata('reply_type')) {
+        case 'forward':
+            $result->flag = $this->flagEntry(array('$forwarded'), true, $result->mbox, $result->uid);
+            break;
+
+        case 'reply':
+        case 'reply_all':
+        case 'reply_list':
+            $result->flag = $this->flagEntry(array('\\answered'), true, $result->mbox, $result->uid);
+            break;
+        }
+
         $imp_compose->destroy('send');
 
         $result->mailbox = $this->_getMailboxResponse($imptree);
@@ -1807,6 +1810,69 @@ class IMP_Ajax_Application extends Horde_Core_Ajax_Application
     }
 
     /**
+     * Generate flag updated entry.
+     *
+     * @param array $flags  List of flags that have changed.
+     * @param boolean $add  Were the flags added?
+     * @param string $mbox  Mailbox where flags changed.
+     * @param string $uids  IMAP UIDs of the changed flags.
+     *
+     * @return stdClass  Object with these properties:
+     * <pre>
+     * 'add' - (array) The list of flags that were added.
+     * 'mbox' - (string) The full mailbox name.
+     * 'remove' - (array) The list of flags that were removed.
+     * 'uids' - (string) Indices of the messages that have changed (IMAP
+     *          sequence string); if empty, all messages in mailbox have
+     *          changed.
+     * </pre>
+     */
+    static public function flagEntry($flags, $add, $mbox, $uids = null)
+    {
+        $changed = $GLOBALS['injector']->getInstance('IMP_Flags')->changed($flags, $add);
+
+        $result = new stdClass;
+        if (!empty($changed['add'])) {
+            $result->add = array_map('strval', $changed['add']);
+        }
+        $result->mbox = $mbox;
+        if (!empty($changed['remove'])) {
+            $result->remove = array_map('strval', $changed['remove']);
+        }
+        if (!is_null($uids)) {
+            $result->uids = strval(new IMP_Indices($mbox, $uids));
+        }
+
+        return $result;
+    }
+
+    /**
+     * Generate poll information for mailboxes.
+     *
+     * @param string $mbox  The full mailbox name.  If null, all polled
+     *                      mailboxes are returned.
+     *
+     * @return array  Keys are mailbox names, values are the number of unseen
+     *                messages.
+     */
+    static public function pollEntry($mbox = null)
+    {
+        $imaptree = $GLOBALS['injector']->getInstance('IMP_Imap_Tree');
+
+        if (is_null($mbox)) {
+            $result = array();
+            foreach ($GLOBALS['injector']->getInstance('IMP_Injector_Factory_Imap')->create()->statusMultiple($imaptree->getPollList(), Horde_Imap_Client::STATUS_UNSEEN) as $key => $val) {
+                $result[$key] = intval($val['unseen']);
+            }
+            return $result;
+        }
+
+        return $imaptree[$mbox]->polled
+            ? array($mbox => $imaptree[$mbox]->poll_info->unseen)
+            : array();
+    }
+
+    /**
      * Setup environment for dimp compose actions.
      *
      * Variables used:
@@ -1990,8 +2056,7 @@ class IMP_Ajax_Application extends Horde_Core_Ajax_Application
             $result->ViewPort->view = $this->_vars->view;
         }
 
-        $poll = $this->_getPollInformation($this->_vars->view);
-        if (!empty($poll)) {
+        if ($poll = $this->pollEntry($this->_vars->view)) {
             $result->poll = $poll;
         }
 
@@ -2092,23 +2157,6 @@ class IMP_Ajax_Application extends Horde_Core_Ajax_Application
     }
 
     /**
-     * Generate poll information for a single mailbox.
-     *
-     * @param string $mbox  The full mailbox name.
-     *
-     * @return array  Key is the mailbox name, value is the number of unseen
-     *                messages.
-     */
-    protected function _getPollInformation($mbox)
-    {
-        $imaptree = $GLOBALS['injector']->getInstance('IMP_Imap_Tree');
-
-        return $imaptree[$mbox]->polled
-            ? array($mbox => $imaptree[$mbox]->poll_info->unseen)
-            : array();
-    }
-
-    /**
      * Generate quota information.
      *
      * @return array  'p': Quota percentage; 'm': Quota message
index 52ba1fe..e787edf 100644 (file)
@@ -223,8 +223,7 @@ class IMP_Api extends Horde_Registry_Api
      * @param string $mailbox  If set, returns the list of flags filtered by
      *                         what the mailbox allows.
      *
-     * @return array  See IMP_Imap_Flags::getList() for the output. The
-     *                'f' key will be set.
+     * @return array  A list of IMP_Flag_Base objects.
      */
     public function flagList($mailbox = null)
     {
@@ -232,16 +231,10 @@ class IMP_Api extends Horde_Registry_Api
             return array();
         }
 
-        $opts = array(
-            'fgcolor' => true,
+        return $GLOBALS['injector']->getInstance('IMP_Flags')->getList(array(
             'imap' => true,
-        );
-
-        if (!is_null($mailbox)) {
-            $opts['mailbox'] = $mailbox;
-        }
-
-        return $GLOBALS['injector']->getInstance('IMP_Imap_Flags')->getList($opts);
+            'mailbox' => $mailbox
+        ));
     }
 
 }
index 5dd2eff..8c904aa 100644 (file)
@@ -100,6 +100,7 @@ class IMP_Application extends Horde_Registry_Application
             'IMP_AuthImap' => 'IMP_Injector_Factory_AuthImap',
             'IMP_Crypt_Pgp' => 'IMP_Injector_Factory_Pgp',
             'IMP_Crypt_Smime' => 'IMP_Injector_Factory_Smime',
+            'IMP_Flags' => 'IMP_Injector_Factory_Flags',
             'IMP_Identity' => 'IMP_Injector_Factory_Identity',
             'IMP_Imap_Tree' => 'IMP_Injector_Factory_Imaptree',
             'IMP_Mail' => 'IMP_Injector_Factory_Mail',
diff --git a/imp/lib/Flag/Base.php b/imp/lib/Flag/Base.php
new file mode 100644 (file)
index 0000000..0dc599b
--- /dev/null
@@ -0,0 +1,229 @@
+<?php
+/**
+ * This class provides the data structure for a message flag.
+ *
+ * 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_Flag_Base implements Serializable
+{
+    /* Default background color. */
+    const DEFAULT_BG = '#fff';
+
+    /**
+     * The abbreviation.
+     *
+     * @var string
+     */
+    protected $_abbreviation = '';
+
+    /**
+     * The background color.
+     *
+     * @var string
+     */
+    protected $_bgcolor = '';
+
+    /**
+     * Is this flag settable by the user?
+     *
+     * @var boolean
+     */
+    protected $_canset = false;
+
+    /**
+     * The CSS class.
+     *
+     * @var string
+     */
+    protected $_css = '';
+
+    /**
+     * The CSS class for the icon.
+     *
+     * @var string
+     */
+    protected $_cssIcon = '';
+
+    /**
+     * Unique ID.
+     *
+     * @var string
+     */
+    protected $_id = '';
+
+    /**
+     * Get object properties.
+     *
+     * @param string $name  Available properties:
+     * <pre>
+     * 'abbreviation' - (string) The abbreviation to use in the mimp view.
+     * 'bgcolor' - (string) The background color.
+     * 'bgdefault' - (boolean) Is the backgroud color the default?
+     * 'canset' - (boolean) Can this flag be set by the user?
+     * 'css' - (string) The CSS class for the icon when the flag is set.
+     * 'cssicon' - (string) The CSS class for the icon.
+     * 'div' - (string) Return DIV HTML to output the icon for use in a
+     *         mailbox row.
+     * 'fgcolor' - (string) The foreground (text) color.
+     * 'form_set' - (string) Form value to use when setting flag.
+     * 'form_unset' - (string) Form value to use when unsetting flag.
+     * 'id' - (string) Unique ID.
+     * 'label' - (string) The query label.
+     * </pre>
+     *
+     * @return mixed  Property value.
+     */
+    public function __get($name)
+    {
+        switch ($name) {
+        case 'abbreviation':
+            return $this->_abbreviation;
+
+        case 'bgcolor':
+            return $this->_bgcolor
+                ? $this->_bgcolor
+                : self::DEFAULT_BG;
+
+        case 'bgdefault':
+            return ($this->bgcolor == self::DEFAULT_BG);
+
+        case 'canset':
+            return $this->_canset;
+
+        case 'css':
+            return $this->_css;
+
+        case 'cssicon':
+            return $this->_cssIcon
+                ? $this->_cssIcon
+                : $this->_css;
+
+        case 'div':
+            return $this->_css
+                ? '<div class="iconImg msgflags ' . $this->css . '" title="' . htmlspecialchars($this->label) . '"></div>'
+                : '';
+
+        case 'fgcolor':
+            return (Horde_Image::brightness($this->bgcolor) < 128)
+                ? '#f6f6f6'
+                : '#000';
+
+        case 'form_set':
+            return $this->id;
+
+        case 'form_unset':
+            return '0\\' . $this->id;
+
+        case 'id':
+            return $this->_id;
+
+        case 'label':
+            return $this->getLabel();
+        }
+    }
+
+    /**
+     * Set properties.
+     *
+     * @param string $name   Available properties:
+     * <pre>
+     * 'bgcolor' - (string) The background color.
+     * </pre>
+     * @param string $value  Property value.
+     */
+    public function __set($name, $value)
+    {
+        switch ($name) {
+        case 'bgcolor':
+            $this->_bgcolor = ($value == self::DEFAULT_BG)
+                ? ''
+                : $value;
+            break;
+        }
+    }
+
+    /**
+     * Given a list of flag objects, determines if this flag's status has
+     * changed.
+     *
+     * @param array $obs    A list of IMP_Flag_Base objects.
+     * @param boolean $add  True if these flags were added, false if they were
+     *                      removed.
+     *
+     * @return mixed  Null if no change, true if flag is added, false if flag
+     *                is removed.
+     */
+    public function changed($obs, $add)
+    {
+        return null;
+    }
+
+    /**
+     * Return the flag label.
+     *
+     * @param boolean $set  Return label for setting the flag?
+     *
+     * @return string  The label.
+     */
+    public function getLabel($set = true)
+    {
+        return $set
+            ? $this->_getLabel()
+            : sprintf(_("Not %s"), $this->_getLabel());
+    }
+
+    /**
+     * Determines if the flag exists given some input data.
+     *
+     * @param mixed $data  The input data to check.
+     *
+     * @return boolean  True if flag exists.
+     */
+    abstract public function match($data);
+
+    /**
+     * Return the flag label.
+     * Necessary evil as gettext strings can not be set directly to object
+     * properties.
+     *
+     * @return string  The label.
+     */
+    abstract protected function _getLabel();
+
+    /* Magic methods. */
+
+    /**
+     * String representation of the object.
+     *
+     * @return string  String representation (Flag ID).
+     */
+    public function __toString()
+    {
+        return $this->id;
+    }
+
+    /* Serializable methods. */
+
+    /**
+     */
+    public function serialize()
+    {
+        return $this->_bgcolor;
+    }
+
+    /**
+     */
+    public function unserialize($data)
+    {
+        $this->_bgcolor = $data;
+    }
+
+}
diff --git a/imp/lib/Flag/Imap.php b/imp/lib/Flag/Imap.php
new file mode 100644 (file)
index 0000000..e0bee28
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+/**
+ * This class provides the data structure for a message flag.
+ *
+ * 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_Flag_Imap extends IMP_Flag_Base
+{
+    /**
+     * The IMAP flag string used on the server.
+     *
+     * @var string
+     */
+    protected $_imapflag;
+
+    /**
+     * @param string $name  Additional properties:
+     * <pre>
+     * 'imapflag' - (string) The IMAP flag string.
+     * </pre>
+     */
+    public function __get($name)
+    {
+        switch ($name) {
+        case 'id':
+        case 'imapflag':
+            return $this->_imapflag;
+
+        default:
+            return parent::__get($name);
+        }
+    }
+
+    /**
+     * @param array $input  List of IMAP flags.
+     */
+    public function match($data)
+    {
+        return in_array($this->imapflag, $data);
+    }
+
+}
diff --git a/imp/lib/Flag/Imap/Answered.php b/imp/lib/Flag/Imap/Answered.php
new file mode 100644 (file)
index 0000000..c7ec23f
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * This class implements the answered flag (RFC 3501 [2.3.2]).
+ *
+ * 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_Flag_Imap_Answered extends IMP_Flag_Imap
+{
+    /**
+     */
+    protected $_bgcolor = '#cfc';
+
+    /**
+     */
+    protected $_css = 'flagAnswered';
+
+    /**
+     */
+    protected $_imapflag = '\\answered';
+
+    /**
+     */
+    protected function _getLabel()
+    {
+        return _("Answered");
+    }
+
+}
diff --git a/imp/lib/Flag/Imap/Deleted.php b/imp/lib/Flag/Imap/Deleted.php
new file mode 100644 (file)
index 0000000..bf87a84
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * This class implements the deleted flag (RFC 3501 [2.3.2]).
+ *
+ * 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_Flag_Imap_Deleted extends IMP_Flag_Imap
+{
+    /**
+     */
+    protected $_abbreviation = 'D';
+
+    /**
+     */
+    protected $_bgcolor = '#999';
+
+    /**
+     */
+    protected $_css = 'flagDeleted';
+
+    /**
+     */
+    protected $_imapflag = '\\deleted';
+
+    /**
+     */
+    protected function _getLabel()
+    {
+        return _("Deleted");
+    }
+
+}
diff --git a/imp/lib/Flag/Imap/Draft.php b/imp/lib/Flag/Imap/Draft.php
new file mode 100644 (file)
index 0000000..39007f4
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * This class implements the draft flag (RFC 3501 [2.3.2]).
+ *
+ * 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_Flag_Imap_Draft extends IMP_Flag_Imap
+{
+    /**
+     */
+    protected $_abbreviation = 'd';
+
+    /**
+     */
+    protected $_bgcolor = '#ffd93d';
+
+    /**
+     */
+    protected $_css = 'flagDraft';
+
+    /**
+     */
+    protected $_imapflag = '\\draft';
+
+    /**
+     */
+    protected function _getLabel()
+    {
+        return _("Draft");
+    }
+
+}
diff --git a/imp/lib/Flag/Imap/Flagged.php b/imp/lib/Flag/Imap/Flagged.php
new file mode 100644 (file)
index 0000000..a197336
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * This class implements the flagged for followup flag (RFC 3501 [2.3.2]).
+ *
+ * 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_Flag_Imap_Flagged extends IMP_Flag_Imap
+{
+    /**
+     */
+    protected $_bgcolor = '#fcc';
+
+    /**
+     */
+    protected $_canset = true;
+
+    /**
+     */
+    protected $_css = 'flagFlagged';
+
+    /**
+     */
+    protected $_imapflag = '\\flagged';
+
+    /**
+     */
+    protected function _getLabel()
+    {
+        return _("Flagged for Followup");
+    }
+
+}
diff --git a/imp/lib/Flag/Imap/Forwarded.php b/imp/lib/Flag/Imap/Forwarded.php
new file mode 100644 (file)
index 0000000..33ac8b3
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * This class implements the forwarded flag (RFC 5550 [5.9]).
+ *
+ * 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_Flag_Imap_Forwarded extends IMP_Flag_Imap
+{
+    /**
+     */
+    protected $_bgcolor = '#bfdfdf';
+
+    /**
+     */
+    protected $_css = 'flagForwarded';
+
+    /**
+     */
+    protected $_imapflag = '$forwarded';
+
+    /**
+     */
+    protected function _getLabel()
+    {
+        return _("Forwarded");
+    }
+
+}
diff --git a/imp/lib/Flag/Imap/Seen.php b/imp/lib/Flag/Imap/Seen.php
new file mode 100644 (file)
index 0000000..26c10b3
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * This class implements the seen flag (RFC 3501 [2.3.2]).
+ * Unseen display formatting is handled by the IMP_Flag_System_Unseen class.
+ *
+ * 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_Flag_Imap_Seen extends IMP_Flag_Imap
+{
+    /**
+     */
+    protected $_canset = true;
+
+    /**
+     */
+    protected $_cssIcon = 'flagSeen';
+
+    /**
+     */
+    protected $_imapflag = '\\seen';
+
+    /**
+     */
+    protected function _getLabel()
+    {
+        return _("Seen");
+    }
+
+}
diff --git a/imp/lib/Flag/System.php b/imp/lib/Flag/System.php
new file mode 100644 (file)
index 0000000..fcb52be
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+/**
+ * This class provides the data structure for an internal system flag.
+ *
+ * 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_Flag_System extends IMP_Flag_Base
+{
+}
diff --git a/imp/lib/Flag/System/Attachment.php b/imp/lib/Flag/System/Attachment.php
new file mode 100644 (file)
index 0000000..2c2766b
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+/**
+ * This class implements the attachment flag.
+ *
+ * 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_Flag_System_Attachment extends IMP_Flag_System
+{
+    /**
+     */
+    protected $_css = 'flagAttachmsg';
+
+    /**
+     */
+    protected $_id = 'attach';
+
+    /**
+     */
+    protected function _getLabel()
+    {
+        return _("Message has Attachments");
+    }
+
+    /**
+     * @param Horde_Mime_Headers $data  Headers object for a message.
+     */
+    public function match($data)
+    {
+        if (!($ctype = $data->getValue('content-type', Horde_Mime_Headers::VALUE_BASE))) {
+            return false;
+        }
+
+        list($primary, $sub) = explode('/', $ctype, 2);
+        return (($primary == 'multipart') &&
+            !in_array($sub, array('alternative', 'encrypt', 'related', 'signed')));
+    }
+
+}
diff --git a/imp/lib/Flag/System/Encrypted.php b/imp/lib/Flag/System/Encrypted.php
new file mode 100644 (file)
index 0000000..806cbcd
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+/**
+ * This class implements the encrypted message flag.
+ *
+ * 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_Flag_System_Encrypted extends IMP_Flag_System
+{
+    /**
+     */
+    protected $_css = 'flagEncryptmsg';
+
+    /**
+     */
+    protected $_id = 'encrypted';
+
+    /**
+     */
+    protected function _getLabel()
+    {
+        return _("Message is Encrypted");
+    }
+
+    /**
+     * @param Horde_Mime_Headers $data  Headers object for a message.
+     */
+    public function match($data)
+    {
+        $ctype = $data->getValue('content-type', Horde_Mime_Headers::VALUE_BASE);
+
+        return (($ctype == 'application/pkcs7-mime') ||
+                ($ctype == 'multipart/encrypted'));
+    }
+
+}
diff --git a/imp/lib/Flag/System/HighPriority.php b/imp/lib/Flag/System/HighPriority.php
new file mode 100644 (file)
index 0000000..0ca3432
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/**
+ * This class implements the high priority flag.
+ *
+ * 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_Flag_System_HighPriority extends IMP_Flag_System
+{
+    /**
+     */
+    protected $_abbreviation = '!';
+
+    /**
+     */
+    protected $_bgcolor = '#fcc';
+
+    /**
+     */
+    protected $_css = 'flagHighpriority';
+
+    /**
+     */
+    protected $_id = 'highp';
+
+    /**
+     */
+    protected function _getLabel()
+    {
+        return _("High Priority");
+    }
+
+    /**
+     * @param Horde_Mime_Headers $data  Headers object for a message.
+     */
+    public function match($data)
+    {
+        return ($GLOBALS['injector']->getInstance('IMP_Ui_Headers')->getPriority($data) == 'high');
+    }
+
+}
diff --git a/imp/lib/Flag/System/LowPriority.php b/imp/lib/Flag/System/LowPriority.php
new file mode 100644 (file)
index 0000000..9e912fc
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * This class implements the low priority flag.
+ *
+ * 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_Flag_System_LowPriority extends IMP_Flag_System
+{
+    /**
+     */
+    protected $_css = 'flagLowpriority';
+
+    /**
+     */
+    protected $_id = 'lowp';
+
+    /**
+     */
+    protected function _getLabel()
+    {
+        return _("Low Priority");
+    }
+
+    /**
+     * @param Horde_Mime_Headers $data  Headers object for a message.
+     */
+    public function match($data)
+    {
+        return ($GLOBALS['injector']->getInstance('IMP_Ui_Headers')->getPriority($data) == 'low');
+    }
+
+}
diff --git a/imp/lib/Flag/System/Personal.php b/imp/lib/Flag/System/Personal.php
new file mode 100644 (file)
index 0000000..ef20692
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+/**
+ * This class implements the personal message flag.
+ *
+ * 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_Flag_System_Personal extends IMP_Flag_System
+{
+    /**
+     */
+    protected $_css = 'flagPersonal';
+
+    /**
+     */
+    protected $_id = 'personal';
+
+    /**
+     */
+    protected function _getLabel()
+    {
+        return _("Personal");
+    }
+
+    /**
+     * @param mixed $data  Either an array of To addresses as returned by
+     *                     Horde_Mime_Address::getAddressesFromObject() or the
+     *                     identity that matched the address list.
+     */
+    public function match($data)
+    {
+        if (is_array($data)) {
+            $identity = $GLOBALS['injector']->getInstance('IMP_Identity');
+
+            foreach ($data as $val) {
+                if ($identity->hasAddress($val['inner'])) {
+                    return true;
+                }
+            }
+        } else if (!is_null($data)) {
+            return true;
+        }
+
+        return false;
+    }
+
+}
diff --git a/imp/lib/Flag/System/Signed.php b/imp/lib/Flag/System/Signed.php
new file mode 100644 (file)
index 0000000..80b8092
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * This class implements the signed message flag.
+ *
+ * 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_Flag_System_Signed extends IMP_Flag_System
+{
+    /**
+     */
+    protected $_css = 'flagSignedmsg';
+
+    /**
+     */
+    protected $_id = 'signed';
+
+    /**
+     */
+    protected function _getLabel()
+    {
+        return _("Message is Signed");
+    }
+
+    /**
+     * @param Horde_Mime_Headers $data  Headers object for a message.
+     */
+    public function match($data)
+    {
+        return ($data->getValue('content-type', Horde_Mime_Headers::VALUE_BASE) == 'multipart/signed');
+    }
+
+}
diff --git a/imp/lib/Flag/System/Unseen.php b/imp/lib/Flag/System/Unseen.php
new file mode 100644 (file)
index 0000000..79809f8
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+/**
+ * This class implements formatting for unseen messages. Unseen occurs when
+ * the seen flag (RFC 3501 [2.3.2]) is NOT set; thus, it can not be handled
+ * in the seen flag object.
+ *
+ * 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_Flag_System_Unseen extends IMP_Flag_System
+{
+    /**
+     */
+    protected $_abbreviation = 'U';
+
+    /**
+     */
+    protected $_bgcolor = '#eef';
+
+    /**
+     */
+    protected $_css = 'flagUnseen';
+
+    /**
+     */
+    protected $_id = 'unseen';
+
+    /**
+     */
+    protected function _getLabel()
+    {
+        return _("Unseen");
+    }
+
+    /**
+     */
+    public function changed($obs, $add)
+    {
+        foreach ($obs as $val) {
+            if ($val instanceof IMP_Flag_Imap_Seen) {
+                return !$add;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @param array $data  List of IMAP flags.
+     */
+    public function match($data)
+    {
+        return !in_array('\\seen', $data);
+    }
+
+}
diff --git a/imp/lib/Flag/User.php b/imp/lib/Flag/User.php
new file mode 100644 (file)
index 0000000..b2b6e23
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * This class provides the data structure for a user-defined message flag.
+ *
+ * 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_Flag_User extends IMP_Flag_Imap
+{
+    /**
+     */
+    protected $_canset = true;
+
+    /**
+     */
+    protected $_css = 'flagUser';
+
+    /**
+     * The flag label.
+     *
+     * @var string
+     */
+    protected $_label;
+
+    /**
+     * Constructor.
+     *
+     * @param string $label    The label.
+     * @param string $flag     The IMAP flag.
+     * @param string $bgcolor  The background color.
+     */
+    public function __construct($label, $flag = null, $bgcolor = null)
+    {
+        $this->label = $label;
+        $this->imapflag = is_null($flag)
+            ? $label
+            : $flag;
+        if (isset($bgcolor)) {
+            $this->bgcolor = $bgcolor;
+        }
+    }
+
+    /**
+     */
+    public function __set($name, $value)
+    {
+        switch ($name) {
+        case 'imapflag':
+            /* IMAP keywords must conform to RFC 3501 [9] (flag-keyword).
+             * Convert whitespace to underscore. */
+            $this->_imapflag = $GLOBALS['injector']->getInstance('IMP_Injector_Factory_Imap')->create()->getUtils()->stripNonAtomChars(Horde_String::convertCharset(strtr($value, ' ', '_'), 'UTF-8', 'UTF7-IMAP'));
+            break;
+
+        case 'label':
+            $this->_label = $value;
+            break;
+
+        default:
+            parent::__set($name, $value);
+            break;
+        }
+    }
+
+    /**
+     */
+    protected function _getLabel()
+    {
+        return $this->_label;
+    }
+
+    /* Serializable methods. */
+
+    /**
+     */
+    public function serialize()
+    {
+        return json_encode(array(
+            parent::serialize(),
+            $this->_label,
+            $this->_imapflag
+        ));
+    }
+
+    /**
+     */
+    public function unserialize($data)
+    {
+        $data = json_decode($data, true);
+
+        parent::unserialize($data[0]);
+        $this->_label = $data[1];
+        $this->_imapflag = $data[2];
+    }
+
+}
diff --git a/imp/lib/Flags.php b/imp/lib/Flags.php
new file mode 100644 (file)
index 0000000..371ccf0
--- /dev/null
@@ -0,0 +1,397 @@
+<?php
+/**
+ * The IMP_Flags class provides an interface to deal with display of
+ * flags/keywords/labels on messages.
+ *
+ * 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_Flags implements ArrayAccess, Serializable
+{
+    /**
+     * Has the object data changed?
+     *
+     * @var boolean
+     */
+    public $changed = false;
+
+    /**
+     * The list of internal flags.
+     *
+     * @var array
+     */
+    protected $_flags = array();
+
+    /**
+     * The list of user flags.
+     *
+     * @var array
+     */
+    protected $_userflags = array();
+
+    /**
+     * Constructor.
+     */
+    public function __construct()
+    {
+        /* Build list of default flags. */
+        foreach (array('Imap', 'System') as $type) {
+            $di = new DirectoryIterator(IMP_BASE . '/lib/Flag/' . $type);
+            foreach ($di as $val) {
+                if ($val->isFile()) {
+                    $cname = 'IMP_Flag_' . $type . '_' . $val->getBasename('.php');
+                    if (class_exists($cname)) {
+                        $ob = new $cname();
+                        $this->_flags[$ob->id] = $ob;
+                    }
+                }
+            }
+        }
+
+        if ($f_list = $GLOBALS['prefs']->getValue('msgflags')) {
+            $f_list = @unserialize($f_list);
+            if (is_array($f_list)) {
+                foreach ($f_list as $val) {
+                    $this->_userflags[$val->id] = $val;
+                }
+            }
+        }
+
+        $this->changed = true;
+    }
+
+    /**
+     * Save the flag list to the prefs backend.
+     */
+    protected function _save()
+    {
+        global $prefs;
+
+        if (!$prefs->isLocked('msgflags')) {
+            $prefs->setValue('msgflags', serialize($this->_userflags));
+        }
+
+        $this->changed = true;
+    }
+
+    /**
+     * Return the raw list of flags.
+     *
+     * @param array $opts  Additional options:
+     * <pre>
+     * 'imap' - (boolean) If true, only return IMAP flags that can be set by
+     *          the user.
+     *          DEFAULT: false
+     * 'mailbox' - (string) A real (not virtual) IMAP mailbox. If set, will
+     *             determine what flags are available in the mailbox.
+     *             DEFAULT: '' (no mailbox check)
+     * </pre>
+     *
+     * @return array  An array of IMP_Flag_Base elements.
+     */
+    public function getList(array $opts = array())
+    {
+        $ret = array_merge($this->_flags, $this->_userflags);
+
+        if (!empty($opts['imap'])) {
+            foreach ($ret as $key => $val) {
+                if (!($val instanceof IMP_Flag_Imap)) {
+                    unset($ret[$key]);
+                }
+            }
+        }
+
+        if (!isset($opts['mailbox']) || !strlen($opts['mailbox'])) {
+            return array_values($ret);
+        }
+
+        /* Alter the list of flags for a mailbox depending on the return
+         * from the PERMANENTFLAGS IMAP response. */
+        try {
+            /* Make sure we are in R/W mailbox mode (SELECT). No flags are
+             * allowed in EXAMINE mode. */
+            $imp_imap = $GLOBALS['injector']->getInstance('IMP_Injector_Factory_Imap')->create();
+            $imp_imap->openMailbox($opts['mailbox'], Horde_Imap_Client::OPEN_READWRITE);
+            $status = $imp_imap->status($opts['mailbox'], Horde_Imap_Client::STATUS_PERMFLAGS);
+        } catch (Horde_Imap_Client_Exception $e) {
+            return array_values($ret);
+        }
+
+        /* Limited flags allowed in mailbox. */
+        if (array_search('\\*', $status['permflags']) === false) {
+            foreach ($ret as $key => $val) {
+                if (($val instanceof IMP_Flag_Imap) &&
+                    !in_array($val->imapflag, $status['permflags'])) {
+                    unset($ret[$key]);
+                }
+            }
+        }
+
+        /* Get list of unknown flags. */
+        if ($GLOBALS['prefs']->getValue('show_all_flags')) {
+            /* Get list of IMAP flags. */
+            $imapflags = array();
+            foreach ($ret as $val) {
+                if ($val instanceof IMP_Flag_Imap) {
+                    $imapflags[] = $val->imapflag;
+                }
+            }
+
+            foreach ($status['permflags'] as $val) {
+                if (($val != '\\*') && !in_array($val, $imapflags)) {
+                    $ob = new IMP_Flag_User(Horde_String::convertCharset($val, 'UTF7-IMAP', 'UTF-8'), $val);
+                    $ret[] = $ob;
+                }
+            }
+        }
+
+        return array_values($ret);
+    }
+
+    /**
+     * Add a user-defined IMAP flag.
+     *
+     * @param string $label  The label to use for the new flag.
+     *
+     * @return string  The IMAP flag name.
+     */
+    public function addFlag($label)
+    {
+        if (strlen($label) == 0) {
+            return;
+        }
+
+        $ob = new IMP_Flag_User($label);
+
+        if (!isset($this->_userflags[$ob->id])) {
+            $this->_userflags[$ob->id] = $ob;
+            $this->_save();
+        }
+
+        return $ob->imapflag;
+    }
+
+    /**
+     * Updates flag properties.
+     *
+     * @param string $key   The flag key.
+     * @param string $type  The property to update. Either 'bgcolor' or
+     *                      'label'.
+     * @param string $data  The updated data.
+     */
+    public function updateFlag($key, $type, $data)
+    {
+        if (isset($this->_flags[$key])) {
+            $ob = $this->_flags[$key];
+        } elseif (isset($this->_userflags[$key])) {
+            $ob = $this->_userflags[$key];
+        } else {
+            return;
+        }
+
+        $ob->$type = $data;
+
+        if (isset($this->_flags[$key]) && ($this->_flags[$key] == $ob)) {
+            if (isset($this->_userflags[$key])) {
+                unset($this->_userflags[$key]);
+                $this->_save();
+            }
+        } else {
+            $this->_userflags[$key] = $ob;
+            $this->_save();
+        }
+    }
+
+    /**
+     * Parse a list of flag information.
+     *
+     * @param array $opts  Options:
+     * <pre>
+     * 'flags' - (array) IMAP flag info. A lowercase list of flags returned
+     *           by the IMAP server.
+     * 'headers' - (Horde_Mime_Headers) Determines attachment and priority
+     *             information from a headers object.
+     * 'personal' - (mixed) Personal message info. Either an array of To
+     *              addresses as returned by
+     *              Horde_Mime_Address::getAddressesFromObject() or the
+     *              identity that matched the address list.
+     * </pre>
+     *
+     * @return array  A list of IMP_Flag_Base objects.
+     */
+    public function parse(array $opts = array())
+    {
+        $opts = array_merge(array(
+            'flags' => array(),
+            'headers' => null,
+            'personal' => null
+        ), $opts);
+
+        $imap = ($GLOBALS['session']->get('imp', 'protocol') == 'imap');
+        $ret = array();
+
+        foreach (array_merge($this->_flags, $this->_userflags) as $val) {
+            switch (get_class($val)) {
+            case 'IMP_Flag_System_Attachment':
+            case 'IMP_Flag_System_Encrypted':
+            case 'IMP_Flag_System_HighPriority':
+            case 'IMP_Flag_System_LowPriority':
+            case 'IMP_Flag_System_Signed':
+                if (!is_null($opts['headers']) &&
+                    $val->match($opts['headers'])) {
+                    $ret[] = $val;
+                }
+                break;
+
+            case 'IMP_Flag_System_Personal':
+                if (!is_null($opts['personal']) &&
+                    $val->match($opts['personal'])) {
+                    $ret[] = $val;
+                }
+                break;
+
+            case 'IMP_Flag_System_Unseen':
+            default:
+                if ($imap && $val->match($opts['flags'])) {
+                    $ret[] = $val;
+                }
+                break;
+            }
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Process a flag ID formatted for use in form data.
+     *
+     * @param string $id  The ID from form data.
+     *
+     * @return array  Two element array:
+     * <pre>
+     * 'flag' - (string) The flag name.
+     * 'set' - (boolean) Whether the flag should be set or not.
+     * </pre>
+     */
+    public function parseFormId($id)
+    {
+        return (strpos($id, '0\\') === 0)
+            ? array('flag' => substr($id, 2), 'set' => false)
+            : array('flag' => $id, 'set' => true);
+    }
+
+    /**
+     * Returns a list of flags that have changed due to IMAP flag changes.
+     *
+     * @param array $flags  The list of IMAP flags added/removed.
+     * @param boolean $add  True if these flags were added, false if they were
+     *                      removed.
+     *
+     * @return array  Array with two keys: 'add' and 'remove'. Each key
+     *                contains a list of IMP_Flag_Base objects.
+     */
+    public function changed($flags, $add)
+    {
+        $ret = array(
+            'add' => array(),
+            'remove' => array()
+        );
+
+        $obs = array();
+        foreach ($flags as $val) {
+            $obs[] = $this[$val];
+        }
+
+        if ($add) {
+            $ret['add'] = $obs;
+        } else {
+            $ret['remove'] = $obs;
+        }
+
+        foreach (array_merge($this->_flags, $this->_userflags) as $val) {
+            $res = $val->changed($obs, $add);
+
+            if ($res === false) {
+                $ret['remove'][] = $val;
+            } elseif ($res === true) {
+                $ret['add'][] = $val;
+            }
+        }
+
+        return $ret;
+    }
+
+    /* ArrayAccess methods. */
+
+    /**
+     */
+    public function offsetExists($offset)
+    {
+        return isset($this->_flags[$offset]) ||
+               isset($this->_userflags[$offset]);
+    }
+
+    /**
+     */
+    public function offsetGet($offset)
+    {
+        if (isset($this->_flags[$offset])) {
+            return $this->_flags[$offset];
+        } elseif ($this->_userflags[$offset]) {
+            return $this->_userflags[$offset];
+        }
+
+        return null;
+    }
+
+    /**
+     * @throws InvalidArgumentException
+     */
+    public function offsetSet($offset, $value)
+    {
+        throw new InvalidArgumentException('Use addFlag()/updateFlag()');
+    }
+
+    /**
+     */
+    public function offsetUnset($offset)
+    {
+        if (isset($this->_userflags[$offset])) {
+            unset($this->_userflags[$offset]);
+            $this->_save();
+        }
+    }
+
+    /* Serializable methods. */
+
+    /**
+     */
+    public function serialize()
+    {
+        return serialize(array(
+            $this->_flags,
+            $this->_userflags
+        ));
+    }
+
+    /**
+     */
+    public function unserialize($data)
+    {
+        $data = @unserialize($data);
+        if (!is_array($data)) {
+            throw new Exception('Cache invalidation.');
+        }
+
+        $this->_flags = $data[0];
+        $this->_userflags = $data[1];
+    }
+
+}
diff --git a/imp/lib/Imap/Flags.php b/imp/lib/Imap/Flags.php
deleted file mode 100644 (file)
index 80784c4..0000000
+++ /dev/null
@@ -1,484 +0,0 @@
-<?php
-/**
- * The IMP_Imap_Flags class provides an interface to deal with display of
- * flags/labels on messages.
- *
- * 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_Imap_Flags
-{
-    /**
-     * The cached list of flags.
-     *
-     * @var array
-     */
-    protected $_flags = null;
-
-    /**
-     * The 'msgflags_user' preference value.
-     *
-     * @var array
-     */
-    protected $_userflags = null;
-
-    /**
-     * Temporary flags (i.e. flags created by other MUAs).
-     *
-     * @var array
-     */
-    protected $_tempflags = array();
-
-    /**
-     * Save the flag list to the prefs backend.
-     *
-     * @param boolean $user  If true, update the user flag list. Otherwise,
-     *                       update the system flag list.
-     */
-    protected function _save($user = true)
-    {
-        global $prefs;
-
-        if ($user) {
-            if (!$prefs->isLocked('msgflags_user')) {
-                $prefs->setValue('msgflags_user', json_encode($this->_userflags));
-            }
-        } elseif (!$prefs->isLocked('msgflags')) {
-            $prefs->setValue('msgflags', json_encode(array_diff_key($this->_flags, $this->_userflags)));
-        }
-    }
-
-    /**
-     * Return the raw list of flags.
-     *
-     * @param array $options  Additional options:
-     * <pre>
-     * 'div' - (boolean) If true, return a DIV tag containing the code
-     *         necessary to display the icon.
-     *         DEFAULT: false
-     * 'fgcolor' - (boolean) If true, add foreground color information to be
-     *             used for text overlay purposes.
-     *             DEFAULT: false
-     * 'imap' - (boolean) If true, only return IMAP flags that can be set by
-     *          the user.
-     *          DEFAULT: false
-     * 'mailbox' - (string) A real (not virtual) IMAP mailbox. If set, will
-     *             determine what flags are available in the mailbox.
-     *             DEFAULT: '' (no mailbox check)
-     * </pre>
-     *
-     * @return array  An array of flag information (see 'msgflags' preference
-     *                for format). If 'fgcolor' option is true, also adds
-     *                a 'f' key to each entry with foreground color info.
-     *                If 'div' option is true, adds a 'div' key with HTML
-     *                text.
-     */
-    public function getList($options = array())
-    {
-        $this->_loadList();
-
-        $avail_flags = array_keys($this->_flags);
-
-        $ret = $types = array();
-        if (!empty($options['imap'])) {
-            $types = array('imapp', 'imapu');
-        }
-
-        /* Reduce the list of flags for the mailbox depending on the return
-         * from the PERMANENTFLAGS IMAP response. */
-        if (!empty($options['mailbox'])) {
-            try {
-                /* Make sure we are in R/W mailbox mode (SELECT). No flags are
-                 * allowed in EXAMINE mode. */
-                $imp_imap = $GLOBALS['injector']->getInstance('IMP_Injector_Factory_Imap')->create();
-                $imp_imap->openMailbox($options['mailbox'], Horde_Imap_Client::OPEN_READWRITE);
-                $status = $imp_imap->status($options['mailbox'], Horde_Imap_Client::STATUS_PERMFLAGS);
-
-                if (($pos = array_search('\\*', $status['permflags'])) !== false) {
-                    if ($GLOBALS['prefs']->getValue('show_all_flags')) {
-                        unset($status['permflags'][$pos]);
-                        $avail_flags = array_keys(array_flip(array_merge($avail_flags, $status['permflags'])));
-                    }
-                } else {
-                    $avail_flags = $GLOBALS['prefs']->getValue('show_all_flags')
-                        ? $status['permflags']
-                        : array_filter(array_diff($status['permflags'], $avail_flags));
-                }
-            } catch (Horde_Imap_Client_Exception $e) {}
-        }
-
-        foreach ($avail_flags as $key) {
-            if (!isset($this->_flags[$key])) {
-                /* Keywords might be UTF7-IMAP encoded. */
-              $ret[$key] = $this->_createEntry(Horde_String::convertCharset($key, 'UTF7-IMAP', 'UTF-8'));
-                $ret[$key]['flag'] = $key;
-                $this->_tempflags[$key] = $ret[$key];
-            } else {
-                $ret[$key] = $this->_flags[$key];
-                if (!empty($options['imap']) &&
-                    !in_array($ret[$key]['t'], $types)) {
-                    unset($ret[$key]);
-                } else {
-                    $ret[$key]['flag'] = $key;
-                    if (!empty($options['fgcolor'])) {
-                        $ret[$key] = $this->_getColor($key, $ret[$key]);
-                    }
-                    if (!empty($options['div']) && isset($ret[$key]['c'])) {
-                        $ret[$key]['div'] = $this->_getDiv($ret[$key]['c'], $ret[$key]['l']);
-                    }
-                }
-            }
-        }
-
-        return $ret;
-    }
-
-    /**
-     * Loads the flag list from the preferences into the local cache.
-     */
-    protected function _loadList()
-    {
-        if (!is_null($this->_flags)) {
-            return;
-        }
-
-        $this->_userflags = json_decode($GLOBALS['prefs']->getValue('msgflags_user'), true);
-        $this->_flags = array_merge(
-            $this->_userflags,
-            json_decode($GLOBALS['prefs']->getValue('msgflags'), true)
-        );
-
-        /* Sanity checking. */
-        if (is_array($this->_flags)) {
-            $this->_flags = array_change_key_case($this->_flags, CASE_LOWER);
-        } else {
-            $this->_flags = array();
-            $this->_save();
-        }
-    }
-
-    /**
-     * Add a user-defined IMAP flag.
-     *
-     * @param string $label  The label to use for the new flag.
-     *
-     * @return string  The IMAP flag name.
-     */
-    public function addFlag($label)
-    {
-        if (strlen($label) == 0) {
-            return;
-        }
-
-        $this->_loadList();
-
-        /* IMAP keywords must conform to RFC 3501 [9] (flag-keyword). Convert
-         * whitespace to underscore. */
-        $key = $GLOBALS['injector']->getInstance('IMP_Injector_Factory_Imap')->create()->getUtils()->stripNonAtomChars(Horde_String::convertCharset(strtr($label, ' ', '_'), 'UTF-8', 'UTF7-IMAP'));
-        if (!isset($this->_flags[$key])) {
-            $entry = $this->_createEntry($label);
-
-            $this->_flags[$key] = $entry;
-            $this->_userflags[$key] = $entry;
-
-            $this->_save();
-        }
-
-        return $key;
-    }
-
-    /**
-     * Creates a flag entry data object.
-     *
-     * @param string $label  The label to use for the flag.
-     *
-     * @return array  Flag data object.
-     */
-    protected function _createEntry($label)
-    {
-        return array(
-            // 'a' => These flags are not shown in mimp
-            'b' => $GLOBALS['prefs']->getValue('msgflags_color'),
-            'c' => 'flagUser',
-            'd' => true,
-            'l' => $label,
-            't' => 'imapp'
-        );
-    }
-
-    /**
-     * Updates a flag.
-     *
-     * @param string $label  The flag label.
-     * @param array $data    The data to update.
-     */
-    public function updateFlag($label, $data)
-    {
-        $this->_loadList();
-
-        if (isset($this->_flags[$label])) {
-            foreach ($data as $key => $val) {
-                $this->_flags[$label][$key] = $val;
-            }
-
-            $this->_save(isset($this->_updateflags[$label]));
-        }
-    }
-
-    /**
-     * Delete a flag from the list.
-     *
-     * @param string $label  The flag label.
-     *
-     * @return boolean  True on success.
-     */
-    public function deleteFlag($label)
-    {
-        $this->_loadList();
-
-        if (isset($this->_flags[$label]) &&
-            $this->_flags[$label]['l'] &&
-            !empty($this->_flags[$label]['d'])) {
-            $user_flag = isset($this->_userflags[$label]);
-            unset($this->_flags[$label], $this->_userflags[$label]);
-            $this->_save($user_flag);
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * Parse a list of flag information.
-     *
-     * @param array $options  Additional options:
-     * <pre>
-     * 'div' - (boolean) If true, return a DIV tag containing the code
-     *         necessary to display the icon.
-     *         DEFAULT: false
-     * 'flags' - (array) [REQUIRED] IMAP flag info. A lowercase list of flags
-     *           returned by the IMAP server.
-     * 'headers' - (Horde_Mime_Headers) Determines attachment and priority
-     *             information from a headers object.
-     * 'personal' - (mixed) Personal message info. Either an array of to
-     *              addresses as returned by
-     *              Horde_Mime_Address::getAddressesFromObject(), or the
-     *              identity that matched the address list.
-     * </pre>
-     *
-     * @return array  A list of flags with the following keys:
-     * <pre>
-     * 'abbrev' - (string) The abbreviation to use.
-     * 'bg' - (string) The background to use.
-     * 'classname' - (string) If set, the flag classname to use.
-     * 'fg' - (string) The foreground color to use.
-     * 'flag' - (string) The matched flag (lowercase).
-     * 'div' - (string) A DIV HTML element, if 'div' option is true and a
-     *         classname is defined.
-     * 'label' - (string) The label of the flag.
-     * 'type' - (string) The flag type.
-     * </pre>
-     */
-    public function parse($options = array())
-    {
-        $this->_loadList();
-
-        $process = $ret = array();
-        $f = $this->_flags;
-
-        if (isset($options['personal'])) {
-            if (is_array($options['personal'])) {
-                $identity = $GLOBALS['injector']->getInstance('IMP_Identity');
-                foreach ($options['personal'] as $val) {
-                    if ($identity->hasAddress($val['inner'])) {
-                        $process['personal'] = $f['personal'];
-                        break;
-                    }
-                }
-            } else if (!is_null($options['personal'])) {
-                $process['personal'] = $f['personal'];
-            }
-        }
-
-        if (!empty($options['headers'])) {
-            $imp_hdr_ui = new IMP_Ui_Headers();
-            switch ($imp_hdr_ui->getPriority($options['headers'])) {
-            case 'high':
-                $process['highpri'] = $f['highpri'];
-                break;
-
-            case 'low':
-                $process['lowpri'] = $f['lowpri'];
-                break;
-            }
-
-            if ($ctype = $options['headers']->getValue('content-type', Horde_Mime_Headers::VALUE_BASE)) {
-                $imp_mbox_ui = new IMP_Ui_Mailbox();
-                if ($type = $imp_mbox_ui->getAttachmentType($ctype)) {
-                    $process[$type] = $f[$type];
-                }
-            }
-        }
-
-        if ($GLOBALS['session']->get('imp', 'protocol') == 'imap') {
-            $flaglist = empty($options['flags'])
-                ? array()
-                : array_map('strtolower', $options['flags']);
-
-            foreach (array_merge($f, $this->_tempflags) as $k => $v) {
-                if (in_array($v['t'], array('imap', 'imapp', 'imapu', 'imp'))) {
-                    $match = in_array($k, $flaglist);
-                    if (!empty($v['n'])) {
-                        $match = !$match;
-                    }
-
-                    if ($match) {
-                        $process[$k] = $v;
-                    }
-                }
-            }
-        }
-
-        foreach ($process as $key => $val) {
-            $color = $this->_getColor($key, $val);
-
-            $tmp = array(
-                'bg' => $color['b'],
-                'fg' => $color['f'],
-                'flag' => $key,
-                'label' => $val['l'],
-                'type' => $val['t']
-            );
-
-            if (isset($val['a'])) {
-                $tmp['abbrev'] = $val['a'];
-            }
-
-            if (isset($val['c'])) {
-                $tmp['classname'] = $val['c'];
-                if (!empty($options['div'])) {
-                    $tmp['div'] = $this->_getDiv($val['c'], $val['l']);
-                }
-            }
-
-            $ret[] = $tmp;
-        }
-
-        return $ret;
-    }
-
-    /**
-     * Get the list of set/unset actions for use in dropdown lists.
-     *
-     * @param string $mbox  The current mailbox.
-     *
-     * @return array  An array with 2 elements: 'set' and 'unset'.
-     */
-    public function getFlagList($mbox)
-    {
-        $ret = array('set' => array(), 'unset' => array());
-
-        foreach ($this->getList(array('imap' => true, 'mailbox' => $mbox)) as $val) {
-            $tmp = array(
-                'f' => $val['flag'],
-                'l' => $val['l']
-            );
-
-            /* Check for 'opposite' flag actions. */
-            $act1 = isset($val['n']) ? 'unset' : 'set';
-            $act2 = ($act1 == 'set') ? 'unset' : 'set';
-
-            $ret[$act1][] = $tmp;
-            $tmp['f'] = '0\\' . $val['flag'];
-            $ret[$act2][] = $tmp;
-        }
-
-        return $ret;
-    }
-
-    /**
-     * Output a DIV element to display the icon.
-     *
-     * @param string $c  A classname.
-     * @param string $l  The flag label.
-     *
-     * @return string  A HTML DIV element.
-     */
-    protected function _getDiv($c, $l)
-    {
-        return '<div class="iconImg msgflags ' . $c . '" title="' . htmlspecialchars($l) . '"></div>';
-    }
-
-    /**
-     * Process a flag ID formatted for use in form data.
-     *
-     * @param string $id  The ID from form data.
-     *
-     * @return array  Two element array:
-     * <pre>
-     * 'flag' - (string) The flag name.
-     * 'set' - (boolean) Whether the flag should be set or not.
-     * </pre>
-     */
-    public function parseFormId($id)
-    {
-        if (strpos($id, '0\\') === 0) {
-            return array('flag' => substr($id, 2), 'set' => false);
-        }
-        return array('flag' => $id, 'set' => true);
-    }
-
-    /**
-     * 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.
-     * @param array $in    The array of flag data.
-     *
-     * @return array  $in with the 'b' and 'f' keys populated.
-     */
-    protected function _getColor($key, $in)
-    {
-        $in['f'] = '#000';
-
-        if (!isset($in['b'])) {
-            $in['b'] = '#fff';
-        } elseif (Horde_Image::brightness($in['b']) < 128) {
-            $in['f'] = '#f6f6f6';
-        }
-
-        return $in;
-    }
-
-}
index 9179c81..f24a12f 100644 (file)
@@ -132,14 +132,33 @@ class IMP_Indices implements Countable, Iterator
     /**
      * Returns mailbox/UID information for the first index.
      *
-     * @return array  A 2-element array with the mailbox and the UID.
+     * @return boolean $all  If true, returns all UIDs for the first index
+     *                       in an array. If false, returns the first UID for
+     *                       the first index as a string.
+     *
+     * @return array  A 2-element array with the mailbox and the UID(s).
      */
-    public function getSingle()
+    public function getSingle($all = false)
     {
         $val = reset($this->_indices);
-        return array(key($this->_indices), reset($val));
+        return array(key($this->_indices), $all ? $val : reset($val));
+    }
+
+    /**
+     * Return a copy of the indices array.
+     *
+     * @return array  The indices array (keys are mailbox names, values are
+     *                arrays of UIDS).
+     */
+    public function indices()
+    {
+        /* This creates a copy of the indices array. Needed because the
+         * Iterator functions rely on pointers. */
+        return $this->_indices;
     }
 
+    /* Countable methods. */
+
     /**
      * Index count.
      *
@@ -156,19 +175,6 @@ class IMP_Indices implements Countable, Iterator
         return $count;
     }
 
-    /**
-     * Return a copy of the indices array.
-     *
-     * @return array  The indices array (keys are mailbox names, values are
-     *                arrays of UIDS).
-     */
-    public function indices()
-    {
-        /* This creates a copy of the indices array. Needed because the
-         * Iterator functions rely on pointers. */
-        return $this->_indices;
-    }
-
     /* Magic methods. */
 
     /**
diff --git a/imp/lib/Injector/Factory/Flags.php b/imp/lib/Injector/Factory/Flags.php
new file mode 100644 (file)
index 0000000..f4f255e
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+/**
+ * A Horde_Injector based factory for the IMP_Flags object.
+ *
+ * PHP version 5
+ *
+ * @author   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/gpl.html GPL
+ * @link     http://pear.horde.org/index.php?package=IMP
+ * @package  IMP
+ */
+
+/**
+ * A Horde_Injector based factory for the IMP_Flags object.
+ *
+ * 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
+ * @link     http://pear.horde.org/index.php?package=IMP
+ * @package  IMP
+ */
+class IMP_Injector_Factory_Flags
+{
+    /**
+     * Return the IMP_Flags instance.
+     *
+     * @return IMP_Flags  The singleton instance.
+     */
+    public function create(Horde_Injector $injector)
+    {
+        try {
+            $instance = $GLOBALS['session']->get('imp', 'flags');
+        } catch (Exception $e) {
+            Horde::logMessage('Could not unserialize stored IMP_Flags object.', 'DEBUG');
+            $instance = null;
+        }
+
+        if (is_null($instance)) {
+            $instance = new IMP_Flags();
+        }
+
+        register_shutdown_function(array($this, 'shutdown'), $instance);
+
+        return $instance;
+    }
+
+    /**
+     * Store serialized version of object in the current session.
+     *
+     * @param IMP_Flags $instance  Flags object.
+     */
+    public function shutdown($instance)
+    {
+        /* Only need to store the object if the object has changed. */
+        if ($instance->changed) {
+            $GLOBALS['session']->set('imp', 'flags', $instance);
+        }
+    }
+
+}
index 6a3930a..0f359b3 100644 (file)
@@ -651,12 +651,12 @@ class IMP_Message
     }
 
     /**
-     * Sets or clears a given flag(s) for all messages in a list of mailboxes.
+     * Adds or removes flag(s) for all messages in a list of mailboxes.
      * This function works with IMAP only, not POP3.
      *
-     * @param array $flags     The IMAP flag(s) to set or clear.
+     * @param array $flags     The IMAP flag(s) to add or remove.
      * @param array $mboxes    The list of mailboxes to flag.
-     * @param boolean $action  If true, set the flag(s), otherwise, clear the
+     * @param boolean $action  If true, add the flag(s), otherwise, remove the
      *                         flag(s).
      *
      * @return boolean  True if successful, false if not.
index fab6e01..9ab665c 100644 (file)
@@ -471,8 +471,7 @@ class IMP_Prefs_Ui
             return $prefs->setValue('default_encrypt', $ui->vars->default_encrypt);
 
         case 'flagmanagement':
-            $this->_updateFlagManagement($ui);
-            return false;
+            return $this->_updateFlagManagement($ui);
 
         case 'initialpageselect':
             return $prefs->setValue('initial_page', IMP::formMbox($ui->vars->initial_page, false));
@@ -935,49 +934,38 @@ class IMP_Prefs_Ui
             'ImpFlagPrefs.confirm_delete' => _("Are you sure you want to delete this flag?")
         ));
 
-        $msgflags_locked = $GLOBALS['prefs']->isLocked('msgflags');
-        $userflags_locked = $GLOBALS['prefs']->isLocked('msgflags_user');
-
         $t = $GLOBALS['injector']->createInstance('Horde_Template');
         $t->setOption('gettext', true);
+        $t->set('locked', $GLOBALS['prefs']->isLocked('msgflags'));
 
         $out = array();
-        $flaglist = $GLOBALS['injector']->getInstance('IMP_Imap_Flags')->getList(array('div' => true, 'fgcolor' => true));
-        foreach ($flaglist as $key => $val) {
-            $hash = hash('md5', $key);
+        $flaglist = $GLOBALS['injector']->getInstance('IMP_Flags')->getList();
+        foreach ($flaglist as $val) {
+            $hash = hash('md5', $val->id);
             $bgid = 'bg_' . $hash;
-            $color = htmlspecialchars($val['b']);
-            $label = htmlspecialchars($val['l']);
+            $color = htmlspecialchars($val->bgcolor);
+            $label = htmlspecialchars($val->label);
             $bgstyle = 'background-color:' . $color;
             $tmp = array();
 
-            if ($val['t'] == 'imapp') {
+            if ($val instanceof IMP_Flag_User) {
                 $tmp['label'] = $label;
-                $tmp['imapp'] = true;
+                $tmp['user'] = true;
                 $tmp['label_name'] = 'label_' . $hash;
-                if ($userflags_locked) {
-                    $tmp['locked'] = true;
-                }
             } else {
                 $tmp['label'] = Horde::label($bgid, $label);
-                $tmp['icon'] = $val['div'];
-                if ($msgflags_locked) {
-                    $tmp['locked'] = true;
-                }
+                $tmp['icon'] = $val->div;
             }
 
-            $tmp['colorstyle'] = $bgstyle . ';color:' . htmlspecialchars($val['f']);
+            $tmp['colorstyle'] = $bgstyle . ';color:' . htmlspecialchars($val->fgcolor);
             $tmp['colorid'] = $bgid;
             $tmp['color'] = $color;
 
-            $tmp['flag_del'] = !empty($val['d']);
-
             $out[] = $tmp;
         }
         $t->set('flags', $out);
 
         $t->set('picker_img', Horde::img('colorpicker.png', _("Color Picker")));
-        $t->set('userflags_notlocked', !$userflags_locked);
 
         return $t->fetch(IMP_TEMPLATES . '/prefs/flags.html');
     }
@@ -986,10 +974,12 @@ class IMP_Prefs_Ui
      * Update IMAP flag related preferences.
      *
      * @param Horde_Core_Prefs_Ui $ui  The UI object.
+     *
+     * @return boolean  True if preferences were updated.
      */
     protected function _updateFlagManagement($ui)
     {
-        $imp_flags = $GLOBALS['injector']->getInstance('IMP_Imap_Flags');
+        $imp_flags = $GLOBALS['injector']->getInstance('IMP_Flags');
 
         if ($ui->vars->flag_action == 'add') {
             $GLOBALS['notification']->push(sprintf(_("Added flag \"%s\"."), $ui->vars->flag_data), 'horde.success');
@@ -997,38 +987,41 @@ class IMP_Prefs_Ui
             return;
         }
 
-        $def_color = $GLOBALS['prefs']->getValue('msgflags_color');
-
-        // Don't set updated on these actions. User may want to do more actions.
-        foreach ($imp_flags->getList() as $key => $val) {
-            $md5 = hash('md5', $key);
+        // Don't set updated on these actions. User may want to do more
+        // actions.
+        $update = false;
+        foreach ($imp_flags->getList() as $val) {
+            $md5 = hash('md5', $val->id);
 
             switch ($ui->vars->flag_action) {
             case 'delete':
-                if (($ui->vars->flag_data == ('bg_' . $md5)) &&
-                    $imp_flags->deleteFlag($key)) {
-                    $GLOBALS['notification']->push(sprintf(_("Deleted flag \"%s\"."), $val['l']), 'horde.success');
+                if ($ui->vars->flag_data == ('bg_' . $md5)) {
+                    unset($imp_flags[$val->id]);
+                    $GLOBALS['notification']->push(sprintf(_("Deleted flag \"%s\"."), $val->label), 'horde.success');
                 }
                 break;
 
             default:
                 /* Change labels for user-defined flags. */
-                if ($val['t'] == 'imapp') {
+                if ($val instanceof IMP_Flag_User) {
                     $label = $ui->vars->get('label_' . $md5);
-                    if (strlen($label) && ($label != $val['l'])) {
-                        $imp_flags->updateFlag($key, array('l' => $label));
+                    if (strlen($label) && ($label != $val->label)) {
+                        $imp_flags->updateFlag($val->id, 'label', $label);
+                        $update = true;
                     }
                 }
 
                 /* Change background for all flags. */
                 $bg = strtolower($ui->vars->get('bg_' . $md5));
-                if ((isset($val['b']) && ($bg != $val['b'])) ||
-                    (!isset($val['b']) && ($bg != $def_color))) {
-                    $imp_flags->updateFlag($key, array('b' => $bg));
+                if ($bg != $val->bgcolor) {
+                    $imp_flags->updateFlag($val->id, 'bgcolor', $bg);
+                    $update = true;
                 }
                 break;
             }
         }
+
+        return $update;
     }
 
     /* Initial page selection. */
index 3d102fa..8f5aeef 100644 (file)
@@ -52,7 +52,8 @@ class IMP_Search_Element_Flag extends IMP_Search_Element
      */
     public function queryText()
     {
-        return sprintf(_("flagged \"%s\""), $GLOBALS['injector']->getInstance('IMP_Imap_Flags')->getLabel($this->_data->f, $this->_data->s));
+        $imp_flags = $GLOBALS['injector']->getInstance('IMP_Flags');
+        return sprintf(_("flagged \"%s\""), $imp_flags[$this->_data->f]->getLabel($this->_data->s));
     }
 
 }
index 4d2ec80..7f59559 100644 (file)
@@ -157,38 +157,6 @@ class IMP_Ui_Mailbox
     }
 
     /**
-     * Return the icon to use for a given attachment.
-     *
-     * @return string  The mailbox display icon type (attach, encrypt,
-     *                 signed).
-     */
-    public function getAttachmentType($type)
-    {
-        list($primary, $sub) = explode('/', $type, 2);
-        if ($primary == 'multipart') {
-            switch ($sub) {
-            case 'signed':
-                return 'signed';
-
-            case 'encrypted':
-                return 'encrypt';
-
-            case 'alternative':
-            case 'related':
-                /* Treat this as no attachments. */
-                break;
-
-            default:
-                return 'attach';
-            }
-        } elseif ($type == 'application/pkcs7-mime') {
-             return 'encrypt';
-        }
-
-        return '';
-    }
-
-    /**
      * Formats the date header.
      *
      * @param integer $date    The UNIX timestamp.
index 2ade2e7..71db094 100644 (file)
@@ -148,7 +148,15 @@ class IMP_Views_ListMessages
             }
 
             /* Generate flag array. */
-            $md->flags = array_keys($GLOBALS['injector']->getInstance('IMP_Imap_Flags')->getList(array('imap' => true, 'mailbox' => $is_search ? null : $mbox)));
+            $flaglist = $GLOBALS['injector']->getInstance('IMP_Flags')->getList(array(
+                'imap' => true,
+                'mailbox' => $is_search ? null : $mbox
+            ));
+
+            $md->flags = array();
+            foreach ($flaglist as $val) {
+                $md->flags[] = $val->imapflag;
+            }
         }
 
         /* The search query may have changed. */
@@ -423,7 +431,7 @@ class IMP_Views_ListMessages
                 }
             }
 
-            $flag_parse = $GLOBALS['injector']->getInstance('IMP_Imap_Flags')->parse(array(
+            $flag_parse = $GLOBALS['injector']->getInstance('IMP_Flags')->parse(array(
                 'flags' => $ob['flags'],
                 'headers' => $ob['headers'],
                 'personal' => Horde_Mime_Address::getAddressesFromObject($ob['envelope']['to'], array('charset' => $charset))
@@ -432,7 +440,7 @@ class IMP_Views_ListMessages
             if (!empty($flag_parse)) {
                 $msg['flag'] = array();
                 foreach ($flag_parse as $val) {
-                    $msg['flag'][] = $val['flag'];
+                    $msg['flag'][] = $val->id;
                 }
             }
 
index bfe945e..3a4d31b 100644 (file)
@@ -108,27 +108,21 @@ class IMP_Views_ShowMessage
 
         /* Get envelope/header information. We don't use flags in this
          * view. */
+        $imp_contents = null;
         try {
             $fetch_ret = $GLOBALS['injector']->getInstance('IMP_Injector_Factory_Imap')->create()->fetch($mailbox, array(
                 Horde_Imap_Client::FETCH_ENVELOPE => true,
                 Horde_Imap_Client::FETCH_HEADERTEXT => array(array('parse' => true, 'peek' => false))
             ), array('ids' => array($uid)));
-        } catch (Horde_Imap_Client_Exception $e) {
-            $result['error'] = $error_msg;
-            $result['errortype'] = 'horde.error';
-            return $result;
-        }
 
-        if (!isset($fetch_ret[$uid]['headertext'])) {
-            $result['error'] = $error_msg;
-            $result['errortype'] = 'horde.error';
-            return $result;
-        }
+            if (isset($fetch_ret[$uid]['headertext'])) {
+                /* Parse MIME info and create the body of the message. */
+                $imp_contents = $GLOBALS['injector']->getInstance('IMP_Injector_Factory_Contents')->create(new IMP_Indices($mailbox, $uid));
+            }
+        } catch (Horde_Imap_Client_Exception $e) {
+        } catch (IMP_Exception $e) {}
 
-        /* Parse MIME info and create the body of the message. */
-        try {
-            $imp_contents = $GLOBALS['injector']->getInstance('IMP_Injector_Factory_Contents')->create(new IMP_Indices($mailbox, $uid));
-        } catch (IMP_Exception $e) {
+        if (is_null($imp_contents)) {
             $result['error'] = $error_msg;
             $result['errortype'] = 'horde.error';
             return $result;
index d785ccd..24fb4fe 100644 (file)
@@ -176,17 +176,17 @@ while (list(,$ob) = each($mbox_info['overview'])) {
     $msg['from'] = Horde_String::truncate($getfrom['from'], 50);
 
     /* Get flag information. */
-    $flag_parse = $injector->getInstance('IMP_Imap_Flags')->parse(array(
+    $flag_parse = $injector->getInstance('IMP_Flags')->parse(array(
         'flags' => $ob['flags'],
-        'personal' => Horde_Mime_Address::getAddressesFromObject($ob['envelope']['to'], array('charset' => 'UTF-8')),
-        'priority' => $ob['headers']
+        'headers' => $ob['headers'],
+        'personal' => Horde_Mime_Address::getAddressesFromObject($ob['envelope']['to'], array('charset' => 'UTF-8'))
     ));
 
     foreach ($flag_parse as $val) {
-        if (isset($val['abbrev'])) {
-            $msg['status'] .= $val['abbrev'];
-        } elseif ($val['type'] == 'imapp') {
-            $msg['subject'] = '*' . Horde_String::truncate($val['label'], 8) . '* ' . $msg['subject'];
+        if ($abbrev = $val->abbreviation) {
+            $msg['status'] .= $abbrev;
+        } elseif ($val instanceof IMP_Flag_User) {
+            $msg['subject'] = '*' . Horde_String::truncate($val->label, 8) . '* ' . $msg['subject'];
         }
     }
 
index 883e91d..8064450 100644 (file)
@@ -64,7 +64,7 @@ if (!Horde_Util::nonInputVar('from_message_page')) {
 }
 
 $do_filter = false;
-$imp_flags = $injector->getInstance('IMP_Imap_Flags');
+$imp_flags = $injector->getInstance('IMP_Flags');
 $imp_imap = $injector->getInstance('IMP_Injector_Factory_Imap')->create();
 $indices = new IMP_Indices($vars->indices);
 
@@ -485,9 +485,25 @@ if ($pageOb['msgcount']) {
     $n_template->set('use_pop', $session->get('imp', 'protocol') == 'pop');
 
     if (!$n_template->get('use_pop')) {
-        $tmp = $imp_flags->getFlagList($search_mbox ? null : IMP::$mailbox);
-        $n_template->set('flaglist_set', $tmp['set']);
-        $n_template->set('flaglist_unset', $tmp['unset']);
+        $args = array(
+            'imap' => true,
+            'mailbox' => $search_mbox ? null : IMP::$mailbox
+        );
+
+        $form_set = $form_unset = array();
+        foreach ($imp_flags->getList($args) as $val) {
+            $form_set[] = array(
+                'f' => $val->form_set,
+                'l' => $val->label
+            );
+            $form_unset[] = array(
+                'f' => $val->form_unset,
+                'l' => $val->label
+            );
+        }
+
+        $n_template->set('flaglist_set', $form_set);
+        $n_template->set('flaglist_unset', $form_unset);
 
         if (!$search_mbox) {
             $filters = array();
@@ -755,7 +771,6 @@ while (list(,$ob) = each($mbox_info['overview'])) {
     } catch (Horde_Exception_HookNotSet $e) {}
 
     $flag_parse = $imp_flags->parse(array(
-        'div' => true,
         'flags' => $ob['flags'],
         'headers' => $ob['headers'],
         'personal' => Horde_Mime_Address::getAddressesFromObject($ob['envelope']['to'], array('charset' => 'UTF-8'))
@@ -763,16 +778,14 @@ while (list(,$ob) = each($mbox_info['overview'])) {
 
     $subject_flags = array();
     foreach ($flag_parse as $val) {
-        if ($val['type'] == 'imapp') {
+        if ($val instanceof IMP_Flag_User) {
             $subject_flags[] = $val;
         } else {
-            if (isset($val['div'])) {
-                $msg['status'] .= $val['div'];
-            }
-            if (isset($val['classname'])) {
-                $msg['class'] = $val['classname'];
+            if (!$val->bgdefault) {
+                $msg['bg'] = $val->bgcolor;
             }
-            $msg['bg'] = $val['bg'];
+            $msg['class'] = $val->css;
+            $msg['status'] .= $val->div;
         }
     }
 
@@ -830,9 +843,9 @@ while (list(,$ob) = each($mbox_info['overview'])) {
 
     /* Add subject flags. */
     foreach ($subject_flags as $val) {
-        $flag_label = Horde_String::truncate($val['label'], 12);
+        $flag_label = Horde_String::truncate($val->label, 12);
 
-        $msg['subject'] = '<span class="' . $val['classname'] . '" style="background:' . htmlspecialchars($val['bg']) . ';color:' . htmlspecialchars($val['fg']) . '" title="' . htmlspecialchars($val['label']) . '">' . htmlspecialchars($flag_label) . '</span>' . $msg['subject'];
+        $msg['subject'] = '<span class="' . $val->css . '" style="' . ($val->bgdefault ? '' : 'background:' . htmlspecialchars($val->bgcolor) . ';') . 'color:' . htmlspecialchars($val->fgcolor) . '" title="' . htmlspecialchars($val->label) . '">' . htmlspecialchars($flag_label) . '</span>' . $msg['subject'];
     }
 
     /* Set up threading tree now. */
index aa27582..2c3bf84 100644 (file)
@@ -29,7 +29,7 @@ switch ($vars->actionID) {
 case 'strip_attachment':
     try {
         $indices = $injector->getInstance('IMP_Message')->stripPart(new IMP_Indices($vars->folder, $vars->uid), $vars->id);
-        $js_vars['-DimpFullmessage.strip'] = 1;
+        $js_vars['-DimpMessage.strip'] = 1;
         list(,$vars->uid) = $indices->getSingle();
         $notification->push(_("Attachment successfully stripped."), 'horde.success');
     } catch (IMP_Exception $e) {
@@ -58,15 +58,22 @@ if (isset($show_msg_result['error'])) {
 $scripts = array(
     array('contextsensitive.js', 'horde'),
     array('textarearesize.js', 'horde'),
-    array('fullmessage-dimp.js', 'imp'),
+    array('message-dimp.js', 'imp'),
     array('imp.js', 'imp'),
 );
 
 foreach (array('from', 'to', 'cc', 'bcc', 'replyTo', 'log', 'uid', 'mailbox') as $val) {
     if (!empty($show_msg_result[$val])) {
-        $js_vars['DimpFullmessage.' . $val] = $show_msg_result[$val];
+        $js_vars['DimpMessage.' . $val] = $show_msg_result[$val];
     }
 }
+
+$js_vars['DimpMessage.flag'] = IMP_Ajax_Application::flagEntry(array('\\seen'), true, $vars->folder, $vars->uid);
+
+if ($poll = IMP_Ajax_Application::pollEntry($vars->folder)) {
+    $js_vars['DimpMessage.poll'] = $poll;
+}
+
 $js_out = Horde::addInlineJsVars($js_vars, array('ret_vars' => true));
 
 /* Determine if compose mode is disabled. */
index 42abcf8..bc76cf6 100644 (file)
@@ -217,20 +217,16 @@ if (!empty($msgAddresses)) {
     }
 }
 
-$flag_parse = $injector->getInstance('IMP_Imap_Flags')->parse(array(
+$flag_parse = $injector->getInstance('IMP_Flags')->parse(array(
     'flags' => $flags,
     'personal' => $match_identity
 ));
 
 foreach ($flag_parse as $val) {
-    if (isset($val['abbrev'])) {
-        $status .= $val['abbrev'];
-    } elseif ($val['type'] == 'imapp') {
-        if (Horde_String::length($val['label']) > 8) {
-            $status .= ' *' . Horde_String::substr($val['label'], 0, 5) . '...*';
-        } else {
-            $status .= ' *' . $val['label'] . '*';
-        }
+    if ($abbrev = $val->abbreviation) {
+        $status .= $abbrev;
+    } elseif ($val instanceof IMP_Flag_User) {
+        $status .= ' *' . Horde_String::truncate($val->label, 8) . '*';
     }
 }
 
index dbc7bc4..4c0fcfc 100644 (file)
@@ -69,7 +69,7 @@ $mailbox_name = $index_array['mailbox'];
 $uid = $index_array['uid'];
 $indices = new IMP_Indices($mailbox_name, $uid);
 
-$imp_flags = $injector->getInstance('IMP_Imap_Flags');
+$imp_flags = $injector->getInstance('IMP_Flags');
 $imp_hdr_ui = new IMP_Ui_Headers();
 $imp_ui = new IMP_Ui_Message();
 
@@ -409,17 +409,16 @@ if (is_null($identity)) {
 }
 
 $flag_parse = $imp_flags->parse(array(
-    'div' => true,
     'flags' => $flags,
     'personal' => $match_identity
 ));
 
 $status = '';
 foreach ($flag_parse as $val) {
-    if ($val['type'] == 'imapp') {
-        $status .= '<span class="' . $val['classname'] . '" style="background:' . htmlspecialchars($val['bg']) . ';color:' . htmlspecialchars($val['fg']) . '">' . htmlspecialchars($val['label']) . '</span>';
+    if ($val instanceof IMP_Flag_User) {
+        $status .= '<span class="' . $val->css . '" style="' . ($val->bgdefault ? '' : 'background:' . htmlspecialchars($val->bgcolor) . ';') . 'color:' . htmlspecialchars($val->fgcolor) . '">' . htmlspecialchars($val->label) . '</span>';
     } else {
-        $status .= $val['div'];
+        $status .= $val->div;
     }
 }
 
@@ -454,9 +453,25 @@ $n_template->set('id', 1);
 if (!$use_pop) {
     $n_template->set('mailbox', IMP::formMbox(IMP::$mailbox, true));
 
-    $tmp = $imp_flags->getFlagList(IMP::$mailbox);
-    $n_template->set('flaglist_set', $tmp['set']);
-    $n_template->set('flaglist_unset', $tmp['unset']);
+    $tmp = $imp_flags->getList(array(
+        'imap' => true,
+        'mailbox' => IMP::$mailbox
+    ));
+
+    $form_set = $form_unset = array();
+    foreach ($tmp as $val) {
+        $form_set[] = array(
+            'f' => $val->form_set,
+            'l' => $val->label
+        );
+        $form_unset[] = array(
+            'f' => $val->form_unset,
+            'l' => $val->label
+        );
+    }
+
+    $n_template->set('flaglist_set', $form_set);
+    $n_template->set('flaglist_unset', $form_unset);
 
     if ($conf['user']['allow_folders']) {
         $n_template->set('move', Horde::widget('#', _("Move to folder"), 'widget moveAction', '', '', _("Move"), true));
index 9896fd7..5e0796e 100644 (file)
@@ -75,7 +75,7 @@ multiple folders, and multiple-language support.</description>
     <file name="flagprefs.js" role="horde" />
     <file name="folderprefs.js" role="horde" />
     <file name="folders.js" role="horde" />
-    <file name="fullmessage-dimp.js" role="horde" />
+    <file name="message-dimp.js" role="horde" />
     <file name="imp.js" role="horde" />
     <file name="login.js" role="horde" />
     <file name="mailbox-dimp.js" role="horde" />
@@ -1141,7 +1141,7 @@ multiple folders, and multiple-language support.</description>
    <install as="imp/js/flagprefs.js" name="js/flagprefs.js" />
    <install as="imp/js/folderprefs.js" name="js/folderprefs.js" />
    <install as="imp/js/folders.js" name="js/folders.js" />
-   <install as="imp/js/fullmessage-dimp.js" name="js/fullmessage-dimp.js" />
+   <install as="imp/js/message-dimp.js" name="js/message-dimp.js" />
    <install as="imp/js/imp.js" name="js/imp.js" />
    <install as="imp/js/login.js" name="js/login.js" />
    <install as="imp/js/mailbox-dimp.js" name="js/mailbox-dimp.js" />
index 58ad0d4..a3b09db 100644 (file)
@@ -68,7 +68,7 @@ if ($vars->search_basic_mbox) {
     }
 
     if ($vars->search_criteria_flag) {
-        $formdata = $injector->getInstance('IMP_Imap_Flags')->parseFormId($vars->search_criteria_flag);
+        $formdata = $injector->getInstance('IMP_Flags')->parseFormId($vars->search_criteria_flag);
         $c_list[] = new IMP_Search_Element_Flag(
             $formdata['flag'],
             ($formdata['set'] && !$vars->search_criteria_flag_not)
@@ -86,12 +86,15 @@ if ($vars->search_basic_mbox) {
     Horde::url('mailbox.php', true)->add('mailbox', strval($q_ob))->redirect();
 }
 
-$flist = $injector->getInstance('IMP_Imap_Flags')->getFlagList($vars->search_mailbox);
+$flist = $injector->getInstance('IMP_Flags')->getList(array(
+    'imap' => true,
+    'mailbox' => $vars->search_mailbox
+));
 $flag_set = array();
-foreach ($flist['set'] as $val) {
+foreach ($flist as $val) {
     $flag_set[] = array(
-        'val' => $val['f'],
-        'label' => $val['l']
+        'val' => $val->form_set,
+        'label' => $val->label
     );
 }
 
index 5d6c501..a492df9 100644 (file)
@@ -148,7 +148,7 @@ if (!$browser->hasFeature('javascript') ||
     exit;
 }
 
-$imp_flags = $injector->getInstance('IMP_Imap_Flags');
+$imp_flags = $injector->getInstance('IMP_Flags');
 $imp_search = $injector->getInstance('IMP_Search');
 $vars = Horde_Variables::getDefaultVariables();
 
@@ -168,7 +168,10 @@ if (isset($vars->search_mailbox)) {
     $search_mailbox = array('INBOX');
 }
 
-$flist = $imp_flags->getFlagList($default_mailbox);
+$flist = $imp_flags->getList(array(
+    'imap' => true,
+    'mailbox' => $default_mailbox
+));
 
 /* Generate the search query if 'criteria_form' is present in the form
  * data. */
@@ -433,12 +436,12 @@ $t->set('filterlist', $f_list);
 
 /* Create the flag list. */
 $flag_set = array();
-foreach ($flist['set'] as $val) {
+foreach ($flist as $val) {
     $flag_set[] = array(
-        'val' => rawurlencode($val['f']),
-        'label' => htmlspecialchars($val['l'])
+        'val' => rawurlencode($val->form_set),
+        'label' => htmlspecialchars($val->label)
     );
-    $types[rawurlencode($val['f'])] = 'flag';
+    $types[rawurlencode($val->form_set)] = 'flag';
 }
 $t->set('flist', $flag_set);
 
index d986613..1c6d409 100644 (file)
@@ -437,6 +437,9 @@ function _simpleButton($id, $text, $image, $nodisplay = false)
 <div class="context" id="ctx_flag" style="display:none">
 </div>
 
+<div class="context" id="ctx_flag_search" style="display:none">
+</div>
+
 <div class="context" id="ctx_contacts" style="display:none">
  <a id="ctx_contacts_new"><span class="iconImg"></span><?php echo _("New Message") ?></a>
  <a id="ctx_contacts_add"><span class="iconImg"></span><?php echo _("Add to Address Book") ?></a>
index 17e90af..d5cd6f1 100644 (file)
@@ -35,17 +35,19 @@ foreach (iterator_to_array($imp_search) as $key => $val) {
 }
 
 /* Generate flag array. */
-foreach ($GLOBALS['injector']->getInstance('IMP_Imap_Flags')->getList(array('fgcolor' => true)) as $val) {
-    $flags[$val['flag']] = array_filter(array(
-        'b' => isset($val['b']) ? $val['b'] : null,
-        'c' => $val['c'],
-        'f' => $val['f'],
-        'l' => $val['l'],
-        'n' => isset($val['n']) ? $val['n'] : null,
-        // Indicate if this is a user *P*ref flag
-        'p' => intval($val['t'] == 'imapp'),
-        // Indicate if this is a flag that can be *S*earched for
-        's' => intval(in_array($val['t'], array('imapp', 'imapu')))
+foreach ($GLOBALS['injector']->getInstance('IMP_Flags')->getList() as $val) {
+    $flags[$val->id] = array_filter(array(
+        // Indicate a flag that can be *a*ltered
+        'a' => $val->canset,
+        'b' => $val->bgdefault ? null : $val->bgcolor,
+        'c' => $val->css,
+        'f' => $val->fgcolor,
+        'i' => $val->css ? null : $val->cssicon,
+        'l' => $val->label,
+        // Indicate a flag that can be *s*earched for
+        's' => intval($val instanceof IMP_Flag_Imap),
+        // Indicate a *u*ser flag
+        'u' => intval($val instanceof IMP_Flag_User)
     ));
 }
 
index 8b95a04..1106eb1 100644 (file)
 $code = $flags = array();
 
 /* Generate flag array. */
-foreach ($GLOBALS['injector']->getInstance('IMP_Imap_Flags')->getList(array('fgcolor' => true)) as $val) {
-    $flags[$val['flag']] = array_filter(array(
-        'b' => isset($val['b']) ? $val['b'] : null,
-        'c' => $val['c'],
-        'f' => $val['f'],
-        'l' => $val['l'],
-        'n' => isset($val['n']) ? $val['n'] : null,
-        // Indicate if this is a user *P*ref flag
-        'p' => intval($val['t'] == 'imapp'),
-        // Indicate if this is a flag that can be *S*earched for
-        's' => intval(in_array($val['t'], array('imapp', 'imapu')))
+foreach ($GLOBALS['injector']->getInstance('IMP_Flags')->getList() as $val) {
+    $flags[$val->id] = array_filter(array(
+        'b' => $val->bgdefault ? null : $val->bgcolor,
+        'c' => $val->css,
+        'f' => $val->fgcolor,
+        'i' => $val->css ? null : $val->cssicon,
+        'l' => $val->label,
+        // Indicate if this is a flag that can be *s*earched for
+        's' => intval($val instanceof IMP_Flag_Imap),
+        // Indicate if this is a *u*ser flag
+        'u' => intval($val instanceof IMP_Flag_User)
     ));
 }
 
index 6130d69..41e26df 100644 (file)
 <loop:flags>
   <tr>
    <td>
-<if:flags.imapp>
-<if:flags.locked>
+<if:flags.user>
+<if:locked>
     <tag:flags.label />
-<else:flags.locked>
+<else:locked>
     <input name="<tag:flags.label_name />" value="<tag:flags.label />" />
-</else:flags.locked></if:flags.locked>
-<else:flags.imapp>
+</else:locked></if:locked>
+<else:flags.user>
     <tag:flags.label />
-</else:flags.imapp></if:flags.imapp>
+</else:flags.user></if:flags.user>
    </td>
    <td class="flagicon">
-<if:flags.imapp><else:flags.imapp>
+<if:flags.user><else:flags.user>
     <tag:flags.icon />
-</else:flags.imapp></if:flags.imapp>
+</else:flags.user></if:flags.user>
    </td>
    <td>
-<if:flags.locked><else:flags.locked>
+<if:locked><else:locked>
     <input type="hidden" id="<tag:flags.colorid />" name="<tag:flags.colorid />" value="<tag:flags.color />" />
     <div class="iconImg msgflags flagUser" style="<tag:flags.colorstyle />"></div>
     <a class="flagcolorpicker" href="#"><tag:picker_img /></a>
-<if:flags.flag_del>
+<if:flags.user>
     <a class="flagdelete" href="#"><span class="iconImg deleteImg"></span></a>
-</if:flags.flag_del>
-</else:flags.locked></if:flags.locked>
+</if:flags.user>
+</else:locked></if:locked>
    </td>
   </tr>
 </loop:flags>
  </tbody>
 </table>
 
-<if:userflags_notlocked>
+<if:locked><else:locked>
 <div>
  <input id="new_button" type="button" class="button" value="<gettext>New Flag</gettext>" />
 </div>
-</if:userflags_notlocked>
+</else:locked></if:locked>
index 07eb2f6..9c1bd9a 100644 (file)
@@ -1012,7 +1012,7 @@ span.dimpactionResume {
 #ctx_folderopts_reload span.iconImg {
     background-image: url("../graphics/reload.png");
 }
-#ctx_flag span.iconImg.flagUser {
+#ctx_flag span.iconImg.flagUser, #ctx_flag_search span.iconImg.flagUser {
     border: 1px black solid;
     height: 15px;
     width: 15px;
index 3e8da7f..6c0d760 100644 (file)
@@ -389,6 +389,9 @@ div.msgflags.flagAttachmsg {
 tr.flagUnseen {
     font-weight: bold;
 }
+span.iconImg.flagSeen {
+    background-image: url("graphics/mail_seen.png");
+}
 div.msgflags.flagUnseen, span.iconImg.flagUnseen {
     background-image: url("graphics/mail_unseen.png");
 }
index 6e148b1..40d0e9a 100644 (file)
@@ -41,6 +41,9 @@ div.msgflags.flagEncryptmsg {
 div.msgflags.flagAttachmsg {
     background-image: url("graphics/attachment.png");
 }
+span.iconImg.flagSeen {
+    background-image: url("graphics/mail_seen.png");
+}
 div.msgflags.flagUnseen, span.iconImg.flagUnseen {
     background-image: url("graphics/mail_unseen.png");
 }