Index: email-ext/src/main/resources/hudson/plugins/emailext/ExtendedEmailPublisher/config.jelly
===================================================================
--- email-ext/src/main/resources/hudson/plugins/emailext/ExtendedEmailPublisher/config.jelly	(revision 19390)
+++ email-ext/src/main/resources/hudson/plugins/emailext/ExtendedEmailPublisher/config.jelly	Wed Dec 02 10:04:49 HST 2009
@@ -42,6 +42,25 @@
       </select>
     </f:entry>
 
+    <f:entry title="Charset"
+             help="${rootURL}/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='+encode(this.value)"/-->
+        <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='+encode(this.value)"/>
+            </j:when>
+            <j:otherwise>
+                <input class="setting-input validated" name="project_charset"
+                       type="text" value="default"
+                       checkUrl="'${rootURL}/publisher/ExtendedEmailPublisher/charsetCheck?value='+encode(this.value)"/>
+            </j:otherwise>
+        </j:choose>
+    </f:entry>
+
 	<!-- This is the default subject line for the project. -->
     <f:entry title="Default Subject"
              help="${rootURL}/plugin/email-ext/help/projectConfig/defaultSubject.html">
@@ -57,6 +76,19 @@
         </j:choose>
     </f:entry>
 
+    <f:entry title="Default Content is Script"
+	     help="${rootURL}/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="${rootURL}/plugin/email-ext/help/projectConfig/defaultBody.html">
@@ -90,7 +122,16 @@
 		<td colspan="2"><div id="contentTokenHelpConf" style="display:none" class="mailHelp">${contentTokenText}</div></td>
 		<td></td>
 	</tr>
-	
+
+    <f:entry title="Build for Testing"
+             help="${rootURL}/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='+encode(this.value)"/>
+    </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,project_default_content_is_script,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-->
Index: email-ext/src/main/webapp/help/projectConfig/mailType/script.html
===================================================================
--- email-ext/src/main/webapp/help/projectConfig/mailType/script.html	Tue Nov 24 17:23:05 HST 2009
+++ email-ext/src/main/webapp/help/projectConfig/mailType/script.html	Tue Nov 24 17:23:05 HST 2009
@@ -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>
Index: email-ext/src/main/java/hudson/plugins/emailext/ExtendedEmailPublisher.java
===================================================================
--- email-ext/src/main/java/hudson/plugins/emailext/ExtendedEmailPublisher.java	(revision 22062)
+++ email-ext/src/main/java/hudson/plugins/emailext/ExtendedEmailPublisher.java	Fri Dec 18 14:06:15 HST 2009
@@ -2,11 +2,7 @@
 
 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.User;
+import hudson.model.*;
 import hudson.plugins.emailext.plugins.ContentBuilder;
 import hudson.plugins.emailext.plugins.EmailTrigger;
 import hudson.plugins.emailext.plugins.EmailTriggerDescriptor;
@@ -30,6 +26,7 @@
 import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import java.nio.charset.Charset;
 
 import javax.mail.Address;
 import javax.mail.Authenticator;
@@ -46,6 +43,7 @@
 import net.sf.json.JSONObject;
 import org.kohsuke.stapler.QueryParameter;
 import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
 
 /**
  * {@link Publisher} that sends notification e-mail.
@@ -67,8 +65,10 @@
 	
 	public static final String PROJECT_DEFAULT_SUBJECT_TEXT = "$PROJECT_DEFAULT_SUBJECT";
 	public static final String PROJECT_DEFAULT_BODY_TEXT = "$PROJECT_DEFAULT_CONTENT";
-	
+
+    private static final String DEFAULT_CHARSET_SENTINAL = "default";
+
-	public static void addEmailTriggerType(EmailTriggerDescriptor triggerType) throws EmailExtException {
+    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.");
@@ -113,7 +113,12 @@
 	 */
 	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;
@@ -123,6 +128,10 @@
 	 */
 	public String defaultContent;
 	
+	public boolean defaultContentIsScript;
+
+    public String buildForTesting;
+
 	/**
 	 * Get the list of configured email triggers for this project.
 	 */
@@ -260,9 +269,16 @@
 		//Set the contents of the email
 		msg.setSentDate(new Date());
 		String subject = new ContentBuilder().transformText(type.getSubject(), this, type, build);
+        String specificCharset = charset;
+        if (specificCharset == null || DEFAULT_CHARSET_SENTINAL.equalsIgnoreCase(specificCharset)) {
+            specificCharset = DESCRIPTOR.getDefaultCharset();
+        }
+        if (specificCharset == null || DEFAULT_CHARSET_SENTINAL.equalsIgnoreCase(specificCharset)) {
-		msg.setSubject(subject);
+            msg.setSubject(subject);
+        } else {
+            msg.setSubject(subject, specificCharset);
+        }
 		String text = new ContentBuilder().transformText(type.getBody(), this, type, build);
-		msg.setContent(text, contentType);
 		String messageContentType = contentType;
 		// contentType is null if the project was not reconfigured after upgrading.
 		if (messageContentType == null || "default".equals(messageContentType)) {
@@ -273,6 +289,9 @@
 				messageContentType = "text/plain";
 			}
 		}
+        if (specificCharset != null && !DEFAULT_CHARSET_SENTINAL.equalsIgnoreCase(specificCharset)) {
+            messageContentType += "; charset=" + specificCharset;
+        }
 		msg.setContent(text, messageContentType);
 
 		// Get the recipients from the global list of addresses
@@ -388,8 +407,13 @@
 		 * This is a global default content type (mime type) for emails.
 		 */
 		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;
@@ -399,9 +423,19 @@
 		 */
 		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";
 		}
@@ -495,11 +529,15 @@
 		public String getSmtpPort() {
 			return smtpPort;
 		}
-		
-		public String getDefaultContentType() {
-			return defaultContentType;
-		}
-		
+
+        public String getDefaultContentType() {
+            return defaultContentType;
+        }
+
+        public String getDefaultCharset() {
+			return defaultCharset;
+		}
+		
 		public String getDefaultSubject() {
 			return defaultSubject;
 		}
@@ -507,7 +545,15 @@
 		public String getDefaultBody() {
 			return defaultBody;
 		}
-		
+
+        public boolean getDefaultIsScript() {
+            return defaultIsScript;
+        }
+
+        public String getDefaultBuildForTesting() {
+            return defaultBuildForTesting;
+        }
+		
 		public boolean getOverrideGlobalSettings() {
 			return overrideGlobalSettings;
 		}
@@ -525,10 +571,13 @@
 			ExtendedEmailPublisher m = new ExtendedEmailPublisher();
 			m.recipientList = listRecipients;
 			m.contentType = req.getParameter("project_content_type");
+            m.charset = req.getParameter("project_charset");
 			m.defaultSubject = req.getParameter("project_default_subject");
 			m.defaultContent = req.getParameter("project_default_content");
+			m.defaultContentIsScript = req.getParameter("project_default_content_is_script")!=null;
 			m.configuredTriggers = new ArrayList<EmailTrigger>();
+            m.buildForTesting = req.getParameter("project_build_for_testing");
-			
+
 			// Create a new email trigger for each one that is configured
 			for (String mailerId : EMAIL_TRIGGER_TYPE_MAP.keySet()) {
 				EmailType type = createMailType(req, mailerId);
@@ -556,6 +605,7 @@
 			m.setSendToRecipientList(req.getParameter(prefix + "sendToRecipientList")!=null);
 			m.setSendToDevelopers(req.getParameter(prefix + "sendToDevelopers")!=null);
 			m.setIncludeCulprits(req.getParameter(prefix + "includeCulprits")!=null);
+			m.setScript(req.getParameter(prefix + "script")!=null);
 			return m;
 		}
 		
@@ -602,11 +652,14 @@
 			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"));
+            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();
@@ -651,9 +704,130 @@
 			}
 			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;
+            }
+        }
 
+        private interface TransformStrategy {
+            String transformText(String origText, ExtendedEmailPublisher publisher, EmailType type, AbstractBuild build);
+        }
 
+        public FormValidation doTestAgainstBuild(StaplerRequest req, @QueryParameter("project_build_for_testing") String buildForTesting) throws IOException {
+            TransformStrategy strategy = new TransformStrategy() {
+                public String transformText(String origText, ExtendedEmailPublisher publisher, EmailType type, AbstractBuild build) {
+                    return new ContentBuilder().transformText(origText, publisher, type, build);
-}
+                }
+            };
+            return doTestAgainstBuild(strategy, PROJECT_DEFAULT_SUBJECT_TEXT, PROJECT_DEFAULT_BODY_TEXT, req, buildForTesting);
+        }
+
+        public FormValidation doGlobalTestAgainstBuild(StaplerRequest req,
+                                                       @QueryParameter("ext_mailer_default_build_for_testing") String buildForTesting,
+                                                       @QueryParameter("ext_mailer_default_is_script") final boolean globalIsScript,
+                                                       @QueryParameter("ext_mailer_default_subject") String globalSubject,
+                                                       @QueryParameter("ext_mailer_default_body") String globalBody
+        ) throws IOException {
+            TransformStrategy strategy = new TransformStrategy() {
+                public String transformText(String origText, ExtendedEmailPublisher publisher, EmailType type, AbstractBuild build) {
+                    // This works around ContentBuilder.transformText()'s static access of the global subject and body,
+                    // which has not been updated before testing.
+                    return new ContentBuilder().transformResolvedText(globalIsScript, origText, publisher, type, build);
+                }
+            };
+            return doTestAgainstBuild(strategy, globalSubject, globalBody, req, buildForTesting);
+        }
+
+        private String testedEmailText;
+
+        private FormValidation doTestAgainstBuild(TransformStrategy strategy,
+                                                  String originalSubject,
+                                                  String originalBody,
+                                                  StaplerRequest req,
+                                                  String buildForTesting
+        ) throws IOException {
+            buildForTesting = nullify(buildForTesting);
+            if (buildForTesting == null) {
+                return FormValidation.error("need to configure a build for testing");
+            }
+            try {
+                AbstractBuild build = getBuildForTesting(buildForTesting);
+                ExtendedEmailPublisher publisher = (ExtendedEmailPublisher) newInstance(req, null);
+                EmailType type = new EmailType();
+                type.setBody(ExtendedEmailPublisher.PROJECT_DEFAULT_BODY_TEXT);
+                type.setSubject(ExtendedEmailPublisher.PROJECT_DEFAULT_SUBJECT_TEXT);
+                String subject = strategy.transformText(originalSubject, publisher, type, build);
+                testedEmailText = strategy.transformText(originalBody, publisher, type, build);
+                String resultUrl = req.getRequestURI().replace("testAgainstBuild", "testedEmailText")
+                        .replace("globalTestAgainstBuild", "testedEmailText"); // todo: some better hack
+                return FormValidation.okWithMarkup("resulting subject: " + subject
+                        + "<br/>resulting body:<br/> <iframe width='100%' height='400px' src='" + resultUrl + "'/>");
+            }
+            catch (FormValidation e) {
+                return e;
+            } catch (FormException e) {
+                return FormValidation.error(e.getMessage());
+            }
+        }
+
+        public void doTestedEmailText(StaplerRequest req, StaplerResponse rsp) throws IOException {
+            rsp.setContentType("text/html");
+            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;
+        }
+	}
+
+
+
+}
Index: email-ext/src/main/resources/hudson/plugins/emailext/tags/mailtype.jelly
===================================================================
--- email-ext/src/main/resources/hudson/plugins/emailext/tags/mailtype.jelly	(revision 14861)
+++ email-ext/src/main/resources/hudson/plugins/emailext/tags/mailtype.jelly	Tue Nov 24 17:23:05 HST 2009
@@ -122,6 +122,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>
@@ -130,4 +135,4 @@
 	<script type="text/javascript">swapEdit("${mailType}");</script>
 
 	<d:invokeBody />
-</j:jelly>
\ No newline at end of file
+</j:jelly>
Index: email-ext/src/main/webapp/help/globalConfig/defaultIsScript.html
===================================================================
--- email-ext/src/main/webapp/help/globalConfig/defaultIsScript.html	Tue Nov 24 18:49:41 HST 2009
+++ email-ext/src/main/webapp/help/globalConfig/defaultIsScript.html	Tue Nov 24 18:49:41 HST 2009
@@ -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>
\ No newline at end of file
Index: email-ext/src/main/webapp/help/globalConfig/defaultCharset.html
===================================================================
--- email-ext/src/main/webapp/help/globalConfig/defaultCharset.html	Mon Nov 30 16:54:59 HST 2009
+++ email-ext/src/main/webapp/help/globalConfig/defaultCharset.html	Mon Nov 30 16:54:59 HST 2009
@@ -0,0 +1,4 @@
+<div>
+    The default charset of the emails sent after a build.
+    If this is set to default, then it uses the Hudson JVM's default charset.
+</div>
Index: email-ext/pom.xml
===================================================================
--- email-ext/pom.xml	(revision 22077)
+++ email-ext/pom.xml	Tue Nov 24 17:23:05 HST 2009
@@ -27,5 +27,11 @@
       <version>2.4</version>
       <scope>provided</scope>
     </dependency> 
+    
+    <dependency>
+      <groupId>org.codehaus.groovy</groupId>
+      <artifactId>groovy-all</artifactId>
+      <version>1.5.6</version>
+    </dependency> 
   </dependencies>
 </project>
Index: email-ext/src/main/resources/hudson/plugins/emailext/ExtendedEmailPublisher/global.jelly
===================================================================
--- email-ext/src/main/resources/hudson/plugins/emailext/ExtendedEmailPublisher/global.jelly	(revision 23570)
+++ email-ext/src/main/resources/hudson/plugins/emailext/ExtendedEmailPublisher/global.jelly	Fri Dec 18 14:19:55 HST 2009
@@ -65,6 +65,12 @@
             >HTML (text/html)</f:option>
         </select>
       </f:entry>
+      <f:entry title="Default Charset"
+               help="${rootURL}/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='+encode(this.value)"/>
+      </f:entry>
       <f:entry title="Default Subject"
                help="${rootURL}/plugin/email-ext/help/globalConfig/defaultSubject.html">
         <input class="setting-input" name="ext_mailer_default_subject"
@@ -76,6 +82,21 @@
                     name="ext_mailer_default_body"
                     value="${descriptor.defaultBody}"/>
       </f:entry>
+      <f:entry title="Default is Script"
+               help="${rootURL}/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="${rootURL}/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='+encode(this.value)"/>
+      </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>
Index: email-ext/src/main/java/hudson/plugins/emailext/plugins/ContentBuilder.java
===================================================================
--- email-ext/src/main/java/hudson/plugins/emailext/plugins/ContentBuilder.java	(revision 15609)
+++ email-ext/src/main/java/hudson/plugins/emailext/plugins/ContentBuilder.java	Fri Dec 18 13:03:08 HST 2009
@@ -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 <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()))
+        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()));
+                .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);
+    }
-						
+
-		newText = replaceTokensWithContent(newText, publisher, type, build);
-		return newText;
+    // 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 @@
 		
 	}
 
+	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);
+		}
+	}
+
+}
Index: email-ext/src/main/webapp/help/projectConfig/buildForTesting.html
===================================================================
--- email-ext/src/main/webapp/help/projectConfig/buildForTesting.html	Thu Dec 17 16:52:41 HST 2009
+++ email-ext/src/main/webapp/help/projectConfig/buildForTesting.html	Thu Dec 17 16:52:41 HST 2009
@@ -0,0 +1,3 @@
+<div>
+    Specifies the build used by the Test Against Build button.  Example: common/12
+</div>
Index: email-ext/src/main/java/hudson/plugins/emailext/EmailType.java
===================================================================
--- email-ext/src/main/java/hudson/plugins/emailext/EmailType.java	(revision 15195)
+++ email-ext/src/main/java/hudson/plugins/emailext/EmailType.java	Tue Nov 24 17:23:05 HST 2009
@@ -40,6 +40,11 @@
 	 */
 	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 @@
 		sendToDevelopers = false;
 		includeCulprits = false;
 		sendToRecipientList = false;
+		script = false;
 	}
 	
 	public String getSubject() {
@@ -104,4 +110,11 @@
 		this.recipientList = recipientList;
 	}
 	
+	public boolean isScript() {
+		return script;
-}
+	}
+	
+	public void setScript(boolean script) {
+		this.script = script;
+	}
+}
Index: email-ext/src/main/webapp/help/projectConfig/charset.html
===================================================================
--- email-ext/src/main/webapp/help/projectConfig/charset.html	Mon Nov 30 16:34:05 HST 2009
+++ email-ext/src/main/webapp/help/projectConfig/charset.html	Mon Nov 30 16:34:05 HST 2009
@@ -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>
Index: email-ext/src/main/webapp/help/globalConfig/buildForTesting.html
===================================================================
--- email-ext/src/main/webapp/help/globalConfig/buildForTesting.html	Thu Dec 17 16:52:41 HST 2009
+++ email-ext/src/main/webapp/help/globalConfig/buildForTesting.html	Thu Dec 17 16:52:41 HST 2009
@@ -0,0 +1,3 @@
+<div>
+    Specifies the build used by the Test Against Build button.  Example: common/12
+</div>