[IMP] web_editor: support 5 dynamic colors for illustrations

Before this commit only the first default palette color was configurable
on illustrations

After this commit all 5 default palette colors are configurable on
illustrations

task-2368585

closes odoo/odoo#60503

Signed-off-by: Jérémy Kersten (jke) <jke@openerp.com>
This commit is contained in:
Benoit Socias
2020-11-20 13:13:15 +00:00
parent f5c1e09772
commit bde8abcfeb
6 changed files with 83 additions and 25 deletions
+10 -4
View File
@@ -5,6 +5,7 @@ import logging
import re
import time
import requests
import werkzeug.urls
import werkzeug.wrappers
from PIL import Image, ImageFont, ImageDraw
from lxml import etree
@@ -12,7 +13,7 @@ from base64 import b64decode, b64encode
from odoo.http import request
from odoo import http, tools, _, SUPERUSER_ID
from odoo.addons.http_routing.models.ir_http import slug
from odoo.addons.http_routing.models.ir_http import slug, unslug
from odoo.exceptions import UserError
from odoo.modules.module import get_module_path, get_resource_path
from odoo.tools.misc import file_open
@@ -519,8 +520,11 @@ class Web_Editor(http.Controller):
"""
svg = None
if module == 'illustration':
attachment = request.env['ir.attachment'].sudo().search([('url', '=like', request.httprequest.path), ('public', '=', True)], limit=1)
if not attachment:
attachment = request.env['ir.attachment'].sudo().browse(unslug(filename)[1])
if (not attachment.exists()
or attachment.type != 'binary'
or not attachment.public
or not attachment.url.startswith(request.httprequest.path)):
raise werkzeug.exceptions.NotFound()
svg = b64decode(attachment.datas).decode('utf-8')
else:
@@ -587,6 +591,7 @@ class Web_Editor(http.Controller):
<media_id>: {
'query': 'space separated search terms',
'is_dynamic_svg': True/False,
'dynamic_colors': maps color names to their color,
}, ...
}
"""
@@ -618,7 +623,8 @@ class Web_Editor(http.Controller):
'res_id': 0,
})
if media[id]['is_dynamic_svg']:
attachment['url'] = '/web_editor/shape/illustration/%s' % slug(attachment)
colorParams = werkzeug.urls.url_encode(media[id]['dynamic_colors'])
attachment['url'] = '/web_editor/shape/illustration/%s?%s' % (slug(attachment), colorParams)
attachments.append(attachment._get_media_info())
return attachments
+2 -1
View File
@@ -40,7 +40,8 @@ class IrAttachment(models.Model):
if attachment.url:
# For attachments-by-url, unique is used as a cachebuster. They
# currently do not leverage max-age headers.
attachment.image_src = '%s?unique=%s' % (attachment.url, unique)
separator = '&' if '?' in attachment.url else '?'
attachment.image_src = '%s%sunique=%s' % (attachment.url, separator, unique)
else:
name = url_quote(attachment.name)
attachment.image_src = '/web/image/%s-%s/%s' % (attachment.id, unique, name)
@@ -2078,7 +2078,12 @@ var SnippetsMenu = Widget.extend({
// Color-customize dynamic SVGs in dropped snippets with current theme colors.
[...$snippetToInsert.find('img[src^="/web_editor/shape/"]')].forEach(dynamicSvg => {
const colorCustomizedURL = new URL(dynamicSvg.getAttribute('src'), window.location.origin);
colorCustomizedURL.searchParams.set('c1', getCSSVariableValue('o-color-1'));
colorCustomizedURL.searchParams.forEach((key, value) => {
const match = key.match(/^c([1-5])$/);
if (match) {
colorCustomizedURL.searchParams.set(key, getCSSVariableValue(`o-color-${match[1]}`))
}
})
dynamicSvg.src = colorCustomizedURL.pathname + colorCustomizedURL.search;
});
@@ -3782,7 +3782,7 @@ registry.BackgroundImage = SnippetOptionWidget.extend({
return;
}
const newURL = new URL(currentSrc, window.location.origin);
newURL.searchParams.set('c1', normalizeColor(widgetValue));
newURL.searchParams.set(params.colorName, normalizeColor(widgetValue));
const src = newURL.pathname + newURL.search;
await loadImage(src);
await this.editorHelpers.setStyle(this.editor, this.$target.get(), 'background-image', `url('${src}')`);
@@ -3825,12 +3825,12 @@ registry.BackgroundImage = SnippetOptionWidget.extend({
/**
* @override
*/
_computeWidgetState: function (methodName) {
_computeWidgetState: function (methodName, params) {
switch (methodName) {
case 'background':
return getBgImageURL(this.$target[0]);
case 'dynamicColor':
return new URL(getBgImageURL(this.$target[0]), window.location.origin).searchParams.get('c1');
return new URL(getBgImageURL(this.$target[0]), window.location.origin).searchParams.get(params.colorName);
}
return this._super(...arguments);
},
@@ -3838,7 +3838,10 @@ registry.BackgroundImage = SnippetOptionWidget.extend({
* @override
*/
_computeWidgetVisibility(widgetName, params) {
if (widgetName === 'dynamic_color_opt') {
if ('colorName' in params) {
const src = new URL(getBgImageURL(this.$target[0]), window.location.origin);
return src.searchParams.has(params.colorName);
} else if (widgetName === 'dynamic_color_opt') {
const src = new URL(getBgImageURL(this.$target[0]), window.location.origin);
return src.origin === window.location.origin && src.pathname.startsWith('/web_editor/shape/');
}
@@ -3956,7 +3959,7 @@ registry.BackgroundShape = SnippetOptionWidget.extend({
_renderCustomXML(uiFragment) {
Object.keys(this._getDefaultColors()).map(colorName => {
uiFragment.querySelector('[data-name="colors"]')
.prepend($(`<we-colorpicker data-color="true" data-color-name="${colorName}">`)[0]);
.prepend($(`<we-colorpicker data-color="true" data-color-name="${colorName}"></we-colorpicker>`)[0]);
});
uiFragment.querySelectorAll('we-select-pager we-button[data-shape]').forEach(btn => {
@@ -4814,7 +4817,7 @@ registry.DynamicSvg = SnippetOptionWidget.extend({
return;
}
const newURL = new URL(target.src, window.location.origin);
newURL.searchParams.set('c1', normalizeColor(widgetValue));
newURL.searchParams.set(params.colorName, normalizeColor(widgetValue));
const src = newURL.pathname + newURL.search;
await loadImage(src);
await this.editorHelpers.setAttribute(this.editor, target, 'src', src);
@@ -4833,7 +4836,16 @@ registry.DynamicSvg = SnippetOptionWidget.extend({
_computeWidgetState(methodName, params) {
switch (methodName) {
case 'color':
return new URL(this.$target[0].src, window.location.origin).searchParams.get('c1');
return new URL(this.$target[0].src, window.location.origin).searchParams.get(params.colorName);
}
return this._super(...arguments);
},
/**
* @override
*/
_computeWidgetVisibility(widgetName, params) {
if ('colorName' in params) {
return new URL(this.$target[0].src, window.location.origin).searchParams.get(params.colorName);
}
return this._super(...arguments);
},
@@ -383,6 +383,7 @@ var FileWidget = SearchableMediaWidget.extend({
media.id, {
query: media.query || '',
is_dynamic_svg: !!media.isDynamicSVG,
dynamic_colors: media.dynamicColors,
}
]));
let mediaAttachments = [];
@@ -395,10 +396,15 @@ var FileWidget = SearchableMediaWidget.extend({
});
}
const selected = this.selectedAttachments.concat(mediaAttachments).map(attachment => {
// Color-customize dynamic SVGs with the primary theme color
// Color-customize dynamic SVGs with the theme colors
if (attachment.image_src && attachment.image_src.startsWith('/web_editor/shape/')) {
const colorCustomizedURL = new URL(attachment.image_src, window.location.origin);
colorCustomizedURL.searchParams.set('c1', getCSSVariableValue('o-color-1'));
colorCustomizedURL.searchParams.forEach((value, key) => {
const match = key.match(/^c([1-5])$/);
if (match) {
colorCustomizedURL.searchParams.set(key, getCSSVariableValue(`o-color-${match[1]}`))
}
})
attachment.image_src = colorCustomizedURL.pathname + colorCustomizedURL.search;
}
return attachment;
@@ -886,13 +892,21 @@ var ImageWidget = FileWidget.extend({
}
const result = await this._super(number, offset);
// Color-substitution for dynamic SVG attachment
const primaryColor = getCSSVariableValue('o-color-1');
const primaryColors = {};
for (let color = 1; color <= 5; color++) {
primaryColors[color] = getCSSVariableValue('o-color-' + color);
}
this.attachments.forEach(attachment => {
if (attachment.image_src.startsWith('/')) {
const newURL = new URL(attachment.image_src, window.location.origin);
// Set the main color of dynamic SVGs to o-color-1
// Set the main colors of dynamic SVGs to o-color-1~5
if (attachment.image_src.startsWith('/web_editor/shape/')) {
newURL.searchParams.set('c1', primaryColor);
newURL.searchParams.forEach((value, key) => {
const match = key.match(/^c([1-5])$/);
if (match) {
newURL.searchParams.set(key, primaryColors[match[1]]);
}
})
} else {
// Set height so that db images load faster
newURL.searchParams.set('height', 2 * this.MIN_ROW_HEIGHT);
@@ -1026,17 +1040,25 @@ var ImageWidget = FileWidget.extend({
try {
const response = await fetch(mediaUrl);
if (response.headers.get('content-type') === 'image/svg+xml') {
const svg = await response.text();
const colorRegex = new RegExp(DEFAULT_PALETTE['1'], 'gi');
if (colorRegex.test(svg)) {
const fileName = mediaUrl.split('/').pop();
const file = new File([svg.replace(colorRegex, getCSSVariableValue('o-color-1'))], fileName, {
let svg = await response.text();
const fileName = mediaUrl.split('/').pop();
const dynamicColors = {};
const combinedColorsRegex = new RegExp(Object.values(DEFAULT_PALETTE).join('|'), 'gi');
svg = svg.replace(combinedColorsRegex, match => {
const colorId = Object.keys(DEFAULT_PALETTE).find(key => DEFAULT_PALETTE[key] === match.toUpperCase());
const colorKey = 'c' + colorId
dynamicColors[colorKey] = getCSSVariableValue('o-color-' + colorId);
return dynamicColors[colorKey];
});
if (Object.keys(dynamicColors).length) {
const file = new File([svg], fileName, {
type: "image/svg+xml",
});
img.src = URL.createObjectURL(file);
const media = this.libraryMedia.find(media => media.id === parseInt(cell.dataset.mediaId));
if (media) {
media.isDynamicSVG = true;
media.dynamicColors = dynamicColors;
}
// We changed the src: wait for the next load event to do the styling
return;
+14 -2
View File
@@ -180,7 +180,13 @@
data-name="bg_image_opt"
data-dependencies="bg_image_opt"/>
</we-row>
<we-colorpicker string=" ⌙ Dynamic color" data-dynamic-color="true" data-name="dynamic_color_opt"/>
<we-row string=" ⌙ Dyn. Colors" title="Dynamic Colors" data-name="dynamic_color_opt"><!-- &emsp; -->
<we-colorpicker data-dynamic-color="true" data-color-name="c1"/>
<we-colorpicker data-dynamic-color="true" data-color-name="c2"/>
<we-colorpicker data-dynamic-color="true" data-color-name="c3"/>
<we-colorpicker data-dynamic-color="true" data-color-name="c4"/>
<we-colorpicker data-dynamic-color="true" data-color-name="c5"/>
</we-row>
</div>
<div data-js="BackgroundPosition"
@@ -372,7 +378,13 @@
</div>
<div data-js="DynamicSvg" data-selector="img">
<we-colorpicker string="Dynamic color" data-color="true"/>
<we-row string="Dynamic Colors" data-name="colors">
<we-colorpicker data-color="true" data-color-name="c1"/>
<we-colorpicker data-color="true" data-color-name="c2"/>
<we-colorpicker data-color="true" data-color-name="c3"/>
<we-colorpicker data-color="true" data-color-name="c4"/>
<we-colorpicker data-color="true" data-color-name="c5"/>
</we-row>
</div>
</template>