diff --git a/addons/mail/models/mail_blacklist.py b/addons/mail/models/mail_blacklist.py index 4fdd6c6764f..7adf2476721 100644 --- a/addons/mail/models/mail_blacklist.py +++ b/addons/mail/models/mail_blacklist.py @@ -69,29 +69,36 @@ class MailBlackList(models.Model): new_args = args return super(MailBlackList, self)._search(new_args, offset=offset, limit=limit, order=order, count=count, access_rights_uid=access_rights_uid) - def _add(self, email): + def _add(self, email, message=None): normalized = tools.email_normalize(email) record = self.env["mail.blacklist"].with_context(active_test=False).search([('email', '=', normalized)]) if len(record) > 0: + if message: + record._track_set_log_message(message) record.action_unarchive() else: record = self.create({'email': email}) + if message: + record.with_context(mail_create_nosubscribe=True).message_post( + body=message, + subtype_id=self.env.ref('mail.mt_note').id, + ) return record - def action_remove_with_reason(self, email, reason=None): - record = self._remove(email) - if reason: - record.message_post(body=_("Unblacklisting Reason: %s", reason)) - - return record - - def _remove(self, email): + def _remove(self, email, message=None): normalized = tools.email_normalize(email) record = self.env["mail.blacklist"].with_context(active_test=False).search([('email', '=', normalized)]) if len(record) > 0: + if message: + record._track_set_log_message(message) record.action_archive() else: record = record.create({'email': email, 'active': False}) + if message: + record.with_context(mail_create_nosubscribe=True).message_post( + body=message, + subtype_id=self.env.ref('mail.mt_note').id, + ) return record def mail_action_blacklist_remove(self): diff --git a/addons/mail/models/res_users.py b/addons/mail/models/res_users.py index bcdba48601c..13ae3c1c5ed 100644 --- a/addons/mail/models/res_users.py +++ b/addons/mail/models/res_users.py @@ -189,11 +189,11 @@ class Users(models.Model): super(Users, self)._deactivate_portal_user(**post) for user in users_to_blacklist: - blacklist = self.env['mail.blacklist']._add(user.email) - blacklist._message_log( - body=_('Blocked by deletion of portal account %(portal_user_name)s by %(user_name)s (#%(user_id)s)', - user_name=current_user.name, user_id=current_user.id, - portal_user_name=user.name), + self.env['mail.blacklist']._add( + user.email, + message=_('Blocked by deletion of portal account %(portal_user_name)s by %(user_name)s (#%(user_id)s)', + user_name=current_user.name, user_id=current_user.id, + portal_user_name=user.name) ) # ------------------------------------------------------------ diff --git a/addons/mail/wizard/mail_blacklist_remove.py b/addons/mail/wizard/mail_blacklist_remove.py index 2fc6374cc3d..f4347d54b9d 100644 --- a/addons/mail/wizard/mail_blacklist_remove.py +++ b/addons/mail/wizard/mail_blacklist_remove.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- -from odoo import fields, models +from markupsafe import Markup +from odoo import fields, models, _ class MailBlacklistRemove(models.TransientModel): @@ -11,4 +12,11 @@ class MailBlacklistRemove(models.TransientModel): reason = fields.Char(name="Reason") def action_unblacklist_apply(self): - return self.env['mail.blacklist'].action_remove_with_reason(self.email, self.reason) + if self.reason: + message = Markup('

%s

') % _("Unblock Reason: %(reason)s", reason=self.reason) + else: + message = None + return self.env['mail.blacklist']._remove( + self.email, + message=message, + ) diff --git a/addons/mass_mailing/__manifest__.py b/addons/mass_mailing/__manifest__.py index 7b0fc32dbf9..36abfb83f75 100644 --- a/addons/mass_mailing/__manifest__.py +++ b/addons/mass_mailing/__manifest__.py @@ -4,7 +4,7 @@ { 'name': 'Email Marketing', 'summary': 'Design, send and track emails', - 'version': '2.5', + 'version': '2.6', 'sequence': 60, 'website': 'https://www.odoo.com/app/email-marketing', 'category': 'Marketing/Email Marketing', @@ -85,6 +85,10 @@ ], 'application': True, 'assets': { + 'mass_mailing.mailing_assets': [ + 'mass_mailing/static/src/scss/mailing_portal.scss', + 'mass_mailing/static/src/js/mailing_portal.js', + ], 'web.assets_backend': [ 'mass_mailing/static/src/scss/mailing_filter_widget.scss', 'mass_mailing/static/src/scss/mass_mailing.scss', @@ -98,7 +102,6 @@ 'mass_mailing/static/src/js/mass_mailing_mobile_preview.js', 'mass_mailing/static/src/js/mass_mailing_html_field.js', 'mass_mailing/static/src/js/mailing_mailing_view_form_full_width.js', - 'mass_mailing/static/src/js/mailing_portal.js', 'mass_mailing/static/src/xml/mailing_filter_widget.xml', 'mass_mailing/static/src/xml/mass_mailing.xml', 'mass_mailing/static/src/views/*.xml', diff --git a/addons/mass_mailing/controllers/__init__.py b/addons/mass_mailing/controllers/__init__.py index 5d4b25db9c0..c3fbd52feea 100644 --- a/addons/mass_mailing/controllers/__init__.py +++ b/addons/mass_mailing/controllers/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import legacy from . import main diff --git a/addons/mass_mailing/controllers/legacy.py b/addons/mass_mailing/controllers/legacy.py new file mode 100644 index 00000000000..03392fb84ce --- /dev/null +++ b/addons/mass_mailing/controllers/legacy.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import http +from odoo.http import request +from odoo.addons.base.models.ir_qweb import keep_query + +class MailingLegacy(http.Controller): + """ Retro compatibility layer for legacy endpoint""" + + @http.route(['/mail/mailing//unsubscribe'], type='http', website=True, auth='public') + def mailing_unsubscribe(self, mailing_id, **post): + return request.redirect( + f'/mailing/{mailing_id}/unsubscribe?{keep_query("*")}' + ) diff --git a/addons/mass_mailing/controllers/main.py b/addons/mass_mailing/controllers/main.py index 2b3cfd32fc7..b567759b5d8 100644 --- a/addons/mass_mailing/controllers/main.py +++ b/addons/mass_mailing/controllers/main.py @@ -7,32 +7,29 @@ import werkzeug from odoo import _, exceptions, http, tools from odoo.http import request from odoo.tools import consteq + +from markupsafe import Markup, escape from werkzeug.exceptions import BadRequest, NotFound class MassMailController(http.Controller): - def _valid_unsubscribe_token(self, mailing_id, res_id, email, token): + def _check_mailing_email_token(self, mailing_id, res_id, email, token): if not (mailing_id and res_id and email and token): return False mailing = request.env['mailing.mailing'].sudo().browse(mailing_id) - return consteq(mailing._unsubscribe_token(res_id, email), token) - - def _log_blacklist_action(self, blacklist_entry, mailing_id, description): - mailing = request.env['mailing.mailing'].sudo().browse(mailing_id) - model_display = mailing.mailing_model_id.display_name - blacklist_entry._message_log(body=description + " ({})".format(model_display)) + return consteq(mailing._generate_mailing_recipient_token(res_id, email), token) # ------------------------------------------------------------ # SUBSCRIPTION MANAGEMENT # ------------------------------------------------------------ - @http.route(['/mail/mailing//unsubscribe'], type='http', website=True, auth='public') - def mailing(self, mailing_id, email=None, res_id=None, token="", **post): + @http.route(['/mailing//unsubscribe'], type='http', website=True, auth='public') + def mailing_unsubscribe(self, mailing_id, email=None, res_id=None, token="", **post): mailing = request.env['mailing.mailing'].sudo().browse(mailing_id) if mailing.exists(): res_id = res_id and int(res_id) - if not self._valid_unsubscribe_token(mailing_id, res_id, email, str(token)): + if not self._check_mailing_email_token(mailing_id, res_id, email, str(token)): raise exceptions.AccessDenied() if mailing.mailing_model_real == 'mailing.contact': @@ -51,7 +48,7 @@ class MassMailController(http.Controller): unique_list_ids = set([list.list_id.id for list in subscription_list_ids]) list_ids = request.env['mailing.list'].sudo().browse(unique_list_ids) unsubscribed_list = ', '.join(str(list.name) for list in mailing.contact_list_ids if list.is_public) - return request.render('mass_mailing.page_unsubscribe', { + return request.render('mass_mailing.page_mailing_unsubscribe', { 'contacts': contacts, 'list_ids': list_ids, 'opt_out_list_ids': opt_out_list_ids, @@ -66,11 +63,16 @@ class MassMailController(http.Controller): ('contact_id.email_normalized', '=', email), ('opt_out', '=', False) ]).mapped('list_id') - blacklist_rec = request.env['mail.blacklist'].sudo()._add(email) - self._log_blacklist_action( - blacklist_rec, mailing_id, - _("""Requested blacklisting via unsubscribe link.""")) - return request.render('mass_mailing.page_unsubscribed', { + + message = Markup('

%s

') % Markup( + _( + 'Blocklist request from unsubscribe link of mailing %(mailing_link)s (document %(record_link)s)', + **self._format_bl_request(mailing, res_id) + ) + ) + _blacklist_rec = request.env['mail.blacklist'].sudo()._add(email, message=message) + + return request.render('mass_mailing.page_mailing_unsubscribe_done', { 'email': email, 'mailing_id': mailing_id, 'res_id': res_id, @@ -80,11 +82,11 @@ class MassMailController(http.Controller): }) return request.redirect('/web') - @http.route('/mail/mailing/unsubscribe', type='json', auth='public') - def unsubscribe(self, mailing_id, opt_in_ids, opt_out_ids, email, res_id, token): + @http.route('/mailing/list/update', type='json', auth='public') + def mailing_update_list_subscription(self, mailing_id, opt_in_ids, opt_out_ids, email, res_id, token): mailing = request.env['mailing.mailing'].sudo().browse(mailing_id) if mailing.exists(): - if not self._valid_unsubscribe_token(mailing_id, res_id, email, token): + if not self._check_mailing_email_token(mailing_id, res_id, email, token): return 'unauthorized' mailing.update_opt_out(email, opt_in_ids, False) mailing.update_opt_out(email, opt_out_ids, True) @@ -92,10 +94,10 @@ class MassMailController(http.Controller): return 'error' @http.route('/mailing/feedback', type='json', auth='public') - def send_feedback(self, mailing_id, res_id, email, feedback, token): + def mailing_send_feedback(self, mailing_id, res_id, email, feedback, token): mailing = request.env['mailing.mailing'].sudo().browse(mailing_id) if mailing.exists() and email: - if not self._valid_unsubscribe_token(mailing_id, res_id, email, token): + if not self._check_mailing_email_token(mailing_id, res_id, email, token): return 'unauthorized' model = request.env[mailing.mailing_model_real] records = model.sudo().search([('email_normalized', '=', tools.email_normalize(email))]) @@ -105,7 +107,7 @@ class MassMailController(http.Controller): return 'error' @http.route(['/unsubscribe_from_list'], type='http', website=True, multilang=False, auth='public', sitemap=False) - def unsubscribe_placeholder_link(self, **post): + def mailing_unsubscribe_placeholder_link(self, **post): """Dummy route so placeholder is not prefixed by language, MUST have multilang=False""" raise werkzeug.exceptions.NotFound() @@ -116,7 +118,8 @@ class MassMailController(http.Controller): @http.route('/mail/track///blank.gif', type='http', auth='public') def track_mail_open(self, mail_id, token, **post): """ Email tracking. """ - if not consteq(token, tools.hmac(request.env(su=True), 'mass_mailing-mail_mail-open', mail_id)): + mail = request.env['mail.mail'].sudo().browse(mail_id).exists() + if not mail or not consteq(token, mail._generate_mail_recipient_token()): raise BadRequest() request.env['mailing.trace'].sudo().set_opened(domain=[('mail_mail_id_int', 'in', [mail_id])]) @@ -145,11 +148,11 @@ class MassMailController(http.Controller): # ------------------------------------------------------------ @http.route('/mailing/report/unsubscribe', type='http', website=True, auth='public') - def turn_off_mailing_reports(self, token, user_id): + def mailing_report_deactivate(self, token, user_id): if not token or not user_id: raise werkzeug.exceptions.NotFound() user_id = int(user_id) - correct_token = consteq(token, request.env['mailing.mailing']._get_unsubscribe_token(user_id)) + correct_token = consteq(token, request.env['mailing.mailing']._generate_mailing_report_token(user_id)) user = request.env['res.users'].sudo().browse(user_id) if correct_token and user.has_group('mass_mailing.group_mass_mailing_user'): request.env['ir.config_parameter'].sudo().set_param('mass_mailing.mass_mailing_reports', False) @@ -160,11 +163,11 @@ class MassMailController(http.Controller): raise werkzeug.exceptions.NotFound() @http.route(['/mailing//view'], type='http', website=True, auth='public') - def view(self, mailing_id, email=None, res_id=None, token=""): + def mailing_view_in_browser(self, mailing_id, email=None, res_id=None, token=""): mailing = request.env['mailing.mailing'].sudo().browse(mailing_id) if mailing.exists(): res_id = int(res_id) if res_id else False - if not self._valid_unsubscribe_token(mailing_id, res_id, email, str(token)) and not request.env.user.has_group('mass_mailing.group_mass_mailing_user'): + if not self._check_mailing_email_token(mailing_id, res_id, email, str(token)) and not request.env.user.has_group('mass_mailing.group_mass_mailing_user'): raise exceptions.AccessDenied() # do not force lang, will simply use user context @@ -178,7 +181,7 @@ class MassMailController(http.Controller): html_markupsafe = html_markupsafe.replace('/unsubscribe_from_list', mailing._get_unsubscribe_url(email, res_id)) - return request.render('mass_mailing.view', { + return request.render('mass_mailing.mailing_view', { 'body': html_markupsafe, }) @@ -189,8 +192,8 @@ class MassMailController(http.Controller): # ------------------------------------------------------------ @http.route('/mailing/blacklist/check', type='json', auth='public') - def blacklist_check(self, mailing_id, res_id, email, token): - if not self._valid_unsubscribe_token(mailing_id, res_id, email, token): + def mail_blacklist_check(self, mailing_id, res_id, email, token): + if not self._check_mailing_email_token(mailing_id, res_id, email, token): return 'unauthorized' if email: record = request.env['mail.blacklist'].sudo().with_context(active_test=False).search([('email', '=', tools.email_normalize(email))]) @@ -200,29 +203,52 @@ class MassMailController(http.Controller): return 'error' @http.route('/mailing/blacklist/add', type='json', auth='public') - def blacklist_add(self, mailing_id, res_id, email, token): - if not self._valid_unsubscribe_token(mailing_id, res_id, email, token): + def mail_blacklist_add(self, mailing_id, res_id, email, token): + if not self._check_mailing_email_token(mailing_id, res_id, email, token): return 'unauthorized' if email: - blacklist_rec = request.env['mail.blacklist'].sudo()._add(email) - self._log_blacklist_action( - blacklist_rec, mailing_id, - _("""Requested blacklisting via unsubscription page.""")) + if mailing_id and res_id: + mailing_sudo = request.env['mailing.mailing'].sudo().browse(mailing_id) + message = Markup('

%s

') % Markup( + _( + 'Blocklist request from portal of mailing %(mailing_link)s (document %(record_link)s)', + **self._format_bl_request(mailing_sudo, res_id) + ) + ) + else: + message = Markup('

%s

') % _('Blocklist request from portal') + + _blacklist_rec = request.env['mail.blacklist'].sudo()._add(email, message=message) return True return 'error' @http.route('/mailing/blacklist/remove', type='json', auth='public') - def blacklist_remove(self, mailing_id, res_id, email, token): - if not self._valid_unsubscribe_token(mailing_id, res_id, email, token): + def mail_blacklist_remove(self, mailing_id, res_id, email, token): + if not self._check_mailing_email_token(mailing_id, res_id, email, token): return 'unauthorized' if email: - blacklist_rec = request.env['mail.blacklist'].sudo()._remove(email) - self._log_blacklist_action( - blacklist_rec, mailing_id, - _("""Requested de-blacklisting via unsubscription page.""")) + if mailing_id and res_id: + mailing_sudo = request.env['mailing.mailing'].sudo().browse(mailing_id) + message = Markup('

%s

') % Markup( + _( + 'Blocklist removal request from portal of mailing %(mailing_link)s (document %(record_link)s)', + **self._format_bl_request(mailing_sudo, res_id) + ) + ) + else: + message = Markup('

%s

') % _('Blocklist removal request from portal') + + _blacklist_rec = request.env['mail.blacklist'].sudo()._remove(email, message=message) return True return 'error' + def _format_bl_request(self, mailing, document_id): + mailing_model_name = request.env['ir.model']._get(mailing.mailing_model_real).display_name + return { + 'mailing_link': Markup(f'{escape(mailing.subject)}'), + 'record_link': Markup(f'{escape(mailing_model_name)}'), + } + # ------------------------------------------------------------ # MISCELLANEOUS # ------------------------------------------------------------ diff --git a/addons/mass_mailing/data/ir_config_parameter_data.xml b/addons/mass_mailing/data/ir_config_parameter_data.xml index 531c0854ad9..bcce9a8c9df 100644 --- a/addons/mass_mailing/data/ir_config_parameter_data.xml +++ b/addons/mass_mailing/data/ir_config_parameter_data.xml @@ -5,5 +5,9 @@ + + diff --git a/addons/mass_mailing/models/mail_mail.py b/addons/mass_mailing/models/mail_mail.py index 71b4c2b6a8d..f0a64a3e961 100644 --- a/addons/mass_mailing/models/mail_mail.py +++ b/addons/mass_mailing/models/mail_mail.py @@ -25,8 +25,15 @@ class MailMail(models.Model): return mails def _get_tracking_url(self): - token = tools.hmac(self.env(su=True), 'mass_mailing-mail_mail-open', self.id) - return werkzeug.urls.url_join(self.get_base_url(), 'mail/track/%s/%s/blank.gif' % (self.id, token)) + token = self._generate_mail_recipient_token() + return werkzeug.urls.url_join( + self.get_base_url(), + f'mail/track/{self.id}/{token}/blank.gif' + ) + + def _generate_mail_recipient_token(self): + self.ensure_one() + return tools.hmac(self.env(su=True), 'mass_mailing-mail_mail-open', self.id) def _send_prepare_body(self): """ Override to add the tracking URL to the body and to add diff --git a/addons/mass_mailing/models/mail_thread.py b/addons/mass_mailing/models/mail_thread.py index 08fb3b67e86..fff1aea4641 100644 --- a/addons/mass_mailing/models/mail_thread.py +++ b/addons/mass_mailing/models/mail_thread.py @@ -2,8 +2,9 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import datetime +from markupsafe import Markup -from odoo import api, models, fields, tools +from odoo import api, models, fields, tools, _ BLACKLIST_MAX_BOUNCED_LIMIT = 5 @@ -59,9 +60,10 @@ class MailThread(models.AbstractModel): stats = self.env['mailing.trace'].search(['&', '&', ('trace_status', '=', 'bounce'), ('write_date', '>', three_months_ago), ('email', '=ilike', bounced_email)]).mapped('write_date') if len(stats) >= BLACKLIST_MAX_BOUNCED_LIMIT and (not bounced_partner or any(p.message_bounce >= BLACKLIST_MAX_BOUNCED_LIMIT for p in bounced_partner)): if max(stats) > min(stats) + datetime.timedelta(weeks=1): - blacklist_rec = self.env['mail.blacklist'].sudo()._add(bounced_email) - blacklist_rec._message_log( - body='This email has been automatically blacklisted because of too much bounced.') + self.env['mail.blacklist'].sudo()._add( + bounced_email, + message=Markup('

%s

') % _('This email has been automatically added in blocklist because of too much bounced.') + ) @api.model def message_new(self, msg_dict, custom_values=None): diff --git a/addons/mass_mailing/models/mailing.py b/addons/mass_mailing/models/mailing.py index d95f295c1b3..393743155f4 100644 --- a/addons/mass_mailing/models/mailing.py +++ b/addons/mass_mailing/models/mailing.py @@ -53,7 +53,7 @@ class MassMailing(models.Model): }) if 'contact_list_ids' in fields_list and not vals.get('contact_list_ids') and vals.get('mailing_model_id'): - if vals.get('mailing_model_id') == self.env['ir.model']._get('mailing.list').id: + if vals.get('mailing_model_id') == self.env['ir.model']._get_id('mailing.list'): mailing_list = self.env['mailing.list'].search([], limit=2) if len(mailing_list) == 1: vals['contact_list_ids'] = [(6, 0, [mailing_list.id])] @@ -154,6 +154,9 @@ class MassMailing(models.Model): mailing_model_name = fields.Char( string='Recipients Model Name', related='mailing_model_id.model', readonly=True, related_sudo=True) + mailing_on_mailing_list = fields.Boolean( + string='Based on Mailing Lists', + compute='_compute_mailing_on_mailing_list') mailing_domain = fields.Char( string='Domain', compute='_compute_mailing_domain', readonly=False, store=True) @@ -383,6 +386,12 @@ class MassMailing(models.Model): for mailing in self: mailing.mailing_model_real = 'mailing.contact' if mailing.mailing_model_id.model == 'mailing.list' else mailing.mailing_model_id.model + @api.depends('mailing_model_id') + def _compute_mailing_on_mailing_list(self): + mailing_list_model_id = self.env['ir.model']._get('mailing.list') + self.mailing_on_mailing_list = False + self.filtered(lambda m: m.mailing_model_id == mailing_list_model_id).mailing_on_mailing_list = True + @api.depends('mailing_model_id', 'contact_list_ids', 'mailing_type', 'mailing_filter_id') def _compute_mailing_domain(self): for mailing in self: @@ -1008,12 +1017,12 @@ class MassMailing(models.Model): def _get_unsubscribe_url(self, email_to, res_id): url = werkzeug.urls.url_join( - self.get_base_url(), 'mail/mailing/%(mailing_id)s/unsubscribe?%(params)s' % { + self.get_base_url(), 'mailing/%(mailing_id)s/unsubscribe?%(params)s' % { 'mailing_id': self.id, 'params': werkzeug.urls.url_encode({ 'res_id': res_id, 'email': email_to, - 'token': self._unsubscribe_token(res_id, email_to), + 'token': self._generate_mailing_recipient_token(res_id, email_to), }), } ) @@ -1026,7 +1035,7 @@ class MassMailing(models.Model): 'params': werkzeug.urls.url_encode({ 'res_id': res_id, 'email': email_to, - 'token': self._unsubscribe_token(res_id, email_to), + 'token': self._generate_mailing_recipient_token(res_id, email_to), }), } ) @@ -1158,7 +1167,7 @@ class MassMailing(models.Model): ** mailing._prepare_statistics_email_values(), } if mail_user.has_group('mass_mailing.group_mass_mailing_user'): - rendering_data['mailing_report_token'] = self._get_unsubscribe_token(mail_user.id) + rendering_data['mailing_report_token'] = self._generate_mailing_report_token(mail_user.id) rendering_data['user_id'] = mail_user.id rendered_body = self.env['ir.qweb']._render( @@ -1258,8 +1267,8 @@ class MassMailing(models.Model): def _get_pretty_mailing_type(self): return _('Emails') - def _get_unsubscribe_token(self, user_id): - """Generate a secure hash for this user. It allows to opt out from + def _generate_mailing_report_token(self, user_id): + """Generate a secure token for this user. It allows to opt out from mailing reports while keeping some security in that process. """ return tools.hmac(self.env(su=True), 'mailing-report-deactivated', user_id) @@ -1285,21 +1294,18 @@ class MassMailing(models.Model): mailing_domain = [('id', 'in', [])] return mailing_domain - def _unsubscribe_token(self, res_id, email): - """Generate a secure hash for this mailing list and parameters. + def _generate_mailing_recipient_token(self, document_id, email): + """Generate a secure token for a given mailing and recipient (based on + their email). This allows notably to unsubscribe from the mailing or + to blacklist their email entirely without need of a user account. - This is appended to the unsubscription URL and then checked at - unsubscription time to ensure no malicious unsubscriptions are - performed. - - :param int res_id: - ID of the resource that will be unsubscribed. - - :param str email: - Email of the resource that will be unsubscribed. + :param int document_id: ID of the business document on which mailing + is performed; + :param str email: recipient email, used to unsubscribe / blacklist; """ + self.ensure_one() secret = self.env["ir.config_parameter"].sudo().get_param("database.secret") - token = (self.env.cr.dbname, self.id, int(res_id), tools.ustr(email)) + token = (self.env.cr.dbname, self.id, int(document_id), tools.ustr(email)) return hmac.new(secret.encode('utf-8'), repr(token).encode('utf-8'), hashlib.sha512).hexdigest() def _convert_inline_images_to_urls(self, body_html): diff --git a/addons/mass_mailing/static/src/js/mailing_portal.js b/addons/mass_mailing/static/src/js/mailing_portal.js index 867c3a6b569..a0c81af4498 100644 --- a/addons/mass_mailing/static/src/js/mailing_portal.js +++ b/addons/mass_mailing/static/src/js/mailing_portal.js @@ -71,7 +71,7 @@ odoo.define('mass_mailing.unsubscribe', function (require) { unchecked_ids[i] = parseInt($(this).val()); }); - ajax.jsonRpc('/mail/mailing/unsubscribe', 'call', {'opt_in_ids': checked_ids, 'opt_out_ids': unchecked_ids, 'email': email, 'mailing_id': mailing_id, 'res_id': res_id, 'token': token}) + ajax.jsonRpc('/mailing/list/update', 'call', {'opt_in_ids': checked_ids, 'opt_out_ids': unchecked_ids, 'email': email, 'mailing_id': mailing_id, 'res_id': res_id, 'token': token}) .then(function (result) { if (result == 'unauthorized'){ $('#subscription_info').text(_t('You are not authorized to do this!')); diff --git a/addons/mass_mailing/static/src/scss/mailing_portal.scss b/addons/mass_mailing/static/src/scss/mailing_portal.scss new file mode 100644 index 00000000000..85ea4a0f20f --- /dev/null +++ b/addons/mass_mailing/static/src/scss/mailing_portal.scss @@ -0,0 +1,16 @@ +.o_mailing_portal_body { + // Taken from webclient.scss to copy backend styling + // Uppercase primary and secondary except when secondary is used as a dropdown + // toggler. + .btn-primary, + .btn-secondary:not(.dropdown-toggle):not(.dropdown-item), + .btn-secondary.o_arrow_button:not(.dropdown-item) { + text-transform: uppercase; + } + + .o_mailing_portal_list_unsubscribed { + margin-left: 20px; + color: #005326; + font-size: 90%; + } +} diff --git a/addons/mass_mailing/static/src/scss/mass_mailing.scss b/addons/mass_mailing/static/src/scss/mass_mailing.scss index 31b9597bd60..06956315fef 100644 --- a/addons/mass_mailing/static/src/scss/mass_mailing.scss +++ b/addons/mass_mailing/static/src/scss/mass_mailing.scss @@ -210,15 +210,6 @@ bottom: 55px; } } -.o_white_body { - background-color: white; -} - -.o_mass_mailing_unsubscribed { - margin-left: 20px; - color: #005326; - font-size: 90%; -} .o_mailing_contact_import_list { diff --git a/addons/mass_mailing/views/mailing_mailing_views.xml b/addons/mass_mailing/views/mailing_mailing_views.xml index a1af5512eee..1a43b40e0a7 100644 --- a/addons/mass_mailing/views/mailing_mailing_views.xml +++ b/addons/mass_mailing/views/mailing_mailing_views.xml @@ -194,14 +194,14 @@ -
+
-
+
+ -
+
@@ -483,7 +484,7 @@ - + @@ -536,8 +537,8 @@
/ Delivered to - - Mailing Contact + + Mailing Contact
@@ -567,8 +568,8 @@ - - Mailing Contact + + Mailing Contact diff --git a/addons/mass_mailing/views/mailing_templates_portal_layouts.xml b/addons/mass_mailing/views/mailing_templates_portal_layouts.xml index 600d026fb6f..1cbc80a3de5 100644 --- a/addons/mass_mailing/views/mailing_templates_portal_layouts.xml +++ b/addons/mass_mailing/views/mailing_templates_portal_layouts.xml @@ -2,18 +2,8 @@ diff --git a/addons/mass_mailing/views/mailing_templates_portal_management.xml b/addons/mass_mailing/views/mailing_templates_portal_management.xml index 43b141f3a8b..cd8ca143bdd 100644 --- a/addons/mass_mailing/views/mailing_templates_portal_management.xml +++ b/addons/mass_mailing/views/mailing_templates_portal_management.xml @@ -25,7 +25,7 @@ -