diff --git a/fetchmail_attach_from_folder/__init__.py b/fetchmail_attach_from_folder/__init__.py new file mode 100644 index 000000000..1c91fe478 --- /dev/null +++ b/fetchmail_attach_from_folder/__init__.py @@ -0,0 +1,25 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV () +# All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import match_algorithm +import model +import wizard diff --git a/fetchmail_attach_from_folder/__openerp__.py b/fetchmail_attach_from_folder/__openerp__.py new file mode 100644 index 000000000..896701e4e --- /dev/null +++ b/fetchmail_attach_from_folder/__openerp__.py @@ -0,0 +1,46 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV () +# All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +{ + 'name': 'Attach mails in an IMAP folder to existing objects', + 'version': '1.0', + 'description': """ +Adds the possibility to attach emails from a certain IMAP folder to objects, +ie partners. Matching is done via several algorithms, ie email address. + +This gives a simple possibility to archive emails in OpenERP without a mail +client integration. + """, + 'author': 'Therp BV', + 'website': 'http://www.therp.nl', + "category": "Tools", + "depends": ['fetchmail'], + 'data': [ + 'view/fetchmail_server.xml', + 'wizard/attach_mail_manually.xml', + 'security/ir.model.access.csv', + ], + 'js': [], + 'installable': True, + 'active': False, + 'certificate': '', +} diff --git a/fetchmail_attach_from_folder/match_algorithm/__init__.py b/fetchmail_attach_from_folder/match_algorithm/__init__.py new file mode 100644 index 000000000..ff3610863 --- /dev/null +++ b/fetchmail_attach_from_folder/match_algorithm/__init__.py @@ -0,0 +1,26 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV () +# All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import base +import email_exact +import email_domain +import openerp_standard diff --git a/fetchmail_attach_from_folder/match_algorithm/base.py b/fetchmail_attach_from_folder/match_algorithm/base.py new file mode 100644 index 000000000..5116c929a --- /dev/null +++ b/fetchmail_attach_from_folder/match_algorithm/base.py @@ -0,0 +1,43 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV () +# All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +class base(object): + name = None + '''Name shown to the user''' + + required_fields = [] + '''Fields on fetchmail_server folder that are required for this algorithm''' + + readonly_fields = [] + '''Fields on fetchmail_server folder that are readonly for this algorithm''' + + + def search_matches(self, cr, uid, conf, mail_message, mail_message_org): + '''Returns ids found for model with mail_message''' + return [] + + def handle_match( + self, cr, uid, connection, object_id, folder, + mail_message, mail_message_org, msgid, context=None): + '''Do whatever it takes to handle a match''' + return folder.server_id.attach_mail(connection, object_id, folder, + mail_message, msgid) diff --git a/fetchmail_attach_from_folder/match_algorithm/email_domain.py b/fetchmail_attach_from_folder/match_algorithm/email_domain.py new file mode 100644 index 000000000..66ab66286 --- /dev/null +++ b/fetchmail_attach_from_folder/match_algorithm/email_domain.py @@ -0,0 +1,44 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV () +# All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from email_exact import email_exact + +class email_domain(email_exact): + '''Search objects by domain name of email address. + Beware of match_first here, this is most likely to get it wrong (gmail)''' + name = 'Domain of email address' + + def search_matches(self, cr, uid, conf, mail_message, mail_message_org): + ids = super(email_domain, self).search_matches( + cr, uid, conf, mail_message, mail_message_org) + if not ids: + domains = [] + for addr in self._get_mailaddresses(conf, mail_message): + domains.append(addr.split('@')[-1]) + ids = conf.pool.get(conf.model_id.model).search( + cr, uid, + self._get_mailaddress_search_domain( + conf, mail_message, + operator='like', + values=['%@'+domain for domain in set(domains)]), + order=conf.model_order) + return ids diff --git a/fetchmail_attach_from_folder/match_algorithm/email_exact.py b/fetchmail_attach_from_folder/match_algorithm/email_exact.py new file mode 100644 index 000000000..728c04461 --- /dev/null +++ b/fetchmail_attach_from_folder/match_algorithm/email_exact.py @@ -0,0 +1,56 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV () +# All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from base import base +from openerp.tools.safe_eval import safe_eval +from openerp.tools.mail import email_split + +class email_exact(base): + '''Search for exactly the mailadress as noted in the email''' + + name = 'Exact mailadress' + required_fields = ['model_field', 'mail_field'] + + def _get_mailaddresses(self, conf, mail_message): + mailaddresses = [] + fields = conf.mail_field.split(',') + for field in fields: + if field in mail_message: + mailaddresses += email_split(mail_message[field]) + return [ addr.lower() for addr in mailaddresses ] + + def _get_mailaddress_search_domain( + self, conf, mail_message, operator='=', values=None): + mailaddresses = values or self._get_mailaddresses( + conf, mail_message) + if not mailaddresses: + return [(0, '=', 1)] + search_domain = ((['|'] * (len(mailaddresses) - 1)) + [ + (conf.model_field, operator, addr) for addr in mailaddresses] + + safe_eval(conf.domain or '[]')) + return search_domain + + def search_matches(self, cr, uid, conf, mail_message, mail_message_org): + conf_model = conf.pool.get(conf.model_id.model) + search_domain = self._get_mailaddress_search_domain(conf, mail_message) + return conf_model.search( + cr, uid, search_domain, order=conf.model_order) diff --git a/fetchmail_attach_from_folder/match_algorithm/openerp_standard.py b/fetchmail_attach_from_folder/match_algorithm/openerp_standard.py new file mode 100644 index 000000000..24a233d0d --- /dev/null +++ b/fetchmail_attach_from_folder/match_algorithm/openerp_standard.py @@ -0,0 +1,51 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV () +# All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from base import base +from openerp.tools.safe_eval import safe_eval + +class openerp_standard(base): + '''No search at all. Use OpenERP's standard mechanism to attach mails to + mail.thread objects. Note that this algorithm always matches.''' + + name = 'OpenERP standard' + readonly_fields = ['model_field', 'mail_field', 'match_first', 'domain', + 'model_order', 'flag_nonmatching'] + + def search_matches(self, cr, uid, conf, mail_message, mail_message_org): + '''Always match. Duplicates will be fished out by message_id''' + return [True] + + def handle_match( + self, cr, uid, connection, object_id, folder, + mail_message, mail_message_org, msgid, context): + result = folder.pool.get('mail.thread').message_process( + cr, uid, + folder.model_id.model, mail_message_org, + save_original=folder.server_id.original, + strip_attachments=(not folder.server_id.attach), + context=context) + + if folder.delete_matching: + connection.store(msgid, '+FLAGS', '\\DELETED') + + return [result] diff --git a/fetchmail_attach_from_folder/model/__init__.py b/fetchmail_attach_from_folder/model/__init__.py new file mode 100644 index 000000000..d7e030949 --- /dev/null +++ b/fetchmail_attach_from_folder/model/__init__.py @@ -0,0 +1,24 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV () +# All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import fetchmail_server +import fetchmail_server_folder diff --git a/fetchmail_attach_from_folder/model/fetchmail_server.py b/fetchmail_attach_from_folder/model/fetchmail_server.py new file mode 100644 index 000000000..814da703c --- /dev/null +++ b/fetchmail_attach_from_folder/model/fetchmail_server.py @@ -0,0 +1,280 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV () +# All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import base64 +import simplejson +from lxml import etree +from openerp.osv.orm import Model, except_orm, browse_null +from openerp.tools.translate import _ +from openerp.osv import fields +from openerp.addons.fetchmail.fetchmail import _logger as logger +from openerp.tools.misc import UnquoteEvalContext +from openerp.tools.safe_eval import safe_eval + + +class fetchmail_server(Model): + _inherit = 'fetchmail.server' + + _columns = { + 'folder_ids': fields.one2many( + 'fetchmail.server.folder', 'server_id', 'Folders'), + } + + _defaults = { + 'type': 'imap', + } + + def __init__(self, pool, cr): + self._columns['object_id'].required = False + return super(fetchmail_server, self).__init__(pool, cr) + + def onchange_server_type( + self, cr, uid, ids, server_type=False, ssl=False, + object_id=False): + retval = super( + fetchmail_server, self).onchange_server_type(cr, uid, + ids, server_type, ssl, + object_id) + retval['value']['state'] = 'draft' + return retval + + def fetch_mail(self, cr, uid, ids, context=None): + if context is None: + context = {} + + check_original = [] + + for this in self.browse(cr, uid, ids, context): + if this.object_id: + check_original.append(this.id) + + context.update( + { + 'fetchmail_server_id': this.id, + 'server_type': this.type + }) + + connection = this.connect() + for folder in this.folder_ids: + this.handle_folder(connection, folder) + + connection.close() + + return super(fetchmail_server, self).fetch_mail( + cr, uid, check_original, context) + + def handle_folder(self, cr, uid, ids, connection, folder, context=None): + '''Return ids of objects matched''' + + matched_object_ids = [] + + for this in self.browse(cr, uid, ids, context=context): + logger.info('start checking for emails in %s server %s', + folder.path, this.name) + + match_algorithm = folder.get_algorithm() + + if connection.select(folder.path)[0] != 'OK': + logger.error( + 'Could not open mailbox %s on %s' % ( + folder.path, this.server)) + connection.select() + continue + result, msgids = this.get_msgids(connection) + if result != 'OK': + logger.error( + 'Could not search mailbox %s on %s' % ( + folder.path, this.server)) + continue + + for msgid in msgids[0].split(): + matched_object_ids += this.apply_matching( + connection, folder, msgid, match_algorithm) + + logger.info('finished checking for emails in %s server %s', + folder.path, this.name) + + return matched_object_ids + + def get_msgids(self, cr, uid, ids, connection, context=None): + '''Return imap ids of messages to process''' + return connection.search(None, 'UNDELETED') + + def apply_matching(self, cr, uid, ids, connection, folder, msgid, + match_algorithm, context=None): + '''Return ids of objects matched''' + + matched_object_ids = [] + + for this in self.browse(cr, uid, ids, context=context): + result, msgdata = connection.fetch(msgid, '(RFC822)') + + if result != 'OK': + logger.error( + 'Could not fetch %s in %s on %s' % ( + msgid, folder.path, this.server)) + continue + + mail_message = self.pool.get('mail.thread').message_parse( + cr, uid, msgdata[0][1], save_original=this.original, + context=context) + + if self.pool.get('mail.message').search(cr, uid, [ + ('message_id', '=', mail_message['message_id'])]): + continue + + found_ids = match_algorithm.search_matches( + cr, uid, folder, + mail_message, msgdata[0][1]) + + if found_ids and (len(found_ids) == 1 or + folder.match_first): + try: + cr.execute('savepoint apply_matching') + match_algorithm.handle_match( + cr, uid, connection, + found_ids[0], folder, mail_message, + msgdata[0][1], msgid, context) + cr.execute('release savepoint apply_matching') + matched_object_ids += found_ids[:1] + except Exception, e: + cr.execute('rollback to savepoint apply_matching') + logger.exception( + "Failed to fetch mail %s from %s", + msgid, this.name) + elif folder.flag_nonmatching: + connection.store(msgid, '+FLAGS', '\\FLAGGED') + + return matched_object_ids + + def attach_mail( + self, cr, uid, ids, connection, object_id, folder, + mail_message, msgid, context=None): + '''Return ids of messages created''' + + mail_message_ids = [] + + for this in self.browse(cr, uid, ids, context): + partner_id = None + if folder.model_id.model == 'res.partner': + partner_id = object_id + if 'partner_id' in self.pool.get(folder.model_id.model)._columns: + partner_id = self.pool.get( + folder.model_id.model).browse( + cr, uid, object_id, context + ).partner_id.id + + attachments=[] + if this.attach and mail_message.get('attachments'): + for attachment in mail_message['attachments']: + fname, fcontent = attachment + if isinstance(fcontent, unicode): + fcontent = fcontent.encode('utf-8') + data_attach = { + 'name': fname, + 'datas': base64.b64encode(str(fcontent)), + 'datas_fname': fname, + 'description': _('Mail attachment'), + 'res_model': folder.model_id.model, + 'res_id': object_id, + } + attachments.append( + self.pool.get('ir.attachment').create( + cr, uid, data_attach, context=context)) + + mail_message_ids.append( + self.pool.get('mail.message').create( + cr, uid, + { + 'author_id': partner_id, + 'model': folder.model_id.model, + 'res_id': object_id, + 'type': 'email', + 'body': mail_message.get('body'), + 'subject': mail_message.get('subject'), + 'email_from': mail_message.get('from'), + 'date': mail_message.get('date'), + 'message_id': mail_message.get('message_id'), + 'attachment_ids': [(6, 0, attachments)], + }, + context)) + + if folder.delete_matching: + connection.store(msgid, '+FLAGS', '\\DELETED') + return mail_message_ids + + def button_confirm_login(self, cr, uid, ids, context=None): + retval = super(fetchmail_server, self).button_confirm_login(cr, uid, + ids, + context) + + for this in self.browse(cr, uid, ids, context): + this.write({'state': 'draft'}) + connection = this.connect() + connection.select() + for folder in this.folder_ids: + if connection.select(folder.path)[0] != 'OK': + raise except_orm( + _('Error'), _('Mailbox %s not found!') % + folder.path) + connection.close() + this.write({'state': 'done'}) + + return retval + + def fields_view_get(self, cr, user, view_id=None, view_type='form', + context=None, toolbar=False, submenu=False): + result = super(fetchmail_server, self).fields_view_get( + cr, user, view_id, view_type, context, toolbar, submenu) + + if view_type == 'form': + view = etree.fromstring( + result['fields']['folder_ids']['views']['form']['arch']) + modifiers = {} + docstr = '' + for algorithm in self.pool.get('fetchmail.server.folder')\ + ._get_match_algorithms().itervalues(): + for modifier in ['required', 'readonly']: + for field in getattr(algorithm, modifier + '_fields'): + modifiers.setdefault(field, {}) + modifiers[field].setdefault(modifier, []) + if modifiers[field][modifier]: + modifiers[field][modifier].insert(0, '|') + modifiers[field][modifier].append( + ("match_algorithm", "==", algorithm.__name__)) + docstr += _(algorithm.name) + '\n' + _(algorithm.__doc__) + \ + '\n\n' + + for field in view: + if field.tag == 'field' and field.get('name') in modifiers: + field.set('modifiers', simplejson.dumps( + dict( + eval(field.attrib['modifiers'], + UnquoteEvalContext({})), + **modifiers[field.attrib['name']]))) + if (field.tag == 'field' and + field.get('name') == 'match_algorithm'): + field.set('help', docstr) + result['fields']['folder_ids']['views']['form']['arch'] = \ + etree.tostring(view) + + return result diff --git a/fetchmail_attach_from_folder/model/fetchmail_server_folder.py b/fetchmail_attach_from_folder/model/fetchmail_server_folder.py new file mode 100644 index 000000000..ea0c07a7b --- /dev/null +++ b/fetchmail_attach_from_folder/model/fetchmail_server_folder.py @@ -0,0 +1,120 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV () +# All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +######################################################################## + +from openerp.osv import fields +from openerp.osv.orm import Model +from .. import match_algorithm + + +class fetchmail_server_folder(Model): + _name = 'fetchmail.server.folder' + _rec_name = 'path' + + def _get_match_algorithms(self): + def get_all_subclasses(cls): + return cls.__subclasses__() + [subsub + for sub in cls.__subclasses__() + for subsub in get_all_subclasses(sub)] + return dict([(cls.__name__, cls) for cls in get_all_subclasses( + match_algorithm.base.base)]) + + def _get_match_algorithms_sel(self, cr, uid, context=None): + algorithms = [] + for cls in self._get_match_algorithms().itervalues(): + algorithms.append((cls.__name__, cls.name)) + algorithms.sort() + return algorithms + + _columns = { + 'sequence': fields.integer('Sequence'), + 'path': fields.char( + 'Path', size=256, help='The path to your mail ' + "folder. Typically would be something like 'INBOX.myfolder'", + required=True), + 'model_id': fields.many2one( + 'ir.model', 'Model', required=True, + help='The model to attach emails to'), + 'model_field': fields.char( + 'Field (model)', size=128, + help='The field in your model that contains the field to match ' + 'against.\n' + 'Examples:\n' + "'email' if your model is res.partner, or " + "'partner_id.email' if you're matching sale orders"), + 'model_order': fields.char( + 'Order (model)', size=128, + help='Fields to order by, this mostly useful in conjunction ' + "with 'Use 1st match'"), + 'match_algorithm': fields.selection( + _get_match_algorithms_sel, + 'Match algorithm', required=True, translate=True, + help='The algorithm used to determine which object an email ' + 'matches.'), + 'mail_field': fields.char( + 'Field (email)', size=128, + help='The field in the email used for matching. Typically ' + "this is 'to' or 'from'"), + 'server_id': fields.many2one('fetchmail.server', 'Server'), + 'delete_matching': fields.boolean( + 'Delete matches', + help='Delete matched emails from server'), + 'flag_nonmatching': fields.boolean( + 'Flag nonmatching', + help="Flag emails in the server that don't match any object " + 'in OpenERP'), + 'match_first': fields.boolean( + 'Use 1st match', + help='If there are multiple matches, use the first one. If ' + 'not checked, multiple matches count as no match at all'), + 'domain': fields.char( + 'Domain', size=128, help='Fill in a search ' + 'filter to narrow down objects to match'), + 'msg_state': fields.selection( + [ + ('sent', 'Sent'), + ('received', 'Received'), + ], + 'Message state', + help='The state messages fetched from this folder should be ' + 'assigned in OpenERP'), + } + + _defaults = { + 'flag_nonmatching': True, + 'msg_state': 'received', + } + + def get_algorithm(self, cr, uid, ids, context=None): + for this in self.browse(cr, uid, ids, context): + return self._get_match_algorithms()[this.match_algorithm]() + + def button_attach_mail_manually(self, cr, uid, ids, context=None): + for this in self.browse(cr, uid, ids, context): + context.update({'default_folder_id': this.id}) + return { + 'type': 'ir.actions.act_window', + 'res_model': 'fetchmail.attach.mail.manually', + 'target': 'new', + 'context': context, + 'view_type': 'form', + 'view_mode': 'form', + } diff --git a/fetchmail_attach_from_folder/security/ir.model.access.csv b/fetchmail_attach_from_folder/security/ir.model.access.csv new file mode 100755 index 000000000..c63f46bb8 --- /dev/null +++ b/fetchmail_attach_from_folder/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_model_fetchmail_server_folder,fetchmail.server.folder,model_fetchmail_server_folder,base.group_system,1,1,1,1 diff --git a/fetchmail_attach_from_folder/view/fetchmail_server.xml b/fetchmail_attach_from_folder/view/fetchmail_server.xml new file mode 100644 index 000000000..49384b039 --- /dev/null +++ b/fetchmail_attach_from_folder/view/fetchmail_server.xml @@ -0,0 +1,56 @@ + + + + + fetchmail.server.form + fetchmail.server + + + + + {'required': [('type', '!=', 'imap')]} + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
diff --git a/fetchmail_attach_from_folder/wizard/__init__.py b/fetchmail_attach_from_folder/wizard/__init__.py new file mode 100644 index 000000000..376a5b392 --- /dev/null +++ b/fetchmail_attach_from_folder/wizard/__init__.py @@ -0,0 +1,23 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV () +# All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import attach_mail_manually diff --git a/fetchmail_attach_from_folder/wizard/attach_mail_manually.py b/fetchmail_attach_from_folder/wizard/attach_mail_manually.py new file mode 100644 index 000000000..d1a16fa1f --- /dev/null +++ b/fetchmail_attach_from_folder/wizard/attach_mail_manually.py @@ -0,0 +1,114 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013 Therp BV () +# All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.osv import fields +from openerp.osv.orm import TransientModel + + +class attach_mail_manually(TransientModel): + _name = 'fetchmail.attach.mail.manually' + + _columns = { + 'folder_id': fields.many2one('fetchmail.server.folder', 'Folder', + readonly=True), + 'mail_ids': fields.one2many( + 'fetchmail.attach.mail.manually.mail', 'wizard_id', 'Emails'), + } + + def default_get(self, cr, uid, fields_list, context=None): + if context is None: + context = {} + + defaults = super(attach_mail_manually, self).default_get(cr, uid, + fields_list, context) + + for folder in self.pool.get('fetchmail.server.folder').browse(cr, uid, + [context.get('default_folder_id')], context): + defaults['mail_ids']=[] + connection = folder.server_id.connect() + connection.select(folder.path) + result, msgids = connection.search(None, + 'FLAGGED' if folder.flag_nonmatching else 'UNDELETED') + if result != 'OK': + logger.error('Could not search mailbox %s on %s' % ( + folder.path, this.server)) + continue + attach_mail_manually_mail._columns['object_id'].selection=[ + (folder.model_id.model, folder.model_id.name)] + for msgid in msgids[0].split(): + result, msgdata = connection.fetch(msgid, '(RFC822)') + if result != 'OK': + logger.error('Could not fetch %s in %s on %s' % ( + msgid, folder.path, this.server)) + continue + mail_message = self.pool.get('mail.thread').message_parse( + cr, uid, msgdata[0][1], + save_original=folder.server_id.original, + context=context) + defaults['mail_ids'].append((0, 0, { + 'msgid': msgid, + 'subject': mail_message.get('subject', ''), + 'date': mail_message.get('date', ''), + 'object_id': folder.model_id.model+',False' + })) + connection.close() + + return defaults + + def attach_mails(self, cr, uid, ids, context=None): + for this in self.browse(cr, uid, ids, context): + for mail in this.mail_ids: + connection = this.folder_id.server_id.connect() + connection.select(this.folder_id.path) + result, msgdata = connection.fetch(mail.msgid, '(RFC822)') + if result != 'OK': + logger.error('Could not fetch %s in %s on %s' % ( + msgid, folder.path, this.server)) + continue + + mail_message = self.pool.get('mail.thread').message_parse( + cr, uid, msgdata[0][1], + save_original=this.folder_id.server_id.original, + context=context) + + this.folder_id.server_id.attach_mail(connection, + mail.object_id.id, this.folder_id, mail_message, + mail.msgid) + connection.close() + return {'type': 'ir.actions.act_window_close'} + +class attach_mail_manually_mail(TransientModel): + _name = 'fetchmail.attach.mail.manually.mail' + + _columns = { + 'wizard_id': fields.many2one('fetchmail.attach.mail.manually', + readonly=True), + 'msgid': fields.char('Message id', size=16, readonly=True), + 'subject': fields.char('Subject', size=128, readonly=True), + 'date': fields.datetime('Date', readonly=True), + 'object_id': fields.reference('Object', + selection=lambda self, cr, uid, context: + [(m.model, m.name) for m in + self.pool.get('ir.model').browse(cr, uid, + self.pool.get('ir.model').search(cr, uid, []), + context)], size=128), + } diff --git a/fetchmail_attach_from_folder/wizard/attach_mail_manually.xml b/fetchmail_attach_from_folder/wizard/attach_mail_manually.xml new file mode 100644 index 000000000..fbe82eea7 --- /dev/null +++ b/fetchmail_attach_from_folder/wizard/attach_mail_manually.xml @@ -0,0 +1,29 @@ + + + + + fetchmail.attach.mail.manually + fetchmail.attach.mail.manually + +
+ + + + + + + + + + + + + +
+
+
+
+