[REN] preliminary migration of fetchmail_attach_from_folder
parent
39f511e324
commit
c63acca18e
|
@ -0,0 +1,46 @@
|
||||||
|
Email gateway - folders
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Adds the possibility to attach emails from a certain IMAP folder to objects,
|
||||||
|
ie partners. Matching is done via several algorithms, ie email address, email
|
||||||
|
address's domain or the original Odoo algorithm.
|
||||||
|
|
||||||
|
This gives a simple possibility to archive emails in Odoo without a mail
|
||||||
|
client integration.
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
|
In your fetchmail configuration, you'll find a new field `folders`. Add your
|
||||||
|
folders here in IMAP notation [TODO]
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
A widespread configuration is to have a shared mailbox with several folders [TODO]
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Holger Brunn <hbrunn@therp.nl>
|
||||||
|
|
||||||
|
Icon
|
||||||
|
----
|
||||||
|
|
||||||
|
http://commons.wikimedia.org/wiki/File:Crystal_Clear_filesystem_folder_favorites.png
|
||||||
|
|
||||||
|
Maintainer
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. image:: http://odoo-community.org/logo.png
|
||||||
|
:alt: Odoo Community Association
|
||||||
|
:target: http://odoo-community.org
|
||||||
|
|
||||||
|
This module is maintained by the OCA.
|
||||||
|
|
||||||
|
OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.
|
||||||
|
|
||||||
|
To contribute to this module, please visit http://odoo-community.org.
|
|
@ -0,0 +1,25 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
from . import match_algorithm
|
||||||
|
from . import model
|
||||||
|
from . import wizard
|
|
@ -0,0 +1,38 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'Email gateway - folders',
|
||||||
|
'summary': 'Attach mails in an IMAP folder to existing objects',
|
||||||
|
'version': '1.0',
|
||||||
|
'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',
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'active': True,
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
from . import base
|
||||||
|
from . import email_exact
|
||||||
|
from . import email_domain
|
||||||
|
from . import openerp_standard
|
|
@ -0,0 +1,43 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
class base(object):
|
||||||
|
name = None
|
||||||
|
'''Name shown to the user'''
|
||||||
|
|
||||||
|
required_fields = []
|
||||||
|
'''Fields on fetchmail_server folder required for this algorithm'''
|
||||||
|
|
||||||
|
readonly_fields = []
|
||||||
|
'''Fields on fetchmail_server folder 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)
|
|
@ -0,0 +1,45 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
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
|
|
@ -0,0 +1,57 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
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)
|
|
@ -0,0 +1,58 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
from .base import base
|
||||||
|
|
||||||
|
|
||||||
|
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]
|
|
@ -0,0 +1,24 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
from . import fetchmail_server
|
||||||
|
from . import fetchmail_server_folder
|
|
@ -0,0 +1,263 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
import logging
|
||||||
|
import base64
|
||||||
|
import simplejson
|
||||||
|
from lxml import etree
|
||||||
|
from openerp import models, fields, api, exceptions
|
||||||
|
from openerp.tools.translate import _
|
||||||
|
from openerp.tools.misc import UnquoteEvalContext
|
||||||
|
|
||||||
|
|
||||||
|
class fetchmail_server(models.Model):
|
||||||
|
_inherit = 'fetchmail.server'
|
||||||
|
|
||||||
|
folder_ids = fields.One2many(
|
||||||
|
'fetchmail.server.folder', 'server_id', 'Folders')
|
||||||
|
object_id = fields.Many2one(required=True)
|
||||||
|
|
||||||
|
_defaults = {
|
||||||
|
'type': 'imap',
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def handle_folder(self, connection, folder):
|
||||||
|
'''Return ids of objects matched'''
|
||||||
|
|
||||||
|
matched_object_ids = []
|
||||||
|
|
||||||
|
for this in self:
|
||||||
|
logging.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':
|
||||||
|
logging.error(
|
||||||
|
'Could not open mailbox %s on %s',
|
||||||
|
folder.path, this.server)
|
||||||
|
connection.select()
|
||||||
|
continue
|
||||||
|
result, msgids = this.get_msgids(connection)
|
||||||
|
if result != 'OK':
|
||||||
|
logging.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)
|
||||||
|
|
||||||
|
logging.info(
|
||||||
|
'finished checking for emails in %s server %s',
|
||||||
|
folder.path, this.name)
|
||||||
|
|
||||||
|
return matched_object_ids
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def get_msgids(self, connection):
|
||||||
|
'''Return imap ids of messages to process'''
|
||||||
|
return connection.search(None, 'UNDELETED')
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def apply_matching(self, connection, folder, msgid, match_algorithm):
|
||||||
|
'''Return ids of objects matched'''
|
||||||
|
|
||||||
|
matched_object_ids = []
|
||||||
|
|
||||||
|
for this in self:
|
||||||
|
result, msgdata = connection.fetch(msgid, '(RFC822)')
|
||||||
|
|
||||||
|
if result != 'OK':
|
||||||
|
logging.error(
|
||||||
|
'Could not fetch %s in %s on %s',
|
||||||
|
msgid, folder.path, this.server)
|
||||||
|
continue
|
||||||
|
|
||||||
|
mail_message = self.env['mail.thread'].message_parse(
|
||||||
|
msgdata[0][1], save_original=this.original)
|
||||||
|
|
||||||
|
if self.env['mail.message'].search(
|
||||||
|
[('message_id', '=', mail_message['message_id'])]):
|
||||||
|
continue
|
||||||
|
|
||||||
|
found_ids = match_algorithm.search_matches(
|
||||||
|
self.env.cr, self.env.uid, folder, mail_message, msgdata[0][1])
|
||||||
|
|
||||||
|
if found_ids and (len(found_ids) == 1 or
|
||||||
|
folder.match_first):
|
||||||
|
try:
|
||||||
|
self.env.cr.execute('savepoint apply_matching')
|
||||||
|
match_algorithm.handle_match(
|
||||||
|
self.env.cr, self.env.uid, connection,
|
||||||
|
found_ids[0], folder, mail_message,
|
||||||
|
msgdata[0][1], msgid, self.env.context)
|
||||||
|
self.env.cr.execute('release savepoint apply_matching')
|
||||||
|
matched_object_ids += found_ids[:1]
|
||||||
|
except Exception:
|
||||||
|
self.env.cr.execute('rollback to savepoint apply_matching')
|
||||||
|
logging.exception(
|
||||||
|
"Failed to fetch mail %s from %s", msgid, this.name)
|
||||||
|
elif folder.flag_nonmatching:
|
||||||
|
connection.store(msgid, '+FLAGS', '\\FLAGGED')
|
||||||
|
|
||||||
|
return matched_object_ids
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def attach_mail(self, connection, object_id, folder, mail_message, msgid):
|
||||||
|
'''Return ids of messages created'''
|
||||||
|
|
||||||
|
mail_message_ids = []
|
||||||
|
|
||||||
|
for this in self:
|
||||||
|
partner_id = None
|
||||||
|
if folder.model_id.model == 'res.partner':
|
||||||
|
partner_id = object_id
|
||||||
|
if 'partner_id' in self.env[folder.model_id.model]._columns:
|
||||||
|
partner_id = self.env[folder.model_id.model].browse(object_id)\
|
||||||
|
.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.env['ir.attachment'].create(data_attach))
|
||||||
|
|
||||||
|
mail_message_ids.append(
|
||||||
|
self.env['mail.message'].create({
|
||||||
|
'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, [a.id for a in attachments])],
|
||||||
|
}))
|
||||||
|
|
||||||
|
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 exceptions.ValidationError(
|
||||||
|
_('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['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.xpath('//field'):
|
||||||
|
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
|
|
@ -0,0 +1,115 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
from openerp import api, models, fields
|
||||||
|
from .. import match_algorithm
|
||||||
|
|
||||||
|
|
||||||
|
class fetchmail_server_folder(models.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):
|
||||||
|
algorithms = []
|
||||||
|
for cls in self._get_match_algorithms().itervalues():
|
||||||
|
algorithms.append((cls.__name__, cls.name))
|
||||||
|
algorithms.sort()
|
||||||
|
return algorithms
|
||||||
|
|
||||||
|
sequence = fields.Integer('Sequence')
|
||||||
|
path = fields.Char(
|
||||||
|
'Path',
|
||||||
|
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)',
|
||||||
|
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)',
|
||||||
|
help='Field(s) to order by, this mostly useful in conjunction '
|
||||||
|
"with 'Use 1st match'")
|
||||||
|
match_algorithm = fields.Selection(
|
||||||
|
_get_match_algorithms_sel,
|
||||||
|
'Match algorithm', required=True,
|
||||||
|
help='The algorithm used to determine which object an email matches.')
|
||||||
|
mail_field = fields.Char(
|
||||||
|
'Field (email)',
|
||||||
|
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 Odoo")
|
||||||
|
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',
|
||||||
|
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 Odoo')
|
||||||
|
|
||||||
|
_defaults = {
|
||||||
|
'flag_nonmatching': True,
|
||||||
|
'msg_state': 'received',
|
||||||
|
}
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def get_algorithm(self):
|
||||||
|
return self._get_match_algorithms()[self.match_algorithm]()
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def button_attach_mail_manually(self):
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.act_window',
|
||||||
|
'res_model': 'fetchmail.attach.mail.manually',
|
||||||
|
'target': 'new',
|
||||||
|
'context': dict(self.env.context, default_folder_id=self.id),
|
||||||
|
'view_type': 'form',
|
||||||
|
'view_mode': 'form',
|
||||||
|
}
|
|
@ -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
|
|
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,56 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<openerp>
|
||||||
|
<data>
|
||||||
|
<record model="ir.ui.view" id="view_email_server_form">
|
||||||
|
<field name="name">fetchmail.server.form</field>
|
||||||
|
<field name="model">fetchmail.server</field>
|
||||||
|
<field name="inherit_id" ref="fetchmail.view_email_server_form" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<data>
|
||||||
|
<field name="object_id" position="attributes">
|
||||||
|
<attribute name="attrs">{'required': [('type', '!=', 'imap')]}</attribute>
|
||||||
|
</field>
|
||||||
|
<xpath expr="//page[@string='Server & Login']/group/group[3]" position="after">
|
||||||
|
<group attrs="{'invisible': [('type','!=','imap')]}" string="Folders to monitor">
|
||||||
|
<field
|
||||||
|
name="folder_ids"
|
||||||
|
nolabel="1"
|
||||||
|
on_change="onchange_server_type(type, is_ssl, object_id)">
|
||||||
|
<tree>
|
||||||
|
<field name="sequence" invisible="1" />
|
||||||
|
<field name="path" />
|
||||||
|
<field name="model_id" />
|
||||||
|
<field name="model_field" />
|
||||||
|
<field name="match_algorithm" />
|
||||||
|
<field name="mail_field" />
|
||||||
|
</tree>
|
||||||
|
<form version="7.0">
|
||||||
|
<header>
|
||||||
|
<button type="object" name="button_attach_mail_manually" string="Attach mail manually" icon="gtk-redo" />
|
||||||
|
</header>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="path" />
|
||||||
|
<field name="model_id" />
|
||||||
|
<field name="model_field" />
|
||||||
|
<field name="match_algorithm" />
|
||||||
|
<field name="mail_field" />
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="delete_matching" />
|
||||||
|
<field name="flag_nonmatching" />
|
||||||
|
<field name="match_first" />
|
||||||
|
<field name="msg_state" />
|
||||||
|
<field name="model_order" attrs="{'readonly': [('match_first','==',False)], 'required': [('match_first','==',True)]}" />
|
||||||
|
<field name="domain" />
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
</data>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</openerp>
|
|
@ -0,0 +1,23 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
from . import attach_mail_manually
|
|
@ -0,0 +1,129 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# This module copyright (C) 2013 Therp BV (<http://therp.nl>)
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
from openerp.osv import fields
|
||||||
|
from openerp.osv.orm import TransientModel
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
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, folder.server_id.name))
|
||||||
|
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, folder.server_id.name))
|
||||||
|
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' % (
|
||||||
|
mail.msgid, this.folder_id.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,
|
||||||
|
),
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<openerp>
|
||||||
|
<data>
|
||||||
|
<record model="ir.ui.view" id="view_attach_mail_manually">
|
||||||
|
<field name="name">fetchmail.attach.mail.manually</field>
|
||||||
|
<field name="model">fetchmail.attach.mail.manually</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form col="4" version="7.0" string="Attach mail manually">
|
||||||
|
<sheet>
|
||||||
|
<group>
|
||||||
|
<field name="folder_id" />
|
||||||
|
<field name="mail_ids" nolabel="1" colspan="4">
|
||||||
|
<tree editable="top" create="0">
|
||||||
|
<field name="subject" />
|
||||||
|
<field name="date" />
|
||||||
|
<field name="object_id" />
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
<footer>
|
||||||
|
<button string="Save" type="object" name="attach_mails" icon="gtk-ok" />
|
||||||
|
<button special="cancel" string="Cancel" icon="gtk-cancel" />
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</openerp>
|
Loading…
Reference in New Issue