TemplateUtils.java
/*
* Copyright (C) 2003-2013 eXo Platform SAS.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see<http://www.gnu.org/licenses/>.
*/
package org.exoplatform.commons.notification.template;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.net.URL;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.exoplatform.commons.api.notification.plugin.config.PluginConfig;
import org.exoplatform.commons.api.notification.service.template.TemplateContext;
import org.exoplatform.commons.api.notification.template.Element;
import org.exoplatform.commons.api.notification.template.ElementVisitor;
import org.exoplatform.commons.notification.NotificationUtils;
import org.exoplatform.commons.notification.impl.NotificationContextImpl;
import org.exoplatform.commons.utils.CommonsUtils;
import org.exoplatform.container.configuration.ConfigurationManager;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.resources.ResourceBundleService;
import org.gatein.common.io.IOTools;
public class TemplateUtils {
private static final Log LOG = ExoLogger.getLogger(TemplateUtils.class);
private static final String DIGEST_TEMPLATE_KEY = "Digest.{0}.{1}";
private static final String SIMPLE_TEMPLATE_KEY = "Simple.{0}.{1}";
private static final Pattern SCRIPT_REMOVE_PATTERN = Pattern.compile("<(script|style)[^>]*>[^<]*</(script|style)>", Pattern.CASE_INSENSITIVE);
private static final Pattern TAGS_REMOVE_PATTERN = Pattern.compile("<[^>]*>", Pattern.CASE_INSENSITIVE);
private static Map<String, Element> cacheTemplate = new ConcurrentHashMap<String, Element>();
private static final int MAX_SUBJECT_LENGTH = 50;
/**
* Process the Groovy template associate with Template context to generate
* It will be use for digest mail
* @param ctx
* @return
*/
public static String processGroovy(TemplateContext ctx) {
Element groovyElement = loadGroovyElement(ctx.getPluginId(), ctx.getLanguage());
ElementVisitor visitor = new GroovyElementVisitor();
String content = visitor.with(ctx).visit(groovyElement).out();
return content;
}
/**
* Gets InputStream for groovy template
*
* @param templatePath
* @return
* @throws Exception
*/
private static InputStream getTemplateInputStream(String templatePath) throws Exception {
try {
ConfigurationManager configurationManager = CommonsUtils.getService(ConfigurationManager.class);
String uri = templatePath;
if (templatePath.indexOf("war") < 0 && templatePath.indexOf("jar") < 0 && templatePath.indexOf("classpath") < 0) {
URL url = null;
if (templatePath.indexOf("/") == 0) {
templatePath = templatePath.substring(1);
}
uri = "war:/" + templatePath;
url = configurationManager.getURL(uri);
if (url == null) {
uri = "jar:/" + templatePath;
url = configurationManager.getURL(uri);
}
}
return configurationManager.getInputStream(uri);
} catch (Exception e) {
throw new RuntimeException("Error to get notification template " + templatePath, e);
}
}
/**
* Loads the Groovy template file
*
* @param templatePath
* @return
* @throws Exception
*/
public static String loadGroovyTemplate(String templatePath) throws Exception {
StringWriter templateText = new StringWriter();
Reader reader = null;
try {
reader = new InputStreamReader(getTemplateInputStream(templatePath));
IOTools.copy(reader, templateText);
} catch (Exception e) {
LOG.debug("Failed to reader template file: " + templatePath, e);
} finally {
if (reader != null) {
reader.close();
}
}
return templateText.toString();
}
/**
* Load the Groovy template element.
*
* @param pluginId The plugin's id
* @param language The language's id.
* @return The Groovy element
*/
public static Element loadGroovyElement(String pluginId, String language) {
PluginConfig templateConfig = getPluginConfig(pluginId);
return new GroovyElement().language(language).config(templateConfig);
}
/**
* Render for Subject template
* @param ctx
* @return
*/
public static String processSubject(TemplateContext ctx) {
Element subjectElement = null;
String key = makeTemplateKey(SIMPLE_TEMPLATE_KEY, ctx.getPluginId(), ctx.getLanguage());
if (cacheTemplate.containsKey(key)) {
subjectElement = cacheTemplate.get(key);
} else {
PluginConfig templateConfig = getPluginConfig(ctx.getPluginId());
subjectElement = NotificationUtils.getSubject(templateConfig, ctx.getPluginId(), ctx.getLanguage()).addNewLine(false);
cacheTemplate.put(key, subjectElement);
}
//The title of activity is escaped on social, then we need to unescape it to process the send email
String value = (String) ctx.get("ACTIVITY");
if (value != null) {
ctx.put("ACTIVITY", StringEscapeUtils.unescapeHtml(value));
}
String subject = (String) ctx.get("SUBJECT");
if (subject != null && subject.length() > 0) {
ctx.put("SUBJECT", getExcerptSubject(subject));
return subjectElement.accept(SimpleElementVistior.instance().with(ctx)).out();
}
//
subject = subjectElement.accept(SimpleElementVistior.instance().with(ctx)).out();
return getExcerptSubject(subject);
}
/**
* Get the excerpt subject of notification mail from origin string
* - Just contains plain text
* - Limit number of characters
* @param subject the origin string
* @return the excerpt of subject
* @since 4.1.x
*/
public static String getExcerptSubject(String subject) {
String newSubject = StringEscapeUtils.unescapeHtml(cleanHtmlTags(subject));
if (newSubject != null && newSubject.length() > MAX_SUBJECT_LENGTH) {
newSubject = newSubject.substring(0, MAX_SUBJECT_LENGTH);
int lastSpace = newSubject.lastIndexOf(" ");
return ((lastSpace > 0) ? newSubject.substring(0, lastSpace) : newSubject) + "...";
}
return newSubject;
}
/**
* Clean all HTML tags on string
*
* @param str the origin string
* @return The string has not contain HTML tags.
* @since 4.1.x
*/
public static String cleanHtmlTags(String str) {
//
if (str == null || str.trim().length() == 0) {
return "";
}
// clean multi-lines
String newSubject = StringUtils.replace(str, "\n", " ");
// clean script
newSubject = SCRIPT_REMOVE_PATTERN.matcher(newSubject).replaceAll("");
// clean tags HTML
newSubject = TAGS_REMOVE_PATTERN.matcher(newSubject).replaceAll(" ");
return newSubject.replaceAll("\\s+", " ").trim();
}
/**
* Render for digest template
* @param ctx
* @return
*/
public static String processDigest(TemplateContext ctx) {
DigestTemplate digest = null;
String key = makeTemplateKey(DIGEST_TEMPLATE_KEY, ctx.getPluginId(), ctx.getLanguage());
if (cacheTemplate.containsKey(key)) {
digest = (DigestTemplate) cacheTemplate.get(key);
} else {
PluginConfig templateConfig = getPluginConfig(ctx.getPluginId());
digest = NotificationUtils.getDigest(templateConfig, ctx.getPluginId(), ctx.getLanguage());
cacheTemplate.put(key, digest);
}
return digest.accept(SimpleElementVistior.instance().with(ctx)).out();
}
private static String makeTemplateKey(String pattern, String pluginId, String language) {
return MessageFormat.format(pattern, pluginId, language);
}
/**
* Gets Plugin configuration for specified PluginId
* @param pluginId
* @return
*/
private static PluginConfig getPluginConfig(String pluginId) {
PluginConfig pluginConfig = NotificationContextImpl.cloneInstance().getPluginSettingService().getPluginConfig(pluginId);
if(pluginConfig == null) {
throw new IllegalStateException("PluginConfig is NULL with plugId = " + pluginId);
}
return pluginConfig;
}
/**
* Gets Resource Bundle value
* @param key
* @param locale
* @param resourcePath
* @return
*/
public static String getResourceBundle(String key, Locale locale, String resourcePath) {
if (key == null || key.trim().length() == 0 || resourcePath == null || resourcePath.isEmpty()) {
return "";
}
if (locale == null || locale.getLanguage().isEmpty()) {
locale = Locale.ENGLISH;
}
ResourceBundleService bundleService = CommonsUtils.getService(ResourceBundleService.class);
ResourceBundle res = bundleService.getResourceBundle(resourcePath, locale);
if (res == null || res.containsKey(key) == false) {
LOG.warn("Resource Bundle key not found. " + key + " in source path: " + resourcePath);
return key;
}
return res.getString(key);
}
}