Merge branch 'stable-2.15' into stable-2.16

* stable-2.15:
  Bump bazel version to 1.0.0
  Upgrade bazlets to latest stable-2.15 to build with 2.15.17 API

Change-Id: I228fc4ca21b6c07a1521ea5c4a48f45ac99f687a
diff --git a/BUILD b/BUILD
index f75bad0..4a6aab6 100644
--- a/BUILD
+++ b/BUILD
@@ -7,8 +7,9 @@
     srcs = glob(["src/main/java/**/*.java"]),
     manifest_entries = [
         "Gerrit-PluginName: uploadvalidator",
-        "Gerrit-ApiVersion: 2.15",
+        "Gerrit-ApiVersion: 2.16",
         "Gerrit-Module: com.googlesource.gerrit.plugins.uploadvalidator.Module",
+        "Gerrit-HttpModule: com.googlesource.gerrit.plugins.uploadvalidator.HttpModule",
     ],
     resources = glob(["src/main/resources/**/*"]),
 )
diff --git a/WORKSPACE b/WORKSPACE
index 8d97a33..473d41b 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,7 +3,7 @@
 load("//:bazlets.bzl", "load_bazlets")
 
 load_bazlets(
-    commit = "ae1cd231b0262b2738e6c0593eb3c504209ad4f5",
+    commit = "ec989bb514e39447764057c60d3f9959bff8e153",
     #local_path = "/home/<user>/projects/bazlets",
 )
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/HttpModule.java b/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/HttpModule.java
new file mode 100644
index 0000000..c5cc519
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/uploadvalidator/HttpModule.java
@@ -0,0 +1,29 @@
+// Copyright (C) 2019 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.googlesource.gerrit.plugins.uploadvalidator;
+
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.webui.JavaScriptPlugin;
+import com.google.gerrit.extensions.webui.WebUiPlugin;
+import com.google.gerrit.httpd.plugins.HttpPluginModule;
+
+public class HttpModule extends HttpPluginModule {
+
+  @Override
+  protected void configureServlets() {
+    DynamicSet.bind(binder(), WebUiPlugin.class)
+        .toInstance(new JavaScriptPlugin("gr-uploadvalidator.html"));
+  }
+}
diff --git a/src/main/resources/static/gr-uploadvalidator-config-table.html b/src/main/resources/static/gr-uploadvalidator-config-table.html
new file mode 100644
index 0000000..d688f01
--- /dev/null
+++ b/src/main/resources/static/gr-uploadvalidator-config-table.html
@@ -0,0 +1,63 @@
+<!--
+@license
+Copyright (C) 2019 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.
+-->
+
+<dom-module id="gr-uploadvalidator-config-table">
+  <template>
+    <style include="shared-styles"></style>
+    <style include="gr-table-styles">
+      .genericList {
+        margin-bottom: 1em;
+      }
+
+      input#newEntry {
+        width: 100%;
+      }
+    </style>
+    <fieldset>
+      <section>
+        <table class="genericList">
+          <tbody>
+            <template id="list" is="dom-repeat" items="{{entries}}">
+              <tr>
+                <td class="nameColumn">{{item}}</td>
+                <td class="deleteColumn">
+                  <gr-button id="{{item}}"
+                             class="deleteEntryButton"
+                             on-click="_handleDeleteEntry">
+                    Delete
+                  </gr-button>
+                </td>
+              </tr>
+            </template>
+            <tr>
+              <td class="nameColumn">
+                <iron-input>
+                  <input id="newEntry" type="text">
+                </iron-input>
+              </td>
+              <td class="deleteColumn">
+                <gr-button id="addEntry"
+                           on-click="_handleAddEntry"
+                           disabled="[[disabled]]">
+                  Add
+                </gr-button>
+              </td>
+            </tr>
+          </tbody>
+        </table>
+      </section>
+    </fieldset>
+  </template>
+  <script src="gr-uploadvalidator-config-table.js"></script>
+</dom-module>
diff --git a/src/main/resources/static/gr-uploadvalidator-config-table.js b/src/main/resources/static/gr-uploadvalidator-config-table.js
new file mode 100644
index 0000000..89d6bd9
--- /dev/null
+++ b/src/main/resources/static/gr-uploadvalidator-config-table.js
@@ -0,0 +1,57 @@
+// Copyright (C) 2019 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-uploadvalidator-config-table',
+
+    properties: {
+      disabled: {
+        type: Boolean,
+        reflectToAttribute: true,
+      },
+      entries: {
+        type: Array,
+        value(){ return []; },
+      },
+    },
+
+    _handleAddEntry() {
+      if (this.$.newEntry.value === '') {
+        return;
+      }
+
+      if (!this.entries.includes(this.$.newEntry.value)) {
+        this.push('entries', this.$.newEntry.value);
+        this.fire('listChanged', {
+          entry: this.$.newEntry.value,
+          allEntries: this.entries,
+        });
+      } else {
+        this.fire('show-alert', {message: 'Value already exists.'});
+      }
+      this.$.newEntry.value = '';
+    },
+
+    _handleDeleteEntry(event) {
+      this.splice('entries', this.entries.indexOf(event.path[1].id), 1);
+      this.fire('listChanged', {
+        entry: event.target.id,
+        allEntries: this.entries.slice(),
+      });
+    },
+  })
+})();
diff --git a/src/main/resources/static/gr-uploadvalidator-config.html b/src/main/resources/static/gr-uploadvalidator-config.html
new file mode 100644
index 0000000..83f399b
--- /dev/null
+++ b/src/main/resources/static/gr-uploadvalidator-config.html
@@ -0,0 +1,189 @@
+<!--
+@license
+Copyright (C) 2019 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="./gr-uploadvalidator-config-table.html">
+
+<dom-module id="gr-uploadvalidator-config">
+  <template>
+    <style include="shared-styles"></style>
+    <style include="gr-form-styles"></style>
+    <style>
+      .sectionTitle {
+        padding-top: 2em;
+      }
+    </style>
+    <fieldset class="gr-form-styles">
+      <h2 class="sectionTitle">Uploadvalidator Options</h2>
+      <section>
+        <section>
+          <h3>Author Email Pattern</h3>
+          <gr-uploadvalidator-config-table id="allowedAuthorEmailPattern"
+                                           entries=[[_changedConfig.allowedAuthorEmailPattern.values]]>
+          </gr-uploadvalidator-config-table>
+        </section>
+        <section>
+          <h3>Committer Email Pattern</h3>
+          <gr-uploadvalidator-config-table id="allowedCommitterEmailPattern"
+                                           entries=[[_changedConfig.allowedCommitterEmailPattern.values]]>
+          </gr-uploadvalidator-config-table>
+        </section>
+        <section>
+          <h3>Binary Types</h3>
+          <gr-uploadvalidator-config-table id="binaryTypes"
+                                           entries=[[_changedConfig.binaryTypes.values]]>
+          </gr-uploadvalidator-config-table>
+        </section>
+        <section>
+          <h3>Blocked Content</h3>
+          <span class="title">List Type</span>
+          <span class="value">
+            <gr-select id="blockedContentTypeWhitelist"
+                       bind-value="{{_changedConfig.blockedContentTypeWhitelist.value}}"
+                       on-change="_handlePrefsChanged">
+              <select>
+                <option value="true">Whitelist</option>
+                <option value="false">Blacklist</option>
+              </select>
+            </gr-select>
+          </span>
+          <gr-uploadvalidator-config-table id="blockedContentType"
+                                           entries=[[_changedConfig.blockedContentType.values]]>
+          </gr-uploadvalidator-config-table>
+        </section>
+        <section>
+          <h3>Blocked File Extensions</h3>
+          <gr-uploadvalidator-config-table id="blockedFileExtension"
+                                           entries=[[_changedConfig.blockedFileExtension.values]]>
+          </gr-uploadvalidator-config-table>
+        </section>
+        <section>
+          <h3>Blocked Keyword Pattern</h3>
+          <gr-uploadvalidator-config-table id="blockedKeywordPattern"
+                                           entries=[[_changedConfig.blockedKeywordPattern.values]]>
+          </gr-uploadvalidator-config-table>
+        </section>
+        <section>
+          <h3>Invalid Filename Pattern</h3>
+          <gr-uploadvalidator-config-table id="invalidFilenamePattern"
+                                           entries=[[_changedConfig.invalidFilenamePattern.values]]>
+          </gr-uploadvalidator-config-table>
+        </section>
+        <section>
+          <h3>Projects</h3>
+          <gr-uploadvalidator-config-table id="project"
+                                           entries=[[_changedConfig.project.values]]>
+          </gr-uploadvalidator-config-table>
+        </section>
+        <section>
+          <h3>Refs</h3>
+          <gr-uploadvalidator-config-table id="ref"
+                                           entries=[[_changedConfig.ref.values]]>
+          </gr-uploadvalidator-config-table>
+        </section>
+        <section>
+          <h3>Required Footers</h3>
+          <gr-uploadvalidator-config-table id="requiredFooter"
+                                           entries=[[_changedConfig.requiredFooter.values]]>
+          </gr-uploadvalidator-config-table>
+        </section>
+        <section>
+          <span class="title">Max Path Length</span>
+          <span class="value">
+            <iron-input bind-value="{{_changedConfig.maxPathLength.value}}">
+              <input id="maxPathLength"
+                     value="{{_changedConfig.maxPathLength.value::input}}"
+                     prevent-invalid-input
+                     allowed-pattern="[0-9]"
+                     type="number"
+                     on-keypress="_handlePrefsChanged"
+                     on-change="_handlePrefsChanged">
+            </iron-input>
+          </span>
+        </section>
+        <section>
+          <span class="title">Reject Duplicate Pathnames Locale</span>
+          <span class="value">
+            <gr-select id="rejectDuplicatePathnamesLocale"
+                       bind-value="{{_changedConfig.rejectDuplicatePathnamesLocale.value}}"
+                       on-change="_handlePrefsChanged">
+              <select>
+                <template is="dom-repeat"
+                          items="[[_changedConfig.rejectDuplicatePathnamesLocale.permitted_values]]">
+                  <option value="[[item]]">[[item]]</option>
+                </template>
+              </select>
+            </gr-select>
+          </span>
+        </section>
+        <section>
+          <span class="title">Reject Duplicate Pathnames</span>
+          <span class="value">
+            <gr-select id="rejectDuplicatePathnames"
+                       bind-value="{{_changedConfig.rejectDuplicatePathnames.value}}"
+                       on-change="_handlePrefsChanged">
+              <select>
+                <option value="true">True</option>
+                <option value="false">False</option>
+              </select>
+            </gr-select>
+          </span>
+        </section>
+        <section>
+          <span class="title">Reject Submodules</span>
+          <span class="value">
+            <gr-select id="rejectSubmodule"
+                       bind-value="{{_changedConfig.rejectSubmodule.value}}"
+                       on-change="_handlePrefsChanged">
+              <select>
+                <option value="true">True</option>
+                <option value="false">False</option>
+              </select>
+            </gr-select>
+          </span>
+        </section>
+        <section>
+          <span class="title">Reject Symbolic Links</span>
+          <span class="value">
+            <gr-select id="rejectSymlink"
+                       bind-value="{{_changedConfig.rejectSymlink.value}}"
+                       on-change="_handlePrefsChanged">
+              <select>
+                <option value="true">True</option>
+                <option value="false">False</option>
+              </select>
+            </gr-select>
+          </span>
+        </section>
+        <section>
+          <span class="title">Reject Windows Line Endings</span>
+          <span class="value">
+            <gr-select id="rejectWindowsLineEndings"
+                       bind-value="{{_changedConfig.rejectWindowsLineEndings.value}}"
+                       on-change="_handlePrefsChanged">
+              <select>
+                <option value="true">True</option>
+                <option value="false">False</option>
+              </select>
+            </gr-select>
+          </span>
+        </section>
+        <gr-button id="saveButton"
+                   on-click="_handlePrefsSave"
+                   disabled="[[!_prefsChanged]]">
+          Save Changes
+        </gr-button>
+    </fieldset>
+  </template>
+  <script src="gr-uploadvalidator-config.js"></script>
+</dom-module>
diff --git a/src/main/resources/static/gr-uploadvalidator-config.js b/src/main/resources/static/gr-uploadvalidator-config.js
new file mode 100644
index 0000000..508231c
--- /dev/null
+++ b/src/main/resources/static/gr-uploadvalidator-config.js
@@ -0,0 +1,79 @@
+// Copyright (C) 2019 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-uploadvalidator-config',
+
+    properties: {
+      repoName: String,
+      _config: Object,
+      _changedConfig: Object,
+      _prefsChanged: {
+        type: Boolean,
+        value: false,
+      },
+      _projectRestApi: Object,
+    },
+
+    listeners: {
+      listChanged: '_handleListDataChanged',
+    },
+
+    attached() {
+      this._projectRestApi = this.plugin.restApi('/projects/');
+
+      this._getPreferences().then(() => {
+        this._changedConfig = Object.assign({}, this._config);
+      });
+    },
+
+    _getPreferences() {
+      return this._projectRestApi.get(`${this.repoName}/config`)
+        .then(config => {
+          if (!config) {
+            return;
+          }
+
+          if (config.plugin_config && config.plugin_config.uploadvalidator) {
+            this._config = config.plugin_config.uploadvalidator;
+          }
+        })
+    },
+
+    _handleListDataChanged(event) {
+      this._changedConfig[event.target.id] = { values: event.detail.allEntries };
+      this._handlePrefsChanged();
+    },
+
+    _handlePrefsChanged() {
+      this._prefsChanged = true;
+    },
+
+    _handlePrefsSave() {
+      let body = { plugin_config_values: {} };
+      body.plugin_config_values.uploadvalidator = this._changedConfig;
+
+      this._projectRestApi.put(`${this.repoName}/config`, body)
+        .then(() => {
+          this._prefsChanged = false;
+        }).catch(response => {
+          this.fire('show-error', { message: response });
+        });
+    },
+
+  });
+})();
diff --git a/src/main/resources/static/gr-uploadvalidator.html b/src/main/resources/static/gr-uploadvalidator.html
new file mode 100644
index 0000000..767915a
--- /dev/null
+++ b/src/main/resources/static/gr-uploadvalidator.html
@@ -0,0 +1,26 @@
+<!--
+@license
+Copyright (C) 2019 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="./gr-uploadvalidator-config.html">
+
+<dom-module id="gr-uploadvalidator">
+  <script>
+    Gerrit.install(plugin => {
+      plugin.registerCustomComponent('repo-config', 'gr-uploadvalidator-config');
+    });
+  </script>
+</dom-module>
diff --git a/src/test/java/com/googlesource/gerrit/plugins/uploadvalidator/FakeUserProvider.java b/src/test/java/com/googlesource/gerrit/plugins/uploadvalidator/FakeUserProvider.java
index bf488e1..390eeee 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/uploadvalidator/FakeUserProvider.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/uploadvalidator/FakeUserProvider.java
@@ -18,9 +18,9 @@
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.replay;
 
-import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.inject.Provider;
 
 public class FakeUserProvider implements Provider<IdentifiedUser> {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/uploadvalidator/SkipValidationTest.java b/src/test/java/com/googlesource/gerrit/plugins/uploadvalidator/SkipValidationTest.java
index 4942391..56bf317 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/uploadvalidator/SkipValidationTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/uploadvalidator/SkipValidationTest.java
@@ -16,10 +16,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.util.time.TimeUtil;
 import org.junit.Test;
 
 public class SkipValidationTest {