Merge branch 'stable-2.15' into stable-2.16

* stable-2.15:
  Update mockito-core to 2.24.0

Change-Id: Ieb95dc9e2d79fdef906e72d96af4c00707bf8205
diff --git a/.bazelrc b/.bazelrc
index 4ed16cf..73fc9ea 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -1,2 +1,4 @@
 build --workspace_status_command=./tools/workspace-status.sh
+build --experimental_strict_action_env
+build --action_env=PATH
 test --build_tests_only
diff --git a/BUILD b/BUILD
index f5393ad..c4386ab 100644
--- a/BUILD
+++ b/BUILD
@@ -5,6 +5,8 @@
     "PLUGIN_TEST_DEPS",
     "gerrit_plugin",
 )
+load("//tools/bzl:genrule2.bzl", "genrule2")
+load("//tools/bzl:js.bzl", "polygerrit_plugin")
 
 gerrit_plugin(
     name = "delete-project",
@@ -15,10 +17,32 @@
         "Gerrit-HttpModule: com.googlesource.gerrit.plugins.deleteproject.HttpModule",
         "Gerrit-SshModule: com.googlesource.gerrit.plugins.deleteproject.SshModule",
     ],
+    resource_jars = [":gr-delete-repo-static"],
     resources = glob(["src/main/resources/**/*"]),
     deps = ["@commons-io//jar"],
 )
 
+genrule2(
+    name = "gr-delete-repo-static",
+    srcs = [":gr-delete-repo"],
+    outs = ["gr-delete-repo-static.jar"],
+    cmd = " && ".join([
+        "mkdir $$TMP/static",
+        "cp -r $(locations :gr-delete-repo) $$TMP/static",
+        "cd $$TMP",
+        "zip -Drq $$ROOT/$@ -g .",
+    ]),
+)
+
+polygerrit_plugin(
+    name = "gr-delete-repo",
+    srcs = glob([
+        "gr-delete-repo/*.html",
+        "gr-delete-repo/*.js",
+    ]),
+    app = "plugin.html",
+)
+
 junit_tests(
     name = "delete_project_tests",
     srcs = glob(["src/test/java/**/*.java"]),
diff --git a/WORKSPACE b/WORKSPACE
index e24b018..312f2f4 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,27 +3,53 @@
 load("//:bazlets.bzl", "load_bazlets")
 
 load_bazlets(
-    commit = "ca34f0cf89b2e041ea7f4aa4b9696efc2d76746f",
+    commit = "a556cd8183cb66d1a008d096decbcc4a5896df78",
     #local_path = "/home/<user>/projects/bazlets",
 )
 
-# Snapshot Plugin API
-#load(
-#    "@com_googlesource_gerrit_bazlets//:gerrit_api_maven_local.bzl",
-#    "gerrit_api_maven_local",
-#)
-
-# Load snapshot Plugin API
-#gerrit_api_maven_local()
-
-# Release Plugin API
+# Polymer dependencies
 load(
-    "@com_googlesource_gerrit_bazlets//:gerrit_api.bzl",
-    "gerrit_api",
+    "@com_googlesource_gerrit_bazlets//:gerrit_polymer.bzl",
+    "gerrit_polymer",
 )
 
+gerrit_polymer()
+
+# Load closure compiler with transitive dependencies
+load("@io_bazel_rules_closure//closure:defs.bzl", "closure_repositories")
+
+closure_repositories()
+
+# Load Gerrit npm_binary toolchain
+load("@com_googlesource_gerrit_bazlets//tools:js.bzl", "GERRIT", "npm_binary")
+
+npm_binary(
+    name = "polymer-bundler",
+    repository = GERRIT,
+)
+
+npm_binary(
+    name = "crisper",
+    repository = GERRIT,
+)
+
+# Snapshot Plugin API
+load(
+    "@com_googlesource_gerrit_bazlets//:gerrit_api_maven_local.bzl",
+    "gerrit_api_maven_local",
+)
+
+# Load snapshot Plugin API
+gerrit_api_maven_local()
+
+# Release Plugin API
+#load(
+#    "@com_googlesource_gerrit_bazlets//:gerrit_api.bzl",
+#    "gerrit_api",
+#)
+
 # Load release Plugin API
-gerrit_api()
+#gerrit_api()
 
 load("//:external_plugin_deps.bzl", "external_plugin_deps")
 
diff --git a/gr-delete-repo/gr-delete-repo.html b/gr-delete-repo/gr-delete-repo.html
new file mode 100644
index 0000000..c2a4a1b
--- /dev/null
+++ b/gr-delete-repo/gr-delete-repo.html
@@ -0,0 +1,57 @@
+<!--
+Copyright (C) 2018 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-delete-repo">
+  <template>
+    <style include="gr-form-styles"></style>
+    <gr-repo-command
+        title="[[action.label]]"
+        tooltip="[[action.title]]"
+        disabled="[[!action.enabled]]"
+        on-command-tap="_handleCommandTap">
+    </gr-repo-command>
+    <gr-overlay id="deleteRepoOverlay" with-backdrop>
+      <gr-dialog
+          id="deleteRepoDialog"
+          confirm-label="Delete"
+          on-confirm="_handleDeleteRepo"
+          on-cancel="_handleCloseDeleteRepo">
+        <div class="header" slot="header">
+          Are you really sure you want to delete the repo: "[[repoName]]"?
+        </div>
+        <div class="main" slot="main">
+          <div class="gr-form-styles">
+            <div id="form">
+                <section>
+                  <input
+                      type="checkbox"
+                      id="forceDeleteOpenChangesCheckBox">
+                  <label for="forceDeleteOpenChangesCheckBox">Delete repo even if open changes exist?</label>
+                </section>
+                <section>
+                  <input
+                      type="checkbox"
+                      id="preserveGitRepoCheckBox">
+                  <label for="preserveGitRepoCheckBox">Preserve GIT Repository?</label>
+                </section>
+            </div>
+          </div>
+        </div>
+      </gr-dialog>
+    </gr-overlay>
+  </template>
+  <script src="gr-delete-repo.js"></script>
+</dom-module>
diff --git a/gr-delete-repo/gr-delete-repo.js b/gr-delete-repo/gr-delete-repo.js
new file mode 100644
index 0000000..8055070
--- /dev/null
+++ b/gr-delete-repo/gr-delete-repo.js
@@ -0,0 +1,63 @@
+// Copyright (C) 2018 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-delete-repo',
+
+    properties: {
+      repoName: String,
+      config: Object,
+      action: Object,
+      actionId: String,
+    },
+
+    attached() {
+      this.actionId = this.plugin.getPluginName() + '~delete';
+      this.action = this.config.actions[this.actionId];
+      this.hidden = !this.action;
+    },
+
+    _handleCommandTap() {
+      this.$.deleteRepoOverlay.open();
+    },
+
+    _handleCloseDeleteRepo() {
+      this.$.deleteRepoOverlay.close();
+    },
+
+    _handleDeleteRepo() {
+      const endpoint = '/projects/' +
+          encodeURIComponent(this.repoName) + '/' +
+          this.actionId;
+
+      const json = {
+        force: this.$.forceDeleteOpenChangesCheckBox.checked,
+        preserve: this.$.preserveGitRepoCheckBox.checked
+      };
+
+      const errFn = response => {
+        this.fire('page-error', {response});
+      };
+
+      return this.plugin.restApi().send(
+          this.action.method, endpoint, json, errFn)
+            .then(r => {
+              this.plugin.restApi().invalidateReposCache();
+              Gerrit.Nav.navigateToRelativeUrl('/admin/repos');
+      });
+    },
+  });
+})();
diff --git a/plugin.html b/plugin.html
new file mode 100644
index 0000000..138397d
--- /dev/null
+++ b/plugin.html
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2018 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-delete-repo/gr-delete-repo.html">
+
+<dom-module id="delete-repo">
+  <script>
+    if (window.Polymer) {
+      Gerrit.install(function(plugin) {
+          plugin.registerCustomComponent(
+              'repo-command', 'gr-delete-repo');
+      });
+    }
+  </script>
+</dom-module>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteCommand.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteCommand.java
index c6a0f1f..9fc119e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteCommand.java
@@ -14,11 +14,9 @@
 
 package com.googlesource.gerrit.plugins.deleteproject;
 
-import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
 import com.google.gwtorm.server.OrmException;
@@ -38,7 +36,7 @@
           + "--yes-really-delete flag.\n";
 
   @Argument(index = 0, required = true, metaVar = "NAME", usage = "project to delete")
-  private ProjectControl projectControl;
+  private ProjectState projectState;
 
   @Option(name = "--yes-really-delete", usage = "confirmation to delete the project")
   private boolean yesReallyDelete;
@@ -75,7 +73,7 @@
       input.force = force;
       input.preserve = preserveGitRepository;
 
-      ProjectResource rsrc = new ProjectResource(projectControl);
+      ProjectResource rsrc = new ProjectResource(projectState, user);
       preConditions.assertDeletePermission(rsrc);
 
       if (!yesReallyDelete) {
@@ -92,11 +90,7 @@
 
       preConditions.assertCanBeDeleted(rsrc, input);
       deleteProject.doDelete(rsrc, input);
-    } catch (AuthException
-        | ResourceNotFoundException
-        | ResourceConflictException
-        | OrmException
-        | IOException e) {
+    } catch (RestApiException | OrmException | IOException e) {
       throw die(e);
     }
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteLog.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteLog.java
index 292d8bd..374bb14 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteLog.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteLog.java
@@ -16,15 +16,15 @@
 
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
-import com.google.gerrit.audit.AuditEvent;
-import com.google.gerrit.audit.AuditService;
-import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.extensions.systemstatus.ServerInformation;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.AuditEvent;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.OutputFormat;
+import com.google.gerrit.server.audit.AuditService;
 import com.google.gerrit.server.util.PluginLogFile;
 import com.google.gerrit.server.util.SystemLog;
+import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import org.apache.log4j.Level;
@@ -73,7 +73,9 @@
             );
 
     event.setProperty(ACCOUNT_ID, user.getAccountId().toString());
-    event.setProperty(USER_NAME, user.getUserName());
+    if (user.getUserName().isPresent()) {
+      event.setProperty(USER_NAME, user.getUserName().get());
+    }
     event.setProperty(PROJECT_NAME, project.get());
 
     if (options != null) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeletePreconditions.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeletePreconditions.java
index 5543961..9c92ec6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeletePreconditions.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeletePreconditions.java
@@ -14,42 +14,43 @@
 
 package com.googlesource.gerrit.plugins.deleteproject;
 
+import static com.google.gerrit.reviewdb.client.RefNames.REFS_HEADS;
+import static com.google.gerrit.reviewdb.client.RefNames.REFS_TAGS;
 import static com.googlesource.gerrit.plugins.deleteproject.DeleteOwnProjectCapability.DELETE_OWN_PROJECT;
 import static com.googlesource.gerrit.plugins.deleteproject.DeleteProjectCapability.DELETE_PROJECT;
-import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toSet;
 
+import com.google.common.collect.Iterables;
 import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.api.access.PluginPermission;
 import com.google.gerrit.extensions.common.ProjectInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.MergeOpRepoManager;
-import com.google.gerrit.server.git.SubmoduleException;
-import com.google.gerrit.server.git.SubmoduleOp;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.ListChildProjects;
+import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.project.ProjectResource;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
+import com.google.gerrit.server.restapi.project.ListChildProjects;
+import com.google.gerrit.server.submit.MergeOpRepoManager;
+import com.google.gerrit.server.submit.SubmoduleException;
+import com.google.gerrit.server.submit.SubmoduleOp;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.deleteproject.DeleteProject.Input;
 import java.io.IOException;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 
 @Singleton
@@ -96,11 +97,13 @@
   }
 
   protected boolean canDelete(ProjectResource rsrc) {
-    PermissionBackend.WithUser userPermission = permissionBackend.user(userProvider);
+    PermissionBackend.WithUser userPermission = permissionBackend.user(userProvider.get());
     return userPermission.testOrFalse(GlobalPermission.ADMINISTRATE_SERVER)
         || userPermission.testOrFalse(new PluginPermission(pluginName, DELETE_PROJECT))
         || (userPermission.testOrFalse(new PluginPermission(pluginName, DELETE_OWN_PROJECT))
-            && rsrc.getControl().isOwner());
+            && userPermission
+                .project(rsrc.getNameKey())
+                .testOrFalse(ProjectPermission.WRITE_CONFIG));
   }
 
   void assertCanBeDeleted(ProjectResource rsrc, Input input) throws ResourceConflictException {
@@ -134,13 +137,13 @@
 
   private void assertHasNoChildProjects(ProjectResource rsrc) throws CannotDeleteProjectException {
     try {
-      List<ProjectInfo> children = listChildProjectsProvider.get().apply(rsrc);
+      List<ProjectInfo> children = listChildProjectsProvider.get().withLimit(1).apply(rsrc);
       if (!children.isEmpty()) {
         throw new CannotDeleteProjectException(
-            "Cannot delete project because it has children: "
-                + children.stream().map(info -> info.name).collect(joining(",")));
+            "Cannot delete project because it has at least one child: "
+                + Iterables.getOnlyElement(children).name);
       }
-    } catch (PermissionBackendException e) {
+    } catch (OrmException | PermissionBackendException | RestApiException e) {
       throw new CannotDeleteProjectException(
           String.format("Unable to verify if '%s' has children projects.", rsrc.getName()));
     }
@@ -150,10 +153,12 @@
       throws CannotDeleteProjectException {
     try (Repository repo = repoManager.openRepository(projectNameKey);
         MergeOpRepoManager mergeOp = mergeOpProvider.get()) {
-      Set<Branch.NameKey> branches = new HashSet<>();
-      for (Ref ref : repo.getRefDatabase().getRefs(RefNames.REFS_HEADS).values()) {
-        branches.add(new Branch.NameKey(projectNameKey, ref.getName()));
-      }
+      Set<Branch.NameKey> branches =
+          repo.getRefDatabase()
+              .getRefsByPrefix(REFS_HEADS)
+              .stream()
+              .map(ref -> new Branch.NameKey(projectNameKey, ref.getName()))
+              .collect(toSet());
       SubmoduleOp sub = subOpFactory.create(branches, mergeOp);
       for (Branch.NameKey b : branches) {
         if (!sub.superProjectSubscriptionsForSubmoduleBranch(b).isEmpty()) {
@@ -177,7 +182,7 @@
 
   private void assertHasNoTags(Project.NameKey projectNameKey) throws CannotDeleteProjectException {
     try (Repository repo = repoManager.openRepository(projectNameKey)) {
-      if (!repo.getRefDatabase().getRefs(Constants.R_TAGS).isEmpty()) {
+      if (!repo.getRefDatabase().getRefsByPrefix(REFS_TAGS).isEmpty()) {
         throw new CannotDeleteProjectException(
             String.format("Project %s has tags", projectNameKey));
       }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProject.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProject.java
index 58844eb..e3fa6b9 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProject.java
@@ -14,10 +14,9 @@
 
 package com.googlesource.gerrit.plugins.deleteproject;
 
-import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.CurrentUser;
@@ -77,8 +76,7 @@
 
   @Override
   public Object apply(ProjectResource rsrc, Input input)
-      throws ResourceNotFoundException, ResourceConflictException, OrmException, IOException,
-          AuthException {
+      throws OrmException, IOException, RestApiException {
     preConditions.assertDeletePermission(rsrc);
     preConditions.assertCanBeDeleted(rsrc, input);
 
@@ -87,8 +85,8 @@
   }
 
   public void doDelete(ProjectResource rsrc, Input input)
-      throws OrmException, IOException, ResourceNotFoundException, ResourceConflictException {
-    Project project = rsrc.getControl().getProject();
+      throws OrmException, IOException, RestApiException {
+    Project project = rsrc.getProjectState().getProject();
     boolean preserve = input != null && input.preserve;
     Exception ex = null;
     try {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HideProject.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HideProject.java
index 8b60ae3..f529c54 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HideProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HideProject.java
@@ -16,18 +16,18 @@
 
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.extensions.client.ProjectState;
-import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
-import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.git.MetaDataUpdate;
-import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.CreateProject;
 import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectConfig;
 import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.restapi.project.CreateProject;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -39,23 +39,22 @@
 
   private final MetaDataUpdate.Server metaDataUpdateFactory;
   private final ProjectCache projectCache;
-  private final CreateProject.Factory createProjectFactory;
+  private final CreateProject createProject;
   private final Configuration cfg;
 
   @Inject
   HideProject(
       MetaDataUpdate.Server metaDataUpdateFactory,
       ProjectCache projectCache,
-      CreateProject.Factory createProjectFactory,
+      CreateProject createProject,
       Configuration cfg) {
     this.metaDataUpdateFactory = metaDataUpdateFactory;
     this.projectCache = projectCache;
-    this.createProjectFactory = createProjectFactory;
+    this.createProject = createProject;
     this.cfg = cfg;
   }
 
-  public void apply(ProjectResource rsrc)
-      throws ResourceNotFoundException, ResourceConflictException, IOException {
+  public void apply(ProjectResource rsrc) throws IOException, RestApiException {
     try {
       MetaDataUpdate md = metaDataUpdateFactory.create(rsrc.getNameKey());
 
@@ -81,16 +80,11 @@
     }
   }
 
-  private void createProjectIfMissing(String projectName)
-      throws ResourceConflictException, IOException {
+  private void createProjectIfMissing(String projectName) throws IOException, RestApiException {
     if (projectCache.get(new Project.NameKey(projectName)) == null) {
       try {
-        createProjectFactory.create(projectName).apply(TopLevelResource.INSTANCE, null);
-      } catch (BadRequestException
-          | UnprocessableEntityException
-          | ResourceNotFoundException
-          | ConfigInvalidException
-          | PermissionBackendException e) {
+        createProject.apply(TopLevelResource.INSTANCE, IdString.fromDecoded(projectName), null);
+      } catch (RestApiException | ConfigInvalidException | PermissionBackendException e) {
         throw new ResourceConflictException(
             String.format("Failed to create project %s", projectName));
       }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HttpModule.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HttpModule.java
index 4d63379..8feca79 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HttpModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HttpModule.java
@@ -33,6 +33,8 @@
     if (cfg.enablePreserveOption()) {
       DynamicSet.bind(binder(), WebUiPlugin.class)
           .toInstance(new JavaScriptPlugin("delete-project.js"));
+      DynamicSet.bind(binder(), WebUiPlugin.class)
+          .toInstance(new JavaScriptPlugin("gr-delete-repo.html"));
     } else {
       DynamicSet.bind(binder(), WebUiPlugin.class)
           .toInstance(new JavaScriptPlugin("delete-project-with-preserve-disabled.js"));
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/cache/CacheDeleteHandler.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/cache/CacheDeleteHandler.java
index bdf3ff5..c453478 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/cache/CacheDeleteHandler.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/cache/CacheDeleteHandler.java
@@ -17,6 +17,7 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.inject.Inject;
+import java.io.IOException;
 
 public class CacheDeleteHandler {
 
@@ -27,7 +28,7 @@
     this.projectCache = projectCache;
   }
 
-  public void delete(Project project) {
+  public void delete(Project project) throws IOException {
     projectCache.remove(project);
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/database/DatabaseDeleteHandler.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/database/DatabaseDeleteHandler.java
index 420a6d5..fa4296d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/database/DatabaseDeleteHandler.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/database/DatabaseDeleteHandler.java
@@ -24,10 +24,10 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.reviewdb.server.ReviewDbUtil;
 import com.google.gerrit.server.StarredChangesUtil;
+import com.google.gerrit.server.UserInitiated;
 import com.google.gerrit.server.account.AccountState;
-import com.google.gerrit.server.account.WatchConfig;
-import com.google.gerrit.server.account.WatchConfig.Accessor;
-import com.google.gerrit.server.account.WatchConfig.ProjectWatchKey;
+import com.google.gerrit.server.account.AccountsUpdate;
+import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
 import com.google.gerrit.server.change.AccountPatchReviewStore;
 import com.google.gerrit.server.index.change.ChangeIndexer;
 import com.google.gerrit.server.project.NoSuchChangeException;
@@ -56,7 +56,7 @@
   private final DynamicItem<AccountPatchReviewStore> accountPatchReviewStore;
   private final ChangeIndexer indexer;
   private final Provider<InternalAccountQuery> accountQueryProvider;
-  private final Provider<Accessor> watchConfig;
+  private final Provider<AccountsUpdate> accountsUpdateProvider;
 
   @Inject
   public DatabaseDeleteHandler(
@@ -65,13 +65,13 @@
       DynamicItem<AccountPatchReviewStore> accountPatchReviewStore,
       ChangeIndexer indexer,
       Provider<InternalAccountQuery> accountQueryProvider,
-      Provider<WatchConfig.Accessor> watchConfig) {
-    this.accountQueryProvider = accountQueryProvider;
-    this.watchConfig = watchConfig;
+      @UserInitiated Provider<AccountsUpdate> accountsUpdateProvider) {
     this.dbProvider = dbProvider;
     this.starredChangesUtil = starredChangesUtil;
     this.accountPatchReviewStore = accountPatchReviewStore;
     this.indexer = indexer;
+    this.accountQueryProvider = accountQueryProvider;
+    this.accountsUpdateProvider = accountsUpdateProvider;
   }
 
   public void delete(Project project) throws OrmException {
@@ -158,7 +158,12 @@
       for (ProjectWatchKey watchKey : a.getProjectWatches().keySet()) {
         if (project.getNameKey().equals(watchKey.project())) {
           try {
-            watchConfig.get().deleteProjectWatches(accountId, singleton(watchKey));
+            accountsUpdateProvider
+                .get()
+                .update(
+                    "Delete Project Watches via API",
+                    accountId,
+                    u -> u.deleteProjectWatches(singleton(watchKey)));
           } catch (IOException | ConfigInvalidException e) {
             log.error(
                 "Removing watch entry for user {} in project {} failed.",
diff --git a/src/main/resources/static/delete-project-with-preserve-disabled.js b/src/main/resources/static/delete-project-with-preserve-disabled.js
index da4478d..550ea6c 100644
--- a/src/main/resources/static/delete-project-with-preserve-disabled.js
+++ b/src/main/resources/static/delete-project-with-preserve-disabled.js
@@ -12,29 +12,31 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-Gerrit.install(function(self) {
-    function onDeleteProject(c) {
-      var f = c.checkbox();
-      var b = c.button('Delete',
-        {onclick: function(){
-          c.call(
-            {force: f.checked, preserve: false},
-            function(r) {
-              c.hide();
-              window.alert('The project: "'
-                + c.project
-                + '" was deleted.'),
-              Gerrit.go('/admin/projects/');
-            });
-        }});
-      c.popup(c.div(
-        c.msg('Are you really sure you want to delete the project: "'
-          + c.project
-          + '"?'),
-        c.br(),
-        c.label(f, 'Delete project even if open changes exist?'),
-        c.br(),
-        b));
-    }
-    self.onAction('project', 'delete', onDeleteProject);
-  });
+if (!window.Polymer) {
+  Gerrit.install(function(self) {
+      function onDeleteProject(c) {
+        var f = c.checkbox();
+        var b = c.button('Delete',
+          {onclick: function(){
+            c.call(
+              {force: f.checked, preserve: false},
+              function(r) {
+                c.hide();
+                window.alert('The project: "'
+                  + c.project
+                  + '" was deleted.'),
+                Gerrit.go('/admin/projects/');
+              });
+          }});
+        c.popup(c.div(
+          c.msg('Are you really sure you want to delete the project: "'
+            + c.project
+            + '"?'),
+          c.br(),
+          c.label(f, 'Delete project even if open changes exist?'),
+          c.br(),
+          b));
+      }
+      self.onAction('project', 'delete', onDeleteProject);
+    });
+}
\ No newline at end of file
diff --git a/src/main/resources/static/delete-project.js b/src/main/resources/static/delete-project.js
index 2200973..2291279 100644
--- a/src/main/resources/static/delete-project.js
+++ b/src/main/resources/static/delete-project.js
@@ -12,32 +12,34 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-Gerrit.install(function(self) {
-    function onDeleteProject(c) {
-      var f = c.checkbox();
-      var p = c.checkbox();
-      var b = c.button('Delete',
-        {onclick: function(){
-          c.call(
-            {force: f.checked, preserve: p.checked},
-            function(r) {
-              c.hide();
-              window.alert('The project: "'
-                + c.project
-                + '" was deleted.'),
-              Gerrit.go('/admin/projects/');
-            });
-        }});
-      c.popup(c.div(
-        c.msg('Are you really sure you want to delete the project: "'
-          + c.project
-          + '"?'),
-        c.br(),
-        c.label(f, 'Delete project even if open changes exist?'),
-        c.br(),
-        c.label(p, 'Preserve GIT Repository?'),
-        c.br(),
-        b));
-    }
-    self.onAction('project', 'delete', onDeleteProject);
-  });
+if (!window.Polymer) {
+  Gerrit.install(function(self) {
+      function onDeleteProject(c) {
+        var f = c.checkbox();
+        var p = c.checkbox();
+        var b = c.button('Delete',
+          {onclick: function(){
+            c.call(
+              {force: f.checked, preserve: p.checked},
+              function(r) {
+                c.hide();
+                window.alert('The project: "'
+                  + c.project
+                  + '" was deleted.'),
+                Gerrit.go('/admin/projects/');
+              });
+          }});
+        c.popup(c.div(
+          c.msg('Are you really sure you want to delete the project: "'
+            + c.project
+            + '"?'),
+          c.br(),
+          c.label(f, 'Delete project even if open changes exist?'),
+          c.br(),
+          c.label(p, 'Preserve GIT Repository?'),
+          c.br(),
+          b));
+      }
+      self.onAction('project', 'delete', onDeleteProject);
+    });
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/deleteproject/DeletePreconditionsTest.java b/src/test/java/com/googlesource/gerrit/plugins/deleteproject/DeletePreconditionsTest.java
index b02763d..6658478 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/deleteproject/DeletePreconditionsTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/deleteproject/DeletePreconditionsTest.java
@@ -30,15 +30,16 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.MergeOpRepoManager;
-import com.google.gerrit.server.git.SubmoduleOp;
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
-import com.google.gerrit.server.project.ListChildProjects;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
+import com.google.gerrit.server.restapi.project.ListChildProjects;
+import com.google.gerrit.server.submit.MergeOpRepoManager;
+import com.google.gerrit.server.submit.SubmoduleOp;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Provider;
 import org.junit.Before;
@@ -60,9 +61,10 @@
   @Mock private Provider<InternalChangeQuery> queryProvider;
   @Mock private GitRepositoryManager repoManager;
   @Mock private SubmoduleOp.Factory subOpFactory;
+  @Mock private CurrentUser currentUser;
   @Mock private Provider<CurrentUser> userProvider;
+  @Mock private ProjectState state;
   @Mock private ProtectedProjects protectedProjects;
-  @Mock private ProjectControl control;
   @Mock private PermissionBackend permissionBackend;
   @Mock private PermissionBackend.WithUser userPermission;
 
@@ -73,7 +75,9 @@
 
   @Before
   public void setUp() {
-    rsrc = new ProjectResource(control);
+    when(userProvider.get()).thenReturn(currentUser);
+    rsrc = new ProjectResource(state, currentUser);
+    when(rsrc.getNameKey()).thenReturn(PROJECT_NAMEKEY);
     preConditions =
         new DeletePreconditions(
             config,
@@ -90,14 +94,14 @@
 
   @Test
   public void testUserCanDeleteIfAdmin() {
-    when(permissionBackend.user(userProvider)).thenReturn(userPermission);
+    when(permissionBackend.user(currentUser)).thenReturn(userPermission);
     when(userPermission.testOrFalse(GlobalPermission.ADMINISTRATE_SERVER)).thenReturn(true);
     assertThat(preConditions.canDelete(rsrc)).isTrue();
   }
 
   @Test
   public void testUserCanDeleteIfHasDeletePermission() {
-    when(permissionBackend.user(userProvider)).thenReturn(userPermission);
+    when(permissionBackend.user(currentUser)).thenReturn(userPermission);
     when(userPermission.testOrFalse(new PluginPermission(PLUGIN_NAME, DELETE_PROJECT)))
         .thenReturn(true);
     assertThat(preConditions.canDelete(rsrc)).isTrue();
@@ -105,16 +109,18 @@
 
   @Test
   public void testUserCanDeleteIfIsOwnerAndHasDeleteOwnPermission() {
-    when(permissionBackend.user(userProvider)).thenReturn(userPermission);
+    when(permissionBackend.user(currentUser)).thenReturn(userPermission);
     when(userPermission.testOrFalse(new PluginPermission(PLUGIN_NAME, DELETE_OWN_PROJECT)))
         .thenReturn(true);
-    when(control.isOwner()).thenReturn(true);
+    PermissionBackend.ForProject projectPermission = mock(PermissionBackend.ForProject.class);
+    when(projectPermission.testOrFalse(ProjectPermission.WRITE_CONFIG)).thenReturn(true);
+    when(userPermission.project(PROJECT_NAMEKEY)).thenReturn(projectPermission);
     assertThat(preConditions.canDelete(rsrc)).isTrue();
   }
 
   @Test
   public void testUserCannotDelete() throws Exception {
-    when(permissionBackend.user(userProvider)).thenReturn(userPermission);
+    when(permissionBackend.user(currentUser)).thenReturn(userPermission);
     expectedException.expect(AuthException.class);
     expectedException.expectMessage("not allowed to delete project");
     preConditions.assertDeletePermission(rsrc);
@@ -132,9 +138,10 @@
     doNothing().when(protectedProjects).assertIsNotProtected(rsrc);
     ListChildProjects childProjects = mock(ListChildProjects.class);
     when(listChildProjectsProvider.get()).thenReturn(childProjects);
+    when(childProjects.withLimit(1)).thenReturn(childProjects);
     when(childProjects.apply(rsrc)).thenReturn(ImmutableList.of(new ProjectInfo()));
     expectedException.expect(ResourceConflictException.class);
-    expectedException.expectMessage("Cannot delete project because it has children:");
+    expectedException.expectMessage("Cannot delete project because it has at least one child:");
     preConditions.assertCanBeDeleted(rsrc, new DeleteProject.Input());
   }
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProjectIT.java b/src/test/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProjectIT.java
index a68edf8..206f231 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProjectIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProjectIT.java
@@ -30,7 +30,7 @@
 import com.google.gerrit.extensions.client.ProjectState;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.project.ProjectConfig;
 import com.googlesource.gerrit.plugins.deleteproject.DeleteProject.Input;
 import java.io.File;
 import java.io.IOException;
@@ -191,7 +191,9 @@
     adminSshSession.exec(cmd);
     assertThat(adminSshSession.getError())
         .isEqualTo(
-            "fatal: Cannot delete project because it has children: " + childrenString + "\n");
+            "fatal: Cannot delete project because it has at least one child: "
+                + childrenString
+                + "\n");
     assertThat(projectDir.exists()).isTrue();
   }
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/deleteproject/fs/FilesystemDeleteHandlerTest.java b/src/test/java/com/googlesource/gerrit/plugins/deleteproject/fs/FilesystemDeleteHandlerTest.java
index c8039f7..c03b810 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/deleteproject/fs/FilesystemDeleteHandlerTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/deleteproject/fs/FilesystemDeleteHandlerTest.java
@@ -55,7 +55,7 @@
   public void setUp() throws Exception {
     basePath = tempFolder.newFolder().toPath().resolve("base");
     deletedListener = new DynamicSet<>();
-    deletedListener.add(projectDeleteListener);
+    deletedListener.add("", projectDeleteListener);
   }
 
   @Test
diff --git a/tools/bzl/genrule2.bzl b/tools/bzl/genrule2.bzl
new file mode 100644
index 0000000..de66f32
--- /dev/null
+++ b/tools/bzl/genrule2.bzl
@@ -0,0 +1 @@
+load("@com_googlesource_gerrit_bazlets//tools:genrule2.bzl", "genrule2")
diff --git a/tools/bzl/js.bzl b/tools/bzl/js.bzl
new file mode 100644
index 0000000..0eba184
--- /dev/null
+++ b/tools/bzl/js.bzl
@@ -0,0 +1 @@
+load("@com_googlesource_gerrit_bazlets//tools:js.bzl", "polygerrit_plugin")