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
Former-commit-id: 3ceec91105
pull/2/head
parent
e15e952b21
commit
611b5bd27f
|
|
@ -204,6 +204,11 @@
|
||||||
<artifactId>javax.json</artifactId>
|
<artifactId>javax.json</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.freemarker</groupId>
|
||||||
|
<artifactId>freemarker</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.sf.saxon</groupId>
|
<groupId>net.sf.saxon</groupId>
|
||||||
<artifactId>Saxon-HE</artifactId>
|
<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.ProcessesThemes;
|
||||||
import org.libreccm.theming.Themes;
|
import org.libreccm.theming.Themes;
|
||||||
import org.libreccm.theming.manifest.ThemeTemplate;
|
import org.libreccm.theming.manifest.ThemeTemplate;
|
||||||
|
import org.libreccm.theming.utils.L10NUtils;
|
||||||
import org.libreccm.theming.utils.SettingsUtils;
|
import org.libreccm.theming.utils.SettingsUtils;
|
||||||
import org.libreccm.theming.utils.SystemInfoUtils;
|
import org.libreccm.theming.utils.SystemInfoUtils;
|
||||||
import org.libreccm.theming.utils.TextUtils;
|
import org.libreccm.theming.utils.TextUtils;
|
||||||
|
|
@ -105,7 +106,7 @@ public class XsltThemeProcessor implements ThemeProcessor {
|
||||||
.getLogger(XsltThemeProcessor.class);
|
.getLogger(XsltThemeProcessor.class);
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private GlobalizationHelper globalizationHelper;
|
private L10NUtils l10nUtils;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private SettingsUtils settingsUtils;
|
private SettingsUtils settingsUtils;
|
||||||
|
|
@ -182,15 +183,6 @@ public class XsltThemeProcessor implements ThemeProcessor {
|
||||||
pathToTemplate = theme.getManifest().getDefaultTemplate();
|
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
|
final InputStream xslFileInputStream = themes
|
||||||
.getFileFromTheme(theme,
|
.getFileFromTheme(theme,
|
||||||
pathToTemplate)
|
pathToTemplate)
|
||||||
|
|
@ -231,9 +223,6 @@ public class XsltThemeProcessor implements ThemeProcessor {
|
||||||
final Transformer transformer;
|
final Transformer transformer;
|
||||||
try {
|
try {
|
||||||
transformer = transformerFactory.newTransformer(xslFileStreamSource);
|
transformer = transformerFactory.newTransformer(xslFileStreamSource);
|
||||||
// transformer.setURIResolver(new CcmUriResolver(theme.getName(),
|
|
||||||
// theme.getVersion(),
|
|
||||||
// themeProvider));
|
|
||||||
transformer.setErrorListener(new ErrorListener() {
|
transformer.setErrorListener(new ErrorListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -492,15 +481,11 @@ public class XsltThemeProcessor implements ThemeProcessor {
|
||||||
key,
|
key,
|
||||||
bundle);
|
bundle);
|
||||||
|
|
||||||
final ResourceBundle resourceBundle = ResourceBundle
|
|
||||||
.getBundle(
|
|
||||||
bundle,
|
|
||||||
globalizationHelper.getNegotiatedLocale(),
|
|
||||||
new LocalizedResourceBundleControl(theme,
|
|
||||||
themeProvider));
|
|
||||||
|
|
||||||
return StringValue
|
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
|
private class TruncateTextFunctionDefinition
|
||||||
extends ExtensionFunctionDefinition {
|
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"
|
||||||
|
}
|
||||||
6
pom.xml
6
pom.xml
|
|
@ -580,6 +580,12 @@
|
||||||
<version>1.1.2</version>
|
<version>1.1.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.freemarker</groupId>
|
||||||
|
<artifactId>freemarker</artifactId>
|
||||||
|
<version>2.3.27-incubating</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.sf.saxon</groupId>
|
<groupId>net.sf.saxon</groupId>
|
||||||
<artifactId>Saxon-HE</artifactId>
|
<artifactId>Saxon-HE</artifactId>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue