mirror of
https://github.com/bringout/oca-ocb-mail.git
synced 2026-04-21 15:22:02 +02:00
19.0 vanilla
This commit is contained in:
parent
5df8c07b59
commit
daa394e8b0
2114 changed files with 564841 additions and 299642 deletions
|
|
@ -79,7 +79,7 @@ gives you a direct overview of your campaign performance.
|
|||
Fully Integrated With Others Apps
|
||||
---------------------------------
|
||||
|
||||
Define automated actions (e.g. ask a salesperson to call, send an email, ...)
|
||||
Define automation rules (e.g. ask a salesperson to call, send an email, ...)
|
||||
based on triggers (no activity since 20 days, answered a promotional email,
|
||||
etc.)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,32 +4,34 @@
|
|||
{
|
||||
'name': 'Email Marketing',
|
||||
'summary': 'Design, send and track emails',
|
||||
'version': '2.5',
|
||||
'version': '2.7',
|
||||
'sequence': 60,
|
||||
'website': 'https://www.odoo.com/app/email-marketing',
|
||||
'category': 'Marketing/Email Marketing',
|
||||
'depends': [
|
||||
'contacts',
|
||||
'mail',
|
||||
'html_builder',
|
||||
'utm',
|
||||
'link_tracker',
|
||||
'web_editor',
|
||||
'web_kanban_gauge',
|
||||
'social_media',
|
||||
'web_tour',
|
||||
'digest',
|
||||
],
|
||||
'data': [
|
||||
'data/res_groups_privilege_data.xml',
|
||||
'security/res_groups_data.xml',
|
||||
'security/security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'data/digest_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',
|
||||
'data/mailing_list_contact.xml',
|
||||
'data/mailing_subscription_optout.xml',
|
||||
'data/mailing_subscription.xml',
|
||||
'data/mass_mailing_tour.xml',
|
||||
'wizard/mail_compose_message_views.xml',
|
||||
'wizard/mailing_contact_import_views.xml',
|
||||
'wizard/mailing_contact_to_list_views.xml',
|
||||
|
|
@ -37,105 +39,145 @@
|
|||
'wizard/mailing_mailing_test_views.xml',
|
||||
'wizard/mailing_mailing_schedule_date_views.xml',
|
||||
'report/mailing_trace_report_views.xml',
|
||||
'views/mail_blacklist_views.xml',
|
||||
'views/mailing_filter_views.xml',
|
||||
'views/mailing_mobile_preview_content.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/mailing_subscription_optout_views.xml',
|
||||
'views/mailing_subscription_views.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/utm_campaign_views.xml',
|
||||
'views/mailing_menus.xml',
|
||||
'views/assets.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',
|
||||
'views/snippets/s_blockquote.xml',
|
||||
'views/snippets/s_call_to_action.xml',
|
||||
'views/snippets/s_coupon_code.xml',
|
||||
'views/snippets/s_cover.xml',
|
||||
'views/snippets/s_color_blocks_2.xml',
|
||||
'views/snippets/s_company_team.xml',
|
||||
'views/snippets/s_comparisons.xml',
|
||||
'views/snippets/s_event.xml',
|
||||
'views/snippets/s_features.xml',
|
||||
'views/snippets/s_features_grid.xml',
|
||||
'views/snippets/s_hr.xml',
|
||||
'views/snippets/s_image_text.xml',
|
||||
'views/snippets/s_masonry_block.xml',
|
||||
'views/snippets/s_media_list.xml',
|
||||
'views/snippets/s_numbers.xml',
|
||||
'views/snippets/s_picture.xml',
|
||||
'views/snippets/s_product_list.xml',
|
||||
'views/snippets/s_rating.xml',
|
||||
'views/snippets/s_references.xml',
|
||||
'views/snippets/s_showcase.xml',
|
||||
'views/snippets/s_text_block.xml',
|
||||
'views/snippets/s_text_highlight.xml',
|
||||
'views/snippets/s_text_image.xml',
|
||||
'views/snippets/s_three_columns.xml',
|
||||
'views/snippets/s_title.xml',
|
||||
'views/snippets/mass_mailing_columns_snippets.xml',
|
||||
'views/snippets/mass_mailing_footer_snippets.xml',
|
||||
'views/snippets/mass_mailing_headers_snippets.xml',
|
||||
'views/snippets/mass_mailing_headings_snippets.xml',
|
||||
'views/snippets/mass_mailing_images_snippets.xml',
|
||||
'views/snippets/mass_mailing_inner_snippets.xml',
|
||||
'views/snippets/mass_mailing_marketing_snippets.xml',
|
||||
'views/snippets/mass_mailing_masonry_snippets.xml',
|
||||
'views/snippets/mass_mailing_people_snippets.xml',
|
||||
'views/snippets/mass_mailing_text_snippets.xml',
|
||||
'views/snippets/mass_mailing_website_snippets.xml',
|
||||
],
|
||||
'demo': [
|
||||
'data/mass_mailing_demo.xml',
|
||||
'demo/utm.xml',
|
||||
'demo/mailing_list_contact.xml',
|
||||
'demo/mailing_subscription.xml',
|
||||
'demo/mailing_mailing.xml',
|
||||
'demo/mailing_trace.xml',
|
||||
'demo/res_users.xml',
|
||||
],
|
||||
'application': True,
|
||||
'assets': {
|
||||
'mass_mailing.assets_builder': [
|
||||
# lazy builder assets NOT applied in iframe
|
||||
('include', 'html_builder.assets'),
|
||||
('remove', 'web/static/fonts/fonts.scss'),
|
||||
'mass_mailing/static/src/builder/**/*',
|
||||
],
|
||||
'mass_mailing.assets_iframe_style': [
|
||||
# minimal style assets required to view the mail content
|
||||
# convert_inline ONLY uses this and inline styles.
|
||||
|
||||
# useful scss from /web web.assets_frontend
|
||||
('include', 'web._assets_helpers'),
|
||||
'web/static/src/scss/bootstrap_overridden.scss',
|
||||
('include', 'web._assets_frontend_helpers'),
|
||||
'web/static/src/scss/pre_variables.scss',
|
||||
'web/static/lib/bootstrap/scss/_variables.scss',
|
||||
'web/static/lib/bootstrap/scss/_maps.scss',
|
||||
'web/static/lib/bootstrap/scss/_alert.scss',
|
||||
('include', 'web._assets_bootstrap_frontend'),
|
||||
|
||||
# useful scss from /html_editor web.assets_frontend
|
||||
# TODO EGGMAIL: could improve load time by splitting scss from JS files
|
||||
('include', 'html_editor.assets_media_dialog'),
|
||||
('include', 'html_editor.assets_readonly'),
|
||||
'html_editor/static/src/public/**/*',
|
||||
'html_editor/static/src/scss/html_editor.common.scss',
|
||||
'html_editor/static/src/scss/html_editor.frontend.scss',
|
||||
'html_editor/static/src/scss/base_style.scss',
|
||||
|
||||
('after', 'web/static/lib/bootstrap/scss/_maps.scss', 'mass_mailing/static/src/scss/mass_mailing.ui.scss'),
|
||||
|
||||
'html_editor/static/src/scss/bootstrap_overridden.scss',
|
||||
'html_builder/static/src/scss/background.scss',
|
||||
|
||||
'web/static/src/libs/fontawesome/css/font-awesome.css',
|
||||
'web/static/lib/odoo_ui_icons/*',
|
||||
'web/static/src/scss/animation.scss',
|
||||
'web/static/src/scss/mimetypes.scss',
|
||||
'web/static/src/scss/ui.scss',
|
||||
'web/static/src/scss/fontawesome_overridden.scss',
|
||||
|
||||
('include', 'mass_mailing.assets_mail_themes'),
|
||||
'mass_mailing/static/src/scss/mass_mailing_mail.scss',
|
||||
'mass_mailing/static/src/iframe_assets/**/*',
|
||||
],
|
||||
# style assets used to view the mail content with a basic editor
|
||||
'mass_mailing.assets_inside_basic_editor_iframe': [
|
||||
('include', 'mass_mailing.assets_iframe_style'),
|
||||
('include', 'html_editor.assets_editor'),
|
||||
],
|
||||
# style assets used to view the mail content in Odoo, but not used
|
||||
# during html conversion, specific to the builder
|
||||
'mass_mailing.assets_inside_builder_iframe': [
|
||||
('include', 'mass_mailing.assets_iframe_style'),
|
||||
('include', 'html_editor.assets_editor'),
|
||||
('include', 'html_builder.assets_inside_builder_iframe'),
|
||||
'mass_mailing/static/src/builder/**/*.inside.scss'
|
||||
],
|
||||
'mass_mailing.iframe_add_dialog': [
|
||||
'mass_mailing/static/src/builder/snippet_viewer/*.scss',
|
||||
],
|
||||
'mass_mailing.mailing_assets': [
|
||||
'mass_mailing/static/src/scss/mailing_portal.scss',
|
||||
'mass_mailing/static/src/js/mailing_portal.js',
|
||||
'mass_mailing/static/src/interactions/subscribe.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.assets_backend': [
|
||||
'mass_mailing/static/src/editor/**/*',
|
||||
'mass_mailing/static/src/fields/**/*',
|
||||
'mass_mailing/static/src/themes/**/*',
|
||||
'mass_mailing/static/src/iframe/**/*',
|
||||
'mass_mailing/static/src/scss/mailing_filter_widget.scss',
|
||||
'mass_mailing/static/src/scss/mass_mailing.scss',
|
||||
'mass_mailing/static/src/scss/mass_mailing_mobile.scss',
|
||||
'mass_mailing/static/src/scss/mass_mailing_mobile_preview.scss',
|
||||
'mass_mailing/static/src/css/email_template.css',
|
||||
'mass_mailing/static/src/views/*.js',
|
||||
'mass_mailing/static/src/js/mailing_m2o_filter.js',
|
||||
'mass_mailing/static/src/js/mass_mailing.js',
|
||||
'mass_mailing/static/src/js/mass_mailing_design_constants.js',
|
||||
'mass_mailing/static/src/js/mass_mailing_mobile_preview.js',
|
||||
'mass_mailing/static/src/js/mass_mailing_html_field.js',
|
||||
'mass_mailing/static/src/js/mailing_mailing_view_form_full_width.js',
|
||||
'mass_mailing/static/src/xml/mailing_filter_widget.xml',
|
||||
'mass_mailing/static/src/xml/mass_mailing.xml',
|
||||
'mass_mailing/static/src/views/*.xml',
|
||||
'mass_mailing/static/src/js/tours/**/*',
|
||||
],
|
||||
'web.assets_backend_lazy': [
|
||||
'mass_mailing/static/src/views/mass_mailing_subscription_graph_renderer.js',
|
||||
],
|
||||
'mass_mailing.assets_mail_themes': [
|
||||
'mass_mailing/static/src/scss/themes/**/*',
|
||||
],
|
||||
'mass_mailing.assets_mail_themes_edition': [
|
||||
('include', 'web._assets_helpers'),
|
||||
'web/static/src/scss/pre_variables.scss',
|
||||
'web/static/lib/bootstrap/scss/_variables.scss',
|
||||
'mass_mailing/static/src/scss/mass_mailing.ui.scss',
|
||||
],
|
||||
'web_editor.assets_wysiwyg': [
|
||||
'mass_mailing/static/src/js/snippets.editor.js',
|
||||
'mass_mailing/static/src/js/wysiwyg.js',
|
||||
'mass_mailing/static/src/xml/mass_mailing.editor.xml',
|
||||
'mass_mailing/static/src/scss/mass_mailing.wysiwyg.scss',
|
||||
],
|
||||
'web.assets_common': [
|
||||
'mass_mailing/static/src/js/tours/**/*',
|
||||
],
|
||||
'web.assets_frontend': [
|
||||
'mass_mailing/static/src/js/tours/**/*',
|
||||
],
|
||||
'web.qunit_suite_tests': [
|
||||
'mass_mailing/static/tests/mass_mailing_favourite_filter_tests.js',
|
||||
'mass_mailing/static/src/js/mass_mailing_snippets.js',
|
||||
'mass_mailing/static/src/snippets/s_media_list/options.js',
|
||||
'mass_mailing/static/src/snippets/s_showcase/options.js',
|
||||
'mass_mailing/static/src/snippets/s_rating/options.js',
|
||||
'mass_mailing/static/tests/mass_mailing_html_tests.js',
|
||||
'mass_mailing/static/tests/mailing_mailing_view_form_tests.js',
|
||||
'web.assets_tests': [
|
||||
'mass_mailing/static/tests/tours/**/*',
|
||||
],
|
||||
'web.assets_unit_tests': [
|
||||
('include', 'mass_mailing.assets_builder'),
|
||||
'mass_mailing/static/tests/mass_mailing_favourite_filter.test.js',
|
||||
'mass_mailing/static/tests/mass_mailing_html_field.test.js',
|
||||
],
|
||||
},
|
||||
'author': 'Odoo S.A.',
|
||||
'license': 'LGPL-3',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import legacy
|
||||
from . import main
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import werkzeug
|
||||
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
class MailingLegacy(http.Controller):
|
||||
""" Retro compatibility layer for legacy endpoint"""
|
||||
|
||||
@http.route(['/mail/mailing/<int:mailing_id>/unsubscribe'], type='http', website=True, auth='public')
|
||||
def mailing_unsubscribe(self, mailing_id, email=None, res_id=None, token="", **post):
|
||||
""" Old route, using mail/mailing prefix, and outdated parameter names """
|
||||
params = werkzeug.urls.url_encode(
|
||||
dict(**post, document_id=res_id, email=email, hash_token=token)
|
||||
)
|
||||
return request.redirect(
|
||||
f'/mailing/{mailing_id}/unsubscribe?{params}'
|
||||
)
|
||||
|
|
@ -4,173 +4,391 @@
|
|||
import base64
|
||||
import urllib.parse
|
||||
|
||||
from odoo import _, exceptions, http, tools
|
||||
from datetime import timedelta
|
||||
from markupsafe import Markup, escape
|
||||
from lxml import etree
|
||||
from werkzeug.exceptions import BadRequest, NotFound, Unauthorized
|
||||
|
||||
from odoo import _, fields, http, tools
|
||||
from odoo.http import request, Response
|
||||
from odoo.tools import consteq
|
||||
from lxml import etree
|
||||
from werkzeug.exceptions import BadRequest, NotFound
|
||||
|
||||
|
||||
class MassMailController(http.Controller):
|
||||
|
||||
def _valid_unsubscribe_token(self, mailing_id, res_id, email, token):
|
||||
if not (mailing_id and res_id and email and token):
|
||||
return False
|
||||
mailing = request.env['mailing.mailing'].sudo().browse(mailing_id)
|
||||
return consteq(mailing._unsubscribe_token(res_id, email), token)
|
||||
def _check_mailing_email_token(self, mailing_id, document_id, email, hash_token,
|
||||
required_mailing_id=False):
|
||||
""" Return the mailing based on given credentials, sudo-ed. Raises if
|
||||
there is an issue fetching it.
|
||||
|
||||
def _log_blacklist_action(self, blacklist_entry, mailing_id, description):
|
||||
mailing = request.env['mailing.mailing'].sudo().browse(mailing_id)
|
||||
model_display = mailing.mailing_model_id.display_name
|
||||
blacklist_entry._message_log(body=description + " ({})".format(model_display))
|
||||
Specific use case
|
||||
* hash_token is always required for public users, no generic page is
|
||||
available for them;
|
||||
* hash_token is not required for generic page for logged user, aka
|
||||
if no mailing_id is given;
|
||||
* hash_token is not required for mailing specific page if the user
|
||||
is a mailing user;
|
||||
* hash_token is not required for generic page for logged user, aka
|
||||
if no mailing_id is given and if mailing_id is not required;
|
||||
* hash_token always requires the triplet mailing_id, email and
|
||||
document_id, as it indicates it comes from a mailing email and
|
||||
is used when comparing hashes;
|
||||
"""
|
||||
if not hash_token:
|
||||
if request.env.user._is_public():
|
||||
raise BadRequest()
|
||||
if mailing_id and not request.env.user.has_group('mass_mailing.group_mass_mailing_user'):
|
||||
raise BadRequest()
|
||||
if hash_token and (not mailing_id or not email or not document_id):
|
||||
raise BadRequest()
|
||||
if mailing_id:
|
||||
mailing_sudo = request.env['mailing.mailing'].sudo().browse(mailing_id)
|
||||
if not mailing_sudo.exists():
|
||||
raise NotFound()
|
||||
if hash_token and not consteq(mailing_sudo._generate_mailing_recipient_token(document_id, email), hash_token):
|
||||
raise Unauthorized()
|
||||
else:
|
||||
if required_mailing_id:
|
||||
raise BadRequest()
|
||||
mailing_sudo = request.env['mailing.mailing'].sudo()
|
||||
return mailing_sudo
|
||||
|
||||
def _fetch_blocklist_record(self, email):
|
||||
if not email or not tools.email_normalize(email):
|
||||
return None
|
||||
return request.env['mail.blacklist'].sudo().with_context(
|
||||
active_test=False
|
||||
).search(
|
||||
[('email', '=', tools.email_normalize(email))]
|
||||
)
|
||||
|
||||
def _fetch_contacts(self, email):
|
||||
if not email or not tools.email_normalize(email):
|
||||
return request.env['mailing.contact']
|
||||
return request.env['mailing.contact'].sudo().search(
|
||||
[('email_normalized', '=', tools.email_normalize(email))]
|
||||
)
|
||||
|
||||
def _fetch_subscription_optouts(self):
|
||||
return request.env['mailing.subscription.optout'].sudo().search([])
|
||||
|
||||
def _fetch_user_information(self, email, hash_token):
|
||||
if hash_token or request.env.user._is_public():
|
||||
return email, hash_token
|
||||
return request.env.user.email_normalized, None
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# SUBSCRIPTION MANAGEMENT
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@http.route('/mailing/my', type='http', website=True, auth='user')
|
||||
def mailing_my(self):
|
||||
email, _hash_token = self._fetch_user_information(None, None)
|
||||
if not email:
|
||||
raise Unauthorized()
|
||||
|
||||
render_values = self._prepare_mailing_subscription_values(
|
||||
request.env['mailing.mailing'], False, email, None
|
||||
)
|
||||
render_values.update(feedback_enabled=False)
|
||||
return request.render(
|
||||
'mass_mailing.page_mailing_unsubscribe',
|
||||
render_values
|
||||
)
|
||||
|
||||
# csrf is disabled here because it will be called by the MUA with unpredictable session at that time
|
||||
@http.route(['/mail/mailing/<int:mailing_id>/unsubscribe_oneclick'], type='http', website=True, auth='public',
|
||||
@http.route(['/mailing/<int:mailing_id>/unsubscribe_oneclick'], type='http', website=True, auth='public',
|
||||
methods=["POST"], csrf=False)
|
||||
def mailing_unsubscribe_oneclick(self, mailing_id, email=None, res_id=None, token="", **post):
|
||||
self.mailing(mailing_id, email=email, res_id=res_id, token=token, **post)
|
||||
def mailing_unsubscribe_oneclick(self, mailing_id, document_id=None, email=None, hash_token=None, **post):
|
||||
self.mailing_unsubscribe(mailing_id, document_id=document_id, email=email, hash_token=hash_token, **post)
|
||||
return Response(status=200)
|
||||
|
||||
@http.route('/mailing/<int:mailing_id>/confirm_unsubscribe', type='http', website=True, auth='public')
|
||||
def mailing_confirm_unsubscribe(self, mailing_id, email=None, res_id=None, token="", **post):
|
||||
def mailing_confirm_unsubscribe(self, mailing_id, document_id=None, email=None, hash_token=None):
|
||||
mailing = request.env['mailing.mailing'].sudo().browse(mailing_id)
|
||||
# Check access (note that this will also raise AccessDenied if the mailing does not exist)
|
||||
if not self._valid_unsubscribe_token(mailing_id, res_id, email, str(token)):
|
||||
raise exceptions.AccessDenied()
|
||||
# check that mailing exists/has access
|
||||
email_found, hash_token_found = self._fetch_user_information(email, hash_token)
|
||||
try:
|
||||
self._check_mailing_email_token(
|
||||
mailing_id, document_id, email_found, hash_token_found,
|
||||
required_mailing_id=True
|
||||
)
|
||||
except NotFound as e: # avoid leaking ID existence
|
||||
raise Unauthorized() from e
|
||||
|
||||
unsubscribed_str = _("Are you sure you want to unsubscribe from our mailing list?")
|
||||
unsubscribed_lists = ''
|
||||
# Display list name if list is public
|
||||
if mailing.mailing_model_real == 'mailing.contact':
|
||||
unsubscribed_lists = ', '.join(mailing_list.name for mailing_list in mailing.contact_list_ids if mailing_list.is_public)
|
||||
if unsubscribed_lists:
|
||||
unsubscribed_str = _(
|
||||
'Are you sure you want to unsubscribe from the mailing list "%(unsubscribed_lists)s"?',
|
||||
unsubscribed_lists=unsubscribed_lists
|
||||
)
|
||||
unsubscribe_btn = _("Unsubscribe")
|
||||
|
||||
template = etree.fromstring("""
|
||||
<t t-call="mass_mailing.layout">
|
||||
<div class="container o_unsubscribe_form">
|
||||
<form action="/mailing/confirm_unsubscribe" method="POST" class="col-lg-6 offset-lg-3 mt-4">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<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"/>
|
||||
<input type="hidden" name="token" t-att-value="token"/>
|
||||
<div id="info_state" class="alert alert-success">
|
||||
<div class="text-center">
|
||||
<p t-out="unsubscribed_str"/>
|
||||
<button type="submit" class="btn btn-primary" t-out="unsubscribe_btn"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</t>
|
||||
""")
|
||||
return request.env['ir.qweb']._render(template, {
|
||||
'main_object': mailing,
|
||||
'token': token,
|
||||
'email': email,
|
||||
return request.render('mass_mailing.page_mailing_confirm_unsubscribe', {
|
||||
'mailing_id': mailing_id,
|
||||
'unsubscribed_str': unsubscribed_str,
|
||||
'res_id': res_id,
|
||||
'unsubscribe_btn': unsubscribe_btn,
|
||||
'document_id': document_id,
|
||||
'email': email,
|
||||
'hash_token': hash_token,
|
||||
'unsubscribed_lists': unsubscribed_lists,
|
||||
})
|
||||
|
||||
# POST method
|
||||
# kept for backwards compatibility, must eventually be merged with mailing/<mailing_id>/unsubscribe
|
||||
@http.route('/mailing/confirm_unsubscribe', type='http', website=True, auth='public', methods=['POST'])
|
||||
def mailing_confirm_unsubscribe_post(self, mailing_id, email=None, res_id=None, token="", **post):
|
||||
url_params = urllib.parse.urlencode({'email': email, 'res_id': res_id, 'token': token})
|
||||
url = f'/mail/mailing/{int(mailing_id)}/unsubscribe?{url_params}'
|
||||
return request.redirect(url)
|
||||
def mailing_confirm_unsubscribe_post(self, mailing_id, document_id=None, email=None, hash_token=None):
|
||||
# Unsubscribe user
|
||||
email_found, hash_token_found = self._fetch_user_information(email, hash_token)
|
||||
try:
|
||||
mailing_sudo = self._check_mailing_email_token(
|
||||
int(mailing_id), document_id, email_found, hash_token_found,
|
||||
required_mailing_id=True
|
||||
)
|
||||
except NotFound as e: # fails if mailing doesn't exist or token is wrong
|
||||
raise Unauthorized() from e
|
||||
|
||||
if mailing_sudo.mailing_on_mailing_list:
|
||||
self._mailing_unsubscribe_from_list(mailing_sudo, document_id, email_found, hash_token_found)
|
||||
else:
|
||||
self._mailing_unsubscribe_from_document(mailing_sudo, document_id, email_found, hash_token_found)
|
||||
|
||||
url_params = urllib.parse.urlencode({
|
||||
'email': email,
|
||||
'document_id': document_id,
|
||||
'hash_token': hash_token,
|
||||
})
|
||||
return request.render('mass_mailing.page_mailing_has_unsubscribed', {
|
||||
'settings_url': f'/mailing/{int(mailing_id)}/unsubscribe?{url_params}',
|
||||
})
|
||||
|
||||
# todo: merge this route with /mail/mailing/confirm_unsubscribe on next minor version
|
||||
@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):
|
||||
mailing = request.env['mailing.mailing'].sudo().browse(mailing_id)
|
||||
if mailing.exists():
|
||||
res_id = res_id and int(res_id)
|
||||
if not self._valid_unsubscribe_token(mailing_id, res_id, email, str(token)):
|
||||
raise exceptions.AccessDenied()
|
||||
@http.route(['/mailing/<int:mailing_id>/unsubscribe'], type='http', website=True, auth='public')
|
||||
def mailing_unsubscribe(self, mailing_id, 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=True
|
||||
)
|
||||
except NotFound as e: # avoid leaking ID existence
|
||||
raise Unauthorized() from e
|
||||
|
||||
if mailing.mailing_model_real == 'mailing.contact':
|
||||
# Unsubscribe directly + Let the user choose their subscriptions
|
||||
mailing.update_opt_out(email, mailing.contact_list_ids.ids, True)
|
||||
if mailing_sudo.mailing_on_mailing_list:
|
||||
return self._mailing_unsubscribe_from_list(mailing_sudo, document_id, email_found, hash_token_found)
|
||||
return self._mailing_unsubscribe_from_document(mailing_sudo, document_id, email_found, hash_token_found)
|
||||
|
||||
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])
|
||||
def _mailing_unsubscribe_from_list(self, mailing, document_id, email, hash_token):
|
||||
# Unsubscribe directly + Let the user choose their subscriptions
|
||||
|
||||
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).filtered('active')
|
||||
unsubscribed_list = ', '.join(str(list.name) for list in mailing.contact_list_ids if list.is_public)
|
||||
return request.render('mass_mailing.page_unsubscribe', {
|
||||
'contacts': contacts,
|
||||
'list_ids': list_ids,
|
||||
'opt_out_list_ids': opt_out_list_ids,
|
||||
'unsubscribed_list': unsubscribed_list,
|
||||
'email': email,
|
||||
'mailing_id': mailing_id,
|
||||
'res_id': res_id,
|
||||
'show_blacklist_button': request.env['ir.config_parameter'].sudo().get_param('mass_mailing.show_blacklist_buttons'),
|
||||
})
|
||||
mailing.contact_list_ids._update_subscription_from_email(email, opt_out=True)
|
||||
# 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)
|
||||
)
|
||||
|
||||
return request.render(
|
||||
'mass_mailing.page_mailing_unsubscribe',
|
||||
dict(
|
||||
self._prepare_mailing_subscription_values(
|
||||
mailing, document_id, email, hash_token
|
||||
),
|
||||
last_action='subscription_updated',
|
||||
unsubscribed_name=lists_unsubscribed_name,
|
||||
)
|
||||
)
|
||||
|
||||
def _mailing_unsubscribe_from_document(self, mailing, document_id, email, hash_token):
|
||||
if document_id:
|
||||
message = Markup(_(
|
||||
'Blocklist request from unsubscribe link of mailing %(mailing_link)s (document %(record_link)s)',
|
||||
**self._format_bl_request(mailing, document_id)
|
||||
))
|
||||
else:
|
||||
message = Markup(_(
|
||||
'Blocklist request from unsubscribe link of mailing %(mailing_link)s (direct link usage)',
|
||||
**self._format_bl_request(mailing, document_id)
|
||||
))
|
||||
_blocklist_rec = request.env['mail.blacklist'].sudo()._add(email, message=Markup('<p>%s</p>') % 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='jsonrpc', 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='jsonrpc', 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:
|
||||
opt_in_lists = request.env['mailing.contact.subscription'].sudo().search([
|
||||
('contact_id.email_normalized', '=', email),
|
||||
('opt_out', '=', False)
|
||||
]).mapped('list_id').filtered('active')
|
||||
blacklist_rec = request.env['mail.blacklist'].sudo()._add(email)
|
||||
self._log_blacklist_action(
|
||||
blacklist_rec, mailing_id,
|
||||
_("""Requested blacklisting via unsubscribe link."""))
|
||||
return request.render('mass_mailing.page_unsubscribed', {
|
||||
'email': email,
|
||||
'mailing_id': mailing_id,
|
||||
'res_id': res_id,
|
||||
'list_ids': opt_in_lists,
|
||||
'show_blacklist_button': request.env['ir.config_parameter'].sudo().get_param(
|
||||
'mass_mailing.show_blacklist_buttons'),
|
||||
})
|
||||
return request.redirect('/web')
|
||||
author_name = email_found
|
||||
message = Markup("<p>%s<br />%s</p>") % (
|
||||
_('Feedback from %(author_name)s', author_name=author_name),
|
||||
feedback
|
||||
)
|
||||
|
||||
@http.route('/mail/mailing/unsubscribe', type='json', auth='public')
|
||||
def unsubscribe(self, mailing_id, opt_in_ids, opt_out_ids, email, res_id, token):
|
||||
mailing = request.env['mailing.mailing'].sudo().browse(mailing_id)
|
||||
if mailing.exists():
|
||||
if not self._valid_unsubscribe_token(mailing_id, res_id, email, token):
|
||||
return 'unauthorized'
|
||||
mailing.update_opt_out(email, opt_in_ids, False)
|
||||
mailing.update_opt_out(email, opt_out_ids, True)
|
||||
return True
|
||||
return 'error'
|
||||
# blocklist addition: opt-out and feedback linked to the mail.blacklist records
|
||||
if last_action == 'blocklist_add':
|
||||
mail_blocklist = self._fetch_blocklist_record(email)
|
||||
if mail_blocklist:
|
||||
if message:
|
||||
mail_blocklist._track_set_log_message(message)
|
||||
mail_blocklist.opt_out_reason_id = opt_out_reason_id
|
||||
|
||||
@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'
|
||||
# opt-outed from mailing lists (either from a mailing or directly from 'my')
|
||||
# -> in that case, update recently-updated subscription records and log on
|
||||
# contacts
|
||||
documents_for_post = []
|
||||
if (last_action in {'subscription_updated', 'subscription_updated_optout'} or
|
||||
(not last_action and (not mailing_sudo or mailing_sudo.mailing_on_mailing_list))):
|
||||
contacts = self._fetch_contacts(email_found)
|
||||
contacts.subscription_ids.filtered(
|
||||
lambda sub: sub.opt_out and sub.opt_out_datetime >= (fields.Datetime.now() - timedelta(minutes=10))
|
||||
).opt_out_reason_id = opt_out_reason_id
|
||||
if message:
|
||||
documents_for_post = contacts
|
||||
# feedback coming from a mailing, without additional context information: log
|
||||
elif mailing_sudo and message:
|
||||
documents_for_post = request.env[mailing_sudo.mailing_model_real].sudo().search(
|
||||
[('id', '=', document_id)
|
||||
])
|
||||
|
||||
for document_sudo in documents_for_post:
|
||||
document_sudo.message_post(body=message)
|
||||
|
||||
return True
|
||||
|
||||
@http.route(['/unsubscribe_from_list'], type='http', website=True, multilang=False, auth='public', sitemap=False)
|
||||
def unsubscribe_placeholder_link(self, **post):
|
||||
def mailing_unsubscribe_placeholder_link(self, **post):
|
||||
"""Dummy route so placeholder is not prefixed by language, MUST have multilang=False"""
|
||||
raise NotFound()
|
||||
return request.redirect('/mailing/my', code=301, local=True)
|
||||
|
||||
@http.route('/view', type='http', auth='user', website=True, sitemap=False)
|
||||
def mailing_view_in_browser_placeholder_link(self):
|
||||
"""Route used to give an example of what would be when the user follows the placeholder links in the mailing editor."""
|
||||
return request.render('mass_mailing.mailing_view_generic')
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# TRACKING
|
||||
|
|
@ -179,8 +397,9 @@ class MassMailController(http.Controller):
|
|||
@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. """
|
||||
if not consteq(token, tools.hmac(request.env(su=True), 'mass_mailing-mail_mail-open', mail_id)):
|
||||
raise BadRequest()
|
||||
expected_token = request.env['mail.mail']._generate_mail_recipient_token(mail_id)
|
||||
if not consteq(token, expected_token):
|
||||
raise Unauthorized()
|
||||
|
||||
request.env['mailing.trace'].sudo().set_opened(domain=[('mail_mail_id_int', 'in', [mail_id])])
|
||||
response = Response()
|
||||
|
|
@ -191,14 +410,10 @@ class MassMailController(http.Controller):
|
|||
|
||||
@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,
|
||||
country_code=request.geoip.country_code,
|
||||
mailing_trace_id=mailing_trace_id
|
||||
)
|
||||
redirect_url = request.env['link.tracker'].get_url_from_code(code)
|
||||
|
|
@ -211,86 +426,134 @@ class MassMailController(http.Controller):
|
|||
# ------------------------------------------------------------
|
||||
|
||||
@http.route('/mailing/report/unsubscribe', type='http', website=True, auth='public')
|
||||
def turn_off_mailing_reports(self, token, user_id):
|
||||
def mailing_report_deactivate(self, token, user_id):
|
||||
if not token or not user_id:
|
||||
raise 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 NotFound()
|
||||
raise BadRequest()
|
||||
user = request.env['res.users'].sudo().browse(int(user_id)).exists()
|
||||
if not user or not user.has_group('mass_mailing.group_mass_mailing_user') or \
|
||||
not consteq(token, request.env['mailing.mailing']._generate_mailing_report_token(user.id)):
|
||||
raise Unauthorized()
|
||||
|
||||
request.env['ir.config_parameter'].sudo().set_param('mass_mailing.mass_mailing_reports', False)
|
||||
render_vals = {}
|
||||
if user.has_group('base.group_system'):
|
||||
render_vals = {'menu_id': request.env.ref('mass_mailing.menu_mass_mailing_global_settings').id}
|
||||
return request.render('mass_mailing.mailing_report_deactivated', render_vals)
|
||||
|
||||
@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)
|
||||
if mailing.exists():
|
||||
res_id = int(res_id) if res_id else False
|
||||
if not self._valid_unsubscribe_token(mailing_id, res_id, email, str(token)) and not request.env.user.has_group('mass_mailing.group_mass_mailing_user'):
|
||||
raise exceptions.AccessDenied()
|
||||
def mailing_view_in_browser(self, mailing_id, email=None, document_id=None, hash_token=None, **kwargs):
|
||||
# backward compatibility: temporary for mailings sent before migation to 17
|
||||
document_id = document_id or kwargs.get('res_id')
|
||||
hash_token = hash_token or kwargs.get('token')
|
||||
try:
|
||||
mailing_sudo = self._check_mailing_email_token(
|
||||
mailing_id, document_id, email, hash_token,
|
||||
required_mailing_id=True,
|
||||
)
|
||||
except NotFound as e:
|
||||
raise Unauthorized() from e
|
||||
|
||||
html_markupsafe = mailing._render_field('body_html', [res_id])[res_id]
|
||||
# Update generic URLs (without parameters) to final ones
|
||||
html_markupsafe = html_markupsafe.replace('/unsubscribe_from_list',
|
||||
mailing._get_unsubscribe_url(email, res_id))
|
||||
# do not force lang, will simply use user context
|
||||
document_id = int(document_id) if document_id and document_id.isdigit() else 0
|
||||
html_markupsafe = mailing_sudo._render_field(
|
||||
'body_html',
|
||||
[document_id],
|
||||
compute_lang=False,
|
||||
options={'post_process': False}
|
||||
)[document_id]
|
||||
# Update generic URLs (without parameters) to final ones
|
||||
if document_id:
|
||||
html_markupsafe = html_markupsafe.replace(
|
||||
'/unsubscribe_from_list',
|
||||
mailing_sudo._get_unsubscribe_url(email, document_id)
|
||||
)
|
||||
else: # when manually trying a /view on a mailing, not through email link
|
||||
html_markupsafe = html_markupsafe.replace(
|
||||
'/unsubscribe_from_list',
|
||||
tools.urls.urljoin(
|
||||
mailing_sudo.get_base_url(),
|
||||
f'/mailing/{mailing_sudo.id}/unsubscribe',
|
||||
)
|
||||
)
|
||||
|
||||
return request.render('mass_mailing.view', {
|
||||
'body': html_markupsafe,
|
||||
})
|
||||
|
||||
return request.redirect('/web')
|
||||
return request.render(
|
||||
'mass_mailing.mailing_view',
|
||||
{
|
||||
'body': html_markupsafe,
|
||||
},
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# BLACKLIST
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@http.route('/mailing/blacklist/check', type='json', auth='public')
|
||||
def blacklist_check(self, mailing_id, res_id, email, token):
|
||||
if not self._valid_unsubscribe_token(mailing_id, res_id, email, token):
|
||||
@http.route('/mailing/blocklist/add', type='jsonrpc', auth='public')
|
||||
def mail_blocklist_add(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 email:
|
||||
record = request.env['mail.blacklist'].sudo().with_context(active_test=False).search([('email', '=', tools.email_normalize(email))])
|
||||
if record['active']:
|
||||
return True
|
||||
return False
|
||||
return 'error'
|
||||
|
||||
@http.route('/mailing/blacklist/add', type='json', auth='public')
|
||||
def blacklist_add(self, mailing_id, res_id, email, token):
|
||||
if not self._valid_unsubscribe_token(mailing_id, res_id, email, token):
|
||||
return 'unauthorized'
|
||||
if email:
|
||||
blacklist_rec = request.env['mail.blacklist'].sudo()._add(email)
|
||||
self._log_blacklist_action(
|
||||
blacklist_rec, mailing_id,
|
||||
_("""Requested blacklisting via unsubscription page."""))
|
||||
return True
|
||||
return 'error'
|
||||
if mailing_sudo:
|
||||
message = Markup(
|
||||
_(
|
||||
'Blocklist request from portal of mailing %(mailing_link)s (document %(record_link)s)',
|
||||
**self._format_bl_request(mailing_sudo, document_id)
|
||||
)
|
||||
)
|
||||
else:
|
||||
message = Markup('<p>%s</p>') % _('Blocklist request from portal')
|
||||
|
||||
@http.route('/mailing/blacklist/remove', type='json', auth='public')
|
||||
def blacklist_remove(self, mailing_id, res_id, email, token):
|
||||
if not self._valid_unsubscribe_token(mailing_id, res_id, email, token):
|
||||
_blocklist_rec = request.env['mail.blacklist'].sudo()._add(email_found, message=message)
|
||||
return True
|
||||
|
||||
@http.route('/mailing/blocklist/remove', type='jsonrpc', 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 email:
|
||||
blacklist_rec = request.env['mail.blacklist'].sudo()._remove(email)
|
||||
self._log_blacklist_action(
|
||||
blacklist_rec, mailing_id,
|
||||
_("""Requested de-blacklisting via unsubscription page."""))
|
||||
return True
|
||||
return 'error'
|
||||
|
||||
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('<p>%s</p>') % _('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'<a href="#" data-oe-model="mailing.mailing" data-oe-id="{mailing.id}">{escape(mailing.subject)}</a>'),
|
||||
'record_link': Markup(
|
||||
f'<a href="#" data-oe-model="{escape(mailing.mailing_model_real)}" data-oe-id="{int(document_id)}">{escape(mailing_model_name)}</a>'
|
||||
) if document_id else '',
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# MISCELLANEOUS
|
||||
# PREVIEW
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@http.route('/mailing/get_preview_assets', type='json', auth='user')
|
||||
def get_mobile_preview_styling(self):
|
||||
""" This route allows a rpc call to get the styling needed for email template conversion.
|
||||
We do this to avoid duplicating the template."""
|
||||
if not request.env.user.has_group('mass_mailing.group_mass_mailing_user'):
|
||||
raise NotFound
|
||||
return request.env['ir.qweb']._render('mass_mailing.iframe_css_assets_edit')
|
||||
@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", {})
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
–
|
||||
<a t-attf-href="/mailing/report/unsubscribe?token=#{mailing_report_token}&user_id=#{user_id}"
|
||||
target="_blank" style="text-decoration: none;">
|
||||
<span style="color: #8f8f8f;">Turn off Mailing Reports</span>
|
||||
<span style="color: #878d97;">Turn off Mailing Reports</span>
|
||||
</a>
|
||||
</t>
|
||||
</xpath>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="0">
|
||||
<data>
|
||||
<!-- Snippets' Default Images -->
|
||||
<record id="mass_mailing.s_cover_default_image" model="ir.attachment">
|
||||
<field name="public" eval="True"/>
|
||||
|
|
@ -28,27 +28,39 @@
|
|||
</record>
|
||||
<record id="mass_mailing.s_company_team_default_image_1" model="ir.attachment">
|
||||
<field name="public" eval="True"/>
|
||||
<field name="name">s_company_team_default_image_1.png</field>
|
||||
<field name="name">s_company_team_default_image_1.jpg</field>
|
||||
<field name="type">url</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_team_member_1.png</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_team_member_1.jpg</field>
|
||||
</record>
|
||||
<record id="mass_mailing.s_company_team_default_image_2" model="ir.attachment">
|
||||
<field name="public" eval="True"/>
|
||||
<field name="name">s_company_team_default_image_2.png</field>
|
||||
<field name="name">s_company_team_default_image_2.jpg</field>
|
||||
<field name="type">url</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_team_member_2.png</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_team_member_2.jpg</field>
|
||||
</record>
|
||||
<record id="mass_mailing.s_company_team_default_image_3" model="ir.attachment">
|
||||
<field name="public" eval="True"/>
|
||||
<field name="name">s_company_team_default_image_3.png</field>
|
||||
<field name="name">s_company_team_default_image_3.jpg</field>
|
||||
<field name="type">url</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_team_member_3.png</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_team_member_3.jpg</field>
|
||||
</record>
|
||||
<record id="mass_mailing.s_company_team_default_image_4" model="ir.attachment">
|
||||
<field name="public" eval="True"/>
|
||||
<field name="name">s_company_team_default_image_4.png</field>
|
||||
<field name="name">s_company_team_default_image_4.jpg</field>
|
||||
<field name="type">url</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_team_member_4.png</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_team_member_4.jpg</field>
|
||||
</record>
|
||||
<record id="mass_mailing.s_company_team_default_image_5" model="ir.attachment">
|
||||
<field name="public" eval="True"/>
|
||||
<field name="name">s_company_team_default_image_5.jpg</field>
|
||||
<field name="type">url</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_team_member_5.jpg</field>
|
||||
</record>
|
||||
<record id="mass_mailing.s_company_team_default_image_6" model="ir.attachment">
|
||||
<field name="public" eval="True"/>
|
||||
<field name="name">s_company_team_default_image_6.jpg</field>
|
||||
<field name="type">url</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_team_member_6.jpg</field>
|
||||
</record>
|
||||
<record id="mass_mailing.s_reference_default_image_1" model="ir.attachment">
|
||||
<field name="public" eval="True"/>
|
||||
|
|
@ -96,13 +108,13 @@
|
|||
<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/theme_default/s_default_image_team_member_2.png</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_team_member_2.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="name">s_image_text_default_image.webp</field>
|
||||
<field name="type">url</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_image_text.jpg</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_image_text.webp</field>
|
||||
</record>
|
||||
<record id="mass_mailing.s_event_default_image_1" model="ir.attachment">
|
||||
<field name="public" eval="True"/>
|
||||
|
|
@ -118,9 +130,9 @@
|
|||
</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="name">s_masonry_block_default_image_1.webp</field>
|
||||
<field name="type">url</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_masonry_block_1.jpg</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_masonry_block_1.webp</field>
|
||||
</record>
|
||||
<record id="mass_mailing.s_masonry_block_default_image_2" model="ir.attachment">
|
||||
<field name="public" eval="True"/>
|
||||
|
|
@ -136,9 +148,9 @@
|
|||
</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="name">s_text_image_default_image.webp</field>
|
||||
<field name="type">url</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_text_image.jpg</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_text_image_default_image.webp</field>
|
||||
</record>
|
||||
<record id="mass_mailing.s_three_columns_default_image_1" model="ir.attachment">
|
||||
<field name="public" eval="True"/>
|
||||
|
|
@ -158,5 +170,11 @@
|
|||
<field name="type">url</field>
|
||||
<field name="url">/mass_mailing/static/src/img/theme_default/s_default_image_three_cols_3.jpg</field>
|
||||
</record>
|
||||
<record id="mass_mailing.s_shape_image_default_image" model="ir.attachment">
|
||||
<field name="public" eval="True"/>
|
||||
<field name="name">s_shape_image_default_image.jpg</field>
|
||||
<field name="type">url</field>
|
||||
<field name="url">/mass_mailing/static/src/img/snippets_demo/s_shape_image_default_image.jpg</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -5,5 +5,9 @@
|
|||
<function model="ir.config_parameter"
|
||||
name="set_param"
|
||||
eval="('mass_mailing.mass_mailing_reports', 'True')"/>
|
||||
<!-- Display blacklist button to customers on portal page -->
|
||||
<function model="ir.config_parameter"
|
||||
name="set_param"
|
||||
eval="('mass_mailing.show_blacklist_buttons', True)"/>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@
|
|||
<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" />
|
||||
<field name="priority">6</field>
|
||||
</record>
|
||||
<!-- Cron that processes the a/b testing -->
|
||||
<record id="ir_cron_mass_mailing_ab_testing" model="ir.cron">
|
||||
|
|
@ -23,8 +22,6 @@
|
|||
<field name="active" eval="False"/>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
<?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>
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
<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"/>
|
||||
<t t-out="mailing_style"/>
|
||||
|
||||
<!--
|
||||
Prevent Outlook from distorting images with DPI scaling (see
|
||||
|
|
@ -34,150 +34,84 @@
|
|||
</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_snippet_general .container {
|
||||
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) {
|
||||
.o_stacking_wrapper {
|
||||
width: 100% !important;
|
||||
height: unset !important;
|
||||
}
|
||||
td {
|
||||
max-width: inherit !important;
|
||||
}
|
||||
img:only-child:not(.img-fluid) {
|
||||
object-fit: cover;
|
||||
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">
|
||||
<t t-if="mailing.state == 'done' and mailing.sent == 0 and mailing.failed == 0 and mailing.canceled == 0">
|
||||
<span>There wasn't enough recipients left for this mailing</span>
|
||||
</t>
|
||||
<t t-if="mailing.ab_testing_completed">
|
||||
<p t-if="mailing.ab_testing_pc == 100">
|
||||
<p t-if="mailing.ab_testing_is_winner_mailing">
|
||||
This <t t-out="mailing.mailing_type_description"/> is the winner of the A/B testing campaign and has been sent to all remaining recipients.
|
||||
</p>
|
||||
<p t-else="">The winner has already been sent. Use <b>Compare Version</b> to get an overview of this A/B testing campaign.</p>
|
||||
</t>
|
||||
<t t-elif="mailing.ab_testing_mailings_count >= 2">
|
||||
<t t-elif="ab_testing_count >= 2">
|
||||
<p>
|
||||
A sample of <b><t t-out="mailing.ab_testing_pc"/>% of recipients</b> will receive this <b><t t-out="mailing.mailing_type_description"/></b>, and <t t-out="other_ab_testing_pc"/>% receive one of the
|
||||
<b><t t-out="mailing.ab_testing_mailings_count - 1"/> other versions</b> from the same campaign.
|
||||
A sample of <b><t t-out="mailing.ab_testing_pc"/>% of recipients</b> will receive this version.<br/>
|
||||
<t t-if="total_ab_testing_pc > 100 and mailing.active">
|
||||
<i class="fa fa-exclamation-triangle text-danger" aria-hidden="true"/>
|
||||
The sum of all percentages for this A/B campaign totals <b class="text-danger"><t t-out="total_ab_testing_pc"/>%</b> of all potential recipients.<br/>
|
||||
<b class="text-danger">Some of the mailings will not be sent</b>, as only 1 email will be sent for each unique recipient in this campaign.
|
||||
</t>
|
||||
</p>
|
||||
<p>
|
||||
<t t-if="mailing.ab_testing_winner_selection == 'manual'">Don't forget to send your preferred version</t>
|
||||
<t t-elif="not mailing.ab_testing_schedule_datetime">Since the date and time for this test has not been scheduled, don't forget to manually send your preferred version.</t>
|
||||
<t t-else="">
|
||||
Then on <b><t t-out="mailing.ab_testing_schedule_datetime.strftime('%b %d, %Y')"/></b> the <t t-out="mailing.mailing_type_description"/> having the <b><t t-out="ab_testing_winner_selection_description"/></b> will be sent
|
||||
</t> to the remaining <t t-out="remaining_ab_testing_pc"/>% of recipients.
|
||||
</t> to the remaining recipients.
|
||||
</p>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<p>
|
||||
A sample of <b><t t-out="mailing.ab_testing_pc"/>% of recipients</b> will receive this <b><t t-out="mailing.mailing_type_description"/></b>.
|
||||
</p>
|
||||
<p>Try different variations in the campaign to compare their <t t-out="ab_testing_winner_selection_description"/>.</p>
|
||||
<p>
|
||||
<t t-if="mailing.ab_testing_winner_selection != 'manual'">Once the best version is identified, we will send the best one to the remaining recipients.</t>
|
||||
<t t-else="">
|
||||
The actual <t t-out="mailing.mailing_type_description"/> will be sent to the remaining recipients.
|
||||
</t>
|
||||
A sample of <b><t t-out="mailing.ab_testing_pc"/>% of recipients</b> will receive this version.
|
||||
</p>
|
||||
<t t-if="mailing.ab_testing_winner_selection != 'manual'">
|
||||
<p>Try different variations in the campaign to compare their <t t-out="ab_testing_winner_selection_description"/>.</p>
|
||||
<p>Once the best version is identified, we will send the best one to the remaining recipients.</p>
|
||||
</t>
|
||||
<p t-else="">Use alternative versions to be able to select the winner.</p>
|
||||
</t>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="mass_mailing.mass_mailing_kpi_link_trackers" name="Marketing: mailing link trackers statistic">
|
||||
<div class="global_layout" t-if="link_trackers">
|
||||
<table bgcolor="#ffffff" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="width: 100%; max-width: 650px;">
|
||||
<tr>
|
||||
<td style="width: 100%;">
|
||||
<table cellspacing="0" cellpadding="0" border="0" width="580" align="center" style="width:100%; max-width:580px;">
|
||||
<tr>
|
||||
<td align="left">
|
||||
<span style="color:#282f33; font-size: 15px; font-weight: bold; line-height: 30px">
|
||||
<t t-esc="'Click Rate Report on %i %s Sent' % (object.expected, mailing_type)"/>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="margin: 0; padding:0;">
|
||||
<table cellspacing="0" cellpadding="0" border="0" width="580" align="center" style="width:100%; max-width:580px;border-collapse: collapse;">
|
||||
<tr style="color: #875a7b; font-size: 16px; font-weight: 500;">
|
||||
<td style="width: 70%;padding: 10px 0; text-align: center; border: 1px solid #e7e7e7;">Button Label</td>
|
||||
<td style="width: 30%;padding: 10px 0; text-align: center; border: 1px solid #e7e7e7;">%Click (Total)</td>
|
||||
</tr>
|
||||
<tr t-foreach="link_trackers" t-as="link_tracker" style="color: #888888; font-size: 15px; font-weight: 300;">
|
||||
<td style="width: 70%;padding: 10px 0; border: 1px solid #e7e7e7;">
|
||||
<a t-att-href="link_tracker.absolute_url" target="_blank" style="color: #56b3b5; text-decoration: none;" t-esc="link_tracker.label or link_tracker.url"/>
|
||||
</td>
|
||||
<td style="width: 30%;padding: 10px 0; text-align: center; border: 1px solid #e7e7e7;">
|
||||
<t t-esc="int(link_tracker.count * 100 / object.sent) if object.sent else 0"/>% (<t t-esc="link_tracker.count"/>)
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<table t-if="link_trackers" class="global_table_layout" bgcolor="#ffffff" cellspacing="0" cellpadding="0" align="center" border="0" style="width: 100%;">
|
||||
<tr>
|
||||
<td style="width: 100%;">
|
||||
<table cellspacing="0" cellpadding="0" border="0" align="center" style="width:100%;">
|
||||
<tr>
|
||||
<td align="left">
|
||||
<span style="color:#282f33; font-size: 15px; font-weight: bold; line-height: 30px">
|
||||
<t t-esc="'Click Rate Report on %i %s Sent' % (object.expected, mailing_type)"/>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="margin: 0; padding:0;">
|
||||
<table cellspacing="0" cellpadding="0" border="0" align="center" style="width:100%; border-collapse: collapse;">
|
||||
<tr t-att-style="'color: ' + (company.email_secondary_color or '#875a7b') + '; font-size: 16px; font-weight: 500;'">
|
||||
<td style="width: 70%;padding: 10px 0; text-align: center; border: 1px solid #e7e7e7;">Button Label</td>
|
||||
<td style="width: 30%;padding: 10px 0; text-align: center; border: 1px solid #e7e7e7;">%Click (Total)</td>
|
||||
</tr>
|
||||
<tr t-foreach="link_trackers" t-as="link_tracker" style="color: #888888; font-size: 15px; font-weight: 300;">
|
||||
<td style="width: 70%;padding: 10px 0; border: 1px solid #e7e7e7;">
|
||||
<a t-att-href="link_tracker.absolute_url" target="_blank" style="color: #56b3b5; text-decoration: none;" t-esc="link_tracker.label or link_tracker.url"/>
|
||||
</td>
|
||||
<td style="width: 30%;padding: 10px 0; text-align: center; border: 1px solid #e7e7e7;">
|
||||
<t t-esc="int(link_tracker.count * 100 / object.sent) if object.sent else 0"/>% (<t t-esc="link_tracker.count"/>)
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</template>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@
|
|||
<data noupdate="1">
|
||||
<record id="mailing_list_data" model="mailing.list">
|
||||
<field name="name">Newsletter</field>
|
||||
<field name="is_public">True</field>
|
||||
</record>
|
||||
<record id="mass_mailing_contact_0" model="mailing.contact">
|
||||
<record id="mass_mail_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')])]"/>
|
||||
<field name="list_ids" eval="[(5, 0, 0)]"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="mailing_list_data_sub_contact_0" model="mailing.subscription">
|
||||
<field name="contact_id" ref="mass_mailing.mass_mail_contact_0"/>
|
||||
<field name="list_id" ref="mass_mailing.mailing_list_data"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data noupdate="0">
|
||||
<record id="mailing_subscription_optout_data_0" model="mailing.subscription.optout">
|
||||
<field name="name">I never subscribed to this list</field>
|
||||
<field name="sequence">1</field>
|
||||
</record>
|
||||
<record id="mailing_subscription_optout_data_1" model="mailing.subscription.optout">
|
||||
<field name="name">I changed my mind</field>
|
||||
<field name="sequence">2</field>
|
||||
</record>
|
||||
<record id="mailing_subscription_optout_data_2" model="mailing.subscription.optout">
|
||||
<field name="name">I receive too many emails from this list</field>
|
||||
<field name="sequence">3</field>
|
||||
</record>
|
||||
<record id="mailing_subscription_optout_data_3" model="mailing.subscription.optout">
|
||||
<field name="name">The content of these emails is not relevant to me</field>
|
||||
<field name="sequence">4</field>
|
||||
</record>
|
||||
<record id="mailing_subscription_optout_data_4" model="mailing.subscription.optout">
|
||||
<field name="name">Other</field>
|
||||
<field name="sequence">99</field>
|
||||
<field name="is_feedback" eval="True"/>
|
||||
</record>
|
||||
</data></odoo>
|
||||
|
|
@ -1,296 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="mass_mail_attach_1" model="ir.attachment">
|
||||
<field name="datas">bWlncmF0aW9uIHRlc3Q=</field>
|
||||
<field name="name">SampleDoc.doc</field>
|
||||
</record>
|
||||
|
||||
<!-- Create Extra Mailing List for Demo -->
|
||||
<record id="mailing_list_1" model="mailing.list">
|
||||
<field name="name">Imported Contacts</field>
|
||||
</record>
|
||||
|
||||
<!-- Create Contacts -->
|
||||
<record id="mass_mail_contact_1" model="mailing.contact">
|
||||
<field name="name">Aristide Antario</field>
|
||||
<field name="email">alexandre.antario@example.com</field>
|
||||
<field name="list_ids" eval="[(6,0,[ref('mass_mailing.mailing_list_data')])]"/>
|
||||
</record>
|
||||
<record id="mass_mail_contact_2" model="mailing.contact">
|
||||
<field name="name">Beverly Bridge</field>
|
||||
<field name="email">beverly.bridge@example.com</field>
|
||||
<field name="list_ids" eval="[(6,0,[ref('mass_mailing.mailing_list_data')])]"/>
|
||||
</record>
|
||||
<record id="mass_mail_contact_3" model="mailing.contact">
|
||||
<field name="name">Carol Cartridge</field>
|
||||
<field name="email">carol.cartridge@example.com</field>
|
||||
<field name="list_ids" eval="[(6,0,[ref('mass_mailing.mailing_list_data'),ref('mass_mailing.mailing_list_1')])]"/>
|
||||
</record>
|
||||
<record id="mass_mail_contact_4" model="mailing.contact">
|
||||
<field name="name">David Dawson</field>
|
||||
<field name="email">david.dawson@example.com</field>
|
||||
</record>
|
||||
<record id="mass_mail_contact_5" model="mailing.contact">
|
||||
<field name="name">Elsa Ericson</field>
|
||||
<field name="email">elsa.ericson@example.com</field>
|
||||
<field name="message_bounce">5</field>
|
||||
<field name="list_ids" eval="[(6,0,[ref('mass_mailing.mailing_list_data')])]"/>
|
||||
</record>
|
||||
<record id="mass_mail_contact_6" model="mailing.contact">
|
||||
<field name="name">Franz Faubourg</field>
|
||||
<field name="email">franz.faubourg@example.com</field>
|
||||
<field name="list_ids" eval="[(6,0,[ref('mass_mailing.mailing_list_1')])]"/>
|
||||
</record>
|
||||
|
||||
<!-- Create Opt-out Records -->
|
||||
<record id="mass_mail_contact_list_rel_1" model="mailing.contact.subscription">
|
||||
<field name="list_id" ref="mass_mailing.mailing_list_data"/>
|
||||
<field name="contact_id" ref="mass_mailing.mass_mail_contact_4"/>
|
||||
<field name="opt_out">True</field>
|
||||
</record>
|
||||
<record id="mass_mail_contact_list_rel_2" model="mailing.contact.subscription">
|
||||
<field name="list_id" ref="mass_mailing.mailing_list_data"/>
|
||||
<field name="contact_id" ref="mass_mailing.mass_mail_contact_6"/>
|
||||
<field name="opt_out">True</field>
|
||||
</record>
|
||||
|
||||
<!-- Create Blacklist Records -->
|
||||
<record id="blacklist_1" model="mail.blacklist">
|
||||
<field name="email">elsa.ericson@example.com</field>
|
||||
</record>
|
||||
|
||||
<!-- Create campaign and mailings -->
|
||||
<record id="utm_source_0" model="utm.source">
|
||||
<field name="name">Newsletter 1</field>
|
||||
</record>
|
||||
<record id="mass_mail_campaign_1" model="utm.campaign">
|
||||
<field name="name">Newsletter</field>
|
||||
<field name="stage_id" ref="utm.campaign_stage_1"/>
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('utm.utm_tag_1')])]"/>
|
||||
</record>
|
||||
|
||||
<record id="mass_mail_1" model="mailing.mailing">
|
||||
<field name="name">Newsletter 1</field>
|
||||
<field name="subject">Monthly Newsletter</field>
|
||||
<field name="state">done</field>
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
<field name="email_from">info@yourcompany.example.com</field>
|
||||
<field name="sent_date" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="campaign_id" ref="mass_mail_campaign_1"/>
|
||||
<field name="source_id" ref="mass_mailing.utm_source_0"/>
|
||||
<field name="mailing_model_id" ref="base.model_res_partner"/>
|
||||
<field name="mailing_domain" eval="[('parent_id', '=', ref('base.res_partner_4'))]"/>
|
||||
<field name="reply_to_mode">new</field>
|
||||
<field name="reply_to">Info <info@yourcompany.example.com></field>
|
||||
<field name="body_arch" type="html">
|
||||
<div class="o_layout o_default_theme oe_unremovable oe_unmovable" data-name="Mailing">
|
||||
<div class="container o_mail_wrapper oe_unremovable oe_unmovable" style="border-collapse:collapse;">
|
||||
<div class="row">
|
||||
<div class="col o_mail_no_options o_mail_wrapper_td bg-white oe_structure o_editable" style="text-align:left;width:100%;">
|
||||
<div class="o_mail_block_header_logo" data-snippet="s_mail_block_header_logo">
|
||||
<div class="o_mail_snippet_general" style="margin:0px auto 0px auto;background-color:rgb(255, 255, 255);max-width:600px;width:100%;">
|
||||
<div class="container o_mail_h_padding" style="padding:0 20px 0 20px;width:100%;border-collapse:separate;">
|
||||
<div class="row">
|
||||
<div valign="center" width="30%" class="col text-center o_mail_v_padding pb0" style="padding:20px 0 0px 0;vertical-align:middle;text-align:center;">
|
||||
<a href="http://www.example.com" style="text-decoration:none;font-weight:bold;background-color:transparent;color:rgb(100, 89, 116);">
|
||||
<img border="0" src="/mass_mailing/static/src/img/theme_default/s_default_image_header_logo.png" style="border-style:none;height:auto;vertical-align:middle;max-width:400px;width:auto"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_mail_block_footer_separator" data-snippet="s_hr" style="margin:0 20px 0 20px;">
|
||||
<div class="o_mail_snippet_general" style="margin:0px auto 0px auto;background-color:rgb(255, 255, 255);max-width:600px;width:100%;">
|
||||
<div class="container" style="width:100%;border-collapse:separate;">
|
||||
<div class="row">
|
||||
<div valign="top" style="padding:20px 0 20px 0;text-align:left;vertical-align:top;width:100%;" class="col o_mail_v_padding o_mail_no_colorpicker">
|
||||
<div style="background-color:rgb(245, 245, 245);height:2px;width:100%;" class="separator"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_mail_block_text" data-snippet="s_text_block">
|
||||
<div class="o_mail_snippet_general" style="margin:0px auto 0px auto;background-color:rgb(255, 255, 255);max-width:600px;width:100%;">
|
||||
<div class="container" style="width:100%;border-collapse:separate;">
|
||||
<div class="row">
|
||||
<div class="col-12 o_mail_h_padding o_mail_v_padding o_mail_no_colorpicker" style="padding:20px;text-align:left;vertical-align:top;">
|
||||
<p style="margin:0px 0 1rem 0;font-size:14px;">
|
||||
Great stories have personality. Consider telling a great story that provides personality. Writing a story with personality for potential clients will assist with making a relationship connection. This shows up in small quirks like word choices or phrases. Write from your point of view, not from someone else's experience.
|
||||
<br/>Great stories are for everyone even when only written for just one person. If you try to write with a wide general audience in mind, your story will ring false and be bland. No one will be interested. Write for one person. If it’s genuine for the one, it’s genuine for the rest.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_mail_block_footer_social o_mail_footer_social_center" data-snippet="s_mail_block_footer_social">
|
||||
<div class="o_mail_snippet_general" style="margin:0px auto 0px auto;background-color:rgb(255, 255, 255);max-width:600px;width:100%;">
|
||||
<div align="center" class="container" style="border-style:solid none none none;padding:20px 0 20px 0;border-top-color:rgb(245, 245, 245);border-top-width:2px;width:100%;border-collapse:separate;">
|
||||
<div class="row">
|
||||
<div class="col o_mail_footer_links o_default_snippet_text" style="padding:10px 0 10px 0;text-align:center;vertical-align:middle;">
|
||||
<a href="/unsubscribe_from_list" class="btn btn-link o_default_snippet_text" style="text-decoration:none;border-radius:0.25rem;border-style:solid;padding:0px;cursor:pointer;line-height:1.5;font-size:12px;border-start-color:transparent;border-bottom-color:transparent;border-end-color:transparent;border-top-color:transparent;border-start-width:1px;border-bottom-width:1px;border-end-width:1px;border-top-width:1px;user-select:none;vertical-align:middle;white-space:nowrap;text-align:center;font-weight:bold;display:inline-block;background-color:transparent;color:rgb(100, 89, 116);">Unsubscribe</a> |
|
||||
|
||||
<a href="/contactus" class="btn btn-link o_default_snippet_text" style="text-decoration:none;border-radius:0.25rem;border-style:solid;padding:0px;cursor:pointer;line-height:1.5;font-size:12px;border-start-color:transparent;border-bottom-color:transparent;border-end-color:transparent;border-top-color:transparent;border-start-width:1px;border-bottom-width:1px;border-end-width:1px;border-top-width:1px;user-select:none;vertical-align:middle;white-space:nowrap;text-align:center;font-weight:bold;display:inline-block;background-color:transparent;color:rgb(100, 89, 116);">Contact</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col" style="text-align:left;vertical-align:middle;">
|
||||
<p class="o_mail_footer_copy" style="margin:0px 0 1rem 0;text-align:center;font-weight:bold;color:rgb(147, 146, 146);font-size:9px;">
|
||||
<img src="/web_editor/font_to_img/61945/rgb(147,146,146)/9" data-class="fa fa-copyright" style="border-style:none;max-width:100%;width:100%;vertical-align:middle;height: auto; width: auto;"/>2018 All Rights Reserved
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div align="center" class="container" style="width:100%;border-collapse:separate;">
|
||||
<div class="row">
|
||||
<div align="center" style="padding:16px 0 16px 0;" class="col pt16 pb16">
|
||||
Powered by <a target="_blank" href="https://www.odoo.com" style="text-decoration:none;background-color:transparent;color:#875A7B;">Odoo</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</field>
|
||||
<field name="attachment_ids" eval="[(4, ref('mass_mail_attach_1'))]"/>
|
||||
</record>
|
||||
<!-- Generate link tracker information from it -->
|
||||
<function model="mailing.mailing" name="convert_links" eval="[ref('mass_mailing.mass_mail_1')]"/>
|
||||
|
||||
<record id="mass_mail_1_stat_0" model="mailing.trace">
|
||||
<field name="mass_mailing_id" ref="mass_mail_1"/>
|
||||
<field name="message_id">1111000@odoo.com</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_address_7"/>
|
||||
<field name="email">billy.fox45@example.com</field>
|
||||
<field name="trace_status">reply</field>
|
||||
<field name="sent_datetime" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="open_datetime" eval="(DateTime.today() - relativedelta(days=4)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="reply_datetime" eval="(DateTime.today() - relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="write_date" eval="(DateTime.today() - relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
</record>
|
||||
<record id="mass_mail_1_stat_1" model="mailing.trace">
|
||||
<field name="mass_mailing_id" ref="mass_mail_1"/>
|
||||
<field name="message_id">1111001@odoo.com</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_address_13"/>
|
||||
<field name="email">kim.snyder96@example.com</field>
|
||||
<field name="trace_status">reply</field>
|
||||
<field name="sent_datetime" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="open_datetime" eval="(DateTime.today() - relativedelta(days=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="reply_datetime" eval="(DateTime.today() - relativedelta(days=0)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="write_date" eval="(DateTime.today() - relativedelta(days=0)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
</record>
|
||||
<record id="mass_mail_1_stat_2" model="mailing.trace">
|
||||
<field name="mass_mailing_id" ref="mass_mail_1"/>
|
||||
<field name="message_id">1111002@odoo.com</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_address_14"/>
|
||||
<field name="email">edith.sanchez68@example.com</field>
|
||||
<field name="trace_status">open</field>
|
||||
<field name="sent_datetime" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="open_datetime" eval="(DateTime.today() - relativedelta(days=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="write_date" eval="(DateTime.today() - relativedelta(days=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
</record>
|
||||
<record id="mass_mail_1_stat_3" model="mailing.trace">
|
||||
<field name="mass_mailing_id" ref="mass_mail_1"/>
|
||||
<field name="message_id">1111003@odoo.com</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_address_24"/>
|
||||
<field name="email">theodore.gardner36@example.com</field>
|
||||
<field name="trace_status">open</field>
|
||||
<field name="sent_datetime" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="open_datetime" eval="(DateTime.today() - relativedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="write_date" eval="(DateTime.today() - relativedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
</record>
|
||||
<record id="mass_mail_1_stat_4" model="mailing.trace">
|
||||
<field name="mass_mailing_id" ref="mass_mail_1"/>
|
||||
<field name="message_id">1111004@odoo.com</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_address_32"/>
|
||||
<field name="email">sandra.neal80@example.com</field>
|
||||
<field name="trace_status">sent</field>
|
||||
<field name="sent_datetime" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="write_date" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
</record>
|
||||
<record id="mass_mail_1_stat_5" model="mailing.trace">
|
||||
<field name="mass_mailing_id" ref="mass_mail_1"/>
|
||||
<field name="message_id">1111005@odoo.com</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_address_33"/>
|
||||
<field name="email">julie.richards84@example.com</field>
|
||||
<field name="trace_status">error</field>
|
||||
<field name="sent_datetime" eval="False"/>
|
||||
</record>
|
||||
<record id="mass_mail_1_stat_6" model="mailing.trace">
|
||||
<field name="mass_mailing_id" ref="mass_mail_1"/>
|
||||
<field name="message_id">1111006@odoo.com</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_address_34"/>
|
||||
<field name="email">travis.mendoza24@example.com</field>
|
||||
<field name="trace_status">bounce</field>
|
||||
<field name="sent_datetime" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="write_date" eval="(DateTime.today() - relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
</record>
|
||||
<record id="mass_mail_1_stat_7" model="mailing.trace">
|
||||
<field name="mass_mailing_id" ref="mass_mail_1"/>
|
||||
<field name="message_id">1111007@odoo.com</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_address_34"/>
|
||||
<field name="email">travis.mendoza24@example.com</field>
|
||||
<field name="trace_status">bounce</field>
|
||||
<field name="sent_datetime" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- Generate some clicks -->
|
||||
<function model="link.tracker.click" name="add_click">
|
||||
<value model="link.tracker.code"
|
||||
search="[('link_id.url', '=', 'http://www.example.com')]"
|
||||
use="code"/>
|
||||
<value name="ip">100.01.02.03</value>
|
||||
<value name="country_code">BE</value>
|
||||
<value name="mailing_trace_id" eval="ref('mass_mail_1_stat_0')"/>
|
||||
</function>
|
||||
<function model="link.tracker.click" name="add_click">
|
||||
<value model="link.tracker.code"
|
||||
search="[('link_id.url', '=', 'http://www.example.net/page/contactus')]"
|
||||
use="code"/>
|
||||
<value name="ip">100.01.02.03</value>
|
||||
<value name="country_code">BE</value>
|
||||
<value name="mailing_trace_id" eval="ref('mass_mail_1_stat_0')"/>
|
||||
</function>
|
||||
<function model="link.tracker.click" name="add_click">
|
||||
<value model="link.tracker.code"
|
||||
search="[('link_id.url', '=', 'http://www.example.com')]"
|
||||
use="code"/>
|
||||
<value name="ip">100.01.02.04</value>
|
||||
<value name="country_code">BE</value>
|
||||
<value name="mailing_trace_id" eval="ref('mass_mail_1_stat_1')"/>
|
||||
</function>
|
||||
<function model="link.tracker.click" name="add_click">
|
||||
<value model="link.tracker.code"
|
||||
search="[('link_id.url', '=', 'http://www.example.net/page/contactus')]"
|
||||
use="code"/>
|
||||
<value name="ip">100.01.02.04</value>
|
||||
<value name="country_code">BE</value>
|
||||
<value name="mailing_trace_id" eval="ref('mass_mail_1_stat_0')"/>
|
||||
</function>
|
||||
<function model="link.tracker.click" name="add_click">
|
||||
<value model="link.tracker.code"
|
||||
search="[('link_id.url', '=', 'http://www.example.com')]"
|
||||
use="code"/>
|
||||
<value name="ip">100.01.02.05</value>
|
||||
<value name="country_code">BE</value>
|
||||
<value name="mailing_trace_id" eval="ref('mass_mail_1_stat_2')"/>
|
||||
</function>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="mass_mailing_tour" model="web_tour.tour">
|
||||
<field name="name">mass_mailing_tour</field>
|
||||
<field name="sequence">200</field>
|
||||
<field name="rainbow_man_message"><![CDATA[
|
||||
Congratulations, I love your first mailing. :)
|
||||
]]></field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record model="res.groups.privilege" id="res_groups_privilege_email_marketing">
|
||||
<field name="name">Email Marketing</field>
|
||||
<field name="sequence">19</field>
|
||||
<field name="category_id" ref="base.module_category_marketing"/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<?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>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Create Extra Mailing List for Demo -->
|
||||
<record id="mailing_list_1" model="mailing.list">
|
||||
<field name="name">Imported Contacts</field>
|
||||
<field name="is_public">False</field>
|
||||
</record>
|
||||
|
||||
<!-- Create Contacts -->
|
||||
<record id="mass_mail_contact_1" model="mailing.contact">
|
||||
<field name="name">Aristide Antario</field>
|
||||
<field name="email">alexandre.antario@example.com</field>
|
||||
<field name="list_ids" eval="[(5, 0, 0)]"/>
|
||||
</record>
|
||||
<record id="mass_mail_contact_2" model="mailing.contact">
|
||||
<field name="name">Beverly Bridge</field>
|
||||
<field name="email">beverly.bridge@example.com</field>
|
||||
<field name="list_ids" eval="[(5, 0, 0)]"/>
|
||||
</record>
|
||||
<record id="mass_mail_contact_3" model="mailing.contact">
|
||||
<field name="name">Carol Cartridge</field>
|
||||
<field name="email">carol.cartridge@example.com</field>
|
||||
<field name="list_ids" eval="[(5, 0, 0)]"/>
|
||||
</record>
|
||||
<record id="mass_mail_contact_4" model="mailing.contact">
|
||||
<field name="name">David Dawson</field>
|
||||
<field name="email">david.dawson@example.com</field>
|
||||
<field name="list_ids" eval="[(5, 0, 0)]"/>
|
||||
</record>
|
||||
<record id="mass_mail_contact_5" model="mailing.contact">
|
||||
<field name="name">Elsa Ericson</field>
|
||||
<field name="email">elsa.ericson@example.com</field>
|
||||
<field name="message_bounce">5</field>
|
||||
<field name="list_ids" eval="[(5, 0, 0)]"/>
|
||||
</record>
|
||||
<record id="mass_mail_contact_6" model="mailing.contact">
|
||||
<field name="name">Franz Faubourg</field>
|
||||
<field name="email">franz.faubourg@example.com</field>
|
||||
<field name="list_ids" eval="[(5, 0, 0)]"/>
|
||||
</record>
|
||||
<record id="mass_mail_contact_7" model="mailing.contact">
|
||||
<field name="name">Gilbert Gilson</field>
|
||||
<field name="email">gilbert.gilson@example.com</field>
|
||||
<field name="list_ids" eval="[(5, 0, 0)]"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="mass_mail_attach_1" model="ir.attachment">
|
||||
<field name="datas">bWlncmF0aW9uIHRlc3Q=</field>
|
||||
<field name="name">SampleDoc.doc</field>
|
||||
</record>
|
||||
|
||||
<record id="mass_mail_1" model="mailing.mailing">
|
||||
<field name="name">Newsletter 1</field>
|
||||
<field name="subject">Monthly Newsletter</field>
|
||||
<field name="state">done</field>
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
<field name="email_from">info@yourcompany.example.com</field>
|
||||
<field name="sent_date" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="campaign_id" ref="mass_mail_campaign_1"/>
|
||||
<field name="source_id" ref="mass_mailing.utm_source_0"/>
|
||||
<field name="mailing_model_id" ref="base.model_res_partner"/>
|
||||
<field name="mailing_domain" eval="[('parent_id', '=', ref('base.res_partner_4'))]"/>
|
||||
<field name="reply_to_mode">new</field>
|
||||
<field name="reply_to">Info <info@yourcompany.example.com></field>
|
||||
<field name="body_arch" type="html">
|
||||
<div class="o_layout o_default_theme oe_unremovable oe_unmovable" data-name="Mailing">
|
||||
<div class="container o_mail_wrapper oe_unremovable oe_unmovable" style="border-collapse:collapse;">
|
||||
<div class="row">
|
||||
<div class="col o_mail_no_options o_mail_wrapper_td bg-white oe_structure o_editable" style="text-align:left;width:100%;">
|
||||
<div class="o_mail_block_header_logo" data-snippet="s_mail_block_header_logo">
|
||||
<div class="o_mail_snippet_general" style="margin:0px auto 0px auto;background-color:rgb(255, 255, 255);max-width:600px;width:100%;">
|
||||
<div class="container o_mail_h_padding" style="padding:0 20px 0 20px;width:100%;border-collapse:separate;">
|
||||
<div class="row">
|
||||
<div valign="center" width="30%" class="col text-center o_mail_v_padding pb0" style="padding:20px 0 0px 0;vertical-align:middle;text-align:center;">
|
||||
<a href="http://www.example.com" style="text-decoration:none;font-weight:bold;background-color:transparent;color:rgb(100, 89, 116);">
|
||||
<img border="0" src="/mass_mailing/static/src/img/theme_default/s_default_image_header_logo.png" style="border-style:none;height:auto;vertical-align:middle;max-width:400px;width:auto"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_mail_block_footer_separator" data-snippet="s_hr" style="margin:0 20px 0 20px;">
|
||||
<div class="o_mail_snippet_general" style="margin:0px auto 0px auto;background-color:rgb(255, 255, 255);max-width:600px;width:100%;">
|
||||
<div class="container" style="width:100%;border-collapse:separate;">
|
||||
<div class="row">
|
||||
<div valign="top" style="padding:20px 0 20px 0;text-align:left;vertical-align:top;width:100%;" class="col o_mail_v_padding o_mail_no_colorpicker">
|
||||
<div style="background-color:rgb(245, 245, 245);height:2px;width:100%;" class="separator"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_mail_block_text" data-snippet="s_text_block">
|
||||
<div class="o_mail_snippet_general" style="margin:0px auto 0px auto;background-color:rgb(255, 255, 255);max-width:600px;width:100%;">
|
||||
<div class="container" style="width:100%;border-collapse:separate;">
|
||||
<div class="row">
|
||||
<div class="col-12 o_mail_h_padding o_mail_v_padding o_mail_no_colorpicker" style="padding:20px;text-align:left;vertical-align:top;">
|
||||
<p style="margin:0px 0 1rem 0;font-size:14px;">
|
||||
Great stories have personality. Consider telling a great story that provides personality. Writing a story with personality for potential clients will assist with making a relationship connection. This shows up in small quirks like word choices or phrases. Write from your point of view, not from someone else's experience.
|
||||
<br/>Great stories are for everyone even when only written for just one person. If you try to write with a wide general audience in mind, your story will ring false and be bland. No one will be interested. Write for one person. If it’s genuine for the one, it’s genuine for the rest.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_mail_block_footer_social o_mail_footer_social_center" data-snippet="s_mail_block_footer_social">
|
||||
<div class="o_mail_snippet_general" style="margin:0px auto 0px auto;background-color:rgb(255, 255, 255);max-width:600px;width:100%;">
|
||||
<div align="center" class="container" style="border-style:solid none none none;padding:20px 0 20px 0;border-top-color:rgb(245, 245, 245);border-top-width:2px;width:100%;border-collapse:separate;">
|
||||
<div class="row">
|
||||
<div class="col o_mail_footer_links" style="padding:10px 0 10px 0;text-align:center;vertical-align:middle;">
|
||||
<a href="/unsubscribe_from_list" class="btn btn-link" style="text-decoration:none;border-radius:0.25rem;border-style:solid;padding:0px;cursor:pointer;line-height:1.5;font-size:12px;border-start-color:transparent;border-bottom-color:transparent;border-end-color:transparent;border-top-color:transparent;border-start-width:1px;border-bottom-width:1px;border-end-width:1px;border-top-width:1px;user-select:none;vertical-align:middle;white-space:nowrap;text-align:center;font-weight:bold;display:inline-block;background-color:transparent;color:rgb(100, 89, 116);">Unsubscribe</a> |
|
||||
|
||||
<a href="/contactus" class="btn btn-link" style="text-decoration:none;border-radius:0.25rem;border-style:solid;padding:0px;cursor:pointer;line-height:1.5;font-size:12px;border-start-color:transparent;border-bottom-color:transparent;border-end-color:transparent;border-top-color:transparent;border-start-width:1px;border-bottom-width:1px;border-end-width:1px;border-top-width:1px;user-select:none;vertical-align:middle;white-space:nowrap;text-align:center;font-weight:bold;display:inline-block;background-color:transparent;color:rgb(100, 89, 116);">Contact</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col" style="text-align:left;vertical-align:middle;">
|
||||
<p class="o_mail_footer_copy" style="margin:0px 0 1rem 0;text-align:center;font-weight:bold;color:rgb(147, 146, 146);font-size:9px;">
|
||||
<img src="/mail/font_to_img/61945/rgb(147,146,146)/9" data-class="fa fa-copyright" style="border-style:none;max-width:100%;width:100%;vertical-align:middle;height: auto; width: auto;"/>2018 All Rights Reserved
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div align="center" class="container" style="width:100%;border-collapse:separate;">
|
||||
<div class="row">
|
||||
<div align="center" style="padding:16px 0 16px 0;" class="col pt16 pb16">
|
||||
Powered by <a target="_blank" href="https://www.odoo.com" style="text-decoration:none;background-color:transparent;color:#875A7B;">Odoo</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</field>
|
||||
<field name="attachment_ids" eval="[(4, ref('mass_mail_attach_1'))]"/>
|
||||
</record>
|
||||
<!-- Generate link tracker information from it -->
|
||||
<function model="mailing.mailing" name="convert_links" eval="[ref('mass_mailing.mass_mail_1')]"/>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Subscription to 'mailing_list_data' -->
|
||||
<record id="mailing_list_data_sub_contact_1" model="mailing.subscription">
|
||||
<field name="contact_id" ref="mass_mailing.mass_mail_contact_1"/>
|
||||
<field name="list_id" ref="mass_mailing.mailing_list_data"/>
|
||||
</record>
|
||||
<record id="mailing_list_data_sub_contact_2" model="mailing.subscription">
|
||||
<field name="contact_id" ref="mass_mailing.mass_mail_contact_2"/>
|
||||
<field name="list_id" ref="mass_mailing.mailing_list_data"/>
|
||||
</record>
|
||||
<record id="mailing_list_data_sub_contact_3" model="mailing.subscription">
|
||||
<field name="contact_id" ref="mass_mailing.mass_mail_contact_3"/>
|
||||
<field name="list_id" ref="mass_mailing.mailing_list_data"/>
|
||||
</record>
|
||||
<record id="mailing_list_data_sub_contact_4" model="mailing.subscription">
|
||||
<field name="contact_id" ref="mass_mailing.mass_mail_contact_4"/>
|
||||
<field name="list_id" ref="mass_mailing.mailing_list_data"/>
|
||||
<field name="opt_out">True</field>
|
||||
<field name="opt_out_reason_id" ref="mass_mailing.mailing_subscription_optout_data_1"/>
|
||||
</record>
|
||||
<record id="mailing_list_data_sub_contact_5" model="mailing.subscription">
|
||||
<field name="contact_id" ref="mass_mailing.mass_mail_contact_5"/>
|
||||
<field name="list_id" ref="mass_mailing.mailing_list_data"/>
|
||||
</record>
|
||||
<record id="mailing_list_data_sub_contact_6" model="mailing.subscription">
|
||||
<field name="contact_id" ref="mass_mailing.mass_mail_contact_6"/>
|
||||
<field name="list_id" ref="mass_mailing.mailing_list_data"/>
|
||||
<field name="opt_out">True</field>
|
||||
<field name="opt_out_reason_id" ref="mass_mailing.mailing_subscription_optout_data_3"/>
|
||||
</record>
|
||||
<record id="mailing_list_data_sub_contact_7" model="mailing.subscription">
|
||||
<field name="contact_id" ref="mass_mailing.mass_mail_contact_7"/>
|
||||
<field name="list_id" ref="mass_mailing.mailing_list_data"/>
|
||||
<field name="opt_out">True</field>
|
||||
<field name="opt_out_reason_id" ref="mass_mailing.mailing_subscription_optout_data_3"/>
|
||||
</record>
|
||||
<!-- Subscription to 'mailing_list_1' -->
|
||||
<record id="mailing_list_1_sub_contact_3" model="mailing.subscription">
|
||||
<field name="contact_id" ref="mass_mailing.mass_mail_contact_3"/>
|
||||
<field name="list_id" ref="mass_mailing.mailing_list_1"/>
|
||||
</record>
|
||||
<record id="mailing_list_1_sub_contact_6" model="mailing.subscription">
|
||||
<field name="contact_id" ref="mass_mailing.mass_mail_contact_6"/>
|
||||
<field name="list_id" ref="mass_mailing.mailing_list_1"/>
|
||||
</record>
|
||||
|
||||
<!-- Create Blacklist Records -->
|
||||
<record id="mail_blacklist_demo_1" model="mail.blacklist">
|
||||
<field name="email">elsa.ericson@example.com</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="mass_mail_1_stat_0" model="mailing.trace">
|
||||
<field name="mass_mailing_id" ref="mass_mail_1"/>
|
||||
<field name="message_id">1111000@odoo.com</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_address_7"/>
|
||||
<field name="email">billy.fox45@example.com</field>
|
||||
<field name="trace_status">reply</field>
|
||||
<field name="sent_datetime" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="open_datetime" eval="(DateTime.today() - relativedelta(days=4)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="reply_datetime" eval="(DateTime.today() - relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="write_date" eval="(DateTime.today() - relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
</record>
|
||||
<record id="mass_mail_1_stat_1" model="mailing.trace">
|
||||
<field name="mass_mailing_id" ref="mass_mail_1"/>
|
||||
<field name="message_id">1111001@odoo.com</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_address_13"/>
|
||||
<field name="email">kim.snyder96@example.com</field>
|
||||
<field name="trace_status">reply</field>
|
||||
<field name="sent_datetime" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="open_datetime" eval="(DateTime.today() - relativedelta(days=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="reply_datetime" eval="(DateTime.today() - relativedelta(days=0)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="write_date" eval="(DateTime.today() - relativedelta(days=0)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
</record>
|
||||
<record id="mass_mail_1_stat_2" model="mailing.trace">
|
||||
<field name="mass_mailing_id" ref="mass_mail_1"/>
|
||||
<field name="message_id">1111002@odoo.com</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_address_14"/>
|
||||
<field name="email">edith.sanchez68@example.com</field>
|
||||
<field name="trace_status">open</field>
|
||||
<field name="sent_datetime" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="open_datetime" eval="(DateTime.today() - relativedelta(days=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="write_date" eval="(DateTime.today() - relativedelta(days=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
</record>
|
||||
<record id="mass_mail_1_stat_3" model="mailing.trace">
|
||||
<field name="mass_mailing_id" ref="mass_mail_1"/>
|
||||
<field name="message_id">1111003@odoo.com</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_address_24"/>
|
||||
<field name="email">theodore.gardner36@example.com</field>
|
||||
<field name="trace_status">open</field>
|
||||
<field name="sent_datetime" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="open_datetime" eval="(DateTime.today() - relativedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="write_date" eval="(DateTime.today() - relativedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
</record>
|
||||
<record id="mass_mail_1_stat_4" model="mailing.trace">
|
||||
<field name="mass_mailing_id" ref="mass_mail_1"/>
|
||||
<field name="message_id">1111004@odoo.com</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_address_32"/>
|
||||
<field name="email">sandra.neal80@example.com</field>
|
||||
<field name="trace_status">sent</field>
|
||||
<field name="sent_datetime" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="write_date" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
</record>
|
||||
<record id="mass_mail_1_stat_5" model="mailing.trace">
|
||||
<field name="mass_mailing_id" ref="mass_mail_1"/>
|
||||
<field name="message_id">1111005@odoo.com</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_address_33"/>
|
||||
<field name="email">julie.richards84@example.com</field>
|
||||
<field name="trace_status">error</field>
|
||||
<field name="sent_datetime" eval="False"/>
|
||||
</record>
|
||||
<record id="mass_mail_1_stat_6" model="mailing.trace">
|
||||
<field name="mass_mailing_id" ref="mass_mail_1"/>
|
||||
<field name="message_id">1111006@odoo.com</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_address_34"/>
|
||||
<field name="email">travis.mendoza24@example.com</field>
|
||||
<field name="trace_status">bounce</field>
|
||||
<field name="sent_datetime" eval="(DateTime.today() - relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="write_date" eval="(DateTime.today() - relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
</record>
|
||||
<record id="mass_mail_1_stat_7" model="mailing.trace">
|
||||
<field name="mass_mailing_id" ref="mass_mail_1"/>
|
||||
<field name="message_id">1111007@odoo.com</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_address_34"/>
|
||||
<field name="email">travis.mendoza24@example.com</field>
|
||||
<field name="trace_status">bounce</field>
|
||||
<field name="sent_datetime" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- Generate some clicks -->
|
||||
<function model="link.tracker.click" name="add_click">
|
||||
<value model="link.tracker.code"
|
||||
search="[('link_id.url', '=', 'http://www.example.com')]"
|
||||
use="code"/>
|
||||
<value name="ip">100.01.02.03</value>
|
||||
<value name="country_code">BE</value>
|
||||
<value name="mailing_trace_id" eval="ref('mass_mail_1_stat_0')"/>
|
||||
</function>
|
||||
<function model="link.tracker.click" name="add_click">
|
||||
<value model="link.tracker.code"
|
||||
search="[('link_id.url', '=', 'http://www.example.net/page/contactus')]"
|
||||
use="code"/>
|
||||
<value name="ip">100.01.02.03</value>
|
||||
<value name="country_code">BE</value>
|
||||
<value name="mailing_trace_id" eval="ref('mass_mail_1_stat_0')"/>
|
||||
</function>
|
||||
<function model="link.tracker.click" name="add_click">
|
||||
<value model="link.tracker.code"
|
||||
search="[('link_id.url', '=', 'http://www.example.com')]"
|
||||
use="code"/>
|
||||
<value name="ip">100.01.02.04</value>
|
||||
<value name="country_code">BE</value>
|
||||
<value name="mailing_trace_id" eval="ref('mass_mail_1_stat_1')"/>
|
||||
</function>
|
||||
<function model="link.tracker.click" name="add_click">
|
||||
<value model="link.tracker.code"
|
||||
search="[('link_id.url', '=', 'http://www.example.net/page/contactus')]"
|
||||
use="code"/>
|
||||
<value name="ip">100.01.02.04</value>
|
||||
<value name="country_code">BE</value>
|
||||
<value name="mailing_trace_id" eval="ref('mass_mail_1_stat_0')"/>
|
||||
</function>
|
||||
<function model="link.tracker.click" name="add_click">
|
||||
<value model="link.tracker.code"
|
||||
search="[('link_id.url', '=', 'http://www.example.com')]"
|
||||
use="code"/>
|
||||
<value name="ip">100.01.02.05</value>
|
||||
<value name="country_code">BE</value>
|
||||
<value name="mailing_trace_id" eval="ref('mass_mail_1_stat_2')"/>
|
||||
</function>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="base.default_user_group" model="res.groups">
|
||||
<field name="implied_ids" eval="[(4,ref('mass_mailing.group_mass_mailing_user'))]"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
15
odoo-bringout-oca-ocb-mass_mailing/mass_mailing/demo/utm.xml
Normal file
15
odoo-bringout-oca-ocb-mass_mailing/mass_mailing/demo/utm.xml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Create campaign and mailings -->
|
||||
<record id="utm_source_0" model="utm.source">
|
||||
<field name="name">Newsletter 1</field>
|
||||
</record>
|
||||
<record id="mass_mail_campaign_1" model="utm.campaign">
|
||||
<field name="name">Newsletter</field>
|
||||
<field name="stage_id" ref="utm.campaign_stage_1"/>
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
<field name="tag_ids" eval="[(6,0,[ref('utm.utm_tag_1')])]"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue