diff --git a/ccm-core/src/main/java/org/libreccm/theming/StaticThemeProvider.java b/ccm-core/src/main/java/org/libreccm/theming/StaticThemeProvider.java
index 589833ccc..2932d8a3f 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/StaticThemeProvider.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/StaticThemeProvider.java
@@ -62,6 +62,7 @@ public class StaticThemeProvider implements ThemeProvider {
* Path the the static themes.
*/
private static final String THEMES_DIR = "/themes";
+ private static final long serialVersionUID = -8701021965452233811L;
@Inject
private ThemeFileInfoUtil themeFileInfoUtil;
diff --git a/ccm-core/src/main/java/org/libreccm/theming/ThemeConstants.java b/ccm-core/src/main/java/org/libreccm/theming/ThemeConstants.java
index 0d95b47bb..3ed285aca 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/ThemeConstants.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeConstants.java
@@ -24,6 +24,8 @@ package org.libreccm.theming;
*/
public final class ThemeConstants {
+ public static final String PAGE_PARAMETER_TEMPLATE = "template";
+
public final static String THEME_MANIFEST_JSON = "theme.json";
public final static String THEME_MANIFEST_XML = "theme.xml";
diff --git a/ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfo.java b/ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfo.java
index 252b736a4..3155347a1 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfo.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfo.java
@@ -18,6 +18,7 @@
*/
package org.libreccm.theming;
+import java.io.Serializable;
import java.util.Objects;
/**
@@ -25,28 +26,30 @@ import java.util.Objects;
*
* @author Jens Pelzetter
*/
-public class ThemeFileInfo {
+public class ThemeFileInfo implements Serializable {
+
+ private static final long serialVersionUID = 2880986115955856570L;
/**
* The name of the file.
*/
private String name;
-
+
/**
* Is the file a directory?
*/
private boolean directory;
-
+
/**
* The type of the file (for example {@code text/xml} or {@code image/jpeg}.
*/
private String mimeType;
-
+
/**
* The size of the file. For directories this will be {@code 0}.
*/
private long size;
-
+
/**
* Is the file writable?
*/
diff --git a/ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfoUtil.java b/ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfoUtil.java
index 55a1c606e..0f32cbc93 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfoUtil.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfoUtil.java
@@ -21,6 +21,7 @@ package org.libreccm.theming;
import org.libreccm.core.UnexpectedErrorException;
import java.io.IOException;
+import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
@@ -33,7 +34,9 @@ import javax.enterprise.context.RequestScoped;
* @author Jens Pelzetter
*/
@RequestScoped
-public class ThemeFileInfoUtil {
+public class ThemeFileInfoUtil implements Serializable {
+
+ private static final long serialVersionUID = -3382896567742774318L;
/**
* Build a {@link ThemeFileInfo} object for a file. Before calling this
diff --git a/ccm-core/src/main/java/org/libreccm/theming/ThemeInfo.java b/ccm-core/src/main/java/org/libreccm/theming/ThemeInfo.java
index 01b61df91..5528f0f84 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/ThemeInfo.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeInfo.java
@@ -20,6 +20,7 @@ package org.libreccm.theming;
import org.libreccm.theming.manifest.ThemeManifest;
+import java.io.Serializable;
import java.util.Objects;
/**
@@ -27,7 +28,9 @@ import java.util.Objects;
*
* @author Jens Pelzetter
*/
-public class ThemeInfo {
+public class ThemeInfo implements Serializable {
+
+ private static final long serialVersionUID = -518244930947022256L;
/**
* The manifest of the theme.
diff --git a/ccm-core/src/main/java/org/libreccm/theming/ThemeProcessor.java b/ccm-core/src/main/java/org/libreccm/theming/ThemeProcessor.java
index 40847044a..c404777e3 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/ThemeProcessor.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeProcessor.java
@@ -18,6 +18,7 @@
*/
package org.libreccm.theming;
+import java.io.Serializable;
import java.util.Map;
import javax.enterprise.context.RequestScoped;
@@ -32,16 +33,17 @@ import javax.enterprise.context.RequestScoped;
*
* @author Jens Pelzetter
*/
-public interface ThemeProcessor {
+public interface ThemeProcessor extends Serializable {
/**
* Process the provided {@link PageModel} {@code page} and convert into HTML
* using the theme {@code theme} provided by the
* {@link ThemeProvider} {@code themeProvider}.
*
- * @param page The page to convert the HTML.
- * @param theme The theme to use.
- * @param themeProvider The {@link ThemeProvider} which provides the the theme.
+ * @param page The page to convert the HTML.
+ * @param theme The theme to use.
+ * @param themeProvider The {@link ThemeProvider} which provides the the
+ * theme.
*
* @return The HTML for the provided {@code page}.
*/
diff --git a/ccm-core/src/main/java/org/libreccm/theming/ThemeProvider.java b/ccm-core/src/main/java/org/libreccm/theming/ThemeProvider.java
index ae0262191..60a0e492b 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/ThemeProvider.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeProvider.java
@@ -21,6 +21,7 @@ package org.libreccm.theming;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.io.Serializable;
import java.util.List;
import java.util.Optional;
@@ -30,7 +31,7 @@ import java.util.Optional;
*
* @author Jens Pelzetter
*/
-public interface ThemeProvider {
+public interface ThemeProvider extends Serializable {
/**
* Provides a list of all themes provided by this theme provider. The list
diff --git a/ccm-core/src/main/java/org/libreccm/theming/Themes.java b/ccm-core/src/main/java/org/libreccm/theming/Themes.java
index 48c366ed0..08e856f29 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/Themes.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/Themes.java
@@ -23,6 +23,7 @@ import org.apache.logging.log4j.Logger;
import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.pagemodel.PageModel;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -40,7 +41,9 @@ import javax.inject.Inject;
* @author Jens Pelzetter
*/
@RequestScoped
-public class Themes {
+public class Themes implements Serializable {
+
+ private static final long serialVersionUID = 6861457919635241221L;
private static final Logger LOGGER = LogManager.getLogger(Themes.class);
@@ -139,5 +142,4 @@ public class Themes {
return processor.process(page, theme, provider);
}
-
}
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 8a45d63bf..9cea077ef 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
@@ -33,23 +33,27 @@ import javax.xml.bind.annotation.XmlRootElement;
import static org.libreccm.theming.ThemeConstants.*;
+import java.io.Serializable;
+
/**
* Each theme contains a Manifest (either in XML or JSON format) which provides
* informations about the theme.
- *
+ *
* @author Jens Pelzetter
*/
@XmlRootElement(name = "theme", namespace = THEMES_XML_NS)
@XmlAccessorType(XmlAccessType.FIELD)
-public class ThemeManifest {
+public class ThemeManifest implements Serializable {
+
+ private static final long serialVersionUID = 699497658459398231L;
/**
- * The name of the theme. Usually the same as the name of directory which
+ * The name of the theme. Usually the same as the name of directory which
* contains the theme.
*/
@XmlElement(name = "name", namespace = THEMES_XML_NS)
private String name;
-
+
/**
* The type of the theme, for example XSLT.
*/
@@ -75,6 +79,12 @@ public class ThemeManifest {
@XmlElement(name = "template", namespace = THEMES_XML_NS)
private List templates;
+ /**
+ * Path of the default template.
+ */
+ @XmlElement(name = "default-template", namespace = THEMES_XML_NS)
+ private String defaultTemplate;
+
public ThemeManifest() {
templates = new ArrayList<>();
}
@@ -90,10 +100,11 @@ public class ThemeManifest {
public String getType() {
return type;
}
-
+
public void setType(final String type) {
- this.type =type;
+ this.type = type;
}
+
public LocalizedString getTitle() {
return title;
}
@@ -126,6 +137,14 @@ public class ThemeManifest {
templates.remove(template);
}
+ public String getDefaultTemplate() {
+ return defaultTemplate;
+ }
+
+ public void setDefaultTemplate(final String defaultTemplate) {
+ this.defaultTemplate = defaultTemplate;
+ }
+
@Override
public int hashCode() {
int hash = 7;
@@ -134,6 +153,7 @@ public class ThemeManifest {
hash = 83 * hash + Objects.hashCode(title);
hash = 83 * hash + Objects.hashCode(description);
hash = 83 * hash + Objects.hashCode(templates);
+ hash = 83 * hash + Objects.hashCode(defaultTemplate);
return hash;
}
@@ -164,7 +184,10 @@ public class ThemeManifest {
if (!Objects.equals(description, other.getDescription())) {
return false;
}
- return Objects.equals(templates, other.getTemplates());
+ if (!Objects.equals(templates, other.getTemplates())) {
+ return false;
+ }
+ return Objects.equals(defaultTemplate, other.getDefaultTemplate());
}
public boolean canEqual(final Object obj) {
@@ -182,13 +205,15 @@ public class ThemeManifest {
+ "name = \"%s\", "
+ "title = \"%s\", "
+ "description = \"%s\", "
- + "templates = %s%s"
+ + "templates = %s, "
+ + "defaultTemplate%s"
+ " }",
super.toString(),
name,
Objects.toString(title),
Objects.toString(description),
Objects.toString(templates),
+ defaultTemplate,
data);
}
diff --git a/ccm-core/src/main/java/org/libreccm/theming/xslt/XsltThemeProcessor.java b/ccm-core/src/main/java/org/libreccm/theming/xslt/XsltThemeProcessor.java
new file mode 100644
index 000000000..3a7f4d71d
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/theming/xslt/XsltThemeProcessor.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 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.xslt;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import org.libreccm.core.UnexpectedErrorException;
+import org.libreccm.theming.ThemeInfo;
+import org.libreccm.theming.ThemeProcessor;
+import org.libreccm.theming.ThemeProvider;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.enterprise.context.RequestScoped;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import static org.libreccm.theming.ThemeConstants.*;
+
+import org.libreccm.theming.manifest.ThemeTemplate;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Optional;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+/**
+ * A {@link ThemeProcessor} implementation for XSLT based themes.
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+public class XsltThemeProcessor implements ThemeProcessor {
+
+ private static final long serialVersionUID = -3883625727845105417L;
+
+ @Override
+ public String process(final Map page,
+ final ThemeInfo theme,
+ final ThemeProvider themeProvider) {
+
+ //Convert page to XML
+ final JacksonXmlModule xmlModule = new JacksonXmlModule();
+ final ObjectMapper mapper = new XmlMapper(xmlModule);
+
+ final String pageAsXml;
+ try {
+ pageAsXml = mapper.writeValueAsString(page);
+ } catch (JsonProcessingException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
+ final DocumentBuilderFactory documentBuilderFactory
+ = DocumentBuilderFactory.newInstance();
+ final DocumentBuilder documentBuilder;
+ try {
+ documentBuilder = documentBuilderFactory.newDocumentBuilder();
+ } catch (ParserConfigurationException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
+ final Document document;
+ try {
+ document = documentBuilder.parse(pageAsXml);
+ } catch (SAXException | IOException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
+ final String pathToTemplate;
+ if (page.containsKey(PAGE_PARAMETER_TEMPLATE)) {
+
+ final String templateName = (String) page
+ .get(PAGE_PARAMETER_TEMPLATE);
+
+ final Optional template = theme
+ .getManifest()
+ .getTemplates()
+ .stream()
+ .filter(current -> current.getName().equals(templateName))
+ .findAny();
+
+ if (template.isPresent()) {
+ pathToTemplate = template.get().getPath();
+ } else {
+ throw new UnexpectedErrorException(String
+ .format("Theme \"%s\" does provide template \"%s\".",
+ theme.getName(),
+ templateName));
+ }
+ } else {
+ pathToTemplate = theme.getManifest().getDefaultTemplate();
+ }
+
+ final InputStream xslFileInputStream = themeProvider
+ .getThemeFileAsStream(theme.getName(),
+ theme.getVersion(),
+ pathToTemplate)
+ .orElseThrow(() -> new UnexpectedErrorException(String
+ .format("Failed to open XSL file \"%s\" from theme \"%s\" for "
+ + "reading.",
+ pathToTemplate,
+ theme.getName())));
+
+ final Reader reader;
+ try {
+ reader = new InputStreamReader(xslFileInputStream, "UTF-8");
+ } catch (UnsupportedEncodingException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
+ final StreamSource xslFileStreamSource = new StreamSource(reader);
+ final TransformerFactory transformerFactory = TransformerFactory
+ .newInstance();
+ final Transformer transformer;
+ try {
+ transformer = transformerFactory.newTransformer(xslFileStreamSource);
+ } catch (TransformerConfigurationException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
+ final StringWriter resultWriter = new StringWriter();
+ final Result result = new StreamResult(resultWriter);
+ try {
+ transformer.transform(new DOMSource(document), result);
+ } catch (TransformerException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
+ return resultWriter.toString();
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 0e921540a..d253c5748 100644
--- a/pom.xml
+++ b/pom.xml
@@ -115,7 +115,7 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.6.1
+ 3.7.0
1.8
1.8
@@ -255,7 +255,7 @@
com.vaadin
vaadin-maven-plugin
- 8.1.3
+ 8.1.5
@@ -405,7 +405,7 @@
com.vaadin
vaadin-bom
- 8.1.3
+ 8.1.5
import
pom
@@ -442,7 +442,7 @@
org.apache.logging.log4j
log4j-bom
- 2.8.2
+ 2.9.1
pom
import
@@ -520,7 +520,7 @@
net.sf.saxon
Saxon-HE
- 9.8.0-3
+ 9.8.0-5