[IMP] mass_mailing[_sms], tools: make mass mailing form focus on body
Make the email body take the entire space to avoid having some wasted space, this will also make the user have more focus when designing an email. Revamp the settings notebook page in order to give more clarity to the user, and move some fields from the main form have been moved to this section to have more space in the bottom for the email body. In the mailing form, when the mailing is sent or is being sent, set fields which are no longer useful for the user to change to readonly mode. Add a wizard that enables the user to schedule a mailing, the schedule field is still kept in the form for the user to be able to change the date when the mailing is in the queue (if they want to send it sooner). Display an action helper-style content when the email is empty, because, currently, the user is left with a big white screen when the email has no content which is not desirable. Update the html_empty function to take into account style attributes to better match the editor's void content. Hide A/B testing fields from SMS mailing form view as these are not supported for SMS marketing. Task-2469409 closes odoo/odoo#68882 Ent-pr: https://github.com/odoo/odoo/pull/68882 Related: odoo/enterprise#18391 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
'wizard/mailing_contact_to_list_views.xml',
|
||||
'wizard/mailing_list_merge_views.xml',
|
||||
'wizard/mailing_mailing_test_views.xml',
|
||||
'wizard/mailing_mailing_schedule_date_views.xml',
|
||||
'views/mailing_mailing_views_menus.xml',
|
||||
'views/mailing_trace_views.xml',
|
||||
'views/link_tracker_views.xml',
|
||||
|
||||
@@ -88,14 +88,18 @@ class MassMailing(models.Model):
|
||||
sent_date = fields.Datetime(string='Sent Date', copy=False)
|
||||
|
||||
schedule_type = fields.Selection([('now', 'Send now'), ('scheduled', 'Send on')], string='Schedule',
|
||||
default='now', required=True, readonly=True, states={'draft': [('readonly', False)]})
|
||||
schedule_date = fields.Datetime(string='Scheduled for', tracking=True, readonly=True, states={'draft': [('readonly', False)]},
|
||||
compute='_compute_schedule_date', store=True, copy=True)
|
||||
default='now', required=True, readonly=True,
|
||||
states={'draft': [('readonly', False)], 'in_queue': [('readonly', False)]})
|
||||
schedule_date = fields.Datetime(string='Scheduled for', tracking=True, readonly=True,
|
||||
states={'draft': [('readonly', False)], 'in_queue': [('readonly', False)]},
|
||||
compute='_compute_schedule_date', store=True, copy=True)
|
||||
calendar_date = fields.Datetime('Calendar Date', compute='_compute_calendar_date', store=True, copy=False,
|
||||
help="Technical field for the calendar view.")
|
||||
# don't translate 'body_arch', the translations are only on 'body_html'
|
||||
body_arch = fields.Html(string='Body', translate=False)
|
||||
body_html = fields.Html(string='Body converted to be sent by mail', sanitize_attributes=False)
|
||||
is_body_empty = fields.Boolean(compute="_compute_is_body_empty",
|
||||
help='Technical field used to determine if the mail body is empty')
|
||||
attachment_ids = fields.Many2many('ir.attachment', 'mass_mailing_ir_attachments_rel',
|
||||
'mass_mailing_id', 'attachment_id', string='Attachments')
|
||||
keep_archives = fields.Boolean(string='Keep Archives')
|
||||
@@ -132,6 +136,9 @@ class MassMailing(models.Model):
|
||||
mailing_domain = fields.Char(
|
||||
string='Domain', compute='_compute_mailing_domain',
|
||||
readonly=False, store=True)
|
||||
mail_server_available = fields.Boolean(
|
||||
compute='_compute_mail_server_available',
|
||||
help="Technical field used to know if the user has activated the outgoing mail server option in the settings")
|
||||
mail_server_id = fields.Many2one('ir.mail_server', string='Mail Server',
|
||||
default=_get_default_mail_server_id,
|
||||
help="Use a specific mail server in priority. Otherwise Odoo relies on the first outgoing mail server available (based on their sequencing) as it does for normal mails.")
|
||||
@@ -290,6 +297,14 @@ class MassMailing(models.Model):
|
||||
else:
|
||||
mailing.calendar_date = False
|
||||
|
||||
@api.depends('body_html')
|
||||
def _compute_is_body_empty(self):
|
||||
for mailing in self:
|
||||
mailing.is_body_empty = tools.is_html_empty(mailing.body_html)
|
||||
|
||||
def _compute_mail_server_available(self):
|
||||
self.mail_server_available = self.env['ir.config_parameter'].sudo().get_param('mass_mailing.outgoing_mail_server')
|
||||
|
||||
# ------------------------------------------------------
|
||||
# ORM
|
||||
# ------------------------------------------------------
|
||||
@@ -353,10 +368,17 @@ class MassMailing(models.Model):
|
||||
}
|
||||
|
||||
def action_launch(self):
|
||||
self.write({'schedule_type': 'now'})
|
||||
return self.action_put_in_queue()
|
||||
|
||||
def action_schedule(self):
|
||||
return self.action_put_in_queue()
|
||||
self.ensure_one()
|
||||
if self.schedule_date:
|
||||
return self.action_put_in_queue()
|
||||
else:
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("mass_mailing.mailing_mailing_schedule_date_action")
|
||||
action['context'] = dict(self.env.context, default_mass_mailing_id=self.id)
|
||||
return action
|
||||
|
||||
def action_put_in_queue(self):
|
||||
self.write({'state': 'in_queue'})
|
||||
|
||||
@@ -18,3 +18,4 @@ access_link_tracker_mailing,access.link.tracker.mailing,link_tracker.model_link_
|
||||
access_mailing_list_merge,access.mailing.list.merge,model_mailing_list_merge,mass_mailing.group_mass_mailing_user,1,1,1,0
|
||||
access_mailing_mailing_test,access.mailing.mailing.test,model_mailing_mailing_test,mass_mailing.group_mass_mailing_user,1,1,1,0
|
||||
access_mailing_contact_to_list,access.mailing.contact.to.list,model_mailing_contact_to_list,mass_mailing.group_mass_mailing_user,1,1,1,1
|
||||
access_mailing_schedule_date,access.mailing.schedule.date,model_mailing_mailing_schedule_date,mass_mailing.group_mass_mailing_user,1,1,1,1
|
||||
|
||||
|
@@ -5,3 +5,10 @@
|
||||
#wrapwrap .o_layout.o_basic_theme {
|
||||
font-family: -apple-system, "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
}
|
||||
|
||||
/*
|
||||
Make sure that the mail body takes all available space
|
||||
*/
|
||||
body.o_readonly.o_in_iframe {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -347,6 +347,11 @@ var MassMailingFieldHtml = FieldHtml.extend({
|
||||
this._super();
|
||||
this.wysiwyg.odooEditor.observerFlush();
|
||||
this.wysiwyg.odooEditor.resetHistory();
|
||||
//this will make the editor take all the bottom space
|
||||
this.wysiwyg.$iframe.css({
|
||||
'min-height': '100vh',
|
||||
width: '100%'
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
@@ -442,6 +447,8 @@ var MassMailingFieldHtml = FieldHtml.extend({
|
||||
$themeSelectorNew.appendTo(this.wysiwyg.$iframeBody);
|
||||
}
|
||||
|
||||
this.wysiwyg.$iframeBody.addClass("o_mass_mailing_iframe")
|
||||
|
||||
/**
|
||||
* Add proposition to install enterprise themes if not installed.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
.o_form_view .o_form_sheet .o_notebook .tab-content .tab-pane .o_mail_body {
|
||||
// cancel the padding of the form_sheet
|
||||
margin-top: -$o-sheet-cancel-hpadding;
|
||||
margin-left: -$o-sheet-cancel-hpadding;
|
||||
margin-right: -$o-sheet-cancel-hpadding;
|
||||
margin-bottom: -40px;
|
||||
}
|
||||
|
||||
@include media-breakpoint-between(lg, xl, $o-extra-grid-breakpoints) {
|
||||
.o_form_view .o_form_sheet .o_notebook .tab-content .tab-pane .o_mail_body {
|
||||
// cancel the padding of the form_sheet when breakpoints are reached
|
||||
margin-left: -$o-sheet-cancel-hpadding*2;
|
||||
margin-right: -$o-sheet-cancel-hpadding*2;
|
||||
}
|
||||
}
|
||||
|
||||
.o_kanban_view {
|
||||
.oe_kanban_mass_mailing {
|
||||
.o_title {
|
||||
@@ -22,6 +38,24 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.o_form_view .o_group.o_inner_group.o_mass_mailing_mailing_group {
|
||||
//used to gain extra space in form
|
||||
display: block;
|
||||
}
|
||||
|
||||
.o_mass_mailing_mailing_form {
|
||||
.alert:not(.o_invisible_modifier) + .clearfix.position-relative.o_form_sheet {
|
||||
//hides extra space between form and alert messages
|
||||
margin-top: -12px;
|
||||
}
|
||||
}
|
||||
|
||||
.o_form_view.o_xxl_form_view.o_mass_mailing_mailing_form {
|
||||
// This will hide the chatter scroll bar
|
||||
height: initial;
|
||||
}
|
||||
|
||||
.o_form_view {
|
||||
// This will display the emoji widget in the right position after a text field with sms option.
|
||||
.o_sms_container ~ .o_mail_add_emoji{
|
||||
|
||||
@@ -268,3 +268,17 @@ body.editor_enable.o_basic_theme.o_in_iframe {
|
||||
:root {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
.o_mass_mailing_iframe {
|
||||
//this will display the email body without padding
|
||||
.note-editable {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
//this will display the editor wrapper and the snippets at the same level
|
||||
.iframe-editor-wrapper {
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,3 +5,4 @@ from . import common
|
||||
from . import test_mailing_internals
|
||||
from . import test_mailing_list
|
||||
from . import test_mailing_controllers
|
||||
from . import test_mailing_mailing_schedule_date
|
||||
|
||||
@@ -370,3 +370,26 @@ Email: <a id="url5" href="mailto:test@odoo.com">test@odoo.com</a></div>""",
|
||||
link_info,
|
||||
link_params=link_params,
|
||||
)
|
||||
|
||||
class TestMailingScheduleDateWizard(MassMailCommon):
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
@users('user_marketing')
|
||||
def test_mailing_schedule_date(self):
|
||||
mailing = self.env['mailing.mailing'].create({
|
||||
'name': 'mailing',
|
||||
'subject': 'some subject'
|
||||
})
|
||||
# create a schedule date wizard
|
||||
wizard_form = Form(
|
||||
self.env['mailing.mailing.schedule.date'].with_context(default_mass_mailing_id=mailing.id))
|
||||
|
||||
# set a schedule date
|
||||
wizard_form.schedule_date = datetime(2021, 4, 30, 9, 0)
|
||||
wizard = wizard_form.save()
|
||||
wizard.action_schedule_date()
|
||||
|
||||
# assert that the schedule_date and schedule_type fields are correct and that the mailing is put in queue
|
||||
self.assertEqual(mailing.schedule_date, datetime(2021, 4, 30, 9, 0))
|
||||
self.assertEqual(mailing.schedule_type, 'scheduled')
|
||||
self.assertEqual(mailing.state, 'in_queue')
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from odoo.addons.mass_mailing.tests.common import MassMailCommon
|
||||
from odoo.tests import users, Form
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
class TestMailingScheduleDateWizard(MassMailCommon):
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
@users('user_marketing')
|
||||
def test_mailing_schedule_date(self):
|
||||
mailing = self.env['mailing.mailing'].create({
|
||||
'name': 'mailing',
|
||||
'subject': 'some subject',
|
||||
'mailing_model_id': self.env['ir.model']._get('res.partner').id,
|
||||
})
|
||||
# create a schedule date wizard
|
||||
wizard_form = Form(
|
||||
self.env['mailing.mailing.schedule.date'].with_context(default_mass_mailing_id=mailing.id))
|
||||
|
||||
# set a schedule date
|
||||
wizard_form.schedule_date = datetime(2021, 4, 30, 9, 0)
|
||||
wizard = wizard_form.save()
|
||||
wizard.action_schedule_date()
|
||||
|
||||
# assert that the schedule_date and schedule_type fields are correct and that the mailing is put in queue
|
||||
self.assertEqual(mailing.schedule_date, datetime(2021, 4, 30, 9, 0))
|
||||
self.assertEqual(mailing.schedule_type, 'scheduled')
|
||||
self.assertEqual(mailing.state, 'in_queue')
|
||||
@@ -54,13 +54,13 @@
|
||||
<field name="name">mailing.mailing.form</field>
|
||||
<field name="model">mailing.mailing</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Mailing">
|
||||
<form string="Mailing" class="o_mass_mailing_mailing_form">
|
||||
<header style="min-height:31px;">
|
||||
<button name="action_launch" type="object" class="oe_highlight" string="Launch"
|
||||
attrs="{'invisible': ['|', ('state', 'in', ('in_queue', 'sending', 'done')), ('schedule_type', '=', 'scheduled')]}"
|
||||
<button name="action_launch" type="object" class="oe_highlight" string="Send"
|
||||
attrs="{'invisible': [('state', 'in', ('in_queue', 'sending', 'done'))]}"
|
||||
confirm="This will send the email to all recipients. Do you still want to proceed ?"/>
|
||||
<button name="action_schedule" type="object" class="oe_highlight" string="Schedule"
|
||||
attrs="{'invisible': ['|', ('state', 'in', ('in_queue', 'sending', 'done')), ('schedule_type', '=', 'now')]}"/>
|
||||
<button name="action_schedule" type="object" class="btn-secondary" string="Schedule"
|
||||
attrs="{'invisible': [('state', 'in', ('in_queue', 'sending', 'done'))]}"/>
|
||||
<button name="action_test" type="object" class="btn-secondary" string="Test"/>
|
||||
<button name="action_cancel" type="object" attrs="{'invisible': [('state', '!=', 'in_queue')]}" class="btn-secondary" string="Cancel"/>
|
||||
<button name="action_retry_failed" type="object" attrs="{'invisible': ['|', ('state', '!=', 'done'), ('failed', '=', 0)]}" class="oe_highlight" string="Retry"/>
|
||||
@@ -156,12 +156,11 @@
|
||||
</button>
|
||||
</div>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<group>
|
||||
<group class="o_mass_mailing_mailing_group">
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="mailing_type" widget="radio" options="{'horizontal': true}" invisible="1"
|
||||
attrs="{'readonly': [('state', '!=', 'draft')]}" force_save="1"/>
|
||||
<field class="o_text_overflow" name="subject" string="Subject" attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}" widget="char_emojis" placeholder="e.g. New Sale on all T-shirts"/>
|
||||
<field class="o_text_overflow" name="preview" string="Preview Text" attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}" widget="char_emojis" placeholder="e.g. Check it out before it's too late!"/>
|
||||
<label for="mailing_model_id" string="Recipients"/>
|
||||
<div name="mailing_model_id_container">
|
||||
<div class="row">
|
||||
@@ -187,28 +186,30 @@
|
||||
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
|
||||
</div>
|
||||
</div>
|
||||
<label for="schedule_type" string="Schedule"/>
|
||||
<div name="schedule_block">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-3">
|
||||
<field name="schedule_type"/>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-3" attrs="{'invisible': [('schedule_type', '=', 'now')]}">
|
||||
<field name="schedule_date" attrs="{'required': [('schedule_type', '=', 'scheduled')]}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Mail Body" name="mail_body">
|
||||
<field name="body_html" class="oe_read_only" widget="html"
|
||||
options="{'cssReadonly': 'mass_mailing.iframe_css_assets_readonly'}"/>
|
||||
<field name="body_arch" class="o_mail_body oe_edit_only" widget="mass_mailing_html"
|
||||
options="{
|
||||
'snippets': 'mass_mailing.email_designer_snippets',
|
||||
'cssEdit': 'mass_mailing.iframe_css_assets_edit',
|
||||
'inline-field': 'body_html'
|
||||
}" attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
|
||||
<div class="mt-n2">
|
||||
<field name="body_html" class="o_mail_body oe_read_only" widget="html"
|
||||
options="{'cssReadonly': 'mass_mailing.iframe_css_assets_readonly'}"/>
|
||||
<field name="body_arch" class="o_mail_body oe_edit_only" widget="mass_mailing_html"
|
||||
options="{
|
||||
'snippets': 'mass_mailing.email_designer_snippets',
|
||||
'cssEdit': 'mass_mailing.iframe_css_assets_edit',
|
||||
'inline-field': 'body_html'
|
||||
}" attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
|
||||
</div>
|
||||
<field name="is_body_empty" invisible="1"/>
|
||||
<div class="o_view_nocontent oe_read_only" attrs="{'invisible': ['|', ('is_body_empty', '=', False), ('state', 'in', ('sending', 'done'))]}">
|
||||
<div class="o_nocontent_help">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
No template picked yet.
|
||||
</p>
|
||||
<p>
|
||||
Start editing your mailing to design something awesome.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</page>
|
||||
<page string="Dynamic Placeholder Generator"
|
||||
name="dynamic_placeholder_generator"
|
||||
@@ -227,9 +228,19 @@
|
||||
</page>
|
||||
<page string="Settings" name="settings">
|
||||
<group>
|
||||
<group>
|
||||
<field name="id" invisible="1"/>
|
||||
<field name="name" required="False" groups="base.group_no_one" class="o_text_overflow" string="Name"/>
|
||||
<group string="Extra Options">
|
||||
<field class="o_text_overflow" name="preview" string="Preview Text" attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}" widget="char_emojis" placeholder="e.g. Check it out before it's too late!"/>
|
||||
<label for="schedule_type" string="Schedule"/>
|
||||
<div name="schedule_block">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<field name="schedule_type"/>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6" attrs="{'invisible': [('schedule_type', '=', 'now')]}">
|
||||
<field name="schedule_date" attrs="{'required': [('schedule_type', '=', 'scheduled')]}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<field name="user_id" domain="[('share', '=', False)]"/>
|
||||
<field name="email_from" attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
|
||||
<label for="reply_to"/>
|
||||
@@ -258,15 +269,18 @@
|
||||
<field name="attachment_ids" widget="many2many_binary" string="Attach a file" class="oe_inline"
|
||||
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
|
||||
</div>
|
||||
<field name="mail_server_id" groups="base.group_no_one" options="{'no_create': True, 'no_open': True}"/>
|
||||
<field name="keep_archives" groups="base.group_no_one" attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
|
||||
</group>
|
||||
<group string="Marketing" groups="base.group_no_one,mass_mailing.group_mass_mailing_campaign">
|
||||
<group string="Tracking" groups="base.group_no_one,mass_mailing.group_mass_mailing_campaign">
|
||||
<field name="campaign_id"
|
||||
string="Campaign"
|
||||
groups="mass_mailing.group_mass_mailing_campaign"
|
||||
attrs="{'readonly': [('state', 'in', ('sending', 'done'))],
|
||||
'required': [('unique_ab_testing', '=', True)]}"/>
|
||||
<field name="medium_id"
|
||||
string="Medium"
|
||||
required="True"
|
||||
groups="base.group_no_one"
|
||||
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
|
||||
<field name="source_id"
|
||||
string="Source"
|
||||
readonly="1"
|
||||
@@ -274,21 +288,23 @@
|
||||
class="o_text_overflow"
|
||||
groups="base.group_no_one"
|
||||
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
|
||||
<field name="medium_id"
|
||||
string="Medium"
|
||||
required="True"
|
||||
groups="base.group_no_one"
|
||||
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
|
||||
<field name="unique_ab_testing"
|
||||
<field name="unique_ab_testing"
|
||||
groups="mass_mailing.group_mass_mailing_campaign"
|
||||
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
|
||||
<label for="contact_ab_pc" groups="mass_mailing.group_mass_mailing_campaign"/>
|
||||
<div groups="mass_mailing.group_mass_mailing_campaign">
|
||||
<div name="contact_ab_pc" groups="mass_mailing.group_mass_mailing_campaign">
|
||||
<field name="contact_ab_pc"
|
||||
class="oe_inline"
|
||||
attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/> %
|
||||
</div>
|
||||
</group>
|
||||
<group string="Advanced" groups="base.group_no_one">
|
||||
<field name="mail_server_available" invisible="1"/>
|
||||
<field name="name" required="False" string="Name" attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
|
||||
<field name="mail_server_id" attrs="{'readonly': [('state', 'in', ('sending', 'done'))],
|
||||
'invisible': [('mail_server_available', '=', False)]}"/>
|
||||
<field name="keep_archives" attrs="{'readonly': [('state', 'in', ('sending', 'done'))]}"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
|
||||
@@ -5,3 +5,4 @@ from . import mail_compose_message
|
||||
from . import mailing_contact_to_list
|
||||
from . import mailing_list_merge
|
||||
from . import mailing_mailing_test
|
||||
from . import mailing_mailing_schedule_date
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class MailingMailingScheduleDate(models.TransientModel):
|
||||
_name = "mailing.mailing.schedule.date"
|
||||
_description = "schedule a mailing"
|
||||
|
||||
schedule_date = fields.Datetime(string='Scheduled for')
|
||||
mass_mailing_id = fields.Many2one('mailing.mailing', required=True)
|
||||
|
||||
def action_schedule_date(self):
|
||||
self.mass_mailing_id.write({'schedule_type': 'scheduled', 'schedule_date': self.schedule_date})
|
||||
self.mass_mailing_id.action_put_in_queue()
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<record id="mailing_mailing_schedule_date_view_form" model="ir.ui.view">
|
||||
<field name="name">mailing.mailing.schedule.date.view.form</field>
|
||||
<field name="model">mailing.mailing.schedule.date</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Take Future Schedule Date">
|
||||
<group>
|
||||
<group>
|
||||
<field name="schedule_date" string="Send on" required="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<footer>
|
||||
<button string="Schedule" name="action_schedule_date" type="object" class="btn-primary"/>
|
||||
<button string="Discard " class="btn-secondary" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mailing_mailing_schedule_date_action" model="ir.actions.act_window">
|
||||
<field name="name">When do you want to send your mailing?</field>
|
||||
<field name="res_model">mailing.mailing.schedule.date</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -20,21 +20,20 @@
|
||||
<field name="arch" type="xml">
|
||||
<!-- Buttons / Actions -->
|
||||
<xpath expr="//button[@name='action_launch']" position="attributes">
|
||||
<attribute name="attrs">{'invisible': ['|', '|', ('mailing_type', '!=', 'mail'), ('state', 'in', ('in_queue', 'done')), ('schedule_type', '=', 'scheduled')]}</attribute>
|
||||
<attribute name="attrs">{'invisible': ['|', ('mailing_type', '!=', 'mail'), ('state', 'in', ('in_queue', 'sending', 'done'))]}</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='action_test']" position="before">
|
||||
<xpath expr="//button[@name='action_schedule']" position="before">
|
||||
<button name="action_put_in_queue" type="object"
|
||||
string="Launch" class="oe_highlight"
|
||||
attrs="{'invisible': ['|', '|', '|', ('mailing_type', '=', 'mail'), ('state', 'in', ('in_queue', 'done')), ('schedule_type', '=', 'scheduled'), ('sms_force_send', '=', True)]}"
|
||||
string="Send" class="oe_highlight"
|
||||
attrs="{'invisible': ['|', '|', ('mailing_type', '=', 'mail'), ('state', 'in', ('in_queue', 'sending', 'done')), ('sms_force_send', '=', True)]}"
|
||||
confirm="This will send SMS to all recipients. Do you still want to proceed ?"/>
|
||||
<button name="action_send_mail" type="object"
|
||||
string="Launch Now" class="oe_highlight"
|
||||
string="Send Now" class="oe_highlight"
|
||||
attrs="{'invisible': ['|', '|', '|', ('mailing_type', '=', 'mail'), ('state', 'in', ('in_queue', 'done')), ('schedule_type', '=', 'scheduled'), ('sms_force_send', '!=', True)]}"
|
||||
confirm="This will send SMS to all recipients now. Do you still want to proceed ?"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[@name='schedule_block']/div[hasclass('row')]" position="inside">
|
||||
<div class="col-xs-12 col-md-9"
|
||||
attrs="{'invisible': ['|', '|', ('mailing_type', '=', 'mail'), ('state', 'in', ['in_queue', 'sending', 'done']), ('schedule_type', '=', 'scheduled')]}">
|
||||
<div attrs="{'invisible': ['|', '|', ('mailing_type', '=', 'mail'), ('state', 'in', ['in_queue', 'sending', 'done']), ('schedule_type', '=', 'scheduled')]}">
|
||||
<label for="sms_force_send" string="Skip Queue"/>
|
||||
<field name="sms_force_send"/>
|
||||
</div>
|
||||
@@ -107,7 +106,7 @@
|
||||
For an SMS Text Message, internal Title of the Message.</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='subject']" position="after">
|
||||
<field class="o_text_overflow" name="sms_subject" string="Title" attrs="{'invisible': [('mailing_type', '!=', 'sms')], 'readonly': [('state', 'in', ('sending', 'done'))], 'required': [('mailing_type', '=', 'sms')]}"/>
|
||||
<field class="o_text_overflow" name="sms_subject" string="Title" placeholder="e.g. Black Friday SMS coupon" attrs="{'invisible': [('mailing_type', '!=', 'sms')], 'readonly': [('state', 'in', ('sending', 'done'))], 'required': [('mailing_type', '=', 'sms')]}"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='preview']" position="attributes">
|
||||
<attribute name="attrs">{'readonly': [('state', 'in', ('sending', 'done'))], 'invisible': [('mailing_type', '!=', 'mail')]}</attribute>
|
||||
@@ -123,7 +122,7 @@
|
||||
</page>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='user_id']" position="after">
|
||||
<field name="sms_allow_unsubscribe" attrs="{'invisible': [('mailing_type', '!=', 'sms')]}"/>
|
||||
<field name="sms_allow_unsubscribe" attrs="{'invisible': [('mailing_type', '!=', 'sms')], 'readonly': [('state', 'in', ('sending', 'done'))]}"/>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//field[@name='contact_list_ids']" position="attributes">
|
||||
@@ -151,6 +150,16 @@
|
||||
<attribute name="attrs">{'invisible': [('mailing_type', '!=', 'mail')]}</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='mail_server_id']" position="attributes">
|
||||
<attribute name="attrs">{'invisible': ['|', ('mailing_type', '!=', 'mail'),
|
||||
('mail_server_available', '=', False)], 'readonly': [('state', 'in', ('sending', 'done'))]}</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='unique_ab_testing']" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('mailing_type', '!=', 'mail')]}</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//div[@name='contact_ab_pc']" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('mailing_type', '!=', 'mail')]}</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//label[@for='contact_ab_pc']" position="attributes">
|
||||
<attribute name="attrs">{'invisible': [('mailing_type', '!=', 'mail')]}</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
|
||||
@@ -328,11 +328,13 @@ class TestHtmlTools(BaseCase):
|
||||
for content in void_strings_samples:
|
||||
self.assertTrue(is_html_empty(content))
|
||||
|
||||
void_html_samples = ['<p><br></p>', '<p><br> </p>', '<p><br /></p >']
|
||||
void_html_samples = ['<p><br></p>', '<p><br> </p>', '<p><br /></p >', '<p style="margin: 4px"></p>',
|
||||
'<div style="margin: 4px"></div>']
|
||||
for content in void_html_samples:
|
||||
self.assertTrue(is_html_empty(content), 'Failed with %s' % content)
|
||||
|
||||
valid_html_samples = ['<p><br>1</p>', '<p>1<br > </p>']
|
||||
valid_html_samples = ['<p><br>1</p>', '<p>1<br > </p>', '<p style="margin: 4px">Hello World</p>',
|
||||
'<div style="margin: 4px"><p>Hello World</p></div>']
|
||||
for content in valid_html_samples:
|
||||
self.assertFalse(is_html_empty(content))
|
||||
|
||||
|
||||
+4
-4
@@ -270,16 +270,16 @@ def validate_url(url):
|
||||
|
||||
|
||||
def is_html_empty(html_content):
|
||||
"""Check if a html content is empty. If there are only formatting tags or
|
||||
a void content return True. Famous use case if a '<p><br></p>' added by
|
||||
some web editor.
|
||||
"""Check if a html content is empty. If there are only formatting tags with style
|
||||
attributes or a void content return True. Famous use case if a
|
||||
'<p style="..."><br></p>' added by some web editor.
|
||||
|
||||
:param str html_content: html content, coming from example from an HTML field
|
||||
:returns: bool, True if no content found or if containing only void formatting tags
|
||||
"""
|
||||
if not html_content:
|
||||
return True
|
||||
tag_re = re.compile(r'\<\s*\/?(?:p|div|span|br|b|i)\s*/?\s*\>')
|
||||
tag_re = re.compile(r'\<\s*\/?(?:p|div|span|br|b|i)\s*(?:style=\".+\")?/?\s*\>')
|
||||
return not bool(re.sub(tag_re, '', html_content).strip())
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user