[REF] mass_mailing: improve portal subscription management page
PURPOSE
Globally improve usability and features given by mailing portal about exclusion
list and opt-out management.
SPECIFICATIONS
Purpose of this commit is to cleanup and improve the portal subscription page
that allows to manage subscription to mailing lists and blacklist status.
Main features updated or added
* allow to give a feedback when unsubscribing from mailing not related to
mailing lists. It was previously limited to mailings done on mailing lists.
Now the feedback is allowed in all cases and posts it on the related
document;
* clean display of opt-in and opt-out lists;
* display all public lists, even if not already join. This allows to opt-in
to new lists, which was not possible before;
* switch on a neutral name for non public lists (as they may contain
marketing hints);
* give UI feedback to customer when using buttons: add confirmation of
block list addition / removal, of updated subscriptions, ...
* globally improve wording;
Unsubscribe from a document now uses the same layout as unsubscribe from
mailing lists. Indeed the first one blacklists the email while the second
one opt-outs from mailing lists. But overall form is the same and options
are also the same. After all previous cleaning we can now keep a single
page for everything.
Task-2150462 (Mass Mailing: Improve subscription management)
Part-of: odoo/odoo#86084
This commit is contained in:
@@ -107,6 +107,9 @@
|
||||
'mass_mailing/static/src/js/mailing_portal_subscription_blocklist.js',
|
||||
'mass_mailing/static/src/js/mailing_portal_subscription_feedback.js',
|
||||
'mass_mailing/static/src/js/mailing_portal_subscription_form.js',
|
||||
'mass_mailing/static/src/xml/mailing_portal_subscription_blocklist.xml',
|
||||
'mass_mailing/static/src/xml/mailing_portal_subscription_feedback.xml',
|
||||
'mass_mailing/static/src/xml/mailing_portal_subscription_form.xml',
|
||||
],
|
||||
'web_editor.assets_wysiwyg': [
|
||||
'mass_mailing/static/src/js/mass_mailing_wysiwyg.js',
|
||||
|
||||
@@ -60,37 +60,29 @@ class MassMailController(http.Controller):
|
||||
# Unsubscribe directly + Let the user choose their subscriptions
|
||||
mailing.contact_list_ids._update_subscription_from_email(email, opt_out=True)
|
||||
|
||||
contacts = request.env['mailing.contact'].sudo().search([('email_normalized', '=', tools.email_normalize(email))])
|
||||
subscription_list_ids = contacts.mapped('subscription_list_ids')
|
||||
# In many user are found : if user is opt_out on the list with contact_id 1 but not with contact_id 2,
|
||||
# assume that the user is not opt_out on both
|
||||
# TODO DBE Fixme : Optimise the following to get real opt_out and opt_in
|
||||
opt_out_list_ids = subscription_list_ids.filtered(lambda rel: rel.opt_out).mapped('list_id')
|
||||
opt_in_list_ids = subscription_list_ids.filtered(lambda rel: not rel.opt_out).mapped('list_id')
|
||||
opt_out_list_ids = set([list.id for list in opt_out_list_ids if list not in opt_in_list_ids])
|
||||
# compute name of unsubscribed list: hide non public lists
|
||||
if all(not mlist.is_public for mlist in mailing.contact_list_ids):
|
||||
lists_unsubscribed_name = _('You are no longer part of our mailing list(s).')
|
||||
elif len(mailing.contact_list_ids) == 1:
|
||||
lists_unsubscribed_name = _('You are no longer part of the %(mailing_name)s mailing list.',
|
||||
mailing_name=mailing.contact_list_ids.name)
|
||||
else:
|
||||
lists_unsubscribed_name = _(
|
||||
'You are no longer part of the %(mailing_names)s mailing list.',
|
||||
mailing_names=', '.join(mlist.name for mlist in mailing.contact_list_ids if mlist.is_public)
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
render_values = self._prepare_mailing_subscription_values(mailing, document_id, email, hash_token)
|
||||
return request.render(
|
||||
'mass_mailing.page_mailing_unsubscribe',
|
||||
dict(
|
||||
**render_values,
|
||||
contacts=contacts,
|
||||
list_ids=list_ids,
|
||||
opt_out_list_ids=opt_out_list_ids,
|
||||
unsubscribed_list=unsubscribed_list,
|
||||
self._prepare_mailing_subscription_values(
|
||||
mailing, document_id, email, hash_token
|
||||
),
|
||||
unsubscribed_name=lists_unsubscribed_name,
|
||||
)
|
||||
)
|
||||
|
||||
def _mailing_unsubscribe_from_document(self, mailing, document_id, email, hash_token):
|
||||
opt_in_lists = request.env['mailing.contact.subscription'].sudo().search([
|
||||
('contact_id.email_normalized', '=', email),
|
||||
('opt_out', '=', False)
|
||||
]).mapped('list_id')
|
||||
|
||||
message = Markup(
|
||||
_(
|
||||
'Blocklist request from unsubscribe link of mailing %(mailing_link)s (document %(record_link)s)',
|
||||
@@ -99,20 +91,39 @@ class MassMailController(http.Controller):
|
||||
)
|
||||
_blocklist_rec = request.env['mail.blacklist'].sudo()._add(email, message=message)
|
||||
|
||||
render_values = self._prepare_mailing_subscription_values(mailing, document_id, email, hash_token)
|
||||
return request.render(
|
||||
'mass_mailing.page_mailing_unsubscribe_done',
|
||||
'mass_mailing.page_mailing_unsubscribe',
|
||||
dict(
|
||||
**render_values,
|
||||
list_ids=opt_in_lists,
|
||||
self._prepare_mailing_subscription_values(
|
||||
mailing, document_id, email, hash_token
|
||||
),
|
||||
unsubscribed_name=_('You are no longer part of our services and will not be contacted again.'),
|
||||
)
|
||||
)
|
||||
|
||||
def _prepare_mailing_subscription_values(self, mailing, document_id, email, hash_token):
|
||||
""" Prepare common values used in various subscription management or
|
||||
""" Prepare common values used in various subscription management or
|
||||
blocklist flows done in portal. """
|
||||
mail_blocklist = self._fetch_blocklist_record(email)
|
||||
email_normalized = tools.email_normalize(email)
|
||||
|
||||
# as there may be several contacts / email -> consider any opt-in overrides
|
||||
# opt-out
|
||||
contacts = self._fetch_contacts(email)
|
||||
lists_optin = contacts.subscription_list_ids.filtered(
|
||||
lambda sub: not sub.opt_out
|
||||
).list_id
|
||||
lists_optout = contacts.subscription_list_ids.filtered(
|
||||
lambda sub: sub.opt_out and sub.list_id not in lists_optin
|
||||
).list_id
|
||||
lists_public = request.env['mailing.list'].sudo().search(
|
||||
[('is_public', '=', True),
|
||||
('id', 'not in', (lists_optin + lists_optout).ids)
|
||||
],
|
||||
limit=10,
|
||||
order='create_date DESC, id DESC',
|
||||
)
|
||||
|
||||
return {
|
||||
# customer
|
||||
'document_id': document_id,
|
||||
@@ -121,6 +132,9 @@ class MassMailController(http.Controller):
|
||||
'hash_token': hash_token,
|
||||
'mailing_id': mailing.id,
|
||||
'res_id': document_id,
|
||||
# feedback
|
||||
'feedback_enabled': True,
|
||||
'feedback_readonly': False,
|
||||
# blocklist
|
||||
'blocklist_enabled': bool(
|
||||
request.env['ir.config_parameter'].sudo().get_param(
|
||||
@@ -130,6 +144,12 @@ class MassMailController(http.Controller):
|
||||
),
|
||||
'blocklist_possible': mail_blocklist is not None,
|
||||
'is_blocklisted': mail_blocklist.active if mail_blocklist else False,
|
||||
# mailing lists
|
||||
'contacts': contacts,
|
||||
'lists_contacts': contacts.subscription_list_ids.list_id,
|
||||
'lists_optin': lists_optin,
|
||||
'lists_optout': lists_optout,
|
||||
'lists_public': lists_public,
|
||||
}
|
||||
|
||||
@http.route('/mailing/list/update', type='json', auth='public', csrf=True)
|
||||
@@ -152,7 +172,7 @@ class MassMailController(http.Controller):
|
||||
# opt-in in either already member, either public (to avoid trying to opt-in
|
||||
# in private lists)
|
||||
lists_to_optin = lists_optin.filtered(
|
||||
lambda mlist: mlist.is_public or mlist in contacts.lists_ids
|
||||
lambda mlist: mlist.is_public or mlist in contacts.list_ids
|
||||
)
|
||||
lists_to_optout._update_subscription_from_email(email, opt_out=True)
|
||||
lists_to_optin._update_subscription_from_email(email, opt_out=False)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/** @odoo-module alias=mailing.PortalSubscription **/
|
||||
|
||||
import publicWidget from "@web/legacy/js/public/public_widget";
|
||||
import { markup } from "@odoo/owl";
|
||||
import { _t } from '@web/core/l10n/translation';
|
||||
|
||||
|
||||
publicWidget.registry.MailingPortalSubscription = publicWidget.Widget.extend({
|
||||
@@ -21,15 +19,12 @@ publicWidget.registry.MailingPortalSubscription = publicWidget.Widget.extend({
|
||||
this.customerData = {...document.getElementById('o_mailing_portal_subscription').dataset};
|
||||
this.customerData.documentId = parseInt(this.customerData.documentId || 0);
|
||||
this.customerData.mailingId = parseInt(this.customerData.mailingId || 0);
|
||||
this.customerData.feedbackEnabled = true;
|
||||
this.lastAction = this.customerData.lastAction;
|
||||
|
||||
// nodes for widgets (jquery due to widget implementation)
|
||||
this.$bl_elem = this.$('#o_mailing_subscription_blocklist');
|
||||
this.$feedback_elem = this.$('#o_mailing_subscription_feedback');
|
||||
this.$form_elem = this.$('#o_mailing_subscription_form');
|
||||
// nodes for text / ui update
|
||||
this.subscriptionInfoNode = document.getElementById('o_mailing_subscription_info');
|
||||
this.subscriptionInfoStateNode = document.getElementById('o_mailing_subscription_info_state');
|
||||
|
||||
this._attachBlocklist();
|
||||
this._attachFeedback();
|
||||
@@ -55,6 +50,7 @@ publicWidget.registry.MailingPortalSubscription = publicWidget.Widget.extend({
|
||||
{customerData: this.customerData}
|
||||
);
|
||||
this.feedbackWidget.attachTo(this.$feedback_elem);
|
||||
this.feedbackWidget._setLastAction(this.lastAction);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -70,8 +66,7 @@ publicWidget.registry.MailingPortalSubscription = publicWidget.Widget.extend({
|
||||
|
||||
_onActionDone: function (callKey) {
|
||||
this.lastAction = callKey;
|
||||
this._updateDisplay(callKey);
|
||||
this._updateSubscriptionInfo(callKey);
|
||||
this._updateDisplay();
|
||||
},
|
||||
|
||||
_onBlocklistAdd: function (event) {
|
||||
@@ -87,17 +82,14 @@ publicWidget.registry.MailingPortalSubscription = publicWidget.Widget.extend({
|
||||
const callKey = event.data.callKey;
|
||||
this.customerData.isBlocklisted = false;
|
||||
if (callKey === 'blocklist_remove') {
|
||||
this.customerData.feedbackEnabled = true;
|
||||
this.customerData.feedbackEnabled = false;
|
||||
}
|
||||
this._onActionDone(callKey);
|
||||
},
|
||||
|
||||
_onFeedbackSent: function (event) {
|
||||
const callKey = event.data.callKey;
|
||||
if (callKey === 'feedback_sent') {
|
||||
this.customerData.feedbackEnabled = true;
|
||||
}
|
||||
this._onActionDone(callKey);
|
||||
this.lastAction = callKey;
|
||||
},
|
||||
|
||||
_onSubscriptionUpdated: function (event) {
|
||||
@@ -108,46 +100,19 @@ publicWidget.registry.MailingPortalSubscription = publicWidget.Widget.extend({
|
||||
this._onActionDone(callKey);
|
||||
},
|
||||
|
||||
_updateDisplay: function (callKey) {
|
||||
_updateDisplay: function () {
|
||||
if (! this.customerData.feedbackEnabled && this.$feedback_elem.length) {
|
||||
this.$feedback_elem.hide();
|
||||
this.$feedback_elem.addClass('d-none');
|
||||
} else if (this.$feedback_elem.length) {
|
||||
this.$feedback_elem.show();
|
||||
this.$feedback_elem.removeClass('d-none');
|
||||
}
|
||||
if (this.formWidget) {
|
||||
this.formWidget._setBlocklisted(this.customerData.isBlocklisted);
|
||||
this.formWidget._setReadonly(this.customerData.isBlocklisted);
|
||||
}
|
||||
if (this.feedbackWidget) {
|
||||
this.feedbackWidget._updateDisplay(true);
|
||||
}
|
||||
},
|
||||
|
||||
_updateSubscriptionInfo: function (callKey) {
|
||||
if (callKey === 'blocklist_add') {
|
||||
this.subscriptionInfoStateNode.innerHTML = markup(
|
||||
_t('You have been successfully <strong>added to our blocklist</strong>. You will not be contacted anymore by our services.')
|
||||
);
|
||||
this.subscriptionInfoNode.setAttribute('class', 'alert alert-success');
|
||||
} else if (callKey === 'blocklist_remove') {
|
||||
this.subscriptionInfoStateNode.innerHTML = markup(
|
||||
_t('You have been successfully <strong>removed from our blocklist</strong>. You are now able to be contacted by our services.')
|
||||
);
|
||||
this.subscriptionInfoNode.setAttribute('class', 'alert alert-success');
|
||||
} else if (callKey == 'feedback_sent') {
|
||||
this.subscriptionInfoStateNode.innerHTML = _t('Thanks for your feedback.');
|
||||
} else if (callKey === 'subscription_updated') {
|
||||
this.subscriptionInfoStateNode.innerHTML = markup(
|
||||
_t('You have successfully <strong>updated your memberships.</strong>')
|
||||
);
|
||||
this.subscriptionInfoNode.setAttribute('class', 'alert alert-success');
|
||||
} else if (callKey === 'unauthorized') {
|
||||
this.subscriptionInfoStateNode.innerHTML = _t('You are not authorized to do this.');
|
||||
this.subscriptionInfoNode.setAttribute('class', 'alert alert-error');
|
||||
} else if (callKey === 'error') {
|
||||
this.subscriptionInfoStateNode.innerHTML = _t('An error occurred. Please try again later or contact us.');
|
||||
this.subscriptionInfoNode.setAttribute('class', 'alert alert-error');
|
||||
} else {
|
||||
this.subscriptionInfoStateNode.setAttribute('class', 'd-none');
|
||||
this.feedbackWidget._updateDisplay(true, false);
|
||||
this.feedbackWidget._setLastAction(this.lastAction);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/** @odoo-module alias=mailing.PortalSubscriptionBlocklist **/
|
||||
|
||||
import { jsonrpc } from "@web/core/network/rpc_service";
|
||||
import { renderToElement } from "@web/core/utils/render";
|
||||
import publicWidget from "@web/legacy/js/public/public_widget";
|
||||
|
||||
|
||||
@@ -46,6 +47,7 @@ publicWidget.registry.MailingPortalSubscriptionBlocklist = publicWidget.Widget.e
|
||||
this.customerData.isBlocklisted = true;
|
||||
}
|
||||
this._updateDisplay();
|
||||
this._updateInfo(result === true ? 'blocklist_add' : 'error');
|
||||
this.trigger_up(
|
||||
'blocklist_add',
|
||||
{'callKey': result === true ? 'blocklist_add' : result,
|
||||
@@ -75,6 +77,7 @@ publicWidget.registry.MailingPortalSubscriptionBlocklist = publicWidget.Widget.e
|
||||
this.customerData.isBlocklisted = false;
|
||||
}
|
||||
this._updateDisplay();
|
||||
this._updateInfo(result === true ? 'blocklist_remove' : 'error');
|
||||
this.trigger_up(
|
||||
'blocklist_remove',
|
||||
{'callKey': result === true ? 'blocklist_remove' : result,
|
||||
@@ -85,8 +88,8 @@ publicWidget.registry.MailingPortalSubscriptionBlocklist = publicWidget.Widget.e
|
||||
},
|
||||
|
||||
/*
|
||||
* Display buttons according to current state. Removing from blocklist is
|
||||
* always available when being blocklisted. Adding in blocklist is available
|
||||
* Display buttons and info according to current state. Removing from blocklist
|
||||
* is always available when being blocklisted. Adding in blocklist is available
|
||||
* when not being blocklisted, if the action is possible (valid email mainly)
|
||||
* and if the option is activated.
|
||||
*/
|
||||
@@ -104,6 +107,34 @@ publicWidget.registry.MailingPortalSubscriptionBlocklist = publicWidget.Widget.e
|
||||
buttonRemoveNode.classList.add('d-none');
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Display feedback (visual tips) to the user concerning the last done action.
|
||||
*/
|
||||
_updateInfo: function (infoKey) {
|
||||
const updateInfo = document.getElementById('o_mailing_subscription_update_info');
|
||||
if (infoKey !== undefined) {
|
||||
const infoContent = renderToElement(
|
||||
"mass_mailing.portal.blocklist_update_info",
|
||||
{
|
||||
infoKey: infoKey,
|
||||
}
|
||||
);
|
||||
updateInfo.innerHTML = infoContent.innerHTML;
|
||||
if (['blocklist_add', 'blocklist_remove'].includes(infoKey)) {
|
||||
updateInfo.classList.add('text-success');
|
||||
updateInfo.classList.remove('text-danger');
|
||||
}
|
||||
else {
|
||||
updateInfo.classList.add('text-danger');
|
||||
updateInfo.classList.remove('text-success');
|
||||
}
|
||||
updateInfo.classList.remove('d-none');
|
||||
}
|
||||
else {
|
||||
updateInfo.classList.add('d-none');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default publicWidget.registry.MailingPortalSubscriptionBlocklist;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/** @odoo-module alias=mailing.PortalSubscriptionFeedback **/
|
||||
|
||||
import { _t } from "@web/core/l10n/translation";
|
||||
import { jsonrpc } from "@web/core/network/rpc_service";
|
||||
import publicWidget from "@web/legacy/js/public/public_widget";
|
||||
import { renderToElement } from "@web/core/utils/render";
|
||||
|
||||
|
||||
publicWidget.registry.MailingPortalSubscriptionFeedback = publicWidget.Widget.extend({
|
||||
@@ -22,7 +24,7 @@ publicWidget.registry.MailingPortalSubscriptionFeedback = publicWidget.Widget.ex
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
this._updateDisplay();
|
||||
this._updateDisplay(true, false);
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
@@ -45,7 +47,12 @@ publicWidget.registry.MailingPortalSubscriptionFeedback = publicWidget.Widget.ex
|
||||
}
|
||||
).then((result) => {
|
||||
if (result === true) {
|
||||
this._updateDisplay(true);
|
||||
this._updateDisplay(false, true);
|
||||
this._updateInfo('feedback_sent');
|
||||
}
|
||||
else {
|
||||
this._updateDisplay(false, false);
|
||||
this._updateInfo(result);
|
||||
}
|
||||
this.trigger_up(
|
||||
'feedback_sent',
|
||||
@@ -54,19 +61,69 @@ publicWidget.registry.MailingPortalSubscriptionFeedback = publicWidget.Widget.ex
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
* Set last done action, which triggers some update in the feedback form
|
||||
* allowing to contextualize the explanation given to the customer.
|
||||
*/
|
||||
_setLastAction: function (lastAction) {
|
||||
this.lastAction = lastAction;
|
||||
if (this.lastAction === 'blocklist_add') {
|
||||
document.querySelector('div#o_mailing_subscription_feedback p').innerHTML = _t(
|
||||
'Please let us know why you want to be in our block list.'
|
||||
);
|
||||
}
|
||||
else {
|
||||
document.querySelector('div#o_mailing_subscription_feedback p').innerHTML = _t(
|
||||
'Please let us know why you updated your subscription.'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Update display after option changes, notably feedback textarea not being
|
||||
* always accessible.
|
||||
*/
|
||||
_updateDisplay: function (cleanFeedback) {
|
||||
_updateDisplay: function (cleanFeedback, setReadonly) {
|
||||
const feedbackArea = document.querySelector('div#o_mailing_subscription_feedback textarea');
|
||||
const feedbackButton = document.getElementById('button_feedback');
|
||||
const feedbackInfo = document.getElementById('o_mailing_subscription_feedback_info');
|
||||
if (this.allowFeedback) {
|
||||
feedbackArea.classList.remove('d-none');
|
||||
} else {
|
||||
feedbackArea.classList.add('d-none');
|
||||
}
|
||||
if (setReadonly) {
|
||||
feedbackArea.setAttribute('disabled', 'disabled');
|
||||
feedbackButton.setAttribute('disabled', 'disabled');
|
||||
}
|
||||
else {
|
||||
feedbackArea.removeAttribute('disabled');
|
||||
feedbackButton.removeAttribute('disabled');
|
||||
}
|
||||
if (cleanFeedback) {
|
||||
feedbackArea.values = '';
|
||||
feedbackArea.value = '';
|
||||
feedbackInfo.innerHTML = "";
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Display feedback (visual tips) to the user concerning the last done action.
|
||||
*/
|
||||
_updateInfo: function (infoKey) {
|
||||
const feedbackInfo = document.getElementById('o_mailing_subscription_feedback_info');
|
||||
if (infoKey !== undefined) {
|
||||
const infoContent = renderToElement(
|
||||
"mass_mailing.portal.feedback_update_info",
|
||||
{
|
||||
infoKey: infoKey,
|
||||
}
|
||||
);
|
||||
feedbackInfo.innerHTML = infoContent.innerHTML;
|
||||
feedbackInfo.classList.add(infoKey === 'feedback_sent' ? 'text-success' : 'text-danger');
|
||||
feedbackInfo.classList.remove('d-none', infoKey === 'feedback_sent' ? 'text-danger': 'text-success');
|
||||
}
|
||||
else {
|
||||
feedbackInfo.classList.add('d-none');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/** @odoo-module alias=mailing.PortalSubscriptionForm **/
|
||||
|
||||
import { jsonrpc } from "@web/core/network/rpc_service";
|
||||
import { renderToFragment } from "@web/core/utils/render";
|
||||
import publicWidget from "@web/legacy/js/public/public_widget";
|
||||
|
||||
|
||||
@@ -17,6 +18,28 @@ publicWidget.registry.MailingPortalSubscriptionForm = publicWidget.Widget.extend
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
* Parse start values of mailing lists subscriptions based on generated DOM
|
||||
* from server. Done here to avoid having to generate it server-side and
|
||||
* propagating it through various layers.
|
||||
*/
|
||||
start: function () {
|
||||
const def = this._super.apply(this, arguments);
|
||||
this.listInfo = [...document.querySelectorAll('#o_mailing_subscription_form_manage input')].map(
|
||||
node => {
|
||||
const listInfo = {
|
||||
id: parseInt(node.getAttribute('value')),
|
||||
member: node.dataset.member === '1',
|
||||
name: node.getAttribute('title'),
|
||||
opt_out: node.getAttribute('checked') !== 'checked',
|
||||
};
|
||||
return listInfo;
|
||||
}
|
||||
);
|
||||
return def;
|
||||
},
|
||||
|
||||
/*
|
||||
* Triggers call to update list subscriptions. Bubble up to let parent
|
||||
* handle returned result if necessary.
|
||||
@@ -24,7 +47,7 @@ publicWidget.registry.MailingPortalSubscriptionForm = publicWidget.Widget.extend
|
||||
_onFormSend: async function (event) {
|
||||
event.preventDefault();
|
||||
const formData = new FormData(document.querySelector('div#o_mailing_subscription_form form'));
|
||||
const mailingListIds = formData.getAll('mailing_list_ids').map(id_str => parseInt(id_str));
|
||||
const mailingListOptinIds = formData.getAll('mailing_list_ids').map(id_str => parseInt(id_str));
|
||||
return await jsonrpc(
|
||||
'/mailing/list/update',
|
||||
{
|
||||
@@ -32,10 +55,14 @@ publicWidget.registry.MailingPortalSubscriptionForm = publicWidget.Widget.extend
|
||||
document_id: this.customerData.documentId,
|
||||
email: this.customerData.email,
|
||||
hash_token: this.customerData.hashToken,
|
||||
lists_optin_ids: mailingListIds,
|
||||
lists_optin_ids: mailingListOptinIds,
|
||||
mailing_id: this.customerData.mailingId,
|
||||
}
|
||||
).then((result) => {
|
||||
if (result === true) {
|
||||
this._updateDisplay(mailingListOptinIds);
|
||||
}
|
||||
this._updateInfo(result === true ? 'subscription_updated' : 'error');
|
||||
this.trigger_up(
|
||||
'subscription_updated',
|
||||
{'callKey': result === true ? 'subscription_updated' : result},
|
||||
@@ -44,11 +71,29 @@ publicWidget.registry.MailingPortalSubscriptionForm = publicWidget.Widget.extend
|
||||
},
|
||||
|
||||
/**
|
||||
* Set form elements as readonly, e.g. because blocklisted email take precedence
|
||||
* Set form elements as hidden / displayed, as this form contains either an
|
||||
* informational text when being blocklisted, either the complete form to
|
||||
* manage their subscription.
|
||||
* @private
|
||||
*/
|
||||
_setBlocklisted: function (isBlocklisted) {
|
||||
if (isBlocklisted) {
|
||||
document.getElementById('o_mailing_subscription_form_blocklisted').classList.remove('d-none');
|
||||
document.getElementById('o_mailing_subscription_form_manage').classList.add('d-none');
|
||||
}
|
||||
else {
|
||||
document.getElementById('o_mailing_subscription_form_blocklisted').classList.add('d-none');
|
||||
document.getElementById('o_mailing_subscription_form_manage').classList.remove('d-none');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set form elements as readonly, e.g. when blocklisted email take precedence
|
||||
* over subscription update.
|
||||
* @private
|
||||
*/
|
||||
_setReadonly: function (isReadonly) {
|
||||
const formInputNodes = document.querySelectorAll('#o_mailing_subscription_form form input');
|
||||
const formInputNodes = document.querySelectorAll('#o_mailing_subscription_form_manage input');
|
||||
const formButtonNode = document.getElementById('button_form_send');
|
||||
if (isReadonly) {
|
||||
formInputNodes.forEach(node => {node.setAttribute('disabled', 'disabled')});
|
||||
@@ -58,6 +103,66 @@ publicWidget.registry.MailingPortalSubscriptionForm = publicWidget.Widget.extend
|
||||
formButtonNode.removeAttribute('disabled');
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Update display after subscription, notably to update mailing list subscription
|
||||
* status. We simply update opt_out status based on the ID being present in the
|
||||
* newly-selected opt-in mailing lists, then rerender the inputs.
|
||||
*/
|
||||
_updateDisplay: function (listOptinIds) {
|
||||
/* update internal status*/
|
||||
this.listInfo.forEach(
|
||||
(listItem) => {
|
||||
listItem.member = listItem.member || listOptinIds.includes(listItem.id);
|
||||
listItem.opt_out = !listOptinIds.includes(listItem.id);
|
||||
}
|
||||
);
|
||||
|
||||
/* update form of lists for update */
|
||||
const formContent = renderToFragment(
|
||||
"mass_mailing.portal.list_form_content",
|
||||
{
|
||||
listsMember: this.listInfo.filter(item => item.member === true),
|
||||
listsProposal: this.listInfo.filter(item => item.member === false),
|
||||
}
|
||||
);
|
||||
const manageForm = document.getElementById('o_mailing_subscription_form_manage');
|
||||
/*manageForm.innerHTML = formContent.innerHTML;*/
|
||||
manageForm.replaceChildren(formContent);
|
||||
|
||||
/* update readonly display of customer's lists */
|
||||
const formReadonlyContent = renderToFragment(
|
||||
"mass_mailing.portal.list_form_content_readonly",
|
||||
{
|
||||
listsOptin: this.listInfo.filter(item => item.opt_out === false),
|
||||
}
|
||||
);
|
||||
const readonlyForm = document.getElementById('o_mailing_subscription_form_blocklisted');
|
||||
/*readonlyForm.innerHTML = formReadonlyContent.innerHTML;*/
|
||||
readonlyForm.replaceChildren(formReadonlyContent);
|
||||
},
|
||||
|
||||
/*
|
||||
* Display feedback (visual tips) to the user concerning the last done action.
|
||||
*/
|
||||
_updateInfo: function (infoKey) {
|
||||
const updateInfo = document.getElementById('o_mailing_subscription_update_info');
|
||||
if (infoKey !== undefined) {
|
||||
const infoContent = renderToFragment(
|
||||
"mass_mailing.portal.list_form_update_info",
|
||||
{
|
||||
infoKey: infoKey,
|
||||
}
|
||||
);
|
||||
/*updateInfo.innerHTML = infoContent.innerHTML;*/
|
||||
updateInfo.replaceChildren(infoContent);
|
||||
updateInfo.classList.add(infoKey === 'error' ? 'text-danger': 'text-success');
|
||||
updateInfo.classList.remove('d-none');
|
||||
}
|
||||
else {
|
||||
updateInfo.classList.add('d-none');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default publicWidget.registry.MailingPortalSubscriptionForm;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="mass_mailing.portal.blocklist_update_info">
|
||||
<span>
|
||||
<t t-if="infoKey == 'blocklist_add'">
|
||||
<i class="fa fa-check me-1"/>
|
||||
<span>Email added to our blocklist</span>
|
||||
</t>
|
||||
<t t-elif="infoKey == 'blocklist_remove'">
|
||||
<i class="fa fa-check me-1"/>
|
||||
<span>Email removed from our blocklist</span>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<i class="fa fa-exclamation-triangle me-1"/>
|
||||
<span>An error occurred. Please retry later.</span>
|
||||
</t>
|
||||
</span>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="mass_mailing.portal.feedback_update_info">
|
||||
<span>
|
||||
<t t-if="infoKey == 'feedback_sent'">
|
||||
<i class="fa fa-check me-1"/>
|
||||
<span>Sent. Thanks you for your feedback!</span>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<i class="fa fa-exclamation-triangle me-1"/>
|
||||
<span>An error occurred. Please retry later or contact us.</span>
|
||||
</t>
|
||||
</span>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<templates xml:space="preserve">
|
||||
<!-- Re-render lists form as no reload is done on portal page -->
|
||||
<t t-name="mass_mailing.portal.list_form_content">
|
||||
<p t-if="listsMember.length">
|
||||
Choose your mailing subscriptions
|
||||
</p>
|
||||
<ul id="o_mailing_subscription_form_lists"
|
||||
t-if="listsMember.length"
|
||||
class="list-group">
|
||||
<li t-foreach="listsMember"
|
||||
t-as="mailing_list"
|
||||
t-key="mailing_list_key"
|
||||
class="list-group-item d-flex align-items-center">
|
||||
<input type="checkbox"
|
||||
name="mailing_list_ids"
|
||||
t-att-data-member="1"
|
||||
t-att-value="mailing_list['id']"
|
||||
t-att-checked="mailing_list['opt_out'] ? undefined : 'checked'"
|
||||
t-att-title="mailing_list['name']"/>
|
||||
<span class="ms-1" t-out="mailing_list['name']"/>
|
||||
<span class="o_mailing_list_unsubscribed ms-auto">
|
||||
<t t-if="mailing_list['opt_out']">
|
||||
Not subscribed
|
||||
</t>
|
||||
<t t-else="">
|
||||
Subscribed
|
||||
</t>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<p t-if="listsProposal.length">
|
||||
You may also be interested in
|
||||
</p>
|
||||
<ul id="o_mailing_subscription_form_lists_additional"
|
||||
t-if="listsProposal.length"
|
||||
class="list-group">
|
||||
<li t-foreach="listsProposal"
|
||||
t-as="mailing_list"
|
||||
t-key="mailing_list_key"
|
||||
class="list-group-item">
|
||||
<input type="checkbox"
|
||||
name="mailing_list_ids"
|
||||
t-att-value="mailing_list['id']"
|
||||
t-att-title="mailing_list['name']"/>
|
||||
<span class="ms-1" t-out="mailing_list['name']"/>
|
||||
</li>
|
||||
</ul>
|
||||
</t>
|
||||
|
||||
<!-- Re-render lists (in readonly mode) as no reload is done on portal page -->
|
||||
<t t-name="mass_mailing.portal.list_form_content_readonly">
|
||||
<p>
|
||||
Your email is currently <strong>in our block list</strong>.
|
||||
<t t-if="listsOptin.length">
|
||||
You will not receive any news from those mailing lists you are a member of:
|
||||
</t>
|
||||
<t t-else="">
|
||||
You will not hear from us anymore.
|
||||
</t>
|
||||
</p>
|
||||
<ul class="list-group mb-4"
|
||||
t-if="listsOptin.length">
|
||||
<t t-foreach="listsOptin"
|
||||
t-as="mailing_list"
|
||||
t-key="mailing_list_key">
|
||||
<li class="list-group-item d-flex align-items-center">
|
||||
<strong t-out="mailing_list['name']"/>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</t>
|
||||
|
||||
<!-- Post action informative span -->
|
||||
<t t-name="mass_mailing.portal.list_form_update_info">
|
||||
<t t-if="infoKey == 'subscription_updated'">
|
||||
<i class="fa fa-check me-1"/>
|
||||
<span>Membership updated</span>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<i class="fa fa-exclamation-triangle me-1"/>
|
||||
<span>An error occurred. Please retry later.</span>
|
||||
</t>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -11,22 +11,35 @@ registry.category("web_tour.tours").add('mailing_portal_unsubscribe_from_documen
|
||||
steps: () => [
|
||||
{
|
||||
content: "Confirmation unsubscribe is done",
|
||||
trigger: "div#o_mailing_subscription_info_state strong:contains('unsubscribed')",
|
||||
trigger: "div#o_mailing_subscription_info span:contains('You are no longer part of our services and will not be contacted again.')",
|
||||
}, {
|
||||
content: "No warning should be displayed",
|
||||
trigger: "div#o_mailing_portal_subscription:not(:has(p:contains('You were still subscribed to those newsletters. You will not receive any news from them anymore')))",
|
||||
trigger: "div#o_mailing_subscription_form_blocklisted:not(:has(p:contains('You will not receive any news from those mailing lists you are a member of')))",
|
||||
}, {
|
||||
contnet: "Warning will not receive anything anymore",
|
||||
trigger: "div#o_mailing_subscription_form_blocklisted p:contains('You will not hear from us anymore.')",
|
||||
}, {
|
||||
content: "Write feedback reason",
|
||||
trigger: "textarea[name='feedback']",
|
||||
run: "text My feedback",
|
||||
}, {
|
||||
content: "Hit Send",
|
||||
trigger: "button#button_feedback",
|
||||
}, {
|
||||
content: "Confirmation feedback is sent",
|
||||
trigger: "div#o_mailing_subscription_feedback_info span:contains('Sent. Thanks you for your feedback!')",
|
||||
}, {
|
||||
content: "Revert exclusion list",
|
||||
trigger: "div#button_blocklist_remove",
|
||||
}, {
|
||||
content: "Confirmation exclusion list is removed",
|
||||
trigger: "div#o_mailing_subscription_info_state strong:contains('removed from our blocklist')",
|
||||
trigger: "div#o_mailing_subscription_update_info span:contains('Email removed from our blocklist')",
|
||||
}, {
|
||||
content: "Now exclude me (again)",
|
||||
trigger: "div#button_blocklist_add",
|
||||
}, {
|
||||
content: "Confirmation exclusion is done",
|
||||
trigger: "div#o_mailing_subscription_info_state strong:contains('added to our blocklist')",
|
||||
trigger: "div#o_mailing_subscription_update_info span:contains('Email added to our blocklist')",
|
||||
isCheck: true,
|
||||
},
|
||||
],
|
||||
@@ -42,25 +55,35 @@ registry.category("web_tour.tours").add('mailing_portal_unsubscribe_from_documen
|
||||
steps: () => [
|
||||
{
|
||||
content: "Confirmation unsubscribe is done",
|
||||
trigger: "div#o_mailing_subscription_info_state strong:contains('unsubscribed')",
|
||||
trigger: "div#o_mailing_subscription_info span:contains('You are no longer part of our services and will not be contacted again.')",
|
||||
}, {
|
||||
content: "Display warning about mailing lists",
|
||||
trigger: "div#o_mailing_portal_subscription p:contains('You were still subscribed to those newsletters. You will not receive any news from them anymore')",
|
||||
trigger: "div#o_mailing_subscription_form_blocklisted p:contains('You will not receive any news from those mailing lists you are a member of')",
|
||||
}, {
|
||||
content: "Warning should contain reference to memberships",
|
||||
trigger: "div#o_mailing_portal_subscription li strong:contains('List1')",
|
||||
trigger: "div#o_mailing_subscription_form_blocklisted li strong:contains('List1')",
|
||||
}, {
|
||||
content: "Write feedback reason",
|
||||
trigger: "textarea[name='feedback']",
|
||||
run: "text My feedback",
|
||||
}, {
|
||||
content: "Hit Send",
|
||||
trigger: "button#button_feedback",
|
||||
}, {
|
||||
content: "Confirmation feedback is sent",
|
||||
trigger: "div#o_mailing_subscription_feedback_info span:contains('Sent. Thanks you for your feedback!')",
|
||||
}, {
|
||||
content: "Revert exclusion list",
|
||||
trigger: "div#button_blocklist_remove",
|
||||
}, {
|
||||
content: "Confirmation exclusion list is removed",
|
||||
trigger: "div#o_mailing_subscription_info_state strong:contains('removed from our blocklist')",
|
||||
trigger: "div#o_mailing_subscription_update_info span:contains('Email removed from our blocklist')",
|
||||
}, {
|
||||
content: "Now exclude me (again)",
|
||||
trigger: "div#button_blocklist_add",
|
||||
}, {
|
||||
content: "Confirmation exclusion is done",
|
||||
trigger: "div#o_mailing_subscription_info_state strong:contains('added to our blocklist')",
|
||||
trigger: "div#o_mailing_subscription_update_info span:contains('Email added to our blocklist')",
|
||||
isCheck: true,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -11,23 +11,23 @@ registry.category("web_tour.tours").add('mailing_portal_unsubscribe_from_list',
|
||||
steps: () => [
|
||||
{
|
||||
content: "Confirmation unsubscribe is done",
|
||||
trigger: "div#o_mailing_subscription_info_state strong:contains('unsubscribed from List1, List2')",
|
||||
trigger: "div#o_mailing_subscription_info span:contains('You are no longer part of the List1, List2 mailing list')",
|
||||
}, {
|
||||
content: "Write feedback reason",
|
||||
trigger: "textarea[name='feedback']",
|
||||
run: "text My feedback",
|
||||
}, {
|
||||
content: "Hit Send",
|
||||
trigger: "div#button_feedback",
|
||||
trigger: "button#button_feedback",
|
||||
}, {
|
||||
content: "Confirmation feedback is sent",
|
||||
trigger: "div#o_mailing_subscription_info_state:contains('Thanks for your feedback')",
|
||||
trigger: "div#o_mailing_subscription_feedback_info span:contains('Sent. Thanks you for your feedback!')",
|
||||
}, {
|
||||
content: "Now exclude me",
|
||||
trigger: "div#button_blocklist_add",
|
||||
}, {
|
||||
content: "Confirmation exclusion is done",
|
||||
trigger: "div#o_mailing_subscription_info_state strong:contains('added to our blocklist')",
|
||||
trigger: "div#o_mailing_subscription_update_info span:contains('Email added to our blocklist')",
|
||||
isCheck: true,
|
||||
},
|
||||
],
|
||||
@@ -45,32 +45,35 @@ registry.category("web_tour.tours").add('mailing_portal_unsubscribe_from_list_wi
|
||||
steps: () => [
|
||||
{
|
||||
content: "Confirmation unsubscribe is done",
|
||||
trigger: "div#o_mailing_subscription_info_state strong:contains('unsubscribed from List1, List2')",
|
||||
trigger: "div#o_mailing_subscription_info span:contains('You are no longer part of the List1, List2 mailing list')",
|
||||
}, {
|
||||
content: "List1 is present, just opt-outed",
|
||||
trigger: "li.list-group-item:contains('List1') span:contains('Unsubscribed')",
|
||||
trigger: "ul#o_mailing_subscription_form_lists li.list-group-item:contains('List1') span:contains('Not subscribed')",
|
||||
}, {
|
||||
content: "List3 is present, opt-outed (test starting data)",
|
||||
trigger: "li.list-group-item:contains('List3') span:contains('Unsubscribed')",
|
||||
trigger: "ul#o_mailing_subscription_form_lists li.list-group-item:contains('List3') span:contains('Not subscribed')",
|
||||
}, {
|
||||
content: "List2 is not present (not member -> not displayed)",
|
||||
trigger: "ul:not(:has(li.list-group-item:contains('List2') span:contains('Unsubscribed')))",
|
||||
content: "List2 is proposed (not member -> proposal to join)",
|
||||
trigger: "ul#o_mailing_subscription_form_lists_additional li.list-group-item:contains('List2')",
|
||||
}, {
|
||||
content: "List4 is not proposed (not member but not private)",
|
||||
trigger: "ul#o_mailing_subscription_form_lists_additional:not(:has(li.list-group-item:contains('List4')))",
|
||||
}, {
|
||||
content: "Write feedback reason",
|
||||
trigger: "textarea[name='feedback']",
|
||||
run: "text My feedback",
|
||||
}, {
|
||||
content: "Hit Send",
|
||||
trigger: "div#button_feedback",
|
||||
trigger: "button#button_feedback",
|
||||
}, {
|
||||
content: "Confirmation feedback is sent",
|
||||
trigger: "div#o_mailing_subscription_info_state:contains('Thanks for your feedback')",
|
||||
trigger: "div#o_mailing_subscription_feedback_info span:contains('Sent. Thanks you for your feedback!')",
|
||||
}, {
|
||||
content: "Now exclude me",
|
||||
trigger: "div#button_blocklist_add",
|
||||
}, {
|
||||
content: "Confirmation exclusion is done",
|
||||
trigger: "div#o_mailing_subscription_info_state strong:contains('added to our blocklist')",
|
||||
trigger: "div#o_mailing_subscription_update_info span:contains('Email added to our blocklist')",
|
||||
}, {
|
||||
content: "This should disable the 'Update my subscriptions' button",
|
||||
trigger: "button#button_form_send[disabled]",
|
||||
@@ -80,26 +83,53 @@ registry.category("web_tour.tours").add('mailing_portal_unsubscribe_from_list_wi
|
||||
trigger: "div#button_blocklist_remove",
|
||||
}, {
|
||||
content: "Confirmation exclusion list is removed",
|
||||
trigger: "div#o_mailing_subscription_info_state strong:contains('removed from our blocklist')",
|
||||
trigger: "div#o_mailing_subscription_update_info span:contains('Email removed from our blocklist')",
|
||||
}, {
|
||||
content: "'Update my subscriptions' button usable again",
|
||||
trigger: "button#button_form_send:not([disabled])",
|
||||
isCheck: true,
|
||||
}, {
|
||||
content: "Choose the mailing list 3 to come back",
|
||||
trigger: "li.list-group-item:contains('List3') input",
|
||||
trigger: "ul#o_mailing_subscription_form_lists input[title='List3']",
|
||||
}, {
|
||||
content: "Add list 2",
|
||||
trigger: "ul#o_mailing_subscription_form_lists_additional input[title='List2']",
|
||||
}, {
|
||||
content: "Update subscription",
|
||||
trigger: "button#button_form_send",
|
||||
}, {
|
||||
content: "Confirmation changes are done",
|
||||
trigger: "div#o_mailing_subscription_info_state strong:contains('updated your memberships')",
|
||||
trigger: "div#o_mailing_subscription_update_info span:contains('Membership updated')",
|
||||
}, {
|
||||
content: "List 3 is noted as subscribed again",
|
||||
trigger: "ul#o_mailing_subscription_form_lists li.list-group-item:contains('List3') span:contains('Subscribed')",
|
||||
}, {
|
||||
content: "List 2 has joined the subscriptions",
|
||||
trigger: "ul#o_mailing_subscription_form_lists li.list-group-item:contains('List2') span:contains('Subscribed')",
|
||||
}, {
|
||||
content: "No list in proposals",
|
||||
trigger: "div#o_mailing_subscription_form_manage:not(:has(ul#o_mailing_subscription_form_lists_additional))",
|
||||
}, {
|
||||
content: "Write feedback reason",
|
||||
trigger: "textarea[name='feedback']",
|
||||
run: "text Another feedback",
|
||||
}, {
|
||||
content: "Hit Send",
|
||||
trigger: "button#button_feedback",
|
||||
}, {
|
||||
content: "Now exclude me (again)",
|
||||
trigger: "div#button_blocklist_add",
|
||||
}, {
|
||||
content: "Confirmation exclusion is done",
|
||||
trigger: "div#o_mailing_subscription_info_state strong:contains('added to our blocklist')",
|
||||
trigger: "div#o_mailing_subscription_update_info span:contains('Email added to our blocklist')",
|
||||
}, {
|
||||
content: "Should display warning about mailing lists",
|
||||
trigger: "div#o_mailing_subscription_form_blocklisted p:contains('You will not receive any news from those mailing lists you are a member of')",
|
||||
}, {
|
||||
content: "Warning should contain reference to memberships",
|
||||
trigger: "div#o_mailing_subscription_form_blocklisted li strong:contains('List2')",
|
||||
extra_trigger: "div#o_mailing_subscription_form_blocklisted li strong:contains('List3')",
|
||||
isCheck: true,
|
||||
}
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -183,10 +183,13 @@ class TestMailingControllers(TestMailingControllersCommon):
|
||||
|
||||
Tour effects
|
||||
* unsubscribe from mailing based on a document = blocklist;
|
||||
* add feedback (block list) 'My feedback';
|
||||
* remove email from exclusion list;
|
||||
* re-add email to exclusion list;
|
||||
"""
|
||||
test_mailing = self.test_mailing_on_documents.with_env(self.env)
|
||||
test_feedback = "My feedback"
|
||||
|
||||
for test_email, tour_name in [
|
||||
('"Not Déboulonneur" <not.fleurus@example.com>', 'mailing_portal_unsubscribe_from_document'),
|
||||
(self.test_email, 'mailing_portal_unsubscribe_from_document_with_lists'),
|
||||
@@ -198,6 +201,7 @@ class TestMailingControllers(TestMailingControllersCommon):
|
||||
})
|
||||
self.assertFalse(test_partner.is_blacklisted)
|
||||
previous_messages = test_partner.message_ids
|
||||
test_email_normalized = tools.email_normalize(test_email)
|
||||
|
||||
# launch unsubscription tour
|
||||
hash_token = test_mailing._generate_mailing_recipient_token(test_partner.id, test_partner.email_normalized)
|
||||
@@ -211,8 +215,14 @@ class TestMailingControllers(TestMailingControllersCommon):
|
||||
# status update check
|
||||
self.assertTrue(test_partner.is_blacklisted)
|
||||
|
||||
# partner (document): no messages added
|
||||
self.assertEqual(test_partner.message_ids, previous_messages)
|
||||
# partner (document): feedback message added
|
||||
self.assertEqual(len(test_partner.message_ids), len(previous_messages) + 1)
|
||||
message_feedback = test_partner.message_ids[0]
|
||||
self.assertEqual(
|
||||
message_feedback.body,
|
||||
Markup(f'<p>Feedback from {test_email_normalized}: {test_feedback}</p>')
|
||||
)
|
||||
|
||||
# posted messages on exclusion list record: activated, deactivated, activated again
|
||||
bl_record = self.env['mail.blacklist'].search([('email', '=', test_partner.email_normalized)])
|
||||
self.assertEqual(len(bl_record.message_ids), 4)
|
||||
@@ -302,10 +312,13 @@ class TestMailingControllers(TestMailingControllersCommon):
|
||||
* add email to exclusion list;
|
||||
* remove email from exclusion list;
|
||||
* come back to List3;
|
||||
* join List2;
|
||||
* add feedback (opt-out) 'Another feedback';'
|
||||
* re-add email to exclusion list;
|
||||
"""
|
||||
test_mailing = self.test_mailing_on_lists.with_env(self.env)
|
||||
test_feedback = "My feedback"
|
||||
test_feedback_2 = "Another feedback"
|
||||
|
||||
# fetch contact and its subscription and blacklist status, to see the tour effects
|
||||
contact_l1 = self.mailing_list_1.contact_ids.filtered(
|
||||
@@ -337,23 +350,38 @@ class TestMailingControllers(TestMailingControllersCommon):
|
||||
self.assertFalse(subscription_l3.opt_out)
|
||||
self.assertFalse(subscription_l3.unsubscription_date)
|
||||
|
||||
# posted messages on contact record for mailing list 1: feedback, unsubscription
|
||||
message_feedback = contact_l1.message_ids[0]
|
||||
# posted messages on contact record for mailing list 1: last feedback, subscription update, feedback, unsubscription
|
||||
message_feedback_2 = contact_l1.message_ids[0]
|
||||
self.assertEqual(
|
||||
message_feedback_2.body,
|
||||
Markup(f'<p>Feedback from {self.test_email_normalized}: {test_feedback_2}</p>')
|
||||
)
|
||||
message_update = contact_l1.message_ids[1]
|
||||
self.assertEqual(
|
||||
message_update.body,
|
||||
Markup(f'<p>{contact_l1.display_name} subscribed to the following mailing list(s)</p><ul><li>{self.mailing_list_2.name}</li></ul>')
|
||||
)
|
||||
message_feedback = contact_l1.message_ids[2]
|
||||
self.assertEqual(
|
||||
message_feedback.body,
|
||||
Markup(f'<p>Feedback from {self.test_email_normalized}: {test_feedback}</p>')
|
||||
)
|
||||
message_unsub = contact_l1.message_ids[1]
|
||||
message_unsub = contact_l1.message_ids[3]
|
||||
self.assertEqual(
|
||||
message_unsub.body,
|
||||
Markup(f'<p>{contact_l1.display_name} unsubscribed from the following mailing list(s)</p><ul><li>{self.mailing_list_1.name}</li></ul>')
|
||||
)
|
||||
|
||||
# posted messages on contact record for mailing list 3: subscription
|
||||
message_unsub = contact_l3.message_ids[0]
|
||||
# posted messages on contact record for mailing list 3: last feedback, subscription
|
||||
message_feedback = contact_l1.message_ids[0]
|
||||
self.assertEqual(
|
||||
message_unsub.body,
|
||||
Markup(f'<p>{contact_l1.display_name} subscribed to the following mailing list(s)</p><ul><li>{self.mailing_list_3.name}</li></ul>')
|
||||
message_feedback_2.body,
|
||||
Markup(f'<p>Feedback from {self.test_email_normalized}: {test_feedback_2}</p>')
|
||||
)
|
||||
message_sub = contact_l3.message_ids[1]
|
||||
self.assertEqual(
|
||||
message_sub.body,
|
||||
Markup(f'<p>{contact_l3.display_name} subscribed to the following mailing list(s)</p><ul><li>{self.mailing_list_3.name}</li><li>{self.mailing_list_2.name}</li></ul>')
|
||||
)
|
||||
|
||||
# posted messages on exclusion list record: activated, deactivated, activated again
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
t-att-data-blocklist-enabled="blocklist_enabled"
|
||||
t-att-data-blocklist-possible="blocklist_possible"
|
||||
t-att-data-document-id="document_id"
|
||||
t-att-data-feedback-enabled="feedback_enabled"
|
||||
t-att-data-feedback-readonly="feedback_readonly"
|
||||
t-att-data-hash-token="hash_token"
|
||||
t-att-data-email="email"
|
||||
t-att-data-email-valid="email_valid"
|
||||
@@ -20,126 +22,125 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-6 offset-lg-3 mt-4">
|
||||
<div id="o_mailing_subscription_info"
|
||||
class="alert alert-success" role="status">
|
||||
<div id="o_mailing_subscription_info_state">
|
||||
<t t-if="unsubscribed_list">
|
||||
You have been <strong>successfully unsubscribed from <t t-out="unsubscribed_list"/></strong>.
|
||||
</t>
|
||||
<t t-else="">
|
||||
You have been <strong>successfully unsubscribed</strong>.
|
||||
</t>
|
||||
</div>
|
||||
<div id="o_mailing_subscription_feedback">
|
||||
<form action="/mail/mailing/feedback" method="POST">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
class="mb-4"
|
||||
role="status">
|
||||
<t t-if="unsubscribed_name">
|
||||
<h1 class="o_page_header">Successfully Unsubscribed</h1>
|
||||
<span t-out="unsubscribed_name"/>
|
||||
</t>
|
||||
<div id="o_mailing_subscription_feedback"
|
||||
t-att-class="'' if feedback_enabled else 'd-none'">
|
||||
<form action="/mail/mailing/feedback" method="POST"
|
||||
class="mt-3">
|
||||
<p>
|
||||
We would appreciate if you provide feedback about why you updated<br/>your subscriptions.
|
||||
Please let us know why you updated your subscription.
|
||||
</p>
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<textarea class="form-control" name="feedback" cols="60" rows="3"></textarea>
|
||||
<br/>
|
||||
<div class="btn btn-primary text-start" id="button_feedback">Send</div>
|
||||
<div class="d-flex align-items-center mt-2">
|
||||
<button type="submit" id="button_feedback"
|
||||
class="btn btn-primary text-start me-2">Send</button>
|
||||
<div id="o_mailing_subscription_feedback_info" class="d-none"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="o_mailing_subscription_form" t-if="contacts">
|
||||
<div id="o_mailing_subscription_form">
|
||||
<form action="/mailing/list/update" method="POST">
|
||||
<h1 class="o_page_header">Mailing Subscriptions</h1>
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<p>Choose your mailing subscriptions</p>
|
||||
<ul id="o_mailing_subscription_form_lists" class="list-group">
|
||||
<li t-foreach="list_ids"
|
||||
t-as="list_id"
|
||||
t-if="list_id.is_public == True"
|
||||
class="list-group-item">
|
||||
<input type="checkbox"
|
||||
name="mailing_list_ids"
|
||||
t-att-disabled="'disabled' if is_blocklisted else None"
|
||||
t-att-value="list_id['id']"
|
||||
t-att-checked="None if list_id['id'] in opt_out_list_ids else 'checked'"/>
|
||||
<t t-esc="list_id.name"/>
|
||||
<span t-if="list_id['id'] in opt_out_list_ids"
|
||||
class="o_mailing_list_unsubscribed">
|
||||
Unsubscribed
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div id="o_mailing_subscription_form_blocklisted"
|
||||
t-att-class="'' if is_blocklisted else 'd-none'">
|
||||
<p>
|
||||
Your email is currently <strong>in our block list</strong>.
|
||||
<t t-if="lists_optin">
|
||||
You will not receive any news from those mailing lists you are a member of:
|
||||
</t>
|
||||
<t t-else="">
|
||||
You will not hear from us anymore.
|
||||
</t>
|
||||
</p>
|
||||
<ul class="list-group mb-4"
|
||||
t-if="lists_optin">
|
||||
<t t-foreach="lists_optin" t-as="mailing_list">
|
||||
<li class="list-group-item d-flex align-items-center">
|
||||
<strong>
|
||||
<t t-if="mailing_list.is_public" t-esc="mailing_list.name"/>
|
||||
<t t-else="">Mailing List #<t t-esc="mailing_list.id"/></t>
|
||||
</strong>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="o_mailing_subscription_form_manage"
|
||||
t-att-class="'d-none' if is_blocklisted else ''">
|
||||
<p t-if="lists_optin + lists_optout">
|
||||
Choose your mailing subscriptions
|
||||
</p>
|
||||
<ul id="o_mailing_subscription_form_lists"
|
||||
t-if="lists_optin + lists_optout"
|
||||
class="list-group">
|
||||
<li t-foreach="(lists_optin + lists_optout)"
|
||||
t-as="mailing_list"
|
||||
class="list-group-item d-flex align-items-center">
|
||||
<t t-set="title" t-value="mailing_list.name if mailing_list.is_public else 'Mailing List #%s' % mailing_list.id"/>
|
||||
<input type="checkbox"
|
||||
name="mailing_list_ids"
|
||||
t-att-data-member="1"
|
||||
t-att-disabled="'disabled' if is_blocklisted else None"
|
||||
t-att-value="mailing_list.id"
|
||||
t-att-checked="None if mailing_list in lists_optout else 'checked'"
|
||||
t-att-title="title"/>
|
||||
<span class="ms-1" t-out="title"/>
|
||||
<span class="o_mailing_list_unsubscribed ms-auto">
|
||||
<t t-if="mailing_list in lists_optout">
|
||||
Not subscribed
|
||||
</t>
|
||||
<t t-else="">
|
||||
Subscribed
|
||||
</t>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<p t-if="lists_public">
|
||||
You may also be interested in
|
||||
</p>
|
||||
<ul id="o_mailing_subscription_form_lists_additional"
|
||||
t-if="lists_public"
|
||||
class="list-group">
|
||||
<li t-foreach="lists_public"
|
||||
t-as="mailing_list"
|
||||
class="list-group-item">
|
||||
<input type="checkbox"
|
||||
name="mailing_list_ids"
|
||||
t-att-disabled="'disabled' if is_blocklisted else None"
|
||||
t-att-value="mailing_list.id"
|
||||
t-att-title="mailing_list.name"/>
|
||||
<span class="ms-1" t-out="mailing_list.name"/>
|
||||
</li>
|
||||
</ul>
|
||||
<p t-if="not lists_optin and not lists_optout and not lists_public">
|
||||
You are not subscribed to any of our mailing list.
|
||||
</p>
|
||||
</div>
|
||||
<div id="o_mailing_subscription_blocklist"
|
||||
class="mb64 pt-3">
|
||||
<div class="btn btn-link float-end pe-0 text-uppercase d-none"
|
||||
t-if="blocklist_enabled and blocklist_possible"
|
||||
id="button_blocklist_add">Exclude Me</div>
|
||||
<div class="btn btn-link float-end pe-0 text-uppercase d-none"
|
||||
id="button_blocklist_remove">Come Back</div>
|
||||
class="d-flex align-items-center mb-4 pt-3">
|
||||
<button type="submit" id="button_form_send"
|
||||
t-att-disabled="'disabled' if is_blocklisted else None"
|
||||
class="btn btn-primary">
|
||||
Update my subscriptions
|
||||
t-att-class="'btn btn-primary ' + ('' if lists_optin or lists_optout or lists_public else 'd-none')">
|
||||
Apply changes
|
||||
</button>
|
||||
<div class="btn btn-link me-2 text-uppercase d-none"
|
||||
t-if="blocklist_enabled and blocklist_possible"
|
||||
id="button_blocklist_add">Exclude Me</div>
|
||||
<div class="btn btn-link text-uppercase d-none"
|
||||
id="button_blocklist_remove">Come Back</div>
|
||||
<div id="o_mailing_subscription_update_info" class="d-none"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div t-else="" class="alert alert-info text-center" role="status">
|
||||
<p>You are not subscribed to any of our mailing list.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="page_mailing_unsubscribe_done" name="Unsubscribed">
|
||||
<t t-call="mass_mailing.layout">
|
||||
<t t-call="mass_mailing.unsubscribe_done"/>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="unsubscribe_done">
|
||||
<div id="o_mailing_portal_subscription"
|
||||
class="container o_mailing_portal_body"
|
||||
t-att-data-blocklist-enabled="blocklist_enabled"
|
||||
t-att-data-blocklist-possible="blocklist_possible"
|
||||
t-att-data-document-id="document_id"
|
||||
t-att-data-hash-token="hash_token"
|
||||
t-att-data-email="email"
|
||||
t-att-data-email-valid="email_valid"
|
||||
t-att-data-is-blocklisted="is_blocklisted"
|
||||
t-att-data-mailing-id="mailing_id">
|
||||
<div class="row">
|
||||
<input type="hidden" name="email" t-att-value="email"/>
|
||||
<input type="hidden" name="mailing_id" t-att-value="mailing_id"/>
|
||||
<input type="hidden" name="res_id" t-att-value="res_id"/>
|
||||
<div class="col-lg-6 offset-lg-3">
|
||||
<h1 class="o_page_header">Mailing Subscriptions</h1>
|
||||
|
||||
<div id="o_mailing_subscription_info"
|
||||
class="alert alert-success" role="status">
|
||||
<div id="o_mailing_subscription_info_state">
|
||||
You have been successfully <strong>unsubscribed</strong>!
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div t-if="list_ids" class="alert alert-warning">
|
||||
<p class="text-center">You were still subscribed to those newsletters. You will not receive any news from them anymore:</p>
|
||||
<ul class="list-group mb-4">
|
||||
<t t-foreach="list_ids" t-as="list_id">
|
||||
<t t-if="list_id.is_public == True">
|
||||
<li class="list-group-item bg-transparent">
|
||||
<strong><t t-esc="list_id.name"/></strong>
|
||||
</li>
|
||||
</t>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="o_mailing_subscription_blocklist"
|
||||
class="mb64 pt-3">
|
||||
<div class="btn btn-link float-end pe-0 text-uppercase d-none"
|
||||
t-if="blocklist_enabled and blocklist_possible"
|
||||
id="button_blocklist_add">Exclude Me</div>
|
||||
<div class="btn btn-link float-end pe-0 text-uppercase d-none"
|
||||
id="button_blocklist_remove">Come Back</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user