email.py 6.45 KB
Newer Older
Nicolas Chauvat's avatar
Nicolas Chauvat committed
1
2
3
"""Specific views for email related entities

:organization: Logilab
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
4
:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
Nicolas Chauvat's avatar
Nicolas Chauvat committed
5
6
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
7
8
__docformat__ = "restructuredtext en"
_ = unicode
Nicolas Chauvat's avatar
Nicolas Chauvat committed
9

10
from logilab.mtconverter import xml_escape
Nicolas Chauvat's avatar
Nicolas Chauvat committed
11

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
12
from cubicweb.selectors import implements
Nicolas Chauvat's avatar
Nicolas Chauvat committed
13
14
from cubicweb.common.uilib import soup2xhtml
from cubicweb.common.mixins import TreeViewMixIn
15
from cubicweb.web import uicfg, formwidgets
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
16
from cubicweb.web.views import baseviews, primary
Nicolas Chauvat's avatar
Nicolas Chauvat committed
17

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
18
for rtype in ('sender', 'recipients', 'cc', 'parts'):
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
19
    uicfg.primaryview_section.tag_subject_of(('Email', rtype, '*'), 'hidden')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
20

21
22
uicfg.autoform_field_kwargs.tag_attribute(('Email', 'subject'),
                                          {'widget': formwidgets.TextInput})
23

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
24
25
26
27
28
29
uicfg.actionbox_appearsin_addmenu.tag_subject_of(('Email', 'attachment', '*'),
                                                 True)

uicfg.actionbox_appearsin_addmenu.tag_object_of(('EmailThread', 'forked_from', 'EmailThread'),
                                                True)

30
31
32
33
34
35
36

def formated_sender(email):
    if email.sender:
        return email.sender[0].view('oneline')
    # sender address has been removed, look in email's headers
    message = email.umessage_headers()
    if message:
37
        return xml_escape(message.get('From'))
38
39
    return email.req._('unknown sender')

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
40
41
class EmailPrimaryView(primary.PrimaryView):
    __select__ = implements('Email')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
42

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
43
    def render_entity_attributes(self, entity):
44
45
46
47
48
        self.w(u'<div class="emailheader"><table>')
        self.w(u'<tr><td>%s</td><td>%s</td></tr>' %
               (self.req._('From'), formated_sender(entity)))
        self.w(u'<tr><td>%s</td><td>%s</td></tr>' %
               (self.req._('To'), ', '.join(ea.view('oneline') for ea in entity.recipients)))
Nicolas Chauvat's avatar
Nicolas Chauvat committed
49
        if entity.cc:
50
51
52
53
54
55
56
            self.w(u'<tr><td>%s</td><td>%s</td></tr>' %
                   (self.req._('CC'), ', '.join(ea.view('oneline') for ea in entity.cc)))
        self.w(u'<tr><td>%s</td><td>%s</td></tr>' %
               (self.req._('Date'), self.format_date(entity.date, time=True)))
        self.w(u'<tr><td>%s</td><td>%s</td></tr>' %
               (self.req._('Subject'), xml_escape(entity.subject)))
        self.w(u'</table></div><div class="emailcontent">')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
57
58
59
60
61
        for part in entity.parts_in_order():
            content, mime = part.content, part.content_format
            if mime == 'text/html':
                content = soup2xhtml(content, self.req.encoding)
            elif mime != 'text/xhtml':
62
                content = xml_escape(content)
Nicolas Chauvat's avatar
Nicolas Chauvat committed
63
64
65
66
67
                if mime == 'text/plain':
                    content = content.replace('\n', '<br/>').replace(' ', '&nbsp;')
            # XXX some headers to skip if html ?
            self.w(content)
            self.w(u'<br class="partseparator"/>')
68
        self.w(u'</div>')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
69

70
71
    def render_entity_title(self, entity):
        self.w(u'<h1><span class="etype">%s</span> %s</h1>'
72
               % (entity.dc_type().capitalize(), xml_escape(entity.dc_title())))
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
73
74


Nicolas Chauvat's avatar
Nicolas Chauvat committed
75
76
77
class EmailHeadersView(baseviews.EntityView):
    """display email's headers"""
    id = 'headers'
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
78
    __select__ = implements('Email')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
79
80
81
    title = _('headers')
    templatable = False
    content_type = 'text/plain'
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
82

Nicolas Chauvat's avatar
Nicolas Chauvat committed
83
84
85
86
    def cell_call(self, row, col):
        entity = self.entity(row, col)
        self.w(entity.headers)

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
87
88

class EmailOneLineView(baseviews.OneLineView):
Nicolas Chauvat's avatar
Nicolas Chauvat committed
89
90
91
    """short view usable in the context of the email sender/recipient (in which
    case the caller should specify its context eid) or outside any context
    """
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
92
    __select__ = implements('Email')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
93
    title = _('oneline')
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
94

Nicolas Chauvat's avatar
Nicolas Chauvat committed
95
96
97
    def cell_call(self, row, col, contexteid=None):
        entity = self.entity(row, col)
        self.w(u'<div class="email">')
98
99
100
101
        self.w(u'<i>%s&nbsp;%s</i> ' % (
            self.req._('email_date'), self.format_date(entity.date, time=True)))
        sender = entity.senderaddr
        if sender is None or contexteid != sender.eid:
Nicolas Chauvat's avatar
Nicolas Chauvat committed
102
            self.w(u'<b>%s</b>&nbsp;%s '
103
                   % (self.req._('email_from'), formated_sender(entity)))
Nicolas Chauvat's avatar
Nicolas Chauvat committed
104
105
106
107
        if contexteid not in (r.eid for r in entity.recipients):
            recipients = ', '.join(r.view('oneline') for r in entity.recipients)
            self.w(u'<b>%s</b>&nbsp;%s'
                   % (self.req._('email_to'), recipients))
108
        self.w(u'<br/>\n<a href="%s">%s</a>' % (
109
            xml_escape(entity.absolute_url()), xml_escape(entity.subject)))
Nicolas Chauvat's avatar
Nicolas Chauvat committed
110
111
        self.w(u'</div>')

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
112

Nicolas Chauvat's avatar
Nicolas Chauvat committed
113
114
115
class EmailOutOfContextView(EmailOneLineView):
    """short view outside the context of the email"""
    id = 'outofcontext'
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
116
    title = _('out of context')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
117

118

Nicolas Chauvat's avatar
Nicolas Chauvat committed
119
120
121
122
123
124
125
class EmailInContextView(EmailOneLineView):
    """short view inside the context of the email"""
    id = 'incontext'


class EmailPartOutOfContextView(baseviews.OutOfContextView):
    """out of context an email part is redirecting to related email view"""
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
126
    __select__ = implements('EmailPart')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
127
128
129
    def cell_call(self, row, col):
        entity = self.entity(row, col)
        entity.reverse_parts[0].view('outofcontext', w=self.w)
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
130
131


Nicolas Chauvat's avatar
Nicolas Chauvat committed
132
133
class EmailThreadView(TreeViewMixIn, baseviews.ListView):
    """display email's headers"""
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
134
    __select__ = implements('Email')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
135
136
137
    title = _('thread view')
    item_vid = 'outofcontext'

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
138

139

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
140
class EmailThreadPrimaryView(primary.PrimaryView):
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
141
142
    __select__ = implements('EmailThread')

Nicolas Chauvat's avatar
Nicolas Chauvat committed
143
144
    def cell_call(self, row, col):
        entity = self.complete_entity(row, col)
145
        self.w(u'<h1>%s</h1>' % xml_escape(entity.title))
Nicolas Chauvat's avatar
Nicolas Chauvat committed
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
        # get top level emails in this thread (ie message which are not a reply
        # of a message in this thread)
        #
        # Warn: adding Y in_thread E changes the meaning of the query since it joins
        # with messages which are not a direct reply (eg if A reply_to B, B reply_to C
        # B is also retreived since it's not a reply of C
        #
        # XXX  union with
        #   DISTINCT Any X,D ORDERBY D WHERE X date D, X in_thread E, X reply_to Y,
        #   NOT Y in_thread E, E eid %(x)s'
        # to get message which are a reply of a message in another thread ?
        # we may get duplicates in this case
        rset =  self.req.execute('DISTINCT Any X,D ORDERBY D '
                                 'WHERE X date D, X in_thread E, '
                                 'NOT X reply_to Y, E eid %(x)s',
                                 {'x': entity.eid}, 'x')
        if rset:
            self.w(u'<ul>')
            self.w(u'\n'.join(email.view('tree') for email in rset.entities()))
            self.w(u'</ul>')
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
166
167