Add documentation about E-mail sending functionality.

Update JUnit version.
Code refactoring.
Imports optimizations.
Changes to UI of E-mail Templates page.
New values for i18n.
Validations of E-mail functionality.
This commit is contained in:
Vova Perebykivskiy 2015-11-11 15:45:24 +02:00 committed by Vova Perebykivskiy
parent 878e067683
commit ffca70fbea
14 changed files with 267 additions and 63 deletions

View file

@ -506,9 +506,9 @@ example:
mvn -Ddefault.passwordsControl=false -Ddefault.exampleUsersDisabled=false clean install
* Set *default.emailSendingDisabled* to true::
* Set *default.emailSendingEnabled* to false::
mvn -Ddefault.emailSendingDisabled=true clean install
mvn -Ddefault.emailSendingEnabled=false clean install
Tests
-----

View file

@ -4,7 +4,7 @@ Connectors
.. contents::
Connectors are Libreplan client applications that could be used to communicate with (web) servers to get
data, process and store them. At this moment there are two connectors, JIRA connector and Tim Enterprise Connector.
data, process and store them. At this moment there are three connectors, JIRA connector, Tim Enterprise Connector and E-mail Connector.
Configuration
=============
@ -171,9 +171,9 @@ in Tim Enterprise server.
Scheduling export
------------------
Export process can also take place through the scheduler. Go to ``Job scheduling`` screen.
Export process can also take place through the scheduler. Go to ``Job Scheduling`` screen.
In that screen you can configure a Tim Export ``job``. The ``job`` searches for last exported
time sheets in the database and re-export them accordingly. see also the Scheduler manual.
time sheets in the database and re-export them accordingly. See also the Scheduler manual.
Import
------
@ -211,5 +211,66 @@ assumed to be a valid ``Exception type`` but the total duration is the sum of al
Contrary to the Libreplan, in Tim Enterprise, the ``total duration`` in case that the worker is on holiday means the worker is
not available for that ``total duration``. But in Libreplan if the worker is on holiday the total duration should be ``Zero``.
The Tim connector also takes care of this translation.
The Tim connector also takes care of this translation.
E-mail connector
==============
E-mail is a method of exchanging digital messages from an author to one or more recipients.
E-mail connector can be used to set Simple Main Transfer Protocol (SMTP) server connection properties.
The *E-mail connector* should be configured properly before being used.
Configuration
-------------
From the configuration's ``Main Settings`` screen choose the tab ``Connectors``.
In the connectors screen select the E-mail connector from the ``pull-down`` list. A ``property editor screen``
is displayed now.
In this screen you can configure the following property values:
* ``Activated``: Y/N, whether you want to use the E-mail connector or not. Default is ``N``.
* ``Protocol``: type of SMTP protocol.
* ``Host``: the absolute path to SMTP server.
* ``Port``: port of SMTP server.
* ``From address``: e-mail address of messages sender.
* ``Username``: username for SMTP server.
* ``Password``: password for SMTP server.
Finally click the ``Test connection`` button to test if you are able to connect to
SMTP server and that your configurations are right.
Edit E-mail template
--------------------
From the project window ``Configuration`` and then ``Edit E-mail Templates`` you are able to modify E-mail templates of
messages.
You are able to choose:
* Template language
* Template type
* E-mail subject
* Template contents
You need to specify language because web application will send e-mail to user in language that user have chosen in
preferences.
You need to choose template type, type is user role, it means that this e-mail will be send only to users who are in\
selected role (type).
You need to set e-mail subject. Subject - a brief summary of the topic of the message.
You need to set e-mail contents. Any information that you want to send to user. Also there are some keywords that you
may use in message; web application will parse it and set a new value instead of keyword.
Scheduling e-mails
------------------
Sending e-mails process can take place only through the scheduler. Go to ``Configuration`` then ``Job Scheduling``
screen.
In that screen you can configure a e-mail sending ``job``. The ``job`` is taking a list of e-mail notifications,
gathering data and sending it to user`s e-mail. See also the Scheduler manual.
.. NOTE::
The success or failure information would be displayed in pop-up window.

View file

@ -47,7 +47,7 @@ public class Configuration {
private Boolean exampleUsersDisabled;
private Boolean emailSendingDisabled;
private Boolean emailSendingEnabled;
private Configuration() {
}
@ -89,14 +89,14 @@ public class Configuration {
/**
* Returns the value of E-mail sending disabled compilation option
*/
public static boolean isEmailSendingDisabled(){
return BooleanUtils.isNotFalse(singleton.getEmailSendingDisabled());
public static boolean isEmailSendingEnabled(){
return BooleanUtils.isNotFalse(singleton.getEmailSendingEnabled());
}
public Boolean getEmailSendingDisabled(){
return emailSendingDisabled;
public Boolean getEmailSendingEnabled(){
return emailSendingEnabled;
}
public void setEmailSendingDisabled(Boolean emailSendingDisabled){
this.emailSendingDisabled = emailSendingDisabled;
public void setEmailSendingEnabled(Boolean emailSendingEnabled){
this.emailSendingEnabled = emailSendingEnabled;
}
}

View file

@ -46,7 +46,6 @@ public class EmailNotificationDAO extends GenericDAOHibernate<EmailNotification,
for (Object item : notifications){
getSession().delete(item);
}
getSession().getTransaction().commit();
if ( list(EmailNotification.class).size() == 0 ) return true;
return false;

View file

@ -146,8 +146,8 @@
<property name="exampleUsersDisabled">
<value>${default.exampleUsersDisabled}</value>
</property>
<property name="emailSendingDisabled">
<value>${default.emailSendingDisabled}</value>
<property name="emailSendingEnabled">
<value>${default.emailSendingEnabled}</value>
</property>
</bean>

View file

@ -47,6 +47,8 @@ import javax.mail.Session;
import javax.mail.PasswordAuthentication;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.NoSuchProviderException;
import java.util.List;
import java.util.Locale;
@ -78,13 +80,14 @@ public class SendEmail implements ISendEmail {
private List<EmailNotification> notifications;
private List<EmailTemplate> emailTemplates;
@Override
public void sendEmail() {
if ( !Configuration.isEmailSendingDisabled() ){
notifications = emailNotificationModel.getAll();
for (int i = 0; i < notifications.size(); i++) composeMessageForUser(notifications.get(i));
deleteAllNotificationsAfterSending();
if ( Configuration.isEmailSendingEnabled() == true ){
if (validConnection() == true){
notifications = emailNotificationModel.getAll();
for (int i = 0; i < notifications.size(); i++) composeMessageForUser(notifications.get(i));
deleteAllNotificationsAfterSending();
}
}
}
@ -199,7 +202,9 @@ public class SendEmail implements ISendEmail {
Transport.send(message);
}catch (MessagingException e){throw new RuntimeException(e);}
} catch (MessagingException e){throw new RuntimeException(e);}
}
@ -231,4 +236,67 @@ public class SendEmail implements ISendEmail {
return workerList.get(i);
return null;
}
private boolean validConnection(){
List<ConnectorProperty> emailConnectorProperties = getEmailConnectorProperties();
String protocol = null;
String host = null;
String port = null;
String usrnme = null;
String psswrd = null;
for (int i = 0; i < emailConnectorProperties.size(); i++){
switch (i){
case 1: {
protocol = emailConnectorProperties.get(1).getValue();
break;
}
case 2: {
host = emailConnectorProperties.get(2).getValue();
break;
}
case 3: {
port = emailConnectorProperties.get(3).getValue();
break;
}
case 5: {
usrnme = emailConnectorProperties.get(5).getValue();
break;
}
case 6: {
psswrd = emailConnectorProperties.get(6).getValue();
break;
}
}
}
// Set properties of connection
Properties properties = new Properties();
Transport transport = null;
try {
if (protocol.equals("SMTP")) {
properties.setProperty("mail.smtp.port", port);
properties.setProperty("mail.smtp.host", host);
Session session = Session.getInstance(properties, null);
transport = session.getTransport("smtp");
if (usrnme.equals("") && psswrd.equals("")) transport.connect();
} else if (protocol.equals("STARTTLS")) {
properties.setProperty("mail.smtps.port", port);
properties.setProperty("mail.smtps.host", host);
Session session = Session.getInstance(properties, null);
transport = session.getTransport("smtps");
if (!usrnme.equals("") && psswrd != null) transport.connect(host, usrnme, psswrd);
}
if (transport.isConnected()) return true;
} catch (NoSuchProviderException e) {}
catch (MessagingException e) {}
return false;
}
}

View file

@ -155,6 +155,8 @@ public class ConfigurationController extends GenericForwardComposer {
private Textbox emailPasswordTextbox;
private Textbox emailSenderTextbox;
@Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
@ -240,8 +242,8 @@ public class ConfigurationController extends GenericForwardComposer {
public void save() throws InterruptedException {
if ( getSelectedConnector().getName().equals("E-mail") && emailUsernamePasswordIsEmpty() == true) {
messages.showMessage(Level.ERROR, _("E-mail username/password - empty"));
if ( getSelectedConnector().getName().equals("E-mail") && isEmailFieldsValid() == false) {
messages.showMessage(Level.ERROR, _("Check username/password/sender fields"));
} else {
ConstraintChecker.isValid(configurationWindow);
if (checkValidEntitySequenceRows()) {
@ -1208,13 +1210,16 @@ public class ConfigurationController extends GenericForwardComposer {
textbox.setType("password");
}
// Need for method emailUsernamePasswordIsEmpty()
// Need for method validateEmailFields()
if ( property.getKey().equals(
PredefinedConnectorProperties.EMAIL_USERNAME) ) emailUsernameTextbox = textbox;
if ( property.getKey().equals(
PredefinedConnectorProperties.EMAIL_PASSWORD) ) emailPasswordTextbox = textbox;
if ( property.getKey().equals(
PredefinedConnectorProperties.EMAIL_SENDER) ) emailSenderTextbox = textbox;
row.appendChild(textbox);
}
@ -1285,26 +1290,24 @@ public class ConfigurationController extends GenericForwardComposer {
"Only {0} allowed", "Y/N"));
}
} else if ( key
.equals(PredefinedConnectorProperties.SERVER_URL)
|| key.equals(PredefinedConnectorProperties.USERNAME)
|| key.equals(PredefinedConnectorProperties.PASSWORD)
|| key.equals(PredefinedConnectorProperties.JIRA_HOURS_TYPE) ) {
.equals(PredefinedConnectorProperties.SERVER_URL) ||
key.equals(PredefinedConnectorProperties.USERNAME) ||
key.equals(PredefinedConnectorProperties.PASSWORD) ||
key.equals(PredefinedConnectorProperties.JIRA_HOURS_TYPE) ||
key.equals(PredefinedConnectorProperties.HOST) ||
key.equals(PredefinedConnectorProperties.PORT) ||
key.equals(PredefinedConnectorProperties.EMAIL_SENDER) ) {
((InputElement) comp).setConstraint("no empty:"
+ _("cannot be empty"));
} else if ( key
.equals(PredefinedConnectorProperties.TIM_NR_DAYS_TIMESHEET)
|| key.equals(PredefinedConnectorProperties.TIM_NR_DAYS_ROSTER) ) {
.equals(PredefinedConnectorProperties.TIM_NR_DAYS_TIMESHEET) ||
key.equals(PredefinedConnectorProperties.TIM_NR_DAYS_ROSTER) ||
key.equals(PredefinedConnectorProperties.PORT) ) {
if ( !isNumeric((String) value) ) {
throw new WrongValueException(comp,
_("Only digits allowed"));
}
}
// Validate E-mail connector
if ( key.equals(PredefinedConnectorProperties.HOST) ||
key.equals(PredefinedConnectorProperties.PORT) ||
key.equals(PredefinedConnectorProperties.EMAIL_SENDER) )
((InputElement) comp).setConstraint("no empty:" + _("cannot be empty"));
}
};
}
@ -1321,16 +1324,17 @@ public class ConfigurationController extends GenericForwardComposer {
};
}
private boolean emailUsernamePasswordIsEmpty(){
private boolean isEmailFieldsValid(){
if ( protocolsCombobox.getSelectedItem().getLabel().equals("STARTTLS") &&
emailUsernameTextbox.getValue() != null &&
emailPasswordTextbox.getValue() != null &&
emailUsernameTextbox.getValue().length() != 0 &&
emailPasswordTextbox.getValue().length() != 0 )
return false;
emailPasswordTextbox.getValue().length() != 0 &&
emailSenderTextbox.getValue().matches("^\\S+@\\S+\\.\\S+$") )
return true;
else return true;
else return false;
}
}

View file

@ -77,6 +77,7 @@ public class EmailNotificationModel implements IEmailNotificationModel {
}
@Override
@Transactional
public boolean deleteAll() {
return emailNotificationDAO.deleteAll();
}

View file

@ -41,6 +41,7 @@ import java.util.List;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import static org.libreplan.web.I18nHelper._;
@ -69,9 +70,6 @@ public class EmailTemplateController extends GenericForwardComposer{
throws Exception {
Language language = (Language) data;
String displayName = language.getDisplayName();
if (language.equals(Language.BROWSER_LANGUAGE)) {
displayName = _(language.getDisplayName());
}
item.setLabel(displayName);
}
};
@ -117,7 +115,7 @@ public class EmailTemplateController extends GenericForwardComposer{
return languagesRenderer;
}
public List<Language> getLanguages() {
List<Language> languages = Arrays.asList(Language.values());
List<Language> languages = new LinkedList<Language>(Arrays.asList(Language.values()));
Collections.sort(languages, new Comparator<Language>() {
@Override
public int compare(Language o1, Language o2) {
@ -130,6 +128,7 @@ public class EmailTemplateController extends GenericForwardComposer{
return o1.getDisplayName().compareTo(o2.getDisplayName());
}
});
languages.remove(0);
return languages;
}

View file

@ -7764,6 +7764,7 @@ msgstr ""
#: libreplan-webapp/src/main/webapp/orders/_orderElementDetails.zul:59
#: libreplan-webapp/src/main/webapp/qualityforms/_listQualityForm.zul:37
#: libreplan-webapp/src/main/webapp/qualityforms/_editQualityForm.zul:49
#: libreplan-webapp/src/main/webapp/email/email_templates.zul:83
msgid "Description"
msgstr ""
@ -9235,10 +9236,46 @@ msgstr ""
msgid "Protocol"
msgstr ""
#: libreplan-webapp/src/main/webapp/email/email_templates.zul:56
msgid "Possible content values:"
#: libreplan-webapp/src/main/webapp/email/email_templates.zul:79
msgid "Possible content keywords"
msgstr ""
#: libreplan-webapp/src/main/webapp/java/org/libreplan/web/planner/tabs/MultipleTabsPlannerController:90
msgid "-- no URL provided --"
msgstr ""
#: libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationController.java:1307
msgid "Not correct E-mail address"
msgstr ""
#: libreplan-webapp/src/main/webapp/email/email_templates.zul:82
msgid "Keyword"
msgstr ""
#: libreplan-webapp/src/main/webapp/email/email_templates.zul:89
msgid "Username of user"
msgstr ""
#: libreplan-webapp/src/main/webapp/email/email_templates.zul:94
msgid "First name of user"
msgstr ""
#: libreplan-webapp/src/main/webapp/email/email_templates.zul:99
msgid "Last name of user"
msgstr ""
#: libreplan-webapp/src/main/webapp/email/email_templates.zul:104
msgid "Name of project"
msgstr ""
#: libreplan-webapp/src/main/webapp/email/email_templates.zul:109
msgid "Name of resource"
msgstr ""
#: libreplan-webapp/src/main/webapp/email/email_templates.zul:114
msgid "Name of task"
msgstr ""
#: libreplan-webapp/src/main/webapp/email/email_templates.zul:119
msgid "Welcome page"
msgstr ""

View file

@ -97,7 +97,7 @@
<columns>
<column label="${i18n:_('Field')}" width="150px"/>
<column label="${i18n:_('Allowed values')}" />
<column label="${i18n:_('Allowed Special Characters')}" />
<column label="${i18n:_('Allowed Special Characters')}" />
</columns>
<rows>
<row>

View file

@ -74,17 +74,53 @@
<textbox id="contentsTextbox"
rows="15" width="400px;"
tabindex="11"/>
<vbox>
<label value="${i18n:_('Possible content values:')}"/>
<label value="${i18n:_('{username}')}"/>
<label value="${i18n:_('{firstname}')}"/>
<label value="${i18n:_('{lastname}')}"/>
<label value="${i18n:_('{project}')}"/>
<label value="${i18n:_('{resource}')}"/>
<label value="${i18n:_('{task}')}"/>
<label value="${i18n:_('{url}')}"/>
</vbox>
<groupbox closable="false" width="270px">
<caption label="${i18n:_('Possible content keywords')}"/>
<grid width="250px">
<columns>
<column label="${i18n:_('Keyword')}"/>
<column label="${i18n:_('Description')}"/>
</columns>
<rows>
<row>
<label value="{username}"/>
<label value="${i18n:_('Username of user')}"/>
</row>
<row>
<label value="{firstname}"/>
<label value="${i18n:_('First name of user')}"/>
</row>
<row>
<label value="{lastname}"/>
<label value="${i18n:_('Last name of user')}"/>
</row>
<row>
<label value="{project}"/>
<label value="${i18n:_('Name of project')}"/>
</row>
<row>
<label value="{resource}"/>
<label value="${i18n:_('Name of resource')}"/>
</row>
<row>
<label value="{task}"/>
<label value="${i18n:_('Name of task')}"/>
</row>
<row>
<label value="{url}"/>
<label value="${i18n:_('Welcome page')}"/>
</row>
</rows>
</grid>
</groupbox>
</row>
</rows>
</grid>

View file

@ -34,7 +34,6 @@ import org.joda.time.LocalDate;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.libreplan.importers.TimSoapClient;
import org.libreplan.importers.tim.DataDTO;
import org.libreplan.importers.tim.DepartmentDTO;
import org.libreplan.importers.tim.DurationDTO;
@ -95,7 +94,7 @@ public class TimSoapClientTest {
return timeRegistrationDTO;
}
private RosterRequestDTO createtRosterRequest() {
private RosterRequestDTO createRosterRequest() {
RosterDTO rosterDTO = new RosterDTO();
rosterDTO.setStartDate(new LocalDate());
rosterDTO.setEndDate(new LocalDate().plusDays(7));
@ -186,7 +185,7 @@ public class TimSoapClientTest {
.sendRequestReceiveResponse(properties.getProperty("url"),
properties.getProperty("username"),
properties.getProperty("password"),
createtRosterRequest(), RosterResponseDTO.class);
createRosterRequest(), RosterResponseDTO.class);
if (rosterResponseDTO == null) {
fail("Roster Response is null");
}

View file

@ -37,7 +37,7 @@
<default.passwordsControl>true</default.passwordsControl>
<default.exampleUsersDisabled>true</default.exampleUsersDisabled>
<default.emailSendingDisabled>false</default.emailSendingDisabled>
<default.emailSendingEnabled>true</default.emailSendingEnabled>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
@ -329,7 +329,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- datasource for testing -->