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

:organization: Logilab
Sylvain Thénault's avatar
Sylvain Thénault committed
4
:copyright: 2003-2010 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 Thénault's avatar
Sylvain Thénault committed
12
from cubicweb.selectors import is_instance
Sylvain Thénault's avatar
Sylvain Thénault committed
13
from cubicweb.uilib import soup2xhtml
14
from cubicweb.web import uicfg, formwidgets
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
15
from cubicweb.web.views import baseviews, primary
Nicolas Chauvat's avatar
Nicolas Chauvat committed
16

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

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

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

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

29
30
31
32
33
34
35

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
36
        return xml_escape(message.get('From', ''))
37
    return email._cw._('unknown sender')
38

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
39
class EmailPrimaryView(primary.PrimaryView):
Sylvain Thénault's avatar
Sylvain Thénault committed
40
    __select__ = is_instance('Email')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
41

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
42
    def render_entity_attributes(self, entity):
43
44
        self.w(u'<div class="emailheader"><table>')
        self.w(u'<tr><td>%s</td><td>%s</td></tr>' %
45
               (self._cw._('From'), formated_sender(entity)))
46
        self.w(u'<tr><td>%s</td><td>%s</td></tr>' %
47
               (self._cw._('To'), ', '.join(ea.view('oneline') for ea in entity.recipients)))
Nicolas Chauvat's avatar
Nicolas Chauvat committed
48
        if entity.cc:
49
            self.w(u'<tr><td>%s</td><td>%s</td></tr>' %
50
                   (self._cw._('CC'), ', '.join(ea.view('oneline') for ea in entity.cc)))
51
        self.w(u'<tr><td>%s</td><td>%s</td></tr>' %
52
               (self._cw._('Date'), self._cw.format_date(entity.date, time=True)))
53
        self.w(u'<tr><td>%s</td><td>%s</td></tr>' %
54
               (self._cw._('Subject'), xml_escape(entity.subject)))
55
        self.w(u'</table></div><div class="emailcontent">')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
56
57
58
        for part in entity.parts_in_order():
            content, mime = part.content, part.content_format
            if mime == 'text/html':
59
                content = soup2xhtml(content, self._cw.encoding)
Nicolas Chauvat's avatar
Nicolas Chauvat committed
60
            elif mime != 'text/xhtml':
61
                content = xml_escape(content)
Nicolas Chauvat's avatar
Nicolas Chauvat committed
62
63
64
65
66
                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"/>')
67
        self.w(u'</div>')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
68

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


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

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

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

class EmailOneLineView(baseviews.OneLineView):
Nicolas Chauvat's avatar
Nicolas Chauvat committed
88
89
90
    """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
91
    __select__ = is_instance('Email')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
92
    title = _('oneline')
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
93

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

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

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

117

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

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

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
128
    __select__ = is_instance('EmailPart')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
129
    def cell_call(self, row, col):
130
        entity = self.cw_rset.get_entity(row, col)
Nicolas Chauvat's avatar
Nicolas Chauvat committed
131
        entity.reverse_parts[0].view('outofcontext', w=self.w)
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
132
133


sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
134
class EmailThreadPrimaryView(primary.PrimaryView):
Sylvain Thénault's avatar
Sylvain Thénault committed
135
    __select__ = is_instance('EmailThread')
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
136

Nicolas Chauvat's avatar
Nicolas Chauvat committed
137
    def cell_call(self, row, col):
138
        entity = self.cw_rset.complete_entity(row, col)
139
        self.w(u'<h1>%s</h1>' % xml_escape(entity.title))
Nicolas Chauvat's avatar
Nicolas Chauvat committed
140
141
142
143
144
145
146
147
148
149
150
151
        # 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
152
        rset =  self._cw.execute('DISTINCT Any X,D ORDERBY D '
Nicolas Chauvat's avatar
Nicolas Chauvat committed
153
154
                                 '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
155
                                 {'x': entity.eid})
Nicolas Chauvat's avatar
Nicolas Chauvat committed
156
157
158
159
        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
160
161