Skip to content
Permalink
Browse files
Merge pull request #13 from ninian/master
JENKINS-7824: Add 'assigned by' field and optional emails
  • Loading branch information
ki82 committed Oct 14, 2014
2 parents dcefc3d + e0ee96d commit 10c6cf1221cab49bc9e59914a33aed3df5a0e6f3
Showing 19 changed files with 419 additions and 28 deletions.
10 pom.xml
@@ -4,7 +4,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>1.490</version>
<version>1.509.4</version>
</parent>

<artifactId>claim</artifactId>
@@ -34,6 +34,14 @@
<url>http://repo.jenkins-ci.org/public/</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>mailer</artifactId>
<version>1.5</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>


@@ -13,6 +13,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.mail.MessagingException;
import javax.servlet.ServletException;

import org.acegisecurity.Authentication;
@@ -31,6 +32,7 @@

private boolean claimed;
private String claimedBy;
private String assignedBy;
private Date claimDate;
private boolean transientClaim;

@@ -49,11 +51,14 @@ public String getIconFileName() {
public String getUrlName() {
return "claim";
}

abstract String getUrl();

public void doClaim(StaplerRequest req, StaplerResponse resp)
throws ServletException, IOException {
Authentication authentication = Hudson.getAuthentication();
String name = authentication.getName(); // Default to self-assignment
String currentUser = authentication.getName();
String name = currentUser; // Default to self-assignment
String assignee = req.getSubmittedForm().getString("assignee");
if (!StringUtils.isEmpty(assignee) && !name.equals(assignee)) {
// Validate the specified assignee.
@@ -68,8 +73,16 @@ public void doClaim(StaplerRequest req, StaplerResponse resp)
String reason = req.getSubmittedForm().getString("reason");
boolean sticky = req.getSubmittedForm().getBoolean("sticky");
if (StringUtils.isEmpty(reason)) reason = null;
claim(name, reason, sticky);
claim(name, reason, currentUser, sticky);
try {
ClaimEmailer.sendEmailIfConfigured(User.get(name, false, Collections.EMPTY_MAP), currentUser, owner.toString(), reason, getUrl());
} catch (MessagingException e) {
LOGGER.log(Level.WARNING, "Exception encountered sending assignment email: " + e.getMessage());
} catch (InterruptedException e) {
LOGGER.log(Level.WARNING, "Interrupted when sending assignment email",e);
}
owner.save();

resp.forwardToPreviousPage(req);
}

@@ -84,6 +97,11 @@ public void doUnclaim(StaplerRequest req, StaplerResponse resp)
public String getClaimedBy() {
return claimedBy;
}

@Exported
public String getAssignedBy() {
return assignedBy;
}

public String getClaimedByName() {
User user = User.get(claimedBy, false,Collections.EMPTY_MAP);
@@ -93,36 +111,51 @@ public String getClaimedByName() {
return claimedBy;
}
}

public String getAssignedByName() {
User user = User.get(assignedBy, false,Collections.EMPTY_MAP);
if (user != null) {
return user.getDisplayName();
} else {
return assignedBy;
}
}

public void setClaimedBy(String claimedBy) {
this.claimedBy = claimedBy;
}

public void setAssignedBy (String assignedBy) {
this.assignedBy = assignedBy;
}

@Exported
public boolean isClaimed() {
return claimed;
}

public void claim(String claimedBy, String reason, boolean sticky) {
public void claim(String claimedBy, String reason, String assignedBy, boolean sticky) {
this.claimed = true;
this.claimedBy = claimedBy;
this.reason = reason;
this.transientClaim = !sticky;
this.claimDate = new Date();
this.assignedBy = assignedBy;
}

/**
* Claim a new Run with the same settings as this one.
*/
public void copyTo(AbstractClaimBuildAction<T> other) {
other.claim(getClaimedBy(), getReason(), isSticky());
other.claim(getClaimedBy(), getReason(), getAssignedBy(), isSticky());
}

public void unclaim() {
this.claimed = false;
this.claimedBy = null;
this.transientClaim = false;
this.claimDate = null;
this.assignedBy = null;
// we remember the reason to show it if someone reclaims this build.
}

@@ -180,7 +213,24 @@ public Date getClaimDate() {
public boolean hasClaimDate() {
return this.claimDate != null;
}

/**
* was the action claimed by someone to themselves?
* @return true if the item was claimed by the user to themselves, false otherwise
*/
public boolean isSelfAssigned() {
boolean ret = true;
if (! isClaimed()) {
ret = false;
} else if (getClaimedBy() == null) {
ret = false;
} else if (! getClaimedBy().equals(getAssignedBy())) {
ret = false;
}
return ret;
}

public abstract String getNoun();


}
@@ -30,4 +30,9 @@ public Object readResolve() throws ObjectStreamException {
return this;
}

@Override
String getUrl() {
return owner.getUrl();
}

}
@@ -0,0 +1,61 @@
package hudson.plugins.claim;

import hudson.Extension;
import jenkins.model.GlobalConfiguration;
import net.sf.json.JSONObject;

import org.kohsuke.stapler.StaplerRequest;

@Extension
public class ClaimConfig extends GlobalConfiguration {

public ClaimConfig() {
load();
}

/**
* Whether we want to send emails to the assignee when items are claimed/assigned
*/
private boolean sendEmails;


/**
* This human readable name is used in the configuration screen.
*/
public String getDisplayName() {
return "Claim";
}

@Override
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
// To persist global configuration information,
// set that to properties and call save().
sendEmails = formData.getBoolean("sendEmails");
save();
return super.configure(req,formData);
}

/**
* This method returns true if the global configuration says we should send mails on build claims
* @return true if configuration is that we send emails for claims, false otherwise
*/
public boolean getSendEmails() {
return sendEmails;
}

/**
* Set whether we should send emails
* @param val the setting to use
*/
public void setSendEmails(boolean val) {
sendEmails = val;
}

/**
* get the current claim configuration
* @return the global claim configuration
*/
public static ClaimConfig get() {
return GlobalConfiguration.all().get(ClaimConfig.class);
}
}
@@ -0,0 +1,124 @@
package hudson.plugins.claim;

import hudson.model.User;
import hudson.tasks.Mailer;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.mail.Address;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import jenkins.model.JenkinsLocationConfiguration;
/**
* Email utility class to allow sending of emails using the setup of the mailer plug-in to do so.
* If the mailer plug-in is not installed, then no emails are sent
*
*
*/
public class ClaimEmailer {

private static final Logger LOGGER = Logger.getLogger("claim-plugin");

private static final boolean mailerLoaded = isMailerLoaded();

private static boolean isMailerLoaded () {
boolean ret = true;
try {
new Mailer.DescriptorImpl();
} catch (Throwable e) {
LOGGER.warning("Mailer plugin is not installed. Mailer plugin must be installed if you want to send emails");
ret = false;
}
return ret;
}

/**
* Send an email to the assignee indicating that the given build has been assigned
* @param assignee the user assigned the failed build
* @param assignedBy the user assigning the build
* @param build the build/action which has been assigned
* @param reason the reason given for the assignment
* @param URL the URL the user can view for the assigned build
* @throws MessagingException if there has been some problem with sending the email
* @throws IOException if there is an IO problem when sending the mail
* @throws InterruptedException if the send operation is interrupted
*/
public static void sendEmailIfConfigured (User assignee, String assignedBy, String build, String reason, String URL) throws MessagingException, IOException, InterruptedException {

ClaimConfig config = ClaimConfig.get();
if (config.getSendEmails() && mailerLoaded) {
MimeMessage msg = createMessage(assignee, assignedBy, build, reason, URL);
Transport.send(msg);
}
}

private static MimeMessage createMessage(User assignee, String assignedBy, String build, String reason, String URL)
throws MessagingException, IOException, InterruptedException {
MimeMessage msg = null;
// create Session
final Mailer.DescriptorImpl mailDescriptor = new Mailer.DescriptorImpl();
msg = createMimeMessage(mailDescriptor);

msg.setSentDate(new Date());
msg.setSubject(Messages.ClaimEmailer_Subject(build),mailDescriptor.getCharset());
//TODO configurable formatting, through email-ext plugin
final String text = Messages.ClaimEmailer_Text(build, assignedBy) +
System.getProperty("line.separator") + Messages.ClaimEmailer_Reason(reason) +
System.getProperty("line.separator") + System.getProperty("line.separator") +
Messages.ClaimEmailer_Details(JenkinsLocationConfiguration.get().getUrl() + URL);

msg.setText(text, mailDescriptor.getCharset());
msg.setRecipient(RecipientType.TO, getUserEmail(assignee,mailDescriptor));

return msg;
}

/**
* Creates MimeMessage using the mailer plugin for jenkins.
*
* @param mailDescriptor a reference to the mailer plugin from which we can get mailing parameters
* @return mimemessage a message which can be emailed
* @throws MessagingException
* @throws UnsupportedEncodingException
* @throws AddressException
*
*/
private static MimeMessage createMimeMessage(final Mailer.DescriptorImpl mailDescriptor) throws AddressException, UnsupportedEncodingException, MessagingException {
MimeMessage ret = new MimeMessage(mailDescriptor.createSession());
ret.setFrom(Mailer.StringToAddress(JenkinsLocationConfiguration.get().getAdminAddress(), mailDescriptor.getCharset()));
return ret;
}

/**
* Returns the email address of a given user.
*
* @param user the user
* @param mailDescriptor the descriptor allowing us to access mail config
* @return email address for this user, null if none can be derived
*/
private static Address getUserEmail(User user, Mailer.DescriptorImpl mailDescriptor) {
if (user != null) {
try {
final Mailer.UserProperty mailProperty = user
.getProperty(Mailer.UserProperty.class);
if (mailProperty != null && mailProperty.getAddress() != null) {
return new InternetAddress(mailProperty.getAddress());
}
return new InternetAddress(user.getId() + mailDescriptor.getDefaultSuffix());
} catch (AddressException e) {
LOGGER.log(Level.WARNING, "Cannot get email address for user " + user.getId(), e);
}
}
return null;
}

}
@@ -16,8 +16,8 @@ public String getDisplayName() {
}

@Override
public void claim(String claimedBy, String reason, boolean sticky) {
super.claim(claimedBy, reason, sticky);
public void claim(String claimedBy, String reason, String assignedBy, boolean sticky) {
super.claim(claimedBy, reason, assignedBy, sticky);
owner.addClaim(testObjectId, this);
}

@@ -26,4 +26,9 @@ public String getNoun() {
return Messages.ClaimTestAction_Noun();
}

@Override
String getUrl() {
return owner.getURL();
}

}
@@ -57,6 +57,10 @@ public static class Data extends TestResultAction.Data implements Saveable {
public Data(AbstractBuild<?,?> build) {
this.build = build;
}

public String getURL() {
return build.getUrl();
}

@Override
public List<TestAction> getTestAction(TestObject testObject) {

0 comments on commit 10c6cf1

Please sign in to comment.