From 402ea5614df5cb1304e90d1dace37b9d6874db33 Mon Sep 17 00:00:00 2001 From: Jens Pelzetter Date: Sun, 13 Sep 2020 12:08:59 +0200 Subject: [PATCH] Integration of CCM themes and EE/MVC Krazo --- .../mvc/freemarker/KrazoTemplateLoader.java | 90 +++++++++++ .../MvcFreemarkerConfigurationProducer.java | 89 +++++++++++ .../mvc/freemarker/ThemesTemplateLoader.java | 149 ++++++++++++++++++ .../libreccm/mvc/freemarker/package-info.java | 22 +++ .../java/org/libreccm/mvc/package-info.java | 25 +++ 5 files changed, 375 insertions(+) create mode 100644 ccm-core/src/main/java/org/libreccm/mvc/freemarker/KrazoTemplateLoader.java create mode 100644 ccm-core/src/main/java/org/libreccm/mvc/freemarker/MvcFreemarkerConfigurationProducer.java create mode 100644 ccm-core/src/main/java/org/libreccm/mvc/freemarker/ThemesTemplateLoader.java create mode 100644 ccm-core/src/main/java/org/libreccm/mvc/freemarker/package-info.java create mode 100644 ccm-core/src/main/java/org/libreccm/mvc/package-info.java diff --git a/ccm-core/src/main/java/org/libreccm/mvc/freemarker/KrazoTemplateLoader.java b/ccm-core/src/main/java/org/libreccm/mvc/freemarker/KrazoTemplateLoader.java new file mode 100644 index 000000000..86c635ed7 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/mvc/freemarker/KrazoTemplateLoader.java @@ -0,0 +1,90 @@ +/* + * 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.mvc.freemarker; + +import freemarker.cache.MultiTemplateLoader; +import freemarker.cache.TemplateLoader; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + +import javax.servlet.ServletContext; + +/** + * + * @author Jens Pelzetter + */ +/** + * A copy of the {@link TemplateLoader} used by Krazo. + * + * The {@code TemplateLoader} used by Krazo is defined as inner class. This + * class provides the same behaviour as "real" class so that we can use it with + * Freemarker {@link MultiTemplateLoader}. + * + * As extension this implementation of the {@code TemplateLoader} interface will + * not process template paths which start with {@code @themes/} or + * {@code /@themes/}. These path are processed by the templates loaders for the + * theming system. + * + * @author Jens Pelzetter + */ +public class KrazoTemplateLoader implements TemplateLoader { + + private final ServletContext servletContext; + + public KrazoTemplateLoader(final ServletContext servletContext) { + super(); + this.servletContext = servletContext; + } + + @Override + public Object findTemplateSource(final String name) throws IOException { + if (name.startsWith("@themes") || name.startsWith("/@themes")) { + return null; + } else { + // Freemarker drops "/" + return servletContext.getResourceAsStream( + String.format("/%s", name) + ); + } + } + + @Override + public long getLastModified(final Object templateSource) { + return -1; + } + + @Override + public Reader getReader( + final Object templateSource, final String encoding + ) throws IOException { + return new InputStreamReader((InputStream) templateSource, encoding); + } + + @Override + public void closeTemplateSource( + final Object templateSource + ) throws IOException { + final InputStream inputStream = (InputStream) templateSource; + inputStream.close(); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/mvc/freemarker/MvcFreemarkerConfigurationProducer.java b/ccm-core/src/main/java/org/libreccm/mvc/freemarker/MvcFreemarkerConfigurationProducer.java new file mode 100644 index 000000000..a209a462a --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/mvc/freemarker/MvcFreemarkerConfigurationProducer.java @@ -0,0 +1,89 @@ +/* + * 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.mvc.freemarker; + +import freemarker.cache.ClassTemplateLoader; +import freemarker.cache.MultiTemplateLoader; +import freemarker.cache.TemplateLoader; +import freemarker.cache.WebappTemplateLoader; +import freemarker.template.Configuration; +import freemarker.template.TemplateExceptionHandler; +import org.eclipse.krazo.engine.ViewEngineConfig; +import org.eclipse.krazo.ext.freemarker.DefaultConfigurationProducer; +import org.libreccm.theming.Themes; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Specializes; +import javax.inject.Inject; +import javax.servlet.ServletContext; +import javax.ws.rs.Produces; + +/** + * Extends the default configuration for Freemarker of Eclipse Krazo to + * support Freemarker templates in CCM themes. + * + * @author Jens Pelzetter + */ +@ApplicationScoped +public class MvcFreemarkerConfigurationProducer + extends DefaultConfigurationProducer { + + @Inject + private ServletContext servletContext; + + @Inject + private Themes themes; + + @Produces + @ViewEngineConfig + @Specializes + @Override + public Configuration getConfiguration() { + final Configuration configuration = new Configuration( + Configuration.VERSION_2_3_30 + ); + + configuration.setDefaultEncoding("UTF-8"); + configuration.setTemplateExceptionHandler( + TemplateExceptionHandler.RETHROW_HANDLER + ); + configuration.setLogTemplateExceptions(false); + configuration.setWrapUncheckedExceptions(false); + configuration.setLocalizedLookup(false); + configuration.setTemplateLoader( + new MultiTemplateLoader( + new TemplateLoader[]{ + new KrazoTemplateLoader(servletContext), + new ThemesTemplateLoader(themes), + // For loading Freemarker macro libraries from WEB-INF + // resources + new WebappTemplateLoader( + servletContext, "/themes/freemarker" + ), + // For loading Freemarker macro libraries from classpath + // resources + new ClassTemplateLoader(getClass(), "/themes/freemarker") + } + ) + ); + + return configuration; + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/mvc/freemarker/ThemesTemplateLoader.java b/ccm-core/src/main/java/org/libreccm/mvc/freemarker/ThemesTemplateLoader.java new file mode 100644 index 000000000..677046738 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/mvc/freemarker/ThemesTemplateLoader.java @@ -0,0 +1,149 @@ +/* + * 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.mvc.freemarker; + +import freemarker.cache.TemplateLoader; +import org.libreccm.theming.ThemeInfo; +import org.libreccm.theming.ThemeVersion; +import org.libreccm.theming.Themes; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.Arrays; +import java.util.Optional; + +/** + * Loads Freemarker templates from a theme. + * + * @author Jens Pelzetter + */ +public class ThemesTemplateLoader implements TemplateLoader { + + private final Themes themes; + + public ThemesTemplateLoader(final Themes themes) { + this.themes = themes; + } + + /** + * Loads the template from a theme. The path of the theme file must follow + * the following format: + * + * {@code @themes/$themeName/$version/$path/$to/$file} + * + * The {@code @themes} prefix is mandantory. {@code $themeName} is the name + * of the theme from which the template is loaded. {@code $version} is the + * version of the theme to use. This token is converted to + * {@link ThemeVersion}. Valid values are therefore {@code DRAFT} and + * {@code LIVE}. The remainder of the path is the path to the file inside the + * theme. + * + * @param path The path of the file. The path must include the theme and its + * version. + * + * @return An {@link InputStream} for the template if the template was found + * in the theme. Otherwise {@code null} is returned. + * + * @throws IOException + */ + @Override + public Object findTemplateSource(final String path) throws IOException { + if (path.startsWith("@themes") || path.startsWith("/@themes")) { + final String[] tokens; + if (path.startsWith("/")) { + tokens = path.substring(1).split("/"); + } else { + tokens = path.split("/"); + } + return findTemplateSource(tokens); + } else { + return null; + } + } + + private InputStream findTemplateSource(final String[] tokens) { + if (tokens.length >= 4) { + final String themeName = tokens[1]; + final ThemeVersion themeVersion = ThemeVersion + .valueOf(tokens[2]); + final String filePath = String.join( + "/", + Arrays.copyOfRange( + tokens, 3, tokens.length, String[].class + ) + ); + + return findTemplateSource(themeName, themeVersion, filePath); + } else { + return null; + } + } + + private InputStream findTemplateSource( + final String themeName, + final ThemeVersion themeVersion, + final String filePath + ) { + final Optional themeInfo = themes.getTheme( + themeName, themeVersion + ); + if (themeInfo.isPresent()) { + return findTemplateSource(themeInfo.get(), filePath); + } else { + return null; + } + } + + private InputStream findTemplateSource( + final ThemeInfo themeInfo, final String filePath + ) { + final Optional source = themes.getFileFromTheme( + themeInfo, filePath + ); + if (source.isPresent()) { + return source.get(); + } else { + return null; + } + } + + @Override + public long getLastModified(Object templateSource) { + return -1; + } + + @Override + public Reader getReader( + final Object templateSource, final String encoding + ) throws IOException { + final InputStream inputStream = (InputStream) templateSource; + return new InputStreamReader(inputStream, encoding); + } + + @Override + public void closeTemplateSource( + final Object templateSource + ) throws IOException { + final InputStream inputStream = (InputStream) templateSource; + inputStream.close(); + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/mvc/freemarker/package-info.java b/ccm-core/src/main/java/org/libreccm/mvc/freemarker/package-info.java new file mode 100644 index 000000000..d3d2157ad --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/mvc/freemarker/package-info.java @@ -0,0 +1,22 @@ +/* + * 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 + */ +/** + * Integration of the Freemarker View Engine of Eclipse Krazo with LibreCCM. + */ +package org.libreccm.mvc.freemarker; diff --git a/ccm-core/src/main/java/org/libreccm/mvc/package-info.java b/ccm-core/src/main/java/org/libreccm/mvc/package-info.java new file mode 100644 index 000000000..5d3b11aa2 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/mvc/package-info.java @@ -0,0 +1,25 @@ +/* + * 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 + */ +/** + * The classes in this package integrate LibreCCM with Jakarta EE MVC and its + * reference implementation Eclipse Krazo. + * + * @see https://www.mvc-spec.org/ + */ +package org.libreccm.mvc;