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 @@ + + + +
+ + + +
+
+ + +
+
+
+ + +

Page

+
+ + + + +

+
+
+
+
+
+
+ + +
+ New + +
+
+
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() {} });