Merge lp:~halvdanhg/dhis2/test-email into lp:dhis2

Proposed by Halvdan Hoem Grelland
Status: Merged
Merged at revision: 15774
Proposed branch: lp:~halvdanhg/dhis2/test-email
Merge into: lp:dhis2
Diff against target: 386 lines (+303/-6)
6 files modified
dhis-2/dhis-api/src/main/java/org/hisp/dhis/email/EmailService.java (+73/-0)
dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/email/DefaultEmailService.java (+111/-0)
dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/message/EmailMessageSender.java (+3/-4)
dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml (+6/-0)
dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/EmailController.java (+94/-0)
dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-settings/src/main/webapp/dhis-web-maintenance-settings/systemEmailSettings.vm (+16/-2)
To merge this branch: bzr merge lp:~halvdanhg/dhis2/test-email
Reviewer Review Type Date Requested Status
Lars Helge Øverland Pending
Review via email: mp+223380@code.launchpad.net

Commit message

Implemented blueprint test-email. Added barebones EmailService and new api endpoint email/sendTestEmail

Description of the change

Implements blueprint 'test-email': https://blueprints.launchpad.net/dhis2/+spec/test-email

Notable:
- The existing email API is abstracted as a MessageService wherein the mode of delivery (i.e. internal message, email, sms) for the message is not directly selectable. Using the MessageService was therefore not a very good solution.
- An EmailService has been implemented instead on top of EmailMessageSender. For now it only implements sendEmail(~) and sendTestEmail(), but could be extended to provide more general email related features (configuration etc.) if need be.
- A new API endpoint has been created at api/email/sendTestEmail which sends an automatically generated test message to the current user upon a POST-request. The reply string informs of the success or failure (missing settings) of the request.

Small remark:
Implementation and testing took a loot more time than it should have due to the default spring async executor (SimpleAsyncTaskExecutor) which is used by EmailMessageSender silently ignoring exceptions. This is still the case and could still cause inexplicable and undebuggable problems when sending emails.

Test by going to /dhis-web-maintenance-settings/systemEmailSettings.action and click the link "send me a test email"

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/email'
2=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/email/EmailService.java'
3--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/email/EmailService.java 1970-01-01 00:00:00 +0000
4+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/email/EmailService.java 2014-06-17 10:19:35 +0000
5@@ -0,0 +1,73 @@
6+package org.hisp.dhis.email;
7+
8+/*
9+ * Copyright (c) 2004-2014, University of Oslo
10+ * All rights reserved.
11+ *
12+ * Redistribution and use in source and binary forms, with or without
13+ * modification, are permitted provided that the following conditions are met:
14+ * Redistributions of source code must retain the above copyright notice, this
15+ * list of conditions and the following disclaimer.
16+ *
17+ * Redistributions in binary form must reproduce the above copyright notice,
18+ * this list of conditions and the following disclaimer in the documentation
19+ * and/or other materials provided with the distribution.
20+ * Neither the name of the HISP project nor the names of its contributors may
21+ * be used to endorse or promote products derived from this software without
22+ * specific prior written permission.
23+ *
24+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
25+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
28+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
31+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34+ */
35+
36+import org.hisp.dhis.user.User;
37+
38+import java.util.Set;
39+
40+/**
41+ * @author Halvdan Hoem Grelland <halvdanhg@gmail.com>
42+ */
43+public interface EmailService
44+{
45+ /**
46+ * Checks whether email is configured for the system or not.
47+ * @return true if all necessary email configurations are set.
48+ */
49+ boolean emailEnabled();
50+
51+ /**
52+ * Sends an email to the recipient user from the sender.
53+ *
54+ * @param subject the subject text of the email.
55+ * @param text the text (body) of the email.
56+ * @param sender the sender of the email.
57+ * @param recipient the recipient of the email.
58+ * @param forceSend if true the email is sent regardless of the recipients' email notification settings.
59+ */
60+ void sendEmail( String subject, String text, User sender, User recipient, boolean forceSend );
61+
62+ /**
63+ * Sends an email to multiple recipients from the sender.
64+ *
65+ * @param subject the subject text of the email.
66+ * @param text the text (body) of the email.
67+ * @param sender the sender of the email.
68+ * @param recipients the recipients of the email.
69+ * @param forceSend if true the email is sent regardless of the email notification settings of the recipients.
70+ */
71+ void sendEmail( String subject, String text, User sender, Set<User> recipients, boolean forceSend);
72+
73+ /**
74+ * Sends an automatically generated email message to the current user.
75+ * Useful for testing the SMTP configuration of the system.
76+ */
77+ void sendTestEmail( );
78+}
79
80=== added directory 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/email'
81=== added file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/email/DefaultEmailService.java'
82--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/email/DefaultEmailService.java 1970-01-01 00:00:00 +0000
83+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/email/DefaultEmailService.java 2014-06-17 10:19:35 +0000
84@@ -0,0 +1,111 @@
85+package org.hisp.dhis.email;
86+
87+/*
88+ * Copyright (c) 2004-2014, University of Oslo
89+ * All rights reserved.
90+ *
91+ * Redistribution and use in source and binary forms, with or without
92+ * modification, are permitted provided that the following conditions are met:
93+ * Redistributions of source code must retain the above copyright notice, this
94+ * list of conditions and the following disclaimer.
95+ *
96+ * Redistributions in binary form must reproduce the above copyright notice,
97+ * this list of conditions and the following disclaimer in the documentation
98+ * and/or other materials provided with the distribution.
99+ * Neither the name of the HISP project nor the names of its contributors may
100+ * be used to endorse or promote products derived from this software without
101+ * specific prior written permission.
102+ *
103+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
104+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
105+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
106+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
107+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
108+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
109+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
110+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
111+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
112+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
113+ */
114+
115+import org.apache.commons.logging.Log;
116+import org.apache.commons.logging.LogFactory;
117+import org.hisp.dhis.message.MessageSender;
118+import org.hisp.dhis.setting.SystemSettingManager;
119+import org.hisp.dhis.user.CurrentUserService;
120+import org.hisp.dhis.user.User;
121+import org.springframework.transaction.annotation.Transactional;
122+
123+import java.util.HashSet;
124+import java.util.Set;
125+
126+/**
127+ * @author Halvdan Hoem Grelland <halvdanhg@gmail.com>
128+ */
129+@Transactional
130+public class DefaultEmailService
131+ implements EmailService
132+{
133+ private static final Log log = LogFactory.getLog( DefaultEmailService.class );
134+
135+ private static final String TEST_EMAIL_SUBJECT = "Test email from DHIS 2";
136+ private static final String TEST_EMAIL_TEXT = "This is an automatically generated email from ";
137+
138+ // -------------------------------------------------------------------------
139+ // Dependencies
140+ // -------------------------------------------------------------------------
141+
142+ private MessageSender emailMessageSender;
143+
144+ public void setEmailMessageSender(MessageSender emailMessageSender)
145+ {
146+ this.emailMessageSender = emailMessageSender;
147+ }
148+
149+ private CurrentUserService currentUserService;
150+
151+ public void setCurrentUserService( CurrentUserService currentUserService )
152+ {
153+ this.currentUserService = currentUserService;
154+ }
155+
156+ private SystemSettingManager systemSettingManager;
157+
158+ public void setSystemSettingManager( SystemSettingManager systemSettingManager )
159+ {
160+ this.systemSettingManager = systemSettingManager;
161+ }
162+
163+ // -------------------------------------------------------------------------
164+ // EmailService implementation
165+ // -------------------------------------------------------------------------
166+
167+ @Override
168+ public boolean emailEnabled()
169+ {
170+ return systemSettingManager.emailEnabled();
171+ }
172+
173+ @Override
174+ public void sendEmail( String subject, String text, User sender, User recipient, boolean forceSend )
175+ {
176+ Set<User> recipients = new HashSet<User>( );
177+ recipients.add( recipient );
178+
179+ /* Method is called asynchronously, must therefore re-instantiate HashSet in method call */
180+ emailMessageSender.sendMessage( subject, text, sender, new HashSet<User>( recipients ), forceSend );
181+ }
182+
183+ @Override
184+ public void sendEmail( String subject, String text, User sender, Set<User> recipients, boolean forceSend )
185+ {
186+ emailMessageSender.sendMessage( subject, text, sender, new HashSet<User>( recipients ), forceSend );
187+ }
188+
189+ @Override
190+ public void sendTestEmail( )
191+ {
192+ String instanceName = systemSettingManager.getSystemSetting( systemSettingManager.KEY_APPLICATION_TITLE ).toString();
193+ sendEmail( TEST_EMAIL_SUBJECT, TEST_EMAIL_TEXT + instanceName, null, currentUserService.getCurrentUser(), true );
194+ }
195+}
196
197=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/message/EmailMessageSender.java'
198--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/message/EmailMessageSender.java 2014-05-20 15:16:46 +0000
199+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/message/EmailMessageSender.java 2014-06-17 10:19:35 +0000
200@@ -86,7 +86,7 @@
201 @Async
202 @Override
203 public String sendMessage( String subject, String text, User sender, Set<User> users, boolean forceSend )
204- {
205+ {
206 String hostName = systemSettingManager.getEmailHostName();
207 int port = systemSettingManager.getEmailPort();
208 String username = systemSettingManager.getEmailUsername();
209@@ -132,7 +132,6 @@
210 if ( hasRecipients )
211 {
212 email.send();
213-
214 log.info( "Email sent using host: " + hostName + " with TLS: " + tls );
215 }
216 }
217@@ -140,7 +139,7 @@
218 {
219 log.warn( "Could not send email: " + ex.getMessage() );
220 }
221-
222+
223 return null;
224 }
225
226@@ -151,7 +150,7 @@
227 email.setHostName( hostName );
228 email.setFrom( defaultIfEmpty( sender, FROM_ADDRESS ), FROM_NAME );
229 email.setSmtpPort( port );
230- email.setTLS( true );
231+ email.setTLS( tls );
232
233 if ( username != null && password != null )
234 {
235
236=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml'
237--- dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml 2014-06-16 09:50:27 +0000
238+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml 2014-06-17 10:19:35 +0000
239@@ -637,6 +637,12 @@
240 <property name="userService" ref="org.hisp.dhis.user.UserService" />
241 </bean>
242
243+ <bean id="org.hisp.dhis.email.EmailService" class="org.hisp.dhis.email.DefaultEmailService">
244+ <property name="emailMessageSender" ref="emailMessageSender" />
245+ <property name="currentUserService" ref="org.hisp.dhis.user.CurrentUserService" />
246+ <property name="systemSettingManager" ref="org.hisp.dhis.setting.SystemSettingManager" />
247+ </bean>
248+
249 <bean id="org.hisp.dhis.concept.ConceptService" class="org.hisp.dhis.concept.DefaultConceptService">
250 <property name="conceptStore" ref="org.hisp.dhis.concept.ConceptStore" />
251 </bean>
252
253=== added file 'dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/EmailController.java'
254--- dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/EmailController.java 1970-01-01 00:00:00 +0000
255+++ dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/EmailController.java 2014-06-17 10:19:35 +0000
256@@ -0,0 +1,94 @@
257+package org.hisp.dhis.webapi.controller;
258+
259+/*
260+ * Copyright (c) 2004-2014, University of Oslo
261+ * All rights reserved.
262+ *
263+ * Redistribution and use in source and binary forms, with or without
264+ * modification, are permitted provided that the following conditions are met:
265+ * Redistributions of source code must retain the above copyright notice, this
266+ * list of conditions and the following disclaimer.
267+ *
268+ * Redistributions in binary form must reproduce the above copyright notice,
269+ * this list of conditions and the following disclaimer in the documentation
270+ * and/or other materials provided with the distribution.
271+ * Neither the name of the HISP project nor the names of its contributors may
272+ * be used to endorse or promote products derived from this software without
273+ * specific prior written permission.
274+ *
275+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
276+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
277+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
278+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
279+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
280+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
281+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
282+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
283+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
284+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
285+ */
286+
287+import org.apache.commons.logging.Log;
288+import org.apache.commons.logging.LogFactory;
289+import org.hisp.dhis.email.EmailService;
290+import org.hisp.dhis.user.CurrentUserService;
291+import org.hisp.dhis.webapi.utils.ContextUtils;
292+import org.springframework.beans.factory.annotation.Autowired;
293+import org.springframework.stereotype.Controller;
294+import org.springframework.web.bind.annotation.RequestMapping;
295+import org.springframework.web.bind.annotation.RequestMethod;
296+import org.springframework.web.bind.annotation.ResponseBody;
297+
298+import javax.servlet.http.HttpServletRequest;
299+import javax.servlet.http.HttpServletResponse;
300+
301+/**
302+ * @author Halvdan Hoem Grelland <halvdanhg@gmail.com>
303+ */
304+@Controller
305+@RequestMapping( value = EmailController.RESOURCE_PATH )
306+public class EmailController
307+{
308+ private static final Log log = LogFactory.getLog( EmailController.class );
309+
310+ public static final String RESOURCE_PATH = "/email";
311+
312+ //--------------------------------------------------------------------------
313+ // Dependencies
314+ //--------------------------------------------------------------------------
315+
316+ @Autowired
317+ private EmailService emailService;
318+
319+ @Autowired
320+ private CurrentUserService currentUserService;
321+
322+ @RequestMapping( value = "/sendTestEmail" , method = RequestMethod.POST, produces = ContextUtils.CONTENT_TYPE_TEXT )
323+ public @ResponseBody String sendTestEmail( HttpServletRequest request, HttpServletResponse response )
324+ {
325+ String responseMessage;
326+ String userEmail = currentUserService.getCurrentUser().getEmail();
327+ boolean smtpConfigured = emailService.emailEnabled();
328+ boolean userEmailConfigured = userEmail != null && !userEmail.isEmpty();
329+
330+ if( smtpConfigured && userEmailConfigured )
331+ {
332+ response.setStatus( HttpServletResponse.SC_OK );
333+ emailService.sendTestEmail( );
334+
335+ responseMessage = "A test email was sent to " + userEmail;
336+ }
337+ else if( userEmailConfigured )
338+ {
339+ response.setStatus( HttpServletResponse.SC_ACCEPTED );
340+ responseMessage = "Could not send test email to " + userEmail + ": SMTP is not configured";
341+ }
342+ else /* smtpConfigured */
343+ {
344+ response.setStatus( HttpServletResponse.SC_ACCEPTED );
345+ responseMessage = "Could not send test email: no user email address configured";
346+ }
347+
348+ return responseMessage;
349+ }
350+}
351
352=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-settings/src/main/webapp/dhis-web-maintenance-settings/systemEmailSettings.vm'
353--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-settings/src/main/webapp/dhis-web-maintenance-settings/systemEmailSettings.vm 2014-05-20 15:16:46 +0000
354+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-settings/src/main/webapp/dhis-web-maintenance-settings/systemEmailSettings.vm 2014-06-17 10:19:35 +0000
355@@ -15,8 +15,17 @@
356 });
357 });
358
359- jQuery( '#smtpHostName' ).blur();
360+ jQuery( '#smtpHostName' ).blur();
361+
362+ jQuery( "#sendTestEmail" ).click( function ( e ){
363+ e.preventDefault();
364+ jQuery.post( '/api/email/sendTestEmail',
365+ function( reply ) {
366+ setHeaderDelayMessage ( reply );
367+ });
368+ });
369 });
370+
371 </script>
372
373 <h3>$i18n.getString( "smtp_settings" ) #openHelp( "systemEmailSettings" )</h3>
374@@ -54,6 +63,11 @@
375
376 <div class="setting"><input type="text" id="emailSender" name="emailSender" value="$!emailSender" autocomplete="off" placeholder="noreply&#64;dhis2.org"></div>
377
378-<div class="setting"><input type="button" value="$i18n.getString( 'save' )" style="width:10em"></div>
379+<div class="setting">
380+ <input type="button" value="$i18n.getString( 'save' )" style="width:10em">
381+ <span style="margin-left: 1em;"><a id="sendTestEmail" href="send-test-email" title="Send an automatically generated email using the supplied SMTP settings to your email address">Send me a test email</a></span>
382+</div>
383
384 </form>
385+
386+