diff --git a/ccm-bundle-devel-wildfly/src/main/resources/log4j2.xml b/ccm-bundle-devel-wildfly/src/main/resources/log4j2.xml index ff0533652..427cbcdd0 100644 --- a/ccm-bundle-devel-wildfly/src/main/resources/log4j2.xml +++ b/ccm-bundle-devel-wildfly/src/main/resources/log4j2.xml @@ -80,10 +80,7 @@ - - - Jens Pelzetter - */ -class CcmUriResolver implements URIResolver { - - private static final Logger LOGGER = LogManager - .getLogger(CcmUriResolver.class); - - private final String theme; - private final ThemeVersion version; - private final ThemeProvider themeProvider; - - protected CcmUriResolver(final String theme, - final ThemeVersion version, - final ThemeProvider themeProvider) { - - Objects.requireNonNull(theme); - Objects.requireNonNull(version); - Objects.requireNonNull(themeProvider); - - LOGGER.debug("Creating new instance of {} with these parameters:", - getClass().getName()); - LOGGER.debug("\ttheme = {}", theme); - LOGGER.debug("\tversion = {}", Objects.toString(version)); - LOGGER.debug("\tthemeProvider = {}", - themeProvider.getClass().getName()); - - this.theme = theme; - this.version = version; - this.themeProvider = themeProvider; - } - - @Override - public Source resolve(final String href, - final String base) throws TransformerException { - - LOGGER.debug("Resolving href = \"{}\" using base = \"{}\"...", - href, - base); - - final String path; - if (base == null) { - path = href; - } else { - path = String.join("/", base, href); - } - LOGGER.debug("Using path \"{}\"...", path); - - final InputStream inputStream = themeProvider - .getThemeFileAsStream(theme, version, path) - .orElseThrow(() -> new TransformerException(String - .format("Failed to resolve URI with href = \"%s\" and base = \"%s\" " - + "for theme \"%s\" (version = \"%s\" using " - + "ThemeProvider \"%s\".", - href, - base, - theme, - version, - themeProvider.getClass().getName()))); - - LOGGER.debug("Resolved href = \"{}\" with base \"{}\" successfully.", - href, - path); - return new StreamSource(inputStream); - } - -} - diff --git a/ccm-core/src/main/java/org/libreccm/theming/xslt/XsltThemeProcessor.java b/ccm-core/src/main/java/org/libreccm/theming/xslt/XsltThemeProcessor.java deleted file mode 100644 index fdafe86e0..000000000 --- a/ccm-core/src/main/java/org/libreccm/theming/xslt/XsltThemeProcessor.java +++ /dev/null @@ -1,534 +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.xslt; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import org.libreccm.core.UnexpectedErrorException; -import org.libreccm.theming.ThemeInfo; -import org.libreccm.theming.ThemeProcessor; -import org.libreccm.theming.ThemeProvider; -import org.w3c.dom.Document; -import org.xml.sax.SAXException; - -import java.io.IOException; -import java.util.Map; - -import javax.enterprise.context.RequestScoped; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - -import static org.libreccm.theming.ThemeConstants.*; - -import com.fasterxml.jackson.databind.SerializationFeature; -import net.sf.saxon.Configuration; -import net.sf.saxon.TransformerFactoryImpl; -import net.sf.saxon.expr.XPathContext; -import net.sf.saxon.lib.ExtensionFunctionCall; -import net.sf.saxon.lib.ExtensionFunctionDefinition; -import net.sf.saxon.om.Item; -import net.sf.saxon.om.Sequence; -import net.sf.saxon.om.StructuredQName; -import net.sf.saxon.trans.XPathException; -import net.sf.saxon.value.SequenceType; -import net.sf.saxon.value.StringValue; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -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; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.nio.charset.StandardCharsets; -import java.util.Optional; -import java.util.PropertyResourceBundle; -import java.util.ResourceBundle; - -import javax.inject.Inject; -import javax.xml.transform.ErrorListener; -import javax.xml.transform.Result; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; - -/** - * A {@link ThemeProcessor} implementation for XSLT based themes. - * - * @author Jens Pelzetter - */ -@ProcessesThemes("xsl") -@RequestScoped -public class XsltThemeProcessor implements ThemeProcessor { - - private static final long serialVersionUID = -3883625727845105417L; - - private static final String FUNCTION_XMLNS = "http://xmlns.libreccm.org"; - private static final String FUNCTION_XMLNS_PREFIX = "ccm"; - - private static final Logger LOGGER = LogManager - .getLogger(XsltThemeProcessor.class); - - @Inject - private L10NUtils l10nUtils; - - @Inject - private SettingsUtils settingsUtils; - - @Inject - private SystemInfoUtils systemInfoUtils; - - @Inject - private TextUtils textUtils; - - @Inject - private Themes themes; - - @Override - public String process(final Map page, - final ThemeInfo theme, - final ThemeProvider themeProvider) { - - //Convert page to XML - final JacksonXmlModule xmlModule = new JacksonXmlModule(); - final ObjectMapper mapper = new XmlMapper(xmlModule); - mapper.enable(SerializationFeature.INDENT_OUTPUT); - - final String pageAsXml; - try { - pageAsXml = mapper - .writer() - .withRootName("page") - .writeValueAsString(page); - } catch (JsonProcessingException ex) { - throw new UnexpectedErrorException(ex); - } - - final DocumentBuilderFactory documentBuilderFactory - = DocumentBuilderFactory.newInstance(); - final DocumentBuilder documentBuilder; - try { - documentBuilder = documentBuilderFactory.newDocumentBuilder(); - } catch (ParserConfigurationException ex) { - throw new UnexpectedErrorException(ex); - } - - final Document document; - try { - final InputStream xmlBytesStream = new ByteArrayInputStream( - pageAsXml.getBytes(StandardCharsets.UTF_8)); - document = documentBuilder.parse(xmlBytesStream); - } catch (SAXException | IOException ex) { - throw new UnexpectedErrorException(ex); - } - - final String pathToTemplate; - if (page.containsKey(PAGE_PARAMETER_TEMPLATE)) { - - final String templateName = (String) page - .get(PAGE_PARAMETER_TEMPLATE); - - final Optional 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(); - } - - final InputStream xslFileInputStream = themes - .getFileFromTheme(theme, - pathToTemplate) - .orElseThrow(() -> new UnexpectedErrorException(String - .format("Failed to open XSL file \"%s\" from theme \"%s\" for " - + "reading.", - pathToTemplate, - theme.getName()))); - - final Reader reader; - try { - reader = new InputStreamReader(xslFileInputStream, "UTF-8"); - } catch (UnsupportedEncodingException ex) { - throw new UnexpectedErrorException(ex); - } - - final StreamSource xslFileStreamSource = new StreamSource(reader); - final TransformerFactory transformerFactory = TransformerFactory - .newInstance(TransformerFactoryImpl.class.getName(), - getClass().getClassLoader()); - transformerFactory.setURIResolver(new CcmUriResolver(theme.getName(), - theme.getVersion(), - themeProvider)); - final TransformerFactoryImpl transformerFactoryImpl - = (TransformerFactoryImpl) transformerFactory; - final Configuration configuration = transformerFactoryImpl - .getConfiguration(); - configuration - .registerExtensionFunction(new GetContextPathFunctionDefinition()); - configuration - .registerExtensionFunction( - new GetSettingFunctionDefinition(theme, themeProvider)); - configuration - .registerExtensionFunction( - new LocalizeFunctionDefinition(theme, themeProvider)); - configuration - .registerExtensionFunction(new TruncateTextFunctionDefinition()); - final Transformer transformer; - try { - transformer = transformerFactory.newTransformer(xslFileStreamSource); - transformer.setErrorListener(new ErrorListener() { - - @Override - public void warning(final TransformerException te) - throws TransformerException { - - LOGGER.warn("A WARNING was reported by the " - + "XSLT Transformer:", - te); - } - - @Override - public void error(final TransformerException te) - throws TransformerException { - - LOGGER.warn("An ERROR was reported by the " - + "XSLT Transformer:", - te); - } - - @Override - public void fatalError(final TransformerException te) - throws TransformerException { - - LOGGER.warn("An FATAL ERROR was reported by the " - + "XSLT Transformer:", - te); - } - - }); - } catch (TransformerConfigurationException ex) { - throw new UnexpectedErrorException(ex); - } - - final StringWriter resultWriter = new StringWriter(); - final Result result = new StreamResult(resultWriter); - try { - transformer.transform(new DOMSource(document), result); - } catch (TransformerException ex) { - throw new UnexpectedErrorException(ex); - } - - return resultWriter.toString(); - } - - private class GetContextPathFunctionDefinition - extends ExtensionFunctionDefinition { - - @Override - public StructuredQName getFunctionQName() { - return new StructuredQName(FUNCTION_XMLNS_PREFIX, - FUNCTION_XMLNS, - "getContextPath"); - } - - @Override - public SequenceType[] getArgumentTypes() { - return new SequenceType[]{}; - } - - @Override - public SequenceType getResultType(final SequenceType[] arguments) { - return SequenceType.SINGLE_STRING; - } - - @Override - public ExtensionFunctionCall makeCallExpression() { - return new ExtensionFunctionCall() { - - @Override - public Sequence call(final XPathContext xPathContext, - final Sequence[] arguments) - throws XPathException { - - return StringValue - .makeStringValue(systemInfoUtils.getContextPath()); - } - - }; - } - - } - - private class GetSettingFunctionDefinition - extends ExtensionFunctionDefinition { - - private final ThemeInfo theme; - private final ThemeProvider themeProvider; - - public GetSettingFunctionDefinition(final ThemeInfo themeInfo, - final ThemeProvider themeProvider) { - this.theme = themeInfo; - this.themeProvider = themeProvider; - } - - @Override - public StructuredQName getFunctionQName() { - return new StructuredQName(FUNCTION_XMLNS_PREFIX, - FUNCTION_XMLNS, - "getSetting"); - } - - @Override - public SequenceType[] getArgumentTypes() { - return new SequenceType[]{SequenceType.SINGLE_STRING, - SequenceType.SINGLE_STRING, - SequenceType.SINGLE_STRING}; - } - - @Override - public int getMinimumNumberOfArguments() { - return 2; - } - - @Override - public int getMaximumNumberOfArguments() { - return 3; - } - - @Override - public SequenceType getResultType(final SequenceType[] arguments) { - return SequenceType.SINGLE_STRING; - } - - @Override - public ExtensionFunctionCall makeCallExpression() { - - return new ExtensionFunctionCall() { - - @Override - public Sequence call(final XPathContext xPathContext, - final Sequence[] arguments) - throws XPathException { - - final String result; - final String filePath = ((Item) arguments[0]) - .getStringValue(); - final String settingName = ((Item) arguments[1]) - .getStringValue(); - switch (arguments.length) { - case 2: - result = settingsUtils.getSetting(theme, - themeProvider, - filePath, - settingName); - break; - case 3: - final String defaultValue = ((Item) arguments[2]) - .getStringValue(); - result = settingsUtils.getSetting(theme, - themeProvider, - filePath, - settingName, - defaultValue); - break; - default: - throw new UnexpectedErrorException( - "Illegal number of arguments."); - } - - return StringValue.makeStringValue(result); - } - - }; - } - - } - - /** - * Definition for XSL function for localising texts in the theme. Allows use - * of {@link ResourceBundle}s instead of a custom XML format which was used - * in legacy versions. - * - * The XSL function {@code ccm:localize} expects one mandatory parameter, - * the {@code key} of the text to localise. The optional second parameter - * {@code bundle} identifies the {@link ResourceBundle} to use. The - * {@code bundle} parameter is passed to - * {@link ResourceBundle#getBundle(java.lang.String)}. All bundle paths are - * resolved relative to the root of the theme using {@link ThemeProvider}. - * If the {@code bundle} parameter is omitted the function will look for - * {@link PropertyResourceBundle} named {@code theme-bundle.properties} in - * the root of the theme. Examples: - * - * {@code } - * - * In this case this function will load the file - * {@code theme-bundle.properties} from the root of the theme and use it to - * create an instance of {@link PropertyResourceBundle}. If this is - * successful the key {@code footer.privacy} is lookup in the resource - * bundle. - * - * {@code } - * - * In this case the function tries find a property file called - * {@code footer.properties} in the texts directory in the theme. - * - * Of course the function, or better {@link ResourceBundle} will also take - * into account the current locale, therefore in both examples the first - * file name will be {@code footer_$locale.properties} where {@code $locale} - * is the the locale returned by - * {@link GlobalizationHelper#getNegotiatedLocale()}. - */ - private class LocalizeFunctionDefinition - extends ExtensionFunctionDefinition { - - private final ThemeInfo theme; - private final ThemeProvider themeProvider; - - public LocalizeFunctionDefinition(final ThemeInfo theme, - final ThemeProvider themeProvider) { - super(); - this.theme = theme; - this.themeProvider = themeProvider; - } - - @Override - public StructuredQName getFunctionQName() { - return new StructuredQName(FUNCTION_XMLNS_PREFIX, - FUNCTION_XMLNS, - "localize"); - } - - @Override - public SequenceType[] getArgumentTypes() { - return new SequenceType[]{SequenceType.SINGLE_STRING}; - } - - @Override - public int getMaximumNumberOfArguments() { - return 2; - } - - @Override - public SequenceType getResultType(final SequenceType[] arguments) { - return SequenceType.SINGLE_STRING; - } - - @Override - public ExtensionFunctionCall makeCallExpression() { - return new ExtensionFunctionCall() { - - @Override - public Sequence call(final XPathContext xPathContext, - final Sequence[] arguments) - throws XPathException { - - final String bundle; - if (arguments.length > 1) { - bundle = ((Item) arguments[1]).getStringValue(); - } else { - bundle = "theme-bundle"; - } - final String key = ((Item) arguments[0]).getStringValue(); - - LOGGER.debug("Localizing key \"{}\" from bundle \"{}\"...", - key, - bundle); - - return StringValue - .makeStringValue(l10nUtils.getText(theme, - themeProvider, - bundle, - key)); - } - - }; - } - - } - - private class TruncateTextFunctionDefinition - extends ExtensionFunctionDefinition { - - @Override - public StructuredQName getFunctionQName() { - return new StructuredQName(FUNCTION_XMLNS_PREFIX, - FUNCTION_XMLNS, - "truncateText"); - } - - @Override - public SequenceType[] getArgumentTypes() { - return new SequenceType[]{SequenceType.SINGLE_STRING, - SequenceType.SINGLE_INTEGER}; - } - - @Override - public SequenceType getResultType(final SequenceType[] arguments) { - return SequenceType.SINGLE_STRING; - } - - @Override - public ExtensionFunctionCall makeCallExpression() { - return new ExtensionFunctionCall() { - - @Override - public Sequence call(final XPathContext xPathContext, - final Sequence[] arguments) - throws XPathException { - - final String text = ((Item) arguments[0]).getStringValue(); - final int length = Integer - .parseInt(((Item) arguments[1]).getStringValue()); - return StringValue - .makeStringValue(textUtils.truncateText(text, length)); - } - - }; - } - - } - -} diff --git a/ccm-core/src/main/java/org/libreccm/theming/xslt/package-info.java b/ccm-core/src/main/java/org/libreccm/theming/xslt/package-info.java deleted file mode 100644 index 43076b904..000000000 --- a/ccm-core/src/main/java/org/libreccm/theming/xslt/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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 - */ -/** - * Support of legacy XSL based themes. - */ -package org.libreccm.theming.xslt;