Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(581)

Side by Side Diff: electronic_mail.py

Issue 3255042: Electronic Mail for Tryton
Patch Set: Created 14 years, 4 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #This file is part of Tryton. The COPYRIGHT file at the top level of
2 #this repository contains the full copyright notices and license terms.
3 "Electronic Mail"
4 from __future__ import with_statement
5
6 import os
7 import base64
8 from sys import getsizeof
9 try:
10 import hashlib
11 except ImportError:
12 hashlib = None
13 import md5
14 from datetime import datetime
15 from time import mktime
16 from email.utils import parsedate
17
18 from trytond.model import ModelView, ModelSQL, fields
19 from trytond.config import CONFIG
20 from trytond.transaction import Transaction
21
22
23 class Mailbox(ModelSQL, ModelView):
24 "Mailbox"
25 _name = "electronic_mail.mailbox"
26 _description = __doc__
27
28 name = fields.Char('Name', required=True)
29 user = fields.Many2One('res.user', 'Owner')
30 parents = fields.Many2Many(
31 'electronic_mail.mailbox-parent-mailbox',
32 'mailbox_parent', 'mailbox_child' ,'Parents')
33 subscribed = fields.Boolean('Subscribed')
34 read_users = fields.Many2Many('electronic_mail.mailbox-read-res.user',
35 'mailbox', 'user', 'Read Users')
36 write_users = fields.Many2Many('electronic_mail.mailbox-write-res.user',
37 'mailbox', 'user', 'Write Users')
38
39 def __init__(self):
ced 2010/11/22 16:57:22 not used
40 super(Mailbox, self).__init__()
pheller 2010/11/22 16:49:31 Do you plan to override the superclass __init__()
41
42 Mailbox()
43
44
45 class MailboxParent(ModelSQL):
46 'Mailbox - parent - Mailbox'
47 _description = __doc__
48 _name = 'electronic_mail.mailbox-parent-mailbox'
ced 2010/11/22 16:57:22 mailbox-mailbox
49
50 mailbox_parent = fields.Many2One('electronic_mail.mailbox', 'Parent',
ced 2010/11/22 16:57:22 simply parent
51 ondelete='CASCADE', required=True, select=1)
52 mailbox_child = fields.Many2One('electronic_mail.mailbox', 'Child',
ced 2010/11/22 16:57:22 simply child
53 ondelete='CASCADE', required=True, select=1)
54
55 MailboxParent()
56
57
58 class ReadUser(ModelSQL):
59 'Electronic Mail - read - User'
60 _description = __doc__
61 _name = 'electronic_mail.mailbox-read-res.user'
62
63 mailbox = fields.Many2One('electronic_mail.mailbox', 'Mailbox',
64 ondelete='CASCADE', required=True, select=1)
65 user = fields.Many2One('res.user', 'User', ondelete='CASCADE',
66 required=True, select=1)
67
68 ReadUser()
69
70
71 class WriteUser(ModelSQL):
72 'Mailbox - write - User'
73 _description = __doc__
74 _name = 'electronic_mail.mailbox-write-res.user'
75
76 mailbox = fields.Many2One('electronic_mail.mailbox', 'mailbox',
77 ondelete='CASCADE', required=True, select=1)
78 user = fields.Many2One('res.user', 'User', ondelete='CASCADE',
79 required=True, select=1)
80
81 WriteUser()
82
83
84 class ElectronicMail(ModelSQL, ModelView):
85 "E-mail"
86 _name = 'electronic_mail'
87 _description = __doc__
88
89 mailbox = fields.Many2One(
90 'electronic_mail.mailbox', 'Mailbox', required=True)
91 from_ = fields.Char('From')
92 sender = fields.Char('Sender')
93 to = fields.Char('To')
94 cc = fields.Char('CC')
95 bcc = fields.Char('BCC')
96 subject = fields.Char('Subject')
97 date = fields.DateTime('Date')
98 message_id = fields.Char('Message-ID', help='Unique Message Identifier')
99 in_reply_to = fields.Char('In-Reply-To')
100 headers = fields.One2Many('electronic_mail.header',
101 'electronic_mail', 'Headers')
ced 2010/11/22 16:57:22 Not Tryton indent
102 digest = fields.Char('MD5 Digest')
ced 2010/11/22 16:57:22 size=32
103 collision = fields.Integer('Collision')
104 email = fields.Function(fields.Binary('Email'), 'get_email', 'set_email')
105 flag_seen = fields.Boolean('Seen')
106 flag_answered = fields.Boolean('Answered')
107 flag_flagged = fields.Boolean('Flagged')
108 flag_draft = fields.Boolean('Draft')
109 flag_recent = fields.Boolean('Recent')
110 size = fields.Integer('Size')
111 mailbox_owner = fields.Function(
112 fields.Many2One('res.user', 'Owner'),
113 'get_mailbox_owner', searcher='search_mailbox_owner')
114 mailbox_read_users = fields.Function(
115 fields.One2Many('res.user', None, 'Read Users'),
116 'get_mailbox_users', searcher='search_mailbox_users')
117 mailbox_write_users = fields.Function(
118 fields.One2Many('res.user', None, 'Write Users'),
119 'get_mailbox_users', searcher='search_mailbox_users')
120
121 def __init__ (self):
ced 2010/11/22 16:57:22 Not used
122 super(ElectronicMail, self).__init__()
pheller 2010/11/22 16:49:31 Do you plan to override the superclass __init__()
123
124 def default_collision(self):
125 return 0
126
127 def default_flag_seen(self):
128 return False
129
130 def default_flag_answered(self):
131 return False
132
133 def default_flag_flagged(self):
134 return False
135
136 def default_flag_recent(self):
137 return False
138
139 def get_mailbox_owner(self, ids, name):
140 mails = self.browse(ids)
141 return dict([(mail.id, mail.mailbox.user.id) for mail in mails])
142
143 def get_mailbox_users(self, ids, name):
144 assert name in ('mailbox_read_users', 'mailbox_write_users')
145 res = {}
146 for mail in self.browse(ids):
147 if name == 'mailbox_read_users':
148 res[mail.id] = [x.id for x in mail.mailbox['read_users']]
149 else:
150 res[mail.id] = [x.id for x in mail.mailbox['write_users']]
151 return res
152
153 def search_mailbox_owner(self, name, clause):
154 return [('mailbox.user',) + clause[1:]]
155
156 def search_mailbox_users(self, name, clause):
157 return [('mailbox.' + name[8:],) + clause[1:]]
158
159 def _get_email(self, electronic_mail):
160 """
161 Returns the email object from reading the FS
162 :param electronic_mail: Browse Record of the mail
163 """
164 db_name = Transaction().cursor.dbname
165 value = u''
166 if electronic_mail.digest:
167 filename = electronic_mail.digest
168 if electronic_mail.collision:
169 filename = filename + '-' + str(electronic_mail.collision)
170 filename = os.path.join(
171 CONFIG['data_path'], db_name,·
172 'email', filename[0:2], filename)
173 try:
174 with open(filename, 'r') as file_p:
175 value = file_p.read()
176 except IOError:
177 pass
178 return value
179
180 def get_email(self, ids, name):
181 """Fetches email from the data_path as email object
182 """
183 result = { }
184 for electronic_mail in self.browse(ids):
185 result[electronic_mail.id] = base64.encodestring(
ced 2010/11/22 16:57:22 Should be False if there is nothing
186 self._get_email(electronic_mail)
187 )
188 return result
189
190 def set_email(self, ids, name, data):
191 """Saves an email to the data path
192
193 :param data: Email as string
194 """
195 if data is False or data is None:
ced 2010/11/22 16:57:22 Should merge it with the attachment one.
196 return
197 db_name = Transaction().cursor.dbname
198 # Prepare Directory <DATA PATH>/<DB NAME>/email
199 directory = os.path.join(CONFIG['data_path'], db_name)
200 if not os.path.isdir(directory):
201 os.makedirs(directory, 0770)
202 digest = self.make_digest(data)
203 directory = os.path.join(directory, 'email', digest[0:2])
204 if not os.path.isdir(directory):
205 os.makedirs(directory, 0770)
206 # Filename <DIRECTORY>/<DIGEST>
207 filename = os.path.join(directory, digest)
208 collision = 0
209
210 if not os.path.isfile(filename):
211 # File doesnt exist already
212 with open(filename, 'w') as file_p:
213 file_p.write(data)
214 else:
215 # File already exists, may be its the same email data
216 # or maybe different.·
217
218 # Case 1: If different: we have to write file with updated
219 # Collission index
220
221 # Case 2: Same file: Leave it as such
222 with open(filename, 'r') as file_p:
223 data2 = file_p.read()
224 if data != data2:
225 cursor = Transaction().cursor
226 cursor.execute(
227 'SELECT DISTINCT(collision) FROM electronic_mail '
228 'WHERE digest = %s AND collision !=0 '
229 'ORDER BY collision', (digest,))
230 collision2 = 0
231 for row in cursor.fetchall():
232 collision2 = row[0]
233 filename = os.path.join(
234 directory, digest + '-' + str(collision2))
235 if os.path.isfile(filename):
236 with open(filename, 'r') as file_p:
237 data2 = file_p.read()
238 if data == data2:
239 collision = collision2
240 break
241 if collision == 0:
242 collision = collision2 + 1
243 filename = os.path.join(
244 directory, digest + '-' + str(collision))
245 with open(filename, 'w') as file_p:
246 file_p.write(data)
247 self.write(ids, {'digest': digest, 'collision': collision})
248
249 def make_digest(self, data):
250 """
251 Returns a digest from the mail
252
253 :param data: Data String
254 :return: Digest
255 """
256 if hashlib:
257 digest = hashlib.md5(data).hexdigest()
258 else:
259 digest = md5.new(data).hexdigest()
260 return digest
261
262 def create_from_email(self, mail, mailbox):
263 """
264 Creates a mail record from a given mail
265 :param mail: email object
ced 2010/11/22 16:57:22 Why not passing the email data?
266 :param mailbox: ID of the mailbox
267 """
268 header_obj = self.pool.get('electronic_mail.header')
269 values = {
270 'mailbox': mailbox,
271 'from_': mail.get('from'),
272 'sender': mail.get('sender'),
273 'to': mail.get('to'),
274 'cc': mail.get('cc'),
275 'bcc': mail.get('bcc'),
276 'subject': mail.get('subject'),
277 'date': datetime.fromtimestamp(
278 mktime(parsedate(mail.get('date')))
279 ),
280 'message_id': mail.get('message-id'),
281 'in_reply_to': mail.get('in-reply-to'),
282 'email': mail.as_string(),
283 'size': getsizeof(mail.as_string()),
284 }
285 create_id = self.create(values)
286 header_obj.create_from_email(mail, create_id)
287 return create_id
288
ced 2010/11/22 16:57:22 We also require to have a method to retrieve origi
289 ElectronicMail()
290
291
292 class Header(ModelSQL, ModelView):
293 "Header fields"
294 _name = 'electronic_mail.header'
295 _description = __doc__
296
297 name = fields.Char('Name', help='Name of Header Field')
298 value = fields.Char('Value', help='Value of Header Field')
299 electronic_mail = fields.Many2One('electronic_mail', 'e-mail')
300
301 def create_from_email(self, mail, mail_id):
302 """
303 :param mail: Email object
304 :param mail_id: ID of the email from electronic_mail
305 """
306 for name, value in mail.items():
307 values = {
308 'electronic_mail':mail_id,
309 'name':name,
310 'value':value,
311 }
312 self.create(values)
313 return True
314
315 Header()
OLDNEW
« no previous file with comments | « __tryton__.py ('k') | electronic_mail.xml » ('j') | electronic_mail.xml » ('J')

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b