Browse Source

Merge pull request #121 from hbrunn/8.0-fetchmail_attach_from_folder

[ADD] port fetchmail_attach_from_folder
pull/126/head
Pedro M. Baeza 10 years ago
parent
commit
f7e88259c8
  1. 131
      __unported__/fetchmail_attach_from_folder/model/fetchmail_server_folder.py
  2. 29
      __unported__/fetchmail_attach_from_folder/wizard/attach_mail_manually.xml
  3. 83
      fetchmail_attach_from_folder/README.rst
  4. 5
      fetchmail_attach_from_folder/__init__.py
  5. 18
      fetchmail_attach_from_folder/__openerp__.py
  6. 8
      fetchmail_attach_from_folder/match_algorithm/__init__.py
  7. 4
      fetchmail_attach_from_folder/match_algorithm/base.py
  8. 4
      fetchmail_attach_from_folder/match_algorithm/email_domain.py
  9. 2
      fetchmail_attach_from_folder/match_algorithm/email_exact.py
  10. 4
      fetchmail_attach_from_folder/match_algorithm/openerp_standard.py
  11. 5
      fetchmail_attach_from_folder/model/__init__.py
  12. 148
      fetchmail_attach_from_folder/model/fetchmail_server.py
  13. 115
      fetchmail_attach_from_folder/model/fetchmail_server_folder.py
  14. 0
      fetchmail_attach_from_folder/security/ir.model.access.csv
  15. BIN
      fetchmail_attach_from_folder/static/description/icon.png
  16. 21
      fetchmail_attach_from_folder/tests/__init__.py
  17. 103
      fetchmail_attach_from_folder/tests/test_match_algorithms.py
  18. 10
      fetchmail_attach_from_folder/view/fetchmail_server.xml
  19. 2
      fetchmail_attach_from_folder/wizard/__init__.py
  20. 82
      fetchmail_attach_from_folder/wizard/attach_mail_manually.py
  21. 28
      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',
}

29
__unported__/fetchmail_attach_from_folder/wizard/attach_mail_manually.xml

@ -1,29 +0,0 @@
<?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>

83
fetchmail_attach_from_folder/README.rst

@ -0,0 +1,83 @@
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 list field `Folders to
monitor`. Add your folders here in IMAP notation (usually something like
`INBOX.your_folder_name.your_subfolder_name`), choose a model to attach mails
to and a matching algorithm to use.
Exact mailaddress
-----------------
Fill in a field to search for the email address in `Field (model)`. For
partners, this would be `email`. Also fill in the header field from the email
to look at in `Field (email)`. If you want to match incoming mails from your
customers, this would be `from`. You can also list header fields, so to match
partners receiving this email, you might fill in `to,cc,bcc`.
Domain of email addresses
-------------------------
Match the domain of the email address(es) found in `Field (email)`. This would
attach a mail to `test1@example.com` to a record with `Field (model)` set to
`test2@example.com`. Given that this is a fuzzy match, you probably want to
check `Use 1st match`, because otherwise nothing happens if multiple possible
matches are found.
Odoo standard
-------------
This is stricly speaking no matching algorithm, but calls the model's standard
action on new incoming mail, which is usually creating a new record.
Usage
=====
A widespread configuration is to have a shared mailbox with several folders,
i.e. one where users drop mails they want to attach to partners. Let this
folder be called `From partners`. Then create a folder configuration for your
server with path `"INBOX.From partners"` (note the quotes because of the space,
this is server dependent). Choose model `Partners`, set `Field (model)` to
`email` and `Field (email)` to `from`. In `Domain`, you could fill in
`[('customer', '=', True)]` to be sure to only match customer records.
Now when your users drop mails into this folder, they will be fetched by Odoo
and attached to the partner in question. After some testing, you might want to
check `Delete matches` in your folder configuration so that this folder doesn't
grow indefinitely.
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

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

@ -20,14 +20,14 @@
#
##############################################################################
from base import base
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'
name = 'Odoo standard'
readonly_fields = [
'model_field',
'mail_field',

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

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

@ -19,33 +19,27 @@
# 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
_logger = logging.getLogger(__name__)
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=False)
_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 +69,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:
_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))
_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))
_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)
_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):
@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))
_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)
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')
_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):
@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 +188,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 +218,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 +235,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 +248,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

21
fetchmail_attach_from_folder/tests/__init__.py

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2015 Therp BV (<http://therp.nl>).
#
# 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 test_match_algorithms

103
fetchmail_attach_from_folder/tests/test_match_algorithms.py

@ -0,0 +1,103 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2015 Therp BV (<http://therp.nl>).
#
# 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 models
from openerp.tests.common import TransactionCase
from openerp.addons.fetchmail_attach_from_folder.match_algorithm import (
email_exact, email_domain, openerp_standard)
class TestMatchAlgorithms(TransactionCase):
def do_matching(self, match_algorithm, expected_xmlid, conf, mail_message,
mail_message_org=None):
matcher = match_algorithm()
matches = matcher.search_matches(
self.env.cr, self.env.uid, conf, mail_message, mail_message_org)
self.assertEqual(len(matches), 1)
self.assertEqual(
matches[0], self.env.ref(expected_xmlid).id)
matcher.handle_match(
self.env.cr, self.env.uid, None, matches[0], conf, mail_message,
mail_message_org, None)
def test_email_exact(self):
mail_message = {
'subject': 'Testsubject',
'to': 'demo@yourcompany.example.com',
'from': 'someone@else.com',
}
conf = self.env['fetchmail.server.folder'].browse([models.NewId()])
conf.model_id = self.env.ref('base.model_res_partner').id
conf.model_field = 'email'
conf.match_algorithm = 'email_exact'
conf.mail_field = 'to,from'
conf.server_id = self.env['fetchmail.server'].browse([models.NewId()])
self.do_matching(
email_exact.email_exact, 'base.user_demo_res_partner',
conf, mail_message)
self.assertEqual(
self.env.ref('base.user_demo_res_partner').message_ids.subject,
mail_message['subject'])
def test_email_domain(self):
mail_message = {
'subject': 'Testsubject',
'to': 'test@seagate.com',
'from': 'someone@else.com',
}
conf = self.env['fetchmail.server.folder'].browse([models.NewId()])
conf.model_id = self.env.ref('base.model_res_partner').id
conf.model_field = 'email'
conf.match_algorithm = 'email_domain'
conf.mail_field = 'to,from'
conf.use_first_match = True
conf.server_id = self.env['fetchmail.server'].browse([models.NewId()])
self.do_matching(
email_domain.email_domain, 'base.res_partner_address_31',
conf, mail_message)
self.assertEqual(
self.env.ref('base.res_partner_address_31').message_ids.subject,
mail_message['subject'])
def test_openerp_standard(self):
mail_message_org = (
"To: demo@yourcompany.example.com\n"
"From: someone@else.com\n"
"Subject: testsubject\n"
"Message-Id: 42\n"
"Hello world"
)
conf = self.env['fetchmail.server.folder'].browse([models.NewId()])
conf.model_id = self.env.ref('base.model_res_partner').id
conf.model_field = 'email'
conf.match_algorithm = 'openerp_standard'
conf.mail_field = 'to,from'
conf.server_id = self.env['fetchmail.server'].browse([models.NewId()])
matcher = openerp_standard.openerp_standard()
matches = matcher.search_matches(
self.env.cr, self.env.uid, conf, None, mail_message_org)
self.assertEqual(len(matches), 1)
matcher.handle_match(
self.env.cr, self.env.uid, None, matches[0], conf, None,
mail_message_org, None, None)
self.assertIn(
'Hello world',
self.env['mail.message']
.search([('subject', '=', 'testsubject')]).body)

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

@ -30,19 +30,19 @@
</header>
<group>
<group>
<field name="path" />
<field name="path" placeholder="INBOX.subfolder1" />
<field name="model_id" />
<field name="model_field" />
<field name="model_field" placeholder="email" />
<field name="match_algorithm" />
<field name="mail_field" />
<field name="mail_field" placeholder="to,from" />
</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" />
<field name="model_order" attrs="{'readonly': [('match_first','==',False)], 'required': [('match_first','==',True)]}" placeholder="name asc,type desc" />
<field name="domain" placeholder="[('state', '=', 'open')]" />
</group>
</group>
</form>

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

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

@ -19,22 +19,18 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import fields
from openerp.osv.orm import TransientModel
from openerp import fields, models
import logging
logger = logging.getLogger(__name__)
_logger = logging.getLogger(__name__)
class attach_mail_manually(TransientModel):
class attach_mail_manually(models.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'),
}
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:
@ -54,16 +50,14 @@ class attach_mail_manually(TransientModel):
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))
_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))
_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],
@ -74,8 +68,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': '%s,-1' % folder.model_id.model,
}))
connection.close()
return defaults
@ -87,8 +81,8 @@ class attach_mail_manually(TransientModel):
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))
_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(
@ -104,26 +98,32 @@ class attach_mail_manually(TransientModel):
connection.close()
return {'type': 'ir.actions.act_window_close'}
def fields_view_get(self, cr, user, view_id=None, view_type='form',
context=None, toolbar=False, submenu=False):
result = super(attach_mail_manually, self).fields_view_get(
cr, user, view_id, view_type, context, toolbar, submenu)
tree = result['fields']['mail_ids']['views']['tree']
for folder in self.pool['fetchmail.server.folder'].browse(
cr, user, [context.get('default_folder_id')], context):
tree['fields']['object_id']['selection'] = [
(folder.model_id.model, folder.model_id.name)
]
return result
class attach_mail_manually_mail(TransientModel):
class attach_mail_manually_mail(models.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,
),
}
wizard_id = fields.Many2one(
'fetchmail.attach.mail.manually', readonly=True)
msgid = fields.Char('Message id', readonly=True)
subject = fields.Char('Subject', readonly=True)
date = fields.Datetime('Date', readonly=True)
object_id = fields.Reference(
lambda self: [
(m.model, m.name)
for m in self.env['ir.model'].search([])
],
string='Object')

28
fetchmail_attach_from_folder/wizard/attach_mail_manually.xml

@ -0,0 +1,28 @@
<?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" string="Attach mail manually">
<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>
<footer>
<button string="Save" type="object" name="attach_mails" class="oe_highlight" />
or
<button special="cancel" string="Cancel" class="oe_link" />
</footer>
</form>
</field>
</record>
</data>
</openerp>
Loading…
Cancel
Save