[IMP] website: warn user about key change when editing special file name

The idea is to warn user if he created a special file (website.page with
supported extension) and he wants to change the file's name, which will at the
end also change the key).
If this special file has been t-called elsewhere, they t-call wont work anymore
This is why warning user is needed.

Note that this is different than the already existing page_search_dependencies
that is just checking if the URL is present in menus, templates, blog or pages.
This is about checking if the key is present somewhere. (we assume that if the
key is present, it is surely about a t-call.

Technically, the flow is:
1. Get supported extension
2. Get key dependencies
3. Check if user modify name
4. If name changed and it is/was a supported extension (special file) and it
   has dependencies (surely t-call) then warn user these call wont work anymore

--

search_page_dependencies is also improved by returning links to be clickable
(on delete dialog eg) and splitting template/page results.

it also refactor dependencies warning that was creating HTML in javascript into
using XML template
This commit is contained in:
rde
2017-10-12 16:20:42 +02:00
committed by Jeremy Kersten
parent a8fc544c5c
commit a91a3a5633
5 changed files with 231 additions and 24 deletions
+78 -11
View File
@@ -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 <b>%s</b> 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 <b>%s (id:%s)</b> contains a link to this page') % (view.key or view.name, view.id),
'link': '#'
dependencies.setdefault(view_key, [])
dependencies[view_key].append({
'text': _('Template <b>%s (id:%s)</b> 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 <b>%s</b>') % 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 <b>%s</b> 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 <b>%s (id:%s)</b> 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
+81 -9
View File
@@ -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<Array>}
*/
_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<Array>}
*/
_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) {
@@ -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{
@@ -1,5 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<!-- Tooltip Dependencies -->
<t t-name="website.get_tooltip_dependencies">
<t t-foreach="dependencies" t-as="dep">
<b><t t-esc="dep"/></b>
<ul class="popover_dep">
<t t-foreach="dep_value" t-as="item">
<li><a t-att-href="item_value['link']"><t t-esc="item_value['item']"/></a></li>
</t>
</ul>
</t>
</t>
<t t-name="website.show_page_key_dependencies">
<div class="col-sm-9 col-sm-offset-3">
<span class="text-muted" id="warn_about_call_message">
<t t-set="depTooltip">
<t t-call="website.get_tooltip_dependencies"/>
</t>
It looks like your file is being called by
<a href="#" data-toggle="popover" t-att-data-content="depTooltip" data-html="true" title="Dependencies"><t t-esc="dep_text" /></a>.
Changing its name will break these calls.
</span>
</div>
</t>
<t t-name="website.show_page_dependencies">
<t t-set="depTooltip">
<t t-call="website.get_tooltip_dependencies"/>
</t>
(could be used in <a href="#" data-toggle="popover" t-att-data-content="depTooltip" data-html="true" title="Dependencies"><t t-esc="dep_text" /></a>)
</t>
<!-- Page Properties -->
<div t-name="website.pagesMenu.page_info" class="o_page_management_info">
<form class="form-horizontal">
<ul class="nav nav-tabs">
@@ -14,6 +48,7 @@
<input type="text" class="form-control input-sm" id="page_name" t-att-value="widget.page.name" />
</div>
</div>
<div class="form-group warn_about_call"></div>
<div class="form-group">
<label class="control-label col-sm-3" for="page_url">Page URL</label>
<div class="col-sm-9">
@@ -24,7 +59,7 @@
</div>
</div>
<div class="form-group ask_for_redirect">
<label class="control-label col-sm-3" for="is_homepage">Redirect Old URL</label>
<label class="control-label col-sm-3" for="create_redirect">Redirect Old URL</label>
<div class="col-sm-2">
<a>
<label class="o_switch" for="create_redirect" >
+29 -1
View File
@@ -20,11 +20,39 @@ class Website(models.Model):
posts = self.env['blog.post'].search(dom)
if posts:
page_key = _('Blog Post')
if len(posts) > 1:
page_key = _('Blog Posts')
dep[page_key] = []
for p in posts:
dep[page_key].append({
'text': _('Blog Post <b>%s</b> seems to have a link to this page !') % p.name,
'link': p.website_url
'item': p.name,
'link': p.website_url,
})
return dep
@api.model
def page_search_key_dependencies(self, page_id=False):
dep = super(Website, self).page_search_key_dependencies(page_id=page_id)
page = self.env['website.page'].browse(int(page_id))
key = page.key
dom = [
('content', 'ilike', key)
]
posts = self.env['blog.post'].search(dom)
if posts:
page_key = _('Blog Post')
if len(posts) > 1:
page_key = _('Blog Posts')
dep[page_key] = []
for p in posts:
dep[page_key].append({
'text': _('Blog Post <b>%s</b> seems to be calling this file !') % p.name,
'item': p.name,
'link': p.website_url,
})
return dep