diff --git a/mail_store_outgoing/README.rst b/mail_store_outgoing/README.rst new file mode 100644 index 00000000..d948e3c9 --- /dev/null +++ b/mail_store_outgoing/README.rst @@ -0,0 +1,83 @@ +=================== +Mail Store Outgoing +=================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github + :target: https://github.com/OCA/server-auth/tree/10.0/mail_store_outgoing + :alt: OCA/server-auth +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-auth-10-0/server-auth-10-0-mail_store_outgoing + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/251/10.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This Module enables Odoo to store outgoing Mails via IMAP +into a selected folder. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To use this module, you need to: + +#. Select Store Outgoing +#. Test the IMAP Server Connection +#. Select the Target Folder + +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 smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Agent ERP GmbH + +Contributors +~~~~~~~~~~~~ + +* Georg A. G. Notter + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +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. + +This module is part of the `OCA/server-auth `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. 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..101d2569 --- /dev/null +++ b/mail_store_outgoing/__manifest__.py @@ -0,0 +1,21 @@ +# 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..983c5626 --- /dev/null +++ b/mail_store_outgoing/models/ir_mail_server.py @@ -0,0 +1,128 @@ +import logging +import re +import imaplib +import threading +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__) +_test_logger = logging.getLogger('odoo.tests') + + +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'] + maillib = None + if self.has_separate_imap_server: + smtp_server = self.separate_imap_server + else: + smtp_server = self.smtp_host + try: + if getattr(threading.currentThread(), 'testing', False) or \ + self.env.registry.in_test_mode(): + _test_logger.info("skip sending email in test mode") + return True + 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: + if maillib: + 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=None): + mail_server = None + smtp_server = None + maillib = 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: + if getattr(threading.currentThread(), 'testing', False) or \ + self.env.registry.in_test_mode(): + _test_logger.info("skip sending email in test mode") + return True + 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: + if maillib: + 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/readme/CONTRIBUTORS.rst b/mail_store_outgoing/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..594f7651 --- /dev/null +++ b/mail_store_outgoing/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Georg A. G. Notter diff --git a/mail_store_outgoing/readme/DESCRIPTION.rst b/mail_store_outgoing/readme/DESCRIPTION.rst new file mode 100644 index 00000000..497fec5c --- /dev/null +++ b/mail_store_outgoing/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This Module enables Odoo to store outgoing Mails via IMAP +into a selected folder. diff --git a/mail_store_outgoing/readme/USAGE.rst b/mail_store_outgoing/readme/USAGE.rst new file mode 100644 index 00000000..d79b3197 --- /dev/null +++ b/mail_store_outgoing/readme/USAGE.rst @@ -0,0 +1,5 @@ +To use this module, you need to: + +#. Select Store Outgoing +#. Test the IMAP Server Connection +#. Select the Target Folder 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 00000000..a2321491 Binary files /dev/null and b/mail_store_outgoing/static/description/icon.png differ diff --git a/mail_store_outgoing/static/description/index.html b/mail_store_outgoing/static/description/index.html new file mode 100644 index 00000000..f3e5ebf2 --- /dev/null +++ b/mail_store_outgoing/static/description/index.html @@ -0,0 +1,430 @@ + + + + + + +Mail Store Outgoing + + + +
+

Mail Store Outgoing

+ + +

Beta License: AGPL-3 OCA/server-auth Translate me on Weblate Try me on Runbot

+

This Module enables Odoo to store outgoing Mails via IMAP +into a selected folder.

+

Table of contents

+ +
+

Usage

+

To use this module, you need to:

+
    +
  1. Select Store Outgoing
  2. +
  3. Test the IMAP Server Connection
  4. +
  5. Select the Target Folder
  6. +
+
+
+

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 smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Agent ERP GmbH
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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.

+

This module is part of the OCA/server-auth project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/mail_store_outgoing/tests/__init__.py b/mail_store_outgoing/tests/__init__.py new file mode 100644 index 00000000..c5031dec --- /dev/null +++ b/mail_store_outgoing/tests/__init__.py @@ -0,0 +1 @@ +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..e7b2452a --- /dev/null +++ b/mail_store_outgoing/tests/test_ir_mail.py @@ -0,0 +1,56 @@ +# Copyright 2018 AGENTERP GMBH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.exceptions import ValidationError +from openerp.tests import common + + +class TestIrMail(common.TransactionCase): + + def setUp(self): + super(TestIrMail, self).setUp() + self.mail_server = self.env['ir.mail_server'].create({ + 'smtp_port': '25', + 'smtp_host': 'localhost', + 'smtp_encryption': 'none', + 'name': 'test', + 'has_separate_imap_server': True, + 'store_outgoing_mail': True, + }) + + def test_parse_list_response(self): + imap_mailbox = \ + b'(\\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') + + def test_imap_connection(self): + try: + self.mail_server.test_imap_connection() + except ValidationError as e: + pass + + def test_send_mail(self): + msg = self.env['ir.mail_server'].build_email( + email_from='test.from@example.com', + reply_to='test.reply@example.com', + email_to=["test.to@example.com"], + subject="Test Subject", + body="test Bosy", + ) + self.env['ir.mail_server'].send_email( + msg, mail_server_id=self.mail_server.id) + + def test_save_sent_message_to_sentbox(self): + msg = self.env['ir.mail_server'].build_email( + email_from='test.from@example.com', + reply_to='test.reply@example.com', + email_to=["test.to@example.com"], + subject="Test Subject", + body="test Bosy", + ) + msg = self.env['ir.mail_server']._save_sent_message_to_sentbox( + msg, self.mail_server.id) 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 + + + + + + + + + + + + + + +