Merge lp:~therp-nl/server-env-tools/7.0-fetchmail_attach_from_folder into lp:~server-env-tools-core-editors/server-env-tools/7.0

Proposed by Holger Brunn (Therp)
Status: Merged
Merged at revision: 74
Proposed branch: lp:~therp-nl/server-env-tools/7.0-fetchmail_attach_from_folder
Merge into: lp:~server-env-tools-core-editors/server-env-tools/7.0
Diff against target: 1019 lines (+939/-0)
15 files modified
fetchmail_attach_from_folder/__init__.py (+25/-0)
fetchmail_attach_from_folder/__openerp__.py (+46/-0)
fetchmail_attach_from_folder/match_algorithm/__init__.py (+26/-0)
fetchmail_attach_from_folder/match_algorithm/base.py (+43/-0)
fetchmail_attach_from_folder/match_algorithm/email_domain.py (+44/-0)
fetchmail_attach_from_folder/match_algorithm/email_exact.py (+56/-0)
fetchmail_attach_from_folder/match_algorithm/openerp_standard.py (+51/-0)
fetchmail_attach_from_folder/model/__init__.py (+24/-0)
fetchmail_attach_from_folder/model/fetchmail_server.py (+280/-0)
fetchmail_attach_from_folder/model/fetchmail_server_folder.py (+120/-0)
fetchmail_attach_from_folder/security/ir.model.access.csv (+2/-0)
fetchmail_attach_from_folder/view/fetchmail_server.xml (+56/-0)
fetchmail_attach_from_folder/wizard/__init__.py (+23/-0)
fetchmail_attach_from_folder/wizard/attach_mail_manually.py (+114/-0)
fetchmail_attach_from_folder/wizard/attach_mail_manually.xml (+29/-0)
To merge this branch: bzr merge lp:~therp-nl/server-env-tools/7.0-fetchmail_attach_from_folder
Reviewer Review Type Date Requested Status
Stefan Rijnhart (Opener) code review Approve
Ronald Portier (Therp) (community) test Approve
Holger Brunn (Therp) Needs Resubmitting
Nicolas JEUDY (community) code review and test install Needs Fixing
Review via email: mp+201970@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

Thanks! I took a diff from the version in lp:server-env-tools/6.1 and the diff is short and clean like you would expect. Only nit I could come up with is ll.788,995: type specification in views is deprecated in 7.0.

review: Needs Fixing
55. By Holger Brunn (Therp)

[IMP] remove deprecated type field for views

Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

thanks!

Revision history for this message
Nicolas JEUDY (njeudy) wrote :

hello

fetchmail_attach_from_folder/view/fetchmail_server.xml ll 822,826, <label without string or/and for attribute will not pass openerp.tools.view_validation in next release (v8)

What are this line for ?

Whithout those lines, module is installed correctly on V7 and futur trunk. I will test with an imap account tomorrow

review: Needs Fixing (code review and test install)
56. By Holger Brunn (Therp)

[IMP] adjust view definitions to 7.0
[FIX] use mail_thread.message_parse in wizard

Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

Thanks Nicholas, those were leftovers from the 6.1 version

Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

forgot to resubmit

review: Needs Resubmitting
Revision history for this message
Ronald Portier (Therp) (rportier1962) wrote :

Tested code before, both on 6.1 and 7.0 and is working really well.

LGTM on the code as well.

review: Approve (test)
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) :
review: Approve (code review)
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

Seeing that Nicolas' comments have been honoured months ago, I'm merging this.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'fetchmail_attach_from_folder'
2=== added file 'fetchmail_attach_from_folder/__init__.py'
3--- fetchmail_attach_from_folder/__init__.py 1970-01-01 00:00:00 +0000
4+++ fetchmail_attach_from_folder/__init__.py 2014-02-03 09:33:52 +0000
5@@ -0,0 +1,25 @@
6+# -*- encoding: utf-8 -*-
7+##############################################################################
8+#
9+# OpenERP, Open Source Management Solution
10+# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
11+# All Rights Reserved
12+#
13+# This program is free software: you can redistribute it and/or modify
14+# it under the terms of the GNU Affero General Public License as
15+# published by the Free Software Foundation, either version 3 of the
16+# License, or (at your option) any later version.
17+#
18+# This program is distributed in the hope that it will be useful,
19+# but WITHOUT ANY WARRANTY; without even the implied warranty of
20+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21+# GNU Affero General Public License for more details.
22+#
23+# You should have received a copy of the GNU Affero General Public License
24+# along with this program. If not, see <http://www.gnu.org/licenses/>.
25+#
26+##############################################################################
27+
28+import match_algorithm
29+import model
30+import wizard
31
32=== added file 'fetchmail_attach_from_folder/__openerp__.py'
33--- fetchmail_attach_from_folder/__openerp__.py 1970-01-01 00:00:00 +0000
34+++ fetchmail_attach_from_folder/__openerp__.py 2014-02-03 09:33:52 +0000
35@@ -0,0 +1,46 @@
36+# -*- encoding: utf-8 -*-
37+##############################################################################
38+#
39+# OpenERP, Open Source Management Solution
40+# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
41+# All Rights Reserved
42+#
43+# This program is free software: you can redistribute it and/or modify
44+# it under the terms of the GNU Affero General Public License as
45+# published by the Free Software Foundation, either version 3 of the
46+# License, or (at your option) any later version.
47+#
48+# This program is distributed in the hope that it will be useful,
49+# but WITHOUT ANY WARRANTY; without even the implied warranty of
50+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51+# GNU Affero General Public License for more details.
52+#
53+# You should have received a copy of the GNU Affero General Public License
54+# along with this program. If not, see <http://www.gnu.org/licenses/>.
55+#
56+##############################################################################
57+
58+{
59+ 'name': 'Attach mails in an IMAP folder to existing objects',
60+ 'version': '1.0',
61+ 'description': """
62+Adds the possibility to attach emails from a certain IMAP folder to objects,
63+ie partners. Matching is done via several algorithms, ie email address.
64+
65+This gives a simple possibility to archive emails in OpenERP without a mail
66+client integration.
67+ """,
68+ 'author': 'Therp BV',
69+ 'website': 'http://www.therp.nl',
70+ "category": "Tools",
71+ "depends": ['fetchmail'],
72+ 'data': [
73+ 'view/fetchmail_server.xml',
74+ 'wizard/attach_mail_manually.xml',
75+ 'security/ir.model.access.csv',
76+ ],
77+ 'js': [],
78+ 'installable': True,
79+ 'active': False,
80+ 'certificate': '',
81+}
82
83=== added directory 'fetchmail_attach_from_folder/match_algorithm'
84=== added file 'fetchmail_attach_from_folder/match_algorithm/__init__.py'
85--- fetchmail_attach_from_folder/match_algorithm/__init__.py 1970-01-01 00:00:00 +0000
86+++ fetchmail_attach_from_folder/match_algorithm/__init__.py 2014-02-03 09:33:52 +0000
87@@ -0,0 +1,26 @@
88+# -*- encoding: utf-8 -*-
89+##############################################################################
90+#
91+# OpenERP, Open Source Management Solution
92+# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
93+# All Rights Reserved
94+#
95+# This program is free software: you can redistribute it and/or modify
96+# it under the terms of the GNU Affero General Public License as
97+# published by the Free Software Foundation, either version 3 of the
98+# License, or (at your option) any later version.
99+#
100+# This program is distributed in the hope that it will be useful,
101+# but WITHOUT ANY WARRANTY; without even the implied warranty of
102+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
103+# GNU Affero General Public License for more details.
104+#
105+# You should have received a copy of the GNU Affero General Public License
106+# along with this program. If not, see <http://www.gnu.org/licenses/>.
107+#
108+##############################################################################
109+
110+import base
111+import email_exact
112+import email_domain
113+import openerp_standard
114
115=== added file 'fetchmail_attach_from_folder/match_algorithm/base.py'
116--- fetchmail_attach_from_folder/match_algorithm/base.py 1970-01-01 00:00:00 +0000
117+++ fetchmail_attach_from_folder/match_algorithm/base.py 2014-02-03 09:33:52 +0000
118@@ -0,0 +1,43 @@
119+# -*- encoding: utf-8 -*-
120+##############################################################################
121+#
122+# OpenERP, Open Source Management Solution
123+# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
124+# All Rights Reserved
125+#
126+# This program is free software: you can redistribute it and/or modify
127+# it under the terms of the GNU Affero General Public License as
128+# published by the Free Software Foundation, either version 3 of the
129+# License, or (at your option) any later version.
130+#
131+# This program is distributed in the hope that it will be useful,
132+# but WITHOUT ANY WARRANTY; without even the implied warranty of
133+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
134+# GNU Affero General Public License for more details.
135+#
136+# You should have received a copy of the GNU Affero General Public License
137+# along with this program. If not, see <http://www.gnu.org/licenses/>.
138+#
139+##############################################################################
140+
141+class base(object):
142+ name = None
143+ '''Name shown to the user'''
144+
145+ required_fields = []
146+ '''Fields on fetchmail_server folder that are required for this algorithm'''
147+
148+ readonly_fields = []
149+ '''Fields on fetchmail_server folder that are readonly for this algorithm'''
150+
151+
152+ def search_matches(self, cr, uid, conf, mail_message, mail_message_org):
153+ '''Returns ids found for model with mail_message'''
154+ return []
155+
156+ def handle_match(
157+ self, cr, uid, connection, object_id, folder,
158+ mail_message, mail_message_org, msgid, context=None):
159+ '''Do whatever it takes to handle a match'''
160+ return folder.server_id.attach_mail(connection, object_id, folder,
161+ mail_message, msgid)
162
163=== added file 'fetchmail_attach_from_folder/match_algorithm/email_domain.py'
164--- fetchmail_attach_from_folder/match_algorithm/email_domain.py 1970-01-01 00:00:00 +0000
165+++ fetchmail_attach_from_folder/match_algorithm/email_domain.py 2014-02-03 09:33:52 +0000
166@@ -0,0 +1,44 @@
167+# -*- encoding: utf-8 -*-
168+##############################################################################
169+#
170+# OpenERP, Open Source Management Solution
171+# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
172+# All Rights Reserved
173+#
174+# This program is free software: you can redistribute it and/or modify
175+# it under the terms of the GNU Affero General Public License as
176+# published by the Free Software Foundation, either version 3 of the
177+# License, or (at your option) any later version.
178+#
179+# This program is distributed in the hope that it will be useful,
180+# but WITHOUT ANY WARRANTY; without even the implied warranty of
181+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
182+# GNU Affero General Public License for more details.
183+#
184+# You should have received a copy of the GNU Affero General Public License
185+# along with this program. If not, see <http://www.gnu.org/licenses/>.
186+#
187+##############################################################################
188+
189+from email_exact import email_exact
190+
191+class email_domain(email_exact):
192+ '''Search objects by domain name of email address.
193+ Beware of match_first here, this is most likely to get it wrong (gmail)'''
194+ name = 'Domain of email address'
195+
196+ def search_matches(self, cr, uid, conf, mail_message, mail_message_org):
197+ ids = super(email_domain, self).search_matches(
198+ cr, uid, conf, mail_message, mail_message_org)
199+ if not ids:
200+ domains = []
201+ for addr in self._get_mailaddresses(conf, mail_message):
202+ domains.append(addr.split('@')[-1])
203+ ids = conf.pool.get(conf.model_id.model).search(
204+ cr, uid,
205+ self._get_mailaddress_search_domain(
206+ conf, mail_message,
207+ operator='like',
208+ values=['%@'+domain for domain in set(domains)]),
209+ order=conf.model_order)
210+ return ids
211
212=== added file 'fetchmail_attach_from_folder/match_algorithm/email_exact.py'
213--- fetchmail_attach_from_folder/match_algorithm/email_exact.py 1970-01-01 00:00:00 +0000
214+++ fetchmail_attach_from_folder/match_algorithm/email_exact.py 2014-02-03 09:33:52 +0000
215@@ -0,0 +1,56 @@
216+# -*- encoding: utf-8 -*-
217+##############################################################################
218+#
219+# OpenERP, Open Source Management Solution
220+# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
221+# All Rights Reserved
222+#
223+# This program is free software: you can redistribute it and/or modify
224+# it under the terms of the GNU Affero General Public License as
225+# published by the Free Software Foundation, either version 3 of the
226+# License, or (at your option) any later version.
227+#
228+# This program is distributed in the hope that it will be useful,
229+# but WITHOUT ANY WARRANTY; without even the implied warranty of
230+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
231+# GNU Affero General Public License for more details.
232+#
233+# You should have received a copy of the GNU Affero General Public License
234+# along with this program. If not, see <http://www.gnu.org/licenses/>.
235+#
236+##############################################################################
237+
238+from base import base
239+from openerp.tools.safe_eval import safe_eval
240+from openerp.tools.mail import email_split
241+
242+class email_exact(base):
243+ '''Search for exactly the mailadress as noted in the email'''
244+
245+ name = 'Exact mailadress'
246+ required_fields = ['model_field', 'mail_field']
247+
248+ def _get_mailaddresses(self, conf, mail_message):
249+ mailaddresses = []
250+ fields = conf.mail_field.split(',')
251+ for field in fields:
252+ if field in mail_message:
253+ mailaddresses += email_split(mail_message[field])
254+ return [ addr.lower() for addr in mailaddresses ]
255+
256+ def _get_mailaddress_search_domain(
257+ self, conf, mail_message, operator='=', values=None):
258+ mailaddresses = values or self._get_mailaddresses(
259+ conf, mail_message)
260+ if not mailaddresses:
261+ return [(0, '=', 1)]
262+ search_domain = ((['|'] * (len(mailaddresses) - 1)) + [
263+ (conf.model_field, operator, addr) for addr in mailaddresses] +
264+ safe_eval(conf.domain or '[]'))
265+ return search_domain
266+
267+ def search_matches(self, cr, uid, conf, mail_message, mail_message_org):
268+ conf_model = conf.pool.get(conf.model_id.model)
269+ search_domain = self._get_mailaddress_search_domain(conf, mail_message)
270+ return conf_model.search(
271+ cr, uid, search_domain, order=conf.model_order)
272
273=== added file 'fetchmail_attach_from_folder/match_algorithm/openerp_standard.py'
274--- fetchmail_attach_from_folder/match_algorithm/openerp_standard.py 1970-01-01 00:00:00 +0000
275+++ fetchmail_attach_from_folder/match_algorithm/openerp_standard.py 2014-02-03 09:33:52 +0000
276@@ -0,0 +1,51 @@
277+# -*- encoding: utf-8 -*-
278+##############################################################################
279+#
280+# OpenERP, Open Source Management Solution
281+# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
282+# All Rights Reserved
283+#
284+# This program is free software: you can redistribute it and/or modify
285+# it under the terms of the GNU Affero General Public License as
286+# published by the Free Software Foundation, either version 3 of the
287+# License, or (at your option) any later version.
288+#
289+# This program is distributed in the hope that it will be useful,
290+# but WITHOUT ANY WARRANTY; without even the implied warranty of
291+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
292+# GNU Affero General Public License for more details.
293+#
294+# You should have received a copy of the GNU Affero General Public License
295+# along with this program. If not, see <http://www.gnu.org/licenses/>.
296+#
297+##############################################################################
298+
299+from base import base
300+from openerp.tools.safe_eval import safe_eval
301+
302+class openerp_standard(base):
303+ '''No search at all. Use OpenERP's standard mechanism to attach mails to
304+ mail.thread objects. Note that this algorithm always matches.'''
305+
306+ name = 'OpenERP standard'
307+ readonly_fields = ['model_field', 'mail_field', 'match_first', 'domain',
308+ 'model_order', 'flag_nonmatching']
309+
310+ def search_matches(self, cr, uid, conf, mail_message, mail_message_org):
311+ '''Always match. Duplicates will be fished out by message_id'''
312+ return [True]
313+
314+ def handle_match(
315+ self, cr, uid, connection, object_id, folder,
316+ mail_message, mail_message_org, msgid, context):
317+ result = folder.pool.get('mail.thread').message_process(
318+ cr, uid,
319+ folder.model_id.model, mail_message_org,
320+ save_original=folder.server_id.original,
321+ strip_attachments=(not folder.server_id.attach),
322+ context=context)
323+
324+ if folder.delete_matching:
325+ connection.store(msgid, '+FLAGS', '\\DELETED')
326+
327+ return [result]
328
329=== added directory 'fetchmail_attach_from_folder/model'
330=== added file 'fetchmail_attach_from_folder/model/__init__.py'
331--- fetchmail_attach_from_folder/model/__init__.py 1970-01-01 00:00:00 +0000
332+++ fetchmail_attach_from_folder/model/__init__.py 2014-02-03 09:33:52 +0000
333@@ -0,0 +1,24 @@
334+# -*- encoding: utf-8 -*-
335+##############################################################################
336+#
337+# OpenERP, Open Source Management Solution
338+# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
339+# All Rights Reserved
340+#
341+# This program is free software: you can redistribute it and/or modify
342+# it under the terms of the GNU Affero General Public License as
343+# published by the Free Software Foundation, either version 3 of the
344+# License, or (at your option) any later version.
345+#
346+# This program is distributed in the hope that it will be useful,
347+# but WITHOUT ANY WARRANTY; without even the implied warranty of
348+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
349+# GNU Affero General Public License for more details.
350+#
351+# You should have received a copy of the GNU Affero General Public License
352+# along with this program. If not, see <http://www.gnu.org/licenses/>.
353+#
354+##############################################################################
355+
356+import fetchmail_server
357+import fetchmail_server_folder
358
359=== added file 'fetchmail_attach_from_folder/model/fetchmail_server.py'
360--- fetchmail_attach_from_folder/model/fetchmail_server.py 1970-01-01 00:00:00 +0000
361+++ fetchmail_attach_from_folder/model/fetchmail_server.py 2014-02-03 09:33:52 +0000
362@@ -0,0 +1,280 @@
363+# -*- encoding: utf-8 -*-
364+##############################################################################
365+#
366+# OpenERP, Open Source Management Solution
367+# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
368+# All Rights Reserved
369+#
370+# This program is free software: you can redistribute it and/or modify
371+# it under the terms of the GNU Affero General Public License as
372+# published by the Free Software Foundation, either version 3 of the
373+# License, or (at your option) any later version.
374+#
375+# This program is distributed in the hope that it will be useful,
376+# but WITHOUT ANY WARRANTY; without even the implied warranty of
377+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
378+# GNU Affero General Public License for more details.
379+#
380+# You should have received a copy of the GNU Affero General Public License
381+# along with this program. If not, see <http://www.gnu.org/licenses/>.
382+#
383+##############################################################################
384+
385+import base64
386+import simplejson
387+from lxml import etree
388+from openerp.osv.orm import Model, except_orm, browse_null
389+from openerp.tools.translate import _
390+from openerp.osv import fields
391+from openerp.addons.fetchmail.fetchmail import _logger as logger
392+from openerp.tools.misc import UnquoteEvalContext
393+from openerp.tools.safe_eval import safe_eval
394+
395+
396+class fetchmail_server(Model):
397+ _inherit = 'fetchmail.server'
398+
399+ _columns = {
400+ 'folder_ids': fields.one2many(
401+ 'fetchmail.server.folder', 'server_id', 'Folders'),
402+ }
403+
404+ _defaults = {
405+ 'type': 'imap',
406+ }
407+
408+ def __init__(self, pool, cr):
409+ self._columns['object_id'].required = False
410+ return super(fetchmail_server, self).__init__(pool, cr)
411+
412+ def onchange_server_type(
413+ self, cr, uid, ids, server_type=False, ssl=False,
414+ object_id=False):
415+ retval = super(
416+ fetchmail_server, self).onchange_server_type(cr, uid,
417+ ids, server_type, ssl,
418+ object_id)
419+ retval['value']['state'] = 'draft'
420+ return retval
421+
422+ def fetch_mail(self, cr, uid, ids, context=None):
423+ if context is None:
424+ context = {}
425+
426+ check_original = []
427+
428+ for this in self.browse(cr, uid, ids, context):
429+ if this.object_id:
430+ check_original.append(this.id)
431+
432+ context.update(
433+ {
434+ 'fetchmail_server_id': this.id,
435+ 'server_type': this.type
436+ })
437+
438+ connection = this.connect()
439+ for folder in this.folder_ids:
440+ this.handle_folder(connection, folder)
441+
442+ connection.close()
443+
444+ return super(fetchmail_server, self).fetch_mail(
445+ cr, uid, check_original, context)
446+
447+ def handle_folder(self, cr, uid, ids, connection, folder, context=None):
448+ '''Return ids of objects matched'''
449+
450+ matched_object_ids = []
451+
452+ for this in self.browse(cr, uid, ids, context=context):
453+ logger.info('start checking for emails in %s server %s',
454+ folder.path, this.name)
455+
456+ match_algorithm = folder.get_algorithm()
457+
458+ if connection.select(folder.path)[0] != 'OK':
459+ logger.error(
460+ 'Could not open mailbox %s on %s' % (
461+ folder.path, this.server))
462+ connection.select()
463+ continue
464+ result, msgids = this.get_msgids(connection)
465+ if result != 'OK':
466+ logger.error(
467+ 'Could not search mailbox %s on %s' % (
468+ folder.path, this.server))
469+ continue
470+
471+ for msgid in msgids[0].split():
472+ matched_object_ids += this.apply_matching(
473+ connection, folder, msgid, match_algorithm)
474+
475+ logger.info('finished checking for emails in %s server %s',
476+ folder.path, this.name)
477+
478+ return matched_object_ids
479+
480+ def get_msgids(self, cr, uid, ids, connection, context=None):
481+ '''Return imap ids of messages to process'''
482+ return connection.search(None, 'UNDELETED')
483+
484+ def apply_matching(self, cr, uid, ids, connection, folder, msgid,
485+ match_algorithm, context=None):
486+ '''Return ids of objects matched'''
487+
488+ matched_object_ids = []
489+
490+ for this in self.browse(cr, uid, ids, context=context):
491+ result, msgdata = connection.fetch(msgid, '(RFC822)')
492+
493+ if result != 'OK':
494+ logger.error(
495+ 'Could not fetch %s in %s on %s' % (
496+ msgid, folder.path, this.server))
497+ continue
498+
499+ mail_message = self.pool.get('mail.thread').message_parse(
500+ cr, uid, msgdata[0][1], save_original=this.original,
501+ context=context)
502+
503+ if self.pool.get('mail.message').search(cr, uid, [
504+ ('message_id', '=', mail_message['message_id'])]):
505+ continue
506+
507+ found_ids = match_algorithm.search_matches(
508+ cr, uid, folder,
509+ mail_message, msgdata[0][1])
510+
511+ if found_ids and (len(found_ids) == 1 or
512+ folder.match_first):
513+ try:
514+ cr.execute('savepoint apply_matching')
515+ match_algorithm.handle_match(
516+ cr, uid, connection,
517+ found_ids[0], folder, mail_message,
518+ msgdata[0][1], msgid, context)
519+ cr.execute('release savepoint apply_matching')
520+ matched_object_ids += found_ids[:1]
521+ except Exception, e:
522+ cr.execute('rollback to savepoint apply_matching')
523+ logger.exception(
524+ "Failed to fetch mail %s from %s",
525+ msgid, this.name)
526+ elif folder.flag_nonmatching:
527+ connection.store(msgid, '+FLAGS', '\\FLAGGED')
528+
529+ return matched_object_ids
530+
531+ def attach_mail(
532+ self, cr, uid, ids, connection, object_id, folder,
533+ mail_message, msgid, context=None):
534+ '''Return ids of messages created'''
535+
536+ mail_message_ids = []
537+
538+ for this in self.browse(cr, uid, ids, context):
539+ partner_id = None
540+ if folder.model_id.model == 'res.partner':
541+ partner_id = object_id
542+ if 'partner_id' in self.pool.get(folder.model_id.model)._columns:
543+ partner_id = self.pool.get(
544+ folder.model_id.model).browse(
545+ cr, uid, object_id, context
546+ ).partner_id.id
547+
548+ attachments=[]
549+ if this.attach and mail_message.get('attachments'):
550+ for attachment in mail_message['attachments']:
551+ fname, fcontent = attachment
552+ if isinstance(fcontent, unicode):
553+ fcontent = fcontent.encode('utf-8')
554+ data_attach = {
555+ 'name': fname,
556+ 'datas': base64.b64encode(str(fcontent)),
557+ 'datas_fname': fname,
558+ 'description': _('Mail attachment'),
559+ 'res_model': folder.model_id.model,
560+ 'res_id': object_id,
561+ }
562+ attachments.append(
563+ self.pool.get('ir.attachment').create(
564+ cr, uid, data_attach, context=context))
565+
566+ mail_message_ids.append(
567+ self.pool.get('mail.message').create(
568+ cr, uid,
569+ {
570+ 'author_id': partner_id,
571+ 'model': folder.model_id.model,
572+ 'res_id': object_id,
573+ 'type': 'email',
574+ 'body': mail_message.get('body'),
575+ 'subject': mail_message.get('subject'),
576+ 'email_from': mail_message.get('from'),
577+ 'date': mail_message.get('date'),
578+ 'message_id': mail_message.get('message_id'),
579+ 'attachment_ids': [(6, 0, attachments)],
580+ },
581+ context))
582+
583+ if folder.delete_matching:
584+ connection.store(msgid, '+FLAGS', '\\DELETED')
585+ return mail_message_ids
586+
587+ def button_confirm_login(self, cr, uid, ids, context=None):
588+ retval = super(fetchmail_server, self).button_confirm_login(cr, uid,
589+ ids,
590+ context)
591+
592+ for this in self.browse(cr, uid, ids, context):
593+ this.write({'state': 'draft'})
594+ connection = this.connect()
595+ connection.select()
596+ for folder in this.folder_ids:
597+ if connection.select(folder.path)[0] != 'OK':
598+ raise except_orm(
599+ _('Error'), _('Mailbox %s not found!') %
600+ folder.path)
601+ connection.close()
602+ this.write({'state': 'done'})
603+
604+ return retval
605+
606+ def fields_view_get(self, cr, user, view_id=None, view_type='form',
607+ context=None, toolbar=False, submenu=False):
608+ result = super(fetchmail_server, self).fields_view_get(
609+ cr, user, view_id, view_type, context, toolbar, submenu)
610+
611+ if view_type == 'form':
612+ view = etree.fromstring(
613+ result['fields']['folder_ids']['views']['form']['arch'])
614+ modifiers = {}
615+ docstr = ''
616+ for algorithm in self.pool.get('fetchmail.server.folder')\
617+ ._get_match_algorithms().itervalues():
618+ for modifier in ['required', 'readonly']:
619+ for field in getattr(algorithm, modifier + '_fields'):
620+ modifiers.setdefault(field, {})
621+ modifiers[field].setdefault(modifier, [])
622+ if modifiers[field][modifier]:
623+ modifiers[field][modifier].insert(0, '|')
624+ modifiers[field][modifier].append(
625+ ("match_algorithm", "==", algorithm.__name__))
626+ docstr += _(algorithm.name) + '\n' + _(algorithm.__doc__) + \
627+ '\n\n'
628+
629+ for field in view:
630+ if field.tag == 'field' and field.get('name') in modifiers:
631+ field.set('modifiers', simplejson.dumps(
632+ dict(
633+ eval(field.attrib['modifiers'],
634+ UnquoteEvalContext({})),
635+ **modifiers[field.attrib['name']])))
636+ if (field.tag == 'field' and
637+ field.get('name') == 'match_algorithm'):
638+ field.set('help', docstr)
639+ result['fields']['folder_ids']['views']['form']['arch'] = \
640+ etree.tostring(view)
641+
642+ return result
643
644=== added file 'fetchmail_attach_from_folder/model/fetchmail_server_folder.py'
645--- fetchmail_attach_from_folder/model/fetchmail_server_folder.py 1970-01-01 00:00:00 +0000
646+++ fetchmail_attach_from_folder/model/fetchmail_server_folder.py 2014-02-03 09:33:52 +0000
647@@ -0,0 +1,120 @@
648+# -*- encoding: utf-8 -*-
649+##############################################################################
650+#
651+# OpenERP, Open Source Management Solution
652+# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
653+# All Rights Reserved
654+#
655+# This program is free software: you can redistribute it and/or modify
656+# it under the terms of the GNU Affero General Public License as
657+# published by the Free Software Foundation, either version 3 of the
658+# License, or (at your option) any later version.
659+#
660+# This program is distributed in the hope that it will be useful,
661+# but WITHOUT ANY WARRANTY; without even the implied warranty of
662+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
663+# GNU Affero General Public License for more details.
664+#
665+# You should have received a copy of the GNU Affero General Public License
666+# along with this program. If not, see <http://www.gnu.org/licenses/>.
667+#
668+########################################################################
669+
670+from openerp.osv import fields
671+from openerp.osv.orm import Model
672+from .. import match_algorithm
673+
674+
675+class fetchmail_server_folder(Model):
676+ _name = 'fetchmail.server.folder'
677+ _rec_name = 'path'
678+
679+ def _get_match_algorithms(self):
680+ def get_all_subclasses(cls):
681+ return cls.__subclasses__() + [subsub
682+ for sub in cls.__subclasses__()
683+ for subsub in get_all_subclasses(sub)]
684+ return dict([(cls.__name__, cls) for cls in get_all_subclasses(
685+ match_algorithm.base.base)])
686+
687+ def _get_match_algorithms_sel(self, cr, uid, context=None):
688+ algorithms = []
689+ for cls in self._get_match_algorithms().itervalues():
690+ algorithms.append((cls.__name__, cls.name))
691+ algorithms.sort()
692+ return algorithms
693+
694+ _columns = {
695+ 'sequence': fields.integer('Sequence'),
696+ 'path': fields.char(
697+ 'Path', size=256, help='The path to your mail '
698+ "folder. Typically would be something like 'INBOX.myfolder'",
699+ required=True),
700+ 'model_id': fields.many2one(
701+ 'ir.model', 'Model', required=True,
702+ help='The model to attach emails to'),
703+ 'model_field': fields.char(
704+ 'Field (model)', size=128,
705+ help='The field in your model that contains the field to match '
706+ 'against.\n'
707+ 'Examples:\n'
708+ "'email' if your model is res.partner, or "
709+ "'partner_id.email' if you're matching sale orders"),
710+ 'model_order': fields.char(
711+ 'Order (model)', size=128,
712+ help='Fields to order by, this mostly useful in conjunction '
713+ "with 'Use 1st match'"),
714+ 'match_algorithm': fields.selection(
715+ _get_match_algorithms_sel,
716+ 'Match algorithm', required=True, translate=True,
717+ help='The algorithm used to determine which object an email '
718+ 'matches.'),
719+ 'mail_field': fields.char(
720+ 'Field (email)', size=128,
721+ help='The field in the email used for matching. Typically '
722+ "this is 'to' or 'from'"),
723+ 'server_id': fields.many2one('fetchmail.server', 'Server'),
724+ 'delete_matching': fields.boolean(
725+ 'Delete matches',
726+ help='Delete matched emails from server'),
727+ 'flag_nonmatching': fields.boolean(
728+ 'Flag nonmatching',
729+ help="Flag emails in the server that don't match any object "
730+ 'in OpenERP'),
731+ 'match_first': fields.boolean(
732+ 'Use 1st match',
733+ help='If there are multiple matches, use the first one. If '
734+ 'not checked, multiple matches count as no match at all'),
735+ 'domain': fields.char(
736+ 'Domain', size=128, help='Fill in a search '
737+ 'filter to narrow down objects to match'),
738+ 'msg_state': fields.selection(
739+ [
740+ ('sent', 'Sent'),
741+ ('received', 'Received'),
742+ ],
743+ 'Message state',
744+ help='The state messages fetched from this folder should be '
745+ 'assigned in OpenERP'),
746+ }
747+
748+ _defaults = {
749+ 'flag_nonmatching': True,
750+ 'msg_state': 'received',
751+ }
752+
753+ def get_algorithm(self, cr, uid, ids, context=None):
754+ for this in self.browse(cr, uid, ids, context):
755+ return self._get_match_algorithms()[this.match_algorithm]()
756+
757+ def button_attach_mail_manually(self, cr, uid, ids, context=None):
758+ for this in self.browse(cr, uid, ids, context):
759+ context.update({'default_folder_id': this.id})
760+ return {
761+ 'type': 'ir.actions.act_window',
762+ 'res_model': 'fetchmail.attach.mail.manually',
763+ 'target': 'new',
764+ 'context': context,
765+ 'view_type': 'form',
766+ 'view_mode': 'form',
767+ }
768
769=== added directory 'fetchmail_attach_from_folder/security'
770=== added file 'fetchmail_attach_from_folder/security/ir.model.access.csv'
771--- fetchmail_attach_from_folder/security/ir.model.access.csv 1970-01-01 00:00:00 +0000
772+++ fetchmail_attach_from_folder/security/ir.model.access.csv 2014-02-03 09:33:52 +0000
773@@ -0,0 +1,2 @@
774+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
775+access_model_fetchmail_server_folder,fetchmail.server.folder,model_fetchmail_server_folder,base.group_system,1,1,1,1
776
777=== added directory 'fetchmail_attach_from_folder/view'
778=== added file 'fetchmail_attach_from_folder/view/fetchmail_server.xml'
779--- fetchmail_attach_from_folder/view/fetchmail_server.xml 1970-01-01 00:00:00 +0000
780+++ fetchmail_attach_from_folder/view/fetchmail_server.xml 2014-02-03 09:33:52 +0000
781@@ -0,0 +1,56 @@
782+<?xml version="1.0" encoding="utf-8"?>
783+<openerp>
784+ <data>
785+ <record model="ir.ui.view" id="view_email_server_form">
786+ <field name="name">fetchmail.server.form</field>
787+ <field name="model">fetchmail.server</field>
788+ <field name="inherit_id" ref="fetchmail.view_email_server_form" />
789+ <field name="arch" type="xml">
790+ <data>
791+ <field name="object_id" position="attributes">
792+ <attribute name="attrs">{'required': [('type', '!=', 'imap')]}</attribute>
793+ </field>
794+ <xpath expr="//page[@string='Server &amp; Login']/group/group[3]" position="after">
795+ <group attrs="{'invisible': [('type','!=','imap')]}" string="Folders to monitor">
796+ <field
797+ name="folder_ids"
798+ nolabel="1"
799+ on_change="onchange_server_type(type, is_ssl, object_id)">
800+ <tree>
801+ <field name="sequence" invisible="1" />
802+ <field name="path" />
803+ <field name="model_id" />
804+ <field name="model_field" />
805+ <field name="match_algorithm" />
806+ <field name="mail_field" />
807+ </tree>
808+ <form version="7.0">
809+ <header>
810+ <button type="object" name="button_attach_mail_manually" string="Attach mail manually" icon="gtk-redo" />
811+ </header>
812+ <group>
813+ <group>
814+ <field name="path" />
815+ <field name="model_id" />
816+ <field name="model_field" />
817+ <field name="match_algorithm" />
818+ <field name="mail_field" />
819+ </group>
820+ <group>
821+ <field name="delete_matching" />
822+ <field name="flag_nonmatching" />
823+ <field name="match_first" />
824+ <field name="msg_state" />
825+ <field name="model_order" attrs="{'readonly': [('match_first','==',False)], 'required': [('match_first','==',True)]}" />
826+ <field name="domain" />
827+ </group>
828+ </group>
829+ </form>
830+ </field>
831+ </group>
832+ </xpath>
833+ </data>
834+ </field>
835+ </record>
836+ </data>
837+</openerp>
838
839=== added directory 'fetchmail_attach_from_folder/wizard'
840=== added file 'fetchmail_attach_from_folder/wizard/__init__.py'
841--- fetchmail_attach_from_folder/wizard/__init__.py 1970-01-01 00:00:00 +0000
842+++ fetchmail_attach_from_folder/wizard/__init__.py 2014-02-03 09:33:52 +0000
843@@ -0,0 +1,23 @@
844+# -*- encoding: utf-8 -*-
845+##############################################################################
846+#
847+# OpenERP, Open Source Management Solution
848+# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
849+# All Rights Reserved
850+#
851+# This program is free software: you can redistribute it and/or modify
852+# it under the terms of the GNU Affero General Public License as
853+# published by the Free Software Foundation, either version 3 of the
854+# License, or (at your option) any later version.
855+#
856+# This program is distributed in the hope that it will be useful,
857+# but WITHOUT ANY WARRANTY; without even the implied warranty of
858+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
859+# GNU Affero General Public License for more details.
860+#
861+# You should have received a copy of the GNU Affero General Public License
862+# along with this program. If not, see <http://www.gnu.org/licenses/>.
863+#
864+##############################################################################
865+
866+import attach_mail_manually
867
868=== added file 'fetchmail_attach_from_folder/wizard/attach_mail_manually.py'
869--- fetchmail_attach_from_folder/wizard/attach_mail_manually.py 1970-01-01 00:00:00 +0000
870+++ fetchmail_attach_from_folder/wizard/attach_mail_manually.py 2014-02-03 09:33:52 +0000
871@@ -0,0 +1,114 @@
872+# -*- encoding: utf-8 -*-
873+##############################################################################
874+#
875+# OpenERP, Open Source Management Solution
876+# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
877+# All Rights Reserved
878+#
879+# This program is free software: you can redistribute it and/or modify
880+# it under the terms of the GNU Affero General Public License as
881+# published by the Free Software Foundation, either version 3 of the
882+# License, or (at your option) any later version.
883+#
884+# This program is distributed in the hope that it will be useful,
885+# but WITHOUT ANY WARRANTY; without even the implied warranty of
886+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
887+# GNU Affero General Public License for more details.
888+#
889+# You should have received a copy of the GNU Affero General Public License
890+# along with this program. If not, see <http://www.gnu.org/licenses/>.
891+#
892+##############################################################################
893+
894+from openerp.osv import fields
895+from openerp.osv.orm import TransientModel
896+
897+
898+class attach_mail_manually(TransientModel):
899+ _name = 'fetchmail.attach.mail.manually'
900+
901+ _columns = {
902+ 'folder_id': fields.many2one('fetchmail.server.folder', 'Folder',
903+ readonly=True),
904+ 'mail_ids': fields.one2many(
905+ 'fetchmail.attach.mail.manually.mail', 'wizard_id', 'Emails'),
906+ }
907+
908+ def default_get(self, cr, uid, fields_list, context=None):
909+ if context is None:
910+ context = {}
911+
912+ defaults = super(attach_mail_manually, self).default_get(cr, uid,
913+ fields_list, context)
914+
915+ for folder in self.pool.get('fetchmail.server.folder').browse(cr, uid,
916+ [context.get('default_folder_id')], context):
917+ defaults['mail_ids']=[]
918+ connection = folder.server_id.connect()
919+ connection.select(folder.path)
920+ result, msgids = connection.search(None,
921+ 'FLAGGED' if folder.flag_nonmatching else 'UNDELETED')
922+ if result != 'OK':
923+ logger.error('Could not search mailbox %s on %s' % (
924+ folder.path, this.server))
925+ continue
926+ attach_mail_manually_mail._columns['object_id'].selection=[
927+ (folder.model_id.model, folder.model_id.name)]
928+ for msgid in msgids[0].split():
929+ result, msgdata = connection.fetch(msgid, '(RFC822)')
930+ if result != 'OK':
931+ logger.error('Could not fetch %s in %s on %s' % (
932+ msgid, folder.path, this.server))
933+ continue
934+ mail_message = self.pool.get('mail.thread').message_parse(
935+ cr, uid, msgdata[0][1],
936+ save_original=folder.server_id.original,
937+ context=context)
938+ defaults['mail_ids'].append((0, 0, {
939+ 'msgid': msgid,
940+ 'subject': mail_message.get('subject', ''),
941+ 'date': mail_message.get('date', ''),
942+ 'object_id': folder.model_id.model+',False'
943+ }))
944+ connection.close()
945+
946+ return defaults
947+
948+ def attach_mails(self, cr, uid, ids, context=None):
949+ for this in self.browse(cr, uid, ids, context):
950+ for mail in this.mail_ids:
951+ connection = this.folder_id.server_id.connect()
952+ connection.select(this.folder_id.path)
953+ result, msgdata = connection.fetch(mail.msgid, '(RFC822)')
954+ if result != 'OK':
955+ logger.error('Could not fetch %s in %s on %s' % (
956+ msgid, folder.path, this.server))
957+ continue
958+
959+ mail_message = self.pool.get('mail.thread').message_parse(
960+ cr, uid, msgdata[0][1],
961+ save_original=this.folder_id.server_id.original,
962+ context=context)
963+
964+ this.folder_id.server_id.attach_mail(connection,
965+ mail.object_id.id, this.folder_id, mail_message,
966+ mail.msgid)
967+ connection.close()
968+ return {'type': 'ir.actions.act_window_close'}
969+
970+class attach_mail_manually_mail(TransientModel):
971+ _name = 'fetchmail.attach.mail.manually.mail'
972+
973+ _columns = {
974+ 'wizard_id': fields.many2one('fetchmail.attach.mail.manually',
975+ readonly=True),
976+ 'msgid': fields.char('Message id', size=16, readonly=True),
977+ 'subject': fields.char('Subject', size=128, readonly=True),
978+ 'date': fields.datetime('Date', readonly=True),
979+ 'object_id': fields.reference('Object',
980+ selection=lambda self, cr, uid, context:
981+ [(m.model, m.name) for m in
982+ self.pool.get('ir.model').browse(cr, uid,
983+ self.pool.get('ir.model').search(cr, uid, []),
984+ context)], size=128),
985+ }
986
987=== added file 'fetchmail_attach_from_folder/wizard/attach_mail_manually.xml'
988--- fetchmail_attach_from_folder/wizard/attach_mail_manually.xml 1970-01-01 00:00:00 +0000
989+++ fetchmail_attach_from_folder/wizard/attach_mail_manually.xml 2014-02-03 09:33:52 +0000
990@@ -0,0 +1,29 @@
991+<?xml version="1.0" encoding="utf-8"?>
992+<openerp>
993+ <data>
994+ <record model="ir.ui.view" id="view_attach_mail_manually">
995+ <field name="name">fetchmail.attach.mail.manually</field>
996+ <field name="model">fetchmail.attach.mail.manually</field>
997+ <field name="arch" type="xml">
998+ <form col="4" version="7.0" string="Attach mail manually">
999+ <sheet>
1000+ <group>
1001+ <field name="folder_id" />
1002+ <field name="mail_ids" nolabel="1" colspan="4">
1003+ <tree editable="top" create="0">
1004+ <field name="subject" />
1005+ <field name="date" />
1006+ <field name="object_id" />
1007+ </tree>
1008+ </field>
1009+ </group>
1010+ </sheet>
1011+ <footer>
1012+ <button string="Save" type="object" name="attach_mails" icon="gtk-ok" />
1013+ <button special="cancel" string="Cancel" icon="gtk-cancel" />
1014+ </footer>
1015+ </form>
1016+ </field>
1017+ </record>
1018+ </data>
1019+</openerp>