This is an automated email from the ASF dual-hosted git repository.
dklco pushed a commit to branch commons-thumbnails
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-app-cms.git
The following commit(s) were added to refs/heads/commons-thumbnails by this
push:
new da69612 A mostly working implementation of transformations
da69612 is described below
commit da69612d8d89226e15640bb1fc4d0e46f41f6c09
Author: Dan Klco <[email protected]>
AuthorDate: Sun Jul 25 22:59:59 2021 -0400
A mostly working implementation of transformations
---
.../reference/components/general/image/edit.json | 11 +-
.../reference/components/general/image/image.jsp | 9 +-
.../components/general/image/transformations.jsp | 8 +-
ui/src/main/frontend/js/cms.fields.js | 245 +++++++++++++--------
ui/src/main/frontend/js/cms.pathfield.js | 39 ++--
.../components/editor/fields/base/base.jsp | 7 +-
.../components/editor/fields/path/path.jsp | 9 +-
.../components/editor/scripts/transformations.jsp | 4 +-
8 files changed, 217 insertions(+), 115 deletions(-)
diff --git
a/reference/src/main/resources/jcr_root/apps/reference/components/general/image/edit.json
b/reference/src/main/resources/jcr_root/apps/reference/components/general/image/edit.json
index ef07361..4b3cf95 100644
---
a/reference/src/main/resources/jcr_root/apps/reference/components/general/image/edit.json
+++
b/reference/src/main/resources/jcr_root/apps/reference/components/general/image/edit.json
@@ -13,7 +13,11 @@
"label": "Image Source",
"name": "src",
"titleProperty": "jcr:content/jcr:title",
- "required": true
+ "required": true,
+ "events": {
+ "input": "const srcValue =
document.querySelector('input[name=src]').value;\r\nconst transformationField =
document.querySelector('select[name=transformation]').closest('.field');\r\ntransformationField.disabled=true;\r\nconst
url =
`${transformationField.dataset.path}.html${location.pathname.replace('\/cms\/editor\/edit.html','')}?src=${srcValue}`;\r\nfetch(url).then(res
=> res.text()).then(html => {\r\n const div =
document.createElement('div');\r\n div.innerHTML [...]
+ "load": "const srcValue =
document.querySelector('input[name=src]').value;\r\nconst transformationField =
document.querySelector('select[name=transformation]').closest('.field');\r\ntransformationField.disabled=true;\r\nconst
url =
`${transformationField.dataset.path}.html${location.pathname.replace('\/cms\/editor\/edit.html','')}?src=${srcValue}`;\r\nfetch(url).then(res
=> res.text()).then(html => {\r\n const div =
document.createElement('div');\r\n div.innerHTML = [...]
+ }
},
"transformation": {
"jcr:primaryType": "nt:unstructured",
@@ -30,6 +34,11 @@
"name": "transformationFormat",
"required": false,
"options": {
+ "default": {
+ "jcr:primaryType": "nt:unstructured",
+ "label": "Default Extension",
+ "value": "default"
+ },
"png": {
"jcr:primaryType": "nt:unstructured",
"label": "PNG",
diff --git
a/reference/src/main/resources/jcr_root/apps/reference/components/general/image/image.jsp
b/reference/src/main/resources/jcr_root/apps/reference/components/general/image/image.jsp
index f5d9d4c..58ac1c9 100644
---
a/reference/src/main/resources/jcr_root/apps/reference/components/general/image/image.jsp
+++
b/reference/src/main/resources/jcr_root/apps/reference/components/general/image/image.jsp
@@ -19,7 +19,14 @@
<%@include file="/libs/sling-cms/global.jsp"%>
<c:if test="${not empty properties.src}">
<c:if test="${not empty properties.transformation}">
- <c:set var="transform"
value=".transform/${properties.transformation}.${properties.transformationFormat}"
/>
+ <c:choose>
+ <c:when test="${properties.transformationFormat == 'default'}">
+ <c:set var="transform"
value=".transform/${properties.transformation}" />
+ </c:when>
+ <c:otherwise>
+ <c:set var="transform"
value=".transform/${properties.transformation}.${properties.transformationFormat}"
/>
+ </c:otherwise>
+ </c:choose>
</c:if>
<img src="${properties.src}${transform}" alt="${properties.alt}"
class="${properties.imageClass}" />
</c:if>
\ No newline at end of file
diff --git
a/reference/src/main/resources/jcr_root/apps/reference/components/general/image/transformations.jsp
b/reference/src/main/resources/jcr_root/apps/reference/components/general/image/transformations.jsp
index 50eb1e8..a69d9e0 100644
---
a/reference/src/main/resources/jcr_root/apps/reference/components/general/image/transformations.jsp
+++
b/reference/src/main/resources/jcr_root/apps/reference/components/general/image/transformations.jsp
@@ -17,10 +17,10 @@
* under the License.
*/ --%>
<%@include file="/libs/sling-cms/global.jsp"%>
-<sling:adaptTo adaptable="${slingRequest.requestPathInfo.suffixResource}"
adaptTo="org.apache.sling.thumbnails.TransformationManager"
var="transformationManager" />
+<sling:adaptTo adaptable="${slingRequest}"
adaptTo="org.apache.sling.thumbnails.RenderedResource" var="rendered" />
<option value="">None</option>
-<c:forEach var="transformation"
items="${transformationManager.transformations}">
- <option
${slingRequest.requestPathInfo.suffixResource.valueMap.transformation ==
transformation.name ? 'selected' : ''}
value="${sling:encode(transformation.name,'HTML_ATTR')}">
- ${sling:encode(transformation.name,'HTML')}
+<c:forEach var="rendition" items="${rendered.supportedRenditions}">
+ <option
${slingRequest.requestPathInfo.suffixResource.valueMap.transformation ==
rendition ? 'selected' : ''} value="${sling:encode(rendition,'HTML_ATTR')}">
+ ${sling:encode(rendition,'HTML')}
</option>
</c:forEach>
\ No newline at end of file
diff --git a/ui/src/main/frontend/js/cms.fields.js
b/ui/src/main/frontend/js/cms.fields.js
index 11a9b8b..e759258 100644
--- a/ui/src/main/frontend/js/cms.fields.js
+++ b/ui/src/main/frontend/js/cms.fields.js
@@ -18,11 +18,11 @@
*/
/* global wysihtml, wysihtmlParserRules */
-rava.bind('.file', {
+rava.bind(".file", {
callbacks: {
created() {
const field = this;
- const close = field.closest('form').querySelector('a.close');
+ const close = field.closest("form").querySelector("a.close");
function setProgress(m, progress) {
const meter = m;
@@ -37,89 +37,131 @@ rava.bind('.file', {
formData.append("_charset_", "utf-8");
const xhr = new XMLHttpRequest();
- xhr.upload.addEventListener('loadstart', () => {
- setProgress(meter, 0);
- }, false);
- xhr.upload.addEventListener('progress', (event) => {
- const percent = (event.loaded / event.total) * 100;
- setProgress(meter, percent);
- }, false);
- xhr.upload.addEventListener('load', () => {
- meter.classList.add('is-info');
- }, false);
- xhr.addEventListener('readystatechange', (event) => {
- let status; let text; let
- readyState;
- try {
- readyState = event.target.readyState;
- text = event.target.responseText;
- status = event.target.status;
- } catch (e) {
- meter.classList.add('is-danger');
- }
- if (readyState === 4) {
- meter.classList.remove('is-info');
- if (status === 200 && text) {
- meter.classList.add('is-success');
- } else {
- meter.classList.add('is-danger');
- console.warn('Failed to upload %s, recieved message %s',
file.name, text); // eslint-disable-line no-console
+ xhr.upload.addEventListener(
+ "loadstart",
+ () => {
+ setProgress(meter, 0);
+ },
+ false
+ );
+ xhr.upload.addEventListener(
+ "progress",
+ (event) => {
+ const percent = (event.loaded / event.total) * 100;
+ setProgress(meter, percent);
+ },
+ false
+ );
+ xhr.upload.addEventListener(
+ "load",
+ () => {
+ meter.classList.add("is-info");
+ },
+ false
+ );
+ xhr.addEventListener(
+ "readystatechange",
+ (event) => {
+ let status;
+ let text;
+ let readyState;
+ try {
+ readyState = event.target.readyState;
+ text = event.target.responseText;
+ status = event.target.status;
+ } catch (e) {
+ meter.classList.add("is-danger");
}
- }
- }, false);
- xhr.open('POST', action, true);
+ if (readyState === 4) {
+ meter.classList.remove("is-info");
+ if (status === 200 && text) {
+ meter.classList.add("is-success");
+ } else {
+ meter.classList.add("is-danger");
+ console.warn(
+ "Failed to upload %s, recieved message %s",
+ file.name,
+ text
+ ); // eslint-disable-line no-console
+ }
+ }
+ },
+ false
+ );
+ xhr.open("POST", action, true);
xhr.send(formData);
}
function handleFile(scope, file) {
- const it = document.createElement('div');
- const ctr =
scope.closest('.control').querySelector('.file-item-container');
+ const it = document.createElement("div");
+ const ctr = scope
+ .closest(".control")
+ .querySelector(".file-item-container");
let meter = null;
- it.innerHTML = document.querySelector('.file-item-template').innerHTML;
- meter = it.querySelector('.progress');
- it.querySelector('.file-item-name').innerText = file.name;
- ctr.classList.remove('is-hidden');
+ it.innerHTML = document.querySelector(".file-item-template").innerHTML;
+ meter = it.querySelector(".progress");
+ it.querySelector(".file-item-name").innerText = file.name;
+ ctr.classList.remove("is-hidden");
ctr.appendChild(it);
- uploadFile(meter, scope.closest('form').action, file);
+ uploadFile(meter, scope.closest("form").action, file);
}
- field.addEventListener('dragover', (event) => {
- event.preventDefault();
- }, false);
- field.addEventListener('dragenter', (event) => {
- event.preventDefault();
- field.classList.add('is-primary');
- }, false);
- field.addEventListener('dragleave', (event) => {
- event.preventDefault();
- if(!field.contains(event.fromElement)){
- field.classList.remove('is-primary');
- }
- }, false);
- field.addEventListener('drop', (event) => {
- event.preventDefault();
- field.classList.remove('is-primary');
- if (event.dataTransfer.items) {
- const { items } = event.dataTransfer;
- for (let i = 0; i < items.length; i++) { // eslint-disable-line
no-plusplus
- if (items[i].kind === 'file') {
- handleFile(field, items[i].getAsFile());
- }
+ field.addEventListener(
+ "dragover",
+ (event) => {
+ event.preventDefault();
+ },
+ false
+ );
+ field.addEventListener(
+ "dragenter",
+ (event) => {
+ event.preventDefault();
+ field.classList.add("is-primary");
+ },
+ false
+ );
+ field.addEventListener(
+ "dragleave",
+ (event) => {
+ event.preventDefault();
+ if (!field.contains(event.fromElement)) {
+ field.classList.remove("is-primary");
}
- } else {
- const { files } = event.dataTransfer;
- for (let i = 0; i < files.length; i++) { // eslint-disable-line
no-plusplus
- handleFile(field, files[i]);
+ },
+ false
+ );
+ field.addEventListener(
+ "drop",
+ (event) => {
+ event.preventDefault();
+ field.classList.remove("is-primary");
+ if (event.dataTransfer.items) {
+ const { items } = event.dataTransfer;
+ for (let i = 0; i < items.length; i++) {
+ // eslint-disable-line no-plusplus
+ if (items[i].kind === "file") {
+ handleFile(field, items[i].getAsFile());
+ }
+ }
+ } else {
+ const { files } = event.dataTransfer;
+ for (let i = 0; i < files.length; i++) {
+ // eslint-disable-line no-plusplus
+ handleFile(field, files[i]);
+ }
}
- }
- }, false);
- field.closest('form').querySelector('button[type=submit]').remove();
- close.innerText = 'Done';
- close.addEventListener('click', () => {
+ },
+ false
+ );
+ field.closest("form").querySelector("button[type=submit]").remove();
+ close.innerText = "Done";
+ close.addEventListener("click", () => {
window.Sling.CMS.ui.reloadContext();
});
- field.querySelector('input').addEventListener('change', (event) => {
+ field.querySelector("input").addEventListener("change", (event) => {
const { files } = event.target;
- for (let i = 0; i < files.length; i++) { // eslint-disable-line
no-plusplus
+ for (let i = 0; i < files.length; i++) {
+ // eslint-disable-line no-plusplus
handleFile(field, files[i]);
}
});
@@ -128,37 +170,41 @@ rava.bind('.file', {
});
/* Support for updating the namehint when creating a component */
-rava.bind('.namehint', {
+rava.bind(".namehint", {
callbacks: {
created() {
const field = this;
-
this.closest('.Form-Ajax').querySelector('select[name="sling:resourceType"]').addEventListener('change',
(evt) => {
- const resourceType = evt.target.value.split('/');
- field.value = resourceType[resourceType.length - 1];
- });
+ this.closest(".Form-Ajax")
+ .querySelector('select[name="sling:resourceType"]')
+ .addEventListener("change", (evt) => {
+ const resourceType = evt.target.value.split("/");
+ field.value = resourceType[resourceType.length - 1];
+ });
},
},
});
/* Support for repeating form fields */
-rava.bind('.repeating', {
+rava.bind(".repeating", {
callbacks: {
created() {
const ctr = this;
- this.querySelectorAll('.repeating__add').forEach((el) => {
- el.addEventListener('click', (event) => {
+ this.querySelectorAll(".repeating__add").forEach((el) => {
+ el.addEventListener("click", (event) => {
event.stopPropagation();
event.preventDefault();
- const node = ctr.querySelector('.repeating__template >
.repeating__item').cloneNode(true);
- ctr.querySelector('.repeating__container').appendChild(node);
+ const node = ctr
+ .querySelector(".repeating__template > .repeating__item")
+ .cloneNode(true);
+ ctr.querySelector(".repeating__container").appendChild(node);
});
});
},
},
});
-rava.bind('.repeating__item', {
+rava.bind(".repeating__item", {
events: {
- ':scope .repeating__remove': {
+ ":scope .repeating__remove": {
click(event) {
event.stopPropagation();
event.preventDefault();
@@ -168,13 +214,38 @@ rava.bind('.repeating__item', {
},
});
-rava.bind('.rte', {
+rava.bind(".rte", {
callbacks: {
created() {
- new wysihtml.Editor(this.querySelector('.rte-editor'), { //
eslint-disable-line no-new, new-cap
- toolbar: this.querySelector('.rte-toolbar'),
+ new wysihtml.Editor(this.querySelector(".rte-editor"), {
+ // eslint-disable-line no-new, new-cap
+ toolbar: this.querySelector(".rte-toolbar"),
parserRules: wysihtmlParserRules,
});
},
},
});
+
+rava.bind('.field[data-events]:not([data-events=""])', {
+ callbacks: {
+ async created() {
+ const events = this.dataset.events.split(",").filter((e) => e !== "");
+ const res = await fetch(`${this.dataset.path}/events.json`, {
+ cache: "no-cache",
+ headers: {
+ Accept: "application/json",
+ },
+ });
+ const handlers = await res.json();
+ for (var event of events) {
+ this.querySelectorAll("input,select,textarea").forEach((el) => {
+ if (event === "load") {
+ Function(handlers[event])();
+ } else {
+ el.addEventListener(event, Function(handlers[event]));
+ }
+ });
+ }
+ },
+ },
+});
diff --git a/ui/src/main/frontend/js/cms.pathfield.js
b/ui/src/main/frontend/js/cms.pathfield.js
index e66b14d..fbf0f0c 100644
--- a/ui/src/main/frontend/js/cms.pathfield.js
+++ b/ui/src/main/frontend/js/cms.pathfield.js
@@ -19,24 +19,28 @@
/* global autoComplete */
Sling.CMS.pathfield = null;
-rava.bind('input.pathfield', {
+rava.bind("input.pathfield", {
callbacks: {
created() {
- const { type } = this.dataset;
- const { base } = this.dataset;
-
- new autoComplete({ // eslint-disable-line no-new, new-cap
+ const { base, type } = this.dataset;
+ const field = this;
+ // eslint-disable-line no-new, new-cap
+ new autoComplete({
minChars: 1,
selector: this,
+ debounce: 100,
+ onSelect: function (e, term, item) {
+ setTimeout(() => field.dispatchEvent(new Event("input")), 500);
+ },
source(t, response) {
let term = t;
- if (term === '/') {
+ if (term === "/") {
term = base;
}
fetch(
`/bin/cms/paths?path=${encodeURIComponent(
- term,
- )}&type=${encodeURIComponent(type)}`,
+ term
+ )}&type=${encodeURIComponent(type)}`
)
.then((resp) => resp.json())
.then((data) => {
@@ -48,15 +52,15 @@ rava.bind('input.pathfield', {
},
});
-rava.bind('.search-button', {
+rava.bind(".search-button", {
events: {
click() {
- Sling.CMS.pathfield = this.closest('.field').querySelector('.pathfield');
+ Sling.CMS.pathfield = this.closest(".field").querySelector(".pathfield");
},
},
});
-rava.bind('.search-select-button', {
+rava.bind(".search-select-button", {
events: {
click() {
const { path } = this.dataset;
@@ -65,23 +69,24 @@ rava.bind('.search-select-button', {
} else {
Sling.CMS.pathfield.postMessage(
{
- action: 'slingcms.setpath',
+ action: "slingcms.setpath",
path,
},
- window.location.origin,
+ window.location.origin
);
}
- this.closest('.modal').remove();
+ this.closest(".modal").remove();
},
},
});
window.addEventListener(
- 'message',
+ "message",
(event) => {
- if (event.data.action === 'slingcms.setpath') {
+ if (event.data.action === "slingcms.setpath") {
Sling.CMS.pathfield.value = event.data.path;
+ Sling.CMS.pathfield.dispatchEvent(new Event("change"));
}
},
- false,
+ false
);
diff --git
a/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/base/base.jsp
b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/base/base.jsp
index e2b2031..a9f3246 100644
---
a/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/base/base.jsp
+++
b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/base/base.jsp
@@ -48,7 +48,12 @@
<c:set var="value" value="${editProperties[properties.name]}"
scope="request" />
</c:otherwise>
</c:choose>
-<div class="field">
+<c:forEach var="event"
items="${sling:getRelativeResource(resource,'./events').valueMap}">
+ <c:if test="${!fn:contains(event.key,':')}">
+ <c:set var="events" value="${events},${event.key}" />
+ </c:if>
+</c:forEach>
+<div class="field" data-events="${events}" data-path="${resource.path}">
<c:if test="${not empty properties.label}">
<label class="label" for="${properties.name}">
<fmt:message key="${properties.label}" var="label" />
diff --git
a/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/path/path.jsp
b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/path/path.jsp
index f74ac86..9786855 100644
---
a/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/path/path.jsp
+++
b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/fields/path/path.jsp
@@ -55,9 +55,14 @@
<c:if test="${properties.required}"><span
class="has-text-danger">*</span></c:if>
</label>
</c:if>
+<c:forEach var="event"
items="${sling:getRelativeResource(resource,'./events').valueMap}">
+ <c:if test="${!fn:contains(event.key,':')}">
+ <c:set var="events" value="${events},${event.key}" />
+ </c:if>
+</c:forEach>
<c:choose>
<c:when test="${properties.hidesearch != true}">
- <div class="field has-addons">
+ <div class="field has-addons" data-events="${events}"
data-path="${resource.path}">
<div class="control is-expanded">
<input class="input pathfield" type="text"
id="${properties.name}" name="${properties.name}" value="${value}" ${required}
${disabled} data-type="${properties.type}" data-base="${properties.basePath}"
autocomplete="off" />
</div>
@@ -72,7 +77,7 @@
</div>
</c:when>
<c:otherwise>
- <div class="field">
+ <div class="field" data-events="${events}"
data-path="${resource.path}">
<div class="control">
<input class="input pathfield" type="text"
name="${properties.name}" value="${value}" ${required} ${disabled}
data-type="${properties.type}" data-base="${properties.basePath}"
autocomplete="off" />
</div>
diff --git
a/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/scripts/transformations.jsp
b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/scripts/transformations.jsp
index c36aa39..7f9524e 100644
---
a/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/scripts/transformations.jsp
+++
b/ui/src/main/resources/jcr_root/libs/sling-cms/components/editor/scripts/transformations.jsp
@@ -17,9 +17,9 @@
* under the License.
*/ --%>
<%@include file="/libs/sling-cms/global.jsp"%>
-<sling:adaptTo adaptable="${slingRequest.requestPathInfo.suffixResource}"
adaptTo="org.apache.sling.thumbnails.TransformationManager"
var="transformationManager" />
+<sling:adaptTo adaptable="${slingRequest.requestPathInfo.suffixResource}"
adaptTo="org.apache.sling.thumbnails.RenderedResource" var="rendered" />
<option value="">None</option>
-<c:forEach var="transformation"
items="${transformationManager.transformations}">
+<c:forEach var="transformation" items="${rendered.availableTransformations}">
<option
${slingRequest.requestPathInfo.suffixResource.valueMap.transformation ==
transformation.name ? 'selected' : ''}
value="${sling:encode(transformation.path,'HTML_ATTR')}">
${sling:encode(transformation.name,'HTML')}
</option>