Merge "Treat NoteDb LOCK_FAILURE as HTTP 409"
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
index 5feb918..98d8d4c 100644
--- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
+++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
@@ -16,12 +16,14 @@
 
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.gson.FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES;
+import static java.util.stream.Collectors.toList;
 import static org.apache.commons.codec.binary.Base64.decodeBase64;
 import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Streams;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.index.FieldDef.FillArgs;
@@ -183,15 +185,21 @@
     return new io.searchbox.core.Index.Builder(doc).index(indexName).type(type).id(id).build();
   }
 
+  private static boolean shouldAddElement(Object element) {
+    return !(element instanceof String) || !((String) element).isEmpty();
+  }
+
   private String toDoc(V v) throws IOException {
     XContentBuilder builder = jsonBuilder().startObject();
     for (Values<V> values : schema.buildFields(v, fillArgs)) {
       String name = values.getField().getName();
       if (values.getField().isRepeatable()) {
-        builder.array(name, values.getValues());
+        builder.field(
+            name,
+            Streams.stream(values.getValues()).filter(e -> shouldAddElement(e)).collect(toList()));
       } else {
         Object element = Iterables.getOnlyElement(values.getValues(), "");
-        if (!(element instanceof String) || !((String) element).isEmpty()) {
+        if (shouldAddElement(element)) {
           builder.field(name, element);
         }
       }
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
index 870b88a..77cae9b 100644
--- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
+++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
@@ -354,20 +354,23 @@
           cd);
       decodeUnresolvedCommentCount(source, ChangeField.UNRESOLVED_COMMENT_COUNT.getName(), cd);
 
-      if (source.get(ChangeField.REF_STATE.getName()) != null) {
-        JsonArray refStates = source.get(ChangeField.REF_STATE.getName()).getAsJsonArray();
-        cd.setRefStates(Iterables.transform(refStates, e -> Base64.decodeBase64(e.getAsString())));
+      if (fields.contains(ChangeField.REF_STATE.getName())) {
+        cd.setRefStates(getByteArray(source, ChangeField.REF_STATE.getName()));
       }
-      if (source.get(ChangeField.REF_STATE_PATTERN.getName()) != null) {
-        JsonArray refStatePatterns =
-            source.get(ChangeField.REF_STATE_PATTERN.getName()).getAsJsonArray();
-        cd.setRefStatePatterns(
-            Iterables.transform(refStatePatterns, e -> Base64.decodeBase64(e.getAsString())));
+      if (fields.contains(ChangeField.REF_STATE_PATTERN.getName())) {
+        cd.setRefStatePatterns(getByteArray(source, ChangeField.REF_STATE_PATTERN.getName()));
       }
 
       return cd;
     }
 
+    private Iterable<byte[]> getByteArray(JsonObject source, String name) {
+      JsonElement element = source.get(name);
+      return element != null
+          ? Iterables.transform(element.getAsJsonArray(), e -> Base64.decodeBase64(e.getAsString()))
+          : Collections.emptyList();
+    }
+
     private void decodeSubmitRecords(
         JsonObject doc, String fieldName, SubmitRuleOptions opts, ChangeData out) {
       JsonArray records = doc.getAsJsonArray(fieldName);
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSet.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSet.soy
index 93c1c3b..e41bdda 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSet.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSet.soy
@@ -60,11 +60,4 @@
         {$patchSet.refName}
     {\n}
   {/if}
-
-  {if $email.includeDiff}
-    {\n}
-    {\n}
-    {$email.unifiedDiff}
-    {\n}
-  {/if}
 {/template}
\ No newline at end of file
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy
index 23a7630..05c60a1 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy
@@ -49,8 +49,4 @@
           {$patchSet.refName}
     {/param}{/call}
   {/if}
-
-  {if $email.includeDiff}
-    {call .Pre}{param content: $email.unifiedDiff /}{/call}
-  {/if}
 {/template}
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.html b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.html
index 0dee091..527485d 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.html
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.html
@@ -15,38 +15,11 @@
 -->
 
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../shared/gr-placeholder/gr-placeholder.html">
 
 <dom-module id="gr-admin-view">
   <template>
-    <style>
-      main {
-        margin: 2em auto;
-        max-width: 46em;
-      }
-      h1 {
-        margin-bottom: .1em;
-      }
-      @media only screen and (max-width: 67em) {
-        main {
-          margin: 2em 0 2em 15em;
-        }
-      }
-      @media only screen and (max-width: 53em) {
-        .loading {
-          padding: 0 var(--default-horizontal-margin);
-        }
-        main {
-          margin: 2em 1em;
-        }
-    </style>
-    <main>
-      <h1>Admin</h1>
-      <section>
-        This page is not yet implemented in PolyGerrit. View it in the
-        <a id="gwtLink" href$="/?polygerrit=0#[[path]]" rel="external">
-        Old UI</a>
-      </section>
-    </main>
+    <gr-placeholder title="Admin" path="[[path]]"></gr-placeholder>
   </template>
   <script src="gr-admin-view.js"></script>
 </dom-module>
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
index d8f9c32..eb650ca 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -198,6 +198,17 @@
       app.params = params;
     });
 
+    page(/^\/settings\/(agreements|new-agreement)/, loadUser, function(data) {
+      restAPI.getLoggedIn().then(function(loggedIn) {
+        if (loggedIn) {
+          data.params.view = 'gr-cla-view';
+          app.params = data.params;
+        } else {
+          page.redirect('/login/' + encodeURIComponent(data.canonicalPath));
+        }
+      });
+    });
+
     page(/^\/settings\/VE\/(\S+)/, function(data) {
       restAPI.getLoggedIn().then(function(loggedIn) {
         if (loggedIn) {
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html
index aa66986..3af9535 100644
--- a/polygerrit-ui/app/elements/gr-app.html
+++ b/polygerrit-ui/app/elements/gr-app.html
@@ -30,6 +30,7 @@
 <link rel="import" href="./change-list/gr-dashboard-view/gr-dashboard-view.html">
 <link rel="import" href="./change/gr-change-view/gr-change-view.html">
 <link rel="import" href="./diff/gr-diff-view/gr-diff-view.html">
+<link rel="import" href="./settings/gr-cla-view/gr-cla-view.html">
 <link rel="import" href="./settings/gr-registration-dialog/gr-registration-dialog.html">
 <link rel="import" href="./settings/gr-settings-view/gr-settings-view.html">
 
@@ -128,6 +129,9 @@
       <template is="dom-if" if="[[_showAdminView]]" restamp="true">
         <gr-admin-view path="[[_path]]"></gr-admin-view>
       </template>
+      <template is="dom-if" if="[[_showCLAView]]" restamp="true">
+        <gr-cla-view path="[[_path]]"></gr-cla-view>
+      </template>
       <div id="errorView" class="errorView" hidden>
         <div class="errorEmoji">[[_lastError.emoji]]</div>
         <div class="errorText">[[_lastError.text]]</div>
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index 82c71be..0874b5d 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -121,6 +121,7 @@
       this.set('_showDiffView', view === 'gr-diff-view');
       this.set('_showSettingsView', view === 'gr-settings-view');
       this.set('_showAdminView', view === 'gr-admin-view');
+      this.set('_showCLAView', view === 'gr-cla-view');
       if (this.params.justRegistered) {
         this.$.registration.open();
       }
diff --git a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html
new file mode 100644
index 0000000..b667d66
--- /dev/null
+++ b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2017 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.
+-->
+
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../shared/gr-placeholder/gr-placeholder.html">
+
+<dom-module id="gr-cla-view">
+  <template>
+    <gr-placeholder title="Agreements" path="[[path]]"></gr-placeholder>
+  </template>
+  <script src="gr-cla-view.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js
new file mode 100644
index 0000000..71dc71b
--- /dev/null
+++ b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2017 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.
+(function() {
+  'use strict';
+
+  Polymer({
+    is: 'gr-cla-view',
+
+    properties: {
+      path: String,
+    },
+  });
+})();
diff --git a/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.html b/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.html
new file mode 100644
index 0000000..2e624cc
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.html
@@ -0,0 +1,53 @@
+<!--
+Copyright (C) 2017 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.
+-->
+
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+
+<dom-module id="gr-placeholder">
+  <template>
+    <style>
+      main {
+        margin: 2em auto;
+        max-width: 46em;
+      }
+      h1 {
+        margin-bottom: .1em;
+      }
+      @media only screen and (max-width: 67em) {
+        main {
+          margin: 2em 0 2em 15em;
+        }
+      }
+      @media only screen and (max-width: 53em) {
+        .loading {
+          padding: 0 var(--default-horizontal-margin);
+        }
+        main {
+          margin: 2em 1em;
+        }
+      }
+    </style>
+    <main>
+      <h1>[[title]]</h1>
+      <section>
+        This page is not yet implemented in PolyGerrit. View it in the
+        <a id="gwtLink" href$="/?polygerrit=0#[[path]]" rel="external">
+        Old UI</a>
+      </section>
+    </main>
+  </template>
+  <script src="gr-placeholder.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.js b/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.js
new file mode 100644
index 0000000..0ea2979
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-placeholder/gr-placeholder.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2017 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.
+(function() {
+  'use strict';
+
+  Polymer({
+    is: 'gr-placeholder',
+
+    properties: {
+      path: String,
+      title: String,
+    },
+  });
+})();