CCM NG: Implementieren eines ThemeProcessors für Freemarker (#2759)
git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@5289 8810af33-2d31-482b-a856-94f89814c4df
parent
ee857ec4c8
commit
3ceec91105
|
|
@ -204,6 +204,11 @@
|
|||
<artifactId>javax.json</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.freemarker</groupId>
|
||||
<artifactId>freemarker</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.sf.saxon</groupId>
|
||||
<artifactId>Saxon-HE</artifactId>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Copyright (C) 2018 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.freemarker;
|
||||
|
||||
import freemarker.cache.TemplateLoader;
|
||||
import freemarker.cache.TemplateLookupStrategy;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.TemplateExceptionHandler;
|
||||
import org.libreccm.core.UnexpectedErrorException;
|
||||
import org.libreccm.theming.ThemeInfo;
|
||||
import org.libreccm.theming.Themes;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
@ApplicationScoped
|
||||
class FreemarkerConfigurationProvider {
|
||||
|
||||
@Inject
|
||||
private Themes themes;
|
||||
|
||||
private final Map<ThemeInfo, Configuration> configurations = new HashMap<>();
|
||||
|
||||
protected Configuration getConfiguration(final ThemeInfo forTheme) {
|
||||
|
||||
if (configurations.containsKey(forTheme)) {
|
||||
|
||||
return configurations.get(forTheme);
|
||||
} else {
|
||||
|
||||
final Configuration configuration = new Configuration(
|
||||
Configuration.VERSION_2_3_27);
|
||||
configuration.setDefaultEncoding("UTF-8");
|
||||
configuration
|
||||
.setTemplateExceptionHandler(
|
||||
TemplateExceptionHandler.RETHROW_HANDLER);
|
||||
configuration.setLogTemplateExceptions(false);
|
||||
configuration.setWrapUncheckedExceptions(false);
|
||||
configuration.setLocalizedLookup(false);
|
||||
configuration.setTemplateLoader(new CcmTemplateLoader(forTheme));
|
||||
|
||||
configurations.put(forTheme, configuration);
|
||||
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
|
||||
private class CcmTemplateLoader implements TemplateLoader {
|
||||
|
||||
private final ThemeInfo fromTheme;
|
||||
|
||||
public CcmTemplateLoader(final ThemeInfo fromTheme) {
|
||||
this.fromTheme = fromTheme;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object findTemplateSource(final String name) throws IOException {
|
||||
|
||||
return themes
|
||||
.getFileFromTheme(fromTheme, name)
|
||||
.orElseThrow(() -> new UnexpectedErrorException(String
|
||||
.format("Failed to open Freemarker Template \"%s\" from "
|
||||
+ "theme \"%s\".",
|
||||
name,
|
||||
fromTheme.getName())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastModified(final 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* Copyright (C) 2018 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.freemarker;
|
||||
|
||||
import static org.libreccm.theming.ThemeConstants.*;
|
||||
|
||||
import freemarker.template.SimpleNumber;
|
||||
import freemarker.template.SimpleScalar;
|
||||
import freemarker.template.Template;
|
||||
import freemarker.template.TemplateException;
|
||||
import freemarker.template.TemplateMethodModelEx;
|
||||
import freemarker.template.TemplateModelException;
|
||||
import freemarker.template.TemplateScalarModel;
|
||||
import org.libreccm.core.UnexpectedErrorException;
|
||||
import org.libreccm.theming.ProcessesThemes;
|
||||
import org.libreccm.theming.ThemeInfo;
|
||||
import org.libreccm.theming.ThemeProcessor;
|
||||
import org.libreccm.theming.ThemeProvider;
|
||||
import org.libreccm.theming.manifest.ThemeTemplate;
|
||||
import org.libreccm.theming.utils.L10NUtils;
|
||||
import org.libreccm.theming.utils.SettingsUtils;
|
||||
import org.libreccm.theming.utils.SystemInfoUtils;
|
||||
import org.libreccm.theming.utils.TextUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
@ProcessesThemes("freemarker")
|
||||
@RequestScoped
|
||||
public class FreemarkerThemeProcessor implements ThemeProcessor {
|
||||
|
||||
private static final long serialVersionUID = -5631706431004020559L;
|
||||
|
||||
@Inject
|
||||
private FreemarkerConfigurationProvider configurationProvider;
|
||||
|
||||
@Inject
|
||||
private L10NUtils l10nUtils;
|
||||
|
||||
@Inject
|
||||
private SystemInfoUtils systemInfoUtils;
|
||||
|
||||
@Inject
|
||||
private SettingsUtils settingsUtils;
|
||||
|
||||
@Inject
|
||||
private TextUtils textUtils;
|
||||
|
||||
@Override
|
||||
public String process(final Map<String, Object> page,
|
||||
final ThemeInfo theme,
|
||||
final ThemeProvider themeProvider) {
|
||||
|
||||
final String pathToTemplate;
|
||||
if (page.containsKey(PAGE_PARAMETER_TEMPLATE)) {
|
||||
|
||||
final String templateName = (String) page
|
||||
.get(PAGE_PARAMETER_TEMPLATE);
|
||||
|
||||
final Optional<ThemeTemplate> 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();
|
||||
}
|
||||
|
||||
page.put("getContextPath", new GetContextPathMethod());
|
||||
page.put("getSetting", new GetSettingMethod(theme, themeProvider));
|
||||
page.put("localize", new LocalizeMethod(theme, themeProvider));
|
||||
page.put("truncateText", new TruncateTextMethod());
|
||||
|
||||
final Template template;
|
||||
try {
|
||||
template = configurationProvider
|
||||
.getConfiguration(theme)
|
||||
.getTemplate(pathToTemplate);
|
||||
} catch (IOException ex) {
|
||||
throw new UnexpectedErrorException(ex);
|
||||
}
|
||||
|
||||
final StringWriter writer = new StringWriter();
|
||||
try {
|
||||
template.process(page, writer);
|
||||
} catch (TemplateException | IOException ex) {
|
||||
throw new UnexpectedErrorException(ex);
|
||||
}
|
||||
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
private class GetContextPathMethod implements TemplateMethodModelEx {
|
||||
|
||||
@Override
|
||||
public Object exec(final List arguments) throws TemplateModelException {
|
||||
|
||||
return systemInfoUtils.getContextPath();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class GetSettingMethod implements TemplateMethodModelEx {
|
||||
|
||||
private final ThemeInfo fromTheme;
|
||||
private final ThemeProvider themeProvider;
|
||||
|
||||
public GetSettingMethod(final ThemeInfo fromTheme,
|
||||
final ThemeProvider themeProvider) {
|
||||
this.fromTheme = fromTheme;
|
||||
this.themeProvider = themeProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object exec(final List arguments) throws TemplateModelException {
|
||||
|
||||
switch (arguments.size()) {
|
||||
case 2: {
|
||||
final String filePath = ((TemplateScalarModel) arguments
|
||||
.get(0))
|
||||
.getAsString();
|
||||
final String settingName = ((TemplateScalarModel) arguments
|
||||
.get(0))
|
||||
.getAsString();
|
||||
|
||||
return settingsUtils.getSetting(fromTheme,
|
||||
themeProvider,
|
||||
filePath,
|
||||
settingName);
|
||||
}
|
||||
case 3: {
|
||||
final String filePath
|
||||
= ((TemplateScalarModel) arguments.get(0))
|
||||
.getAsString();
|
||||
final String settingName
|
||||
= ((TemplateScalarModel) arguments.get(1))
|
||||
.getAsString();
|
||||
final String defaultValue
|
||||
= ((TemplateScalarModel) arguments.get(2))
|
||||
.getAsString();
|
||||
|
||||
return settingsUtils.getSetting(fromTheme,
|
||||
themeProvider,
|
||||
filePath,
|
||||
settingName,
|
||||
defaultValue);
|
||||
}
|
||||
default:
|
||||
throw new TemplateModelException(
|
||||
"Illegal number of arguments.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class LocalizeMethod implements TemplateMethodModelEx {
|
||||
|
||||
private final ThemeInfo fromTheme;
|
||||
private final ThemeProvider themeProvider;
|
||||
|
||||
public LocalizeMethod(final ThemeInfo fromTheme,
|
||||
final ThemeProvider themeProvider) {
|
||||
this.fromTheme = fromTheme;
|
||||
this.themeProvider = themeProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object exec(final List arguments) throws TemplateModelException {
|
||||
|
||||
if (arguments.isEmpty()) {
|
||||
throw new TemplateModelException("No string to localize.");
|
||||
}
|
||||
|
||||
final String bundle;
|
||||
if (arguments.size() > 1) {
|
||||
bundle = ((TemplateScalarModel) arguments.get(1)).getAsString();
|
||||
} else {
|
||||
bundle = "theme-bundle";
|
||||
}
|
||||
|
||||
final String key = ((TemplateScalarModel) arguments.get(0))
|
||||
.getAsString();
|
||||
|
||||
return l10nUtils.getText(fromTheme, themeProvider, bundle, key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class TruncateTextMethod implements TemplateMethodModelEx {
|
||||
|
||||
@Override
|
||||
public Object exec(final List arguments) throws TemplateModelException {
|
||||
|
||||
if (arguments.size() == 2) {
|
||||
final String text = ((TemplateScalarModel) arguments.get(0))
|
||||
.getAsString();
|
||||
final int length = ((SimpleNumber) arguments.get(1))
|
||||
.getAsNumber()
|
||||
.intValue();
|
||||
|
||||
return textUtils.truncateText(text, length);
|
||||
} else {
|
||||
throw new TemplateModelException("Illegal number of arguments.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright (C) 2018 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.utils;
|
||||
|
||||
import org.libreccm.l10n.GlobalizationHelper;
|
||||
import org.libreccm.theming.ThemeInfo;
|
||||
import org.libreccm.theming.ThemeProvider;
|
||||
import org.libreccm.theming.xslt.XsltThemeProcessor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.PropertyResourceBundle;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
@RequestScoped
|
||||
public class L10NUtils implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 7077097386650257429L;
|
||||
|
||||
@Inject
|
||||
private GlobalizationHelper globalizationHelper;
|
||||
|
||||
public ResourceBundle getBundle(final ThemeInfo fromTheme,
|
||||
final ThemeProvider themeProvider,
|
||||
final String bundleName) {
|
||||
|
||||
return ResourceBundle
|
||||
.getBundle(
|
||||
bundleName,
|
||||
globalizationHelper.getNegotiatedLocale(),
|
||||
new LocalizedResourceBundleControl(fromTheme,
|
||||
themeProvider));
|
||||
}
|
||||
|
||||
public String getText(final ThemeInfo fromTheme,
|
||||
final ThemeProvider themeProvider,
|
||||
final String bundleName,
|
||||
final String key) {
|
||||
|
||||
final ResourceBundle bundle = getBundle(fromTheme,
|
||||
themeProvider,
|
||||
bundleName);
|
||||
|
||||
return bundle.getString(key);
|
||||
}
|
||||
|
||||
public String getText(final ThemeInfo fromTheme,
|
||||
final ThemeProvider themeProvider,
|
||||
final String bundleName,
|
||||
final String key,
|
||||
final String arguments) {
|
||||
|
||||
final ResourceBundle bundle = getBundle(fromTheme,
|
||||
themeProvider,
|
||||
bundleName);
|
||||
|
||||
return MessageFormat.format(bundle.getString(key), arguments);
|
||||
}
|
||||
|
||||
private class LocalizedResourceBundleControl
|
||||
extends ResourceBundle.Control {
|
||||
|
||||
private final ThemeInfo theme;
|
||||
private final ThemeProvider themeProvider;
|
||||
|
||||
public LocalizedResourceBundleControl(
|
||||
final ThemeInfo theme,
|
||||
final ThemeProvider themeProvider) {
|
||||
|
||||
this.theme = theme;
|
||||
this.themeProvider = themeProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getFormats(final String baseName) {
|
||||
Objects.requireNonNull(baseName);
|
||||
|
||||
return Arrays.asList("java.properties");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceBundle newBundle(final String baseName,
|
||||
final Locale locale,
|
||||
final String format,
|
||||
final ClassLoader classLoader,
|
||||
final boolean reload)
|
||||
throws IllegalAccessException,
|
||||
InstantiationException,
|
||||
IOException {
|
||||
|
||||
if ("java.properties".equals(format)) {
|
||||
|
||||
final String bundleName = toBundleName(baseName, locale);
|
||||
|
||||
final Optional<InputStream> inputStream = themeProvider
|
||||
.getThemeFileAsStream(theme.getName(),
|
||||
theme.getVersion(),
|
||||
String.format("%s.properties",
|
||||
bundleName));
|
||||
if (inputStream.isPresent()) {
|
||||
return new PropertyResourceBundle(inputStream.get());
|
||||
} else {
|
||||
return super.newBundle(baseName,
|
||||
locale,
|
||||
format,
|
||||
classLoader,
|
||||
reload);
|
||||
}
|
||||
|
||||
} else {
|
||||
return super.newBundle(baseName,
|
||||
locale,
|
||||
format,
|
||||
classLoader,
|
||||
reload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -57,6 +57,7 @@ import org.libreccm.l10n.GlobalizationHelper;
|
|||
import org.libreccm.theming.ProcessesThemes;
|
||||
import org.libreccm.theming.Themes;
|
||||
import org.libreccm.theming.manifest.ThemeTemplate;
|
||||
import org.libreccm.theming.utils.L10NUtils;
|
||||
import org.libreccm.theming.utils.SettingsUtils;
|
||||
import org.libreccm.theming.utils.SystemInfoUtils;
|
||||
import org.libreccm.theming.utils.TextUtils;
|
||||
|
|
@ -105,7 +106,7 @@ public class XsltThemeProcessor implements ThemeProcessor {
|
|||
.getLogger(XsltThemeProcessor.class);
|
||||
|
||||
@Inject
|
||||
private GlobalizationHelper globalizationHelper;
|
||||
private L10NUtils l10nUtils;
|
||||
|
||||
@Inject
|
||||
private SettingsUtils settingsUtils;
|
||||
|
|
@ -182,15 +183,6 @@ public class XsltThemeProcessor implements ThemeProcessor {
|
|||
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 InputStream xslFileInputStream = themes
|
||||
.getFileFromTheme(theme,
|
||||
pathToTemplate)
|
||||
|
|
@ -231,9 +223,6 @@ public class XsltThemeProcessor implements ThemeProcessor {
|
|||
final Transformer transformer;
|
||||
try {
|
||||
transformer = transformerFactory.newTransformer(xslFileStreamSource);
|
||||
// transformer.setURIResolver(new CcmUriResolver(theme.getName(),
|
||||
// theme.getVersion(),
|
||||
// themeProvider));
|
||||
transformer.setErrorListener(new ErrorListener() {
|
||||
|
||||
@Override
|
||||
|
|
@ -492,15 +481,11 @@ public class XsltThemeProcessor implements ThemeProcessor {
|
|||
key,
|
||||
bundle);
|
||||
|
||||
final ResourceBundle resourceBundle = ResourceBundle
|
||||
.getBundle(
|
||||
bundle,
|
||||
globalizationHelper.getNegotiatedLocale(),
|
||||
new LocalizedResourceBundleControl(theme,
|
||||
themeProvider));
|
||||
|
||||
return StringValue
|
||||
.makeStringValue(resourceBundle.getString(key));
|
||||
.makeStringValue(l10nUtils.getText(theme,
|
||||
themeProvider,
|
||||
bundle,
|
||||
key));
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -508,67 +493,6 @@ public class XsltThemeProcessor implements ThemeProcessor {
|
|||
|
||||
}
|
||||
|
||||
private class LocalizedResourceBundleControl
|
||||
extends ResourceBundle.Control {
|
||||
|
||||
private final ThemeInfo theme;
|
||||
private final ThemeProvider themeProvider;
|
||||
|
||||
public LocalizedResourceBundleControl(
|
||||
final ThemeInfo theme,
|
||||
final ThemeProvider themeProvider) {
|
||||
|
||||
this.theme = theme;
|
||||
this.themeProvider = themeProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getFormats(final String baseName) {
|
||||
Objects.requireNonNull(baseName);
|
||||
|
||||
return Arrays.asList("java.properties");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceBundle newBundle(final String baseName,
|
||||
final Locale locale,
|
||||
final String format,
|
||||
final ClassLoader classLoader,
|
||||
final boolean reload)
|
||||
throws IllegalAccessException,
|
||||
InstantiationException,
|
||||
IOException {
|
||||
|
||||
if ("java.properties".equals(format)) {
|
||||
|
||||
final String bundleName = toBundleName(baseName, locale);
|
||||
|
||||
final Optional<InputStream> inputStream = themeProvider
|
||||
.getThemeFileAsStream(theme.getName(),
|
||||
theme.getVersion(),
|
||||
String.format("%s.properties",
|
||||
bundleName));
|
||||
if (inputStream.isPresent()) {
|
||||
return new PropertyResourceBundle(inputStream.get());
|
||||
} else {
|
||||
return super.newBundle(baseName,
|
||||
locale,
|
||||
format,
|
||||
classLoader,
|
||||
reload);
|
||||
}
|
||||
|
||||
} else {
|
||||
return super.newBundle(baseName,
|
||||
locale,
|
||||
format,
|
||||
classLoader,
|
||||
reload);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class TruncateTextFunctionDefinition
|
||||
extends ExtensionFunctionDefinition {
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Category page</title>
|
||||
<link rel="stylesheet" href="${getContextPath()}/theming/ccm/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<ul class="news">
|
||||
|
||||
<#list newsList.items as item>
|
||||
<li>
|
||||
<span>
|
||||
<!--<pre>
|
||||
<xsl:value-of select="count(./attachments[name='.images']/attachments[1])" />
|
||||
</pre>-->
|
||||
|
||||
<#list item.attachments as attachmentList>
|
||||
<#if attachmentList.name = ".images">
|
||||
<img src="${getContextPath()}/content-sections/info/images/uuid-${attachmentList.attachments[0].asset.uuid}"
|
||||
width="354"
|
||||
height="286"
|
||||
alt="" />
|
||||
</#if>
|
||||
</#list>
|
||||
</span>
|
||||
<span>
|
||||
"${item.title}"
|
||||
</span>
|
||||
</li>
|
||||
</#list>
|
||||
</ul>
|
||||
<div class="boxes">
|
||||
<#list articles.items as item>
|
||||
<div>
|
||||
<h1>
|
||||
${item.title}"
|
||||
</h1>
|
||||
<p>
|
||||
<#list item.attachments as attachmentList>
|
||||
<#if attachmentList.name = ".images">
|
||||
<img src="${getContextPath()}/content-sections/info/images/uuid-${attachmentList.attachments[0].asset.uuid}"
|
||||
alt="" />
|
||||
</#if>
|
||||
</#list>
|
||||
${item.description}"
|
||||
</p>
|
||||
</div>
|
||||
</#list>
|
||||
</div>
|
||||
|
||||
<h2>Example of Theme Utils</h2>
|
||||
<dl>
|
||||
<dt>
|
||||
<code>getContextPath</code>
|
||||
</dt>
|
||||
<dd>
|
||||
<code>
|
||||
${getContextPath()}
|
||||
</code>
|
||||
</dd>
|
||||
<dt>
|
||||
<code>getSetting</code>
|
||||
</dt>
|
||||
<dd>
|
||||
<code>
|
||||
${getSetting("settings.properties", "example.setting", "n/a")}
|
||||
</code>
|
||||
</dd>
|
||||
<dt>
|
||||
<code>truncateText</code>
|
||||
</dt>
|
||||
<dd>
|
||||
<code>
|
||||
${truncateText("0123456789 123456789 123456789", 20)}
|
||||
</code>
|
||||
</dd>
|
||||
<dt>
|
||||
<code>localized('label.critical')</code>
|
||||
</dt>
|
||||
<dd>
|
||||
${localize("label.critical", "texts/labels")}
|
||||
</dd>
|
||||
<dt>
|
||||
<code>localized('label.error')</code>
|
||||
</dt>
|
||||
<dd>
|
||||
${localize("label.error", "texts/labels")}
|
||||
</dd>
|
||||
<dt>
|
||||
<code>localized('label.ok')</code>
|
||||
</dt>
|
||||
<dd>
|
||||
${localize("label.ok", "texts/labels")}
|
||||
</dd>
|
||||
<dt>
|
||||
<code>localized('label.warning')</code>
|
||||
</dt>
|
||||
<dd>
|
||||
${localize("label.warning", "texts/labels")}
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
</main>
|
||||
<#include "footer.html.ftl">
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<footer>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/impressum">
|
||||
<!--Impressum-->
|
||||
${localize("footer.impressum")}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/privacy">
|
||||
<!--Privacy-->
|
||||
${localize("footer.privacy")}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<code>imported</code>
|
||||
</li>
|
||||
</ul>
|
||||
</footer>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
example.setting=Properties from the Freemarker theme.
|
||||
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
* {
|
||||
box-sizing: border-box;
|
||||
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul.news {
|
||||
|
||||
background-color: #000;
|
||||
|
||||
margin: 0 auto 3em auto;
|
||||
|
||||
width: 100vw;
|
||||
|
||||
padding: 3em;
|
||||
}
|
||||
|
||||
ul.news li {
|
||||
|
||||
display: flex;
|
||||
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
max-width: 50em;
|
||||
|
||||
}
|
||||
|
||||
ul.news li img {
|
||||
max-height: 20em;
|
||||
}
|
||||
|
||||
ul.news li span {
|
||||
|
||||
color: #fff;
|
||||
|
||||
flex: 1;
|
||||
|
||||
font-size: 2rem;
|
||||
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
main div.boxes {
|
||||
display: flex;
|
||||
|
||||
margin: 3em auto 3em auto;
|
||||
|
||||
max-width: 80em;
|
||||
}
|
||||
|
||||
main div.boxes div {
|
||||
position: relative;
|
||||
|
||||
flex: 1;
|
||||
|
||||
margin: 0 3em;
|
||||
}
|
||||
|
||||
main div.boxes div p img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
main div.boxes div a {
|
||||
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
footer ul {
|
||||
|
||||
list-style: none;
|
||||
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
max-width: 80em;
|
||||
|
||||
padding-top: 4em;
|
||||
padding-bottom: 4em;
|
||||
}
|
||||
|
||||
footer ul li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
footer ul li:not(:first-child) {
|
||||
margin-left: 4em;
|
||||
}
|
||||
|
||||
footer ul li a:link {
|
||||
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
footer ul li a:focus, footer ul li a:hover {
|
||||
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
label.critical=Critical
|
||||
label.error=Error
|
||||
label.ok=OK
|
||||
label.warning=Warning
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
footer.impressum=Impressum
|
||||
footer.privacy=Privacy
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "ccm-freemarker",
|
||||
"type": "freemarker",
|
||||
|
||||
"default-template": "category-page.html.ftl"
|
||||
}
|
||||
Loading…
Reference in New Issue