New message flagging code.
authorMichael M Slusarz <slusarz@curecanti.org>
Tue, 31 Mar 2009 04:41:36 +0000 (22:41 -0600)
committerMichael M Slusarz <slusarz@curecanti.org>
Thu, 2 Apr 2009 06:11:25 +0000 (00:11 -0600)
Framework is in place to allow user-defined
flags/keywords/labels/whatever you want to call them. All code is
present except for actual viewing of the user-defined flags. Still
working out the UI - I think it is going to end up looking like the
message labeling as seen in Gmail - trying to allow custom icons is way
beyond the scope of this feature. But the flags are correctly set on
the messages.

This implements (most) of Ticket #937.

44 files changed:
imp/config/conf.xml
imp/config/hooks.php.dist
imp/config/prefs.php.dist
imp/docs/CHANGES
imp/docs/RFCS
imp/docs/UPGRADING
imp/js/src/ContextSensitive.js
imp/js/src/DimpBase.js
imp/js/src/DimpCore.js
imp/js/src/ViewPort.js
imp/js/src/compose-dimp.js
imp/js/src/flagmanagement.js [new file with mode: 0644]
imp/js/src/fullmessage-dimp.js
imp/js/src/mailbox.js
imp/js/src/message.js
imp/lib/DIMP.php
imp/lib/IMP.php
imp/lib/Imap/Flags.php [new file with mode: 0644]
imp/lib/Mailbox.php
imp/lib/UI/Mailbox.php
imp/lib/Views/ListMessages.php
imp/lib/Views/ShowMessage.php
imp/lib/prefs.php
imp/mailbox-mimp.php
imp/mailbox.php
imp/message-mimp.php
imp/message.php
imp/rss.php
imp/templates/index/index-dimp.inc
imp/templates/javascript/mailbox-dimp.js
imp/templates/javascript_defs.php
imp/templates/mailbox/mailbox.html
imp/templates/mailbox/navbar.html
imp/templates/message/navbar_navigate.html
imp/templates/message/navbar_top.html
imp/templates/prefs/flagmanagement.inc [new file with mode: 0644]
imp/themes/graphics/mail_notanswered.png [deleted file]
imp/themes/graphics/mail_notdraft.png [deleted file]
imp/themes/screen-dimp.css
imp/themes/screen.css
imp/themes/silver/graphics/mail_notanswered.png [deleted file]
imp/themes/silver/graphics/mail_notdraft.png [deleted file]
imp/themes/silver/screen-dimp.css
imp/themes/silver/screen.css

index f6d2c49..95cb2e0 100644 (file)
    a custom function to dynamically determine the icons shown for standard
    folders on the folder page? If so, make sure you define
    _imp_hook_mbox_icons() in hooks.php.">false</configboolean>
+   <configboolean name="mailboxarray" required="false" desc="Should we run a
+   custom function in which array elements can be added to use in the template
+   file, while processing the message lists? If so, make sure you define
+   _imp_hook_mailboxarray() in config/hooks.php.">false</configboolean>
    <configboolean name="mbox_readonly" required="false" desc="Should we use a
    custom function to dynamically determine whether a given folder is
    read-only? If so, make sure you define _imp_hook_mbox_readonly() in
    custom function to dynamically generate the email address to send spam
    reporting emails to? If so, make sure you define _imp_hook_spam_email() in
    hooks.php.">false</configboolean>
-   <configboolean name="msglist_format" required="false" desc="Should we use
-   a custom function to provide additional information/custom formatting of
-   messages in the mailbox message list? If so, make sure you define
-   _imp_hook_msglist_format() in hooks.php.">false</configboolean>
+   <configboolean name="msglist_flags" required="false" desc="Should we use
+   a custom function to provide additional flags to messages in the mailbox
+   message list? If so, make sure you define _imp_hook_msglist_flags() in
+   hooks.php.">false</configboolean>
    <configboolean name="display_folder" required="false" desc="Should we use
    a custom function to dynamically determine if we show a specified IMAP
    mailbox in the folderlist ? If so, make sure you define
   <configheader>Custom Hooks</configheader>
   <configsection name="dimp">
    <configsection name="hooks">
-    <configboolean name="mailboxarray" required="false" desc="Should we
-    run a custom function in which array elements can be added to use in the
-    template file, while processing the message lists? If so, make sure
-    you define _imp_hook_dimp_mailboxarray() in config/hooks.php. The elements
-    will be available as template tags in the imp/templates/imp/mailbox.html
-    template file.">false</configboolean>
     <configboolean name="previewview" required="false" desc="Should we
     run a custom function in which array elements can be modified, and
     javascript code be provided to dynamically update page elements in the
index 53f5a1a..48438de 100644 (file)
 //     }
 // }
 
-// This is an example hook function for displaying additional message
-// information in the message listing screen for a mailbox.  This example hook
-// will add a icon if the message contains attachments and will change the
-// display of the message entry based on the X-Priority header.
-//
-// INPUT:
-// $mbox - (string) The mailbox.
-// $uids - (array) A list of UIDs.
-// $mode - (string) Either 'imp' or 'dimp'.
-//
-// OUTPUT:
-// An array of arrays, with UIDs as keys and the following array values:
-//
-// For IMP:
-// 'class' - (array) CSS classnames that will be added to the row.
-// 'flagbits' - (integer) Flag mask which will be OR'd with the current flags
-//              set for the row.  The flag constants used in IMP can be
-//              found at the top of lib/IMP.php.
-// 'status' - (string) HTML code to add to the status column for the row.
-//
-// For DIMP:
-// 'atc' - (string) Attachment type (either 'signed', 'encrypted', or
-//         'attachment').
-// 'class' - (array) CSS classnames that will be added to the row.
-
-// if (!function_exists('_imp_hook_msglist_format')) {
-//     function _imp_hook_msglist_format($mbox, $uids, $mode)
+// This is an example hook function for adding additional message flags
+// in the message listing screen for a mailbox.  This example hook
+// will add a icon if the message was sent from a user within the same domain.
+//
+// @param array $data   The overview information for a message as returned
+//                      from the IMP_Mailbox::getMailboxArray() call (see
+//                      lib/Mailbox.php for documentation on the structure of
+//                      the array).
+// @param string $mode  Either 'imp' or 'dimp'.
+//
+// @return array  An array of additional flags to add. These flags must be
+//                defined in the 'msgflags' preference. On error, return an
+//                empty array.
+
+// if (!function_exists('_imp_hook_msglist_flags')) {
+//     function _imp_hook_msglist_flags($data, $mode)
 //     {
-//         try {
-//             $imap_res = $GLOBALS['imp_imap']->ob->fetch($mbox, array(
-//                 Horde_Imap_Client::FETCH_HEADERS => array(array('headers' => array('x-priority'), 'label' => 'hdr_search', 'parse' => true, 'peek' => true)),
-//                 Horde_Imap_Client::FETCH_STRUCTURE => array('parse' => true)
-//             ), array('ids' => array_values($uids)));
-//         } catch (Horde_Imap_Client_Exception $e) {
-//             return array();
+//         $flags = array();
+//
+//         $from_ob = Horde_Mime_Address::getAddressesFromObject($ob['envelope']['from']);
+//         if (!empty($from_ob) &&
+//             (strcasecmp($from_ob[0]['hostname'], 'example.com') === 0)) {
+//             /* The '$indomain' flag in this example must have already been
+//              * created in the 'msgflags' preference. */
+//             $flags = array('$indomain');
 //         }
 //
-//         $alt_list = IMP_UI_Mailbox::getAttachmentAltList();
-//         $imp_ui = new IMP_UI_Mailbox($mbox);
-//         $imp_msg_ui = new IMP_UI_Message();
-//         $ret = array();
-//
-//         foreach ($uids as $uid) {
-//             $tmp = array('status' => '');
-//             $res_ptr = &$imap_res[$uid];
-//
-//             // Add attachment information
-//             if (($attachment = $imp_ui->getAttachmentType($res_ptr['structure']->getType()))) {
-//                 switch ($mode) {
-//                 case 'imp':
-//                     $alt_text = (isset($alt_list[$attachment]))
-//                         ? $alt_list[$attachment]
-//                         : $alt_list['attachment'];
-//                     $tmp['status'] = Horde::img($attachment . '.png', $alt_text, array('title' => $alt_text));
-//                     break;
-//
-//                 case 'dimp':
-//                     $tmp['atc'] = $attachment;
-//                     break;
-//                 }
-//             }
-//
-//             // Add X-Priority information
-//             switch ($imp_msg_ui->getXpriority($res_ptr['headers']['hdr_search']->getValue('x-priority'))) {
-//             case 'high':
-//                 if ($mode == 'imp') {
-//                     $tmp['flagbits'] = IMP::FLAG_FLAGGED;
-//                     $tmp['status'] .= Horde::img('mail_priority_high.png', _("High Priority"), array('title' => _("High Priority")));
-//                 }
-//                 $tmp['class'][] = 'important';
-//                 break;
-//
-//             case 'low':
-//                 if ($mode == 'imp') {
-//                     $tmp['status'] .= Horde::img('mail_priority_low.png', _("Low Priority"), array('title' => _("Low Priority")));
-//                 }
-//                 $tmp['class'][] = 'unimportant';
-//                 break;
-//             }
-//
-//             if (!empty($tmp)) {
-//                 $ret[$uid] = $tmp;
-//             }
-//         }
-//
-//         return $ret;
+//         return $flags;
 //     }
-// }
 
 // This is an example hook function for the IMP redirection scheme. This
 // function is called when the user opens a mailbox in IMP, and allows the
 //     }
 // }
 
+// This is an example hook function for the mailbox view. This functions
+// allows additional information to be added/edited from the data that is
+// passed to the mailbox display template:
+//    imp: TODO
+//   dimp: imp/templates/javascript/mailbox-dimp.js.
+// The current entry array is passed in, the value returned should be the
+// altered array to use in the template. If you are going to add new columns,
+// you also have to update these fields:
+//    imp: TODO
+//   dimp: imp/templates/index/dimp.inc to contain the new field in the header
+//         imp/themes/screen-dimp.css to specify the column width.
+
+// if (!function_exists('_imp_hook_mailboxarray')) {
+//     function _imp_hook_mailboxarray($msgs, $view) {
+//         switch ($view) {
+//         case 'dimp':
+//             foreach (array_keys($msgs) as $key) {
+//                 $msgs[$key]['foo'] = true;
+//             }
+//             break;
+//
+//         case 'imp':
+//             // TODO
+//             break;
+//         }
+//
+//         return $msg;
+//     }
+// }
+
 // This is an example hook function to disable composing messages. If the hook
 // returns true, message composition will be disabled.
 
@@ -533,26 +504,6 @@ if (!function_exists('_imp_hook_quota')) {
     }
 }
 
-
-// This is an example hook function for the dynamic (dimp) mailbox view. This
-// function is allows additional information to be added to the array that is
-// is passed to the mailbox display template -
-// imp/templates/javascript/mailbox-dimp.js.  The current entry array is
-// passed in, the value returned should be the altered array to use in the
-// template. If you are going to add new columns, you also have to update
-// imp/templates/index/dimp.inc to contain the new field in the header and
-// imp/themes/screen-dimp.css to specify the column width.
-
-// if (!function_exists('_imp_hook_dimp_mailboxarray')) {
-//     function _imp_hook_dimp_mailboxarray($msgs) {
-//         foreach (array_keys($msgs) as $key) {
-//             $msgs[$key]['foo'] = true;
-//         }
-//
-//         return $msg;
-//     }
-// }
-
 // This is an example hook function for the dynamic (dimp) message view.  This
 // function allows additional information to be added to the array that is
 // passed to the message text display template -
index 3b6a7ba..6755301 100644 (file)
@@ -133,6 +133,13 @@ if (!empty($GLOBALS['conf']['mailbox']['show_preview'])) {
 }
 
 if (!$is_pop3) {
+    $prefGroups['flags'] = array(
+        'column' => _("Message Options"),
+        'label' => _("Message Flags"),
+        'desc' => _("Customize flag highlighting."),
+        'members' => array('flagmanagement')
+    );
+
     $prefGroups['fetchmail'] = array(
         'column' => _("Message Options"),
         'label' => _("Fetch Mail"),
@@ -146,7 +153,7 @@ $prefGroups['display'] = array(
     'label' => _("Mailbox and Folder Display Options"),
     'desc' => _("Change display options such as how many messages you see on each page and how messages are sorted."),
     'members' => array('mailbox_start', 'sortby', 'sortdir', 'max_msgs',
-                       'from_link', 'time_format')
+                       'from_link', 'time_format', 'atc_flag')
 );
 if (!$is_pop3) {
     $prefGroups['display']['members'] = array_merge(
@@ -527,6 +534,7 @@ $_prefs['stationery_link'] = array(
     'desc' => _("To the stationery and form responses."));
 
 $_prefs['stationery'] = array(
+    // value = serialize(array())
     'value' => 'a:0:{}',
     'locked' => false,
     'shared' => false,
@@ -1068,6 +1076,154 @@ $_prefs['preview_show_tooltip'] = array(
 // End Message Preview
 
 
+// IMAP Flag preferences
+
+// UI for flag management.
+$_prefs['flagmanagement'] = array(
+    'type' => 'special'
+);
+
+// Message flags
+$_prefs['msgflags'] = array(
+    // Format:
+    //   KEY: Flag name
+    //   VALUE: Array with the following entries
+    //          'a' - (string) [abbreviation] The abbreviation used in
+    //                the mimp (minimal) 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 flags (not user settable)
+    //                'imapp' - IMAP flags (personal flags - created by user
+                                through the prefs interface)
+    //                'imapu' - IMAP flags (user settable)
+    //                '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' => '#ffcccc',
+            'c' => 'flagHighpriority',
+            'l' => _("High Priority"),
+            't' => 'imp'
+        ),
+        'lowpri' => array(
+            'a' => 'v',
+            '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 exsits 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])
+        '\\seen' => array(
+            'a' => 'N',
+            'b' => '#eeeeff',
+            'c' => 'flagUnseen',
+            'l' => _("Unseen"),
+            'n' => true,
+            't' => 'imap'
+        ),
+        '\\answered' => array(
+            'a' => 'r',
+            'b' => '#ccffcc',
+            'c' => 'flagAnswered',
+            'l' => _("Answered"),
+            't' => 'imap'
+        ),
+        '\\draft' => array(
+            'a' => 'd',
+            'c' => 'flagDraft',
+            'l' => _("Draft"),
+            't' => 'imapu'
+        ),
+        '\\flagged' => array(
+            'a' => '*',
+            'b' => '#ffcccc',
+            'c' => 'flagFlagged',
+            'l' => _("Flagged for Followup"),
+            't' => 'imapu'
+        ),
+        '\\deleted' => array(
+            'a' => 'D',
+            'b' => '#999999',
+            'c' => 'flagDeleted',
+            'l' => _("Deleted"),
+            't' => 'imap'
+        ),
+
+        // Forwarded flag (RFC 4550 [2.8])
+        '$forwarded' => array(
+            'a' => 'F',
+            'b' => '#aadddd',
+            'c' => 'flagForwarded',
+            'l' => _("Forwarded"),
+            // Pursuant to RFC, this flag SHOULD NOT be changed by the user
+            't' => 'imap'
+        )
+    )),
+    'locked' => false,
+    'shared' => false,
+    'type' => 'implicit'
+);
+
+// The default color to use for flags that don't require row highlighting.
+$_prefs['msgflags_color'] = array(
+    'value' => '#ffffff',
+    'locked' => false,
+    'shared' => false,
+    'type' => 'implicit');
+
+// By default, don't allow user to alter flags set by system events.
+$_prefs['msgflags_hidesys'] = array(
+    'value' => true,
+    'locked' => false,
+    'shared' => false,
+    'type' => 'implicit');
+
+// End IMAP Flag preferences
+
+
 // Fetch Mail preferences
 
 // Change this if you want to customize how fetchmailprefs.php works.
@@ -1087,6 +1243,7 @@ $_prefs['fetchmail_menu'] = array(
 
 // Don't change anything here.
 $_prefs['fm_accounts'] = array(
+    // value = serialize(array())
     'value' => 'a:0:{}',
     'locked' => false,
     'shared' => false,
@@ -1143,6 +1300,7 @@ $_prefs['sortdir'] = array(
 
 // sort prefs for individual folders
 $_prefs['sortpref'] = array(
+    // value = serialize(array())
     'value' => 'a:0:{}',
     'locked' => false,
     'shared' => false,
@@ -1169,6 +1327,16 @@ $_prefs['from_link'] = array(
     ),
     'desc' => _("The From: column of the message should be linked:"));
 
+// Display attachment information in mailbox list.
+// Disabled by default, and not shown to user, because this display requires
+// substantial overhead to parse the message structures of all message in
+// the mailbox list at view time.
+$_prefs['atc_flag'] = array(
+    'value' => 0,
+    'locked' => true,
+    'type' => 'checkbox',
+    'desc' => _("Display attachment information about a message in the mailbox listing?"));
+
 // Time format for messages dated today
 $_prefs['time_format'] = array(
     'value' => '%X',
@@ -1227,6 +1395,7 @@ $_prefs['nav_poll_all'] = array(
 
 // list of folders to expand by default
 $_prefs['expanded_folders'] = array(
+    // value = serialize(array())
     'value' => 'a:0:{}',
     'locked' => false,
     'shared' => false,
index 759a861..8c5373d 100644 (file)
@@ -2,6 +2,10 @@
 v5.0-git
 --------
 
+[mms] Add support for defining and displaying custom IMAP flags and for
+      configuring the highlighting of system flags (Request #937).
+[mms] Move attachment dispaly on mailbox page from hook to preference.
+[mms] Always do X-Priority header processing by default.
 [mms] Support $MDNSent keyword (RFC 3503) on IMAP server.
 [mms] Link URLs/e-mails in subjects in message views (Request #7487).
 [mms] Implement spellcheck on send in DIMP.
index 57775b8..be0b6dd 100644 (file)
@@ -31,6 +31,7 @@ RFC 3691        UNSELECT
 RFC 4315        UIDPLUS
 RFC 4422        SASL Authentication (for DIGEST-MD5)
 RFC 4466        Collected extensions (updates RFCs 2088, 3501, 3502, 3516)
+RFC 4550        Lemonade Profile (specifically [2.8] - $Forwarded flag)
 RFC 4551        CONDSTORE
 RFC 4731        ESEARCH
 RFC 4959        SASL-IR
index 4a12ef1..7256c33 100644 (file)
@@ -25,7 +25,11 @@ supported.
 * imp_hook_vinfo has changed
 * alternative_display, attachment_display, forward_default pref is gone.
 * imp_hook_spam_bounce -> imp_hook_spam_email
-* imp_hook_msglist_format input has changed.
+* imp_hook_msglist_format hook has been removed - instead:
+*  x-priority now handled in the core code
+*  old attachment icon handling see atc_flag pref
+*  imp_hook_msglist_flags hook now used to dynamically set flags on messages
+
 
 
 Upgrading IMP From 4.1.x To 4.2.x
index c990453..6b7dc41 100644 (file)
 
 var ContextSensitive = Class.create({
 
+    queueSettings: {
+        limit: 5,
+        position: 'end',
+        scope: 'contextsensitive'
+    },
+
     initialize: function(opts)
     {
-        this.basectx = this.target = null;
+        this.basectx = this.submenu = null;
         this.elements = $H();
         this.submenus = $H();
         this.current = [];
@@ -66,9 +72,9 @@ var ContextSensitive = Class.create({
     /**
      * Hide the currently displayed element(s).
      */
-    close: function(immediate)
+    close: function()
     {
-        this._closeSubmenu(0, immediate);
+        this._closeSubmenu(0, true);
     },
 
     /**
@@ -78,14 +84,12 @@ var ContextSensitive = Class.create({
     {
         if (this.current.size()) {
             this.current.splice(idx, this.current.size() - idx).each(function(s) {
-                if (immediate) {
-                    $(s).hide();
-                } else {
-                    Effect.Fade(s, { duration: 0.2, queue: { position: 'end', scope: 'cm_' + s, limit: 2 } });
-                }
-            });
-            this.target = this.current[idx];
-            this.basectx = null;
+                Effect.Fade(s, {
+                    duration: 0.15,
+                    afterFinish: function() { this.basectx = this.submenu = null; }.bind(this),
+                    queue: immediate ? 'global' : this.queueSettings
+                });
+            }, this);
         }
     },
 
@@ -124,7 +128,7 @@ var ContextSensitive = Class.create({
      */
     _leftClickHandler: function(e)
     {
-        var curr, elt, elt_up;
+        var base, elt, elt_up, submenu;
 
         // Check for a right click. FF on Linux triggers an onclick event even
         // w/a right click, so disregard.
@@ -144,10 +148,11 @@ var ContextSensitive = Class.create({
                     elt_up.readAttribute('id') != this.currentmenu()) {
                     this._closeSubmenu(this.current.indexOf(elt.readAttribute('id')));
                 } else {
-                    curr = $(this.target);
-                    this.close(true);
+                    base = this.current.first();
+                    submenu = this.submenu;
+                    this.close();
                     if (this.opts.onClick) {
-                        this.opts.onClick(elt.readAttribute('id'), curr);
+                        this.opts.onClick(elt, base, submenu);
                     }
                 }
                 return;
@@ -202,7 +207,6 @@ var ContextSensitive = Class.create({
         // Register the current element that will be shown and the element
         // that was clicked on.
         this.close();
-        this.target = ctx.id;
 
         offset = ctx.opts.offset;
         if (!offset && (Object.isUndefined(x) || Object.isUndefined(y))) {
@@ -226,7 +230,7 @@ var ContextSensitive = Class.create({
     /**
      * Display the [sub]menu on the screen.
      */
-    _displayMenu: function(elt, x, y)
+    _displayMenu: function(elt, x, y, submenu)
     {
         // Get window/element dimensions
         var id = elt.readAttribute('id'),
@@ -245,9 +249,16 @@ var ContextSensitive = Class.create({
             this.opts.onShow(id, this.basectx);
         }
 
-        Effect.Appear(elt.setStyle({ left: x + 'px', top: y + 'px' }), { duration: 0.2, queue: { position: 'end', scope: 'cm_' + id, limit: 2 } });
-
-        this.current.push(id);
+        Effect.Appear(elt, {
+            from: 0.0,
+            afterSetup: function() {
+                elt.setStyle({ left: x + 'px', top: y + 'px' })
+                this.current.push(id);
+                this.submenu = submenu;
+            }.bind(this),
+            duration: 0.15,
+            queue: this.queueSettings
+        });
     },
 
     /**
@@ -288,7 +299,7 @@ var ContextSensitive = Class.create({
 
         if (elt.hasClassName('contextSubmenu')) {
             sub = this.submenus.get(id);
-            if (sub != cm) {
+            if (sub != cm || this.submenu != id) {
                 if (id_div != cm) {
                     this._closeSubmenu(this.current.indexOf(id_div) + 1);
                 }
@@ -297,7 +308,7 @@ var ContextSensitive = Class.create({
                 voffsets = document.viewport.getScrollOffsets();
                 x = offsets[0] + voffsets.left + elt.getWidth();
                 y = offsets[1] + voffsets.top;
-                this._displayMenu($(sub), x, y);
+                this._displayMenu($(sub), x, y, id);
             }
         } else if ((this.current.size() > 1) &&
                    elt_up.hasClassName('contextMenu') &&
index 158057c..41bc7ea 100644 (file)
@@ -31,17 +31,6 @@ var DimpBase = {
         sf_subject: 'subject'
     }),
 
-    flags: $H({
-        unseen: 'Unseen',
-        flagged: 'Flagged',
-        deletedmsg: 'Deleted',
-        unimportant: 'LowPriority',
-        important: 'HighPriority',
-        answered: 'Answered',
-        forwarded: 'Forwarded',
-        draft: 'Draft'
-    }),
-
     // Message selection functions
 
     // vs = (ViewPort_Selection) A ViewPort_Selection object.
@@ -342,7 +331,7 @@ var DimpBase = {
     // r = ViewPort row data
     msgWindow: function(r)
     {
-        this.updateUnseenUID(r, 0);
+        this.updateSeenUID(r, 1);
         var url = DIMP.conf.message_url;
         url += (url.include('?') ? '&' : '?') +
                $H({ folder: r.view,
@@ -418,10 +407,13 @@ var DimpBase = {
                     search = this.sfilters.get(this._getSearchfilterField()).capitalize();
                     mf = new RegExp("(" + $F('msgList_filter') + ")", "i");
                 }
+
                 rows.get('dataob').each(function(row) {
                     var elt, tmp, u,
                         r = $(row.domid);
 
+                    this.updateStatusFlags(row);
+
                     // Add thread graphics
                     if (thread && thread.get(row.imapuid)) {
                         elt = r.down('.msgSubject');
@@ -574,9 +566,6 @@ var DimpBase = {
                 }
                 return this.cacheids[id];
             }.bind(this),
-            onUpdateClass: function(row) {
-                this.updateStatusFlags(row);
-            }.bind(this),
             onSplitBarChange: function() {
                 this._updatePrefs('dimp_splitbar', this.viewport.getPageSize());
             }.bind(this),
@@ -619,27 +608,29 @@ var DimpBase = {
         DimpCore.DMenu.removeElement($(elt).identify());
     },
 
-    contextOnClick: function(parentfunc, id, elt)
+    contextOnClick: function(parentfunc, elt, base, submenu)
     {
+        var id = elt.readAttribute('id');
+
         switch (id) {
         case 'ctx_folder_create':
-            this.createSubFolder(elt);
+            this.createSubFolder(base);
             break;
 
         case 'ctx_container_rename':
         case 'ctx_folder_rename':
-            this.renameFolder(elt);
+            this.renameFolder(base);
             break;
 
         case 'ctx_folder_empty':
-            mbox = elt.readAttribute('mbox');
+            mbox = base.readAttribute('mbox');
             if (window.confirm(DIMP.text.empty_folder)) {
                 DimpCore.doAction('EmptyFolder', { view: mbox }, null, this._emptyFolderCallback.bind(this));
             }
             break;
 
         case 'ctx_folder_delete':
-            mbox = elt.readAttribute('mbox');
+            mbox = base.readAttribute('mbox');
             if (window.confirm(DIMP.text.delete_folder)) {
                 DimpCore.doAction('DeleteFolder', { view: mbox }, null, this.bcache.get('folderC') || this.bcache.set('folderC', this._folderCallback.bind(this)));
             }
@@ -647,25 +638,28 @@ var DimpBase = {
 
         case 'ctx_folder_seen':
         case 'ctx_folder_unseen':
-            this.flag(id == 'ctx_folder_seen' ? 'allSeen' : 'allUnseen', { mailbox: elt.readAttribute('mbox') });
+            this.flag(id == 'ctx_folder_seen' ? 'allSeen' : 'allUnseen', { mailbox: base.readAttribute('mbox') });
             break;
 
         case 'ctx_folder_poll':
         case 'ctx_folder_nopoll':
-            this.modifyPollFolder(elt.readAttribute('mbox'), id == 'ctx_folder_poll');
+            this.modifyPollFolder(base.readAttribute('mbox'), id == 'ctx_folder_poll');
             break;
 
         case 'ctx_container_create':
-            this.createSubFolder(elt);
+            this.createSubFolder(base);
             break;
 
         case 'ctx_message_spam':
         case 'ctx_message_ham':
         case 'ctx_message_blacklist':
         case 'ctx_message_whitelist':
+            this.flag(id.substring(12));
+            break;
+
         case 'ctx_message_deleted':
         case 'ctx_message_undeleted':
-            this.flag(id.substring(12));
+            this.flag('imapflag', { imap: '\\deleted', set: id == 'ctx_message_deleted' });
             break;
 
         case 'ctx_message_forward':
@@ -676,11 +670,9 @@ var DimpBase = {
             this.composeMailbox('resume');
             break;
 
-        case 'ctx_draft_flagged':
-        case 'ctx_draft_clear':
         case 'ctx_draft_deleted':
         case 'ctx_draft_undeleted':
-            this.flag(id.substring(10));
+            this.flag('imapflag', { imap: '\\deleted', set: id == 'ctx_draft_deleted' });
             break;
 
         case 'ctx_reply_reply':
@@ -693,23 +685,14 @@ var DimpBase = {
             this.togglePreviewPane();
             break;
 
-        case 'flag_seen':
-        case 'flag_unseen':
-        case 'flag_flagged':
-        case 'flag_clear':
-        case 'flag_answered':
-        case 'flag_unanswered':
-        case 'flag_draft':
-        case 'flag_notdraft':
-            this.flag(id.substring(5));
-            break;
-
         case 'oa_blacklist':
         case 'oa_whitelist':
-        case 'oa_undeleted':
             this.flag(id.substring(3));
             break;
 
+        case 'oa_undeleted':
+            this.flag('imapflag', { imap: '\\deleted', set: false });
+
         case 'oa_selectall':
             this.selectAll();
             break;
@@ -719,7 +702,17 @@ var DimpBase = {
             break;
 
         default:
-            parentfunc(id, elt);
+            if (submenu == 'ctx_message_setflag' ||
+                submenu == 'ctx_draft_setflag' ||
+                submenu == 'oa_setflag') {
+                this.flag('imapflag', { imap: elt.readAttribute('flag'), set: true });
+            } else if (submenu == 'ctx_message_unsetflag' ||
+                       submenu == 'ctx_draft_unsetflag' ||
+                       submenu == 'oa_unsetflag') {
+                this.flag('imapflag', { imap: elt.readAttribute('flag'), set: false });
+            } else {
+                parentfunc(elt, base, submenu);
+            }
             break;
         }
     },
@@ -734,7 +727,8 @@ var DimpBase = {
             folder = $(ctx.ctx);
             if (folder.readAttribute('mbox') == 'INBOX') {
                 elts.invoke('hide');
-            } else if (DIMP.conf.fixed_folders.indexOf(folder.readAttribute('mbox')) != -1) {
+            } else if (DIMP.conf.fixed_folders &&
+                       DIMP.conf.fixed_folders.indexOf(folder.readAttribute('mbox')) != -1) {
                 elts.shift();
                 elts.invoke('hide');
             } else {
@@ -899,8 +893,8 @@ var DimpBase = {
                   // 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.
-                if (data.bg.indexOf('unseen') != -1) {
-                    this.flag('seen');
+                if (this.isUnseen(data)) {
+                    this.flag('imapflag', { imap: '\\seen', set: true });
                 }
                 return this._loadPreviewCallback(this.ppcache[pp_uid]);
             }
@@ -923,7 +917,7 @@ var DimpBase = {
             search = this.viewport.getViewportSelection().search({ imapuid: { equal: [ r.index ] }, view: { equal: [ r.folder ] } });
             if (search.size()) {
                 row = search.get('dataob').first();
-                this.updateUnseenUID(row, 0);
+                this.updateSeenUID(row, 1);
             }
         }
 
@@ -958,7 +952,7 @@ var DimpBase = {
         switch (r.priority) {
         case 'high':
         case 'low':
-            tmp.invoke('insert', { top: new Element('SPAN').addClassName('status' + r.priority.capitalize() + 'Priority') });
+            tmp.invoke('insert', { top: new Element('DIV').addClassName('flag' + r.priority.capitalize() + 'priority') });
             break;
         }
 
@@ -1047,25 +1041,24 @@ var DimpBase = {
     },
 
     // Labeling functions
-    updateUnseenUID: function(r, setflag)
+    updateSeenUID: function(r, setflag)
     {
-        var sel, unseen, unseenset;
-        if (!r.bg) {
-            return false;
-        }
-        unseenset = r.bg.indexOf('unseen') != -1;
-        if ((setflag && unseenset) || (!setflag && !unseenset)) {
+        var isunseen = this.isUnseen(r),
+            sel, unseen;
+
+        if ((setflag && !isunseen) || (!setflag && isunseen)) {
             return false;
         }
 
         sel = this.viewport.createSelection('dataob', r);
         unseen = Number($(this.getFolderId(r.view)).readAttribute('u'));
+
         if (setflag) {
-            this.viewport.updateFlag(sel, 'unseen', true);
-            ++unseen;
-        } else {
-            this.viewport.updateFlag(sel, 'unseen', false);
+            this.updateFlag(sel, '\\seen', true);
             --unseen;
+        } else {
+            this.updateFlag(sel, '\\seen', false);
+            ++unseen;
         }
 
         this.updateUnseenStatus(r.view, unseen);
@@ -1333,7 +1326,7 @@ var DimpBase = {
                     DimpCore.doAction('CopyMessage', this.viewport.addRequestParams({ tofld: foldername }), uids, this.bcache.get('pollFC') || this.bcache.set('pollFC', this._pollFoldersCallback.bind(this)));
                 } else if (this.folder != foldername) {
                     // Don't allow drag/drop to the current folder.
-                    this.viewport.updateFlag(uids, 'deletedmsg', true);
+                    this.updateFlag(uids, '\\deleted', true);
                     DimpCore.doAction('MoveMessage', this.viewport.addRequestParams({ tofld: foldername }), uids, this.bcache.get('deleteC') || this.bcache.set('deleteC', this._deleteCallback.bind(this)));
                 }
             }
@@ -1396,7 +1389,7 @@ var DimpBase = {
             if (e.shiftKey) {
                 this.moveSelected((r.last().rownum == this.viewport.getMetaData('total_rows')) ? (r.first().rownum - 1) : (r.last().rownum + 1), true);
             }
-            this.flag('deleted', { index: r });
+            this.flag('imapflag', { imap: '\\deleted', index: r, set: true });
             e.stop();
             break;
 
@@ -1516,7 +1509,7 @@ var DimpBase = {
         }
 
         var elt = e.element(),
-            id, mbox, tmp;
+            id, tmp;
 
         while (Object.isElement(elt)) {
             id = elt.readAttribute('id');
@@ -1581,13 +1574,17 @@ var DimpBase = {
                 this.composeMailbox(id == 'button_reply' ? 'reply' : 'forward');
                 break;
 
-            case 'button_deleted':
             case 'button_ham':
             case 'button_spam':
                 this.flag(id.substring(7));
                 e.stop();
                 return;
 
+            case 'button_deleted':
+                this.flag('imapflag', { imap: '\\deleted', set: true });
+                e.stop();
+                return;
+
             case 'button_other':
                 DimpCore.DMenu.trigger(e.findElement('A').next(), true);
                 e.stop();
@@ -1816,7 +1813,7 @@ var DimpBase = {
                 this._expirePPCache(uids);
             } else {
                 // Need this to catch spam deletions.
-                this.viewport.updateFlag(search, 'deletedmsg', true);
+                this.updateFlag(search, '\\deleted', true);
             }
         }
     },
@@ -2059,12 +2056,11 @@ var DimpBase = {
     },
 
     /* Flag actions for message list. */
-    // opts = 'index', 'mailbox', 'noserver' (only for answered/unanswered)
+    // opts = 'imap' 'index', 'mailbox', 'noserver' (only for answered/unanswered), 'set'
     flag: function(action, opts)
     {
         var actionCall, args, vs,
-            obs = [],
-            unseenstatus = 1;
+            flags = [];
         opts = opts || {};
 
         if (opts.index) {
@@ -2085,12 +2081,10 @@ var DimpBase = {
         case 'allSeen':
             DimpCore.doAction((action == 'allUnseen') ? 'MarkFolderUnseen' : 'MarkFolderSeen', { view: opts.mailbox }, null, this.bcache.get('flagAC') || this.bcache.set('flagAC', this._flagAllCallback.bind(this)));
             if (opts.mailbox == this.folder) {
-                this.viewport.updateFlag(this.createSelection('rownum', $A($R(1, this.viewport.getMetaData('total_rows')))), 'unseen', action == 'allUnseen');
+                this.updateFlag(this.createSelection('rownum', $A($R(1, this.viewport.getMetaData('total_rows')))), '\\seen', action != 'allUnseen');
             }
             break;
 
-        case 'deleted':
-        case 'undeleted':
         case 'spam':
         case 'ham':
         case 'blacklist':
@@ -2099,146 +2093,136 @@ var DimpBase = {
                 break;
             }
 
-            // 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).
-            if (action == 'deleted') {
-                vs = vs.search({ isdel: { not: [ true ] } });
-                if (!vs.size()) {
-                    break;
-                }
-                vs.set({ isdel: true });
-            }
-
             args = this.viewport.addRequestParams({});
-            if (action == 'deleted' || action == 'undeleted') {
-                this.viewport.updateFlag(vs, 'deletedmsg', action == 'deleted');            }
 
-            if (action == 'undeleted') {
-                DimpCore.doAction('UndeleteMessage', args, vs);
-                vs.set({ isdel: false });
-            } else {
-                actionCall = { deleted: 'DeleteMessage', spam: 'ReportSpam', ham: 'ReportHam', blacklist: 'Blacklist', whitelist: 'Whitelist' };
-                // This needs to be synchronous Ajax if we are calling from a
-                // popup window because Mozilla will not correctly call the
-                // callback function if the calling window has been closed.
-                DimpCore.doAction(actionCall[action], args, vs, this.bcache.get('deleteC') || this.bcache.set('deleteC', this._deleteCallback.bind(this)), { asynchronous: !(opts.index && opts.mailbox) });
-
-                // If reporting spam, to indicate to the user that something is
-                // happening (since spam reporting may not be instantaneous).
-                if (action == 'spam' || action == 'ham') {
-                    this.msgListLoading(true);
-                }
-            }
-            break;
+            actionCall = {
+                spam: 'ReportSpam',
+                ham: 'ReportHam',
+                blacklist: 'Blacklist',
+                whitelist: 'Whitelist'
+            };
 
-        case 'unseen':
-        case 'seen':
-            if (!vs.size()) {
-                break;
-            }
-            args = { view: this.folder, flags: [ '-\\seen' ].toJSON() };
-            if (action == 'seen') {
-                unseenstatus = 0;
-                args.flags = [ '\\seen' ].toJSON();
-            }
-            obs = vs.get('dataob');
-            if (obs.size()) {
-                obs.each(function(s) {
-                    this.updateUnseenUID(s, unseenstatus);
-                }, this);
-                DimpCore.doAction('MarkMessage', args, this.viewport.createSelection('dataob', obs));
+            // This needs to be synchronous Ajax if we are calling from a
+            // popup window because Mozilla will not correctly call the
+            // callback function if the calling window has been closed.
+            DimpCore.doAction(actionCall[action], this.viewport.addRequestParams({}), vs, this.bcache.get('deleteC') || this.bcache.set('deleteC', this._deleteCallback.bind(this)), { asynchronous: !(opts.index && opts.mailbox) });
+
+            // If reporting spam, to indicate to the user that something is
+            // happening (since spam reporting may not be instantaneous).
+            if (action == 'spam' || action == 'ham') {
+                this.msgListLoading(true);
             }
             break;
 
-        case 'flagged':
-        case 'clear':
+        case 'imapflag':
             if (!vs.size()) {
                 break;
             }
-            args = {
-                view: this.folder,
-                flags: [ ((action == 'flagged') ? '' : '-') + '\\flagged' ].toJSON()
-            };
-            this.viewport.updateFlag(vs, 'flagged', action == 'flagged');
-            DimpCore.doAction('MarkMessage', args, vs);
-            break;
 
-        case 'answered':
-            if (!vs.size()) {
+            flags = [ (opts.set ? '' : '-') + opts.imap ];
+
+            switch (opts.imap) {
+            case '\\deleted':
+                // 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).
+                if (opts.set) {
+                    vs = vs.search({ isdel: { not: [ true ] } });
+                    if (!vs.size()) {
+                        return;
+                    }
+                    vs.set({ isdel: true });
+                } else {
+                    vs.set({ isdel: false });
+                }
+
+                this.updateFlag(vs, opts.imap, opts.set);
+                DimpCore.doAction(opts.set ? 'DeleteMessage' : 'UndeleteMessage', this.viewport.addRequestParams({}), vs, this.bcache.get('deleteC') || this.bcache.set('deleteC', this._deleteCallback.bind(this)), { asynchronous: !(opts.index && opts.mailbox) });
+                return;
+
+            case '\\seen':
+                vs.get('dataob').each(function(s) {
+                    this.updateSeenUID(s, opts.set);
+                }, this);
                 break;
-            }
-            this.viewport.updateFlag(vs, 'answered', true);
-            this.viewport.updateFlag(vs, 'flagged', false);
-            if (!opts.noserver) {
-                args = {
-                    view: this.folder,
-                    flags: [ '\\answered', '-\\flagged' ].toJSON()
-                };
-                DimpCore.doAction('MarkMessage', args, vs);
-            }
-            break;
 
-        case 'unanswered':
-            if (!vs.size()) {
+            case '\\answered':
+                if (opts.set) {
+                    this.updateFlag(vs, '\\flagged', false);
+                    flags.push('-\\flagged');
+                }
                 break;
             }
-            this.viewport.updateFlag(vs, 'answered', false);
+
+            this.updateFlag(vs, opts.imap, opts.set);
             if (!opts.noserver) {
-                args = {
-                    view: this.folder,
-                    flags: [ '-\\answered' ].toJSON()
-                };
-                DimpCore.doAction('MarkMessage', args, vs);
+                DimpCore.doAction('MarkMessage', { flags: flags.toJSON(), view: this.folder }, vs);
             }
-            break;
+        }
+    },
 
-        case 'draft':
-        case 'notdraft':
-            if (!vs.size()) {
-                break;
-            }
-            args = {
-                view: this.folder,
-                flags: [ ((action == 'draft') ? '' : '-') + '\\draft' ].toJSON()
-            };
-            this.viewport.updateFlag(vs, 'draft', action == 'draft');
-            DimpCore.doAction('MarkMessage', args, vs);
-            break;
+    isUnseen: function(r)
+    {
+        /* Unseen is a weird flag. Since we are doing a reverse match on this
+         * flag (knowing a message is SEEN is not as important as knowing the
+         * message lacks the SEEN FLAG), the presence of \\seen indicates that
+         * the message is in reality unseen. */
+        return r.flag.include('\\seen');
+    },
 
-        case 'forwarded':
-            this.viewport.updateFlag(vs, 'forwarded', true);
-            break;
+    updateFlag: function(vs, flag, add)
+    {
+        /* See isUnseen() - if flag is \\seen, need to do the opposite
+         * action. */
+        if (flag == '\\seen') {
+            add = !add;
         }
+
+        vs.get('dataob').each(function(ob) {
+            ob.flag = ob.flag.without(flag);
+            if (add) {
+                ob.flag.push(flag);
+            } else {
+                var r = $(ob.domid);
+                if (r) {
+                    r.removeClassName(DIMP.conf.flags[flag].c);
+                }
+            }
+            this.updateStatusFlags(ob);
+        }, this);
     },
 
     updateStatusFlags: function(row)
     {
-        var elt = new Element('DIV'),
+        var bg = null,
+            f = document.createDocumentFragment(),
             r = $(row.domid),
-            s = r.down('.msgStatus'),
-            tmp;
+            s;
 
-        // Add attachment graphic
-        if (row.atc) {
-            tmp = 'status' + row.atc.capitalize();
-            if (!s.down('.' + tmp)) {
-                s.insert($(elt.cloneNode(false)).writeAttribute({ className: tmp, title: DIMP.conf.atc_list[row.atc] || null }));
-            }
+        if (!r) {
+            return;
         }
 
-        this.flags.each(function(c) {
-            tmp = 'status' + c.value;
-            var d = s.down('.' + tmp);
-            if (r.hasClassName(c.key)) {
-                if (!d) {
-                    s.insert($(elt.cloneNode(false)).writeAttribute({ className: tmp, title: DIMP.text[tmp] || null }));
-                }
-            } else if (d) {
-                d.remove();
+        s = r.down('.msgStatus');
+
+        row.flag.each(function(a) {
+            var ptr = DIMP.conf.flags[a];
+            if (!ptr.elt) {
+                ptr.elt = new Element('DIV', { className: 'msgflags ' + ptr.c, title: ptr.l });
+            }
+            r.addClassName(ptr.c);
+            f.appendChild(ptr.elt.cloneNode(false));
+            if (ptr.b) {
+                bg = ptr.b;
             }
         });
+
+        /* Clear existing flags. */
+        s.down().nextSiblings().invoke('remove');
+
+        s.appendChild(f);
+        r.setStyle({ background: bg });
     },
 
     /* Miscellaneous folder actions. */
@@ -2349,9 +2333,12 @@ var DimpBase = {
         /* Add popdown menus. Check for disabled compose at the same time. */
         this._addMouseEvents({ id: 'button_other', type: 'otheractions' }, true);
         DM.addSubMenu('ctx_message_reply', 'ctx_reply');
-        DM.addSubMenu('ctx_message_setflag', 'ctx_flag');
-        DM.addSubMenu('oa_setflag', 'ctx_flag');
-        DM.addSubMenu('ctx_draft_setflag', 'ctx_flag');
+        [ 'ctx_message_', 'oa_', 'ctx_draft_' ].each(function(i) {
+            if ($(i + 'setflag')) {
+                DM.addSubMenu(i + 'setflag', 'ctx_flag');
+                DM.addSubMenu(i + 'unsetflag', 'ctx_flag');
+            }
+        });
 
         if (DIMP.conf.disable_compose) {
             $('button_reply', 'button_forward').compact().invoke('up', 'SPAN').concat($('button_compose', 'composelink', 'ctx_contacts_new')).compact().invoke('remove');
index 15d7e8d..cbe26ca 100644 (file)
@@ -524,9 +524,9 @@ DimpCore = {
     // By default, no context onShow action
     contextOnShow: Prototype.emptyFunction,
 
-    contextOnClick: function(id, elt)
+    contextOnClick: function(elt, base, submenu)
     {
-        switch (id) {
+        switch (elt.readAttribute('id')) {
         case 'ctx_contacts_new':
             this.compose('new', { to: elt.readAttribute('address') });
             break;
index 47bf394..e955c78 100644 (file)
@@ -704,12 +704,6 @@ var ViewPort = Class.create({
                 c_nodes.push(this.template.evaluate(r));
             }, this);
             c.update(c_nodes.join(''));
-
-            if (this.opts.onUpdateClass) {
-                rows.get('dataob').each(function(d) {
-                    this.opts.onUpdateClass(d);
-                }, this);
-            }
         } else {
             // If loading a viewport for the first time, show a blank
             // viewport rather than the empty viewport status message.
@@ -790,54 +784,11 @@ var ViewPort = Class.create({
     },
 
     // vs = (Viewport_Selection) A Viewport_Selection object.
-    // flag = (string) Flag name.
-    // add = (boolean) Whether to set/unset flag.
-    updateFlag: function(vs, flag, add)
-    {
-        this._updateFlag(vs, flag, add, this.isFiltering());
-        this._updateClass(vs, flag, add);
-    },
-
-    // vs = (Viewport_Selection) A Viewport_Selection object.
-    // flag = (string) Flag name.
-    // add = (boolean) Whether to set/unset flag.
-    // filter = (boolean) Are we filtering results?
-    _updateFlag: function(vs, flag, add, filter)
-    {
-        vs.get('dataob').each(function(r) {
-            if (add) {
-                r.bg.push(flag);
-            } else {
-                r.bg = r.bg.without(flag);
-            }
-            if (filter) {
-                this._updateFlag(this.createSelection('uid', r.vp_id, r.view), flag, add);
-            }
-        }, this);
-    },
-
-    // vs = (Viewport_Selection) A Viewport_Selection object.
-    // flag = (string) Flag name.
+    // cname = (string) Class name.
     // add = (boolean) Whether to set/unset flag.
-    _updateClass: function(vs, flag, add)
+    _updateClass: function(vs, cname, add)
     {
-        var divs = vs.get('div'),
-            sel = new ViewPort_Selection(this._getBuffer(), 'div', divs);
-
-        divs.each(function(d) {
-            if (add) {
-                d.addClassName(flag);
-            } else {
-                d.removeClassName(flag);
-            }
-        }, this);
-
-
-        if (this.opts.onUpdateClass) {
-            sel.get('dataob').each(function(d) {
-                this.opts.onUpdateClass(d);
-            }, this);
-        }
+        vs.get('div').invoke(add ? 'addClassName' : 'removeClassName', cname);
     },
 
     _getLineHeight: function()
index d33c5e5..45d2006 100644 (file)
@@ -236,16 +236,7 @@ var DimpCompose = {
             case 'send_message':
                 this.button_pressed = false;
                 if (DIMP.baseWindow) {
-                    switch (d.reply_type) {
-                    case 'reply':
-                        DIMP.baseWindow.DimpBase.flag('answered', { index: d.index, mailbox: d.reply_folder, noserver: true });
-                        break;
-
-                    case 'forward':
-                        DIMP.baseWindow.DimpBase.flag('forwarded', { index: d.index, mailbox: d.reply_folder, noserver: true });
-                        break;
-                    }
-
+                    DIMP.baseWindow.DimpBase.flag('imapflag', { imap: d.reply_type == 'reply' ? '\\answered' : '$forwarded', index: d.index, mailbox: d.reply_folder, noserver: true, set: true });
 
                     if (d.folder) {
                         DIMP.baseWindow.DimpBase.createFolder(d.folder);
diff --git a/imp/js/src/flagmanagement.js b/imp/js/src/flagmanagement.js
new file mode 100644 (file)
index 0000000..92ef8e7
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+ * Provides the javascript for managing message flags.
+ *
+ * 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 ImpFlagmanagement = {
+    // Variables set by other code: new_prompt
+
+    addFlag: function()
+    {
+        var category = window.prompt(this.new_prompt, '');
+        if (category) {
+            this._sendData('add', category);
+        }
+    },
+
+    _sendData: function(a, d)
+    {
+        $('flag_action').setValue(a)
+        $('flag_data').setValue(d);
+        $('prefs').submit();
+    },
+
+    clickHandler: function(e)
+    {
+        if (e.isRightClick()) {
+            return;
+        }
+
+        var elt = e.element(), elt2, id;
+
+        while (Object.isElement(elt)) {
+            if (elt.hasClassName('flagcolorpicker')) {
+                elt2 = elt.previous('INPUT');
+                id = elt2.readAttribute('id');
+                new ColorPicker({
+                    color: $F(elt2),
+                    offsetParent: elt,
+                    update: [[ id, 'value' ], [ id, 'background' ]]
+                });
+                e.stop();
+                return;
+            }
+
+            if (elt.hasClassName('flagdelete')) {
+                this._sendData('delete', elt.previous('INPUT').readAttribute('id'));
+                e.stop();
+                return;
+            }
+
+            switch (elt.readAttribute('id')) {
+            case 'new_button':
+                this.addFlag();
+                break;
+            }
+
+            elt = elt.up();
+        }
+    },
+
+    resetHandler: function()
+    {
+        $('prefs').getInputs('text').each(function(i) {
+            if (i.readAttribute('id').startsWith('color_')) {
+                i.setStyle({ backgroundColor: $F(i) });
+            }
+        });
+    }
+
+};
+
+document.observe('dom:loaded', function() {
+    var fm = ImpFlagmanagement;
+    document.observe('click', fm.clickHandler.bindAsEventListener(fm));
+    $('prefs').observe('reset', function() { fm.resetHandler.defer(); });
+});
index 0692ad7..60076ec 100644 (file)
@@ -91,7 +91,11 @@ var DimpFullmessage = {
             case 'button_deleted':
             case 'button_ham':
             case 'button_spam':
-                DIMP.baseWindow.DimpBase.flag(id.substring(7), { index: DIMP.conf.msg_index, mailbox: DIMP.conf.msg_folder });
+                if (id == 'button_deleted') {
+                    DIMP.baseWindow.DimpBase.flag('imapflag', { imap: '\\deleted', index: DIMP.conf.msg_index, mailbox: DIMP.conf.msg_folder, set: true });
+                } else {
+                    DIMP.baseWindow.DimpBase.flag(id.substring(7), { index: DIMP.conf.msg_index, mailbox: DIMP.conf.msg_folder });
+                }
                 window.close();
                 e.stop();
                 return;
@@ -109,8 +113,10 @@ var DimpFullmessage = {
         parentfunc(e);
     },
 
-    contextOnClick: function(parentfunc, id, elt)
+    contextOnClick: function(parentfunc, elt, base, submenu)
     {
+        var id = elt.readAttribute('id');
+
         switch (id) {
         case 'ctx_reply_reply':
         case 'ctx_reply_reply_all':
@@ -119,7 +125,7 @@ var DimpFullmessage = {
             break;
 
         default:
-            parentfunc(id, elt);
+            parentfunc(elt, base, submenu);
             break;
         }
     },
index 5d00ffd..5f77f38 100644 (file)
@@ -5,31 +5,25 @@
  * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
  */
 
-var ImpMessage = {
+var ImpMailbox = {
     // The following variables are defined in mailbox.php:
-    //  messagelist, sortlimit, unread
-    keyId: null,
-    startrange: null,
+    //  sortlimit, unread
 
     anySelected: function()
     {
-        return $H(this.messagelist).keys().detect(function(e) {
-            return $('check' + e).checked;
-        });
+        return $('messages').select('[name="indices[]"]').detect(Form.Element.getValue);
     },
 
     selectRow: function(id, select)
     {
-        var rid = $(id.replace(/check/, 'row'));
-
         if (select) {
-            rid.addClassName('selectedRow');
+            id.addClassName('selectedRow');
         } else {
             // Make sure to remove both regular and -over versions.
-            rid.removeClassName('selectedRow').removeClassName('selectedRow-over');
+            id.removeClassName('selectedRow').removeClassName('selectedRow-over');
         }
 
-        $(id).checked = select;
+        id.down('INPUT.checkbox').setValue(select);
     },
 
     confirmDialog: function(url, msg)
@@ -70,80 +64,39 @@ var ImpMessage = {
         $('messages').submit();
     },
 
-    makeSelection: function(form)
-    {
-        var flag = '';
-
-        switch (parseInt(form)) {
-        case -1:
-            if ($('checkAll').checked) {
-                flag = '!';
-            }
-            flag += IMP.conf.IMP_ALL;
-            break;
-
-        case 1:
-            flag = $F('filter1');
-            break;
-
-        default:
-            flag = $F('filter2');
-        }
-
-        // Fixes Bug #6893
-        if (flag.empty()) {
-            return;
-        } else if (flag.startsWith('!')) {
-            this.selectFlagged(parseInt(flag.substring(1)), false);
-        } else if (flag.startsWith('+')) {
-            this.selectFlagged(flag.substring(0, 1), null);
-        } else {
-            this.selectFlagged(parseInt(flag), true);
-        }
-
-        // Reset the form.
-        switch (parseInt(form)) {
-        case 1:
-            $('select1').reset();
-            break;
-
-        default:
-            $('select2').reset();
-        }
-    },
-
     selectRange: function(e)
     {
-        var id = e.element().readAttribute('id'),
-            checkbox = $(id),
-            count = 0,
-            checked, elts;
-
-        if (!checkbox) {
-            return;
-        }
-
-        checked = checkbox.checked;
-
-        if (this.startrange !== null && e.shiftKey) {
-            elts = [ $(this.startrange).readAttribute('id'), checkbox.readAttribute('id') ];
-            $H(this.messagelist).keys().detect(function(r) {
-                r = 'check' + r;
-                if (elts.indexOf(r) != -1) {
-                    ++count;
+        // elt = checkbox element
+        var elt = e.element(),
+            tr = elt.up('TR'),
+            checked = $F(elt),
+            end, start;
+
+        if (this.startrange && e.shiftKey) {
+            if (this.startrange != elt) {
+                // Dirty trick - use position in page to determine which way
+                // to traverse
+                if (this.startrange.offsetTop < tr.offsetTop) {
+                    start = this.startrange.next();
+                    end = tr;
+                } else {
+                    start = tr;
+                    end = this.startrange.previous();
                 }
-                if (count) {
-                    this.selectRow(r, checked);
-                    if (count == 2) {
-                        return true;
+
+                do {
+                    this.selectRow(start, checked);
+                    if (start == end) {
+                        break;
                     }
-                }
-            }, this);
+                    start = start.next();
+                } while (start);
+            }
         } else {
-            this.selectRow(id, checked);
+            this.selectRow(tr, checked);
         }
 
-        this.startrange = id;
+        this.startrange = tr;
     },
 
     updateFolders: function(form)
@@ -189,22 +142,6 @@ var ImpMessage = {
         }
     },
 
-    // Put everything reliant on IMAP flags in this section.
-    selectFlagged: function(flag, val)
-    {
-        $H(this.messagelist).keys().each(function(e) {
-            var check, elt = $('check' + e);
-            if (flag == '+') {
-                check = !elt.checked;
-            } else if (flag & this.messagelist[e]) {
-                check = val;
-            } else {
-                check = !val;
-            }
-            this.selectRow(elt.id, check);
-        }, this);
-    },
-
     flagMessages: function(form)
     {
         var f1 = $('flag1'), f2 = $('flag2');
@@ -212,8 +149,7 @@ var ImpMessage = {
         if ((form == 1 && $F(f1) != "") ||
             (form == 2 && $F(f2) != "")) {
             if (this.anySelected()) {
-                // Can't use $() here.  See Bug #4736.
-                document.messages.flag.value = (form == 1) ? $F(f1) : $F(f2);
+                $('messages').down('[name=flag]').setValue((form == 1) ? $F(f1) : $F(f2));
                 this.submit('flag_messages');
             } else {
                 if (form == 1) {
@@ -249,12 +185,12 @@ var ImpMessage = {
     {
         var id = e.element().readAttribute('id');
 
-        if (id.startsWith('filter')) {
-            this.makeSelection(id.substring(6));
-        } else if (id.startsWith('flag')) {
-            this.flagMessages(id.substring(4));
-        } else if (id.startsWith('targetMailbox')) {
-            this.updateFolders(id.substring(13));
+        if (id) {
+            if (id.startsWith('flag')) {
+                this.flagMessages(id.substring(4));
+            } else if (id.startsWith('targetMailbox')) {
+                this.updateFolders(id.substring(13));
+            }
         }
     },
 
@@ -267,8 +203,6 @@ var ImpMessage = {
         var elt = e.element(), id;
 
         while (Object.isElement(elt)) {
-            id = elt.readAttribute('id');
-
             if (elt.match('.msgactions A.widget')) {
                 if (elt.hasClassName('moveAction')) {
                     this._transfer('move_messages');
@@ -300,7 +234,13 @@ var ImpMessage = {
 
                 e.stop();
                 return;
-            } else if (!id) {
+            } else if (elt.hasClassName('checkbox')) {
+                this.selectRange(e);
+                // Fall through to elt.up() call below.
+            }
+
+            id = elt.readAttribute('id');
+            if (!id) {
                 elt = elt.up();
                 continue;
             }
@@ -311,15 +251,16 @@ var ImpMessage = {
                 if (id == 'checkheader') {
                     $('checkAll').checked = !$('checkAll').checked;
                 }
-                this.makeSelection(-1);
+
+                $('messages').select('TABLE.messageList TR[id]').each(function(i, s) {
+                    this.selectRow(i, $F('checkAll'));
+                }, this);
                 return;
             }
 
-            if (id.startsWith('check') && elt.hasClassName('checkbox')) {
-                this.selectRange(e);
-            } else if (!this.sortlimit &&
-                      elt.match('TH') &&
-                      elt.up('TABLE.messageList')) {
+            if (!this.sortlimit &&
+                elt.match('TH') &&
+                elt.up('TABLE.messageList')) {
                 document.location.href = elt.down('A').href;
             }
 
@@ -329,82 +270,67 @@ var ImpMessage = {
 
     keyDownHandler: function(e)
     {
-        var o = e.element(),
+        var elt = e.element(),
             key = e.keyCode,
-            checkinc, loc, next, nextId, old, row, subjinc;
+            loc, search;
 
         if (e.altKey || e.ctrlKey) {
-            switch (key) {
-            case Event.KEY_UP:
-                checkinc = -1;
-                subjinc = -1;
-                break;
-
-            case Event.KEY_DOWN:
-                checkinc = 1;
-                subjinc = 1;
-                break;
-
-            default:
+            if (!(key == Event.KEY_UP || key == Event.KEY_DOWN)) {
                 return;
             }
 
-            if (typeof this.messagelist == 'undefined') {
-                return;
+            if (!this.cursor) {
+                this.cursor = elt.up('TABLE.messageList TR');
             }
 
-            if (o.id.indexOf('check') == 0 && o.tagName == 'INPUT') {
-                old = o.id.substring(5);
-                this.keyId = this.getMessage(old, checkinc);
-                next = $('subject' + this.keyId);
-            } else if (o.id.indexOf('subject') == 0 && o.tagName == 'A') {
-                old = o.id.substring(7);
-                this.keyId = this.getMessage(old, subjinc);
-                next = $('subject' + this.keyId);
-            } else {
-                this.keyId = ((checkinc + subjinc) > 0) ? $H(this.messagelist).keys().first() : $H(this.messagelist).keys().last();
-                if (Event.KEY_UP || Event.KEY_DOWN) {
-                    next = $('subject' + this.keyId);
+            if (this.cursor) {
+                if (e.altKey) {
+                    this.selectRow(this.cursor, !$F(this.cursor.down('INPUT.checkbox')));
                 }
+
+                switch (key) {
+                case Event.KEY_UP:
+                    this.cursor = this.cursor.previous();
+                    if (!this.cursor.readAttribute('id')) {
+                        search = 'last';
+                    }
+                    break;
+
+                case Event.KEY_DOWN:
+                    this.cursor = this.cursor.next();
+                    if (!this.cursor) {
+                        search = 'first';
+                    }
+                    break;
+                }
+            } else {
+                search = Event.KEY_DOWN ? 'first' : 'last';
+            }
+
+            if (search) {
+                this.cursor = (search == 'first')
+                    ? $('messages').select('TABLE.messageList TR[id]').first()
+                    : $('messages').select('TABLE.messageList TR[id]').last();
             }
-        } else if (key == 32 &&
-               o.id.indexOf('subject') == 0 &&
-               o.tagName == 'A') {
-            // Space key - toggle selection of the current message.
-            this.startrange = 'check' + this.keyId;
-            this.selectRow(this.startrange, !$(this.startrange).checked);
+
+            this.cursor.down('TD a.mboxSubject').focus();
+        } else if (key == 32 && this.cursor) {
+            this.selectRow(this.cursor, !$F(this.cursor.down('INPUT.checkbox')));
         } else if (!e.shiftKey) {
             if (key == Event.KEY_LEFT && $('prev')) {
-                loc = $('prev').href;
+                loc = $('prev');
             } else if (key == Event.KEY_RIGHT && $('next')) {
-                loc = $('next').href;
+                loc = $('next');
             }
 
             if (loc) {
-                document.location.href = loc;
+                document.location.href = loc.readAttribute('href');
             }
             return;
         } else {
             return;
         }
 
-        if (next) {
-            next.focus();
-            row = $('row' + this.keyId);
-            if (e.altKey) {
-                nextId = next.id.replace(/subject/, 'check');
-                this.selectRow(nextId, !$(nextId).checked);
-            } else if (old != next.id && row.className.indexOf('-over') == -1) {
-                row.className += '-over';
-            }
-            if (old) {
-                row = $('row' + old);
-                if (old != next.id) {
-                    row.className = row.className.replace(/-over/, '');
-                }
-            }
-        }
-
         e.stop();
     },
 
@@ -417,15 +343,17 @@ var ImpMessage = {
 
 };
 
-document.observe('change', ImpMessage.changeHandler.bindAsEventListener(ImpMessage));
-document.observe('click', ImpMessage.clickHandler.bindAsEventListener(ImpMessage));
-document.observe('keydown', ImpMessage.keyDownHandler.bindAsEventListener(ImpMessage));
-document.observe('submit', ImpMessage.submitHandler.bindAsEventListener(ImpMessage));
+document.observe('dom:loaded', function() {
+    var im = ImpMailbox;
+
+    document.observe('change', im.changeHandler.bindAsEventListener(im));
+    document.observe('click', im.clickHandler.bindAsEventListener(im));
+    document.observe('keydown', im.keyDownHandler.bindAsEventListener(im));
+    document.observe('submit', im.submitHandler.bindAsEventListener(im));
 
-Event.observe(window, 'load', function() {
     if (window.fluid) {
         try {
-            window.fluid.setDockBadge(ImpMessage.unread);
+            window.fluid.setDockBadge(ImpMailbox.unread);
         } catch (e) {}
     }
 });
index e0304c8..a38c282 100644 (file)
@@ -53,9 +53,10 @@ var ImpMessage = {
     flagMessage: function(form)
     {
         var f1 = $('flag1'), f2 = $('flag2');
-        if ((form == 1 && $F(f1)) ||
-            (form == 2 && $F(f2))) {
-            $('flag').setValue((form == 1) ? $F(f1) : $F(f2));
+
+        if ((form == 1 && $F(f1) != "") ||
+            (form == 2 && $F(f2) != "")) {
+            $('messages').down('[name=flag]').setValue((form == 1) ? $F(f1) : $F(f2));
             this.submit('flag_message');
         }
     },
index 5b901b5..58ce017 100644 (file)
@@ -148,6 +148,17 @@ class DIMP
             }
         }
 
+        /* Generate flag array. */
+        $flags = array();
+        $imp_flags = &IMP_Imap_Flags::singleton();
+        foreach ($imp_flags->getList() as $val) {
+            $flags[$val['flag']] = array_filter(array(
+                'b' => isset($val['b']) ? $val['b'] : null,
+                'c' => $val['c'],
+                'l' => $val['l']
+            ));
+        }
+
         /* Variables used in core javascript files. */
         $code['conf'] = array_filter(array(
             'URI_DIMP_INBOX' => Horde::applicationUrl('index-dimp.php', true, -1),
@@ -169,6 +180,8 @@ class DIMP
             'popup_width' => 820,
             'popup_height' => 610,
 
+            'flags' => $flags,
+
             'spam_folder' => IMP::folderPref($prefs->getValue('spam_folder'), true),
             'spam_reporting' => intval(!empty($conf['spam']['reporting'])),
             'spam_spamfolder' => intval(!empty($conf['spam']['spamfolder'])),
@@ -197,8 +210,6 @@ class DIMP
             'background_inbox' => intval(!empty($conf['dimp']['viewport']['background_inbox'])),
             'splitbar_pos' => $prefs->getValue('dimp_splitbar'),
 
-            'atc_list' => IMP_UI_Mailbox::getAttachmentAltList(),
-
             // Turn debugging on?
             'debug' => intval(!empty($conf['dimp']['js']['debug'])),
         ));
@@ -239,10 +250,6 @@ class DIMP
             'alog_message' => _("Message"),
             'alog_success' => _("Success"),
             'alog_warning' => _("Warning"),
-            'statusAnswered' => _("Replied"),
-            'statusForwarded' => _("Forwarded"),
-            'statusUnseen' => _("Unseen"),
-            'statusFlagged' => _("Flagged"),
         ));
 
         /* Gettext strings with individual escaping. */
index 6d90ede..c71c405 100644 (file)
@@ -25,16 +25,6 @@ class IMP
     const PGP_SYM_ENCRYPT = 8;
     const PGP_SYM_SIGNENC = 9;
 
-    /* IMAP flag constants. */
-    const FLAG_ALL = 0;
-    const FLAG_UNSEEN = 1;
-    const FLAG_DELETED = 2;
-    const FLAG_ANSWERED = 4;
-    const FLAG_FLAGGED = 8;
-    const FLAG_DRAFT = 16;
-    const FLAG_PERSONAL = 32;
-    const FLAG_FORWARDED = 64;
-
     /* IMP Mailbox view constants. */
     const MAILBOX_START_FIRSTUNSEEN = 1;
     const MAILBOX_START_LASTUNSEEN = 2;
@@ -244,9 +234,9 @@ class IMP
             (!empty($GLOBALS['conf']['hooks']['permsdenied']) ||
              (self::hasPermission('create_folders') &&
               self::hasPermission('max_folders')))) {
-            $text .= '<option value="" disabled="disabled">- - - - - - - - -</option>' . "\n";
+            $text .= '<option value="" disabled="disabled">- - - - - - - -</option>' . "\n";
             $text .= '<option value="*new*">' . _("New Folder") . "</option>\n";
-            $text .= '<option value="" disabled="disabled">- - - - - - - - -</option>' . "\n";
+            $text .= '<option value="" disabled="disabled">- - - - - - - -</option>' . "\n";
         }
 
         /* Add the list of mailboxes to the lists. */
diff --git a/imp/lib/Imap/Flags.php b/imp/lib/Imap/Flags.php
new file mode 100644 (file)
index 0000000..bca35eb
--- /dev/null
@@ -0,0 +1,360 @@
+<?php
+/**
+ * The IMP_Imap_Flags class provides an interface to deal with display of
+ * flags/labels on messages.
+ *
+ * Copyright 2009 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>
+ * @package IMP
+ */
+class IMP_Imap_Flags
+{
+    /* IMAP flag prefix for IMP-specific flags. */
+    const PREFIX = '$IMPFlag';
+
+    /**
+     * Singleton instance.
+     *
+     * @var IMP_Imap_Flags
+     */
+    static protected $_instance;
+
+    /**
+     * The cached list of flags.
+     *
+     * @var array
+     */
+    protected $_flags = null;
+
+    /**
+     * Attempts to return a reference to a concrete object instance.
+     * It will only create a new instance if no instance currently exists.
+     *
+     * @return IMP_Imap_Flags  The created concrete instance.
+     */
+    static public function singleton()
+    {
+        if (!isset(self::$_instance)) {
+            self::$_instance = new IMP_Imap_Flags();
+        }
+
+        return self::$_instance;
+    }
+
+    /**
+     * Save the flag list to the prefs backend.
+     */
+    protected function _save()
+    {
+        $GLOBALS['prefs']->setValue('msgflags', json_encode($this->_flags));
+    }
+
+    /**
+     * 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.
+     *          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();
+
+        $def_color = $GLOBALS['prefs']->getValue('msgflags_color');
+        $ret = $this->_flags;
+        $avail_flags = array_keys($ret);
+
+        if (!empty($options['imap'])) {
+            $types = array('imapp', 'imapu');
+            if (!$GLOBALS['prefs']->getValue('msgflags_hidesys')) {
+                $types[] = 'imap';
+            }
+        }
+
+        /* Reduce the list of flags for the mailbox depending on the return
+         * from the PERMANENTFLAGS IMAP response. */
+        if (!empty($options['mailbox'])) {
+            try {
+                $status = $GLOBALS['imp_imap']->ob->status($options['mailbox'], Horde_Imap_Client::STATUS_PERMFLAGS);
+                if (!in_array('\\*', $status['permflags'])) {
+                    $avail_flags = array_intersect($avail_flags, $status['permflags']);
+                }
+            } catch (Horde_Imap_Client_Exception $e) {}
+        }
+
+        foreach ($avail_flags as $key) {
+            $ret[$key]['flag'] = $key;
+
+            if (!empty($options['fgcolor'])) {
+                $ret[$key]['f'] = '#000';
+                if (!isset($ret[$key]['b'])) {
+                    $ret[$key]['b'] = $def_color;
+                } elseif (Horde_Image::brightness($this->_flags[$key]['b']) < 128) {
+                    $ret[$key]['f'] = '#f6f6f6';
+                }
+            }
+
+            if (!empty($options['imap']) &&
+                !in_array($ret[$key]['t'], $types)) {
+                unset($ret[$key]);
+            } elseif (!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->_flags = json_decode($GLOBALS['prefs']->getValue('msgflags'), true);
+
+        /* Sanity checking. */
+        if (!is_array($this->_flags)) {
+            $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();
+
+        /* Flags are named PREFIX{$i}. Keep incrementing until we find the
+         * next available flag. */
+        for ($i = 0; ; ++$i) {
+            $curr = self::PREFIX . $i;
+            if (!isset($this->_flags[$curr])) {
+                $this->_flags[$curr] = array(
+                    // 'a' => These flags are not shown in mimp
+                    // TODO: Generate random background
+                    'b' => '#ffffff',
+                    'c' => 'flagUser',
+                    'd' => true,
+                    'l' => $label,
+                    't' => 'imapp'
+                );
+
+                $this->_save();
+                return $curr;
+            }
+        }
+    }
+
+    /**
+     * 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();
+        }
+    }
+
+    /**
+     * 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'])) {
+            unset($this->_flags[$label]);
+            $this->_save();
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Parse a list of flag information.
+     *
+     * @param array $options  Additional options:
+     * <pre>
+     * 'atc' - (Horde_Mime_Part) Attachment info. A Horde_Mime_Part object
+     *         representing the message structure.
+     *         DEFAULT: not parsed
+     * '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.
+     * '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..
+     * 'priority' - (string) Message priority. The content of the X-Priority
+     *              header.
+     * </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.
+     * '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.
+     * </pre>
+     */
+    public function parse($options = array())
+    {
+        $this->_loadList();
+
+        $process = $ret = array();
+        $f = $this->_flags;
+
+        if (isset($options['personal'])) {
+            if (is_array($options['personal'])) {
+                require_once 'Horde/Identity.php';
+                $identity = Identity::singleton(array('imp', 'imp'));
+                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['priority'])) {
+            $imp_msg_ui = new IMP_UI_Message();
+            switch ($imp_msg_ui->getXpriority($options['priority'])) {
+            case 'high':
+                $process['highpri'] = $f['highpri'];
+                break;
+
+            case 'low':
+                $process['lowpri'] = $f['lowpri'];
+                break;
+            }
+        }
+
+        if (!empty($options['atc'])) {
+            $imp_mbox_ui = new IMP_UI_Mailbox();
+            if ($type = $imp_ui->getAttachmentType($ob['structure']->getType())) {
+                $process[$type] = $f[$type];
+            }
+        }
+
+        if (($_SESSION['imp']['protocol'] == 'imap') &&
+            isset($options['flags'])) {
+            if (!empty($options['flags'])) {
+                $options['flags'] = array_map('strtolower', $options['flags']);
+            }
+
+            foreach ($f as $k => $v) {
+                if (in_array($v['t'], array('imap', 'imapp', 'imapu', 'imp'))) {
+                    if (empty($v['n'])) {
+                        $match = in_array($k, $options['flags']);
+                    } elseif (empty($options['flags'])) {
+                        $match = true;
+                    } else {
+                        $match = !in_array($k, $options['flags']);
+                    }
+
+                    if ($match) {
+                        $process[$k] = $v;
+                    }
+                }
+            }
+        }
+
+        $def_color = $GLOBALS['prefs']->getValue('msgflags_color');
+
+        foreach ($process as $key => $val) {
+            $tmp = array(
+                'bg' => isset($val['b']) ? $val['b'] : $def_color,
+                'flag' => $key,
+                'label' => $val['l']
+            );
+
+            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;
+    }
+
+    /**
+     * 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="msgflags ' . $c . '" title="' . htmlspecialchars($l) . '"></div>';
+    }
+
+}
index 5ed1249..cd86057 100644 (file)
@@ -116,21 +116,49 @@ class IMP_Mailbox
      * Build the array of message information.
      *
      * @param array $msgnum   An array of message sequence numbers.
-     * @param mixed $preview  Include preview information?  If empty, add no
-     *                        preview information. If 1, uses value from
-     *                        prefs.  If 2, forces addition of preview info.
-     * @param array $headers  A list of non-standard (non-envelope) headers to
-     *                        return.
+     * @param array $options  Additional options:
+     * <pre>
+     * 'headers' - (array) A list of non-standard (non-envelope) headers to
+     *             return.
+     *             DEFAULT: Only envelope headers returned.
+     * 'preview' - (mixed) Include preview information?  If empty, add no
+     *                     preview information. If 1, uses value from prefs.
+     *                     If 2, forces addition of preview info.
+     *                     DEFAULT: No preview information.
+     * 'structure' - (boolean) Get structure information from server.
+     *               Contained in the 'strucutre' entry.
+     *               DEFAULT: false
+     * </pre>
      *
      * @return array  An array with the following keys:
      * <pre>
-     * 'overview' - (array) The overview information.
+     * 'overview' - (array) The overview information. Contains the following:
+     *              'envelope' - (array) Envelope information returned from
+     *                           the IMAP server. See
+     *                           Horde_Imap_Client::fetch() for format.
+     *              'flags' - (array) The list of IMAP flags returned from
+     *                        the server. See Horde_Imap_Client::fetch() for
+     *                        the format.
+     *              'headers' - (array) Any headers requested in
+     *                          $options['headers']. Horde_Mime_Headers objects
+     *                          are returned.  See Horde_Imap_Client::fetch()
+     *                          for the format.
+     *              'mailbox' - (string) The mailbox containing the message.
+     *              'preview' - (string) If requested in $options['preview'],
+     *                          the preview text.
+     *              'previewcut'- (boolean) Has the preview text been cut?
+     *              'seq' - (integer) The sequence number of the message.
+     *              'size' - (integer) The size of the message in bytes.
+     *              'structure'- (array) The structure of the message. Only
+     *                           set if $options['structure'] is true. See
+     *                           Horde_Imap_Client::fetch() for format.
+     *              'uid' - (string) The unique ID of the message.
+     *
      * 'uids' - (array) The array of UIDs. It is in the same format as used
      *          for IMP::parseIndicesList().
      * </pre>
      */
-    public function getMailboxArray($msgnum, $preview = false,
-                                    $headers = array())
+    public function getMailboxArray($msgnum, $options = array())
     {
         $this->_buildMailbox();
 
@@ -159,18 +187,27 @@ class IMP_Mailbox
             Horde_Imap_Client::FETCH_SEQ => true
         );
 
-        if (!empty($headers)) {
-            $fetch_criteria[Horde_Imap_Client::FETCH_HEADERS] = array(array('headers' => $headers, 'label' => 'imp', 'parse' => true, 'peek' => true));
+        if (!empty($options['headers'])) {
+            $fetch_criteria[Horde_Imap_Client::FETCH_HEADERS] = array(array('headers' => $options['headers'], 'label' => 'imp', 'parse' => true, 'peek' => true));
         }
 
-        $cache = $preview ? $GLOBALS['imp_imap']->ob->getCache() : null;
+        if (!empty($options['structure'])) {
+            $fetch_criteria[Horde_Imap_Client::FETCH_STRUCTURE] = array('parse' => true);
+        }
+
+        if (empty($options['preview'])) {
+            $cache = null;
+            $options['preview'] = 0;
+        } else {
+            $cache = $GLOBALS['imp_imap']->ob->getCache();
+        }
 
         /* Retrieve information from each mailbox. */
         foreach ($to_process as $mbox => $ids) {
             try {
                 $fetch_res = $GLOBALS['imp_imap']->ob->fetch($mbox, $fetch_criteria, array('ids' => array_keys($ids)));
 
-                if ($preview) {
+                if ($options['preview']) {
                     $preview_info = $tostore = array();
                     if ($cache) {
                         try {
@@ -186,10 +223,9 @@ class IMP_Mailbox
                         $v['headers'] = $v['headers']['imp'];
                     }
 
-                    if ($preview &&
-                        (($preview === 2) ||
-                         !$GLOBALS['prefs']->getValue('preview_show_unread') ||
-                         !in_array('\\seen', $v['flags']))) {
+                    if (($options['preview'] === 2) ||
+                        !$GLOBALS['prefs']->getValue('preview_show_unread') ||
+                        !in_array('\\seen', $v['flags'])) {
                         if (empty($preview_info[$k])) {
                             try {
                                 $imp_contents = IMP_Contents::singleton($k . IMP::IDX_SEP . $mbox);
index 4a5c367..5532765 100644 (file)
@@ -159,23 +159,10 @@ class IMP_UI_Mailbox
     }
 
     /**
-     * The list of ALT text to use for mailbox display icons.
-     *
-     * @return array  Type -> ALT text mappings.
-     */
-    static public function getAttachmentAltList()
-    {
-        return array(
-            'signed' => _("Message is signed"),
-            'encrypted' => _("Message is encrypted"),
-            'attachment' => _("Message has attachments")
-        );
-    }
-
-    /**
      * Return the icon to use for a given attachment.
      *
-     * @return string  The mailbox display icon type.
+     * @return string  The mailbox display icon type (attach, encrypt,
+     *                 signed).
      */
     public function getAttachmentType($type)
     {
@@ -186,7 +173,7 @@ class IMP_UI_Mailbox
                 return 'signed';
 
             case 'encrypted':
-                return 'encrypted';
+                return 'encrypt';
 
             case 'alternative':
             case 'related':
@@ -194,10 +181,10 @@ class IMP_UI_Mailbox
                 break;
 
             default:
-                return 'attachment';
+                return 'attach';
             }
         } elseif ($type == 'application/pkcs7-mime') {
-             return 'encrypted';
+             return 'encrypt';
         }
 
         return '';
index ef0baa8..9dc9ade 100644 (file)
@@ -262,11 +262,11 @@ class IMP_Views_ListMessages
      *                                  to process.
      * @param boolean $search           Is this a search mbox?
      *
-     * @return array TODO
+     * @return array  TODO
      */
     private function _getOverviewData($imp_mailbox, $folder, $msglist, $search)
     {
-        $lookup = $msgs = array();
+        $msgs = array();
 
         if (empty($msglist)) {
             return $msgs;
@@ -275,7 +275,7 @@ class IMP_Views_ListMessages
         require_once 'Horde/Identity.php';
 
         /* Get mailbox information. */
-        $overview = $imp_mailbox->getMailboxArray($msglist, false, array('list-post'));
+        $overview = $imp_mailbox->getMailboxArray($msglist, array('headers' => array('list-post', 'x-priority'), 'structure' => $GLOBALS['prefs']->getValue('atc_flag')));
         $charset = NLS::getCharset();
         $imp_ui = new IMP_UI_Mailbox($folder);
 
@@ -284,6 +284,8 @@ class IMP_Views_ListMessages
         while (list(,$ob) = each($overview['overview'])) {
             /* Initialize the header fields. */
             $msg = array(
+                'bg' => array('msgRow'),
+                'flag' => array(),
                 'imapuid' => $ob['uid'],
                 'menutype' => 'message',
                 'rownum' => $ob['seq'],
@@ -291,31 +293,27 @@ class IMP_Views_ListMessages
             );
 
             /* Get all the flag information. */
-            $bg = array('msgRow');
-            if ($_SESSION['imp']['protocol'] != 'pop') {
-                if (!in_array('\\seen', $ob['flags'])) {
-                    $bg[] = 'unseen';
-                }
-                if (in_array('\\answered', $ob['flags'])) {
-                    $bg[] = 'answered';
-                }
-                if (in_array('\\draft', $ob['flags'])) {
-                    $bg[] = 'draft';
-                    $msg['menutype'] = 'draft';
-                    $msg['draft'] = 1;
-                }
-                if (in_array('\\flagged', $ob['flags'])) {
-                    $bg[] = 'flagged';
-                }
-                if (in_array('\\deleted', $ob['flags'])) {
-                    $bg[] = 'deletedmsg';
-                }
-                if (in_array('$forwarded', $ob['flags'])) {
-                    $bg[] = 'forwarded';
-                }
+            if (!empty($GLOBALS['conf']['hooks']['msglist_flags'])) {
+                $ob['flags'] = array_merge($ob['flags'], Horde::callHook('_imp_hook_msglist_flags', array($ob, 'dimp'), 'imp'));
             }
 
-            $msg['bg'] = $bg;
+            $imp_flags = &IMP_Imap_Flags::singleton();
+            $flag_parse = $imp_flags->parse(array(
+                'atc' => isset($ob['structure']) ? $ob['structure'] : null,
+                'flags' => $ob['flags'],
+                'personal' => Horde_Mime_Address::getAddressesFromObject($ob['envelope']['to']),
+                'priority' => $ob['headers']->getValue('x-priority')
+            ));
+
+            foreach ($flag_parse as $val) {
+                $msg['flag'][] = $val['flag'];
+            }
+
+            /* Specific flag checking. */
+            if (in_array('\\draft', $ob['flags'])) {
+                $msg['menutype'] = 'draft';
+                $msg['draft'] = 1;
+            }
 
             /* Format size information. */
             $msg['size'] = htmlspecialchars($imp_ui->getSize($ob['size']), ENT_QUOTES, $charset);
@@ -345,39 +343,11 @@ class IMP_Views_ListMessages
             } else {
                 $msgs[$ob['uid']] = $msg;
             }
-
-            if (!isset($lookup[$ob['mailbox']])) {
-                $lookup[$ob['mailbox']] = array();
-            }
-            $lookup[$ob['mailbox']][] = $ob['uid'];
-        }
-
-        /* Add user supplied information from hook. */
-        if (!empty($GLOBALS['conf']['imp']['hooks']['msglist_format'])) {
-            foreach (array_keys($lookup) as $mbox) {
-                $ob_f = Horde::callHook('_imp_hook_msglist_format', array($mbox, $lookup[$mbox], 'dimp'), 'imp');
-
-                foreach ($ob_f as $uid => $val) {
-                    if ($search) {
-                        $ptr = &$msgs[$uid . $mbox];
-                    } else {
-                        $ptr = &$msgs[$uid];
-                    }
-
-                    if (!empty($val['atc'])) {
-                        $ptr['atc'] = $val['atc'];
-                    }
-
-                    if (!empty($val['class'])) {
-                        $ptr['bg'] = array_merge($ptr['bg'], $val['class']);
-                    }
-                }
-            }
         }
 
         /* Allow user to alter template array. */
-        if (!empty($GLOBALS['conf']['imp']['dimp']['hooks']['mailboxarray'])) {
-            $msgs = Horde::callHook('_imp_hook_dimp_mailboxarray', array($msgs), 'imp');
+        if (!empty($GLOBALS['conf']['imp']['hooks']['mailboxarray'])) {
+            $msgs = Horde::callHook('_imp_hook_mailboxarray', array($msgs, 'dimp'), 'imp');
         }
 
         return $msgs;
index bb71a49..1ddb195 100644 (file)
@@ -101,16 +101,9 @@ class IMP_Views_ShowMessage
         /* Set the current time zone. */
         NLS::setTimeZone();
 
-        /* Get envelope/flag/header information. */
+        /* Get envelope/header information. We don't use flags in this
+         * view. */
         try {
-            $flags_ret = $GLOBALS['imp_imap']->ob->fetch($folder, array(
-                Horde_Imap_Client::FETCH_FLAGS => true,
-            ), array('ids' => array($index)));
-            if (!isset($flags_ret[$index])) {
-                $result['error'] = $error_msg;
-                $result['errortype'] = 'horde.error';
-                return $result;
-            }
             $fetch_ret = $GLOBALS['imp_imap']->ob->fetch($folder, array(
                 Horde_Imap_Client::FETCH_ENVELOPE => true,
                 Horde_Imap_Client::FETCH_HEADERTEXT => array(array('parse' => true, 'peek' => false))
index 6df1941..794a7b8 100644 (file)
@@ -158,6 +158,53 @@ function handle_soundselect($updated)
     return $GLOBALS['prefs']->setValue('nav_audio', Util::getFormData('nav_audio'));
 }
 
+function handle_flagmanagement($updated)
+{
+    $imp_flags = &IMP_Imap_Flags::singleton();
+    $flag_action = Util::getFormData('flag_action');
+    $flag_data = Util::getFormData('flag_data');
+
+    if ($flag_action == 'add') {
+        $imp_flags->addFlag($flag_data);
+        return false;
+    }
+
+    $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);
+
+        switch ($flag_action) {
+        case 'delete':
+            if ($flag_data == ('bg_' . $md5)) {
+                $imp_flags->deleteFlag($key);
+                return false;
+            }
+            break;
+
+        default:
+            /* Change labels for user-defined flags. */
+            if ($val['t'] == 'imapp') {
+                $label = Util::getFormData('label_' . $md5);
+                if (strlen($label) && ($label != $val['l'])) {
+                    $imp_flags->updateFlag($key, array('l' => $label));
+                }
+            }
+
+            /* Change background for all flags. */
+            $bg = strtolower(Util::getFormData('bg_' . $md5));
+            if ((isset($val['b']) && ($bg != $val['b'])) ||
+                (!isset($val['b']) && ($bg != $def_color))) {
+                $imp_flags->updateFlag($key, array('b' => $bg));
+            }
+            break;
+        }
+    }
+
+    return false;
+}
+
 function prefs_callback()
 {
     global $prefs;
index 159fba4..fbaa686 100644 (file)
@@ -87,7 +87,7 @@ $sortpref = IMP::getSort($imp_mbox['mailbox']);
 $imp_ui = new IMP_UI_Mailbox($imp_mbox['mailbox']);
 
 /* Build the array of message information. */
-$mbox_info = $imp_mailbox->getMailboxArray(range($pageOb['begin'], $pageOb['end']));
+$mbox_info = $imp_mailbox->getMailboxArray(range($pageOb['begin'], $pageOb['end']), array('headers' => array('x-priority')));
 
 /* Get thread information. */
 $threadob = ($sortpref['by'] == Horde_Imap_Client::SORT_THREAD)
@@ -98,7 +98,7 @@ reset($mbox_info);
 while (list(,$ob) = each($mbox_info['overview'])) {
     /* Initialize the header fields. */
     $msg = array(
-        'number' => $ob['seq'],
+        'subject' => $imp_ui->getSubject($ob['envelope']['subject']),
         'status' => ''
     );
 
@@ -109,8 +109,6 @@ while (list(,$ob) = each($mbox_info['overview'])) {
         $msg['from'] = String::substr($msg['from'], 0, $conf['mimp']['mailbox']['max_from_chars']) . '...';
     }
 
-    $msg['subject'] = $imp_ui->getSubject($ob['envelope']['subject']);
-
     if (!is_null($threadob) && ($threadob->getThreadIndent($ob['uid']))) {
         $msg['subject'] = '>> ' . ltrim($msg['subject']);
     }
@@ -119,38 +117,25 @@ while (list(,$ob) = each($mbox_info['overview'])) {
         $msg['subject'] = String::substr($msg['subject'], 0, $conf['mimp']['mailbox']['max_subj_chars']) . '...';
     }
 
-    /* Generate the target link. */
-    $target = IMP::generateIMPUrl('message-mimp.php', $imp_mbox['mailbox'], $ob['uid'], $ob['mailbox']);
-
     /* Get flag information. */
-    if ($_SESSION['imp']['protocol'] != 'pop') {
-        $to_ob = Horde_Mime_Address::getAddressesFromObject($ob['envelope']['to']);
-        if (!empty($to_ob) && $identity->hasAddress($to_ob[0]['inner'])) {
-            $msg['status'] .= '+';
-        }
-        if (!in_array('\\seen', $ob['flags'])) {
-            $msg['status'] .= 'N';
-        }
-        if (in_array('\\answered', $ob['flags'])) {
-            $msg['status'] .= 'r';
-        }
-        if (in_array('\\draft', $ob['flags'])) {
-            $target = IMP::composeLink(array(), array('a' => 'd', 'thismailbox' => $imp_mbox['mailbox'], 'index' => $ob['uid'], 'bodypart' => 1));
-        }
-        if (in_array('\\flagged', $ob['flags'])) {
-            $msg['status'] .= 'I';
-        }
-        if (in_array('\\deleted', $ob['flags'])) {
-            $msg['status'] .= 'D';
-        }
-
-        /* Support for the pseudo-standard '$Forwarded' flag. */
-        if (in_array('$forwarded', $ob['flags'])) {
-            $msg['status'] .= 'F';
+    $imp_flags = &IMP_Imap_Flags::singleton();
+    $flag_parse = $imp_flags->parse(array(
+        'flags' => $ob['flags'],
+        'personal' => Horde_Mime_Address::getAddressesFromObject($ob['envelope']['to']),
+        'priority' => $ob['headers']->getValue('x-priority')
+    ));
+
+    foreach ($flag_parse as $val) {
+        if (isset($val['abbrev'])) {
+            $msg['status'] .= $val['abbrev'];
         }
     }
 
-    $msg['target'] = $target;
+    /* Generate the target link. */
+    $msg['target'] = in_array('\\draft', $ob['flags'])
+        ? IMP::composeLink(array(), array('a' => 'd', 'thismailbox' => $imp_mbox['mailbox'], 'index' => $ob['uid'], 'bodypart' => 1))
+         : IMP::generateIMPUrl('message-mimp.php', $imp_mbox['mailbox'], $ob['uid'], $ob['mailbox']);
+
     $msgs[] = $msg;
 }
 
index a012821..f7c248a 100644 (file)
  * @author  Michael Slusarz <slusarz@horde.org>
  */
 
-function _outputSummaries($msgs, $mbox, &$ids)
+function _outputSummaries($msgs, $mbox)
 {
     static $template;
 
-    if (!empty($GLOBALS['conf']['hooks']['msglist_format'])) {
-        $ob_f = Horde::callHook('_imp_hook_msglist_format', array($mbox, array_keys($msgs), 'imp'), 'imp');
-
-        foreach ($ob_f as $uid => $val) {
-            $ptr = &$msgs[$uid];
-
-            if (!empty($val['class'])) {
-                $ptr['bg'] = implode(' ', array_keys(array_flip(array_merge(explode(' ', $ptr['bg']), $val['class']))));
-            }
-
-            if (!empty($val['flagbits'])) {
-                $ids[$ptr['id']] |= $val['flagbits'];
-            }
-
-            if (!empty($val['status'])) {
-                $ptr['status'] .= $val['status'];
-            }
-        }
+    /* Allow user to alter template array. */
+    if (!empty($GLOBALS['conf']['imp']['hooks']['mailboxarray'])) {
+        $msgs = Horde::callHook('_imp_hook_mailboxarray', array($msgs, 'imp'), 'imp');
     }
 
     if (!isset($template)) {
@@ -52,6 +37,7 @@ function _outputSummaries($msgs, $mbox, &$ids)
     echo $template->fetch(IMP_TEMPLATES . '/mailbox/mailbox.html');
 }
 
+
 require_once dirname(__FILE__) . '/lib/base.php';
 
 /* Call the mailbox redirection hook, if requested. */
@@ -92,10 +78,6 @@ if (!is_array(($indices = Util::getFormData('indices')))) {
 /* Set the current time zone. */
 NLS::setTimeZone();
 
-/* Initialize the user's identities. */
-require_once 'Horde/Identity.php';
-$identity = Identity::singleton(array('imp', 'imp'));
-
 $do_filter = false;
 $open_compose_window = null;
 
@@ -258,10 +240,14 @@ $imp_mailbox = IMP_Mailbox::singleton($imp_mbox['mailbox']);
 $pageOb = $imp_mailbox->buildMailboxPage(Util::getFormData('page'), $start);
 $show_preview = ($conf['mailbox']['show_preview'] && $prefs->getValue('preview_enabled'));
 
-$overview_headers = empty($conf['fetchmail']['show_account_colors'])
-    ? array()
-    : array('x-color');
-$mbox_info = $imp_mailbox->getMailboxArray(range($pageOb['begin'], $pageOb['end']), $show_preview, $overview_headers);
+$overview_headers = array('x-priority');
+if (empty($conf['fetchmail']['show_account_colors'])) {
+    $fetchmail = false;
+} else {
+    $fetchmail = true;
+    $overview_headers[] = 'x-color';
+}
+$mbox_info = $imp_mailbox->getMailboxArray(range($pageOb['begin'], $pageOb['end']), array('preview' => $show_preview, 'headers' => $overview_headers, 'structure' => $prefs->getValue('atc_flag')));
 
 /* Determine sorting preferences. */
 $sortpref = IMP::getSort();
@@ -460,7 +446,7 @@ if ($_SESSION['imp']['protocol'] != 'pop') {
     if (!$search_mbox) {
         $hdr_template->set('search', Horde::link(Util::addParameter(Horde::applicationUrl('search.php'), 'search_mailbox', $imp_mbox['mailbox']), sprintf(_("Search %s"), $rawtitle)) . Horde::img('search.png', _("Search"), '', $graphicsdir) . '</a>');
         if (!$readonly) {
-            $hdr_template->set('empty', Horde::link(Util::addParameter($mailbox_imp_url, array('actionID' => 'empty_mailbox', 'mailbox' => $imp_mbox['mailbox'], 'mailbox_token' => $mailbox_token)), _("Empty folder"), '', '', "ImpMessage.confirmDialog(this.href, '" . addslashes(_("Are you sure you wish to delete all mail in this folder?")) . "'); return false;") . Horde::img('empty_spam.png', _("Empty folder")) . '</a>');
+            $hdr_template->set('empty', Horde::link(Util::addParameter($mailbox_imp_url, array('actionID' => 'empty_mailbox', 'mailbox' => $imp_mbox['mailbox'], 'mailbox_token' => $mailbox_token)), _("Empty folder"), '', '', "ImpMailbox.confirmDialog(this.href, '" . addslashes(_("Are you sure you wish to delete all mail in this folder?")) . "'); return false;") . Horde::img('empty_spam.png', _("Empty folder")) . '</a>');
         }
     } else {
         if ($imp_search->isEditableVFolder()) {
@@ -503,6 +489,8 @@ if (empty($pageOb['end'])) {
     exit;
 }
 
+$imp_flags = &IMP_Imap_Flags::singleton();
+
 /* Display the navbar and actions if there is at least 1 message in mailbox. */
 if ($pageOb['msgcount']) {
     $use_trash = $prefs->getValue('use_trash');
@@ -515,21 +503,37 @@ if ($pageOb['msgcount']) {
     $n_template->set('use_folders', $conf['user']['allow_folders']);
     $n_template->set('readonly', $readonly);
     $n_template->set('use_pop', $_SESSION['imp']['protocol'] == 'pop');
-    $n_template->set('use_trash', $use_trash);
-    $n_template->set('imp_all', IMP::FLAG_ALL);
-    $n_template->set('imp_unseen', IMP::FLAG_UNSEEN);
-    $n_template->set('imp_flagged', IMP::FLAG_FLAGGED);
-    $n_template->set('imp_answered', IMP::FLAG_ANSWERED);
-    $n_template->set('imp_deleted', IMP::FLAG_DELETED);
-    $n_template->set('imp_draft', IMP::FLAG_DRAFT);
-    $n_template->set('imp_personal', IMP::FLAG_PERSONAL);
-    $n_template->set('imp_forwarded', IMP::FLAG_FORWARDED);
-    if ($n_template->get('use_folders')) {
-        $n_template->set('move', Horde::widget('#', _("Move to folder"), 'widget moveAction', '', '', _("Move"), true));
-        $n_template->set('copy', Horde::widget('#', _("Copy to folder"), 'widget copyAction', '', '', _("Copy"), true));
-        $n_template->set('folder_options', $folder_options);
 
+    if (!$n_template->get('use_pop')) {
+        $flaglist_set = $flaglist_unset = array();
+        foreach ($imp_flags->getList(array('imap' => true, 'mailbox' => $search_mbox ? null : $imp_mbox['mailbox'])) as $val) {
+            $tmp = array(
+                'f' => $val['flag'],
+                'l' => $val['l']
+            );
+
+            /* These entries are 'opposite' flag actions. */
+            if (isset($val['n'])) {
+                $flaglist_unset[] = $tmp;
+                $tmp['f'] = '0' . $val['flag'];
+                $flaglist_set[] = $tmp;
+            } else {
+                $flaglist_set[] = $tmp;
+                $tmp['f'] = '0' . $val['flag'];
+                $flaglist_unset[] = $tmp;
+            }
+        }
+
+        $n_template->set('flaglist_set', $flaglist_set);
+        $n_template->set('flaglist_unset', $flaglist_unset);
+
+        if ($n_template->get('use_folders')) {
+            $n_template->set('move', Horde::widget('#', _("Move to folder"), 'widget moveAction', '', '', _("Move"), true));
+            $n_template->set('copy', Horde::widget('#', _("Copy to folder"), 'widget copyAction', '', '', _("Copy"), true));
+            $n_template->set('folder_options', $folder_options);
+        }
     }
+
     $n_template->set('mailbox_url', $mailbox_url);
     $n_template->set('mailbox', htmlspecialchars($imp_mbox['mailbox']));
     if ($pageOb['pagecount'] > 1) {
@@ -708,18 +712,18 @@ if ($pageOb['msgcount']) {
     }
 }
 
-/* Cache some repetitively used variables. */
+/* Initialize repetitively used variables. */
 $fromlinkstyle = $prefs->getValue('from_link');
 $imp_ui = new IMP_UI_Mailbox($imp_mbox['mailbox']);
 
 /* Display message information. */
-$ids = $msgs = array();
+$msgs = array();
 $search_template = null;
 while (list(,$ob) = each($mbox_info['overview'])) {
     if ($search_mbox) {
         if (empty($lastMbox) || ($ob['mailbox'] != $lastMbox)) {
             if (!empty($lastMbox)) {
-                _outputSummaries($msgs, $lastMbox, $ids);
+                _outputSummaries($msgs, $lastMbox);
                 $msgs = array();
             }
             $folder_link = Horde::url(Util::addParameter('mailbox.php', 'mailbox', $ob['mailbox']));
@@ -741,15 +745,16 @@ while (list(,$ob) = each($mbox_info['overview'])) {
 
     $lastMbox = $ob['mailbox'];
 
-    /* Initialize the header fields. */
+    /* Initialize the data fields. */
     $msg = array(
         'bg' => '',
+        'class' => '',
         'color' => '',
         'date' => htmlspecialchars($imp_ui->getDate($ob['envelope']['date'])),
         'number' => $ob['seq'],
         'preview' => '',
-        'size' => htmlspecialchars($imp_ui->getSize($ob['size'])),
         'status' => '',
+        'size' => htmlspecialchars($imp_ui->getSize($ob['size'])),
         'uid' => htmlspecialchars($ob['uid'] . IMP::IDX_SEP . $ob['mailbox']),
     );
 
@@ -762,58 +767,34 @@ while (list(,$ob) = each($mbox_info['overview'])) {
     $target = IMP::generateIMPUrl('message.php', $imp_mbox['mailbox'], $ob['uid'], $ob['mailbox']);
 
     /* Get all the flag information. */
-    $bg = array();
-    $flagbits = 0;
-
-    $to_ob = Horde_Mime_Address::getAddressesFromObject($ob['envelope']['to']);
-    if (!empty($to_ob) && $identity->hasAddress($to_ob[0]['inner'])) {
-        $msg['status'] .= Horde::img('mail_personal.png', _("Personal"), array('title' => _("Personal")));
-        $flagbits |= IMP::FLAG_PERSONAL;
+    if (!empty($GLOBALS['conf']['hooks']['msglist_flags'])) {
+        $ob['flags'] = array_merge($ob['flags'], Horde::callHook('_imp_hook_msglist_flags', array($ob, 'imp'), 'imp'));
     }
 
-    if ($_SESSION['imp']['protocol'] != 'pop') {
-        if (!in_array('\\seen', $ob['flags'])) {
-            $flagbits |= IMP::FLAG_UNSEEN;
-            $msg['status'] .= Horde::img('mail_unseen.png', _("Unseen"), array('title' => _("Unseen")));
-            $bg[] = 'unseen';
-        } else {
-            $bg[] = 'seen';
-        }
-        if (in_array('\\answered', $ob['flags'])) {
-            $flagbits |= IMP::FLAG_ANSWERED;
-            $msg['status'] .= Horde::img('mail_answered.png', _("Answered"), array('title' => _("Answered")));
-            $bg[] = 'answered';
-        }
-        if (in_array('\\draft', $ob['flags'])) {
-            $flagbits |= IMP::FLAG_DRAFT;
-            $msg['status'] .= Horde::img('mail_draft.png', _("Draft"), array('title' => _("Draft")));
-            $target = IMP::composeLink(array(), array('actionID' => 'draft', 'thismailbox' => $ob['mailbox'], 'index' => $ob['uid']));
-        }
-        if (in_array('\\flagged', $ob['flags'])) {
-            $flagbits |= IMP::FLAG_FLAGGED;
-            $msg['status'] .= Horde::img('mail_flagged.png', _("Flagged For Followup"), array('title' => _("Flagged For Followup")));
-            $bg[] = 'flagged';
-        }
-        if (in_array('\\deleted', $ob['flags'])) {
-            $flagbits |= IMP::FLAG_DELETED;
-            $msg['status'] .= Horde::img('mail_deleted.png', _("Deleted"), array('title' => _("Deleted")));
-            $bg[] = 'deleted';
-        }
+    $flag_parse = $imp_flags->parse(array(
+        'atc' => isset($ob['structure']) ? $ob['structure'] : null,
+        'div' => true,
+        'flags' => $ob['flags'],
+        'personal' => Horde_Mime_Address::getAddressesFromObject($ob['envelope']['to']),
+        'priority' => $ob['headers']->getValue('x-priority')
+    ));
 
-        /* Support for the pseudo-standard '$Forwarded' flag. */
-        if (in_array('$forwarded', $ob['flags'])) {
-            $flagbits |= IMP::FLAG_FORWARDED;
-            $msg['status'] .= Horde::img('mail_forwarded.png', _("Forwarded"), array('title' => _("Forwarded")));
-            $bg[] = 'forwarded';
+    foreach ($flag_parse as $val) {
+        if (isset($val['div'])) {
+            $msg['status'] .= $val['div'];
+        }
+        if (isset($val['classname'])) {
+            $msg['class'] = $val['classname'];
         }
+        $msg['bg'] = $val['bg'];
     }
 
-    $ids[$msg['id']] = $flagbits;
-    $msg['bg'] = implode(' ', $bg);
-
     /* Show colors for fetchmail messages? */
-    if (!empty($ob['headers']['x-color'])) {
-        $msg['color'] = htmlspecialchars($color);
+    if ($fetchmail) {
+        $color = $ob['headers']->getValue('x-color');
+        if ($color) {
+            $msg['color'] = htmlspecialchars($color);
+        }
     }
 
     /* Show message preview? */
@@ -862,9 +843,9 @@ while (list(,$ob) = each($mbox_info['overview'])) {
     /* Format the Subject: Header. */
     $msg['subject'] = $imp_ui->getSubject($ob['envelope']['subject'], true);
     if ($preview_tooltip) {
-        $msg['subject'] = substr(Horde::linkTooltip($target, $msg['preview'], '', '', '', $msg['preview']), 0, -1) . ' id="subject' . $msg['id'] . '">' . $msg['subject'] . '</a>';
+        $msg['subject'] = substr(Horde::linkTooltip($target, $msg['preview'], '', '', '', $msg['preview']), 0, -1) . ' class="mboxSubject">' . $msg['subject'] . '</a>';
     } else {
-        $msg['subject'] = substr(Horde::link($target, $msg['preview']), 0, -1) . ' id="subject' . $msg['id'] . '">' . $msg['subject'] . '</a>' . (!empty($msg['preview']) ? '<br /><small>' . $msg['preview'] . '</small>' : '');
+        $msg['subject'] = substr(Horde::link($target, $msg['preview']), 0, -1) . ' class="mboxSubject">' . $msg['subject'] . '</a>' . (!empty($msg['preview']) ? '<br /><small>' . $msg['preview'] . '</small>' : '');
     }
 
     /* Set up threading tree now. */
@@ -877,7 +858,7 @@ while (list(,$ob) = each($mbox_info['overview'])) {
     $msgs[$ob['uid']] = $msg;
 }
 
-_outputSummaries($msgs, $lastMbox, $ids);
+_outputSummaries($msgs, $lastMbox);
 
 /* Prepare the message footers template. */
 $mf_template = new IMP_Template();
@@ -894,9 +875,8 @@ if (($pageOb['end'] - $pageOb['begin']) >= 20) {
 }
 
 IMP::addInlineScript(array(
-    'ImpMessage.messagelist = ' . Horde_Serialize::serialize($ids, Horde_Serialize::JSON, NLS::getCharset()),
-    'ImpMessage.sortlimit = ' . intval($sortpref['limit']),
-    'ImpMessage.unread = ' . strval($unread)
+    'ImpMailbox.sortlimit = ' . intval($sortpref['limit']),
+    'ImpMailbox.unread = ' . strval($unread)
 ));
 
 require $registry->get('templates', 'horde') . '/common-footer.inc';
index bf7f027..beedafa 100644 (file)
@@ -108,7 +108,7 @@ try {
 
 $envelope = $fetch_ret[$index]['envelope'];
 $flags = $flags_ret[$index]['flags'];
-$mime_headers = $fetch_ret[$index]['headertext'][0];
+$mime_headers = reset($fetch_ret[$index]['headertext']);
 $use_pop = ($_SESSION['imp']['protocol'] == 'pop');
 
 /* Parse the message. */
@@ -206,35 +206,24 @@ case 'low':
 
 /* Set the status information of the message. */
 $status = '';
-$identity = null;
-if (!$use_pop) {
-    if (!empty($msgAddresses)) {
-        $identity = $user_identity->getMatchingIdentity($msgAddresses);
-        if (!is_null($identity) ||
-            ($user_identity->getMatchingIdentity($msgAddresses, false) !== null)) {
-            $status .= '+';
-        }
-        if (is_null($identity)) {
-            $identity = $user_identity->getDefault();
-        }
-    }
+$match_identity = $identity = null;
 
-    /* Set status flags. */
-    if (!in_array('\\seen', $flags)) {
-        $status .= 'N';
+if (!empty($msgAddresses)) {
+    $match_identity = $identity = $user_identity->getMatchingIdentity($msgAddresses);
+    if (is_null($identity)) {
+        $identity = $user_identity->getDefault();
     }
-    $flag_array = array(
-        '\\answered' => 'r',
-        '\\draft' => 'D',
-        '\\flagged' => '!',
-        '\\deleted' => 'd',
-        /* Support for the pseudo-standard '$Forwarded' flag. */
-        '$forwarded' => 'F'
-    );
-    foreach ($flag_array as $flag => $desc) {
-        if (in_array($flag, $flags)) {
-            $status .= $desc;
-        }
+}
+
+$imp_flags = &IMP_Imap_Flags::singleton();
+$flag_parse = $imp_flags->parse(array(
+    'flags' => $flags,
+    'personal' => $match_identity
+));
+
+foreach ($flag_parse as $val) {
+    if (isset($val['abbrev'])) {
+        $status .= $val['abbrev'];
     }
 }
 
index 337596a..76778d3 100644 (file)
@@ -146,12 +146,11 @@ case 'notspam_report':
 
 case 'flag_message':
     $flag = Util::getFormData('flag');
-    if ($flag) {
+    if ($flag && !empty($indices_array)) {
+        $set = true;
         if ($flag[0] == '0') {
             $flag = substr($flag, 1);
             $set = false;
-        } else {
-            $set = true;
         }
         $imp_message->flag(array($flag), $indices_array, $set);
         if ($prefs->getValue('mailbox_return')) {
@@ -301,12 +300,12 @@ $xpriority = $mime_headers->getValue('x-priority');
 switch ($imp_ui->getXpriority($xpriority)) {
 case 'high':
     $basic_headers['priority'] = _("Priority");
-    $display_headers['priority'] = Horde::img('mail_priority_high.png', _("High Priority")) . '&nbsp;' . $xpriority;
+    $display_headers['priority'] = '<div class="msgflags flagHighpriority" title="' . htmlspecialchars(_("High Priority")) . '"></div>' . '&nbsp;' . $xpriority;
     break;
 
 case 'low':
     $basic_headers['priority'] = _("Priority");
-    $display_headers['priority'] = Horde::img('mail_priority_low.png', _("Low Priority")) . '&nbsp;' . $xpriority;
+    $display_headers['priority'] = '<div class="msgflags flagLowpriority" title="' . htmlspecialchars(_("Low Priority")) . '"></div>' . '&nbsp;' . $xpriority;
     break;
 }
 
@@ -394,35 +393,25 @@ if (!IMP::$printMode && !empty($conf['maillog']['use_maillog'])) {
 /* Everything below here is related to preparing the output. */
 if (!IMP::$printMode) {
     /* Set the status information of the message. */
-    $identity = $status = null;
-    if (!$use_pop) {
-        if (!empty($msgAddresses)) {
-            $identity = $user_identity->getMatchingIdentity($msgAddresses);
-            if (($identity !== null) ||
-                $user_identity->getMatchingIdentity($msgAddresses, false) !== null) {
-                $status .= Horde::img('mail_personal.png', _("Personal"), array('title' => _("Personal")));
-            }
-            if ($identity === null) {
-                $identity = $user_identity->getDefault();
-            }
-        }
+    $identity = $match_identity = $status = null;
 
-        /* Set status flags. */
-        if (!in_array('\\seen', $flags)) {
-            $status .= Horde::img('mail_unseen.png', _("Unseen"), array('title' => _("Unseen")));
+    if (!empty($msgAddresses)) {
+        $identity = $match_identity = $user_identity->getMatchingIdentity($msgAddresses);
+        if (is_null($identity)) {
+            $identity = $user_identity->getDefault();
         }
-        $flag_array = array(
-            '\\answered' => _("Answered"),
-            '\\draft'    => _("Draft"),
-            '\\flagged'  => _("Flagged For Followup"),
-            '\\deleted'  => _("Deleted"),
-            /* Support for the pseudo-standard '$Forwarded' flag. */
-            '$forwarded' => _("Forwarded")
-        );
-        foreach ($flag_array as $flag => $desc) {
-            if (in_array($flag, $flags)) {
-                $status .= Horde::img('mail_' . ltrim($flag, '\\$') . '.png', $desc, array('title' => $desc));
-            }
+    }
+
+    $imp_flags = &IMP_Imap_Flags::singleton();
+    $flag_parse = $imp_flags->parse(array(
+        'div' => true,
+        'flags' => $flags,
+        'personal' => $match_identity
+    ));
+
+    foreach ($flag_parse as $val) {
+        if (isset($val['div'])) {
+            $status .= $val['div'];
         }
     }
 
@@ -454,10 +443,14 @@ if (!IMP::$printMode) {
     $n_template->set('usepop', $use_pop);
     $n_template->set('id', 1);
 
-    if ($conf['user']['allow_folders']) {
-        $n_template->set('move', Horde::widget('#', _("Move to folder"), 'widget moveAction', '', '', _("Move"), true));
-        $n_template->set('copy', Horde::widget('#', _("Copy to folder"), 'widget copyAction', '', '', _("Copy"), true));
-        $n_template->set('options', IMP::flistSelect(array('heading' => _("This message to"), 'new_folder' => true, 'inc_tasklists' => true, 'inc_notepads' => true)));
+    if (!$use_pop) {
+        $n_template->set('flaglist', $imp_flags->getList(array('imap' => true, 'mailbox' => $imp_mbox['mailbox'])));
+
+        if ($conf['user']['allow_folders']) {
+            $n_template->set('move', Horde::widget('#', _("Move to folder"), 'widget moveAction', '', '', _("Move"), true));
+            $n_template->set('copy', Horde::widget('#', _("Copy to folder"), 'widget copyAction', '', '', _("Copy"), true));
+            $n_template->set('options', IMP::flistSelect(array('heading' => _("This message to"), 'new_folder' => true, 'inc_tasklists' => true, 'inc_notepads' => true)));
+        }
     }
 
     $n_template->set('back_to', Horde::widget($mailbox_url, sprintf(_("Back to %s"), $h_page_label), 'widget', '', '', sprintf(_("Bac_k to %s"), $h_page_label), true));
@@ -747,7 +740,6 @@ if (IMP::$printMode) {
 echo $m_template->fetch(IMP_TEMPLATES . '/message/message.html');
 
 if (!IMP::$printMode) {
-    echo '<input type="hidden" name="flag" id="flag" value="" />';
     $a_template->set('isbottom', true);
     echo $a_template->fetch(IMP_TEMPLATES . '/message/navbar_actions.html');
 
index e9b15d7..0f1825c 100644 (file)
@@ -62,7 +62,7 @@ $ids = $imp_search->runSearchQuery($query, $mailbox, Horde_Imap_Client::SORT_DAT
 if (!empty($ids)) {
     $imp_ui = new IMP_UI_Mailbox($imp_mbox['mailbox']);
 
-    $overview = $imp_mailbox->getMailboxArray(array_slice($ids, 0, 20), $conf['mailbox']['show_preview'] && $prefs->getValue('preview_enabled'));
+    $overview = $imp_mailbox->getMailboxArray(array_slice($ids, 0, 20), array('preview' => $conf['mailbox']['show_preview'] && $prefs->getValue('preview_enabled')));
 
     foreach ($overview['overview'] as $ob) {
         $from_addr = $imp_ui->getFrom($ob['envelope'], array('fullfrom' => true));
index 3b1f90a..04cdd1d 100644 (file)
@@ -17,6 +17,10 @@ $sidebar_width = max((int)$prefs->getValue('sidebar_width') - 50, 150) . 'px';
 // Quota information
 $show_quota = (isset($_SESSION['imp']['quota']) && is_array($_SESSION['imp']['quota']));
 
+// Get the list of available IMAP flags
+$imp_flags = &IMP_Imap_Flags::singleton();
+$flag_list = $imp_flags->getList(array('imap' => true));
+
 // Small utility functions to simplify creating dimpactions buttons.
 // As of right now, we don't show text only links.
 function _createDA($text, $image, $id = null, $class = '', $show_text = true)
@@ -354,12 +358,15 @@ function _simpleButton($id, $text, $image, $imagedir = null)
 <div class="context" id="ctx_message" style="display:none">
  <a id="ctx_message_reply"><span class="contextImg"></span><?php echo _("Reply") ?></a>
  <a id="ctx_message_forward" class="sep"><span class="contextImg"></span><?php echo _("Forward") ?></a>
- <a id="ctx_message_setflag" class="sep"><span class="contextImg"></span><?php echo _("Mark as") ?></a>
+<?php if (!empty($flag_list)): ?>
+ <a id="ctx_message_setflag"><span class="contextImg"></span><?php echo _("Mark as") ?></a>
+ <a id="ctx_message_unsetflag" class="sep"><span class="contextImg"></span><?php echo _("Unmark as") ?></a>
+<?php endif; ?>
 <?php if (!empty($conf['spam']['reporting'])): ?>
- <a id="ctx_message_spam"><span class="contextImg"></span><?php echo _("Mark as Spam") ?></a>
+ <a id="ctx_message_spam"><span class="contextImg"></span><?php echo _("Report as Spam") ?></a>
 <?php endif; ?>
 <?php if (!empty($conf['notspam']['reporting'])): ?>
- <a id="ctx_message_ham"><span class="contextImg"></span><?php echo _("Mark as Innocent") ?></a>
+ <a id="ctx_message_ham"><span class="contextImg"></span><?php echo _("Report as Innocent") ?></a>
 <?php endif; ?>
 <?php if ($has_blacklist): ?>
  <a id="ctx_message_blacklist"><span class="contextImg"></span><?php echo _("Blacklist") ?></a>
@@ -375,7 +382,10 @@ function _simpleButton($id, $text, $image, $imagedir = null)
 
 <div class="context" id="ctx_draft" style="display:none">
  <a id="ctx_draft_resume" class="sep"><span class="contextImg"></span><?php echo _("Resume Draft") ?></a>
- <a id="ctx_draft_setflag" class="sep"><span class="contextImg"></span><?php echo _("Mark as") ?></a>
+<?php if (!empty($flag_list)): ?>
+ <a id="ctx_draft_setflag"><span class="contextImg"></span><?php echo _("Mark as") ?></a>
+ <a id="ctx_draft_unsetflag" class="sep"><span class="contextImg"></span><?php echo _("Unmark as") ?></a>
+<?php endif; ?>
  <a id="ctx_draft_deleted"><span class="contextImg"></span><?php echo _("Delete") ?></a>
 <?php if (!$usetrash): ?>
  <a id="ctx_draft_undeleted"><span class="contextImg"></span><?php echo _("Undelete") ?></a>
@@ -390,7 +400,10 @@ function _simpleButton($id, $text, $image, $imagedir = null)
 
 <div class="context" id="ctx_otheractions" style="display:none">
  <a id="previewtoggle" class="sep"><span class="contextImg"></span><?php echo ($prefs->getValue('dimp_show_preview') ? _("Hide Preview") : _("Show Preview")); ?></a>
- <a id="oa_setflag" class="sep"><span class="contextImg"></span><?php echo _("Mark as") ?></a>
+<?php if (!empty($flag_list)): ?>
+ <a id="oa_setflag"><span class="contextImg"></span><?php echo _("Mark as") ?></a>
+ <a id="oa_unsetflag" class="sep"><span class="contextImg"></span><?php echo _("Unmark as") ?></a>
+<?php endif; ?>
 <?php if ($has_blacklist || $has_whitelist): ?>
 <?php if ($has_blacklist): ?>
  <a id="oa_blacklist"<?php if (!$has_whitelist): ?> class="sep"<?php endif; ?>><span class="contextImg"></span><?php echo _("Blacklist") ?></a>
@@ -406,16 +419,13 @@ function _simpleButton($id, $text, $image, $imagedir = null)
 <?php endif; ?>
 </div>
 
+<?php if (!empty($flag_list)): ?>
 <div class="context" id="ctx_flag" style="display:none">
- <a id="flag_seen"><span class="contextImg"></span><?php echo _("Seen") ?></a>
- <a id="flag_unseen"><span class="contextImg"></span><?php echo _("Unseen") ?></a>
- <a id="flag_flagged"><span class="contextImg"></span><?php echo _("Flagged For Followup") ?></a>
- <a id="flag_clear"><span class="contextImg"></span><?php echo _("Not Flagged") ?></a>
- <a id="flag_answered"><span class="contextImg"></span><?php echo _("Answered") ?></a>
- <a id="flag_unanswered"><span class="contextImg"></span><?php echo _("Not Answered") ?></a>
- <a id="flag_draft"><span class="contextImg"></span><?php echo _("Draft") ?></a>
- <a id="flag_notdraft"><span class="contextImg"></span><?php echo _("Not Draft") ?></a>
+<?php foreach ($flag_list as $key => $val): ?>
+ <a flag="<?php echo htmlspecialchars($key) ?>"><span class="contextImg <?php echo htmlspecialchars($val['c']) ?>"></span><?php echo htmlspecialchars($val['l']) ?></a>
+<?php endforeach; ?>
 </div>
+<?php endif; ?>
 
 <div class="context" id="ctx_contacts" style="display:none">
  <a id="ctx_contacts_new"><span class="contextImg"></span><?php echo _("New Message") ?></a>
index 2ecd666..8cb3b89 100644 (file)
@@ -20,5 +20,4 @@ DimpBase.message_list_template =
  '<div class="msgSubject" title="#{subject}">#{subject}</div>' +
  '<div class="msgDate">#{date}</div>' +
  '<div class="msgSize">#{size}</div>' +
- '<div class="clear">&nbsp;</div>' +
 '</div>';
index 751aa79..93945db 100644 (file)
@@ -11,7 +11,6 @@
 $code = array(
 /* Variables used in core javascript files. */
     'conf' => array(
-        'IMP_ALL' => IMP::FLAG_ALL,
         'isIE' => intval($GLOBALS['browser']->isBrowser('msie')),
         'pop3' => intval((isset($_SESSION['imp']) && ($_SESSION['imp']['protocol'] == 'pop'))),
         'fixed_folders' => empty($GLOBALS['conf']['server']['fixed_folders'])
index 55a04da..bac3885 100644 (file)
@@ -1,7 +1,7 @@
 <loop:messages>
- <tr id="row<tag:messages.id />" class="<tag:messages.bg />">
+ <tr id="row<tag:messages.id />"<if:messages.class> class="<tag:messages.class />"</if:messages.class><if:messages.bg> style="background-color:<tag:messages.bg />"</if:messages.bg>>
   <td>
-   <tag:overflow_begin /><label><input id="check<tag:messages.id />" type="checkbox" class="checkbox" name="indices[]" value="<tag:messages.uid />" /><tag:messages.status />&nbsp;</label><tag:overflow_end />
+   <tag:overflow_begin /><label><input type="checkbox" class="checkbox" name="indices[]" value="<tag:messages.uid />" /><tag:messages.status />&nbsp;</label><tag:overflow_end />
   </td>
   <td<if:messages.color> style="background:<tag:messages.color />"</if:messages.color>>
    <tag:overflow_begin /><tag:messages.number /><tag:overflow_end />
index a75f8a4..dc211fb 100644 (file)
@@ -1,54 +1,29 @@
 <table width="100%" cellspacing="0">
  <tr class="mboxcontrol">
   <td<if:isbottom> class="bottomborder"</if:isbottom>>
+<if:readonly><else:readonly>
+<if:use_pop><else:use_pop>
    <div class="leftFloat">
     <form id="select<tag:id />">
     <input type="hidden" name="mailbox" value="<tag:mailbox />" />
-    <label for="filter<tag:id />" class="hidden"><gettext>Select:</gettext></label>
-    <select id="filter<tag:id />" name="filter">
-     <option value="" selected="selected"><gettext>Select:</gettext></option>
-     <option value="!<tag:imp_all />"><gettext>All</gettext></option>
-     <option value="<tag:imp_all />"><gettext>None</gettext></option>
-     <option value="+<tag:imp_all />"><gettext>Invert</gettext></option>
-     <option value="<tag:imp_personal />"><gettext>Personal</gettext></option>
-     <option value="!<tag:imp_personal />"><gettext>Not Personal</gettext></option>
-<if:use_pop><else:use_pop>
-     <option value="!<tag:imp_unseen />"><gettext>Seen</gettext></option>
-     <option value="<tag:imp_unseen />"><gettext>Unseen</gettext></option>
-     <option value="<tag:imp_flagged />"><gettext>Flagged For Followup</gettext></option>
-     <option value="!<tag:imp_flagged />"><gettext>Not Flagged</gettext></option>
-     <option value="<tag:imp_answered />"><gettext>Answered</gettext></option>
-     <option value="!<tag:imp_answered />"><gettext>Unanswered</gettext></option>
-     <option value="<tag:imp_forwarded />"><gettext>Forwarded</gettext></option>
-     <option value="!<tag:imp_forwarded />"><gettext>Not Forwarded</gettext></option>
-     <option value="<tag:imp_deleted />"><gettext>Deleted</gettext></option>
-     <option value="!<tag:imp_deleted />"><gettext>Not Deleted</gettext></option>
-     <option value="<tag:imp_draft />"><gettext>Draft</gettext></option>
-     <option value="!<tag:imp_draft />"><gettext>Not Draft</gettext></option>
-    </select>
-<if:readonly><else:readonly>
-    <label for="flag<tag:id />" class="hidden"><gettext>Mark as:</gettext></label>
+    <label for="flag<tag:id />" class="hidden"><gettext>Mark Messages</gettext></label>
     <select id="flag<tag:id />" name="flag">
-     <option value="" selected="selected"><gettext>Mark as:</gettext></option>
-     <option value="\seen"><gettext>Seen</gettext></option>
-     <option value="0\seen"><gettext>Unseen</gettext></option>
-     <option value="\flagged"><gettext>Flagged For Followup</gettext></option>
-     <option value="0\flagged"><gettext>Not Flagged</gettext></option>
-     <option value="\answered"><gettext>Answered</gettext></option>
-     <option value="0\answered"><gettext>Unanswered</gettext></option>
-<if:use_trash><else:use_trash>
-     <option value="\deleted"><gettext>Deleted</gettext></option>
-     <option value="0\deleted"><gettext>Not Deleted</gettext></option>
-</else:use_trash></if:use_trash>
-     <option value="\draft"><gettext>Draft</gettext></option>
-     <option value="0\draft"><gettext>Not Draft</gettext></option>
-</else:use_pop></if:use_pop>
+     <option value="" selected="selected"><gettext>Mark Messages</gettext></option>
+     <option value="" disabled="disabled">- - - - - - - -</option>
+     <option value=""><gettext>Mark as:</gettext></option>
+<loop:flaglist_set>
+     <option value="<tag:flaglist_set.f />">&nbsp;&nbsp;<tag:flaglist_set.l /></option>
+</loop:flaglist_set>
+     <option value="" disabled="disabled">- - - - - - - -</option>
+     <option value=""><gettext>Unmark as:</gettext></option>
+<loop:flaglist_unset>
+     <option value="<tag:flaglist_unset.f />">&nbsp;&nbsp;<tag:flaglist_unset.l /></option>
+</loop:flaglist_unset>
     </select>
-</else:readonly></if:readonly>
     </form>
    </div>
+</else:use_pop></if:use_pop>
 <if:use_folders>
-<if:readonly><else:readonly>
    <div class="leftFloat" style="padding-left:10px">
     <form method="post" action="<tag:mailbox_url />">
     <input type="hidden" name="mailbox" value="<tag:mailbox />" />
@@ -62,8 +37,8 @@
     </select>
     </form>
    </div>
-</else:readonly></if:readonly>
 </if:use_folders>
+</else:readonly></if:readonly>
    <div class="rightFloat">
 <if:multiple_page>
     <form method="get" action="<tag:mailbox_url />">
index 4c8943d..c3429c1 100644 (file)
@@ -4,19 +4,24 @@
 <if:readonly><else:readonly>
 <if:usepop><else:usepop>
    <div class="leftFloat">
-    <label for="flag<tag:id />" class="hidden"><gettext>Mark as:</gettext></label>
-    <select id="flag<tag:id />" name="flag<tag:id />">
-     <option value="" selected="selected"><gettext>Mark as:</gettext></option>
-     <option value="0\seen"><gettext>Unseen</gettext></option>
-     <option value="\flagged"><gettext>Flagged For Followup</gettext></option>
-     <option value="0\flagged"><gettext>Not Flagged</gettext></option>
-     <option value="\answered"><gettext>Answered</gettext></option>
-     <option value="0\answered"><gettext>Unanswered</gettext></option>
-     <option value="\draft"><gettext>Draft</gettext></option>
-     <option value="0\draft"><gettext>Not Draft</gettext></option>
+    <form id="select<tag:id />">
+    <input type="hidden" name="mailbox" value="<tag:mailbox />" />
+    <label for="flag<tag:id />" class="hidden"><gettext>Mark Messages</gettext></label>
+    <select id="flag<tag:id />" name="flag">
+     <option value="" selected="selected"><gettext>Mark Messages</gettext></option>
+     <option value="" disabled="disabled">- - - - - - - -</option>
+     <option value=""><gettext>Mark as:</gettext></option>
+<loop:flaglist>
+     <option value="<tag:flaglist.flag />">&nbsp;&nbsp;<tag:flaglist.l /></option>
+</loop:flaglist>
+     <option value="" disabled="disabled">- - - - - - - -</option>
+     <option value=""><gettext>Unmark as:</gettext></option>
+<loop:flaglist>
+     <option value="0<tag:flaglist.flag />">&nbsp;&nbsp;<tag:flaglist.l /></option>
+</loop:flaglist>
     </select>
+    </form>
    </div>
-</else:usepop></if:usepop>
 <if:move>
    <div class="leftFloat msgActions">
     <ul class="msgactions">
@@ -31,6 +36,7 @@
     </select>
    </div>
 </if:move>
+</else:usepop></if:usepop>
 </else:readonly></if:readonly>
    <div class="rightFloat nowrap">
     <tag:back_to />&nbsp;
index 36fc85c..3d76eca 100644 (file)
@@ -1,13 +1,14 @@
 <form method="post" id="messages" name="messages" action="<tag:message_url />">
 <tag:form_input />
-<input type="hidden" id="targetMbox" name="targetMbox" value="" />
-<input type="hidden" id="actionID" name="actionID" value="" />
+<input type="hidden" id="targetMbox" name="targetMbox" />
+<input type="hidden" id="actionID" name="actionID" />
 <input type="hidden" id="message_token" name="message_token" value="<tag:message_token />" />
 <input type="hidden" name="mailbox" value="<tag:mailbox />" />
 <input type="hidden" name="thismailbox" value="<tag:thismailbox />" />
 <input type="hidden" name="start" value="<tag:start />" />
 <input type="hidden" name="index" value="<tag:index />" />
-<input type="hidden" id="newMbox" name="newMbox" value="0" />
+<input type="hidden" id="newMbox" name="newMbox" />
+<input type="hidden" name="flag" />
 <h1 class="header">
  <tag:label /> <span style="unicode-bidi: embed"><tag:msg_count /></span> <tag:status />
 </h1>
diff --git a/imp/templates/prefs/flagmanagement.inc b/imp/templates/prefs/flagmanagement.inc
new file mode 100644 (file)
index 0000000..4a9d375
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+/* Add necessary javascript files. */
+Horde::addScriptFile('prototype.js', 'horde', true);
+Horde::addScriptFile('colorpicker.js', 'horde', true);
+Horde::addScriptFile('flagmanagement.js', 'imp', true);
+
+/* Get the list of flags. */
+$imp_flags = &IMP_Imap_Flags::singleton();
+$flaglist = $imp_flags->getList(array('div' => true, 'fgcolor' => true));
+
+/* Get the graphics. */
+$picker_img = Horde::img('colorpicker.png', _("Color Picker"), '', $registry->getImageDir('horde'));
+$delete_img = Horde::img('delete.png');
+
+IMP::addInlineScript(array(
+    'ImpFlagmanagement.new_prompt = ' . Horde_Serialize::serialize(_("Please enter the label for the new flag:"), Horde_Serialize::JSON, NLS::getCharset())
+));
+
+?>
+<input type="hidden" name="flag_action" id="flag_action" />
+<input type="hidden" name="flag_data" id="flag_data" />
+<input id="new_button" type="button" class="button" value="<?php echo _("New Flag") ?>" />
+<table>
+<?php foreach ($flaglist as $key => $val):
+    $hash = hash('md5', $key);
+    $bgid = 'bg_' . $hash;
+    $color = htmlspecialchars($val['b']);
+    $fgcolor = htmlspecialchars($val['f']);
+    $label = htmlspecialchars($val['l']);
+    $bgstyle = 'background-color:' . $color . ';color:' . $fgcolor;
+?>
+ <tr>
+<?php if ($val['t'] == 'imapp'): ?>
+  <td><input style="<?php echo $bgstyle ?>" name="<?php echo 'label_' . $hash ?>" value="<?php echo $label ?>" /></td>
+<?php else: ?>
+   <td style="<?php echo $bgstyle ?>"><strong><?php echo Horde::label($bgid, $label) ?></strong></td>
+<?php endif; ?>
+  <td>
+<?php if (isset($val['div'])) { echo $val['div']; } ?>
+  </td>
+  <td>
+   <input size="7" style="background:<?php echo $color ?>;color:<?php echo $fgcolor ?>" id="<?php echo $bgid ?>" name="<?php echo $bgid ?>" value="<?php echo $color ?>" />
+   <a class="flagcolorpicker" href="#"><?php echo $picker_img ?></a>
+<?php if (!empty($val['d'])): ?>
+   <a class="flagdelete" href="#"><?php echo $delete_img ?></a>
+<?php endif; ?>
+  </td>
+ </tr>
+<?php endforeach; ?>
+</table>
diff --git a/imp/themes/graphics/mail_notanswered.png b/imp/themes/graphics/mail_notanswered.png
deleted file mode 100644 (file)
index 3e86548..0000000
Binary files a/imp/themes/graphics/mail_notanswered.png and /dev/null differ
diff --git a/imp/themes/graphics/mail_notdraft.png b/imp/themes/graphics/mail_notdraft.png
deleted file mode 100644 (file)
index b539221..0000000
Binary files a/imp/themes/graphics/mail_notdraft.png and /dev/null differ
index 072abbe..3440e19 100644 (file)
@@ -103,11 +103,8 @@ span.loadingImg {
 }
 
 /* Columns */
-div.msgStatus, div.msgFrom, div.msgSubject, div.msgDate, div.msgSize {
-    float: left;
-    overflow: hidden;
-}
 div.msgStatus {
+    float: left;
     width: 8%;
 }
 div.msgStatus div, #msgHeadersContent .subject span {
@@ -120,15 +117,19 @@ div.msgStatus div, #msgHeadersContent .subject span {
     vertical-align: inherit;
 }
 div.msgFrom {
+    float: left;
     width: 20%;
 }
 div.msgSubject {
+    float: left;
     width: 52%;
 }
 div.msgDate {
+    float: left;
     width: 14%;
 }
 div.msgSize {
+    float: left;
     width: 6%;
 }
 
@@ -181,7 +182,7 @@ div.msgSize {
 }
 
 /* Rows */
-.msglist div.msgRow, .msglist div div {
+.msglist div.msgRow, .msglist div.msgRow div {
     cursor: pointer;
     font-size: 95%;
     white-space: nowrap;
@@ -190,33 +191,8 @@ div.msgSize {
     height: 16px;
     margin-bottom: 1px;
 }
-/* Flags */
-.msglist div.unseen {
-    font-weight: bold;
-}
-.msglist div.important {
-    background: #fcc;
-}
-.msglist div.answered {
-    background: #cfc;
-}
-.msglist div.forwarded {
-    background: #add;
-}
-.msglist div.flagged {
-    background: #fcc;
-}
-.msglist div.unimportant {
-    font-weight: normal;
-}
-.msglist div.deletedmsg {
-    background: #999;
-}
-.msglist div.deletedmsg div.msgFrom, .msglist div.deletedmsg div.msgSubject, .msglist div.deletedmsg div.msgDate, .msglist div.deletedmsg div.msgSize {
-    text-decoration: line-through;
-}
 .msglist div.selectedRow {
-    background: #ffc;
+    background: #ffc !important;
 }
 
 /* Checkbox images */
@@ -230,55 +206,18 @@ div.msgSize {
     background-image: url("graphics/checkbox_on.png");
 }
 
-/* Status images and flags selectors */
-.msglist div.statusUnseen {
-    background-image: url("graphics/mail_unseen.png");
-}
-.msglist div.statusFlagged {
-    background-image: url("graphics/mail_flagged.png");
-}
-.msglist div.statusDeleted {
-    background-image: url("graphics/mail_deleted.png");
-}
-.msglist div.statusLowPriority, #msgHeadersContent span.statusLowPriority {
-    background-image: url("graphics/mail_priority_low.png");
-}
-.msglist div.statusHighPriority, #msgHeadersContent span.statusHighPriority {
-    background-image: url("graphics/mail_priority_high.png");
-}
-.msglist div.statusAnswered {
-    background-image: url("graphics/mail_answered.png");
-}
-.msglist div.statusForwarded {
-    background-image: url("graphics/mail_forwarded.png");
-}
-.msglist div.statusDraft {
-    background-image: url("graphics/mail_draft.png");
-}
-.msglist div.statusAttachment {
-    background-image: url("graphics/attachment.png");
-}
-.msglist div.statusEncrypted {
-    background-image: url("graphics/encrypted.png");
-}
-.msglist div.statusSigned {
-    background-image: url("graphics/signed.png");
-}
-
 /* Scroller */
 .sbdiv {
     position: relative;
     right: 0;
     width: 14px;
     overflow: hidden;
-    background-image: url("graphics/scroller_back.png");
-    background-repeat: repeat-y;
+    background: url("graphics/scroller_back.png") repeat-y;
 }
 .sbcursor {
     width: 13px;
     border-left: 1px solid silver;
-    background-image: url("graphics/scroller.png");
-    background-repeat: repeat-y;
+    background: url("graphics/scroller.png") repeat-y;
 }
 .sbup {
     width: 13px;
@@ -1017,30 +956,21 @@ span.dimpactionDrafts {
 #ctx_message_undeleted span.contextImg, #ctx_draft_undeleted span.contextImg, #oa_undeleted span.contextImg {
     background-image: url("graphics/undelete.png");
 }
-#flag_seen span.contextImg, #ctx_folder_seen span.contextImg {
+#ctx_folder_seen span.contextImg {
     background-image: url("graphics/mail_seen.png");
 }
-#flag_unseen span.contextImg, #ctx_folder_unseen span.contextImg, #ctx_folder_poll span.contextImg, #ctx_folder_nopoll span.contextImg {
+#ctx_folder_unseen span.contextImg, #ctx_folder_poll span.contextImg, #ctx_folder_nopoll span.contextImg {
     background-image: url("graphics/mail_unseen.png");
 }
-#ctx_message_setflag span.contextImg, #ctx_draft_setflag span.contextImg, #oa_setflag span.contextImg, #flag_flagged span.contextImg {
+#ctx_message_setflag span.contextImg, #ctx_draft_setflag span.contextImg, #oa_setflag span.contextImg {
     background-image: url("graphics/mail_flagged.png");
 }
-#flag_clear span.contextImg {
+#ctx_message_unsetflag span.contextImg, #ctx_draft_unsetflag span.contextImg, #oa_unsetflag span.contextImg {
     background-image: url("graphics/mail_clearflag.png");
 }
-#flag_answered span.contextImg {
-    background-image: url("graphics/mail_answered.png");
-}
-#flag_unanswered span.contextImg {
-    background-image: url("graphics/mail_notanswered.png");
-}
-#flag_draft span.contextImg, #ctx_draft_resume span.contextImg {
+#ctx_draft_resume span.contextImg {
     background-image: url("graphics/mail_draft.png");
 }
-#flag_notdraft span.contextImg {
-    background-image: url("graphics/mail_notdraft.png");
-}
 #previewtoggle span.contextImg {
     background-image: url("graphics/preview.png");
 }
index 6073307..8e97b6b 100644 (file)
@@ -217,47 +217,68 @@ form#search div {
 .seen {
     background: #fff;
 }
-.seen:hover, .seen-over {
-    background: #f3f3f3;
+
+/* Message flags definitions.
+ * For background color, see config/prefs.php [msgflags preference]. */
+div.msgflags {
+    display: -moz-inline-stack;
+    display: inline-block;
+    padding-right: 2px;
+    width: 16px;
+    height: 16px;
+    background-repeat: no-repeat;
+    vertical-align: inherit;
 }
-.unseen {
-    font-weight: bold;
-    background: #eef;
+.header div.msgflags {
+    vertical-align: sub;
 }
-tr.unseen:hover, tr.unseen-over {
-    font-weight: bold;
-    background: #ccf;
+
+div.msgflags.flagPersonal {
+    background-image: url("graphics/mail_personal.png");
+}
+div.msgflags.flagHighpriority {
+    background-image: url("graphics/mail_priority_high.png");
+}
+tr.flagLowpriority, div.msgRow.flagLowpriority {
+    font-weight: normal !important;
 }
-.answered {
-    background: #cfc;
+div.msgflags.flagLowpriority {
+    background-image: url("graphics/mail_priority_low.png");
 }
-tr.answered:hover, tr.answered-over {
-    background: #9e9;
+div.msgflags.flagSignedmsg {
+    background-image: url("graphics/signed.png");
 }
-.forwarded {
-    background: #add;
+div.msgflags.flagEncryptmsg {
+    background-image: url("graphics/encrypted.png");
 }
-tr.forwarded:hover, tr.forwarded-over {
-    background: #8bd;
+div.msgflags.flagAttachmsg {
+    background-image: url("graphics/attachment.png");
 }
-.important, .flagged {
-    background: #fcc;
+tr.flagUnseen, div.msgRow.flagUnseen {
+    font-weight: bold;
 }
-tr.important:hover, tr.important-over, tr.flagged:hover, tr.flagged-over {
-    background: #faa;
+div.msgflags.flagUnseen, span.contextImg.flagUnseen {
+    background-image: url("graphics/mail_unseen.png");
 }
-.unimportant {
-    font-weight: normal;
+div.msgflags.flagAnswered, span.contextImg.flagAnswered {
+    background-image: url("graphics/mail_answered.png");
 }
-.deleted {
-    background: #999;
+div.msgflags.flagDraft, span.contextImg.flagDraft {
+    background-image: url("graphics/mail_draft.png");
 }
-tr.deleted:hover, tr.deleted-over {
-    background: #777;
+div.msgflags.flagFlagged, span.contextImg.flagFlagged {
+    background-image: url("graphics/mail_flagged.png");
 }
-.deleted * {
+tr.flagDeleted *, div.msgRow.flagDeleted {
     text-decoration: line-through;
 }
+div.msgflags.flagDeleted, span.contextImg.flagDeleted {
+    background-image: url("graphics/mail_deleted.png");
+}
+div.msgflags.flagForwarded, span.contextImg.flagForwarded {
+    background-image: url("graphics/mail_forwarded.png");
+}
+
 .folderunsub {
     background: #bbb;
     font-style: italic;
diff --git a/imp/themes/silver/graphics/mail_notanswered.png b/imp/themes/silver/graphics/mail_notanswered.png
deleted file mode 100644 (file)
index 2277b91..0000000
Binary files a/imp/themes/silver/graphics/mail_notanswered.png and /dev/null differ
diff --git a/imp/themes/silver/graphics/mail_notdraft.png b/imp/themes/silver/graphics/mail_notdraft.png
deleted file mode 100644 (file)
index 03b9b91..0000000
Binary files a/imp/themes/silver/graphics/mail_notdraft.png and /dev/null differ
index d65c3b4..5acfe38 100644 (file)
@@ -1,25 +1,6 @@
-.msglist div.statusDeleted {
-    background-image: url("graphics/mail_deleted.png");
-}
-.msglist div.statusForwarded, #hordeAlerts div.imp-forward, #hordeAlerts div.imp-redirect {
+#hordeAlerts div.imp-forward, #hordeAlerts div.imp-redirect {
     background-image: url("graphics/mail_forwarded.png");
 }
-.msglist div.statusLowPriority {
-    background-image: url("graphics/mail_priority_low.png");
-}
-.msglist div.statusHighPriority {
-    background-image: url("graphics/mail_priority_high.png");
-}
-.msglist div.statusAttachment {
-    background-image: url("graphics/attachment.png");
-}
-.msglist div.statusEncrypted {
-    background-image: url("graphics/encrypted.png");
-}
-.msglist div.statusSigned {
-    background-image: url("graphics/signed.png");
-}
-
 
 #sidebarPanel .base {
     background-image: url("graphics/folder.png");
@@ -119,30 +100,24 @@ span.dimpactionDrafts {
 #ctx_message_undeleted span.contextImg, #ctx_draft_undeleted span.contextImg, #oa_undeleted span.contextImg {
     background-image: url("graphics/undelete.png");
 }
-#flag_seen span.contextImg, #ctx_folder_seen span.contextImg {
+#ctx_folder_seen span.contextImg {
     background-image: url("graphics/mail_seen.png");
 }
-.msglist div.statusUnseen, #flag_unseen span.contextImg, #ctx_folder_unseen span.contextImg, #ctx_folder_poll span.contextImg, #ctx_folder_nopoll span.contextImg {
+#ctx_folder_unseen span.contextImg, #ctx_folder_poll span.contextImg, #ctx_folder_nopoll span.contextImg {
     background-image: url("graphics/mail_unseen.png");
 }
-.msglist div.statusFlagged, #ctx_message_setflag span.contextImg, #ctx_draft_setflag span.contextImg, #oa_setflag span.contextImg, #flag_flagged span.contextImg {
+#ctx_message_setflag span.contextImg, #ctx_draft_setflag span.contextImg, #oa_setflag span.contextImg {
     background-image: url("graphics/mail_flagged.png");
 }
-#flag_clear span.contextImg {
+#ctx_message_unsetflag span.contextImg, #ctx_draft_unsetflag span.contextImg, #oa_unsetflag span.contextImg {
     background-image: url("graphics/mail_clearflag.png");
 }
-.msglist div.statusAnswered, #hordeAlerts div.imp-reply, #flag_answered span.contextImg {
+#hordeAlerts div.imp-reply {
     background-image: url("graphics/mail_answered.png");
 }
-#flag_unanswered span.contextImg {
-    background-image: url("graphics/mail_notanswered.png");
-}
-.msglist div.statusDraft, #flag_draft span.contextImg, #ctx_draft_resume span.contextImg {
+#ctx_draft_resume span.contextImg {
     background-image: url("graphics/mail_draft.png");
 }
-#flag_notdraft span.contextImg {
-    background-image: url("graphics/mail_notdraft.png");
-}
 #previewtoggle span.contextImg {
     background-image: url("graphics/preview.png");
 }
index 8f4b5ab..b451c86 100644 (file)
@@ -20,3 +20,40 @@ div.composebody {
 span.spellcheckPopdownImg {
     background-image: url("graphics/popdown.png");
 }
+
+div.msgflags.flagPersonal {
+    background-image: url("graphics/mail_personal.png");
+}
+div.msgflags.flagHighpriority {
+    background-image: url("graphics/mail_priority_high.png");
+}
+div.msgflags.flagLowpriority {
+    background-image: url("graphics/mail_priority_low.png");
+}
+div.msgflags.flagSignedmsg {
+    background-image: url("graphics/signed.png");
+}
+div.msgflags.flagEncryptmsg {
+    background-image: url("graphics/encrypted.png");
+}
+div.msgflags.flagAttachmsg {
+    background-image: url("graphics/attachment.png");
+}
+div.msgflags.flagUnseen, span.contextImg.flagUnseen {
+    background-image: url("graphics/mail_unseen.png");
+}
+div.msgflags.flagAnswered, span.contextImg.flagAnswered {
+    background-image: url("graphics/mail_answered.png");
+}
+div.msgflags.flagDraft, span.contextImg.flagDraft {
+    background-image: url("graphics/mail_draft.png");
+}
+div.msgflags.flagFlagged, span.contextImg.flagFlagged {
+    background-image: url("graphics/mail_flagged.png");
+}
+div.msgflags.flagDeleted, span.contextImg.flagDeleted {
+    background-image: url("graphics/mail_deleted.png");
+}
+div.msgflags.flagForwarded, span.contextImg.flagForwarded {
+    background-image: url("graphics/mail_forwarded.png");
+}