Skip to content

Mailing List Subscribe Option #4191

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 26, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Mailing List Subscribe Option
  • Loading branch information
AlessandroLupo authored and loco-odoo committed Mar 26, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit 179097c8cf0a8472e4a03527fbef6a99de663767
1 change: 0 additions & 1 deletion addons/html_builder/__manifest__.py
Original file line number Diff line number Diff line change
@@ -42,7 +42,6 @@

'html_builder/static/src/**/*',
('remove', 'html_builder/static/src/website_preview/**/*'),
('remove', 'html_builder/static/src/website_mass_mailing/**/*'),
('remove', 'html_builder/static/src/website_builder/plugins/website_edit_service.js'),
('remove', 'html_builder/static/src/interactions/**/*'),
],
11 changes: 9 additions & 2 deletions addons/html_builder/static/src/core/drop_zone_plugin.js
Original file line number Diff line number Diff line change
@@ -297,9 +297,16 @@ export class DropZonePlugin extends Plugin {
insertMethod = "appendChild";
}
this.clearDropZone();
return (elementToAdd) => {
return async (elementToAdd) => {
// TODO: refactor if a new mutex system is implemented
target[insertMethod](elementToAdd);
this.dispatchTo("on_add_element_handlers", { elementToAdd: elementToAdd });
const proms = [];
for (const handler of this.getResource("on_add_element_handlers")) {
proms.push(handler({ elementToAdd: elementToAdd }));
}
this.services.ui.block();
await Promise.all(proms);
this.services.ui.unblock();
scrollToWindow(elementToAdd, { behavior: "smooth", offset: 50 });
this.dependencies.history.addStep();
};
Original file line number Diff line number Diff line change
@@ -35,8 +35,7 @@ class BackgroundOptionPlugin extends Plugin {
applyFunDependOnSelectorAndExclude(
this.markColorLevel,
root,
coloredLevelBackgroundParam.selector,
coloredLevelBackgroundParam.exclude
coloredLevelBackgroundParam
);
}
}
27 changes: 25 additions & 2 deletions addons/html_builder/static/src/plugins/utils.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
export function applyFunDependOnSelectorAndExclude(fn, rootEl, selector, exclude) {
export function applyFunDependOnSelectorAndExclude(fn, rootEl, { selector, exclude, applyTo }) {
const closestSelector = rootEl.closest(selector);
let editingEls = closestSelector ? [closestSelector] : [...rootEl.querySelectorAll(selector)];
if (exclude) {
editingEls = editingEls.filter((selectorEl) => !selectorEl.matches(exclude));
}
for (const editingEl of editingEls) {
fn(editingEl);
const targetEls = applyTo ? editingEl.querySelectorAll(applyTo) : [editingEl];
for (const targetEl of targetEls) {
fn(targetEl);
}
}
}

export async function applyAsyncFunDependOnSelectorAndExclude(
fn,
rootEl,
{ selector, exclude, applyTo }
) {
const closestSelector = rootEl.closest(selector);
let editingEls = closestSelector ? [closestSelector] : [...rootEl.querySelectorAll(selector)];
if (exclude) {
editingEls = editingEls.filter((selectorEl) => !selectorEl.matches(exclude));
}
const proms = [];
for (const editingEl of editingEls) {
const targetEls = applyTo ? editingEl.querySelectorAll(applyTo) : [editingEl];
for (const targetEl of targetEls) {
proms.push(fn(targetEl));
}
}
await Promise.all(proms);
}
10 changes: 5 additions & 5 deletions addons/html_builder/static/src/sidebar/block_tab.js
Original file line number Diff line number Diff line change
@@ -68,16 +68,16 @@ export class BlockTab extends Component {
onDrag: ({ element, x, y }) => {
this.dropzonePlugin.dragElement(element, x, y);
},
onDrop: ({ element, x, y }) => {
onDrop: async ({ element, x, y }) => {
const { height, width } = element.getClientRects()[0];

const position = { x, y, height, width };
const { category, snippet } = this.dragState;
if (category === "snippet_groups") {
this.openSnippetDialog(snippet, position);
await this.openSnippetDialog(snippet, position);
return;
}
const addElement = this.dropzonePlugin.getAddElement(position);
const addElement = await this.dropzonePlugin.getAddElement(position);
if (!addElement) {
return;
}
@@ -98,7 +98,7 @@ export class BlockTab extends Component {
return this.env.editor.shared.disableSnippets;
}

openSnippetDialog(snippet, position) {
async openSnippetDialog(snippet, position) {
if (snippet.moduleId) {
return;
}
@@ -107,7 +107,7 @@ export class BlockTab extends Component {
this.dropzonePlugin.getSelectors(snippet);
this.dropzonePlugin.displayDropZone(selectorSiblings, selectorChildren);
}
const addElement = this.dropzonePlugin.getAddElement(position);
const addElement = await this.dropzonePlugin.getAddElement(position);
if (!addElement) {
return;
}
Original file line number Diff line number Diff line change
@@ -19,7 +19,9 @@ class ProcessStepsOptionPlugin extends Plugin {
// connectors even if there were no step added (e.g: a column of the
// snippet is being resized).
content_updated_handlers: (rootEl) =>
applyFunDependOnSelectorAndExclude(reloadConnectors, rootEl, this.selector),
applyFunDependOnSelectorAndExclude(reloadConnectors, rootEl, {
selector: this.selector,
}),
};
getActions() {
return {
Original file line number Diff line number Diff line change
@@ -121,11 +121,9 @@ class TableOfContentOptionPlugin extends Plugin {
for (const navbar of root.querySelectorAll(".s_table_of_content_navbar")) {
navbar.setAttribute("contenteditable", "false");
}
applyFunDependOnSelectorAndExclude(
this.updateTableOfContentNavbar.bind(this),
root,
".s_table_of_content_main"
);
applyFunDependOnSelectorAndExclude(this.updateTableOfContentNavbar.bind(this), root, {
selector: ".s_table_of_content_main",
});
}

updateTableOfContentNavbar(tableOfContentMain) {
Original file line number Diff line number Diff line change
@@ -82,8 +82,7 @@ class WebsiteParallaxPlugin extends Plugin {
applyFunDependOnSelectorAndExclude(
this.removeParallax.bind(this),
rootEl,
backgroundOptionSelector.selector,
backgroundOptionSelector.exclude
backgroundOptionSelector
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.o_enable_preview {
display: block !important;
}
.o_disable_preview {
display: none !important;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { onWillStart } from "@odoo/owl";
import { BaseOptionComponent } from "@html_builder/core/utils";

export class MailingListSubscribeOption extends BaseOptionComponent {
static template = "html_builder.MailingListSubscribeOption";
static props = {
fetchMailingLists: Function,
};

setup() {
this.mailingLists = [];
onWillStart(async () => {
this.mailingLists = await this.props.fetchMailingLists();
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">

<!-- TODO: form_opt dependency -->

<t t-name="html_builder.MailingListSubscribeOption">

<BuilderRow label.translate="Newsletter">
<BuilderSelect dataAttributeAction="'listId'">
<t t-foreach="mailingLists" t-as="item" t-key="item.id">
<BuilderSelectItem dataAttributeActionValue="item.id.toString()">
<t t-out = "item.name"/>
</BuilderSelectItem>
</t>
<t t-if="!mailingLists.length">
<BuilderSelectItem>None</BuilderSelectItem>
</t>
</BuilderSelect>
</BuilderRow>

<BuilderRow label.translate="Display Thanks Message">
<BuilderCheckbox action="'toggleThanksMessage'"/>
</BuilderRow>

</t>

<t t-name="html_builder.MailingListSubscribeFormOption">

<BuilderRow label.translate="Placeholder">
<BuilderTextInput attributeAction="'placeholder'" applyTo="'.s_newsletter_subscribe_form_input'"/>
</BuilderRow>

</t>

</templates>
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
import { Plugin } from "@html_editor/plugin";
import { registry } from "@web/core/registry";
import { user } from "@web/core/user";
import { _t } from "@web/core/l10n/translation";
import { NewsletterSubscribeCommonOption } from "./newsletter_subscribe_common_option";
import { getSelectorParams } from "@html_builder/utils/utils";
import {
applyFunDependOnSelectorAndExclude,
applyAsyncFunDependOnSelectorAndExclude,
} from "@html_builder/plugins/utils";

class MailingListSubscribeOptionPlugin extends Plugin {
static id = "mailingListSubscribeOption";
static dependencies = ["remove", "savePlugin"];
static shared = ["fetchMailingLists"];
resources = {
builder_actions: [
{
toggleThanksMessage: {
apply: ({ editingElement }) => {
this.setThanksMessageVisibility(editingElement, true);
},
clean: ({ editingElement }) => {
this.setThanksMessageVisibility(editingElement, false);
},
isApplied: ({ editingElement }) =>
editingElement
.querySelector(".js_subscribed_wrap")
.classList.contains("o_enable_preview"),
},
},
],
on_add_element_handlers: this.onAddElement.bind(this),
clean_for_save_handlers: this.cleanForSave.bind(this),
};

setup() {
this.mailingListSubscribeOptionSelectorParams = getSelectorParams(
this.getResource("builder_options"),
NewsletterSubscribeCommonOption
);
}

setThanksMessageVisibility(editingElement, isVisible) {
const toSubscribeEl = editingElement.querySelector(".js_subscribe_wrap");
const thanksMessageEl = editingElement.querySelector(".js_subscribed_wrap");
thanksMessageEl.classList.toggle("o_enable_preview", isVisible);
thanksMessageEl.classList.toggle("o_disable_preview", !isVisible);
toSubscribeEl.classList.toggle("o_enable_preview", !isVisible);
toSubscribeEl.classList.toggle("o_disable_preview", isVisible);
}

async onAddElement({ elementToAdd }) {
const proms = [];
for (const mailingListSubscribeOptionSelector of this
.mailingListSubscribeOptionSelectorParams) {
proms.push(
applyAsyncFunDependOnSelectorAndExclude(
this.addNewsletterListElement.bind(this),
elementToAdd,
mailingListSubscribeOptionSelector
)
);
}
await Promise.all(proms);
}

async addNewsletterListElement(elementToAdd) {
await this.fetchMailingLists();
if (this.mailingLists.length) {
elementToAdd.dataset.listId = this.mailingLists[0].id;
} else {
this.services.dialog.add(ConfirmationDialog, {
body: _t(
"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?"
),
confirm: async () => {
await this.dependencies.savePlugin.save();
window.location.href =
"/odoo/action-mass_mailing.action_view_mass_mailing_lists";
},
cancel: () => {
this.dependencies.remove.removeElement(elementToAdd);
},
});
}
}

async fetchMailingLists() {
if (!this.mailingLists) {
const context = Object.assign({}, user.context, {
website_id: this.services.website.currentWebsite.id,
lang: this.services.website.currentWebsite.metadata.lang,
user_lang: user.context.lang,
});
const response = await this.services.orm.call(
"mailing.list",
"name_search",
["", [["is_public", "=", true]]],
{ context }
);
this.mailingLists = [];
for (const entry of response) {
this.mailingLists.push({ id: entry[0], name: entry[1] });
}
}
return this.mailingLists;
}

cleanForSave({ root }) {
for (const mailingListSubscribeOptionSelector of this
.mailingListSubscribeOptionSelectorParams) {
applyFunDependOnSelectorAndExclude(
this.removePreview.bind(this),
root,
mailingListSubscribeOptionSelector
);
}
}

removePreview(editingElement) {
const previewClasses = ["o_disable_preview", "o_enable_preview"];
const toCleanElsSelector = ".js_subscribe_wrap, .js_subscribed_wrap";
const toCleanEls = editingElement.querySelectorAll(toCleanElsSelector);
for (const toCleanEl of toCleanEls) {
toCleanEl.classList.remove(...previewClasses);
}
}
}

registry
.category("website-plugins")
.add(MailingListSubscribeOptionPlugin.id, MailingListSubscribeOptionPlugin);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { BaseOptionComponent } from "@html_builder/core/utils";
import { MailingListSubscribeOption } from "./mailing_list_subscribe_option";
import { RecaptchaSubscribeOption } from "./recaptcha_subscribe_option";

export class NewsletterSubscribeCommonOption extends BaseOptionComponent {
static template = "html_builder.NewsletterSubscribeCommonOption";
static components = {
MailingListSubscribeOption,
RecaptchaSubscribeOption,
};
static props = {
fetchMailingLists: Function,
hasRecaptcha: Function,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">

<t t-name="html_builder.NewsletterSubscribeCommonOption">

<MailingListSubscribeOption fetchMailingLists="props.fetchMailingLists"/>
<RecaptchaSubscribeOption hasRecaptcha="props.hasRecaptcha"/>

</t>

</templates>
Loading