Add a Restored.vm template and use it.

The restore action has been erroneously using the Abandoned.vm
template.  Create a template and sender for the restore
command.  Refactor some code to make this additonal sender
share much in common with the AbandonedSender.

Change-Id: Id1c61ead7cf71c3476759f0a968f1bf013b77b47
diff --git a/Documentation/config-mail.txt b/Documentation/config-mail.txt
index 168bbfe..8aa7d08 100644
--- a/Documentation/config-mail.txt
+++ b/Documentation/config-mail.txt
@@ -83,6 +83,14 @@
 related to a user submitting a new patchset for a change.  It is a
 `ChangeEmail`: see `ChangeSubject.vm` and `ChangeFooter.vm`.
 
+Restored.vm
+~~~~~~~~~~~
+
+The `Restored.vm` template will determine the contents of the email related
+to a change being restored.  It is a `ChangeEmail`: see `ChangeSubject.vm` and
+`ChangeFooter.vm`.
+
+
 
 Mail Variables and Methods
 --------------------------
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/AbandonChange.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/AbandonChange.java
index 8eb1bbe..b4f595c 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/AbandonChange.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/AbandonChange.java
@@ -43,7 +43,7 @@
   private final ChangeControl.Factory changeControlFactory;
   private final ReviewDb db;
   private final IdentifiedUser currentUser;
-  private final AbandonedSender.Factory abandonedSenderFactory;
+  private final AbandonedSender.Factory senderFactory;
   private final ChangeDetailFactory.Factory changeDetailFactory;
 
   private final PatchSet.Id patchSetId;
@@ -55,14 +55,14 @@
   @Inject
   AbandonChange(final ChangeControl.Factory changeControlFactory,
       final ReviewDb db, final IdentifiedUser currentUser,
-      final AbandonedSender.Factory abandonedSenderFactory,
+      final AbandonedSender.Factory senderFactory,
       final ChangeDetailFactory.Factory changeDetailFactory,
       @Assisted final PatchSet.Id patchSetId,
       @Assisted @Nullable final String message, final ChangeHookRunner hooks) {
     this.changeControlFactory = changeControlFactory;
     this.db = db;
     this.currentUser = currentUser;
-    this.abandonedSenderFactory = abandonedSenderFactory;
+    this.senderFactory = senderFactory;
     this.changeDetailFactory = changeDetailFactory;
 
     this.patchSetId = patchSetId;
@@ -81,8 +81,8 @@
       throw new NoSuchChangeException(changeId);
     }
 
-    ChangeUtil.abandon(patchSetId, currentUser, message, db,
-       abandonedSenderFactory, hooks);
+    ChangeUtil.abandon(patchSetId, currentUser, message, db, senderFactory,
+        hooks);
 
     return changeDetailFactory.create(changeId).call();
   }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RestoreChange.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RestoreChange.java
index fa8785a..7bd826e 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RestoreChange.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RestoreChange.java
@@ -21,8 +21,8 @@
 import com.google.gerrit.reviewdb.*;
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.mail.AbandonedSender;
 import com.google.gerrit.server.mail.EmailException;
+import com.google.gerrit.server.mail.RestoredSender;
 import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.project.InvalidChangeOperationException;
@@ -41,7 +41,7 @@
   private final ChangeControl.Factory changeControlFactory;
   private final ReviewDb db;
   private final IdentifiedUser currentUser;
-  private final AbandonedSender.Factory abandonedSenderFactory;
+  private final RestoredSender.Factory senderFactory;
   private final ChangeDetailFactory.Factory changeDetailFactory;
 
   private final PatchSet.Id patchSetId;
@@ -53,14 +53,14 @@
   @Inject
   RestoreChange(final ChangeControl.Factory changeControlFactory,
       final ReviewDb db, final IdentifiedUser currentUser,
-      final AbandonedSender.Factory abandonedSenderFactory,
+      final RestoredSender.Factory senderFactory,
       final ChangeDetailFactory.Factory changeDetailFactory,
       @Assisted final PatchSet.Id patchSetId,
       @Assisted @Nullable final String message, final ChangeHookRunner hooks) {
     this.changeControlFactory = changeControlFactory;
     this.db = db;
     this.currentUser = currentUser;
-    this.abandonedSenderFactory = abandonedSenderFactory;
+    this.senderFactory = senderFactory;
     this.changeDetailFactory = changeDetailFactory;
 
     this.patchSetId = patchSetId;
@@ -79,8 +79,8 @@
       throw new NoSuchChangeException(changeId);
     }
 
-    ChangeUtil.restore(patchSetId, currentUser, message, db,
-       abandonedSenderFactory, hooks);
+    ChangeUtil.restore(patchSetId, currentUser, message, db, senderFactory,
+        hooks);
 
     return changeDetailFactory.create(changeId).call();
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
index a3dcfea..57ec5f6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
@@ -38,6 +38,8 @@
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.mail.AbandonedSender;
 import com.google.gerrit.server.mail.EmailException;
+import com.google.gerrit.server.mail.ReplyToChangeSender;
+import com.google.gerrit.server.mail.RestoredSender;
 import com.google.gerrit.server.mail.RevertedSender;
 import com.google.gwtorm.client.AtomicUpdate;
 import com.google.gwtorm.client.OrmConcurrencyException;
@@ -211,7 +213,7 @@
 
   public static void abandon(final PatchSet.Id patchSetId,
       final IdentifiedUser user, final String message, final ReviewDb db,
-      final AbandonedSender.Factory abandonedSenderFactory,
+      final AbandonedSender.Factory senderFactory,
       final ChangeHookRunner hooks) throws NoSuchChangeException,
       InvalidChangeOperationException, EmailException, OrmException {
     final Change.Id changeId = patchSetId.getParentKey();
@@ -246,15 +248,9 @@
       }
     });
 
-    updatedChange(db, updatedChange, cmsg,
+    updatedChange(db, user, updatedChange, cmsg, senderFactory,
         "Change is no longer open or patchset is not latest");
 
-    // Email the reviewers
-    final AbandonedSender cm = abandonedSenderFactory.create(updatedChange);
-    cm.setFrom(user.getAccountId());
-    cm.setChangeMessage(cmsg);
-    cm.send();
-
     hooks.doChangeAbandonedHook(updatedChange, user.getAccount(), message);
   }
 
@@ -362,7 +358,7 @@
 
   public static void restore(final PatchSet.Id patchSetId,
       final IdentifiedUser user, final String message, final ReviewDb db,
-      final AbandonedSender.Factory abandonedSenderFactory,
+      final RestoredSender.Factory senderFactory,
       final ChangeHookRunner hooks) throws NoSuchChangeException,
       InvalidChangeOperationException, EmailException, OrmException {
     final Change.Id changeId = patchSetId.getParentKey();
@@ -397,25 +393,20 @@
       }
     });
 
-    updatedChange(db, updatedChange, cmsg,
+    updatedChange(db, user, updatedChange, cmsg, senderFactory,
        "Change is not abandoned or patchset is not latest");
 
-    // Email the reviewers
-    final AbandonedSender cm = abandonedSenderFactory.create(updatedChange);
-    cm.setFrom(user.getAccountId());
-    cm.setChangeMessage(cmsg);
-    cm.send();
-
     hooks.doChangeRestoreHook(updatedChange, user.getAccount(), message);
   }
 
-  private static void updatedChange(final ReviewDb db, final Change change,
-      final ChangeMessage cmsg, final String err) throws NoSuchChangeException,
-      InvalidChangeOperationException, OrmException {
+  private static void updatedChange(final ReviewDb db, final IdentifiedUser user,
+      final Change change, final ChangeMessage cmsg,
+      ReplyToChangeSender.Factory senderFactory, final String err)
+      throws NoSuchChangeException, InvalidChangeOperationException,
+      EmailException, OrmException {
     if (change == null) {
       throw new InvalidChangeOperationException(err);
     }
-
     db.changeMessages().insert(Collections.singleton(cmsg));
 
     final List<PatchSetApproval> approvals =
@@ -424,6 +415,12 @@
       a.cache(change);
     }
     db.patchSetApprovals().update(approvals);
+
+    // Email the reviewers
+    final ReplyToChangeSender cm = senderFactory.create(change);
+    cm.setFrom(user.getAccountId());
+    cm.setChangeMessage(cmsg);
+    cm.send();
   }
 
   public static String sortKey(long lastUpdated, int id){
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
index 4c386e0..72dcd31 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
@@ -35,6 +35,7 @@
 import com.google.gerrit.server.mail.MergedSender;
 import com.google.gerrit.server.mail.RegisterNewEmailSender;
 import com.google.gerrit.server.mail.ReplacePatchSetSender;
+import com.google.gerrit.server.mail.RestoredSender;
 import com.google.gerrit.server.mail.RevertedSender;
 import com.google.gerrit.server.patch.AddReviewer;
 import com.google.gerrit.server.patch.PublishComments;
@@ -79,6 +80,7 @@
     factory(ReplacePatchSetSender.Factory.class);
     factory(AbandonedSender.Factory.class);
     factory(RemoveReviewer.Factory.class);
+    factory(RestoredSender.Factory.class);
     factory(RevertedSender.Factory.class);
     factory(CommentSender.Factory.class);
     factory(MergedSender.Factory.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java
index e679849..bb28a84 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java
@@ -20,7 +20,8 @@
 
 /** Send notice about a change being abandoned by its owner. */
 public class AbandonedSender extends ReplyToChangeSender {
-  public static interface Factory {
+  public static interface Factory extends
+      ReplyToChangeSender.Factory<AbandonedSender> {
     AbandonedSender create(Change change);
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplyToChangeSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplyToChangeSender.java
index 4c3ed76..401a0b8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplyToChangeSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplyToChangeSender.java
@@ -18,6 +18,10 @@
 
 /** Alert a user to a reply to a change, usually commentary made during review. */
 public abstract class ReplyToChangeSender extends ChangeEmail {
+  public static interface Factory<T extends ReplyToChangeSender> {
+    public T create(Change change);
+  }
+
   protected ReplyToChangeSender(EmailArguments ea, Change c, String mc) {
     super(ea, c, mc);
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/RestoredSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RestoredSender.java
new file mode 100644
index 0000000..fee73d6
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RestoredSender.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2011 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.
+
+package com.google.gerrit.server.mail;
+
+import com.google.gerrit.reviewdb.Change;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+/** Send notice about a change being restored by its owner. */
+public class RestoredSender extends ReplyToChangeSender {
+  public static interface Factory extends
+      ReplyToChangeSender.Factory<RestoredSender> {
+    RestoredSender create(Change change);
+  }
+
+  @Inject
+  public RestoredSender(EmailArguments ea, @Assisted Change c) {
+    super(ea, c, "restore");
+  }
+
+  @Override
+  protected void init() throws EmailException {
+    super.init();
+
+    ccAllApprovals();
+    bccStarredBy();
+    bccWatchesNotifyAllComments();
+  }
+
+  @Override
+  protected void formatChange() throws EmailException {
+    appendText(velocifyFile("Restored.vm"));
+  }
+}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Restored.vm b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Restored.vm
new file mode 100644
index 0000000..c0e6b91
--- /dev/null
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Restored.vm
@@ -0,0 +1,44 @@
+## Copyright (C) 2011 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 Restored.vm template will determine the contents of the email related
+## to a change being restored.   It is a ChangeEmail: see ChangeSubject.vm and
+## ChangeFooter.vm.
+##
+$fromName has restored this change.
+
+Change subject: $change.subject
+......................................................................
+
+
+#if ($coverLetter)
+$coverLetter
+
+#end
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
index 467488a..f6ba0a2 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -32,6 +32,7 @@
 import com.google.gerrit.server.git.MergeQueue;
 import com.google.gerrit.server.mail.AbandonedSender;
 import com.google.gerrit.server.mail.EmailException;
+import com.google.gerrit.server.mail.RestoredSender;
 import com.google.gerrit.server.patch.PublishComments;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.project.InvalidChangeOperationException;
@@ -127,6 +128,9 @@
   private PublishComments.Factory publishCommentsFactory;
 
   @Inject
+  private RestoredSender.Factory restoredSenderFactory;
+
+  @Inject
   private ChangeHookRunner hooks;
 
   private List<ApproveOption> optionList;
@@ -237,7 +241,7 @@
       if (restoreChange) {
         if (changeControl.canRestore()) {
           ChangeUtil.restore(patchSetId, currentUser, changeComment, db,
-              abandonedSenderFactory, hooks);
+              restoredSenderFactory, hooks);
         } else {
           throw error("Not permitted to restore change");
         }