Browse Source

[REN] preliminary migration of fetchmail_attach_from_folder

pull/123/head
Holger Brunn 10 years ago
parent
commit
862d5a6bb8
  1. 131
      __unported__/fetchmail_attach_from_folder/model/fetchmail_server_folder.py
  2. 46
      fetchmail_attach_from_folder/README.rst
  3. 5
      fetchmail_attach_from_folder/__init__.py
  4. 18
      fetchmail_attach_from_folder/__openerp__.py
  5. 8
      fetchmail_attach_from_folder/match_algorithm/__init__.py
  6. 4
      fetchmail_attach_from_folder/match_algorithm/base.py
  7. 4
      fetchmail_attach_from_folder/match_algorithm/email_domain.py
  8. 2
      fetchmail_attach_from_folder/match_algorithm/email_exact.py
  9. 2
      fetchmail_attach_from_folder/match_algorithm/openerp_standard.py
  10. 5
      fetchmail_attach_from_folder/model/__init__.py
  11. 147
      fetchmail_attach_from_folder/model/fetchmail_server.py
  12. 115
      fetchmail_attach_from_folder/model/fetchmail_server_folder.py
  13. 0
      fetchmail_attach_from_folder/security/ir.model.access.csv
  14. BIN
      fetchmail_attach_from_folder/static/description/icon.png
  15. 0
      fetchmail_attach_from_folder/view/fetchmail_server.xml
  16. 2
      fetchmail_attach_from_folder/wizard/__init__.py
  17. 4
      fetchmail_attach_from_folder/wizard/attach_mail_manually.py
  18. 0
      fetchmail_attach_from_folder/wizard/attach_mail_manually.xml

131
__unported__/fetchmail_attach_from_folder/model/fetchmail_server_folder.py

@ -1,131 +0,0 @@
# -*- 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 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',
}

46
fetchmail_attach_from_folder/README.rst

@ -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.

5
__unported__/fetchmail_attach_from_folder/model/__init__.py → fetchmail_attach_from_folder/__init__.py

@ -20,5 +20,6 @@
#
##############################################################################
import fetchmail_server
import fetchmail_server_folder
from . import match_algorithm
from . import model
from . import wizard

18
__unported__/fetchmail_attach_from_folder/__openerp__.py → fetchmail_attach_from_folder/__openerp__.py

@ -21,15 +21,9 @@
##############################################################################
{
'name': 'Attach mails in an IMAP folder to existing objects',
'name': 'Email gateway - folders',
'summary': '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",
@ -38,9 +32,7 @@ client integration.
'view/fetchmail_server.xml',
'wizard/attach_mail_manually.xml',
'security/ir.model.access.csv',
],
'js': [],
'installable': False,
'active': False,
'certificate': '',
],
'installable': True,
'active': True,
}

8
__unported__/fetchmail_attach_from_folder/match_algorithm/__init__.py → fetchmail_attach_from_folder/match_algorithm/__init__.py

@ -20,7 +20,7 @@
#
##############################################################################
import base
import email_exact
import email_domain
import openerp_standard
from . import base
from . import email_exact
from . import email_domain
from . import openerp_standard

4
__unported__/fetchmail_attach_from_folder/match_algorithm/base.py → fetchmail_attach_from_folder/match_algorithm/base.py

@ -26,10 +26,10 @@ class base(object):
'''Name shown to the user'''
required_fields = []
'''Fields on fetchmail_server folder that are required for this algorithm'''
'''Fields on fetchmail_server folder required for this algorithm'''
readonly_fields = []
'''Fields on fetchmail_server folder that are readonly for this algorithm'''
'''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'''

4
__unported__/fetchmail_attach_from_folder/match_algorithm/email_domain.py → fetchmail_attach_from_folder/match_algorithm/email_domain.py

@ -20,7 +20,7 @@
#
##############################################################################
from email_exact import email_exact
from .email_exact import email_exact
class email_domain(email_exact):
@ -40,6 +40,6 @@ class email_domain(email_exact):
self._get_mailaddress_search_domain(
conf, mail_message,
operator='like',
values=['%@'+domain for domain in set(domains)]),
values=['%@' + domain for domain in set(domains)]),
order=conf.model_order)
return ids

2
__unported__/fetchmail_attach_from_folder/match_algorithm/email_exact.py → fetchmail_attach_from_folder/match_algorithm/email_exact.py

@ -20,7 +20,7 @@
#
##############################################################################
from base import base
from .base import base
from openerp.tools.safe_eval import safe_eval
from openerp.tools.mail import email_split

2
__unported__/fetchmail_attach_from_folder/match_algorithm/openerp_standard.py → fetchmail_attach_from_folder/match_algorithm/openerp_standard.py

@ -20,7 +20,7 @@
#
##############################################################################
from base import base
from .base import base
class openerp_standard(base):

5
__unported__/fetchmail_attach_from_folder/__init__.py → fetchmail_attach_from_folder/model/__init__.py

@ -20,6 +20,5 @@
#
##############################################################################
import match_algorithm
import model
import wizard
from . import fetchmail_server
from . import fetchmail_server_folder

147
__unported__/fetchmail_attach_from_folder/model/fetchmail_server.py → fetchmail_attach_from_folder/model/fetchmail_server.py

@ -19,33 +19,26 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import logging
import base64
import simplejson
from lxml import etree
from openerp.osv.orm import Model, except_orm
from openerp import models, fields, api, exceptions
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
class fetchmail_server(Model):
class fetchmail_server(models.Model):
_inherit = 'fetchmail.server'
_columns = {
'folder_ids': fields.one2many(
'fetchmail.server.folder', 'server_id', 'Folders'),
}
folder_ids = fields.One2many(
'fetchmail.server.folder', 'server_id', 'Folders')
object_id = fields.Many2one(required=True)
_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):
@ -75,111 +68,109 @@ class fetchmail_server(Model):
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):
@api.multi
def handle_folder(self, connection, folder):
'''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)
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':
logger.error(
'Could not open mailbox %s on %s' % (folder.path, this.server))
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':
logger.error(
'Could not search mailbox %s on %s' % (
folder.path, this.server))
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)
logger.info('finished checking for emails in %s server %s',
folder.path, this.name)
logging.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):
@api.multi
def get_msgids(self, connection):
'''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):
@api.multi
def apply_matching(self, connection, folder, msgid, match_algorithm):
'''Return ids of objects matched'''
matched_object_ids = []
for this in self.browse(cr, uid, ids, context=context):
for this in self:
result, msgdata = connection.fetch(msgid, '(RFC822)')
if result != 'OK':
logger.error(
'Could not fetch %s in %s on %s' % (msgid, folder.path, this.server))
logging.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)
mail_message = self.env['mail.thread'].message_parse(
msgdata[0][1], save_original=this.original)
if self.pool.get('mail.message').search(
cr, uid, [
('message_id', '=', mail_message['message_id'])]):
if self.env['mail.message'].search(
[('message_id', '=', mail_message['message_id'])]):
continue
found_ids = match_algorithm.search_matches(
cr, uid, folder,
mail_message, msgdata[0][1])
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:
cr.execute('savepoint apply_matching')
self.env.cr.execute('savepoint apply_matching')
match_algorithm.handle_match(
cr, uid, connection,
self.env.cr, self.env.uid, connection,
found_ids[0], folder, mail_message,
msgdata[0][1], msgid, context)
cr.execute('release savepoint apply_matching')
msgdata[0][1], msgid, self.env.context)
self.env.cr.execute('release savepoint apply_matching')
matched_object_ids += found_ids[:1]
except Exception:
cr.execute('rollback to savepoint apply_matching')
logger.exception(
"Failed to fetch mail %s from %s",
msgid, this.name)
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
def attach_mail(
self, cr, uid, ids, connection, object_id, folder,
mail_message, msgid, context=None):
@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.browse(cr, uid, ids, context):
for this in self:
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
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'):
@ -196,34 +187,29 @@ class fetchmail_server(Model):
'res_id': object_id,
}
attachments.append(
self.pool.get('ir.attachment').create(
cr, uid, data_attach, context=context))
self.env['ir.attachment'].create(data_attach))
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))
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)
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'})
@ -231,9 +217,8 @@ class fetchmail_server(Model):
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)
raise exceptions.ValidationError(
_('Mailbox %s not found!') % folder.path)
connection.close()
this.write({'state': 'done'})
@ -249,7 +234,7 @@ class fetchmail_server(Model):
result['fields']['folder_ids']['views']['form']['arch'])
modifiers = {}
docstr = ''
for algorithm in self.pool.get('fetchmail.server.folder')\
for algorithm in self.pool['fetchmail.server.folder']\
._get_match_algorithms().itervalues():
for modifier in ['required', 'readonly']:
for field in getattr(algorithm, modifier + '_fields'):
@ -262,7 +247,7 @@ class fetchmail_server(Model):
docstr += _(algorithm.name) + '\n' + _(algorithm.__doc__) + \
'\n\n'
for field in view:
for field in view.xpath('//field'):
if field.tag == 'field' and field.get('name') in modifiers:
field.set('modifiers', simplejson.dumps(
dict(

115
fetchmail_attach_from_folder/model/fetchmail_server_folder.py

@ -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
__unported__/fetchmail_attach_from_folder/security/ir.model.access.csv → fetchmail_attach_from_folder/security/ir.model.access.csv

BIN
fetchmail_attach_from_folder/static/description/icon.png

After

Width: 128  |  Height: 128  |  Size: 12 KiB

0
__unported__/fetchmail_attach_from_folder/view/fetchmail_server.xml → fetchmail_attach_from_folder/view/fetchmail_server.xml

2
__unported__/fetchmail_attach_from_folder/wizard/__init__.py → fetchmail_attach_from_folder/wizard/__init__.py

@ -20,4 +20,4 @@
#
##############################################################################
import attach_mail_manually
from . import attach_mail_manually

4
__unported__/fetchmail_attach_from_folder/wizard/attach_mail_manually.py → fetchmail_attach_from_folder/wizard/attach_mail_manually.py

@ -74,8 +74,8 @@ class attach_mail_manually(TransientModel):
'msgid': msgid,
'subject': mail_message.get('subject', ''),
'date': mail_message.get('date', ''),
'object_id': folder.model_id.model+',False'
}))
'object_id': folder.model_id.model + ',False'
}))
connection.close()
return defaults

0
__unported__/fetchmail_attach_from_folder/wizard/attach_mail_manually.xml → fetchmail_attach_from_folder/wizard/attach_mail_manually.xml

Loading…
Cancel
Save