diff --git a/ccm-cms/src/main/java/org/librecms/ui/authoring/article/ArticlePropertiesStep.java b/ccm-cms/src/main/java/org/librecms/ui/authoring/article/ArticlePropertiesStep.java
new file mode 100644
index 000000000..7024cb8ed
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/ui/authoring/article/ArticlePropertiesStep.java
@@ -0,0 +1,64 @@
+/*
+ * 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.librecms.ui.authoring.article;
+
+import com.vaadin.ui.CustomComponent;
+import com.vaadin.ui.TextField;
+import org.librecms.contentsection.ContentItem;
+import org.librecms.contenttypes.Article;
+import org.librecms.ui.ContentSectionViewController;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public class ArticlePropertiesStep
+ extends CustomComponent
+ implements Serializable {
+
+ private static final long serialVersionUID = 1587965921855375545L;
+
+ private final ContentSectionViewController controller;
+ private final Article article;
+
+ public ArticlePropertiesStep(final ContentSectionViewController controller,
+ final ContentItem item) {
+
+ Objects.requireNonNull(controller);
+ Objects.requireNonNull(item);
+
+ if (!(item instanceof Article)) {
+ throw new IllegalArgumentException(String
+ .format("The provided ContentItem is not an instance "
+ + "of class \"%s\" but of class \"%s\".",
+ Article.class.getName(),
+ item.getClass().getName()));
+ }
+
+ this.controller = controller;
+ article = (Article) item;
+
+ final TextField titleField = new TextField("Title");
+
+ }
+
+}
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 2ba8616b4..589833ccc 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/StaticThemeProvider.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/StaticThemeProvider.java
@@ -18,9 +18,13 @@
*/
package org.libreccm.theming;
+import static org.libreccm.theming.ThemeConstants.*;
+
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.libreccm.core.UnexpectedErrorException;
+import org.libreccm.theming.manifest.ThemeManifest;
+import org.libreccm.theming.manifest.ThemeManifestUtil;
import java.io.IOException;
import java.io.InputStream;
@@ -36,28 +40,40 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
/**
* A theme provider implementation which serves themes from the class path
- * ({@code /themes)}.
+ * ({@code /themes)}. This implementation does not support changes to the
+ * theme(s) and files.
*
* @author Jens Pelzetter
*/
+@RequestScoped
public class StaticThemeProvider implements ThemeProvider {
private static final Logger LOGGER = LogManager
.getLogger(StaticThemeProvider.class);
+ /**
+ * Path the the static themes.
+ */
private static final String THEMES_DIR = "/themes";
- private static final String THEME_XML = "theme.xml";
- private static final String THEME_JSON = "theme.json";
+
+ @Inject
+ private ThemeFileInfoUtil themeFileInfoUtil;
+
+ @Inject
+ private ThemeManifestUtil themeManifests;
@Override
public List getThemes() {
LOGGER.debug("Retrieving info about all static themes...");
- final List themeInfos = new ArrayList<>();
try (final FileSystem jarFileSystem = FileSystems.newFileSystem(
getJarUri(), Collections.emptyMap())) {
@@ -85,31 +101,195 @@ public class StaticThemeProvider implements ThemeProvider {
}
@Override
- public Optional getThemeInfo(String theme, ThemeVersion version) {
- throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ public Optional getThemeInfo(final String theme,
+ final ThemeVersion version) {
+
+ Objects.requireNonNull(theme);
+ Objects.requireNonNull(version);
+
+ if (theme.matches("\\s*")) {
+ throw new IllegalArgumentException(
+ "The name of the theme can't be empty.");
+ }
+
+ LOGGER.debug("Trying to find static theme \"{}\"...",
+ theme);
+
+ try (final FileSystem jarFileSystem = FileSystems
+ .newFileSystem(getJarUri(), Collections.emptyMap())) {
+
+ final Path themePath = jarFileSystem
+ .getPath(String.format(THEMES_DIR + "/%s", theme));
+
+ if (isTheme(themePath)) {
+ return Optional.of(generateThemeInfo(themePath));
+ } else {
+ return Optional.empty();
+ }
+
+ } catch (IOException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
}
@Override
- public boolean providesTheme(String theme, ThemeVersion version) {
- throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ public boolean providesTheme(final String theme,
+ final ThemeVersion version) {
+
+ Objects.requireNonNull(theme);
+ Objects.requireNonNull(version);
+
+ if (theme.isEmpty() || theme.matches("\\s*")) {
+ throw new IllegalArgumentException(
+ "The name of the theme can't be empty.");
+ }
+
+ LOGGER.debug("Determining if there is static theme \"{}\"...",
+ theme);
+
+ try (final FileSystem jarFileSystem = FileSystems
+ .newFileSystem(getJarUri(), Collections.emptyMap())) {
+
+ final Path themePath = jarFileSystem
+ .getPath(String.format(THEMES_DIR + "/%s", theme));
+
+ LOGGER.debug("Is there a static theme \"{}\": {}",
+ theme,
+ isTheme(themePath));
+ return isTheme(themePath);
+
+ } catch (IOException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
}
@Override
- public List listThemeFiles(String theme, ThemeVersion version,
- String path) {
- throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ public List listThemeFiles(final String theme,
+ final ThemeVersion version,
+ final String path) {
+
+ Objects.requireNonNull(theme);
+ Objects.requireNonNull(version);
+ Objects.requireNonNull(path);
+
+ if (theme.isEmpty() || theme.matches("\\s*")) {
+ throw new IllegalArgumentException(
+ "The name of the theme can't be empty.");
+ }
+
+ final String pathToDir;
+ if ("".equals(path)) {
+ pathToDir = "/";
+ } else {
+ pathToDir = path;
+ }
+
+ LOGGER.debug("Listing all files in path \"{]\" of theme \"{}\"...",
+ path,
+ theme);
+
+ final List infos;
+ try (final FileSystem jarFileSystem = FileSystems
+ .newFileSystem(getJarUri(), Collections.emptyMap())) {
+
+ final Path themePath = jarFileSystem
+ .getPath(String.format(THEMES_DIR + "/%s", theme));
+
+ if (!isTheme(themePath)) {
+ throw new IllegalArgumentException(String
+ .format("Theme \"%s\" does not exist.",
+ theme));
+ }
+
+ final Path dirPath = themePath.resolve(pathToDir);
+ if (Files.exists(dirPath)) {
+
+ if (Files.isDirectory(dirPath)) {
+
+ try (final Stream stream = Files.list(dirPath)) {
+ infos = stream
+ .map(themeFileInfoUtil::buildThemeInfo)
+ .collect(Collectors.toList());
+ }
+ } else {
+ infos = new ArrayList<>();
+ infos.add(themeFileInfoUtil.buildThemeInfo(dirPath));
+ }
+ } else {
+ throw new IllegalArgumentException(String
+ .format("No file/directory \"%s\" in theme \"%s\".",
+ path,
+ theme));
+ }
+
+ } catch (IOException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
+ LOGGER.debug("Files in path \"{}\" of static theme \"{}\": {}",
+ pathToDir,
+ theme,
+ Objects.toString(infos));
+ return infos;
}
@Override
- public Optional getThemeFileAsStream(String theme,
- ThemeVersion version,
- String path) {
- throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ public Optional getThemeFileAsStream(final String theme,
+ final ThemeVersion version,
+ final String path) {
+ Objects.requireNonNull(theme);
+ Objects.requireNonNull(version);
+ Objects.requireNonNull(path);
+
+ if (theme.isEmpty() || theme.matches("\\s*")) {
+ throw new IllegalArgumentException(
+ "The name of the theme can't be empty.");
+ }
+
+ if (path.isEmpty() || path.matches("\\s*")) {
+ throw new IllegalArgumentException(
+ "The name of the theme can't be empty.");
+ }
+
+ try (final FileSystem jarFileSystem = FileSystems
+ .newFileSystem(getJarUri(), Collections.emptyMap())) {
+
+ final Path themePath = jarFileSystem
+ .getPath(String.format(THEMES_DIR + "/%s", theme));
+
+ final Path filePath;
+ if (path.charAt(0) == '/') {
+ filePath = themePath.resolve(path.substring(1));
+ } else {
+ filePath = themePath.resolve(path);
+ }
+
+ if (!Files.isRegularFile(filePath)) {
+ throw new IllegalArgumentException(String
+ .format("The provided path \"%s\" in theme \"%s\" points "
+ + "not to a regular file.",
+ path,
+ theme));
+ }
+
+ if (Files.exists(filePath)) {
+ return Optional.of(Files.newInputStream(filePath));
+ } else {
+ return Optional.empty();
+ }
+
+ } catch (IOException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
}
@Override
- public OutputStream getOutputStreamForThemeFile(String theme, String path) {
- throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ public OutputStream getOutputStreamForThemeFile(final String theme,
+ final String path) {
+
+ throw new UnsupportedOperationException("Not supported by this"
+ + " implemetentation");
}
@Override
@@ -152,8 +332,8 @@ public class StaticThemeProvider implements ThemeProvider {
return false;
}
- final Path manifestPathJson = path.resolve(THEME_JSON);
- final Path manifestPathXml = path.resolve(THEME_XML);
+ final Path manifestPathJson = path.resolve(THEME_MANIFEST_JSON);
+ final Path manifestPathXml = path.resolve(THEME_MANIFEST_XML);
return Files.exists(manifestPathJson) || Files.exists(manifestPathXml);
}
@@ -169,27 +349,27 @@ public class StaticThemeProvider implements ThemeProvider {
path.toString()));
}
- final Path manifestPathJson = path.resolve(THEME_JSON);
- final Path manifestPathXml = path.resolve(THEME_XML);
+ final Path manifestPathJson = path.resolve(THEME_MANIFEST_JSON);
+ final Path manifestPathXml = path.resolve(THEME_MANIFEST_XML);
+ final ThemeManifest manifest;
if (Files.exists(manifestPathJson)) {
- return generateThemeInfoFromJson(manifestPathJson);
+ manifest = themeManifests.loadManifest(manifestPathJson);
} else if (Files.exists(manifestPathXml)) {
- return generateThemeInfoFromXml(manifestPathXml);
+ manifest = themeManifests.loadManifest(manifestPathXml);
} else {
throw new IllegalArgumentException(String
.format("The provided path \"%s\" does "
+ "contain a theme manifest file.",
path.toString()));
}
- }
- private ThemeInfo generateThemeInfoFromJson(final Path path) {
- throw new UnsupportedOperationException();
- }
+ final ThemeInfo themeInfo = new ThemeInfo();
+ themeInfo.setVersion(ThemeVersion.LIVE);
+ themeInfo.setProvider(getClass());
+ themeInfo.setManifest(manifest);
- private ThemeInfo generateThemeInfoFromXml(final Path path) {
- throw new UnsupportedOperationException();
+ return themeInfo;
}
}
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 f027318db..0d95b47bb 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/ThemeConstants.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeConstants.java
@@ -23,10 +23,14 @@ package org.libreccm.theming;
* @author Jens Pelzetter
*/
public final class ThemeConstants {
-
+
+ public final static String THEME_MANIFEST_JSON = "theme.json";
+ public final static String THEME_MANIFEST_XML = "theme.xml";
+
+ public final static String THEMES_XML_NS = "http://themes.libreccm.org";
+
private ThemeConstants() {
//Nothing
}
-
- public final static String THEMES_XML_NS = "http://themes.libreccm.org";
+
}
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 0f69aedac..252b736a4 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfo.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfo.java
@@ -27,10 +27,29 @@ import java.util.Objects;
*/
public class ThemeFileInfo {
+ /**
+ * 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?
+ */
private boolean writable;
public String getName() {
diff --git a/ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfoUtil.java b/ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfoUtil.java
new file mode 100644
index 000000000..55a1c606e
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeFileInfoUtil.java
@@ -0,0 +1,65 @@
+/*
+ * 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;
+
+import org.libreccm.core.UnexpectedErrorException;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Objects;
+
+import javax.enterprise.context.RequestScoped;
+
+/**
+ * Utility for building a {@link ThemeFileInfo} object for a file.
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+public class ThemeFileInfoUtil {
+
+ /**
+ * Build a {@link ThemeFileInfo} object for a file. Before calling this
+ * method the caller should check if the file to {@code path} points exists.
+ *
+ * @param path The path of the file.
+ * @return A {@link ThemeFileInfo} object with informations about the file.
+ */
+ public ThemeFileInfo buildThemeInfo(final Path path) {
+
+ Objects.requireNonNull(path);
+
+ try {
+ final ThemeFileInfo fileInfo = new ThemeFileInfo();
+ fileInfo.setName(path.getFileName().toString());
+ fileInfo.setDirectory(Files.isDirectory(path));
+ fileInfo.setWritable(Files.isWritable(path));
+ if (!Files.isDirectory(path)) {
+ fileInfo.setSize(Files.size(path));
+ }
+ fileInfo.setMimeType(Files.probeContentType(path));
+
+ return fileInfo;
+ } catch (IOException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+ }
+
+}
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 9ca338fd7..01b61df91 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/ThemeInfo.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeInfo.java
@@ -23,18 +23,27 @@ import org.libreccm.theming.manifest.ThemeManifest;
import java.util.Objects;
/**
+ * Informations about a theme.
*
* @author Jens Pelzetter
*/
public class ThemeInfo {
+ /**
+ * The manifest of the theme.
+ */
private ThemeManifest manifest;
-// private String name;
+ /**
+ * The version of the theme.
+ */
private ThemeVersion version;
-// private String type;
- private Class provider;
+ /**
+ * The {@link ThemeProvider} implementation which is responsible for the
+ * theme.
+ */
+ private Class extends ThemeProvider> provider;
public ThemeManifest getManifest() {
return manifest;
@@ -47,16 +56,12 @@ public class ThemeInfo {
/**
* Convenient getter for name of theme.
*
- * @return {@link ThemeManifest#getName()}
+ * @return {@link #manifest#getName()}
*/
public String getName() {
-// return name;
return manifest.getName();
}
-// public void setName(final String name) {
-// this.name = name;
-// }
public ThemeVersion getVersion() {
return version;
}
@@ -68,21 +73,17 @@ public class ThemeInfo {
/**
* Convenient getter for type of theme.
*
- * @return {@link ThemeManifest#getType()}
+ * @return {@link #manifest#getType()}
*/
public String getType() {
-// return type;
return manifest.getType();
}
-// public void setType(final String type) {
-// this.type = type;
-// }
- public Class getProvider() {
+ public Class extends ThemeProvider> getProvider() {
return provider;
}
- public void setProvider(final Class provider) {
+ public void setProvider(final Class extends ThemeProvider> provider) {
this.provider = provider;
}
@@ -90,9 +91,7 @@ public class ThemeInfo {
public int hashCode() {
int hash = 5;
hash = 73 * hash + Objects.hashCode(manifest);
-// hash = 73 * hash + Objects.hashCode(name);
hash = 73 * hash + Objects.hashCode(version);
-// hash = 73 * hash + Objects.hashCode(type);
if (provider != null) {
hash = 73 * hash + Objects.hashCode(provider.getName());
}
@@ -127,12 +126,6 @@ public class ThemeInfo {
return false;
}
}
-// if (!Objects.equals(name, other.getName())) {
-// return false;
-// }
-// if (!Objects.equals(type, other.getType())) {
-// return false;
-// }
return version == other.getVersion();
}
@@ -146,27 +139,23 @@ public class ThemeInfo {
}
public String toString(final String data) {
-
+
final String providerClassName;
if (provider == null) {
- providerClassName = "";
+ providerClassName = "";
} else {
providerClassName = provider.getName();
}
-
+
return String.format("%s{ "
+ "mainfest = %s, "
- // + "name = \"%s\", "
+ "version = %s, "
+ "provider = %s, "
- // + "type = \"%s\"%s"
+ "%s }",
super.toString(),
Objects.toString(manifest),
- // name,
Objects.toString(version),
providerClassName,
- // type,
data);
}
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 79f9f5685..40847044a 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/ThemeProcessor.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeProcessor.java
@@ -20,14 +20,33 @@ package org.libreccm.theming;
import java.util.Map;
+import javax.enterprise.context.RequestScoped;
+
/**
+ * Interface for theme processors. A theme processor is responsible for
+ * converting the result of rendering a {@link PageModel} into HTML.
+ *
+ * An implementation must be a CDI bean (recommended scope:
+ * {@link RequestScoped}) which also annotated with the {@link ProcessesThemes}
+ * annotation.
*
* @author Jens Pelzetter
*/
public interface ThemeProcessor {
-
- String process(Map page,
+
+ /**
+ * 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.
+ *
+ * @return The HTML for the provided {@code page}.
+ */
+ String process(Map page,
ThemeInfo theme,
ThemeProvider themeProvider);
-
+
}
diff --git a/ccm-core/src/main/java/org/libreccm/theming/ThemeProcessors.java b/ccm-core/src/main/java/org/libreccm/theming/ThemeProcessors.java
index d31c04781..c0d7ade2c 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/ThemeProcessors.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeProcessors.java
@@ -29,6 +29,7 @@ import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Inject;
/**
+ * Provides access to the available implementations of {@link ThemeProcessor}.
*
* @author Jens Pelzetter
*/
@@ -37,9 +38,22 @@ public class ThemeProcessors implements Serializable {
private static final long serialVersionUID = -2019759931022734946L;
+ /**
+ * All available implementations of {@link ThemeProcessor}.
+ */
@Inject
private Instance processors;
+ /**
+ * Find the implementation of {@link ThemeProcessor} for {@code type}.
+ *
+ * @param type The type of the theme to process.
+ *
+ * @return The implementation {@link ThemeProcessor} which can process
+ * themes of the provided {@code type} or an empty {@code Optional}
+ * if there is no suitable implementation of {@link ThemeProcessor}
+ * is available.
+ */
public Optional findThemeProcessorForType(final String type) {
final ProcessesThemeLiteral literal = new ProcessesThemeLiteral(type);
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 3d2e5c2e2..ae0262191 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/ThemeProvider.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/ThemeProvider.java
@@ -19,6 +19,7 @@
package org.libreccm.theming;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.List;
import java.util.Optional;
@@ -86,7 +87,7 @@ public interface ThemeProvider {
* will ignore this parameter.
* @param path The path of the directory of which the files are listed.
* The path is relative to the root of the theme.To get the
- * root directory provided an empty string. Implementations
+ * root directory provide an empty string. Implementations
* should throw an NullPointerException if {@code null} is
* provided as path.
*
@@ -94,6 +95,10 @@ public interface ThemeProvider {
* path in the theme the list is empty. If the path is the path of a
* file and not a directory the list should have one element, the
* data about the file itself.
+ *
+ * @throws IllegalArgumentException If {@code theme} is an empty string,
+ * if there is no theme with the name provided by {@code theme} or
+ * if there is no file/directory with the provided path in the theme.
*/
List listThemeFiles(String theme,
ThemeVersion version,
@@ -102,7 +107,11 @@ public interface ThemeProvider {
/**
* Retrieve a file from a theme. We use an {@link InputStream} here because
* that is the most universal interface in the Java API which works for all
- * sorts of resources and is independent from any other API.
+ * sorts of resources and is independent from any other API. Hint: In most
+ * cases it is recommended to wrap the {@link InputStream} provided by this
+ * method in a {@link InputStreamReader} by using one of constructors of
+ * {@link InputStreamReader} which allows the caller to set the charset of
+ * the data read (which should be UTF-8 in most cases).
*
* @param theme The theme from which the file is retrieved.
* @param version The version of the theme from which the file is retrieved.
diff --git a/ccm-core/src/main/java/org/libreccm/theming/ThemeType.java b/ccm-core/src/main/java/org/libreccm/theming/ThemeType.java
deleted file mode 100644
index 374211ef6..000000000
--- a/ccm-core/src/main/java/org/libreccm/theming/ThemeType.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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;
-
-/**
- *
- * @author Jens Pelzetter
- */
-public @interface ThemeType {
-
- String value();
-
-}
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 7850ff50c..48c366ed0 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/Themes.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/Themes.java
@@ -21,6 +21,7 @@ package org.libreccm.theming;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.libreccm.core.UnexpectedErrorException;
+import org.libreccm.pagemodel.PageModel;
import java.util.ArrayList;
import java.util.List;
@@ -29,10 +30,12 @@ import java.util.Optional;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Instance;
-import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Inject;
/**
+ * Central interface for using themes. In most cases users of the theming system
+ * will use this class instead of directly working with {@link ThemeProvider}s
+ * and {@link ThemeProcessor}s.
*
* @author Jens Pelzetter
*/
@@ -43,10 +46,19 @@ public class Themes {
@Inject
private Instance providers;
+//
+// @Inject
+// private Instance processors;
@Inject
- private Instance processors;
+ private ThemeProcessors themeProcessors;
+ /**
+ * Retrieve all available themes.
+ *
+ * @return A list with information about all available themes (draft
+ * versions).
+ */
public List getAvailableThemes() {
final List themes = new ArrayList<>();
@@ -57,6 +69,11 @@ public class Themes {
return themes;
}
+ /**
+ * Retrieve all available live themes.
+ *
+ * @return A list with informations about all live themes.
+ */
public List getLiveThemes() {
final List themes = new ArrayList<>();
@@ -66,52 +83,41 @@ public class Themes {
return themes;
}
-
- public Optional getTheme(final String name,
+
+ /**
+ * Get information about a specific theme.
+ *
+ * @param name Then name of the theme.
+ * @param version The version of the theme.
+ *
+ * @return An {@link Optional} with informations about theme {@code theme}
+ * or an empty optional if there is no such theme.
+ */
+ public Optional getTheme(final String name,
final ThemeVersion version) {
-
- for(final ThemeProvider provider : providers) {
+
+ for (final ThemeProvider provider : providers) {
if (provider.providesTheme(name, version)) {
return provider.getThemeInfo(name, version);
}
}
-
+
return Optional.empty();
}
- public String process(Map page, ThemeInfo theme) {
+ /**
+ * Creates HTML from the result of rendering a {@link PageModel}.
+ *
+ * @param page The page to convert to HTML.
+ * @param theme The theme to use.
+ *
+ * @return The HTML representation of the page.
+ */
+ public String process(final Map page,
+ final ThemeInfo theme) {
- final ThemeTypeLiteral themeType = new ThemeTypeLiteral(theme.getType());
-
- final Instance forType = processors.select(themeType);
- if (forType.isUnsatisfied()) {
- LOGGER.error("No ThemeProcessor implementation for type \"{}\" of "
- + "theme \"{}\".",
- theme.getType(),
- theme.getName());
- throw new UnexpectedErrorException(String
- .format("No ThemeProcessor implementation for type \"%s\" of "
- + "theme \"%s\".",
- theme.getType(),
- theme.getName()));
- }
-
- if (forType.isAmbiguous()) {
- LOGGER.error(
- "Mutiple ThemeProcessor implementations for type \"{}\" of "
- + "theme \"{}\".",
- theme.getType(),
- theme.getName());
- throw new UnexpectedErrorException(String
- .format(
- "Mutiple ThemeProcessor implementations for type \"%s\" of "
- + "theme \"%s\".",
- theme.getType(),
- theme.getName()));
- }
-
- final Instance forTheme = providers.select(theme
- .getProvider());
+ final Instance extends ThemeProvider> forTheme = providers.select(
+ theme.getProvider());
if (forTheme.isUnsatisfied()) {
LOGGER.error("ThemeProvider \"{}\" not found.",
@@ -121,28 +127,17 @@ public class Themes {
theme.getProvider().getName()));
}
- final ThemeProcessor processor = forType.get();
+ final ThemeProcessor processor = themeProcessors
+ .findThemeProcessorForType(theme.getType())
+ .orElseThrow(() -> new UnexpectedErrorException(String
+ .format("No ThemeProcessor implementation for type \"%s\" of "
+ + "theme \"%s\".",
+ theme.getType(),
+ theme.getName())));
final ThemeProvider provider = forTheme.get();
return processor.process(page, theme, provider);
}
- private static class ThemeTypeLiteral extends AnnotationLiteral
- implements ThemeType {
-
- private static final long serialVersionUID = 3377237291286175824L;
-
- private final String value;
-
- public ThemeTypeLiteral(final String value) {
- this.value = value;
- }
-
- @Override
- public String value() {
- return value;
- }
-
- }
}
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 ca44830d3..8a45d63bf 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,25 +34,43 @@ import javax.xml.bind.annotation.XmlRootElement;
import static org.libreccm.theming.ThemeConstants.*;
/**
- *
+ * 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 {
+ /**
+ * 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.
+ */
@XmlElement(name = "type", namespace = THEMES_XML_NS)
private String type;
+ /**
+ * The (localised) title of the theme.
+ */
@XmlElement(name = "title", namespace = THEMES_XML_NS)
private LocalizedString title;
+ /**
+ * A (localised) description of the theme.
+ */
@XmlElement(name = "description", namespace = THEMES_XML_NS)
private LocalizedString description;
+ /**
+ * The templates provided by the theme.
+ */
@XmlElementWrapper(name = "templates", namespace = THEMES_XML_NS)
@XmlElement(name = "template", namespace = THEMES_XML_NS)
private List templates;
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
new file mode 100644
index 000000000..1f96a18be
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/theming/manifest/ThemeManifestUtil.java
@@ -0,0 +1,90 @@
+/*
+ * 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.manifest;
+
+import static org.libreccm.theming.ThemeConstants.*;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
+import org.libreccm.core.UnexpectedErrorException;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Serializable;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Locale;
+
+import javax.enterprise.context.RequestScoped;
+
+/**
+ * A Utility class for loading them manifest file of a theme.
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+public class ThemeManifestUtil implements Serializable {
+
+ private static final long serialVersionUID = -7650437144515619682L;
+
+ /**
+ * Reads the manifest file at {@code path}.
+ *
+ * @param path The path of the manifest file.
+ * @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"));
+ } catch (IOException ex) {
+ throw new UnexpectedErrorException(ex);
+ }
+
+ 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;
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/theming/manifest/ThemeTemplate.java b/ccm-core/src/main/java/org/libreccm/theming/manifest/ThemeTemplate.java
index 3deb7be58..236862e77 100644
--- a/ccm-core/src/main/java/org/libreccm/theming/manifest/ThemeTemplate.java
+++ b/ccm-core/src/main/java/org/libreccm/theming/manifest/ThemeTemplate.java
@@ -29,7 +29,8 @@ import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
- *
+ * Informations about a template provided by a theme.
+ *
* @author Jens Pelzetter
*/
@XmlRootElement(name = "template", namespace = "http://themes.libreccm.org")
@@ -38,15 +39,27 @@ public class ThemeTemplate implements Serializable {
private static final long serialVersionUID = -9034588759798295569L;
+ /**
+ * The name of the template (usually the filename).
+ */
@XmlElement(name = "name", namespace = "http://themes.libreccm.org")
private String name;
+ /**
+ * The (localised) title of the template.
+ */
@XmlElement(name = "title", namespace = "http://themes.libreccm.org")
private LocalizedString title;
+ /**
+ * A (localised) description of the template.
+ */
@XmlElement(name = "description", namespace = "http://themes.libreccm.org")
private LocalizedString description;
+ /**
+ * Path of template relative to the directory of the theme.
+ */
@XmlElement(name = "path", namespace = "http://themes.libreccm.org")
private String path;