From e79557c2a8f8210401b0cbada7d5d819311c8a5a Mon Sep 17 00:00:00 2001
From: Thorsten Marx
Date: Tue, 26 May 2026 15:26:07 +0200
Subject: [PATCH 1/4] add html pipeline for html lots
---
.../condation/cms/api/hooks/HookSystem.java | 2 -
.../com/condation/cms/api/hooks/Hooks.java | 2 +
.../cms/content/DefaultContentRenderer.java | 6 +-
.../cms/content/pipeline/ContentPipeline.java | 2 +-
.../cms/content/pipeline/HTMLPipeline.java | 68 ++++++++
.../content/pipeline/HTMLPipelineTest.java | 159 ++++++++++++++++++
.../themes/demo/extensions/theme.extension.js | 7 +
7 files changed, 242 insertions(+), 4 deletions(-)
create mode 100644 cms-content/src/main/java/com/condation/cms/content/pipeline/HTMLPipeline.java
create mode 100644 cms-content/src/test/java/com/condation/cms/content/pipeline/HTMLPipelineTest.java
diff --git a/cms-api/src/main/java/com/condation/cms/api/hooks/HookSystem.java b/cms-api/src/main/java/com/condation/cms/api/hooks/HookSystem.java
index 896486ab..1f05a49c 100644
--- a/cms-api/src/main/java/com/condation/cms/api/hooks/HookSystem.java
+++ b/cms-api/src/main/java/com/condation/cms/api/hooks/HookSystem.java
@@ -25,12 +25,10 @@
import com.condation.cms.api.utils.AnnotationsUtil;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
-import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
diff --git a/cms-api/src/main/java/com/condation/cms/api/hooks/Hooks.java b/cms-api/src/main/java/com/condation/cms/api/hooks/Hooks.java
index cffc774e..a1018578 100644
--- a/cms-api/src/main/java/com/condation/cms/api/hooks/Hooks.java
+++ b/cms-api/src/main/java/com/condation/cms/api/hooks/Hooks.java
@@ -34,6 +34,8 @@ public enum Hooks {
/* content */
CONTENT_TAGS("system/content/tags"),
CONTENT_FILTER("system/content/filter"),
+ CONTENT_SLOT_HEADER("system/content/slot/header"),
+ CONTENT_SLOT_FOOTER("system/content/slot/footer"),
/*query*/
DB_QUERY_OPERATIONS("system/db/query/operations"),
/* scheduler */
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 0583ee5e..7b3899fd 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,6 +49,7 @@
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.content.pipeline.HTMLPipeline;
import com.condation.cms.extensions.hooks.DBHooks;
import com.condation.cms.extensions.hooks.TemplateHooks;
import com.condation.cms.content.template.functions.LinkFunction;
@@ -218,7 +219,10 @@ public String render(final ReadOnlyFile contentFile, final RequestContext contex
model.values.putAll(namespace.getNamespaces());
- return templates.get().render((String) meta.get("template"), model);
+ var htmlContent = templates.get().render((String) meta.get("template"), model);
+
+ HTMLPipeline htmlPipeline = new HTMLPipeline(context.get(HookSystemFeature.class).hookSystem());
+ return htmlPipeline.process(htmlContent);
}
protected MarkdownFunction createMarkdownFunction(final RequestContext context) {
diff --git a/cms-content/src/main/java/com/condation/cms/content/pipeline/ContentPipeline.java b/cms-content/src/main/java/com/condation/cms/content/pipeline/ContentPipeline.java
index bf542bb1..17a9c627 100644
--- a/cms-content/src/main/java/com/condation/cms/content/pipeline/ContentPipeline.java
+++ b/cms-content/src/main/java/com/condation/cms/content/pipeline/ContentPipeline.java
@@ -63,7 +63,7 @@ protected void init() {
});
}
-
+
public String process(String rawContent) {
return hookSystem.filter(Hooks.CONTENT_FILTER.hook(), rawContent).value();
}
diff --git a/cms-content/src/main/java/com/condation/cms/content/pipeline/HTMLPipeline.java b/cms-content/src/main/java/com/condation/cms/content/pipeline/HTMLPipeline.java
new file mode 100644
index 00000000..e0fffed2
--- /dev/null
+++ b/cms-content/src/main/java/com/condation/cms/content/pipeline/HTMLPipeline.java
@@ -0,0 +1,68 @@
+package com.condation.cms.content.pipeline;
+
+/*-
+ * #%L
+ * CMS Content
+ * %%
+ * Copyright (C) 2023 - 2026 CondationCMS
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License
+ * along with this program. If not, see .
+ * #L%
+ */
+import com.condation.cms.api.hooks.HookSystem;
+import com.condation.cms.api.hooks.Hooks;
+import java.util.Objects;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ *
+ * @author thmar
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class HTMLPipeline {
+
+ private final HookSystem hookSystem;
+
+ public String process(String rawContent) {
+ rawContent = updateSlot(Hooks.CONTENT_SLOT_HEADER, "", rawContent);
+ return updateSlot(Hooks.CONTENT_SLOT_FOOTER, "
+ content
+ ", rawContent);
+ }
+
+ public String updateSlot (Hooks hook, String elementName, String rawContent) {
+
+ if (!rawContent.contains(elementName)) {
+ log.warn("No {} found, skipping header slot injection", elementName);
+ return rawContent;
+ }
+
+ var result = hookSystem.execute(hook.hook());
+
+ var hookValues = result.results().stream()
+ .filter(Objects::nonNull)
+ .filter(String.class::isInstance)
+ .map(String.class::cast)
+ .toList();
+ if (hookValues.isEmpty()) {
+ return rawContent;
+ }
+
+
+ var mergedValue = String.join("\n\n", hookValues);
+
+ return rawContent.replace(elementName, mergedValue + "\n" + elementName);
+ }
+
+}
diff --git a/cms-content/src/test/java/com/condation/cms/content/pipeline/HTMLPipelineTest.java b/cms-content/src/test/java/com/condation/cms/content/pipeline/HTMLPipelineTest.java
new file mode 100644
index 00000000..d23d0390
--- /dev/null
+++ b/cms-content/src/test/java/com/condation/cms/content/pipeline/HTMLPipelineTest.java
@@ -0,0 +1,159 @@
+package com.condation.cms.content.pipeline;
+
+/*-
+ * #%L
+ * CMS Content
+ * %%
+ * Copyright (C) 2023 - 2026 CondationCMS
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero 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 Affero General Public License
+ * along with this program. If not, see .
+ * #L%
+ */
+import com.condation.cms.api.hooks.HookSystem;
+import com.condation.cms.api.hooks.Hooks;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class HTMLPipelineTest {
+
+ private HookSystem hookSystem;
+ private HTMLPipeline pipeline;
+
+ @BeforeEach
+ void setUp() {
+ hookSystem = new HookSystem();
+ pipeline = new HTMLPipeline(hookSystem);
+ }
+
+ @Test
+ void shouldInjectHeaderSlot() {
+ // given
+ String html = """
+
+