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
-
18fetchmail_attach_from_folder/__openerp__.py
-
8fetchmail_attach_from_folder/match_algorithm/__init__.py
-
4fetchmail_attach_from_folder/match_algorithm/base.py
-
4fetchmail_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
-
148fetchmail_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
-
82fetchmail_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