%s
') % message) + + return request.render( + 'mass_mailing.page_mailing_unsubscribe', + dict( + self._prepare_mailing_subscription_values( + mailing, document_id, email, hash_token + ), + last_action='blocklist_add', + 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 + blocklist flows done in portal. """ + mail_blocklist = self._fetch_blocklist_record(email) + email_normalized = tools.email_normalize(email) + + # fetch optout/blacklist reasons + opt_out_reasons = self._fetch_subscription_optouts() + + # as there may be several contacts / email -> consider any opt-in overrides + # opt-out + contacts = self._fetch_contacts(email) + lists_optin = contacts.subscription_ids.filtered( + lambda sub: not sub.opt_out + ).list_id.filtered('active') + lists_optout = contacts.subscription_ids.filtered( + lambda sub: sub.opt_out and sub.list_id not in lists_optin + ).list_id.filtered('active') + 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, + 'email': email, + 'email_valid': bool(email_normalized), + 'hash_token': hash_token, + 'mailing_id': mailing.id, + 'res_id': document_id, + # feedback + 'feedback_enabled': True, + 'feedback_readonly': False, + 'opt_out_reasons': opt_out_reasons, + # blocklist + 'blocklist_enabled': bool( + request.env['ir.config_parameter'].sudo().get_param( + 'mass_mailing.show_blacklist_buttons', + default=True, + ) + ), + '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_ids.list_id.filtered('active'), + 'lists_optin': lists_optin, + 'lists_optout': lists_optout, + 'lists_public': lists_public, + } + + @http.route('/mailing/list/update', type='json', auth='public', csrf=True) + def mailing_update_list_subscription(self, mailing_id=None, document_id=None, + email=None, hash_token=None, + lists_optin_ids=None, **post): + email_found, hash_token_found = self._fetch_user_information(email, hash_token) + try: + _mailing_sudo = self._check_mailing_email_token( + mailing_id, document_id, email_found, hash_token_found, + required_mailing_id=False + ) + except BadRequest: + return 'error' + except (NotFound, Unauthorized): + return 'unauthorized' + + contacts = self._fetch_contacts(email_found) + lists_optin = request.env['mailing.list'].sudo().browse(lists_optin_ids or []).exists() + # opt-out all not chosen lists + lists_to_optout = contacts.subscription_ids.filtered( + lambda sub: not sub.opt_out and sub.list_id not in lists_optin + ).list_id + # 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.list_ids + ) + lists_to_optout._update_subscription_from_email(email_found, opt_out=True) + lists_to_optin._update_subscription_from_email(email_found, opt_out=False) + + return len(lists_to_optout) + + @http.route('/mailing/feedback', type='json', auth='public', csrf=True) + def mailing_send_feedback(self, mailing_id=None, document_id=None, + email=None, hash_token=None, + last_action=None, + opt_out_reason_id=False, feedback=None, + **post): + """ Feedback can be given after some actions, notably after opt-outing + from mailing lists or adding an email in the blocklist. + + This controller tries to write the customer feedback in the most relevant + record. Feedback consists in two parts, the opt-out reason (based on data + in 'mailing.subscription.optout' model) and the feedback itself (which + is triggered by the optout reason 'is_feedback' fields). + """ + email_found, hash_token_found = self._fetch_user_information(email, hash_token) + try: + mailing_sudo = self._check_mailing_email_token( + mailing_id, document_id, email_found, hash_token_found, + required_mailing_id=False, + ) + except BadRequest: + return 'error' + except (NotFound, Unauthorized): + return 'unauthorized' + + if not opt_out_reason_id: + return 'error' + feedback = feedback.strip() if feedback else '' + message = '' + if feedback: + if not request.env.user._is_public(): + author_name = f'{request.env.user.name} ({email_found})' + else: + author_name = email_found + message = Markup("%s
%s
%s
') % _('Blocklist request from portal') + + _blocklist_rec = request.env['mail.blacklist'].sudo()._add(email_found, message=message) + return True + + @http.route('/mailing/blocklist/remove', type='json', auth='public') + def mail_blocklist_remove(self, mailing_id=None, document_id=None, + email=None, hash_token=None): + email_found, hash_token_found = self._fetch_user_information(email, hash_token) + try: + mailing_sudo = self._check_mailing_email_token( + mailing_id, document_id, email_found, hash_token_found, + required_mailing_id=False, + ) + except BadRequest: + return 'error' + except (NotFound, Unauthorized): + return 'unauthorized' + + if mailing_sudo and document_id: + message = Markup( + _( + 'Blocklist removal request from portal of mailing %(mailing_link)s (document %(record_link)s)', + **self._format_bl_request(mailing_sudo, document_id) + ) + ) + else: + message = Markup('%s
') % _('Blocklist removal request from portal') + + _blocklist_rec = request.env['mail.blacklist'].sudo()._remove(email_found, message=message) + return True + + 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)}' + ) if document_id else '', + } + + # ------------------------------------------------------------ + # PREVIEW + # ------------------------------------------------------------ + + @http.route('/mailing/mobile/preview', methods=['GET'], type='http', auth='user', website=True) + def mass_mailing_preview_mobile_content(self): + return request.render("mass_mailing.preview_content_mobile", {}) diff --git a/data/digest_data.xml b/data/digest_data.xml new file mode 100644 index 0000000..b429110 --- /dev/null +++ b/data/digest_data.xml @@ -0,0 +1,16 @@ + +
+ This
The winner has already been sent. Use Compare Version to get an overview of this A/B testing campaign.
+
+ A sample of
+
+ Some of the mailings will not be sent, as only 1 email will be sent for each unique recipient in this campaign.
+
+
+ A sample of
Try different variations in the campaign to compare their
Once the best version is identified, we will send the best one to the remaining recipients.
+Use alternative versions to be able to select the winner.
+
+
|
+ ||||
+
|
+