Use a template to set the subject line.

Add an admin editable ChangeSubject.vm template used to
format the subject header in change emails.

Change-Id: Iea58807b9a947bf0a4eba31c04977582430137ed
This commit is contained in:
Martin Fick 2010-07-23 10:23:03 -06:00
parent bfa5a181b3
commit b7f3b2d5be
16 changed files with 193 additions and 36 deletions

View File

@ -0,0 +1,120 @@
Gerrit Code Review - Mail Templates
===================================
Gerrit uses velocity templates for the bulk of the standard mails it sends out.
There are builtin default templates which are used if they are not overridden.
These defaults are also provided as examples so that administrators may copy
them and easily modify them to tweak their contents.
Template Locations and Extensions:
----------------------------------
The default example templates reside under: `'$site_path'/etc/mail` and are
terminated with the double extension `.vm.example`. Modifying these example
files will have no effect on the behavior of Gerrit. However, copying an
example template to an equivalently named file without the `.example` extension
and modifying it will allow an administrator to customize the template.
Supported Mail Templates:
-------------------------
Each mail that Gerrit sends out is controlled by at least one template, these
are listed below. Change emails are influenced by two additional templates,
one to set the subject line, and one to set the footer which gets appended to
all the change emails (see `ChangeSubject.vm` and `ChangeFooter.vm` below.)
ChangeSubject.vm
~~~~~~~~~~~~~~~~
The `ChangeSubject.vm` template will determine the contents of the email
subject line for ALL emails related to changes.
Mail Variables and Methods
--------------------------
Mail templates can access and display objects currently made available to them
via the velocity context. While the base objects are documented here, it is
possible to call public methods on these objects from templates. Those methods
are not documented here since they could change with every release. As these
templates are meant to be modified only by a qualified sysadmin, it is accepted
that writing templates for Gerrit emails is likely to require some basic
knowledge of the class structure to be useful. Browsing the source code might
be necessary for anything more than a minor formatting change.
Warning
~~~~~~~
Be aware that modifying templates can cause them to fail to parse and therefor
not send out the actual email, or worse, calling methods on the available
objects could have internal side effects which would adversely affect the
health of your Gerrit server and/or data.
All OutgoingEmails
~~~~~~~~~~~~~~~~~~
All outgoing emails have the following variables available to them:
$email::
+
A reference to the class constructing the current `OutgoingEmail`. With this
reference it is possible to call any public method on the OutgoingEmail class
or the current child class inherited from it.
$messageClass::
+
A String containing the messageClass
$StringUtils::
+
A reference to the Apache `StringUtils` class. This can be very useful for
formatting strings.
Change Emails
~~~~~~~~~~~~~
All change related emails have the following additional variables available to them:
$change::
+
A reference to the current `Change` object
$changeId::
+
Id of the current change (a `Change.Key`)
$coverLetter::
+
The text of the `ChangeMessage`
$branch::
+
A reference to the branch of this change (a `Branch.NameKey`)
$fromName::
+
The name of the from user
$projectName::
+
The name of this change's project
$patchSet::
+
A reference to the current `PatchSet`
$patchSetInfo::
+
A reference to the current `PatchSetInfo`
See Also
--------
* link:http://velocity.apache.org/[velocity]
GERRIT
------
Part of link:index.html[Gerrit Code Review]

View File

@ -31,6 +31,7 @@ Configuration
* link:config-sso.html[Single Sign-On Systems]
* link:config-apache2.html[Apache 2 Reverse Proxy]
* link:config-hooks.html[Hooks]
* link:config-mail.html[Mail Templates]
Developer Documentation
-----------------------

View File

@ -25,11 +25,13 @@ import static com.google.gerrit.pgm.init.InitUtil.version;
import com.google.gerrit.pgm.Init;
import com.google.gerrit.pgm.util.ConsoleUI;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.mail.OutgoingEmail;
import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.TypeLiteral;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@ -66,6 +68,7 @@ public class SitePathInitializer {
mkdir(site.etc_dir);
mkdir(site.lib_dir);
mkdir(site.logs_dir);
mkdir(site.mail_dir);
mkdir(site.static_dir);
for (InitStep step : steps) {
@ -82,11 +85,19 @@ public class SitePathInitializer {
extract(site.gerrit_sh, Init.class, "gerrit.sh");
chmod(0755, site.gerrit_sh);
extractMailExample("ChangeSubject.vm");
if (!ui.isBatch()) {
System.err.println();
}
}
private void extractMailExample(String orig) throws Exception {
File ex = new File(site.mail_dir, orig + ".example");
extract(ex, OutgoingEmail.class, orig);
chmod(0444, ex);
}
private static List<InitStep> stepsOf(final Injector injector) {
final ArrayList<InitStep> r = new ArrayList<InitStep>();
for (Binding<InitStep> b : all(injector)) {

View File

@ -30,7 +30,7 @@ public class AbandonedSender extends ReplyToChangeSender {
}
@Override
protected void init() {
protected void init() throws EmailException {
super.init();
ccAllApprovals();
@ -39,7 +39,7 @@ public class AbandonedSender extends ReplyToChangeSender {
}
@Override
protected void formatChange() {
protected void formatChange() throws EmailException {
appendText(getNameFor(fromId));
appendText(" has abandoned change " + change.getKey().abbreviate() + ":\n");
appendText("\n");

View File

@ -32,7 +32,7 @@ public class AddReviewerSender extends NewChangeSender {
}
@Override
protected void init() {
protected void init() throws EmailException {
super.init();
ccExistingReviewers();

View File

@ -122,7 +122,7 @@ public abstract class ChangeEmail extends OutgoingEmail {
protected abstract void formatChange() throws EmailException;
/** Setup the message headers and envelope (TO, CC, BCC). */
protected void init() {
protected void init() throws EmailException {
if (args.projectCache != null) {
projectState = args.projectCache.get(change.getProject());
projectName =
@ -194,23 +194,8 @@ public abstract class ChangeEmail extends OutgoingEmail {
}
}
private void setChangeSubjectHeader() {
final StringBuilder subj = new StringBuilder();
subj.append("[");
subj.append(change.getDest().getShortName());
subj.append("] ");
subj.append("Change ");
subj.append(change.getKey().abbreviate());
subj.append(": (");
subj.append(projectName);
subj.append(") ");
if (change.getSubject().length() > 60) {
subj.append(change.getSubject().substring(0, 60));
subj.append("...");
} else {
subj.append(change.getSubject());
}
setHeader("Subject", subj.toString());
private void setChangeSubjectHeader() throws EmailException {
setHeader("Subject", velocifyFile("ChangeSubject.vm"));
}
/** Get a link to the change; null if the server doesn't know its own address. */
@ -434,7 +419,10 @@ public abstract class ChangeEmail extends OutgoingEmail {
protected void setupVelocityContext() {
super.setupVelocityContext();
velocityContext.put("change", change);
velocityContext.put("changeId", change.getKey());
velocityContext.put("coverLetter", getCoverLetter());
velocityContext.put("branch", change.getDest());
velocityContext.put("fromName", getNameFor(fromId));
velocityContext.put("projectName", projectName);
velocityContext.put("patchSet", patchSet);
velocityContext.put("patchSetInfo", patchSetInfo);

View File

@ -56,7 +56,7 @@ public class CommentSender extends ReplyToChangeSender {
}
@Override
protected void init() {
protected void init() throws EmailException {
super.init();
ccAllApprovals();
@ -65,7 +65,7 @@ public class CommentSender extends ReplyToChangeSender {
}
@Override
protected void formatChange() {
protected void formatChange() throws EmailException {
if (!"".equals(getCoverLetter()) || !inlineComments.isEmpty()) {
appendText("Comments on Patch Set " + patchSet.getPatchSetId() + ":\n");
appendText("\n");

View File

@ -40,7 +40,7 @@ public class CreateChangeSender extends NewChangeSender {
}
@Override
protected void init() {
protected void init() throws EmailException {
super.init();
bccWatchers();

View File

@ -30,14 +30,14 @@ public class MergeFailSender extends ReplyToChangeSender {
}
@Override
protected void init() {
protected void init() throws EmailException {
super.init();
ccExistingReviewers();
}
@Override
protected void formatChange() {
protected void formatChange() throws EmailException {
appendText("Change " + change.getKey().abbreviate());
if (patchSetInfo != null && patchSetInfo.getAuthor() != null
&& patchSetInfo.getAuthor().getName() != null) {

View File

@ -51,7 +51,7 @@ public class MergedSender extends ReplyToChangeSender {
}
@Override
protected void init() {
protected void init() throws EmailException {
super.init();
ccAllApprovals();
@ -61,7 +61,7 @@ public class MergedSender extends ReplyToChangeSender {
}
@Override
protected void formatChange() {
protected void formatChange() throws EmailException {
appendText("Change " + change.getKey().abbreviate());
if (patchSetInfo != null && patchSetInfo.getAuthor() != null
&& patchSetInfo.getAuthor().getName() != null) {

View File

@ -46,7 +46,7 @@ public abstract class NewChangeSender extends ChangeEmail {
}
@Override
protected void init() {
protected void init() throws EmailException {
super.init();
setHeader("Message-ID", getChangeMessageThreadId());
@ -57,7 +57,7 @@ public abstract class NewChangeSender extends ChangeEmail {
}
@Override
protected void formatChange() {
protected void formatChange() throws EmailException {
formatSalutation();
formatChangeDetail();

View File

@ -124,7 +124,7 @@ public abstract class OutgoingEmail {
protected abstract void format() throws EmailException;
/** Setup the message headers and envelope (TO, CC, BCC). */
protected void init() {
protected void init() throws EmailException {
setupVelocityContext();
smtpFromAddress = args.fromAddressGenerator.from(fromId);

View File

@ -40,7 +40,7 @@ public class RegisterNewEmailSender extends OutgoingEmail {
}
@Override
protected void init() {
protected void init() throws EmailException {
super.init();
setHeader("Subject", "[Gerrit Code Review] Email Verification");
add(RecipientType.TO, new Address(addr));
@ -52,7 +52,7 @@ public class RegisterNewEmailSender extends OutgoingEmail {
}
@Override
protected void format() {
protected void format() throws EmailException {
final StringBuilder url = new StringBuilder();
url.append(getGerritUrl());
url.append("#VE,");

View File

@ -53,7 +53,7 @@ public class ReplacePatchSetSender extends ReplyToChangeSender {
}
@Override
protected void init() {
protected void init() throws EmailException {
super.init();
if (fromId != null) {
@ -67,7 +67,7 @@ public class ReplacePatchSetSender extends ReplyToChangeSender {
}
@Override
protected void formatChange() {
protected void formatChange() throws EmailException {
formatSalutation();
formatChangeDetail();

View File

@ -23,7 +23,7 @@ public abstract class ReplyToChangeSender extends ChangeEmail {
}
@Override
protected void init() {
protected void init() throws EmailException {
super.init();
final String threadId = getChangeMessageThreadId();

View File

@ -0,0 +1,37 @@
## Copyright (C) 2010 The Android Open Source Project
##
## Licensed under the Apache License, Version 2.0 (the "License");
## you may not use this file except in compliance with the License.
## You may obtain a copy of the License at
##
## http://www.apache.org/licenses/LICENSE-2.0
##
## Unless required by applicable law or agreed to in writing, software
## distributed under the License is distributed on an "AS IS" BASIS,
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
## See the License for the specific language governing permissions and
## limitations under the License.
##
##
## Template Type:
## -------------
## This is a velocity mail template, see: http://velocity.apache.org and the
## gerrit-docs:config-mail.txt for more info on modifying gerrit mail templates.
##
## Template File Names and extensions:
## ----------------------------------
## Gerrit will use templates ending in ".vm" but will ignore templates ending
## in ".vm.example". If a .vm template does not exist, the default internal
## gerrit template which is the same as the .vm.example will be used. If you
## want to override the default template, copy the .vm.exmaple file to a .vm
## file and edit it appropriately.
##
## This Template:
## --------------
## The ChangeSubject.vm template will determine the contents of the email
## subject line for ALL emails related to changes.
##
#macro(elipses $length $str)
#if($str.length() > $length)${str.substring(0,$length)}...#else$str#end
#end
[$branch.shortName] Change $changeId.abbreviate(): ($projectName) #elipses(60, $change.subject)