Merge branch 'stable-3.2' into master

* stable-3.2:
  Fix event handling on error

Change-Id: I1ca0d89a1e5245360bf00039fc2abb33fee18f91
diff --git a/BUILD b/BUILD
index c982cdb..d560d98 100644
--- a/BUILD
+++ b/BUILD
@@ -1,5 +1,5 @@
 load("@rules_java//java:defs.bzl", "java_library")
-load("@npm_bazel_rollup//:index.bzl", "rollup_bundle")
+load("@npm//@bazel/rollup:index.bzl", "rollup_bundle")
 load("//tools/bzl:junit.bzl", "junit_tests")
 load("//tools/js:eslint.bzl", "eslint")
 load(
@@ -47,9 +47,9 @@
     name = "delete-project-bundle",
     srcs = glob(["gr-delete-repo/*.js"]),
     entry_point = "gr-delete-repo/plugin.js",
+    format = "iife",
     rollup_bin = "//tools/node_tools:rollup-bin",
     sourcemap = "hidden",
-    format = "iife",
     deps = [
         "@tools_npm//rollup-plugin-node-resolve",
     ],
diff --git a/gr-delete-repo/gr-delete-repo_html.js b/gr-delete-repo/gr-delete-repo_html.js
index 31962d8..e3a985d 100644
--- a/gr-delete-repo/gr-delete-repo_html.js
+++ b/gr-delete-repo/gr-delete-repo_html.js
@@ -16,13 +16,24 @@
  */
 
 export const htmlTemplate = Polymer.html`
-    <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>
+    <style include="shared-styles"></style>
+    <style include="gr-form-styles">
+    :host {
+      display: block;
+      margin-bottom: var(--spacing-xxl);
+    }
+    </style>
+    <h3 class="heading-3">
+      [[action.label]]
+    </h3>
+    <gr-button
+      title="[[action.title]]"
+      loading="[[_deleting]]"
+      disabled="[[!action.enabled]]"
+      on-click="_handleCommandTap"
+    >
+      [[action.label]]
+    </gr-button>
     <gr-overlay id="deleteRepoOverlay" with-backdrop>
       <gr-dialog
           id="deleteRepoDialog"
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 417c968..de96ec1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeletePreconditions.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeletePreconditions.java
@@ -158,7 +158,7 @@
               .collect(toSet());
       SubmoduleOp sub = subOpFactory.create(branches, mergeOp);
       for (BranchNameKey b : branches) {
-        if (!sub.superProjectSubscriptionsForSubmoduleBranch(b).isEmpty()) {
+        if (sub.hasSuperproject(b)) {
           throw new CannotDeleteProjectException("Project is subscribed by other projects.");
         }
       }
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 4b36826..40463b2 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProject.java
@@ -86,7 +86,7 @@
       if (!preserve || !cfg.projectOnPreserveHidden()) {
         dbHandler.delete(project);
         try {
-          fsHandler.delete(project, preserve);
+          fsHandler.delete(project.getNameKey(), preserve);
         } catch (RepositoryNotFoundException e) {
           throw new ResourceNotFoundException(project.getName(), e);
         }
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 7656726..b756364 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HideProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HideProject.java
@@ -14,7 +14,7 @@
 
 package com.googlesource.gerrit.plugins.deleteproject;
 
-import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.entities.AccessSection;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.client.ProjectState;
 import com.google.gerrit.extensions.restapi.IdString;
@@ -58,12 +58,9 @@
   }
 
   public void apply(ProjectResource rsrc) throws IOException, RestApiException {
-    try {
-      MetaDataUpdate md = metaDataUpdateFactory.create(rsrc.getNameKey());
-
+    try (MetaDataUpdate md = metaDataUpdateFactory.create(rsrc.getNameKey())) {
       ProjectConfig projectConfig = projectConfigFactory.read(md);
-      Project p = projectConfig.getProject();
-      p.setState(ProjectState.HIDDEN);
+      projectConfig.updateProject(p -> p.setState(ProjectState.HIDDEN));
 
       for (AccessSection as : projectConfig.getAccessSections()) {
         projectConfig.remove(as);
@@ -71,7 +68,7 @@
 
       String parentForDeletedProjects = cfg.getDeletedProjectsParent();
       createProjectIfMissing(parentForDeletedProjects);
-      p.setParentName(parentForDeletedProjects);
+      projectConfig.updateProject(p -> p.setParent(parentForDeletedProjects));
 
       md.setMessage("Hide project\n");
       projectConfig.commit(md);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/fs/FilesystemDeleteHandler.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/fs/FilesystemDeleteHandler.java
index a5436c1..053b2fc 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/fs/FilesystemDeleteHandler.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/fs/FilesystemDeleteHandler.java
@@ -58,14 +58,14 @@
     this.config = config;
   }
 
-  public void delete(Project project, boolean preserveGitRepository)
+  public void delete(Project.NameKey project, boolean preserveGitRepository)
       throws IOException, RepositoryNotFoundException {
     // Remove from the jgit cache
-    Repository repository = repoManager.openRepository(project.getNameKey());
+    Repository repository = repoManager.openRepository(project);
     cleanCache(repository);
     if (!preserveGitRepository) {
       Path repoPath = repository.getDirectory().toPath();
-      String projectName = project.getNameKey().get();
+      String projectName = project.get();
       if (config.shouldArchiveDeletedRepos()) {
         archiveGitRepository(projectName, repoPath);
       } else {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/deleteproject/ConfigurationTest.java b/src/test/java/com/googlesource/gerrit/plugins/deleteproject/ConfigurationTest.java
index 70d1715..9b59f63 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/deleteproject/ConfigurationTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/deleteproject/ConfigurationTest.java
@@ -36,7 +36,7 @@
   private static final long DEFAULT_ARCHIVE_DURATION_MS = TimeUnit.DAYS.toMillis(180);
   private static final String CUSTOM_DURATION = "100";
   private static final String CUSTOM_PARENT = "customParent";
-  private static final String INVALID_CUSTOM_FOLDER = "\0";
+  private static final String INVALID_CUSTOM_FOLDER = "//\\\\\\///////";
   private static final String INVALID_ARCHIVE_DURATION = "180weeks180years";
   private static final String PLUGIN_NAME = "delete-project";
 
@@ -56,7 +56,7 @@
   @Test
   public void defaultValuesAreLoaded() {
     when(pluginConfigFactoryMock.getFromGerritConfig(PLUGIN_NAME))
-        .thenReturn(new PluginConfig(PLUGIN_NAME, new Config()));
+        .thenReturn(PluginConfig.create(PLUGIN_NAME, new Config(), null));
     deleteConfig = new Configuration(pluginConfigFactoryMock, PLUGIN_NAME, pluginDataDir);
 
     assertThat(deleteConfig.getDeletedProjectsParent()).isEqualTo("Deleted-Projects");
@@ -69,7 +69,7 @@
 
   @Test
   public void customValuesAreLoaded() {
-    PluginConfig pluginConfig = new PluginConfig(PLUGIN_NAME, new Config());
+    PluginConfig.Update pluginConfig = PluginConfig.Update.forTest(PLUGIN_NAME, new Config());
     pluginConfig.setString("parentForDeletedProjects", CUSTOM_PARENT);
     pluginConfig.setBoolean("allowDeletionOfReposWithTags", false);
     pluginConfig.setBoolean("hideProjectOnPreserve", true);
@@ -77,7 +77,8 @@
     pluginConfig.setString("deleteArchivedReposAfter", CUSTOM_DURATION);
     pluginConfig.setString("archiveFolder", customArchiveFolder.toString());
 
-    when(pluginConfigFactoryMock.getFromGerritConfig(PLUGIN_NAME)).thenReturn(pluginConfig);
+    when(pluginConfigFactoryMock.getFromGerritConfig(PLUGIN_NAME))
+        .thenReturn(pluginConfig.asPluginConfig());
     deleteConfig = new Configuration(pluginConfigFactoryMock, PLUGIN_NAME, pluginDataDir);
 
     assertThat(deleteConfig.getDeletedProjectsParent()).isEqualTo(CUSTOM_PARENT);
@@ -91,10 +92,11 @@
 
   @Test
   public void archiveDurationWithUnitIsLoaded() {
-    PluginConfig pluginConfig = new PluginConfig(PLUGIN_NAME, new Config());
+    PluginConfig.Update pluginConfig = PluginConfig.Update.forTest(PLUGIN_NAME, new Config());
     pluginConfig.setString("deleteArchivedReposAfter", CUSTOM_DURATION + "years");
 
-    when(pluginConfigFactoryMock.getFromGerritConfig(PLUGIN_NAME)).thenReturn(pluginConfig);
+    when(pluginConfigFactoryMock.getFromGerritConfig(PLUGIN_NAME))
+        .thenReturn(pluginConfig.asPluginConfig());
     deleteConfig = new Configuration(pluginConfigFactoryMock, PLUGIN_NAME, pluginDataDir);
 
     assertThat(deleteConfig.getArchiveDuration())
@@ -103,10 +105,11 @@
 
   @Test
   public void invalidArchiveDuration() {
-    PluginConfig pluginConfig = new PluginConfig(PLUGIN_NAME, new Config());
+    PluginConfig.Update pluginConfig = PluginConfig.Update.forTest(PLUGIN_NAME, new Config());
     pluginConfig.setString("deleteArchivedReposAfter", INVALID_ARCHIVE_DURATION);
 
-    when(pluginConfigFactoryMock.getFromGerritConfig(PLUGIN_NAME)).thenReturn(pluginConfig);
+    when(pluginConfigFactoryMock.getFromGerritConfig(PLUGIN_NAME))
+        .thenReturn(pluginConfig.asPluginConfig());
     deleteConfig = new Configuration(pluginConfigFactoryMock, PLUGIN_NAME, pluginDataDir);
 
     assertThat(deleteConfig.getArchiveDuration()).isEqualTo(DEFAULT_ARCHIVE_DURATION_MS);
@@ -114,10 +117,11 @@
 
   @Test
   public void invalidTargetArchiveFolder() {
-    PluginConfig pluginConfig = new PluginConfig(PLUGIN_NAME, new Config());
+    PluginConfig.Update pluginConfig = PluginConfig.Update.forTest(PLUGIN_NAME, new Config());
     pluginConfig.setString("archiveFolder", INVALID_CUSTOM_FOLDER);
 
-    when(pluginConfigFactoryMock.getFromGerritConfig(PLUGIN_NAME)).thenReturn(pluginConfig);
+    when(pluginConfigFactoryMock.getFromGerritConfig(PLUGIN_NAME))
+        .thenReturn(pluginConfig.asPluginConfig());
     deleteConfig = new Configuration(pluginConfigFactoryMock, PLUGIN_NAME, pluginDataDir);
 
     assertThat(deleteConfig.getArchiveFolder().toString()).isEqualTo(pluginDataDir.toString());
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 4960816..e878096 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProjectIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProjectIT.java
@@ -31,12 +31,12 @@
 import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
-import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.entities.CachedProjectConfig;
+import com.google.gerrit.entities.Permission;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.client.ProjectState;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.server.project.ProjectConfig;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.deleteproject.DeleteProject.Input;
 import java.io.File;
@@ -174,7 +174,8 @@
     String cmd = createDeleteCommand("--preserve-git-repository", project.get());
     adminSshSession.exec(cmd);
 
-    ProjectConfig cfg = projectCache.get(project).orElseThrow(illegalState(project)).getConfig();
+    CachedProjectConfig cfg =
+        projectCache.get(project).orElseThrow(illegalState(project)).getConfig();
     ProjectState state = cfg.getProject().getState();
 
     assertThat(state).isEqualTo(ProjectState.HIDDEN);
diff --git a/src/test/java/com/googlesource/gerrit/plugins/deleteproject/ProtectedProjectsTest.java b/src/test/java/com/googlesource/gerrit/plugins/deleteproject/ProtectedProjectsTest.java
index c4c370a..75ea96b 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/deleteproject/ProtectedProjectsTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/deleteproject/ProtectedProjectsTest.java
@@ -42,7 +42,7 @@
   @Mock private AllUsersNameProvider allUsersMock;
   @Mock private PluginConfigFactory pluginConfigFactoryMock;
 
-  private PluginConfig pluginConfig;
+  private PluginConfig.Update pluginConfig;
   private Configuration deleteConfig;
   private ProtectedProjects protectedProjects;
   private File pluginData = new File("data");
@@ -51,8 +51,9 @@
   public void setup() throws Exception {
     when(allProjectsMock.get()).thenReturn(new AllProjectsName("All-Projects"));
     when(allUsersMock.get()).thenReturn(new AllUsersName("All-Users"));
-    pluginConfig = new PluginConfig(PLUGIN_NAME, new Config());
-    when(pluginConfigFactoryMock.getFromGerritConfig(PLUGIN_NAME)).thenReturn(pluginConfig);
+    pluginConfig = PluginConfig.Update.forTest(PLUGIN_NAME, new Config());
+    when(pluginConfigFactoryMock.getFromGerritConfig(PLUGIN_NAME))
+        .thenReturn(pluginConfig.asPluginConfig());
     deleteConfig = new Configuration(pluginConfigFactoryMock, PLUGIN_NAME, pluginData);
     protectedProjects = new ProtectedProjects(allProjectsMock, allUsersMock, deleteConfig);
   }
@@ -76,7 +77,8 @@
   public void customProjectIsProtected() throws Exception {
     List<String> projects = ImmutableList.of("Custom-Parent", "^protected-.*");
     pluginConfig.setStringList("protectedProject", projects);
-    when(pluginConfigFactoryMock.getFromGerritConfig(PLUGIN_NAME)).thenReturn(pluginConfig);
+    when(pluginConfigFactoryMock.getFromGerritConfig(PLUGIN_NAME))
+        .thenReturn(pluginConfig.asPluginConfig());
     deleteConfig = new Configuration(pluginConfigFactoryMock, PLUGIN_NAME, pluginData);
     assertThat(deleteConfig.protectedProjects()).hasSize(projects.size());
     protectedProjects = new ProtectedProjects(allProjectsMock, allUsersMock, deleteConfig);
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 ce291e4..9cffd14 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
@@ -63,11 +63,10 @@
     String repoName = "testRepo";
     Repository repository = createRepository(repoName);
     Project.NameKey nameKey = Project.nameKey(repoName);
-    Project project = new Project(nameKey);
     when(repoManager.openRepository(nameKey)).thenReturn(repository);
     when(config.shouldArchiveDeletedRepos()).thenReturn(false);
     fsDeleteHandler = new FilesystemDeleteHandler(repoManager, deletedListener, config);
-    fsDeleteHandler.delete(project, false);
+    fsDeleteHandler.delete(nameKey, false);
     assertThat(repository.getDirectory().exists()).isFalse();
   }
 
@@ -76,10 +75,9 @@
     String repoName = "a/b/c";
     Repository repository = createRepository(repoName);
     Project.NameKey nameKey = Project.nameKey(repoName);
-    Project project = new Project(nameKey);
     when(repoManager.openRepository(nameKey)).thenReturn(repository);
     fsDeleteHandler = new FilesystemDeleteHandler(repoManager, deletedListener, config);
-    fsDeleteHandler.delete(project, false);
+    fsDeleteHandler.delete(nameKey, false);
     assertThat(repository.getDirectory().exists()).isFalse();
   }
 
@@ -92,10 +90,9 @@
     Repository repoToKeep = createRepository(repoToKeepName);
 
     Project.NameKey nameKey = Project.nameKey(repoToDeleteName);
-    Project project = new Project(nameKey);
     when(repoManager.openRepository(nameKey)).thenReturn(repoToDelete);
     fsDeleteHandler = new FilesystemDeleteHandler(repoManager, deletedListener, config);
-    fsDeleteHandler.delete(project, false);
+    fsDeleteHandler.delete(nameKey, false);
     assertThat(repoToDelete.getDirectory().exists()).isFalse();
     assertThat(repoToKeep.getDirectory().exists()).isTrue();
   }
@@ -105,10 +102,9 @@
     String repoName = "preservedRepo";
     Repository repository = createRepository(repoName);
     Project.NameKey nameKey = Project.nameKey(repoName);
-    Project project = new Project(nameKey);
     when(repoManager.openRepository(nameKey)).thenReturn(repository);
     fsDeleteHandler = new FilesystemDeleteHandler(repoManager, deletedListener, config);
-    fsDeleteHandler.delete(project, true);
+    fsDeleteHandler.delete(nameKey, true);
     assertThat(repository.getDirectory().exists()).isTrue();
   }
 
@@ -127,10 +123,9 @@
     when(config.shouldArchiveDeletedRepos()).thenReturn(true);
     when(config.getArchiveFolder()).thenReturn(archiveFolder);
     Project.NameKey nameKey = Project.nameKey(repoName);
-    Project project = new Project(nameKey);
     when(repoManager.openRepository(nameKey)).thenReturn(repository);
     fsDeleteHandler = new FilesystemDeleteHandler(repoManager, deletedListener, config);
-    fsDeleteHandler.delete(project, false);
+    fsDeleteHandler.delete(nameKey, false);
     assertThat(repository.getDirectory().exists()).isFalse();
     String patternToVerify = archiveFolder.resolve(repoName).toString() + "*%archived%.git";
     assertThat(pathExistsWithPattern(archiveFolder, patternToVerify)).isTrue();