summaryrefslogtreecommitdiffstats
path: root/plugins/sonar-email-notifications-plugin
diff options
context:
space:
mode:
authorEvgeny Mandrikov <mandrikov@gmail.com>2011-07-26 14:25:57 +0400
committerEvgeny Mandrikov <mandrikov@gmail.com>2011-07-26 18:23:56 +0400
commitb508728dc8d993a5b4ad5839b9553228b3ef4b47 (patch)
tree3582bdc3962c5c10438cadde42849610e42214e3 /plugins/sonar-email-notifications-plugin
parentfea2ef37ed0a1dfa73ebdd87799f5caa298aa2f2 (diff)
downloadsonarqube-b508728dc8d993a5b4ad5839b9553228b3ef4b47.tar.gz
sonarqube-b508728dc8d993a5b4ad5839b9553228b3ef4b47.zip
SONAR-2596,SONAR-2601 UI for email notifications
* Add email configuration to sidebar menu * I18n for email and notifications settings * Email configuration properties must be secured * Rename sonar-email-plugin to sonar-email-notifications-plugin
Diffstat (limited to 'plugins/sonar-email-notifications-plugin')
-rw-r--r--plugins/sonar-email-notifications-plugin/pom.xml63
-rw-r--r--plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/EmailConfiguration.java86
-rw-r--r--plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/EmailNotificationChannel.java190
-rw-r--r--plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/EmailNotificationsPlugin.java42
-rw-r--r--plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/api/EmailMessage.java115
-rw-r--r--plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/api/EmailTemplate.java32
-rw-r--r--plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/reviews/ChangesInReviewAssignedToMe.java48
-rw-r--r--plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/reviews/ChangesInReviewCreatedByMe.java44
-rw-r--r--plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/reviews/ReviewEmailTemplate.java117
-rw-r--r--plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/EmailConfigurationTest.java53
-rw-r--r--plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/EmailNotificationChannelTest.java182
-rw-r--r--plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/EmailNotificationsPluginTest.java34
-rw-r--r--plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/reviews/ChangesInReviewAssignedToMeTest.java78
-rw-r--r--plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/reviews/ChangesInReviewCreatedByMeTest.java74
-rw-r--r--plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/reviews/ReviewEmailTemplateTest.java330
15 files changed, 1488 insertions, 0 deletions
diff --git a/plugins/sonar-email-notifications-plugin/pom.xml b/plugins/sonar-email-notifications-plugin/pom.xml
new file mode 100644
index 00000000000..181e56b59e3
--- /dev/null
+++ b/plugins/sonar-email-notifications-plugin/pom.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar</artifactId>
+ <version>2.10-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+
+ <groupId>org.codehaus.sonar.plugins</groupId>
+ <artifactId>sonar-email-notifications-plugin</artifactId>
+ <packaging>sonar-plugin</packaging>
+
+ <name>Sonar :: Plugins :: Email Notifications</name>
+ <description>Email Notifications</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-plugin-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-email</artifactId>
+ <version>1.2</version>
+ </dependency>
+
+ <!-- unit tests -->
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-testing-harness</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>dumbster</groupId>
+ <artifactId>dumbster</artifactId>
+ <version>1.6</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-packaging-maven-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <pluginName>Email notifications</pluginName>
+ <pluginClass>org.sonar.plugins.emailnotifications.EmailNotificationsPlugin</pluginClass>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/EmailConfiguration.java b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/EmailConfiguration.java
new file mode 100644
index 00000000000..907d9bf599c
--- /dev/null
+++ b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/EmailConfiguration.java
@@ -0,0 +1,86 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.emailnotifications;
+
+import org.apache.commons.configuration.Configuration;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.ServerExtension;
+
+/**
+ * Ruby uses constants from this class.
+ *
+ * @since 2.10
+ */
+public class EmailConfiguration implements ServerExtension {
+
+ public static final String SMTP_HOST = "email.smtp_host.secured";
+ public static final String SMTP_HOST_DEFAULT = "";
+ public static final String SMTP_PORT = "email.smtp_port.secured";
+ public static final String SMTP_PORT_DEFAULT = "25";
+ public static final String SMTP_USE_TLS = "email.smtp_use_tls.secured";
+ public static final boolean SMTP_USE_TLS_DEFAULT = false;
+ public static final String SMTP_USERNAME = "email.smtp_username.secured";
+ public static final String SMTP_USERNAME_DEFAULT = "";
+ public static final String SMTP_PASSWORD = "email.smtp_password.secured";
+ public static final String SMTP_PASSWORD_DEFAULT = "";
+ public static final String FROM = "email.from";
+ public static final String FROM_DEFAULT = "noreply@nowhere";
+ public static final String PREFIX = "email.prefix";
+ public static final String PREFIX_DEFAULT = "[SONAR]";
+
+ private Configuration configuration;
+
+ public EmailConfiguration(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ public String getSmtpHost() {
+ return configuration.getString(SMTP_HOST, SMTP_HOST_DEFAULT);
+ }
+
+ public String getSmtpPort() {
+ return configuration.getString(SMTP_PORT, SMTP_PORT_DEFAULT);
+ }
+
+ public boolean isUseTLS() {
+ return configuration.getBoolean(SMTP_USE_TLS, SMTP_USE_TLS_DEFAULT);
+ }
+
+ public String getSmtpUsername() {
+ return configuration.getString(SMTP_USERNAME, SMTP_USERNAME_DEFAULT);
+ }
+
+ public String getSmtpPassword() {
+ return configuration.getString(SMTP_PASSWORD, SMTP_PASSWORD_DEFAULT);
+ }
+
+ public String getFrom() {
+ return configuration.getString(FROM, FROM_DEFAULT);
+ }
+
+ public String getPrefix() {
+ return configuration.getString(PREFIX, PREFIX_DEFAULT);
+ }
+
+ public String getServerBaseURL() {
+ return configuration.getString(CoreProperties.SERVER_BASE_URL, CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE);
+ }
+
+}
diff --git a/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/EmailNotificationChannel.java b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/EmailNotificationChannel.java
new file mode 100644
index 00000000000..74fb59d5d0e
--- /dev/null
+++ b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/EmailNotificationChannel.java
@@ -0,0 +1,190 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.emailnotifications;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.mail.EmailException;
+import org.apache.commons.mail.SimpleEmail;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.database.model.User;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationChannel;
+import org.sonar.api.security.UserFinder;
+import org.sonar.plugins.emailnotifications.api.EmailMessage;
+import org.sonar.plugins.emailnotifications.api.EmailTemplate;
+
+/**
+ * References:
+ * <ul>
+ * <li><a href="http://tools.ietf.org/html/rfc4021">Registration of Mail and MIME Header Fields</a></li>
+ * <li><a href="http://tools.ietf.org/html/rfc2919">List-Id: A Structured Field and Namespace for the Identification of Mailing Lists</a></li>
+ * <li><a href="https://github.com/blog/798-threaded-email-notifications">GitHub: Threaded Email Notifications</a></li>
+ * </ul>
+ *
+ * @since 2.10
+ */
+public class EmailNotificationChannel extends NotificationChannel {
+
+ private static final Logger LOG = LoggerFactory.getLogger(EmailNotificationChannel.class);
+
+ /**
+ * @see org.apache.commons.mail.Email#setSocketConnectionTimeout(int)
+ * @see org.apache.commons.mail.Email#setSocketTimeout(int)
+ */
+ private static final int SOCKET_TIMEOUT = 30000;
+
+ /**
+ * Email Header Field: "List-ID".
+ * Value of this field should contain mailing list identifier as specified in <a href="http://tools.ietf.org/html/rfc2919">RFC 2919</a>.
+ */
+ private static final String LIST_ID_HEADER = "List-ID";
+
+ /**
+ * Email Header Field: "List-Archive".
+ * Value of this field should contain URL of mailing list archive as specified in <a href="http://tools.ietf.org/html/rfc2369">RFC 2369</a>.
+ */
+ private static final String LIST_ARCHIVE_HEADER = "List-Archive";
+
+ /**
+ * Email Header Field: "In-Reply-To".
+ * Value of this field should contain related message identifier as specified in <a href="http://tools.ietf.org/html/rfc2822">RFC 2822</a>.
+ */
+ private static final String IN_REPLY_TO_HEADER = "In-Reply-To";
+
+ /**
+ * Email Header Field: "References".
+ * Value of this field should contain related message identifier as specified in <a href="http://tools.ietf.org/html/rfc2822">RFC 2822</a>
+ */
+ private static final String REFERENCES_HEADER = "References";
+
+ private static final String FROM_NAME_DEFAULT = "Sonar";
+ private static final String SUBJECT_DEFAULT = "Notification";
+
+ private EmailConfiguration configuration;
+ private EmailTemplate[] templates;
+ private UserFinder userFinder;
+
+ public EmailNotificationChannel(EmailConfiguration configuration, EmailTemplate[] templates, UserFinder userFinder) {
+ this.configuration = configuration;
+ this.templates = templates;
+ this.userFinder = userFinder;
+ }
+
+ @Override
+ public void deliver(Notification notification, String username) {
+ User user = userFinder.findByLogin(username);
+ if (StringUtils.isBlank(user.getEmail())) {
+ LOG.warn("Email not defined for user: " + username);
+ return;
+ }
+ EmailMessage emailMessage = format(notification);
+ if (emailMessage != null) {
+ emailMessage.setTo(user.getEmail());
+ deliver(emailMessage);
+ }
+ }
+
+ private EmailMessage format(Notification notification) {
+ for (EmailTemplate template : templates) {
+ EmailMessage email = template.format(notification);
+ if (email != null) {
+ return email;
+ }
+ }
+ LOG.warn("Email template not found for notification: {}", notification);
+ return null;
+ }
+
+ /**
+ * Visibility has been relaxed for tests.
+ */
+ void deliver(EmailMessage emailMessage) {
+ if (StringUtils.isBlank(configuration.getSmtpHost())) {
+ LOG.warn("SMTP host was not configured - email will not be sent");
+ return;
+ }
+ try {
+ send(emailMessage);
+ } catch (EmailException e) {
+ LOG.error("Unable to send email", e);
+ }
+ }
+
+ private void send(EmailMessage emailMessage) throws EmailException {
+ LOG.info("Sending email: {}", emailMessage);
+ String host = null;
+ try {
+ host = new URL(configuration.getServerBaseURL()).getHost();
+ } catch (MalformedURLException e) {
+ // ignore
+ }
+
+ SimpleEmail email = new SimpleEmail();
+ if (StringUtils.isNotBlank(host)) {
+ /*
+ * Set headers for proper threading: GMail will not group messages, even if they have same subject, but don't have "In-Reply-To" and
+ * "References" headers. TODO investigate threading in other clients like KMail, Thunderbird, Outlook
+ */
+ if (StringUtils.isNotEmpty(emailMessage.getMessageId())) {
+ String messageId = "<" + emailMessage.getMessageId() + "@" + host + ">";
+ email.addHeader(IN_REPLY_TO_HEADER, messageId);
+ email.addHeader(REFERENCES_HEADER, messageId);
+ }
+ // Set headers for proper filtering
+ email.addHeader(LIST_ID_HEADER, "Sonar <sonar." + host + ">");
+ email.addHeader(LIST_ARCHIVE_HEADER, configuration.getServerBaseURL());
+ }
+ // Set general information
+ email.setFrom(configuration.getFrom(), StringUtils.defaultIfBlank(emailMessage.getFrom(), FROM_NAME_DEFAULT));
+ email.addTo(emailMessage.getTo(), " ");
+ String subject = StringUtils.defaultIfBlank(StringUtils.trimToEmpty(configuration.getPrefix()) + " ", "")
+ + StringUtils.defaultString(emailMessage.getSubject(), SUBJECT_DEFAULT);
+ email.setSubject(subject);
+ email.setMsg(emailMessage.getMessage());
+ // Send
+ email.setHostName(configuration.getSmtpHost());
+ email.setSmtpPort(Integer.parseInt(configuration.getSmtpPort()));
+ email.setTLS(configuration.isUseTLS());
+ if (StringUtils.isNotBlank(configuration.getSmtpUsername()) || StringUtils.isNotBlank(configuration.getSmtpPassword())) {
+ email.setAuthentication(configuration.getSmtpUsername(), configuration.getSmtpPassword());
+ }
+ email.setSocketConnectionTimeout(SOCKET_TIMEOUT);
+ email.setSocketTimeout(SOCKET_TIMEOUT);
+ email.send();
+ }
+
+ /**
+ * Send test email. This method called from Ruby.
+ *
+ * @throws EmailException when unable to send
+ */
+ public void sendTestEmail(String toAddress, String subject, String message) throws EmailException {
+ EmailMessage emailMessage = new EmailMessage();
+ emailMessage.setTo(toAddress);
+ emailMessage.setSubject(subject);
+ emailMessage.setMessage(message);
+ send(emailMessage);
+ }
+
+}
diff --git a/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/EmailNotificationsPlugin.java b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/EmailNotificationsPlugin.java
new file mode 100644
index 00000000000..c5cc4ccc7de
--- /dev/null
+++ b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/EmailNotificationsPlugin.java
@@ -0,0 +1,42 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.emailnotifications;
+
+import org.sonar.api.SonarPlugin;
+import org.sonar.plugins.emailnotifications.reviews.ChangesInReviewAssignedToMe;
+import org.sonar.plugins.emailnotifications.reviews.ChangesInReviewCreatedByMe;
+import org.sonar.plugins.emailnotifications.reviews.ReviewEmailTemplate;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class EmailNotificationsPlugin extends SonarPlugin {
+
+ public List getExtensions() {
+ return Arrays.asList(
+ EmailConfiguration.class,
+ EmailNotificationChannel.class,
+
+ ReviewEmailTemplate.class,
+ ChangesInReviewAssignedToMe.class,
+ ChangesInReviewCreatedByMe.class);
+ }
+
+}
diff --git a/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/api/EmailMessage.java b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/api/EmailMessage.java
new file mode 100644
index 00000000000..f03d90caea6
--- /dev/null
+++ b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/api/EmailMessage.java
@@ -0,0 +1,115 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.emailnotifications.api;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+/**
+ * @since 2.10
+ */
+public class EmailMessage {
+
+ private String from;
+ private String to;
+ private String subject;
+ private String message;
+ private String messageId;
+
+ /**
+ * @param from full name of user, who initiated this message or null, if message was initiated by Sonar
+ */
+ public EmailMessage setFrom(String from) {
+ this.from = from;
+ return this;
+ }
+
+ /**
+ * @see #setFrom(String)
+ */
+ public String getFrom() {
+ return from;
+ }
+
+ /**
+ * @param to email address where to send this message
+ */
+ public EmailMessage setTo(String to) {
+ this.to = to;
+ return this;
+ }
+
+ /**
+ * @see #setTo(String)
+ */
+ public String getTo() {
+ return to;
+ }
+
+ /**
+ * @param subject message subject
+ */
+ public EmailMessage setSubject(String subject) {
+ this.subject = subject;
+ return this;
+ }
+
+ /**
+ * @see #setSubject(String)
+ */
+ public String getSubject() {
+ return subject;
+ }
+
+ /**
+ * @param message message body
+ */
+ public EmailMessage setMessage(String message) {
+ this.message = message;
+ return this;
+ }
+
+ /**
+ * @see #setMessage(String)
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * @param messageId id of message for threading
+ */
+ public EmailMessage setMessageId(String messageId) {
+ this.messageId = messageId;
+ return this;
+ }
+
+ /**
+ * @see #setMessageId(String)
+ */
+ public String getMessageId() {
+ return messageId;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this);
+ }
+
+}
diff --git a/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/api/EmailTemplate.java b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/api/EmailTemplate.java
new file mode 100644
index 00000000000..fe638eada73
--- /dev/null
+++ b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/api/EmailTemplate.java
@@ -0,0 +1,32 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.emailnotifications.api;
+
+import org.sonar.api.ServerExtension;
+import org.sonar.api.notifications.Notification;
+
+/**
+ * @since 2.10
+ */
+public abstract class EmailTemplate implements ServerExtension {
+
+ public abstract EmailMessage format(Notification notification);
+
+}
diff --git a/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/reviews/ChangesInReviewAssignedToMe.java b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/reviews/ChangesInReviewAssignedToMe.java
new file mode 100644
index 00000000000..c5859ee13da
--- /dev/null
+++ b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/reviews/ChangesInReviewAssignedToMe.java
@@ -0,0 +1,48 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.emailnotifications.reviews;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationDispatcher;
+
+/**
+ * This dispatcher means: "notify me when when someone changes review assigned to me".
+ *
+ * @since 2.10
+ */
+public class ChangesInReviewAssignedToMe extends NotificationDispatcher {
+
+ @Override
+ public void dispatch(Notification notification, Context context) {
+ if (StringUtils.startsWith(notification.getType(), "review")) {
+ String author = notification.getFieldValue("author"); // author of change
+ String oldAssignee = notification.getFieldValue("old.assignee"); // previous assignee
+ String assignee = notification.getFieldValue("assignee"); // current assignee
+ if (!StringUtils.equals(author, oldAssignee)) {
+ context.addUser(oldAssignee);
+ }
+ if (!StringUtils.equals(author, assignee)) {
+ context.addUser(assignee);
+ }
+ }
+ }
+
+}
diff --git a/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/reviews/ChangesInReviewCreatedByMe.java b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/reviews/ChangesInReviewCreatedByMe.java
new file mode 100644
index 00000000000..85f104637d0
--- /dev/null
+++ b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/reviews/ChangesInReviewCreatedByMe.java
@@ -0,0 +1,44 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.emailnotifications.reviews;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationDispatcher;
+
+/**
+ * This dispatcher means: "notify me when when someone changes review created by me".
+ *
+ * @since 2.10
+ */
+public class ChangesInReviewCreatedByMe extends NotificationDispatcher {
+
+ @Override
+ public void dispatch(Notification notification, Context context) {
+ if (StringUtils.startsWith(notification.getType(), "review")) {
+ String author = notification.getFieldValue("author"); // author of change
+ String creator = notification.getFieldValue("creator"); // creator of review
+ if (!StringUtils.equals(author, creator)) {
+ context.addUser(creator);
+ }
+ }
+ }
+
+}
diff --git a/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/reviews/ReviewEmailTemplate.java b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/reviews/ReviewEmailTemplate.java
new file mode 100644
index 00000000000..f09082a2d07
--- /dev/null
+++ b/plugins/sonar-email-notifications-plugin/src/main/java/org/sonar/plugins/emailnotifications/reviews/ReviewEmailTemplate.java
@@ -0,0 +1,117 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.emailnotifications.reviews;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.database.model.User;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.security.UserFinder;
+import org.sonar.plugins.emailnotifications.EmailConfiguration;
+import org.sonar.plugins.emailnotifications.api.EmailMessage;
+import org.sonar.plugins.emailnotifications.api.EmailTemplate;
+
+/**
+ * Creates email message for notification "review-changed".
+ *
+ * @since 2.10
+ */
+public class ReviewEmailTemplate extends EmailTemplate {
+
+ private EmailConfiguration configuration;
+ private UserFinder userFinder;
+
+ public ReviewEmailTemplate(EmailConfiguration configuration, UserFinder userFinder) {
+ this.configuration = configuration;
+ this.userFinder = userFinder;
+ }
+
+ @Override
+ public EmailMessage format(Notification notification) {
+ if ( !"review-changed".equals(notification.getType())) {
+ return null;
+ }
+ String reviewId = notification.getFieldValue("reviewId");
+ String author = notification.getFieldValue("author");
+ StringBuilder sb = new StringBuilder();
+
+ append(sb, "Status", notification.getFieldValue("old.status"), notification.getFieldValue("new.status"));
+ append(sb, "Resolution", notification.getFieldValue("old.resolution"), notification.getFieldValue("new.resolution"));
+ append(sb, "Assignee", getUserFullName(notification.getFieldValue("old.assignee")), getUserFullName(notification.getFieldValue("new.assignee")));
+ appendComment(sb, notification);
+ appendFooter(sb, notification);
+
+ EmailMessage message = new EmailMessage()
+ .setMessageId("review/" + reviewId)
+ .setSubject("Review #" + reviewId)
+ .setMessage(sb.toString());
+ if (author != null) {
+ message.setFrom(getUserFullName(author));
+ }
+ return message;
+ }
+
+ private void append(StringBuilder sb, String name, String oldValue, String newValue) {
+ if (oldValue != null || newValue != null) {
+ sb.append(name).append(": ");
+ if (newValue != null) {
+ sb.append(newValue);
+ }
+ if (oldValue != null) {
+ sb.append(" (was ").append(oldValue).append(")");
+ }
+ sb.append('\n');
+ }
+ }
+
+ private void appendComment(StringBuilder sb, Notification notification) {
+ String newComment = notification.getFieldValue("new.comment");
+ String oldComment = notification.getFieldValue("old.comment");
+
+ if (newComment != null) { // comment was added or modified
+ sb.append("Comment:\n ").append(newComment).append('\n');
+ if (oldComment != null) { // comment was modified
+ sb.append("Was:\n ").append(oldComment).append('\n');
+ }
+ } else if (oldComment != null) { // comment was deleted
+ sb.append("Comment deleted, was:\n ").append(oldComment).append('\n');
+ }
+ }
+
+ private void appendFooter(StringBuilder sb, Notification notification) {
+ String reviewId = notification.getFieldValue("reviewId");
+ sb.append("\n--\n")
+ .append("See it in Sonar: ").append(configuration.getServerBaseURL()).append("/review/view/").append(reviewId).append('\n');
+ }
+
+ /**
+ * Visibility has been relaxed for tests.
+ */
+ String getUserFullName(String login) {
+ if (login == null) {
+ return null;
+ }
+ User user = userFinder.findByLogin(login);
+ if (user == null) { // most probably user was deleted
+ return login;
+ }
+ return StringUtils.defaultIfBlank(user.getName(), login);
+ }
+
+}
diff --git a/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/EmailConfigurationTest.java b/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/EmailConfigurationTest.java
new file mode 100644
index 00000000000..92c3ab5c1df
--- /dev/null
+++ b/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/EmailConfigurationTest.java
@@ -0,0 +1,53 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.emailnotifications;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.apache.commons.configuration.BaseConfiguration;
+import org.apache.commons.configuration.Configuration;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.CoreProperties;
+
+public class EmailConfigurationTest {
+
+ private EmailConfiguration emailConfiguration;
+
+ @Before
+ public void setUp() {
+ Configuration configuration = new BaseConfiguration();
+ emailConfiguration = new EmailConfiguration(configuration);
+ }
+
+ @Test
+ public void shouldReturnDefaultValues() {
+ assertThat(emailConfiguration.getSmtpHost(), is(""));
+ assertThat(emailConfiguration.getSmtpPort(), is("25"));
+ assertThat(emailConfiguration.getSmtpUsername(), is(""));
+ assertThat(emailConfiguration.getSmtpPassword(), is(""));
+ assertThat(emailConfiguration.isUseTLS(), is(false));
+ assertThat(emailConfiguration.getFrom(), is("noreply@nowhere"));
+ assertThat(emailConfiguration.getPrefix(), is("[SONAR]"));
+ assertThat(emailConfiguration.getServerBaseURL(), is(CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE));
+ }
+
+}
diff --git a/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/EmailNotificationChannelTest.java b/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/EmailNotificationChannelTest.java
new file mode 100644
index 00000000000..18c1e238de5
--- /dev/null
+++ b/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/EmailNotificationChannelTest.java
@@ -0,0 +1,182 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.emailnotifications;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+
+import org.apache.commons.mail.EmailException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.sonar.plugins.emailnotifications.api.EmailMessage;
+
+import com.dumbster.smtp.SimpleSmtpServer;
+import com.dumbster.smtp.SmtpMessage;
+
+public class EmailNotificationChannelTest {
+
+ private static int port;
+
+ private SimpleSmtpServer server;
+
+ private EmailConfiguration configuration;
+ private EmailNotificationChannel channel;
+
+ @BeforeClass
+ public static void selectPort() {
+ port = getNextAvailablePort();
+ }
+
+ private static int getNextAvailablePort() {
+ try {
+ ServerSocket socket = new ServerSocket(0);
+ int unusedPort = socket.getLocalPort();
+ socket.close();
+ return unusedPort;
+ } catch (IOException e) {
+ throw new RuntimeException("Error getting an available port from system", e);
+ }
+ }
+
+ @Before
+ public void setUp() {
+ server = SimpleSmtpServer.start(port);
+ configuration = mock(EmailConfiguration.class);
+ channel = new EmailNotificationChannel(configuration, null, null);
+ }
+
+ @After
+ public void tearDown() {
+ if (!server.isStopped()) {
+ server.stop();
+ }
+ }
+
+ @Test
+ public void shouldSendTestEmail() throws Exception {
+ configure();
+ channel.sendTestEmail("user@nowhere", "Test Message from Sonar", "This is a test message from Sonar.");
+
+ assertThat(server.getReceivedEmailSize(), is(1));
+ SmtpMessage email = (SmtpMessage) server.getReceivedEmail().next();
+
+ assertThat(email.getHeaderValue("From"), is("Sonar <server@nowhere>"));
+ assertThat(email.getHeaderValue("To"), is("<user@nowhere>"));
+ assertThat(email.getHeaderValue("Subject"), is("[SONAR] Test Message from Sonar"));
+ assertThat(email.getBody(), is("This is a test message from Sonar."));
+ }
+
+ @Test(expected = EmailException.class)
+ public void shouldThrowAnExceptionWhenUnableToSendTestEmail() throws Exception {
+ configure();
+ server.stop();
+
+ channel.sendTestEmail("user@nowhere", "Test Message from Sonar", "This is a test message from Sonar.");
+ }
+
+ @Test
+ public void shouldNotSendEmailWhenHostnameNotConfigured() throws Exception {
+ EmailMessage emailMessage = new EmailMessage()
+ .setTo("user@nowhere")
+ .setSubject("Foo")
+ .setMessage("Bar");
+ channel.deliver(emailMessage);
+ assertThat(server.getReceivedEmailSize(), is(0));
+ }
+
+ @Test
+ public void shouldSendThreadedEmail() throws Exception {
+ configure();
+ EmailMessage emailMessage = new EmailMessage()
+ .setMessageId("reviews/view/1")
+ .setFrom("Full Username")
+ .setTo("user@nowhere")
+ .setSubject("Review #3")
+ .setMessage("I'll take care of this violation.");
+ channel.deliver(emailMessage);
+
+ assertThat(server.getReceivedEmailSize(), is(1));
+ SmtpMessage email = (SmtpMessage) server.getReceivedEmail().next();
+
+ assertThat(email.getHeaderValue("In-Reply-To"), is("<reviews/view/1@nemo.sonarsource.org>"));
+ assertThat(email.getHeaderValue("References"), is("<reviews/view/1@nemo.sonarsource.org>"));
+
+ assertThat(email.getHeaderValue("List-ID"), is("Sonar <sonar.nemo.sonarsource.org>"));
+ assertThat(email.getHeaderValue("List-Archive"), is("http://nemo.sonarsource.org"));
+
+ assertThat(email.getHeaderValue("From"), is("Full Username <server@nowhere>"));
+ assertThat(email.getHeaderValue("To"), is("<user@nowhere>"));
+ assertThat(email.getHeaderValue("Subject"), is("[SONAR] Review #3"));
+ assertThat(email.getBody(), is("I'll take care of this violation."));
+ }
+
+ @Test
+ public void shouldSendNonThreadedEmail() throws Exception {
+ configure();
+ EmailMessage emailMessage = new EmailMessage()
+ .setTo("user@nowhere")
+ .setSubject("Foo")
+ .setMessage("Bar");
+ channel.deliver(emailMessage);
+
+ assertThat(server.getReceivedEmailSize(), is(1));
+ SmtpMessage email = (SmtpMessage) server.getReceivedEmail().next();
+
+ assertThat(email.getHeaderValue("In-Reply-To"), nullValue());
+ assertThat(email.getHeaderValue("References"), nullValue());
+
+ assertThat(email.getHeaderValue("List-ID"), is("Sonar <sonar.nemo.sonarsource.org>"));
+ assertThat(email.getHeaderValue("List-Archive"), is("http://nemo.sonarsource.org"));
+
+ assertThat(email.getHeaderValue("From"), is("Sonar <server@nowhere>"));
+ assertThat(email.getHeaderValue("To"), is("<user@nowhere>"));
+ assertThat(email.getHeaderValue("Subject"), is("[SONAR] Foo"));
+ assertThat(email.getBody(), is("Bar"));
+ }
+
+ @Test
+ public void shouldNotThrowAnExceptionWhenUnableToSendEmail() throws Exception {
+ configure();
+ server.stop();
+
+ EmailMessage emailMessage = new EmailMessage()
+ .setTo("user@nowhere")
+ .setSubject("Foo")
+ .setMessage("Bar");
+ channel.deliver(emailMessage);
+ }
+
+ private void configure() {
+ when(configuration.getSmtpHost()).thenReturn("localhost");
+ when(configuration.getSmtpPort()).thenReturn(Integer.toString(port));
+ when(configuration.getFrom()).thenReturn("server@nowhere");
+ when(configuration.getPrefix()).thenReturn("[SONAR]");
+ when(configuration.getServerBaseURL()).thenReturn("http://nemo.sonarsource.org");
+ }
+
+}
diff --git a/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/EmailNotificationsPluginTest.java b/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/EmailNotificationsPluginTest.java
new file mode 100644
index 00000000000..b6deef32eb8
--- /dev/null
+++ b/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/EmailNotificationsPluginTest.java
@@ -0,0 +1,34 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.emailnotifications;
+
+import static org.hamcrest.Matchers.greaterThan;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Test;
+
+public class EmailNotificationsPluginTest {
+
+ @Test
+ public void testGetExtensions() {
+ assertThat(new EmailNotificationsPlugin().getExtensions().size(), greaterThan(1));
+ }
+
+}
diff --git a/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/reviews/ChangesInReviewAssignedToMeTest.java b/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/reviews/ChangesInReviewAssignedToMeTest.java
new file mode 100644
index 00000000000..e9b169c6363
--- /dev/null
+++ b/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/reviews/ChangesInReviewAssignedToMeTest.java
@@ -0,0 +1,78 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.emailnotifications.reviews;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationDispatcher;
+
+public class ChangesInReviewAssignedToMeTest {
+
+ private NotificationDispatcher.Context context;
+ private ChangesInReviewAssignedToMe dispatcher;
+
+ @Before
+ public void setUp() {
+ context = mock(NotificationDispatcher.Context.class);
+ dispatcher = new ChangesInReviewAssignedToMe();
+ }
+
+ @Test
+ public void dispatchToOldAndNewAssignee() {
+ Notification notification = new Notification("review-assignee-changed")
+ .setFieldValue("author", "freddy")
+ .setFieldValue("old.assignee", "godin")
+ .setFieldValue("assignee", "simon");
+
+ dispatcher.dispatch(notification, context);
+
+ verify(context).addUser("godin");
+ verify(context).addUser("simon");
+ verifyNoMoreInteractions(context);
+ }
+
+ @Test
+ public void doNotDispatchToAuthorOfChanges() {
+ Notification notification = new Notification("review-assignee-changed")
+ .setFieldValue("author", "simon")
+ .setFieldValue("old.assignee", "simon")
+ .setFieldValue("assignee", "godin");
+
+ dispatcher.dispatch(notification, context);
+
+ verify(context).addUser("godin");
+ verifyNoMoreInteractions(context);
+ }
+
+ @Test
+ public void shouldNotDispatch() {
+ Notification notification = new Notification("other");
+
+ dispatcher.dispatch(notification, context);
+
+ verifyNoMoreInteractions(context);
+ }
+
+}
diff --git a/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/reviews/ChangesInReviewCreatedByMeTest.java b/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/reviews/ChangesInReviewCreatedByMeTest.java
new file mode 100644
index 00000000000..3f3142ccced
--- /dev/null
+++ b/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/reviews/ChangesInReviewCreatedByMeTest.java
@@ -0,0 +1,74 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.emailnotifications.reviews;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.notifications.NotificationDispatcher;
+
+public class ChangesInReviewCreatedByMeTest {
+
+ private NotificationDispatcher.Context context;
+ private ChangesInReviewCreatedByMe dispatcher;
+
+ @Before
+ public void setUp() {
+ context = mock(NotificationDispatcher.Context.class);
+ dispatcher = new ChangesInReviewCreatedByMe();
+ }
+
+ @Test
+ public void dispatchToCreator() {
+ Notification notification = new Notification("review-comment-added")
+ .setFieldValue("author", "godin")
+ .setFieldValue("creator", "simon");
+
+ dispatcher.dispatch(notification, context);
+
+ verify(context).addUser("simon");
+ verifyNoMoreInteractions(context);
+ }
+
+ @Test
+ public void doNotDispatchToAuthorOfChanges() {
+ Notification notification = new Notification("review-comment-added")
+ .setFieldValue("author", "simon")
+ .setFieldValue("creator", "simon");
+
+ dispatcher.dispatch(notification, context);
+
+ verifyNoMoreInteractions(context);
+ }
+
+ @Test
+ public void shouldNotDispatch() {
+ Notification notification = new Notification("other");
+
+ dispatcher.dispatch(notification, context);
+
+ verifyNoMoreInteractions(context);
+ }
+
+}
diff --git a/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/reviews/ReviewEmailTemplateTest.java b/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/reviews/ReviewEmailTemplateTest.java
new file mode 100644
index 00000000000..70fca60f6b8
--- /dev/null
+++ b/plugins/sonar-email-notifications-plugin/src/test/java/org/sonar/plugins/emailnotifications/reviews/ReviewEmailTemplateTest.java
@@ -0,0 +1,330 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.emailnotifications.reviews;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.database.model.User;
+import org.sonar.api.notifications.Notification;
+import org.sonar.api.security.UserFinder;
+import org.sonar.plugins.emailnotifications.EmailConfiguration;
+import org.sonar.plugins.emailnotifications.api.EmailMessage;
+
+public class ReviewEmailTemplateTest {
+
+ private ReviewEmailTemplate template;
+
+ @Before
+ public void setUp() {
+ EmailConfiguration configuration = mock(EmailConfiguration.class);
+ when(configuration.getServerBaseURL()).thenReturn("http://nemo.sonarsource.org");
+ UserFinder userFinder = mock(UserFinder.class);
+ when(userFinder.findByLogin(eq("freddy.mallet"))).thenReturn(new User().setName("Freddy Mallet"));
+ when(userFinder.findByLogin(eq("simon.brandhof"))).thenReturn(new User().setName("Simon Brandhof"));
+ when(userFinder.findByLogin(eq("evgeny.mandrikov"))).thenReturn(new User().setName("Evgeny Mandrikov"));
+ template = new ReviewEmailTemplate(configuration, userFinder);
+ }
+
+ /**
+ * <pre>
+ * Subject: Review #1
+ * From: Freddy Mallet
+ *
+ * Comment:
+ * This is my first comment
+ *
+ * --
+ * See it in Sonar: http://nemo.sonarsource.org/review/view/1
+ * </pre>
+ */
+ @Test
+ public void shouldFormatCommentAdded() {
+ Notification notification = new Notification("review-changed")
+ .setFieldValue("reviewId", "1")
+ .setFieldValue("author", "freddy.mallet")
+ .setFieldValue("old.comment", null)
+ .setFieldValue("new.comment", "This is my first comment");
+ EmailMessage message = template.format(notification);
+ assertThat(message.getMessageId(), is("review/1"));
+ assertThat(message.getSubject(), is("Review #1"));
+ assertThat(message.getFrom(), is("Freddy Mallet"));
+ assertThat(message.getMessage(), is("Comment:\n This is my first comment\n\n--\nSee it in Sonar: http://nemo.sonarsource.org/review/view/1\n"));
+ }
+
+ /**
+ * <pre>
+ * Subject: Review #1
+ * From: Freddy Mallet
+ *
+ * Comment:
+ * This is another comment
+ * Was:
+ * This is my first comment
+ *
+ * --
+ * See it in Sonar: http://nemo.sonarsource.org/review/view/1
+ * </pre>
+ */
+ @Test
+ public void shouldFormatCommentEdited() {
+ Notification notification = new Notification("review-changed")
+ .setFieldValue("reviewId", "1")
+ .setFieldValue("author", "freddy.mallet")
+ .setFieldValue("old.comment", "This is my first comment")
+ .setFieldValue("new.comment", "This is another comment");
+ EmailMessage message = template.format(notification);
+ assertThat(message.getMessageId(), is("review/1"));
+ assertThat(message.getSubject(), is("Review #1"));
+ assertThat(message.getFrom(), is("Freddy Mallet"));
+ assertThat(message.getMessage(), is("Comment:\n This is another comment\nWas:\n This is my first comment\n\n--\nSee it in Sonar: http://nemo.sonarsource.org/review/view/1\n"));
+ }
+
+ /**
+ * <pre>
+ * Subject: Review #1
+ * From: Freddy Mallet
+ *
+ * Comment deleted, was:
+ * This is deleted comment
+ *
+ * --
+ * See it in Sonar: http://nemo.sonarsource.org/review/view/1
+ * </pre>
+ */
+ @Test
+ public void shouldFormatCommentDeleted() {
+ Notification notification = new Notification("review-changed")
+ .setFieldValue("reviewId", "1")
+ .setFieldValue("old.comment", "This is deleted comment")
+ .setFieldValue("new.comment", null)
+ .setFieldValue("author", "freddy.mallet");
+ EmailMessage message = template.format(notification);
+ assertThat(message.getMessageId(), is("review/1"));
+ assertThat(message.getSubject(), is("Review #1"));
+ assertThat(message.getFrom(), is("Freddy Mallet"));
+ assertThat(message.getMessage(), is("Comment deleted, was:\n This is deleted comment\n\n--\nSee it in Sonar: http://nemo.sonarsource.org/review/view/1\n"));
+ }
+
+ /**
+ * <pre>
+ * Subject: Review #1
+ * From: Freddy Mallet
+ *
+ * Assignee: Evgeny Mandrikov
+ *
+ * --
+ * See it in Sonar: http://nemo.sonarsource.org/review/view/1
+ * </pre>
+ */
+ @Test
+ public void shouldFormatAssigneed() {
+ Notification notification = new Notification("review-changed")
+ .setFieldValue("reviewId", "1")
+ .setFieldValue("author", "freddy.mallet")
+ .setFieldValue("old.assignee", null)
+ .setFieldValue("new.assignee", "evgeny.mandrikov");
+ EmailMessage message = template.format(notification);
+ assertThat(message.getMessageId(), is("review/1"));
+ assertThat(message.getSubject(), is("Review #1"));
+ assertThat(message.getFrom(), is("Freddy Mallet"));
+ assertThat(message.getMessage(), is("Assignee: Evgeny Mandrikov\n\n--\nSee it in Sonar: http://nemo.sonarsource.org/review/view/1\n"));
+ }
+
+ /**
+ * <pre>
+ * Subject: Review #1
+ * From: Freddy Mallet
+ *
+ * Assignee: Simon Brandhof (was Evgeny Mandrikov)
+ *
+ * --
+ * See it in Sonar: http://nemo.sonarsource.org/review/view/1
+ * </pre>
+ */
+ @Test
+ public void shouldFormatAssigneedToAnotherPerson() {
+ Notification notification = new Notification("review-changed")
+ .setFieldValue("reviewId", "1")
+ .setFieldValue("author", "freddy.mallet")
+ .setFieldValue("old.assignee", "evgeny.mandrikov")
+ .setFieldValue("new.assignee", "simon.brandhof");
+ EmailMessage message = template.format(notification);
+ assertThat(message.getMessageId(), is("review/1"));
+ assertThat(message.getSubject(), is("Review #1"));
+ assertThat(message.getFrom(), is("Freddy Mallet"));
+ assertThat(message.getMessage(), is("Assignee: Simon Brandhof (was Evgeny Mandrikov)\n\n--\nSee it in Sonar: http://nemo.sonarsource.org/review/view/1\n"));
+ }
+
+ /**
+ * <pre>
+ * Subject: Review #1
+ * From: Freddy Mallet
+ *
+ * Assignee: (was Simon Brandhof)
+ *
+ * --
+ * See it in Sonar: http://nemo.sonarsource.org/review/view/1
+ * </pre>
+ */
+ @Test
+ public void shouldFormatUnassigned() {
+ Notification notification = new Notification("review-changed")
+ .setFieldValue("reviewId", "1")
+ .setFieldValue("author", "freddy.mallet")
+ .setFieldValue("old.assignee", "simon.brandhof")
+ .setFieldValue("new.assignee", null);
+ EmailMessage message = template.format(notification);
+ assertThat(message.getMessageId(), is("review/1"));
+ assertThat(message.getSubject(), is("Review #1"));
+ assertThat(message.getFrom(), is("Freddy Mallet"));
+ assertThat(message.getMessage(), is("Assignee: (was Simon Brandhof)\n\n--\nSee it in Sonar: http://nemo.sonarsource.org/review/view/1\n"));
+ }
+
+ /**
+ * <pre>
+ * Subject: Review #1
+ * From: Sonar
+ *
+ * Status: CLOSED (was OPEN)
+ *
+ * --
+ * See it in Sonar: http://nemo.sonarsource.org/review/view/1
+ * </pre>
+ */
+ @Test
+ public void shouldFormatClosed() {
+ Notification notification = new Notification("review-changed")
+ .setFieldValue("reviewId", "1")
+ .setFieldValue("old.status", "OPEN")
+ .setFieldValue("new.status", "CLOSED");
+ EmailMessage message = template.format(notification);
+ assertThat(message.getMessageId(), is("review/1"));
+ assertThat(message.getSubject(), is("Review #1"));
+ assertThat(message.getFrom(), nullValue());
+ assertThat(message.getMessage(), is("Status: CLOSED (was OPEN)\n\n--\nSee it in Sonar: http://nemo.sonarsource.org/review/view/1\n"));
+ }
+
+ /**
+ * <pre>
+ * Subject: Review #1
+ * From: Simon Brandhof
+ *
+ * Status: REOPENED (was RESOLVED)
+ * Resolution: (was FIXED)
+ *
+ * --
+ * See it in Sonar: http://nemo.sonarsource.org/review/view/1
+ * </pre>
+ */
+ @Test
+ public void shouldFormatReopened() {
+ Notification notification = new Notification("review-changed")
+ .setFieldValue("reviewId", "1")
+ .setFieldValue("old.resolution", "FIXED")
+ .setFieldValue("new.resolution", null)
+ .setFieldValue("old.status", "RESOLVED")
+ .setFieldValue("new.status", "REOPENED");
+ EmailMessage message = template.format(notification);
+ assertThat(message.getMessageId(), is("review/1"));
+ assertThat(message.getSubject(), is("Review #1"));
+ assertThat(message.getFrom(), nullValue());
+ assertThat(message.getMessage(), is("Status: REOPENED (was RESOLVED)\nResolution: (was FIXED)\n\n--\nSee it in Sonar: http://nemo.sonarsource.org/review/view/1\n"));
+ }
+
+ /**
+ * <pre>
+ * Subject: Review #1
+ * From: Simon Brandhof
+ *
+ * Status: RESOLVED (was OPEN)
+ * Resolution: FIXED
+ *
+ * --
+ * See it in Sonar: http://nemo.sonarsource.org/review/view/1
+ * </pre>
+ */
+ @Test
+ public void shouldFormatResolvedAsFixed() {
+ Notification notification = new Notification("review-changed")
+ .setFieldValue("reviewId", "1")
+ .setFieldValue("author", "simon.brandhof")
+ .setFieldValue("old.status", "OPEN")
+ .setFieldValue("old.resolution", null)
+ .setFieldValue("new.status", "RESOLVED")
+ .setFieldValue("new.resolution", "FIXED");
+ EmailMessage message = template.format(notification);
+ assertThat(message.getMessageId(), is("review/1"));
+ assertThat(message.getSubject(), is("Review #1"));
+ assertThat(message.getFrom(), is("Simon Brandhof"));
+ assertThat(message.getMessage(), is("Status: RESOLVED (was OPEN)\nResolution: FIXED\n\n--\nSee it in Sonar: http://nemo.sonarsource.org/review/view/1\n"));
+ }
+
+ /**
+ * <pre>
+ * Subject: Review #1
+ * From: Simon Brandhof
+ *
+ * Status: RESOLVED (was REOPENED)
+ * Resolution: FALSE-POSITIVE
+ * Comment:
+ * Because!
+ *
+ * --
+ * See it in Sonar: http://nemo.sonarsource.org/review/view/1
+ * </pre>
+ */
+ @Test
+ public void shouldFormatResolvedAsFalsePositive() {
+ Notification notification = new Notification("review-changed")
+ .setFieldValue("reviewId", "1")
+ .setFieldValue("author", "freddy.mallet")
+ .setFieldValue("old.status", "REOPENED")
+ .setFieldValue("old.resolution", null)
+ .setFieldValue("new.status", "RESOLVED")
+ .setFieldValue("new.resolution", "FALSE-POSITIVE")
+ .setFieldValue("new.comment", "Because!");
+ EmailMessage message = template.format(notification);
+ assertThat(message.getMessageId(), is("review/1"));
+ assertThat(message.getSubject(), is("Review #1"));
+ assertThat(message.getFrom(), is("Freddy Mallet"));
+ assertThat(message.getMessage(), is("Status: RESOLVED (was REOPENED)\nResolution: FALSE-POSITIVE\nComment:\n Because!\n\n--\nSee it in Sonar: http://nemo.sonarsource.org/review/view/1\n"));
+ }
+
+ @Test
+ public void shouldNotFormat() {
+ Notification notification = new Notification("other");
+ EmailMessage message = template.format(notification);
+ assertThat(message, nullValue());
+ }
+
+ @Test
+ public void shouldReturnFullNameOrLogin() {
+ assertThat(template.getUserFullName("freddy.mallet"), is("Freddy Mallet"));
+ assertThat(template.getUserFullName("deleted"), is("deleted"));
+ }
+
+}