Commit d41ee4c2 by jhrabal

templates

parent 45b9c4f9
-- default admin user -- default admin user
insert into APP_ROLE (ID, NAME) values (1, 'ADMIN'); insert into APP_ROLE (ID, NAME) values (1, 'ADMIN');
insert into APP_USER (ID, EMAIL, FIRST_NAME, LAST_NAME, PASSWORD, PASSWORD_SALT) VAlUES (1, 'janhrabal@seznam.cz', 'Jan', 'Hrabal', 'NKz1rz7kSEBXFGbolvEhdomvcDQYmD0IKGADVpuoxL1ztsE1NAnOMvbiSkvc3vwLQvBdPHw449XzgRderNJc9MMnnMTEgJdl3S6dtBGiEYDRHK19toXdwttfaDrXewjyZBJkzy7CxE/BOad4XkiTreIFAUGRedK9TGZ+RWbrJ2KIRrkSX3H1J2eT7HLF8bblkxz2qhjsF5s0k37e3sFI0xAdyCy6qAYS4/MW4WYQ3o0YyZc4krGE2k3y9kfPxWEh/favQKoFIX92ZkRh6ZIXNF7i4oUBl1pcg6r5ykCT83IAWm9avM768NEitEVOx0V8P0PQ2WxGA3n7nicKmwYjow==', 'GACR2Rea1kIhZAlImqK8HauZwTah5eMyKiTzr9HDriryN92YkE5UkWe3Gn7oRLkKEftaNfEfa2Ujj18Rrsed2a6QN69UZCkpRHnwgoBp5ckOOaC6s4undHSjYZW5rJx8CuKXTJpO1TS1LlsjwCyir8oA2gGm480jgGwOefm+r2s='); insert into APP_USER (ID, EMAIL, FIRST_NAME, LAST_NAME, PASSWORD, PASSWORD_SALT, LOCALE, TIMEZONE_ID) VAlUES (1, 'janhrabal@seznam.cz', 'Jan', 'Hrabal', 'NKz1rz7kSEBXFGbolvEhdomvcDQYmD0IKGADVpuoxL1ztsE1NAnOMvbiSkvc3vwLQvBdPHw449XzgRderNJc9MMnnMTEgJdl3S6dtBGiEYDRHK19toXdwttfaDrXewjyZBJkzy7CxE/BOad4XkiTreIFAUGRedK9TGZ+RWbrJ2KIRrkSX3H1J2eT7HLF8bblkxz2qhjsF5s0k37e3sFI0xAdyCy6qAYS4/MW4WYQ3o0YyZc4krGE2k3y9kfPxWEh/favQKoFIX92ZkRh6ZIXNF7i4oUBl1pcg6r5ykCT83IAWm9avM768NEitEVOx0V8P0PQ2WxGA3n7nicKmwYjow==', 'GACR2Rea1kIhZAlImqK8HauZwTah5eMyKiTzr9HDriryN92YkE5UkWe3Gn7oRLkKEftaNfEfa2Ujj18Rrsed2a6QN69UZCkpRHnwgoBp5ckOOaC6s4undHSjYZW5rJx8CuKXTJpO1TS1LlsjwCyir8oA2gGm480jgGwOefm+r2s=', 'en', 'UTC');
insert into APP_USER_ROLE (USER_ID, ROLE_ID) values (1, 1); insert into APP_USER_ROLE (USER_ID, ROLE_ID) values (1, 1);
-- default templates
insert into TEMPLATE (ID, CODE, TEMPLATE) values (1, 'email.layout', '{{content}}');
insert into TEMPLATE (ID, CODE, TEMPLATE) values (2, 'email.signup.subject', 'Welcome to ...');
insert into TEMPLATE (ID, CODE, TEMPLATE) values (3, 'email.signup.text', 'Welcome to ...');
insert into TEMPLATE (ID, CODE, TEMPLATE) values (4, 'email.signup.html', 'Welcome to ...');
insert into TEMPLATE (ID, CODE, TEMPLATE) values (5, 'email.password.subject', 'Password subject');
insert into TEMPLATE (ID, CODE, TEMPLATE) values (6, 'email.password.text', 'Password text {{token}}');
insert into TEMPLATE (ID, CODE, TEMPLATE) values (7, 'email.password.html', 'Password HTML');
...@@ -78,6 +78,9 @@ CREATE TABLE APP_USER ( ...@@ -78,6 +78,9 @@ CREATE TABLE APP_USER (
FIRST_NAME VARCHAR(250), FIRST_NAME VARCHAR(250),
LAST_NAME VARCHAR(250), LAST_NAME VARCHAR(250),
TIMEZONE_ID VARCHAR(50),
LOCALE VARCHAR(50),
CONSTRAINT PK_APP_USER PRIMARY KEY(ID) CONSTRAINT PK_APP_USER PRIMARY KEY(ID)
); );
CREATE INDEX APP_USER_EMAIL_IDX ON APP_USER(lower(EMAIL)); CREATE INDEX APP_USER_EMAIL_IDX ON APP_USER(lower(EMAIL));
...@@ -170,18 +173,18 @@ CREATE TABLE EMAIL_ATTACHMENT ( ...@@ -170,18 +173,18 @@ CREATE TABLE EMAIL_ATTACHMENT (
CREATE INDEX EMAIL_ATTACHMENT_IDX ON EMAIL_ATTACHMENT(EMAIL_ID); CREATE INDEX EMAIL_ATTACHMENT_IDX ON EMAIL_ATTACHMENT(EMAIL_ID);
-- CUSTOMIZABLE TEMPLATES -- TEMPLATES
CREATE TABLE CUSTOMIZABLE_TEMPLATE ( CREATE TABLE TEMPLATE (
ID INT8 NOT NULL, ID INT8 NOT NULL,
CODE VARCHAR(250), CODE VARCHAR(250),
NAME VARCHAR(100), NAME VARCHAR(100),
TEMPLATE TEXT, TEMPLATE TEXT,
CONSTRAINT PK_CUSTOMIZABLE_TEMPLATE PRIMARY KEY(ID) CONSTRAINT PK_TEMPLATE PRIMARY KEY(ID)
); );
CREATE INDEX CUSTOMIZABLE_TEMPLATE_CODE_IDX ON CUSTOMIZABLE_TEMPLATE(CODE); CREATE INDEX TEMPLATE_CODE_IDX ON TEMPLATE(CODE);
CREATE TABLE CUSTOMIZABLE_TEMPLATE_LABEL ( CREATE TABLE CUSTOMIZABLE_TEMPLATE_LABEL (
...@@ -197,7 +200,7 @@ CREATE TABLE CUSTOMIZABLE_TEMPLATE_LABEL ( ...@@ -197,7 +200,7 @@ CREATE TABLE CUSTOMIZABLE_TEMPLATE_LABEL (
POSITION NUMERIC(19,0), POSITION NUMERIC(19,0),
CONSTRAINT PK_CUSTOMIZABLE_TEMPLATE_LABEL PRIMARY KEY(ID), CONSTRAINT PK_CUSTOMIZABLE_TEMPLATE_LABEL PRIMARY KEY(ID),
CONSTRAINT FK_CUSTOMIZABLE_TEMPLATE_LABEL_TEMPLATE FOREIGN KEY (TEMPLATE_ID) REFERENCES CUSTOMIZABLE_TEMPLATE(ID) CONSTRAINT FK_CUSTOMIZABLE_TEMPLATE_LABEL_TEMPLATE FOREIGN KEY (TEMPLATE_ID) REFERENCES TEMPLATE(ID)
); );
CREATE INDEX CUSTOMIZABLE_TEMPLATE_LABEL_TEMPLATE_IDX ON CUSTOMIZABLE_TEMPLATE_LABEL(TEMPLATE_ID); CREATE INDEX CUSTOMIZABLE_TEMPLATE_LABEL_TEMPLATE_IDX ON CUSTOMIZABLE_TEMPLATE_LABEL(TEMPLATE_ID);
......
package com.jh.boot.security; package com.jh.boot.security;
import com.jh.boot.security.model.AppUser; import com.jh.boot.security.model.AppUser;
import com.jh.boot.security.model.ResetPasswordToken;
public interface AuthServiceListener { public interface AuthServiceListener {
void registerUser(AppUser user); void registerUser(AppUser user);
void generateResetToken(AppUser user); void generateResetToken(AppUser user, ResetPasswordToken token);
} }
...@@ -66,6 +66,12 @@ public class AppUser extends AbstractIdEntity { ...@@ -66,6 +66,12 @@ public class AppUser extends AbstractIdEntity {
) )
private Set<AppRole> roles; private Set<AppRole> roles;
@Column(name = "LOCALE")
private String locale;
@Column(name = "TIMEZONE_ID")
private String timezone;
/** /**
* Gets the email. * Gets the email.
* *
...@@ -200,4 +206,20 @@ public class AppUser extends AbstractIdEntity { ...@@ -200,4 +206,20 @@ public class AppUser extends AbstractIdEntity {
this.roles = roles; this.roles = roles;
} }
public String getLocale() {
return locale;
}
public void setLocale(String locale) {
this.locale = locale;
}
public String getTimezone() {
return timezone;
}
public void setTimezone(String timezone) {
this.timezone = timezone;
}
} }
...@@ -82,7 +82,7 @@ public class AppUserAuthService implements AuthService { ...@@ -82,7 +82,7 @@ public class AppUserAuthService implements AuthService {
appUserRepository.saveResetPasswordToken(token); appUserRepository.saveResetPasswordToken(token);
if (authListeners != null) { if (authListeners != null) {
authListeners.forEach(al -> al.generateResetToken(user)); authListeners.forEach(al -> al.generateResetToken(user, token));
} }
return token.getToken(); return token.getToken();
......
package com.jh.boot.security.utils; package com.jh.boot.security.utils;
import java.io.StringWriter;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import com.jh.boot.email.Email; import com.jh.boot.email.Email;
import com.jh.boot.email.EmailRecipient;
import com.jh.boot.email.EmailService; import com.jh.boot.email.EmailService;
import com.jh.boot.email.RecipientType;
import com.jh.boot.security.AuthServiceListener; import com.jh.boot.security.AuthServiceListener;
import com.jh.boot.security.model.AppUser; import com.jh.boot.security.model.AppUser;
import com.jh.boot.security.model.ResetPasswordToken;
import com.jh.boot.template.TemplateService; import com.jh.boot.template.TemplateService;
//TODO make it configurable
public class TemplateEmailAuthServiceListener implements AuthServiceListener { public class TemplateEmailAuthServiceListener implements AuthServiceListener {
@Autowired @Autowired
...@@ -23,31 +27,60 @@ public class TemplateEmailAuthServiceListener implements AuthServiceListener { ...@@ -23,31 +27,60 @@ public class TemplateEmailAuthServiceListener implements AuthServiceListener {
@Autowired @Autowired
private TemplateService templateService; private TemplateService templateService;
@Value("${security.auth.template.signup:security.auth.signup}") @Value("${template.email.layout:email.layout}")
private String layoutTemplateCode;
@Value("${template.email.signup.subject:email.signup.subject}")
private String signupSubjectCode;
@Value("${template.email.signup.text:template.email.signup.text}")
private String signupTemplateCode; private String signupTemplateCode;
@Value("${security.auth.template.signup.html:security.auth.signup.html}") @Value("${template.email.signup.html:template.email.signup.html}")
private String signupTemplateHtmlCode; private String signupTemplateHtmlCode;
@Value("${security.auth.template.resetPassword:security.auth.resetPassword}") @Value("${template.email.password.subject:email.password.subject}")
private String resetPasswordTemplateCode; private String passwordSubjectCode;
@Value("${template.email.password.text:email.password.text}")
private String passwordTemplateCode;
@Value("${template.email.password.html:email.password.html}")
private String passwordTemplateHtmlCode;
@Override @Override
public void registerUser(AppUser user) { public void registerUser(AppUser user) {
StringWriter sw = new StringWriter(); sendEmail(signupSubjectCode, signupTemplateCode, signupTemplateHtmlCode, user, Collections.singletonMap("user", user));
templateService.evaluate(sw, signupTemplateCode, Collections.singletonMap("user", user), Locale.ENGLISH);
Email email = new Email();
email.setText(sw.toString());
emailService.sendEmail(email);
} }
@Override @Override
public void generateResetToken(AppUser user) { public void generateResetToken(AppUser user, ResetPasswordToken token) {
StringWriter sw = new StringWriter(); Map<String, Object> data = new HashMap<>();
templateService.evaluate(sw, resetPasswordTemplateCode, Collections.singletonMap("user", user), Locale.ENGLISH); data.put("user", user);
data.put("token", token.getToken());
sendEmail(passwordSubjectCode, passwordTemplateCode, passwordTemplateHtmlCode, user, data);
}
protected void sendEmail(String subject, String content, String html, AppUser user, Map<String, Object> data) {
Locale locale = new Locale(user.getLocale());
subject = templateService.evaluate(subject, data, locale);
content = templateService.evaluate(content, data, locale);
html = templateService.evaluate(html, data, locale);
//is layout defined?
String layout = templateService.evaluate(layoutTemplateCode, Collections.singletonMap("content", html), locale);
Email email = new Email();
email.setRecipients(Collections.singleton(new EmailRecipient(user.getEmail(), RecipientType.TO)));
email.setSubject(subject);
email.setText(content);
email.setHtml(StringUtils.hasText(layout) ? layout : html);
emailService.sendEmail(email);
} }
} }
package com.jh.boot.template;
import org.springframework.context.annotation.Bean;
import com.jh.common.template.handlebars.HandlebarsTemplateService;
public class JhTemplateConfig {
@Bean
public TemplateLoader templateLoader() {
return null;
}
@Bean
public TemplateService templateService() {
HandlebarsTemplateService service = new HandlebarsTemplateService();
service.setTemplateLoader(templateLoader());
return service;
}
}
...@@ -3,12 +3,11 @@ package com.jh.boot.template; ...@@ -3,12 +3,11 @@ package com.jh.boot.template;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.Transient;
import com.jh.boot.jpa.AbstractIdEntity; import com.jh.boot.jpa.AbstractIdEntity;
@Entity @Entity
@Table(name = "CUSTOMIZABLE_TEMPLATE") @Table(name = "TEMPLATE")
public class Template extends AbstractIdEntity { public class Template extends AbstractIdEntity {
@Column(name = "CODE") @Column(name = "CODE")
...@@ -20,10 +19,6 @@ public class Template extends AbstractIdEntity { ...@@ -20,10 +19,6 @@ public class Template extends AbstractIdEntity {
@Column(name = "TEMPLATE") @Column(name = "TEMPLATE")
private String template; private String template;
// @Column(name = "TRANSLATE_NAME")
@Transient
private Boolean translateName;
public Template() { public Template() {
super(); super();
} }
...@@ -57,13 +52,4 @@ public class Template extends AbstractIdEntity { ...@@ -57,13 +52,4 @@ public class Template extends AbstractIdEntity {
this.template = template; this.template = template;
} }
public Boolean getTranslateName() {
return translateName;
}
public void setTranslateName(Boolean translateName) {
this.translateName = translateName;
}
} }
...@@ -3,6 +3,6 @@ package com.jh.boot.template; ...@@ -3,6 +3,6 @@ package com.jh.boot.template;
public interface TemplateLoader { public interface TemplateLoader {
String loadTemplate(String code); Template loadTemplate(String code);
} }
package com.jh.boot.template; package com.jh.boot.template;
import java.io.Writer;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
public interface TemplateService { public interface TemplateService {
void evaluate(Writer writer, String templateCode, Map<String, Object> data, Locale locale); String evaluate(String templateCode, Map<String, Object> data, Locale locale);
} }
package com.jh.boot.template.customizable; package com.jh.boot.template.customizable;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import com.jh.boot.template.Template; import com.jh.boot.template.Template;
import com.jh.boot.template.TemplateLoader; import com.jh.boot.template.TemplateLoader;
...@@ -11,12 +12,9 @@ public class CustomizableTemplateLoader implements TemplateLoader { ...@@ -11,12 +12,9 @@ public class CustomizableTemplateLoader implements TemplateLoader {
private CustomizableLabelsRepository repository; private CustomizableLabelsRepository repository;
@Override @Override
public String loadTemplate(String code) { @Transactional
Template t = repository.fetchTemplate(code); public Template loadTemplate(String code) {
if (t == null) { return repository.fetchTemplate(code);
return null;
}
return t.getTemplate();
} }
} }
package com.jh.boot.template.customizable; package com.jh.boot.template.customizable;
import java.io.Writer; import java.io.StringWriter;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
...@@ -21,7 +21,7 @@ public class CustomizableTemplateService implements TemplateService { ...@@ -21,7 +21,7 @@ public class CustomizableTemplateService implements TemplateService {
@Override @Override
public void evaluate(Writer writer, String templateCode, Map<String, Object> data, Locale locale) { public String evaluate(String templateCode, Map<String, Object> data, Locale locale) {
if (data == null) { if (data == null) {
data = Collections.emptyMap(); data = Collections.emptyMap();
} }
...@@ -30,7 +30,11 @@ public class CustomizableTemplateService implements TemplateService { ...@@ -30,7 +30,11 @@ public class CustomizableTemplateService implements TemplateService {
data = new HashMap<String, Object>(data); data = new HashMap<String, Object>(data);
data.put("labels", labels); data.put("labels", labels);
StringWriter writer = new StringWriter();
engine.evaluate(writer, templateCode, data); engine.evaluate(writer, templateCode, data);
return writer.toString();
} }
} }
...@@ -157,12 +157,13 @@ public class HandlebarsTemplateEngine implements TemplateEngine { ...@@ -157,12 +157,13 @@ public class HandlebarsTemplateEngine implements TemplateEngine {
Template template = useCache ? templateCache.get(name) : null; Template template = useCache ? templateCache.get(name) : null;
if (template == null) { if (template == null) {
String s = templateLoader.loadTemplate(templateCode); com.jh.boot.template.Template t = templateLoader.loadTemplate(templateCode);
if (s == null) { if (t == null) {
throw new RuntimeException(String.format("Template %s not found", templateCode)); // throw new RuntimeException(String.format("Template %s not found", templateCode));
return; //TODO log warning
} }
try { try {
template = handlebars.compileInline(s); template = handlebars.compileInline(t.getTemplate());
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("Cannot evaluate template", e); throw new RuntimeException("Cannot evaluate template", e);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment