Merge branch 'stable-2.13'

* stable-2.13:
  Remove leftovers of starred_changes for postgres index

Change-Id: Ia54895f75d0cfc0c23748c324560132d8b5bde3b
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 7acda4c..57e6d01 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -36,7 +36,7 @@
 ----
 mvn archetype:generate -DarchetypeGroupId=com.google.gerrit \
     -DarchetypeArtifactId=gerrit-plugin-archetype \
-    -DarchetypeVersion=2.13-SNAPSHOT \
+    -DarchetypeVersion=2.14-SNAPSHOT \
     -DgroupId=com.googlesource.gerrit.plugins.testplugin \
     -DartifactId=testplugin
 ----
diff --git a/VERSION b/VERSION
index 573f909..3035c93 100644
--- a/VERSION
+++ b/VERSION
@@ -2,4 +2,4 @@
 # Used by :api_install and :api_deploy targets
 # when talking to the destination repository.
 #
-GERRIT_VERSION = '2.13-SNAPSHOT'
+GERRIT_VERSION = '2.14-SNAPSHOT'
diff --git a/gerrit-acceptance-framework/pom.xml b/gerrit-acceptance-framework/pom.xml
index b5156c0..d9d701c 100644
--- a/gerrit-acceptance-framework/pom.xml
+++ b/gerrit-acceptance-framework/pom.xml
@@ -2,7 +2,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-acceptance-framework</artifactId>
-  <version>2.13-SNAPSHOT</version>
+  <version>2.14-SNAPSHOT</version>
   <packaging>jar</packaging>
   <name>Gerrit Code Review - Acceptance Test Framework</name>
   <description>Framework for Gerrit's acceptance tests</description>
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 006d8a4..47d97e3 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -755,12 +755,19 @@
     assertThat(reviewers.iterator().next()._accountId)
         .isEqualTo(user.getId().get());
 
+    sender.clear();
     gApi.changes()
         .id(changeId)
         .reviewer(user.getId().toString())
         .remove();
     assertThat(gApi.changes().id(changeId).get().reviewers.isEmpty());
 
+    assertThat(sender.getMessages()).hasSize(1);
+    Message message = sender.getMessages().get(0);
+    assertThat(message.body()).contains(
+        "Removed reviewer " + user.fullName + ".");
+    assertThat(message.body()).doesNotContain("with the following votes");
+
     // Make sure the reviewer can still be added again.
     gApi.changes()
         .id(changeId)
@@ -810,12 +817,19 @@
     assertThat(reviewerIt.next()._accountId)
         .isEqualTo(user.getId().get());
 
+    sender.clear();
     setApiUser(admin);
     gApi.changes()
         .id(changeId)
         .reviewer(user.getId().toString())
         .remove();
 
+    assertThat(sender.getMessages()).hasSize(1);
+    Message message = sender.getMessages().get(0);
+    assertThat(message.body()).contains(
+        "Removed reviewer " + user.fullName + " with the following votes");
+    assertThat(message.body()).contains("* Code-Review+1 by " + user.fullName);
+
     reviewers = gApi.changes()
         .id(changeId)
         .get()
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java
index 752f0d2..b402f6c 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.common.audit.Audit;
 import com.google.gerrit.common.auth.SignInRequired;
-import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gwtjsonrpc.common.AsyncCallback;
 import com.google.gwtjsonrpc.common.RemoteJsonService;
@@ -39,11 +38,6 @@
 
   @Audit
   @SignInRequired
-  void updateContact(String fullName, String emailAddr,
-      AsyncCallback<Account> callback);
-
-  @Audit
-  @SignInRequired
   void enterAgreement(String agreementName,
       AsyncCallback<VoidResult> callback);
 }
diff --git a/gerrit-extension-api/pom.xml b/gerrit-extension-api/pom.xml
index 023362f..7375893 100644
--- a/gerrit-extension-api/pom.xml
+++ b/gerrit-extension-api/pom.xml
@@ -2,7 +2,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-extension-api</artifactId>
-  <version>2.13-SNAPSHOT</version>
+  <version>2.14-SNAPSHOT</version>
   <packaging>jar</packaging>
   <name>Gerrit Code Review - Extension API</name>
   <description>API for Gerrit Extensions</description>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountApi.java
index acd2e78..6073e2d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountApi.java
@@ -83,6 +83,14 @@
     new RestApi("/accounts/").id(account).view("name").get(cb);
   }
 
+  /** Set the account name */
+  public static void setName(String account, String name,
+      AsyncCallback<NativeString> cb) {
+    AccountNameInput input = AccountNameInput.create();
+    input.name(name);
+    new RestApi("/accounts/").id(account).view("name").put(input, cb);
+  }
+
   /** Retrieve email addresses */
   public static void getEmails(String account,
       AsyncCallback<JsArray<EmailInfo>> cb) {
@@ -97,6 +105,13 @@
         .ifNoneMatch().put(in, cb);
   }
 
+  /** Set preferred email address */
+  public static void setPreferredEmail(String account, String email,
+      AsyncCallback<NativeString> cb) {
+    new RestApi("/accounts/").id(account).view("emails")
+        .id(email).view("preferred").put(cb);
+  }
+
   /** Retrieve SSH keys */
   public static void getSshKeys(String account,
       AsyncCallback<JsArray<SshKeyInfo>> cb) {
@@ -227,6 +242,17 @@
     }
   }
 
+  private static class AccountNameInput extends JavaScriptObject {
+    final native void name(String n) /*-{ if(n)this.name=n; }-*/;
+
+    static AccountNameInput create() {
+      return createObject().cast();
+    }
+
+    protected AccountNameInput() {
+    }
+  }
+
   public static void addGpgKey(String account, String armored,
       AsyncCallback<NativeMap<GpgKeyInfo>> cb) {
     new RestApi("/accounts/")
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
index ae3599d..e078e60 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.client.account;
 
 import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.FormatUtil;
 import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.client.info.AccountInfo;
 import com.google.gerrit.client.rpc.CallbackGroup;
@@ -46,7 +45,6 @@
 import com.google.gwt.user.client.ui.Widget;
 import com.google.gwtexpui.globalkey.client.NpTextBox;
 import com.google.gwtexpui.user.client.AutoCenterDialogBox;
-import com.google.gwtjsonrpc.common.AsyncCallback;
 
 class ContactPanelShort extends Composite {
   protected final FlowPanel body;
@@ -146,7 +144,7 @@
     save.addClickHandler(new ClickHandler() {
       @Override
       public void onClick(final ClickEvent event) {
-        doSave(null);
+        doSave();
       }
     });
 
@@ -347,10 +345,13 @@
     inEmail.setFocus(true);
   }
 
-  void doSave(final AsyncCallback<Account> onSave) {
-    String newName = canEditFullName() ? nameTxt.getText() : null;
-    if (newName != null && newName.trim().isEmpty()) {
+  void doSave() {
+    final String newName;
+    String name = canEditFullName() ? nameTxt.getText() : null;
+    if (name != null && name.trim().isEmpty()) {
       newName = null;
+    } else {
+      newName = name;
     }
 
     final String newEmail;
@@ -368,24 +369,40 @@
     save.setEnabled(false);
     registerNewEmail.setEnabled(false);
 
-    Util.ACCOUNT_SEC.updateContact(newName, newEmail,
-        new GerritCallback<Account>() {
-          @Override
-          public void onSuccess(Account result) {
-            registerNewEmail.setEnabled(true);
-            onSaveSuccess(FormatUtil.asInfo(result));
-            if (onSave != null) {
-              onSave.onSuccess(result);
-            }
-          }
+    CallbackGroup group = new CallbackGroup();
+    if (!newEmail.equals(currentEmail)) {
+      AccountApi.setPreferredEmail("self", newEmail,
+          group.add(new GerritCallback<NativeString>() {
+        @Override
+        public void onSuccess(NativeString result) {
+        }
+      }));
+    }
+    AccountApi.setName("self", newName,
+        group.add(new GerritCallback<NativeString>() {
+      @Override
+      public void onSuccess(NativeString result) {
+      }
 
-          @Override
-          public void onFailure(final Throwable caught) {
-            save.setEnabled(true);
-            registerNewEmail.setEnabled(true);
-            super.onFailure(caught);
-          }
-        });
+      @Override
+      public void onFailure(Throwable caught) {
+        save.setEnabled(true);
+        registerNewEmail.setEnabled(true);
+        super.onFailure(caught);
+      }
+    }));
+    group.done();
+    group.addListener(new GerritCallback<Void>() {
+      @Override
+      public void onSuccess(Void result) {
+        currentEmail = newEmail;
+        AccountInfo me = Gerrit.getUserAccount();
+        me.email(currentEmail);
+        me.name(newName);
+        onSaveSuccess(me);
+        registerNewEmail.setEnabled(true);
+      }
+    });
   }
 
   void onSaveSuccess(AccountInfo result) {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
index 8fcf9ea..873dc0a 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
@@ -14,12 +14,10 @@
 
 package com.google.gerrit.httpd.rpc.account;
 
-import com.google.common.base.Strings;
 import com.google.gerrit.audit.AuditService;
 import com.google.gerrit.common.data.AccountSecurity;
 import com.google.gerrit.common.data.ContributorAgreement;
 import com.google.gerrit.common.errors.NoSuchEntityException;
-import com.google.gerrit.common.errors.PermissionDeniedException;
 import com.google.gerrit.httpd.rpc.BaseServiceImplementation;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
@@ -28,10 +26,8 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.AccountByEmailCache;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.GroupCache;
-import com.google.gerrit.server.account.Realm;
 import com.google.gerrit.server.extensions.events.AgreementSignup;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gwtjsonrpc.common.AsyncCallback;
@@ -47,10 +43,8 @@
 
 class AccountSecurityImpl extends BaseServiceImplementation implements
     AccountSecurity {
-  private final Realm realm;
   private final ProjectCache projectCache;
   private final Provider<IdentifiedUser> user;
-  private final AccountByEmailCache byEmailCache;
   private final AccountCache accountCache;
 
   private final DeleteExternalIds.Factory deleteExternalIdsFactory;
@@ -63,19 +57,17 @@
   @Inject
   AccountSecurityImpl(final Provider<ReviewDb> schema,
       final Provider<CurrentUser> currentUser,
-      final Realm r, final Provider<IdentifiedUser> u,
+      final Provider<IdentifiedUser> u,
       final ProjectCache pc,
-      final AccountByEmailCache abec, final AccountCache uac,
+      final AccountCache uac,
       final DeleteExternalIds.Factory deleteExternalIdsFactory,
       final ExternalIdDetailFactory.Factory externalIdDetailFactory,
       final GroupCache groupCache,
       final AuditService auditService,
       AgreementSignup agreementSignup) {
     super(schema, currentUser);
-    realm = r;
     user = u;
     projectCache = pc;
-    byEmailCache = abec;
     accountCache = uac;
     this.auditService = auditService;
     this.deleteExternalIdsFactory = deleteExternalIdsFactory;
@@ -96,42 +88,6 @@
   }
 
   @Override
-  public void updateContact(final String name, final String emailAddr,
-      final AsyncCallback<Account> callback) {
-    run(callback, new Action<Account>() {
-      @Override
-      public Account run(ReviewDb db)
-          throws OrmException, Failure, IOException {
-        IdentifiedUser self = user.get();
-        final Account me = db.accounts().get(self.getAccountId());
-        final String oldEmail = me.getPreferredEmail();
-        if (realm.allowsEdit(Account.FieldName.FULL_NAME)) {
-          me.setFullName(Strings.emptyToNull(name));
-        }
-        if (!Strings.isNullOrEmpty(emailAddr)
-            && !self.hasEmailAddress(emailAddr)) {
-          throw new Failure(new PermissionDeniedException("Email address must be verified"));
-        }
-        me.setPreferredEmail(Strings.emptyToNull(emailAddr));
-        db.accounts().update(Collections.singleton(me));
-        if (!eq(oldEmail, me.getPreferredEmail())) {
-          byEmailCache.evict(oldEmail);
-          byEmailCache.evict(me.getPreferredEmail());
-        }
-        accountCache.evict(me.getId());
-        return me;
-      }
-    });
-  }
-
-  private static boolean eq(final String a, final String b) {
-    if (a == null && b == null) {
-      return true;
-    }
-    return a != null && a.equals(b);
-  }
-
-  @Override
   public void enterAgreement(final String agreementName,
       final AsyncCallback<VoidResult> callback) {
     run(callback, new Action<VoidResult>() {
diff --git a/gerrit-plugin-api/pom.xml b/gerrit-plugin-api/pom.xml
index 77bb111..9309921 100644
--- a/gerrit-plugin-api/pom.xml
+++ b/gerrit-plugin-api/pom.xml
@@ -2,7 +2,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-plugin-api</artifactId>
-  <version>2.13-SNAPSHOT</version>
+  <version>2.14-SNAPSHOT</version>
   <packaging>jar</packaging>
   <name>Gerrit Code Review - Plugin API</name>
   <description>API for Gerrit Plugins</description>
diff --git a/gerrit-plugin-archetype/pom.xml b/gerrit-plugin-archetype/pom.xml
index d8e3583..e69da7c 100644
--- a/gerrit-plugin-archetype/pom.xml
+++ b/gerrit-plugin-archetype/pom.xml
@@ -20,7 +20,7 @@
 
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-plugin-archetype</artifactId>
-  <version>2.13-SNAPSHOT</version>
+  <version>2.14-SNAPSHOT</version>
   <name>Gerrit Code Review - Plugin Archetype</name>
   <description>Maven Archetype for Gerrit Plugins</description>
   <url>https://www.gerritcodereview.com/</url>
diff --git a/gerrit-plugin-gwt-archetype/pom.xml b/gerrit-plugin-gwt-archetype/pom.xml
index 362b45c..5e9bc33 100644
--- a/gerrit-plugin-gwt-archetype/pom.xml
+++ b/gerrit-plugin-gwt-archetype/pom.xml
@@ -20,7 +20,7 @@
 
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-plugin-gwt-archetype</artifactId>
-  <version>2.13-SNAPSHOT</version>
+  <version>2.14-SNAPSHOT</version>
   <name>Gerrit Code Review - Web UI GWT Plugin Archetype</name>
   <description>Maven Archetype for Gerrit Web UI GWT Plugins</description>
   <url>https://www.gerritcodereview.com/</url>
diff --git a/gerrit-plugin-gwtui/pom.xml b/gerrit-plugin-gwtui/pom.xml
index 982d268..4b104c6 100644
--- a/gerrit-plugin-gwtui/pom.xml
+++ b/gerrit-plugin-gwtui/pom.xml
@@ -2,7 +2,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-plugin-gwtui</artifactId>
-  <version>2.13-SNAPSHOT</version>
+  <version>2.14-SNAPSHOT</version>
   <packaging>jar</packaging>
   <name>Gerrit Code Review - Plugin GWT UI</name>
   <description>Common Classes for Gerrit GWT UI Plugins</description>
diff --git a/gerrit-plugin-js-archetype/pom.xml b/gerrit-plugin-js-archetype/pom.xml
index e22478a..d31b455 100644
--- a/gerrit-plugin-js-archetype/pom.xml
+++ b/gerrit-plugin-js-archetype/pom.xml
@@ -20,7 +20,7 @@
 
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-plugin-js-archetype</artifactId>
-  <version>2.13-SNAPSHOT</version>
+  <version>2.14-SNAPSHOT</version>
   <name>Gerrit Code Review - Web UI JavaScript Plugin Archetype</name>
   <description>Maven Archetype for Gerrit Web UI JavaScript Plugins</description>
   <url>https://www.gerritcodereview.com/</url>
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetEmails.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetEmails.java
index 14cc74e..99e6bd8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetEmails.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetEmails.java
@@ -15,6 +15,8 @@
 package com.google.gerrit.server.account;
 
 import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
 import java.util.ArrayList;
@@ -24,11 +26,20 @@
 
 @Singleton
 public class GetEmails implements RestReadView<AccountResource> {
+  private final IdentifiedUser.GenericFactory identifiedUserFactory;
+
+  @Inject
+  GetEmails(IdentifiedUser.GenericFactory identifiedUserFactory) {
+    this.identifiedUserFactory = identifiedUserFactory;
+  }
 
   @Override
   public List<EmailInfo> apply(AccountResource rsrc) {
+    IdentifiedUser user =
+        identifiedUserFactory.create(rsrc.getUser().getAccountId());
+
     List<EmailInfo> emails = new ArrayList<>();
-    for (String email : rsrc.getUser().getEmailAddresses()) {
+    for (String email : user.getEmailAddresses()) {
       if (email != null) {
         EmailInfo e = new EmailInfo();
         e.email = email;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
index bdefa93..a14cf6b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
@@ -148,47 +148,48 @@
       }
 
       StringBuilder msg = new StringBuilder();
+      msg.append("Removed reviewer " + reviewer.getFullName());
+      StringBuilder removedVotesMsg = new StringBuilder();
+      removedVotesMsg.append(" with the following votes:\n\n");
+      boolean votesRemoved = false;
       for (PatchSetApproval a : approvals(ctx, reviewerId)) {
         if (ctx.getControl().canRemoveReviewer(a)) {
           del.add(a);
           if (a.getPatchSetId().equals(currPs.getId()) && a.getValue() != 0) {
             oldApprovals.put(a.getLabel(), a.getValue());
-            if (msg.length() == 0) {
-              msg.append("Removed reviewer ").append(reviewer.getFullName())
-                  .append(" with the following votes:\n\n");
-            }
-            msg.append("* ").append(a.getLabel())
+            removedVotesMsg.append("* ").append(a.getLabel())
                 .append(formatLabelValue(a.getValue())).append(" by ")
                 .append(userFactory.create(a.getAccountId()).getNameEmail())
                 .append("\n");
+            votesRemoved = true;
           }
         } else {
           throw new AuthException("delete reviewer not permitted");
         }
       }
 
+      if (votesRemoved) {
+        msg.append(removedVotesMsg);
+      } else {
+        msg.append(".");
+      }
+
       ctx.getDb().patchSetApprovals().delete(del);
       ChangeUpdate update = ctx.getUpdate(currPs.getId());
       update.removeReviewer(reviewerId);
 
-      if (msg.length() > 0) {
-        changeMessage = new ChangeMessage(
-            new ChangeMessage.Key(currChange.getId(),
-                ChangeUtil.messageUUID(ctx.getDb())),
-            ctx.getAccountId(), ctx.getWhen(), currPs.getId());
-        changeMessage.setMessage(msg.toString());
-        cmUtil.addChangeMessage(ctx.getDb(), update, changeMessage);
-      }
+      changeMessage = new ChangeMessage(
+          new ChangeMessage.Key(currChange.getId(),
+              ChangeUtil.messageUUID(ctx.getDb())),
+          ctx.getAccountId(), ctx.getWhen(), currPs.getId());
+      changeMessage.setMessage(msg.toString());
+      cmUtil.addChangeMessage(ctx.getDb(), update, changeMessage);
 
       return true;
     }
 
     @Override
     public void postUpdate(Context ctx) {
-      if (changeMessage == null) {
-        return;
-      }
-
       emailReviewers(ctx.getProject(), currChange, del, changeMessage);
       reviewerDeleted.fire(currChange, currPs, reviewer,
           ctx.getAccount(),
diff --git a/gerrit-war/pom.xml b/gerrit-war/pom.xml
index f5b078e..acc9b86 100644
--- a/gerrit-war/pom.xml
+++ b/gerrit-war/pom.xml
@@ -2,7 +2,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-war</artifactId>
-  <version>2.13-SNAPSHOT</version>
+  <version>2.14-SNAPSHOT</version>
   <packaging>war</packaging>
   <name>Gerrit Code Review - WAR</name>
   <description>Gerrit WAR</description>