Skip to content

/ Zope / gocept svn checkins / Archive / 2010 / 2010-02 / SVN: r30566 - webmailer/gocept.restmail/branches/4892-jquery/gocept/restmail

[ << ] [ >> ]

[ SVN: r30532 - gocept.paltrydirectory / Christian ... ] [ SVN: r30586 - webmailer/mytum.webmail/trunk / ... ]

SVN: r30566 - webmailer/gocept.restmail/branches/4892-jquery/gocept/restmail
Thomas Lotze <tl(at)gocept.com>
2010-02-01 17:22:13 [ FULL ]
Author: thomas
Date: Mon Feb  1 17:22:11 2010
New Revision: 30566

Log:
log an error whenever an invalid message sort criterion is requested by the UI


Modified:
   webmailer/gocept.restmail/branches/4892-jquery/gocept/restmail/draft.py
  
webmailer/gocept.restmail/branches/4892-jquery/gocept/restmail/imapaccount.py

Modified:
webmailer/gocept.restmail/branches/4892-jquery/gocept/restmail/draft.py
==============================================================================
---
webmailer/gocept.restmail/branches/4892-jquery/gocept/restmail/draft.py	(original)
+++ webmailer/gocept.restmail/branches/4892-jquery/gocept/restmail/draft.py	Mon
Feb  1 17:22:11 2010
(at)(at) -11,6 +11,7 (at)(at)
 import email.MIMEBase
 import email.Parser
 import email.Utils
+import logging
 import os
 import os.path
 import shutil
(at)(at) -38,6 +39,8 (at)(at)
 
 parser = email.Parser.Parser()
 
+log = logging.getLogger('gocept.restmail')
+
 
 def split(line):
     r"""Split an address line into words and chunks of non-word characters.
(at)(at) -94,6 +97,8 (at)(at)
         if sort_by in ('to', 'subject', 'date'):
             messages = sorted(messages, key=lambda x:getattr(x, sort_by),
                               reverse=sort_dir == 'desc')
+        else:
+            log.error('Invalid sort criterion (drafts): %r' % sort_by)
         return [message for message in messages[i:j]]
 
 

Modified:
webmailer/gocept.restmail/branches/4892-jquery/gocept/restmail/imapaccount.py
==============================================================================
---
webmailer/gocept.restmail/branches/4892-jquery/gocept/restmail/imapaccount.py	(original)
+++
webmailer/gocept.restmail/branches/4892-jquery/gocept/restmail/imapaccount.py	Mon
Feb  1 17:22:11 2010
(at)(at) -15,6 +15,7 (at)(at)
 import gocept.imapapi.folder
 import gocept.imapapi.interfaces
 import gocept.restmail.interfaces
+import logging
 import pytz
 import tempfile
 import transaction
(at)(at) -22,6 +23,9 (at)(at)
 import zope.interface
 
 
+log = logging.getLogger('gocept.restmail')
+
+
 class IMAPAccount(ObjectManager, PropertyManager, Item):
     """A delegate for the gocept.imapapi account object."""
 
(at)(at) -258,6 +262,7 (at)(at)
 
     def filtered_messages(self, sort_by, sort_dir='asc', i=0, j=None):
         if sort_by not in ('from_name', 'subject', 'date'):
+            log.error('Invalid sort criterion (imap): %r' % sort_by)
             sort_by = ''
         messages = self.folder.messages.filtered(sort_by, sort_dir)
         return [Message(message).__of__(self.aq_inner)

SVN: r30567 - webmailer/gocept.restmail/trunk/gocept/restmail
Thomas Lotze <tl(at)gocept.com>
2010-02-01 17:27:57 [ FULL ]
Author: thomas
Date: Mon Feb  1 17:27:56 2010
New Revision: 30567

Log:
log an error whenever an invalid message sort criterion is requested by the UI


Modified:
   webmailer/gocept.restmail/trunk/gocept/restmail/draft.py
   webmailer/gocept.restmail/trunk/gocept/restmail/imapaccount.py

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/draft.py
==============================================================================
--- webmailer/gocept.restmail/trunk/gocept/restmail/draft.py	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/draft.py	Mon Feb  1
17:27:56 2010
(at)(at) -11,6 +11,7 (at)(at)
 import email.MIMEBase
 import email.Parser
 import email.Utils
+import logging
 import os
 import os.path
 import shutil
(at)(at) -38,6 +39,8 (at)(at)
 
 parser = email.Parser.Parser()
 
+log = logging.getLogger('gocept.restmail')
+
 
 def split(line):
     r"""Split an address line into words and chunks of non-word characters.
(at)(at) -94,6 +97,8 (at)(at)
         if sort_by in ('to', 'subject', 'date'):
             messages = sorted(messages, key=lambda x:getattr(x, sort_by),
                               reverse=sort_dir == 'desc')
+        else:
+            log.error('Invalid sort criterion (drafts): %r' % sort_by)
         return [message for message in messages[i:j]]
 
 

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/imapaccount.py
==============================================================================
--- webmailer/gocept.restmail/trunk/gocept/restmail/imapaccount.py	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/imapaccount.py	Mon Feb  1
17:27:56 2010
(at)(at) -15,6 +15,7 (at)(at)
 import gocept.imapapi.folder
 import gocept.imapapi.interfaces
 import gocept.restmail.interfaces
+import logging
 import pytz
 import tempfile
 import transaction
(at)(at) -22,6 +23,9 (at)(at)
 import zope.interface
 
 
+log = logging.getLogger('gocept.restmail')
+
+
 class IMAPAccount(ObjectManager, PropertyManager, Item):
     """A delegate for the gocept.imapapi account object."""
 
(at)(at) -258,6 +262,7 (at)(at)
 
     def filtered_messages(self, sort_by, sort_dir='asc', i=0, j=None):
         if sort_by not in ('from_name', 'subject', 'date'):
+            log.error('Invalid sort criterion (imap): %r' % sort_by)
             sort_by = ''
         messages = self.folder.messages.filtered(sort_by, sort_dir)
         return [Message(message).__of__(self.aq_inner)

SVN: r30568 - webmailer/gocept.imapapi/trunk/gocept/imapapi
Thomas Lotze <tl(at)gocept.com>
2010-02-02 16:53:34 [ FULL ]
Author: thomas
Date: Tue Feb  2 16:53:32 2010
New Revision: 30568

Log:
caught a certain case of an empty IMAP response that could cause a
StopIteration traceback, added some traceback info to track down another


Modified:
   webmailer/gocept.imapapi/trunk/gocept/imapapi/message.py

Modified: webmailer/gocept.imapapi/trunk/gocept/imapapi/message.py
==============================================================================
--- webmailer/gocept.imapapi/trunk/gocept/imapapi/message.py	(original)
+++ webmailer/gocept.imapapi/trunk/gocept/imapapi/message.py	Tue Feb  2
16:53:32 2010
(at)(at) -559,7 +559,9 (at)(at)
         self._store(flag, '-')
 
     def _update(self, data=None):
-        if data is None:
+        if data is None or data == [None]:
+            # None: called without a previous FETCH or STORE
+            # [None]: empty FETCH response
             code, data = self.server.uid('FETCH', self.message.UID, 'FLAGS')
             assert code == 'OK'
             __traceback_info__ = 'Server response: %s, %r' % (code, data)
(at)(at) -591,6 +593,7 (at)(at)
     data_item_req = '(%s)' % data_item_req
     code, data = server.uid('FETCH', msg_uid, data_item_req)
     assert code == 'OK'
+    __traceback_info__ = 'Server response to FETCH uid %s: %s, %r' %
(data_item_req, code, data)
     data = gocept.imapapi.parser.fetch(data)
     return data[data_item_resp]

SVN: r30569 - webmailer/gocept.imapapi/trunk/gocept/imapapi
Thomas Lotze <tl(at)gocept.com>
2010-02-02 17:46:51 [ FULL ]
Author: thomas
Date: Tue Feb  2 17:46:49 2010
New Revision: 30569

Log:
don't call SORT on the IMAP server if the sort criterion is empty


Modified:
   webmailer/gocept.imapapi/trunk/gocept/imapapi/message.py

Modified: webmailer/gocept.imapapi/trunk/gocept/imapapi/message.py
==============================================================================
--- webmailer/gocept.imapapi/trunk/gocept/imapapi/message.py	(original)
+++ webmailer/gocept.imapapi/trunk/gocept/imapapi/message.py	Tue Feb  2
17:46:49 2010
(at)(at) -454,6 +454,10 (at)(at)
         self.container._select()
         if sort_criterion == 'FROM_NAME':
             uids = self._filtered_by_header('FROM', self.from_name, sort_dir)
+        elif not sort_criterion:
+            uids = [gocept.imapapi.parser.fetch(line)['UID']
+                    for line in self._fetch_lines('%s:%s' % (1, len(self)),
+                                                    '(UID)')]
         else:
             uids = self._filtered_by_imap(sort_criterion, sort_dir)
         uids = [self._key(uid) for uid in uids]

SVN: r30570 - webmailer/gocept.imapapi/trunk/gocept/imapapi
Thomas Lotze <tl(at)gocept.com>
2010-02-02 18:24:35 [ FULL ]
Author: thomas
Date: Tue Feb  2 18:24:34 2010
New Revision: 30570

Log:
added a test for sorting on an empty key


Modified:
   webmailer/gocept.imapapi/trunk/gocept/imapapi/folder.txt

Modified: webmailer/gocept.imapapi/trunk/gocept/imapapi/folder.txt
==============================================================================
--- webmailer/gocept.imapapi/trunk/gocept/imapapi/folder.txt	(original)
+++ webmailer/gocept.imapapi/trunk/gocept/imapapi/folder.txt	Tue Feb  2
18:24:34 2010
(at)(at) -188,6 +188,18 (at)(at)
  u'Thomas Lotze <tl(at)gocept.com>',
  u'Zaphod <admin(at)example.com>']
 
+Calling the ``filtered`` method with an empty sort key returns the messages in
+the order they have inside the folder:
+
+>>> messages = INBOX.messages.filtered(sort_by='')
+>>> [m.headers['From'] for m in messages]
+[u'test(at)localhost',
+ u'',
+ u'Zaphod <admin(at)example.com>',
+ u'Christian Zagrodnick <cz(at)gocept.com>',
+ u'Thomas Lotze <tl(at)gocept.com>',
+ u'Thomas Lotze <tl(at)gocept.com>']
+
 
 Appending messages to folders
 =============================

SVN: r30571 - webmailer/gocept.imapapi/trunk/gocept/imapapi
Thomas Lotze <tl(at)gocept.com>
2010-02-02 18:41:18 [ FULL ]
Author: thomas
Date: Tue Feb  2 18:41:17 2010
New Revision: 30571

Log:
when setting or unsetting a flag, no longer fetch new flags from IMAP at all


Modified:
   webmailer/gocept.imapapi/trunk/gocept/imapapi/message.py
   webmailer/gocept.imapapi/trunk/gocept/imapapi/message.txt

Modified: webmailer/gocept.imapapi/trunk/gocept/imapapi/message.py
==============================================================================
--- webmailer/gocept.imapapi/trunk/gocept/imapapi/message.py	(original)
+++ webmailer/gocept.imapapi/trunk/gocept/imapapi/message.py	Tue Feb  2
18:41:17 2010
(at)(at) -576,8 +576,10 (at)(at)
         code, data = self.server.uid(
             'STORE', '%s' % self.message.UID, '%sFLAGS' % sign, '(%s)' % flag)
         assert code == 'OK'
-        __traceback_info__ = 'Server response to STORE uid %sFLAGS (%s): %s,
%r' % (sign, flag, code, data)
-        self._update(data)
+        if sign == '+':
+            self.flags.add(flag)
+        else:
+            self.flags.discard(flag)
 
 
 def _fetch(server, mailbox, msg_uid, data_item, chunk_no=None):

Modified: webmailer/gocept.imapapi/trunk/gocept/imapapi/message.txt
==============================================================================
--- webmailer/gocept.imapapi/trunk/gocept/imapapi/message.txt	(original)
+++ webmailer/gocept.imapapi/trunk/gocept/imapapi/message.txt	Tue Feb  2
18:41:17 2010
(at)(at) -259,7 +259,7 (at)(at)
 
 >>> message.flags.add(r'\Answered')
 >>> message.flags
-flags(['\\Seen', '\\Answered'])
+flags(['\\Answered', '\\Seen'])
 >>> message.server.uid('FETCH', str(message.UID), 'FLAGS')
 ('OK', ['4 (UID ... FLAGS (\\Answered \\Seen))'])

SVN: r30572 - webmailer/gocept.restmail/trunk/gocept/restmail
Thomas Lotze <tl(at)gocept.com>
2010-02-03 07:59:43 [ FULL ]
Author: thomas
Date: Wed Feb  3 07:59:41 2010
New Revision: 30572

Log:
no longer log an error when an invalid sort key is provided


Modified:
   webmailer/gocept.restmail/trunk/gocept/restmail/imapaccount.py

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/imapaccount.py
==============================================================================
--- webmailer/gocept.restmail/trunk/gocept/restmail/imapaccount.py	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/imapaccount.py	Wed Feb  3
07:59:41 2010
(at)(at) -15,7 +15,6 (at)(at)
 import gocept.imapapi.folder
 import gocept.imapapi.interfaces
 import gocept.restmail.interfaces
-import logging
 import pytz
 import tempfile
 import transaction
(at)(at) -23,9 +22,6 (at)(at)
 import zope.interface
 
 
-log = logging.getLogger('gocept.restmail')
-
-
 class IMAPAccount(ObjectManager, PropertyManager, Item):
     """A delegate for the gocept.imapapi account object."""
 
(at)(at) -262,7 +258,6 (at)(at)
 
     def filtered_messages(self, sort_by, sort_dir='asc', i=0, j=None):
         if sort_by not in ('from_name', 'subject', 'date'):
-            log.error('Invalid sort criterion (imap): %r' % sort_by)
             sort_by = ''
         messages = self.folder.messages.filtered(sort_by, sort_dir)
         return [Message(message).__of__(self.aq_inner)

SVN: r30573 - webmailer/gocept.imapapi/trunk/gocept/imapapi
Thomas Lotze <tl(at)gocept.com>
2010-02-03 08:12:34 [ FULL ]
Author: thomas
Date: Wed Feb  3 08:12:33 2010
New Revision: 30573

Log:
when asking the server for a certain header line, interpret a response missing
the corresponding line as indicating an empty value


Modified:
   webmailer/gocept.imapapi/trunk/gocept/imapapi/message.py

Modified: webmailer/gocept.imapapi/trunk/gocept/imapapi/message.py
==============================================================================
--- webmailer/gocept.imapapi/trunk/gocept/imapapi/message.py	(original)
+++ webmailer/gocept.imapapi/trunk/gocept/imapapi/message.py	Wed Feb  3
08:12:33 2010
(at)(at) -478,7 +478,7 (at)(at)
         assert code == 'OK'
         items = gocept.imapapi.parser.fetch(data, fetch_all=True)
         for item in items:
-            lines = item['BODY[HEADER.FIELDS (%s)]' % field]
+            lines = item.get('BODY[HEADER.FIELDS (%s)]' % field, '')
             line = lines.splitlines()[0]
             if line:
                 assert line.upper().startswith(field.upper() + ':'), \

SVN: r30574 - webmailer/gocept.restmail/trunk/gocept/restmail
Thomas Lotze <tl(at)gocept.com>
2010-02-03 08:32:09 [ FULL ]
Author: thomas
Date: Wed Feb  3 08:32:08 2010
New Revision: 30574

Log:
display a raw message even if no sender identity has ever been selected for it,
or its sender identity can no longer be resolved


Modified:
   webmailer/gocept.restmail/trunk/gocept/restmail/draft.py

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/draft.py
==============================================================================
--- webmailer/gocept.restmail/trunk/gocept/restmail/draft.py	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/draft.py	Wed Feb  3
08:32:08 2010
(at)(at) -166,7 +166,10 (at)(at)
 
     def raw(self, headers=None):
         profile = gocept.restmail.interfaces.IProfile(self)
-        identity = profile.get_identity(self.identity)
+        try:
+            identity = profile.get_identity(self.identity)
+        except KeyError:
+            identity = None
 
         # 1. Convert to RFC 822 message
         # XXX We really want to use MIMEText, which unfortunately treats the
(at)(at) -209,7 +212,8 (at)(at)
         else:
             message = text_part
 
-        message['From'] = quote_names(identity.From())
+        if identity:
+            message['From'] = quote_names(identity.From())
         message['To'] = quote_names(self.to)
         if self.cc:
             message['CC'] = quote_names(self.cc)

SVN: r30575 - webmailer/gocept.restmail/trunk/gocept/restmail
Thomas Lotze <tl(at)gocept.com>
2010-02-03 16:57:50 [ FULL ]
Author: thomas
Date: Wed Feb  3 16:57:47 2010
New Revision: 30575

Log:
added traceback info to track down a mysterious XML error


Modified:
   webmailer/gocept.restmail/trunk/gocept/restmail/render.py

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/render.py
==============================================================================
--- webmailer/gocept.restmail/trunk/gocept/restmail/render.py	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/render.py	Wed Feb  3
16:57:47 2010
(at)(at) -312,6 +312,7 (at)(at)
                 pre = lxml.html.Element('pre')
                 block.append(pre)
                 pre.text = ''
+            __traceback_info__ = 'Offending line: %r' % line
             pre.text += line + '\n'
             last_level = level
         body = root.getchildren()[0]

SVN: r30576 - webmailer/gocept.restmail/trunk/gocept/restmail/browser
Thomas Lotze <tl(at)gocept.com>
2010-02-03 17:43:45 [ FULL ]
Author: thomas
Date: Wed Feb  3 17:43:45 2010
New Revision: 30576

Log:
users have been observed to include a port in the host name; strip it as we
never wanted it in the ID and as a : cannot be part of a name


Modified:
   webmailer/gocept.restmail/trunk/gocept/restmail/browser/profile.py

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/browser/profile.py
==============================================================================
---
webmailer/gocept.restmail/trunk/gocept/restmail/browser/profile.py	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/browser/profile.py	Wed Feb 
3 17:43:45 2010
(at)(at) -89,7 +89,8 (at)(at)
             self.context)
         mailhost_id = self.add_mailhost(
             smtp_host, smtp_port, smtp_user, smtp_password)
-        account_id = name_chooser.chooseName('%s-%s' % (user, host), None)
+        account_id = name_chooser.chooseName(
+            '%s-%s' % (user, host.split(':')[0]), None)
         identity_id = self.add_identity(
             name, address, account_id, mailhost_id)
         sent_folder = tuple(unicode(name) for name in sent_folder)
(at)(at) -113,7 +114,7 (at)(at)
         name_chooser = zope.app.container.interfaces.INameChooser(
             self.context)
         mailhost_id = name_chooser.chooseName(
-            '%s-%s' % (smtp_user, smtp_host), None)
+            '%s-%s' % (smtp_user, smtp_host.split(':')[0]), None)
         mailhost = Products.MailHost.MailHost.MailHost(
             id=mailhost_id,
             smtp_host=smtp_host,

SVN: r30577 - webmailer/gocept.imapapi/trunk/gocept/imapapi
Thomas Lotze <tl(at)gocept.com>
2010-02-03 22:59:08 [ FULL ]
Author: thomas
Date: Wed Feb  3 22:59:07 2010
New Revision: 30577

Log:
added debug info


Modified:
   webmailer/gocept.imapapi/trunk/gocept/imapapi/folder.py

Modified: webmailer/gocept.imapapi/trunk/gocept/imapapi/folder.py
==============================================================================
--- webmailer/gocept.imapapi/trunk/gocept/imapapi/folder.py	(original)
+++ webmailer/gocept.imapapi/trunk/gocept/imapapi/folder.py	Wed Feb  3 22:59:07
2010
(at)(at) -118,7 +118,7 (at)(at)
         """
         if self._message_count_cache is None:
             code, data = self.server.status(self.encoded_path, "(MESSAGES)")
-            assert code == 'OK'
+            assert code == 'OK', '%s %r' % (code, data)
             self._message_count_cache = (
                 gocept.imapapi.parser.status(data[0])['MESSAGES'])
         return self._message_count_cache

SVN: r30578 - webmailer/gocept.webmail/trunk/gocept/webmail/browser
Thomas Lotze <tl(at)gocept.com>
2010-02-04 07:28:06 [ FULL ]
Author: thomas
Date: Thu Feb  4 07:28:04 2010
New Revision: 30578

Log:
translated composer template to German, made the label of the save-as-draft
button clearer


Modified:
   webmailer/gocept.webmail/trunk/gocept/webmail/browser/composer.pt

Modified: webmailer/gocept.webmail/trunk/gocept/webmail/browser/composer.pt
==============================================================================
---
webmailer/gocept.webmail/trunk/gocept/webmail/browser/composer.pt	(original)
+++ webmailer/gocept.webmail/trunk/gocept/webmail/browser/composer.pt	Thu Feb 
4 07:28:04 2010
(at)(at) -7,26 +7,26 (at)(at)
     <div metal:fill-slot="layout">
 
       <div class="menubar" id="menubar-composer">
-        <button id="menuitem-send">Send</button>
-        <button id="menuitem-save">Save</button>
+        <button id="menuitem-send">Abschicken</button>
+        <button id="menuitem-save">Entwurf speichern</button>
       </div>
 
       <div id="yui-layout-center">
         <div id="composeBarWrap">
           <ul id="composeBarTabs" class="yui-nav">
-            <li class="selected"><a
href="#tab1">Message</a></li>
-            <li><a id="composeBarTabAttachments"
href="#tab2">Attachments</a></li>
+            <li class="selected"><a
href="#tab1">Nachricht</a></li>
+            <li><a id="composeBarTabAttachments"
href="#tab2">Anh&auml;nge</a></li>
           </ul>
           <div class="yui-content">
             <div>
               <div id="composeAddr">
                 <div id="composeIdentity">
-                  <label for="composeFrom">From:</label>
+                  <label for="composeFrom">Von:</label>
                   <select id="composeFrom" name="composeFrom">
                   </select>
                 </div>
                 <div>
-                  <label for="composeTo">To:</label>
+                  <label for="composeTo">An:</label>
                   <input type="text" name="composeTo" id="composeTo"/>
                 </div>
                 <div>
(at)(at) -38,7 +38,7 (at)(at)
                   <input type="text" name="composeBCC" id="composeBCC"/>
                 </div>
                 <div>
-                  <label for="composeSubject">Subject:</label>
+                  <label for="composeSubject">Betreff:</label>
                   <input type="text" name="composeSubject"
id="composeSubject"/>
                 </div>
               </div>
(at)(at) -48,9 +48,10 (at)(at)
             <div id="attachments">
               <form id="composeAttachments" method="post"
enctype="multipart/form-data">
                 <ul id="composeAttachmentsList"></ul>
-                <strong>Add new attachment:</strong>
+                <strong>Neuen Anhang hinzuf&uuml;gen:</strong>
                 <input type="file" name="file"
id="composeAttachmentsFile"/>
-                <span id="composeAttachmentsUploading" style="display:
none">uploading...</span>
+                <span id="composeAttachmentsUploading"
+                      style="display: none">lade hoch...</span>
               </form>
             </div>
           </div>

SVN: r30579 - webmailer/gocept.restmail/trunk/gocept/restmail
Thomas Lotze <tl(at)gocept.com>
2010-02-08 16:51:17 [ FULL ]
Author: thomas
Date: Mon Feb  8 16:51:14 2010
New Revision: 30579

Log:
added debug info to track down a system error


Modified:
   webmailer/gocept.restmail/trunk/gocept/restmail/render.py

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/render.py
==============================================================================
--- webmailer/gocept.restmail/trunk/gocept/restmail/render.py	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/render.py	Mon Feb  8
16:51:14 2010
(at)(at) -249,6 +249,7 (at)(at)
 
     (at)classmethod
     def parse(cls, text, encoding):
+        __traceback_info__ = 'Encoding: %s' % encoding
         try:
             parser = lxml.html.HTMLParser(encoding=encoding)
         except LookupError:

SVN: r30580 - webmailer/gocept.restmail/trunk/gocept/restmail
Thomas Lotze <tl(at)gocept.com>
2010-02-08 17:42:28 [ FULL ]
Author: thomas
Date: Mon Feb  8 17:42:27 2010
New Revision: 30580

Log:
replace any control chars except tabs and line breaks when putting text inside
an HTML element


Modified:
   webmailer/gocept.restmail/trunk/gocept/restmail/render.py

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/render.py
==============================================================================
--- webmailer/gocept.restmail/trunk/gocept/restmail/render.py	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/render.py	Mon Feb  8
17:42:27 2010
(at)(at) -289,11 +289,13 (at)(at)
 
         Make sure we don't fail if the input text contains a null byte:
 
-        >>> BodyTextPlain.requote(u'asd\x00f').text
-        'asdf\n'
+        >>> BodyTextPlain.requote(u'a\x07s\x0ad\x00f').text
+        'as\ndf\n'
 
         """
-        text = text.replace('\x00', '')
+        for i in xrange(32):
+            if i not in (9, 10, 13):
+                text = text.replace(chr(i), '')
         if not text:
             return
         # XXX try to rewrite in a way that doesn't blow one's mind

SVN: r30581 - webmailer/gocept.restmail/trunk/gocept/restmail/browser
Thomas Lotze <tl(at)gocept.com>
2010-02-08 19:55:39 [ FULL ]
Author: thomas
Date: Mon Feb  8 19:55:38 2010
New Revision: 30581

Log:
make sure folder renaming works with non-ASCII characters this side of the REST
interface


Modified:
   webmailer/gocept.restmail/trunk/gocept/restmail/browser/folder.txt

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/browser/folder.txt
==============================================================================
---
webmailer/gocept.restmail/trunk/gocept/restmail/browser/folder.txt	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/browser/folder.txt	Mon Feb 
8 19:55:38 2010
(at)(at) -97,20 +97,28 (at)(at)
 Rename folders
 --------------
 
+Let's create a folder which we are going to rename. We make a point of using
+non-ASCII characters all over the place:
+
 >>> json_request('http://localhost/profile/test-localhost/(at)(at)create_folder',
-...              name=u'Knuddelwurz')
-{'status': 'OK', 'url': 'http://localhost/profile/test-localhost/+Knuddelwurz'}
+...              name=u'Knuddelw\xfcrz')
+{'status': 'OK', 'url': 'http://localhost/profile/test-localhost/+Knuddelw%C3%BCrz'}
 
 Folders can be renamed:
 
->>> json_request('http://localhost/profile/test-localhost/+Knuddelwurz/(at)(at)rename',
-...              name=u'Wulliknurz')
-{'status': 'OK', 'url': 'http://localhost/profile/test-localhost/+Wulliknurz'}
+>>> json_request('http://localhost/profile/test-localhost/+Knuddelw%C3%BCrz/(at)(at)rename',
+...              name=u'Wullikn\xfcrz')
+{'status': 'OK', 'url': 'http://localhost/profile/test-localhost/+Wullikn%C3%BCrz'}
 >>> json_request(
-...     'http://localhost/profile/test-localhost/+Wulliknurz/(at)(at)has_content')
+...     'http://localhost/profile/test-localhost/+Wullikn%C3%BCrz/(at)(at)has_content')
 {'message': False}
 
-Renaming to a mailbox, which already exists nearby, results into an error:
+>>> json_request('http://localhost/profile/test-localhost/+Wullikn%C3%BCrz/(at)(at)rename',
+...              name=u'Wulliknurz')
+{'status': 'OK', 'url': 'http://localhost/profile/test-localhost/+Wulliknurz'}
+
+Trying to change the name of a folder into one that is already used for a
+folder at the same location results in an error:
 
 >>> json_request('http://localhost/profile/test-localhost/+Testmessages/(at)(at)rename',
 ...              name=u'Bar')

SVN: r30582 - webmailer/gocept.imapapi/trunk/gocept/imapapi
Thomas Lotze <tl(at)gocept.com>
2010-02-09 07:25:22 [ FULL ]
Author: thomas
Date: Tue Feb  9 07:25:20 2010
New Revision: 30582

Log:
fixed a silly bug that caused folder names with an umlaut somewhere after a
dash to be decoded wrong from modified UTF7


Modified:
   webmailer/gocept.imapapi/trunk/gocept/imapapi/folder.py

Modified: webmailer/gocept.imapapi/trunk/gocept/imapapi/folder.py
==============================================================================
--- webmailer/gocept.imapapi/trunk/gocept/imapapi/folder.py	(original)
+++ webmailer/gocept.imapapi/trunk/gocept/imapapi/folder.py	Tue Feb  9 07:25:20
2010
(at)(at) -272,6 +272,9 (at)(at)
     >>> encode_modified_utf7(u'\xe4\xf6\xfc')
     '&AOQA9gD8-'
 
+    >>> encode_modified_utf7(u'as-d\xe4f')
+    'as-d&AOQ-f'
+
     """
     def encode_buffer(buffer):
         return buffer.encode('utf7').replace('/', ',').replace('+', '&')
(at)(at) -305,6 +308,12 (at)(at)
     >>> decode_modified_utf7('\xef')
     u'\\xef'
 
+    Regression: Strings which contain a UTF7-encoded character somewhere after
+    a dash used to be decoded wrong:
+
+    >>> decode_modified_utf7('as-d&AOQ-f')
+    u'as-d\xe4f'
+
     """
     def decode_utf7(buffer):
         return buffer.replace('&', '+').replace(',', '/').decode('utf7')
(at)(at) -320,7 +329,7 (at)(at)
         start = bytes.index('&')
         text += decode_ascii(bytes[:start])
 
-        stop = bytes.index('-') + 1
+        stop = bytes.index('-', start) + 1
         if stop == start + 2:
             text += u'&'
         else:

SVN: r30583 - webmailer/gocept.restmail/trunk/gocept/restmail/browser
Thomas Lotze <tl(at)gocept.com>
2010-02-09 07:29:06 [ FULL ]
Author: thomas
Date: Tue Feb  9 07:29:03 2010
New Revision: 30583

Log:
used even funnier folder names to test renaming as there had been a decoding
bug involving folder names with an umlaut somewhere after a dash


Modified:
   webmailer/gocept.restmail/trunk/gocept/restmail/browser/folder.txt

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/browser/folder.txt
==============================================================================
---
webmailer/gocept.restmail/trunk/gocept/restmail/browser/folder.txt	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/browser/folder.txt	Tue Feb 
9 07:29:03 2010
(at)(at) -107,13 +107,13 (at)(at)
 Folders can be renamed:
 
 >>> json_request('http://localhost/profile/test-localhost/+Knuddelw%C3%BCrz/(at)(at)rename',
-...              name=u'Wullikn\xfcrz')
-{'status': 'OK', 'url': 'http://localhost/profile/test-localhost/+Wullikn%C3%BCrz'}
+...              name=u'Wulli-kn\xfcrz')
+{'status': 'OK', 'url': 'http://localhost/profile/test-localhost/+Wulli-kn%C3%BCrz'}
 >>> json_request(
-...     'http://localhost/profile/test-localhost/+Wullikn%C3%BCrz/(at)(at)has_content')
+...     'http://localhost/profile/test-localhost/+Wulli-kn%C3%BCrz/(at)(at)has_content')
 {'message': False}
 
->>> json_request('http://localhost/profile/test-localhost/+Wullikn%C3%BCrz/(at)(at)rename',
+>>> json_request('http://localhost/profile/test-localhost/+Wulli-kn%C3%BCrz/(at)(at)rename',
 ...              name=u'Wulliknurz')
 {'status': 'OK', 'url': 'http://localhost/profile/test-localhost/+Wulliknurz'}

SVN: r30584 - in webmailer/gocept.restmail/trunk/gocept/restmail: . browser
Thomas Lotze <tl(at)gocept.com>
2010-02-09 08:16:57 [ FULL ]
Author: thomas
Date: Tue Feb  9 08:16:56 2010
New Revision: 30584

Log:
use the date formatter utility instead of applying ISO format to draft dates


Modified:
   webmailer/gocept.restmail/trunk/gocept/restmail/browser/draft.py
  
webmailer/gocept.restmail/trunk/gocept/restmail/browser/draft_snippet_header.pt
   webmailer/gocept.restmail/trunk/gocept/restmail/browser/profile.py
   webmailer/gocept.restmail/trunk/gocept/restmail/draft.py

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/browser/draft.py
==============================================================================
--- webmailer/gocept.restmail/trunk/gocept/restmail/browser/draft.py	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/browser/draft.py	Tue Feb  9
08:16:56 2010
(at)(at) -13,7 +13,7 (at)(at)
     def data(self):
         """Return data of the draft message."""
         return {'identity': self.context.identity,
-                'date': self.context.date.isoformat(' '),
+                'date': self.context.formatted_date,
                 'to': self.context.to,
                 'cc': self.context.cc,
                 'bcc': self.context.bcc,

Modified:
webmailer/gocept.restmail/trunk/gocept/restmail/browser/draft_snippet_header.pt
==============================================================================
---
webmailer/gocept.restmail/trunk/gocept/restmail/browser/draft_snippet_header.pt	(original)
+++
webmailer/gocept.restmail/trunk/gocept/restmail/browser/draft_snippet_header.pt	Tue
Feb  9 08:16:56 2010
(at)(at) -16,7 +16,7 (at)(at)
     </tal:block>
 
     <dt>Date:</dt>
-    <dd tal:content="draft/date"></dd>
+    <dd tal:content="draft/formatted_date"></dd>
 </dl>
 
 <div class="novisclear"></div>

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/browser/profile.py
==============================================================================
---
webmailer/gocept.restmail/trunk/gocept/restmail/browser/profile.py	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/browser/profile.py	Tue Feb 
9 08:16:56 2010
(at)(at) -146,7 +146,7 (at)(at)
                 'url': zope.app.zapi.absoluteURL(message, self.request),
                 'to': message.to,
                 'subject': message.subject,
-                'date': message.date.isoformat(' '),
+                'date': message.formatted_date,
                 } for message in messages]
         data = {
             'messages': messages,
(at)(at) -183,7 +183,7 (at)(at)
                              num_attachments=0), # XXX this should not be 0
                         message.to,
                         message.subject,
-                        message.date.isoformat(' '), # XXX use date utility
+                        message.formatted_date,
                         ],
                     ))
 

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/draft.py
==============================================================================
--- webmailer/gocept.restmail/trunk/gocept/restmail/draft.py	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/draft.py	Tue Feb  9
08:16:56 2010
(at)(at) -290,6 +290,17 (at)(at)
     def add_attachment(self):
         return new_attachment(self)
 
+    (at)property
+    def formatted_date(self):
+        # Try to format the time according to site configuration, fall back to
+        # ISO format.
+        format = zope.component.queryUtility(
+            gocept.restmail.interfaces.IDateFormatter)
+        if format:
+            return format(self.date)
+        else:
+            return self.date.isoformat(' ')
+
 
 def new_draft(container, message=None, forward=False):
     id = str(uuid.uuid1())

SVN: r30585 - webmailer/gocept.restmail/trunk/gocept/restmail/browser
Thomas Lotze <tl(at)gocept.com>
2010-02-09 18:21:53 [ FULL ]
Author: thomas
Date: Tue Feb  9 18:21:52 2010
New Revision: 30585

Log:
validate address lists (to, cc, bcc) syntactically before passing them to the
MailHost


Modified:
   webmailer/gocept.restmail/trunk/gocept/restmail/browser/draft.py
   webmailer/gocept.restmail/trunk/gocept/restmail/browser/tests.py

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/browser/draft.py
==============================================================================
--- webmailer/gocept.restmail/trunk/gocept/restmail/browser/draft.py	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/browser/draft.py	Tue Feb  9
18:21:52 2010
(at)(at) -6,6 +6,53 (at)(at)
 import gocept.restmail.browser.json
 
 
+def generate_addresses(line):
+    r"""Split a text line into email addresses that may come with real names.
+
+    The line is split on commas with anything between pairs of double quotes
+    being considered atomic. Double quotes may be escaped in turn.
+
+    >>> list(generate_addresses(
+    ...     '"foo, bar" <baz(at)example.org>, foo\\", \\"bar
<baz(at)example.com>'))
+    ['"foo, bar" <baz(at)example.org>', ' foo\\"', ' \\"bar
<baz(at)example.com>']
+
+    """
+    inside_quote = False
+    bs = False
+    item = ''
+    for c in line:
+        if c == ',' and not inside_quote:
+            yield item
+            bs = False
+            item = ''
+            continue
+        if c == '"' and not bs:
+            inside_quote = not inside_quote
+        bs = (c == '\\')
+        item += c
+    yield item
+
+
+def validate_address_list(line):
+    r"""Check the syntax of a list of email addresses including real names.
+
+    See RfC 2822, A.1.2.
+
+    >>> validate_address_list(
+    ...     '"foo, bar" <baz(at)example.org>, "foo\\", \\"bar"
<baz(at)example.com>')
+    True
+
+    >>> validate_address_list(
+    ...     '"foo, bar" <baz(at)example.org>, foo\\", \\"bar
<baz(at)example.com>')
+    False
+
+    """
+    for item in generate_addresses(line):
+        if '(at)' not in item:
+            return False
+    return True
+
+
 class WebAPI(object):
     """Web API for drafts."""
 
(at)(at) -40,6 +87,15 (at)(at)
         if not self.context.to:
             return {'status': 'ValidationError',
                     'message': u'Bitte Empfänger angeben.'}
+        if not validate_address_list(self.context.to):
+            return {'status': 'ValidationError',
+                    'message': u'Die Empfängerliste ist fehlerhaft.'}
+        if self.context.cc and not validate_address_list(self.context.cc):
+            return {'status': 'ValidationError',
+                    'message': u'Die Cc-Empfängerliste ist fehlerhaft.'}
+        if self.context.bcc and not validate_address_list(self.context.bcc):
+            return {'status': 'ValidationError',
+                    'message': u'Die BCc-Empfängerliste ist fehlerhaft.'}
         error = self.context.send()
         if error:
             return {'status': 'SentError', 'message': str(error)}

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/browser/tests.py
==============================================================================
--- webmailer/gocept.restmail/trunk/gocept/restmail/browser/tests.py	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/browser/tests.py	Tue Feb  9
18:21:52 2010
(at)(at) -3,6 +3,7 (at)(at)
 # See also LICENSE.txt
 """Test harness for gocept.restmail."""
 
+from zope.testing import doctest
 import cjson
 import gocept.restmail.tests
 import unittest
(at)(at) -120,6 +121,8 (at)(at)
 def test_suite():
     patch_zpublisher_response_for_iteration()
     suite = unittest.TestSuite()
+    suite.addTest(doctest.DocTestSuite(
+        'gocept.restmail.browser.draft'))
     suite.addTest(gocept.restmail.tests.FunctionalDocFileSuite(
         'README.txt',
         'profile.txt',

SVN: r30589 - webmailer/gocept.webmail/trunk/gocept/webmail/browser/resources/styles
Thomas Lotze <tl(at)gocept.com>
2010-02-11 17:16:13 [ FULL ]
Author: thomas
Date: Thu Feb 11 17:16:13 2010
New Revision: 30589

Log:
added some styles for the render-error page


Modified:
  
webmailer/gocept.webmail/trunk/gocept/webmail/browser/resources/styles/webmail.css

Modified:
webmailer/gocept.webmail/trunk/gocept/webmail/browser/resources/styles/webmail.css
==============================================================================
---
webmailer/gocept.webmail/trunk/gocept/webmail/browser/resources/styles/webmail.css	(original)
+++
webmailer/gocept.webmail/trunk/gocept/webmail/browser/resources/styles/webmail.css	Thu
Feb 11 17:16:13 2010
(at)(at) -90,6 +90,33 (at)(at)
     padding: 10px;
 }
 
+.render-error {
+    margin:10%;
+    padding:5%;
+    background-color:white;
+    border:1px solid red;
+}
+
+.render-error h1 {
+    font-size: 200%;
+    font-weight: bold;
+    margin-bottom: 1em;
+}
+
+.render-error table {
+    margin-top: 20px;
+}
+
+.render-error th {
+    text-align: right;
+    padding-right: 0.5em;
+    font-weight: bold;
+}
+
+html.webmailer .yui-layout-bd .render-error pre {
+    padding: 0px;
+}
+
 html.webmailer .yui-layout-bd dl dt {
     width: 7em;
     padding-right: 0.5em;

SVN: r30595 - in webmailer/gocept.restmail/trunk/gocept/restmail: . browser
Thomas Lotze <tl(at)gocept.com>
2010-02-16 00:03:32 [ FULL ]
Author: thomas
Date: Tue Feb 16 00:03:30 2010
New Revision: 30595

Log:
show a gravatar for the from address of a message on display, floating right of
the headers


Added:
   webmailer/gocept.restmail/trunk/gocept/restmail/avatar.py   (contents, props
changed)
Modified:
  
webmailer/gocept.restmail/trunk/gocept/restmail/browser/draft_snippet_header.pt
   webmailer/gocept.restmail/trunk/gocept/restmail/browser/inline.txt
   webmailer/gocept.restmail/trunk/gocept/restmail/browser/snippet_header.pt
   webmailer/gocept.restmail/trunk/gocept/restmail/draft.py
   webmailer/gocept.restmail/trunk/gocept/restmail/render.py

Added: webmailer/gocept.restmail/trunk/gocept/restmail/avatar.py
==============================================================================
--- (empty file)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/avatar.py	Tue Feb 16
00:03:30 2010
(at)(at) -0,0 +1,15 (at)(at)
+# Copyright (c) 2010 gocept gmbh & co. kg
+# See also LICENSE.txt
+
+import md5
+
+
+def avatar(line):
+    if not(line and '(at)' in line):
+        return None
+
+    for split_char in '\t\n\x0b\x0c\r <>()"\'':
+        line = [item for item in line.split(split_char) if '(at)' in item][0]
+
+    return ('https://secure.gravatar.com/avatar/%s.jpg?s=64&d=404&r=pg'
%
+            md5.new(line.lower()).hexdigest())

Modified:
webmailer/gocept.restmail/trunk/gocept/restmail/browser/draft_snippet_header.pt
==============================================================================
---
webmailer/gocept.restmail/trunk/gocept/restmail/browser/draft_snippet_header.pt	(original)
+++
webmailer/gocept.restmail/trunk/gocept/restmail/browser/draft_snippet_header.pt	Tue
Feb 16 00:03:30 2010
(at)(at) -1,3 +1,7 (at)(at)
+<div class="header">
+  <img class="sender-avatar" alt=""
+       tal:condition="context/sender_avatar"
+       tal:attributes="src context/sender_avatar" />
 <dl tal:define="draft context/context">
     <dt>Subject:</dt>
     <dd tal:content="draft/subject"></dd>
(at)(at) -20,3 +24,4 (at)(at)
 </dl>
 
 <div class="novisclear"></div>
+</div>

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/browser/inline.txt
==============================================================================
---
webmailer/gocept.restmail/trunk/gocept/restmail/browser/inline.txt	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/browser/inline.txt	Tue Feb
16 00:03:30 2010
(at)(at) -75,6 +75,7 (at)(at)
 >>> r = render_message(folder, '09 - AppleMail, Simple HTML with
image')
 >>> print r['header']
 <div class="header">
+  <img class="sender-avatar" alt="" src="..." />
   <dl>
         <dt>Subject:</dt>
         <dd>09 - AppleMail, Simple HTML with image</dd>
(at)(at) -116,6 +117,7 (at)(at)
 >>> r = render_message(folder, '10 - Claws, Signed, Text attachment')
 >>> print r['header']
 <div class="header">
+  <img class="sender-avatar" alt="" src="..." />
   <dl>
         <dt>Subject:</dt>
         <dd>10 - Claws, Signed, Text attachment</dd>
(at)(at) -170,6 +172,7 (at)(at)
 >>> r = render_message(folder, '11 - AppleMail, Complex HTML with
many images')
 >>> print r['header']
 <div class="header">
+  <img class="sender-avatar" alt="" src="..." />
   <dl>
         <dt>Subject:</dt>
         <dd>11 - AppleMail, Complex HTML with many images</dd>
(at)(at) -252,6 +255,7 (at)(at)
 >>> r = render_message(folder, '12 - Evolution, multipart/mixed,
inline image, gzip attachment')
 >>> print r['header']
 <div class="header">
+  <img class="sender-avatar" alt="" src="..." />
   <dl>
         <dt>Subject:</dt>
         <dd>12 - Evolution, multipart/mixed, inline image, gzip
attachment</dd>

Modified:
webmailer/gocept.restmail/trunk/gocept/restmail/browser/snippet_header.pt
==============================================================================
---
webmailer/gocept.restmail/trunk/gocept/restmail/browser/snippet_header.pt	(original)
+++
webmailer/gocept.restmail/trunk/gocept/restmail/browser/snippet_header.pt	Tue
Feb 16 00:03:30 2010
(at)(at) -1,4 +1,7 (at)(at)
 <div class="header">
+  <img class="sender-avatar" alt=""
+       tal:condition="context/sender_avatar"
+       tal:attributes="src context/sender_avatar" />
   <dl>
     <tal:block repeat="header python:['Subject', 'From', 'To', 'CC',
'Date']">
       <tal:block tal:define="header_content context/headers/?header |
nothing"

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/draft.py
==============================================================================
--- webmailer/gocept.restmail/trunk/gocept/restmail/draft.py	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/draft.py	Tue Feb 16
00:03:30 2010
(at)(at) -1,4 +1,4 (at)(at)
-# Copyright (c) 2007-2009 gocept gmbh & co. kg
+# Copyright (c) 2007-2010 gocept gmbh & co. kg
 # See also LICENSE.txt
 
 import datetime
(at)(at) -29,6 +29,7 (at)(at)
 import Products.MailHost.interfaces
 
 import gocept.imapapi.interfaces
+import gocept.restmail.avatar
 import gocept.restmail.interfaces
 import gocept.restmail.render
 
(at)(at) -455,6 +456,14 (at)(at)
         super(RenderedDraftMessage, self).__init__(
             context, rendered_body)
 
+        profile = gocept.restmail.interfaces.IProfile(context)
+        try:
+            identity = profile.get_identity(context.identity)
+            self.sender_avatar = gocept.restmail.avatar.avatar(
+                identity.address)
+        except KeyError:
+            self.sender_avatar = None
+
 
 class RenderedAttachment(gocept.restmail.render.RenderedBase):
     """A structured rendition of an attached message body part."""

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/render.py
==============================================================================
--- webmailer/gocept.restmail/trunk/gocept/restmail/render.py	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/render.py	Tue Feb 16
00:03:30 2010
(at)(at) -1,4 +1,4 (at)(at)
-# Copyright (c) 2008-2009 gocept gmbh & co. kg
+# Copyright (c) 2008-2010 gocept gmbh & co. kg
 # See also LICENSE.txt
 
 """Rendering messages"""
(at)(at) -15,6 +15,7 (at)(at)
 
 import gocept.imapapi.interfaces
 import gocept.imapapi.message
+import gocept.restmail.avatar
 import gocept.restmail.interfaces
 import gocept.restmail.imapaccount
 
(at)(at) -112,6 +113,8 (at)(at)
         super(RenderedMessage, self).__init__(context, body)
         self.headers = self.context.headers
         self.body.parent = self
+        self.sender_avatar = gocept.restmail.avatar.avatar(
+            self.headers.get('From'))
 
 
 class RenderedBroken(RenderedInline):

SVN: r30596 - webmailer/gocept.webmail/trunk/gocept/webmail/browser/resources/styles
Thomas Lotze <tl(at)gocept.com>
2010-02-16 00:04:18 [ FULL ]
Author: thomas
Date: Tue Feb 16 00:04:17 2010
New Revision: 30596

Log:
added some styling for sender avatars


Modified:
  
webmailer/gocept.webmail/trunk/gocept/webmail/browser/resources/styles/webmail.css

Modified:
webmailer/gocept.webmail/trunk/gocept/webmail/browser/resources/styles/webmail.css
==============================================================================
---
webmailer/gocept.webmail/trunk/gocept/webmail/browser/resources/styles/webmail.css	(original)
+++
webmailer/gocept.webmail/trunk/gocept/webmail/browser/resources/styles/webmail.css	Tue
Feb 16 00:04:17 2010
(at)(at) -131,6 +131,11 (at)(at)
     padding:1em;
 }
 
+html.webmailer .header .sender-avatar {
+    float: right;
+    padding: 1em;
+}
+
 html.webmailer .yui-layout-bd div.messagepart {
     clear:both;
     background-color: white;

SVN: r30600 - in webmailer/gocept.restmail/trunk: . gocept/restmail/browser
Thomas Lotze <tl(at)gocept.com>
2010-02-16 20:12:24 [ FULL ]
Author: thomas
Date: Tue Feb 16 20:12:22 2010
New Revision: 30600

Log:
re #4932: merged the multiselection features from the 4892-jquery branch


Modified:
   webmailer/gocept.restmail/trunk/   (props changed)
   webmailer/gocept.restmail/trunk/gocept/restmail/browser/configure.zcml
   webmailer/gocept.restmail/trunk/gocept/restmail/browser/message.py
   webmailer/gocept.restmail/trunk/gocept/restmail/browser/message.txt  
(contents, props changed)

Modified:
webmailer/gocept.restmail/trunk/gocept/restmail/browser/configure.zcml
==============================================================================
---
webmailer/gocept.restmail/trunk/gocept/restmail/browser/configure.zcml	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/browser/configure.zcml	Tue
Feb 16 20:12:22 2010
(at)(at) -277,6 +277,18 (at)(at)
       name="clear"
       attribute="clear"
       />
+    <browser:page
+      name="delete_messages"
+      attribute="delete_messages"
+      />
+    <browser:page
+      name="copy_messages"
+      attribute="copy_messages"
+     />
+    <browser:page
+      name="move_messages"
+      attribute="move_messages"
+     />
   </browser:pages>
 
   <browser:pages

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/browser/message.py
==============================================================================
---
webmailer/gocept.restmail/trunk/gocept/restmail/browser/message.py	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/browser/message.py	Tue Feb
16 20:12:22 2010
(at)(at) -100,6 +100,51 (at)(at)
         self.context.delete_messages()
         return {}
 
+    (at)gocept.restmail.browser.json.view
+    def delete_messages(self, message_urls):
+        """Delete messages.
+        """
+        messages = self._get_messages(message_urls)
+        
+        account = gocept.restmail.interfaces.IIMAPAccount(self.context)
+        trash = account.get_function_folder('trash', create=True)
+
+        for message in messages:
+            parent = message.getParentNode()
+            if parent != self.context:
+                continue
+            if self.context != trash:
+                trash.add_message(message)
+            # XXX for some reason self.context.delete_message results
+            # in a TypeError in imapapi, while parent.delete_message DOES
+            # work!
+            parent.delete_message(message)
+        return {}
+
+    (at)gocept.restmail.browser.json.view
+    def copy_messages(self, message_urls):
+        """Copy messages to this folder"""
+        messages = self._get_messages(message_urls)
+        for message in messages:
+            self.context.add_message(message)
+        return {}
+
+    (at)gocept.restmail.browser.json.view
+    def move_messages(self, message_urls):
+        """Move messages to this folder"""
+        messages = self._get_messages(message_urls)
+        for message in messages:
+            previous_container = message.getParentNode()
+            self.context.add_message(message)
+            previous_container.delete_message(message)
+        return {}
+
+    def _get_messages(self, message_urls):
+        """Turn list of submitted message_urls into messages.
+        """
+        return [gocept.restmail.browser.traversal.traverse(
+                url, self.context, self.request) for
+                url in message_urls]
 
 def from_name(value):
     name, addr = email.Utils.parseaddr(value)
(at)(at) -182,6 +227,9 (at)(at)
         folder.add_message(self.context.aq_inner)
         return {}
 
+    # XXX This method is not tested anymore since multiselection made it
+    # obsolete (at least for the time being). It should either be removed or
+    # consciously kept and tested.
     (at)gocept.restmail.browser.json.view
     def move(self, folder_url):
         """Move message to target folder"""

Modified: webmailer/gocept.restmail/trunk/gocept/restmail/browser/message.txt
==============================================================================
---
webmailer/gocept.restmail/trunk/gocept/restmail/browser/message.txt	(original)
+++ webmailer/gocept.restmail/trunk/gocept/restmail/browser/message.txt	Tue Feb
16 20:12:22 2010
(at)(at) -104,10 +104,13 (at)(at)
 Copying and moving messages
 ---------------------------
 
-Messages can be copied and moved using the `(at)(at)copy` and `(at)(at)move`
views,
-respectively. You must pass the url of the target folder. Let's copy a message
-to a folder with a non-ASCII name, copy it back and move it back and forth
-between that folder and another.
+Messages can be copied and moved using the `(at)(at)copy_messages` and
+`(at)(at)move_messages` views, respectively. These views exist on the target
+folder, and you must pass one or more urls of the messages you want to
+copy or move to it.
+
+Let's copy a message to a folder with a non-ASCII name, copy it back
+and move it back and forth between that folder and another.
 
 When copying, the source message will not be deleted:
 
(at)(at) -120,7 +123,8 (at)(at)
 >>> other_messages['total']
 0
 
->>> json_request(messages['messages'][-1]['url']+'/(at)(at)copy',
folder_url=other_url, post=True)
+>>> json_request(other_url + '/(at)(at)copy_messages', 
+...    message_urls=[messages['messages'][-1]['url']])
 {}
 >>> messages = json_request(INBOX_url + '/(at)(at)messages')
 >>> messages['total']
(at)(at) -129,8 +133,8 (at)(at)
 >>> other_messages['total']
 1
 
->>>
json_request(other_messages['messages'][-1]['url']+'/(at)(at)copy',
-...              folder_url=INBOX_url, post=True)
+>>> json_request(INBOX_url + '/(at)(at)copy_messages',
+...    message_urls=[other_messages['messages'][-1]['url']], post=True)
 {}
 >>> messages = json_request(INBOX_url + '/(at)(at)messages')
 >>> messages['total']
(at)(at) -141,8 +145,8 (at)(at)
 
 Moving messages implies the deletion of the source message:
 
->>>
json_request(other_messages['messages'][-1]['url']+'/(at)(at)move',
-...              folder_url=INBOX_url, post=True)
+>>> json_request(INBOX_url + '/(at)(at)move_messages',
+...    message_urls=[other_messages['messages'][-1]['url']], post=True)
 {}
 >>> messages = json_request(INBOX_url + '/(at)(at)messages')
 >>> messages['total']
(at)(at) -151,7 +155,8 (at)(at)
 >>> other_messages['total']
 0
 
->>> json_request(messages['messages'][-1]['url']+'/(at)(at)move',
folder_url=other_url, post=True)
+>>> json_request(other_url + '/(at)(at)move_messages',
+...     message_urls=[messages['messages'][-1]['url']], post=True)
 {}
 >>> messages = json_request(INBOX_url + '/(at)(at)messages')
 >>> messages['total']
(at)(at) -164,27 +169,30 (at)(at)
 Deleting messages
 -----------------
 
-Messages can be deleted using the `(at)(at)delete` view. We demonstrate this
both
-for a folder with more than one message in it and for a folder with a
-non-ASCII name.
+Messages can be deleted using the `(at)(at)delete_messages` view on folders.
We
+demonstrate this both for a folder with more than one message in it
+and for a folder with a non-ASCII name.
 
 Messages that are deleted are moved to the trash, which is empty at first. The
 trash folder is identified as a `function folder` in the account information
 (see above):
 
->>> trash = json_request('http://localhost/profile/test-localhost/+Trash/(at)(at)messages')
+>>> trash_url = 'http://localhost/profile/test-localhost/+Trash'
+>>> trash = json_request(trash_url + '/(at)(at)messages')
 >>> trash['total']
 0
 
 We delete two messages:
 
->>> json_request(messages['messages'][-1]['url']+'/(at)(at)delete',
post=True)
+>>> json_request(INBOX_url + '/(at)(at)delete_messages',
+...     message_urls=[messages['messages'][-1]['url']], post=True)
 {}
 >>> messages = json_request(INBOX_url + '/(at)(at)messages')
 >>> messages['total']
 2
 
->>>
json_request(other_messages['messages'][-1]['url']+'/(at)(at)delete',
post=True)
+>>> json_request(other_url + '/(at)(at)delete_messages',
+...    message_urls=[other_messages['messages'][-1]['url']], post=True)
 {}
 >>> other_messages = json_request(other_url + '/(at)(at)messages')
 >>> other_messages['total']
(at)(at) -192,18 +200,69 (at)(at)
 
 And find them in the trash now:
 
->>> trash = json_request('http://localhost/profile/test-localhost/+Trash/(at)(at)messages')
+>>> trash = json_request(trash_url + '/(at)(at)messages')
 >>> trash['total']
 2
 
 Deleting messages in the trash makes them go away completely:
 
->>> json_request(trash['messages'][0]['url']+'/(at)(at)delete',
post=True)
+>>> json_request(trash_url + '/(at)(at)delete_messages',
+...    message_urls=[trash['messages'][0]['url']], post=True)
 {}
->>> trash = json_request('http://localhost/profile/test-localhost/+Trash/(at)(at)messages')
+>>> trash = json_request(trash_url + '/(at)(at)messages')
 >>> trash['total']
 1
 
+Copying, moving and and deleting multiple messages at once
+----------------------------------------------------------
+
+XXX these tests fail due to some issue in imapapi which in some cases
+raises a TypeError, trying to do addition or substraction with "None".
+
+We can also copy, move and delete multiple messages at the same time.
+
+We copy both messages into the other folder::
+
+>>> messages = json_request(INBOX_url + '/(at)(at)messages')
+>>> messages['total']
+2
+>>> json_request(other_url + '/(at)(at)copy_messages',
+...   message_urls=[message['url'] for message in messages['messages']], 
+...   post=True)
+{}
+>>> other_messages = json_request(other_url + '/(at)(at)messages')
+>>> other_messages['total']
+2
+
+Let's move them back again::
+
+>>> json_request(INBOX_url + '/(at)(at)move_messages',
+...   message_urls=[message['url'] for message in other_messages['messages']],
+...   post=True)
+{}
+>>> other_messages = json_request(other_url + '/(at)(at)messages')
+>>> other_messages['total']
+0
+>>> messages = json_request(INBOX_url + '/(at)(at)messages')
+>>> messages['total']
+4
+
+We remove the last two messages by deleting them (but first, we clear the
+trash folder to make the outcome more obvious)::
+
+>>> json_request(trash_url + '/(at)(at)clear')
+{}
+
+>>> json_request(INBOX_url + '/(at)(at)delete_messages',
+...   message_urls=[message['url'] for message in messages['messages'][-2:]],
+...   post=True)
+{}
+>>> messages = json_request(INBOX_url + '/(at)(at)messages')
+>>> messages['total']
+2
+>>> trash = json_request(trash_url + '/(at)(at)messages')
+>>> trash['total']
+2
 
 Sending messages
 ================

MailBoxer