diff --git a/cms-api/pom.xml b/cms-api/pom.xml index 4f38e26c..68490723 100644 --- a/cms-api/pom.xml +++ b/cms-api/pom.xml @@ -4,7 +4,7 @@ com.condation.cms cms-parent - 6.2.1 + 6.3.0 cms-api jar diff --git a/cms-api/src/main/java/com/condation/cms/api/ThemeProperties.java b/cms-api/src/main/java/com/condation/cms/api/ThemeProperties.java index 52f9bdc8..6d78fc06 100644 --- a/cms-api/src/main/java/com/condation/cms/api/ThemeProperties.java +++ b/cms-api/src/main/java/com/condation/cms/api/ThemeProperties.java @@ -45,6 +45,10 @@ public ThemeProperties(final Map properties) { public Double version() { return (Double)properties.get("version"); } + + public String parent() { + return (String)properties.get("parent"); + } public String templateEngine() { return (String) getSubMap("template").get("engine"); diff --git a/cms-api/src/main/java/com/condation/cms/api/theme/Theme.java b/cms-api/src/main/java/com/condation/cms/api/theme/Theme.java index 7c7d0f80..0971fb67 100644 --- a/cms-api/src/main/java/com/condation/cms/api/theme/Theme.java +++ b/cms-api/src/main/java/com/condation/cms/api/theme/Theme.java @@ -46,6 +46,12 @@ public interface Theme { ThemeProperties properties(); + Theme getParentTheme (); + + Path resolveExtension (String path); + Path resolveAsset (String path); + Path resolveTemplate (String path); + /** * empty theme is used for sites without configured theme * @return diff --git a/cms-auth/pom.xml b/cms-auth/pom.xml index 89ba4617..eb4ce8eb 100644 --- a/cms-auth/pom.xml +++ b/cms-auth/pom.xml @@ -4,7 +4,7 @@ com.condation.cms cms-parent - 6.2.1 + 6.3.0 cms-auth jar diff --git a/cms-content/pom.xml b/cms-content/pom.xml index 90a7ec98..8fb8ef82 100644 --- a/cms-content/pom.xml +++ b/cms-content/pom.xml @@ -4,7 +4,7 @@ com.condation.cms cms-parent - 6.2.1 + 6.3.0 cms-content jar 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 e9fa9a14..32cdc22e 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 @@ -160,8 +160,9 @@ public String render(final ReadOnlyFile contentFile, final RequestContext contex model.values.put("mediaService", context.get(SiteMediaServiceFeature.class).mediaService()); model.values.put("taxonomies", context.get(InjectorFeature.class).injector().getInstance(TaxonomyFunction.class)); - model.values.put("messages", context.get(InjectorFeature.class).injector().getInstance(MessageSource.class)); - + //model.values.put("messages", context.get(InjectorFeature.class).injector().getInstance(MessageSource.class)); + model.values.put("messages", context.get(RenderContext.class).theme().getMessages()); + model.values.put("hooks", context.get(HookSystemFeature.class).hookSystem()); model.values.put("links", new LinkFunction(context)); diff --git a/cms-core/pom.xml b/cms-core/pom.xml index e553a311..213c3cc7 100644 --- a/cms-core/pom.xml +++ b/cms-core/pom.xml @@ -4,7 +4,7 @@ com.condation.cms cms-parent - 6.2.1 + 6.3.0 cms-core jar diff --git a/cms-core/src/main/java/com/condation/cms/core/messages/DefaultMessageSource.java b/cms-core/src/main/java/com/condation/cms/core/messages/DefaultMessageSource.java index 95a2972b..85858ba3 100644 --- a/cms-core/src/main/java/com/condation/cms/core/messages/DefaultMessageSource.java +++ b/cms-core/src/main/java/com/condation/cms/core/messages/DefaultMessageSource.java @@ -62,7 +62,7 @@ public String getLabel(final String bundle, final String label) { public String getLabel(final String bundle, final String label, final List data) { try { var resourceBundle = fromClassLoader(bundle, siteProperties.locale()); - if (resourceBundle != null) { + if (resourceBundle != null && resourceBundle.containsKey(label)) { var messageFormat = new MessageFormat(resourceBundle.getString(label), siteProperties.locale()); return messageFormat.format(data.toArray()); } diff --git a/cms-core/src/main/java/com/condation/cms/core/messages/ThemeMessageSource.java b/cms-core/src/main/java/com/condation/cms/core/messages/ThemeMessageSource.java index 6c713624..59a52f9c 100644 --- a/cms-core/src/main/java/com/condation/cms/core/messages/ThemeMessageSource.java +++ b/cms-core/src/main/java/com/condation/cms/core/messages/ThemeMessageSource.java @@ -48,7 +48,7 @@ public ThemeMessageSource(SiteProperties siteProperties, Path messageFolder, Mes public String getLabel (final String bundle, final String label) { var message = priorizedMessageSource.getLabel(bundle, label); - if (!("[" + label + "]").equals(label)) { + if (!("[" + label + "]").equals(message)) { return message; } @@ -59,16 +59,16 @@ public String getLabel (final String bundle, final String label) { @Override public String getLabel (final String bundle, final String label, final List data) { - var message = priorizedMessageSource.getLabel(bundle, bundle, data); - if (!("[" + label + "]").equals(label)) { + var message = priorizedMessageSource.getLabel(bundle, label, data); + if (!("[" + label + "]").equals(message)) { return message; } try { var resourceBundle = fromClassLoader(bundle, siteProperties.locale()); - if (resourceBundle != null) { + if (resourceBundle != null && resourceBundle.containsKey(label)) { var messageFormat = new MessageFormat(resourceBundle.getString(label), siteProperties.locale()); - return messageFormat.format(data); + return messageFormat.format(data.toArray()); } } catch (Exception e) { log.error("bundle not found", bundle); diff --git a/cms-server/src/main/java/com/condation/cms/theme/DefaultTheme.java b/cms-core/src/main/java/com/condation/cms/core/theme/DefaultTheme.java similarity index 60% rename from cms-server/src/main/java/com/condation/cms/theme/DefaultTheme.java rename to cms-core/src/main/java/com/condation/cms/core/theme/DefaultTheme.java index 23aea90f..4686f435 100644 --- a/cms-server/src/main/java/com/condation/cms/theme/DefaultTheme.java +++ b/cms-core/src/main/java/com/condation/cms/core/theme/DefaultTheme.java @@ -1,4 +1,4 @@ -package com.condation.cms.theme; +package com.condation.cms.core.theme; /*- * #%L @@ -22,6 +22,7 @@ * #L% */ import com.condation.cms.api.Constants; +import com.condation.cms.api.ServerProperties; import com.condation.cms.api.SiteProperties; import com.condation.cms.api.ThemeProperties; import com.condation.cms.core.messages.EmptyMessageSource; @@ -52,13 +53,28 @@ public class DefaultTheme implements Theme { private final ThemeProperties properties; private final MessageSource messages; private boolean empty = false; + + private Theme parent; private DefaultTheme(final Path themePath, final ThemeProperties themeProperties, final boolean empty, final MessageSource messages) { this(themePath, themeProperties, messages); this.empty = empty; } - public static Theme load(Path themePath, SiteProperties siteProperties, MessageSource siteMessages) throws IOException { + public static Theme load( + Path themePath, + SiteProperties siteProperties, + MessageSource siteMessages, + ServerProperties serverProperties) throws IOException { + + return load(themePath, siteProperties, siteMessages, serverProperties, true); + } + + private static Theme load( + Path themePath, + SiteProperties siteProperties, + MessageSource siteMessages, + ServerProperties serverProperties, boolean withParent) throws IOException { Yaml yaml = new Yaml(); Path themeYaml = themePath.resolve("theme.yaml"); @@ -66,8 +82,21 @@ public static Theme load(Path themePath, SiteProperties siteProperties, MessageS var content = Files.readString(themeYaml, StandardCharsets.UTF_8); Map config = (Map) yaml.load(content); + + final ThemeProperties themeProperties = new ThemeProperties(config); + final DefaultTheme defaultTheme = new DefaultTheme(themePath, themeProperties, messages); + if (withParent && themeProperties.parent() != null) { + var parentTheme = DefaultTheme.load( + serverProperties.getThemesFolder().resolve(themeProperties.parent()), + siteProperties, + messages, + serverProperties, + false + ); + defaultTheme.parent = parentTheme; + } - return new DefaultTheme(themePath, new ThemeProperties(config), messages); + return defaultTheme; } @Override @@ -112,6 +141,53 @@ public Path extensionsPath() { @Override public MessageSource getMessages() { + if (parent != null) { + return parent.getMessages(); + } return messages; } + + @Override + public Theme getParentTheme() { + return parent; + } + + + public Path resolve(String path, Path override, Path parent) { + var resolved = override.resolve(path); + if (resolved != null) { + return resolved; + } + if (parent == null) { + return null; + } + return parent.resolve(path); + } + + @Override + public Path resolveExtension(String path) { + return resolve( + path, + extensionsPath(), + getParentTheme() != null ? getParentTheme().extensionsPath() : null + ); + } + + @Override + public Path resolveAsset(String path) { + return resolve( + path, + assetsPath(), + getParentTheme() != null ? getParentTheme().assetsPath() : null + ); + } + + @Override + public Path resolveTemplate(String path) { + return resolve( + path, + templatesPath(), + getParentTheme() != null ? getParentTheme().templatesPath() : null + ); + } } diff --git a/cms-core/src/test/java/com/condation/cms/core/messages/ThemeMessageSourceTest.java b/cms-core/src/test/java/com/condation/cms/core/messages/ThemeMessageSourceTest.java new file mode 100644 index 00000000..e5cffe3b --- /dev/null +++ b/cms-core/src/test/java/com/condation/cms/core/messages/ThemeMessageSourceTest.java @@ -0,0 +1,74 @@ +package com.condation.cms.core.messages; + +/*- + * #%L + * cms-core + * %% + * Copyright (C) 2023 - 2024 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.SiteProperties; +import java.nio.file.Path; +import java.util.Map; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +/** + * + * @author t.marx + */ +public class ThemeMessageSourceTest { + + private static DefaultMessageSource messageSource; + private static ThemeMessageSource themeMessageSource; + + @BeforeAll + public static void setup() { + final SiteProperties siteProperties = new SiteProperties(Map.of("language", "de")); + messageSource = new DefaultMessageSource( + siteProperties, + Path.of("src/test/resources/messages") + ); + + themeMessageSource = new ThemeMessageSource( + siteProperties, + Path.of("src/test/resources/parent_messages"), + messageSource + ); + } + + @Test + public void from_child() { + var label = themeMessageSource.getLabel("abundle", "message.child"); + Assertions.assertThat(label).isEqualTo("hello child"); + } + + @Test + public void from_parent() { + var label = themeMessageSource.getLabel("abundle", "message.parent"); + Assertions.assertThat(label).isEqualTo("hello parent"); + } + + @Test + public void override_parent() { + var label = themeMessageSource.getLabel("abundle", "parent"); + Assertions.assertThat(label).isEqualTo("I override the parent"); + } +} diff --git a/cms-core/src/test/resources/messages/abundle.properties b/cms-core/src/test/resources/messages/abundle.properties index 764e2f10..9f5430b8 100644 --- a/cms-core/src/test/resources/messages/abundle.properties +++ b/cms-core/src/test/resources/messages/abundle.properties @@ -20,3 +20,5 @@ ### button.submit=Absenden +message.child=hello child +parent=I override the parent \ No newline at end of file diff --git a/cms-core/src/test/resources/parent_messages/abundle.properties b/cms-core/src/test/resources/parent_messages/abundle.properties new file mode 100644 index 00000000..1049fb8c --- /dev/null +++ b/cms-core/src/test/resources/parent_messages/abundle.properties @@ -0,0 +1,23 @@ +# #%L +# cms-api +# %% +# Copyright (C) 2023 - 2024 Marx-Software +# %% +# 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% +### + +message.parent=hello parent +parent=I'm the parent \ No newline at end of file diff --git a/cms-extensions/pom.xml b/cms-extensions/pom.xml index 7b17a4fb..d16b5cb7 100644 --- a/cms-extensions/pom.xml +++ b/cms-extensions/pom.xml @@ -4,7 +4,7 @@ com.condation.cms cms-parent - 6.2.1 + 6.3.0 cms-extensions jar diff --git a/cms-extensions/src/main/java/com/condation/cms/extensions/ExtensionFileSystem.java b/cms-extensions/src/main/java/com/condation/cms/extensions/ExtensionFileSystem.java index 4580df4b..e6cd0e7d 100644 --- a/cms-extensions/src/main/java/com/condation/cms/extensions/ExtensionFileSystem.java +++ b/cms-extensions/src/main/java/com/condation/cms/extensions/ExtensionFileSystem.java @@ -55,17 +55,17 @@ public ExtensionFileSystem(final Path siteExtensionPath, final Theme theme) { @Override public Path parsePath(URI uri) { - throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + throw new UnsupportedOperationException("Not supported yet."); } @Override public Path parsePath(String path) { - if (path.startsWith("system/")) { + if (path.startsWith("system/") || path.startsWith("system\\")) { return Path.of(path); } var resolved = siteExtensionPath.resolve(path); if (!Files.exists(resolved) && !theme.empty() ) { - resolved = theme.extensionsPath().resolve(path); + resolved = theme.resolveExtension(path); } return resolved; } @@ -76,17 +76,17 @@ public void checkAccess(Path path, Set modes, LinkOption.. @Override public void createDirectory(Path dir, FileAttribute... attrs) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + throw new UnsupportedOperationException("Not supported yet."); } @Override public void delete(Path path) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + throw new UnsupportedOperationException("Not supported yet."); } @Override public SeekableByteChannel newByteChannel(Path path, Set options, FileAttribute... attrs) throws IOException { - if (path.startsWith("system/")) { + if (path.startsWith("system/") || path.startsWith("system\\")) { var localPath = path.toString().replaceAll("\\\\", "/"); InputStream resourceAsStream = ExtensionFileSystem.class.getResourceAsStream(localPath); @@ -101,7 +101,7 @@ public SeekableByteChannel newByteChannel(Path path, Set o @Override public DirectoryStream newDirectoryStream(Path dir, DirectoryStream.Filter filter) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + throw new UnsupportedOperationException("Not supported yet."); } @Override @@ -116,7 +116,7 @@ public Path toRealPath(Path path, LinkOption... linkOptions) throws IOException @Override public Map readAttributes(Path path, String attributes, LinkOption... options) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + throw new UnsupportedOperationException("Not supported yet."); } } diff --git a/cms-extensions/src/main/java/com/condation/cms/extensions/ExtensionManager.java b/cms-extensions/src/main/java/com/condation/cms/extensions/ExtensionManager.java index ad751664..29206e7f 100644 --- a/cms-extensions/src/main/java/com/condation/cms/extensions/ExtensionManager.java +++ b/cms-extensions/src/main/java/com/condation/cms/extensions/ExtensionManager.java @@ -21,7 +21,6 @@ * . * #L% */ - import com.condation.cms.api.ServerProperties; import com.condation.cms.api.db.DB; import com.condation.cms.api.request.RequestContext; @@ -78,27 +77,10 @@ private ClassLoader getClassLoader() throws IOException { return new URLClassLoader(urls.toArray(URL[]::new), ClassLoader.getSystemClassLoader()); } - protected void loadExtensions(final Path extPath, final List sources) throws IOException { - Files.list(extPath) - .filter(path -> !Files.isDirectory(path) && path.getFileName().toString().endsWith(".js")) - .forEach(extFile -> { - try { - log.debug("load extension {}", extFile.getFileName().toString()); - Source source = Source.newBuilder( - "js", - Files.readString(extFile, StandardCharsets.UTF_8), - extFile.getFileName().toString() + ".mjs") - .encoding(StandardCharsets.UTF_8) - .build(); - - sources.add(source); - } catch (IOException ex) { - log.error("", ex); - } - }); - } - protected void loadExtensions(final Path extPath, final Consumer loader) throws IOException { + if (!Files.exists(extPath)) { + return; + } Files.list(extPath) .filter(path -> !Files.isDirectory(path) && path.getFileName().toString().endsWith(".js")) .map(extFile -> { @@ -118,7 +100,7 @@ protected void loadExtensions(final Path extPath, final Consumer loader) }).filter(source -> source != null) .forEach(loader); } - + public RequestExtensions newContext(Theme theme, RequestContext requestContext) throws IOException { var context = Context.newBuilder() .allowAllAccess(true) @@ -136,24 +118,17 @@ public RequestExtensions newContext(Theme theme, RequestContext requestContext) final Value bindings = context.getBindings("js"); setUpBinding(bindings, requestExtensions, theme, requestContext); - var extPath = db.getFileSystem().resolve("extensions/"); - if (Files.exists(extPath)) { - log.debug("load extensions from site"); - loadExtensions(extPath, context::eval); - } - if (!theme.empty()) { - var themeExtPath = theme.extensionsPath(); - if (Files.exists(themeExtPath)) { - log.debug("load extensions from theme"); - loadExtensions(themeExtPath, context::eval); - } + List extPaths = getExtensionPaths(theme); + for (var extPath : extPaths) { + log.debug("load extensions from " + extPath); + loadExtensions(extPath, context::eval); } return requestExtensions; } - - private void setUpBinding (Value bindings, + + private void setUpBinding(Value bindings, RequestExtensions requestExtensions, Theme theme, RequestContext requestContext) { bindings.putMember("extensions", requestExtensions); bindings.putMember("fileSystem", db.getFileSystem()); @@ -164,4 +139,17 @@ private void setUpBinding (Value bindings, bindings.putMember("requestContext", requestContext); bindings.putMember("ENV", serverProperties.env()); } + + private List getExtensionPaths(Theme theme) { + var extPaths = new ArrayList(); + extPaths.add(db.getFileSystem().resolve("extensions/")); + + if (theme.getParentTheme() != null) { + extPaths.add(theme.getParentTheme().extensionsPath()); + } + if (!theme.empty() && theme.extensionsPath() != null) { + extPaths.add(theme.extensionsPath()); + } + return extPaths; + } } diff --git a/cms-filesystem/pom.xml b/cms-filesystem/pom.xml index 87b696c2..ed8187d3 100644 --- a/cms-filesystem/pom.xml +++ b/cms-filesystem/pom.xml @@ -4,7 +4,7 @@ com.condation.cms cms-parent - 6.2.1 + 6.3.0 cms-filesystem jar diff --git a/cms-git/pom.xml b/cms-git/pom.xml index d5f4acb1..62d06700 100644 --- a/cms-git/pom.xml +++ b/cms-git/pom.xml @@ -4,7 +4,7 @@ com.condation.cms cms-parent - 6.2.1 + 6.3.0 cms-git jar diff --git a/cms-media/pom.xml b/cms-media/pom.xml index 05f7d339..2cc3c79d 100644 --- a/cms-media/pom.xml +++ b/cms-media/pom.xml @@ -4,7 +4,7 @@ com.condation.cms cms-parent - 6.2.1 + 6.3.0 cms-media jar diff --git a/cms-media/src/main/java/com/condation/cms/media/MediaManager.java b/cms-media/src/main/java/com/condation/cms/media/MediaManager.java index 0ef33245..9e9007a5 100644 --- a/cms-media/src/main/java/com/condation/cms/media/MediaManager.java +++ b/cms-media/src/main/java/com/condation/cms/media/MediaManager.java @@ -35,6 +35,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import lombok.extern.slf4j.Slf4j; @@ -49,7 +50,7 @@ @Slf4j public abstract class MediaManager implements EventListener { - protected Path assetBase; + protected List assetBase; protected Path tempFolder; protected Theme theme; protected Configuration configuration; @@ -57,13 +58,24 @@ public abstract class MediaManager implements EventListener mediaFormats; protected Path tempDirectory; - + protected MediaManager (List assetPath, Path tempFolder, Theme theme, Configuration configuration) { + this.assetBase = assetPath; + this.tempFolder = tempFolder; + this.theme = theme; + this.configuration = configuration; + } public abstract void reloadTheme (Theme updateTheme); public Path resolve (String uri) { - return assetBase.resolve(uri); + for (Path assets : assetBase) { + var resolved = assets.resolve(uri); + if (Files.exists(resolved)) { + return resolved; + } + } + return null; } public boolean hasMediaFormat (String format) { @@ -85,9 +97,9 @@ private Path getTempDirectory() throws IOException { public Optional getScaledContent(final String mediaPath, final MediaFormat mediaFormat) throws IOException { - Path resolve = assetBase.resolve(mediaPath); + Path resolve = resolve(mediaPath); - if (Files.exists(resolve)) { + if (resolve != null) { Optional tempContent = getTempContent(mediaPath, mediaFormat); if (tempContent.isPresent()) { return tempContent; diff --git a/cms-media/src/main/java/com/condation/cms/media/SiteMediaManager.java b/cms-media/src/main/java/com/condation/cms/media/SiteMediaManager.java index ea8a18a1..766cdae9 100644 --- a/cms-media/src/main/java/com/condation/cms/media/SiteMediaManager.java +++ b/cms-media/src/main/java/com/condation/cms/media/SiteMediaManager.java @@ -26,6 +26,7 @@ import com.condation.cms.api.configuration.Configuration; import com.condation.cms.api.theme.Theme; import java.nio.file.Path; +import java.util.List; /** * @@ -33,10 +34,7 @@ */ public class SiteMediaManager extends MediaManager { public SiteMediaManager(Path assetBase, Path tempFolder, Theme theme, Configuration configuration) { - this.assetBase = assetBase; - this.tempFolder = tempFolder; - this.theme = theme; - this.configuration = configuration; + super(List.of(assetBase), tempFolder, theme, configuration); } @Override diff --git a/cms-media/src/main/java/com/condation/cms/media/ThemeMediaManager.java b/cms-media/src/main/java/com/condation/cms/media/ThemeMediaManager.java index 6851ab7b..8a4dcafc 100644 --- a/cms-media/src/main/java/com/condation/cms/media/ThemeMediaManager.java +++ b/cms-media/src/main/java/com/condation/cms/media/ThemeMediaManager.java @@ -26,6 +26,8 @@ import com.condation.cms.api.configuration.Configuration; import com.condation.cms.api.theme.Theme; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; /** * @@ -33,15 +35,21 @@ */ public class ThemeMediaManager extends MediaManager { public ThemeMediaManager(Path tempFolder, Theme theme, Configuration configuration) { - this.assetBase = theme.assetsPath(); - this.tempFolder = tempFolder; - this.theme = theme; - this.configuration = configuration; + super(getThemeAssetPath(theme), tempFolder, theme, configuration); + } + + private static List getThemeAssetPath (Theme theme) { + List assetBases = new ArrayList<>(); + assetBases.add(theme.assetsPath()); + if (theme.getParentTheme() != null) { + assetBases.add(theme.getParentTheme().assetsPath()); + } + return assetBases; } @Override public void reloadTheme (Theme updateTheme) { - this.assetBase = updateTheme.assetsPath(); + this.assetBase = getThemeAssetPath(updateTheme); this.theme = updateTheme; this.mediaFormats = null; } diff --git a/cms-media/src/test/java/com/condation/cms/media/TestTheme.java b/cms-media/src/test/java/com/condation/cms/media/TestTheme.java index fcf4118b..b91b4051 100644 --- a/cms-media/src/test/java/com/condation/cms/media/TestTheme.java +++ b/cms-media/src/test/java/com/condation/cms/media/TestTheme.java @@ -72,5 +72,25 @@ public boolean empty() { public MessageSource getMessages() { throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody } + + @Override + public Theme getParentTheme() { + throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + } + + @Override + public Path resolveExtension(String path) { + throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + } + + @Override + public Path resolveAsset(String path) { + throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + } + + @Override + public Path resolveTemplate(String path) { + throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + } } diff --git a/cms-sandbox/pom.xml b/cms-sandbox/pom.xml index 5356ec6a..4576e01b 100644 --- a/cms-sandbox/pom.xml +++ b/cms-sandbox/pom.xml @@ -4,7 +4,7 @@ com.condation.cms cms-parent - 6.2.1 + 6.3.0 cms-sandbox pom diff --git a/cms-server/hosts/features/content/shortcodes/index.md b/cms-server/hosts/features/content/shortcodes/index.md index 98d87709..545ac4da 100644 --- a/cms-server/hosts/features/content/shortcodes/index.md +++ b/cms-server/hosts/features/content/shortcodes/index.md @@ -19,4 +19,8 @@ Or call a shortcode provided by a module [[example /]] -\[\[example /\]\] \ No newline at end of file +\[\[example /\]\] + +## Parent theme + +[[parent_name /]] \ No newline at end of file diff --git a/cms-server/hosts/features/messages/labels.properties b/cms-server/hosts/features/messages/labels.properties index e7e409bc..79803d66 100644 --- a/cms-server/hosts/features/messages/labels.properties +++ b/cms-server/hosts/features/messages/labels.properties @@ -1,2 +1,3 @@ contact.form.title={0} Formular -contact.form.button.submit=Abschicken \ No newline at end of file +contact.form.button.submit=Abschicken +message.site=Site \ No newline at end of file diff --git a/cms-server/pom.xml b/cms-server/pom.xml index 6730ac6d..df6ba32a 100644 --- a/cms-server/pom.xml +++ b/cms-server/pom.xml @@ -4,7 +4,7 @@ com.condation.cms cms-parent - 6.2.1 + 6.3.0 cms-server jar diff --git a/cms-server/src/main/java/com/condation/cms/cli/tools/ThemesUtil.java b/cms-server/src/main/java/com/condation/cms/cli/tools/ThemesUtil.java index b92b8f24..630a2b0c 100644 --- a/cms-server/src/main/java/com/condation/cms/cli/tools/ThemesUtil.java +++ b/cms-server/src/main/java/com/condation/cms/cli/tools/ThemesUtil.java @@ -51,6 +51,14 @@ public static Set filterUnInstalled (Set themes) { } public static Set getRequiredThemes () { + var themes = getRequiredSiteThemes(); + + themes.addAll(getRequiredParentThemes()); + + return themes; + } + + public static Set getRequiredSiteThemes () { Set requiredThemes = new HashSet<>(); try { Files.list(Path.of("hosts/")) @@ -73,6 +81,33 @@ public static Set getRequiredThemes () { return requiredThemes; } + private static Set getRequiredParentThemes () { + Set requiredThemes = new HashSet<>(); + try { + Files.list(Path.of("themes/")) + .filter(ThemesUtil::isTheme) + .map(host -> host.resolve("theme.yaml")) + .forEach(themeConfig -> { + try { + var themeProperties = PropertiesLoader.themeProperties(themeConfig); + if (!Strings.isNullOrEmpty(themeProperties.parent())) { + requiredThemes.add(themeProperties.parent()); + } + } catch (IOException ex) { + log.error("", ex); + } + }); + } catch (IOException ex) { + log.error("", ex); + } + + return requiredThemes; + } + + public static boolean isTheme(Path host) { + return Files.exists(host.resolve("theme.yaml")); + } + public static boolean isHost(Path host) { return Files.exists(host.resolve("site.yaml")); } 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 d50f56ce..98414584 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 @@ -66,7 +66,7 @@ import com.condation.cms.request.RequestContextFactory; import com.condation.cms.content.template.functions.taxonomy.TaxonomyFunction; import com.condation.cms.core.scheduler.SiteCronJobScheduler; -import com.condation.cms.theme.DefaultTheme; +import com.condation.cms.core.theme.DefaultTheme; import com.condation.modules.api.ModuleManager; import com.google.inject.AbstractModule; import com.google.inject.Injector; @@ -121,7 +121,7 @@ public Theme loadTheme(Configuration configuration, MessageSource messageSource) if (siteProperties.theme() != null) { Path themeFolder = serverProperties.getThemesFolder().resolve(siteProperties.theme()); - return DefaultTheme.load(themeFolder, siteProperties, messageSource); + return DefaultTheme.load(themeFolder, siteProperties, messageSource, serverProperties); } return DefaultTheme.EMPTY; diff --git a/cms-server/src/main/java/com/condation/cms/server/configs/ThemeModule.java b/cms-server/src/main/java/com/condation/cms/server/configs/ThemeModule.java index 2757fa2c..5d12c9cf 100644 --- a/cms-server/src/main/java/com/condation/cms/server/configs/ThemeModule.java +++ b/cms-server/src/main/java/com/condation/cms/server/configs/ThemeModule.java @@ -21,8 +21,6 @@ * . * #L% */ - - import com.condation.cms.api.ServerProperties; import com.condation.cms.api.configuration.Configuration; import com.condation.cms.api.db.DB; @@ -40,6 +38,7 @@ import java.util.concurrent.TimeUnit; import lombok.RequiredArgsConstructor; import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.util.resource.ResourceFactory; /** * @@ -50,12 +49,12 @@ public class ThemeModule extends AbstractModule { @Provides @Singleton - public ThemeMediaManager themeMediaManager (Theme theme, Configuration configuration, DB db, EventBus eventBus) throws IOException { - var mediaManager = new ThemeMediaManager(db.getFileSystem().resolve("temp"), theme, configuration); + public ThemeMediaManager themeMediaManager(Theme theme, Configuration configuration, DB db, EventBus eventBus) throws IOException { + var mediaManager = new ThemeMediaManager(db.getFileSystem().resolve("temp"), theme, configuration); eventBus.register(SitePropertiesChanged.class, mediaManager); return mediaManager; } - + @Provides @Singleton @Named("theme") @@ -69,7 +68,16 @@ public JettyMediaHandler themeMediaHandler(ThemeMediaManager mediaManager) throw public ResourceHandler resourceHandler(Theme theme, ServerProperties serverProperties) { ResourceHandler assetsHandler = new ResourceHandler(); assetsHandler.setDirAllowed(false); - assetsHandler.setBaseResource(new FileFolderPathResource(theme.assetsPath())); + + if (theme.getParentTheme() != null) { + assetsHandler.setBaseResource(ResourceFactory.combine( + new FileFolderPathResource(theme.assetsPath()), + new FileFolderPathResource(theme.getParentTheme().assetsPath()) + )); + } else { + assetsHandler.setBaseResource(new FileFolderPathResource(theme.assetsPath())); + } + if (serverProperties.dev()) { assetsHandler.setCacheControl("no-cache"); } else { diff --git a/cms-server/src/main/java/com/condation/cms/server/handler/media/JettyMediaHandler.java b/cms-server/src/main/java/com/condation/cms/server/handler/media/JettyMediaHandler.java index ea75fb32..2b16e420 100644 --- a/cms-server/src/main/java/com/condation/cms/server/handler/media/JettyMediaHandler.java +++ b/cms-server/src/main/java/com/condation/cms/server/handler/media/JettyMediaHandler.java @@ -64,7 +64,7 @@ public boolean handle(Request request, Response response, Callback callback) thr if ("##original##".equalsIgnoreCase(formatValue)) { var mediaPath = getRelativeMediaPath(request); Path assetPath = mediaManager.resolve(mediaPath); - if (Files.exists(assetPath)) { + if (assetPath != null) { var bytes = Files.readAllBytes(assetPath); var mimetype = Files.probeContentType(assetPath); @@ -99,6 +99,7 @@ public boolean handle(Request request, Response response, Callback callback) thr callback.failed(e); } response.setStatus(404); + callback.succeeded(); return true; } diff --git a/cms-server/src/test/java/com/condation/cms/TestHelper.java b/cms-server/src/test/java/com/condation/cms/TestHelper.java index 43559bd5..12f3f72e 100644 --- a/cms-server/src/test/java/com/condation/cms/TestHelper.java +++ b/cms-server/src/test/java/com/condation/cms/TestHelper.java @@ -48,7 +48,7 @@ import com.condation.cms.extensions.hooks.TemplateHooks; import com.condation.cms.extensions.request.RequestExtensions; import com.condation.cms.media.FileMediaService; -import com.condation.cms.theme.DefaultTheme; +import com.condation.cms.core.theme.DefaultTheme; import com.google.inject.Injector; import java.util.Map; import org.mockito.Mockito; diff --git a/cms-server/themes/parent/assets/images/parent.jpg b/cms-server/themes/parent/assets/images/parent.jpg new file mode 100644 index 00000000..cd50479c Binary files /dev/null and b/cms-server/themes/parent/assets/images/parent.jpg differ diff --git a/cms-server/themes/parent/assets/parent.js b/cms-server/themes/parent/assets/parent.js new file mode 100644 index 00000000..52b08867 --- /dev/null +++ b/cms-server/themes/parent/assets/parent.js @@ -0,0 +1,3 @@ +/** + * parent theme js + */ \ No newline at end of file diff --git a/cms-server/themes/test/extensions/theme.extension.js b/cms-server/themes/parent/extensions/theme.extension.js similarity index 100% rename from cms-server/themes/test/extensions/theme.extension.js rename to cms-server/themes/parent/extensions/theme.extension.js diff --git a/cms-server/themes/parent/messages/labels.properties b/cms-server/themes/parent/messages/labels.properties new file mode 100644 index 00000000..343199f7 --- /dev/null +++ b/cms-server/themes/parent/messages/labels.properties @@ -0,0 +1,2 @@ +contact.form.button.submit=Submit +message.theme.parent=Parent theme \ No newline at end of file diff --git a/cms-server/themes/test/templates/blog-entry.html b/cms-server/themes/parent/templates/blog-entry.html similarity index 100% rename from cms-server/themes/test/templates/blog-entry.html rename to cms-server/themes/parent/templates/blog-entry.html diff --git a/cms-server/themes/test/templates/blog.html b/cms-server/themes/parent/templates/blog.html similarity index 100% rename from cms-server/themes/test/templates/blog.html rename to cms-server/themes/parent/templates/blog.html diff --git a/cms-server/themes/parent/templates/content.html b/cms-server/themes/parent/templates/content.html new file mode 100644 index 00000000..6c834165 --- /dev/null +++ b/cms-server/themes/parent/templates/content.html @@ -0,0 +1,32 @@ + + + + + + + + + + + + +
+
+ +
+ + + +
+ +
+ + + + + + + + \ No newline at end of file diff --git a/cms-server/themes/test/templates/content.part.html b/cms-server/themes/parent/templates/content.part.html similarity index 100% rename from cms-server/themes/test/templates/content.part.html rename to cms-server/themes/parent/templates/content.part.html diff --git a/cms-server/themes/test/templates/error.html b/cms-server/themes/parent/templates/error.html similarity index 100% rename from cms-server/themes/test/templates/error.html rename to cms-server/themes/parent/templates/error.html diff --git a/cms-server/themes/test/templates/libs/fragments.html b/cms-server/themes/parent/templates/libs/fragments.html similarity index 100% rename from cms-server/themes/test/templates/libs/fragments.html rename to cms-server/themes/parent/templates/libs/fragments.html diff --git a/cms-server/themes/test/templates/navigation.html b/cms-server/themes/parent/templates/navigation.html similarity index 100% rename from cms-server/themes/test/templates/navigation.html rename to cms-server/themes/parent/templates/navigation.html diff --git a/cms-server/themes/test/templates/search.html b/cms-server/themes/parent/templates/search.html similarity index 100% rename from cms-server/themes/test/templates/search.html rename to cms-server/themes/parent/templates/search.html diff --git a/cms-server/themes/test/templates/start.html b/cms-server/themes/parent/templates/start.html similarity index 100% rename from cms-server/themes/test/templates/start.html rename to cms-server/themes/parent/templates/start.html diff --git a/cms-server/themes/parent/theme.yaml b/cms-server/themes/parent/theme.yaml new file mode 100644 index 00000000..828a0194 --- /dev/null +++ b/cms-server/themes/parent/theme.yaml @@ -0,0 +1,26 @@ +name: test +template: + engine: thymeleaf +modules: + active: + - example-module + - thymeleaf-module + - search-module +media: + formats: + - name: small + width: 256 + height: 256 + format: webp + compression: true + - name: big + width: 512 + height: 512 + format: webp + compression: true + - name: cropped + width: 512 + height: 256 + format: webp + compression: true + cropped: true \ No newline at end of file diff --git a/cms-server/themes/test/extensions/parent.extension.js b/cms-server/themes/test/extensions/parent.extension.js new file mode 100644 index 00000000..0aafe92f --- /dev/null +++ b/cms-server/themes/test/extensions/parent.extension.js @@ -0,0 +1,10 @@ +import { $hooks } from 'system/hooks.mjs'; + + +$hooks.registerAction("system/content/shortcodes", (context) => { + context.arguments().get("shortCodes").put( + "parent_name", + (params) => `Hello, I'm your father.` + ) + return null; +}) \ No newline at end of file diff --git a/cms-server/themes/test/messages/labels.properties b/cms-server/themes/test/messages/labels.properties index 237b8b1f..d10f0f28 100644 --- a/cms-server/themes/test/messages/labels.properties +++ b/cms-server/themes/test/messages/labels.properties @@ -1 +1,2 @@ -contact.form.button.submit=Submit \ No newline at end of file +contact.form.button.submit=Submit +message.theme.child=Child theme \ No newline at end of file diff --git a/cms-server/themes/test/templates/content.html b/cms-server/themes/test/templates/content.html index 6c834165..2962af30 100644 --- a/cms-server/themes/test/templates/content.html +++ b/cms-server/themes/test/templates/content.html @@ -1,32 +1,43 @@ - - - - - - - - - -
-
- -
- - - + + + + + + + + + +
+
+

Overridden template from parent

+
+ +
+ +
+ + + +
+ +
+

Messages

+

message.theme.child

+

message.theme.parent

+

message.site

+
+
-
- - + - + - + \ No newline at end of file diff --git a/cms-server/themes/test/theme.yaml b/cms-server/themes/test/theme.yaml index 828a0194..f3a5f754 100644 --- a/cms-server/themes/test/theme.yaml +++ b/cms-server/themes/test/theme.yaml @@ -1,6 +1,7 @@ name: test template: engine: thymeleaf +parent: parent modules: active: - example-module diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index b291bd2c..1e363556 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -4,7 +4,7 @@ com.condation.cms cms-parent - 6.2.1 + 6.3.0 integration-tests jar diff --git a/modules-framework/api/pom.xml b/modules-framework/api/pom.xml index 705ef7e3..cc02afa5 100644 --- a/modules-framework/api/pom.xml +++ b/modules-framework/api/pom.xml @@ -4,7 +4,7 @@ com.condation.cms.module.framework module-framework - 6.2.1 + 6.3.0 modules-api jar diff --git a/modules-framework/manager/pom.xml b/modules-framework/manager/pom.xml index 7fc7a988..d9d742ca 100644 --- a/modules-framework/manager/pom.xml +++ b/modules-framework/manager/pom.xml @@ -4,7 +4,7 @@ com.condation.cms.module.framework module-framework - 6.2.1 + 6.3.0 modules-manager jar diff --git a/modules-framework/pom.xml b/modules-framework/pom.xml index eaee0dfc..41a5f369 100644 --- a/modules-framework/pom.xml +++ b/modules-framework/pom.xml @@ -4,7 +4,7 @@ com.condation.cms cms-parent - 6.2.1 + 6.3.0 com.condation.cms.module.framework module-framework diff --git a/modules/example-module/pom.xml b/modules/example-module/pom.xml index c0553f89..a3a66be5 100644 --- a/modules/example-module/pom.xml +++ b/modules/example-module/pom.xml @@ -4,7 +4,7 @@ com.condation.cms.modules cms-modules - 6.2.1 + 6.3.0 example-module jar diff --git a/modules/pom.xml b/modules/pom.xml index a72eaf52..b4de6341 100644 --- a/modules/pom.xml +++ b/modules/pom.xml @@ -4,7 +4,7 @@ com.condation.cms cms-parent - 6.2.1 + 6.3.0 com.condation.cms.modules cms-modules diff --git a/pom.xml b/pom.xml index 14ec6645..4d542b01 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.condation.cms cms-parent - 6.2.1 + 6.3.0 pom UTF-8