diff --git a/src/core/utils.js b/src/core/utils.js
index a26557913e..0d1f37537b 100644
--- a/src/core/utils.js
+++ b/src/core/utils.js
@@ -1,5 +1,6 @@
import $ from "jquery";
import logging from "@patternslib/patternslib/src/core/logging";
+import _ from "lodash";
const logger = logging.getLogger("core utils");
@@ -438,6 +439,78 @@ const resolveIcon = async function (name) {
}
};
+/**
+ * This is for avoiding CSP issues with underscore's template
+ *
+ * More details: https://github.com/plone/mockup/issues/1306
+ *
+ * Implementation was taken from https://github.com/silvermine/undertemplate/blob/master/src/index.js
+ *
+ */
+var template = function (text, userSettings) {
+ const ESCAPE_ENTITIES = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": ''', // eslint-disable-line quotes
+ '`': '`',
+ };
+
+ const DEFAULT_SETTINGS = {
+ escape: /<%-([\s\S]+?)%>/g,
+ interpolate: /<%=([\s\S]+?)%>/g,
+ };
+
+ let parts = [],
+ index = 0,
+ settings = _.defaults({}, userSettings, DEFAULT_SETTINGS),
+ regExpPattern, matcher;
+
+ regExpPattern = [
+ settings.escape.source,
+ settings.interpolate.source,
+ ];
+ matcher = new RegExp(regExpPattern.join('|') + '|$', 'g');
+
+ text.replace(matcher, function(match, escape, interpolate, offset) {
+ parts.push(text.slice(index, offset));
+ index = offset + match.length;
+
+ if (escape) {
+ parts.push(function(data) {
+ return escapeHTML(getValue(escape, data));
+ });
+ } else if (interpolate) {
+ parts.push(getValue.bind(null, interpolate));
+ }
+ });
+
+ return function(data) {
+ return _.reduce(parts, function(str, part) {
+ return str + (_.isFunction(part) ? part(data) : part);
+ }, '');
+ };
+
+ function escapeHTML(str) {
+ let pattern = '(?:' + _.keys(ESCAPE_ENTITIES).join('|') + ')',
+ testRegExp = new RegExp(pattern),
+ replaceRegExp = new RegExp(pattern, 'g');
+
+ if (testRegExp.test(str)) {
+ return str.replace(replaceRegExp, function(match) {
+ return ESCAPE_ENTITIES[match];
+ });
+ }
+
+ return str;
+ }
+
+ function getValue(path, data) {
+ return _.get(data, _.trim(path), '');
+ }
+}
+
export default {
bool: bool,
escapeHTML: escapeHTML,
@@ -453,4 +526,5 @@ export default {
resolveIcon: resolveIcon,
setId: setId,
storage: storage,
+ template: template,
};
diff --git a/src/pat/relateditems/relateditems.js b/src/pat/relateditems/relateditems.js
index 49a958740a..252e7e44b7 100644
--- a/src/pat/relateditems/relateditems.js
+++ b/src/pat/relateditems/relateditems.js
@@ -66,6 +66,10 @@ export default Base.extend({
resultTemplateSelector: null,
selectionTemplateSelector: null,
toolbarTemplateSelector: null,
+ toolbarNotAutoTemplateSelector: null,
+ toolbarRecentlyUsedTemplateSelector: null,
+ toolbarFavoritesTemplateSelector: null,
+ toolbarUploadTemplateSelector: null,
// needed
multiple: true,
@@ -114,7 +118,7 @@ export default Base.extend({
one_level_up: _t("Go one level up"),
});
options._item = item;
- return _.template(template)(options);
+ return utils.template(template)(options);
},
setAjax() {
@@ -230,9 +234,9 @@ export default Base.extend({
async renderToolbar() {
const path = this.currentPath;
- let html;
const parts = path.split("/");
+ let html = "";
let itemPath = "";
let itemsHtml = "";
for (const part of parts) {
@@ -255,30 +259,67 @@ export default Base.extend({
let recentlyUsedHtml = "";
if (this.options.recentlyUsed) {
const recentlyUsed = this.recentlyUsed(true); // filter out only those items which can actually be selected
- for (const item of recentlyUsed.reverse()) {
+ for (let item of recentlyUsed.reverse()) {
// reverse to get newest first.
+ if (item['imgsrc'] === undefined) {
+ item['imgsrc'] = '';
+ }
+
+ if (item['getURL'] && (item['getIcon'] || item['portal_type'] === "Image")){
+ item['imgsrc'] = ''
+ }
+
+ var klass = [
+ 'pat-relateditems-recentlyused-title',
+ item['portal_type'] ? ' contenttype-' + item['portal_type'].toLowerCase() : '',
+ item['review_state'] ? ' state-' + item['review_state'] : ''
+ ]
+
+ item['itemclass'] = klass.join(' ')
+
recentlyUsedHtml =
recentlyUsedHtml + this.applyTemplate("recentlyused", item);
}
}
- html = this.applyTemplate("toolbar", {
+ if (this.options.mode !=='auto'){
+ html += this.applyTemplate('toolbarNotAuto', {
+ searchModeClass: (this.options.mode=='search') ? 'btn-primary':'btn-default',
+ searchModeText: _t('Search'),
+ browseModeClass: (this.options.mode=='browse') ? 'btn-primary':'btn-default',
+ browseModeText: _t('Browse'),
+ });
+ }
+
+ html += this.applyTemplate("toolbar", {
items: itemsHtml,
- favItems: favoritesHtml,
- favText: _t("Favorites"),
searchText: _t("Current path:"),
- searchModeText: _t("Search"),
- browseModeText: _t("Browse"),
- recentlyUsedItems: recentlyUsedHtml,
- recentlyUsedText: _t("Recently Used"),
icon_root: await utils.resolveIcon("house-fill"),
- icon_recently_used: await utils.resolveIcon("grid-fill"),
- icon_favorites: await utils.resolveIcon("star-fill"),
- icon_upload: await utils.resolveIcon("cloud-arrow-up"),
- upload: this.options.upload,
- upload_text: _t("Upload"),
});
+ if (recentlyUsedHtml != "") {
+ html += this.applyTemplate('toolbarRecentlyUsed', {
+ recentlyUsedItems: recentlyUsedHtml,
+ recentlyUsedText: _t('Recently Used'),
+ icon_recently_used: await utils.resolveIcon("grid-fill"),
+ });
+ }
+
+ if (this.options.favorites.length > 0){
+ html += this.applyTemplate('toolbarFavorites', {
+ favItems: favoritesHtml,
+ favText: _t('Favorites'),
+ icon_favorites: await utils.resolveIcon("star-fill"),
+ });
+ }
+
+ if (this.options.upload){
+ html += this.applyTemplate('toolbarUpload', {
+ icon_upload: await utils.resolveIcon("cloud-arrow-up"),
+ upload_text: _t("Upload"),
+ });
+ }
+
this.$toolbar.html(html);
// unbind mouseup event from select2 to override the behavior:
@@ -512,6 +553,9 @@ export default Base.extend({
this.resultTemplate = (await import("./templates/result.xml")).default; // prettier-ignore
this.selectionTemplate = (await import("./templates/selection.xml")).default; // prettier-ignore
this.toolbarTemplate = (await import("./templates/toolbar.xml")).default; // prettier-ignore
+ this.toolbarNotAutoTemplate = (await import("./templates/toolbar_not_auto.xml")).default; // prettier-ignore
+ this.toolbarRecentlyUsedTemplate = (await import("./templates/toolbar_recently_used.xml")).default; // prettier-ignore
+ this.toolbarFavoritesTemplate = (await import("./templates/toolbar_favorites.xml")).default; // prettier-ignore
this.browsing = this.options.mode !== "search";
@@ -542,6 +586,13 @@ export default Base.extend({
},
item
);
+ if (item['imgsrc'] === undefined) {
+ item['imgsrc'] = '';
+ }
+
+ if (item['getURL'] && (item['getIcon'] || item['portal_type'] === "Image")){
+ item['imgsrc'] = '