Skip to content

Commit 196d3e6

Browse files
Mailing List Subscribe Option
1 parent c03a931 commit 196d3e6

14 files changed

+286
-29
lines changed

addons/html_builder/__manifest__.py

-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242

4343
'html_builder/static/src/**/*',
4444
('remove', 'html_builder/static/src/website_preview/**/*'),
45-
('remove', 'html_builder/static/src/website_mass_mailing/**/*'),
4645
],
4746
'html_builder.inside_builder_style': [
4847
('include', 'web._assets_helpers'),

addons/html_builder/static/src/core/drop_zone_plugin.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -297,9 +297,16 @@ export class DropZonePlugin extends Plugin {
297297
insertMethod = "appendChild";
298298
}
299299
this.clearDropZone();
300-
return (elementToAdd) => {
300+
return async (elementToAdd) => {
301+
// TODO: refactor if a new mutex system is implemented
301302
target[insertMethod](elementToAdd);
302-
this.dispatchTo("on_add_element_handlers", { elementToAdd: elementToAdd });
303+
const proms = [];
304+
for (const handler of this.getResource("on_add_element_handlers")) {
305+
proms.push(handler({ elementToAdd: elementToAdd }));
306+
}
307+
this.services.ui.block();
308+
await proms;
309+
this.services.ui.unblock();
303310
scrollToWindow(elementToAdd, { behavior: "smooth", offset: 50 });
304311
this.dependencies.history.addStep();
305312
};

addons/html_builder/static/src/plugins/background_option/background_option_plugin.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class BackgroundOptionPlugin extends Plugin {
3636
this.markColorLevel,
3737
root,
3838
coloredLevelBackgroundParam.selector,
39-
coloredLevelBackgroundParam.exclude
39+
{ exclude: coloredLevelBackgroundParam.exclude }
4040
);
4141
}
4242
}
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
export function applyFunDependOnSelectorAndExclude(fn, rootEl, selector, exclude) {
1+
export function applyFunDependOnSelectorAndExclude(fn, rootEl, selector, options = {}) {
2+
const { exclude = null, applyTo = null } = options;
23
const closestSelector = rootEl.closest(selector);
34
let editingEls = closestSelector ? [closestSelector] : [...rootEl.querySelectorAll(selector)];
45
if (exclude) {
56
editingEls = editingEls.filter((selectorEl) => !selectorEl.matches(exclude));
67
}
78
for (const editingEl of editingEls) {
8-
fn(editingEl);
9+
const targetEls = applyTo ? editingEl.querySelectorAll(applyTo) : [editingEl];
10+
for (const targetEl of targetEls) {
11+
fn(targetEl);
12+
}
913
}
1014
}

addons/html_builder/static/src/sidebar/block_tab.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,16 @@ export class BlockTab extends Component {
6868
onDrag: ({ element, x, y }) => {
6969
this.dropzonePlugin.dragElement(element, x, y);
7070
},
71-
onDrop: ({ element, x, y }) => {
71+
onDrop: async ({ element, x, y }) => {
7272
const { height, width } = element.getClientRects()[0];
7373

7474
const position = { x, y, height, width };
7575
const { category, snippet } = this.dragState;
7676
if (category === "snippet_groups") {
77-
this.openSnippetDialog(snippet, position);
77+
await this.openSnippetDialog(snippet, position);
7878
return;
7979
}
80-
const addElement = this.dropzonePlugin.getAddElement(position);
80+
const addElement = await this.dropzonePlugin.getAddElement(position);
8181
if (!addElement) {
8282
return;
8383
}
@@ -98,7 +98,7 @@ export class BlockTab extends Component {
9898
return this.env.editor.shared.disableSnippets;
9999
}
100100

101-
openSnippetDialog(snippet, position) {
101+
async openSnippetDialog(snippet, position) {
102102
if (snippet.moduleId) {
103103
return;
104104
}
@@ -107,7 +107,7 @@ export class BlockTab extends Component {
107107
this.dropzonePlugin.getSelectors(snippet);
108108
this.dropzonePlugin.displayDropZone(selectorSiblings, selectorChildren);
109109
}
110-
const addElement = this.dropzonePlugin.getAddElement(position);
110+
const addElement = await this.dropzonePlugin.getAddElement(position);
111111
if (!addElement) {
112112
return;
113113
}

addons/html_builder/static/src/website_builder/plugins/parallax_option_plugin.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class WebsiteParallaxPlugin extends Plugin {
8383
this.removeParallax.bind(this),
8484
rootEl,
8585
backgroundOptionSelector.selector,
86-
backgroundOptionSelector.exclude
86+
{ exclude: backgroundOptionSelector.exclude }
8787
);
8888
}
8989
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.o_enable_preview {
2+
display: block !important;
3+
}
4+
.o_disable_preview {
5+
display: none !important;
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { onWillStart } from "@odoo/owl";
2+
import { BaseOptionComponent } from "@html_builder/core/utils";
3+
4+
export class MailingListSubscribeOption extends BaseOptionComponent {
5+
static template = "html_builder.MailingListSubscribeOption";
6+
static props = {
7+
fetchMailingLists: Function,
8+
};
9+
10+
setup() {
11+
this.mailingLists = [];
12+
onWillStart(async () => {
13+
this.mailingLists = await this.props.fetchMailingLists();
14+
});
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<templates xml:space="preserve">
3+
4+
<!-- TODO: form_opt dependency -->
5+
6+
<t t-name="html_builder.MailingListSubscribeOption">
7+
8+
<BuilderRow label.translate="Newsletter">
9+
<BuilderSelect dataAttributeAction="'listId'">
10+
<t t-foreach="mailingLists" t-as="item" t-key="item.id">
11+
<BuilderSelectItem dataAttributeActionValue="item.id.toString()">
12+
<t t-out = "item.name"/>
13+
</BuilderSelectItem>
14+
</t>
15+
<t t-if="!mailingLists.length">
16+
<BuilderSelectItem>None</BuilderSelectItem>
17+
</t>
18+
</BuilderSelect>
19+
</BuilderRow>
20+
21+
<BuilderRow label.translate="Display Thanks Message">
22+
<BuilderCheckbox action="'toggleThanksMessage'"/>
23+
</BuilderRow>
24+
25+
</t>
26+
27+
<t t-name="html_builder.MailingListSubscribeFormOption">
28+
29+
<BuilderRow label.translate="Placeholder">
30+
<BuilderTextInput attributeAction="'placeholder'" applyTo="'.s_newsletter_subscribe_form_input'"/>
31+
</BuilderRow>
32+
33+
</t>
34+
35+
</templates>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
2+
import { Plugin } from "@html_editor/plugin";
3+
import { registry } from "@web/core/registry";
4+
import { user } from "@web/core/user";
5+
import { _t } from "@web/core/l10n/translation";
6+
import { NewsletterSubscribeCommonOption } from "./newsletter_subscribe_common_option";
7+
import { getSelectorParams } from "@html_builder/utils/utils";
8+
import { applyFunDependOnSelectorAndExclude } from "@html_builder/plugins/utils";
9+
10+
class MailingListSubscribeOptionPlugin extends Plugin {
11+
static id = "mailingListSubscribeOption";
12+
static dependencies = ["remove", "savePlugin"];
13+
static shared = ["fetchMailingLists"];
14+
resources = {
15+
builder_actions: [
16+
{
17+
toggleThanksMessage: {
18+
apply: ({ editingElement }) => {
19+
this.setThanksMessageVisibility(editingElement, true);
20+
},
21+
clean: ({ editingElement }) => {
22+
this.setThanksMessageVisibility(editingElement, false);
23+
},
24+
isApplied: ({ editingElement }) =>
25+
editingElement
26+
.querySelector(".js_subscribed_wrap")
27+
.classList.contains("o_enable_preview"),
28+
},
29+
},
30+
],
31+
on_add_element_handlers: this.onAddElement.bind(this),
32+
clean_for_save_handlers: this.cleanForSave.bind(this),
33+
};
34+
35+
setup() {
36+
this.mailingListSubscribeOptionSelectorParams = getSelectorParams(
37+
this.getResource("builder_options"),
38+
NewsletterSubscribeCommonOption
39+
);
40+
}
41+
42+
setThanksMessageVisibility(editingElement, isVisible) {
43+
const toSubscribeEl = editingElement.querySelector(".js_subscribe_wrap");
44+
const thanksMessageEl = editingElement.querySelector(".js_subscribed_wrap");
45+
thanksMessageEl.classList.toggle("o_enable_preview", isVisible);
46+
thanksMessageEl.classList.toggle("o_disable_preview", !isVisible);
47+
toSubscribeEl.classList.toggle("o_enable_preview", !isVisible);
48+
toSubscribeEl.classList.toggle("o_disable_preview", isVisible);
49+
}
50+
51+
async onAddElement({ elementToAdd }) {
52+
for (const mailingListSubscribeOptionSelector of this
53+
.mailingListSubscribeOptionSelectorParams) {
54+
applyFunDependOnSelectorAndExclude(
55+
this.addNewsletterListElement.bind(this),
56+
elementToAdd,
57+
mailingListSubscribeOptionSelector.selector,
58+
{
59+
exclude: mailingListSubscribeOptionSelector.exclude,
60+
applyTo: mailingListSubscribeOptionSelector.applyTo,
61+
}
62+
);
63+
}
64+
}
65+
66+
async addNewsletterListElement(elementToAdd) {
67+
await this.fetchMailingLists();
68+
if (this.mailingLists.length) {
69+
elementToAdd.dataset.listId = this.mailingLists[0].id;
70+
} else {
71+
this.services.dialog.add(ConfirmationDialog, {
72+
body: _t(
73+
"No mailing list found, do you want to create a new one? This will save all your changes, are you sure you want to proceed?"
74+
),
75+
confirm: async () => {
76+
await this.dependencies.savePlugin.save();
77+
window.location.href =
78+
"/odoo/action-mass_mailing.action_view_mass_mailing_lists";
79+
},
80+
cancel: () => {
81+
this.dependencies.remove.removeElement(elementToAdd);
82+
},
83+
});
84+
}
85+
}
86+
87+
async fetchMailingLists() {
88+
if (!this.mailingLists) {
89+
const context = Object.assign({}, user.context, {
90+
website_id: this.services.website.currentWebsite.id,
91+
lang: this.services.website.currentWebsite.metadata.lang,
92+
user_lang: user.context.lang,
93+
});
94+
const response = await this.services.orm.call(
95+
"mailing.list",
96+
"name_search",
97+
["", [["is_public", "=", true]]],
98+
{ context }
99+
);
100+
this.mailingLists = [];
101+
for (const entry of response) {
102+
this.mailingLists.push({ id: entry[0], name: entry[1] });
103+
}
104+
}
105+
return this.mailingLists;
106+
}
107+
108+
cleanForSave({ root }) {
109+
for (const mailingListSubscribeOptionSelector of this
110+
.mailingListSubscribeOptionSelectorParams) {
111+
applyFunDependOnSelectorAndExclude(
112+
this.removePreview.bind(this),
113+
root,
114+
mailingListSubscribeOptionSelector.selector,
115+
{
116+
exclude: mailingListSubscribeOptionSelector.exclude,
117+
applyTo: mailingListSubscribeOptionSelector.applyTo,
118+
}
119+
);
120+
}
121+
}
122+
123+
removePreview(editingElement) {
124+
const previewClasses = ["o_disable_preview", "o_enable_preview"];
125+
const toCleanElsSelector = ".js_subscribe_wrap, .js_subscribed_wrap";
126+
const toCleanEls = editingElement.querySelectorAll(toCleanElsSelector);
127+
for (const toCleanEl of toCleanEls) {
128+
toCleanEl.classList.remove(...previewClasses);
129+
}
130+
}
131+
}
132+
133+
registry
134+
.category("website-plugins")
135+
.add(MailingListSubscribeOptionPlugin.id, MailingListSubscribeOptionPlugin);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { BaseOptionComponent } from "@html_builder/core/utils";
2+
import { MailingListSubscribeOption } from "./mailing_list_subscribe_option";
3+
import { RecaptchaSubscribeOption } from "./recaptcha_subscribe_option";
4+
5+
export class NewsletterSubscribeCommonOption extends BaseOptionComponent {
6+
static template = "html_builder.NewsletterSubscribeCommonOption";
7+
static components = {
8+
MailingListSubscribeOption,
9+
RecaptchaSubscribeOption,
10+
};
11+
static props = {
12+
fetchMailingLists: Function,
13+
hasRecaptcha: Function,
14+
};
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<templates xml:space="preserve">
3+
4+
<t t-name="html_builder.NewsletterSubscribeCommonOption">
5+
6+
<MailingListSubscribeOption fetchMailingLists="props.fetchMailingLists"/>
7+
<RecaptchaSubscribeOption hasRecaptcha="props.hasRecaptcha"/>
8+
9+
</t>
10+
11+
</templates>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { Plugin } from "@html_editor/plugin";
2+
import { registry } from "@web/core/registry";
3+
import { NewsletterSubscribeCommonOption } from "./newsletter_subscribe_common_option";
4+
5+
class NewsletterSubscribeCommonOptionPlugin extends Plugin {
6+
static id = "newsletterSubscribeCommonOption";
7+
static dependencies = ["mailingListSubscribeOption", "recaptchaSubscribeOption"];
8+
resources = {
9+
builder_options: [
10+
{
11+
OptionComponent: NewsletterSubscribeCommonOption,
12+
props: this.getProps(),
13+
selector: ".s_newsletter_list",
14+
exclude: [
15+
".s_newsletter_block .s_newsletter_list",
16+
".o_newsletter_popup .s_newsletter_list",
17+
".s_newsletter_box .s_newsletter_list",
18+
".s_newsletter_centered .s_newsletter_list",
19+
".s_newsletter_grid .s_newsletter_list",
20+
].join(", "),
21+
},
22+
{
23+
OptionComponent: NewsletterSubscribeCommonOption,
24+
props: this.getProps(),
25+
selector: ".o_newsletter_popup",
26+
applyTo: ".s_newsletter_list",
27+
},
28+
{
29+
template: "html_builder.MailingListSubscribeFormOption",
30+
selector: ".s_newsletter_subscribe_form",
31+
},
32+
],
33+
};
34+
35+
getProps() {
36+
return {
37+
fetchMailingLists: this.dependencies.mailingListSubscribeOption.fetchMailingLists,
38+
hasRecaptcha: this.dependencies.recaptchaSubscribeOption.hasRecaptcha,
39+
};
40+
}
41+
}
42+
43+
registry
44+
.category("website-plugins")
45+
.add(NewsletterSubscribeCommonOptionPlugin.id, NewsletterSubscribeCommonOptionPlugin);

addons/html_builder/static/src/website_mass_mailing/recaptcha_subscribe_option_plugin.js

+1-17
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,12 @@
11
import { Plugin } from "@html_editor/plugin";
22
import { registry } from "@web/core/registry";
3-
import { RecaptchaSubscribeOption } from "./recaptcha_subscribe_option";
43
import { renderToElement } from "@web/core/utils/render";
54

65
class RecaptchaSubscribeOptionPlugin extends Plugin {
76
static id = "recaptchaSubscribeOption";
87
static dependencies = ["websiteSession"];
9-
8+
static shared = ["hasRecaptcha"];
109
resources = {
11-
builder_options: [
12-
{
13-
OptionComponent: RecaptchaSubscribeOption,
14-
props: { hasRecaptcha: this.hasRecaptcha.bind(this) },
15-
selector: ".s_newsletter_list",
16-
exclude:
17-
".s_newsletter_block .s_newsletter_list, .o_newsletter_popup .s_newsletter_list, .s_newsletter_box .s_newsletter_list, .s_newsletter_centered .s_newsletter_list, .s_newsletter_grid .s_newsletter_list",
18-
},
19-
{
20-
OptionComponent: RecaptchaSubscribeOption,
21-
props: { hasRecaptcha: this.hasRecaptcha.bind(this) },
22-
selector: ".o_newsletter_popup",
23-
applyTo: ".s_newsletter_list",
24-
},
25-
],
2610
builder_actions: [
2711
{
2812
toggleRecaptchaLegal: {

0 commit comments

Comments
 (0)