From d020997ea07fef738ddd0e8d9e96a0705968617b Mon Sep 17 00:00:00 2001 From: ArtiomKichojalSAMSAIT Date: Thu, 23 Oct 2025 15:22:23 +0200 Subject: [PATCH] initial commit --- mail_drop_target/README.rst | 114 +++++ mail_drop_target/__init__.py | 4 + mail_drop_target/__manifest__.py | 19 + mail_drop_target/controllers/__init__.py | 3 + mail_drop_target/controllers/discuss.py | 49 ++ mail_drop_target/i18n/it.po | 83 ++++ mail_drop_target/i18n/mail_drop_target.pot | 72 +++ mail_drop_target/models/__init__.py | 5 + mail_drop_target/models/ir_attachment.py | 49 ++ mail_drop_target/models/mail_thread.py | 161 ++++++ .../models/res_config_settings.py | 17 + mail_drop_target/pyproject.toml | 3 + mail_drop_target/readme/CONFIGURATION.rst | 3 + mail_drop_target/readme/CONTRIBUTORS.md | 4 + mail_drop_target/readme/CREDITS.md | 0 mail_drop_target/readme/DESCRIPTION.md | 10 + mail_drop_target/readme/ROADMAP.md | 4 + mail_drop_target/readme/USAGE.md | 5 + mail_drop_target/static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/index.html | 463 ++++++++++++++++++ .../static/src/js/file_uploader.esm.js | 32 ++ mail_drop_target/tests/__init__.py | 1 + mail_drop_target/tests/sample.eml | 75 +++ .../tests/sample.eml:OECustomProperty | Bin 0 -> 685 bytes mail_drop_target/tests/sample.msg | Bin 0 -> 16896 bytes .../tests/sample_include_attachment.msg | Bin 0 -> 73216 bytes .../tests/test_mail_drop_target.py | 102 ++++ .../views/res_config_settings_views.xml | 27 + requirements.txt | 2 + 29 files changed, 1307 insertions(+) create mode 100644 mail_drop_target/README.rst create mode 100644 mail_drop_target/__init__.py create mode 100644 mail_drop_target/__manifest__.py create mode 100644 mail_drop_target/controllers/__init__.py create mode 100644 mail_drop_target/controllers/discuss.py create mode 100644 mail_drop_target/i18n/it.po create mode 100644 mail_drop_target/i18n/mail_drop_target.pot create mode 100644 mail_drop_target/models/__init__.py create mode 100644 mail_drop_target/models/ir_attachment.py create mode 100644 mail_drop_target/models/mail_thread.py create mode 100644 mail_drop_target/models/res_config_settings.py create mode 100644 mail_drop_target/pyproject.toml create mode 100644 mail_drop_target/readme/CONFIGURATION.rst create mode 100644 mail_drop_target/readme/CONTRIBUTORS.md create mode 100644 mail_drop_target/readme/CREDITS.md create mode 100644 mail_drop_target/readme/DESCRIPTION.md create mode 100644 mail_drop_target/readme/ROADMAP.md create mode 100644 mail_drop_target/readme/USAGE.md create mode 100644 mail_drop_target/static/description/icon.png create mode 100644 mail_drop_target/static/description/index.html create mode 100644 mail_drop_target/static/src/js/file_uploader.esm.js create mode 100644 mail_drop_target/tests/__init__.py create mode 100644 mail_drop_target/tests/sample.eml create mode 100644 mail_drop_target/tests/sample.eml:OECustomProperty create mode 100644 mail_drop_target/tests/sample.msg create mode 100644 mail_drop_target/tests/sample_include_attachment.msg create mode 100644 mail_drop_target/tests/test_mail_drop_target.py create mode 100644 mail_drop_target/views/res_config_settings_views.xml create mode 100644 requirements.txt diff --git a/mail_drop_target/README.rst b/mail_drop_target/README.rst new file mode 100644 index 000000000..57dd02683 --- /dev/null +++ b/mail_drop_target/README.rst @@ -0,0 +1,114 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +========================== +Drag & drop emails to Odoo +========================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:85f4a6f39edb713ec1bd1513532b8f903b033bf76662c64946375473b5d5c119 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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%2Fmail-lightgray.png?logo=github + :target: https://github.com/OCA/mail/tree/19.0/mail_drop_target + :alt: OCA/mail +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/mail-19-0/mail-19-0-mail_drop_target + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/mail&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module was written to allow users to drag&drop emails from their +desktop to Odoo. + +It supports as well RFC822 .eml files as Outlook .msg (those only if `an +extra library `__ is +installed) files. + +When the mail is dropped to an odoo record, it will automatically send a +notification of that new message that has been added to all the existing +followers. It is possible to disable this notification. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To use this module, you need to: + +1. save your emails on the desktop / somewhere in the file system +2. drag them to your browser, and drop them on the chatter of the record + you want to attach your email to + +Known issues / Roadmap +====================== + +- most mail clients won't allow you to drag mails directly from the mail + client, you'll need some plugin for that +- for corporate environments, it might be feasible to support imap URLs + and get the mail in question on the server side + +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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Therp BV + +Contributors +------------ + +- Holger Brunn +- Enric Tobella +- Lois Rilo +- Nguyen Minh Chien + +Other credits +------------- + + + +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/mail `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/mail_drop_target/__init__.py b/mail_drop_target/__init__.py new file mode 100644 index 000000000..9484ea350 --- /dev/null +++ b/mail_drop_target/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2018 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from . import controllers +from . import models diff --git a/mail_drop_target/__manifest__.py b/mail_drop_target/__manifest__.py new file mode 100644 index 000000000..6b3be407a --- /dev/null +++ b/mail_drop_target/__manifest__.py @@ -0,0 +1,19 @@ +# Copyright 2018 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +{ + "name": "Drag & drop emails to Odoo", + "version": "19.0.1.0.1", + "author": "Therp BV,Odoo Community Association (OCA)", + "license": "AGPL-3", + "category": "Discuss", + "website": "https://github.com/OCA/mail", + "summary": "Attach emails to Odoo by dragging them from your desktop", + "depends": ["mail"], + "external_dependencies": {"python": ["extract_msg"]}, + "data": ["views/res_config_settings_views.xml"], + "assets": { + "web.assets_backend": [ + "mail_drop_target/static/src/js/file_uploader.esm.js", + ], + }, +} diff --git a/mail_drop_target/controllers/__init__.py b/mail_drop_target/controllers/__init__.py new file mode 100644 index 000000000..636ed8add --- /dev/null +++ b/mail_drop_target/controllers/__init__.py @@ -0,0 +1,3 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import discuss diff --git a/mail_drop_target/controllers/discuss.py b/mail_drop_target/controllers/discuss.py new file mode 100644 index 000000000..9bf47864e --- /dev/null +++ b/mail_drop_target/controllers/discuss.py @@ -0,0 +1,49 @@ +# Copyright Nguyen Minh Chien (chien@trobz.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import http +from odoo.exceptions import AccessError, UserError +from odoo.http import request + +from odoo.addons.mail.controllers import attachment + + +class AttachmentController(attachment.AttachmentController): + @http.route("/mail/attachment/upload", methods=["POST"], type="http", auth="public") + def mail_attachment_upload( + self, ufile, thread_id, thread_model, is_pending=False, **kwargs + ): + if not is_pending or is_pending == "false": + # Add this point, make sure the message related to the uploaded + # file does exist. + resp = self.mail_attachment_upload_email(ufile, thread_id, thread_model) + if resp: + return resp + + return super().mail_attachment_upload( + ufile, thread_id, thread_model, is_pending, **kwargs + ) + + def mail_attachment_upload_email(self, ufile, thread_id, thread_model): + channel_member = request.env["discuss.channel.member"] + if thread_model == "discuss.channel": + channel_member = request.env[ + "discuss.channel.member" + ]._get_as_sudo_from_request_or_raise( + request=request, channel_id=int(thread_id) + ) + try: + mail_resp = channel_member.env["ir.attachment"].read_mail_file_content( + ufile.filename, ufile.read(), int(thread_id), thread_model + ) + ufile.seek(0) + if not mail_resp: + return False + responseData = {"email_upload": 1} + except AccessError: + responseData = { + "error": self.env._("You are not allowed to upload an attachment here.") + } + except UserError as err: + responseData = {"error": str(err)} + return request.make_json_response(responseData) diff --git a/mail_drop_target/i18n/it.po b/mail_drop_target/i18n/it.po new file mode 100644 index 000000000..b629c7912 --- /dev/null +++ b/mail_drop_target/i18n/it.po @@ -0,0 +1,83 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mail_drop_target +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-02-26 16:34+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: mail_drop_target +#: model:ir.model,name:mail_drop_target.model_ir_attachment +msgid "Attachment" +msgstr "Allegato" + +#. module: mail_drop_target +#: model:ir.model,name:mail_drop_target.model_res_config_settings +msgid "Config Settings" +msgstr "Impostazioni configurazione" + +#. module: mail_drop_target +#: model_terms:ir.ui.view,arch_db:mail_drop_target.res_config_settings_view_form +msgid "Disable Mail Drag&Drop Notification" +msgstr "Disabilita notifica e-mail trascina&rilascia" + +#. module: mail_drop_target +#: model:ir.model.fields,field_description:mail_drop_target.field_res_config_settings__disable_notify_mail_drop_target +msgid "Disable Notification followers on mail dropped to a Thread" +msgstr "Disabilita notifica chi segue per e-mail inserita in una discussione" + +#. module: mail_drop_target +#: model:ir.model,name:mail_drop_target.model_mail_thread +msgid "Email Thread" +msgstr "Discussione e-mail" + +#. module: mail_drop_target +#. odoo-python +#: code:addons/mail_drop_target/models/mail_thread.py:0 +#, python-format +msgid "Install the msg-extractor library to handle .msg files" +msgstr "Installare la libreria msg-extractor per festire i file .msg" + +#. module: mail_drop_target +#. odoo-python +#: code:addons/mail_drop_target/models/mail_thread.py:0 +#, python-format +msgid "This message is already imported." +msgstr "Questo messaggio è già stato importato." + +#. module: mail_drop_target +#: model_terms:ir.ui.view,arch_db:mail_drop_target.res_config_settings_view_form +msgid "" +"When a user drops an email into an existing thread\n" +" the followers of the thread will not be notified." +msgstr "" +"Quando un utente rilascia una e-mail in una discussione esistente\n" +" chi segue la discussione non verrà avvisato." + +#. module: mail_drop_target +#: model:ir.model.fields,help:mail_drop_target.field_res_config_settings__disable_notify_mail_drop_target +msgid "" +"When this setting is set, when a user drops an email into an existing thread" +" the followers of the thread will not be notified. This sets an " +"ir.config.parameter mail_drop_target.disable_notify" +msgstr "" +"Quanto è attiva questa impostazione, quando un utente rilascia una e.mail in " +"una discussione esistente, chi segue la discussione non verrà avvisato. " +"Questo imposta un ir.config.parameter in mail_drop_target.disable_notify" + +#. module: mail_drop_target +#. odoo-python +#: code:addons/mail_drop_target/controllers/discuss.py:0 +#, python-format +msgid "You are not allowed to upload an attachment here." +msgstr "Non si ha l'autorizzazione per caricare qui un allegato." diff --git a/mail_drop_target/i18n/mail_drop_target.pot b/mail_drop_target/i18n/mail_drop_target.pot new file mode 100644 index 000000000..d7eeef933 --- /dev/null +++ b/mail_drop_target/i18n/mail_drop_target.pot @@ -0,0 +1,72 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mail_drop_target +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: mail_drop_target +#: model:ir.model,name:mail_drop_target.model_ir_attachment +msgid "Attachment" +msgstr "" + +#. module: mail_drop_target +#: model:ir.model,name:mail_drop_target.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: mail_drop_target +#: model_terms:ir.ui.view,arch_db:mail_drop_target.res_config_settings_view_form +msgid "Disable Mail Drag&Drop Notification" +msgstr "" + +#. module: mail_drop_target +#: model:ir.model.fields,field_description:mail_drop_target.field_res_config_settings__disable_notify_mail_drop_target +msgid "Disable Notification followers on mail dropped to a Thread" +msgstr "" + +#. module: mail_drop_target +#: model:ir.model,name:mail_drop_target.model_mail_thread +msgid "Email Thread" +msgstr "" + +#. module: mail_drop_target +#. odoo-python +#: code:addons/mail_drop_target/models/mail_thread.py:0 +msgid "Install the msg-extractor library to handle .msg files" +msgstr "" + +#. module: mail_drop_target +#. odoo-python +#: code:addons/mail_drop_target/models/mail_thread.py:0 +msgid "This message is already imported." +msgstr "" + +#. module: mail_drop_target +#: model_terms:ir.ui.view,arch_db:mail_drop_target.res_config_settings_view_form +msgid "" +"When a user drops an email into an existing thread\n" +" the followers of the thread will not be notified." +msgstr "" + +#. module: mail_drop_target +#: model:ir.model.fields,help:mail_drop_target.field_res_config_settings__disable_notify_mail_drop_target +msgid "" +"When this setting is set, when a user drops an email into an existing thread" +" the followers of the thread will not be notified. This sets an " +"ir.config.parameter mail_drop_target.disable_notify" +msgstr "" + +#. module: mail_drop_target +#. odoo-python +#: code:addons/mail_drop_target/controllers/discuss.py:0 +msgid "You are not allowed to upload an attachment here." +msgstr "" diff --git a/mail_drop_target/models/__init__.py b/mail_drop_target/models/__init__.py new file mode 100644 index 000000000..98fd6d1ef --- /dev/null +++ b/mail_drop_target/models/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2018 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from . import ir_attachment +from . import mail_thread +from . import res_config_settings diff --git a/mail_drop_target/models/ir_attachment.py b/mail_drop_target/models/ir_attachment.py new file mode 100644 index 000000000..9807cb799 --- /dev/null +++ b/mail_drop_target/models/ir_attachment.py @@ -0,0 +1,49 @@ +# Copyright Nguyen Minh Chien (chien@trobz.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +import base64 +import os + +from odoo import api, models + + +class IrAttachment(models.Model): + _inherit = "ir.attachment" + + def _get_email_file_extensions(self): + return ["msg", "eml"] + + def _process_email_file_msg(self, res_obj, raw_content): + if not hasattr(res_obj, "message_process_msg"): + return False + message = base64.b64encode(raw_content) + thread_id = res_obj.message_process_msg( + res_obj._name, message, thread_id=res_obj.id + ) + return thread_id + + @api.model + def _process_email_file_default(self, res_obj, raw_content): + if not hasattr(res_obj, "message_drop"): + return False + message = raw_content + thread_id = res_obj.message_drop(res_obj._name, message, thread_id=res_obj.id) + return thread_id + + def read_mail_file_content(self, file_name, raw_content, res_id, res_model): + file_extensions = self._get_email_file_extensions() + name_lst = os.path.splitext(file_name) + file_extension = name_lst[-1].lower().replace(".", "") + if not file_extension or file_extension not in file_extensions: + return False + + res_obj = self.env[res_model].browse(res_id) + if not res_obj: + return False + + handler = f"_process_email_file_{file_extension}" + if not hasattr(self, handler): + handler = "_process_email_file_default" + + res = getattr(self, handler)(res_obj, raw_content) + return res diff --git a/mail_drop_target/models/mail_thread.py b/mail_drop_target/models/mail_thread.py new file mode 100644 index 000000000..aa7c5a900 --- /dev/null +++ b/mail_drop_target/models/mail_thread.py @@ -0,0 +1,161 @@ +# Copyright 2018 Therp BV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from base64 import b64decode + +import chardet +import lxml.html + +from odoo import api, exceptions, models + +try: + from extract_msg import Message +except ImportError: + Message = None + + +class MailThread(models.AbstractModel): + _inherit = "mail.thread" + + @api.model + def message_drop( + self, + model, + message, + custom_values=None, + save_original=False, + strip_attachments=False, + thread_id=None, + ): + disable_notify_mail_drop_target = ( + self.env["ir.config_parameter"] + .sudo() + .get_param("mail_drop_target.disable_notify", default=False) + ) + self_message_process = self + if disable_notify_mail_drop_target: + self_message_process = self_message_process.with_context( + message_create_from_mail_mail=True + ) + result = self_message_process.message_process( + model, + message, + custom_values=custom_values, + save_original=save_original, + strip_attachments=strip_attachments, + thread_id=thread_id, + ) + if not result: + return self.message_drop_existing( + model, + message, + custom_values=custom_values, + save_original=save_original, + strip_attachments=strip_attachments, + thread_id=thread_id, + ) + return result + + @api.model + def message_drop_existing( + self, + model, + message, + custom_values=None, + save_original=False, + strip_attachments=False, + thread_id=None, + ): + message = self.env._("This message is already imported.") + raise exceptions.UserError(message) + + @api.model + def message_process_msg( + self, + model, + message, + custom_values=None, + save_original=False, + strip_attachments=False, + thread_id=None, + ): + """Convert message to RFC2822 and pass to message_process""" + if not Message: + raise exceptions.UserError( + self.env._("Install the msg-extractor library to handle .msg files") + ) + message_msg = Message(b64decode(message)) + try: + message_id = message_msg.messageId + except AttributeError: + # Using extract_msg < 0.24.4 + message_id = message_msg.message_id + msg_body = message_msg.htmlBody or message_msg.body + if isinstance(msg_body, bytes): + detection = chardet.detect(msg_body) + encoding = detection.get("encoding") + msg_body = msg_body.decode(encoding) + + subtype = ( + lxml.html.fromstring(msg_body).find(".//*") is not None + and "html" + or "plain" + ) + + message_email = self.env["ir.mail_server"].build_email( + message_msg.sender, + message_msg.to.split(","), + message_msg.subject, + # prefer html bodies to text + msg_body, + email_cc=message_msg.cc, + message_id=message_id.strip(), + attachments=[ + (attachment.longFilename, attachment.data, attachment.mimetype) + for attachment in message_msg.attachments + ], + subtype=subtype, + ) + # We need to override message date, as an error rises when processing it + # directly with headers + del message_email["date"] + message_email["date"] = message_msg.date + return self.message_drop( + model, + message_email.as_string(), + custom_values=custom_values, + save_original=save_original, + strip_attachments=strip_attachments, + thread_id=thread_id, + ) + + def _notify_thread_by_email( + self, + message, + recipients_data, + msg_vals=False, + mail_auto_delete=True, # mail.mail + model_description=False, + force_email_company=False, + force_email_lang=False, # rendering + resend_existing=False, + force_send=True, + send_after_commit=True, # email send + subtitles=None, + **kwargs, + ): + if self.env.context.get("message_create_from_mail_mail", False): + return + return super()._notify_thread_by_email( + message, + recipients_data, + msg_vals=msg_vals, + mail_auto_delete=mail_auto_delete, + model_description=model_description, + force_email_company=force_email_company, + force_email_lang=force_email_lang, + resend_existing=resend_existing, + force_send=force_send, + send_after_commit=send_after_commit, + subtitles=subtitles, + **kwargs, + ) diff --git a/mail_drop_target/models/res_config_settings.py b/mail_drop_target/models/res_config_settings.py new file mode 100644 index 000000000..6122fb002 --- /dev/null +++ b/mail_drop_target/models/res_config_settings.py @@ -0,0 +1,17 @@ +# Copyright 2019-20 ForgeFlow S.L. (https://www.forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + disable_notify_mail_drop_target = fields.Boolean( + "Disable Notification followers on mail dropped to a Thread", + config_parameter="mail_drop_target.disable_notify", + help="When this setting is set, when a user drops an " + "email into an existing thread the followers of the thread will " + "not be notified. This sets an ir.config.parameter " + "mail_drop_target.disable_notify", + ) diff --git a/mail_drop_target/pyproject.toml b/mail_drop_target/pyproject.toml new file mode 100644 index 000000000..4231d0ccc --- /dev/null +++ b/mail_drop_target/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/mail_drop_target/readme/CONFIGURATION.rst b/mail_drop_target/readme/CONFIGURATION.rst new file mode 100644 index 000000000..79e2eb7b3 --- /dev/null +++ b/mail_drop_target/readme/CONFIGURATION.rst @@ -0,0 +1,3 @@ +To disable the automatic notification to existing followers when you drop an +email to a record, go to *Settings*, activate the developer mode, and then go to +*Settings / General Settings* and set the flag 'Disable Mail Drag&Drop Notification'. diff --git a/mail_drop_target/readme/CONTRIBUTORS.md b/mail_drop_target/readme/CONTRIBUTORS.md new file mode 100644 index 000000000..606ac2b2b --- /dev/null +++ b/mail_drop_target/readme/CONTRIBUTORS.md @@ -0,0 +1,4 @@ +- Holger Brunn \ +- Enric Tobella \ +- Lois Rilo \ +- Nguyen Minh Chien \ diff --git a/mail_drop_target/readme/CREDITS.md b/mail_drop_target/readme/CREDITS.md new file mode 100644 index 000000000..e69de29bb diff --git a/mail_drop_target/readme/DESCRIPTION.md b/mail_drop_target/readme/DESCRIPTION.md new file mode 100644 index 000000000..c871abc30 --- /dev/null +++ b/mail_drop_target/readme/DESCRIPTION.md @@ -0,0 +1,10 @@ +This module was written to allow users to drag&drop emails from their +desktop to Odoo. + +It supports as well RFC822 .eml files as Outlook .msg (those only if [an +extra library](https://github.com/mattgwwalker/msg-extractor) is +installed) files. + +When the mail is dropped to an odoo record, it will automatically send a +notification of that new message that has been added to all the existing +followers. It is possible to disable this notification. diff --git a/mail_drop_target/readme/ROADMAP.md b/mail_drop_target/readme/ROADMAP.md new file mode 100644 index 000000000..49decb662 --- /dev/null +++ b/mail_drop_target/readme/ROADMAP.md @@ -0,0 +1,4 @@ +- most mail clients won't allow you to drag mails directly from the mail + client, you'll need some plugin for that +- for corporate environments, it might be feasible to support imap URLs + and get the mail in question on the server side diff --git a/mail_drop_target/readme/USAGE.md b/mail_drop_target/readme/USAGE.md new file mode 100644 index 000000000..20a9963db --- /dev/null +++ b/mail_drop_target/readme/USAGE.md @@ -0,0 +1,5 @@ +To use this module, you need to: + +1. save your emails on the desktop / somewhere in the file system +2. drag them to your browser, and drop them on the chatter of the + record you want to attach your email to diff --git a/mail_drop_target/static/description/icon.png b/mail_drop_target/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/mail_drop_target/static/description/index.html b/mail_drop_target/static/description/index.html new file mode 100644 index 000000000..d1666bc39 --- /dev/null +++ b/mail_drop_target/static/description/index.html @@ -0,0 +1,463 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Drag & drop emails to Odoo

+ +

Beta License: AGPL-3 OCA/mail Translate me on Weblate Try me on Runboat

+

This module was written to allow users to drag&drop emails from their +desktop to Odoo.

+

It supports as well RFC822 .eml files as Outlook .msg (those only if an +extra library is +installed) files.

+

When the mail is dropped to an odoo record, it will automatically send a +notification of that new message that has been added to all the existing +followers. It is possible to disable this notification.

+

Table of contents

+ +
+

Usage

+

To use this module, you need to:

+
    +
  1. save your emails on the desktop / somewhere in the file system
  2. +
  3. drag them to your browser, and drop them on the chatter of the record +you want to attach your email to
  4. +
+
+
+

Known issues / Roadmap

+
    +
  • most mail clients won’t allow you to drag mails directly from the mail +client, you’ll need some plugin for that
  • +
  • for corporate environments, it might be feasible to support imap URLs +and get the mail in question on the server side
  • +
+
+
+

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

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Therp BV
  • +
+
+
+

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/mail project on GitHub.

+

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

+
+
+
+
+ + diff --git a/mail_drop_target/static/src/js/file_uploader.esm.js b/mail_drop_target/static/src/js/file_uploader.esm.js new file mode 100644 index 000000000..abb33cd60 --- /dev/null +++ b/mail_drop_target/static/src/js/file_uploader.esm.js @@ -0,0 +1,32 @@ +/* @odoo-module */ +import {AttachmentUploadService} from "@mail/core/common/attachment_upload_service"; +import {Chatter} from "@mail/chatter/web_portal/chatter"; +import {patch} from "@web/core/utils/patch"; +import {useService} from "@web/core/utils/hooks"; + +patch(AttachmentUploadService.prototype, { + async _processLoaded(thread, composer, attachmentData, tmpId, def) { + if (attachmentData.email_upload === 1) { + this._fileUploadBus.trigger("REFRESH_CHATTER", thread.id); + } else { + return super._processLoaded(thread, composer, attachmentData, tmpId, def); + } + }, +}); + +patch(Chatter.prototype, { + setup() { + super.setup(...arguments); + + this.attachmentUploadService = useService("mail.attachment_upload"); + + this.attachmentUploadService._fileUploadBus.addEventListener( + "REFRESH_CHATTER", + ({detail: threadId}) => { + if (this.state.thread?.id === threadId) { + this.reloadParentView(); + } + } + ); + }, +}); diff --git a/mail_drop_target/tests/__init__.py b/mail_drop_target/tests/__init__.py new file mode 100644 index 000000000..1dd00d054 --- /dev/null +++ b/mail_drop_target/tests/__init__.py @@ -0,0 +1 @@ +from . import test_mail_drop_target diff --git a/mail_drop_target/tests/sample.eml b/mail_drop_target/tests/sample.eml new file mode 100644 index 000000000..cd2cfa6ef --- /dev/null +++ b/mail_drop_target/tests/sample.eml @@ -0,0 +1,75 @@ +X-MDAV-Result: clean +X-MDAV-Processed: mail3.creublanca.es, Wed, 26 Jun 2019 18:08:19 +0200 +Return-path: +Authentication-Results: mail3.creublanca.es; + auth=pass (login) smtp.auth=etobella@creublanca.es +Received: from WorldClient by creublanca.es with ESMTPA id md50020655392.msg; + Wed, 26 Jun 2019 18:08:19 +0200 +X-Spam-Processed: mail3.creublanca.es, Wed, 26 Jun 2019 18:08:19 +0200 + (not processed: message from trusted or authenticated source) +X-MDArrival-Date: Wed, 26 Jun 2019 18:08:19 +0200 +X-Authenticated-Sender: etobella@creublanca.es +X-Rcpt-To: etobella@creublanca.es +X-MDRcpt-To: etobella@creublanca.es +X-Return-Path: prvs=10800b8cdb=etobella@creublanca.es +X-Envelope-From: etobella@creublanca.es +X-MDaemon-Deliver-To: etobella@creublanca.es +Received: by creublanca.es via MDaemon Webmail with HTTP; + Wed, 26 Jun 2019 18:08:16 +0200 +Date: Wed, 26 Jun 2019 18:08:16 +0200 +From: "Enric Tobella" +To: "Enric Tobella" +Subject: Test +MIME-Version: 1.0 +Content-Type: multipart/alternative; boundary="0626-1608-16-03-PART_BREAK" +Message-ID: +X-Mailer: MDaemon Webmail 19.0.2 + +--0626-1608-16-03-PART_BREAK +Content-Type: text/plain; charset=iso-8859-1 +Content-Transfer-Encoding: 8bit + +TEST + + + + +Tanto este mensaje como los documentos que, en su caso, lleve como anexos, +pueden contener informacin reservada y/o confidencial, destinada exclusivamente +para el uso del destinatario o la persona responsable de entregarlo al mismo, +estando su uso no autorizado prohibido legalmente. +Su contenido no constituye un compromiso para Creu Blanca (la empresa remitente) +salvo ratificacin escrita por ambas partes. En caso de su recepcin por error, +rogamos nos lo comunique por igual va, se abstenga de realizar copias del mensaje +o documentos adjuntos, remitirlo o facilitarlo a un tercero, y proceda en su defecto, +a su eliminacin. + +--0626-1608-16-03-PART_BREAK +Content-Type: text/html; charset=iso-8859-1 +Content-Transfer-Encoding: quoted-printable + + +
TEST
+

+
+
+Tanto este mensaje como los documentos que, en su caso, lleve como anexos, = +
+pueden contener informaci=F3n reservada y/o confidencial, destinada exclusi= +vamente
+para el uso del destinatario o la persona responsable de entregarlo al mism= +o,
+estando su uso no autorizado prohibido legalmente.
+Su contenido no constituye un compromiso para Creu Blanca (la empresa remit= +ente)
+salvo ratificaci=F3n escrita por ambas partes. En caso de su recepci=F3n po= +r error,
+rogamos nos lo comunique por igual v=EDa, se abstenga de realizar copias de= +l mensaje
+o documentos adjuntos, remitirlo o facilitarlo a un tercero, y proceda en s= +u defecto,
+a su eliminaci=F3n.
+ + +--0626-1608-16-03-PART_BREAK-- diff --git a/mail_drop_target/tests/sample.eml:OECustomProperty b/mail_drop_target/tests/sample.eml:OECustomProperty new file mode 100644 index 0000000000000000000000000000000000000000..86ec7b12173d63b8171463ea5073f8c96a70852d GIT binary patch literal 685 zcmZ3<#K5qdfq}s=I3V~z>-I4tO>+Q46Y1$3`Go? z49N@%3?U5p3`q>B3^_oU2$WM`umSQ)P(&SoDvN-kr9hP+Re3;uB7+`Kq?o}DWHf^; z5Ch!+v4j;!hkzwZKtfE-TfdoN(SbQwYO4hKQdP#8WHHHKJHzh8B+zfI7*eF*<2L})v kruYK0_pU(RKDUWlM}M!_kZ%oAoCZ<=vKO2HK^!0e0AQqZ$p8QV literal 0 HcmV?d00001 diff --git a/mail_drop_target/tests/sample.msg b/mail_drop_target/tests/sample.msg new file mode 100644 index 0000000000000000000000000000000000000000..0a5ffcd8203e43ab258112d401a89fd138b3d34f GIT binary patch literal 16896 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 literal 0 HcmV?d00001 diff --git a/mail_drop_target/tests/sample_include_attachment.msg b/mail_drop_target/tests/sample_include_attachment.msg new file mode 100644 index 0000000000000000000000000000000000000000..9f46bed57b3bd870e1de1dbd45d382f5e06fc1e0 GIT binary patch literal 73216 zcmeEv2V4`)(|_m!QthY^R1lOv0-;I^O?tOCLPzNx8;FS55ETV0qGB&#S5!n)?4a0D zz=jP4dqLnobIB?C2n3Pm_rAaXyZL-`w|jd#JA1QpJF|Otr6n>|*?DpgD8B?_iU6gt zNt7boES(?r`N+65g@SQGKx0!=6PfG{I}rW9@V}%58lawd4dnqs0KDEJfGz+rfG9v5 z&=nv7kOW8p;PO*s0J4B?fbIY}Ko3AqKrcXVKp#L~Kz~3#fC7LD&;=+01_K5F1_A~F zlmN;A6@V&W2w*5c4L}2^12h1100W>2&;l?4`T%W!4nPku24Daf1~3E|0gM4A08;=9 zUmR)FCEYk&>F7GMXk2RHy60ZssCfD6D4;0hQ4a0iS8i~@`Wj0TJcVE-5g z+lhb)01v<<^appWH^>KF1jZky`>GTjvNumEPBSTtpG)s*a}i4iLDT&H?b9_BobQ@$_irJ zg;GFlIXzeu?mVb}Md8kaI#3+$KAVWR1m!)km84jM5KF+FXgsl%rX&#O%TQ(!TUp4( zTfl1{3wM?v=>Ifm{aCmX{%}=-K^OQyN(ks8KO!CgdXx=1f}@vcupb36%!_T0vdxBb zz2K}+NDqVmMdh`Ugbmdw}BG41$njDqJ@VEzw7<%hA1U>R&>4i9lIc38K(8{>0!o zj$bgePG6@}_!kGdT?p!J(bwmRKK4H>k5~S2EJyPHXEKj>YqFmNfcys$<=McV8%VAn z$gU4aH0pg!bA=ci;Q3l=H$rgU9hFNi>F#w|Lyt+HE+#_|rg+fBy9Rb#BjR<2dI}137;B z)AL*F{|vCIJH!5wfy$EipVsMX^Fp6!J?vAw%0G?{dFy|jbN}zgGkxCf|ESNfO?l^a;A{;lCFT^f{wn zy#Hbkv}%zp^grNG8m}p@{G;wC`=1SXvK+w2;s_Q(B#h148-~LrjdDVI*Ov1jG5f1{P9vPDDpq4GY|Najtu(dVoGt9#RW-~Vja)6w zZJcc^9o^k9X3lbOHnX(1SEE|FT3S$59GuNH^~|*`^cf7Ajy_$B#?)o$)6AHJuXi8f?M!o4k~=T4eQ%k4kXg)&s*K_VZwu)oNdlbgHQ7&WQ`%hismVxh0C zuW6;PPt&r{Z%O~6+-ZTAmKXUWWuI61>jV6uPXt5j^vwwAM?YLk0p%a{DfTH|>Gy@a z$=m{4r;qxl?f%zZ{?YfsCEo`Byp4ZRpJAWom4A82yM|il-KNsgdBpiUSWl+0UoIo(lANXYhEi2rqu?W&8{B-)SA&95h=H z*t4FX=TPrqD_DW9L;IK4R`?hA|D*noqrc9Oe?<_7L0pk&t^YB3w7+mXfqrFP<$nO= z&EknZ-mCt8|5pOqLwKUkoBtpEEa-pam49W(`#+`s|K0m{i(=kUHOIYL|J?jQ^$^v@JkNaCgcYxCIu@qVrVz&iYs+CNjU@6F~Wx5mFVkNy{ucV6k! zc%-jG*xzmIAH1Ja0ldNmT3+%I4rO# zhC_@Bdlrzx2I4l5&mBhkEcmy9nKC>&9O8JUGwiFt-E$1&wt-nWCn&=Tu7MVu;|yu` zaE>ZaF^6)D;2cZXL;J-B_8j3D)_`*i#~nESFoPrZ@Z;Q-@eB)s24`%d39AImVa0$2 ztQHUg9-9+TCFwu9P6WP`A2&K%j8x5aW0M*`$HE>Me1$YAMNe`dO#n?>=;_-pCfN= zwLgw$P@nS3zdqz8`#)ca!`)*7iTeI~#|m52BnYhQmn5FJ7&7n}{yX)D6QQNdKofDS z8Ww09J3@1yuFwW(f;P~H9}TnxrlEdd!oLpuXrMW)ARYI#AU6|!7N93Ezb5RVUsWG; z3R+nI>Go?~{?Px~cK>Uy{2Kt@ja>O^ojz~=hk;NQt{34|{*56oDgUk0*XtDiO@Kac z{PX62!126FJNQSrGvrP=bk6~?MHUiF%gcS5@;<`8$Si8f3zN`;vnnZ6_hhD7?X_z_z zMkG|i{~-ah(Ep_90GfkC5$_|omW@}6m|GWqND1vw-0E}b#emNg*Pc+J-fZwb1i&7y zp5xR3^$SwMT4L|j<;L+oHVksQgOAmR&}KSI2_|?! zf3P}mg69q(`1vO_^fjZ0*cWJqL#eoO(F?A)H&KcoQ4ddIAHB|TL@DT#L771L7yumb z>a`gF`3(t)Lg`WmIzOLjO8CF9wmxt)2x_7Zts4RBOtGchfhLEdE2QEY9~#_Ka4in5 z?!kKtau*JDWx>_N7Wvs{#G#J7VIDR&k2=K2(&E0C)^bgd&H%W7;TjAq2d_OxQf#3d z^uMEz9zAvz&`(q$FY-M3`!RJ`kM!f*w&<7+cGtdB${*Emlr=IhcC`t#1yzrKh9~H3a9Oypp|e9RWQMeC2$qP<*p@; zP+G`5>To7`ep{3r;aZ?3L%P;L1Lgcz8hCX$Yt$lu3)EA1M$08*OSwV2dcrlu6*!SZ zn{}S$!I6A_xR#uCKd2!>Kq~|QZR`#ICgmS%>yR=FYM>9cq9)WW2Bh5&S|kwa7D4=T z?9e#K#Uk`Pu5e;OyO~3-7_c~vh+bm>R|R`Jme(FlietWCHK_xX69GTY8bKOZvKB*cF=b{ATO3p|khSdM3 zUK#YRI6&QL;8SV7eje-0S=0VAr|oGm>;pL3aD!GLSCL}5ze-j6$2uLa6AO9|)&=`H z_R4VB;!JeMOUAxQ$`ogfBZp_^_p(vOaJ3_jT~J1F>jzf=eNvbg$BnwsLvh@Q<2Oy> zhg&A}(Fxzn;mCXYuEk0@h7WGQV!qP_e_0sXe; ze~>tC=ewRZ3y4SC7=Z$p-jwgp{VTT`3PGS_1;8Tz_9TAEfm3`-`1!O+*HGc5Gz zI##BTg=TKeaBy~H(CBncb%=Yb2l)Xad?VF^eIn7Q{=@Ju%zv79*1`_nEs5VZ&flFs z>NGZgUwKIVPy4Sr$A9s^Xa3-a4TBZ~Y1{m4F`&)gk_FSU{zv_T>)-yq|1S>YrfvTl z(#QKx+w_M*c^Uv-_5W0)&j)`HTIL^l@0|I6-q!yheVqSqn}2*B4(IfE&I2+{yc080VO0O^3`fE9oYKqdgoTm{?J z09DMyT$WNwp1$Ygp1=IoF0Nw)L0p0^X06qdf z0X_r10KNk10p9=(0J8r0F#oTslu%c+?Sp_qxZ%(IT1e$*e-ss-wEmqh3Ul)AQ6g)_ z`?m9Z5xmvG2KYlOlk;2@$A?V?n}*}lAy?Jn8avK&f#?ki0e>XN(#HJgzd-Nnk0WTS zp+}Egfu{j`=-DIh4`|hp{@&L0U+1jVt%T#&BAiSd zYZiToF(5&KutjeR+R>c3h34zCIZwOdoJb6j17!?lmt3=rK2IvdLWuvLWl$Z`&`PHg zmNMzj;he!HtkDRNV$x$6K}axX{)Hp$q)d~u1~}S5pEP>3IX*Ou<8!o}I^k2f=&!}u zAoS#5y*M?(8IvDe^`T%Q?!u1i8A_7k>@E;S_p8CGcDNvkQ#d5vF)%oqK6mv z(EEV2f7h>@)8X)r{<5EYF-f0OWAwQALCqtHHX?awsjY&bZk*gyg4;l7Q!12zJVwDj zXI=$s$?@d;^BUu90BW0mwi`a#iIRaFa;}p)Q73FC>i0cz?JG(U`g?ILupRh5aHfOf ztsqy*;&}*bXhpn==pzpyyeS;|HXtoF-^=|$-4*J9&!6IK56A1mxz6O($KyED@}pHT zH+qL~7J}pd<~+kq)&QSb2qvy0UN@9YDtK_PK7r6`lD3_OP3Wb`%S+!JSx z3CnYab|+<`8x_al=*MM%j?#s#F8C?X3xxCPBy}q&XEc=db8b!Wec<{5^z+gIWNu5~ zog=@TGjOgG`Q?3PiY)2JHETm%w0hVA^oUzGct6TmtEk6`19l5Q8-#m>`Y|-M6Ey|Q z{Ud_{83K&Yo&ny16ahM)0N*FEgu(oq1Rn8Iy!^#ZM)-V=q*Fz@@h3>nrZ@|ZEa);q zWUuT9Nf!Y=nNga;lzQPJ*%4ZTRx)F{O_BBLQX3@tX;>d8NxHVIYgZ31KZ>)sD4(~M zj!cz!4l|r8oj7E?I)6_e%E<^?kVKV$ zN5te1z66@k`XGf;$An(GKGOy%DOSe}h#$CEDO4!eTSBoqNGVM+ zH!{>fW4ngZ2X*Q#b;U4^K}=5#zEV0rSS2GyEmu88OPxr|g!LT|xTw%_z%>xXT2B-~)poH03Q-#9^nDk^D7@7vlO);ZH z7_nHM)^VXGf@$`y=H-f1YpRYbEwn^PS}0eaYDm%7H(c4f=(T~Err}CWk61-#q1lY8 zH~K7ts9?ca_AV|mb~k563sUr{q6zj{_Mv(SfyaAS#fT<2lsHy8y$8UqrizjHUw_akYU~x6rO>2EYNQFsHbN)aDgAY?q8H9a}H|eQcrIV$= zrz)*8N!NdzzHl1VPt$`Lsz{~kt4uI9RG4Kfw$U)8K&eV5!NHl2&*zjNHQFzGn&uMb zl1RlW_cSR}%E{Plr8EXzQ%A8%BhE)hBTiHISx_Ahg zj+%Nm&TrbO(5aWa8CC4~=`z#fW(`g7n4zFPUWZ>}s7AbeskTg&p>UOVoPe1$CDhNz z(vdczUZ~FA)ksWp>d6J>V=UB7E9E07iURccOQ+Mdj885+`%b1xeyF~Vg0wlGCRN;H zhM=@j*w=+%&!tMqZ`3i-V9icj{FoK9L~3a_8){P@=Ru+7(o7eb+!fA@QUPjtbSR~C ziXc^yP8F?N;jG)c%E39qD^ginsMOiKilyOTVJ%cDc2Xv^#F%bXCr@GI#zaJ~=Qlmn zEpPlO@dUrjFh&)1MBTJL&dc9KD&;X<7SyGK>d8}-gFW5T_!GV?JU5ulXS(Fp z@~e!h486 zGZR-G$Xly1P)~D$^SWvJlFsoGmnV`P|8?bN$DGn_*`n`R|QN1U$AS2$mJq4r|aWvQks3MCq)CPnKU=o`{{ zU$r>da5&7Sl3JE^eb0@8n)z2a(0UR5@|uB~l)BlT9{oyL2U4^AH^HKekg_m}QbJS(-+Gct7NMSh*E z02Li=hN^Czwk}O`y0ci=m&&iTP4!I;QjH2t8Wa;gC;mwSA#r-4tkHs%Lbbw8UHCK$ z7kkk3b?LfY3Pekq#45#WyEaKmNhwHc$e74FRi?7X@9S33y`;x|-)k;I=nZ8`rWrCX zOjXnDs}9!n%4+Jpr%!=gN#DwTwf&nEq^NNJ&^TA1VsdtIaTcRiHEYP8f}sW4wY18a zG0-PuG!|z+UARr}ux3APy_D?vW;5m0byGI%d^0y~h8dOehoPhMk*(PuxOR~j{}XBZ z@M?b?0$$NqgrOz+9M2bT&;MybIl2H|=^ur>(f|yt(dXEJe}DaNKG3xysJBI*x99J5 zpe#KAulyf}yj{8U(YpTID+Rbk^R$Wjcs_dC-DNgezM~`#>Byq1vE4y9Dsbc5>t^Qr z=Zw9gwAkNNgc2%MK=0YO%yHR<0v$6sU2uN+4;>60Ln82@XCJ? z(&vLe2>0Oo6*YWcsJ0migrEv2yi_bdLPg~ zf<3P7=a0Og9DM!=eF^w3D_lQ`et`&>h2k9JtykmoM_wSk-eBeb?)f7&q|;zc2l@Pw zFZX_oZ+QvXBw<49kd{?~TD|L7c4IG9`M*6EXXVDfJHPfqdq|6!19 z0^n8tFF@Y5=yTTI@kSr%lHs2<{AbD~NJ5`2hL-r}(C1D6;L;0I0M-FEZHCK`gQWkT zp6UKi({I23?C0kFPoIzP$qj`SUw>MU*5wc9AO3#-ztT+pTBdIWdYm`=Lm$es0q`pS zC6E{InHXB4k6v3#;{N~l$N!~3w@Wmy1t<^L+=?Zu^^*LSz+fE5(W zfuU-}nwL zEf^`_bI>>^pa*HV9vsK!_=GqUyWoC`=7%(+8=*k|J?xkVgL-S z^N-iQZTdJmvIg+V|4qny4}d`*Bx59uB!9ed-RPG%t{=ze$+U>ku^Eh$v8UoZ>yIbP zRJqUi;M^I`i5WqCa7McA(D60oZPREi2c5@ywp0eW;*OW6iGEiGUf&--Em)F+mI%H- z8sE#0PbqO)sI_MlH6Z7o+CZ(#AFe-cTmDe)$nZNJe@(j!3B2`xp4K0N#-qUfgLnBy z*>B7K#{Q2sS!eKnALy$AFtpbGoaa8S)nTqx< z=NWom*yB7yk7++YL(hgW?a$B9dxNdZd4}Gb&`CHq?$s8vJRTQ@eAp!9mORarB0}ef z2))jh^9>0~m!IMSluuH49K$oKzI_BJ?}<33r8r?+f{FWal7bYE8GZXGPy{70z7!xp z=^w2?5g_7Ii8#K;hM$OI9sz(jd}XJ3odjUy);z8NZ`sDU?DzZwi8w@wAOK%e!Q*5Q zhm{D;<0yZ)?uN65ES&I);CdQfzr#TX`R6fYrZiU z_}UJZM+R{i<6@kv-k?8u6LCyKdI}(GxGxMDIPp*-PV&po7e>UoLe7geMvm_aJU}j9$YU0iU<*hB|1b9pbRA9gD?{W0gA>i@i-#>qy4m{G3yE< zFXm4m;-swc^NG{(IF6?UDC>Y9X(Db##5X{^I}z{7JuXh^NyN$W#VMu+cs>~ypj4P( zJPU;yf&k?W5f|ll7zN5#BL1U&>N)v|ylVvoC=EpXF*m;erH?6=M+O0k+%NIIzr_3f z6jz`OAmUgC=Xww1#G%5RIGu=xem~CmDK0?aAH$)~vG@fjqC_0^I|c;`-vm64qh(Hc z{J+FSe~CYtg!v7*`9Z7_IORhEk;eB^9PS@K#RVt=KgGo zD^R)+aSMVr>I+dKPBqI3M17$^5hLQ{{Yil$PQ*F#fc$qQ;y5PZ@Gn8c$?_B^l0+PN z#N*-=DIyNLL=dM){}fk%kuc`P_T}VP_(h&6(s(=>~1BH6+jbb!!L5?77DsVe15N2DkuR0c8FWus^a{9Mi`_d>jD3RfO@0u${!+C(moX z^@U@8+~WbToeY=)2m}NH$h@JjAJ#06=}{1m2E+hjxyPo$b{cn|JeS<^IREDwgxJ3^=NdamM@K|rNyLqdeQZ0noM1~K3&(_TbJR}wpjn! z^CLgX?fCrR|HStf^42PvFlzRN@tQ9D*~88nYbv`bZon%jf6&M8rvX2R{uF=QabJ)AUn$ z{CppY{(<8(0SY1Cg#06Y&R0xv4+8&sbftADw5Wgn-iL+k*xum*K5A6ksK5Xp_^yzJ zwJOuwS6^3C+l%I{r_H1>*}6J3wk|`P#_-nBVR&n5>w9_rxXdl`$Jl^ z?i~MjU%2RPjlbLD|3=@+L;$b-zaP+V-v591{cm#Q-x>Cg0?=3HUXqsdFNZ#F_Rj<; z%c~vyQ=7H_f2aM-Td!b(1&lM0_>MMhXh9mp^G%XLZW)k@ZwvH>6kL1j zO`M?*=XgS_BjNAbPj)N|R~t)EGb8M6EV*jN!V1-lIn9DlGb~UuEJDp#fSR$;Y+e4y zUSQQCi+^JODx&-WFtkpeH~ZHE%JBs7+W(b-b~5D0&@%nbvwu{9KAl6p75co}KWr!q zt$1Gf9|H7A{iCgKZq^YAZtq)yxom&m@YYkmXaC2}-?E?ol#hkVz_2jEry)qp;ZOEI*>e`nf1v}XOUb^5&7Kfb_+KY&;M)qy@a z{^{uW6Wb6&$L!z8@Aj_>Q5M>(?XiC&dA5Hwzn9Oke@S|++do+T@9iIGN+OW<53Vb3 zZvT+w|7`zYdF`=(2>uBB2kHG{|8Qr@IrV7oKg!>~ZT~WWiH_O7cojJT?cZ^*I|mTN zP5X62+P%_Uz5ZeUw$%SR)Be!~{{N=^GZ`2T1n}Db^?*Lv|FNxFy#M??``0WSrViQR z-jQllH+B&5VO=dfPlk@Sk1tJ+&S2A+`gAr;UmxBwOQ(BjF?{G=x(rWLY;A;&z8mhp z)BfSDRq&~TmgJut`FDo>I}GG+2=}_SPMD{Syr3gaUZwAJ_gSBXJ05zv_TJ>qE>Mb5=dG3HujkiE$5C+;YC6gRZ@F`RkZ+&fjO<#R|nwGad_rv6DUo9P)mNt{= z?WwP?>CIs3>*9<~+o9#BtjM=NtF~H5OZVT-w13?|{{N=^8w(ub^HaR`e|Mn2hTAGF z@lTHYJH!4N1@s5;M4vbNCl1PrZwLRQo3;Ocr~TVpV+@+K>qLul%!tKB<2?`uuv!J&m{f zPe<)n(tg3WVuusY%ZGqX6A6BaIEpE;hj04&`P;16?quj_{jr=D#}9wce(ALFL(B5l zdG@a-$lu?ze-q#uV1MRS{=I-c+Cdmv;=eQJ|Gb;^zt-vVHvg9hd?W#Q<=+SBHy{7} z+xCw?(C?W2LtZ^%{Udynf%E;Ld9eLmAnl&qrQZJ|=6`~L|Gzo@MPF_TfY<9E0`y7y zmuxT6e?i)FyslqHIl}dspRKy%8-I-zhA%n1p_&Mi|djxjkI6M@=5!ZQ# zKe2xzkv;%J>-_U(|ICMS76N$XKMrUoBXJ1$cDRm&zij{7yE!?6nz0xKlXx68W2q0H zqBUb!(98%m!_qXTnXypKm=S8mf;TnOB8&L#oEXA(z&C8+cYQeDjpD_2z(PB!zy6l} z^QU~YEPtJ6|4s+_qjRrF%k+7-e-}VmivhgK{|untw*Kin`)4N5|C{ztD)7Ff9sI`w zeX`fL)c&1m|0M9x{_UCnNdw-OwuAp!K%X4{BLDxk{gVXrJ7)jj8}B)Rod3y&Xa<@1 z%l5n7Gt%cD_7A=XA`$8{8-V+BV4Dn>3rGRX1CZ~ESP1)zn#D1F3B;EImT}XU!*&IC zpFFSmb}bxR$34Ctw%LFUfQ^7n05b2EX8V}7gB#xk+ueXYfLuTxU@u@FU_amh;2_`- z;4t6_;3yy;a13x9Z~{;OI0+~OoB|X9P6N&W&H{=7=K$vc7XTLlmjIUmR{$k|QovQf zH9#5QI^YK2Cg2v}HsB86E}$H64{#q)0jLC20UiJz0+7eYuzdok20R5k13U-dvCaw| zy?>#owP=6*MgLc4*&k$A`BVEB^+)O9@ouq%IDa>B)vbif5~w$ zY5)GZewi$vw0}u{|J43PdcWAeqadk6p+))od-kuRPc%HeY0VCf;EYODnc6-~U$zd; z;d;T_Z`f=GEGuJr(tNZub$xZTy*(LPGN*?z=y2}dFGY> zOrTH7p9Wa4Ki(bf1n*J8@8^t$H_BU6SnwtI;Si(3o&~&N*#_b^@U|6qc$VnZqe(s1oEddFyxR||X{_@*rUvID+J7`LY??l0)EekSI-5f zw6aKW)?B2t{oH@GAf4PvcN;ZTyY=lfCfo&i`$N zJ|xDiKJ`oetV}k;z@J@e~^~C$t;gtUi|G&1tCHM1_i|EqFdwrF%x+Oc*COchr@Lcs` zowWA?-KPXDxV^O0{@UZ(VbMKp&m{(|KYsi6wPQZXakH;(AF@$Nka~ZvCg3PFK_7<$iS({GpPq1az7>+hRKYnZX zH-6`GMm-<@gj#?9^=$jKO1amw<;=T^Xryhkw7L3netby!rG-7NzN?<-u)NEY83C@A zYVPX0jH%BLt7)nFXQ&TJPQ5DPEGJIce&M!A(Ly%AMPS!Ch8Oy*KOMbk+$hETZ8sVTGacdd8zd#*I36QsH>mW4!?GCd~eFNqk_lwmPSa_p|Gky7hD2lxeaFP2N~#~yXRB5+ddru5aGx9YdKUFHjtfoKbsKgu`iZ%(__mVrt*gu~+RX0pZcUlY`T7A~0_&9Tj(2+BbWksBfZe=O z>#BE#mGdJ^U%s5Bp5?#o^YJT&54R*eaPa=jE<7XUJ_&gLXD_J`ZBRke`@Jq4_b3w{rxJh zK61~a;%-V@FmyPi`Rzu6=VsN^<%yv?b*PTB7hh?bl7BSVZhF`gzr`!|j6N6A>x#Fu z{`?oE)S;PU2Nj=Jo_CtMr|7Y6^7#ypy?u+k?dav&>~qf&ZVa27X?Aqy^Qv4@(;!t@_x{NmS#tmuhj$-MMS-d%h9>odiu~2sE)lD#Ad|N`YvA1T~kP zg{~K&k4YaJ_(p0)?e&0bhgP|pB|H;Sbh*24p6;R%#i@aF_s5rDz?0z@h(z#eKV1Nx#i1 z-jY(vAwx<}49;7Bc=(sy=?6C6*_$SI@c6(}FTc(iwY7e$ip#w(!xfEE(o@5jv~PO3 z4l^pP=ak3EDy94MTDE6J7nRtJ--KVSy~EfO+r4<1br<)ue!7av{HFUD28Y;z>D z$lvLyYGJm->b2cZ*asM{z2f2)|FLWMq^X;nmWEx=+c)&9G<9OPAy*6UkL&e9deZhC zQ?;yA z|90N$T@HOu2hWucSh-@f+XmBXHZ%I(eU!jgbd;UJm;Z_$IJA^MKQc>s+}vFPUweGC zw%yzNqTMy&zBg~owHOdTy{Yoiz^D&*XQyPF4;b_$tbE6W-BVLe8!MFQ`A>^UZm{bs zzf`Q)>r$-!-HJ8VwRdmU*G@RIddMIdR<7}i)rViru~k~VeExfO?~4}=6&aTt4yWWM zj*@Xd%dfz9KV`(t?KgatPBA|^FC0jnHcd0zm}Ty8t^7jDr{2fT+P>h+nm0??VNDO= zVe4)(&93-lh(@N2DHkyIn#mj_l|Hxk3Nhuo6K{MwcH!m?u?qWMj02uQ>i#sYsxugl-|v5m}zS?*IuujHYbEKedDTAy;M??4UaMxh%Uadee{C5 z8R}mtrKZU}7A;vd*gJ7X%0u;3t3=Psln~kRnR(_X#>-thmiAuA!M(hmUYI5$K6TH! z_@)W)iO@GC!3pBF6eXKdZV2^P_Lw>PRUC^xImY>tCEH;dL+@*hAmq@MT zFr~o*!%p-ll6LbGlAIMS`h1;O|EgX3zCMQ~!xqJiGJT^WFfr?XjCcPe&&~d#Py4bT zQ$FeW^;fr*2sM8G6?_o+0iJ9GYL*gLUKDqaHGevdaZj~_~s@V_^gb^J;{1Dl!B zc4o2HS9a~%Z#*@3%xu9?3NAe)&DNM!PJG8)V!C;gz&u&?2gm%A?Ef#(>h~Eq!sE*RJ{zMPs&-zwxo49F>(DLs$TG9FE5}_<@twb+ zh&F1>>f38B7Ct^QnVvbWn?YZ}2Iwv-1=@p$HjNdyG}T2c-F@Reo4gZzg>`#u_@W~x z*eQ2=xMRaU@qu@vKWfu*rf*K^o%CoR(^D%fp|HWgot4E{9{PwqByr}fm-<0LT2FdD zWj>#u;VJA_%&;?Cvtr!Z4P%tY9agnI(zSXQRsPw?A^9$^zirP(vF+xnBz|>)RD= z(q1#@2A@RQ`do!=&eD6Oe9l``s-B+wyu!z3Z+hCit%-*<^E?-yQ)HV@5Li7m4BQFU4#4UNI3X5jX!e8f_=HM_cp3$pshXiaaW$l|q*! zy;r#1w6$(!Lsho!bw!heyYs8+=O_!a1zcvF*O9mGWj#zU@$7lYyB^mz?F=ZpX)}&i zzEk6H+2t?m=m%HSs5KgI$$M9Q$>-_ghn8}~4&-`G8Fuj6^{P9|RK?7f)lj|qd7Vh_ zVdDGd>Wt9$lGAlE8A=ZH!YxB)WzUt*G)#DWsNdyly9(eI_0K`+upmvlLO5au!-|1F zh7=M0K0ff(3eNbHh!FQPVsx6cxN!a{dUwlD0*&Ue)0W+w)79KfcfjEhPX#=`-W4v- zNc+^iVcNcaJ|~|%2`zb|v%21JTVG$vC2H4?^3Qqf)yqG(ZrWX10V_}M#l7yLG{ZD@ znfi%U0*;Qh^NQ0(_YlvU<1(nBchSt#ck)t2x1||Z-?5n&M?e2%LEqAy+b0LTog}hf z!|_0^+T$6cW*Zo}j%ln(ouObDys_Yl_1lsp8~rp7;|8WP{h1)Y8Ba;eYgIXMTT z&K49L+}G>M^t6NNw6W(t_c5IG^i#}pnfn#Glvy%`Ul-~sM$VI-sO_<7ajt;fac8l%#?_ zpMKQ$*-_ER+e!hoqpUBy6FxSewo5nKij3j9n${bl_a?_YpB!W~C0p+3I{k=&%Bd=n zw?FJFNm8Q=GTm;LM$OvYd$`n^5h-ljsRuHemNr4#|5Gk`xGGxzcUAuFt1|F({M0KF z6iS45i+zRA6KmE^2q{0;Be_=my-Am&lk7Hz^f}nePK+h6-}LIM;sXQ9@}w_@D=FRi zR(~aJ?>7CCVT>)h`C)HAr%Cx;Ikx}OlHi#QUZ1YlkL;gX-taD{a)Lzt@fUIX3M!4` zPrPb+nWZpu%=>A@WnaI{Y}g;aJGkCtyI0e#e3Pc<)%A^aPd<+xv1Q$!y6MMWnXGfv zxHY>l?A_>BH;Z>P*nOyJdcuysz00ZbV$svWH&2{*)xHe5Yb0S+^dK;ITu4^E!ci5sfKS|cXM!jV{!4Ti*F9x4KB;M7d+$h+Ye7NF3D}gfHQ!&7i z;@bVo4LAEHU91@9E_JA(^4^CFio$x(|CN>2?dSDd&72_G)y!avDz)FR%CnS zuve2B#~Q7<@Z$OT_)piG9C|Nxs&B}s+ofQ{d_P?OW~FTE-ikE`E4K^pS$ut1<00d> zFWDj?#mDmln!ZhyRCqD#TYYxSB%_@L#%mk%ni?}E*ME8Q*va>WhW%mVTc1ou-Uxdn za#rEnWTu4X>V{<+51Pu$gNnMo>lNKFyrHSKyuR#FP&~6>)xyp1=Da(WHQD6U4fzk# zR_`e;oB1g#Iqa#egGAGws6~0Bf*WTT=C8Uk_t9%5<)^ZZO?#A-x1ZkfBKB*pMCyx$ z!i&bwxIgk`+1J7 z$_@>5F5pd-Euwh|>o_?vOH3#6HeD3O*tS4*Y6DsxNXWf3+E5#^& z)}Sl#>mnNYbe@LzEjF!rFT+VQx#vIrQkJxuSG?}qcL%hlFMB#&?!@l3SJb9k&i>rv zu|%JhPjUu5+&R}&R{rAcM^B$BRK@nQsy;Wz)PC^_e#ZU%10Kpgt^HxG;NE55ay(QD4Q%G6&A+ddneDaj zZFfid<}Y@I&WTY=Wff)f?dYb(zE4!40`D>svxy3*M`w7O;58M_k7iPOKfU_ z4>^8_IrGV}TiLNQE8pyBD8F2GT8%%)gE}yCll{o>#hIJCl~>KrlThz%TQ?dgE?&Fk zMoNG|p2Y6n&?{ZW1D7U>#g(&s9Sn1TbZ4A-Z|0UcEGWdSC8jSx%+KrQ1vv$%#{I+ zuJknNOFJh}H}tx4Ryi;K{>kU0S_ZDg=5(r-*2OS=<)U@eWi>z24#hE)iOcvs*YJsH1jo=Qvi5Ga zX)6y!eEBxbKr@}XY-y&qQj9~vvU`auK?;NfL<=)mw>UZlAHCr&r}I9k`^&?QY2`O6SyS9&UP(SusgeSyjd}@158%c8dC?Lh&=1 z6&gZCOCYDLVo#7Ni(aqhU;XxR$^aBWPy2U!1eYD1w>D`sNUZde$N~HO`|dq|^y}6) zLyDJpto61h56CVYQ+PoXub;z~fRP@zBBu*w_d@}9l~zRQtv$!h6lE?rCo%V& z>;~bLo2;zQURi16lX_HfuS2j)o`rqz@bO|7P{#&#RC)-{c~lt&kfSvdurNQkLoo89@WfDbnS6!jb}Dcm|Bv# z6)4nbozLNxcIMNC*wkV9_cy1dE$S+|a>TTp^#`5bzpXJGS}^7A)Xb>;X=!7Y*a_XaEIw59 zKukuS^~j`>mD{hdELhSq(w5!RWzXBb(T}*XsOHtKBzy4;ezVzKHXqkoFn8e4(@z(U zC}1-yu8y$o(sQSUxTflYon5oEMyj&ThP+znVCQm9`LQj1TQB{mWBN=i3tL>4c=csX z>O$A<8-&D_X!gl5(|Y<;LzR_5^lD3n-;iPXlQyQOjp;X1 zc5=o_f4*^<2~uVcKDnDTS%x=Ec(ZYmF_CoV`{t- zPi$QLnLRzwVB=_&F2?+cPDQ876rXe(aKrwvPlt2G!|4((YqDck99Y&+Q&cXr^V)XjX}O~ZzjX8{ z42|x_EJ(B;zI=nMwgpIHQ`FF0=|lX{)6Bz?H@M`D>uM{ya`o%wNp~&g7oWM;^;p@r zg+4kaeRM4kNk7a@vuUtDM*l>q9ZkQ9@)0ELGvjtFd%ci&j@k<4&+)-7Wz|*Hn!OUk z%AzOhu2rXw?xGwZv*G;!`N}*GJukIW<#n0o!lDmZs8YwGs0sSVEKm-9RB_aQ1HdF4PPj$xhf-XE@ZPA`8-j{BUu);k=*B+_PI!#c22Km#~9FH zX*Oe%l4>gMUvl%)z1-bSNRiRSWww32r(2}>-b-S&t5Us6?nn%tlXPk7c(Ih8_O(fK z;>{HO=yHZ1Pp)xID_SRM2~yn4&XP$D&g}6(LVkx{e!#}k^e>{@+|xtZsaqzPjgEdT z)0B{0?A42*melW~)j8Mb^RCWgl?M4HiwTU}Djj}X@j>#Y(_5u&cO>1uzul73)7ty(?^QlS($xmYmXsG_l`}7UFGqqWOL~5@I)Eg9o|0q@!OM%4;@kw3y+RT zQRv|^e@N1`A&Qn-R&%mnR#z)6Ic2hmpB=O0@}43y+e>t_fDGxJKI--p8AV<6^DECt ziK}Ltsq9+1{6k}y-1yLJvvcfmscL8XoOQKX{&i8=(Z%y)v$VH&ImeDjjenawN@xhb z?T*J!j!zw~^Zs~3zk|;{7l$0hN9y42lL(|L7>_+4;*y-(%O-W+e`)#{i@7kk!kF(-%xEzRk+A_aX9kmFk2Zt5@7t zZ4@n-Iptv2KFYa86Ne72sJ(pWvi-#51A4`R=~EkvOd?m@P=6?Kf6e`Q?o7)^xA@Y< zL%Jr3XYaQ=cYR?PTX0yu;(4r*+-B8%x4$@POCM0Lm~iLPUg;gZbnefb+ut}>@tE+V z#qU2(32!>m^Ubo!74ugb$vylqi1kHi*zTyts^r-Hw=yakb3Z72c8;eM2Q~S`>-E1`zRcv*hmVa1<3GnvijOX;diBWc zb)(2{E%RVT!$?@F^9iOsx^w&`5&rCOA8)tF@PJ^y2(-%6$AJB(*K@`skH(@o;+Nbs zd-_~l>btC~|Ga~}?b1zcJ`M_Dy<98ozb1ICx^H8H{9T)?N=nfp{)p}sA$X6qQJZ0jqk4z zvU_51_dTa?ynj+XdGbu-rqriLdIn8t8Y^CLbNP}%ADOlxM(beinP?xPr#?M)Gup6QjJxZV<=t~OzE%@+ zya z$EijiE-||`b6pR9Uzd4aFDg!4cGcS}?Yv=$pRU+cU8z$|FYl>Y*F>DXfA;(ll_w+p zPpAonSyZhN9B`<_FL(Ts%~=ZTjGLC-J@I7zknORx79P%JD_`dBa*qou4!C%6sqXQf zaWi9ByH?l+XWJB(JKi0;@Lcrt<5%~-da>M8w$y5*U3QQSLww%l{D*6l;OGP2i29i~ zrzCANci694Y*JIJ=FLz_tBk&G@Nl)+ikv`|X&<({4htXNU$$bzaz>JI*Ad1Wc9e#j zABs(RTD@nNP47WlO^+|TBkWhRr^n>f6?=Qw9?iCYx^T4Ni$N2fiXAwdMC%c=-G5uX zy{X0ozrMYCjqZA2#nIim14r5%H=lq1!(`K4-ml%J6dl;MF=F#b&7pcPY`bad^tx+v zYtTGZ@06vplyzN0KH1$3Jy8AnLu_@vI&-PH_ne)l zD|PQ3G7>ZA<6}SFe8t-I!!^k%a+as%iFMAW=)S<-F{A?JQRWiclMMaC{2RCj-pSk%Q?8e2a`4_srlY!ly{LVrr=i2di* z@U!VkLDQ&X*b*_<2I=4d%e7AIp0va6#?hhCn-@=JirngU(zIks zpuF0-lNYI0%3W0rcTbi(b=!#6%pDB1Pd!X?89C~MwM5RP)h`38gp$)) z4R`!A^9@S2@#h|WJ|{S#x4PAy$+Kg(y3`$z%RA3EHG1jk#>1Ivx8E+zpYW>ZdcNL* z>(*TzF|eC%l>YVThoyb%Z}(=%PtfsOcRhD?tA|s+h%a2`rhRFMKmj)|labQNYK;Zr$!Q;U%^EUe#ZW zIC6W4vZ{N(!4gHTYfM)QS1(zboKmZ|h3S<#H#Mj0%-()ew(dGNZrq_gJ>u;a-AllODlTEWjxW@65~;Ia8C_i;mW7I&G+{9layFbDd?|yMEz)DtoBW4bT`9jl~Iw9 zw%P3T%r^%|Ix23Fk)1KVLiZ3gvRAsp%Wf}b=e+m5Hn8iZSRa+$6S9S@hdACGW+pDI zF`%d3k%6D(DzwK@w#3?x&Y4_)!`Anjq0ZjrV{8^FM+BdG8lM#@q&7{0Pr&M`bswJG7EnZ#Zv+_w|YM-7CN+Le7ZGTW!WbblF#j- zF>FC0?x{g-e)?9~M_cLS>&IT0xcHdW_kJg*QX2<1l>{;p(}RY$qneHMKta-VHYo1VYsO#SkOojvcdMgBYeQYRd5 zNIb$#PsV?=mc?aDQhU=L>~=dT9pNLfSTkgHU5mFJ-rEdg@a^PeD{xRep1~O7ct&eL zDCCj5HTUApZPZ%6rd`*UCJa8+rX@@@b7Yp++;EoPx}%6e!FjCNHTd<*TU6J{cXGhC z;s)szD#CBbqe9!%1MQ7PykE*+yG>qr{B@~Yn@R@V`I{A&-h~ZMkWV}Iqwfq024LOo z)J8Re$-burms$mN^@pMZL_+l=xO!NljLE~$NvTIlgT zH_MlkPfL}(V#0U91z|rJ`|r^je;%ePi#nLl#o}^HNafCGR$>lblJ-^`U#3r~o^<)G z0wixnZ3I=sxGCMh8HV>~b{(vd>ztxDv!;k5qq#7isD4!LP=F5kQN1(wIQDowtd$`g zV=V0n$mJspObq}2$razHN0(c`Gn;NON&&Z|q31T%HV&pn!~sZ?R?po|5ypqyC>KUY z^>M>w!P;_pj2k zoRj;cuN-6V%wnKml(PK~J3d0XuCI`YSm1Xz%Y!Cf?02S&78cZQnwB8pVHdn%r-F7( zCMUCK8qOSNE)9wIQr6*n%~D!?X>$KX6Xop#N49aP^)JaXJwC_5$g=+=l>*}fmpH}G zOw1omtBz*)lou-vdIyJI3Nn?}NG`wVZ`by4hWuLEpZp(`ov1DS4yyzV7i;~x6@arE~ zYJu+_u-;7}_C?aNu+Nu1D0-=Leg^Ms`!(#jQ2)?3W6X;h#o2Z%$so1Vz{F-pF7^Jz zN%zswkvgK#kQ_CK=-f#yO2U*CEZ$U4b;&?;OVv#nTX$v8ylbhNPyB<%?Vo#l8a- zLVT}%ecm^f2`*|GNpv~jTU>cgRLzES+^0-=JR`KANRV$(N#*8EeZ+kCzn6^d`yVBi~Lcb*N;dMnAG~PI##K zY?7kBK}!Df*FOawG`9qolQu?H z7maLepzBKE4Ya7xO%rpJu8KvcA|uzyUTUiCc?ka4Q;5iHwsf97ikg`(l`8!AK9e}t zEFXL-BL35v$NfysrB-$B%a@&|7?APhxMy?0(X4S+Zj{t_b{*N>78+`CJwrT$K`6Xl zTp1x#>X~qf;&#Bc{PTyvlb}{(?mu7u@#?}>)9}EOjz7%sXIvg8f6d1j`?MgM%V+8- zZGyv6QX7vo&{;lUG7jDE^M<`gtRTy{+=g>n#qb(W(*0Pwrynfha`%|N^DV$&7ca{s zG6ybMyqTeVHd~naeL1oC%3Fry#lV<@a|LyU=~Riq3JZnEk2hp4wGY15c*LEj!-x8I ze5n4lRN@CWE4m@(B6qjlS*UKFKbb8Mn<$Af%=Pm$TaJ!+ zSana2h3$@*YWh3RPXgzLRJHaVs-dJSEwu}+QqnLOWR0rWE7xO4Tk*Z&3BpI%7t?lUcpe#PD_~H^CafSbpMQZC(bj~K z?I;WtZm(?GV$h}{mB%aQgx5w?Y~$A-%w!;&A2cbMHDrmQb(0FBQ?oAfqHhmV+w)X$ zYox=B+UZ($N#Xhi`+fawPqiJZ!^m*Rh@J%s(VJ%HKfK$m_8v3g%o3r5JZwkK&O=pf zQ%jZmUGVw2!ym?Y7MK<#7z5eEXdJ~-eHrY=X1}uK)*bLriQ}vi;pC^@Yln1Vq<=sM ze`n9*|DfSSr1nwU)9R`^lF2Wzf@M|GiT7@vy>K{HBc&yyji#~fi98c|Ykj9K;7@oR67RB!hD@@CgZMtuXnifP-BIOm(@apKQKPgI>4jQXM8j{7lS?Mso} zqc`VrrTBp*uW=1rm4A-koSaFs&O*vtWiae+MCS5wsVS$J)j$?^@fPM5qwD?e~^*Ws$-yR|94T`PVy(Cp?Gr_oCVr>?Y6=LnpY_K&5y zR@cw&s*-&2L}Ulgg^$M8u5XS!yzAYYZDeoHw!}f0YcG!F>PA)VdBc{&PB(S+)th(m z`9`O@b{=lCMduAaaqLO1+a=|x(0BX$H(nPv7w*`&BHOE|x_Y>$-ir%&dPCVP5ljzx zd;M^1c4UsvJB523!g(wx3@qsSQpL6IdPyjLQGe8{`)r3!O>bE$&!q9Lo_yD=y-PD)nF6&R zRlEG#17VFi`=}B)tDAwL?T(+%>IN28M*lgj1D&lDFds|3WH0Nt$v+4Tbhocm=Bgbu zdvi1+Kt(mF-v0DbmL!WZY9artqGXD#Bh+ z`BQbB^~dcN8aFsEx@moKqvb4of99cspOu<}S#Nz4+jIUR>aZw*p<}0IMkKzq&2TE+ z;+fRA*43maHj;hITXKNgl*XQ^+*+VWw1}fbP^5`s2dAGKgA(2(fqw5%kL?F|Z;L+L zN|-(-(m-&Cyr_6yH`je3^u@h$(;}yPmXl`EtidtQf}*_pW_RLpAKH&5ctqd1{f%rW zzV{XTjg%v3|8sbHEnDU!TF+g+QTLxSXPA#4%IA~s;;iW!p|71tm}79=>U^~0vNWG? z#Z10Vq%%{12`l!@cKlq1*?1fF4Qb zVKQ<-a3}Zo?_aKJdv*Prw=p$Teg*jTZLrmb&^iN*RDXImY-7V;Uy%8^VLO;+53cVv zd0l2B&c@dy){)r348WE7eXB3rY;3%7R{whAc<{b7XuNTb|3-j~Gb+~s%y+DD|Eu|x z8v!>?gj=Ccy90!3o;luEKzh3ZMu)Rgfe_=0|NjU4B8?i2k+v;qQRCtO&Q3D zuowJ6Wp&L<+=jvAz?uyF5zESoM653%qzh7k+c^EMbb)s z5?Vm~+so&u*M5%1_pd7(YaTOHmAs)Oa!pa_pR2!>B{w<{H`>!vT?+q}-TgJGJLmmC z`rDz5mn${>#c-PUNUA3?`N?RK+gQQ_RII~9wC&`Fd()9-(-9`KVYg;O4d#ONL6!UAiA(4U6|nx%y2tqs1-BVf*EMS^f#h=8_>PA=&K+gj%xY1r5vb!$Y zgG8bSTQLL8n7#(UVmjjPX82#@gYdKCpmu(Gho3>=XUFg}W4M`7AP9aMutPK_`mErO z1J(7_$2=vGkWnBOEx;mE@69*E&rb-(&rjm#CgIs3U0FeZ3lO9|{dL>NR}hPtF(Aka z{=_gaQ$yS{5($ig1x6Wa11x|$ZH8Zv7(!T>#xG2Fe+~mnPzm!>`1z^!^gzHAKRW@q zkN`;*X^dY}R}U5eC$Sihw%rWBFewbbG>c!H?a7NEEY5-ow(*NIotbYTcJos}5E4Fq z76mM|t}ftXqrJe5u_K-1u@0Nz7o~)O=Aia{{m}d6eNS!#5ConR(v=+ybOqP}LE1mQ z0`|qvO#m~sB(13nI5Uw20zMZ1U^D#UlyCxO5!C*IXaaf>s(?f z;_gR(K4<*(Ho))B30rZk%(rW<)s`9noEfmd6Il$WdavP2c*p|G#?gjaL`~($_tN2# z{K34Ofvoht)a1VSxSpt}o{0B7;a~}N6Sxc`V9}rgi;x6?tiLb~ECNN%%ou(O0m5+T zqwhMtmmGwTY`9nQnj`Wicd+(8pW_D|_g?Uknpm7RDSRpye^u|}T{LD1jad}brC7#c z30O>bE-=&58Veu@(R;_C*q=fSW%#e*OFmav=|8esFi?Hpb6FlY{=;^AjNhJp*^+uP z%lvq%(dV90G!~7+;{>lz_2oxxbgeANwIJ^W9yK$LpB^3j6u6H6;w->#LN?YSYh&x< z{;+4`wcR0W&rpHtkS}>G_tnw0HWH1;PNQeQZi3ggF5~gbcpTsYq=0kln-(MI56txUk04-q_ptU;1lrH(bneC=KE!S zC$lV0=H5U1SXD`1_}op=!I56TqGO^R!oSOotjs`Eq$9qkjg+Mfe@g~}yUsvW0`zs8 z@@C*97Nli;!N<)yzw&=bKmbmxMEvicqC-5a{th@`0r1yqU4B~a*Hox}NvY0DuFgxU z{+#sVb7ECaLS=S*Wp-ReR_ynz*ov$e0M5+3V$h#^`@g*nZo2%YP{8)l6EE6VzN3H< z7$6!6UNTi`JN%>2uq@aCQFUu%kG(;LwFliq;38<|DJ>^RNQ#7$X zcx(#jY?{^IzdTQ*q5A<_M4;OV%VY4lPPJMxIoMVrUF*bcTMB4$GBPRXdngYkBoR3o zIgtjNfPPQO4Wl5^&;=0mJvWS!NVBccTZr^3KXS5dL>juTrGQ0HQowc*X|Ox7P{1%Q v#M>Jyv>XgeqUGQpE}zMum4o4ap+SDsU-qYg5jN1vztAhN|5^P%)4+cLIY3&@ literal 0 HcmV?d00001 diff --git a/mail_drop_target/tests/test_mail_drop_target.py b/mail_drop_target/tests/test_mail_drop_target.py new file mode 100644 index 000000000..3af803c5a --- /dev/null +++ b/mail_drop_target/tests/test_mail_drop_target.py @@ -0,0 +1,102 @@ +import base64 +from unittest.mock import patch + +from odoo import exceptions, tools +from odoo.tests.common import TransactionCase + + +class TestMailDropTarget(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.partner = cls.env["res.partner"].create({"name": "TEST PARTNER"}) + cls.partner.message_subscribe(partner_ids=cls.partner.ids) + + def test_eml(self): + message = tools.file_open("addons/mail_drop_target/tests/sample.eml").read() + comments = len(self.partner.message_ids) + self.partner.message_process( + self.partner._name, message, thread_id=self.partner.id + ) + self.partner.invalidate_recordset() + self.assertEqual(comments + 1, len(self.partner.message_ids)) + with self.assertRaises(exceptions.UserError): + self.partner.message_drop( + self.partner._name, message, thread_id=self.partner.id + ) + + def test_msg(self): + message = base64.b64encode( + tools.file_open( + "addons/mail_drop_target/tests/sample.msg", mode="rb" + ).read() + ) + comments = len(self.partner.message_ids) + self.partner.message_process_msg( + self.partner._name, message, thread_id=self.partner.id + ) + self.partner.invalidate_recordset() + self.assertEqual(comments + 1, len(self.partner.message_ids)) + msg = self.partner.message_ids.filtered(lambda m: m.subject == "Test") + self.assertIsNotNone(msg.notified_partner_ids) + with self.assertRaises(exceptions.UserError): + self.partner.message_process_msg( + self.partner._name, message, thread_id=self.partner.id + ) + + def test_msg_with_attachment(self): + message = base64.b64encode( + tools.file_open( + "addons/mail_drop_target/tests/sample_include_attachment.msg", mode="rb" + ).read() + ) + comments = len(self.partner.message_ids) + self.partner.message_process_msg( + self.partner._name, message, thread_id=self.partner.id + ) + self.partner.invalidate_recordset() + self.assertEqual(comments + 1, len(self.partner.message_ids)) + msg = self.partner.message_ids.filtered( + lambda m: m.subject == "Test Mail Attachment" + ) + self.assertIsNotNone(msg.notified_partner_ids) + with self.assertRaises(exceptions.UserError): + self.partner.message_process_msg( + self.partner._name, message, thread_id=self.partner.id + ) + + def test_no_msgextract(self): + with ( + self.assertRaises(exceptions.UserError), + patch("odoo.addons.mail_drop_target.models.mail_thread.Message", new=False), + ): + self.test_msg() + + with ( + self.assertRaises(exceptions.UserError), + patch("odoo.addons.mail_drop_target.models.mail_thread.Message", new=False), + ): + self.test_msg_with_attachment() + + def test_msg_no_notification(self): + message = base64.b64encode( + tools.file_open( + "addons/mail_drop_target/tests/sample.msg", mode="rb" + ).read() + ) + settings = self.env["res.config.settings"].create({}) + settings.disable_notify_mail_drop_target = True + settings.execute() + comments = len(self.partner.message_ids) + self.partner.message_process_msg( + self.partner._name, message, thread_id=self.partner.id + ) + self.partner.invalidate_recordset() + self.assertEqual(comments + 1, len(self.partner.message_ids)) + msg = self.partner.message_ids.filtered(lambda m: m.subject == "Test") + self.assertEqual(len(msg.notified_partner_ids), 0) + with self.assertRaises(exceptions.UserError): + self.partner.message_process_msg( + self.partner._name, message, thread_id=self.partner.id + ) diff --git a/mail_drop_target/views/res_config_settings_views.xml b/mail_drop_target/views/res_config_settings_views.xml new file mode 100644 index 000000000..1aa059746 --- /dev/null +++ b/mail_drop_target/views/res_config_settings_views.xml @@ -0,0 +1,27 @@ + + + + res.config.settings.view.form.inherit.mail + res.config.settings + + + +
+
+ +
+
+
+
+
+
+
+
diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..804d6e55a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +# generated from manifests external_dependencies +extract_msg