From d3d08bf6b6d9e3c43f7fd31ddc8bc64c66e31077 Mon Sep 17 00:00:00 2001 From: thmarx Date: Mon, 9 Feb 2026 09:35:12 +0100 Subject: [PATCH 1/2] some cleanups --- .../ui-module/src/main/resources/manager/index.html | 5 ++--- .../ui-module/src/main/resources/manager/js/manager.js | 9 +++++---- modules/ui-module/src/main/ts/dist/js/manager.js | 9 +++++---- modules/ui-module/src/main/ts/src/js/manager.js | 10 ++++++---- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/modules/ui-module/src/main/resources/manager/index.html b/modules/ui-module/src/main/resources/manager/index.html index b698ad1c..07c0e275 100644 --- a/modules/ui-module/src/main/resources/manager/index.html +++ b/modules/ui-module/src/main/resources/manager/index.html @@ -95,7 +95,7 @@ baseUrl: '{{ managerBaseURL }}', contextPath: '{{ contextPath }}', siteId: '{{ siteId }}', - previewUrl: "{{ links.createUrl('/?preview=manager&preview-token22=' + previewToken) | raw }}" + previewUrl: "{{ links.createUrl('/?preview=manager') | raw }}" } @@ -211,8 +211,7 @@
- +
{ document.addEventListener("DOMContentLoaded", function () { //PreviewHistory.init("/"); //updateStateButton(); + activatePreviewOverlay(); const intervalId = window.setInterval(() => { var token = createCSRFToken({}); token.then((token) => { setCSRFToken(token.result); }); }, 5 * 60 * 1000); - const iframe = document.getElementById('contentPreview'); + const iframe = getPreviewFrame(); iframe.addEventListener("load", previewLoadedHandler); const urlParams = new URLSearchParams(window.location.search); const pageUrl = urlParams.get('page'); @@ -64,9 +65,8 @@ document.addEventListener("DOMContentLoaded", function () { initMessageHandlers(); }); const previewLoadedHandler = () => { - EventBus.emit("preview:loaded", {}); try { - const iframe = document.getElementById('contentPreview'); + const iframe = getPreviewFrame(); const currentUrl = iframe.contentWindow.location.href; const url = new URL(currentUrl); const preview_url = url.pathname + url.search; @@ -76,6 +76,7 @@ const previewLoadedHandler = () => { }; UIStateManager.setTabState("preview", preview_update); updateStateButton(); + EventBus.emit("preview:loaded", {}); } catch (e) { console.log(e); diff --git a/modules/ui-module/src/main/ts/dist/js/manager.js b/modules/ui-module/src/main/ts/dist/js/manager.js index 04b61c86..6686e151 100644 --- a/modules/ui-module/src/main/ts/dist/js/manager.js +++ b/modules/ui-module/src/main/ts/dist/js/manager.js @@ -20,7 +20,7 @@ * #L% */ import frameMessenger from '@cms/modules/frameMessenger.js'; -import { loadPreview } from '@cms/modules/preview.utils.js'; +import { activatePreviewOverlay, getPreviewFrame, loadPreview } from '@cms/modules/preview.utils.js'; import { UIStateManager } from '@cms/modules/ui-state.js'; import { updateStateButton } from '@cms/modules/manager-ui.js'; import { EventBus } from '@cms/modules/event-bus.js'; @@ -33,13 +33,14 @@ frameMessenger.on('load', (payload) => { document.addEventListener("DOMContentLoaded", function () { //PreviewHistory.init("/"); //updateStateButton(); + activatePreviewOverlay(); const intervalId = window.setInterval(() => { var token = createCSRFToken({}); token.then((token) => { setCSRFToken(token.result); }); }, 5 * 60 * 1000); - const iframe = document.getElementById('contentPreview'); + const iframe = getPreviewFrame(); iframe.addEventListener("load", previewLoadedHandler); const urlParams = new URLSearchParams(window.location.search); const pageUrl = urlParams.get('page'); @@ -64,9 +65,8 @@ document.addEventListener("DOMContentLoaded", function () { initMessageHandlers(); }); const previewLoadedHandler = () => { - EventBus.emit("preview:loaded", {}); try { - const iframe = document.getElementById('contentPreview'); + const iframe = getPreviewFrame(); const currentUrl = iframe.contentWindow.location.href; const url = new URL(currentUrl); const preview_url = url.pathname + url.search; @@ -76,6 +76,7 @@ const previewLoadedHandler = () => { }; UIStateManager.setTabState("preview", preview_update); updateStateButton(); + EventBus.emit("preview:loaded", {}); } catch (e) { console.log(e); diff --git a/modules/ui-module/src/main/ts/src/js/manager.js b/modules/ui-module/src/main/ts/src/js/manager.js index 8d44d353..12145e1d 100644 --- a/modules/ui-module/src/main/ts/src/js/manager.js +++ b/modules/ui-module/src/main/ts/src/js/manager.js @@ -21,7 +21,7 @@ */ import frameMessenger from '@cms/modules/frameMessenger.js'; -import { loadPreview } from '@cms/modules/preview.utils.js'; +import { activatePreviewOverlay, getPreviewFrame, loadPreview } from '@cms/modules/preview.utils.js'; import { UIStateManager } from '@cms/modules/ui-state.js'; @@ -41,6 +41,8 @@ document.addEventListener("DOMContentLoaded", function () { //PreviewHistory.init("/"); //updateStateButton(); + activatePreviewOverlay(); + const intervalId = window.setInterval(() => { var token = createCSRFToken({}); token.then((token) => { @@ -48,7 +50,7 @@ document.addEventListener("DOMContentLoaded", function () { }) }, 5 * 60 * 1000); - const iframe = document.getElementById('contentPreview'); + const iframe = getPreviewFrame(); iframe.addEventListener("load", previewLoadedHandler) const urlParams = new URLSearchParams(window.location.search); @@ -76,9 +78,8 @@ document.addEventListener("DOMContentLoaded", function () { }); const previewLoadedHandler = () => { - EventBus.emit("preview:loaded", {}); try { - const iframe = document.getElementById('contentPreview'); + const iframe = getPreviewFrame(); const currentUrl = iframe.contentWindow.location.href; const url = new URL(currentUrl); @@ -92,6 +93,7 @@ const previewLoadedHandler = () => { UIStateManager.setTabState("preview", preview_update) updateStateButton(); + EventBus.emit("preview:loaded", {}); } catch (e) { console.log(e) } From 4160ea3714189ea5aee1b5f3b3353f6408a14563 Mon Sep 17 00:00:00 2001 From: thmarx Date: Mon, 9 Feb 2026 10:50:36 +0100 Subject: [PATCH 2/2] new template functions to select_node or only select_node_meta --- .../cms/content/DefaultContentRenderer.java | 1 - .../cms/templates/CMSTemplateEngine.java | 2 +- .../cms/templates/DefaultTemplate.java | 2 + .../functions/impl/AbstractNodeFunction.java | 83 +++++++++++++++++++ .../functions/impl/NodeFunction.java | 60 +++++--------- .../functions/impl/NodeMetaFunction.java | 55 ++++++++++++ .../TemplateEngineExceptionTest.java | 1 + test-server/hosts/demo/content/about.md | 2 +- .../themes/demo/templates/default.html | 33 ++++++-- test-server/themes/demo/templates/start.html | 4 +- 10 files changed, 191 insertions(+), 52 deletions(-) create mode 100644 cms-templates/src/main/java/com/condation/cms/templates/functions/impl/AbstractNodeFunction.java create mode 100644 cms-templates/src/main/java/com/condation/cms/templates/functions/impl/NodeMetaFunction.java diff --git a/cms-content/src/main/java/com/condation/cms/content/DefaultContentRenderer.java b/cms-content/src/main/java/com/condation/cms/content/DefaultContentRenderer.java index 08b69028..c1474576 100644 --- a/cms-content/src/main/java/com/condation/cms/content/DefaultContentRenderer.java +++ b/cms-content/src/main/java/com/condation/cms/content/DefaultContentRenderer.java @@ -49,7 +49,6 @@ import com.condation.cms.content.pipeline.ContentPipelineFactory; import com.condation.cms.content.views.model.View; import com.condation.cms.api.content.MapAccess; -import com.condation.cms.api.utils.HTTPUtil; import com.condation.cms.extensions.hooks.DBHooks; import com.condation.cms.extensions.hooks.TemplateHooks; import com.condation.cms.content.template.functions.LinkFunction; diff --git a/cms-templates/src/main/java/com/condation/cms/templates/CMSTemplateEngine.java b/cms-templates/src/main/java/com/condation/cms/templates/CMSTemplateEngine.java index e3b5240c..a6cc91ef 100644 --- a/cms-templates/src/main/java/com/condation/cms/templates/CMSTemplateEngine.java +++ b/cms-templates/src/main/java/com/condation/cms/templates/CMSTemplateEngine.java @@ -61,7 +61,7 @@ private JexlEngine createJexlEngine() { builder .cache(512) .safe(false) - .strict(true) + .strict(false) .silent(false) .debug(false); } else { diff --git a/cms-templates/src/main/java/com/condation/cms/templates/DefaultTemplate.java b/cms-templates/src/main/java/com/condation/cms/templates/DefaultTemplate.java index 7750bd11..2686b5cd 100644 --- a/cms-templates/src/main/java/com/condation/cms/templates/DefaultTemplate.java +++ b/cms-templates/src/main/java/com/condation/cms/templates/DefaultTemplate.java @@ -25,6 +25,7 @@ import com.condation.cms.templates.functions.JexlTemplateFunction; import com.condation.cms.templates.functions.impl.DateFunction; import com.condation.cms.templates.functions.impl.NodeFunction; +import com.condation.cms.templates.functions.impl.NodeMetaFunction; import com.condation.cms.templates.parser.ASTNode; import com.condation.cms.templates.renderer.Renderer; import com.condation.cms.templates.renderer.ScopeStack; @@ -75,6 +76,7 @@ private ScopeStack createScope (Map context, DynamicConfiguratio var scope = new ScopeStack(context); scope.setVariable(DateFunction.NAME, new JexlTemplateFunction(new DateFunction())); scope.setVariable(NodeFunction.NAME, new JexlTemplateFunction(new NodeFunction(dynamicConfiguration.requestContext()))); + scope.setVariable(NodeMetaFunction.NAME, new JexlTemplateFunction(new NodeMetaFunction(dynamicConfiguration.requestContext()))); dynamicConfiguration.templateFunctions().forEach(tf -> { scope.setVariable(tf.name(), new JexlTemplateFunction(tf)); diff --git a/cms-templates/src/main/java/com/condation/cms/templates/functions/impl/AbstractNodeFunction.java b/cms-templates/src/main/java/com/condation/cms/templates/functions/impl/AbstractNodeFunction.java new file mode 100644 index 00000000..393763f4 --- /dev/null +++ b/cms-templates/src/main/java/com/condation/cms/templates/functions/impl/AbstractNodeFunction.java @@ -0,0 +1,83 @@ +package com.condation.cms.templates.functions.impl; + +/*- + * #%L + * cms-templates + * %% + * Copyright (C) 2023 - 2026 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.condation.cms.api.content.MapAccess; +import com.condation.cms.api.db.ContentNode; +import com.condation.cms.api.db.DB; +import com.condation.cms.api.db.cms.ReadOnlyFile; +import com.condation.cms.api.feature.features.InjectorFeature; +import com.condation.cms.api.request.RequestContext; +import com.condation.cms.api.utils.PathUtil; +import com.condation.cms.core.content.ContentResolvingStrategy; +import com.condation.cms.templates.functions.TemplateFunction; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import lombok.RequiredArgsConstructor; + +/** + * + * @author thmar + */ +@RequiredArgsConstructor +public abstract class AbstractNodeFunction implements TemplateFunction { + protected final RequestContext requestContext; + + protected void extendMap (Map node, ReadOnlyFile contentFile) { + + } + + @Override + public Object invoke(Object... params) { + + if (params == null || params.length == 0 ) { + return null; + } + if (!(params[0] instanceof String)) { + return null; + } + String uri = ContentResolvingStrategy.uriToPath((String)params[0]); + + var db = requestContext.get(InjectorFeature.class).injector().getInstance(DB.class); + var contentBase = db.getReadOnlyFileSystem().contentBase(); + + Optional contentFileOpt = ContentResolvingStrategy.resolve(uri, db); + + if (contentFileOpt.isPresent()) { + var node_uri = PathUtil.toRelativeFile(contentFileOpt.get(), contentBase); + final Optional nodeByUri = db.getContent().byUri(node_uri); + if (nodeByUri.isPresent()) { + var node = new HashMap(); + node.put("meta", new MapAccess(nodeByUri.get().data())); + node.put("uri", PathUtil.toURL(contentFileOpt.get(), contentBase)); + + extendMap(node, contentFileOpt.get()); + + return node; + } + } + + return null; + } +} diff --git a/cms-templates/src/main/java/com/condation/cms/templates/functions/impl/NodeFunction.java b/cms-templates/src/main/java/com/condation/cms/templates/functions/impl/NodeFunction.java index cb46a730..3ebcf240 100644 --- a/cms-templates/src/main/java/com/condation/cms/templates/functions/impl/NodeFunction.java +++ b/cms-templates/src/main/java/com/condation/cms/templates/functions/impl/NodeFunction.java @@ -22,61 +22,43 @@ * #L% */ -import com.condation.cms.api.content.MapAccess; import com.condation.cms.api.db.ContentNode; import com.condation.cms.api.db.DB; import com.condation.cms.api.db.cms.ReadOnlyFile; -import com.condation.cms.api.feature.features.DBFeature; import com.condation.cms.api.feature.features.InjectorFeature; -import com.condation.cms.api.feature.features.RequestFeature; import com.condation.cms.api.request.RequestContext; -import com.condation.cms.api.utils.PathUtil; -import com.condation.cms.core.content.ContentResolvingStrategy; -import com.condation.cms.templates.functions.TemplateFunction; -import java.util.Date; +import com.condation.cms.content.ContentRenderer; +import com.condation.cms.content.Section; +import java.io.IOException; +import java.util.List; import java.util.Map; -import java.util.Optional; -import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; /** * * @author thorstenmarx */ -@RequiredArgsConstructor -public class NodeFunction implements TemplateFunction { +@Slf4j +public class NodeFunction extends AbstractNodeFunction { public static final String NAME = "select_node"; - - private final RequestContext requestContext; + + public NodeFunction(RequestContext requestContext) { + super(requestContext); + } @Override - public Object invoke(Object... params) { - - if (params == null || params.length == 0 ) { - return null; - } - if (!(params[0] instanceof String)) { - return null; - } - String uri = ContentResolvingStrategy.uriToPath((String)params[0]); - - var db = requestContext.get(InjectorFeature.class).injector().getInstance(DB.class); - var contentBase = db.getReadOnlyFileSystem().contentBase(); - - Optional contentFileOpt = ContentResolvingStrategy.resolve(uri, db); - - if (contentFileOpt.isPresent()) { - var node_uri = PathUtil.toRelativeFile(contentFileOpt.get(), contentBase); - final Optional nodeByUri = db.getContent().byUri(node_uri); - if (nodeByUri.isPresent()) { - return Map.of( - "meta", new MapAccess(nodeByUri.get().data()), - "uri", PathUtil.toURL(contentFileOpt.get(), contentBase) - ); - } + protected void extendMap(Map node, ReadOnlyFile contentFile) { + try { + var db = requestContext.get(InjectorFeature.class).injector().getInstance(DB.class); + var contentRenderer = requestContext.get(InjectorFeature.class).injector().getInstance(ContentRenderer.class); + List sections = db.getContent().listSections(contentFile); + + Map> renderedSections = contentRenderer.renderSections(sections, requestContext); + node.put("sections", renderedSections); + } catch (IOException iOException) { + log.error("error loading sections", iOException); } - - return null; } @Override diff --git a/cms-templates/src/main/java/com/condation/cms/templates/functions/impl/NodeMetaFunction.java b/cms-templates/src/main/java/com/condation/cms/templates/functions/impl/NodeMetaFunction.java new file mode 100644 index 00000000..53a00627 --- /dev/null +++ b/cms-templates/src/main/java/com/condation/cms/templates/functions/impl/NodeMetaFunction.java @@ -0,0 +1,55 @@ +package com.condation.cms.templates.functions.impl; + +/*- + * #%L + * cms-templates + * %% + * Copyright (C) 2023 - 2026 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.condation.cms.api.db.ContentNode; +import com.condation.cms.api.db.DB; +import com.condation.cms.api.db.cms.ReadOnlyFile; +import com.condation.cms.api.feature.features.InjectorFeature; +import com.condation.cms.api.request.RequestContext; +import com.condation.cms.content.ContentRenderer; +import com.condation.cms.content.Section; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import lombok.extern.slf4j.Slf4j; + +/** + * + * @author thorstenmarx + */ +@Slf4j +public class NodeMetaFunction extends AbstractNodeFunction { + + public static final String NAME = "select_node_meta"; + + public NodeMetaFunction(RequestContext requestContext) { + super(requestContext); + } + + @Override + public String name() { + return NAME; + } + +} diff --git a/cms-templates/src/test/java/com/condation/cms/templates/TemplateEngineExceptionTest.java b/cms-templates/src/test/java/com/condation/cms/templates/TemplateEngineExceptionTest.java index ad10fedd..cb0d49f1 100644 --- a/cms-templates/src/test/java/com/condation/cms/templates/TemplateEngineExceptionTest.java +++ b/cms-templates/src/test/java/com/condation/cms/templates/TemplateEngineExceptionTest.java @@ -51,6 +51,7 @@ public void test_invalid_template() { } @Test + @Disabled("because disabling the strict mode") public void test_unknown_variable() { Assertions.assertThatCode( () -> { diff --git a/test-server/hosts/demo/content/about.md b/test-server/hosts/demo/content/about.md index 42d5a92c..5d9d08f9 100644 --- a/test-server/hosts/demo/content/about.md +++ b/test-server/hosts/demo/content/about.md @@ -1,6 +1,6 @@ --- title: 'New of: start.html' -template: start.html +template: default.html parent: text: testsdf count: 46 diff --git a/test-server/themes/demo/templates/default.html b/test-server/themes/demo/templates/default.html index 9e36dc66..e4d72134 100644 --- a/test-server/themes/demo/templates/default.html +++ b/test-server/themes/demo/templates/default.html @@ -36,15 +36,32 @@

-
- {% if sections.containsKey('asection') %} - {% for section in sections.get('asection') %} - {{ section.content() | raw }} - {% endfor %} - {% endif %} + {% if !empty(sections) %} +
+ {% if sections.containsKey('asection') %} + {% for section in sections.get('asection') %} + {{ section.content() | raw }} + {% endfor %} + {% endif %} +
+ {% endif %} + +
+

Test Node references

+
+ {% assign globals = select_node("") %} + {% if globals != null %} +

load secions from start page

+ {% if globals.sections.containsKey('asection') %} + {% for section in globals.sections.get('asection') %} + {{ section.content() | raw }} + {% endfor %} + {% endif %} + {% endif %} +
diff --git a/test-server/themes/demo/templates/start.html b/test-server/themes/demo/templates/start.html index 62925817..faf0cbf5 100644 --- a/test-server/themes/demo/templates/start.html +++ b/test-server/themes/demo/templates/start.html @@ -74,8 +74,8 @@

template function test

Test Node references

- {% assign globals = select_node("/.technical/globals") %} - {% if !empty(globals) %} + {% assign globals = select_node_meta("/.technical/globals") %} + {% if globals != null %} Version: {{ globals.meta.get('version') }}
{% else %} Globals node not found!