diff --git a/addons/website/__manifest__.py b/addons/website/__manifest__.py
index d94645bfde4..118ce911c2e 100644
--- a/addons/website/__manifest__.py
+++ b/addons/website/__manifest__.py
@@ -235,7 +235,6 @@
'website/static/src/js/menu/debug_menu.js',
'website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js',
'website/static/src/systray_items/mobile_preview.js',
- 'website/static/src/systray_items/new_content.js',
'website/static/src/components/dialog/seo.js',
'website/static/src/systray_items/translate_website.js',
'website/static/src/js/set_view_track.js',
diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py
index 0a76e721883..deb5eff4006 100644
--- a/addons/website/controllers/main.py
+++ b/addons/website/controllers/main.py
@@ -303,6 +303,16 @@ class Website(Home):
]
}
+ @http.route('/website/get_modules_info', type='json', auth="user")
+ def get_modules_info(self, xml_ids):
+ def get_module_info(xml_id):
+ module_info = request.env.ref(xml_id)
+ return {'id': module_info.id, 'name': module_info.shortdesc}
+ modules_info = {}
+ for xml_id in xml_ids:
+ modules_info[xml_id] = get_module_info(xml_id)
+ return modules_info
+
@http.route('/website/snippet/filters', type='json', auth='public', website=True)
def get_dynamic_filter(self, filter_id, template_key, limit=None, search_domain=None, with_sample=False):
dynamic_filter = request.env['website.snippet.filter'].sudo().search(
diff --git a/addons/website/static/src/client_actions/website_preview/website_preview.js b/addons/website/static/src/client_actions/website_preview/website_preview.js
index c5716b08dfb..1c8f8762a85 100644
--- a/addons/website/static/src/client_actions/website_preview/website_preview.js
+++ b/addons/website/static/src/client_actions/website_preview/website_preview.js
@@ -22,6 +22,7 @@ export class WebsitePreview extends Component {
useEffect(() => {
this.websiteService.currentWebsiteId = this.websiteId;
+ this.websiteService.context.showNewContentModal = this.props.action.context.params && this.props.action.context.params.display_new_content;
return () => this.websiteService.currentWebsiteId = null;
}, () => [this.props.action.context.params]);
}
@@ -83,6 +84,10 @@ export class WebsitePreview extends Component {
this.currentUrl = this.iframe.el.contentDocument.location.href;
history.replaceState({}, this.props.action.display_name, this.currentUrl);
+ this.websiteService.currentMetadata = {
+ path: this.currentUrl,
+ };
+
// Before leaving the iframe, its content is replicated on an
// underlying iframe, to avoid for white flashes (visible on
// Chrome Windows/Linux).
diff --git a/addons/website/static/src/components/navbar/navbar.js b/addons/website/static/src/components/navbar/navbar.js
index f70cddd517a..05c9e6baf3b 100644
--- a/addons/website/static/src/components/navbar/navbar.js
+++ b/addons/website/static/src/components/navbar/navbar.js
@@ -3,7 +3,6 @@
import { NavBar } from '@web/webclient/navbar/navbar';
import { useService, useBus } from '@web/core/utils/hooks';
import { registry } from "@web/core/registry";
-
import { patch } from 'web.utils';
const websiteSystrayRegistry = registry.category('website_systray');
diff --git a/addons/website/static/src/scss/website.ui.scss b/addons/website/static/src/scss/website.ui.scss
index 5b8cc3cd9c1..e73fa3a9cb5 100644
--- a/addons/website/static/src/scss/website.ui.scss
+++ b/addons/website/static/src/scss/website.ui.scss
@@ -471,83 +471,6 @@ body .modal {
}
}
-// ADD NEW PAGE MODAL
-
-.o_new_content_open {
- // Kill the scroll on the body
- overflow: hidden;
-}
-
-#o_new_content_menu_choices {
- @include o-w-preserve-base;
- @include o-position-absolute($o-navbar-height, 0, 0, 0);
- position: fixed;
- display: flex;
- overflow: auto;
- background-color: rgba(0, 0, 0, 0.8);
- font-family: $o-we-font-family;
-
- &::before {
- content: " ";
- @include o-position-absolute(0, 0, 0, 0);
- z-index: -1;
- pointer-events: none;
- }
-
- .container {
- max-width: 720px;
- margin: auto;
- }
-
- .o_new_content_element {
- opacity: 0;
- animation: fadeInDownSmall 1s forwards;
-
- .o_uninstalled_module {
- filter: brightness(70%) contrast(60%);
-
- }
-
- .module_icon {
- display: block;
- margin: auto;
- }
-
- a {
- display: block;
- font-size: 34px;
- text-align: center;
- text-decoration: none;
-
- i {
- width: 110px;
- height: 110px;
- border: 3px solid lighten(#2C2C36, 10%);
- border-radius: 100%;
- line-height: 104px;
- background-color: #2C2C36;
- color: white;
-
- transition: all 0.5s cubic-bezier(0.19, 1, 0.22, 1) 0s;
- }
- p {
- color: white;
- margin-top: 0.7em;
- font-size: 0.5em;
- }
-
- &:hover, &:focus {
- text-decoration: none;
- outline: none; // remove ugly dotted border on Firefox
- i {
- border-color: #1cc1a9;
- box-shadow: 0 0 10px rgba(28, 193, 169, 0.46);
- }
- }
- }
- }
-}
-
// LOGIN FORM
.oe_login_form, .oe_signup_form, .oe_reset_password_form {
max-width: 300px;
diff --git a/addons/website/static/src/services/website_service.js b/addons/website/static/src/services/website_service.js
index 4bd8d15a2b1..0b6a1d95fcf 100644
--- a/addons/website/static/src/services/website_service.js
+++ b/addons/website/static/src/services/website_service.js
@@ -2,6 +2,8 @@
import { registry } from '@web/core/registry';
+const { reactive } = owl;
+
const websiteSystrayRegistry = registry.category('website_systray');
const unslugHtmlDataObject = (repr) => {
@@ -20,6 +22,10 @@ export const websiteService = {
async start(env, { orm, action }) {
let websites = [];
let currentWebsiteId;
+ let currentMetadata = {};
+ const context = reactive({
+ showNewContentModal: false,
+ });
const setCurrentWebsiteId = id => {
currentWebsiteId = id;
@@ -29,12 +35,22 @@ export const websiteService = {
set currentWebsiteId(id) {
setCurrentWebsiteId(id);
},
+ set currentMetadata(metadata) {
+ currentMetadata = metadata;
+ },
get currentWebsite() {
- return websites.find(website => website.id === currentWebsiteId);
+ const currentWebsite = websites.find(w => w.id === currentWebsiteId);
+ if (currentWebsite) {
+ currentWebsite.metadata = currentMetadata;
+ }
+ return currentWebsite;
},
get websites() {
return websites;
},
+ get context() {
+ return context;
+ },
goToWebsite({ websiteId = currentWebsiteId || websites[0].id, path = '/' }) {
action.doAction('website.website_preview', {
clearBreadcrumbs: true,
diff --git a/addons/website/static/src/systray_items/new_content.js b/addons/website/static/src/systray_items/new_content.js
index 1eac6393be9..52fafd2dd92 100644
--- a/addons/website/static/src/systray_items/new_content.js
+++ b/addons/website/static/src/systray_items/new_content.js
@@ -1,354 +1,204 @@
-odoo.define('website.newMenu', function (require) {
-'use strict';
+/** @odoo-module **/
-var core = require('web.core');
-var Dialog = require('web.Dialog');
-var websiteNavbarData = require('website.navbar');
-var wUtils = require('website.utils');
-var tour = require('web_tour.tour');
+import { registry } from '@web/core/registry';
+import { useService } from '@web/core/utils/hooks';
+import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
+import { useHotkey } from "@web/core/hotkeys/hotkey_hook";
-const { registry } = require("@web/core/registry");
+const { Component, xml, useState, onWillStart } = owl;
-const {qweb, _t} = core;
+export const MODULE_STATUS = {
+ NOT_INSTALLED: 'NOT_INSTALLED',
+ INSTALLING: 'INSTALLING',
+ FAILED_TO_INSTALL: 'FAILED_TO_INSTALL',
+ INSTALLED: 'INSTALLED',
+};
-var enableFlag = 'enable_new_content';
+class NewContentElement extends Component {
+ setup() {
+ this.MODULE_STATUS = MODULE_STATUS;
+ }
-var NewContentMenu = websiteNavbarData.WebsiteNavbarActionWidget.extend({
- xmlDependencies: ['/website/static/src/xml/website.editor.xml'],
- actions: _.extend({}, websiteNavbarData.WebsiteNavbarActionWidget.prototype.actions || {}, {
- close_all_widgets: '_handleCloseDemand',
- new_page: '_createNewPage',
- }),
- events: _.extend({}, websiteNavbarData.WebsiteNavbarActionWidget.prototype.events || {}, {
- 'click': '_onBackgroundClick',
- 'click [data-module-id]': '_onModuleIdClick',
- 'keydown': '_onBackgroundKeydown',
- }),
- // allow text to be customized with inheritance
- newContentText: {
- failed: _t('Failed to install "%s"'),
- installInProgress: _t("The installation of an App is already in progress."),
- installNeeded: _t('Do you want to install the "%s" App?'),
- installPleaseWait: _t('Installing "%s"'),
- },
-
- /**
- * Prepare the navigation and find the modules to install.
- * Move not installed module buttons after installed modules buttons,
- * but keep the original index to be able to move back the pending install
- * button at its final position, so the user can click at the same place.
- *
- * @override
- */
- start: function () {
- this.pendingInstall = false;
- this.$newContentMenuChoices = this.$('#o_new_content_menu_choices');
-
- var $modules = this.$newContentMenuChoices.find('.o_new_content_element');
- _.each($modules, function (el, index) {
- var $el = $(el);
- $el.data('original-index', index);
- if ($el.data('module-id')) {
- $el.appendTo($el.parent());
- $el.find('a i, a p').addClass('o_uninstalled_module');
- }
- });
-
- this.$firstLink = this.$newContentMenuChoices.find('a:eq(0)');
- this.$lastLink = this.$newContentMenuChoices.find('a:last');
-
- if ($.deparam.querystring()[enableFlag] !== undefined) {
- Object.keys(tour.tours).forEach(
- el => {
- let element = tour.tours[el];
- if (element.steps[0].trigger == '#new-content-menu > a'
- && !element.steps[0].extra_trigger) {
- element.steps[0].auto = true;
- }
- }
- );
- this._showMenu();
- }
- this.$loader = $(qweb.render('website.new_content_loader'));
- return this._super.apply(this, arguments);
- },
-
- //--------------------------------------------------------------------------
- // Actions
- //--------------------------------------------------------------------------
-
- /**
- * Asks the user information about a new page to create, then creates it and
- * redirects the user to this new page.
- *
- * @private
- * @returns {Promise} Unresolved if there is a redirection
- */
- _createNewPage: function () {
- return wUtils.prompt({
- id: 'editor_new_page',
- window_title: _t("New Page"),
- input: _t("Page Title"),
- init: function () {
- var $group = this.$dialog.find('div.form-group');
- $group.removeClass('mb0');
-
- var $add = $('
', {'class': 'form-group mb0 row'})
- .append($('', {'class': 'offset-md-3 col-md-9 text-left'})
- .append(qweb.render('website.components.switch', {id: 'switch_addTo_menu', label: _t("Add to menu")})));
- $add.find('input').prop('checked', true);
- $group.after($add);
- }
- }).then(function (result) {
- var val = result.val;
- var $dialog = result.dialog;
- if (!val) {
- return;
- }
- var url = '/website/add/' + encodeURIComponent(val);
- wUtils.sendRequest(url, {
- add_menu: $dialog.find('input[type="checkbox"]').is(':checked') || '',
- });
- return new Promise(function () {});
- });
- },
- /**
- * @private
- */
- _handleCloseDemand: function () {
- this._hideMenu();
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * Set the focus on the first link
- *
- * @private
- */
- _focusFirstLink: function () {
- this.$firstLink.focus();
- },
- /**
- * Set the focus on the last link
- *
- * @private
- */
- _focusLastLink: function () {
- this.$lastLink.focus();
- },
- /**
- * Hide the menu
- *
- * @private
- */
- _hideMenu: function () {
- this.shown = false;
- this.$newContentMenuChoices.addClass('o_hidden');
- $('body').removeClass('o_new_content_open');
- },
- /**
- * Install a module
- *
- * @private
- * @param {number} moduleId: the module to install
- * @return {Promise}
- */
- _install: function (moduleId) {
- this.pendingInstall = true;
- $('body').css('pointer-events', 'none');
- return this._rpc({
- model: 'ir.module.module',
- method: 'button_immediate_install',
- args: [[moduleId]],
- }).guardedCatch(function () {
- $('body').css('pointer-events', '');
- });
- },
- /**
- * Show the menu
- *
- * @private
- * @returns {Promise}
- */
- _showMenu: function () {
- var self = this;
- return new Promise(function (resolve, reject) {
- self.trigger_up('action_demand', {
- actionName: 'close_all_widgets',
- onSuccess: resolve,
- onFailure: reject,
- });
- }).then(function () {
- self.firstTab = true;
- self.shown = true;
- self.$newContentMenuChoices.removeClass('o_hidden');
- $('body').addClass('o_new_content_open');
- self.$('> a').focus();
- });
- },
- /**
- * Called to add loader element in DOM.
- *
- * @param {string} moduleName
- * @private
- */
- _addLoader(moduleName) {
- const newContentLoaderText = _.str.sprintf(_t("Building your %s"), moduleName);
- this.$loader.find('#new_content_loader_text').replaceWith(newContentLoaderText);
- $('body').append(this.$loader);
- },
- /**
- * @private
- */
- _removeLoader() {
- this.$loader.remove();
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * Called when the menu's toggle button is clicked:
- * -> Opens the menu and reset the tab navigation (if closed)
- * -> Close the menu (if open)
- * Called when a click outside the menu's options occurs -> Close the menu
- *
- * @private
- * @param {Event} ev
- */
- _onBackgroundClick: function (ev) {
- if (this.$newContentMenuChoices.hasClass('o_hidden')) {
- this._showMenu();
- } else {
- this._hideMenu();
- }
- },
- /**
- * Called when a keydown occurs:
- * ESC -> Closes the modal
- * TAB -> Navigation (captured in the modal)
- *
- * @private
- * @param {Event} ev
- */
- _onBackgroundKeydown: function (ev) {
- if (!this.shown) {
- return;
- }
- switch (ev.which) {
- case $.ui.keyCode.ESCAPE:
- this._hideMenu();
- ev.stopPropagation();
- break;
- case $.ui.keyCode.TAB:
- if (ev.shiftKey) {
- if (this.firstTab || document.activeElement === this.$firstLink[0]) {
- this._focusLastLink();
- ev.preventDefault();
- }
- } else {
- if (this.firstTab || document.activeElement === this.$lastLink[0]) {
- this._focusFirstLink();
- ev.preventDefault();
- }
- }
- this.firstTab = false;
- break;
- }
- },
- /**
- * Open the install dialog related to an element:
- * - open the dialog depending on access right and another pending install
- * - if ok to install, prepare the install action:
- * - call the proper action on click
- * - change the button text and style
- * - handle the result (reload on the same page or error)
- *
- * @private
- * @param {Event} ev
- */
- _onModuleIdClick: function (ev) {
- var self = this;
- var $el = $(ev.currentTarget);
- var $i = $el.find('a i');
- var $p = $el.find('a p');
-
- var title = $p.text();
- var content = '';
- var buttons;
-
- var moduleId = $el.data('module-id');
- var name = $el.data('module-shortdesc');
-
- ev.stopPropagation();
+ onClick(ev) {
ev.preventDefault();
+ ev.stopPropagation();
+ this.props.onClick();
+ }
+}
+NewContentElement.template = "website.NewContentElement";
+NewContentElement.props = {
+ name: { type: String, optional: true },
+ title: String,
+ onClick: Function,
+ status: { type: String, optional: true },
+ slots: Object,
+};
+NewContentElement.defaultProps = {
+ status: MODULE_STATUS.INSTALLED,
+};
- if (this.pendingInstall) {
- content = this.newContentText.installInProgress;
+export class NewContentModal extends Component {
+ setup() {
+ this.user = useService('user');
+ this.orm = useService('orm');
+ this.rpc = useService('rpc');
+ this.dialogs = useService('dialog');
+ this.website = useService('website');
+ this.isSystem = this.user.isSystem;
+
+ this.newContentText = {
+ failed: this.env._t('Failed to install "%s"'),
+ installInProgress: this.env._t("The installation of an App is already in progress."),
+ installNeeded: this.env._t('Do you want to install the "%s" App?'),
+ installPleaseWait: this.env._t('Installing "%s"'),
+ };
+
+ this.state = useState({
+ newContentElements: [
+ {
+ moduleXmlId: 'base.module_website_blog',
+ status: MODULE_STATUS.NOT_INSTALLED,
+ icon: xml``,
+ title: this.env._t('Blog Post'),
+ },
+ {
+ moduleXmlId: 'base.module_website_event',
+ status: MODULE_STATUS.NOT_INSTALLED,
+ icon: xml``,
+ title: this.env._t('Event'),
+ },
+ {
+ moduleXmlId: 'base.module_website_forum',
+ status: MODULE_STATUS.NOT_INSTALLED,
+ icon: xml``,
+ redirectUrl: '/forum',
+ title: this.env._t('Forum'),
+ },
+ {
+ moduleXmlId: 'base.module_website_hr_recruitment',
+ status: MODULE_STATUS.NOT_INSTALLED,
+ icon: xml``,
+ title: this.env._t('Job Offer'),
+ },
+ {
+ moduleXmlId: 'base.module_website_sale',
+ status: MODULE_STATUS.NOT_INSTALLED,
+ icon: xml``,
+ title: this.env._t('Product'),
+ },
+ {
+ moduleXmlId: 'base.module_website_slides',
+ status: MODULE_STATUS.NOT_INSTALLED,
+ icon: xml``,
+ title: this.env._t('Course'),
+ },
+ {
+ moduleXmlId: 'base.module_website_livechat',
+ status: MODULE_STATUS.NOT_INSTALLED,
+ icon: xml``,
+ title: this.env._t('Livechat Widget'),
+ redirectUrl: '/livechat'
+ },
+ ]
+ });
+
+ this.websiteContext = useState(this.website.context);
+
+ onWillStart(async () => {
+ this.isDesigner = await this.user.hasGroup('website.group_website_designer');
+
+ const xmlIds = this.state.newContentElements.filter(({status}) => status === MODULE_STATUS.NOT_INSTALLED).map(({moduleXmlId}) => moduleXmlId);
+
+ this.modulesInfo = await this.rpc('/website/get_modules_info', {xml_ids: xmlIds});
+ });
+
+ useHotkey('escape', () => {
+ if (this.websiteContext.showNewContentModal) {
+ this.websiteContext.showNewContentModal = false;
+ }
+ });
+ }
+
+ get sortedNewContentElements() {
+ return this.state.newContentElements.filter(({status}) => status !== MODULE_STATUS.NOT_INSTALLED).concat(this.state.newContentElements.filter(({status}) => status === MODULE_STATUS.NOT_INSTALLED));
+ }
+
+ createNewPage() {}
+
+ async installModule(id, redirectUrl) {
+ await this.orm.call(
+ 'ir.module.module',
+ 'button_immediate_install',
+ [id],
+ );
+ if (redirectUrl) {
+ window.location.replace(redirectUrl);
} else {
- content = _.str.sprintf(this.newContentText.installNeeded, name);
- buttons = [{
- text: _t("Install"),
- classes: 'btn-primary',
- close: true,
- click: function () {
- // move the element where it will be after installation
- var $finalPosition = self.$newContentMenuChoices
- .find('.o_new_content_element:not([data-module-id])')
- .filter(function () {
- return $(this).data('original-index') < $el.data('original-index');
- }).last();
- if ($finalPosition) {
- $el.fadeTo(400, 0, function () {
- // if once installed, button disapeear, don't need to move it.
- if (!$el.hasClass('o_new_content_element_once')) {
- $el.insertAfter($finalPosition);
- }
- // change style to use spinner
- $i.removeClass()
- .addClass('fa fa-spin fa-circle-o-notch fa-spin')
- .css('background-image', 'none');
- $p.removeClass('o_uninstalled_module')
- .text(_.str.sprintf(self.newContentText.installPleaseWait, name));
- $el.fadeTo(1000, 1);
- self._addLoader(name);
- });
- }
+ const { id, metadata: { path } } = this.website.currentWebsite;
+ // A reload is needed after installing a new module, to instantiate
+ // a NewContentModal with patches from the installed module.
+ window.location.replace(`/web#action=website.website_preview&website_id=${id}&path=${encodeURIComponent(path)}&display_new_content=true`);
+ }
+ }
- self._install(moduleId).then(function () {
- var origin = window.location.origin;
- var redirectURL = $el.find('a').data('url') || (window.location.pathname + '?' + enableFlag);
- window.location.href = origin + redirectURL;
- self._removeLoader();
- }, function () {
- $i.removeClass()
- .addClass('fa fa-exclamation-triangle');
- $p.text(_.str.sprintf(self.newContentText.failed, name));
- });
- }
- }, {
- text: _t("Cancel"),
- close: true,
- }];
+ onClickNewContent(element) {
+ if (element.createNewContent) {
+ return element.createNewContent();
}
- new Dialog(this, {
- title: title,
- size: 'medium',
- $content: $('', {text: content}),
- buttons: buttons
- }).open();
- },
-});
+ const {id, name} = this.modulesInfo[element.moduleXmlId];
+ const dialogProps = {
+ title: this.env._t("Install"),
+ body: _.str.sprintf(this.newContentText.installNeeded, name),
+ confirm: async () => {
+ // Update the NewContentElement with installing icon and text.
+ this.state.newContentElements = this.state.newContentElements.map(el => {
+ if (el.moduleXmlId === element.moduleXmlId) {
+ el.status = MODULE_STATUS.INSTALLING;
+ el.icon = xml``;
+ el.title = _.str.sprintf(this.newContentText.installPleaseWait, name);
+ }
+ return el;
+ });
+ try {
+ await this.installModule(id, element.redirectUrl);
+ } catch (error) {
+ // Update the NewContentElement with failure icon and text.
+ this.state.newContentElements = this.state.newContentElements.map(el => {
+ if (el.moduleXmlId === element.moduleXmlId) {
+ el.status = MODULE_STATUS.FAILED_TO_INSTALL;
+ el.icon = xml``;
+ el.title = _.str.sprintf(this.newContentText.failed, name);
+ }
+ return el;
+ });
+ console.error(error);
+ }
+ },
+ cancel: () => {},
+ };
+ this.dialogs.add(ConfirmationDialog, dialogProps);
+ }
+}
+NewContentModal.template = "website.NewContentModal";
+NewContentModal.components = { NewContentElement };
-registry.category("website_navbar_widgets").add("NewContentMenu", {
- Widget: NewContentMenu,
- selector: '.o_new_content_menu',
-});
+class NewContentSystray extends Component {
+ setup() {
+ this.rpc = useService('rpc');
+ this.website = useService('website');
+ this.websiteContext = useState(this.website.context);
+ }
-return NewContentMenu;
-});
+ onClick() {
+ this.websiteContext.showNewContentModal = !this.websiteContext.showNewContentModal;
+ }
+}
+NewContentSystray.template = "website.NewContentSystray";
+NewContentSystray.components = { NewContentModal };
+
+export const systrayItem = {
+ Component: NewContentSystray,
+};
+
+registry.category("website_systray").add("NewContent", systrayItem, { sequence: 10 });
diff --git a/addons/website/static/src/systray_items/new_content.scss b/addons/website/static/src/systray_items/new_content.scss
new file mode 100644
index 00000000000..5153ce089e6
--- /dev/null
+++ b/addons/website/static/src/systray_items/new_content.scss
@@ -0,0 +1,80 @@
+@keyframes fadeInDownSmall {
+ 0% {
+ opacity: 0;
+ transform: translate(0, -5px);
+ }
+ 100% {
+ opacity: 1;
+ transform: translate(0, 0);
+ }
+}
+
+#o_new_content_menu_choices {
+ @include o-position-absolute(0, 0, 0, 0);
+ position: fixed;
+ display: flex;
+ overflow: auto;
+ background-color: rgba(0, 0, 0, 0.8);
+ font-family: $o-we-font-family;
+ z-index: $zindex-fixed - 1;
+
+ &::before {
+ content: " ";
+ @include o-position-absolute(0, 0, 0, 0);
+ z-index: -1;
+ pointer-events: none;
+ }
+
+ .container {
+ max-width: 720px;
+ margin: auto;
+ }
+
+ .o_new_content_element {
+ opacity: 0;
+ animation: fadeInDownSmall 1s forwards;
+
+ .o_uninstalled_module {
+ filter: brightness(70%) contrast(60%);
+
+ }
+
+ .module_icon {
+ display: block;
+ margin: auto;
+ }
+
+ a {
+ display: block;
+ font-size: 34px;
+ text-align: center;
+ text-decoration: none;
+
+ i {
+ width: 110px;
+ height: 110px;
+ border: 3px solid lighten(#2C2C36, 10%);
+ border-radius: 100%;
+ line-height: 104px;
+ background-color: #2C2C36;
+ color: white;
+
+ transition: all 0.5s cubic-bezier(0.19, 1, 0.22, 1) 0s;
+ }
+ p {
+ color: white;
+ margin-top: 0.7em;
+ font-size: 0.5em;
+ }
+
+ &:hover, &:focus {
+ text-decoration: none;
+ outline: none; // remove ugly dotted border on Firefox
+ i {
+ border-color: #1cc1a9;
+ box-shadow: 0 0 10px rgba(28, 193, 169, 0.46);
+ }
+ }
+ }
+ }
+}
diff --git a/addons/website/static/src/systray_items/new_content.xml b/addons/website/static/src/systray_items/new_content.xml
new file mode 100644
index 00000000000..19ae618fa10
--- /dev/null
+++ b/addons/website/static/src/systray_items/new_content.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/website_blog/__manifest__.py b/addons/website_blog/__manifest__.py
index 3db7e32d2a3..a050761700d 100644
--- a/addons/website_blog/__manifest__.py
+++ b/addons/website_blog/__manifest__.py
@@ -43,6 +43,9 @@
'website_blog/static/src/js/contentshare.js',
'website_blog/static/src/js/website_blog.js',
],
+ 'web.assets_backend': [
+ 'website_blog/static/src/js/systray_items/new_content.js',
+ ],
},
'license': 'LGPL-3',
}
diff --git a/addons/website_blog/static/src/js/systray_items/new_content.js b/addons/website_blog/static/src/js/systray_items/new_content.js
index 7cc9aa09f33..a9c015f3522 100644
--- a/addons/website_blog/static/src/js/systray_items/new_content.js
+++ b/addons/website_blog/static/src/js/systray_items/new_content.js
@@ -1,57 +1,16 @@
-odoo.define('website_blog.new_blog_post', function (require) {
-'use strict';
+/** @odoo-module **/
-var core = require('web.core');
-var wUtils = require('website.utils');
-var WebsiteNewMenu = require('website.newMenu');
+import { NewContentModal, MODULE_STATUS } from '@website/systray_items/new_content';
+import { patch } from 'web.utils';
-var _t = core._t;
+patch(NewContentModal.prototype, 'website_blog_new_content', {
+ setup() {
+ this._super();
-WebsiteNewMenu.include({
- actions: _.extend({}, WebsiteNewMenu.prototype.actions || {}, {
- new_blog_post: '_createNewBlogPost',
- }),
-
- //--------------------------------------------------------------------------
- // Actions
- //--------------------------------------------------------------------------
-
- /**
- * Asks the user information about a new blog post to create, then creates
- * it and redirects the user to this new post.
- *
- * @private
- * @returns {Promise} Unresolved if there is a redirection
- */
- _createNewBlogPost: function () {
- return this._rpc({
- model: 'blog.blog',
- method: 'search_read',
- args: [wUtils.websiteDomain(this), ['name']],
- }).then(function (blogs) {
- if (blogs.length === 1) {
- document.location = '/blog/' + blogs[0]['id'] + '/post/new';
- return new Promise(function () {});
- } else if (blogs.length > 1) {
- return wUtils.prompt({
- id: 'editor_new_blog',
- window_title: _t("New Blog Post"),
- select: _t("Select Blog"),
- init: function (field) {
- return _.map(blogs, function (blog) {
- return [blog['id'], blog['name']];
- });
- },
- }).then(function (result) {
- var blog_id = result.val;
- if (!blog_id) {
- return;
- }
- document.location = '/blog/' + blog_id + '/post/new';
- return new Promise(function () {});
- });
- }
- });
+ const newBlogElement = this.state.newContentElements.find(element => element.moduleXmlId === 'base.module_website_blog');
+ newBlogElement.createNewContent = () => this.createNewBlogPost();
+ newBlogElement.status = MODULE_STATUS.INSTALLED;
},
-});
+
+ createNewBlogPost() {}
});
diff --git a/addons/website_event/__manifest__.py b/addons/website_event/__manifest__.py
index 8a6650cddda..8d1f25dffdd 100644
--- a/addons/website_event/__manifest__.py
+++ b/addons/website_event/__manifest__.py
@@ -66,6 +66,9 @@
'website.assets_wysiwyg': [
'/website_event/static/src/snippets/s_events/options.js',
],
+ 'web.assets_backend': [
+ 'website_event/static/src/js/systray_items/new_content.js',
+ ],
},
'license': 'LGPL-3',
}
diff --git a/addons/website_event/static/src/js/systray_items/new_content.js b/addons/website_event/static/src/js/systray_items/new_content.js
index 4b61e93dd88..c05c1aa8270 100644
--- a/addons/website_event/static/src/js/systray_items/new_content.js
+++ b/addons/website_event/static/src/js/systray_items/new_content.js
@@ -1,277 +1,16 @@
-odoo.define('website_event.editor', function (require) {
-"use strict";
+/** @odoo-module **/
-var core = require('web.core');
-var Dialog = require('web.Dialog');
-var time = require('web.time');
-var WebsiteNewMenu = require('website.newMenu');
-var _t = core._t;
+import { NewContentModal, MODULE_STATUS } from '@website/systray_items/new_content';
+import { patch } from 'web.utils';
-var EventCreateDialog = Dialog.extend({
- xmlDependencies: Dialog.prototype.xmlDependencies.concat(['/website_event/static/src/xml/event_create.xml']),
- template: 'website_event.event_create_dialog',
- events: _.extend({}, Dialog.prototype.events, {
- 'change select[name="event_location"]': '_onLocationChanged',
- 'click .input-group-append': '_onDatetimeClicked',
- }),
- jsLibs: [
- '/web/static/lib/daterangepicker/daterangepicker.js',
- '/web/static/src/legacy/js/libs/daterangepicker.js',
- ],
-
+patch(NewContentModal.prototype, 'website_event_new_content', {
+ setup() {
+ this._super();
- /**
- * @override
- * @param {Object} parent
- * @param {Object} options
- */
- init: function (parent, options) {
- options = _.defaults(options || {}, {
- title: _t("New Event"),
- size: 'medium',
- buttons: [
- {
- text: _t("Create"),
- classes: 'btn-primary',
- click: this._onClickCreate.bind(this),
- },
- {
- text: _t("Discard"),
- close: true
- },
- ]
- });
- this.eventStart = moment();
- this.eventEnd = moment().add(1, "d");
- this._super(parent, options);
+ const newEventElement = this.state.newContentElements.find(element => element.moduleXmlId === 'base.module_website_event');
+ newEventElement.createNewContent = () => this.createNewEvent();
+ newEventElement.status = MODULE_STATUS.INSTALLED;
},
- /**
- * @override
- */
- start: function () {
- var self = this;
- return this._super.apply(this, arguments).then(() => {
- // stat / end datetimes widgets
- self.$('input.daterange-input').each(function () {
- self._initDateRangePicker($(this));
- });
- // address select2 configuration
- self.$('input.o_wevent_js_address_id').select2(self._getSelect2AddressConfig());
- });
- },
-
- /**
- * @param {*} errors
- */
- _display_errors: function (errors) {
- this.$("p.text-danger").toggleClass('d-none', true);
- for (let i = 0; i < errors.length; i++) {
- this.$("#" + errors[i] + " p.text-danger").toggleClass('d-none', false);
- }
- },
-
- _getSelect2AddressConfig: function () {
- var self = this;
- return {
- allowClear: true,
- formatNoMatches: false,
- multiple: false,
- selection_data: false,
- width: '100%',
-
- createSearchChoice: function (name, data) {
- var addedPartner = $(this.opts.element).select2('data');
- if (_.filter(_.union(addedPartner, data), function (partner) {
- return partner.text.toLowerCase().localeCompare(name.toLowerCase()) === 0;
- }).length === 0) {
- return {
- create: true,
- id: _.uniqueId('address_'),
- name: name,
- text: _.str.sprintf(_t('Create "%s"'), name),
- };
- }
- },
- fill_data: function (query, data) {
- var that = this;
- var partners = {results: []};
- _.each(data, function (partner) {
- var contact_address = partner.contact_address.replace(/[\n]+/g, '').trim();
- if (contact_address && that.matcher(query.term, contact_address)) {
- partners.results.push({
- id: partner.id,
- text: contact_address,
- });
- }
- });
- query.callback(partners);
- },
- formatSelection: function (data) {
- if (data.name) {
- data.text = data.name;
- }
- return data.text;
- },
- query: function (query) {
- var that = this;
- // fetch data only once and store it
- if (!this.selection_data) {
- self._rpc({
- model: 'res.partner',
- method: 'search_read',
- fields: ['contact_address'],
- domain: [],
- }).then(function (data) {
- that.fill_data(query, data);
- that.selection_data = data;
- });
- } else {
- this.fill_data(query, this.selection_data);
- }
- }
- }
- },
-
- _getSelect2AddressValues: function () {
- var select2Value = this.$('input.o_wevent_js_address_id').select2('data');
- if (select2Value) {
- if (select2Value.create) {
- return [0, {'name': select2Value.name}];
- } else {
- return [select2Value.id, {}];
- }
- }
- return [];
- },
-
- /**
- *
- * @param {*} $dateGroup
- */
- _initDateRangePicker: function ($dateGroup) {
- var minDate = moment().subtract(1, "d");
- var maxDate = moment().add(200, "y");
- var self = this;
- var textDirection = _t.database.parameters.direction;
-
- $dateGroup.daterangepicker({
- // dates
- endDate: this.eventEnd,
- maxDate: maxDate,
- minDate: minDate,
- startDate: this.eventStart,
- // display
- locale: {
- direction: textDirection,
- format: time.getLangDatetimeFormat().replace(':ss', ''),
- applyLabel: _t('Apply'),
- cancelLabel: _t('Cancel'),
- weekLabel: 'W',
- customRangeLabel: _t('Custom Range'),
- daysOfWeek: moment.weekdaysMin(),
- monthNames: moment.monthsShort(),
- firstDay: moment.localeData().firstDayOfWeek()
- },
- opens: 'left',
- timePicker: true,
- timePicker24Hour: true,
- viewDate: moment(new Date()).hours(minDate.hours()).minutes(minDate.minutes()).seconds(minDate.seconds()).milliseconds(minDate.milliseconds()),
- }, function (start, end, label) {
- self.eventStart = start;
- self.eventEnd = end;
- });
- },
- /**
- * @private
- */
- _prepareFormValues: function () {
- return {
- address_values: this._getSelect2AddressValues(),
- event_start: this.eventStart,
- event_end: this.eventEnd,
- location: this.$('select[name=event_location]').val(),
- name: this.$('input[name=name]').val().trim(),
- };
- },
- /**
- * @private
- * @param {*} values
- */
- _validateForm: function (values) {
- var errors = [];
- if (!values.name){
- errors.push('name');
- }
- if (values.location === 'on_site' && !values.address_values.length) {
- errors.push('address');
- }
- if (!values.event_start || !values.event_end) {
- errors.push('event_dates');
- }
- return errors;
- },
-
- /**
- * @private
- */
- _submitForm: function () {
- var eventValues = this._prepareFormValues();
- var errors = this._validateForm(eventValues)
- console.log(eventValues, errors);
- if (errors.length) {
- this._display_errors(errors);
- return;
- }
- return this._rpc({
- route: '/event/add_event',
- params: eventValues,
- }).then((url) => {
- window.location.href = url;
- return new Promise(function () {});
- });
- },
-
- /**
- * @private
- */
- _onClickCreate: function () {
- return this._submitForm();
- },
-
- /**
- * @private
- * @param {*} ev
- */
- _onLocationChanged: function (ev) {
- this.$('.show_visibility_address').toggleClass('d-none', ev.target.value === 'online');
- },
-});
-
-WebsiteNewMenu.include({
- actions: _.extend({}, WebsiteNewMenu.prototype.actions || {}, {
- new_event: '_createNewEvent',
- }),
-
- //--------------------------------------------------------------------------
- // Actions
- //--------------------------------------------------------------------------
-
- /**
- * Asks the user information about a new event to create, then creates it
- * and redirects the user to this new event.
- *
- * @private
- * @returns {Promise} Unresolved if there is a redirection
- */
- _createNewEvent: function () {
- var self = this;
- var def = new Promise(function (resolve) {
- var dialog = new EventCreateDialog(self, {});
- dialog.open();
- dialog.on('closed', self, resolve);
- });
- return def;
- },
-});
+ createNewEvent() {}
});
diff --git a/addons/website_forum/__manifest__.py b/addons/website_forum/__manifest__.py
index 2ece59da5de..676e7fe4da0 100644
--- a/addons/website_forum/__manifest__.py
+++ b/addons/website_forum/__manifest__.py
@@ -52,6 +52,9 @@ Ask questions, get answers, no distractions
'website_forum/static/src/js/website_forum.js',
'website_forum/static/src/js/website_forum.share.js',
],
+ 'web.assets_backend': [
+ 'website_forum/static/src/js/systray_items/new_content.js',
+ ],
'web.assets_qweb': [
'website_forum/static/src/xml/*.xml',
],
diff --git a/addons/website_forum/static/src/js/systray_items/new_content.js b/addons/website_forum/static/src/js/systray_items/new_content.js
index 74072c592b4..532856fe8a8 100644
--- a/addons/website_forum/static/src/js/systray_items/new_content.js
+++ b/addons/website_forum/static/src/js/systray_items/new_content.js
@@ -1,144 +1,16 @@
-odoo.define('website_forum.editor', function (require) {
-"use strict";
+/** @odoo-module **/
-var core = require('web.core');
-var WebsiteNewMenu = require('website.newMenu');
-var Dialog = require('web.Dialog');
+import { NewContentModal, MODULE_STATUS } from '@website/systray_items/new_content';
+import { patch } from 'web.utils';
-var _t = core._t;
+patch(NewContentModal.prototype, 'website_forum_new_content', {
+ setup() {
+ this._super();
-var ForumCreateDialog = Dialog.extend({
- xmlDependencies: Dialog.prototype.xmlDependencies.concat(
- ['/website_forum/static/src/xml/website_forum_templates.xml']
- ),
- template: 'website_forum.add_new_forum',
- events: _.extend({}, Dialog.prototype.events, {
- 'change input[name="privacy"]': '_onPrivacyChanged',
- }),
+ const newForumElement = this.state.newContentElements.find(element => element.moduleXmlId === 'base.module_website_forum');
+ newForumElement.createNewContent = () => this.createNewForum();
+ newForumElement.status = MODULE_STATUS.INSTALLED;
+ },
- /**
- * @override
- * @param {Object} parent
- * @param {Object} options
- */
- init: function (parent, options) {
- options = _.defaults(options || {}, {
- title: _t("New Forum"),
- size: 'medium',
- buttons: [
- {
- text: _t("Create"),
- classes: 'btn-primary',
- click: this.onCreateClick.bind(this),
- },
- {
- text: _t("Discard"),
- close: true
- },
- ]
- });
- this._super(parent, options);
- },
- start: function () {
- var self = this;
- return this._super.apply(this, arguments).then(function () {
- var $input = self.$('#group_id');
- $input.select2({
- width: '100%',
- allowClear: true,
- formatNoMatches: false,
- multiple: false,
- selection_data: false,
- fill_data: function (query, data) {
- var that = this;
- var tags = {results: []};
- _.each(data, function (obj) {
- if (that.matcher(query.term, obj.display_name)) {
- tags.results.push({id: obj.id, text: obj.display_name});
- }
- });
- query.callback(tags);
- },
- query: function (query) {
- var that = this;
- // fetch data only once and store it
- if (!this.selection_data) {
- self._rpc({
- model: 'res.groups',
- method: 'search_read',
- args: [[], ['display_name']],
- }).then(function (data) {
- that.fill_data(query, data);
- that.selection_data = data;
- });
- } else {
- this.fill_data(query, this.selection_data);
- }
- }
- });
- });
- },
- onCreateClick: function () {
- var $dialog = this.$el;
- var $forumName = $dialog.find('input[name=forum_name]');
- if (!$forumName.val()) {
- $forumName.addClass('border-danger');
- return;
- }
- var $forumPrivacyGroup = $dialog.find('input[name=group_id]');
- var forumPrivacy = $dialog.find('input:radio[name=privacy]:checked').val();
- if (forumPrivacy === 'private' && !$forumPrivacyGroup.val()) {
- this.$("#group-required").removeClass('d-none');
- return;
- }
- var addMenu = ($dialog.find('input[type="checkbox"]').is(':checked'));
- var forumMode = $dialog.find('input:radio[name=mode]:checked').val();
- return this._rpc({
- route: '/forum/new',
- params: {
- forum_name: $forumName.val(),
- forum_mode: forumMode,
- forum_privacy: forumPrivacy,
- forum_privacy_group: $forumPrivacyGroup.val(),
- add_menu: addMenu || "",
- },
- }).then(function (url) {
- window.location.href = url;
- return new Promise(function () {});
- });
- },
- /**
- * @private
- */
- _onPrivacyChanged: function (ev) {
- this.$('.show_visibility_group').toggleClass('d-none', ev.target.value !== 'private');
- },
-});
-
-WebsiteNewMenu.include({
- actions: _.extend({}, WebsiteNewMenu.prototype.actions || {}, {
- new_forum: '_createNewForum',
- }),
-
- //--------------------------------------------------------------------------
- // Actions
- //--------------------------------------------------------------------------
-
- /**
- * Asks the user information about a new forum to create, then creates it
- * and redirects the user to this new forum.
- *
- * @private
- * @returns {Promise} Unresolved if there is a redirection
- */
- _createNewForum: function () {
- var self = this;
- var def = new Promise(function (resolve) {
- var dialog = new ForumCreateDialog(self, {});
- dialog.open();
- dialog.on('closed', self, resolve);
- });
- return def;
- },
-});
+ createNewForum() {}
});
diff --git a/addons/website_hr_recruitment/__manifest__.py b/addons/website_hr_recruitment/__manifest__.py
index b9ac5d18383..783e97d3316 100644
--- a/addons/website_hr_recruitment/__manifest__.py
+++ b/addons/website_hr_recruitment/__manifest__.py
@@ -27,6 +27,9 @@
'web.assets_frontend': [
'website_hr_recruitment/static/src/scss/**/*',
],
+ 'web.assets_backend': [
+ 'website_hr_recruitment/static/src/js/systray_items/new_content.js',
+ ],
'website.assets_editor': [
'website_hr_recruitment/static/src/js/**/*',
],
diff --git a/addons/website_hr_recruitment/static/src/js/systray_items/new_content.js b/addons/website_hr_recruitment/static/src/js/systray_items/new_content.js
new file mode 100644
index 00000000000..2d2e4205576
--- /dev/null
+++ b/addons/website_hr_recruitment/static/src/js/systray_items/new_content.js
@@ -0,0 +1,16 @@
+/** @odoo-module **/
+
+import { NewContentModal, MODULE_STATUS } from '@website/systray_items/new_content';
+import { patch } from 'web.utils';
+
+patch(NewContentModal.prototype, 'website_hr_recruitment_new_content', {
+ setup() {
+ this._super();
+
+ const newJobElement = this.state.newContentElements.find(element => element.moduleXmlId === 'base.module_website_hr_recruitment');
+ newJobElement.createNewContent = () => this.createNewJob();
+ newJobElement.status = MODULE_STATUS.INSTALLED;
+ },
+
+ async createNewJob() {}
+});
diff --git a/addons/website_livechat/__manifest__.py b/addons/website_livechat/__manifest__.py
index d44a5235f9c..dea83dfcf48 100644
--- a/addons/website_livechat/__manifest__.py
+++ b/addons/website_livechat/__manifest__.py
@@ -41,6 +41,7 @@ Allow website visitors to chat with the collaborators. This module also brings a
'website_livechat/static/src/components/*/*.js',
'website_livechat/static/src/components/*/*.scss',
'website_livechat/static/src/models/*.js',
+ 'website_livechat/static/src/js/systray_items/new_content.js',
],
'web.assets_tests': [
'website_livechat/static/tests/tours/**/*',
diff --git a/addons/website_livechat/static/src/js/systray_items/new_content.js b/addons/website_livechat/static/src/js/systray_items/new_content.js
index 67f68bba9d9..46a2b933a97 100644
--- a/addons/website_livechat/static/src/js/systray_items/new_content.js
+++ b/addons/website_livechat/static/src/js/systray_items/new_content.js
@@ -1,46 +1,16 @@
/** @odoo-module **/
-import { _t } from 'web.core';
-import wUtils from 'website.utils';
-import WebsiteNewMenu from 'website.newMenu';
+import { NewContentModal, MODULE_STATUS } from '@website/systray_items/new_content';
+import { patch } from 'web.utils';
-WebsiteNewMenu.include({
- actions: _.extend({}, WebsiteNewMenu.prototype.actions || {}, {
- new_channel: '_createNewChannel',
- }),
+patch(NewContentModal.prototype, 'website_livechat_new_content', {
+ setup() {
+ this._super();
- //--------------------------------------------------------------------------
- // Actions
- //--------------------------------------------------------------------------
-
- /**
- * Asks the user information about a new channel to create, then creates it
- * and redirects the user to this new channel.
- *
- * @private
- * @returns {Promise} Unresolved if there is a redirection
- */
- _createNewChannel: function () {
- var self = this;
- return wUtils.prompt({
- window_title: _t("New Channel"),
- input: _t("Name"),
- }).then(function (result) {
- var name = result.val;
- if (!name) {
- return;
- }
- return self._rpc({
- model: 'im_livechat.channel',
- method: 'create_and_get_website_url',
- args: [[]],
- kwargs: {
- name: name,
- },
- }).then(function (url) {
- window.location.href = url;
- return new Promise(function () {});
- });
- });
+ const newChannelElement = this.state.newContentElements.find(element => element.moduleXmlId === 'base.module_website_livechat');
+ newChannelElement.createNewContent = () => this.createNewChannel();
+ newChannelElement.status = MODULE_STATUS.INSTALLED;
},
+
+ createNewChannel() {}
});
diff --git a/addons/website_sale/__manifest__.py b/addons/website_sale/__manifest__.py
index 4191da76126..fa0fa3fb868 100644
--- a/addons/website_sale/__manifest__.py
+++ b/addons/website_sale/__manifest__.py
@@ -66,6 +66,7 @@
'website_sale/static/src/scss/website_sale_dashboard.scss',
'website_sale/static/src/scss/website_sale_backend.scss',
'website_sale/static/src/js/tours/website_sale_shop_backend.js',
+ 'website_sale/static/src/js/systray_items/new_content.js',
],
'website.assets_wysiwyg': [
'website_sale/static/src/scss/website_sale.editor.scss',
diff --git a/addons/website_sale/static/src/js/systray_items/new_content.js b/addons/website_sale/static/src/js/systray_items/new_content.js
new file mode 100644
index 00000000000..f1985c4d566
--- /dev/null
+++ b/addons/website_sale/static/src/js/systray_items/new_content.js
@@ -0,0 +1,16 @@
+/** @odoo-module **/
+
+import { NewContentModal, MODULE_STATUS } from '@website/systray_items/new_content';
+import { patch } from 'web.utils';
+
+patch(NewContentModal.prototype, 'website_sale_new_content', {
+ setup() {
+ this._super();
+
+ const newProductElement = this.state.newContentElements.find(element => element.moduleXmlId === 'base.module_website_sale');
+ newProductElement.createNewContent = () => this.createNewProduct();
+ newProductElement.status = MODULE_STATUS.INSTALLED;
+ },
+
+ createNewProduct() {}
+});
diff --git a/addons/website_slides/__manifest__.py b/addons/website_slides/__manifest__.py
index abda6179c1b..bfbf938b2ce 100644
--- a/addons/website_slides/__manifest__.py
+++ b/addons/website_slides/__manifest__.py
@@ -70,6 +70,7 @@ Featuring
'website_slides/static/src/js/slide_category_one2many.js',
'website_slides/static/src/js/rating_field_backend.js',
'website_slides/static/src/models/*.js',
+ 'website_slides/static/src/js/systray_items/new_content.js',
],
'web.assets_frontend': [
'website_slides/static/src/scss/website_slides.scss',
diff --git a/addons/website_slides/static/src/js/systray_items/new_content.js b/addons/website_slides/static/src/js/systray_items/new_content.js
index 284faca7ecf..c5ea11000bb 100644
--- a/addons/website_slides/static/src/js/systray_items/new_content.js
+++ b/addons/website_slides/static/src/js/systray_items/new_content.js
@@ -1,183 +1,16 @@
/** @odoo-module **/
-import { _t } from 'web.core';
-import Dialog from 'web.Dialog';
-import WebsiteNewMenu from 'website.newMenu';
-import SlidesCourseTagAdd from '@website_slides/js/slides_course_tag_add';
+import { NewContentModal, MODULE_STATUS } from '@website/systray_items/new_content';
+import { patch } from 'web.utils';
-const TagCourseDialog = SlidesCourseTagAdd.TagCourseDialog;
+patch(NewContentModal.prototype, 'website_slides_new_content', {
+ setup() {
+ this._super();
-export const ChannelCreateDialog = Dialog.extend({
- template: 'website.slide.channel.create',
- xmlDependencies: Dialog.prototype.xmlDependencies.concat(
- ['/website_slides/static/src/xml/website_slides_channel.xml',
- '/website_slides/static/src/xml/website_slides_channel_tag.xml']
- ),
- events: _.extend({}, Dialog.prototype.events, {
- 'change input#tag_ids' : '_onChangeTag',
- }),
- custom_events: _.extend({}, Dialog.prototype.custom_events, {
- 'tag_refresh': '_onTagRefresh',
- 'tag_remove_new': '_onTagRemoveNew',
- }),
- /**
- * @override
- * @param {Object} parent
- * @param {Object} options
- */
- init: function (parent, options) {
- options = _.defaults(options || {}, {
- title: _t("New Course"),
- size: 'medium',
- buttons: [{
- text: _t("Create"),
- classes: 'btn-primary',
- click: this._onClickFormSubmit.bind(this)
- }, {
- text: _t("Discard"),
- close: true
- },]
- });
- this._super(parent, options);
- },
- start: function () {
- var self = this;
- return this._super.apply(this, arguments).then(function () {
- var $input = self.$('#tag_ids');
- $input.select2({
- width: '100%',
- allowClear: true,
- formatNoMatches: false,
- multiple: true,
- selection_data: false,
- formatSelection: function (data) {
- if (data.tag) {
- data.text = data.tag;
- }
- return data.text;
- },
- createSearchChoice: function(term, data) {
- var addedTags = $(this.opts.element).select2('data');
- if (_.filter(_.union(addedTags, data), function (tag) {
- return tag.text.toLowerCase().localeCompare(term.toLowerCase()) === 0;
- }).length === 0) {
- if (this.opts.can_create) {
- return {
- id: _.uniqueId('tag_'),
- create: true,
- tag: term,
- text: _.str.sprintf(_t("Create new Tag '%s'"), term),
- };
- } else {
- return undefined;
- }
- }
- },
- fill_data: function (query, data) {
- var that = this,
- tags = {results: []};
- _.each(data, function (obj) {
- if (that.matcher(query.term, obj.name)) {
- tags.results.push({id: obj.id, text: obj.name});
- }
- });
- query.callback(tags);
- },
- query: function (query) {
- var that = this;
- // fetch data only once and store it
- if (!this.selection_data) {
- self._rpc({
- route: '/slides/channel/tag/search_read',
- params: {
- fields: ['name'],
- domain: [],
- }
- }).then(function (data) {
- that.can_create = data.can_create;
- that.fill_data(query, data.read_results);
- that.selection_data = data.read_results;
- });
- } else {
- this.fill_data(query, this.selection_data);
- }
- }
- });
- });
- },
- _onClickFormSubmit: function (ev) {
- var $form = this.$("#slide_channel_add_form");
- var $title = this.$("#title");
- if (!$title[0].value){
- $title.addClass('border-danger');
- this.$("#title-required").removeClass('d-none');
- } else {
- $form.submit();
- }
- },
- _onChangeTag: function (ev) {
- var self = this;
- var tags = $(ev.currentTarget).select2('data');
- tags.forEach(function (element) {
- if (element.create) {
- new TagCourseDialog(self, { defaultTag: element.text }).open();
- }
- });
- },
- /**
- * Replace the new tag ID by its real ID
- * @param ev
- * @private
- */
- _onTagRefresh: function (ev) {
- var $tag_ids = $('#tag_ids');
- var tags = $tag_ids.select2('data');
- tags.forEach(function (element) {
- if (element.create) {
- element.id = ev.data.tag_id;
- element.create = false;
- }
- });
- $tag_ids.select2('data', tags);
- // Set selection_data to false to force tag reload
- $tag_ids.data('select2').opts.selection_data = false;
- },
- /**
- * Remove the created tag if the user clicks on 'Discard' on the create tag Dialog
- * @private
- */
- _onTagRemoveNew: function () {
- var tags = $('#tag_ids').select2('data');
- tags = tags.filter(function (value) {
- return !value.create;
- });
- $('#tag_ids').select2('data', tags);
+ const newSlidesChannelElement = this.state.newContentElements.find(element => element.moduleXmlId === 'base.module_website_slides');
+ newSlidesChannelElement.createNewContent = () => this.createNewSlidesChannel();
+ newSlidesChannelElement.status = MODULE_STATUS.INSTALLED;
},
-});
-
-WebsiteNewMenu.include({
- actions: _.extend({}, WebsiteNewMenu.prototype.actions || {}, {
- new_slide_channel: '_createNewSlideChannel',
- }),
-
- //--------------------------------------------------------------------------
- // Actions
- //--------------------------------------------------------------------------
-
- /**
- * Displays the popup to create a new slide channel,
- * and redirects the user to this channel.
- *
- * @private
- * @returns {Promise} Unresolved if there is a redirection
- */
- _createNewSlideChannel: function () {
- var self = this;
- var def = new Promise(function (resolve) {
- var dialog = new ChannelCreateDialog(self, {});
- dialog.open();
- dialog.on('closed', self, resolve);
- });
- return def;
- },
+
+ createNewSlidesChannel() {}
});