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

:organization: Logilab
Nicolas Chauvat's avatar
Nicolas Chauvat committed
4
:copyright: 2003-2013 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

12
from cubicweb.predicates import is_instance
Sylvain Thénault's avatar
Sylvain Thénault committed
13
from cubicweb.uilib import soup2xhtml
14
from cubicweb.view import EntityView
Julien Cristau's avatar
Julien Cristau committed
15
16
from cubicweb.web import formwidgets
from cubicweb.web.views import baseviews, primary, uicfg
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:
Carlos's avatar
Carlos committed
37
        return xml_escape(message.get('From', ''))
38
    return email._cw._('unknown sender')
39

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
40
class EmailPrimaryView(primary.PrimaryView):
Sylvain Thénault's avatar
Sylvain Thénault committed
41
    __select__ = is_instance('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
        self.w(u'<div class="emailheader"><table>')
        self.w(u'<tr><td>%s</td><td>%s</td></tr>' %
46
               (self._cw._('From'), formated_sender(entity)))
47
        self.w(u'<tr><td>%s</td><td>%s</td></tr>' %
48
               (self._cw._('To'), ', '.join(ea.view('oneline') for ea in entity.recipients)))
Nicolas Chauvat's avatar
Nicolas Chauvat committed
49
        if entity.cc:
50
            self.w(u'<tr><td>%s</td><td>%s</td></tr>' %
51
                   (self._cw._('CC'), ', '.join(ea.view('oneline') for ea in entity.cc)))
52
        self.w(u'<tr><td>%s</td><td>%s</td></tr>' %
53
               (self._cw._('Date'), self._cw.format_date(entity.date, time=True)))
54
        self.w(u'<tr><td>%s</td><td>%s</td></tr>' %
55
               (self._cw._('Subject'), xml_escape(entity.subject)))
56
        self.w(u'</table></div><div class="emailcontent">')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
57
58
59
        for part in entity.parts_in_order():
            content, mime = part.content, part.content_format
            if mime == 'text/html':
60
                content = soup2xhtml(content, self._cw.encoding)
Aurelien Campeas's avatar
Aurelien Campeas committed
61
62
            elif 'pgp-signature' in mime:
                content = entity._cw_mtc_transform(content, mime, 'text/html', self._cw.encoding)
Nicolas Chauvat's avatar
Nicolas Chauvat committed
63
            elif mime != 'text/xhtml':
64
                content = xml_escape(content)
Nicolas Chauvat's avatar
Nicolas Chauvat committed
65
66
67
68
69
                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"/>')
70
        self.w(u'</div>')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
71

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


Nicolas Chauvat's avatar
Nicolas Chauvat committed
77
78
class EmailHeadersView(baseviews.EntityView):
    """display email's headers"""
Sylvain Thénault's avatar
Sylvain Thénault committed
79
    __regid__ = 'headers'
Sylvain Thénault's avatar
Sylvain Thénault committed
80
    __select__ = is_instance('Email')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
81
82
83
    title = _('headers')
    templatable = False
    content_type = 'text/plain'
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
84

Nicolas Chauvat's avatar
Nicolas Chauvat committed
85
    def cell_call(self, row, col):
86
        entity = self.cw_rset.get_entity(row, col)
Nicolas Chauvat's avatar
Nicolas Chauvat committed
87
88
        self.w(entity.headers)

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
89
90

class EmailOneLineView(baseviews.OneLineView):
Nicolas Chauvat's avatar
Nicolas Chauvat committed
91
92
93
    """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 Thénault's avatar
Sylvain Thénault committed
94
    __select__ = is_instance('Email')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
95
    title = _('oneline')
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
96

Nicolas Chauvat's avatar
Nicolas Chauvat committed
97
    def cell_call(self, row, col, contexteid=None):
98
        entity = self.cw_rset.get_entity(row, col)
Nicolas Chauvat's avatar
Nicolas Chauvat committed
99
        self.w(u'<div class="email">')
100
        self.w(u'<i>%s&nbsp;%s</i> ' % (
101
            self._cw._('email_date'), self._cw.format_date(entity.date, time=True)))
102
103
        sender = entity.senderaddr
        if sender is None or contexteid != sender.eid:
Nicolas Chauvat's avatar
Nicolas Chauvat committed
104
            self.w(u'<b>%s</b>&nbsp;%s '
105
                   % (self._cw._('email_from'), formated_sender(entity)))
Nicolas Chauvat's avatar
Nicolas Chauvat committed
106
107
108
        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'
109
                   % (self._cw._('email_to'), recipients))
110
        self.w(u'<br/>\n<a href="%s">%s</a>' % (
111
            xml_escape(entity.absolute_url()), xml_escape(entity.subject)))
Nicolas Chauvat's avatar
Nicolas Chauvat committed
112
113
        self.w(u'</div>')

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

Nicolas Chauvat's avatar
Nicolas Chauvat committed
115
116
class EmailOutOfContextView(EmailOneLineView):
    """short view outside the context of the email"""
Sylvain Thénault's avatar
Sylvain Thénault committed
117
    __regid__ = 'outofcontext'
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
118
    title = _('out of context')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
119

120

Nicolas Chauvat's avatar
Nicolas Chauvat committed
121
122
class EmailInContextView(EmailOneLineView):
    """short view inside the context of the email"""
Sylvain Thénault's avatar
Sylvain Thénault committed
123
    __regid__ = 'incontext'
Nicolas Chauvat's avatar
Nicolas Chauvat committed
124

Sylvain Thénault's avatar
Sylvain Thénault committed
125
126
127
class EmailTreeItemView(EmailOutOfContextView):
    __regid__ = 'treeitem'
    title = None
Nicolas Chauvat's avatar
Nicolas Chauvat committed
128
129
130

class EmailPartOutOfContextView(baseviews.OutOfContextView):
    """out of context an email part is redirecting to related email view"""
Sylvain Thénault's avatar
Sylvain Thénault committed
131
    __select__ = is_instance('EmailPart')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
132
    def cell_call(self, row, col):
133
        entity = self.cw_rset.get_entity(row, col)
Nicolas Chauvat's avatar
Nicolas Chauvat committed
134
        entity.reverse_parts[0].view('outofcontext', w=self.w)
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
135
136


137
138
class EmailThreadView(EntityView):
    __regid__ = 'emailthread'
Sylvain Thénault's avatar
Sylvain Thénault committed
139
    __select__ = is_instance('EmailThread')
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
140

141
    def entity_call(self, entity):
Nicolas Chauvat's avatar
Nicolas Chauvat committed
142
143
144
145
146
147
148
149
150
151
152
153
        # 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
154
        rset =  self._cw.execute('DISTINCT Any X,D ORDERBY D '
Nicolas Chauvat's avatar
Nicolas Chauvat committed
155
156
                                 'WHERE X date D, X in_thread E, '
                                 'NOT X reply_to Y, E eid %(x)s',
Sylvain Thénault's avatar
Sylvain Thénault committed
157
                                 {'x': entity.eid})
Nicolas Chauvat's avatar
Nicolas Chauvat committed
158
159
160
161
        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
162
163


164
165
166
167
168
169
170
171
class EmailThreadPrimaryView(primary.PrimaryView):
    __select__ = is_instance('EmailThread')

    def cell_call(self, row, col):
        entity = self.cw_rset.complete_entity(row, col)
        self.w(u'<h1>%s</h1>' % xml_escape(entity.title))
        entity.view('emailthread', w=self.w)