Merge branch 'stable-2.14' into stable-2.15

* stable-2.14:
  Fix broken build status badge link
  Allow to protect specific projects against deletion
  Add tests for protected projects

Change-Id: I507c76291c1467f42260f7e7d90c6b4724ffa985
diff --git a/WORKSPACE b/WORKSPACE
index bf1bac2..84d17fc 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -3,7 +3,7 @@
 load("//:bazlets.bzl", "load_bazlets")
 
 load_bazlets(
-    commit = "2190d8b6c9690c445b7762aeed57c96726ae9766",
+    commit = "3995849fe742201d93e40f2b109dcff13dd0a4ed",
     #    local_path = "/home/<user>/projects/bazlets",
 )
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteAction.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteAction.java
index 1ce0ee3..19ce6e3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteAction.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteAction.java
@@ -17,6 +17,8 @@
 import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.webui.UiAction;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.notedb.NotesMigration;
+import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.project.ProjectResource;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -39,7 +41,9 @@
       @PluginName String pluginName,
       DeleteLog deleteLog,
       Configuration cfg,
-      HideProject hideProject) {
+      HideProject hideProject,
+      PermissionBackend permissionBackend,
+      NotesMigration migration) {
     super(
         dbHandler,
         fsHandler,
@@ -49,7 +53,9 @@
         pluginName,
         deleteLog,
         cfg,
-        hideProject);
+        hideProject,
+        permissionBackend,
+        migration);
     this.protectedProjects = protectedProjects;
   }
 
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 066da65..472198e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProject.java
@@ -18,6 +18,7 @@
 import static com.googlesource.gerrit.plugins.deleteproject.DeleteProjectCapability.DELETE_PROJECT;
 
 import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.extensions.api.access.PluginPermission;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -26,7 +27,9 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.CapabilityControl;
+import com.google.gerrit.server.notedb.NotesMigration;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.project.ProjectResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -57,6 +60,8 @@
   private final DeleteLog deleteLog;
   private final Configuration cfg;
   private final HideProject hideProject;
+  private PermissionBackend permissionBackend;
+  private NotesMigration migration;
 
   @Inject
   DeleteProject(
@@ -68,7 +73,9 @@
       @PluginName String pluginName,
       DeleteLog deleteLog,
       Configuration cfg,
-      HideProject hideProject) {
+      HideProject hideProject,
+      PermissionBackend permissionBackend,
+      NotesMigration migration) {
     this.dbHandler = dbHandler;
     this.fsHandler = fsHandler;
     this.cacheHandler = cacheHandler;
@@ -78,6 +85,8 @@
     this.deleteLog = deleteLog;
     this.cfg = cfg;
     this.hideProject = hideProject;
+    this.permissionBackend = permissionBackend;
+    this.migration = migration;
   }
 
   @Override
@@ -106,10 +115,11 @@
   }
 
   protected boolean canDelete(ProjectResource rsrc) {
-    CapabilityControl ctl = userProvider.get().getCapabilities();
-    return ctl.canAdministrateServer()
-        || ctl.canPerform(pluginName + "-" + DELETE_PROJECT)
-        || (ctl.canPerform(pluginName + "-" + DELETE_OWN_PROJECT) && rsrc.getControl().isOwner());
+    PermissionBackend.WithUser userPermission = permissionBackend.user(userProvider);
+    return userPermission.testOrFalse(GlobalPermission.ADMINISTRATE_SERVER)
+        || userPermission.testOrFalse(new PluginPermission(pluginName, DELETE_PROJECT))
+        || (userPermission.testOrFalse(new PluginPermission(pluginName, DELETE_OWN_PROJECT))
+            && rsrc.getControl().isOwner());
   }
 
   public void assertCanDelete(ProjectResource rsrc, Input input) throws ResourceConflictException {
@@ -133,7 +143,9 @@
     Exception ex = null;
     try {
       if (!preserve || !cfg.projectOnPreserveHidden()) {
-        dbHandler.delete(project);
+        if (!migration.disableChangeReviewDb()) {
+          dbHandler.delete(project);
+        }
         try {
           fsHandler.delete(project, preserve);
         } catch (RepositoryNotFoundException 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 397a65c..8b60ae3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HideProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HideProject.java
@@ -24,6 +24,7 @@
 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.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.CreateProject;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectResource;
@@ -88,7 +89,8 @@
       } catch (BadRequestException
           | UnprocessableEntityException
           | ResourceNotFoundException
-          | ConfigInvalidException e) {
+          | 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/database/DatabaseDeleteHandler.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/database/DatabaseDeleteHandler.java
index 5e0a2f8..b99735b 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
@@ -34,6 +34,7 @@
 import com.google.gerrit.server.change.AccountPatchReviewStore;
 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.index.change.ChangeIndexer;
 import com.google.gerrit.server.project.NoSuchChangeException;
@@ -206,6 +207,8 @@
     } catch (RepositoryNotFoundException e) {
       // we're trying to delete the repository,
       // so this exception should not stop us
+    } catch (SubmoduleException e) {
+      throw new CannotDeleteProjectException("Project has submodule.");
     } catch (IOException e) {
       throw new CannotDeleteProjectException("Project is subscribed by other projects.");
     }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/projectconfig/ProjectConfigDeleteHandler.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/projectconfig/ProjectConfigDeleteHandler.java
index 5cdbcad..271105d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/projectconfig/ProjectConfigDeleteHandler.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/projectconfig/ProjectConfigDeleteHandler.java
@@ -18,6 +18,7 @@
 import com.google.common.base.Joiner;
 import com.google.common.collect.Iterables;
 import com.google.gerrit.extensions.common.ProjectInfo;
+import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ListChildProjects;
 import com.google.gerrit.server.project.ProjectResource;
 import com.google.inject.Inject;
@@ -44,21 +45,26 @@
   }
 
   private void assertHasNoChildProjects(ProjectResource rsrc) throws CannotDeleteProjectException {
-    List<ProjectInfo> children = listChildProjectsProvider.get().apply(rsrc);
-    if (!children.isEmpty()) {
-      String childrenString =
-          Joiner.on(", ")
-              .join(
-                  Iterables.transform(
-                      children,
-                      new Function<ProjectInfo, String>() {
-                        @Override
-                        public String apply(ProjectInfo info) {
-                          return info.name;
-                        }
-                      }));
+    try {
+      List<ProjectInfo> children = listChildProjectsProvider.get().apply(rsrc);
+      if (!children.isEmpty()) {
+        String childrenString =
+            Joiner.on(", ")
+                .join(
+                    Iterables.transform(
+                        children,
+                        new Function<ProjectInfo, String>() {
+                          @Override
+                          public String apply(ProjectInfo info) {
+                            return info.name;
+                          }
+                        }));
+        throw new CannotDeleteProjectException(
+            "Cannot delete project because it has children: " + childrenString);
+      }
+    } catch (PermissionBackendException e) {
       throw new CannotDeleteProjectException(
-          "Cannot delete project because it has children: " + childrenString);
+          "Cannot delete project because of failure in permission backend.");
     }
   }
 }