paramMap) {
+ java.util.Locale userLocale = LocaleUtil.getLocale(System.getenv(Constants.DEFAULT_LANGUAGE_ENV));
+ String subject = MessageUtil.getMessage(userLocale, "email.template." + templateName + ".subject");
+ sendEmail(templateName, recipientUser, subject, paramMap);
+ }
+}
diff --git a/docs-core/src/main/java/com/sismics/util/LocaleUtil.java b/docs-core/src/main/java/com/sismics/util/LocaleUtil.java
new file mode 100644
index 00000000..11ab9c7b
--- /dev/null
+++ b/docs-core/src/main/java/com/sismics/util/LocaleUtil.java
@@ -0,0 +1,36 @@
+package com.sismics.util;
+
+import com.google.common.base.Strings;
+
+import java.util.Locale;
+
+/**
+ * Locale utilities.
+ *
+ * @author jtremeaux
+ */
+public class LocaleUtil {
+ /**
+ * Returns a locale from the language / country / variation code (ex: fr_FR).
+ *
+ * @param localeCode Locale code
+ * @return Locale instance
+ */
+ public static Locale getLocale(String localeCode) {
+ if (Strings.isNullOrEmpty(localeCode)) {
+ return Locale.ENGLISH;
+ }
+
+ String[] localeCodeArray = localeCode.split("_");
+ String language = localeCodeArray[0];
+ String country = "";
+ String variant = "";
+ if (localeCodeArray.length >= 2) {
+ country = localeCodeArray[1];
+ }
+ if (localeCodeArray.length >= 3) {
+ variant = localeCodeArray[2];
+ }
+ return new Locale(language, country, variant);
+ }
+}
diff --git a/docs-core/src/main/java/com/sismics/util/MessageUtil.java b/docs-core/src/main/java/com/sismics/util/MessageUtil.java
new file mode 100644
index 00000000..a2898bd0
--- /dev/null
+++ b/docs-core/src/main/java/com/sismics/util/MessageUtil.java
@@ -0,0 +1,43 @@
+package com.sismics.util;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Messages utilities.
+ *
+ * @author jtremeaux
+ */
+public class MessageUtil {
+ /**
+ * Returns a localized message in the specified language.
+ * Returns **key** if no message exists for this key.
+ *
+ * @param locale Locale
+ * @param key Message key
+ * @param args Arguments to format
+ * @return Formatted message
+ */
+ public static String getMessage(Locale locale, String key, Object... args) {
+ ResourceBundle resources = ResourceBundle.getBundle("messages", locale);
+ String message;
+ try {
+ message = resources.getString(key);
+ } catch (MissingResourceException e) {
+ message = "**" + key + "**";
+ }
+ return MessageFormat.format(message, args);
+ }
+
+ /**
+ * Returns the resource bundle corresponding to the specified language.
+ *
+ * @param locale Locale
+ * @return Resource bundle
+ */
+ public static ResourceBundle getMessage(Locale locale) {
+ return ResourceBundle.getBundle("messages", locale);
+ }
+}
diff --git a/docs-core/src/main/java/com/sismics/util/ResourceBundleModel.java b/docs-core/src/main/java/com/sismics/util/ResourceBundleModel.java
new file mode 100644
index 00000000..cff2a583
--- /dev/null
+++ b/docs-core/src/main/java/com/sismics/util/ResourceBundleModel.java
@@ -0,0 +1,55 @@
+package com.sismics.util;
+
+import freemarker.ext.beans.BeansWrapper;
+import freemarker.ext.beans.StringModel;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Override of {@link freemarker.ext.beans.ResourceBundleModel}
+ * to threat single quotes uniformely.
+ *
+ * @author bgamard
+ */
+public class ResourceBundleModel extends freemarker.ext.beans.ResourceBundleModel {
+
+ /**
+ * Default constructor.
+ *
+ * @param bundle Resource bundle
+ * @param wrapper Beans wrapper
+ */
+ public ResourceBundleModel(ResourceBundle bundle, BeansWrapper wrapper) {
+ super(bundle, wrapper);
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Object exec(List arguments) throws TemplateModelException {
+ // Must have at least one argument - the key
+ if (arguments.size() < 1)
+ throw new TemplateModelException("No message key was specified");
+ // Read it
+ Iterator it = arguments.iterator();
+ String key = unwrap((TemplateModel) it.next()).toString();
+ try {
+ // Copy remaining arguments into an Object[]
+ int args = arguments.size() - 1;
+ Object[] params = new Object[args];
+ for (int i = 0; i < args; ++i)
+ params[i] = unwrap((TemplateModel) it.next());
+
+ // Invoke format
+ return new StringModel(format(key, params), wrapper);
+ } catch (MissingResourceException e) {
+ throw new TemplateModelException("No such key: " + key);
+ } catch (Exception e) {
+ throw new TemplateModelException(e.getMessage());
+ }
+ }
+}
diff --git a/docs-core/src/main/resources/META-INF/persistence.xml b/docs-core/src/main/resources/META-INF/persistence.xml
index 7c40e310..b62e7679 100644
--- a/docs-core/src/main/resources/META-INF/persistence.xml
+++ b/docs-core/src/main/resources/META-INF/persistence.xml
@@ -4,6 +4,6 @@
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
- org.hibernate.ejb.HibernatePersistence
+ org.hibernate.jpa.HibernatePersistenceProvider
\ No newline at end of file
diff --git a/docs-core/src/main/resources/config.properties b/docs-core/src/main/resources/config.properties
index a56b42a4..ca77caa4 100644
--- a/docs-core/src/main/resources/config.properties
+++ b/docs-core/src/main/resources/config.properties
@@ -1 +1 @@
-db.version=12
\ No newline at end of file
+db.version=13
\ No newline at end of file
diff --git a/docs-core/src/main/resources/db/update/dbupdate-013-0.sql b/docs-core/src/main/resources/db/update/dbupdate-013-0.sql
new file mode 100644
index 00000000..1407d548
--- /dev/null
+++ b/docs-core/src/main/resources/db/update/dbupdate-013-0.sql
@@ -0,0 +1,2 @@
+create cached table T_PASSWORD_RECOVERY ( PWR_ID_C varchar(36) not null, PWR_USERNAME_C varchar(50) not null, PWR_CREATEDATE_D datetime, PWR_DELETEDATE_D datetime, primary key (PWR_ID_C) );
+update T_CONFIG set CFG_VALUE_C = '13' where CFG_ID_C = 'DB_VERSION';
diff --git a/docs-core/src/main/resources/email_template/layout.ftl b/docs-core/src/main/resources/email_template/layout.ftl
new file mode 100644
index 00000000..74a8dd09
--- /dev/null
+++ b/docs-core/src/main/resources/email_template/layout.ftl
@@ -0,0 +1,16 @@
+<#macro email>
+
+
+
+ ${base_url}
+ |
+
+
+
+
+ <#nested>
+
+ |
+
+
+#macro>
\ No newline at end of file
diff --git a/docs-core/src/main/resources/email_template/password_recovery/template.ftl b/docs-core/src/main/resources/email_template/password_recovery/template.ftl
new file mode 100644
index 00000000..84c371fa
--- /dev/null
+++ b/docs-core/src/main/resources/email_template/password_recovery/template.ftl
@@ -0,0 +1,8 @@
+<#import "../layout.ftl" as layout>
+<@layout.email>
+ ${app_name} - ${messages['email.template.password_recovery.subject']}
+ ${messages('email.template.password_recovery.hello', user_name)}
+ ${messages['email.template.password_recovery.instruction1']}
+ ${messages['email.template.password_recovery.instruction2']}
+ ${messages['email.template.password_recovery.click_here']}
+@layout.email>
\ No newline at end of file
diff --git a/docs-core/src/main/resources/messages.properties b/docs-core/src/main/resources/messages.properties
new file mode 100644
index 00000000..fc8d7a39
--- /dev/null
+++ b/docs-core/src/main/resources/messages.properties
@@ -0,0 +1,5 @@
+email.template.password_recovery.subject=Please reset your password
+email.template.password_recovery.hello=Hello {0}.
+email.template.password_recovery.instruction1=We have received a request to reset your password.
If you did not request help, then feel free to ignore this email.
+email.template.password_recovery.instruction2=To reset your password, please visit the link below:
+email.template.password_recovery.click_here=Click here to reset your password
\ No newline at end of file
diff --git a/docs-core/src/main/resources/messages_fr.properties b/docs-core/src/main/resources/messages_fr.properties
new file mode 100644
index 00000000..6ddc4b41
--- /dev/null
+++ b/docs-core/src/main/resources/messages_fr.properties
@@ -0,0 +1,5 @@
+email.template.password_recovery.subject=Réinitialiser votre mot de passe
+email.template.password_recovery.hello=Bonjour {0}.
+email.template.password_recovery.instruction1=Nous avons reçu une demande de réinitialisation de mot de passe.
Si vous n''avez rien demandé, vous pouvez ignorer cet mail.
+email.template.password_recovery.instruction2=Pour réinitialiser votre mot de passe, cliquez sur le lien ci-dessous :
+email.template.password_recovery.click_here=Cliquez ici pour réinitialiser votre mot de passe.
\ No newline at end of file
diff --git a/docs-web-common/pom.xml b/docs-web-common/pom.xml
index 5a7e8a30..efa9aced 100644
--- a/docs-web-common/pom.xml
+++ b/docs-web-common/pom.xml
@@ -93,7 +93,13 @@
jersey-container-grizzly2-servlet
test