email.py 6.02 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
11

from logilab.mtconverter import html_escape

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
37
38
39

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:
        return html_escape(message.get('From'))
    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
        self.field('from', formated_sender(entity))
Nicolas Chauvat's avatar
Nicolas Chauvat committed
45
46
47
        self.field('to', ', '.join(ea.view('oneline') for ea in entity.recipients))
        if entity.cc:
            self.field('cc', ', '.join(ea.view('oneline') for ea in entity.cc))
48
        self.field('subject', html_escape(entity.subject))
Nicolas Chauvat's avatar
Nicolas Chauvat committed
49
50
51
52
53
54
55
56
57
58
59
60
61
        self.field('date', self.format_date(entity.date, time=True))
        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':
                content = html_escape(content)
                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"/>')

62
63
64
    def render_entity_title(self, entity):
        self.w(u'<h1><span class="etype">%s</span> %s</h1>'
               % (entity.dc_type().capitalize(), html_escape(entity.dc_title())))
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
65
66


Nicolas Chauvat's avatar
Nicolas Chauvat committed
67
68
69
class EmailHeadersView(baseviews.EntityView):
    """display email's headers"""
    id = 'headers'
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
70
    __select__ = implements('Email')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
71
72
73
    title = _('headers')
    templatable = False
    content_type = 'text/plain'
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
74

Nicolas Chauvat's avatar
Nicolas Chauvat committed
75
76
77
78
    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
79
80

class EmailOneLineView(baseviews.OneLineView):
Nicolas Chauvat's avatar
Nicolas Chauvat committed
81
82
83
    """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
84
    __select__ = implements('Email')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
85
    title = _('oneline')
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
86

Nicolas Chauvat's avatar
Nicolas Chauvat committed
87
88
89
    def cell_call(self, row, col, contexteid=None):
        entity = self.entity(row, col)
        self.w(u'<div class="email">')
90
91
92
93
        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
94
            self.w(u'<b>%s</b>&nbsp;%s '
95
                   % (self.req._('email_from'), formated_sender(entity)))
Nicolas Chauvat's avatar
Nicolas Chauvat committed
96
97
98
99
        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))
100
101
        self.w(u'<br/>\n<a href="%s">%s</a>' % (
            html_escape(entity.absolute_url()), html_escape(entity.subject)))
Nicolas Chauvat's avatar
Nicolas Chauvat committed
102
103
        self.w(u'</div>')

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

Nicolas Chauvat's avatar
Nicolas Chauvat committed
105
106
107
class EmailOutOfContextView(EmailOneLineView):
    """short view outside the context of the email"""
    id = 'outofcontext'
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
108
    title = _('out of context')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
109

110

Nicolas Chauvat's avatar
Nicolas Chauvat committed
111
112
113
114
115
116
117
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
118
    __select__ = implements('EmailPart')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
119
120
121
    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
122
123


Nicolas Chauvat's avatar
Nicolas Chauvat committed
124
125
class EmailThreadView(TreeViewMixIn, baseviews.ListView):
    """display email's headers"""
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
126
    __select__ = implements('Email')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
127
128
129
    title = _('thread view')
    item_vid = 'outofcontext'

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

131

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
132
class EmailThreadPrimaryView(primary.PrimaryView):
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
133
134
    __select__ = implements('EmailThread')

Nicolas Chauvat's avatar
Nicolas Chauvat committed
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
    def cell_call(self, row, col):
        entity = self.complete_entity(row, col)
        self.w(u'<h1>%s</h1>' % html_escape(entity.title))
        # 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
158
159