From 99854e05175ce10e420549631d88883f717aa09a Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Fri, 25 Sep 2015 18:14:21 +0200 Subject: [PATCH 1/6] Add beautier pages for unsubscription process. --- mass_mailing_custom_unsubscribe/README.rst | 34 ++++++----- mass_mailing_custom_unsubscribe/__init__.py | 2 +- .../__manifest__.py | 6 +- .../controllers.py | 15 +++++ .../views/pages.xml | 61 +++++++++++++++++++ 5 files changed, 101 insertions(+), 17 deletions(-) create mode 100644 mass_mailing_custom_unsubscribe/controllers.py create mode 100644 mass_mailing_custom_unsubscribe/views/pages.xml diff --git a/mass_mailing_custom_unsubscribe/README.rst b/mass_mailing_custom_unsubscribe/README.rst index 784b2706..d2294e44 100644 --- a/mass_mailing_custom_unsubscribe/README.rst +++ b/mass_mailing_custom_unsubscribe/README.rst @@ -1,9 +1,9 @@ .. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg :alt: License: AGPL-3 -==================================================== -Customizable unsubscribe link on mass mailing emails -==================================================== +========================================================== +Customizable unsubscription process on mass mailing emails +========================================================== With this module you can set a custom unsubscribe link append at bottom of mass mailing emails. @@ -12,22 +12,27 @@ mailing emails. Configuration ============= -To configure unsubscribe label go to Setting > Technical > Parameters > System parameters -and add a 'mass_mailing.unsubscribe.label' parameter with html to set at bottom -of mass emailing emails. Including '%(url)s' variable where unsubscribe link +To configure unsubscribe label go to *Setting > Technical > Parameters > System +parameters* and add a 'mass_mailing.unsubscribe.label' parameter with html to +set at bottom of mass emailing emails. Including ``%(url)s`` variable where +unsubscribe link. -For example: +For example:: -.. code:: html + You can unsubscribe here - You can unsubscribe here +Additionally, you can disable this link if you set this parameter to ``False``. +If this parameter (``mass_mailing.unsubscribe.label``) is not set (or set to +``''``) default 'Click to unsubscribe' link will appear. This default text is +translatable via *Settings > Translations > Application Terms > Translated +terms*. -Additionally, you can disable this link if you set this parameter to 'False' +Also your unsubscriptors will recieve a beautier good bye page. You can +customize it clicking here **after installing the module**: -If this parameter (mass_mailing.unsubscribe.label) is not set (or set to '') -default 'Click to unsubscribe' link will appear. This default text is -translatable via Settings > Translations > Application Terms > Translated terms +* `Unsubscription successful `_. +* `Unsubscription failed `_. Usage @@ -69,6 +74,7 @@ Contributors * Rafael Blasco * Antonio Espinosa +* Jairo Llopis Maintainer ---------- @@ -83,4 +89,4 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -To contribute to this module, please visit http://odoo-community.org. \ No newline at end of file +To contribute to this module, please visit http://odoo-community.org. diff --git a/mass_mailing_custom_unsubscribe/__init__.py b/mass_mailing_custom_unsubscribe/__init__.py index 8764d62c..17b13c3f 100644 --- a/mass_mailing_custom_unsubscribe/__init__.py +++ b/mass_mailing_custom_unsubscribe/__init__.py @@ -4,4 +4,4 @@ # For copyright and license notices, see __openerp__.py file in root directory ############################################################################## -from . import models +from . import controllers, models diff --git a/mass_mailing_custom_unsubscribe/__manifest__.py b/mass_mailing_custom_unsubscribe/__manifest__.py index 5429b38b..65d727f7 100644 --- a/mass_mailing_custom_unsubscribe/__manifest__.py +++ b/mass_mailing_custom_unsubscribe/__manifest__.py @@ -21,13 +21,15 @@ # ############################################################################## { - 'name': "Customizable unsubscribe link on mass mailing emails", + 'name': "Customizable unsubscription process on mass mailing emails", 'category': 'Marketing', - 'version': '8.0.1.0.0', + 'version': '8.0.2.0.0', 'depends': [ 'mass_mailing', + 'website_crm', ], 'data': [ + 'views/pages.xml', ], 'author': 'Antiun Ingeniería S.L., ' 'Odoo Community Association (OCA)', diff --git a/mass_mailing_custom_unsubscribe/controllers.py b/mass_mailing_custom_unsubscribe/controllers.py new file mode 100644 index 00000000..b05ceee5 --- /dev/null +++ b/mass_mailing_custom_unsubscribe/controllers.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# © 2015 Antiun Ingeniería S.L. (http://www.antiun.com) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from openerp import http +from openerp.addons.mass_mailing.controllers.main import MassMailController + + +class CustomUnsuscribe(MassMailController): + @http.route() + def mailing(self, *args, **kwargs): + path = "/page/mass_mail_unsubscription_%s" + result = super(CustomUnsuscribe, self).mailing(*args, **kwargs) + return http.local_redirect( + path % ("success" if result.data == "OK" else "failure")) diff --git a/mass_mailing_custom_unsubscribe/views/pages.xml b/mass_mailing_custom_unsubscribe/views/pages.xml new file mode 100644 index 00000000..566f6f69 --- /dev/null +++ b/mass_mailing_custom_unsubscribe/views/pages.xml @@ -0,0 +1,61 @@ + + + + + + + + + + From 4706a6984cc050e65ff0b3a1288e4b665bac0096 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Wed, 14 Oct 2015 16:54:15 +0200 Subject: [PATCH 2/6] Fix typos and aesthetic failures. --- mass_mailing_custom_unsubscribe/README.rst | 20 +++++++++---------- .../__manifest__.py | 2 +- .../views/pages.xml | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/mass_mailing_custom_unsubscribe/README.rst b/mass_mailing_custom_unsubscribe/README.rst index d2294e44..293670cc 100644 --- a/mass_mailing_custom_unsubscribe/README.rst +++ b/mass_mailing_custom_unsubscribe/README.rst @@ -5,17 +5,17 @@ Customizable unsubscription process on mass mailing emails ========================================================== -With this module you can set a custom unsubscribe link append at bottom of mass -mailing emails. +With this module you can set a custom unsubscribe link appended at the bottom +of mass mailing emails. Configuration ============= -To configure unsubscribe label go to *Setting > Technical > Parameters > System -parameters* and add a 'mass_mailing.unsubscribe.label' parameter with html to -set at bottom of mass emailing emails. Including ``%(url)s`` variable where -unsubscribe link. +To configure unsubscribe label go to *Settings > Technical > Parameters > +System parameters* and add a ``mass_mailing.unsubscribe.label`` parameter +with HTML to set at the bottom of mass emailing emails. Including ``%(url)s`` +variable where unsubscribe link. For example:: @@ -23,12 +23,12 @@ For example:: Additionally, you can disable this link if you set this parameter to ``False``. -If this parameter (``mass_mailing.unsubscribe.label``) is not set (or set to -``''``) default 'Click to unsubscribe' link will appear. This default text is +If this parameter (``mass_mailing.unsubscribe.label``) does not exist, the +default 'Click to unsubscribe' link will appear, with the advantage that it is translatable via *Settings > Translations > Application Terms > Translated terms*. -Also your unsubscriptors will recieve a beautier good bye page. You can +Also your unsubscriptors will recieve a beautier goodbye page. You can customize it clicking here **after installing the module**: * `Unsubscription successful `_. @@ -46,7 +46,7 @@ Usage Known issues / Roadmap ====================== -* This custom html is not translatable, so as a suggestion, you can define +* This custom HTML is not translatable, so as a suggestion, you can define the same text in several languages in several lines. For example: diff --git a/mass_mailing_custom_unsubscribe/__manifest__.py b/mass_mailing_custom_unsubscribe/__manifest__.py index 65d727f7..5d749a3e 100644 --- a/mass_mailing_custom_unsubscribe/__manifest__.py +++ b/mass_mailing_custom_unsubscribe/__manifest__.py @@ -23,7 +23,7 @@ { 'name': "Customizable unsubscription process on mass mailing emails", 'category': 'Marketing', - 'version': '8.0.2.0.0', + 'version': '8.0.1.1.0', 'depends': [ 'mass_mailing', 'website_crm', diff --git a/mass_mailing_custom_unsubscribe/views/pages.xml b/mass_mailing_custom_unsubscribe/views/pages.xml index 566f6f69..b4ed5cf5 100644 --- a/mass_mailing_custom_unsubscribe/views/pages.xml +++ b/mass_mailing_custom_unsubscribe/views/pages.xml @@ -11,7 +11,7 @@

You were successfully unsubscribed from our - mailing list + mailing list.

It's sad to see you go, but if you love @@ -40,10 +40,10 @@

There was an error processing your unsubscription - request + request.

- We apologize for the inconvinience. You can contact us + We apologize for the inconvenience. You can contact us and we will handle your unsubscription manually.

Thanks for your patience.

From 97566e1c4784046cb3c7e5f802369265b39993d7 Mon Sep 17 00:00:00 2001 From: Yajo Date: Mon, 25 Jul 2016 12:50:32 +0200 Subject: [PATCH 3/6] [8.0][IMP][mass_mailing_custom_unsubscribe] Get reasons for unsubscription (#58) * [8.0][IMP][mass_mailing_custom_unsubscribe] Get reasons for unsubscription. --- mass_mailing_custom_unsubscribe/README.rst | 56 ++- .../__manifest__.py | 15 +- .../controllers.py | 15 - .../controllers/__init__.py | 5 + .../controllers/main.py | 237 +++++++++++ .../data/install_salt.xml | 11 + .../data/mail.unsubscription.reason.csv | 5 + mass_mailing_custom_unsubscribe/exceptions.py | 9 + mass_mailing_custom_unsubscribe/i18n/es.po | 376 +++++++++++++++++- .../images/failure.png | Bin 0 -> 42059 bytes .../images/form.png | Bin 0 -> 36564 bytes .../images/success.png | Bin 0 -> 35465 bytes .../migrations/8.0.2.0.0/pre-migrate.py | 27 ++ .../models/__init__.py | 8 +- .../models/mail_mail.py | 35 +- .../models/mail_mass_mailing.py | 35 ++ .../models/mail_mass_mailing_list.py | 15 + .../models/mail_unsubscription.py | 74 ++++ .../security/ir.model.access.csv | 6 + .../static/src/js/require_details.js | 13 + .../tests/__init__.py | 7 + .../tests/test_controller.py | 111 ++++++ .../tests/test_mail_mail.py | 97 +++++ .../tests/test_unsubscription.py | 21 + .../views/assets.xml | 17 + .../views/mail_mass_mailing_list_view.xml | 21 + .../views/mail_unsubscription_reason_view.xml | 60 +++ .../views/mail_unsubscription_view.xml | 89 +++++ .../views/pages.xml | 106 ++++- 29 files changed, 1411 insertions(+), 60 deletions(-) delete mode 100644 mass_mailing_custom_unsubscribe/controllers.py create mode 100644 mass_mailing_custom_unsubscribe/controllers/__init__.py create mode 100644 mass_mailing_custom_unsubscribe/controllers/main.py create mode 100644 mass_mailing_custom_unsubscribe/data/install_salt.xml create mode 100644 mass_mailing_custom_unsubscribe/data/mail.unsubscription.reason.csv create mode 100644 mass_mailing_custom_unsubscribe/exceptions.py create mode 100644 mass_mailing_custom_unsubscribe/images/failure.png create mode 100644 mass_mailing_custom_unsubscribe/images/form.png create mode 100644 mass_mailing_custom_unsubscribe/images/success.png create mode 100644 mass_mailing_custom_unsubscribe/migrations/8.0.2.0.0/pre-migrate.py create mode 100644 mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py create mode 100644 mass_mailing_custom_unsubscribe/models/mail_mass_mailing_list.py create mode 100644 mass_mailing_custom_unsubscribe/models/mail_unsubscription.py create mode 100644 mass_mailing_custom_unsubscribe/security/ir.model.access.csv create mode 100644 mass_mailing_custom_unsubscribe/static/src/js/require_details.js create mode 100644 mass_mailing_custom_unsubscribe/tests/__init__.py create mode 100644 mass_mailing_custom_unsubscribe/tests/test_controller.py create mode 100644 mass_mailing_custom_unsubscribe/tests/test_mail_mail.py create mode 100644 mass_mailing_custom_unsubscribe/tests/test_unsubscription.py create mode 100644 mass_mailing_custom_unsubscribe/views/assets.xml create mode 100644 mass_mailing_custom_unsubscribe/views/mail_mass_mailing_list_view.xml create mode 100644 mass_mailing_custom_unsubscribe/views/mail_unsubscription_reason_view.xml create mode 100644 mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml diff --git a/mass_mailing_custom_unsubscribe/README.rst b/mass_mailing_custom_unsubscribe/README.rst index 293670cc..551b6d17 100644 --- a/mass_mailing_custom_unsubscribe/README.rst +++ b/mass_mailing_custom_unsubscribe/README.rst @@ -8,10 +8,17 @@ Customizable unsubscription process on mass mailing emails With this module you can set a custom unsubscribe link appended at the bottom of mass mailing emails. +It also displays a beautiful and simple unsubscription form when somebody +unsubscribes, to let you know why and let the user unsubscribe form another +mailing lists at the same time; and then displays a beautiful and customizable +goodbye message. Configuration ============= +Unsubscription Message In Mail Footer +------------------------------------- + To configure unsubscribe label go to *Settings > Technical > Parameters > System parameters* and add a ``mass_mailing.unsubscribe.label`` parameter with HTML to set at the bottom of mass emailing emails. Including ``%(url)s`` @@ -28,34 +35,63 @@ default 'Click to unsubscribe' link will appear, with the advantage that it is translatable via *Settings > Translations > Application Terms > Translated terms*. -Also your unsubscriptors will recieve a beautier goodbye page. You can -customize it clicking here **after installing the module**: +Unsubscription Reasons +---------------------- + +You can customize what reasons will be displayed to your unsubscriptors when +they are going to unsubscribe. To do it: + +#. Go to *Marketing > Configuration > Unsubscription Reasons*. +#. Create / edit / remove / sort as usual. +#. If *Details required* is enabled, they will have to fill a text area to + continue. + +Unsubscription Goodbye Message +------------------------------ -* `Unsubscription successful `_. -* `Unsubscription failed `_. +Your unsubscriptors will receive a beautier goodbye page. You can customize it +with these links **after installing the module**: +* `Unsubscription successful `_. +* `Unsubscription failed `_. Usage ===== +Once configured, just send mass mailings as usual. + +If somebody gets unsubscribed, you will see logs about that under +*Marketing > Mass Mailing > Unsubscriptions*. + .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot :target: https://runbot.odoo-community.org/runbot/205/8.0 - Known issues / Roadmap ====================== +* This needs tests. * This custom HTML is not translatable, so as a suggestion, you can define the same text in several languages in several lines. -For example: + For example: .. code:: html [EN] You can unsubscribe here
[ES] Puedes darte de baja aquí +* If you use the ``website_multi`` module, you will probably find that the + views are not visible by default. +* This module adds a security hash for mass mailing unsubscription URLs, which + makes to not work anymore URLs of mass mailing messages sent before its + installation. If you need backwards compatibility, disable this security + feature by removing the ``mass_mailing.salt`` system parameter. To avoid + breaking current installations, you will not get a salt if you are upgrading + the addon. If you want a salt, create the above system parameter and assign a + random value to it. +* Security should be patched upstream. Remove security features in the version + where https://github.com/odoo/odoo/pull/12040 gets merged (if it does). Bug Tracker =========== @@ -63,8 +99,10 @@ 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 -`here `_. - +`here `_. Credits ======= @@ -89,4 +127,4 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -To contribute to this module, please visit http://odoo-community.org. +To contribute to this module, please visit https://odoo-community.org. diff --git a/mass_mailing_custom_unsubscribe/__manifest__.py b/mass_mailing_custom_unsubscribe/__manifest__.py index 5d749a3e..e4d97195 100644 --- a/mass_mailing_custom_unsubscribe/__manifest__.py +++ b/mass_mailing_custom_unsubscribe/__manifest__.py @@ -23,15 +23,28 @@ { 'name': "Customizable unsubscription process on mass mailing emails", 'category': 'Marketing', - 'version': '8.0.1.1.0', + 'version': '8.0.2.0.0', 'depends': [ 'mass_mailing', 'website_crm', ], 'data': [ + 'security/ir.model.access.csv', + 'data/install_salt.xml', + 'data/mail.unsubscription.reason.csv', + 'views/assets.xml', + 'views/mail_unsubscription_reason_view.xml', + 'views/mail_mass_mailing_list_view.xml', + 'views/mail_unsubscription_view.xml', 'views/pages.xml', ], + 'images': [ + 'images/failure.png', + 'images/form.png', + 'images/success.png', + ], 'author': 'Antiun Ingeniería S.L., ' + 'Tecnativa,' 'Odoo Community Association (OCA)', 'website': 'http://www.antiun.com', 'license': 'AGPL-3', diff --git a/mass_mailing_custom_unsubscribe/controllers.py b/mass_mailing_custom_unsubscribe/controllers.py deleted file mode 100644 index b05ceee5..00000000 --- a/mass_mailing_custom_unsubscribe/controllers.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -# © 2015 Antiun Ingeniería S.L. (http://www.antiun.com) -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). - -from openerp import http -from openerp.addons.mass_mailing.controllers.main import MassMailController - - -class CustomUnsuscribe(MassMailController): - @http.route() - def mailing(self, *args, **kwargs): - path = "/page/mass_mail_unsubscription_%s" - result = super(CustomUnsuscribe, self).mailing(*args, **kwargs) - return http.local_redirect( - path % ("success" if result.data == "OK" else "failure")) diff --git a/mass_mailing_custom_unsubscribe/controllers/__init__.py b/mass_mailing_custom_unsubscribe/controllers/__init__.py new file mode 100644 index 00000000..49478571 --- /dev/null +++ b/mass_mailing_custom_unsubscribe/controllers/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# © 2016 Jairo Llopis +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import main diff --git a/mass_mailing_custom_unsubscribe/controllers/main.py b/mass_mailing_custom_unsubscribe/controllers/main.py new file mode 100644 index 00000000..263d39b4 --- /dev/null +++ b/mass_mailing_custom_unsubscribe/controllers/main.py @@ -0,0 +1,237 @@ +# -*- coding: utf-8 -*- +# © 2015 Antiun Ingeniería S.L. (http://www.antiun.com) +# © 2016 Jairo Llopis +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from openerp import exceptions +from openerp.http import local_redirect, request, route +from openerp.addons.mass_mailing.controllers.main import MassMailController +from .. import exceptions as _ex + + +class CustomUnsubscribe(MassMailController): + def _mailing_list_contacts_by_email(self, email): + """Gets the mailing list contacts by email. + + This should not be displayed to the final user if security validations + have not been matched. + """ + return request.env["mail.mass_mailing.contact"].sudo().search([ + ("email", "=", email), + ("opt_out", "=", False), + ("list_id.not_cross_unsubscriptable", "=", False), + ]) + + def unsubscription_reason(self, mailing_id, email, res_id, token, + qcontext_extra=None): + """Get the unsubscription reason form. + + :param mail.mass_mailing mailing_id: + Mailing where the unsubscription is being processed. + + :param str email: + Email to be unsubscribed. + + :param int res_id: + ID of the unsubscriber. + + :param dict qcontext_extra: + Additional dictionary to pass to the view. + """ + values = self.unsubscription_qcontext(mailing_id, email, res_id, token) + values.update(qcontext_extra or dict()) + return request.website.render( + "mass_mailing_custom_unsubscribe.reason_form", + values) + + def unsubscription_qcontext(self, mailing_id, email, res_id, token): + """Get rendering context for unsubscription form. + + :param mail.mass_mailing mailing_id: + Mailing where the unsubscription is being processed. + + :param str email: + Email to be unsubscribed. + + :param int res_id: + ID of the unsubscriber. + """ + email_fname = origin_name = None + domain = [("id", "=", res_id)] + record_ids = request.env[mailing_id.mailing_model].sudo() + + if "email_from" in record_ids._fields: + email_fname = "email_from" + elif "email" in record_ids._fields: + email_fname = "email" + + if not (email_fname and email): + # Trying to unsubscribe without email? Bad boy... + raise exceptions.AccessDenied() + + domain.append((email_fname, "ilike", email)) + + # Search additional mailing lists for the unsubscriber + additional_contacts = self._mailing_list_contacts_by_email(email) + + if record_ids._name == "mail.mass_mailing.contact": + domain.append( + ("list_id", "in", mailing_id.contact_list_ids.ids)) + + # Unsubscription targets + record_ids = record_ids.search(domain) + + if record_ids._name == "mail.mass_mailing.contact": + additional_contacts -= record_ids + + if not record_ids: + # Trying to unsubscribe with fake criteria? Bad boy... + raise exceptions.AccessDenied() + + # Get data to identify the source of the unsubscription + fnames = self.unsubscription_special_fnames(record_ids._name) + first = record_ids[:1] + contact_name = first[fnames.get("contact", "name")] + origin_model_name = request.env["ir.model"].search( + [("model", "=", first._name)]).name + try: + first = first[fnames["related"]] + except KeyError: + pass + try: + origin_name = first[fnames["origin"]] + except KeyError: + pass + + # Get available reasons + reason_ids = ( + request.env["mail.unsubscription.reason"].search([])) + + return { + "additional_contact_ids": additional_contacts, + "contact_name": contact_name, + "email": email, + "mailing_id": mailing_id, + "origin_model_name": origin_model_name, + "origin_name": origin_name, + "reason_ids": reason_ids, + "record_ids": record_ids, + "res_id": res_id, + "token": token, + } + + def unsubscription_special_fnames(self, model): + """Define special field names to generate the unsubscription qcontext. + + :return dict: + Special fields will depend on the model, so this method should + return something like:: + + { + "related": "parent_id", + "origin": "display_name", + "contact": "contact_name", + } + + Where: + + - ``model.name`` is the technical name of the model. + - ``related`` indicates the name of a field in ``model.name`` that + contains a :class:`openerp.fields.Many2one` field which is + considered what the user is unsubscribing from. + - ``origin``: is the name of the field that contains the name of + what the user is unsubscribing from. + - ``contact`` is the name of the field that contains the name of + the user that is unsubscribing. + + Missing keys will mean that nothing special is required for that + model and it will use the default values. + """ + specials = { + "mail.mass_mailing.contact": { + "related": "list_id", + "origin": "display_name", + }, + "crm.lead": { + "origin": "name", + "contact": "contact_name", + }, + "hr.applicant": { + "related": "job_id", + "origin": "name", + }, + # In case you install OCA's event_registration_mass_mailing + "event.registration": { + "related": "event_id", + "origin": "name", + }, + } + return specials.get(model, dict()) + + @route(auth="public", website=True) + def mailing(self, mailing_id, email=None, res_id=None, **post): + """Display a confirmation form to get the unsubscription reason.""" + mailing = request.env["mail.mass_mailing"] + path = "/page/mass_mailing_custom_unsubscribe.%s" + good_token = mailing.hash_create(mailing_id, res_id, email) + + # Trying to unsubscribe with fake hash? Bad boy... + if good_token and post.get("token") != good_token: + return local_redirect(path % "failure") + + mailing = mailing.sudo().browse(mailing_id) + contact = request.env["mail.mass_mailing.contact"].sudo() + unsubscription = request.env["mail.unsubscription"].sudo() + + if not post.get("reason_id"): + # We need to know why you leave, get to the form + return self.unsubscription_reason( + mailing, email, res_id, post.get("token")) + + # Save reason and details + try: + with request.env.cr.savepoint(): + records = unsubscription.create({ + "email": email, + "unsubscriber_id": ",".join( + (mailing.mailing_model, res_id)), + "reason_id": int(post["reason_id"]), + "details": post.get("details", False), + "mass_mailing_id": mailing_id, + }) + + # Should provide details, go back to form + except _ex.DetailsRequiredError: + return self.unsubscription_reason( + mailing, email, res_id, post.get("token"), + {"error_details_required": True}) + + # Unsubscribe from additional lists + for key, value in post.iteritems(): + try: + label, list_id = key.split(",") + if label != "list_id": + raise ValueError + list_id = int(list_id) + except ValueError: + pass + else: + contact_id = contact.browse(int(value)) + if contact_id.list_id.id == list_id: + contact_id.opt_out = True + records += unsubscription.create({ + "email": email, + "unsubscriber_id": ",".join((contact._name, value)), + "reason_id": int(post["reason_id"]), + "details": post.get("details", False), + "mass_mailing_id": mailing_id, + }) + + # All is OK, unsubscribe + result = super(CustomUnsubscribe, self).mailing( + mailing_id, email, res_id, **post) + records.write({"success": result.data == "OK"}) + + # Redirect to the result + return local_redirect(path % ("success" if result.data == "OK" + else "failure")) diff --git a/mass_mailing_custom_unsubscribe/data/install_salt.xml b/mass_mailing_custom_unsubscribe/data/install_salt.xml new file mode 100644 index 00000000..46732d60 --- /dev/null +++ b/mass_mailing_custom_unsubscribe/data/install_salt.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/mass_mailing_custom_unsubscribe/data/mail.unsubscription.reason.csv b/mass_mailing_custom_unsubscribe/data/mail.unsubscription.reason.csv new file mode 100644 index 00000000..061d1587 --- /dev/null +++ b/mass_mailing_custom_unsubscribe/data/mail.unsubscription.reason.csv @@ -0,0 +1,5 @@ +"id","name","sequence","details_required" +"reason_not_interested","I'm not interested",10,"False" +"reason_not_requested","I did not request this",20,"False" +"reason_too_many","I get too many emails",30,"False" +"reason_other","Other reason",100,"True" diff --git a/mass_mailing_custom_unsubscribe/exceptions.py b/mass_mailing_custom_unsubscribe/exceptions.py new file mode 100644 index 00000000..efaac908 --- /dev/null +++ b/mass_mailing_custom_unsubscribe/exceptions.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# © 2016 Jairo Llopis +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import exceptions + + +class DetailsRequiredError(exceptions.ValidationError): + pass diff --git a/mass_mailing_custom_unsubscribe/i18n/es.po b/mass_mailing_custom_unsubscribe/i18n/es.po index a5a9da56..4405dea3 100644 --- a/mass_mailing_custom_unsubscribe/i18n/es.po +++ b/mass_mailing_custom_unsubscribe/i18n/es.po @@ -1,29 +1,381 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: -# * mass_mailing_custom_unsubscribe -# -# Translators: +# * mass_mailing_custom_unsubscribe +# msgid "" msgstr "" -"Project-Id-Version: social (8.0)\n" +"Project-Id-Version: Odoo Server 8.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-04 14:42+0000\n" -"PO-Revision-Date: 2015-09-04 14:43+0000\n" -"Last-Translator: OCA Transbot \n" -"Language-Team: Spanish (http://www.transifex.com/oca/OCA-social-8-0/language/es/)\n" +"POT-Creation-Date: 2016-05-23 14:21+0000\n" +"PO-Revision-Date: 2016-05-23 14:21+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" -"Language: es\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: \n" #. module: mass_mailing_custom_unsubscribe -#: code:addons/mass_mailing_custom_unsubscribe/models/mail_mail.py:37 +#: view:website:mass_mailing_custom_unsubscribe.reason_form +msgid "Anything else you want to say before you leave?" +msgstr "¿Algo más que quiera decir antes de irse?" + +#. module: mass_mailing_custom_unsubscribe +#: view:website:mass_mailing_custom_unsubscribe.reason_form +msgid "But before continuing, could you please tell us why do you want to unsubscribe?" +msgstr "Pero antes de continuar, ¿podría decirnos por qué quiere darse de baja?" + +#. module: mass_mailing_custom_unsubscribe +#: help:mail.unsubscription,details_required:0 +#: help:mail.unsubscription.reason,details_required:0 +msgid "Check to ask for more details when this reason is selected." +msgstr "Marcar para pedir más detalles cuando está razón se selecciona." + +#. module: mass_mailing_custom_unsubscribe +#: code:addons/mass_mailing_custom_unsubscribe/models/mail_mail.py:39 #, python-format msgid "Click to unsubscribe" -msgstr "Haz click para darte de baja" +msgstr "Haga click para darse de baja" + +#. module: mass_mailing_custom_unsubscribe +#: view:website:mass_mailing_custom_unsubscribe.failure +#: view:website:mass_mailing_custom_unsubscribe.success +msgid "Contact us" +msgstr "Contáctenos" + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,create_uid:0 +#: field:mail.unsubscription.reason,create_uid:0 +msgid "Created by" +msgstr "Creado por" + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,create_date:0 +#: field:mail.unsubscription.reason,create_date:0 +msgid "Created on" +msgstr "Creado en" + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,date:0 +msgid "Date" +msgstr "Fecha" + +#. module: mass_mailing_custom_unsubscribe +#: help:mail.unsubscription,message_last_post:0 +msgid "Date of the last message posted on the record." +msgstr "Fecha del último mensaje publicado en el registro." + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,details:0 +msgid "Details" +msgstr "Detalles" + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,details_required:0 +#: field:mail.unsubscription.reason,details_required:0 +msgid "Details required" +msgstr "Detalles requeridos" + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,display_name:0 +#: field:mail.unsubscription.reason,display_name:0 +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.mass_mailing.list,not_cross_unsubscriptable:0 +msgid "Don't show this list in the other unsubscriptions" +msgstr "No mostrar esta lista en las otras desuscripciones" + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,email:0 +msgid "Email" +msgstr "Correo electrónico" + +#. module: mass_mailing_custom_unsubscribe +#: model:ir.model,name:mass_mailing_custom_unsubscribe.model_mail_unsubscription +msgid "Email Thread" +msgstr "Hilo de mensajes" + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,message_follower_ids:0 +msgid "Followers" +msgstr "Seguidores" + +#. module: mass_mailing_custom_unsubscribe +#: view:mail.unsubscription:mass_mailing_custom_unsubscribe.mail_unsubscription_view_search +msgid "Group by" +msgstr "Agrupar por" + +#. module: mass_mailing_custom_unsubscribe +#: view:website:mass_mailing_custom_unsubscribe.reason_form +msgid "Hello," +msgstr "Hola," + +#. module: mass_mailing_custom_unsubscribe +#: help:mail.unsubscription,message_summary:0 +msgid "Holds the Chatter summary (number of messages, ...). This summary is directly in html format in order to be inserted in kanban views." +msgstr "Contiene el resumen del chatter (nº de mensajes, ...). Este resumen está directamente en formato html para ser insertado en vistas kanban." + +#. module: mass_mailing_custom_unsubscribe +#: model:mail.unsubscription.reason,name:mass_mailing_custom_unsubscribe.reason_not_requested +msgid "I did not request this" +msgstr "No lo solicité" + +#. module: mass_mailing_custom_unsubscribe +#: model:mail.unsubscription.reason,name:mass_mailing_custom_unsubscribe.reason_too_many +msgid "I get too many emails" +msgstr "Tengo demasiados correos electrónicos" + +#. module: mass_mailing_custom_unsubscribe +#: model:mail.unsubscription.reason,name:mass_mailing_custom_unsubscribe.reason_not_interested +msgid "I'm not interested" +msgstr "No estoy interesado" + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,id:0 +#: field:mail.unsubscription.reason,id:0 +msgid "ID" +msgstr "ID" + +#. module: mass_mailing_custom_unsubscribe +#: help:mail.unsubscription,message_unread:0 +msgid "If checked new messages require your attention." +msgstr "Si está marcado, hay nuevos mensajes que requieren de su atención." + +#. module: mass_mailing_custom_unsubscribe +#: help:mail.unsubscription,success:0 +msgid "If this is unchecked, it indicates some failure happened in the unsubscription process." +msgstr "Si no está marcado, indica que algún fallo ha ocurrido en el proceso de desuscripción." + +#. module: mass_mailing_custom_unsubscribe +#: help:mail.mass_mailing.list,not_cross_unsubscriptable:0 +msgid "If you mark this field, this list won't be shown when unsubscribing from other mailing list, in the section: 'Is there any other mailing list you want to leave?'" +msgstr "Si marca esta casilla, esta lista no será mostrada cuando se dé de baja de otra lista de correo, en la sección: '¿Hay alguna otra lista de correo que quiera dejar?'" + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,message_is_follower:0 +msgid "Is a Follower" +msgstr "Es un seguidor" + +#. module: mass_mailing_custom_unsubscribe +#: view:website:mass_mailing_custom_unsubscribe.reason_form +msgid "Is there any other mailing list you want to leave?" +msgstr "¿Hay alguna otra lista de correo que quiera dejar?" + +#. module: mass_mailing_custom_unsubscribe +#: view:website:mass_mailing_custom_unsubscribe.success +msgid "Is there anything else you want to tell us?" +msgstr "¿Hay algo más que quiera decirnos?" + +#. module: mass_mailing_custom_unsubscribe +#: view:website:mass_mailing_custom_unsubscribe.success +msgid "It's sad to see you go, but if you love\n" +" something, let it go." +msgstr "Es triste verlo partir, pero si quiere a algo, debe dejarlo marchar." + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,message_last_post:0 +msgid "Last Message Date" +msgstr "Fecha del último mensaje" + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,__last_update:0 +#: field:mail.unsubscription.reason,__last_update:0 +msgid "Last Modified on" +msgstr "Última modificación en" + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,write_uid:0 +#: field:mail.unsubscription.reason,write_uid:0 +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,write_date:0 +#: field:mail.unsubscription.reason,write_date:0 +msgid "Last Updated on" +msgstr "Última actualización en" + +#. module: mass_mailing_custom_unsubscribe +#: model:ir.model,name:mass_mailing_custom_unsubscribe.model_mail_mass_mailing_list +msgid "Mailing List" +msgstr "Lista de correo" + +#. module: mass_mailing_custom_unsubscribe +#: model:ir.model,name:mass_mailing_custom_unsubscribe.model_mail_mass_mailing +msgid "Mass Mailing" +msgstr "Envío masivo" + +#. module: mass_mailing_custom_unsubscribe +#: view:mail.unsubscription:mass_mailing_custom_unsubscribe.mail_unsubscription_view_search +#: field:mail.unsubscription,mass_mailing_id:0 +msgid "Mass mailing" +msgstr "Envío masivo" + +#. module: mass_mailing_custom_unsubscribe +#: help:mail.unsubscription,mass_mailing_id:0 +msgid "Mass mailing from which he was unsubscribed." +msgstr "Envío masivo del que ha sido dado de baja." + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,message_ids:0 +msgid "Messages" +msgstr "Mensajes" + +#. module: mass_mailing_custom_unsubscribe +#: help:mail.unsubscription,message_ids:0 +msgid "Messages and communication history" +msgstr "Mensajes e historial de comunicación" + +#. module: mass_mailing_custom_unsubscribe +#: view:mail.unsubscription:mass_mailing_custom_unsubscribe.mail_unsubscription_view_search +msgid "Month" +msgstr "Mes" + +#. module: mass_mailing_custom_unsubscribe +#: help:mail.unsubscription,details:0 +msgid "More details on why the unsubscription was made." +msgstr "Más detalles de por qué se dio de baja." + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription.reason,name:0 +msgid "Name" +msgstr "Nombre" + +#. module: mass_mailing_custom_unsubscribe +#: model:mail.unsubscription.reason,name:mass_mailing_custom_unsubscribe.reason_other +msgid "Other reason" +msgstr "Otra razón" #. module: mass_mailing_custom_unsubscribe #: model:ir.model,name:mass_mailing_custom_unsubscribe.model_mail_mail msgid "Outgoing Mails" msgstr "Correos salientes" + +#. module: mass_mailing_custom_unsubscribe +#: help:mail.unsubscription.reason,sequence:0 +msgid "Position of the reason in the list." +msgstr "Posición de la razón en la lista." + +#. module: mass_mailing_custom_unsubscribe +#: view:mail.unsubscription:mass_mailing_custom_unsubscribe.mail_unsubscription_view_search +#: field:mail.unsubscription,reason_id:0 +msgid "Reason" +msgstr "Razón" + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription.reason,sequence:0 +msgid "Sequence" +msgstr "Secuencia" + +#. module: mass_mailing_custom_unsubscribe +#: view:mail.unsubscription:mass_mailing_custom_unsubscribe.mail_unsubscription_view_search +#: field:mail.unsubscription,success:0 +msgid "Success" +msgstr "Éxito" + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,message_summary:0 +msgid "Summary" +msgstr "Resumen" + +#. module: mass_mailing_custom_unsubscribe +#: view:website:mass_mailing_custom_unsubscribe.reason_form +msgid "Thank you!" +msgstr "¡Gracias!" + +#. module: mass_mailing_custom_unsubscribe +#: view:website:mass_mailing_custom_unsubscribe.failure +msgid "Thanks for your patience." +msgstr "Gracias por su paciencia." + +#. module: mass_mailing_custom_unsubscribe +#: view:website:mass_mailing_custom_unsubscribe.failure +msgid "There was an error processing your unsubscription\n" +" request." +msgstr "There was an error processing your unsubscription\n" +" request." + +#. module: mass_mailing_custom_unsubscribe +#: code:addons/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py:59 +#, python-format +msgid "This reason requires an explanation." +msgstr "Esta razón requiere rellenar la explicación." + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,message_unread:0 +msgid "Unread Messages" +msgstr "Mensajes sin leer" + +#. module: mass_mailing_custom_unsubscribe +#: view:website:mass_mailing_custom_unsubscribe.reason_form +msgid "Unsubscribe now" +msgstr "Darse de baja ahora" + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,unsubscriber_id:0 +msgid "Unsubscriber" +msgstr "Desuscriptor" + +#. module: mass_mailing_custom_unsubscribe +#: model:ir.actions.act_window,name:mass_mailing_custom_unsubscribe.mail_unsubscription_reason_action +#: model:ir.ui.menu,name:mass_mailing_custom_unsubscribe.mail_unsubscription_reason_menu +msgid "Unsubscription Reasons" +msgstr "Razones de desuscripción" + +#. module: mass_mailing_custom_unsubscribe +#: model:ir.actions.act_window,name:mass_mailing_custom_unsubscribe.mail_unsubscription_action +#: model:ir.ui.menu,name:mass_mailing_custom_unsubscribe.mail_unsubscription_menu +msgid "Unsubscriptions" +msgstr "Desuscripciones" + +#. module: mass_mailing_custom_unsubscribe +#: view:website:mass_mailing_custom_unsubscribe.failure +msgid "We apologize for the inconvenience. You can contact us\n" +" and we will handle your unsubscription manually." +msgstr "Lamentamos los inconvenientes. Puede contactarnos\n" +" y realizaremos la desuscripción manualmente." + +#. module: mass_mailing_custom_unsubscribe +#: field:mail.unsubscription,website_message_ids:0 +msgid "Website Messages" +msgstr "Mensajes del sitio web" + +#. module: mass_mailing_custom_unsubscribe +#: help:mail.unsubscription,website_message_ids:0 +msgid "Website communication history" +msgstr "Historial de comunicación del sitio web" + +#. module: mass_mailing_custom_unsubscribe +#: help:mail.unsubscription,unsubscriber_id:0 +msgid "Who was unsubscribed." +msgstr "Quién se dio de baja." + +#. module: mass_mailing_custom_unsubscribe +#: help:mail.unsubscription,reason_id:0 +msgid "Why the unsubscription was made." +msgstr "Por qué se dio de baja." + +#. module: mass_mailing_custom_unsubscribe +#: view:mail.unsubscription:mass_mailing_custom_unsubscribe.mail_unsubscription_view_search +msgid "Year" +msgstr "Año" + +#. module: mass_mailing_custom_unsubscribe +#: view:website:mass_mailing_custom_unsubscribe.reason_form +msgid "You are trying to unsubscribe from all massive mailings" +msgstr "Está intentando darse de baja de todos los envíos masivos" + +#. module: mass_mailing_custom_unsubscribe +#: view:website:mass_mailing_custom_unsubscribe.success +msgid "You were successfully unsubscribed from our\n" +" mailing list." +msgstr "Fue dado de baja correctamente de nuestra\n" +" lista de correo." + +#. module: mass_mailing_custom_unsubscribe +#: view:website:mass_mailing_custom_unsubscribe.reason_form +msgid "sent to followers of" +msgstr "enviados a los seguidores de" + diff --git a/mass_mailing_custom_unsubscribe/images/failure.png b/mass_mailing_custom_unsubscribe/images/failure.png new file mode 100644 index 0000000000000000000000000000000000000000..ae751bfc42e9e076728bc0426b3a2224b252bff7 GIT binary patch literal 42059 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 u&R- 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