[REF] web_editor, website: refactor dialogs to new API

After the core Dialog API was refactored in [1], the web_editor and
website dialogs had to be reworked.

These dialogs are using a base WebsiteDialog component, that allows for
a default footer and adds the o_website_dialog content class.

[1]: https://github.com/odoo/odoo/commit/7c4f200b8edf0bc695c623c7afc62288f838db36

See merge commit for more information.

task-2687506
This commit is contained in:
Younn Olivier
2022-06-24 10:28:07 +02:00
committed by qsm-odoo
parent 66af6664c1
commit 28a78fb4e4
17 changed files with 285 additions and 231 deletions
@@ -25,28 +25,31 @@ class RemoveButton extends Component {
}
RemoveButton.template = xml`<i class="fa fa-trash o_existing_attachment_remove p-2" t-att-title="removeTitle" role="img" t-att-aria-label="removeTitle" t-on-click="this.remove"/>`;
export class AttachmentError extends Dialog {
export class AttachmentError extends Component {
setup() {
super.setup();
this.title = this.env._t("Alert");
}
}
AttachmentError.bodyTemplate = xml `
<div class="form-text">
<p>The image could not be deleted because it is used in the
following pages or views:</p>
<ul t-foreach="props.views" t-as="view" t-key="view.id">
<li>
<a t-att-href="'/web#model=ir.ui.view&amp;id=' + view.id">
<t t-esc="view.name"/>
</a>
</li>
</ul>
</div>`;
AttachmentError.footerTemplate = xml`
<button class="btn btn-primary" t-on-click="() => this.close()">
Ok
</button>`;
AttachmentError.components = { Dialog };
AttachmentError.template = xml `
<Dialog title="title">
<div class="form-text">
<p>The image could not be deleted because it is used in the
following pages or views:</p>
<ul t-foreach="props.views" t-as="view" t-key="view.id">
<li>
<a t-att-href="'/web#model=ir.ui.view&amp;id=' + view.id">
<t t-esc="view.name"/>
</a>
</li>
</ul>
</div>
<t t-set-slot="footer">
<button class="btn btn-primary" t-on-click="() => this.props.close()">
Ok
</button>
</t>
</Dialog>`;
export class Attachment extends Component {
setup() {
@@ -69,7 +72,6 @@ export class Attachment extends Component {
});
}
},
cancel: () => {},
});
}
}
@@ -33,10 +33,9 @@ export const TABS = {
},
};
export class MediaDialog extends Dialog {
export class MediaDialog extends Component {
setup() {
super.setup();
this.size = 'modal-xl';
this.size = 'xl';
this.contentClass = 'o_select_media_dialog';
this.title = this.env._t("Select a media");
@@ -173,16 +172,16 @@ export class MediaDialog extends Dialog {
this.props.save(elements[0]);
}
}
this.close();
this.props.close();
}
}
MediaDialog.bodyTemplate = 'web_editor.MediaDialogBody';
MediaDialog.footerTemplate = 'web_editor.MediaDialogFooter';
MediaDialog.template = 'web_editor.MediaDialog';
MediaDialog.defaultProps = {
useMediaLibrary: true,
};
MediaDialog.components = {
...Object.keys(TABS).map(key => TABS[key].Component),
Dialog,
};
export class MediaDialogWrapper extends Component {
@@ -190,16 +189,7 @@ export class MediaDialogWrapper extends Component {
this.dialogs = useWowlService('dialog');
onRendered(() => {
this.dialogs.add(MediaDialog, {
...this.props,
close: () => {
if (this.props.close) {
this.props.close();
}
this.destroy();
},
});
this.dialogs.add(MediaDialog, this.props);
});
}
}
@@ -1,21 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<templates id="template" xml:space="preserve">
<t t-name="web_editor.MediaDialogBody" owl="1">
<ul class="nav nav-tabs" role="tablist">
<t t-foreach="tabs" t-as="tab" t-key="tab.id">
<li class="nav-item"><a class="nav-link" t-att-class="{ active: tab.id === state.activeTab }" t-on-click="() => state.activeTab = tab.id"><t t-esc="tab.title"/></a></li>
<t t-name="web_editor.MediaDialog" owl="1">
<Dialog contentClass="contentClass"
size="size"
title="title">
<ul class="nav nav-tabs" role="tablist">
<t t-foreach="tabs" t-as="tab" t-key="tab.id">
<li class="nav-item"><a class="nav-link" t-att-class="{ active: tab.id === state.activeTab }" t-on-click="() => state.activeTab = tab.id"><t t-esc="tab.title"/></a></li>
</t>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<t t-foreach="tabs" t-as="tab" t-key="tab.id">
<div class="tab-pane fade" t-att-class="tab.id === state.activeTab ? 'active show' : ''"><t t-component="tab.Component" t-props="tab.props"/></div>
</t>
</div>
<t t-set-slot="footer">
<button class="btn btn-primary" t-on-click="() => this.save()">Add</button>
<button class="btn btn-secondary" t-on-click="() => this.props.close()">Discard</button>
</t>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<t t-foreach="tabs" t-as="tab" t-key="tab.id">
<div class="tab-pane fade" t-att-class="tab.id === state.activeTab ? 'active show' : ''"><t t-component="tab.Component" t-props="tab.props"/></div>
</t>
</div>
</t>
<t t-name="web_editor.MediaDialogFooter" owl="1">
<button class="btn btn-primary" t-on-click="save">Add</button>
<button class="btn btn-secondary" t-on-click="close">Discard</button>
</Dialog>
</t>
</templates>
@@ -2,65 +2,66 @@
import { Dialog } from '@web/core/dialog/dialog';
import { Switch } from '@website/components/switch/switch';
import { _t } from 'web.core';
const { xml, useState } = owl;
const { xml, useState, Component } = owl;
export class WebsiteDialog extends Dialog {
setup() {
super.setup();
this.title = this.props.title || this.env._t("Confirmation");
this.primaryTitle = this.props.primaryTitle || this.env._t("Ok");
this.secondaryTitle = this.props.secondaryTitle || this.env._t("Cancel");
this.closeOnClick = this.props.closeOnClick === false ? false : true;
}
get showSecondaryButton() {
return this.props.showSecondaryButton;
}
primaryClick() {
export class WebsiteDialog extends Component {
async primaryClick() {
if (this.props.primaryClick) {
this.props.primaryClick();
await this.props.primaryClick();
}
if (this.closeOnClick) {
this.close();
if (this.props.closeOnClick) {
this.props.close();
}
}
secondaryClick() {
async secondaryClick() {
if (this.props.secondaryClick) {
this.props.secondaryClick();
await this.props.secondaryClick();
}
if (this.closeOnClick) {
this.close();
if (this.props.closeOnClick) {
this.props.close();
}
}
get contentClasses() {
const websiteDialogClass = 'o_website_dialog';
if (this.props.contentClass) {
return `${websiteDialogClass} ${this.props.contentClass}`;
}
return websiteDialogClass;
}
}
WebsiteDialog.components = { Dialog };
WebsiteDialog.props = {
...Dialog.props,
title: { type: String, optional: true },
body: { type: String, optional: true },
primaryTitle: { type: String, optional: true },
primaryClick: { type: Function, optional: true },
secondaryTitle: { type: String, optional: true },
secondaryClick: { type: Function, optional: true },
showSecondaryButton: { type: Boolean, optional: true },
close: Function,
closeOnClick: { type: Boolean, optional: true },
close: { type: Function, optional: true },
};
WebsiteDialog.defaultProps = {
...Dialog.defaultProps,
title: _t("Confirmation"),
primaryTitle: _t("Ok"),
secondaryTitle: _t("Cancel"),
showSecondaryButton: true,
size: "md",
closeOnClick: true,
};
WebsiteDialog.bodyTemplate = "website.DialogBody";
WebsiteDialog.footerTemplate = "website.DialogFooter";
WebsiteDialog.size = "modal-md";
WebsiteDialog.contentClass = "o_website_dialog";
WebsiteDialog.template = "website.WebsiteDialog";
export class AddPageDialog extends WebsiteDialog {
export class AddPageDialog extends Component {
setup() {
super.setup();
this.title = this.env._t("New Page");
this.primaryTitle = this.env._t("Create");
this.switchLabel = this.env._t("Add to menu");
this.state = useState({
addMenu: true,
@@ -72,20 +73,20 @@ export class AddPageDialog extends WebsiteDialog {
this.state.addMenu = value;
}
async primaryClick() {
async addPage() {
await this.props.addPage(this.state.name, this.state.addMenu);
this.close();
}
}
AddPageDialog.props = {
...WebsiteDialog.props,
addPage: Function,
};
AddPageDialog.components = {
Switch,
WebsiteDialog,
};
AddPageDialog.bodyTemplate = xml`
<div>
AddPageDialog.template = xml`
<WebsiteDialog
title="title"
primaryTitle="primaryTitle"
primaryClick="() => this.addPage()"
close="props.close">
<div class="form-group row">
<label class="col-form-label col-md-3">
Page Title
@@ -94,6 +95,6 @@ AddPageDialog.bodyTemplate = xml`
<input type="text" t-model="state.name" class="form-control" required="required"/>
</div>
</div>
<Switch extraClasses="'offset-md-3 col-md-9 text-left'" label="'Add to menu'" value="state.addMenu" onChange="(value) => this.onChangeAddMenu(value)"/>
</div>
<Switch extraClasses="'offset-md-3 col-md-9 text-left'" label="switchLabel" value="state.addMenu" onChange="(value) => this.onChangeAddMenu(value)"/>
</WebsiteDialog>
`;
@@ -1,15 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="website.DialogBody" owl="1">
<t t-esc="props.body"/>
</t>
<t t-name="website.DialogFooter" owl="1">
<button class="btn btn-primary" t-on-click="primaryClick">
<t t-esc="primaryTitle"/>
</button>
<button t-if="showSecondaryButton" class="btn btn-secondary" t-on-click="secondaryClick">
<t t-esc="secondaryTitle"/>
</button>
<t t-name="website.WebsiteDialog" owl="1">
<Dialog contentClass="contentClasses"
size="props.size"
title="props.title">
<t t-slot="default"/>
<t t-set-slot="footer">
<t t-if="props.slots and props.slots.footer" t-slot="footer"/>
<t t-else="">
<button class="btn btn-primary" t-on-click="primaryClick">
<t t-esc="props.primaryTitle"/>
</button>
<button t-if="props.showSecondaryButton" class="btn btn-secondary" t-on-click="secondaryClick">
<t t-esc="props.secondaryTitle"/>
</button>
</t>
</t>
</Dialog>
</t>
</templates>
@@ -5,10 +5,8 @@ import { WebsiteDialog } from './dialog';
const { Component, useState, useEffect, onWillStart, useRef, onMounted } = owl;
export class MenuDialog extends WebsiteDialog {
export class MenuDialog extends Component {
setup() {
super.setup();
this.title = this.env._t("Add a menu item");
this.state = useState({
@@ -17,23 +15,23 @@ export class MenuDialog extends WebsiteDialog {
});
}
primaryClick() {
onClickOk() {
this.props.save(this.state.name, this.state.url);
this.close();
}
}
MenuDialog.bodyTemplate = 'website.MenuDialog';
MenuDialog.template = 'website.MenuDialog';
MenuDialog.props = {
...WebsiteDialog.props,
name: { type: String, optional: true },
url: { type: String, optional: true },
isMegaMenu: { type: Boolean, optional: true },
save: Function,
close: Function,
};
MenuDialog.defaultProps = {
name: '',
url: '/',
};
MenuDialog.components = { WebsiteDialog };
class MenuRow extends Component {
edit() {
@@ -54,15 +52,14 @@ MenuRow.components = {
MenuRow,
};
export class EditMenuDialog extends WebsiteDialog {
export class EditMenuDialog extends Component {
setup() {
super.setup();
this.orm = useService('orm');
this.website = useService('website');
this.dialogs = useService('dialog');
this.title = this.env._t("Edit Menu");
this.primaryTitle = this.env._t("Save");
this.saveButton = this.env._t("Save");
this.menuEditor = useRef('menu-editor');
@@ -152,7 +149,7 @@ export class EditMenuDialog extends WebsiteDialog {
this.toDelete.push(id);
}
async primaryClick() {
async onClickSave() {
const newMenus = this.$sortables.nestedSortable('toArray', {startDepthCount: 0});
const levels = [];
const data = [];
@@ -178,7 +175,6 @@ export class EditMenuDialog extends WebsiteDialog {
'to_delete': this.toDelete,
}
]);
this.close();
if (this.props.save) {
this.props.save();
} else {
@@ -186,7 +182,8 @@ export class EditMenuDialog extends WebsiteDialog {
}
}
}
EditMenuDialog.bodyTemplate = 'website.EditMenuDialog';
EditMenuDialog.template = 'website.EditMenuDialog';
EditMenuDialog.components = {
MenuRow,
WebsiteDialog,
};
@@ -1,22 +1,24 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="website.MenuDialog" owl="1">
<div class="form-group row">
<label class="col-form-label col-md-3">
Name
</label>
<div class="col-md-9">
<input type="text" t-model="state.name" class="form-control" required="required"/>
<WebsiteDialog close="props.close" title="title" primaryClick="() => this.onClickOk()">
<div class="form-group row">
<label class="col-form-label col-md-3">
Name
</label>
<div class="col-md-9">
<input type="text" t-model="state.name" class="form-control" required="required"/>
</div>
</div>
</div>
<div t-if="!props.isMegaMenu" class="form-group row">
<label class="col-form-label col-md-3">
Url or Email
</label>
<div class="col-md-9">
<input type="text" t-model="state.url" class="form-control" required="required"/>
<div t-if="!props.isMegaMenu" class="form-group row">
<label class="col-form-label col-md-3">
Url or Email
</label>
<div class="col-md-9">
<input type="text" t-model="state.url" class="form-control" required="required"/>
</div>
</div>
</div>
</WebsiteDialog>
</t>
<t t-name="website.MenuRow" owl="1">
@@ -46,21 +48,23 @@
</t>
<t t-name="website.EditMenuDialog" owl="1">
<ul t-ref="menu-editor" class="oe_menu_editor list-unstyled">
<t t-foreach="state.rootMenu.children" t-as="menu" t-key="menu.fields['id']">
<MenuRow menu="menu" edit="(id) => this.editMenu(id)" delete="(id) => this.deleteMenu(id)"/>
</t>
</ul>
<div class="mt32">
<small class="float-right text-muted">
Drag to the right to get a submenu
</small>
<a href="#" t-on-click="() => this.addMenu(false)">
<i class="fa fa-plus-circle"/> Add Menu Item
</a><br/>
<a href="#" t-on-click="() => this.addMenu(true)">
<i class="fa fa-plus-circle"/> Add Mega Menu Item
</a>
</div>
<WebsiteDialog close="props.close" title="title" primaryTitle="saveButton" primaryClick="() => this.onClickSave()">
<ul t-ref="menu-editor" class="oe_menu_editor list-unstyled">
<t t-foreach="state.rootMenu.children" t-as="menu" t-key="menu.fields['id']">
<MenuRow menu="menu" edit="(id) => this.editMenu(id)" delete="(id) => this.deleteMenu(id)"/>
</t>
</ul>
<div class="mt32">
<small class="float-right text-muted">
Drag to the right to get a submenu
</small>
<a href="#" t-on-click="() => this.addMenu(false)">
<i class="fa fa-plus-circle"/> Add Menu Item
</a><br/>
<a href="#" t-on-click="() => this.addMenu(true)">
<i class="fa fa-plus-circle"/> Add Mega Menu Item
</a>
</div>
</WebsiteDialog>
</t>
</templates>
@@ -80,14 +80,13 @@ PageDependencies.props = {
},
};
export class DeletePageDialog extends WebsiteDialog {
export class DeletePageDialog extends Component {
setup() {
super.setup();
this.orm = useService('orm');
this.website = useService('website');
this.title = this.env._t("Delete Page");
this.primaryTitle = this.env._t("Ok");
this.deleteButton = this.env._t("Ok");
this.cancelButton = this.env._t("Cancel");
this.state = useState({
confirm: false,
@@ -98,28 +97,29 @@ export class DeletePageDialog extends WebsiteDialog {
this.state.confirm = checked;
}
async primaryClick() {
async onClickDelete() {
await this.orm.unlink("website.page", [
this.props.pageId,
]);
this.website.goToWebsite();
this.close();
this.props.close();
this.props.onClose();
}
}
DeletePageDialog.components = {PageDependencies, CheckBox};
DeletePageDialog.bodyTemplate = 'website.DeletePageContent';
DeletePageDialog.footerTemplate = 'website.DeletePageAction';
DeletePageDialog.components = {
PageDependencies,
CheckBox,
WebsiteDialog
};
DeletePageDialog.template = 'website.DeletePageDialog';
DeletePageDialog.props = {
...WebsiteDialog.props,
pageId: Number,
onClose: Function,
close: Function,
};
export class DuplicatePageDialog extends WebsiteDialog {
export class DuplicatePageDialog extends Component {
setup() {
super.setup();
this.orm = useService('orm');
this.website = useService('website');
@@ -128,7 +128,7 @@ export class DuplicatePageDialog extends WebsiteDialog {
});
}
async primaryClick() {
async duplicate() {
if (this.state.name) {
const res = await this.orm.call(
'website.page',
@@ -137,11 +137,12 @@ export class DuplicatePageDialog extends WebsiteDialog {
);
this.website.goToWebsite({path: res, edition: true});
}
this.close();
this.props.onClose();
}
}
DuplicatePageDialog.bodyTemplate = xml`
DuplicatePageDialog.components = {WebsiteDialog};
DuplicatePageDialog.template = xml`
<WebsiteDialog close="props.close" primaryClick="() => this.duplicate()">
<div class="form-group row">
<label class="col-form-label col-md-3">
Page Name
@@ -150,10 +151,12 @@ DuplicatePageDialog.bodyTemplate = xml`
<input type="text" t-model="state.name" class="form-control" required="required"/>
</div>
</div>
</WebsiteDialog>
`;
DuplicatePageDialog.props = {
...WebsiteDialog.props,
onClose: Function,
close: Function,
pageId: Number,
};
export class PagePropertiesDialogWrapper extends Component {
@@ -55,19 +55,24 @@
</t>
</t>
<t t-name="website.DeletePageContent" owl="1">
<p>Are you sure you want to delete this page ?</p>
<p class="text-warning">Don't forget to update all links referring to this page.</p>
<PageDependencies pageId="props.pageId" mode="'collapse'"/>
<CheckBox onChange.bind="onConfirmCheckboxChange">
<p class="text-warning">I am sure about this.</p>
</CheckBox>
</t>
<t t-name="website.DeletePageDialog" owl="1">
<WebsiteDialog title="title" close="props.close">
<p>Are you sure you want to delete this page ?</p>
<p class="text-warning">Don't forget to update all links referring to this page.</p>
<PageDependencies pageId="props.pageId" mode="'collapse'"/>
<CheckBox onChange.bind="onConfirmCheckboxChange">
<p class="text-warning">I am sure about this.</p>
</CheckBox>
<t t-name="website.DeletePageAction" t-inherit="website.DialogFooter" t-inherit-mode="primary" owl="1">
<xpath expr="//button[hasclass('btn-primary')]" position="attributes">
<attribute name="t-att-disabled">!state.confirm</attribute>
</xpath>
<t t-set-slot="footer">
<button class="btn btn-primary" t-on-click="() => this.onClickDelete()" t-att-disabled="!this.state.confirm">
Ok
</button>
<button class="btn btn-secondary" t-on-click="() => this.props.close()">
Cancel
</button>
</t>
</WebsiteDialog>
</t>
</templates>
@@ -1,6 +1,6 @@
/** @odoo-module **/
import { useService, useListener } from '@web/core/utils/hooks';
import { useService } from '@web/core/utils/hooks';
import { MediaDialog } from '@web_editor/components/media_dialog/media_dialog';
import { WebsiteDialog } from './dialog';
@@ -327,18 +327,17 @@ TitleDescription.components = {
SEOPreview,
};
export class OptimizeSEODialog extends WebsiteDialog {
export class OptimizeSEODialog extends Component {
setup() {
super.setup();
this.rpc = useService('rpc');
this.website = useService('website');
this.dialogs = useService('dialog');
this.orm = useService('orm');
this.title = this.env._t("Optimize SEO");
this.primaryTitle = this.env._t("Save");
this.size = 'modal-lg';
this.contentClass = `${this.contentClass} oe_seo_configuration`;
this.saveButton = this.env._t("Save");
this.size = 'lg';
this.contentClass = "oe_seo_configuration";
onWillStart(async () => {
const { metadata: { mainObject, seoObject, path } } = this.website.currentWebsite;
@@ -405,7 +404,7 @@ export class OptimizeSEODialog extends WebsiteDialog {
return el && el.content;
}
async primaryClick() {
async save() {
const data = {};
if (this.canEditTitle) {
data.website_meta_title = seoContext.title;
@@ -423,12 +422,12 @@ export class OptimizeSEODialog extends WebsiteDialog {
}
data.website_meta_og_img = seoContext.metaImage;
await this.orm.write(this.object.model, [this.object.id], data);
this.close();
this.website.goToWebsite({path: this.url.replace(this.previousSeoName || this.seoNameDefault, seoContext.seoName)});
}
}
OptimizeSEODialog.bodyTemplate = 'website.OptimizeSEODialog';
OptimizeSEODialog.template = 'website.OptimizeSEODialog';
OptimizeSEODialog.components = {
WebsiteDialog,
TitleDescription,
ImageSelector,
MetaKeywords,
@@ -166,20 +166,27 @@
</t>
<t t-name="website.OptimizeSEODialog" owl="1">
<TitleDescription canEditDescription="canEditDescription"
canEditUrl="canEditUrl"
canEditTitle="canEditTitle"
seoNameHelp="seoNameHelp"
seoNameDefault="seoNameDefault"
isIndexed="isIndexed"
defaultTitle="defaultTitle"
previewDescription="previewDescription"
url="url"/>
<MetaKeywords/>
<ImageSelector previewDescription="socialPreviewDescription"
defaultTitle="defaultTitle"
hasSocialDefaultImage="hasSocialDefaultImage"
pageImages="pageImages"
url="url"/>
<WebsiteDialog close="props.close"
title="title"
size="size"
primaryClick="() => this.save()"
primaryTitle="saveButton"
contentClass="contentClass">
<TitleDescription canEditDescription="canEditDescription"
canEditUrl="canEditUrl"
canEditTitle="canEditTitle"
seoNameHelp="seoNameHelp"
seoNameDefault="seoNameDefault"
isIndexed="isIndexed"
defaultTitle="defaultTitle"
previewDescription="previewDescription"
url="url"/>
<MetaKeywords/>
<ImageSelector previewDescription="socialPreviewDescription"
defaultTitle="defaultTitle"
hasSocialDefaultImage="hasSocialDefaultImage"
pageImages="pageImages"
url="url"/>
</WebsiteDialog>
</t>
</templates>
@@ -4,13 +4,12 @@ import { WebsiteEditorComponent } from '../editor/editor';
import { WebsiteDialog } from '../dialog/dialog';
import localStorage from 'web.local_storage';
const { useEffect, useRef } = owl;
const { useEffect, useRef, Component } = owl;
const localStorageNoDialogKey = 'website_translator_nodialog';
class AttributeTranslateDialog extends WebsiteDialog {
class AttributeTranslateDialog extends Component {
setup() {
super.setup();
this.title = this.env._t("Translate Attribute");
this.formEl = useRef('form-container');
@@ -38,10 +37,21 @@ class AttributeTranslateDialog extends WebsiteDialog {
}, () => [this.props.node]);
}
}
AttributeTranslateDialog.bodyTemplate = 'website.AttributeTranslateDialog';
AttributeTranslateDialog.components = { WebsiteDialog };
AttributeTranslateDialog.template = 'website.AttributeTranslateDialog';
class TranslatorInfoDialog extends WebsiteDialog {}
TranslatorInfoDialog.bodyTemplate = 'website.TranslatorInfoDialog';
class TranslatorInfoDialog extends Component {
setup() {
this.strongOkButton = this.env._t("Ok, never show me this again");
this.okButton = this.env._t("Ok");
}
onStrongOkClick() {
localStorage.setItem(localStorageNoDialogKey, true);
}
}
TranslatorInfoDialog.components = { WebsiteDialog };
TranslatorInfoDialog.template = 'website.TranslatorInfoDialog';
const savableSelector = '[data-oe-translation-id], ' +
'[data-oe-model][data-oe-id][data-oe-field], ' +
@@ -183,11 +193,7 @@ export class WebsiteTranslator extends WebsiteEditorComponent {
});
if (!localStorage.getItem(localStorageNoDialogKey)) {
this.dialogService.add(TranslatorInfoDialog, {
primaryClick: () => localStorage.setItem(localStorageNoDialogKey, true),
primaryTitle: this.env._t("Ok, never show me this again"),
secondaryTitle: this.env._t("Ok"),
});
this.dialogService.add(TranslatorInfoDialog);
}
// Apply data-oe-readonly on nested data.
@@ -228,7 +234,7 @@ export class WebsiteTranslator extends WebsiteEditorComponent {
});
this.$translations.prependEvent('click.translator', (ev) => {
this.dialogService.add(AttributeTranslateDialog, { node: ev.target, showSecondaryButton: false });
this.dialogService.add(AttributeTranslateDialog, { node: ev.target });
});
}
@@ -3,10 +3,10 @@
margin: 10px;
padding: 4px;
[data-oe-translation-state] {
&[data-oe-translation-state] {
background: $o-we-translated-content-color;
}
[data-oe-translation-state="to_translate"] {
&[data-oe-translation-state="to_translate"] {
background: $o-we-content-to-translate-color;
}
}
@@ -1,19 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<templates id="template" xml:space="preserve">
<div t-name="website.TranslatorInfoDialog" owl="1">
<p>You are about to enter the translation mode.</p>
<p>Here are the visuals used to help you translate efficiently:</p>
<ul class="oe_translate_examples">
<li data-oe-translation-state="to_translate">Content to translate</li>
<li data-oe-translation-state="translated">Translated content</li>
</ul>
<p>
In this mode, you can only translate texts. To change the structure of the page, you must edit the master page.
Each modification on the master page is automatically applied to all translated versions.
</p>
<WebsiteDialog close="props.close"
title="title"
primaryTitle="strongOkButton"
secondaryTitle="okButton"
primaryClick="() => this.onStrongOkClick()">
<p>You are about to enter the translation mode.</p>
<p>Here are the visuals used to help you translate efficiently:</p>
<ul class="oe_translate_examples">
<li data-oe-translation-state="to_translate">Content to translate</li>
<li data-oe-translation-state="translated">Translated content</li>
</ul>
<p>
In this mode, you can only translate texts. To change the structure of the page, you must edit the master page.
Each modification on the master page is automatically applied to all translated versions.
</p>
</WebsiteDialog>
</div>
<div t-name="website.AttributeTranslateDialog" owl="1">
<div t-ref="form-container"/>
</div>
<t t-name="website.AttributeTranslateDialog" owl="1">
<WebsiteDialog close="props.close"
title="title"
showSecondaryButton="false">
<div t-ref="form-container"/>
</WebsiteDialog>
</t>
</templates>
@@ -128,8 +128,8 @@ export const websiteService = {
clearBreadcrumbs: true,
additionalContext: {
params: {
website_id: websiteId,
path,
website_id: websiteId || currentWebsiteId,
path: path || (contentWindow && contentWindow.location.href) || '/',
enable_editor: edition,
edit_translations: translation,
},
@@ -37,6 +37,19 @@ NewContentElement.defaultProps = {
status: MODULE_STATUS.INSTALLED,
};
class InstallModuleDialog extends Component {
setup() {
this.installButton = this.env._t("Install");
}
onClickInstall() {
this.props.close();
this.props.installModule();
}
}
InstallModuleDialog.components = { WebsiteDialog };
InstallModuleDialog.template = "website.InstallModuleDialog";
export class NewContentModal extends Component {
setup() {
this.user = useService('user');
@@ -158,9 +171,8 @@ export class NewContentModal extends Component {
const {id, name} = this.modulesInfo[element.moduleXmlId];
const dialogProps = {
title: element.title,
body: _.str.sprintf(this.newContentText.installNeeded, name),
primaryTitle: this.env._t("Install"),
primaryClick: async () => {
installationText: _.str.sprintf(this.newContentText.installNeeded, name),
installModule: async () => {
// Update the NewContentElement with installing icon and text.
this.state.newContentElements = this.state.newContentElements.map(el => {
if (el.moduleXmlId === element.moduleXmlId) {
@@ -186,7 +198,7 @@ export class NewContentModal extends Component {
}
},
};
this.dialogs.add(WebsiteDialog, dialogProps);
this.dialogs.add(InstallModuleDialog, dialogProps);
}
}
NewContentModal.template = "website.NewContentModal";
@@ -41,4 +41,13 @@
<NewContentModal t-if="websiteContext.showNewContentModal"/>
</div>
</t>
<t t-name="website.InstallModuleDialog" owl="1">
<WebsiteDialog close="props.close"
title="props.title"
primaryTitle="installButton"
primaryClick="() => this.onClickInstall()">
<t t-esc="props.installationText"/>
</WebsiteDialog>
</t>
</templates>