Browse Source
Merge pull request #121 from hbrunn/8.0-fetchmail_attach_from_folder
Merge pull request #121 from hbrunn/8.0-fetchmail_attach_from_folder
[ADD] port fetchmail_attach_from_folderpull/126/head
Pedro M. Baeza
10 years ago
21 changed files with 485 additions and 317 deletions
-
131__unported__/fetchmail_attach_from_folder/model/fetchmail_server_folder.py
-
29__unported__/fetchmail_attach_from_folder/wizard/attach_mail_manually.xml
-
83fetchmail_attach_from_folder/README.rst
-
5fetchmail_attach_from_folder/__init__.py
-
16fetchmail_attach_from_folder/__openerp__.py
-
8fetchmail_attach_from_folder/match_algorithm/__init__.py
-
4fetchmail_attach_from_folder/match_algorithm/base.py
-
2fetchmail_attach_from_folder/match_algorithm/email_domain.py
-
2fetchmail_attach_from_folder/match_algorithm/email_exact.py
-
4fetchmail_attach_from_folder/match_algorithm/openerp_standard.py
-
5fetchmail_attach_from_folder/model/__init__.py
-
126fetchmail_attach_from_folder/model/fetchmail_server.py
-
115fetchmail_attach_from_folder/model/fetchmail_server_folder.py
-
0fetchmail_attach_from_folder/security/ir.model.access.csv
-
BINfetchmail_attach_from_folder/static/description/icon.png
-
21fetchmail_attach_from_folder/tests/__init__.py
-
103fetchmail_attach_from_folder/tests/test_match_algorithms.py
-
10fetchmail_attach_from_folder/view/fetchmail_server.xml
-
2fetchmail_attach_from_folder/wizard/__init__.py
-
76fetchmail_attach_from_folder/wizard/attach_mail_manually.py
-
28fetchmail_attach_from_folder/wizard/attach_mail_manually.xml
@ -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', |
|||
} |
@ -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> |
@ -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. |
@ -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', |
|||
} |
After Width: 128 | Height: 128 | Size: 12 KiB |
@ -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 |
@ -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) |
@ -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> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue