diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 888c876a930..520e96c66d0 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -10,7 +10,7 @@ from werkzeug import urls from werkzeug.exceptions import NotFound from odoo import api, fields, models, tools -from odoo.addons.http_routing.models.ir_http import slugify +from odoo.addons.http_routing.models.ir_http import slugify, _guess_mimetype from odoo.addons.website.models.ir_http import sitemap_qs2dom from odoo.addons.portal.controllers.portal import pager from odoo.tools import pycompat @@ -136,6 +136,10 @@ class Website(models.Model): }) return result + @api.model + def guess_mimetype(self): + return _guess_mimetype() + def get_unique_path(self, page_url): """ Given an url, return that url suffixed by counter if it already exists :param page_url : the url to be checked for uniqueness @@ -194,20 +198,21 @@ class Website(models.Model): website_id = self._context.get('website_id') url = page.url - - page_key = _('Page') - # search for website_page with link website_page_search_dom = [ '|', ('website_ids', 'in', website_id), ('website_ids', '=', False), ('view_id.arch_db', 'ilike', url) ] pages = self.env['website.page'].search(website_page_search_dom) + page_key = _('Page') + if len(pages) > 1: + page_key = _('Pages') page_view_ids = [] for page in pages: dependencies.setdefault(page_key, []) dependencies[page_key].append({ 'text': _('Page %s contains a link to this page') % page.url, - 'link': page.url + 'item': page.name, + 'link': page.url, }) page_view_ids.append(page.view_id.id) @@ -217,23 +222,85 @@ class Website(models.Model): ('arch_db', 'ilike', url), ('id', 'not in', page_view_ids) ] views = self.env['ir.ui.view'].search(page_search_dom) + view_key = _('Template') + if len(views) > 1: + view_key = _('Templates') for view in views: - dependencies.setdefault(page_key, []) - dependencies[page_key].append({ - 'text': _('Template %s (id:%s) contains a link to this page') % (view.key or view.name, view.id), - 'link': '#' + dependencies.setdefault(view_key, []) + dependencies[view_key].append({ + 'text': _('Template %s (id:%s) contains a link to this page') % (view.key or view.name, view.id), + 'link': '/web#id=%s&view_type=form&model=ir.ui.view' % view.id, + 'item': _('%s (id:%s)') % (view.key or view.name, view.id), }) # search for menu with link menu_search_dom = [ '|', ('website_id', '=', website_id), ('website_id', '=', False), ('url', 'ilike', '%s' % url) ] - menu_key = _('Menu') menus = self.env['website.menu'].search(menu_search_dom) + menu_key = _('Menu') + if len(menus) > 1: + menu_key = _('Menus') for menu in menus: dependencies.setdefault(menu_key, []).append({ 'text': _('This page is in the menu %s') % menu.name, - 'link': False + 'link': '/web#id=%s&view_type=form&model=website.menu' % menu.id, + 'item': menu.name, + }) + + return dependencies + + @api.model + def page_search_key_dependencies(self, page_id=False): + """ Search dependencies just for information. It will not catch 100% + of dependencies and False positive is more than possible + Each module could add dependences in this dict + :returns a dictionnary where key is the 'categorie' of object related to the given + view, and the value is the list of text and link to the resource using given page + """ + dependencies = {} + if not page_id: + return dependencies + + page = self.env['website.page'].browse(int(page_id)) + website_id = self._context.get('website_id') + key = page.key + + # search for website_page with link + website_page_search_dom = [ + '|', ('website_ids', 'in', website_id), ('website_ids', '=', False), ('view_id.arch_db', 'ilike', key), + ('id', '!=', page.id), + ] + pages = self.env['website.page'].search(website_page_search_dom) + page_key = _('Page') + if len(pages) > 1: + page_key = _('Pages') + page_view_ids = [] + for p in pages: + dependencies.setdefault(page_key, []) + dependencies[page_key].append({ + 'text': _('Page %s is calling this file') % p.url, + 'item': p.name, + 'link': p.url, + }) + page_view_ids.append(p.view_id.id) + + # search for ir_ui_view (not from a website_page) with link + page_search_dom = [ + '|', ('website_id', '=', website_id), ('website_id', '=', False), + ('arch_db', 'ilike', key), ('id', 'not in', page_view_ids), + ('id', '!=', page.view_id.id), + ] + views = self.env['ir.ui.view'].search(page_search_dom) + view_key = _('Template') + if len(views) > 1: + view_key = _('Templates') + for view in views: + dependencies.setdefault(view_key, []) + dependencies[view_key].append({ + 'text': _('Template %s (id:%s) is calling this file') % (view.key or view.name, view.id), + 'item': _('%s (id:%s)') % (view.key or view.name, view.id), + 'link': '/web#id=%s&view_type=form&model=ir.ui.view' % view.id, }) return dependencies diff --git a/addons/website/static/src/js/menu/content.js b/addons/website/static/src/js/menu/content.js index b2408c3b571..ccba660466f 100644 --- a/addons/website/static/src/js/menu/content.js +++ b/addons/website/static/src/js/menu/content.js @@ -19,6 +19,7 @@ var PagePropertiesDialog = widget.Dialog.extend({ ['/website/static/src/xml/website.pageProperties.xml'] ), events: _.extend({}, widget.Dialog.prototype.events, { + 'keyup input#page_name': '_onNameChanged', 'keyup input#page_url': '_onUrlChanged', 'change input#create_redirect': '_onCreateRedirectChanged', }), @@ -90,19 +91,39 @@ var PagePropertiesDialog = widget.Dialog.extend({ this.$(".ask_for_redirect").hide(); this.$(".redirect_type").hide(); + this.$(".warn_about_call").hide(); - //TODO: remove.html() & set self.var & display in view instead of controller (here) this._getPageDependencies(self.page_id, context) .then(function (dependencies) { - var dep = []; - _.each(dependencies, function( value, index ) { - if (dependencies[index].length > 0) { - dep.push(dependencies[index].length + ' ' + index.toLowerCase() + (dependencies[index].length > 1 ? 's': '')); + var dep_text = []; + _.each(dependencies, function(value, index){ + if (value.length > 0) { + dep_text.push(value.length + ' ' + index.toLowerCase()); } }); - if (dep.length > 0) { - $("#dependencies_redirect").html("(" + _("used in") + " " + dep.join(", ") + ")"); - } + dep_text = dep_text.join(', '); + $('#dependencies_redirect').html(qweb.render('website.show_page_dependencies', { dependencies: dependencies, dep_text: dep_text })); + $('#dependencies_redirect [data-toggle="popover"]').popover({ + container: 'body' + }); + }); + this._getSupportedMimetype(context) + .then(function (mimetypes) { + self.supportedMimetype = mimetypes; + }); + this._getPageKeyDependencies(self.page_id, context) + .then(function (dependencies) { + var dep_text = []; + _.each(dependencies, function(value, index){ + if (value.length > 0) { + dep_text.push(value.length + ' ' + index.toLowerCase()); + } + }); + dep_text = dep_text.join(', '); + $('.warn_about_call').html(qweb.render('website.show_page_key_dependencies', { dependencies: dependencies, dep_text: dep_text })); + $('.warn_about_call [data-toggle="popover"]').popover({ + container: 'body' + }); }); var l10n = _t.database.parameters; @@ -133,6 +154,13 @@ var PagePropertiesDialog = widget.Dialog.extend({ return this._super.apply(this, arguments); }, + /** + * @override + */ + destroy: function(){ + $('.popover').popover('hide'); + return this._super.apply(this, arguments); + }, //-------------------------------------------------------------------------- // Public @@ -184,7 +212,7 @@ var PagePropertiesDialog = widget.Dialog.extend({ //-------------------------------------------------------------------------- /** - * Retrieves the page dependencies for the given object id. + * Retrieves the page URL dependencies for the given object id. * * @private * @param {integer} moID @@ -199,6 +227,36 @@ var PagePropertiesDialog = widget.Dialog.extend({ context: context, }); }, + /** + * Retrieves the page's key dependencies for the given object id. + * + * @private + * @param {integer} moID + * @param {Object} context + * @returns {Deferred} + */ + _getPageKeyDependencies: function (moID, context) { + return this._rpc({ + model: 'website', + method: 'page_search_key_dependencies', + args: [moID], + context: context, + }); + }, + /** + * Retrieves supported mimtype + * + * @private + * @param {Object} context + * @returns {Deferred} + */ + _getSupportedMimetype: function (context) { + return this._rpc({ + model: 'website', + method: 'guess_mimetype', + context: context, + }); + }, /** * Returns information about the page main object. * @@ -227,6 +285,20 @@ var PagePropertiesDialog = widget.Dialog.extend({ this.$(".ask_for_redirect").hide(); } }, + _onNameChanged: function () { + var name = this.$('input#page_name').val(); + //If the file type is a supported mimetype, check if it is t-called. If so, warn user + //Note: different from page_search_dependencies which check only for url and not key + var ext = '.' + this.page.name.split('.').pop(); + if(ext in this.supportedMimetype && ext != '.html'){ + if (name != this.page.name) { + this.$(".warn_about_call").show(); + } + else { + this.$(".warn_about_call").hide(); + } + } + }, _onCreateRedirectChanged: function () { var createRedirect = this.$('input#create_redirect').prop('checked'); if (createRedirect) { diff --git a/addons/website/static/src/less/website.ui.components.less b/addons/website/static/src/less/website.ui.components.less index 7f85829460a..8cbf1ba7d98 100644 --- a/addons/website/static/src/less/website.ui.components.less +++ b/addons/website/static/src/less/website.ui.components.less @@ -453,8 +453,13 @@ body .modal { } // Pages Management -.o_page_management_info .o_switch{ - padding-top: 7px; +.o_page_management_info{ + .o_switch{ + padding-top: 7px; + } +} +.popover_dep{ + overflow-x: auto; } #edit_website_pages{ .fa-check{ diff --git a/addons/website/static/src/xml/website.pageProperties.xml b/addons/website/static/src/xml/website.pageProperties.xml index b7b3b337d91..3746e294b0d 100644 --- a/addons/website/static/src/xml/website.pageProperties.xml +++ b/addons/website/static/src/xml/website.pageProperties.xml @@ -1,5 +1,39 @@ + + + + + + +
    + +
  • +
    +
+
+
+ +
+ + + + + It looks like your file is being called by + . + Changing its name will break these calls. + +
+
+ + + + + (could be used in ) + + + +
+
@@ -24,7 +59,7 @@