diff --git a/mail_post_defer/README.rst b/mail_post_defer/README.rst new file mode 100644 index 000000000..e9488133a --- /dev/null +++ b/mail_post_defer/README.rst @@ -0,0 +1,139 @@ +======================== +Deferred Message Posting +======================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github + :target: https://github.com/OCA/social/tree/15.0/mail_post_defer + :alt: OCA/social +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/social-15-0/social-15-0-mail_post_defer + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/205/15.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module enhances mail threads by using the mail queue by default. + +Without this module, Odoo attempts to notify recipients of your message immediately. +If your mail server is slow or you have many followers, this can mean a lot of time. +Install this module and make Odoo more snappy! + +All emails will be kept in the outgoing queue by at least 30 seconds, +giving you some time to re-think what you wrote. During that time, +you can still delete the message and start again. + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +You need to do nothing. The module is configured appropriately out of the box. + +The mail queue processing is made by a cron job. This is normal Odoo behavior, +not specific to this module. However, since you will start using that queue for +every message posted by any user in any thread, this module configures that job +to execute every minute by default. + +You can still change that cadence after installing the module (although it is +not recommended). To do so: + +#. Log in with an administrator user. +#. Activate developer mode. +#. Go to *Settings > Technical > Automation > Scheduled Actions*. +#. Edit the action named "Mail: Email Queue Manager". +#. Lower down the frequency in the field *Execute Every*. Recommended: 1 minute. + +Usage +===== + +To use this module, you need to: + +#. Go to the form view of any record that has a mail thread. It can be a partner, for example. +#. Post a message. + +The mail is now in the outgoing mail queue. It will be there for at least 30 +seconds. It will be really sent the next time the "Mail: Email Queue Manager" +cron job is executed. + +While the message has not been yet sent: + +#. Hover over the little envelope. You will see a paper airplane icon, + indicating it is still outgoing. +#. Hover over the message and click on the little trash icon to delete it. + Mails will not be sent. + +Known issues / Roadmap +====================== + +* Add minimal deferring time configuration if it ever becomes necessary. See + https://github.com/OCA/social/pull/1001#issuecomment-1461581573 for the + rationale behind current hardcoded value of 30 seconds. + +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 +~~~~~~~ + +* Moduon + +Contributors +~~~~~~~~~~~~ + +* Jairo Llopis (https://www.moduon.team/) + +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. + +.. |maintainer-Yajo| image:: https://github.com/Yajo.png?size=40px + :target: https://github.com/Yajo + :alt: Yajo + +Current `maintainer `__: + +|maintainer-Yajo| + +This module is part of the `OCA/social `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/mail_post_defer/__init__.py b/mail_post_defer/__init__.py new file mode 100644 index 000000000..cc6b6354a --- /dev/null +++ b/mail_post_defer/__init__.py @@ -0,0 +1,2 @@ +from . import models +from .hooks import post_init_hook diff --git a/mail_post_defer/__manifest__.py b/mail_post_defer/__manifest__.py new file mode 100644 index 000000000..976821623 --- /dev/null +++ b/mail_post_defer/__manifest__.py @@ -0,0 +1,25 @@ +# Copyright 2022-2023 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). +{ + "name": "Deferred Message Posting", + "summary": "Faster and cancellable outgoing messages", + "version": "16.0.1.0.0", + "development_status": "Alpha", + "category": "Productivity/Discuss", + "website": "https://github.com/OCA/social", + "author": "Moduon, Odoo Community Association (OCA)", + "maintainers": ["Yajo"], + "license": "LGPL-3", + "depends": [ + "mail", + ], + "post_init_hook": "post_init_hook", + "assets": { + # This could go in mail.assets_messaging, but that's included in + # mail.assets_discuss_public and we don't want the public to be able to + # edit their messages; only the backend. + "web.assets_backend": [ + "mail_post_defer/static/src/**/*.js", + ], + }, +} diff --git a/mail_post_defer/hooks.py b/mail_post_defer/hooks.py new file mode 100644 index 000000000..5b51bc922 --- /dev/null +++ b/mail_post_defer/hooks.py @@ -0,0 +1,23 @@ +# Copyright 2022-2023 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). +import logging + +from odoo import SUPERUSER_ID, api + +_logger = logging.getLogger(__name__) + + +def post_init_hook(cr, registry): + """Increase cadence of mail queue cron.""" + env = api.Environment(cr, SUPERUSER_ID, {}) + try: + cron = env.ref("mail.ir_cron_mail_scheduler_action") + except ValueError: + _logger.warning( + "Couldn't find the standard mail scheduler cron. " + "Maybe no mails will be ever sent!" + ) + else: + _logger.info("Setting mail queue cron cadence to 1 minute") + cron.interval_number = 1 + cron.interval_type = "minutes" diff --git a/mail_post_defer/i18n/mail_post_defer.pot b/mail_post_defer/i18n/mail_post_defer.pot new file mode 100644 index 000000000..528b87631 --- /dev/null +++ b/mail_post_defer/i18n/mail_post_defer.pot @@ -0,0 +1,31 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * mail_post_defer +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.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_post_defer +#: model:ir.model,name:mail_post_defer.model_mail_thread +msgid "Email Thread" +msgstr "" + +#. module: mail_post_defer +#: model:ir.model,name:mail_post_defer.model_mail_message +msgid "Message" +msgstr "" + +#. module: mail_post_defer +#. openerp-web +#: code:addons/mail_post_defer/static/src/xml/message.xml:0 +#, python-format +msgid "messageActionList.message.canBeEdited" +msgstr "" diff --git a/mail_post_defer/models/__init__.py b/mail_post_defer/models/__init__.py new file mode 100644 index 000000000..eccc2881b --- /dev/null +++ b/mail_post_defer/models/__init__.py @@ -0,0 +1,2 @@ +from . import mail_message +from . import mail_thread diff --git a/mail_post_defer/models/mail_message.py b/mail_post_defer/models/mail_message.py new file mode 100644 index 000000000..7f59f4f5c --- /dev/null +++ b/mail_post_defer/models/mail_message.py @@ -0,0 +1,16 @@ +# Copyright 2022-2023 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). + +from odoo import models + + +class MailMessage(models.Model): + _inherit = "mail.message" + + def _cleanup_side_records(self): + """Delete pending outgoing mails.""" + self.mail_ids.filtered(lambda mail: mail.state == "outgoing").unlink() + self.env["mail.message.schedule"].search( + [("mail_message_id", "in", self.ids)] + ).unlink() + return super()._cleanup_side_records() diff --git a/mail_post_defer/models/mail_thread.py b/mail_post_defer/models/mail_thread.py new file mode 100644 index 000000000..ef2ed02d4 --- /dev/null +++ b/mail_post_defer/models/mail_thread.py @@ -0,0 +1,70 @@ +# Copyright 2022-2023 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). + +from datetime import timedelta + +from odoo import _, fields, models +from odoo.exceptions import UserError + + +class MailThread(models.AbstractModel): + _inherit = "mail.thread" + + def message_post(self, **kwargs): + """Post messages using queue by default.""" + _self = self + force_send = self.env.context.get("mail_notify_force_send") or kwargs.get( + "force_send", False + ) + kwargs.setdefault("force_send", force_send) + if not force_send: + # If deferring message, give the user some minimal time to revert it + _self = self.with_context(mail_defer_seconds=30) + return super(MailThread, _self).message_post(**kwargs) + + def _notify_thread(self, message, msg_vals=False, **kwargs): + """Defer emails by default.""" + defer_seconds = self.env.context.get("mail_defer_seconds") + if defer_seconds: + kwargs.setdefault( + "scheduled_date", + fields.Datetime.now() + timedelta(seconds=defer_seconds), + ) + return super()._notify_thread(message, msg_vals=msg_vals, **kwargs) + + def _check_can_update_message_content(self, messages): + """Allow updating unsent messages. + + Upstream Odoo only allows updating notes. We want to be able to update + any message that is not sent yet. When a message is scheduled, + notifications and mails will still not exist. Another possibility is + that they exist but are not sent yet. In those cases, we are still on + time to update it. + """ + try: + # If upstream allows editing, we are done + return super()._check_can_update_message_content(messages) + except UserError: + # Repeat upstream checks that are still valid for us + if messages.tracking_value_ids: + raise + if any(message.message_type != "comment" for message in messages): + raise + # Check that no notification or mail has been sent yet + if any( + ntf.notification_status == "sent" for ntf in messages.notification_ids + ): + raise UserError( + _("Cannot modify message; notifications were already sent.") + ) from None + if any(mail.state in {"sent", "received"} for mail in messages.mail_ids): + raise UserError( + _("Cannot modify message; notifications were already sent.") + ) from None + + def _message_update_content(self, *args, **kwargs): + """Defer messages by extra 30 seconds after updates.""" + kwargs.setdefault( + "scheduled_date", fields.Datetime.now() + timedelta(seconds=30) + ) + return super()._message_update_content(*args, **kwargs) diff --git a/mail_post_defer/readme/CONFIGURE.rst b/mail_post_defer/readme/CONFIGURE.rst new file mode 100644 index 000000000..c70ccb340 --- /dev/null +++ b/mail_post_defer/readme/CONFIGURE.rst @@ -0,0 +1,15 @@ +You need to do nothing. The module is configured appropriately out of the box. + +The mail queue processing is made by a cron job. This is normal Odoo behavior, +not specific to this module. However, since you will start using that queue for +every message posted by any user in any thread, this module configures that job +to execute every minute by default. + +You can still change that cadence after installing the module (although it is +not recommended). To do so: + +#. Log in with an administrator user. +#. Activate developer mode. +#. Go to *Settings > Technical > Automation > Scheduled Actions*. +#. Edit the action named "Mail: Email Queue Manager". +#. Lower down the frequency in the field *Execute Every*. Recommended: 1 minute. diff --git a/mail_post_defer/readme/CONTRIBUTORS.rst b/mail_post_defer/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..c5eed53c6 --- /dev/null +++ b/mail_post_defer/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Jairo Llopis (https://www.moduon.team/) diff --git a/mail_post_defer/readme/DESCRIPTION.rst b/mail_post_defer/readme/DESCRIPTION.rst new file mode 100644 index 000000000..69740ce49 --- /dev/null +++ b/mail_post_defer/readme/DESCRIPTION.rst @@ -0,0 +1,9 @@ +This module enhances mail threads by using the mail queue by default. + +Without this module, Odoo attempts to notify recipients of your message immediately. +If your mail server is slow or you have many followers, this can mean a lot of time. +Install this module and make Odoo more snappy! + +All emails will be kept in the outgoing queue by at least 30 seconds, +giving you some time to re-think what you wrote. During that time, +you can still delete the message and start again. diff --git a/mail_post_defer/readme/ROADMAP.rst b/mail_post_defer/readme/ROADMAP.rst new file mode 100644 index 000000000..e02d4e0d8 --- /dev/null +++ b/mail_post_defer/readme/ROADMAP.rst @@ -0,0 +1,3 @@ +* Add minimal deferring time configuration if it ever becomes necessary. See + https://github.com/OCA/social/pull/1001#issuecomment-1461581573 for the + rationale behind current hardcoded value of 30 seconds. diff --git a/mail_post_defer/readme/USAGE.rst b/mail_post_defer/readme/USAGE.rst new file mode 100644 index 000000000..95b7a47af --- /dev/null +++ b/mail_post_defer/readme/USAGE.rst @@ -0,0 +1,15 @@ +To use this module, you need to: + +#. Go to the form view of any record that has a mail thread. It can be a partner, for example. +#. Post a message. + +The mail is now in the outgoing mail queue. It will be there for at least 30 +seconds. It will be really sent the next time the "Mail: Email Queue Manager" +cron job is executed. + +While the message has not been yet sent: + +#. Hover over the little envelope. You will see a paper airplane icon, + indicating it is still outgoing. +#. Hover over the message and click on the little trash icon to delete it. + Mails will not be sent. diff --git a/mail_post_defer/static/description/icon.png b/mail_post_defer/static/description/icon.png new file mode 100644 index 000000000..94a2bcd5e Binary files /dev/null and b/mail_post_defer/static/description/icon.png differ diff --git a/mail_post_defer/static/description/index.html b/mail_post_defer/static/description/index.html new file mode 100644 index 000000000..184437546 --- /dev/null +++ b/mail_post_defer/static/description/index.html @@ -0,0 +1,479 @@ + + + + + + +Deferred Message Posting + + + +
+

Deferred Message Posting

+ + +

Alpha License: LGPL-3 OCA/social Translate me on Weblate Try me on Runbot

+

This module enhances mail threads by using the mail queue by default.

+

Without this module, Odoo attempts to notify recipients of your message immediately. +If your mail server is slow or you have many followers, this can mean a lot of time. +Install this module and make Odoo more snappy!

+

All emails will be kept in the outgoing queue by at least 30 seconds, +giving you some time to re-think what you wrote. During that time, +you can still delete the message and start again.

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Configuration

+

You need to do nothing. The module is configured appropriately out of the box.

+

The mail queue processing is made by a cron job. This is normal Odoo behavior, +not specific to this module. However, since you will start using that queue for +every message posted by any user in any thread, this module configures that job +to execute every minute by default.

+

You can still change that cadence after installing the module (although it is +not recommended). To do so:

+
    +
  1. Log in with an administrator user.
  2. +
  3. Activate developer mode.
  4. +
  5. Go to Settings > Technical > Automation > Scheduled Actions.
  6. +
  7. Edit the action named “Mail: Email Queue Manager”.
  8. +
  9. Lower down the frequency in the field Execute Every. Recommended: 1 minute.
  10. +
+
+
+

Usage

+

To use this module, you need to:

+
    +
  1. Go to the form view of any record that has a mail thread. It can be a partner, for example.
  2. +
  3. Post a message.
  4. +
+

The mail is now in the outgoing mail queue. It will be there for at least 30 +seconds. It will be really sent the next time the “Mail: Email Queue Manager” +cron job is executed.

+

While the message has not been yet sent:

+
    +
  1. Hover over the little envelope. You will see a paper airplane icon, +indicating it is still outgoing.
  2. +
  3. Hover over the message and click on the little trash icon to delete it. +Mails will not be sent.
  4. +
+
+
+

Known issues / Roadmap

+ +
+
+

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

+
    +
  • Moduon
  • +
+
+
+

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.

+

Current maintainer:

+

Yajo

+

This module is part of the OCA/social project on GitHub.

+

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

+
+
+
+ + diff --git a/mail_post_defer/static/src/js/message.esm.js b/mail_post_defer/static/src/js/message.esm.js new file mode 100644 index 000000000..2bad491a0 --- /dev/null +++ b/mail_post_defer/static/src/js/message.esm.js @@ -0,0 +1,51 @@ +/** @odoo-module **/ +import {registerPatch} from "@mail/model/model_core"; + +// Ensure that the model definition is loaded before the patch +import "@mail/models/message"; + +registerPatch({ + name: "Message", + + fields: { + canBeDeleted: { + /** + * Whether this message can be updated. + * + * Despite the field name, this method is used upstream to determine + * whether a message can be edited or deleted. + * + * Upstream Odoo allows updating notes. We want to allow updating any + * user message that is not yet sent. If there's a race condition, + * anyways the server will repeat these checks. + * + * @returns {Boolean} + * Whether this message can be updated. + * @override + */ + compute() { + // If upstream allows editing, we are done + if (this._super()) { + return true; + } + // Repeat upstream checks that are still valid for us + if (this.trackingValues.length > 0) { + return false; + } + if (this.message_type !== "comment") { + return false; + } + if (this.originThread.model === "mail.channel") { + return true; + } + // Check that no notification has been sent yet + if ( + this.notifications.some((ntf) => ntf.notification_status === "sent") + ) { + return false; + } + return true; + }, + }, + }, +}); diff --git a/mail_post_defer/tests/__init__.py b/mail_post_defer/tests/__init__.py new file mode 100644 index 000000000..f8340d864 --- /dev/null +++ b/mail_post_defer/tests/__init__.py @@ -0,0 +1,2 @@ +from . import test_install +from . import test_mail diff --git a/mail_post_defer/tests/test_install.py b/mail_post_defer/tests/test_install.py new file mode 100644 index 000000000..c83fd2ddb --- /dev/null +++ b/mail_post_defer/tests/test_install.py @@ -0,0 +1,12 @@ +# Copyright 2022-2023 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). + +from odoo.tests.common import TransactionCase + + +class InstallationCase(TransactionCase): + def test_cron_cadence(self): + """Test that the post_init_hook was properly executed.""" + cron = self.env.ref("mail.ir_cron_mail_scheduler_action") + cadence = cron.interval_number, cron.interval_type + self.assertEqual(cadence, (1, "minutes")) diff --git a/mail_post_defer/tests/test_mail.py b/mail_post_defer/tests/test_mail.py new file mode 100644 index 000000000..a65310a52 --- /dev/null +++ b/mail_post_defer/tests/test_mail.py @@ -0,0 +1,180 @@ +# Copyright 2022-2023 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). + +import freezegun + +from odoo.exceptions import UserError + +from odoo.addons.mail.tests.common import MailCommon + + +@freezegun.freeze_time("2023-01-02 10:00:00") +class MessagePostCase(MailCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls._create_portal_user() + # Notify employee by email + cls.user_employee.notification_type = "email" + + def test_standard(self): + """A normal call just uses the queue by default.""" + with self.mock_mail_gateway(): + msg = self.partner_portal.message_post( + body="test body", + subject="test subject", + message_type="comment", + partner_ids=self.partner_employee.ids, + ) + schedules = self.env["mail.message.schedule"].search( + [ + ("mail_message_id", "=", msg.id), + ("scheduled_datetime", "=", "2023-01-02 10:00:30"), + ] + ) + self.assertEqual(len(schedules), 1) + self.assertNoMail(self.partner_employee) + + def test_forced_arg(self): + """A forced send via method argument is sent directly.""" + with self.mock_mail_gateway(): + self.partner_portal.message_post( + body="test body", + subject="test subject", + message_type="comment", + partner_ids=self.partner_employee.ids, + force_send=True, + ) + self.assertMailMail( + self.partner_employee, + "sent", + author=self.env.user.partner_id, + content="test body", + fields_values={"scheduled_date": False}, + ) + + def test_forced_context(self): + """A forced send via context is sent directly.""" + with self.mock_mail_gateway(): + self.partner_portal.with_context(mail_notify_force_send=True).message_post( + body="test body", + subject="test subject", + message_type="comment", + partner_ids=self.partner_employee.ids, + ) + self.assertMailMail( + self.partner_employee, + "sent", + author=self.env.user.partner_id, + content="test body", + fields_values={"scheduled_date": False}, + ) + + def test_msg_edit(self): + """Can update messages. + + Upstream Odoo allows only updating notes, regardless of their sent + status. We allow updating any message that is not sent yet. + """ + with self.mock_mail_gateway(): + msg = self.partner_portal.message_post( + body="test body", + subject="test subject", + message_type="comment", + partner_ids=self.partner_employee.ids, + subtype_xmlid="mail.mt_comment", + ) + schedules = self.env["mail.message.schedule"].search( + [ + ("mail_message_id", "=", msg.id), + ("scheduled_datetime", "=", "2023-01-02 10:00:30"), + ] + ) + self.assertEqual(len(schedules), 1) + self.assertNoMail(self.partner_employee) + # After 15 seconds, the user updates the message + with freezegun.freeze_time("2023-01-02 10:00:15"): + self.partner_portal._message_update_content(msg, "new body") + schedules = self.env["mail.message.schedule"].search( + [ + ("mail_message_id", "=", msg.id), + ("scheduled_datetime", "=", "2023-01-02 10:00:45"), + ] + ) + self.assertEqual(len(schedules), 1) + self.assertNoMail(self.partner_employee) + # After a minute, the mail is created + with freezegun.freeze_time("2023-01-02 10:01:00"): + self.env["mail.message.schedule"]._send_notifications_cron() + self.assertMailMail( + self.partner_employee, + "outgoing", + author=self.env.user.partner_id, + content="new body", + ) + + def test_queued_msg_delete(self): + """A user can delete a message before it's sent.""" + with self.mock_mail_gateway(): + msg = self.partner_portal.message_post( + body="test body", + subject="test subject", + message_type="comment", + partner_ids=self.partner_employee.ids, + subtype_xmlid="mail.mt_comment", + ) + schedules = self.env["mail.message.schedule"].search( + [ + ("mail_message_id", "=", msg.id), + ("scheduled_datetime", "=", "2023-01-02 10:00:30"), + ] + ) + self.assertEqual(len(schedules), 1) + # Emulate user clicking on delete button and going through the + # `/mail/message/update_content` controller + self.partner_portal._message_update_content(msg, "", []) + self.env.flush_all() + self.assertFalse(schedules.exists()) + self.assertNoMail( + self.partner_employee, + author=self.env.user.partner_id, + ) + # One minute later, the cron has no mails to send + with freezegun.freeze_time("2023-01-02 10:01:00"): + self.env["mail.message.schedule"]._send_notifications_cron() + self.env["mail.mail"].process_email_queue() + self.assertNoMail( + self.partner_employee, + author=self.env.user.partner_id, + ) + + def test_no_sent_msg_delete(self): + """A user cannot delete a message after it's sent. + + Usually, the trash button will be hidden in UI if the message is sent. + However, the server-side protection is still important, because there + can be a race condition when the mail is sent in the background but + the user didn't refresh the view. + """ + with self.mock_mail_gateway(): + msg = self.partner_portal.message_post( + body="test body", + subject="test subject", + message_type="comment", + partner_ids=self.partner_employee.ids, + subtype_xmlid="mail.mt_comment", + ) + # One minute later, the cron sends the mail + with freezegun.freeze_time("2023-01-02 10:01:00"): + self.env["mail.message.schedule"]._send_notifications_cron() + self.env["mail.mail"].process_email_queue() + self.assertMailMail( + self.partner_employee, + "sent", + author=self.env.user.partner_id, + content="test body", + ) + # Emulate user clicking on delete button and going through the + # `/mail/message/update_content` controller + with self.assertRaises(UserError): + self.partner_portal._message_update_content(msg, "", []) diff --git a/setup/mail_post_defer/odoo/addons/mail_post_defer b/setup/mail_post_defer/odoo/addons/mail_post_defer new file mode 120000 index 000000000..68bdda7fc --- /dev/null +++ b/setup/mail_post_defer/odoo/addons/mail_post_defer @@ -0,0 +1 @@ +../../../../mail_post_defer \ No newline at end of file diff --git a/setup/mail_post_defer/setup.py b/setup/mail_post_defer/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/mail_post_defer/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)