diff --git a/ccm-themedirector/lib/freemarker.jar b/ccm-themedirector/lib/freemarker.jar new file mode 100644 index 000000000..75edf6c03 Binary files /dev/null and b/ccm-themedirector/lib/freemarker.jar differ diff --git a/ccm-core/src/org/libreccm/l10n/L10NConstants.java.off b/ccm-themedirector/src/org/libreccm/l10n/L10NConstants.java similarity index 100% rename from ccm-core/src/org/libreccm/l10n/L10NConstants.java.off rename to ccm-themedirector/src/org/libreccm/l10n/L10NConstants.java diff --git a/ccm-core/src/org/libreccm/l10n/LocalizedString.java.off b/ccm-themedirector/src/org/libreccm/l10n/LocalizedString.java similarity index 65% rename from ccm-core/src/org/libreccm/l10n/LocalizedString.java.off rename to ccm-themedirector/src/org/libreccm/l10n/LocalizedString.java index d0b765fa2..fb21d3732 100644 --- a/ccm-core/src/org/libreccm/l10n/LocalizedString.java.off +++ b/ccm-themedirector/src/org/libreccm/l10n/LocalizedString.java @@ -15,38 +15,48 @@ * 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.l10n; + */package org.libreccm.l10n; import com.fasterxml.jackson.annotation.JsonIgnore; +import org.libreccm.l10n.jaxb.LocalizedStringValuesAdapter; +import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; -/** - * A helper class for localisable string properties. This class is declared as - * embeddable, so that it can be used in every other entity. The localised - * values are stored in a {@link Map}. This class is not designed to be - * overwritten. But because it is an entity class we can't make the class final. - * - * @author Jens Pelzetter + */ +@XmlRootElement(name = "localized-string", + namespace = L10NConstants.L10N_XML_NS) +@XmlAccessorType(XmlAccessType.FIELD) +public class LocalizedString implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * The localised values of the string. + */ + @XmlElement(name = "values", namespace = L10NConstants.L10N_XML_NS) + @XmlJavaTypeAdapter(LocalizedStringValuesAdapter.class) private Map values; /** * Constructor. Only creates the initial, empty map for new instances. */ public LocalizedString() { - this.values = new HashMap<>(); + values = new HashMap<>(); } /** @@ -70,7 +80,11 @@ public class LocalizedString { * @param values The new map of values. */ protected void setValues(final Map values) { - this.values = values; + if (values == null) { + this.values = new HashMap<>(); + } else { + this.values = new HashMap<>(values); + } } /** @@ -80,7 +94,6 @@ public class LocalizedString { * application is running on. In most cases this is not what you * want. Use {@link #getValue(java.util.Locale)} instead. */ - @JsonIgnore public String getValue() { return getValue(Locale.getDefault()); } @@ -93,7 +106,6 @@ public class LocalizedString { * @return The localised for the {@code locale} or {@code null} if there is * no value for the provided locale. */ - @JsonIgnore public String getValue(final Locale locale) { return values.get(locale); } @@ -140,4 +152,42 @@ public class LocalizedString { public Set getAvailableLocales() { return values.keySet(); } + + @Override + public int hashCode() { + int hash = 7; + hash = 41 * hash + Objects.hashCode(this.values); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (!(obj instanceof LocalizedString)) { + return false; + } + final LocalizedString other = (LocalizedString) obj; + if (!other.canEqual(this)) { + return false; + } + + return Objects.equals(values, other.getValues()); + } + + public boolean canEqual(final Object obj) { + return obj instanceof LocalizedString; + } + + @Override + public String toString() { + return String.format( + "%s{ " + + "%s" + + " }", + super.toString(), + Objects.toString(values)); + } + } diff --git a/ccm-themedirector/src/org/libreccm/l10n/jaxb/LocalizedStringValue.java b/ccm-themedirector/src/org/libreccm/l10n/jaxb/LocalizedStringValue.java new file mode 100644 index 000000000..260a45dd6 --- /dev/null +++ b/ccm-themedirector/src/org/libreccm/l10n/jaxb/LocalizedStringValue.java @@ -0,0 +1,112 @@ +/* + * 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.l10n.jaxb; + +import static org.libreccm.l10n.L10NConstants.*; + +import java.io.Serializable; +import java.util.Objects; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlValue; + +/** + * + * @author Jens Pelzetter + */ +@XmlAccessorType(XmlAccessType.FIELD) +public class LocalizedStringValue implements Serializable { + + private static final long serialVersionUID = 8435485565736441379L; + + @XmlAttribute(name = "lang", namespace = L10N_XML_NS) + private String locale; + + @XmlValue + private String value; + + public String getLocale() { + return locale; + } + + public void setLocale(final String locale) { + this.locale = locale; + } + + public String getValue() { + return value; + } + + public void setValue(final String value) { + this.value = value; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 97 * hash + Objects.hashCode(locale); + hash = 97 * hash + Objects.hashCode(value); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof LocalizedStringValue)) { + return false; + } + final LocalizedStringValue other = (LocalizedStringValue) obj; + if (!other.canEqual(this)) { + return false; + } + if (!Objects.equals(locale, other.getLocale())) { + return false; + } + return Objects.equals(value, other.getValue()); + } + + public boolean canEqual(final Object obj) { + return obj instanceof LocalizedStringValue; + } + + @Override + public final String toString() { + return toString(""); + } + + public String toString(final String data) { + + return String.format("%s{ " + + "locale = %s, " + + "value = \"%s\"%s" + + " }", + super.toString(), + Objects.toString(locale), + value, + data); + } + +} diff --git a/ccm-themedirector/src/org/libreccm/l10n/jaxb/LocalizedStringValues.java b/ccm-themedirector/src/org/libreccm/l10n/jaxb/LocalizedStringValues.java new file mode 100644 index 000000000..ee74ccc20 --- /dev/null +++ b/ccm-themedirector/src/org/libreccm/l10n/jaxb/LocalizedStringValues.java @@ -0,0 +1,98 @@ +/* + * 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.l10n.jaxb; + +import static org.libreccm.l10n.L10NConstants.*; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; + +/** + * + * @author Jens Pelzetter + */ +@XmlAccessorType(XmlAccessType.FIELD) +public class LocalizedStringValues implements Serializable { + + private static final long serialVersionUID = 1L; + + @JacksonXmlElementWrapper(useWrapping = false) + @XmlElement(name = "value", namespace = L10N_XML_NS) + private List values; + + public List getValues() { + return new ArrayList<>(values); + } + + public void setValues(final List values) { + this.values = new ArrayList<>(values); + } + + @Override + public int hashCode() { + int hash = 3; + hash = 41 * hash + Objects.hashCode(values); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof LocalizedStringValues)) { + return false; + } + final LocalizedStringValues other = (LocalizedStringValues) obj; + if (!other.canEqual(this)) { + return false; + } + return Objects.equals(values, other.getValues()); + } + + public boolean canEqual(final Object obj) { + return obj instanceof LocalizedStringValues; + } + + @Override + public final String toString() { + return toString(""); + } + + public String toString(final String data) { + return String.format("%s{ " + + "values = %s%s" + + " }", + super.toString(), + Objects.toString(values), + data); + } + +} diff --git a/ccm-themedirector/src/org/libreccm/l10n/jaxb/LocalizedStringValuesAdapter.java b/ccm-themedirector/src/org/libreccm/l10n/jaxb/LocalizedStringValuesAdapter.java new file mode 100644 index 000000000..bb472b1af --- /dev/null +++ b/ccm-themedirector/src/org/libreccm/l10n/jaxb/LocalizedStringValuesAdapter.java @@ -0,0 +1,77 @@ +/* + * 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.l10n.jaxb; + +import org.libreccm.l10n.LocalizedString; + +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.xml.bind.annotation.adapters.XmlAdapter; + +/** + * JAXB adapter for {@link LocalizedString#values} to produce a more compact XML + * for the values map. + * + * @author Jens Pelzetter + */ +public class LocalizedStringValuesAdapter + extends XmlAdapter> { + + @Override + public Map unmarshal(final LocalizedStringValues values) + throws Exception { + + return values + .getValues() + .stream() + .collect(Collectors.toMap(value -> new Locale(value.getLocale()), + value -> value.getValue())); + + } + + @Override + public LocalizedStringValues marshal(final Map values) + throws Exception { + + final List list = values + .entrySet() + .stream() + .map(this::generateValue) + .collect(Collectors.toList()); + + final LocalizedStringValues result = new LocalizedStringValues(); + result.setValues(list); + + return result; + } + + private LocalizedStringValue generateValue( + final Map.Entry entry) { + + final LocalizedStringValue value = new LocalizedStringValue(); + value.setLocale(entry.getKey().toString()); + value.setValue(entry.getValue()); + + return value; + } + +} diff --git a/ccm-themedirector/src/org/libreccm/theming/ApplicationTemplate.java b/ccm-themedirector/src/org/libreccm/theming/ApplicationTemplate.java new file mode 100644 index 000000000..5df031e09 --- /dev/null +++ b/ccm-themedirector/src/org/libreccm/theming/ApplicationTemplate.java @@ -0,0 +1,117 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.libreccm.theming; + +import static org.libreccm.theming.ThemeConstants.*; + +import java.util.Objects; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * + * @author Jens Pelzetter + */ +@XmlRootElement(name = "application-template", namespace = THEMES_XML_NS) +@XmlAccessorType(XmlAccessType.FIELD) +public class ApplicationTemplate { + + @XmlElement(name = "application-name", namespace = THEMES_XML_NS) + private String applicationName; + + @XmlElement(name = "application-class", namespace = THEMES_XML_NS) + private String applicationClass; + + @XmlElement(name = "template", namespace = THEMES_XML_NS) + private String template; + + public String getApplicationName() { + return applicationName; + } + + public void setApplicationName(final String applicationName) { + this.applicationName = applicationName; + } + + public String getApplicationClass() { + return applicationClass; + } + + public void setApplicationClass(final String applicationClass) { + this.applicationClass = applicationClass; + } + + public String getTemplate() { + return template; + } + + public void setTemplate(final String template) { + this.template = template; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 79 * hash + Objects.hashCode(applicationName); + hash = 79 * hash + Objects.hashCode(applicationClass); + hash = 79 * hash + Objects.hashCode(template); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ApplicationTemplate)) { + return false; + } + final ApplicationTemplate other = (ApplicationTemplate) obj; + if (!other.canEqual(this)) { + return false; + } + if (!Objects.equals(applicationName, other.getApplicationName())) { + return false; + } + if (!Objects.equals(applicationClass, other.getApplicationClass())) { + return false; + } + + return Objects.equals(template, other.getTemplate()); + } + + public boolean canEqual(final Object obj) { + + return obj instanceof ApplicationTemplate; + } + + @Override + public final String toString() { + return toString(""); + } + + public String toString(final String data) { + + return String.format("%s{ " + + "applicationName = \"%s\", " + + "applicationClass = \"%s\", " + + "template = \"%s\"%s" + + " }", + super.toString(), + applicationName, + applicationClass, + template, + data + ); + } + +} diff --git a/ccm-themedirector/src/org/libreccm/theming/ContentItemTemplate.java b/ccm-themedirector/src/org/libreccm/theming/ContentItemTemplate.java new file mode 100644 index 000000000..bf235be16 --- /dev/null +++ b/ccm-themedirector/src/org/libreccm/theming/ContentItemTemplate.java @@ -0,0 +1,165 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.libreccm.theming; + +import static org.libreccm.theming.ThemeConstants.*; + +import java.util.Objects; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * + * @author Jens Pelzetter + */ +@XmlRootElement(name = "contentitem-template", namespace = THEMES_XML_NS) +@XmlAccessorType(XmlAccessType.FIELD) +public class ContentItemTemplate { + + @XmlElement(name = "view", namespace = THEMES_XML_NS) + private ContentItemViews view; + + @XmlElement(name = "contenttype", namespace = THEMES_XML_NS) + private String contentType; + + @XmlElement(name = "style", namespace = THEMES_XML_NS) + private String style; + + @XmlElement(name = "contentsection", namespace = THEMES_XML_NS) + private String contentSection; + + @XmlElement(name = "category", namespace = THEMES_XML_NS) + private String category; + + @XmlElement(name = "template", namespace = THEMES_XML_NS) + private String template; + + public ContentItemViews getView() { + return view; + } + + public void setView(final ContentItemViews view) { + this.view = view; + } + + public String getContentType() { + return contentType; + } + + public void setContentType(final String contentType) { + this.contentType = contentType; + } + + public String getStyle() { + return style; + } + + public void setStyle(final String style) { + this.style = style; + } + + public String getContentSection() { + return contentSection; + } + + public void setContentSection(final String contentSection) { + this.contentSection = contentSection; + } + + public String getCategory() { + return category; + } + + public void setCategory(final String category) { + this.category = category; + } + + public String getTemplate() { + return template; + } + + public void setTemplate(final String template) { + this.template = template; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 73 * hash + Objects.hashCode(view); + hash = 73 * hash + Objects.hashCode(contentType); + hash = 73 * hash + Objects.hashCode(style); + hash = 73 * hash + Objects.hashCode(contentSection); + hash = 73 * hash + Objects.hashCode(category); + hash = 73 * hash + Objects.hashCode(template); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ContentItemTemplate)) { + return false; + } + final ContentItemTemplate other = (ContentItemTemplate) obj; + if (!other.canEqual(this)) { + return false; + } + if (!Objects.equals(contentType, other.getContentType())) { + return false; + } + if (!Objects.equals(style, other.getStyle())) { + return false; + } + if (!Objects.equals(contentSection, other.getContentSection())) { + return false; + } + if (!Objects.equals(category, other.getCategory())) { + return false; + } + if (view != other.getView()) { + return false; + } + return Objects.equals(template, other.getTemplate()); + } + + public boolean canEqual(final Object obj) { + + return obj instanceof ContentItemTemplate; + } + + @Override + public final String toString() { + + return toString(""); + } + + public String toString(final String data) { + + return String.format("%s{ " + + "contentType = \"%s\", " + + "style = \"%s\", " + + "contentSection = \"%s\", " + + "category = \"%s\"" + + "template = \"%s\"%s" + + " }", + super.toString(), + contentType, + style, + contentSection, + category, + template, + data); + } + +} diff --git a/ccm-themedirector/src/org/libreccm/theming/ContentItemViews.java b/ccm-themedirector/src/org/libreccm/theming/ContentItemViews.java new file mode 100644 index 000000000..e62829390 --- /dev/null +++ b/ccm-themedirector/src/org/libreccm/theming/ContentItemViews.java @@ -0,0 +1,18 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.libreccm.theming; + +/** + * + * @author Jens Pelzetter + */ +public enum ContentItemViews { + + DETAIL, + GREETING_ITEM, + LIST, + PORTLET_ITEM, +} diff --git a/ccm-themedirector/src/org/libreccm/theming/FreeMarkerPresentationManager.java b/ccm-themedirector/src/org/libreccm/theming/FreeMarkerPresentationManager.java new file mode 100644 index 000000000..9c5b43013 --- /dev/null +++ b/ccm-themedirector/src/org/libreccm/theming/FreeMarkerPresentationManager.java @@ -0,0 +1,353 @@ +package org.libreccm.theming; + +import com.arsdigita.bebop.Bebop; +import com.arsdigita.bebop.page.PageTransformer; +import com.arsdigita.domain.DataObjectNotFoundException; +import com.arsdigita.globalization.GlobalizationHelper; +import com.arsdigita.subsite.Site; +import com.arsdigita.templating.PresentationManager; +import com.arsdigita.themedirector.ThemeDirector; +import com.arsdigita.util.UncheckedWrapperException; +import com.arsdigita.web.Web; +import com.arsdigita.xml.Document; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule; +import freemarker.cache.MultiTemplateLoader; +import freemarker.cache.TemplateLoader; +import freemarker.cache.WebappTemplateLoader; +import freemarker.ext.dom.NodeModel; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import org.libreccm.theming.manifest.ThemeManifest; +import org.libreccm.theming.manifest.ThemeManifestUtil; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * + * @author Jens Pelzetter + */ +public class FreeMarkerPresentationManager implements PresentationManager { + + @Override + public void servePage(final Document document, + final HttpServletRequest request, + final HttpServletResponse response) { + + final org.w3c.dom.Document w3cDocument = document.getInternalDocument(); + + final Node root = w3cDocument.getDocumentElement(); + + final String currentSiteName = Web.getConfig().getSiteName(); + Site subSite; + try { + subSite = Site.findByHostname(currentSiteName); + } catch (DataObjectNotFoundException ex) { + subSite = null; + } + final boolean isSubSite = subSite != null; + + final String defaultTheme; + if (subSite == null) { + defaultTheme = ThemeDirector + .getThemeDirector() + .getDefaultTheme() + .getURL(); + } else { + defaultTheme = subSite.getStyleDirectory(); + } + + final String selectedTheme; + if (request.getParameter("theme") == null) { + selectedTheme = defaultTheme; + } else { + selectedTheme = request.getParameter("theme"); + } + + final String previewParam = request.getParameter("preview-theme"); + boolean preview = previewParam != null + && ("true".equalsIgnoreCase(previewParam) + || "yes".equalsIgnoreCase(previewParam)); + + final StringBuilder themePathBuilder = new StringBuilder( + "/themes/"); + if (preview) { + themePathBuilder.append("devel-themedir/"); + } else { + themePathBuilder.append("published-themedir/"); + } + themePathBuilder.append(selectedTheme).append("/"); + final String themePath = themePathBuilder.toString(); + final String themeManifestPath = String.format( + "%s" + ThemeConstants.THEME_MANIFEST_JSON, themePath); + + final ServletContext servletContext = Web.getServletContext(); + +// final String themeManifest = ""; +// final String themeManifest = new BufferedReader( +// new InputStreamReader( +// servletContext.getResourceAsStream(themeManifestPath), +// StandardCharsets.UTF_8)) +// .lines() +// .collect(Collectors.joining(System.lineSeparator())); +// +// String name = "???"; +// final JsonFactory jsonFactory = new JsonFactory(); +// try { +// final JsonParser parser = jsonFactory.createParser(servletContext +// .getResourceAsStream(themeManifestPath)); +// +// while (!parser.isClosed()) { +// +// final JsonToken token = parser.nextToken(); +// if (JsonToken.FIELD_NAME.equals(token)) { +// final String fieldName = parser.getCurrentName(); +// +// if ("name".equals(fieldName)) { +// +// final JsonToken valueToken = parser.nextToken(); +// final String value = parser.getValueAsString(); +// name = value; +// } +// } +// +// } +// +// } catch (IOException ex) { +// throw new UncheckedWrapperException(ex); +// } + final InputStream manifestInputStream = servletContext + .getResourceAsStream(themeManifestPath); + if (manifestInputStream == null) { + final PageTransformer pageTransformer = new PageTransformer(); + pageTransformer.servePage(document, request, response); + return; + } + final ThemeManifestUtil manifestUtil = ThemeManifestUtil.getInstance(); + + final ThemeManifest manifest = manifestUtil + .loadManifest(manifestInputStream, + themeManifestPath); + + final ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JaxbAnnotationModule()); + final Templates templates; + try { + templates = objectMapper.readValue( + servletContext.getResourceAsStream( + String.format("%stemplates.json", themePath)), + Templates.class); + } catch (IOException ex) { + throw new UncheckedWrapperException(ex); + } + + // ToDo + final NamedNodeMap pageAttrs = root.getAttributes(); + final Node applicationNameAttr = pageAttrs.getNamedItem("application"); + final Node applicationClassAttr = pageAttrs.getNamedItem("class"); + final String applicationName = applicationNameAttr.getNodeValue(); + final String applicationClass = applicationClassAttr.getNodeValue(); + + final Optional applicationTemplate + = findApplicationTemplate( + templates, + applicationName, + applicationClass); + final String applicationTemplatePath; + if (applicationTemplate.isPresent()) { + applicationTemplatePath = applicationTemplate.get().getTemplate(); + } else { + applicationTemplatePath = templates.getDefaultApplicationTemplate(); + } + + if ("XSL_FALLBACK.XSL".equals(applicationTemplatePath)) { + final PageTransformer pageTransformer = new PageTransformer(); + pageTransformer.servePage(document, request, response); + return; + } + + final Configuration configuration = new Configuration( + Configuration.VERSION_2_3_28); + final WebappTemplateLoader themeTemplateLoader + = new WebappTemplateLoader(servletContext, + themePath); + final WebappTemplateLoader macrosLoader = new WebappTemplateLoader( + servletContext, + "/themes/freemarker"); + final MultiTemplateLoader templateLoader = new MultiTemplateLoader( + new TemplateLoader[]{themeTemplateLoader, macrosLoader}); +// configuration.setServletContextForTemplateLoading(servletContext, +// themePath); + configuration.setTemplateLoader(templateLoader); + configuration.setDefaultEncoding("UTF-8"); + + final Map data = new HashMap<>(); + + // The XML document + data.put("model", NodeModel.wrap(root)); + + // Parameters (in XSL provided as XSL parameters) + data.put("contextPath", request.getContextPath()); + data.put("contextPrefix", + Web.getWebContext().getRequestURL().getContextPath()); + data.put("dcpOnButtons", + Bebop.getConfig().doubleClickProtectionOnButtons()); + data.put("dcpOnLinks", + Bebop.getConfig().doubleClickProtectionOnLinks()); + data.put("dispatcherPrefix", com.arsdigita.web.URL.getDispatcherPath()); + final String host; + if (request.getServerPort() == 80) { + host = String.format("%s://%s", + request.getScheme(), + request.getServerName()); + } else { + host = String.format("%s://%s:%d", + request.getScheme(), + request.getServerName(), + request.getServerPort()); + } + data.put("host", host); + data.put("internalTheme", + Web.getWebContext().getRequestURL().getContextPath() + + com.arsdigita.web.URL.INTERNAL_THEME_DIR); + data.put("negotiatedLanguage", + GlobalizationHelper.getNegotiatedLocale().getLanguage()); + data.put("requestScheme", request.getScheme()); + data.put("rootContextPrefix", + Web.getConfig().getDispatcherContextPath()); + final Locale selectedLocale = GlobalizationHelper + .getSelectedLocale(request); + if (selectedLocale == null) { + data.put("selectedLanguage", ""); + } else { + data.put("selectedLanguage", selectedLocale.getLanguage()); + } + data.put("serverName", request.getServerName()); + data.put("serverPort", request.getServerPort()); + data.put("userAgent", request.getHeader("user-Agent")); + + final Template template; + try { + template = configuration.getTemplate(applicationTemplatePath); + } catch (IOException ex) { + throw new UncheckedWrapperException(ex); + } + + response.setCharacterEncoding(StandardCharsets.UTF_8.toString()); + response.setContentType("text/html"); + + try (PrintWriter writer = response.getWriter()) { + + template.process(data, writer); + +// writer.append("Data:\n"); +// writer +// .append("Current Site Name: ") +// .append(currentSiteName).append("\n"); +// writer +// .append("isSubSite: ") +// .append(Boolean.toString(isSubSite)) +// .append("\n"); +// writer +// .append("default theme: ") +// .append(defaultTheme) +// .append("\n"); +// writer +// .append("selected theme: ") +// .append(selectedTheme) +// .append("\n"); +// writer +// .append("preview theme? ") +// .append(Boolean.toString(preview)) +// .append("\n"); +// writer +// .append("themePath: ") +// .append(themePath) +// .append("\n"); +// writer +// .append("themeManifestPath: ") +// .append(themeManifestPath) +// .append("\n"); +// writer +// .append("themeManifest: ") +// .append(manifest.toString()) +// .append("\n"); +// writer +// .append("theme name: ") +// .append(manifest.getName()) +// .append("\n"); +// writer +// .append("Application name: ") +// .append(applicationName) +// .append("\n"); +// writer +// .append("Application class: ") +// .append(applicationClass) +// .append("\n"); +// writer +// .append("Application templates:\n"); +// for (final ApplicationTemplate template : templates +// .getApplications()) { +// writer +// .append("\t") +// .append(template.toString()) +// .append("\n"); +// } + } catch (IOException | TemplateException ex) { + throw new UncheckedWrapperException(ex); + } + +// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + private Optional findApplicationTemplate( + final Templates templates, + final String applicationName, + final String applicationClass) { + + final Optional forNameAndClass = templates + .getApplications() + .stream() + .filter(template -> filterApplicationTemplates(template, + applicationName, + applicationClass)) + .findAny(); + if (forNameAndClass.isPresent()) { + return forNameAndClass; + } else { + + final Optional forName = templates + .getApplications() + .stream() + .filter(tpl -> tpl.getApplicationName().equals(applicationName)) + .findAny(); + + return forName; + } + } + + private boolean filterApplicationTemplates( + final ApplicationTemplate template, + final String applicationName, + final String applicationClass) { + + return template.getApplicationName().equals(applicationName) + && template.getApplicationClass().equals(applicationClass); + } + +} diff --git a/ccm-themedirector/src/org/libreccm/theming/Templates.java b/ccm-themedirector/src/org/libreccm/theming/Templates.java new file mode 100644 index 000000000..7a7138c92 --- /dev/null +++ b/ccm-themedirector/src/org/libreccm/theming/Templates.java @@ -0,0 +1,108 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.libreccm.theming; + + +import static org.libreccm.theming.ThemeConstants.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * + * @author Jens Pelzetter + */ +@XmlRootElement(name = "templates", namespace = THEMES_XML_NS) +@XmlAccessorType(XmlAccessType.FIELD) +public class Templates { + + @XmlElementWrapper(name = "applications", namespace = THEMES_XML_NS) + @XmlElement(name = "applications", namespace = THEMES_XML_NS) + private List applications; + + @XmlElement(name = "default-application-template", + namespace = THEMES_XML_NS) + private String defaultApplicationTemplate; + + @XmlElementWrapper(name = "contentitems", namespace = THEMES_XML_NS) + @XmlElement(name = "contentitems", namespace = THEMES_XML_NS) + private List contentItems; + + @XmlElement(name = "default-contentitem-template", + namespace = THEMES_XML_NS) + private String defaultContentItemsTemplate; + + public Templates() { + + applications = new ArrayList<>(); + contentItems= new ArrayList<>(); + } + + public List getApplications() { + + return Collections.unmodifiableList(applications); + } + + public void addApplication(final ApplicationTemplate template) { + + applications.add(template); + } + + public void removeApplication(final ApplicationTemplate template) { + + applications.remove(template); + } + + public void setApplications(final List applications) { + + this.applications = new ArrayList<>(applications); + } + + public String getDefaultApplicationTemplate() { + return defaultApplicationTemplate; + } + + public void setDefaultApplicationTemplate( + final String defaultApplicationTemplate) { + this.defaultApplicationTemplate = defaultApplicationTemplate; + } + + public List getContentItems() { + + return Collections.unmodifiableList(contentItems); + } + + public void addContentItem(final ContentItemTemplate template) { + + contentItems.add(template); + } + + public void removeContentItem(final ContentItemTemplate template) { + + contentItems.remove(template); + } + + public void setContentItems(final List contentItems) { + + this.contentItems = new ArrayList<>(contentItems); + } + + public String getDefaultContentItemsTemplate() { + return defaultContentItemsTemplate; + } + + public void setDefaultContentItemsTemplate( + final String defaultContentItemsTemplate) { + this.defaultContentItemsTemplate = defaultContentItemsTemplate; + } +} diff --git a/ccm-themedirector/src/org/libreccm/theming/ThemeConstants.java b/ccm-themedirector/src/org/libreccm/theming/ThemeConstants.java new file mode 100644 index 000000000..3ed285aca --- /dev/null +++ b/ccm-themedirector/src/org/libreccm/theming/ThemeConstants.java @@ -0,0 +1,38 @@ +/* + * 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; + +/** + * + * @author Jens Pelzetter + */ +public final class ThemeConstants { + + public static final String PAGE_PARAMETER_TEMPLATE = "template"; + + public final static String THEME_MANIFEST_JSON = "theme.json"; + public final static String THEME_MANIFEST_XML = "theme.xml"; + + public final static String THEMES_XML_NS = "http://themes.libreccm.org"; + + private ThemeConstants() { + //Nothing + } + +} diff --git a/ccm-themedirector/src/org/libreccm/theming/manifest/ThemeManifest.java b/ccm-themedirector/src/org/libreccm/theming/manifest/ThemeManifest.java new file mode 100644 index 000000000..76133ed7e --- /dev/null +++ b/ccm-themedirector/src/org/libreccm/theming/manifest/ThemeManifest.java @@ -0,0 +1,240 @@ +/* + * 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.manifest; + +import org.libreccm.l10n.LocalizedString; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; + +import static org.libreccm.theming.ThemeConstants.*; + +import java.io.Serializable; + +/** + * Each theme contains a Manifest (either in XML or JSON format) which provides + * informations about the theme. + * + * @author Jens Pelzetter + */ +@XmlRootElement(name = "theme", namespace = THEMES_XML_NS) +@XmlAccessorType(XmlAccessType.FIELD) +public class ThemeManifest implements Serializable { + + private static final long serialVersionUID = 699497658459398231L; + + /** + * The name of the theme. Usually the same as the name of directory which + * contains the theme. + */ + @XmlElement(name = "name", namespace = THEMES_XML_NS) + private String name; + + /** + * The type of the theme, for example XSLT. + */ + @XmlElement(name = "type", namespace = THEMES_XML_NS) + private String type; + + @XmlElement(name = "master-theme", namespace = THEMES_XML_NS) + private String masterTheme; + + /** + * The (localised) title of the theme. + */ + @XmlElement(name = "title", namespace = THEMES_XML_NS) + private LocalizedString title; + + /** + * A (localised) description of the theme. + */ + @XmlElement(name = "description", namespace = THEMES_XML_NS) + private LocalizedString description; + + /** + * The templates provided by the theme. + */ + @XmlElementWrapper(name = "templates", namespace = THEMES_XML_NS) + @XmlElement(name = "template", namespace = THEMES_XML_NS) + private List templates; + + /** + * Path of the default template. + */ + @XmlElement(name = "default-template", namespace = THEMES_XML_NS) + private String defaultTemplate; + + public ThemeManifest() { + templates = new ArrayList<>(); + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(final String type) { + this.type = type; + } + + public String getMasterTheme() { + return masterTheme; + } + + public void setMasterTheme(final String masterTheme) { + this.masterTheme = masterTheme; + } + + public LocalizedString getTitle() { + return title; + } + + public void setTitle(final LocalizedString title) { + this.title = title; + } + + public LocalizedString getDescription() { + return description; + } + + public void setDescription(final LocalizedString description) { + this.description = description; + } + + public List getTemplates() { + return Collections.unmodifiableList(templates); + } + + public void setTemplates(final List templates) { + this.templates = new ArrayList<>(templates); + } + + public void addThemeTemplate(final ThemeTemplate template) { + templates.add(template); + } + + public void removeThemeTemplate(final ThemeTemplate template) { + templates.remove(template); + } + + public String getDefaultTemplate() { + return defaultTemplate; + } + + public void setDefaultTemplate(final String defaultTemplate) { + this.defaultTemplate = defaultTemplate; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 83 * hash + Objects.hashCode(name); + hash = 83 * hash + Objects.hashCode(type); + hash = 83 * hash + Objects.hashCode(masterTheme); + hash = 83 * hash + Objects.hashCode(title); + hash = 83 * hash + Objects.hashCode(description); + hash = 83 * hash + Objects.hashCode(templates); + hash = 83 * hash + Objects.hashCode(defaultTemplate); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ThemeManifest)) { + return false; + } + final ThemeManifest other = (ThemeManifest) obj; + if (!other.canEqual(this)) { + return false; + } + if (!Objects.equals(name, other.getName())) { + return false; + } + if (!Objects.equals(type, other.getType())) { + return false; + } + if (!Objects.equals(masterTheme, other.getMasterTheme())) { + return false; + } + if (!Objects.equals(title, other.getTitle())) { + return false; + } + if (!Objects.equals(description, other.getDescription())) { + return false; + } + if (!Objects.equals(templates, other.getTemplates())) { + return false; + } + return Objects.equals(defaultTemplate, other.getDefaultTemplate()); + } + + public boolean canEqual(final Object obj) { + return obj instanceof ThemeManifest; + } + + @Override + public String toString() { + return toString(""); + } + + public String toString(final String data) { + + return String.format("%s{ " + + "name = \"%s\", " + + "type = \"%s\", " + + "masterTheme = \"%s\", " + + "title = \"%s\", " + + "description = \"%s\", " + + "templates = %s, " + + "defaultTemplate%s" + + " }", + super.toString(), + name, + type, + masterTheme, + Objects.toString(title), + Objects.toString(description), + Objects.toString(templates), + defaultTemplate, + data); + + } + +} diff --git a/ccm-themedirector/src/org/libreccm/theming/manifest/ThemeManifestUtil.java b/ccm-themedirector/src/org/libreccm/theming/manifest/ThemeManifestUtil.java new file mode 100644 index 000000000..5841515bd --- /dev/null +++ b/ccm-themedirector/src/org/libreccm/theming/manifest/ThemeManifestUtil.java @@ -0,0 +1,202 @@ +/* + * 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.manifest; + +import com.arsdigita.util.UncheckedWrapperException; + +import static org.libreccm.theming.ThemeConstants.*; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.Serializable; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Locale; + + +/** + * A Utility class for loading them manifest file of a theme. + * + * @author Jens Pelzetter + */ +public class ThemeManifestUtil implements Serializable { + + private static final long serialVersionUID = -7650437144515619682L; + + private static final ThemeManifestUtil INSTANCE = new ThemeManifestUtil(); + + private ThemeManifestUtil() {}; + + public static final ThemeManifestUtil getInstance() { + + return INSTANCE; + } + + /** + * Reads the manifest file at {@code path}. + * + * @param path The path of the manifest file. + * + * @return The parsed manifest file. + */ + public ThemeManifest loadManifest(final Path path) { + +// final String pathStr = path.toString().toLowerCase(Locale.ROOT); + final BufferedReader reader; + try { + reader = Files.newBufferedReader(path, Charset.forName("UTF-8")); + } catch (IOException ex) { + throw new UncheckedWrapperException(ex); + } + + return parseManifest(reader, path.toString()); + +// final ObjectMapper mapper; +// if (pathStr.endsWith(THEME_MANIFEST_JSON)) { +// mapper = new ObjectMapper(); +// } else if (pathStr.endsWith(THEME_MANIFEST_XML)) { +// final JacksonXmlModule xmlModule = new JacksonXmlModule(); +// mapper = new XmlMapper(xmlModule); +// } else { +// throw new IllegalArgumentException(String +// .format("The provided path \"%s\" does not point to a theme " +// + "manifest file.", +// path.toString())); +// } +// +// mapper.registerModule(new JaxbAnnotationModule()); +// +// final ThemeManifest manifest; +// try { +// manifest = mapper.readValue(reader, ThemeManifest.class); +// } catch (IOException ex) { +// throw new UnexpectedErrorException(ex); +// } +// return manifest; + } + + public ThemeManifest loadManifest(final InputStream inputStream, + final String fileName) { + + final InputStreamReader reader; + try { + reader = new InputStreamReader(inputStream, "UTF-8"); + } catch (UnsupportedEncodingException ex) { + throw new UncheckedWrapperException(ex); + } + + return parseManifest(reader, fileName); + +// final ObjectMapper mapper; +// if (fileName.endsWith(THEME_MANIFEST_JSON)) { +// mapper = new ObjectMapper(); +// } else if (fileName.endsWith(THEME_MANIFEST_XML)) { +// final JacksonXmlModule xmlModule = new JacksonXmlModule(); +// mapper = new XmlMapper(xmlModule); +// } else { +// throw new IllegalArgumentException(String +// .format("The provided path \"%s\" does not point to a theme " +// + "manifest file.", +// fileName)); +// } +// +// mapper.registerModule(new JaxbAnnotationModule()); +// +// final ThemeManifest manifest; +// try { +// manifest = mapper.readValue(reader, ThemeManifest.class); +// } catch (IOException ex) { +// throw new UnexpectedErrorException(ex); +// } +// return manifest; + } + + public String serializeManifest(final ThemeManifest manifest, + final String format) { + + final ObjectMapper mapper; + + switch (format) { + case THEME_MANIFEST_JSON: + mapper = new ObjectMapper(); + break; + case THEME_MANIFEST_XML: + final JacksonXmlModule xmlModule = new JacksonXmlModule(); + mapper = new XmlMapper(xmlModule); + break; + default: + throw new IllegalArgumentException( + "Unsupported format for ThemeManifest"); + } + + mapper.registerModule(new JaxbAnnotationModule()); + mapper.configure(SerializationFeature.INDENT_OUTPUT, true); + + final StringWriter writer = new StringWriter(); + try { + mapper.writeValue(writer, manifest); + } catch (IOException ex) { + throw new UncheckedWrapperException(ex); + } + + return writer.toString(); + } + + private ThemeManifest parseManifest(final Reader reader, + final String path) { + + final String pathStr = path.toLowerCase(Locale.ROOT); + + final ObjectMapper mapper; + if (pathStr.endsWith(THEME_MANIFEST_JSON)) { + mapper = new ObjectMapper(); + } else if (pathStr.endsWith(THEME_MANIFEST_XML)) { + final JacksonXmlModule xmlModule = new JacksonXmlModule(); + mapper = new XmlMapper(xmlModule); + } else { + throw new IllegalArgumentException(String + .format("The provided path \"%s\" does not point to a theme " + + "manifest file.", + path)); + } + + mapper.registerModule(new JaxbAnnotationModule()); + + final ThemeManifest manifest; + try { + manifest = mapper.readValue(reader, ThemeManifest.class); + } catch (IOException ex) { + throw new UncheckedWrapperException(ex); + } + return manifest; + } + +} diff --git a/ccm-themedirector/src/org/libreccm/theming/manifest/ThemeTemplate.java b/ccm-themedirector/src/org/libreccm/theming/manifest/ThemeTemplate.java new file mode 100644 index 000000000..236862e77 --- /dev/null +++ b/ccm-themedirector/src/org/libreccm/theming/manifest/ThemeTemplate.java @@ -0,0 +1,160 @@ +/* + * 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.manifest; + +import org.libreccm.l10n.LocalizedString; + +import java.io.Serializable; +import java.util.Objects; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Informations about a template provided by a theme. + * + * @author Jens Pelzetter + */ +@XmlRootElement(name = "template", namespace = "http://themes.libreccm.org") +@XmlAccessorType(XmlAccessType.FIELD) +public class ThemeTemplate implements Serializable { + + private static final long serialVersionUID = -9034588759798295569L; + + /** + * The name of the template (usually the filename). + */ + @XmlElement(name = "name", namespace = "http://themes.libreccm.org") + private String name; + + /** + * The (localised) title of the template. + */ + @XmlElement(name = "title", namespace = "http://themes.libreccm.org") + private LocalizedString title; + + /** + * A (localised) description of the template. + */ + @XmlElement(name = "description", namespace = "http://themes.libreccm.org") + private LocalizedString description; + + /** + * Path of template relative to the directory of the theme. + */ + @XmlElement(name = "path", namespace = "http://themes.libreccm.org") + private String path; + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public LocalizedString getTitle() { + return title; + } + + public void setTitle(final LocalizedString title) { + this.title = title; + } + + public LocalizedString getDescription() { + return description; + } + + public void setDescription(final LocalizedString description) { + this.description = description; + } + + public String getPath() { + return path; + } + + public void setPath(final String path) { + this.path = path; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 67 * hash + Objects.hashCode(name); + hash = 67 * hash + Objects.hashCode(title); + hash = 67 * hash + Objects.hashCode(description); + hash = 67 * hash + Objects.hashCode(path); + return hash; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ThemeTemplate)) { + return false; + } + final ThemeTemplate other = (ThemeTemplate) obj; + if (!other.canEqual(this)) { + return false; + } + if (!Objects.equals(name, other.getName())) { + return false; + } + if (!Objects.equals(path, other.getPath())) { + return false; + } + if (!Objects.equals(title, other.getTitle())) { + return false; + } + return Objects.equals(description, other.getDescription()); + } + + public boolean canEqual(final Object obj) { + return obj instanceof ThemeTemplate; + } + + @Override + public String toString() { + return toString(""); + } + + public String toString(final String data) { + + return String.format("%s{ " + + "name = \"%s\", " + + "title = %s, " + + "description = %s, " + + "path = \"%s\"%s" + + " }", + super.toString(), + name, + Objects.toString(title), + Objects.toString(description), + path, + data); + } + +} diff --git a/ccm-themedirector/web/themes/freemarker/language.ftl b/ccm-themedirector/web/themes/freemarker/language.ftl new file mode 100644 index 000000000..c253dd3f4 --- /dev/null +++ b/ccm-themedirector/web/themes/freemarker/language.ftl @@ -0,0 +1,23 @@ +<#ftl ns_prefixes={ +"bebop":"http://www.arsdigita.com/bebop/1.0", +"cms":"http://www.arsdigita.com/cms/1.0", +"nav":"http://ccm.redhat.com/navigation", +"ui": "http://www.arsdigita.com/ui/1.0"} +> + +<#macro availableLanguages> + + <#assign langs=["empty"]> + <#if (model["/bebop:page/cms:contentPanel"]?size > 0)> + <#assign langs=model["/bebop:page/cms:contentPanel/availableLanguages/language/@locale"]> + <#elseif (model["/bebop:page/nav:greetingItem"]?size > 0)> + <#assign langs=model["/bebop:page/nav:greetingItem/availableLanguages/language/@locale"]> + <#else> + <#assign langs=model["/bebop:page/ui:siteBanner/supportedLanguages/language/@locale"]> + + + <#list langs?sort as lang> + <#nested lang, lang==negotiatedLanguage> + + + diff --git a/ccm-themedirector/web/themes/freemarker/macros.ftl b/ccm-themedirector/web/themes/freemarker/macros.ftl new file mode 100644 index 000000000..531e752ae --- /dev/null +++ b/ccm-themedirector/web/themes/freemarker/macros.ftl @@ -0,0 +1,12 @@ +<#ftl ns_prefixes={ +"bebop":"http://www.arsdigita.com/bebop/1.0", +"cms":"http://www.arsdigita.com/cms/1.0", +"nav":"http://ccm.redhat.com/navigation", +"ui": "http://www.arsdigita.com/ui/1.0"} +> + +<#-- Move to Navigation tags? --> +<#macro pageTitle> + ${model["//nav:categoryMenu/nav:category/@title"]} + +