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 6d79e5f20..c46f253c5 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 @@ -29,17 +29,23 @@ */ public enum Hooks { + /* navigation */ NAVIGATION_PATH("system/navigation/%s/path"), NAVIGATION_LIST("system/navigation/%s/list"), + /* content */ CONTENT_TAGS("system/content/tags"), CONTENT_FILTER("system/content/filter"), - TEMPLATE_COMPONENT("system/template/component"), + /*query*/ DB_QUERY_OPERATIONS("system/db/query/operations"), + /* scheduler */ SCHEDULER_REGISTER("system/scheduler/register"), SCHEDULER_REMOVE("system/scheduler/remove"), + /* http */ HTTP_EXTENSION("system/server/http/extension"), HTTP_ROUTE("system/server/http/route"), API_ROUTE("system/server/api/route"), + /* Template */ + TEMPLATE_COMPONENT("system/template/component"), TEMPLATE_SUPPLIER("system/template/supplier"), TEMPLATE_FUNCTION("system/template/function") ; diff --git a/cms-content/src/main/java/com/condation/cms/content/ContentResolver.java b/cms-content/src/main/java/com/condation/cms/content/ContentResolver.java index 74ebcca6d..def781a33 100644 --- a/cms-content/src/main/java/com/condation/cms/content/ContentResolver.java +++ b/cms-content/src/main/java/com/condation/cms/content/ContentResolver.java @@ -90,34 +90,6 @@ public Optional getErrorContent (final RequestContext context) } private Optional getContent(final RequestContext context, boolean checkVisibility) { - /* - String path; - if (Strings.isNullOrEmpty(context.get(RequestFeature.class).uri())) { - path = ""; - } else if (context.get(RequestFeature.class).uri().startsWith("/")) { - // remove leading slash - path = context.get(RequestFeature.class).uri().substring(1); - } else { - path = context.get(RequestFeature.class).uri(); - } - - var contentBase = db.getReadOnlyFileSystem().contentBase(); - var contentPath = contentBase.resolve(path); - ReadOnlyFile contentFile = null; - if (contentPath.exists() && contentPath.isDirectory()) { - // use index.md - var tempFile = contentPath.resolve("index.md"); - if (tempFile.exists()) { - contentFile = tempFile; - } - } else { - var temp = contentBase.resolve(path + ".md"); - if (temp.exists()) { - contentFile = temp; - } - } - */ - var contentBase = db.getReadOnlyFileSystem().contentBase(); var path = ContentResolvingStrategy.uriToPath(context.get(RequestFeature.class).uri()); Optional contentFileOpt = ContentResolvingStrategy.resolve(context.get(RequestFeature.class).uri(), db); diff --git a/cms-filesystem/src/main/java/com/condation/cms/filesystem/metadata/AbstractMetaData.java b/cms-filesystem/src/main/java/com/condation/cms/filesystem/metadata/AbstractMetaData.java index 4f9b8ee04..f8c14bffd 100644 --- a/cms-filesystem/src/main/java/com/condation/cms/filesystem/metadata/AbstractMetaData.java +++ b/cms-filesystem/src/main/java/com/condation/cms/filesystem/metadata/AbstractMetaData.java @@ -25,6 +25,8 @@ import com.condation.cms.api.Constants; import com.condation.cms.api.db.ContentNode; +import com.condation.cms.api.feature.features.IsPreviewFeature; +import com.condation.cms.api.request.RequestContextScope; import com.condation.cms.filesystem.MetaData; import com.google.common.base.Strings; import java.util.Arrays; @@ -72,6 +74,13 @@ public ConcurrentMap getTree() { } public static boolean isVisible (ContentNode node) { + + if (RequestContextScope.REQUEST_CONTEXT.isBound() + && RequestContextScope.REQUEST_CONTEXT.get().has(IsPreviewFeature.class) + && RequestContextScope.REQUEST_CONTEXT.get().get(IsPreviewFeature.class).mode().equals(com.condation.cms.api.feature.features.IsPreviewFeature.Mode.MANAGER) + ) { + return true; + } if (node == null || node.isSection()) { return false; diff --git a/cms-server/src/main/java/com/condation/cms/server/JettyServer.java b/cms-server/src/main/java/com/condation/cms/server/JettyServer.java index 170807aea..f398797f0 100644 --- a/cms-server/src/main/java/com/condation/cms/server/JettyServer.java +++ b/cms-server/src/main/java/com/condation/cms/server/JettyServer.java @@ -111,6 +111,9 @@ public void startup() throws IOException { MdcScope.forSite(site.id()).run(() -> { try { var host = new VHost(site.id(), site.basePath(), ServerUtil.getPath(Constants.Folders.MODULES), globalInjector); + log.debug("warmup host {}", site.id()); + host.warmup(); + log.debug("init host {}", site.id()); host.init(); vhosts.add(host); globalInjector.getInstance(SiteService.class).add(new Site(host.getInjector())); diff --git a/cms-server/src/main/java/com/condation/cms/server/configs/SiteGlobalModule.java b/cms-server/src/main/java/com/condation/cms/server/configs/SiteGlobalModule.java index 8789f1cf4..354964418 100644 --- a/cms-server/src/main/java/com/condation/cms/server/configs/SiteGlobalModule.java +++ b/cms-server/src/main/java/com/condation/cms/server/configs/SiteGlobalModule.java @@ -130,6 +130,7 @@ public CacheProvider cacheProvider (ModuleManager moduleManager, SiteProperties @Provides @Singleton public SiteConfigInitializer siteConfigInitializer (Injector injector) { - return new SiteConfigInitializer(injector); + var configInitializer = new SiteConfigInitializer(injector); + return configInitializer; } } diff --git a/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java b/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java index 97e23d046..bb539e8dd 100644 --- a/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java +++ b/cms-server/src/main/java/com/condation/cms/server/configs/SiteModule.java @@ -143,8 +143,7 @@ public TagParser tagParser (Configuration configuration) { @Provides @Singleton public ConfigManagement configurationManagement(SiteCronJobScheduler scheduler, EventBus eventBus) throws IOException { - ConfigManagement cm = ConfigurationFactory.create(hostBase, eventBus, scheduler); - + ConfigManagement cm = ConfigurationFactory.create(hostBase, eventBus, scheduler); return cm; } /** diff --git a/cms-server/src/main/java/com/condation/cms/server/host/VHost.java b/cms-server/src/main/java/com/condation/cms/server/host/VHost.java index 8415a23e1..223b1ce25 100644 --- a/cms-server/src/main/java/com/condation/cms/server/host/VHost.java +++ b/cms-server/src/main/java/com/condation/cms/server/host/VHost.java @@ -83,8 +83,10 @@ import com.condation.cms.server.handler.media.JettyMediaHandler; import com.condation.cms.server.handler.module.JettyModuleHandler; import com.condation.modules.api.ModuleManager; +import com.google.inject.Binding; import com.google.inject.Injector; import com.google.inject.Key; +import com.google.inject.Provider; import com.google.inject.name.Names; import lombok.Getter; @@ -166,6 +168,12 @@ public void reload() { public List hostnames() { return injector.getInstance(SiteProperties.class).hostnames(); } + + public void warmup () { + injector.getAllBindings().values().stream() + .map(Binding::getProvider) + .forEach(Provider::get); // Trigger eager Load + } public void init() throws IOException { diff --git a/cms-templates/pom.xml b/cms-templates/pom.xml index 4c0f72f69..16fb8a597 100644 --- a/cms-templates/pom.xml +++ b/cms-templates/pom.xml @@ -32,7 +32,6 @@ com.condation.cms cms-core - test 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 3568d056b..7750bd116 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 @@ -24,6 +24,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.parser.ASTNode; import com.condation.cms.templates.renderer.Renderer; import com.condation.cms.templates.renderer.ScopeStack; @@ -72,7 +73,8 @@ public void evaluate (ScopeStack scopes, Writer writer, DynamicConfiguration dyn private ScopeStack createScope (Map context, DynamicConfiguration dynamicConfiguration) { var scope = new ScopeStack(context); - scope.setVariable("date", new JexlTemplateFunction(new DateFunction())); + scope.setVariable(DateFunction.NAME, new JexlTemplateFunction(new DateFunction())); + scope.setVariable(NodeFunction.NAME, new JexlTemplateFunction(new NodeFunction(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/DateFunction.java b/cms-templates/src/main/java/com/condation/cms/templates/functions/impl/DateFunction.java index cc983aa50..bb678cf4e 100644 --- a/cms-templates/src/main/java/com/condation/cms/templates/functions/impl/DateFunction.java +++ b/cms-templates/src/main/java/com/condation/cms/templates/functions/impl/DateFunction.java @@ -31,6 +31,8 @@ */ public class DateFunction implements TemplateFunction { + public static final String NAME = "date"; + @Override public Object invoke(Object... params) { return new Date(); @@ -38,7 +40,7 @@ public Object invoke(Object... params) { @Override public String name() { - return "date"; + return NAME; } } 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 new file mode 100644 index 000000000..cb46a7301 --- /dev/null +++ b/cms-templates/src/main/java/com/condation/cms/templates/functions/impl/NodeFunction.java @@ -0,0 +1,87 @@ +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.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 java.util.Map; +import java.util.Optional; +import lombok.RequiredArgsConstructor; + +/** + * + * @author thorstenmarx + */ +@RequiredArgsConstructor +public class NodeFunction implements TemplateFunction { + + public static final String NAME = "select_node"; + + private final RequestContext 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) + ); + } + } + + return null; + } + + @Override + public String name() { + return NAME; + } + +} diff --git a/cms-templates/src/main/java/com/condation/cms/templates/renderer/ScopeStack.java b/cms-templates/src/main/java/com/condation/cms/templates/renderer/ScopeStack.java index 708937cca..e98510fba 100644 --- a/cms-templates/src/main/java/com/condation/cms/templates/renderer/ScopeStack.java +++ b/cms-templates/src/main/java/com/condation/cms/templates/renderer/ScopeStack.java @@ -77,7 +77,7 @@ public void setVariable(String name, Object value) { public Optional getVariable(String name) { for (Map scope : scopes) { if (scope.containsKey(name)) { - return Optional.of(scope.get(name)); + return Optional.ofNullable(scope.get(name)); } } if (parent != null) { 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 8928645ad..ad10fedd4 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 @@ -26,6 +26,7 @@ import com.condation.cms.templates.exceptions.RenderException; import com.condation.cms.templates.loaders.StringTemplateLoader; import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** diff --git a/test-server/hosts/demo/content/.technical/globals.md b/test-server/hosts/demo/content/.technical/globals.md new file mode 100644 index 000000000..0fd1f81f9 --- /dev/null +++ b/test-server/hosts/demo/content/.technical/globals.md @@ -0,0 +1,4 @@ +--- +name: "Global Values" +version: "v2026.01" +--- \ No newline at end of file diff --git a/test-server/themes/demo/templates/error.html b/test-server/themes/demo/templates/error.html index add88ea6d..309ee8000 100644 --- a/test-server/themes/demo/templates/error.html +++ b/test-server/themes/demo/templates/error.html @@ -13,7 +13,9 @@
-
+
{{ node.content | raw}}
diff --git a/test-server/themes/demo/templates/start.html b/test-server/themes/demo/templates/start.html index 371959b5d..62925817b 100644 --- a/test-server/themes/demo/templates/start.html +++ b/test-server/themes/demo/templates/start.html @@ -71,6 +71,18 @@

template function test

+
+

Test Node references

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