diff --git a/pom.xml b/pom.xml index 87656dd..7f51638 100644 --- a/pom.xml +++ b/pom.xml @@ -62,5 +62,11 @@ <version>1.8.2</version> <scope>test</scope> </dependency> + + <dependency> + <groupId>org.codehaus.groovy</groupId> + <artifactId>groovy-all</artifactId> + <version>1.5.6</version> + </dependency> </dependencies> </project> diff --git a/src/main/java/hudson/plugins/emailext/EmailType.java b/src/main/java/hudson/plugins/emailext/EmailType.java index 20f5f94..e5687df 100644 --- a/src/main/java/hudson/plugins/emailext/EmailType.java +++ b/src/main/java/hudson/plugins/emailext/EmailType.java @@ -40,6 +40,11 @@ public class EmailType { */ private boolean sendToRecipientList; + /** + * Specifies whether the mail's content should be interpreted as Groovy script + */ + private boolean script; + public EmailType(){ subject = ""; body = ""; @@ -47,6 +52,7 @@ public class EmailType { sendToDevelopers = false; includeCulprits = false; sendToRecipientList = false; + script = false; } public String getSubject() { @@ -104,4 +110,11 @@ public class EmailType { this.recipientList = recipientList; } + public boolean isScript() { + return script; + } + + public void setScript(boolean script) { + this.script = script; + } } diff --git a/src/main/java/hudson/plugins/emailext/ExtendedEmailPublisher.java b/src/main/java/hudson/plugins/emailext/ExtendedEmailPublisher.java index dd65fc9..7d70b8d 100644 --- a/src/main/java/hudson/plugins/emailext/ExtendedEmailPublisher.java +++ b/src/main/java/hudson/plugins/emailext/ExtendedEmailPublisher.java @@ -2,13 +2,7 @@ package hudson.plugins.emailext; import hudson.Extension; import hudson.Launcher; -import hudson.model.AbstractBuild; -import hudson.model.AbstractProject; -import hudson.model.BuildListener; -import hudson.model.Hudson; -import hudson.model.ParametersAction; -import hudson.model.Result; -import hudson.model.User; +import hudson.model.*; import hudson.plugins.emailext.plugins.ContentBuilder; import hudson.plugins.emailext.plugins.EmailTrigger; import hudson.plugins.emailext.plugins.EmailTriggerDescriptor; @@ -21,8 +15,7 @@ import hudson.tasks.Notifier; import hudson.tasks.Publisher; import hudson.util.FormValidation; import net.sf.json.JSONObject; -import org.kohsuke.stapler.QueryParameter; -import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.*; import javax.mail.Address; import javax.mail.Authenticator; @@ -47,6 +40,10 @@ import java.util.Properties; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import java.nio.charset.Charset; + +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.StaplerRequest; /** * {@link Publisher} that sends notification e-mail. @@ -70,8 +67,10 @@ public class ExtendedEmailPublisher extends Notifier { public static final String PROJECT_DEFAULT_BODY_TEXT = "$PROJECT_DEFAULT_CONTENT"; public static final String CHARSET = "utf-8"; - - public static void addEmailTriggerType(EmailTriggerDescriptor triggerType) throws EmailExtException { + + private static final String DEFAULT_CHARSET_SENTINAL = "default"; + + public static void addEmailTriggerType(EmailTriggerDescriptor triggerType) throws EmailExtException { if(EMAIL_TRIGGER_TYPE_MAP.containsKey(triggerType.getMailerId())) throw new EmailExtException("An email trigger type with name " + triggerType.getTriggerName() + " was already added."); @@ -117,6 +116,11 @@ public class ExtendedEmailPublisher extends Notifier { public String contentType; /** + * The charset of the emails for this project. + */ + public String charset; + + /** * The default subject of the emails for this project. ($PROJECT_DEFAULT_SUBJECT) */ public String defaultSubject; @@ -126,6 +130,10 @@ public class ExtendedEmailPublisher extends Notifier { */ public String defaultContent; + public boolean defaultContentIsScript; + + public String buildForTesting; + /** * Get the list of configured email triggers for this project. */ @@ -263,8 +271,7 @@ public class ExtendedEmailPublisher extends Notifier { msg.setFrom(new InternetAddress(ExtendedEmailPublisher.DESCRIPTOR.getAdminAddress())); } - // Set the contents of the email - + //Set the contents of the email msg.setSentDate(new Date()); setSubject( type, build, msg ); @@ -327,18 +334,37 @@ public class ExtendedEmailPublisher extends Notifier { return msg; } + private static boolean isNullOrBlank(String s) { // or, add dependency on commons-lang StringUtils instead? + return s == null || s.trim().length() == 0; + } + + private String getCharset() { + String cs = charset; + if (isNullOrBlank(cs) || DEFAULT_CHARSET_SENTINAL.equalsIgnoreCase(cs)) { + cs = DESCRIPTOR.getDefaultCharset(); + } + if (isNullOrBlank(cs) || DEFAULT_CHARSET_SENTINAL.equalsIgnoreCase(cs)) { + return CHARSET; + } else { + return cs; + } + } + private void setSubject( final EmailType type, final AbstractBuild<?, ?> build, MimeMessage msg ) throws MessagingException { String subject = new ContentBuilder().transformText(type.getSubject(), this, type, (AbstractBuild)build); - msg.setSubject(subject, CHARSET); + msg.setSubject(subject, getCharset()); } private void setContent( final EmailType type, final AbstractBuild<?, ?> build, MimeMessage msg ) throws MessagingException { final String text = new ContentBuilder().transformText(type.getBody(), this, type, (AbstractBuild)build); + msg.setContent(text, getContentType()); + } + private String getContentType() { String messageContentType = contentType; // contentType is null if the project was not reconfigured after upgrading. if (messageContentType == null || "default".equals(messageContentType)) { @@ -349,9 +375,8 @@ public class ExtendedEmailPublisher extends Notifier { messageContentType = "text/plain"; } } - messageContentType += "; charset=" + CHARSET; - - msg.setContent(text, messageContentType); + messageContentType += "; charset=" + getCharset(); + return messageContentType; } private static void addAddress(List<InternetAddress> addresses, String address, BuildListener listener) { @@ -429,6 +454,11 @@ public class ExtendedEmailPublisher extends Notifier { private String defaultContentType; /** + * This is a global default charset (mime type) for emails. + */ + private String defaultCharset; + + /** * This is a global default subject line for sending emails. */ private String defaultSubject; @@ -438,9 +468,19 @@ public class ExtendedEmailPublisher extends Notifier { */ private String defaultBody; + /** + * This indicates that the global default body or subject line should be evaluated as a script. + */ + private boolean defaultIsScript; + + /** + * This just remembers the last build for testing that was saved, for the user's convenience. + */ + public String defaultBuildForTesting; + private boolean overrideGlobalSettings; - - @Override + + @Override public String getDisplayName() { return "Editable Email Notification"; } @@ -535,9 +575,13 @@ public class ExtendedEmailPublisher extends Notifier { public String getSmtpPort() { return smtpPort; } - - public String getDefaultContentType() { - return defaultContentType; + + public String getDefaultContentType() { + return defaultContentType; + } + + public String getDefaultCharset() { + return defaultCharset; } public String getDefaultSubject() { @@ -547,6 +591,14 @@ public class ExtendedEmailPublisher extends Notifier { public String getDefaultBody() { return defaultBody; } + + public boolean getDefaultIsScript() { + return defaultIsScript; + } + + public String getDefaultBuildForTesting() { + return defaultBuildForTesting; + } public boolean getOverrideGlobalSettings() { return overrideGlobalSettings; @@ -565,10 +617,13 @@ public class ExtendedEmailPublisher extends Notifier { ExtendedEmailPublisher m = new ExtendedEmailPublisher(); m.recipientList = listRecipients; m.contentType = formData.getString("project_content_type"); - m.defaultSubject = formData.getString("project_default_subject"); + m.charset = formData.getString("project_charset"); + m.defaultSubject = formData.getString("project_default_subject"); m.defaultContent = formData.getString("project_default_content"); - m.configuredTriggers = new ArrayList<EmailTrigger>(); - + m.defaultContentIsScript = formData.optBoolean("project_default_content_is_script"); + m.buildForTesting = formData.getString("project_build_for_testing"); + m.configuredTriggers = new ArrayList<EmailTrigger>(); + // Create a new email trigger for each one that is configured for (String mailerId : EMAIL_TRIGGER_TYPE_MAP.keySet()) { if("true".equalsIgnoreCase(formData.optString("mailer_" + mailerId + "_configured"))) { @@ -577,7 +632,6 @@ public class ExtendedEmailPublisher extends Notifier { m.configuredTriggers.add(trigger); } } - return m; } @@ -590,6 +644,7 @@ public class ExtendedEmailPublisher extends Notifier { m.setSendToRecipientList(formData.optBoolean(prefix + "sendToRecipientList")); m.setSendToDevelopers(formData.optBoolean(prefix + "sendToDevelopers")); m.setIncludeCulprits(formData.optBoolean(prefix + "includeCulprits")); + m.setScript(formData.optBoolean(prefix + "script")); return m; } @@ -636,12 +691,15 @@ public class ExtendedEmailPublisher extends Notifier { smtpPort = nullify(req.getParameter("ext_mailer_smtp_port")); defaultContentType = nullify(req.getParameter("ext_mailer_default_content_type")); + defaultCharset = nullify(req.getParameter("ext_mailer_default_charset")); // Allow global defaults to be set for the subject and body of the email defaultSubject = nullify(req.getParameter("ext_mailer_default_subject")); defaultBody = nullify(req.getParameter("ext_mailer_default_body")); - - overrideGlobalSettings = req.getParameter("ext_mailer_override_global_settings") != null; + defaultIsScript = req.getParameter("ext_mailer_default_is_script") != null; + defaultBuildForTesting = req.getParameter("ext_mailer_default_build_for_testing"); + + overrideGlobalSettings = req.getParameter("ext_mailer_use_global_settings") != null; save(); return super.configure(req, formData); @@ -685,9 +743,130 @@ public class ExtendedEmailPublisher extends Notifier { } return FormValidation.ok(); } - - } + public FormValidation doCharsetCheck(StaplerRequest req, StaplerResponse rsp, @QueryParameter final String value) throws IOException, ServletException { + String charset = nullify(value); + if (charset == null || DEFAULT_CHARSET_SENTINAL.equalsIgnoreCase(charset) || Charset.isSupported(charset)) { + return FormValidation.ok(); + } else { + return FormValidation.error("unsupported charset"); + } + } + + public FormValidation doBuildForTestingCheck(StaplerRequest req, StaplerResponse rsp, @QueryParameter final String value) throws IOException, ServletException { + String buildForTesting = nullify(value); + if (buildForTesting == null) { + return FormValidation.ok(); + } + try { + getBuildForTesting(buildForTesting); + return FormValidation.ok(); + } + catch (FormValidation e) { + return e; + } + } + + // validateButton in config.jelly + public FormValidation doTestAgainstBuild(StaplerRequest req) throws IOException, ServletException { + ExtendedEmailPublisher publisher = new ExtendedEmailPublisher(); + publisher.contentType = req.getParameter("project_content_type"); + publisher.charset = req.getParameter("project_charset"); + publisher.defaultSubject = req.getParameter("project_default_subject"); + publisher.defaultContent = req.getParameter("project_default_content"); + publisher.defaultContentIsScript = Boolean.valueOf(req.getParameter("project_default_content_is_script")); + publisher.buildForTesting = req.getParameter("project_build_for_testing"); + return doTestAgainstBuild(publisher, false, req); + } + + // validateButton in global.jelly + public FormValidation doGlobalTestAgainstBuild(StaplerRequest req) throws IOException, ServletException { + ExtendedEmailPublisher publisher = new ExtendedEmailPublisher(); + // testing at project level because the corresponding globals are static + publisher.contentType = req.getParameter("ext_mailer_default_content_type"); + publisher.charset = req.getParameter("ext_mailer_default_charset"); + publisher.defaultSubject = req.getParameter("ext_mailer_default_subject"); + publisher.defaultContent = req.getParameter("ext_mailer_default_body"); + publisher.defaultContentIsScript = Boolean.valueOf(req.getParameter("ext_mailer_default_is_script")); + publisher.buildForTesting = req.getParameter("ext_mailer_default_build_for_testing"); + return doTestAgainstBuild(publisher, true, req); + } + + // for iframe callback + private String testedEmailText; + private String testedEmailContentType; + + private FormValidation doTestAgainstBuild(ExtendedEmailPublisher publisher, + boolean globallyResolved, + StaplerRequest req + ) throws FormValidation { + if (nullify(publisher.buildForTesting) == null) { + return FormValidation.error("need to specify a build for testing"); + } + testedEmailContentType = publisher.getContentType(); + AbstractBuild build = getBuildForTesting(publisher.buildForTesting); + String subject; + if (globallyResolved) { + // Work around ContentBuilder.transformText()'s static access of the global subject and body, + // which has not been updated before testing. + subject = transformResolvedText(publisher.defaultSubject, publisher, build); + testedEmailText = transformResolvedText(publisher.defaultContent, publisher, build); + } else { + // use default tokens to induce resolution for project-level script flag + subject = transformText(ExtendedEmailPublisher.PROJECT_DEFAULT_SUBJECT_TEXT, publisher, build); + testedEmailText = transformText(ExtendedEmailPublisher.PROJECT_DEFAULT_BODY_TEXT, publisher, build); + } + String resultUrl = req.getRequestURI().replace("testAgainstBuild", "testedEmailText") + .replace("globalTestAgainstBuild", "testedEmailText"); // todo: something less hacky? + return FormValidation.okWithMarkup("resulting subject: " + subject // todo: subject charset? + + "<br/>resulting body:<br/> <iframe width='100%' height='400px' src='" + resultUrl + "'/>"); + } + private static String transformResolvedText(String text, ExtendedEmailPublisher publisher, AbstractBuild build) { + return new ContentBuilder().transformResolvedText(publisher.defaultContentIsScript, text, publisher, new EmailType(), build); + } + private static String transformText(String text, ExtendedEmailPublisher publisher, AbstractBuild build) { + return new ContentBuilder().transformText(text, publisher, new EmailType(), build); + } + + // callback from iframe + public void doTestedEmailText(StaplerRequest req, StaplerResponse rsp) throws IOException { + rsp.setContentType(testedEmailContentType); + rsp.getWriter().write(testedEmailText); + } + + private AbstractBuild getBuildForTesting(String buildForTesting) throws FormValidation { + int slashIndex = buildForTesting.indexOf('/'); + if (slashIndex == -1) { + throw FormValidation.error("must format as '<jobName>/<buildNumber>'"); + } + String jobName = buildForTesting.substring(0, slashIndex); + String buildNumber = buildForTesting.substring(slashIndex + 1); + Job job; + try { + job = (Job) Hudson.getInstance().getItem(jobName); + } + catch (ClassCastException e) { + throw FormValidation.error(jobName + " is not a job"); + } + if (job == null) { + throw FormValidation.error(jobName + " job not found"); + } + AbstractBuild build; + try { + build = (AbstractBuild) job.getBuildByNumber(Integer.valueOf(buildNumber)); + } + catch (NumberFormatException e) { + throw FormValidation.error("cannot parse build number: " + e.getMessage()); + } + catch (ClassCastException e) { + throw FormValidation.error("not a build: " + e.getMessage()); + } + if (build == null) { + throw FormValidation.error("build " + buildNumber + " not found"); + } + return build; + } + } } diff --git a/src/main/java/hudson/plugins/emailext/plugins/ContentBuilder.java b/src/main/java/hudson/plugins/emailext/plugins/ContentBuilder.java index 55a7b21..58b6b56 100644 --- a/src/main/java/hudson/plugins/emailext/plugins/ContentBuilder.java +++ b/src/main/java/hudson/plugins/emailext/plugins/ContentBuilder.java @@ -1,5 +1,7 @@ package hudson.plugins.emailext.plugins; +import groovy.text.SimpleTemplateEngine; +import groovy.text.Template; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.plugins.emailext.EmailExtException; @@ -61,13 +63,23 @@ public class ContentBuilder { public <P extends AbstractProject<P, B>, B extends AbstractBuild<P, B>> String transformText(String origText, ExtendedEmailPublisher publisher, EmailType type, B build) { - String newText = origText.replaceAll(PROJECT_DEFAULT_BODY, Matcher.quoteReplacement(publisher.defaultContent)) - .replaceAll(PROJECT_DEFAULT_SUBJECT, Matcher.quoteReplacement(publisher.defaultSubject)) - .replaceAll(DEFAULT_BODY, Matcher.quoteReplacement(ExtendedEmailPublisher.DESCRIPTOR.getDefaultBody())) - .replaceAll(DEFAULT_SUBJECT, Matcher.quoteReplacement(ExtendedEmailPublisher.DESCRIPTOR.getDefaultSubject())); - - newText = replaceTokensWithContent(newText, publisher, type, build); - return newText; + boolean isScript = type.isScript(); + String projectlyResolvedText = origText.replaceAll(PROJECT_DEFAULT_BODY, Matcher.quoteReplacement(publisher.defaultContent)) + .replaceAll(PROJECT_DEFAULT_SUBJECT, Matcher.quoteReplacement(publisher.defaultSubject)); + boolean usingSomeProjectDefaultContent = !projectlyResolvedText.equals(origText); + isScript |= publisher.defaultContentIsScript && usingSomeProjectDefaultContent; + String globallyResolvedText = projectlyResolvedText.replaceAll(DEFAULT_BODY, Matcher.quoteReplacement(ExtendedEmailPublisher.DESCRIPTOR.getDefaultBody())) + .replaceAll(DEFAULT_SUBJECT, Matcher.quoteReplacement(ExtendedEmailPublisher.DESCRIPTOR.getDefaultSubject())); + boolean usingSomeGlobalDefaultContent = !globallyResolvedText.equals(projectlyResolvedText); + isScript |= ExtendedEmailPublisher.DESCRIPTOR.getDefaultIsScript() && usingSomeGlobalDefaultContent; + return transformResolvedText(isScript, globallyResolvedText, publisher, type, build); + } + + // exposed for testing + public <P extends AbstractProject<P, B>, B extends AbstractBuild<P, B>> + String transformResolvedText(boolean isScript, String text, ExtendedEmailPublisher publisher, EmailType type, B build) { + return isScript ? transformUsingScript(text, publisher, type, build) + : replaceTokensWithContent(text, publisher, type, build); } private static <P extends AbstractProject<P, B>, B extends AbstractBuild<P, B>> @@ -193,4 +205,34 @@ public class ContentBuilder { } + private <P extends AbstractProject<P, B>, B extends AbstractBuild<P, B>> + String transformUsingScript(String origText, ExtendedEmailPublisher publisher, EmailType type, B build) { + SimpleTemplateEngine engine = new SimpleTemplateEngine(); + Template template = null; + try { + template = engine.createTemplate(origText); + Map binding = new HashMap(); + Map<String,Object> args = new HashMap<String,Object>(); + //Setting the AbstractBuild as a bind variable + binding.put("build", build); + + //Set all the EmailContent as bind variables to be directly used in the script + for(Map.Entry<String, EmailContent> contentEntry : EMAIL_CONTENT_TYPE_MAP.entrySet()){ + EmailContent content = contentEntry.getValue(); + String contentText = content.getContent(build, publisher, type, args); + if(content.hasNestedContent()){ + contentText = replaceTokensWithContent(contentText, publisher, type, build); + } + binding.put(contentEntry.getKey(), contentText); + } + + return template.make(binding).toString(); + } catch (Exception e) { + LOGGER.log(Level.WARNING, "Error creating GroovyTemplate from text ["+origText+"]",e); + //Throwing the exception as Runtime as any exception here would mostly (?) be due to incorrect script + //and not an expected reason + throw new RuntimeException("Error using the template",e); + } + } + } diff --git a/src/main/resources/hudson/plugins/emailext/ExtendedEmailPublisher/config.jelly b/src/main/resources/hudson/plugins/emailext/ExtendedEmailPublisher/config.jelly index 770159b..e3a3fd8 100644 --- a/src/main/resources/hudson/plugins/emailext/ExtendedEmailPublisher/config.jelly +++ b/src/main/resources/hudson/plugins/emailext/ExtendedEmailPublisher/config.jelly @@ -19,7 +19,7 @@ help="/plugin/email-ext/help/projectConfig/globalRecipientList.html"> <input class="setting-input validated" name="recipientlist_recipients" type="text" value="${instance.recipientList}" - checkUrl="'${rootURL}/publisher/ExtendedEmailPublisher/recipientListRecipientsCheck?value='+encode(this.value)"/> + checkUrl="'${rootURL}/publisher/ExtendedEmailPublisher/recipientListRecipientsCheck?value='+toValue(this)"/> </f:entry> <!-- This is the content type for the emails. --> @@ -39,6 +39,25 @@ </select> </f:entry> + <f:entry title="Charset" + help="/plugin/email-ext/help/projectConfig/charset.html"> + <!--input class="setting-input validated" name="project_charset" + type="text" value="${instance.charset}" + checkUrl="'${rootURL}/publisher/ExtendedEmailPublisher/charsetCheck?value='+toValue(this)"/--> + <j:choose> + <j:when test="${instance.configured}"> + <input class="setting-input validated" name="project_charset" + type="text" value="${instance.charset}" + checkUrl="'${rootURL}/publisher/ExtendedEmailPublisher/charsetCheck?value='+toValue(this)"/> + </j:when> + <j:otherwise> + <input class="setting-input validated" name="project_charset" + type="text" value="default" + checkUrl="'${rootURL}/publisher/ExtendedEmailPublisher/charsetCheck?value='+toValue(this)"/> + </j:otherwise> + </j:choose> + </f:entry> + <!-- This is the default subject line for the project. --> <f:entry title="Default Subject" help="/plugin/email-ext/help/projectConfig/defaultSubject.html"> @@ -54,6 +73,19 @@ </j:choose> </f:entry> + <f:entry title="Default Content is Script" + help="/plugin/email-ext/help/projectConfig/mailType/script.html"> + <j:choose> + <j:when test="${instance.configured}"> + <f:checkbox name="project_default_content_is_script" + checked="${instance.defaultContentIsScript}" /> + </j:when> + <j:otherwise> + <f:checkbox name="project_default_content_is_script" checked="false" /> + </j:otherwise> + </j:choose> + </f:entry> + <!-- This is the default content for the project. --> <f:entry title="Default Content" help="/plugin/email-ext/help/projectConfig/defaultBody.html"> @@ -87,7 +119,16 @@ <td colspan="2"><div id="${secId}contentTokenHelpConf" class="help" style="display:none">${contentTokenText}</div></td> <td></td> </tr> - + + <f:entry title="Build for Testing" + help="/plugin/email-ext/help/projectConfig/buildForTesting.html"> + <input class="setting-input validated" name="project_build_for_testing" + type="text" value="${instance.buildForTesting}" + checkUrl="'${rootURL}/publisher/ExtendedEmailPublisher/buildForTestingCheck?value='+toValue(this)"/> + </f:entry> + + <f:validateButton title="${%Test Against Build}" method="testAgainstBuild" with="project_build_for_testing,project_default_content_is_script,project_default_content,project_default_subject,project_charset,project_content_type,recipientlist_recipients" /> + <!-- Configure advanced properties like per-build-result status email contents, whether or not to send email to developers who made changes, and whether or not to send email to the global list of devs--> diff --git a/src/main/resources/hudson/plugins/emailext/ExtendedEmailPublisher/global.jelly b/src/main/resources/hudson/plugins/emailext/ExtendedEmailPublisher/global.jelly index 28c11f1..bda7c58 100644 --- a/src/main/resources/hudson/plugins/emailext/ExtendedEmailPublisher/global.jelly +++ b/src/main/resources/hudson/plugins/emailext/ExtendedEmailPublisher/global.jelly @@ -15,7 +15,7 @@ <f:entry title="System Admin E-mail Address" help="/descriptor/hudson.tasks.Mailer/help/adminAddress"> <f:textbox name="ext_mailer_admin_address" value="${descriptor.adminAddress}" - checkUrl="'${rootURL}/publisher/Mailer/addressCheck?value='+encode(this.value)"/> + checkUrl="'${rootURL}/publisher/Mailer/addressCheck?value='+toValue(this)"/> </f:entry> <f:entry title="Hudson URL" help="/descriptor/hudson.tasks.Mailer/help/url"> @@ -57,6 +57,12 @@ >HTML (text/html)</f:option> </select> </f:entry> + <f:entry title="Default Charset" + help="/plugin/email-ext/help/globalConfig/defaultCharset.html"> + <input class="setting-input validated" name="ext_mailer_default_charset" + type="text" value="${descriptor.defaultCharset}" + checkUrl="'${rootURL}/publisher/ExtendedEmailPublisher/charsetCheck?value='+toValue(this)"/> + </f:entry> <f:entry title="Default Subject" help="/plugin/email-ext/help/globalConfig/defaultSubject.html"> <input class="setting-input" name="ext_mailer_default_subject" @@ -68,6 +74,21 @@ name="ext_mailer_default_body" value="${descriptor.defaultBody}"/> </f:entry> + <f:entry title="Default is Script" + help="/plugin/email-ext/help/globalConfig/defaultIsScript.html"> + <f:checkbox name="ext_mailer_default_is_script" checked="${descriptor.defaultIsScript}" /> + </f:entry> + + <f:entry title="Build for Testing" + help="/plugin/email-ext/help/globalConfig/buildForTesting.html"> + <input class="setting-input validated" name="ext_mailer_default_build_for_testing" + type="text" value="${descriptor.defaultBuildForTesting}" + checkUrl="'${rootURL}/publisher/ExtendedEmailPublisher/buildForTestingCheck?value='+toValue(this)"/> + </f:entry> + + <f:validateButton title="${%Test Against Build}" method="globalTestAgainstBuild" + with="ext_mailer_default_build_for_testing,ext_mailer_default_is_script,ext_mailer_default_subject,ext_mailer_default_body,ext_mailer_default_charset,ext_mailer_default_content_type" /> + <!-- This is the help section. It displays a bunch of dynamic help for all content tokens. --> <tr> <td></td> diff --git a/src/main/resources/hudson/plugins/emailext/tags/mailtype.jelly b/src/main/resources/hudson/plugins/emailext/tags/mailtype.jelly index a0e0e85..aa4d03d 100644 --- a/src/main/resources/hudson/plugins/emailext/tags/mailtype.jelly +++ b/src/main/resources/hudson/plugins/emailext/tags/mailtype.jelly @@ -123,6 +123,11 @@ name="mailer_${mailType}_body" value="${mailTypeObj.body}"/> </f:entry> + <f:entry title="Content is Script" + help="${rootURL}/plugin/email-ext/help/projectConfig/mailType/script.html"> + <f:checkbox name="mailer_${mailType}_script" + checked="${mailTypeObj.script}" /> + </f:entry> </table> </td> </tr> diff --git a/src/main/webapp/help/globalConfig/buildForTesting.html b/src/main/webapp/help/globalConfig/buildForTesting.html new file mode 100644 index 0000000..017db19 --- /dev/null +++ b/src/main/webapp/help/globalConfig/buildForTesting.html @@ -0,0 +1,3 @@ +<div> + Specifies the build used by the Test Against Build button. Example: common/12 +</div> diff --git a/src/main/webapp/help/globalConfig/defaultCharset.html b/src/main/webapp/help/globalConfig/defaultCharset.html new file mode 100644 index 0000000..9f73c3c --- /dev/null +++ b/src/main/webapp/help/globalConfig/defaultCharset.html @@ -0,0 +1,4 @@ +<div> + The default charset of the emails sent after a build. + If this is set to "default", then it uses UTF-8. +</div> diff --git a/src/main/webapp/help/globalConfig/defaultIsScript.html b/src/main/webapp/help/globalConfig/defaultIsScript.html new file mode 100644 index 0000000..45ba123 --- /dev/null +++ b/src/main/webapp/help/globalConfig/defaultIsScript.html @@ -0,0 +1,4 @@ +<div> + Indicates that the global default subject or content includes a script. + If they are used within an email, the whole email will be evaluated as a script. +</div> diff --git a/src/main/webapp/help/projectConfig/buildForTesting.html b/src/main/webapp/help/projectConfig/buildForTesting.html new file mode 100644 index 0000000..017db19 --- /dev/null +++ b/src/main/webapp/help/projectConfig/buildForTesting.html @@ -0,0 +1,3 @@ +<div> + Specifies the build used by the Test Against Build button. Example: common/12 +</div> diff --git a/src/main/webapp/help/projectConfig/charset.html b/src/main/webapp/help/projectConfig/charset.html new file mode 100644 index 0000000..a30d9bf --- /dev/null +++ b/src/main/webapp/help/projectConfig/charset.html @@ -0,0 +1,4 @@ +<div> + The charset of the emails sent after a build. If this is set + to "default", then it uses the value set on the main configuration page. +</div> diff --git a/src/main/webapp/help/projectConfig/mailType/script.html b/src/main/webapp/help/projectConfig/mailType/script.html new file mode 100644 index 0000000..c97b9fd --- /dev/null +++ b/src/main/webapp/help/projectConfig/mailType/script.html @@ -0,0 +1,6 @@ +<div> + Specifies whether the mail's content should be interpreted as a Groovy + <a href="http://groovy.codehaus.org/Groovy+Templates">SimpleTemplate</a> script. + If so, the script can access the <code>AbstractBuild</code> object using the + <code>build</code> bind variable. +</div> diff --git a/src/test/java/hudson/plugins/emailext/ExtendedEmailPublisherTest.java b/src/test/java/hudson/plugins/emailext/ExtendedEmailPublisherTest.java index f3fd4fc..5591cd2 100644 --- a/src/test/java/hudson/plugins/emailext/ExtendedEmailPublisherTest.java +++ b/src/test/java/hudson/plugins/emailext/ExtendedEmailPublisherTest.java @@ -254,9 +254,11 @@ public class ExtendedEmailPublisherTest { JSONObject form = new JSONObject(); form.put( "project_content_type", "default" ); + form.put( "project_charset", "default" ); form.put( "recipientlist_recipients", "ashlux@gmail.com" ); form.put( "project_default_subject", "Make millions in Nigeria" ); form.put( "project_default_content", "Give me a $1000 check and I'll mail you back $5000!!!" ); + form.put( "project_build_for_testing", "" ); publisher = (ExtendedEmailPublisher) ExtendedEmailPublisher.DESCRIPTOR.newInstance( null, form ); diff --git a/src/test/java/hudson/plugins/emailext/plugins/ContentBuilderTest.java b/src/test/java/hudson/plugins/emailext/plugins/ContentBuilderTest.java index 6b98b0c..33b91c8 100644 --- a/src/test/java/hudson/plugins/emailext/plugins/ContentBuilderTest.java +++ b/src/test/java/hudson/plugins/emailext/plugins/ContentBuilderTest.java @@ -1,6 +1,7 @@ package hudson.plugins.emailext.plugins; import hudson.model.AbstractBuild; +import hudson.plugins.emailext.EmailType; import hudson.plugins.emailext.ExtendedEmailPublisher; import org.jvnet.hudson.test.HudsonTestCase; @@ -146,18 +147,18 @@ public class ContentBuilderTest public void testTransformText_shouldExpand_$PROJECT_DEFAULT_CONTENT() throws IOException, InterruptedException { - assertEquals(publisher.defaultContent, new ContentBuilder().transformText( "$PROJECT_DEFAULT_CONTENT", publisher, null, + assertEquals(publisher.defaultContent, new ContentBuilder().transformText( "$PROJECT_DEFAULT_CONTENT", publisher, new EmailType(), mock( AbstractBuild.class ) )); - assertEquals(publisher.defaultContent, new ContentBuilder().transformText( "${PROJECT_DEFAULT_CONTENT}", publisher, null, + assertEquals(publisher.defaultContent, new ContentBuilder().transformText( "${PROJECT_DEFAULT_CONTENT}", publisher, new EmailType(), mock( AbstractBuild.class ) )); } public void testTransformText_shouldExpand_$PROJECT_DEFAULT_SUBJECT() throws IOException, InterruptedException { - assertEquals(publisher.defaultSubject, new ContentBuilder().transformText( "$PROJECT_DEFAULT_SUBJECT", publisher, null, + assertEquals(publisher.defaultSubject, new ContentBuilder().transformText( "$PROJECT_DEFAULT_SUBJECT", publisher, new EmailType(), mock( AbstractBuild.class ) )); - assertEquals(publisher.defaultSubject, new ContentBuilder().transformText( "${PROJECT_DEFAULT_SUBJECT}", publisher, null, + assertEquals(publisher.defaultSubject, new ContentBuilder().transformText( "${PROJECT_DEFAULT_SUBJECT}", publisher, new EmailType(), mock( AbstractBuild.class ) )); } @@ -165,10 +166,10 @@ public class ContentBuilderTest throws IOException, InterruptedException { assertEquals( ExtendedEmailPublisher.DESCRIPTOR.getDefaultBody(), - new ContentBuilder().transformText( "$DEFAULT_CONTENT", publisher, null, + new ContentBuilder().transformText( "$DEFAULT_CONTENT", publisher, new EmailType(), mock( AbstractBuild.class ) ) ); assertEquals( ExtendedEmailPublisher.DESCRIPTOR.getDefaultBody(), - new ContentBuilder().transformText( "${DEFAULT_CONTENT}", publisher, null, + new ContentBuilder().transformText( "${DEFAULT_CONTENT}", publisher, new EmailType(), mock( AbstractBuild.class ) ) ); } @@ -176,10 +177,10 @@ public class ContentBuilderTest throws IOException, InterruptedException { assertEquals( ExtendedEmailPublisher.DESCRIPTOR.getDefaultSubject(), - new ContentBuilder().transformText( "$DEFAULT_SUBJECT", publisher, null, + new ContentBuilder().transformText( "$DEFAULT_SUBJECT", publisher, new EmailType(), mock( AbstractBuild.class ) ) ); assertEquals( ExtendedEmailPublisher.DESCRIPTOR.getDefaultSubject(), - new ContentBuilder().transformText( "${DEFAULT_SUBJECT}", publisher, null, + new ContentBuilder().transformText( "${DEFAULT_SUBJECT}", publisher, new EmailType(), mock( AbstractBuild.class ) ) ); } }