[FIX] website, *: avoid using unescaped query parameters
*: google_recaptcha, web_editor, website_blog, website_event_booth, website_event_exhibitor, website_event_track, website_forum, website_jitsi, website_sale_comparison, website_slides, website_livechat, website_twitter This commit fixes improperly escaped query parameters across javascripts of website-related modules. See https://github.com/odoo/enterprise/pull/31711 closes odoo/odoo#118599 X-original-commit: 89be076aa51b097ada48d0046006be4d9692ac1e Related: odoo/enterprise#39783 Signed-off-by: Bojabza Soukéina (sobo) <sobo@odoo.com>
This commit is contained in:
@@ -21,7 +21,7 @@ const ReCaptcha = Class.extend({
|
||||
*/
|
||||
loadLibs: function () {
|
||||
if (this._publicKey) {
|
||||
this._recaptchaReady = loadJS(`https://www.recaptcha.net/recaptcha/api.js?render=${this._publicKey}`)
|
||||
this._recaptchaReady = loadJS(`https://www.recaptcha.net/recaptcha/api.js?render=${encodeURIComponent(this._publicKey)}`)
|
||||
.then(() => new Promise(resolve => window.grecaptcha.ready(() => resolve())));
|
||||
return this._recaptchaReady.then(() => !!document.querySelector('.grecaptcha-badge'));
|
||||
}
|
||||
|
||||
@@ -48,14 +48,14 @@ files.filter(f => f.endsWith('svg')).forEach(filePath => {
|
||||
repeatX: fileName.includes('repeatx'),
|
||||
repeatY: fileName.includes('repeaty'),
|
||||
};
|
||||
shape.optionXML = `<we-button data-shape="web_editor/${shape.page}/${shape.name}" data-select-label="${shape.page} ${shape.name}"/>`;
|
||||
shape.optionXML = `<we-button data-shape="web_editor/${encodeURIComponent(shape.page)}/${encodeURIComponent(shape.name)}" data-select-label="${shape.page} ${shape.name}"/>`;
|
||||
if (shape.position[0] === 'stretch') {
|
||||
shape.position = ['center'];
|
||||
shape.size = '100% 100%';
|
||||
} else {
|
||||
shape.size = '100% auto';
|
||||
}
|
||||
shape.scss = `'${shape.page}/${shape.name}': ('position': ${shape.position[0]}, 'size': ${shape.size}, 'colors': (${shape.colors.join(', ')})${shape.repeatX ? ", 'repeat-x': true" : ""}${shape.repeatY ? ", 'repeat-y': true" : ""})`;
|
||||
shape.scss = `'${encodeURIComponent(shape.page)}/${encodeURIComponent(shape.name)}': ('position': ${shape.position[0]}, 'size': ${shape.size}, 'colors': (${shape.colors.join(', ')})${shape.repeatX ? ", 'repeat-x': true" : ""}${shape.repeatY ? ", 'repeat-y': true" : ""})`;
|
||||
shapes.push(shape);
|
||||
});
|
||||
const xml = shapes.map(shape => shape.optionXML).join('\n');
|
||||
|
||||
@@ -49,7 +49,7 @@ export class DocumentSelector extends FileSelector {
|
||||
static async createElements(selectedMedia, { orm }) {
|
||||
return Promise.all(selectedMedia.map(async attachment => {
|
||||
const linkEl = document.createElement('a');
|
||||
let href = `/web/content/${attachment.id}?unique=${attachment.checksum}&download=true`;
|
||||
let href = `/web/content/${encodeURIComponent(attachment.id)}?unique=${encodeURIComponent(attachment.checksum)}&download=true`;
|
||||
if (!attachment.public) {
|
||||
let accessToken = attachment.access_token;
|
||||
if (!accessToken) {
|
||||
@@ -59,7 +59,7 @@ export class DocumentSelector extends FileSelector {
|
||||
[attachment.id],
|
||||
);
|
||||
}
|
||||
href += `&access_token=${accessToken}`;
|
||||
href += `&access_token=${encodeURIComponent(accessToken)}`;
|
||||
}
|
||||
linkEl.href = href;
|
||||
linkEl.title = attachment.name;
|
||||
|
||||
@@ -40,7 +40,7 @@ AttachmentError.template = xml `
|
||||
following pages or views:</p>
|
||||
<ul t-foreach="props.views" t-as="view" t-key="view.id">
|
||||
<li>
|
||||
<a t-att-href="'/web#model=ir.ui.view&id=' + view.id">
|
||||
<a t-att-href="'/web#model=ir.ui.view&id=' + window.encodeURIComponent(view.id)">
|
||||
<t t-esc="view.name"/>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -272,7 +272,7 @@ export class ImageSelector extends FileSelector {
|
||||
[attachment.id],
|
||||
);
|
||||
}
|
||||
src += `?access_token=${accessToken}`;
|
||||
src += `?access_token=${encodeURIComponent(accessToken)}`;
|
||||
}
|
||||
imageEl.src = src;
|
||||
imageEl.alt = attachment.description || '';
|
||||
|
||||
@@ -89,11 +89,11 @@ export class VideoSelector extends Component {
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all(this.props.vimeoPreviewIds.map(async (videoId) => {
|
||||
const { thumbnail_url: thumbnailSrc } = await this.http.get(`https://vimeo.com/api/oembed.json?url=http%3A//vimeo.com/${videoId}`);
|
||||
const { thumbnail_url: thumbnailSrc } = await this.http.get(`https://vimeo.com/api/oembed.json?url=http%3A//vimeo.com/${encodeURIComponent(videoId)}`);
|
||||
this.state.vimeoPreviews.push({
|
||||
id: videoId,
|
||||
thumbnailSrc,
|
||||
src: `https://player.vimeo.com/video/${videoId}`
|
||||
src: `https://player.vimeo.com/video/${encodeURIComponent(videoId)}`
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -721,7 +721,7 @@ function fontToImg($editable) {
|
||||
const image = document.createElement('img');
|
||||
image.setAttribute('width', intrinsicWidth);
|
||||
image.setAttribute('height', intrinsicHeight);
|
||||
image.setAttribute('src', `/web_editor/font_to_img/${content.charCodeAt(0)}/${window.encodeURI(color)}/${window.encodeURI(bg)}/${Math.max(1, Math.round(intrinsicWidth))}x${Math.max(1, Math.round(intrinsicHeight))}`);
|
||||
image.setAttribute('src', `/web_editor/font_to_img/${content.charCodeAt(0)}/${encodeURIComponent(color)}/${encodeURIComponent(bg)}/${Math.max(1, Math.round(intrinsicWidth))}x${Math.max(1, Math.round(intrinsicHeight))}`);
|
||||
image.setAttribute('data-class', font.getAttribute('class'));
|
||||
image.setAttribute('data-style', style);
|
||||
image.setAttribute('style', style);
|
||||
|
||||
@@ -4397,7 +4397,7 @@ export class OdooEditor extends EventTarget {
|
||||
videoElement.setAttribute('height', '315');
|
||||
videoElement.setAttribute(
|
||||
'src',
|
||||
`https://www.youtube.com/embed/${youtubeUrl[1]}`,
|
||||
`https://www.youtube.com/embed/${encodeURIComponent(youtubeUrl[1])}`,
|
||||
);
|
||||
videoElement.setAttribute('title', 'YouTube video player');
|
||||
videoElement.setAttribute('frameborder', '0');
|
||||
|
||||
@@ -3751,7 +3751,7 @@ var SnippetsMenu = Widget.extend({
|
||||
$content: $('<div/>', {text: sprintf(_t("Do you want to install the %s App?"), name)}).append(
|
||||
$('<a/>', {
|
||||
target: '_blank',
|
||||
href: '/web#id=' + moduleID + '&view_type=form&model=ir.module.module&action=base.open_module_tree',
|
||||
href: '/web#id=' + encodeURIComponent(moduleID) + '&view_type=form&model=ir.module.module&action=base.open_module_tree',
|
||||
text: _t("More info about this app."),
|
||||
class: 'ml4',
|
||||
})
|
||||
|
||||
@@ -6128,7 +6128,7 @@ registry.ImageTools = ImageHandlerOption.extend({
|
||||
const [module, directory, fileName] = shapeName.split('/');
|
||||
let shape = this.shapeCache[fileName];
|
||||
if (!shape) {
|
||||
const shapeURL = `/${module}/static/image_shapes/${directory}/${fileName}.svg`;
|
||||
const shapeURL = `/${encodeURIComponent(module)}/static/image_shapes/${encodeURIComponent(directory)}/${encodeURIComponent(fileName)}.svg`;
|
||||
shape = await (await fetch(shapeURL)).text();
|
||||
this.shapeCache[fileName] = shape;
|
||||
}
|
||||
@@ -6334,7 +6334,7 @@ registry.ImageTools = ImageHandlerOption.extend({
|
||||
uiFragment.querySelectorAll('we-select-page we-button[data-set-img-shape]').forEach(btn => {
|
||||
const image = document.createElement('img');
|
||||
const [moduleName, directory, shapeName] = btn.dataset.setImgShape.split('/');
|
||||
image.src = `/${moduleName}/static/image_shapes/${directory}/${shapeName}.svg`;
|
||||
image.src = `/${encodeURIComponent(moduleName)}/static/image_shapes/${encodeURIComponent(directory)}/${encodeURIComponent(shapeName)}.svg`;
|
||||
$(btn).prepend(image);
|
||||
|
||||
if (btn.dataset.animated) {
|
||||
@@ -6411,7 +6411,7 @@ registry.ImageTools = ImageHandlerOption.extend({
|
||||
// attribute.
|
||||
match = match.slice(0, -12);
|
||||
}
|
||||
return this._loadImageInfo(`/web/image/${match}`);
|
||||
return this._loadImageInfo(`/web/image/${encodeURIComponent(match)}`);
|
||||
}
|
||||
return _super(...arguments);
|
||||
},
|
||||
@@ -7163,9 +7163,9 @@ registry.BackgroundShape = SnippetOptionWidget.extend({
|
||||
return `${colorName}=${encodedCol}`;
|
||||
});
|
||||
if (flip.length) {
|
||||
searchParams.push(`flip=${flip.sort().join('')}`);
|
||||
searchParams.push(`flip=${encodeURIComponent(flip.sort().join(''))}`);
|
||||
}
|
||||
return `/web_editor/shape/${shape}.svg?${searchParams.join('&')}`;
|
||||
return `/web_editor/shape/${encodeURIComponent(shape)}.svg?${searchParams.join('&')}`;
|
||||
},
|
||||
/**
|
||||
* Retrieves current shape data from the target's dataset.
|
||||
|
||||
@@ -209,7 +209,7 @@ const LinkPopoverWidget = Widget.extend({
|
||||
// would need to fetch the page through the server (s2s), involving
|
||||
// enduser fetching problematic pages such as illicit content.
|
||||
this.$previewFaviconImg.attr({
|
||||
'src': `https://www.google.com/s2/favicons?sz=16&domain=${url}`
|
||||
'src': `https://www.google.com/s2/favicons?sz=16&domain=${encodeURIComponent(url)}`
|
||||
}).removeClass('d-none');
|
||||
this.$previewFaviconFa.addClass('d-none');
|
||||
} else {
|
||||
|
||||
@@ -219,7 +219,7 @@ const Wysiwyg = Widget.extend({
|
||||
categories: powerboxOptions.categories,
|
||||
plugins: options.editorPlugins,
|
||||
direction: options.direction || localization.direction || 'ltr',
|
||||
collaborationClientAvatarUrl: `${browser.location.origin}/web/image?model=res.users&field=avatar_128&id=${this.getSession().uid}`,
|
||||
collaborationClientAvatarUrl: `${browser.location.origin}/web/image?model=res.users&field=avatar_128&id=${encodeURIComponent(this.getSession().uid)}`,
|
||||
renderingClasses: ['o_dirty', 'o_transform_removal', 'oe_edited_link', 'o_menu_loading'],
|
||||
dropImageAsAttachment: options.dropImageAsAttachment,
|
||||
foldSnippets: !!options.foldSnippets,
|
||||
@@ -503,7 +503,7 @@ const Wysiwyg = Widget.extend({
|
||||
}
|
||||
return this._userName;
|
||||
},
|
||||
get_client_avatar: () => `${browser.location.origin}/web/image?model=res.users&field=avatar_128&id=${this.getSession().uid}`,
|
||||
get_client_avatar: () => `${browser.location.origin}/web/image?model=res.users&field=avatar_128&id=${encodeURIComponent(this.getSession().uid)}`,
|
||||
get_missing_steps: (params) => this.odooEditor.historyGetMissingSteps(params.requestPayload),
|
||||
get_history_from_snapshot: () => this.odooEditor.historyGetSnapshotSteps(),
|
||||
get_collaborative_selection: () => this.odooEditor.getCurrentCollaborativeSelection(),
|
||||
@@ -964,7 +964,7 @@ const Wysiwyg = Widget.extend({
|
||||
// it was modified previously, as the other modified image may be used
|
||||
// elsewhere if the snippet was duplicated or was saved as a custom one.
|
||||
const newAttachmentSrc = await this._rpc({
|
||||
route: `/web_editor/modify_image/${el.dataset.originalId}`,
|
||||
route: `/web_editor/modify_image/${encodeURIComponent(el.dataset.originalId)}`,
|
||||
params: {
|
||||
res_model: resModel,
|
||||
res_id: parseInt(resId),
|
||||
|
||||
@@ -372,7 +372,7 @@ class ApplyConfiguratorScreen extends Component {
|
||||
// Here the website service goToWebsite method is not used because
|
||||
// the web client needs to be reloaded after the new modules have
|
||||
// been installed.
|
||||
window.location.replace(`/web#action=website.website_preview&website_id=${resp.website_id}&enable_editor=1&with_loader=1`);
|
||||
window.location.replace(`/web#action=website.website_preview&website_id=${encodeURIComponent(resp.website_id)}&enable_editor=1&with_loader=1`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -634,7 +634,7 @@ export class Configurator extends Component {
|
||||
}
|
||||
|
||||
get pathname() {
|
||||
return `/website/configurator${this.state.currentStep ? `/${this.state.currentStep}` : ''}`;
|
||||
return `/website/configurator${this.state.currentStep ? `/${encodeURIComponent(this.state.currentStep)}` : ''}`;
|
||||
}
|
||||
|
||||
get storageItemName() {
|
||||
|
||||
@@ -65,9 +65,9 @@ export class WebsitePreview extends Component {
|
||||
// URL (event if it wasn't, it wouldn't be an issue as those are
|
||||
// really considered as the same domain, the user will share the
|
||||
// same session and CORS errors won't be a thing in such a case)
|
||||
window.location.href = `${this.websiteDomain}/web#action=website.website_preview&path=${encodedPath}&website_id=${this.websiteId}`;
|
||||
window.location.href = `${encodeURI(this.websiteDomain)}/web#action=website.website_preview&path=${encodedPath}&website_id=${encodeURIComponent(this.websiteId)}`;
|
||||
} else {
|
||||
this.initialUrl = `/website/force/${this.websiteId}?path=${encodedPath}`;
|
||||
this.initialUrl = `/website/force/${encodeURIComponent(this.websiteId)}?path=${encodedPath}`;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class ImageSelector extends Component {
|
||||
this.seoContext = useState(seoContext);
|
||||
|
||||
const firstImageId = this.props.hasSocialDefaultImage ? 'social_default_image' : 'logo';
|
||||
const firstImageSrc = `/web/image/website/${this.website.currentWebsite.id}/${firstImageId}`;
|
||||
const firstImageSrc = `/web/image/website/${encodeURIComponent(this.website.currentWebsite.id)}/${firstImageId}`;
|
||||
const firstImage = {
|
||||
src: firstImageSrc,
|
||||
active: this.areSameImages(firstImageSrc, this.seoContext.metaImage),
|
||||
|
||||
@@ -696,7 +696,7 @@ export class WysiwygAdapterComponent extends ComponentAdapter {
|
||||
} else if (event.data.reloadWebClient) {
|
||||
const currentPath = encodeURIComponent(window.location.pathname);
|
||||
const websiteId = this.websiteService.currentWebsite.id;
|
||||
callback = () => window.location = `/web#action=website.website_preview&website_id=${websiteId}&path=${currentPath}&enable_editor=1`;
|
||||
callback = () => window.location = `/web#action=website.website_preview&website_id=${encodeURIComponent(websiteId)}&path=${currentPath}&enable_editor=1`;
|
||||
} else if (event.data.action) {
|
||||
callback = () => {
|
||||
this.leaveEditMode({
|
||||
|
||||
@@ -103,7 +103,7 @@ var Dashboard = AbstractAction.extend({
|
||||
on_go_to_website: function (ev) {
|
||||
ev.preventDefault();
|
||||
var website = this.websites.find(website => website.selected);
|
||||
window.location.replace(`/web#action=website.website_preview&website_id=${website.id}`);
|
||||
window.location.replace(`/web#action=website.website_preview&website_id=${encodeURIComponent(website.id)}`);
|
||||
},
|
||||
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ export const WebsiteRoot = publicRootData.PublicRoot.extend(KeyboardNavigationMi
|
||||
this._gmapAPILoading = false;
|
||||
return;
|
||||
}
|
||||
await loadJS(`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=places&callback=odoo_gmap_api_post_load&key=${key}`);
|
||||
await loadJS(`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=places&callback=odoo_gmap_api_post_load&key=${encodeURIComponent(key)}`);
|
||||
});
|
||||
}
|
||||
return this._gmapAPILoading;
|
||||
@@ -173,7 +173,7 @@ export const WebsiteRoot = publicRootData.PublicRoot.extend(KeyboardNavigationMi
|
||||
var $target = $(ev.currentTarget);
|
||||
// retrieve the hash before the redirect
|
||||
var redirect = {
|
||||
lang: $target.data('url_code'),
|
||||
lang: encodeURIComponent($target.data('url_code')),
|
||||
url: encodeURIComponent($target.attr('href').replace(/[&?]edit_translations[^&?]+/, '')),
|
||||
hash: encodeURIComponent(window.location.hash)
|
||||
};
|
||||
|
||||
@@ -169,7 +169,7 @@ const wSnippetMenu = weSnippetEditor.SnippetsMenu.extend({
|
||||
*/
|
||||
async _validateGMapAPIKey(key) {
|
||||
try {
|
||||
const response = await fetch(`https://maps.googleapis.com/maps/api/staticmap?center=belgium&size=10x10&key=${key}`);
|
||||
const response = await fetch(`https://maps.googleapis.com/maps/api/staticmap?center=belgium&size=10x10&key=${encodeURIComponent(key)}`);
|
||||
const isValid = (response.status === 200);
|
||||
return {
|
||||
isValid: isValid,
|
||||
|
||||
@@ -128,7 +128,7 @@ const FontFamilyPickerUserValueWidget = SelectUserValueWidget.extend({
|
||||
}
|
||||
for (const font of this.googleLocalFonts) {
|
||||
const attachmentId = font.split(/\s*:\s*/)[1];
|
||||
const fontURL = `/web/content/${attachmentId}`;
|
||||
const fontURL = `/web/content/${encodeURIComponent(attachmentId)}`;
|
||||
fontsToLoad.push(fontURL);
|
||||
}
|
||||
// TODO ideally, remove the <link> elements created once this widget
|
||||
@@ -233,7 +233,9 @@ const FontFamilyPickerUserValueWidget = SelectUserValueWidget.extend({
|
||||
let isValidFamily = false;
|
||||
|
||||
try {
|
||||
const result = await fetch("https://fonts.googleapis.com/css?family=" + m[1]+':300,300i,400,400i,700,700i', {method: 'HEAD'});
|
||||
// Font family is an encoded query parameter:
|
||||
// "Open+Sans" needs to remain "Open+Sans".
|
||||
const result = await fetch("https://fonts.googleapis.com/css?family=" + m[1] + ':300,300i,400,400i,700,700i', {method: 'HEAD'});
|
||||
// Google fonts server returns a 400 status code if family is not valid.
|
||||
if (result.ok) {
|
||||
isValidFamily = true;
|
||||
@@ -1627,7 +1629,7 @@ options.registry.company_data = options.Class.extend({
|
||||
args: [session.uid, ['company_id']],
|
||||
});
|
||||
}).then(function (res) {
|
||||
proto.__link = '/web#action=base.action_res_company_form&view_type=form&id=' + (res && res[0] && res[0].company_id[0] || 1);
|
||||
proto.__link = '/web#action=base.action_res_company_form&view_type=form&id=' + encodeURIComponent(res && res[0] && res[0].company_id[0] || 1);
|
||||
});
|
||||
}
|
||||
return Promise.all([this._super.apply(this, arguments), prom]);
|
||||
|
||||
@@ -193,7 +193,7 @@ export const websiteService = {
|
||||
this.websiteRootInstance = undefined;
|
||||
if (lang) {
|
||||
invalidateSnippetCache = true;
|
||||
path = `/website/lang/${lang}?r=${encodeURIComponent(path)}`;
|
||||
path = `/website/lang/${encodeURIComponent(lang)}?r=${encodeURIComponent(path)}`;
|
||||
}
|
||||
action.doAction('website.website_preview', {
|
||||
clearBreadcrumbs: true,
|
||||
|
||||
@@ -128,7 +128,7 @@ options.registry.SocialMedia = options.Class.extend({
|
||||
const faIcon = isDbField ? `fa-${entry.media}` : 'fa-pencil';
|
||||
anchorEl.querySelector('i').classList.add(faIcon);
|
||||
if (isDbField) {
|
||||
anchorEl.href = `/website/social/${entry.media}`;
|
||||
anchorEl.href = `/website/social/${encodeURIComponent(entry.media)}`;
|
||||
anchorEl.classList.add(`s_social_media_${entry.media}`);
|
||||
}
|
||||
}
|
||||
@@ -199,7 +199,7 @@ options.registry.SocialMedia = options.Class.extend({
|
||||
return {
|
||||
id: generateHTMLId(),
|
||||
display_name: media ? dbSocialValues[`social_${media}`] : el.getAttribute('href'),
|
||||
placeholder: `https://${media || 'example'}.com/yourPage`,
|
||||
placeholder: `https://${encodeURIComponent(media) || 'example'}.com/yourPage`,
|
||||
undeletable: !!media,
|
||||
notToggleable: !media,
|
||||
selected: true,
|
||||
@@ -211,13 +211,13 @@ options.registry.SocialMedia = options.Class.extend({
|
||||
// Adds the DB social media links that are not in the DOM.
|
||||
for (let [media, link] of Object.entries(dbSocialValues)) {
|
||||
media = media.split('social_').pop();
|
||||
if (!this.$target[0].querySelector(`:scope > a[href="/website/social/${media}"]`)) {
|
||||
if (!this.$target[0].querySelector(`:scope > a[href="/website/social/${encodeURIComponent(media)}"]`)) {
|
||||
const entryNotInDom = this.entriesNotInDom.find(entry => entry.media === media);
|
||||
if (!entryNotInDom) {
|
||||
this.entriesNotInDom.push({
|
||||
id: generateHTMLId(),
|
||||
display_name: link,
|
||||
placeholder: `https://${media}.com/yourPage`,
|
||||
placeholder: `https://${encodeURIComponent(media)}.com/yourPage`,
|
||||
undeletable: true,
|
||||
selected: false,
|
||||
listPosition: listPosition++,
|
||||
|
||||
@@ -770,7 +770,7 @@ options.registry.WebsiteFormEditor = FormEditor.extend({
|
||||
* @param {string} action
|
||||
*/
|
||||
_redirectToAction: function (action) {
|
||||
window.location.replace(`/web#action=${action}`);
|
||||
window.location.replace(`/web#action=${encodeURIComponent(action)}`);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
@@ -21,7 +21,7 @@ export class WebsiteSwitcherSystray extends Component {
|
||||
if (website.domain && !wUtils.isHTTPSorNakedDomainRedirection(website.domain, window.location.origin)) {
|
||||
const { location: { pathname, search, hash } } = this.websiteService.contentWindow;
|
||||
const path = pathname + search + hash;
|
||||
window.location.href = `${website.domain}/web#action=website.website_preview&path=${encodeURI(path)}&website_id=${website.id}`;
|
||||
window.location.href = `${encodeURI(website.domain)}/web#action=website.website_preview&path=${encodeURIComponent(path)}&website_id=${encodeURIComponent(website.id)}`;
|
||||
} else {
|
||||
this.websiteService.goToWebsite({ websiteId: website.id });
|
||||
}
|
||||
|
||||
@@ -70,16 +70,16 @@ publicWidget.registry.websiteBlog = publicWidget.Widget.extend({
|
||||
ev.preventDefault();
|
||||
var url = '';
|
||||
var $element = $(ev.currentTarget);
|
||||
var blogPostTitle = encodeURIComponent($('#o_wblog_post_name').html() || '');
|
||||
var articleURL = encodeURIComponent(window.location.href);
|
||||
var blogPostTitle = $('#o_wblog_post_name').html() || '';
|
||||
var articleURL = window.location.href;
|
||||
if ($element.hasClass('o_twitter')) {
|
||||
var twitterText = core._t("Amazing blog article: %s! Check it live: %s");
|
||||
var tweetText = _.string.sprintf(twitterText, blogPostTitle, articleURL);
|
||||
url = 'https://twitter.com/intent/tweet?tw_p=tweetbutton&text=' + tweetText;
|
||||
url = 'https://twitter.com/intent/tweet?tw_p=tweetbutton&text=' + encodeURIComponent(tweetText);
|
||||
} else if ($element.hasClass('o_facebook')) {
|
||||
url = 'https://www.facebook.com/sharer/sharer.php?u=' + articleURL;
|
||||
url = 'https://www.facebook.com/sharer/sharer.php?u=' + encodeURIComponent(articleURL);
|
||||
} else if ($element.hasClass('o_linkedin')) {
|
||||
url = 'https://www.linkedin.com/sharing/share-offsite/?url=' + articleURL;
|
||||
url = 'https://www.linkedin.com/sharing/share-offsite/?url=' + encodeURIComponent(articleURL);
|
||||
}
|
||||
window.open(url, '', 'menubar=no, width=500, height=400');
|
||||
},
|
||||
|
||||
@@ -204,7 +204,7 @@ publicWidget.registry.boothRegistration = publicWidget.Widget.extend({
|
||||
if (this._isConfirmationFormValid($form)) {
|
||||
const formData = new FormData($form[0]);
|
||||
const response = await $.ajax({
|
||||
url: `/event/${this.$el.data('eventId')}/booth/confirm`,
|
||||
url: `/event/${encodeURIComponent(this.$el.data('eventId'))}/booth/confirm`,
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
|
||||
@@ -47,7 +47,7 @@ var ExhibitorConnectClosedDialog = Dialog.extend({
|
||||
*/
|
||||
async _fetchSponsor() {
|
||||
const sponsorData = await this._rpc({
|
||||
route: `/event_sponsor/${this.sponsorId}/read`
|
||||
route: `/event_sponsor/${encodeURIComponent(this.sponsorId)}/read`
|
||||
});
|
||||
sponsorData.website_description = Markup(sponsorData.website_description);
|
||||
this.sponsorData = sponsorData;
|
||||
|
||||
@@ -171,7 +171,7 @@ publicWidget.registry.websiteEventTrackProposalForm = publicWidget.Widget.extend
|
||||
const formData = new FormData(this.$el[0]);
|
||||
|
||||
const response = await $.ajax({
|
||||
url: `/event/${this.$el.data('eventId')}/track_proposal/post`,
|
||||
url: `/event/${encodeURIComponent(this.$el.data('eventId'))}/track_proposal/post`,
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
|
||||
@@ -8,7 +8,7 @@ export class AddForumFormController extends NewContentFormController {
|
||||
* @override
|
||||
*/
|
||||
computePath() {
|
||||
return `/forum/${this.model.root.data.id}`;
|
||||
return `/forum/${encodeURIComponent(this.model.root.data.id)}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({
|
||||
// welcome message action button
|
||||
var forumLogin = _.string.sprintf('%s/web?redirect=%s',
|
||||
window.location.origin,
|
||||
escape(window.location.href)
|
||||
encodeURIComponent(window.location.href)
|
||||
);
|
||||
$('.forum_register_url').attr('href', forumLogin);
|
||||
|
||||
@@ -252,7 +252,7 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({
|
||||
const linkLabel = _t("Read the guidelines to know how to gain karma.");
|
||||
notifOptions.message = Markup`
|
||||
${notifOptions.message}<br/>
|
||||
<a class="alert-link" href="/forum/${forumID}/faq">${linkLabel}</a>
|
||||
<a class="alert-link" href="/forum/${encodeURIComponent(forumID)}/faq">${linkLabel}</a>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ publicWidget.registry.ChatRoom = publicWidget.Widget.extend({
|
||||
_openMobileApplication: async function (roomName) {
|
||||
if (config.device.isMobile) {
|
||||
// we are on mobile, open the room in the application
|
||||
window.location = `intent://${this.jitsiServer}/${roomName}#Intent;scheme=org.jitsi.meet;package=org.jitsi.meet;end`;
|
||||
window.location = `intent://${this.jitsiServer}/${encodeURIComponent(roomName)}#Intent;scheme=org.jitsi.meet;package=org.jitsi.meet;end`;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -7,7 +7,7 @@ patch(Persona.prototype, "website_livechat", {
|
||||
get countryFlagUrl() {
|
||||
const country = this.partner?.country ?? this.country;
|
||||
return country
|
||||
? `/base/static/img/country_flags/${country.code.toLowerCase()}.png`
|
||||
? `/base/static/img/country_flags/${encodeURIComponent(country.code.toLowerCase())}.png`
|
||||
: undefined;
|
||||
},
|
||||
get nameOrDisplayName() {
|
||||
|
||||
@@ -21,7 +21,7 @@ patch(ThreadService.prototype, "website_livechat", {
|
||||
avatarUrl(persona, thread) {
|
||||
if (persona?.type === "visitor" && thread?.id) {
|
||||
return persona.partner
|
||||
? `/mail/channel/${thread.id}/partner/${persona.id}/avatar_128`
|
||||
? `/mail/channel/${encodeURIComponent(thread.id)}/partner/${encodeURIComponent(persona.id)}/avatar_128`
|
||||
: DEFAULT_AVATAR;
|
||||
}
|
||||
return this._super(persona, thread);
|
||||
|
||||
@@ -67,8 +67,8 @@ var ProductComparison = publicWidget.Widget.extend(VariantMixin, {
|
||||
$(document.body).on('click.product_comparaison_widget', '.o_comparelist_remove', function (ev) {
|
||||
self._removeFromComparelist(ev);
|
||||
self.guard.exec(function() {
|
||||
var new_link = '/shop/compare?products=' + self.comparelist_product_ids.toString();
|
||||
window.location.href = _.isEmpty(self.comparelist_product_ids) ? '/shop' : new_link;
|
||||
const newLink = '/shop/compare?products=' + encodeURIComponent(self.comparelist_product_ids);
|
||||
window.location.href = _.isEmpty(self.comparelist_product_ids) ? '/shop' : newLink;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -240,7 +240,8 @@ var ProductComparison = publicWidget.Widget.extend(VariantMixin, {
|
||||
this.$('.o_comparelist_products').addClass('d-md-block');
|
||||
if (this.comparelist_product_ids.length >=2) {
|
||||
this.$('.o_comparelist_button').addClass('d-md-block');
|
||||
this.$('.o_comparelist_button a').attr('href', '/shop/compare?products='+this.comparelist_product_ids.toString());
|
||||
this.$('.o_comparelist_button a').attr('href',
|
||||
'/shop/compare?products=' + encodeURIComponent(this.comparelist_product_ids));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -609,7 +609,7 @@
|
||||
} else if (slideData.category === 'video' && slideData.videoSourceType === 'vimeo') {
|
||||
slideData.embedCode = Markup(slideData.embedCode);
|
||||
} else if (slideData.category === 'infographic') {
|
||||
slideData.embedUrl = `/web/image/slide.slide/${slideData.id}/image_1024`;
|
||||
slideData.embedUrl = `/web/image/slide.slide/${encodeURIComponent(slideData.id)}/image_1024`;
|
||||
} else if (slideData.category === 'document') {
|
||||
slideData.embedUrl = $(slideData.embedCode).attr('src');
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ var CourseJoinWidget = publicWidget.Widget.extend({
|
||||
url += '?fullscreen=1';
|
||||
}
|
||||
} else {
|
||||
url = `/slides/${this.channel.channelId}`;
|
||||
url = `/slides/${encodeURIComponent(this.channel.channelId)}`;
|
||||
}
|
||||
document.location = sprintf('/web/login?redirect=%s', encodeURIComponent(url));
|
||||
},
|
||||
@@ -119,7 +119,7 @@ var CourseJoinWidget = publicWidget.Widget.extend({
|
||||
const message = data.error_signup_allowed ?
|
||||
_t('Please <a href="/web/login?redirect=%s">login</a> or <a href="/web/signup?redirect=%s">create an account</a> to join this course') :
|
||||
_t('Please <a href="/web/login?redirect=%s">login</a> to join this course');
|
||||
self._popoverAlert(self.$el, sprintf(message, document.URL, document.URL));
|
||||
self._popoverAlert(self.$el, sprintf(message, encodeURIComponent(document.URL), encodeURIComponent(document.URL)));
|
||||
} else if (data.error === 'join_done') {
|
||||
self._popoverAlert(self.$el, _t('You have already joined this channel'));
|
||||
} else {
|
||||
|
||||
@@ -791,7 +791,7 @@
|
||||
.text(_t('Mark To Do'))
|
||||
.removeAttr('title')
|
||||
.removeAttr('aria-disabled')
|
||||
.attr('href', `/slides/slide/${slide.id}/set_uncompleted`);
|
||||
.attr('href', `/slides/slide/${encodeURIComponent(slide.id)}/set_uncompleted`);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ var SlideLikeWidget = publicWidget.Widget.extend({
|
||||
const message = data.error_signup_allowed ?
|
||||
_t('Please <a href="/web/login?redirect=%s">login</a> or <a href="/web/signup?redirect=%s">create an account</a> to vote for this lesson') :
|
||||
_t('Please <a href="/web/login?redirect=%s">login</a> to vote for this lesson');
|
||||
self._popoverAlert(self.$el, sprintf(message, document.URL, document.URL));
|
||||
self._popoverAlert(self.$el, sprintf(message, encodeURIComponent(document.URL), encodeURIComponent(document.URL)));
|
||||
} else if (data.error === 'slide_access') {
|
||||
self._popoverAlert(self.$el, _t('You don\'t have access to this lesson'));
|
||||
} else if (data.error === 'channel_membership_required') {
|
||||
|
||||
@@ -62,7 +62,7 @@ publicWidget.registry.twitter = publicWidget.Widget.extend({
|
||||
.replace(
|
||||
/[#]+[A-Za-z0-9_]+/g,
|
||||
function (hashtag) {
|
||||
return _makeLink('http://twitter.com/search?q='+hashtag.replace('#',''), hashtag);
|
||||
return _makeLink('http://twitter.com/search?q=' + encodeURIComponent(hashtag.replace('#', '')), hashtag);
|
||||
}
|
||||
));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user