entities.py 4.11 KB
Newer Older
Nicolas Chauvat's avatar
Nicolas Chauvat committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
"""entity classes for entity types provided by the cubicweb email package

:organization: Logilab
:copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""

__docformat__ = "restructuredtext en"

import re

from logilab.common import umessage

from cubicweb.interfaces import ITree
from cubicweb.common.mixins import TreeMixIn
from cubicweb.entities import AnyEntity, fetch_config

from cubes.email.emailcites import parse_body


class Email(TreeMixIn, AnyEntity):
    """customized class for Email entities"""
    id = 'Email'
    fetch_attrs, fetch_order = fetch_config(['subject'])
    __implements__ = AnyEntity.__implements__ + (ITree,)
26

Nicolas Chauvat's avatar
Nicolas Chauvat committed
27
    tree_attribute = 'reply_to'
28
29
30
31

    def parent(self):
        """for breadcrumbs"""
        return self.thread
32

Nicolas Chauvat's avatar
Nicolas Chauvat committed
33
34
35
36
37
38
    def dc_title(self):
        return self.subject

    @property
    def senderaddr(self):
        return self.sender[0]
39

Nicolas Chauvat's avatar
Nicolas Chauvat committed
40
41
42
    @property
    def in_reply_to(self):
        return self.reply_to and self.reply_to[0]
43

Nicolas Chauvat's avatar
Nicolas Chauvat committed
44
45
46
47
    @property
    def thread(self):
        return self.in_thread and self.in_thread[0]

48

Nicolas Chauvat's avatar
Nicolas Chauvat committed
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
    def parts_in_order(self, prefered_mime_type='text/html'):
        """sort an email parts in order, selecting among alternatives according to a
        prefered mime type
        """
        parts_by_eid = {}
        alternatives = []
        result = []
        for part in self.parts:
            parts_by_eid[part.eid] = part
            if part.alternative:
                for altset in alternatives:
                    if part.eid in altset:
                        break
                else:
                    alternatives.append(set(p.eid for p in part.alternative))
                    alternatives[-1].add(part.eid)
            else:
                result.append((part.ordernum, part))
        if alternatives:
            for altset in alternatives:
                selected = [parts_by_eid[peid] for peid in altset
                            if parts_by_eid[peid].content_format == prefered_mime_type]
                if selected:
                    selected = selected[0]
                else:
                    selected = parts_by_eid[altset.pop()]
                result.append((selected.ordernum, selected))
        result.sort()
        return [p[1] for p in result]

    def references(self):
        result = set()
        message = self.umessage_headers()
        if not message:
            return result
        replyto = message.get('In-reply-to')
        if replyto:
            result.add(replyto)
        result.update(message.get_all('References', ()))
        return result

    lines_rgx = re.compile('^Lines:\s*\d+\s*\n', re.I|re.U|re.M)
    clength_rgx = re.compile('^Content-Length:\s*\d+\s*\n', re.I|re.U|re.M)
    ctype_rgx = re.compile('^Content-Type:[^:]', re.I|re.U|re.M)
93

Nicolas Chauvat's avatar
Nicolas Chauvat committed
94
95
96
97
98
99
100
101
    def umessage_headers(self):
        if not self.headers:
            return None
        headers = self.lines_rgx.sub('', self.headers)
        headers = self.clength_rgx.sub('', headers)
        headers = self.ctype_rgx.sub('Content-type: text/plain; charset=utf8', headers)
        headers = headers.encode('utf8')
        return umessage.message_from_string(headers + '\n\n')
102
103


Nicolas Chauvat's avatar
Nicolas Chauvat committed
104
105
106
107
108
109
110
class EmailPart(AnyEntity):
    """customized class for EmailPart entities"""
    id = 'EmailPart'

    def dc_title(self):
        return '%s (%s %s)' % (self.email.subject,
                               self.req._('part'), self.ordernum)
111

Nicolas Chauvat's avatar
Nicolas Chauvat committed
112
113
114
    @property
    def email(self):
        return self.reverse_parts[0]
115

116
117
118
    def parent(self):
        """for breadcrumbs"""
        return self.email
119

Nicolas Chauvat's avatar
Nicolas Chauvat committed
120
121
122
123
124
125
    def actual_content(self):
        """return content of this part with citations and signature removed

        this method may raise `TransformError` exception if the part can't
        be displayed as text/plain.
        """
126
        content = self.printable_value('content', format='text/plain')
Nicolas Chauvat's avatar
Nicolas Chauvat committed
127
        return parse_body(content).actual_content
128
129


Nicolas Chauvat's avatar
Nicolas Chauvat committed
130
131
132
133
134
135
136
class EmailThread(AnyEntity):
    """customized class for EmailThread entities"""
    id = 'EmailThread'
    fetch_attrs, fetch_order = fetch_config(['title'])

    def dc_title(self):
        return self.title