Skip to content

Commit eb9c6f7

Browse files
committed
new features: mail service, warning message when no data to display
inside a result grid,
1 parent d2739ff commit eb9c6f7

15 files changed

+361
-79
lines changed

pom.xml

+14
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@
8888
<hibernate.ehcache.configuration.file>ehcache.xml</hibernate.ehcache.configuration.file>
8989
<hibernate.use.second.level.cache>false</hibernate.use.second.level.cache>
9090
<hibernate.use.query.cache>false</hibernate.use.query.cache>
91+
92+
<!--spring-data-releasetrain.version>Hopper-SR1</spring-data-releasetrain.version-->
9193
</properties>
9294

9395
<dependencies>
@@ -156,6 +158,18 @@
156158
<scope>test</scope>
157159
</dependency>
158160

161+
<!-- for sending mail -->
162+
<dependency>
163+
<groupId>org.springframework.boot</groupId>
164+
<artifactId>spring-boot-starter-mail</artifactId>
165+
</dependency>
166+
167+
<!-- for email templates -->
168+
<dependency>
169+
<groupId>org.springframework.boot</groupId>
170+
<artifactId>spring-boot-starter-thymeleaf</artifactId>
171+
</dependency>
172+
159173
<dependency>
160174
<groupId>javax.inject</groupId>
161175
<artifactId>javax.inject</artifactId>

src/main/celerio/angularjs/assets/js/admin/translations.p.vm.js

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ app.config(['${dollar}translateProvider', function (${dollar}translateProvider)
3838
'MORE_TOOLTIP': 'Click here to see more',
3939
'MORE': 'More',
4040
'LESS': 'Less',
41+
'NO_DATA': 'There isn\'t record for this entity !',
4142

4243
// menu
4344
'MENU_ENTITIES': 'Entities',

src/main/celerio/angularjs/assets/tpl/apps/entity.e.vm.html

+5
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ <h3 class="table-title">{{data.length}} of {{pagination.totalElements}} {{settin
7474
</tr>
7575
</thead>
7676
<tbody>
77+
78+
<tr ng-show="data.length == 0">
79+
<td colspan="1" align="center"><a ng-click="searchItem()">{{ 'NO_DATA' | translate }}</a></td>
80+
</tr>
81+
7782
<tr ng-repeat="(key, item) in data" ng-click="item.selected=!item.selected" ng-dblclick="editItem(item)">
7883
<td><input type="checkbox" class="relative" ng-model="item.selected" ng-click="${dollar}event.stopPropagation()" /></td>
7984
<td ng-bind-html="item.icon" class="f20"></td>

src/main/celerio/springboot/src/main/java/config/ApplicationProperties.p.vm.java

+20
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,17 @@
1313
public class ApplicationProperties {
1414

1515
private final Async async = new Async();
16+
17+
private final Mail mail = new Mail();
1618

1719
public Async getAsync() {
1820
return async;
1921
}
2022

23+
public Mail getMail() {
24+
return mail;
25+
}
26+
2127
/**
2228
* Properties linked to Spring Boot asynchronous engine.
2329
*
@@ -54,4 +60,18 @@ public void setQueueCapacity(int queueCapacity) {
5460
this.queueCapacity = queueCapacity;
5561
}
5662
}
63+
64+
public static class Mail {
65+
66+
// FIXME: hard coded
67+
private String from = "application@localhost";
68+
69+
public String getFrom() {
70+
return from;
71+
}
72+
73+
public void setFrom(String from) {
74+
this.from = from;
75+
}
76+
}
5777
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
$output.java("${configuration.rootPackage}.config", "LocaleConfiguration")##
2+
3+
$output.require("org.springframework.boot.bind.RelaxedPropertyResolver")##
4+
$output.require("org.springframework.context.EnvironmentAware")##
5+
$output.require("org.springframework.context.MessageSource")##
6+
$output.require("org.springframework.context.annotation.Bean")##
7+
$output.require("org.springframework.context.annotation.Configuration")##
8+
$output.require("org.springframework.context.support.ReloadableResourceBundleMessageSource")##
9+
$output.require("org.springframework.core.env.Environment")##
10+
$output.require("org.springframework.web.servlet.LocaleResolver")##
11+
$output.require("org.springframework.web.servlet.config.annotation.InterceptorRegistry")##
12+
$output.require("org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter")##
13+
$output.require("org.springframework.web.servlet.i18n.LocaleChangeInterceptor")##
14+
15+
$output.require("com.jaxio.demo.config.locale.AngularCookieLocaleResolver")##
16+
17+
/**
18+
* Configure i18n messages for server side.
19+
*
20+
*/
21+
@Configuration
22+
public class LocaleConfiguration extends WebMvcConfigurerAdapter implements EnvironmentAware {
23+
24+
private RelaxedPropertyResolver propertyResolver;
25+
26+
@Override
27+
public void setEnvironment(Environment environment) {
28+
this.propertyResolver = new RelaxedPropertyResolver(environment, "spring.messages.");
29+
}
30+
31+
@Bean(name = "localeResolver")
32+
public LocaleResolver localeResolver() {
33+
AngularCookieLocaleResolver cookieLocaleResolver = new AngularCookieLocaleResolver();
34+
cookieLocaleResolver.setCookieName("NG_TRANSLATE_LANG_KEY");
35+
return cookieLocaleResolver;
36+
}
37+
38+
@Bean
39+
public MessageSource messageSource() {
40+
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
41+
messageSource.setBasename("classpath:/i18n/messages");
42+
messageSource.setDefaultEncoding("UTF-8");
43+
messageSource.setCacheSeconds(propertyResolver.getProperty("cache-seconds", Integer.class, -1));
44+
return messageSource;
45+
}
46+
47+
@Override
48+
public void addInterceptors(InterceptorRegistry registry) {
49+
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
50+
localeChangeInterceptor.setParamName("language");
51+
registry.addInterceptor(localeChangeInterceptor);
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
$output.java("${configuration.rootPackage}.config", "ThymeleafConfiguration")##
2+
3+
$output.require("org.apache.commons.lang.CharEncoding")##
4+
$output.require("org.slf4j.Logger")##
5+
$output.require("org.slf4j.LoggerFactory")##
6+
$output.require("org.springframework.context.MessageSource")##
7+
$output.require("org.springframework.context.annotation.*")##
8+
$output.require("org.springframework.context.support.ReloadableResourceBundleMessageSource")##
9+
$output.require("org.thymeleaf.templateresolver.ClassLoaderTemplateResolver")##
10+
11+
/**
12+
* Configures Thymeleaf framework, especially templates directory
13+
*
14+
*/
15+
@Configuration
16+
public class ThymeleafConfiguration {
17+
18+
private final Logger log = LoggerFactory.getLogger(ThymeleafConfiguration.class);
19+
20+
@Bean
21+
@Description("Thymeleaf template resolver serving HTML 5 emails")
22+
public ClassLoaderTemplateResolver emailTemplateResolver() {
23+
ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver();
24+
emailTemplateResolver.setPrefix("mails/");
25+
emailTemplateResolver.setSuffix(".html");
26+
emailTemplateResolver.setTemplateMode("HTML5");
27+
emailTemplateResolver.setCharacterEncoding(CharEncoding.UTF_8);
28+
emailTemplateResolver.setOrder(1);
29+
return emailTemplateResolver;
30+
}
31+
32+
@Bean
33+
@Description("Spring mail message resolver")
34+
public MessageSource emailMessageSource() {
35+
log.info("loading non-reloadable mail messages resources");
36+
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
37+
messageSource.setBasename("classpath:/mails/messages/messages");
38+
messageSource.setDefaultEncoding(CharEncoding.UTF_8);
39+
return messageSource;
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
$output.java("${configuration.rootPackage}.config.locale", "AngularCookieLocaleResolver")##
2+
3+
$output.require("org.springframework.context.i18n.LocaleContext")##
4+
$output.require("org.springframework.context.i18n.TimeZoneAwareLocaleContext")##
5+
$output.require("org.springframework.util.StringUtils")##
6+
$output.require("org.springframework.web.servlet.i18n.CookieLocaleResolver")##
7+
$output.require("org.springframework.web.util.WebUtils")##
8+
9+
$output.require("java.util.Locale")##
10+
$output.require("java.util.TimeZone")##
11+
12+
$output.require("javax.servlet.http.*")##
13+
14+
/**
15+
* Angular cookie saved the locale with a double quote (%22en%22).
16+
* So the default CookieLocaleResolver#StringUtils.parseLocaleString(localePart)
17+
* is not able to parse the locale.
18+
*
19+
* This class will check if a double quote has been added, if so it will remove it.
20+
*/
21+
public class AngularCookieLocaleResolver extends CookieLocaleResolver {
22+
23+
@Override
24+
public Locale resolveLocale(HttpServletRequest request) {
25+
parseLocaleCookieIfNecessary(request);
26+
return (Locale) request.getAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME);
27+
}
28+
29+
@Override
30+
public LocaleContext resolveLocaleContext(final HttpServletRequest request) {
31+
parseLocaleCookieIfNecessary(request);
32+
return new TimeZoneAwareLocaleContext() {
33+
@Override
34+
public Locale getLocale() {
35+
return (Locale) request.getAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME);
36+
}
37+
38+
@Override
39+
public TimeZone getTimeZone() {
40+
return (TimeZone) request.getAttribute(TIME_ZONE_REQUEST_ATTRIBUTE_NAME);
41+
}
42+
};
43+
}
44+
45+
@Override
46+
public void addCookie(HttpServletResponse response, String cookieValue) {
47+
// Mandatory cookie modification for angular to support the locale switching on the server side.
48+
cookieValue = "%22" + cookieValue + "%22";
49+
super.addCookie(response, cookieValue);
50+
}
51+
52+
private void parseLocaleCookieIfNecessary(HttpServletRequest request) {
53+
if (request.getAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME) == null) {
54+
// Retrieve and parse cookie value.
55+
Cookie cookie = WebUtils.getCookie(request, getCookieName());
56+
Locale locale = null;
57+
TimeZone timeZone = null;
58+
if (cookie != null) {
59+
String value = cookie.getValue();
60+
61+
// Remove the double quote
62+
value = StringUtils.replace(value, "%22", "");
63+
64+
String localePart = value;
65+
String timeZonePart = null;
66+
int spaceIndex = localePart.indexOf(' ');
67+
if (spaceIndex != -1) {
68+
localePart = value.substring(0, spaceIndex);
69+
timeZonePart = value.substring(spaceIndex + 1);
70+
}
71+
locale = (!"-".equals(localePart) ? StringUtils.parseLocaleString(localePart.replace('-', '_')) : null);
72+
if (timeZonePart != null) {
73+
timeZone = StringUtils.parseTimeZoneString(timeZonePart);
74+
}
75+
if (logger.isTraceEnabled()) {
76+
logger.trace("Parsed cookie value [" + cookie.getValue() + "] into locale '" + locale +
77+
"'" + (timeZone != null ? " and time zone '" + timeZone.getID() + "'" : ""));
78+
}
79+
}
80+
request.setAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME,
81+
(locale != null ? locale: determineDefaultLocale(request)));
82+
83+
request.setAttribute(TIME_ZONE_REQUEST_ATTRIBUTE_NAME,
84+
(timeZone != null ? timeZone : determineDefaultTimeZone(request)));
85+
}
86+
}
87+
}

src/main/celerio/springboot/src/main/java/rest/EntityResource.e.vm.java

+21-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
$output.require("org.springframework.jdbc.core.BeanPropertyRowMapper")##
5858
$output.require("org.springframework.beans.factory.annotation.Autowired")##
5959
$output.require("org.springframework.data.domain.PageImpl")##
60+
$output.require("org.springframework.data.domain.PageRequest")##
6061
6162
@RestController
6263
@RequestMapping("/api/${entity.model.vars}")
@@ -261,9 +262,28 @@ public ResponseEntity<Void> delete(@PathVariable $entity.primaryKey.type[] id) t
261262
produces = MediaType.APPLICATION_JSON_VALUE)
262263
@Async
263264
public void indexAll${entity.model.varsUp}() {
264-
log.debug("REST request to index all $entity.model.varsUp");
265+
log.debug("REST request to index all $entity.model.varsUp, START");
265266
#if (($entity.hasSimplePk()))
266267
${entity.model.var}Repository.findAll().forEach(p -> {log.debug("indexing");${entity.model.var}SearchRepository.index(p);});
268+
269+
PageRequest request = new PageRequest(0, 1000);
270+
try {
271+
Page<$entity.model.type> page = findAllByPage(request);
272+
page.forEach(p -> ${entity.model.var}SearchRepository.index(p));
273+
274+
while (page.hasNext()) {
275+
request = new PageRequest(request.getPageNumber() + 1, 1000);
276+
277+
log.debug("we are indexing page: " + (request.getPageNumber() + 1));
278+
279+
page = findAllByPage(request);
280+
page.forEach(p -> ${entity.model.var}SearchRepository.index(p));
281+
}
282+
} catch (Exception e) {
283+
log.error("", e);
284+
}
285+
286+
log.debug("REST request to index all $entity.model.varsUp, EXIT");
267287
#end
268288
}
269289

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
$output.java("${configuration.rootPackage}.service","MailService")##
2+
3+
$output.require("org.apache.commons.lang.CharEncoding")##
4+
$output.require("org.slf4j.Logger")##
5+
$output.require("org.slf4j.LoggerFactory")##
6+
$output.require("org.springframework.context.MessageSource")##
7+
$output.require("org.springframework.mail.javamail.JavaMailSenderImpl")##
8+
$output.require("org.springframework.mail.javamail.MimeMessageHelper")##
9+
$output.require("org.springframework.scheduling.annotation.Async")##
10+
$output.require("org.springframework.stereotype.Service")##
11+
$output.require("org.thymeleaf.context.Context")##
12+
$output.require("org.thymeleaf.spring4.SpringTemplateEngine")##
13+
14+
$output.require("com.jaxio.demo.config.ApplicationProperties")##
15+
16+
$output.require("javax.inject.Inject")##
17+
$output.require("javax.mail.internet.MimeMessage")##
18+
$output.require("java.util.Locale")##
19+
20+
/**
21+
* Service for sending e-mails.
22+
* <p/>
23+
* <p>
24+
* We use the @Async annotation to send e-mails asynchronously.
25+
* </p>
26+
*/
27+
@Service
28+
public class MailService {
29+
30+
private final Logger log = LoggerFactory.getLogger(MailService.class);
31+
32+
@Inject
33+
private ApplicationProperties applicationProperties;
34+
35+
@Inject
36+
private JavaMailSenderImpl javaMailSender;
37+
38+
@Inject
39+
private MessageSource messageSource;
40+
41+
@Inject
42+
private SpringTemplateEngine templateEngine;
43+
44+
@Async
45+
public void sendEmail(String to, String subject, String content, boolean isMultipart, boolean isHtml) {
46+
log.debug("Send e-mail[multipart '{}' and html '{}'] to '{}' with subject '{}' and content={}",
47+
isMultipart, isHtml, to, subject, content);
48+
49+
// Prepare message using a Spring helper
50+
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
51+
try {
52+
MimeMessageHelper message = new MimeMessageHelper(mimeMessage, isMultipart, CharEncoding.UTF_8);
53+
message.setTo(to);
54+
message.setFrom(applicationProperties.getMail().getFrom());
55+
message.setSubject(subject);
56+
message.setText(content, isHtml);
57+
javaMailSender.send(mimeMessage);
58+
log.debug("Sent e-mail to User '{}'", to);
59+
} catch (Exception e) {
60+
log.warn("E-mail could not be sent to user '{}', exception is: {}", to, e.getMessage());
61+
}
62+
}
63+
64+
@Async
65+
public void sendWelcomeEmail(String to, Locale locale) {
66+
log.debug("Sending activation e-mail to '{}'", to);
67+
68+
// add variables to template
69+
Context context = new Context(locale);
70+
//context.setVariable("varName", var);
71+
72+
String content = templateEngine.process("welcomeEmail", context);
73+
String subject = messageSource.getMessage("email.welcome.title", null, locale);
74+
75+
sendEmail(to, subject, content, false, true);
76+
}
77+
}

0 commit comments

Comments
 (0)