Add UI projects actions for copy and resume copy
On the ProjectInfoScreen there are now action buttons to:
a) copy this project to a new project
b) resume copy for the project from the copy source
The action for resume copy is only shown if the the project is a copy
of another project (in the same Gerrit server) and if this source
project still exists.
Change-Id: I99cf6365ef7e94bb7cf292486640c14a9a9d1e4c
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
diff --git a/BUCK b/BUCK
index 8f8013f..63446b4 100644
--- a/BUCK
+++ b/BUCK
@@ -20,6 +20,7 @@
'Gerrit-ApiVersion: 2.11-SNAPSHOT',
'Gerrit-Module: com.googlesource.gerrit.plugins.importer.Module',
'Gerrit-SshModule: com.googlesource.gerrit.plugins.importer.SshModule',
+ 'Gerrit-HttpModule: com.googlesource.gerrit.plugins.importer.HttpModule',
],
deps = [
HTTP_LIB,
diff --git a/pom.xml b/pom.xml
index 0870deb..2698efb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,6 +41,7 @@
<Gerrit-PluginName>importer</Gerrit-PluginName>
<Gerrit-Module>com.googlesource.gerrit.plugins.importer.Module</Gerrit-Module>
<Gerrit-SshModule>com.googlesource.gerrit.plugins.importer.SshModule</Gerrit-SshModule>
+ <Gerrit-HttpModule>com.googlesource.gerrit.plugins.importer.HttpModule</Gerrit-HttpModule>
<Implementation-Vendor>Gerrit Code Review</Implementation-Vendor>
<Implementation-URL>http://code.google.com/p/gerrit/</Implementation-URL>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/CopyProject.java b/src/main/java/com/googlesource/gerrit/plugins/importer/CopyProject.java
index 7bbc414..eba963c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/CopyProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/CopyProject.java
@@ -16,15 +16,18 @@
import com.google.common.base.Strings;
import com.google.gerrit.common.errors.NoSuchAccountException;
+import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.restapi.BadRequestException;
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.extensions.webui.UiAction;
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.AccountState;
+import com.google.gerrit.server.account.CapabilityControl;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.project.NoSuchChangeException;
@@ -43,7 +46,8 @@
@Singleton
@RequiresCapability(CopyProjectCapability.ID)
-class CopyProject implements RestModifyView<ProjectResource, Input> {
+class CopyProject implements RestModifyView<ProjectResource, Input>,
+ UiAction<ProjectResource> {
public static class Input {
public String name;
}
@@ -51,15 +55,18 @@
private final ImportProject.Factory importProjectFactory;
private final String canonicalWebUrl;
private final Provider<CurrentUser> currentUserProvider;
+ private final String pluginName;
@Inject
CopyProject(
ImportProject.Factory importProjectFactory,
@CanonicalWebUrl String canonicalWebUrl,
- Provider<CurrentUser> currentUserProvider) {
+ Provider<CurrentUser> currentUserProvider,
+ @PluginName String pluginName) {
this.importProjectFactory = importProjectFactory;
this.canonicalWebUrl = canonicalWebUrl;
this.currentUserProvider = currentUserProvider;
+ this.pluginName = pluginName;
}
@Override
@@ -81,4 +88,18 @@
return importProjectFactory.create(new Project.NameKey(input.name))
.apply(new ConfigResource(), in);
}
+
+ @Override
+ public UiAction.Description getDescription(ProjectResource rsrc) {
+ return new UiAction.Description()
+ .setLabel("Copy...")
+ .setTitle(String.format("Copy project %s", rsrc.getName()))
+ .setVisible(canCopy(rsrc));
+ }
+
+ private boolean canCopy(ProjectResource rsrc) {
+ CapabilityControl ctl = currentUserProvider.get().getCapabilities();
+ return ctl.canAdministrateServer()
+ || ctl.canPerform(pluginName + "-" + CopyProjectCapability.ID);
+ }
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/HttpModule.java b/src/main/java/com/googlesource/gerrit/plugins/importer/HttpModule.java
new file mode 100644
index 0000000..e5b6ce5
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/HttpModule.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2015 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.importer;
+
+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("copy-project.js"));
+ DynamicSet.bind(binder(), WebUiPlugin.class)
+ .toInstance(new JavaScriptPlugin("resume-copy-project.js"));
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/ResumeCopyProject.java b/src/main/java/com/googlesource/gerrit/plugins/importer/ResumeCopyProject.java
index 7383137..00e22e1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/ResumeCopyProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/ResumeCopyProject.java
@@ -15,16 +15,23 @@
package com.googlesource.gerrit.plugins.importer;
import com.google.gerrit.common.errors.NoSuchAccountException;
+import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.restapi.IdString;
+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.extensions.webui.UiAction;
+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.AccountState;
+import com.google.gerrit.server.account.CapabilityControl;
+import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.validators.ValidationException;
import com.google.gwtorm.server.OrmException;
@@ -40,19 +47,29 @@
@Singleton
@RequiresCapability(CopyProjectCapability.ID)
-class ResumeCopyProject implements RestModifyView<ProjectResource, Input> {
+class ResumeCopyProject implements RestModifyView<ProjectResource, Input>,
+ UiAction<ProjectResource> {
private final ResumeProjectImport resumeProjectImport;
private final ProjectsCollection projectsCollection;
private final Provider<CurrentUser> currentUserProvider;
+ private final String pluginName;
+ private final String canonicalWebUrl;
+ private final ProjectCache projectCache;
@Inject
ResumeCopyProject(
ResumeProjectImport resumeProjectImport,
ProjectsCollection projectsCollection,
- Provider<CurrentUser> currentUserProvider) {
+ Provider<CurrentUser> currentUserProvider,
+ @PluginName String pluginName,
+ @CanonicalWebUrl String canonicalWebUrl,
+ ProjectCache projectCache) {
this.resumeProjectImport = resumeProjectImport;
this.projectsCollection = projectsCollection;
this.currentUserProvider = currentUserProvider;
+ this.pluginName = pluginName;
+ this.canonicalWebUrl = canonicalWebUrl;
+ this.projectCache = projectCache;
}
@Override
@@ -70,4 +87,38 @@
IdString.fromDecoded(rsrc.getName()));
return resumeProjectImport.apply(projectResource, in);
}
+
+ @Override
+ public UiAction.Description getDescription(
+ ProjectResource rsrc) {
+ return new UiAction.Description()
+ .setLabel("Resume Copy...")
+ .setTitle(String.format("Resume copy for project %s", rsrc.getName()))
+ .setVisible(canResumeCopy(rsrc) && isCopied(rsrc));
+ }
+
+ private boolean canResumeCopy(ProjectResource rsrc) {
+ CapabilityControl ctl = currentUserProvider.get().getCapabilities();
+ return ctl.canAdministrateServer()
+ || (ctl.canPerform(pluginName + "-" + CopyProjectCapability.ID)
+ && rsrc.getControl().isOwner());
+ }
+
+ private boolean isCopied(ProjectResource rsrc) {
+ try {
+ ImportProjectResource projectResource =
+ projectsCollection.parse(new ConfigResource(),
+ IdString.fromDecoded(rsrc.getName()));
+ ImportProjectInfo info = projectResource.getInfo();
+ if (!canonicalWebUrl.equals(info.from)) {
+ // no copy, but an import from another system
+ return false;
+ }
+
+ // check that source project still exists
+ return projectCache.get(new Project.NameKey(info.name)) != null;
+ } catch (ResourceNotFoundException | IOException e) {
+ return false;
+ }
+ }
}
diff --git a/src/main/resources/static/copy-project.js b/src/main/resources/static/copy-project.js
new file mode 100644
index 0000000..a7bc91d
--- /dev/null
+++ b/src/main/resources/static/copy-project.js
@@ -0,0 +1,42 @@
+// Copyright (C) 2015 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.
+
+Gerrit.install(function(self) {
+ function onCopyProject(c) {
+ var t = c.textfield();
+ var b = c.button('Copy',
+ {onclick: function(){
+ c.call(
+ {name: t.value},
+ function(r) {
+ c.hide();
+ window.alert('The project: "'
+ + c.project
+ + '" was copied to "'
+ + t.value
+ + '".'),
+ Gerrit.go('/admin/projects/' + t.value);
+ });
+ }});
+ c.popup(c.div(
+ c.msg('Copy project "'
+ + c.project
+ + '" to '),
+ t,
+ c.br(),
+ c.br(),
+ b));
+ }
+ self.onAction('project', 'copy', onCopyProject);
+ });
diff --git a/src/main/resources/static/resume-copy-project.js b/src/main/resources/static/resume-copy-project.js
new file mode 100644
index 0000000..9d61631
--- /dev/null
+++ b/src/main/resources/static/resume-copy-project.js
@@ -0,0 +1,38 @@
+// Copyright (C) 2015 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.
+
+Gerrit.install(function(self) {
+ function onResumeCopyProject(c) {
+ var b = c.button('Resume',
+ {onclick: function(){
+ c.call(
+ {},
+ function(r) {
+ c.hide();
+ window.alert('Copy for project "'
+ + c.project
+ + '" was resumed."'),
+ Gerrit.go('/admin/projects/' + c.project);
+ });
+ }});
+ c.popup(c.div(
+ c.msg('Resume copy for project "'
+ + c.project
+ + '" ?'),
+ c.br(),
+ c.br(),
+ b));
+ }
+ self.onAction('project', 'copy.resume', onResumeCopyProject);
+ });