From 1b0bc1a9fc967bb6a0d6f9182d60ee99b6c2b584 Mon Sep 17 00:00:00 2001 From: notze Date: Mon, 8 Oct 2018 00:35:12 +0200 Subject: [PATCH 1/4] added mail_store_outgoing --- mail_store_outgoing/README.rst | 78 +++++++++++ mail_store_outgoing/__init__.py | 1 + mail_store_outgoing/__manifest__.py | 21 +++ mail_store_outgoing/i18n/de.po | 123 ++++++++++++++++++ mail_store_outgoing/models/__init__.py | 1 + mail_store_outgoing/models/ir_mail_server.py | 113 ++++++++++++++++ .../security/ir.model.access.csv | 2 + .../static/description/icon.png | Bin 0 -> 1005 bytes mail_store_outgoing/tests/__init__.py | 2 + mail_store_outgoing/tests/test_ir_mail.py | 20 +++ .../views/ir_mail_server_view.xml | 31 +++++ 11 files changed, 392 insertions(+) create mode 100644 mail_store_outgoing/README.rst create mode 100644 mail_store_outgoing/__init__.py create mode 100644 mail_store_outgoing/__manifest__.py create mode 100644 mail_store_outgoing/i18n/de.po create mode 100644 mail_store_outgoing/models/__init__.py create mode 100644 mail_store_outgoing/models/ir_mail_server.py create mode 100644 mail_store_outgoing/security/ir.model.access.csv create mode 100644 mail_store_outgoing/static/description/icon.png create mode 100644 mail_store_outgoing/tests/__init__.py create mode 100644 mail_store_outgoing/tests/test_ir_mail.py create mode 100644 mail_store_outgoing/views/ir_mail_server_view.xml diff --git a/mail_store_outgoing/README.rst b/mail_store_outgoing/README.rst new file mode 100644 index 00000000..23111dd2 --- /dev/null +++ b/mail_store_outgoing/README.rst @@ -0,0 +1,78 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: https://www.gnu.org/licenses/agpl + :alt: License: AGPL-3 + +=================== +Mail Store Outgoing +=================== + +This Module enables Odoo to store outgoing Mails via IMAP into a selected folder. + +Installation +============ + +Just install the module via the module list. + +Configuration +============= + +To configure this module, you need to test the connection and then select your folder out of the provided list. + +Usage +===== + +If you sent and email, it will appear in your selected mailfolder. + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/205/10.0 + +Known issues / Roadmap +====================== + +No known issues + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smash it by providing detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Georg Notter, georg.notter@agenterp.com (Agent ERP GmbH) + +Do not contact contributors directly about support or help with technical issues. + +Funders +------- + +The development of this module has been financially supported by: + +* Agent ERP GmbH + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://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 https://odoo-community.org. diff --git a/mail_store_outgoing/__init__.py b/mail_store_outgoing/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/mail_store_outgoing/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/mail_store_outgoing/__manifest__.py b/mail_store_outgoing/__manifest__.py new file mode 100644 index 00000000..11cf630c --- /dev/null +++ b/mail_store_outgoing/__manifest__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Georg Notter, Agent ERP GmbH +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Mail Store Outgoing", + "summary": "Store outgoing Mails via IMAP into a selected folder.", + "version": "12.0.1.0.0", + "category": "mail", + "website": "https://github.com/OCA/social", + "author": "Agent ERP GmbH, Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": [ + "mail", + ], + "data": [ + 'views/ir_mail_server_view.xml', + 'security/ir.model.access.csv', + ], +} diff --git a/mail_store_outgoing/i18n/de.po b/mail_store_outgoing/i18n/de.po new file mode 100644 index 00000000..3faa2212 --- /dev/null +++ b/mail_store_outgoing/i18n/de.po @@ -0,0 +1,123 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mail_store_outgoing +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-15 14:53+0000\n" +"PO-Revision-Date: 2017-10-15 16:57+0100\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" +"X-Generator: Poedit 1.5.4\n" + +#. module: mail_store_outgoing +#: code:addons/mail_store_outgoing/models/ir_mail_server.py:48 +#, python-format +msgid "Connection Test Failed!" +msgstr "Verbindung fehlgeschlagen!" + +#. module: mail_store_outgoing +#: model:ir.model.fields,field_description:mail_store_outgoing.field_ir_mail_imap_folder_create_uid +msgid "Created by" +msgstr "Erstellt von" + +#. module: mail_store_outgoing +#: model:ir.model.fields,field_description:mail_store_outgoing.field_ir_mail_imap_folder_create_date +msgid "Created on" +msgstr "Angelegt am" + +#. module: mail_store_outgoing +#: model:ir.model.fields,field_description:mail_store_outgoing.field_ir_mail_imap_folder_display_name +msgid "Display Name" +msgstr "Anzeigename" + +#. module: mail_store_outgoing +#: model:ir.model.fields,field_description:mail_store_outgoing.field_ir_mail_imap_folder_name +msgid "Foldername" +msgstr "Verzeichnisname" + +#. module: mail_store_outgoing +#: code:addons/mail_store_outgoing/models/ir_mail_server.py:48 +#, python-format +msgid "" +"Here is what we got instead:\n" +" %s" +msgstr "" +"Fehlermeldung:\n" +" %s" + +#. module: mail_store_outgoing +#: model:ir.model.fields,field_description:mail_store_outgoing.field_ir_mail_imap_folder_id +msgid "ID" +msgstr "ID" + +#. module: mail_store_outgoing +#: model:ir.model.fields,field_description:mail_store_outgoing.field_ir_mail_server_imap_mailbox_verified +msgid "IMAP Connection Verified" +msgstr "IMAP Verbindung verifiziert" + +#. module: mail_store_outgoing +#: model:ir.model,name:mail_store_outgoing.model_ir_mail_imap_folder +msgid "Imap Folder" +msgstr "Imap Verzeichnis" + +#. module: mail_store_outgoing +#: model:ir.model.fields,field_description:mail_store_outgoing.field_ir_mail_server_imap_mailbox_folder +msgid "Imap Folders" +msgstr "Imap Verzeichnisse" + +#. module: mail_store_outgoing +#: model:ir.model.fields,field_description:mail_store_outgoing.field_ir_mail_imap_folder___last_update +msgid "Last Modified on" +msgstr "Zuletzt geƤndert am" + +#. module: mail_store_outgoing +#: model:ir.model.fields,field_description:mail_store_outgoing.field_ir_mail_imap_folder_write_uid +msgid "Last Updated by" +msgstr "Zuletzt aktualisiert durch" + +#. module: mail_store_outgoing +#: model:ir.model.fields,field_description:mail_store_outgoing.field_ir_mail_imap_folder_write_date +msgid "Last Updated on" +msgstr "Zuletzt aktualisiert am" + +#. module: mail_store_outgoing +#: model:ir.model.fields,field_description:mail_store_outgoing.field_ir_mail_imap_folder_server_id +msgid "Mail Server" +msgstr "Mailserver" + +#. module: mail_store_outgoing +#: model:ir.ui.view,arch_db:mail_store_outgoing.ir_mail_server_form +msgid "Outgoing Mail Settings" +msgstr "Maileinstellungen" + +#. module: mail_store_outgoing +#: model:ir.model.fields,field_description:mail_store_outgoing.field_ir_mail_server_separate_imap_server +msgid "Separate Imap Server" +msgstr "Alternativer Imapserver" + +#. module: mail_store_outgoing +#: model:ir.model.fields,field_description:mail_store_outgoing.field_ir_mail_server_store_outgoing_mail +msgid "Store Outgoing Mail" +msgstr "Ausgehende Mails speichern" + +#. module: mail_store_outgoing +#: model:ir.ui.view,arch_db:mail_store_outgoing.ir_mail_server_form +msgid "Test Imap Connection" +msgstr "Verbindung testen " + +#. module: mail_store_outgoing +#: model:ir.model.fields,field_description:mail_store_outgoing.field_ir_mail_server_has_separate_imap_server +msgid "Use Separate Imap Server" +msgstr "Verwende alternativen IMAP Server" + +#. module: mail_store_outgoing +#: model:ir.model,name:mail_store_outgoing.model_ir_mail_server +msgid "ir.mail_server" +msgstr "ir.mail_server" diff --git a/mail_store_outgoing/models/__init__.py b/mail_store_outgoing/models/__init__.py new file mode 100644 index 00000000..abbcb50b --- /dev/null +++ b/mail_store_outgoing/models/__init__.py @@ -0,0 +1 @@ +from . import ir_mail_server diff --git a/mail_store_outgoing/models/ir_mail_server.py b/mail_store_outgoing/models/ir_mail_server.py new file mode 100644 index 00000000..b45c6d53 --- /dev/null +++ b/mail_store_outgoing/models/ir_mail_server.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- +import logging +import re +import imaplib +from odoo import tools +from odoo.exceptions import ValidationError +from odoo import fields +from odoo import models +from odoo import api +from odoo.tools.translate import _ + +_logger = logging.getLogger(__name__) + + +class IrMailServer(models.Model): + _inherit = 'ir.mail_server' + + imap_mailbox_folder = fields.Many2one('ir.mail.imap.folder', + 'Imap Folders',) + imap_mailbox_verified = fields.Boolean('IMAP Connection Verified', ) + store_outgoing_mail = fields.Boolean() + has_separate_imap_server = fields.Boolean('Use Separate Imap Server', ) + separate_imap_server = fields.Char() + active = fields.Boolean() + + @api.model + def parse_list_response(self, line): + line = line.decode('utf-8') + list_response_pattern = re.compile( + r'\((?P.*?)\) "(?P.*)" (?P.*)' + ) + flags, delimiter, mailbox_name = \ + list_response_pattern.match(line).groups() + mailbox_name = mailbox_name.strip('"') + return (flags, delimiter, mailbox_name) + + @api.multi + def test_imap_connection(self): + self.ensure_one() + imap_pool = self.env['ir.mail.imap.folder'] + if self.has_separate_imap_server: + smtp_server = self.separate_imap_server + else: + smtp_server = self.smtp_host + try: + maillib = imaplib.IMAP4_SSL(smtp_server) + maillib.login(self.smtp_user, self.smtp_pass) + typ, mBoxes = maillib.list() + folder_ids = imap_pool.search([('server_id', '=', self.id)]) + for folder in folder_ids: + folder.unlink() + for line in mBoxes: + flags, delimiter, mailbox_name = self.parse_list_response(line) + res = {'server_id': self.id, 'name': mailbox_name, } + imap_pool.create(res) + self.write({'imap_mailbox_verified': True}) + except Exception as e: + raise ValidationError( + _("Connection Test Failed! " + "Here is what we got instead:\n %s") % tools.ustr(e)) + finally: + maillib.logout() + + @api.model + def send_email(self, message, mail_server_id=None, + smtp_server=None, smtp_port=None, smtp_user=None, + smtp_password=None, smtp_encryption=None, smtp_debug=False, + smtp_session=None): + res = super(IrMailServer, self).send_email( + message, mail_server_id, smtp_server, smtp_port, + smtp_user, smtp_password, smtp_encryption, smtp_debug, + smtp_session) + self._save_sent_message_to_sentbox(message, mail_server_id) + return res + + @api.model + def _save_sent_message_to_sentbox(self, msg, mail_server_id): + mail_server = None + smtp_server = None + if mail_server_id: + mail_server = self.sudo().browse(mail_server_id) + else: + mail_server = self.sudo().search([], order='sequence', limit=1) + if mail_server: + if not mail_server.store_outgoing_mail: + return True + if mail_server.has_separate_imap_server: + smtp_server = mail_server.separate_imap_server + else: + smtp_server = mail_server.smtp_host + smtp_user = mail_server.smtp_user + smtp_password = mail_server.smtp_pass + try: + maillib = imaplib.IMAP4_SSL(smtp_server) + maillib.login(smtp_user, smtp_password) + folder = mail_server.imap_mailbox_folder.name.join('""') + maillib.append(str.encode(folder), r'\Seen', None, str(msg).encode()) + except Exception as ex: + _logger.error(_( + 'Failed attaching mail via imap to server %s %s') + % (ex, msg)) + finally: + maillib.logout() + return True + + +class IrMailImapFolder(models.Model): + + _name = 'ir.mail.imap.folder' + _description = 'Imap Folder' + + server_id = fields.Many2one('ir.mail_server', 'Mail Server', ) + name = fields.Char('Foldername',) diff --git a/mail_store_outgoing/security/ir.model.access.csv b/mail_store_outgoing/security/ir.model.access.csv new file mode 100644 index 00000000..9ad5e16a --- /dev/null +++ b/mail_store_outgoing/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_ir_mail_imap_folder,access_ir_mail_imap_folder,model_ir_mail_imap_folder,,1,0,1,0 diff --git a/mail_store_outgoing/static/description/icon.png b/mail_store_outgoing/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a2321491711043dbaceebdc24c4ab5120fcd0793 GIT binary patch literal 1005 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_5;mUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VW5Sk9 zpZV_PLFpYJSzvL!;*yB;cMo5{eS!4_69Z@P->#!IuhyRV=^wU$`+@D!GS9Xz zi*4^!yUd&LlIMVYYy4KkrS>ejUusz-ICJs>?)v z$>L)T{qDXD5=$?cZMil@BrfsF_4#TJ_U7|MR%JQ${;q%8RyUi0vv2D5?;vi}`;F(f zv+Fj4iYxsHUwyi8z z-#*7{lGGDNvlK&}18a`_<4*Rf_S$J}Y4K)aP}Omj($E>a%mycSXY4mzy|a9|)@$Bc z){~s?eP%KBe9-Z>yne-VV$taud-dq=XF50?W@W#O&e}Qor&(vEXI;H-l(@pViC=P( zvSvSe+_6LMz_u;&EkBrVA8!0{^BQBs#B1EYdX}@lcpy z(Tq!WPP(<}>PyCHN56}{Ok*hIds=z=*B6H9NB<{OC_B`ai-i5};$_I&nKQ{OH*(hL zwx2ek<}QmiMa?j0UD2sxwVLzp!GoO->lOR2Sp8eNh5LiJ;L|NP6ZN;A1g31&64!{5 zl*E!$tK_0oAjM#0U}&LhXrOCg7Gh*zWnctE+91+E_u#X56b-rgDVb@NxHZJgiF*vx wAPKS|I6tkVJh3R1p}f3YFEcN@I61K(RWH9NefB#WDWD<-Pgg&ebxsLQ09!nZz5oCK literal 0 HcmV?d00001 diff --git a/mail_store_outgoing/tests/__init__.py b/mail_store_outgoing/tests/__init__.py new file mode 100644 index 00000000..3b163854 --- /dev/null +++ b/mail_store_outgoing/tests/__init__.py @@ -0,0 +1,2 @@ +from . import test_ir_mail + diff --git a/mail_store_outgoing/tests/test_ir_mail.py b/mail_store_outgoing/tests/test_ir_mail.py new file mode 100644 index 00000000..47a9e581 --- /dev/null +++ b/mail_store_outgoing/tests/test_ir_mail.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 AGENTERP GMBH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp.tests.common import TransactionCase + + +class TestIrMail(TransactionCase): + + def setUp(self): + super(TestIrMail, self).setUp() + + def test_parse_list_response(self): + imap_mailbox = \ + '(\\HasNoChildren \\UnMarked) "." "INBOX.Deleted Messages"' + flags, delimiter, mailbox_name = \ + self.env['ir.mail_server'].parse_list_response(imap_mailbox) + self.assertEqual(flags, '\\HasNoChildren \\UnMarked') + self.assertEqual(delimiter, '.') + self.assertEqual(mailbox_name, 'INBOX.Deleted Messages') diff --git a/mail_store_outgoing/views/ir_mail_server_view.xml b/mail_store_outgoing/views/ir_mail_server_view.xml new file mode 100644 index 00000000..584a7c39 --- /dev/null +++ b/mail_store_outgoing/views/ir_mail_server_view.xml @@ -0,0 +1,31 @@ + + + + ir.mail_server + ir_mail_server_form_save_outgoing + + + + + + + + + + + + + + +