JavaDoc for the integration of EE MVC and the themeing system

ccm-docs
Jens Pelzetter 2021-01-07 20:18:19 +01:00
parent 32d5eed14d
commit 375e5eff65
8 changed files with 191 additions and 17 deletions

View File

@ -58,6 +58,22 @@ import javax.mvc.engine.ViewEngineException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
/** /**
* Customized version of the Freemarker View Engine. This class is based of the
* View Engine from the Krazo project, but has been extended:
* <ul>
* <li>Named Beans are supported</li>
* <li>Freemarker template have access to the MvcContext under the name
* {@code mvc}, as in Facelet-based templates</li>
* <li>The current {@link HttpServletRequest} is made avaiable in Freemarker
* templates as {@link request}.</li>
* <li>The following utility functions are made available:
* <ul>
* <li>{@code getSetting}: retreives the value of a setting from the theme</li>
* <li>{@code localize}: retreives a localized value from the theme</li>
* <li>{@code truncateText}: Truncates text to a specific length.</li>
* </ul>
* </li>
* </ul>
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ -124,8 +140,6 @@ public class FreemarkerViewEngine extends ViewEngineBase {
} }
model.put("truncateText", new TruncateTextMethod()); model.put("truncateText", new TruncateTextMethod());
final Map<String, Object> namedBeans = beanManager final Map<String, Object> namedBeans = beanManager
.getBeans(Object.class) .getBeans(Object.class)
.stream() .stream()
@ -149,6 +163,13 @@ public class FreemarkerViewEngine extends ViewEngineBase {
} }
} }
/**
* Helper method for retrieving a an instance of a named bean using CDI.
*
* @param bean The bean to retrieve.
*
* @return An instance of the bean.
*/
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private Optional<NamedBeanInstance> findBeanInstance(final Bean<?> bean) { private Optional<NamedBeanInstance> findBeanInstance(final Bean<?> bean) {
final Context context = beanManager.getContext(bean.getScope()); final Context context = beanManager.getContext(bean.getScope());
@ -166,10 +187,19 @@ public class FreemarkerViewEngine extends ViewEngineBase {
} }
} }
/**
* Helper class encapsulating the information about a named bean.
*/
private class NamedBeanInstance { private class NamedBeanInstance {
/**
* The name of the bean.
*/
private final String name; private final String name;
/**
* The bean instance.
*/
private final Object beanInstance; private final Object beanInstance;
public NamedBeanInstance(String name, Object beanInstance) { public NamedBeanInstance(String name, Object beanInstance) {
@ -187,6 +217,9 @@ public class FreemarkerViewEngine extends ViewEngineBase {
} }
/**
* Retrieves a setting from the theme using the {@link SettingsUtils}.
*/
private class GetSettingMethod implements TemplateMethodModelEx { private class GetSettingMethod implements TemplateMethodModelEx {
private final ThemeInfo fromTheme; private final ThemeInfo fromTheme;
@ -241,6 +274,9 @@ public class FreemarkerViewEngine extends ViewEngineBase {
} }
/**
* Retrieves a localized value from the theme using the {@link L10NUtils}.
*/
private class LocalizeMethod implements TemplateMethodModelEx { private class LocalizeMethod implements TemplateMethodModelEx {
private final ThemeInfo fromTheme; private final ThemeInfo fromTheme;
@ -275,6 +311,9 @@ public class FreemarkerViewEngine extends ViewEngineBase {
} }
/**
* Truncates text to a specific length.
*/
private class TruncateTextMethod implements TemplateMethodModelEx { private class TruncateTextMethod implements TemplateMethodModelEx {
@Override @Override

View File

@ -21,13 +21,20 @@ package org.libreccm.mvc.freemarker;
import org.libreccm.theming.ThemeInfo; import org.libreccm.theming.ThemeInfo;
/** /**
* Encapulates the data of a template.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
class TemplateInfo { class TemplateInfo {
/**
* Info about the theme providing the template.
*/
private final ThemeInfo themeInfo; private final ThemeInfo themeInfo;
/**
* The path of the template,
*/
private final String filePath; private final String filePath;
public TemplateInfo(ThemeInfo themeInfo, String filePath) { public TemplateInfo(ThemeInfo themeInfo, String filePath) {

View File

@ -34,13 +34,15 @@ import javax.enterprise.inject.Instance;
import javax.inject.Inject; import javax.inject.Inject;
/** /**
* Utility class for retreving a {@link TemplateInfo} instance for a template.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@RequestScoped @RequestScoped
class ThemeTemplateUtil { class ThemeTemplateUtil {
private static final Logger LOGGER = LogManager.getLogger(ThemeTemplateUtil.class); private static final Logger LOGGER = LogManager.getLogger(
ThemeTemplateUtil.class);
@Inject @Inject
private Instance<ThemeProvider> themeProviders; private Instance<ThemeProvider> themeProviders;
@ -48,11 +50,28 @@ class ThemeTemplateUtil {
@Inject @Inject
private Themes themes; private Themes themes;
/**
* Checks if the provided path points to a template.
*
* @param templatePath The path of the template.
*
* @return {@code true} if the path points to a template, {@code false}
* otherwise.
*/
public boolean isValidTemplatePath(final String templatePath) { public boolean isValidTemplatePath(final String templatePath) {
return templatePath.startsWith("@themes") return templatePath.startsWith("@themes")
|| templatePath.startsWith("/@themes"); || templatePath.startsWith("/@themes");
} }
/**
* Get the {@link TemplateInfo} for the template.
*
* @param templatePath The path of the template.
*
* @return An {@link Optional} with a {@link TemplateInfo} for the template.
* If the template is not available, an empty {@link Optional} is
* returned.
*/
public Optional<TemplateInfo> getTemplateInfo(final String templatePath) { public Optional<TemplateInfo> getTemplateInfo(final String templatePath) {
if (!isValidTemplatePath(templatePath)) { if (!isValidTemplatePath(templatePath)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
@ -74,6 +93,13 @@ class ThemeTemplateUtil {
return getTemplateInfo(tokens); return getTemplateInfo(tokens);
} }
/**
* Find the {@link ThemeProvider} for a theme.
*
* @param forTheme The theme
*
* @return The {@link ThemeProvider} for the theme.
*/
public ThemeProvider findThemeProvider(final ThemeInfo forTheme) { public ThemeProvider findThemeProvider(final ThemeInfo forTheme) {
final Instance<? extends ThemeProvider> provider = themeProviders final Instance<? extends ThemeProvider> provider = themeProviders
.select(forTheme.getProvider()); .select(forTheme.getProvider());
@ -92,6 +118,15 @@ class ThemeTemplateUtil {
return provider.get(); return provider.get();
} }
/**
* Retrieves the {@link TemplateInfo} for a template.
*
* @param tokens The tokens of the template path.
*
* @return An {@link Optional} with a {@link TemplateInfo} for the template.
* If the template is not available, an empty {@link Optional} is
* returned.
*/
private Optional<TemplateInfo> getTemplateInfo(final String[] tokens) { private Optional<TemplateInfo> getTemplateInfo(final String[] tokens) {
if (tokens.length >= 4) { if (tokens.length >= 4) {
final String themeName = tokens[1]; final String themeName = tokens[1];

View File

@ -19,7 +19,6 @@
package org.libreccm.mvc.freemarker; package org.libreccm.mvc.freemarker;
import freemarker.cache.TemplateLoader; import freemarker.cache.TemplateLoader;
import org.libreccm.theming.ThemeInfo;
import org.libreccm.theming.ThemeVersion; import org.libreccm.theming.ThemeVersion;
import org.libreccm.theming.Themes; import org.libreccm.theming.Themes;
@ -27,7 +26,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import java.util.Arrays;
import java.util.Optional; import java.util.Optional;
/** /**

View File

@ -21,7 +21,6 @@ package org.libreccm.theming.mvc;
import org.libreccm.theming.ThemeFileInfo; import org.libreccm.theming.ThemeFileInfo;
import org.libreccm.theming.ThemeProvider; import org.libreccm.theming.ThemeProvider;
import org.libreccm.theming.ThemeVersion; import org.libreccm.theming.ThemeVersion;
import org.libreccm.theming.manager.Themes;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
@ -38,6 +37,9 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
/** /**
* Provides access to the resources/assets of themes.
*
* @see ThemeResources
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ -45,13 +47,30 @@ import javax.ws.rs.core.Response;
@Path("/") @Path("/")
public class ThemeResourceProvider { public class ThemeResourceProvider {
/**
* Injection point for the available {@link ThemeProvider}s.
*/
@Inject @Inject
@Any @Any
private Instance<ThemeProvider> providers; private Instance<ThemeProvider> providers;
@Inject /**
private Themes themes; * Serves a resources from a theme. This endpoint is mounted at
* {@code /@themes/{theme}/{themeVersion}/{path:.+}}. If the provided theme
* is not found, the provided theme is not available in the requested
* version, or if the theme does not provide the requested resource the
* endpoint will response with a 404 response. If the response is found, a
* response with the asset and the correct mime type is returned.
*
* @param themeName The name of the theme providing the resource.
* @param themeVersionParam The version of the theme to use (either
* {@code LIVE} or {@code DRAFT}.
* @param pathParam The path of the resource to serve.
*
* @return A response with the resource and the correct mime type, or a 404
* response if the resource, the theme version or the theme is not
* available.
*/
@GET @GET
@Path("/{theme}/{themeVersion}/{path:.+}") @Path("/{theme}/{themeVersion}/{path:.+}")
public Response getThemeFile( public Response getThemeFile(
@ -123,8 +142,15 @@ public class ThemeResourceProvider {
} }
} }
/**
* Helper method for finding the provider of a theme.
*
* @param forTheme The theme.
*
* @return An {@link Optional} with the provider of the theme. If there is
* no matching provider, an empty {@link Optional} is returned.
*/
private Optional<ThemeProvider> findProvider(final String forTheme) { private Optional<ThemeProvider> findProvider(final String forTheme) {
final List<ThemeProvider> providersList = new ArrayList<>(); final List<ThemeProvider> providersList = new ArrayList<>();
providers providers
.forEach(provider -> providersList.add(provider)); .forEach(provider -> providersList.add(provider));

View File

@ -25,6 +25,8 @@ import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application; import javax.ws.rs.core.Application;
/** /**
* JAX-RS application providing the resources/assets of a theme (images, CSS
* files, etc) under the {@code /@themes} URL.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ -38,6 +40,4 @@ public class ThemeResources extends Application {
return classes; return classes;
} }
} }

View File

@ -18,7 +18,6 @@
*/ */
package org.libreccm.theming.mvc; package org.libreccm.theming.mvc;
import org.libreccm.core.UnexpectedErrorException;
import org.libreccm.sites.Site; import org.libreccm.sites.Site;
import org.libreccm.sites.SiteRepository; import org.libreccm.sites.SiteRepository;
import org.libreccm.theming.ThemeInfo; import org.libreccm.theming.ThemeInfo;
@ -30,7 +29,6 @@ import org.libreccm.theming.manifest.ThemeTemplate;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import javax.enterprise.context.RequestScoped; import javax.enterprise.context.RequestScoped;
import javax.inject.Inject; import javax.inject.Inject;
@ -42,6 +40,7 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
/** /**
* Main integration point for MVC application with the theme system.
* *
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a> * @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/ */
@ -60,6 +59,16 @@ public class ThemesMvc {
@Inject @Inject
private Themes themes; private Themes themes;
/**
* Get the template for a specific application and view from the current
* theme.
*
* @param uriInfo URI is required for some tasks in inside the method.
* @param application The application for which the template is requested.
* @param view The view for which the template is requested.
*
* @return The path of the template to use for the view of the application.
*/
public String getMvcTemplate( public String getMvcTemplate(
final UriInfo uriInfo, final UriInfo uriInfo,
final String application, final String application,
@ -136,6 +145,13 @@ public class ThemesMvc {
); );
} }
/**
* Helper method of retrieving the current site.
*
* @param uriInfo Used to extract the current site.
*
* @return The current site.
*/
private Site getSite(final UriInfo uriInfo) { private Site getSite(final UriInfo uriInfo) {
Objects.requireNonNull(uriInfo); Objects.requireNonNull(uriInfo);
@ -154,6 +170,16 @@ public class ThemesMvc {
return site; return site;
} }
/**
* Helper method for retrieving a the theme to use.
*
* @param site The current site.
* @param theme The theme to retrieve.
* @param themeVersion The version of the theme to retrieve.
*
* @return A {@link ThemeInfo} object providing access to to the theme and
* its resources.
*/
private ThemeInfo getTheme( private ThemeInfo getTheme(
final Site site, final Site site,
final String theme, final String theme,
@ -187,6 +213,15 @@ public class ThemesMvc {
} }
} }
/**
* Helper method for parsing the {@code theme} query parameter which can be
* used to override the default theme of a site.
*
* @param uriInfo Information about the current URI.
*
* @return The value of the {@link theme} query parameter if present, or
* {@code --DEFAULT--} if the query parameter is not present.
*/
private String parseThemeParam(final UriInfo uriInfo) { private String parseThemeParam(final UriInfo uriInfo) {
if (uriInfo.getQueryParameters().containsKey("theme")) { if (uriInfo.getQueryParameters().containsKey("theme")) {
return uriInfo.getQueryParameters().getFirst("theme"); return uriInfo.getQueryParameters().getFirst("theme");
@ -195,6 +230,18 @@ public class ThemesMvc {
} }
} }
/**
* Helper method for parsing the {@code preview} query parameter. The
* {@code preview} query parameter allows it to test the draft version of a
* theme.
*
* @param uriInfo Information about the current URI.
*
* @return If the value of the parameter is {@code theme} or {@code all}
* {@link ThemeVersion#DRAFT} is returned. If the query parameter is
* not present or has another value, {@link ThemeVersion#LIVE} is
* returned.
*/
private ThemeVersion parsePreviewParam(final UriInfo uriInfo) { private ThemeVersion parsePreviewParam(final UriInfo uriInfo) {
if (uriInfo.getQueryParameters().containsKey("preview")) { if (uriInfo.getQueryParameters().containsKey("preview")) {
final List<String> values = uriInfo final List<String> values = uriInfo

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2021 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 Theming System with Jakarta EE MVC.
*/
package org.libreccm.theming.mvc;