From ed15e4d16e227bf636f13d08b387a5f319f3be1c Mon Sep 17 00:00:00 2001 From: Jens Pelzetter Date: Sat, 19 Dec 2020 19:10:12 +0100 Subject: [PATCH] Integration of Themes with MVC Former-commit-id: 9bbab62c8acf3833fda245e36bfa59a19b75869d --- .../theming/manifest/ThemeManifest.java | 113 +++++++++-- .../theming/manifest/ThemeManifestUtil.java | 63 +----- .../org/libreccm/theming/mvc/ThemesMvc.java | 179 ++++++++++++++++++ 3 files changed, 281 insertions(+), 74 deletions(-) create mode 100644 ccm-core/src/main/java/org/libreccm/theming/mvc/ThemesMvc.java diff --git a/ccm-core/src/main/java/org/libreccm/theming/manifest/ThemeManifest.java b/ccm-core/src/main/java/org/libreccm/theming/manifest/ThemeManifest.java index 76133ed7e..e70d61884 100644 --- a/ccm-core/src/main/java/org/libreccm/theming/manifest/ThemeManifest.java +++ b/ccm-core/src/main/java/org/libreccm/theming/manifest/ThemeManifest.java @@ -34,6 +34,9 @@ import javax.xml.bind.annotation.XmlRootElement; import static org.libreccm.theming.ThemeConstants.*; import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; /** * Each theme contains a Manifest (either in XML or JSON format) which provides @@ -88,8 +91,13 @@ public class ThemeManifest implements Serializable { @XmlElement(name = "default-template", namespace = THEMES_XML_NS) private String defaultTemplate; + @XmlElementWrapper(name = "mvcTemplates", namespace = THEMES_XML_NS) + @XmlElement(name = "template", namespace = THEME_MANIFEST_XML) + private Map> mvcTemplates; + public ThemeManifest() { templates = new ArrayList<>(); + mvcTemplates = new HashMap<>(); } public String getName() { @@ -156,6 +164,65 @@ public class ThemeManifest implements Serializable { this.defaultTemplate = defaultTemplate; } + public Map> getMvcTemplates() { + return Collections.unmodifiableMap(mvcTemplates); + } + + public Optional> getMvcTemplatesOfCategory( + final String category + ) { + return Optional.ofNullable(mvcTemplates.get(category)); + } + + public void addMvcTemplatesCategory(final String category) { + mvcTemplates.put(category, new HashMap<>()); + } + + public void addMvcTemplatesCategory( + final String category, final Map templates + ) { + mvcTemplates.put(category, templates); + } + + public Optional getMvcTemplate( + final String category, final String objectType + ) { + final Optional> templatesInCat + = getMvcTemplatesOfCategory(category); + + if (templatesInCat.isPresent()) { + return Optional.ofNullable(templatesInCat.get().get(objectType)); + } else { + return Optional.empty(); + } + } + + public void addMvcTemplate( + final String category, + final String objectType, + final ThemeTemplate template + ) { + if (!mvcTemplates.containsKey(category)) { + addMvcTemplatesCategory(category); + } + + mvcTemplates + .get(category) + .put( + objectType, + Objects.requireNonNull( + template, + "Template can't be null." + ) + ); + } + + protected void setMvcTemplates( + final Map> mvcTemplates + ) { + this.mvcTemplates = mvcTemplates; + } + @Override public int hashCode() { int hash = 7; @@ -166,6 +233,7 @@ public class ThemeManifest implements Serializable { hash = 83 * hash + Objects.hashCode(description); hash = 83 * hash + Objects.hashCode(templates); hash = 83 * hash + Objects.hashCode(defaultTemplate); + hash = 83 * hash + Objects.hashCode(mvcTemplates); return hash; } @@ -202,7 +270,10 @@ public class ThemeManifest implements Serializable { if (!Objects.equals(templates, other.getTemplates())) { return false; } - return Objects.equals(defaultTemplate, other.getDefaultTemplate()); + if (!Objects.equals(defaultTemplate, other.getDefaultTemplate())) { + return false; + } + return mvcTemplates.equals(other.getMvcTemplates()); } public boolean canEqual(final Object obj) { @@ -216,24 +287,28 @@ public class ThemeManifest implements Serializable { public String toString(final String data) { - return String.format("%s{ " - + "name = \"%s\", " - + "type = \"%s\", " - + "masterTheme = \"%s\", " - + "title = \"%s\", " - + "description = \"%s\", " - + "templates = %s, " - + "defaultTemplate%s" - + " }", - super.toString(), - name, - type, - masterTheme, - Objects.toString(title), - Objects.toString(description), - Objects.toString(templates), - defaultTemplate, - data); + return String.format( + "%s{ " + + "name = \"%s\", " + + "type = \"%s\", " + + "masterTheme = \"%s\", " + + "title = \"%s\", " + + "description = \"%s\", " + + "templates = %s, " + + "defaultTemplate, " + + "mvcTemplates = %s%s" + + " }", + super.toString(), + name, + type, + masterTheme, + Objects.toString(title), + Objects.toString(description), + Objects.toString(templates), + defaultTemplate, + Objects.toString(mvcTemplates), + data + ); } diff --git a/ccm-core/src/main/java/org/libreccm/theming/manifest/ThemeManifestUtil.java b/ccm-core/src/main/java/org/libreccm/theming/manifest/ThemeManifestUtil.java index 9a3ef5c2a..aada91252 100644 --- a/ccm-core/src/main/java/org/libreccm/theming/manifest/ThemeManifestUtil.java +++ b/ccm-core/src/main/java/org/libreccm/theming/manifest/ThemeManifestUtil.java @@ -18,7 +18,8 @@ */ package org.libreccm.theming.manifest; -import static org.libreccm.theming.ThemeConstants.*; +import static org.libreccm.theming.ThemeConstants.THEME_MANIFEST_JSON; +import static org.libreccm.theming.ThemeConstants.THEME_MANIFEST_XML; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; @@ -60,8 +61,6 @@ public class ThemeManifestUtil implements Serializable { * @return The parsed manifest file. */ public ThemeManifest loadManifest(final Path path) { - -// final String pathStr = path.toString().toLowerCase(Locale.ROOT); final BufferedReader reader; try { reader = Files.newBufferedReader(path, Charset.forName("UTF-8")); @@ -70,34 +69,11 @@ public class ThemeManifestUtil implements Serializable { } return parseManifest(reader, path.toString()); - -// final ObjectMapper mapper; -// if (pathStr.endsWith(THEME_MANIFEST_JSON)) { -// mapper = new ObjectMapper(); -// } else if (pathStr.endsWith(THEME_MANIFEST_XML)) { -// final JacksonXmlModule xmlModule = new JacksonXmlModule(); -// mapper = new XmlMapper(xmlModule); -// } else { -// throw new IllegalArgumentException(String -// .format("The provided path \"%s\" does not point to a theme " -// + "manifest file.", -// path.toString())); -// } -// -// mapper.registerModule(new JaxbAnnotationModule()); -// -// final ThemeManifest manifest; -// try { -// manifest = mapper.readValue(reader, ThemeManifest.class); -// } catch (IOException ex) { -// throw new UnexpectedErrorException(ex); -// } -// return manifest; } - public ThemeManifest loadManifest(final InputStream inputStream, - final String fileName) { - + public ThemeManifest loadManifest( + final InputStream inputStream, final String fileName + ) { final InputStreamReader reader; try { reader = new InputStreamReader(inputStream, "UTF-8"); @@ -106,34 +82,11 @@ public class ThemeManifestUtil implements Serializable { } return parseManifest(reader, fileName); - -// final ObjectMapper mapper; -// if (fileName.endsWith(THEME_MANIFEST_JSON)) { -// mapper = new ObjectMapper(); -// } else if (fileName.endsWith(THEME_MANIFEST_XML)) { -// final JacksonXmlModule xmlModule = new JacksonXmlModule(); -// mapper = new XmlMapper(xmlModule); -// } else { -// throw new IllegalArgumentException(String -// .format("The provided path \"%s\" does not point to a theme " -// + "manifest file.", -// fileName)); -// } -// -// mapper.registerModule(new JaxbAnnotationModule()); -// -// final ThemeManifest manifest; -// try { -// manifest = mapper.readValue(reader, ThemeManifest.class); -// } catch (IOException ex) { -// throw new UnexpectedErrorException(ex); -// } -// return manifest; } - public String serializeManifest(final ThemeManifest manifest, - final String format) { - + public String serializeManifest( + final ThemeManifest manifest, final String format + ) { final ObjectMapper mapper; switch (format) { diff --git a/ccm-core/src/main/java/org/libreccm/theming/mvc/ThemesMvc.java b/ccm-core/src/main/java/org/libreccm/theming/mvc/ThemesMvc.java new file mode 100644 index 000000000..862da79bd --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/theming/mvc/ThemesMvc.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2020 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.theming.mvc; + +import org.libreccm.sites.Site; +import org.libreccm.sites.SiteRepository; +import org.libreccm.theming.ThemeInfo; +import org.libreccm.theming.ThemeVersion; +import org.libreccm.theming.Themes; +import org.libreccm.theming.manifest.ThemeManifest; +import org.libreccm.theming.manifest.ThemeTemplate; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +public class ThemesMvc { + + @Inject + private SiteRepository siteRepo; + + @Inject + private Themes themes; + + public String getMvcTemplate( + final UriInfo uriInfo, + final String application + ) { + final Site site = getSite(uriInfo); + final String theme = parseThemeParam(uriInfo); + final ThemeVersion themeVersion = parsePreviewParam(uriInfo); + final ThemeInfo themeInfo = getTheme( + site, + theme, + themeVersion + ); + final ThemeManifest manifest = themeInfo.getManifest(); + final Map applicationTemplates = manifest + .getMvcTemplatesOfCategory("applications") + .orElseThrow( + () -> new WebApplicationException( + String.format( + "Manifest of theme %s has no application templates.", + themeInfo.getName() + ), + Response.Status.INTERNAL_SERVER_ERROR + ) + ); + final ThemeTemplate themeTemplate; + if (applicationTemplates.containsKey(application)) { + themeTemplate = applicationTemplates.get(application); + } else { + themeTemplate = Optional.ofNullable( + applicationTemplates.get("@default") + ).orElseThrow( + () -> new WebApplicationException( + String.format( + "Theme %s does not provide a template for application " + + "%s and has not default template for " + + "applications.", + theme, + application + ) + ) + ); + } + + return String.format( + "@themes/%s/%s/%s", + theme, + Objects.toString(themeVersion), + themeTemplate.getPath() + ); + } + + private Site getSite(final UriInfo uriInfo) { + Objects.requireNonNull(uriInfo); + + final String domain = uriInfo.getBaseUri().getHost(); + + final Site site; + if (siteRepo.hasSiteForDomain(domain)) { + site = siteRepo.findByDomain(domain).get(); + } else { + site = siteRepo + .findDefaultSite() + .orElseThrow(() -> new NotFoundException( + "No matching Site and no default Site.")); + } + + return site; + } + + private ThemeInfo getTheme( + final Site site, + final String theme, + final ThemeVersion themeVersion) { + if ("--DEFAULT--".equals(theme)) { + return themes + .getTheme(site.getDefaultTheme(), themeVersion) + .orElseThrow( + () -> new WebApplicationException( + String.format( + "The configured default theme \"%s\" for " + + "site \"%s\" is not available.", + site.getDefaultTheme(), + site.getDomainOfSite() + ), + Response.Status.INTERNAL_SERVER_ERROR + ) + ); + } else { + return themes + .getTheme(theme, themeVersion) + .orElseThrow( + () -> new WebApplicationException( + String.format( + "The theme \"%s\" is not available.", + theme + ), + Response.Status.BAD_REQUEST + ) + ); + } + } + + private String parseThemeParam(final UriInfo uriInfo) { + if (uriInfo.getQueryParameters().containsKey("theme")) { + return uriInfo.getQueryParameters().getFirst("theme"); + } else { + return "--DEFAULT--"; + } + } + + private ThemeVersion parsePreviewParam(final UriInfo uriInfo) { + if (uriInfo.getQueryParameters().containsKey("preview")) { + final List values = uriInfo + .getQueryParameters() + .get("preview"); + if (values.contains("theme") || values.contains("all")) { + return ThemeVersion.DRAFT; + } else { + return ThemeVersion.LIVE; + } + } else { + return ThemeVersion.LIVE; + } + } + +}