diff --git a/addons/website/models/mixins.py b/addons/website/models/mixins.py
index 93786227252..ab64ea80d7d 100644
--- a/addons/website/models/mixins.py
+++ b/addons/website/models/mixins.py
@@ -284,8 +284,10 @@ class WebsitePublishedMultiMixin(WebsitePublishedMixin):
def open_website_url(self):
client_action_url = self.env['website'].get_client_action_url(self.website_url)
- if self.website_id.domain:
- client_action_url = url_join(self.website_id.domain, f'{client_action_url}&website_id={self.website_id.id}')
+ if self.website_id:
+ client_action_url = f'{client_action_url}&website_id={self.website_id.id}'
+ if self.website_id.domain:
+ client_action_url = url_join(self.website_id.domain, client_action_url)
return {
'type': 'ir.actions.act_url',
'url': client_action_url,
diff --git a/addons/website/models/website_page.py b/addons/website/models/website_page.py
index f742e24775d..e789119974e 100644
--- a/addons/website/models/website_page.py
+++ b/addons/website/models/website_page.py
@@ -83,6 +83,13 @@ class Page(models.Model):
# If the page is no longer in menu, we should remove its website_menu
page.menu_ids.unlink()
+ # This update was added to make sure the mixin calculations are correct
+ # (page.website_url > page.url).
+ @api.depends('url')
+ def _compute_website_url(self):
+ for page in self:
+ page.website_url = page.url
+
def _get_most_specific_pages(self):
''' Returns the most specific pages in self. '''
ids = []
@@ -277,6 +284,15 @@ class Page(models.Model):
url = '/' + slugify(page_url, max_length=1024, path=True)
return self.env['website'].with_context(website_id=website_id).get_unique_path(url)
+ def action_page_debug_view(self):
+ return {
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'ir.ui.view',
+ 'res_id': self.view_id.id,
+ 'view_mode': 'form',
+ 'view_id': self.env.ref('website.view_view_form_extend').id,
+ }
+
# this is just a dummy function to be used as ormcache key
def _cached_response():
diff --git a/addons/website/static/src/components/dialog/page_properties.js b/addons/website/static/src/components/dialog/page_properties.js
index 3011d23013a..28bf4437a84 100644
--- a/addons/website/static/src/components/dialog/page_properties.js
+++ b/addons/website/static/src/components/dialog/page_properties.js
@@ -7,7 +7,7 @@ import {WebsiteDialog} from './dialog';
import {FormViewDialog} from "@web/views/view_dialogs/form_view_dialog";
import {qweb, _t} from 'web.core';
-const {Component, onWillStart, onRendered, useState, xml, useRef, markup} = owl;
+const {Component, onWillStart, useState, xml, useRef, markup} = owl;
export class PageDependencies extends Component {
setup() {
@@ -209,54 +209,3 @@ PagePropertiesDialog.defaultProps = {
},
onClose: () => {},
};
-
-// TODO remove this once the 'website.page' listView is adapted to OWL.
-export class PagePropertiesDialogManager extends Component {
- setup() {
- this.dialog = useService('dialog');
- onRendered(this.createDialog);
- }
-
- createDialog() {
- switch (this.props.mode) {
- case 'clone':
- this.dialog.add(DuplicatePageDialog, {
- pageId: this.props.resId,
- onDuplicate: this.props.onClose,
- });
- break;
- case 'delete':
- this.dialog.add(DeletePageDialog, {
- pageId: this.props.resId,
- onDelete: this.props.onClose,
- });
- break;
- default:
- this.dialog.add(PagePropertiesDialog, {
- resId: this.props.resId,
- onClose: this.props.onClose,
- onRecordSaved: this.props.onRecordSaved,
- });
- break;
- }
- }
-}
-PagePropertiesDialogManager.template = xml``;
-PagePropertiesDialogManager.props = {
- onClose: {
- type: Function,
- optional: true,
- },
- mode: {
- type: String,
- optional: true,
- },
- resId: {
- type: [Number, Boolean],
- optional: true,
- },
- onRecordSaved: {
- type: Function,
- optional: true,
- }
-};
diff --git a/addons/website/static/src/components/fields/fields.js b/addons/website/static/src/components/fields/fields.js
index 35aae70d298..0905895f91e 100644
--- a/addons/website/static/src/components/fields/fields.js
+++ b/addons/website/static/src/components/fields/fields.js
@@ -5,8 +5,6 @@ import {standardFieldProps} from '@web/views/fields/standard_field_props';
import {useInputField} from '@web/views/fields/input_field_hook';
import {useService} from '@web/core/utils/hooks';
import {Switch} from '@website/components/switch/switch';
-import AbstractFieldOwl from 'web.AbstractFieldOwl';
-import fieldRegistry from 'web.field_registry_owl';
import {registry} from '@web/core/registry';
import {formatChar} from '@web/views/fields/formatters';
@@ -114,28 +112,6 @@ PageNameField.supportedTypes = ['char'];
registry.category("fields").add("page_name", PageNameField);
-/**
- * Displays 'char' field's value prefixed by a FA icon.
- * The prefix is shown by default, but the visibility can be updated depending on
- * other field value.
- * e.g. `` renders the icon only when 'is_locked' is True.
- */
-class FieldFaPrefix extends AbstractFieldOwl {
- get prefix() {
- const {icon, visibility, title} = this.nodeOptions;
- return {
- class: icon.split(' ').filter(str => str.indexOf('fa-') === 0).join(' '),
- visible: !visibility || !!this.recordData[visibility],
- help: title || '',
- };
- }
-}
-FieldFaPrefix.supportedFieldTypes = ['char'];
-FieldFaPrefix.template = 'website.FieldFaPrefix';
-
-fieldRegistry.add('fa_prefix', FieldFaPrefix);
-
/**
* Displays 'Selection' field's values as images to select.
* Image src for each value can be added using the option 'images' on field XML.
diff --git a/addons/website/static/src/components/fields/fields.xml b/addons/website/static/src/components/fields/fields.xml
index 2364ade9278..eec1073bb0c 100644
--- a/addons/website/static/src/components/fields/fields.xml
+++ b/addons/website/static/src/components/fields/fields.xml
@@ -62,10 +62,6 @@
-
-
-
-
diff --git a/addons/website/static/src/components/views/page_list.js b/addons/website/static/src/components/views/page_list.js
index fe6917063e9..656841201dd 100644
--- a/addons/website/static/src/components/views/page_list.js
+++ b/addons/website/static/src/components/views/page_list.js
@@ -1,158 +1,109 @@
/** @odoo-module **/
-import ListController from 'web.ListController';
-import viewRegistry from 'web.view_registry';
-import ListView from 'web.ListView';
-import {ComponentWrapper} from 'web.OwlCompatibility';
-import {PagePropertiesDialogManager} from '@website/components/dialog/page_properties';
-import {qweb} from 'web.core';
+import {AddPageDialog} from "../dialog/dialog";
+import {registry} from '@web/core/registry';
+import {listView} from '@web/views/list/list_view';
+import {useService} from "@web/core/utils/hooks";
+import { csrf_token } from 'web.core';
-const WebsitePageListController = ListController.extend({
+const {onWillStart, useState} = owl;
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
+export class PageListController extends listView.Controller {
/**
* @override
*/
- willStart() {
- return Promise.all([this._super(...arguments), this._getRenderContext()]);
- },
- /**
- * @override
- */
- renderButtons($node) {
- this.$buttons = $(qweb.render('PageListView.WebsiteSelect', {
- websites: this.websites,
- active: this._getWebsite(0),
- }));
- this.$buttons.find('.dropdown-item').click(this._onButtonClick.bind(this));
- if ($node) {
- this.$buttons.appendTo($node);
- }
- },
+ setup() {
+ super.setup();
+ this.website = useService('website');
+ this.http = useService('http');
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
+ this.websiteSelection = [{id: 0, name: this.env._t("All Websites")}];
- /**
- * @override
- */
- async _callButtonAction(attrs, record) {
- switch (attrs.name) {
- case 'action_optimize_seo':
- this._goToPage(record.data.url, record.data.website_id.res_id, {
- enable_seo: true,
- });
- break;
- case 'action_manage_page':
- await this._addDialog(record);
- break;
- case 'action_clone_page':
- await this._addDialog(record, 'clone');
- break;
- case 'action_delete_page':
- await this._addDialog(record, 'delete');
- break;
- case 'action_edit_page':
- this.do_action({
- type: 'ir.actions.act_window',
- res_model: 'ir.ui.view',
- res_id: record.data.view_id.res_id,
- views: [[false, 'form']],
- });
- break;
- default:
- return this._super(...arguments);
- }
- },
- /**
- * @private
- */
- _goToPage(path, website, options = {}) {
- this.do_action('website.website_preview', {
- additional_context: {
- params: {
- path: path,
- website_id: website || '',
- ...options,
- }
- },
+ this.state = useState({
+ activeWebsite: this.websiteSelection[0],
});
- },
- /**
- * @private
- */
- async _addDialog(record, mode = '') {
- this._pagePropertiesDialog = new ComponentWrapper(this, PagePropertiesDialogManager, {
- resId: record.data.id,
- onClose: this._onCloseDialog.bind(this),
- onRecordSaved: this.reload.bind(this),
- mode: mode,
- });
- await this._pagePropertiesDialog.mount(this.el);
- },
- /**
- * Removes page properties wrapper when dialog is closed.
- *
- * @private
- */
- _onCloseDialog() {
- if (this._pagePropertiesDialog) {
- this._pagePropertiesDialog.destroy();
- }
- this._pagePropertiesDialog = undefined;
- },
- /**
- * @private
- */
- _getRenderContext() {
- return this._rpc({
- model: 'website',
- method: 'search_read',
- fields: ['id', 'name'],
- }).then((result) => {
- this.websites = [{id: 0, name: 'All Websites'}, ...result];
- });
- },
- /**
- * @private
- */
- _getWebsite(id) {
- return this.websites.find(website => website.id === id);
- },
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * @override
- */
- _onOpenRecord(event) {
- const record = this.model.get(event.data.id, {raw: true});
- this._goToPage(record.data.url, record.data.website_id);
- },
- _onButtonClick: async function (event) {
- const activeWebsite = this._getWebsite(parseInt(event.target.dataset.websiteId));
- this.$buttons[0].querySelector('.o_website_active').textContent = activeWebsite.name;
- this.reload({
- domain: activeWebsite.id ? [['website_id', '=', activeWebsite.id]] : [],
+ onWillStart(async () => {
+ await this.website.fetchWebsites();
+ this.websiteSelection.push(...this.website.websites);
});
}
-});
-const WebsitePageListView = ListView.extend({
- config: _.extend({}, ListView.prototype.config, {
- Controller: WebsitePageListController,
- }),
-});
+ /**
+ * @override
+ */
+ onClickCreate() {
+ if (this.props.resModel === 'website.page') {
+ return this.dialogService.add(AddPageDialog, {
+ addPage: async (name, addMenu) => {
+ // TODO this is duplicated code from new_content.js, this
+ // should be shared somehow.
+ // FIXME this always create on website 1 whatever we do.
+ const url = `/website/add/${encodeURIComponent(name)}`;
+ const data = await this.http.post(url, { 'add_menu': addMenu || '', csrf_token });
+ if (data.view_id) {
+ this.actionService.doAction({
+ 'res_model': 'ir.ui.view',
+ 'res_id': data.view_id,
+ 'views': [[false, 'form']],
+ 'type': 'ir.actions.act_window',
+ 'view_mode': 'form',
+ });
+ } else {
+ this.website.goToWebsite({ path: data.url, edition: true });
+ }
+ },
+ });
+ }
+ const action = this.props.context.create_action;
+ if (action) {
+ if (/^\//.test(action)) {
+ window.location.replace(action);
+ return;
+ }
+ this.actionService.doAction(action, {
+ onClose: (data) => {
+ if (data) {
+ this.website.goToWebsite({path: data.path});
+ }
+ },
+ });
+ }
+ }
-viewRegistry.add('website_page_list', WebsitePageListView);
+ onSelectWebsite(website) {
+ this.state.activeWebsite = website;
+ }
+}
+PageListController.template = `website.PageListView`;
-export default {
- WebsitePageListController: WebsitePageListController,
- WebsitePageListView: WebsitePageListView,
+export class PageListRenderer extends listView.Renderer {
+ /**
+ * The goal here is to tweak the renderer to display pages following some
+ * rules:
+ * - All websites (props.activeWebsite.id === 0):
+ * -> Show all generic/specific pages.
+ * - A website is selected:
+ * -> Display website-specific pages & generic ones (only those without
+ * specific clones).
+ */
+ pageFilter(record, records) {
+ return !this.props.activeWebsite.id
+ || this.props.activeWebsite.id === record.data.website_id[0]
+ || !record.data.website_id[0] && records.filter(rec => rec.data.url === record.data.url).length === 1;
+ }
+}
+PageListRenderer.props = [
+ ...listView.Renderer.props,
+ "activeWebsite",
+];
+PageListRenderer.recordRowTemplate = "website.PageListRenderer.RecordRow";
+
+export const PageListView = {
+ ...listView,
+ Renderer: PageListRenderer,
+ Controller: PageListController,
};
+
+registry.category("views").add("website_pages_list", PageListView);
diff --git a/addons/website/static/src/components/views/page_list.xml b/addons/website/static/src/components/views/page_list.xml
index 49bc995364d..9949e251d05 100644
--- a/addons/website/static/src/components/views/page_list.xml
+++ b/addons/website/static/src/components/views/page_list.xml
@@ -1,18 +1,42 @@
-
-
-
-
-
+
+
+
+
+
+
+
+ pageFilter(record, list.records)
+
+
+
+
+
+ state.activeWebsite
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/website/static/src/systray_items/new_content.js b/addons/website/static/src/systray_items/new_content.js
index bbc627c926e..2a633017d90 100644
--- a/addons/website/static/src/systray_items/new_content.js
+++ b/addons/website/static/src/systray_items/new_content.js
@@ -148,14 +148,14 @@ export class NewContentModal extends Component {
this.dialogs.add(AddPageDialog, {
addPage: async (name, addMenu) => {
const url = `/website/add/${encodeURIComponent(name)}`;
- const data = await this.http.post(url, { add_menu: addMenu || '', csrf_token });
+ const data = await this.http.post(url, { 'add_menu': addMenu || '', csrf_token });
if (data.view_id) {
this.action.doAction({
- res_model: 'ir.ui.view',
- res_id: data.view_id,
- views: [[false, 'form']],
- type: 'ir.actions.act_window',
- view_mode: 'form',
+ 'res_model': 'ir.ui.view',
+ 'res_id': data.view_id,
+ 'views': [[false, 'form']],
+ 'type': 'ir.actions.act_window',
+ 'view_mode': 'form',
});
} else {
this.website.goToWebsite({ path: data.url, edition: true });
diff --git a/addons/website/views/website_pages_views.xml b/addons/website/views/website_pages_views.xml
index aad6e7707c6..5f7da5b6bd9 100644
--- a/addons/website/views/website_pages_views.xml
+++ b/addons/website/views/website_pages_views.xml
@@ -74,25 +74,24 @@
website.page.list
website.page
-
+
-
+
+
-
-
+
+
+
-
-
+
+
+
+
-
-
-
-
-
-
+