[MERGE] mass_mailing: reorganize addon according to guidelines

PURPOSE

Reorganize addon according to guidelines, helping finding and updating code.

SPECIFICATIONS

Perform various code linting and cleanups, line breaks, linter issues...
No code or feature change should occur with this commit.

Correctly split data into separate files. A lot of content was put a bit
randomly in data files, leading to a hard discovery of code and features.

Move subscription model into its own file, to ease future improvements. Do
the same for its views.

Also move menu entries into a specific file for menus, allowing to easily
understand menu organization in mass mailing.

Reorganize templates in order to see a bit what's going on here. Notably split
subscription (opt-out, blacklist) from management (view, reporting) templates.

Reorganize controller code by main section in order to separate a bit various
code purpose.

Task-2864264 (Mass Mailing Module Reorganization)

closes odoo/odoo#92509

Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
Odoo's Mergebot
2022-05-31 16:02:26 +02:00
committed by GitHub
43 changed files with 734 additions and 763 deletions
+14 -8
View File
@@ -20,31 +20,37 @@
'digest',
],
'data': [
'security/mass_mailing_security.xml',
'security/res_groups_data.xml',
'security/ir.model.access.csv',
'data/digest_data.xml',
'data/mail_data.xml',
'data/mailing_data_templates.xml',
'data/mass_mailing_data.xml',
'data/ir_attachment_data.xml',
'data/ir_config_parameter_data.xml',
'data/ir_cron_data.xml',
'data/ir_module_data.xml',
'data/mailing_data_templates.xml',
'data/mailing_list_data.xml',
'data/res_users_data.xml',
'wizard/mail_compose_message_views.xml',
'wizard/mailing_contact_import_views.xml',
'wizard/mailing_contact_to_list_views.xml',
'wizard/mailing_list_merge_views.xml',
'wizard/mailing_mailing_test_views.xml',
'wizard/mailing_mailing_schedule_date_views.xml',
'report/mailing_trace_report_views.xml',
'views/mailing_filter_views.xml',
'views/mailing_mailing_views_menus.xml',
'views/mailing_trace_views.xml',
'views/link_tracker_views.xml',
'views/mailing_contact_views.xml',
'views/mailing_contact_subscription_views.xml',
'views/mailing_list_views.xml',
'views/mailing_mailing_views.xml',
'views/res_config_settings_views.xml',
'views/utm_campaign_views.xml',
'report/mailing_trace_report_views.xml',
'views/mailing_menus.xml',
'views/assets.xml',
'views/mass_mailing_templates_portal.xml',
'views/mailing_templates_portal_layouts.xml',
'views/mailing_templates_portal_management.xml',
'views/mailing_templates_portal_unsubscribe.xml',
'views/themes_templates.xml',
'views/snippets_themes.xml',
'views/snippets/s_alert.xml',
@@ -90,7 +96,7 @@
'mass_mailing/static/src/js/mass_mailing_design_constants.js',
'mass_mailing/static/src/js/mass_mailing_widget.js',
'mass_mailing/static/src/js/mailing_mailing_view_form_full_width.js',
'mass_mailing/static/src/js/unsubscribe.js',
'mass_mailing/static/src/js/mailing_portal.js',
],
'mass_mailing.assets_mail_themes': [
'mass_mailing/static/src/scss/themes/**/*',
+62 -46
View File
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
import base64
import werkzeug
from odoo import _, exceptions, http, tools
@@ -23,10 +23,9 @@ class MassMailController(http.Controller):
model_display = mailing.mailing_model_id.display_name
blacklist_entry._message_log(body=description + " ({})".format(model_display))
@http.route(['/unsubscribe_from_list'], type='http', website=True, multilang=False, auth='public', sitemap=False)
def unsubscribe_placeholder_link(self, **post):
"""Dummy route so placeholder is not prefixed by language, MUST have multilang=False"""
raise werkzeug.exceptions.NotFound()
# ------------------------------------------------------------
# SUBSCRIPTION MANAGEMENT
# ------------------------------------------------------------
@http.route(['/mail/mailing/<int:mailing_id>/unsubscribe'], type='http', website=True, auth='public')
def mailing(self, mailing_id, email=None, res_id=None, token="", **post):
@@ -92,6 +91,28 @@ class MassMailController(http.Controller):
return True
return 'error'
@http.route('/mailing/feedback', type='json', auth='public')
def 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):
return 'unauthorized'
model = request.env[mailing.mailing_model_real]
records = model.sudo().search([('email_normalized', '=', tools.email_normalize(email))])
for record in records:
record.sudo().message_post(body=_("Feedback from %(email)s: %(feedback)s", email=email, feedback=feedback))
return bool(records)
return 'error'
@http.route(['/unsubscribe_from_list'], type='http', website=True, multilang=False, auth='public', sitemap=False)
def unsubscribe_placeholder_link(self, **post):
"""Dummy route so placeholder is not prefixed by language, MUST have multilang=False"""
raise werkzeug.exceptions.NotFound()
# ------------------------------------------------------------
# TRACKING
# ------------------------------------------------------------
@http.route('/mail/track/<int:mail_id>/<string:token>/blank.gif', type='http', auth='public')
def track_mail_open(self, mail_id, token, **post):
""" Email tracking. """
@@ -105,6 +126,39 @@ class MassMailController(http.Controller):
return response
@http.route('/r/<string:code>/m/<int:mailing_trace_id>', type='http', auth="public")
def full_url_redirect(self, code, mailing_trace_id, **post):
# don't assume geoip is set, it is part of the website module
# which mass_mailing doesn't depend on
country_code = request.geoip.get('country_code')
request.env['link.tracker.click'].sudo().add_click(
code,
ip=request.httprequest.remote_addr,
country_code=country_code,
mailing_trace_id=mailing_trace_id
)
return request.redirect(request.env['link.tracker'].get_url_from_code(code), code=301, local=False)
# ------------------------------------------------------------
# MAILING MANAGEMENT
# ------------------------------------------------------------
@http.route('/mailing/report/unsubscribe', type='http', website=True, auth='public')
def turn_off_mailing_reports(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))
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)
if user.has_group('base.group_system'):
menu_id = request.env.ref('mass_mailing.menu_mass_mailing_global_settings').id
return request.render('mass_mailing.mailing_report_deactivated', {'menu_id': menu_id})
return request.render('mass_mailing.mailing_report_deactivated')
raise werkzeug.exceptions.NotFound()
@http.route(['/mailing/<int:mailing_id>/view'], type='http', website=True, auth='public')
def view(self, mailing_id, email=None, res_id=None, token=""):
mailing = request.env['mailing.mailing'].sudo().browse(mailing_id)
@@ -134,19 +188,9 @@ class MassMailController(http.Controller):
return request.redirect('/web')
@http.route('/r/<string:code>/m/<int:mailing_trace_id>', type='http', auth="public")
def full_url_redirect(self, code, mailing_trace_id, **post):
# don't assume geoip is set, it is part of the website module
# which mass_mailing doesn't depend on
country_code = request.geoip.get('country_code')
request.env['link.tracker.click'].sudo().add_click(
code,
ip=request.httprequest.remote_addr,
country_code=country_code,
mailing_trace_id=mailing_trace_id
)
return request.redirect(request.env['link.tracker'].get_url_from_code(code), code=301, local=False)
# ------------------------------------------------------------
# BLACKLIST
# ------------------------------------------------------------
@http.route('/mailing/blacklist/check', type='json', auth='public')
def blacklist_check(self, mailing_id, res_id, email, token):
@@ -182,31 +226,3 @@ class MassMailController(http.Controller):
_("""Requested de-blacklisting via unsubscription page."""))
return True
return 'error'
@http.route('/mailing/feedback', type='json', auth='public')
def 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):
return 'unauthorized'
model = request.env[mailing.mailing_model_real]
records = model.sudo().search([('email_normalized', '=', tools.email_normalize(email))])
for record in records:
record.sudo().message_post(body=_("Feedback from %(email)s: %(feedback)s", email=email, feedback=feedback))
return bool(records)
return 'error'
@http.route('/mailing/report/unsubscribe', type='http', website=True, auth='public')
def turn_off_mailing_reports(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))
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)
if user.has_group('base.group_system'):
menu_id = request.env.ref('mass_mailing.menu_mass_mailing_global_settings').id
return request.render('mass_mailing.mailing_report_deactivated', {'menu_id': menu_id})
return request.render('mass_mailing.mailing_report_deactivated')
raise werkzeug.exceptions.NotFound()
@@ -1,7 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- Snippets' Default Images (to be replaced by themes) -->
<!-- Snippets' Default Images -->
<record id="mass_mailing.s_cover_default_image" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_cover_default_image.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_block_banner.jpg</field>
</record>
<record id="mass_mailing.s_media_list_default_image_1" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_media_list_default_image_1.jpg</field>
@@ -128,6 +134,18 @@
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_blockquote_cover.jpg</field>
</record>
<record id="mass_mailing.s_image_text_default_image" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_image_text_default_image.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_block_image_text.jpg</field>
</record>
<record id="mass_mailing.s_mail_block_event_default_image" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_mail_block_event_default_image.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_block_event.jpg</field>
</record>
<record id="mass_mailing.s_masonry_block_default_image_1" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_masonry_block_default_image_1.jpg</field>
@@ -140,5 +158,35 @@
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_cover.jpg</field>
</record>
<record id="mass_mailing.s_picture_default_image" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_picture_default_image.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_block_image.jpg</field>
</record>
<record id="mass_mailing.s_text_image_default_image" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_text_image_default_image.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_block_text_image.jpg</field>
</record>
<record id="mass_mailing.s_three_columns_default_image_1" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_three_columns_default_image_1.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_block_three_cols_1.jpg</field>
</record>
<record id="mass_mailing.s_three_columns_default_image_2" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_three_columns_default_image_2.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_block_three_cols_2.jpg</field>
</record>
<record id="mass_mailing.s_three_columns_default_image_3" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_three_columns_default_image_3.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_block_three_cols_3.jpg</field>
</record>
</data>
</odoo>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- Enable Mass Mailing Reports -->
<function model="ir.config_parameter"
name="set_param"
eval="('mass_mailing.mass_mailing_reports', 'True')"/>
</data>
</odoo>
+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- Cron that processes the mass mailing queue -->
<record id="ir_cron_mass_mailing_queue" model="ir.cron">
<field name="name">Mail Marketing: Process queue</field>
<field name="model_id" ref="model_mailing_mailing"/>
<field name="state">code</field>
<field name="code">model._process_mass_mailing_queue()</field>
<field name="user_id" ref="base.user_root" />
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall" />
</record>
<!-- Cron that processes the a/b testing -->
<record id="ir_cron_mass_mailing_ab_testing" model="ir.cron">
<field name="name">Mail Marketing: A/B Testing</field>
<field name="model_id" ref="model_utm_campaign"/>
<field name="state">code</field>
<field name="code">model._cron_process_mass_mailing_ab_testing()</field>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="doall" eval="True"/>
</record>
</data>
</odoo>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="0">
<record model="ir.module.category" id="base.module_category_marketing_email_marketing">
<field name="sequence">19</field>
<field name="description">Helps you manage your mass mailing to design
professional emails and reuse templates.</field>
</record>
</data>
</odoo>
-23
View File
@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data noupdate="1">
<!--
Reference: https://litmus.com/community/learning/24-how-to-code-a-responsive-email-from-scratch
https://www.campaignmonitor.com/css/link-element/link-in-head/
-->
<template id="mass_mailing_mail_layout">
&lt;!DOCTYPE html&gt;
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="format-detection" content="telephone=no"/>
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=no;"/>
<meta http-equiv="X-UA-Compatible" content="IE=9; IE=8; IE=7; IE=EDGE" />
<t t-call="mass_mailing.mass_mailing_mail_style"/>
</head>
<body>
<t t-out="body"/>
</body>
</html>
</template>
</data></odoo>
@@ -1,5 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!--
Reference: https://litmus.com/community/learning/24-how-to-code-a-responsive-email-from-scratch
https://www.campaignmonitor.com/css/link-element/link-in-head/
-->
<template id="mass_mailing_mail_layout">
&lt;!DOCTYPE html&gt;
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="format-detection" content="telephone=no"/>
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=no;"/>
<meta http-equiv="X-UA-Compatible" content="IE=9; IE=8; IE=7; IE=EDGE" />
<t t-call="mass_mailing.mass_mailing_mail_style"/>
</head>
<body>
<t t-out="body"/>
</body>
</html>
</template>
<template id="mass_mailing_mail_style">
<style>
.o_layout * {
box-sizing: border-box !important;
}
.o_layout :not(.fa) {
font-family: Arial,Helvetica Neue,Helvetica,sans-serif;
}
/* Remove space around the email design. */
html,
body {
margin: 0 auto !important;
padding: 0 !important;
height: 100% !important;
width: 100% !important;
}
/* Stop Outlook resizing small text. */
* {
-ms-text-size-adjust: 100%;
}
/* Stop Outlook from adding extra spacing to tables. */
table,
td {
mso-table-lspace: 0pt !important;
mso-table-rspace: 0pt !important;
}
/* Use a better rendering method when resizing images in Outlook IE. */
img {
-ms-interpolation-mode:bicubic;
}
/* Prevent Windows 10 Mail from underlining links. Styles for underlined links should be inline. */
a {
text-decoration: none;
}
@media screen and (max-width: 768px) {
.o_mail_table_styles {
width: 100% !important;
}
.s_header_social table td, .s_header_text_social table td, .s_header_logo table td,
.o_mail_block_footer_social td,
.s_showcase td,
.s_mail_product_list td,
.s_references td {
text-align: center !important;
}
}
@media screen and (max-width: 1135px) {
td {
max-width: inherit !important;
}
td > img:only-child {
min-width: 100% !important;
}
.o_desktop_h100 {
height: unset !important;
}
}
</style>
</template>
</data>
<data noupdate="0">
<template id="ab_testing_description" name="Mass Mailing: A/B Test Description">
<div name="ab_testing_description" class="mb-2">
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="mailing_list_data" model="mailing.list">
<field name="name">Newsletter</field>
</record>
<record id="mass_mailing_contact_0" model="mailing.contact">
<field name="name" model="res.users" eval="obj().env.ref('base.user_admin').name"/>
<field name="email" model="res.users" eval="obj().env.ref('base.user_admin').email"/>
<field name="list_ids" eval="[(6,0,[ref('mass_mailing.mailing_list_data')])]"/>
</record>
</data>
</odoo>
@@ -1,226 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- Enable Mass Mailing Reports -->
<function model="ir.config_parameter" name="set_param" eval="('mass_mailing.mass_mailing_reports', 'True')" />
<!-- Cron that processes the mass mailing queue -->
<record id="ir_cron_mass_mailing_queue" model="ir.cron">
<field name="name">Mail Marketing: Process queue</field>
<field name="model_id" ref="model_mailing_mailing"/>
<field name="state">code</field>
<field name="code">model._process_mass_mailing_queue()</field>
<field name="user_id" ref="base.user_root" />
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall" />
</record>
<!-- Cron that processes the a/b testing -->
<record id="ir_cron_mass_mailing_ab_testing" model="ir.cron">
<field name="name">Mail Marketing: A/B Testing</field>
<field name="model_id" ref="model_utm_campaign"/>
<field name="state">code</field>
<field name="code">model._cron_process_mass_mailing_ab_testing()</field>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="doall" eval="True"/>
</record>
<record id="mailing_list_data" model="mailing.list">
<field name="name">Newsletter</field>
</record>
<record id="mass_mailing_contact_0" model="mailing.contact">
<field name="name" model="res.users" eval="obj().env.ref('base.user_admin').name"/>
<field name="email" model="res.users" eval="obj().env.ref('base.user_admin').email"/>
<field name="list_ids" eval="[(6,0,[ref('mass_mailing.mailing_list_data')])]"/>
</record>
<!-- Snippets' Default Images -->
<record id="mass_mailing.s_cover_default_image" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_cover_default_image.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_block_banner.jpg</field>
</record>
<record id="mass_mailing.s_media_list_default_image_1" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_media_list_default_image_1.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_media_list_1.jpg</field>
</record>
<record id="mass_mailing.s_media_list_default_image_2" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_media_list_default_image_2.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_media_list_2.jpg</field>
</record>
<record id="mass_mailing.s_media_list_default_image_3" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_media_list_default_image_3.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_media_list_3.jpg</field>
</record>
<record id="mass_mailing.s_company_team_image_1" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_company_team_image_1.png</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_team_member_1.png</field>
</record>
<record id="mass_mailing.s_company_team_image_2" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_company_team_image_2.png</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_team_member_2.png</field>
</record>
<record id="mass_mailing.s_company_team_image_3" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_company_team_image_3.png</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_team_member_3.png</field>
</record>
<record id="mass_mailing.s_company_team_image_4" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_company_team_image_4.png</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_team_member_4.png</field>
</record>
<record id="mass_mailing.s_reference_demo_image_1" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_reference_demo_image_1.png</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_references_1.png</field>
</record>
<record id="mass_mailing.s_reference_demo_image_2" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_reference_demo_image_2.png</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_references_2.png</field>
</record>
<record id="mass_mailing.s_reference_demo_image_3" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_reference_demo_image_3.png</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_references_3.png</field>
</record>
<record id="mass_mailing.s_reference_demo_image_4" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_reference_demo_image_4.png</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_references_4.png</field>
</record>
<record id="mass_mailing.s_reference_demo_image_5" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_reference_demo_image_5.png</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_references_5.png</field>
</record>
<record id="mass_mailing.s_reference_default_image_6" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_reference_default_image_6.png</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_references_6.png</field>
</record>
<record id="mass_mailing.s_product_list_default_image_1" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_product_list_default_image_1.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/furniture.jpg</field>
</record>
<record id="mass_mailing.s_product_list_default_image_2" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_product_list_default_image_2.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/clothes.jpg</field>
</record>
<record id="mass_mailing.s_product_list_default_image_3" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_product_list_default_image_3.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/books.jpg</field>
</record>
<record id="mass_mailing.s_product_list_default_image_4" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_product_list_default_image_4.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/essentials_oils.jpg</field>
</record>
<record id="mass_mailing.s_product_list_default_image_5" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_product_list_default_image_5.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/services.jpg</field>
</record>
<record id="mass_mailing.s_product_list_default_image_6" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_product_list_default_image_6.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/multimedia.jpg</field>
</record>
<record id="mass_mailing.s_blockquote_default_image" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_blockquote_default_image.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_team_member_2.png</field>
</record>
<record id="mass_mailing.s_blockquote_cover_default_image" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_blockquote_cover_default_image.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_blockquote_cover.jpg</field>
</record>
<record id="mass_mailing.s_image_text_default_image" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_image_text_default_image.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_block_image_text.jpg</field>
</record>
<record id="mass_mailing.s_mail_block_event_default_image" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_mail_block_event_default_image.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_block_event.jpg</field>
</record>
<record id="mass_mailing.s_masonry_block_default_image_1" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_masonry_block_default_image_1.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_masonry_block_1.jpg</field>
</record>
<record id="mass_mailing.s_masonry_block_default_image_2" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_masonry_block_default_image_2.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_cover.jpg</field>
</record>
<record id="mass_mailing.s_picture_default_image" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_picture_default_image.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_block_image.jpg</field>
</record>
<record id="mass_mailing.s_text_image_default_image" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_text_image_default_image.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_block_text_image.jpg</field>
</record>
<record id="mass_mailing.s_three_columns_default_image_1" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_three_columns_default_image_1.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_block_three_cols_1.jpg</field>
</record>
<record id="mass_mailing.s_three_columns_default_image_2" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_three_columns_default_image_2.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_block_three_cols_2.jpg</field>
</record>
<record id="mass_mailing.s_three_columns_default_image_3" model="ir.attachment">
<field name="public" eval="True"/>
<field name="name">s_three_columns_default_image_3.jpg</field>
<field name="type">url</field>
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_block_three_cols_3.jpg</field>
</record>
</data>
</odoo>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="base.default_user" model="res.users">
<field name="groups_id" eval="[(4,ref('mass_mailing.group_mass_mailing_user'))]"/>
</record>
</data>
</odoo>
+1
View File
@@ -3,6 +3,7 @@
from . import ir_model
from . import link_tracker
from . import mailing_contact_subscription
from . import mailing_contact
from . import mailing_list
from . import mailing_trace
+112 -79
View File
@@ -30,7 +30,11 @@ class MassMailing(models.Model):
A mass mailing is an occurence of sending emails. """
_name = 'mailing.mailing'
_description = 'Mass Mailing'
_inherit = ['mail.thread', 'mail.activity.mixin', 'mail.render.mixin', 'utm.source.mixin']
_inherit = ['mail.thread',
'mail.activity.mixin',
'mail.render.mixin',
'utm.source.mixin'
]
_order = 'calendar_date DESC'
_rec_name = "subject"
@@ -65,67 +69,93 @@ class MassMailing(models.Model):
return False
active = fields.Boolean(default=True, tracking=True)
subject = fields.Char('Subject', help='Subject of your Mailing', required=True, translate=False)
subject = fields.Char(
'Subject', help='Subject of your Mailing',
required=True, translate=False)
preview = fields.Char(
'Preview', translate=False,
help='Catchy preview sentence that encourages recipients to open this email.\n'
'In most inboxes, this is displayed next to the subject.\n'
'Keep it empty if you prefer the first characters of your email content to appear instead.')
email_from = fields.Char(string='Send From', required=True, store=True, readonly=False, compute='_compute_email_from',
default=lambda self: self.env.user.email_formatted)
email_from = fields.Char(
string='Send From',
compute='_compute_email_from', readonly=False, required=True, store=True,
default=lambda self: self.env.user.email_formatted)
favorite = fields.Boolean('Favorite', copy=False, tracking=True)
favorite_date = fields.Datetime('Favorite Date', help='When this mailing was added in the favorites',
store=True, copy=False, compute='_compute_favorite_date')
favorite_date = fields.Datetime(
'Favorite Date',
compute='_compute_favorite_date', store=True,
copy=False,
help='When this mailing was added in the favorites')
sent_date = fields.Datetime(string='Sent Date', copy=False)
schedule_type = fields.Selection([('now', 'Send now'), ('scheduled', 'Send on')], string='Schedule',
default='now', required=True, readonly=True,
states={'draft': [('readonly', False)], 'in_queue': [('readonly', False)]})
schedule_date = fields.Datetime(string='Scheduled for', tracking=True, readonly=True,
states={'draft': [('readonly', False)], 'in_queue': [('readonly', False)]},
compute='_compute_schedule_date', store=True, copy=True)
calendar_date = fields.Datetime('Calendar Date', compute='_compute_calendar_date', store=True, copy=False,
schedule_type = fields.Selection(
[('now', 'Send now'), ('scheduled', 'Send on')],
string='Schedule', default='now',
readonly=True, required=True,
states={'draft': [('readonly', False)], 'in_queue': [('readonly', False)]})
schedule_date = fields.Datetime(
string='Scheduled for',
compute='_compute_schedule_date', readonly=True, store=True,
copy=True, tracking=True,
states={'draft': [('readonly', False)], 'in_queue': [('readonly', False)]})
calendar_date = fields.Datetime(
'Calendar Date',
compute='_compute_calendar_date', store=True,
copy=False,
help="Date at which the mailing was or will be sent.")
# don't translate 'body_arch', the translations are only on 'body_html'
body_arch = fields.Html(string='Body', translate=False, sanitize=False)
body_html = fields.Html(string='Body converted to be sent by mail', render_engine='qweb', sanitize=False)
is_body_empty = fields.Boolean(compute="_compute_is_body_empty",
help='Technical field used to determine if the mail body is empty')
attachment_ids = fields.Many2many('ir.attachment', 'mass_mailing_ir_attachments_rel',
attachment_ids = fields.Many2many(
'ir.attachment', 'mass_mailing_ir_attachments_rel',
'mass_mailing_id', 'attachment_id', string='Attachments')
keep_archives = fields.Boolean(string='Keep Archives')
campaign_id = fields.Many2one('utm.campaign', string='UTM Campaign', index=True, ondelete='set null')
medium_id = fields.Many2one(
'utm.medium', string='Medium',
compute='_compute_medium_id', readonly=False, store=True, ondelete='restrict',
compute='_compute_medium_id', readonly=False, store=True,
ondelete='restrict',
help="UTM Medium: delivery method (email, sms, ...)")
state = fields.Selection([('draft', 'Draft'), ('in_queue', 'In Queue'), ('sending', 'Sending'), ('done', 'Sent')],
string='Status', required=True, tracking=True, copy=False, default='draft', group_expand='_group_expand_states')
state = fields.Selection(
[('draft', 'Draft'), ('in_queue', 'In Queue'),
('sending', 'Sending'), ('done', 'Sent')],
string='Status',
default='draft', required=True,
copy=False, tracking=True,
group_expand='_group_expand_states')
color = fields.Integer(string='Color Index')
user_id = fields.Many2one('res.users', string='Responsible', tracking=True, default=lambda self: self.env.user)
user_id = fields.Many2one(
'res.users', string='Responsible',
tracking=True,
default=lambda self: self.env.user)
# mailing options
mailing_type = fields.Selection([('mail', 'Email')], string="Mailing Type", default="mail", required=True)
mailing_type_description = fields.Char('Mailing Type Description', compute="_compute_mailing_type_description")
reply_to_mode = fields.Selection([
('update', 'Recipient Followers'), ('new', 'Specified Email Address')],
string='Reply-To Mode', compute='_compute_reply_to_mode',
readonly=False, store=True,
reply_to_mode = fields.Selection(
[('update', 'Recipient Followers'), ('new', 'Specified Email Address')],
string='Reply-To Mode',
compute='_compute_reply_to_mode', readonly=False, store=True,
help='Thread: replies go to target document. Email: replies are routed to a given email.')
reply_to = fields.Char(
string='Reply To', compute='_compute_reply_to', readonly=False, store=True,
string='Reply To',
compute='_compute_reply_to', readonly=False, store=True,
help='Preferred Reply-To Address')
# recipients
mailing_model_real = fields.Char(string='Recipients Real Model', compute='_compute_mailing_model_real')
mailing_model_real = fields.Char(
string='Recipients Real Model', compute='_compute_mailing_model_real')
mailing_model_id = fields.Many2one(
'ir.model', string='Recipients Model', ondelete='cascade', required=True,
'ir.model', string='Recipients Model',
ondelete='cascade', required=True,
domain=[('is_mailing_enabled', '=', True)],
default=lambda self: self.env.ref('mass_mailing.model_mailing_list').id)
mailing_model_name = fields.Char(
string='Recipients Model Name', related='mailing_model_id.model',
readonly=True, related_sudo=True)
string='Recipients Model Name',
related='mailing_model_id.model', readonly=True, related_sudo=True)
mailing_domain = fields.Char(
string='Domain', compute='_compute_mailing_domain',
readonly=False, store=True)
string='Domain',
compute='_compute_mailing_domain', readonly=False, store=True)
mail_server_available = fields.Boolean(
compute='_compute_mail_server_available',
help="Technical field used to know if the user has activated the outgoing mail server option in the settings")
@@ -136,25 +166,30 @@ class MassMailing(models.Model):
# Mailing Filter
mailing_filter_id = fields.Many2one(
'mailing.filter', string='Favorite Filter',
domain="[('mailing_model_name', '=', mailing_model_name)]",
compute='_compute_mailing_filter_id', readonly=False, store=True)
compute='_compute_mailing_filter_id', readonly=False, store=True,
domain="[('mailing_model_name', '=', mailing_model_name)]")
mailing_filter_domain = fields.Char('Favorite filter domain', related='mailing_filter_id.mailing_domain')
mailing_filter_count = fields.Integer('# Favorite Filters', compute='_compute_mailing_filter_count')
# A/B Testing
ab_testing_completed = fields.Boolean(related='campaign_id.ab_testing_completed', store=True)
ab_testing_description = fields.Html('A/B Testing Description', compute="_compute_ab_testing_description")
ab_testing_enabled = fields.Boolean(string='Allow A/B Testing', default=False,
ab_testing_enabled = fields.Boolean(
string='Allow A/B Testing', default=False,
help='If checked, recipients will be mailed only once for the whole campaign. '
'This lets you send different mailings to randomly selected recipients and test '
'the effectiveness of the mailings, without causing duplicate messages.')
ab_testing_mailings_count = fields.Integer(related="campaign_id.ab_testing_mailings_count")
ab_testing_pc = fields.Integer(string='A/B Testing percentage',
help='Percentage of the contacts that will be mailed. Recipients will be chosen randomly.', default=10)
ab_testing_schedule_datetime = fields.Datetime(related="campaign_id.ab_testing_schedule_datetime", readonly=False,
ab_testing_pc = fields.Integer(
string='A/B Testing percentage',
default=10,
help='Percentage of the contacts that will be mailed. Recipients will be chosen randomly.')
ab_testing_schedule_datetime = fields.Datetime(
related="campaign_id.ab_testing_schedule_datetime", readonly=False,
default=lambda self: fields.Datetime.now() + relativedelta(days=1))
ab_testing_winner_selection = fields.Selection(related="campaign_id.ab_testing_winner_selection",
default="opened_ratio", readonly=False, copy=True)
ab_testing_winner_selection = fields.Selection(
related="campaign_id.ab_testing_winner_selection", readonly=False,
default="opened_ratio",
copy=True)
kpi_mail_required = fields.Boolean('KPI mail required', copy=False)
# statistics data
mailing_trace_ids = fields.One2many('mailing.trace', 'mass_mailing_id', string='Emails Statistics')
@@ -345,7 +380,7 @@ class MassMailing(models.Model):
@api.depends('mailing_model_id')
def _compute_mailing_model_real(self):
for mailing in self:
mailing.mailing_model_real = (mailing.mailing_model_id.model != 'mailing.list') and mailing.mailing_model_id.model or 'mailing.contact'
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', 'contact_list_ids', 'mailing_type', 'mailing_filter_id')
def _compute_mailing_domain(self):
@@ -552,10 +587,9 @@ class MassMailing(models.Model):
self.ensure_one()
if self.schedule_date and self.schedule_date > fields.Datetime.now():
return self.action_put_in_queue()
else:
action = self.env["ir.actions.actions"]._for_xml_id("mass_mailing.mailing_mailing_schedule_date_action")
action['context'] = dict(self.env.context, default_mass_mailing_id=self.id)
return action
action = self.env["ir.actions.actions"]._for_xml_id("mass_mailing.mailing_mailing_schedule_date_action")
action['context'] = dict(self.env.context, default_mass_mailing_id=self.id)
return action
def action_put_in_queue(self):
self.write({'state': 'in_queue'})
@@ -684,6 +718,37 @@ class MassMailing(models.Model):
action['domain'] = [('list_ids', 'in', self.contact_list_ids.ids)]
return action
@api.model
def action_fetch_favorites(self, extra_domain=None):
"""Return all mailings set as favorite and skip mailings with empty body.
Return archived mailing templates as well, so the user can archive the templates
while keeping using it, without cluttering the Kanban view if they're a lot of
templates.
"""
domain = [('favorite', '=', True)]
if extra_domain:
domain = expression.AND([domain, extra_domain])
values_list = self.with_context(active_test=False).search_read(
domain=domain,
fields=['id', 'subject', 'body_arch', 'user_id', 'mailing_model_id'],
order='favorite_date DESC',
)
values_list = [
values for values in values_list
if not tools.is_html_empty(values['body_arch'])
]
# You see first the mailings without responsible, then your mailings and then the others
values_list.sort(
key=lambda values:
values['user_id'][0] != self.env.user.id if values['user_id'] else -1
)
return values_list
def update_opt_out(self, email, list_ids, value):
if len(list_ids) > 0:
model = self.env['mailing.contact'].with_context(active_test=False)
@@ -1173,8 +1238,8 @@ class MassMailing(models.Model):
kpi,
{
'kpi_fullname': _('Business Benefits on %(expected)i %(mailing_type)s Sent',
expected=self.expected,
mailing_type=mailing_type
expected=self.expected,
mailing_type=mailing_type
),
'kpi_action': None,
'kpi_col1': {},
@@ -1188,8 +1253,7 @@ class MassMailing(models.Model):
}
def _get_pretty_mailing_type(self):
if self.mailing_type == 'mail':
return _('Emails')
return _('Emails')
def _get_unsubscribe_token(self, user_id):
"""Generate a secure hash for this user. It allows to opt out from
@@ -1268,34 +1332,3 @@ class MassMailing(models.Model):
return lxml.html.tostring(root, encoding='unicode')
return body_html
@api.model
def action_fetch_favorites(self, extra_domain=None):
"""Return all mailings set as favorite and skip mailings with empty body.
Return archived mailing templates as well, so the user can archive the templates
while keeping using it, without cluttering the Kanban view if they're a lot of
templates.
"""
domain = [('favorite', '=', True)]
if extra_domain:
domain = expression.AND([domain, extra_domain])
values_list = self.with_context(active_test=False).search_read(
domain=domain,
fields=['id', 'subject', 'body_arch', 'user_id', 'mailing_model_id'],
order='favorite_date DESC',
)
values_list = [
values for values in values_list
if not tools.is_html_empty(values['body_arch'])
]
# You see first the mailings without responsible, then your mailings and then the others
values_list.sort(
key=lambda values:
values['user_id'][0] != self.env.user.id if values['user_id'] else -1
)
return values_list
+11 -50
View File
@@ -6,47 +6,6 @@ from odoo.exceptions import UserError
from odoo.osv import expression
class MassMailingContactListRel(models.Model):
""" Intermediate model between mass mailing list and mass mailing contact
Indicates if a contact is opted out for a particular list
"""
_name = 'mailing.contact.subscription'
_description = 'Mass Mailing Subscription Information'
_table = 'mailing_contact_list_rel'
_rec_name = 'contact_id'
_order = 'list_id DESC, contact_id DESC'
contact_id = fields.Many2one('mailing.contact', string='Contact', ondelete='cascade', required=True)
list_id = fields.Many2one('mailing.list', string='Mailing List', ondelete='cascade', required=True)
opt_out = fields.Boolean(string='Opt Out',
help='The contact has chosen not to receive mails anymore from this list', default=False)
unsubscription_date = fields.Datetime(string='Unsubscription Date')
message_bounce = fields.Integer(related='contact_id.message_bounce', store=False, readonly=False)
is_blacklisted = fields.Boolean(related='contact_id.is_blacklisted', store=False, readonly=False)
_sql_constraints = [
('unique_contact_list', 'unique (contact_id, list_id)',
'A mailing contact cannot subscribe to the same mailing list multiple times.')
]
@api.model_create_multi
def create(self, vals_list):
now = fields.Datetime.now()
for vals in vals_list:
if 'opt_out' in vals and 'unsubscription_date' not in vals:
vals['unsubscription_date'] = now if vals['opt_out'] else False
if vals.get('unsubscription_date'):
vals['opt_out'] = True
return super().create(vals_list)
def write(self, vals):
if 'opt_out' in vals and 'unsubscription_date' not in vals:
vals['unsubscription_date'] = fields.Datetime.now() if vals['opt_out'] else False
if vals.get('unsubscription_date'):
vals['opt_out'] = True
return super(MassMailingContactListRel, self).write(vals)
class MassMailingContact(models.Model):
"""Model of a contact. This model is different from the partner model
because it holds only some basic information: name, email. The purpose is to
@@ -58,12 +17,12 @@ class MassMailingContact(models.Model):
_order = 'email'
_mailing_enabled = True
def default_get(self, fields):
def default_get(self, fields_list):
""" When coming from a mailing list we may have a default_list_ids context
key. We should use it to create subscription_list_ids default value that
are displayed to the user as list_ids is not displayed on form view. """
res = super(MassMailingContact, self).default_get(fields)
if 'subscription_list_ids' in fields and not res.get('subscription_list_ids'):
res = super(MassMailingContact, self).default_get(fields_list)
if 'subscription_list_ids' in fields_list and not res.get('subscription_list_ids'):
list_ids = self.env.context.get('default_list_ids')
if 'default_list_ids' not in res and list_ids and isinstance(list_ids, (list, tuple)):
res['subscription_list_ids'] = [
@@ -77,12 +36,15 @@ class MassMailingContact(models.Model):
list_ids = fields.Many2many(
'mailing.list', 'mailing_contact_list_rel',
'contact_id', 'list_id', string='Mailing Lists')
subscription_list_ids = fields.One2many('mailing.contact.subscription', 'contact_id', string='Subscription Information')
subscription_list_ids = fields.One2many(
'mailing.contact.subscription', 'contact_id', string='Subscription Information')
country_id = fields.Many2one('res.country', string='Country')
tag_ids = fields.Many2many('res.partner.category', string='Tags')
opt_out = fields.Boolean('Opt Out', compute='_compute_opt_out', search='_search_opt_out',
help='Opt out flag for a specific mailing list.'
'This field should not be used in a view without a unique and active mailing list context.')
opt_out = fields.Boolean(
'Opt Out',
compute='_compute_opt_out', search='_search_opt_out',
help='Opt out flag for a specific mailing list. '
'This field should not be used in a view without a unique and active mailing list context.')
@api.model
def _search_opt_out(self, operator, value):
@@ -97,8 +59,7 @@ class MassMailingContact(models.Model):
[active_list_id] = self._context['default_list_ids']
contacts = self.env['mailing.contact.subscription'].search([('list_id', '=', active_list_id)])
return [('id', 'in', [record.contact_id.id for record in contacts if record.opt_out == value])]
else:
return expression.FALSE_DOMAIN if value else expression.TRUE_DOMAIN
return expression.FALSE_DOMAIN if value else expression.TRUE_DOMAIN
@api.depends('subscription_list_ids')
@api.depends_context('default_list_ids')
@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class MassMailingContactListRel(models.Model):
""" Intermediate model between mass mailing list and mass mailing contact
Indicates if a contact is opted out for a particular list
"""
_name = 'mailing.contact.subscription'
_description = 'Mass Mailing Subscription Information'
_table = 'mailing_contact_list_rel'
_rec_name = 'contact_id'
_order = 'list_id DESC, contact_id DESC'
contact_id = fields.Many2one('mailing.contact', string='Contact', ondelete='cascade', required=True)
list_id = fields.Many2one('mailing.list', string='Mailing List', ondelete='cascade', required=True)
opt_out = fields.Boolean(
string='Opt Out',
default=False,
help='The contact has chosen not to receive mails anymore from this list')
unsubscription_date = fields.Datetime(string='Unsubscription Date')
message_bounce = fields.Integer(related='contact_id.message_bounce', store=False, readonly=False)
is_blacklisted = fields.Boolean(related='contact_id.is_blacklisted', store=False, readonly=False)
_sql_constraints = [
('unique_contact_list', 'unique (contact_id, list_id)',
'A mailing contact cannot subscribe to the same mailing list multiple times.')
]
@api.model_create_multi
def create(self, vals_list):
now = fields.Datetime.now()
for vals in vals_list:
if 'opt_out' in vals and 'unsubscription_date' not in vals:
vals['unsubscription_date'] = now if vals['opt_out'] else False
if vals.get('unsubscription_date'):
vals['opt_out'] = True
return super().create(vals_list)
def write(self, vals):
if 'opt_out' in vals and 'unsubscription_date' not in vals:
vals['unsubscription_date'] = fields.Datetime.now() if vals['opt_out'] else False
if vals.get('unsubscription_date'):
vals['opt_out'] = True
return super(MassMailingContactListRel, self).write(vals)
+10 -6
View File
@@ -28,13 +28,17 @@ class MassMailingList(models.Model):
'mailing.contact', 'mailing_contact_list_rel', 'list_id', 'contact_id',
string='Mailing Lists', copy=False)
mailing_count = fields.Integer(compute="_compute_mailing_list_count", string="Number of Mailing")
mailing_ids = fields.Many2many('mailing.mailing', 'mail_mass_mailing_list_rel', string='Mass Mailings', copy=False)
mailing_ids = fields.Many2many(
'mailing.mailing', 'mail_mass_mailing_list_rel',
string='Mass Mailings', copy=False)
subscription_ids = fields.One2many(
'mailing.contact.subscription', 'list_id', string='Subscription Information',
'mailing.contact.subscription', 'list_id',
string='Subscription Information',
copy=True, depends=['contact_ids'])
is_public = fields.Boolean(default=True, string='Show In Preferences',
help='The mailing list can be accessible by recipient in the unsubscription'
' page to allows them to update their subscription preferences.')
is_public = fields.Boolean(
string='Show In Preferences', default=True,
help='The mailing list can be accessible by recipients in the subscription '
'management page to allows them to update their preferences.')
# ------------------------------------------------------
# COMPUTE / ONCHANGE
@@ -318,7 +322,7 @@ class MassMailingList(models.Model):
if mass_mailing.id not in contact_counts:
contact_counts[mass_mailing.id] = {
field: 0
for field in self._get_contact_statistics_fields().keys()
for field in mass_mailing._get_contact_statistics_fields()
}
return contact_counts
@@ -7,15 +7,25 @@ from odoo import api, fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
group_mass_mailing_campaign = fields.Boolean(string="Mailing Campaigns", implied_group='mass_mailing.group_mass_mailing_campaign', help="""This is useful if your marketing campaigns are composed of several emails""")
mass_mailing_outgoing_mail_server = fields.Boolean(string="Dedicated Server", config_parameter='mass_mailing.outgoing_mail_server',
group_mass_mailing_campaign = fields.Boolean(
string="Mailing Campaigns",
implied_group='mass_mailing.group_mass_mailing_campaign',
help="""This is useful if your marketing campaigns are composed of several emails""")
mass_mailing_outgoing_mail_server = fields.Boolean(
string="Dedicated Server",
config_parameter='mass_mailing.outgoing_mail_server',
help='Use a specific mail server in priority. Otherwise Odoo relies on the first outgoing mail server available (based on their sequencing) as it does for normal mails.')
mass_mailing_mail_server_id = fields.Many2one('ir.mail_server', string='Mail Server', config_parameter='mass_mailing.mail_server_id')
show_blacklist_buttons = fields.Boolean(string="Blacklist Option when Unsubscribing",
config_parameter='mass_mailing.show_blacklist_buttons',
help="""Allow the recipient to manage themselves their state in the blacklist via the unsubscription page.""")
mass_mailing_reports = fields.Boolean(string='24H Stat Mailing Reports', config_parameter='mass_mailing.mass_mailing_reports',
help='Check how well your mailing is doing a day after it has been sent.')
mass_mailing_mail_server_id = fields.Many2one(
'ir.mail_server', string='Mail Server',
config_parameter='mass_mailing.mail_server_id')
show_blacklist_buttons = fields.Boolean(
string="Blacklist Option when Unsubscribing",
config_parameter='mass_mailing.show_blacklist_buttons',
help="""Allow the recipient to manage themselves their state in the blacklist via the unsubscription page.""")
mass_mailing_reports = fields.Boolean(
string='24H Stat Mailing Reports',
config_parameter='mass_mailing.mass_mailing_reports',
help='Check how well your mailing is doing a day after it has been sent.')
@api.onchange('mass_mailing_outgoing_mail_server')
def _onchange_mass_mailing_outgoing_mail_server(self):
@@ -95,8 +95,4 @@
</p>
</field>
</record>
<menuitem name="Reporting" id="menu_mass_mailing_report" sequence="99"
parent="mass_mailing_menu_root"
action="mailing_trace_report_action_mail"/>
</odoo>
@@ -1,11 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.module.category" id="base.module_category_marketing_email_marketing">
<field name="sequence">19</field>
<field name="description">Helps you manage your mass mailing to design
professional emails and reuse templates.</field>
</record>
<record id="group_mass_mailing_user" model="res.groups">
<field name="name">User</field>
<field name="category_id" ref="base.module_category_marketing_email_marketing"/>
@@ -20,10 +14,6 @@ professional emails and reuse templates.</field>
</record>
<data noupdate="1">
<record id="base.default_user" model="res.users">
<field name="groups_id" eval="[(4,ref('mass_mailing.group_mass_mailing_user'))]"/>
</record>
<record id="group_mass_mailing_user" model="res.groups">
<field name="implied_ids" eval="[(4, ref('mail.group_mail_template_editor'))]"/>
</record>
+1 -66
View File
@@ -16,69 +16,4 @@
<link rel="stylesheet" type="text/scss" href="/mass_mailing/static/src/css/basic_theme_readonly.css"/>
<t t-call="mass_mailing.mass_mailing_mail_style"/>
</template>
<template id="mass_mailing_mail_style">
<style>
.o_layout * {
box-sizing: border-box !important;
}
.o_layout :not(.fa) {
font-family: Arial,Helvetica Neue,Helvetica,sans-serif;
}
/* Remove space around the email design. */
html,
body {
margin: 0 auto !important;
padding: 0 !important;
height: 100% !important;
width: 100% !important;
}
/* Stop Outlook resizing small text. */
* {
-ms-text-size-adjust: 100%;
}
/* Stop Outlook from adding extra spacing to tables. */
table,
td {
mso-table-lspace: 0pt !important;
mso-table-rspace: 0pt !important;
}
/* Use a better rendering method when resizing images in Outlook IE. */
img {
-ms-interpolation-mode:bicubic;
}
/* Prevent Windows 10 Mail from underlining links. Styles for underlined links should be inline. */
a {
text-decoration: none;
}
@media screen and (max-width: 768px) {
.o_mail_table_styles {
width: 100% !important;
}
.s_header_social table td, .s_header_text_social table td, .s_header_logo table td,
.o_mail_block_footer_social td,
.s_showcase td,
.s_mail_product_list td,
.s_references td {
text-align: center !important;
}
}
@media screen and (max-width: 1135px) {
td {
max-width: inherit !important;
}
td > img:only-child {
min-width: 100% !important;
}
.o_desktop_h100 {
height: unset !important;
}
}
</style>
</template>
</odoo>
</odoo>
@@ -78,12 +78,4 @@
</xpath>
</field>
</record>
<!-- MENU TO HANLDE LINK DATA IN MM -->
<menuitem id="link_tracker_menu_mass_mailing"
name="Link Tracker"
parent="mass_mailing_configuration"
sequence="5"
action="link_tracker.link_tracker_action"/>
</odoo>
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.ui.view" id="mailing_contact_subscription_view_form">
<field name="name">mailing.contact.subscription.view.form</field>
<field name="model">mailing.contact.subscription</field>
<field name="priority">10</field>
<field name="arch" type="xml">
<form string="Mailing List Subscription">
<sheet>
<group>
<field name="list_id"/>
<field name="is_blacklisted" invisible="1"/>
<label for="contact_id" class="oe_inline"/>
<div class="o_row o_row_readonly">
<i class="fa fa-ban text-danger" role="img" title="This email is blacklisted for mass mailings"
aria-label="Blacklisted" attrs="{'invisible': [('is_blacklisted', '=', False)]}" groups="base.group_user"></i>
<field name="contact_id"/>
</div>
<field name="unsubscription_date" readonly="1"/>
<field name="opt_out"/>
<field name="message_bounce" readonly="1"/>
</group>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="mailing_contact_subscription_view_tree">
<field name="name">mailing.contact.subscription.view.tree</field>
<field name="model">mailing.contact.subscription</field>
<field name="arch" type="xml">
<tree string="Mailing List Subscriptions">
<field name="contact_id"/>
<field name="unsubscription_date"/>
<field name="opt_out"/>
<field name="message_bounce"/>
<field name="is_blacklisted"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="mailing_contact_subscription_view_search">
<field name="name">mailing.contact.subscription.view.search</field>
<field name="model">mailing.contact.subscription</field>
<field name="arch" type="xml">
<search string="Mailing List Subscriptions">
<field name="contact_id"/>
<field name="opt_out"/>
<field name="list_id"/>
</search>
</field>
</record>
</odoo>
@@ -1,58 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- MAILING CONTACT SUBSCRIPTION -->
<record model="ir.ui.view" id="mailing_contact_subscription_view_form">
<field name="name">mailing.contact.subscription.view.form</field>
<field name="model">mailing.contact.subscription</field>
<field name="priority">10</field>
<field name="arch" type="xml">
<form string="Mailing List Subscription">
<sheet>
<group>
<field name="list_id"/>
<field name="is_blacklisted" invisible="1"/>
<label for="contact_id" class="oe_inline"/>
<div class="o_row o_row_readonly">
<i class="fa fa-ban text-danger" role="img" title="This email is blacklisted for mass mailings"
aria-label="Blacklisted" attrs="{'invisible': [('is_blacklisted', '=', False)]}" groups="base.group_user"></i>
<field name="contact_id"/>
</div>
<field name="unsubscription_date" readonly="1"/>
<field name="opt_out"/>
<field name="message_bounce" readonly="1"/>
</group>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="mailing_contact_subscription_view_tree">
<field name="name">mailing.contact.subscription.view.tree</field>
<field name="model">mailing.contact.subscription</field>
<field name="arch" type="xml">
<tree string="Mailing List Subscriptions">
<field name="contact_id"/>
<field name="unsubscription_date"/>
<field name="opt_out"/>
<field name="message_bounce"/>
<field name="is_blacklisted"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="mailing_contact_subscription_view_search">
<field name="name">mailing.contact.subscription.view.search</field>
<field name="model">mailing.contact.subscription</field>
<field name="arch" type="xml">
<search string="Mailing List Subscriptions">
<field name="contact_id"/>
<field name="opt_out"/>
<field name="list_id"/>
</search>
</field>
</record>
<record id="mailing_contact_view_search" model="ir.ui.view">
<record id="mailing_contact_view_search" model="ir.ui.view">
<field name="name">mailing.contact.view.search</field>
<field name="model">mailing.contact</field>
<field name="arch" type="xml">
@@ -241,8 +189,4 @@
</p>
</field>
</record>
<menuitem name="Mailing List Contacts" id="menu_email_mass_mailing_contacts"
parent="mass_mailing_mailing_list_menu" sequence="4"
action="action_view_mass_mailing_contacts"/>
</odoo>
@@ -293,8 +293,4 @@
</p>
</field>
</record>
<menuitem name="Mailing Lists" id="menu_email_mass_mailing_lists"
parent="mass_mailing_mailing_list_menu" sequence="3"
action="action_view_mass_mailing_lists"/>
</odoo>
@@ -718,10 +718,4 @@
<field name="res_model">mailing.mailing</field>
<field name="view_mode">form</field>
</record>
<menuitem name="Mailings" id="mass_mailing_menu"
parent="mass_mailing_menu_root"
sequence="1"
action="mailing_mailing_action_mail"/>
</odoo>
@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Marketing / Mailing -->
<menuitem name="Email Marketing" id="mass_mailing_menu_root" sequence="115" web_icon="mass_mailing,static/description/icon.svg" groups="mass_mailing.group_mass_mailing_user"/>
<menuitem name="Mailing Lists" id="mass_mailing_mailing_list_menu"
parent="mass_mailing_menu_root" sequence="2"/>
<!-- Marketing / Configuration -->
<menuitem name="Configuration" id="mass_mailing_configuration"
parent="mass_mailing_menu_root"
sequence="100"/>
<!-- Configuration / Blacklist -->
<menuitem id="mail_blacklist_mm_menu" name="Blacklisted Email Addresses"
action="mail.mail_blacklist_action"
parent="mass_mailing_configuration"/>
<!-- Configuration / Favorite Filters -->
<menuitem id="mailing_filter_menu_action"
action="mailing_filter_action"
parent="mass_mailing_configuration"/>
<!-- Technical / Mass Mailing -->
<menuitem id="mailing_mailing_menu_technical"
name="Mass Mailing"
sequence="4"
parent="base.menu_custom"/>
</odoo>
+103
View File
@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Marketing / Mailing -->
<menuitem name="Email Marketing"
id="mass_mailing_menu_root"
sequence="115"
web_icon="mass_mailing,static/description/icon.svg"
groups="mass_mailing.group_mass_mailing_user"/>
<!-- Mailings -->
<menuitem name="Mailings"
id="mass_mailing_menu"
parent="mass_mailing_menu_root"
sequence="1"
action="mailing_mailing_action_mail"/>
<!-- Mailing lists -->
<menuitem name="Mailing Lists"
id="mass_mailing_mailing_list_menu"
parent="mass_mailing_menu_root"
sequence="2"/>
<menuitem name="Mailing Lists"
id="menu_email_mass_mailing_lists"
parent="mass_mailing_mailing_list_menu"
sequence="1"
action="action_view_mass_mailing_lists"/>
<menuitem name="Mailing List Contacts"
id="menu_email_mass_mailing_contacts"
parent="mass_mailing_mailing_list_menu"
sequence="2"
action="action_view_mass_mailing_contacts"/>
<!-- Campaigns -->
<menuitem name="Campaigns"
id="menu_email_campaigns"
parent="mass_mailing_menu_root"
sequence="3"
action="action_view_utm_campaigns"
groups="mass_mailing.group_mass_mailing_campaign"/>
<!-- Reporting -->
<menuitem name="Reporting"
id="menu_mass_mailing_report"
sequence="90"
parent="mass_mailing_menu_root"
action="mailing_trace_report_action_mail"/>
<!-- Configuration -->
<menuitem name="Configuration"
id="mass_mailing_configuration"
parent="mass_mailing_menu_root"
sequence="100"/>
<!-- Configuration / Settings -->
<menuitem name="Settings"
id="menu_mass_mailing_global_settings"
parent="mass_mailing_configuration"
sequence="0"
action="action_mass_mailing_configuration"
groups="base.group_system"/>
<!-- Configuration / Campaign stages -->
<menuitem name="Campaign Stages"
id="menu_view_mass_mailing_stages"
parent="mass_mailing_configuration"
sequence="1"
groups="mass_mailing.group_mass_mailing_campaign"
action="utm.action_view_utm_stage"/>
<!-- Configuration / Utm Tags -->
<menuitem id="mass_mailing_tag_menu"
parent="mass_mailing_configuration"
action="utm.action_view_utm_tag"
sequence="2"
groups="mass_mailing.group_mass_mailing_campaign"/>
<!-- Configuration / Link trackers -->
<menuitem id="link_tracker_menu_mass_mailing"
name="Link Tracker"
parent="mass_mailing_configuration"
sequence="10"
action="link_tracker.link_tracker_action"/>
<!-- Configuration / Blacklist -->
<menuitem id="mail_blacklist_mm_menu"
name="Blacklisted Email Addresses"
action="mail.mail_blacklist_action"
parent="mass_mailing_configuration"
sequence="20"/>
<!-- Configuration / Favorite Filters -->
<menuitem id="mailing_filter_menu_action"
action="mailing_filter_action"
parent="mass_mailing_configuration"
sequence="30"/>
<!-- Technical / Mass Mailing -->
<menuitem id="mailing_mailing_menu_technical"
name="Mass Mailing"
sequence="4"
parent="base.menu_custom"/>
<menuitem id="menu_email_statistics"
name="Mailing Traces"
parent="mass_mailing.mailing_mailing_menu_technical"
sequence="2"
action="mailing_trace_action"/>
</odoo>
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- new layout for mass_mailing -->
<template id="mass_mailing.layout" name="Mass Mailing Layout">
<t t-call="web.layout">
<t t-set="head">
<t t-call-assets="web.assets_common"/>
<t t-call-assets="web.assets_backend"/>
</t>
<body class="o_white_body">
<header>
<div><title>Odoo</title></div>
<div class="text-center">
<img t-attf-src="/web/binary/company_logo?company={{ res_company.id }}"/>
</div>
</header>
<div id="wrap" class="oe_structure oe_empty"/>
<main>
<t t-out="0"/>
</main>
</body>
<xpath expr="//footer" position="replace">
<div class="container mt16 mb8">
<div class="float-end" t-ignore="true" t-if="not editable">
Create a <a target="_blank" href="https://www.odoo.com/app/website">free website</a> with
<a target="_blank" class="label label-danger" href="https://www.odoo.com/app/website">Odoo</a>
</div>
<div class="float-start text-muted" itemscope="itemscope" itemtype="https://schema.org/Organization">
<t t-call="web.debug_icon"/>
Copyright &amp;copy; <span t-field="res_company.name" itemprop="name">Company name</span>
</div>
</div>
</xpath>
</t>
</template>
</odoo>
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="mailing_report_deactivated" name="Report Unsubscribed">
<t t-call="mass_mailing.layout">
<div class="container mt8">
<div class="row">
<div class="col-lg-6 offset-lg-3">
<h3>Mailing Reports Turned Off</h3>
<div class="alert alert-success text-center" role="status">
<p>
Mailing Reports have been turned off for all users. <br/>
If needed, they can be turned back on from the
<a t-if="menu_id" t-attf-href="/web#menu_id=#{menu_id}">
Settings Menu.
</a>
<t t-else="">
Settings Menu.
</t>
</p>
</div>
</div>
</div>
</div>
</t>
</template>
<!-- Dummy layout to "view" a template content (html) -->
<template id="view" name="Browser View">
<!-- Raw body inserted here because it is a rendered mailing, therefore internal content -->
<t t-out="body"/>
</template>
</odoo>
@@ -1,29 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="mailing_report_deactivated" name="Report Unsubscribed">
<t t-call="mass_mailing.layout">
<div class="container mt8">
<div class="row">
<div class="col-lg-6 offset-lg-3">
<h3>Mailing Reports Turned Off</h3>
<div class="alert alert-success text-center" role="status">
<p>
Mailing Reports have been turned off for all users. <br/>
If needed, they can be turned back on from the
<a t-if="menu_id" t-attf-href="/web#menu_id=#{menu_id}">
Settings Menu.
</a>
<t t-else="">
Settings Menu.
</t>
</p>
</div>
</div>
</div>
</div>
</t>
</template>
<template id="unsubscribe">
<div class="container o_unsubscribe_form">
<div class="row">
@@ -121,11 +97,6 @@
</div>
</template>
<template id="view" name="Browser View">
<!-- Raw body inserted here because it is a rendered mailing, therefore internal content -->
<t t-out="body"/>
</template>
<template id="page_unsubscribe" name="Unsubscribe">
<t t-call="mass_mailing.layout">
<t t-call="mass_mailing.unsubscribe"/>
@@ -137,38 +108,4 @@
<t t-call="mass_mailing.unsubscribed"/>
</t>
</template>
<!-- new layout for mass_mailing -->
<template id="mass_mailing.layout" name="Mass Mailing Layout">
<t t-call="web.layout">
<t t-set="head">
<t t-call-assets="web.assets_common"/>
<t t-call-assets="web.assets_backend"/>
</t>
<body class="o_white_body">
<header>
<div><title>Odoo</title></div>
<div class="text-center">
<img t-attf-src="/web/binary/company_logo?company={{ res_company.id }}"/>
</div>
</header>
<div id="wrap" class="oe_structure oe_empty"/>
<main>
<t t-out="0"/>
</main>
</body>
<xpath expr="//footer" position="replace">
<div class="container mt16 mb8">
<div class="float-end" t-ignore="true" t-if="not editable">
Create a <a target="_blank" href="https://www.odoo.com/app/website">free website</a> with
<a target="_blank" class="label label-danger" href="https://www.odoo.com/app/website">Odoo</a>
</div>
<div class="float-start text-muted" itemscope="itemscope" itemtype="https://schema.org/Organization">
<t t-call="web.debug_icon"/>
Copyright &amp;copy; <span t-field="res_company.name" itemprop="name">Company name</span>
</div>
</div>
</xpath>
</t>
</template>
</odoo>
@@ -140,10 +140,4 @@
</p>
</field>
</record>
<!-- Add in Technical/Email -->
<menuitem id="menu_email_statistics"
name="Mailing Traces"
parent="mass_mailing.mailing_mailing_menu_technical" sequence="2"
action="mailing_trace_action"/>
</odoo>
@@ -78,7 +78,4 @@
<field name="target">inline</field>
<field name="context">{'module' : 'mass_mailing', 'bin_size': False}</field>
</record>
<menuitem id="menu_mass_mailing_global_settings" name="Settings"
parent="mass_mailing_configuration" sequence="0" action="action_mass_mailing_configuration" groups="base.group_system"/>
</odoo>
@@ -111,20 +111,4 @@
</field>
<field name="domain">[('is_auto_campaign', '=', False)]</field>
</record>
<menuitem name="Campaigns" id="menu_email_campaigns"
parent="mass_mailing_menu_root" sequence="5"
action="action_view_utm_campaigns"
groups="mass_mailing.group_mass_mailing_campaign"/>
<menuitem name="Campaign Stages" id="menu_view_mass_mailing_stages"
parent="mass_mailing_configuration" sequence="1"
groups="mass_mailing.group_mass_mailing_campaign"
action="utm.action_view_utm_stage"/>
<menuitem id="mass_mailing_tag_menu"
parent="mass_mailing_configuration"
action="utm.action_view_utm_tag"
sequence="2"
groups="mass_mailing.group_mass_mailing_campaign"/>
</odoo>
+2 -3
View File
@@ -15,15 +15,14 @@
'data': [
'data/utm_data.xml',
'security/ir.model.access.csv',
'views/mailing_sms_menus.xml',
'report/mailing_trace_report_views.xml',
'views/mailing_list_views.xml',
'views/mailing_contact_views.xml',
'views/mailing_trace_views.xml',
'views/mailing_mailing_views.xml',
'views/link_tracker_views.xml',
'views/mass_mailing_sms_templates_portal.xml',
'views/utm_campaign_views.xml',
'report/mailing_trace_report_views.xml',
'views/mailing_sms_menus.xml',
'wizard/sms_composer_views.xml',
'wizard/mailing_sms_test_views.xml',
],
@@ -30,11 +30,15 @@ class Mailing(models.Model):
# otherwise 'sms_subject' will get the old helper from 'mass_mailing' module.
# overriding 'subject' field helper in this model is not working, since the helper will keep the new value
# even when 'mass_mailing_sms' removed (see 'mailing_mailing_view_form_sms' for more details).
sms_subject = fields.Char('Title', help='For an email, the subject your recipients will see in their inbox.\n'
'For an SMS, the internal title of the message.',
related='subject', translate=False, readonly=False)
sms_subject = fields.Char(
'Title', related='subject',
readonly=False, translate=False,
help='For an email, the subject your recipients will see in their inbox.\n'
'For an SMS, the internal title of the message.')
# sms options
body_plaintext = fields.Text('SMS Body', compute='_compute_body_plaintext', store=True, readonly=False)
body_plaintext = fields.Text(
'SMS Body', compute='_compute_body_plaintext',
store=True, readonly=False)
sms_template_id = fields.Many2one('sms.template', string='SMS Template', ondelete='set null')
sms_has_insufficient_credit = fields.Boolean(
'Insufficient IAP credits', compute='_compute_sms_has_iap_failure',
@@ -47,7 +51,9 @@ class Mailing(models.Model):
# opt_out_link
sms_allow_unsubscribe = fields.Boolean('Include opt-out link', default=False)
# A/B Testing
ab_testing_sms_winner_selection = fields.Selection(related="campaign_id.ab_testing_sms_winner_selection", default="clicks_ratio", readonly=False, copy=True)
ab_testing_sms_winner_selection = fields.Selection(
related="campaign_id.ab_testing_sms_winner_selection",
default="clicks_ratio", readonly=False, copy=True)
@api.depends('mailing_type')
def _compute_medium_id(self):
@@ -78,7 +84,7 @@ class Mailing(models.Model):
trace_dict = dict.fromkeys(self.ids, {key: False for key in failures})
for t in traces:
trace_dict[t['mass_mailing_id'][0]][t['failure_type']] = t['__count'] and True or False
trace_dict[t['mass_mailing_id'][0]][t['failure_type']] = bool(t['__count'])
for mail in self:
mail.sms_has_insufficient_credit = trace_dict[mail.id]['sms_credit']
@@ -83,9 +83,4 @@
<field name="act_window_id" ref="mailing_trace_report_action_sms"/>
<field name="view_id" ref="mailing_trace_report_sms_view_tree"/>
</record>
<!-- SMS Marketing / Reporting -->
<record id="mass_mailing_sms_menu_reporting" model="ir.ui.menu">
<field name="action" ref="mailing_trace_report_action_sms"/>
</record>
</odoo>
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<menuitem id="link_tracker_menu"
name="Link Tracker"
parent="mass_mailing_sms_menu_configuration"
sequence="2"
action="link_tracker.link_tracker_action"/>
</odoo>
@@ -91,10 +91,4 @@
</p>
</field>
</record>
<!-- SMS Marketing / Contacts Lists / Contacts Lists -->
<record id="mass_mailing_sms.mailing_contact_menu_sms" model="ir.ui.menu">
<field name="action" ref="mailing_contact_action_sms"/>
</record>
</odoo>
@@ -42,9 +42,4 @@
</p>
</field>
</record>
<!-- SMS Marketing / Contacts Lists / Contacts Lists -->
<record id="mass_mailing_sms.mailing_list_menu_sms" model="ir.ui.menu">
<field name="action" ref="mailing_list_action_sms"/>
</record>
</odoo>
@@ -281,9 +281,4 @@
<field name="act_window_id" ref="mailing_mailing_action_sms"/>
</record>
<!-- SMS Marketing / SMS Marketing -->
<record id="mass_mailing_sms_menu_mass_sms" model="ir.ui.menu">
<field name="action" ref="mailing_mailing_action_sms"/>
</record>
</data></odoo>
@@ -10,6 +10,7 @@
<!-- SMS Marketing / SMS Marketing -->
<menuitem id="mass_mailing_sms_menu_mass_sms"
name="SMS Marketing"
action="mailing_mailing_action_sms"
parent="mass_mailing_sms_menu_root"
sequence="1"
groups="mass_mailing.group_mass_mailing_user"/>
@@ -23,19 +24,30 @@
<!-- SMS Marketing / Contacts Lists / Contacts Lists -->
<menuitem id="mailing_list_menu_sms"
name="Mailing Lists"
action="mailing_list_action_sms"
parent="mass_mailing_sms_menu_contacts"
sequence="1"
groups="mass_mailing.group_mass_mailing_user"/>
<!-- SMS Marketing / Contacts Lists / Contacts -->
<!-- SMS Marketing / Contacts Lists / Contacts -->
<menuitem id="mailing_contact_menu_sms"
name="Mailing List Contacts"
action="mailing_contact_action_sms"
parent="mass_mailing_sms_menu_contacts"
sequence="2"
groups="mass_mailing.group_mass_mailing_user"/>
<!-- SMS Markrting / Campaigns -->
<menuitem id="menu_email_campaigns"
name="Campaigns"
parent="mass_mailing_sms_menu_root"
sequence="5"
action="mass_mailing.action_view_utm_campaigns"
groups="mass_mailing.group_mass_mailing_campaign"/>
<!-- SMS Marketing / Reporting -->
<menuitem id="mass_mailing_sms_menu_reporting"
name="Reporting"
action="mailing_trace_report_action_sms"
parent="mass_mailing_sms_menu_root"
sequence="80"
groups="mass_mailing.group_mass_mailing_user"/>
@@ -55,7 +67,7 @@
groups="mass_mailing.group_mass_mailing_user"/>
<!-- SMS Marketing / Configuration / Link Tracker -->
<menuitem id="link_tracker_menu"
name="Link Tracker Blacklist"
name="Link Tracker"
parent="mass_mailing_sms_menu_configuration"
sequence="2"
action="link_tracker.link_tracker_action"
@@ -58,9 +58,4 @@
</xpath>
</field>
</record>
<menuitem name="Campaigns" id="menu_email_campaigns"
parent="mass_mailing_sms_menu_root" sequence="5"
action="mass_mailing.action_view_utm_campaigns"
groups="mass_mailing.group_mass_mailing_campaign"/>
</odoo>