Merge "Add project UI action to complete a project import"
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/AccountUtil.java b/src/main/java/com/googlesource/gerrit/plugins/importer/AccountUtil.java
index b996657..f23cef1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/AccountUtil.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/AccountUtil.java
@@ -68,7 +68,7 @@
case HTTP_LDAP:
case CLIENT_SSL_CERT_LDAP:
case LDAP:
- return createAccountByLdapAndAddSshKeys(api, acc, a);
+ return createAccountByLdapAndAddSshKeys(api, acc);
default:
throw new NoSuchAccountException(String.format("User %s not found",
acc.username));
@@ -84,7 +84,7 @@
}
private Account.Id createAccountByLdapAndAddSshKeys(RemoteApi api,
- AccountInfo acc, AccountState a)
+ AccountInfo acc)
throws NoSuchAccountException, BadRequestException, IOException,
OrmException {
if (!acc.username.matches(Account.USER_NAME_PATTERN)) {
@@ -96,7 +96,7 @@
AuthRequest req = AuthRequest.forUser(acc.username);
req.setSkipAuthentication(true);
Account.Id id = accountManager.authenticate(req).getAccountId();
- addSshKeys(api, acc, a);
+ addSshKeys(api, acc);
return id;
} catch (AccountException e) {
throw new NoSuchAccountException(
@@ -104,9 +104,10 @@
}
}
- private void addSshKeys(RemoteApi api, AccountInfo acc, AccountState a) throws
+ private void addSshKeys(RemoteApi api, AccountInfo acc) throws
BadRequestException, IOException, OrmException {
List<SshKeyInfo> sshKeys = api.getSshKeys(acc.username);
+ AccountState a = accountCache.getByUsername(acc.username);
db.get().accountSshKeys().upsert(toAccountSshKey(a, sshKeys));
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/AddHashtagsStep.java b/src/main/java/com/googlesource/gerrit/plugins/importer/AddHashtagsStep.java
index e40fc5e..f3bf629 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/AddHashtagsStep.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/AddHashtagsStep.java
@@ -15,12 +15,10 @@
package com.googlesource.gerrit.plugins.importer;
import com.google.gerrit.extensions.api.changes.HashtagsInput;
-import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.HashtagsUtil;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
@@ -39,7 +37,7 @@
}
private final HashtagsUtil hashtagsUtil;
- private final IdentifiedUser.GenericFactory genericUserFactory;
+ private final CurrentUser currentUser;
private final ChangeControl.GenericFactory changeControlFactory;
private final Change change;
private final ChangeInfo changeInfo;
@@ -47,7 +45,7 @@
@Inject
AddHashtagsStep(HashtagsUtil hashtagsUtil,
- IdentifiedUser.GenericFactory genericUserFactory,
+ CurrentUser currentUser,
ChangeControl.GenericFactory changeControlFactory,
@Assisted Change change,
@Assisted ChangeInfo changeInfo,
@@ -55,14 +53,14 @@
this.hashtagsUtil = hashtagsUtil;
this.change = change;
this.changeInfo = changeInfo;
- this.genericUserFactory = genericUserFactory;
+ this.currentUser = currentUser;
this.changeControlFactory = changeControlFactory;
this.resume = resume;
}
void add() throws IllegalArgumentException, AuthException, IOException,
ValidationException, OrmException, NoSuchChangeException {
- ChangeControl ctrl = control(change, changeInfo.owner);
+ ChangeControl ctrl = changeControlFactory.controlFor(change, currentUser);
if (resume) {
HashtagsInput input = new HashtagsInput();
@@ -74,15 +72,4 @@
input.add = new HashSet<>(changeInfo.hashtags);
hashtagsUtil.setHashtags(ctrl, input, false, false);
}
-
- private ChangeControl control(Change change, AccountInfo acc)
- throws NoSuchChangeException {
- return control(change, new Account.Id(acc._accountId));
- }
-
- private ChangeControl control(Change change, Account.Id id)
- throws NoSuchChangeException {
- return changeControlFactory.controlFor(change,
- genericUserFactory.create(id));
- }
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/ImportProject.java b/src/main/java/com/googlesource/gerrit/plugins/importer/ImportProject.java
index 76fefdd..74b8eb0 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/ImportProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/ImportProject.java
@@ -97,6 +97,7 @@
private final Project.NameKey targetProject;
private Project.NameKey srcProject;
private Project.NameKey parent;
+ private boolean force;
private Writer err;
@@ -146,9 +147,10 @@
}
}
- public Response<String> resume(String user, String pass, File importStatus)
- throws RestApiException, OrmException, IOException, ValidationException,
- GitAPIException, NoSuchChangeException, NoSuchAccountException {
+ public Response<String> resume(String user, String pass, boolean force,
+ File importStatus) throws RestApiException, OrmException, IOException,
+ ValidationException, GitAPIException, NoSuchChangeException,
+ NoSuchAccountException {
LockFile lockFile = lockForImport();
try {
ImportProjectInfo info = ImportJson.parse(importStatus);
@@ -160,6 +162,8 @@
input.name = info.name;
input.parent = info.parent;
+ this.force = force;
+
return apply(lockFile, input, info);
} finally {
lockFile.unlock();
@@ -189,7 +193,7 @@
gitFetchStep.fetch(input.user, input.pass, repo, pm);
configProjectStep.configure(targetProject, parent, pm);
replayChangesFactory.create(input.from, input.user, input.pass, repo,
- srcProject, targetProject, resume, pm).replay();
+ srcProject, targetProject, force, resume, pm).replay();
} finally {
repo.close();
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/ImportProjectMenu.java b/src/main/java/com/googlesource/gerrit/plugins/importer/ImportProjectMenu.java
index 50f5529..f470720 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/ImportProjectMenu.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/ImportProjectMenu.java
@@ -40,6 +40,7 @@
List<MenuItem> projectItems = Lists.newArrayListWithExpectedSize(2);
if (canImport()) {
projectItems.add(new MenuItem("Import Project", "#/x/" + pluginName + "/project", ""));
+ projectItems.add(new MenuItem("List Imports", "#/x/" + pluginName + "/list", ""));
}
if (!projectItems.isEmpty()) {
menuEntries.add(new MenuEntry("Projects", projectItems));
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/ListImportedProjects.java b/src/main/java/com/googlesource/gerrit/plugins/importer/ListImportedProjects.java
index 7ba24db..1ac50be 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/ListImportedProjects.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/ListImportedProjects.java
@@ -16,6 +16,7 @@
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
+import com.google.common.io.Files;
import com.google.gerrit.extensions.annotations.PluginData;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.restapi.RestReadView;
@@ -26,8 +27,10 @@
import org.kohsuke.args4j.Option;
import java.io.File;
-import java.io.FilenameFilter;
import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Locale;
import java.util.Map;
@Singleton
@@ -59,14 +62,16 @@
return importedProjects;
}
- private File[] listImportFiles() {
- match = Strings.nullToEmpty(match);
-
- return lockRoot.listFiles(new FilenameFilter() {
- @Override
- public boolean accept(File dir, String name) {
- return !name.endsWith(".lock") && name.toLowerCase().contains(match);
+ private Collection<File> listImportFiles() {
+ match = Strings.nullToEmpty(match).toLowerCase(Locale.ENGLISH);
+ Collection<File> importFiles = new HashSet<>();
+ for (File f : Files.fileTreeTraverser().preOrderTraversal(lockRoot)) {
+ if (f.isFile()
+ && !f.getName().endsWith(".lock")
+ && f.getName().toLowerCase(Locale.ENGLISH).contains(match)) {
+ importFiles.add(f);
}
- });
+ }
+ return importFiles;
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/RemoteApi.java b/src/main/java/com/googlesource/gerrit/plugins/importer/RemoteApi.java
index 3322095..55de14d 100755
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/RemoteApi.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/RemoteApi.java
@@ -14,6 +14,8 @@
package com.googlesource.gerrit.plugins.importer;
+import static com.google.gerrit.extensions.restapi.Url.encode;
+
import com.google.common.collect.Iterables;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.common.ChangeInfo;
@@ -44,6 +46,7 @@
public ProjectInfo getProject(String projectName) throws IOException,
BadRequestException {
+ projectName = encode(projectName);
String endPoint = "/projects/" + projectName;
try (RestResponse r = checkedGet(endPoint)) {
return newGson().fromJson(r.getReader(),
@@ -81,6 +84,7 @@
public GroupInfo getGroup(String groupName) throws IOException,
BadRequestException {
+ groupName = encode(groupName);
String endPoint = "/groups/" + groupName + "/detail";
try (RestResponse r = checkedGet(endPoint)) {
return newGson().fromJson(r.getReader(),
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayChangesStep.java b/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayChangesStep.java
index e32d1ec..bffae8c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayChangesStep.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/ReplayChangesStep.java
@@ -53,7 +53,8 @@
Repository repo,
@Assisted("srcProject") Project.NameKey srcProject,
@Assisted("targetProject") Project.NameKey targetProject,
- boolean resume,
+ @Assisted("force") boolean force,
+ @Assisted("resume") boolean resume,
ProgressMonitor pm);
}
@@ -72,6 +73,7 @@
private final Repository repo;
private final Project.NameKey srcProject;
private final Project.NameKey targetProject;
+ private final boolean force;
private final boolean resume;
private final ProgressMonitor pm;
@@ -93,7 +95,8 @@
@Assisted Repository repo,
@Assisted("srcProject") Project.NameKey srcProject,
@Assisted("targetProject") Project.NameKey targetProject,
- @Assisted boolean resume,
+ @Assisted("force") boolean force,
+ @Assisted("resume") boolean resume,
@Assisted ProgressMonitor pm) {
this.replayRevisionsFactory = replayRevisionsFactory;
this.replayInlineCommentsFactory = replayInlineCommentsFactory;
@@ -110,6 +113,7 @@
this.repo = repo;
this.srcProject = srcProject;
this.targetProject = targetProject;
+ this.force = force;
this.resume = resume;
this.pm = pm;
}
@@ -147,7 +151,7 @@
change = createChange(c);
} else {
resumeChange = true;
- if (change.getLastUpdatedOn().equals(c.updated)) {
+ if (!force && change.getLastUpdatedOn().equals(c.updated)) {
// change was not modified since last import
return;
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/ResumeProjectCommand.java b/src/main/java/com/googlesource/gerrit/plugins/importer/ResumeProjectCommand.java
index 4aa3d05..5dfb115 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/ResumeProjectCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/ResumeProjectCommand.java
@@ -35,6 +35,9 @@
usage = "password of remote user")
private String pass;
+ @Option(name = "--force", usage = "Whether the resume should be done forcefully.")
+ private boolean force;
+
@Option(name = "--quiet", usage = "suppress progress messages")
private boolean quiet;
@@ -58,6 +61,7 @@
ResumeProjectImport.Input input = new ResumeProjectImport.Input();
input.user = user;
input.pass = pass;
+ input.force = force;
resume.apply(rsrc, input);
} catch (RestApiException e) {
throw die(e.getMessage());
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/ResumeProjectImport.java b/src/main/java/com/googlesource/gerrit/plugins/importer/ResumeProjectImport.java
index 521be04..ad172d4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/ResumeProjectImport.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/ResumeProjectImport.java
@@ -50,6 +50,7 @@
public static class Input {
public String user;
public String pass;
+ public boolean force;
}
private final ImportProject.Factory importProjectFactory;
@@ -74,7 +75,8 @@
ImportProject importer = importProjectFactory.create(rsrc.getName());
importer.setErr(err);
- return importer.resume(input.user, input.pass, rsrc.getImportStatus());
+ return importer.resume(input.user, input.pass, input.force,
+ rsrc.getImportStatus());
}
public static class OnProjects implements
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/client/AccountInfo.java b/src/main/java/com/googlesource/gerrit/plugins/importer/client/AccountInfo.java
new file mode 100644
index 0000000..18b44fa
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/client/AccountInfo.java
@@ -0,0 +1,27 @@
+// 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.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+public class AccountInfo extends JavaScriptObject {
+ public final native int _account_id() /*-{ return this._account_id || 0; }-*/;
+ public final native String name() /*-{ return this.name; }-*/;
+ public final native String username() /*-{ return this.username; }-*/;
+ public final native String email() /*-{ return this.email; }-*/;
+
+ protected AccountInfo() {
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/client/CompleteImportDialog.java b/src/main/java/com/googlesource/gerrit/plugins/importer/client/CompleteImportDialog.java
new file mode 100644
index 0000000..5874d8a
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/client/CompleteImportDialog.java
@@ -0,0 +1,115 @@
+// 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.client;
+
+import com.google.gerrit.plugin.client.Plugin;
+import com.google.gerrit.plugin.client.rpc.NoContent;
+import com.google.gerrit.plugin.client.rpc.RestApi;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.DialogBox;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwtexpui.globalkey.client.GlobalKey;
+import com.google.gwtexpui.user.client.AutoCenterDialogBox;
+
+public class CompleteImportDialog extends AutoCenterDialogBox {
+
+ private final Button cancelButton;
+ private final Button completeButton;
+
+ public CompleteImportDialog(final String project) {
+ super(/* auto hide */false, /* modal */true);
+ setGlassEnabled(true);
+ setText("Complete Project Import");
+
+ FlowPanel buttons = new FlowPanel();
+
+ completeButton = new Button();
+ completeButton.setText("Complete");
+ completeButton.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ hide();
+
+ new RestApi("config").id("server")
+ .view(Plugin.get().getName(), "projects").id(project)
+ .delete(new AsyncCallback<NoContent>() {
+ @Override
+ public void onSuccess(NoContent result) {
+ Plugin.get().go("/x/" + Plugin.get().getName() + "/list");
+
+ final DialogBox successDialog = new DialogBox();
+ successDialog.setText("Project Import Completed");
+ successDialog.setAnimationEnabled(true);
+
+ Panel p = new VerticalPanel();
+ p.setStyleName("importer-message-panel");
+ p.add(new Label("The project import was completed."));
+ Button okButton = new Button("OK");
+ okButton.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ successDialog.hide();
+ }
+ });
+
+ p.add(okButton);
+ successDialog.add(p);
+
+ successDialog.center();
+ successDialog.show();
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ }
+ });
+ }
+ });
+ buttons.add(completeButton);
+
+ cancelButton = new Button();
+ cancelButton.addStyleName("importer-cancel-button");
+ cancelButton.setText("Cancel");
+ cancelButton.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ hide();
+ }
+ });
+ buttons.add(cancelButton);
+
+ FlowPanel center = new FlowPanel();
+ Label msg = new Label("Complete import of project '" + project + "'");
+ msg.addStyleName("importer-complete-message");
+ center.add(msg);
+
+ center.add(buttons);
+ add(center);
+
+ setWidget(center);
+ }
+
+ @Override
+ public void center() {
+ super.center();
+ GlobalKey.dialog(this);
+ cancelButton.setFocus(true);
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImportActionPanel.java b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImportActionPanel.java
new file mode 100644
index 0000000..408dd89
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImportActionPanel.java
@@ -0,0 +1,39 @@
+// 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.client;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.FlowPanel;
+
+public class ImportActionPanel extends FlowPanel {
+
+ ImportActionPanel(final String project) {
+ setStyleName("importer-action-panel");
+ add(new Button("Resume...", new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ (new ResumeImportDialog(project)).center();
+ }
+ }));
+ add(new Button("Complete...", new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ (new CompleteImportDialog(project)).center();
+ }
+ }));
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImportInfo.java b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImportInfo.java
new file mode 100644
index 0000000..82978f0
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImportInfo.java
@@ -0,0 +1,26 @@
+// 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.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+public class ImportInfo extends JavaScriptObject {
+ public final native String timestamp() /*-{ return this.timestamp; }-*/;
+ public final native AccountInfo user() /*-{ return this.user; }-*/;
+ public final native String remoteUser() /*-{ return this.remote_user; }-*/;
+
+ protected ImportInfo() {
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImportProjectInfo.java b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImportProjectInfo.java
new file mode 100644
index 0000000..e89f058
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImportProjectInfo.java
@@ -0,0 +1,28 @@
+// 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.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+
+public class ImportProjectInfo extends JavaScriptObject {
+ public final native String from() /*-{ return this.from; }-*/;
+ public final native String name() /*-{ return this.name; }-*/;
+ public final native String parent() /*-{ return this.parent; }-*/;
+ public final native JsArray<ImportInfo> imports() /*-{ return this.imports; }-*/;
+
+ protected ImportProjectInfo() {
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImportProjectListScreen.java b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImportProjectListScreen.java
new file mode 100644
index 0000000..36be3dc
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImportProjectListScreen.java
@@ -0,0 +1,124 @@
+// 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.client;
+
+import com.google.gerrit.client.rpc.NativeMap;
+import com.google.gerrit.client.rpc.Natives;
+import com.google.gerrit.plugin.client.Plugin;
+import com.google.gerrit.plugin.client.rpc.RestApi;
+import com.google.gerrit.plugin.client.screen.Screen;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.FlexTable;
+import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+import com.google.gwt.user.client.ui.InlineHyperlink;
+import com.google.gwt.user.client.ui.VerticalPanel;
+
+import java.util.List;
+
+public class ImportProjectListScreen extends VerticalPanel {
+ static class Factory implements Screen.EntryPoint {
+ @Override
+ public void onLoad(Screen screen) {
+ screen.setPageTitle("Imported Projects");
+ screen.show(new ImportProjectListScreen());
+ }
+ }
+
+ ImportProjectListScreen() {
+ setStyleName("importer-imports-panel");
+
+ new RestApi("config").id("server")
+ .view(Plugin.get().getPluginName(), "projects")
+ .get(new AsyncCallback<NativeMap<ImportProjectInfo>>() {
+ @Override
+ public void onSuccess(NativeMap<ImportProjectInfo> info) {
+ display(info);
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ // never invoked
+ }
+ });
+ }
+
+ private void display(NativeMap<ImportProjectInfo> map) {
+ int columns = 5;
+ FlexTable t = new FlexTable();
+ t.setStyleName("importer-importProjectTable");
+ FlexCellFormatter fmt = t.getFlexCellFormatter();
+ for (int c = 0; c < columns; c++) {
+ fmt.addStyleName(0, c, "dataHeader");
+ fmt.addStyleName(0, c, "topMostCell");
+ }
+ fmt.addStyleName(0, 0, "leftMostCell");
+
+ t.setText(0, 0, "Project Name");
+ t.setText(0, 1, "From");
+ t.setText(0, 2, "Last Import By");
+ t.setText(0, 3, "Last Import At");
+ t.setText(0, 4, "Actions");
+
+ int row = 1;
+ for (final String project : map.keySet()) {
+ ImportProjectInfo info = map.get(project);
+
+ for (int c = 0; c < columns; c++) {
+ fmt.addStyleName(row, c, "dataCell");
+ fmt.addStyleName(row, 0, "leftMostCell");
+ }
+
+ t.setWidget(row, 0, new InlineHyperlink(
+ project, "/x/" + Plugin.get().getName() + "/projects/" + project));
+
+ String srcProjectUrl = projectUrl(info, project);
+ t.setWidget(row, 1, new Anchor(srcProjectUrl, srcProjectUrl));
+
+ List<ImportInfo> importList = Natives.asList(info.imports());
+ if (!importList.isEmpty()) {
+ ImportInfo lastImportInfo = importList.get(importList.size() - 1);
+ t.setText(row, 2, lastImportInfo.user().username());
+ t.setText(row, 3, removeNs(lastImportInfo.timestamp()));
+ } else {
+ t.setText(row, 2, "N/A");
+ t.setText(row, 3, "N/A");
+ }
+
+ t.setWidget(row, 4, new ImportActionPanel(project));
+
+ row++;
+ }
+
+ add(t);
+ }
+
+ public static String removeNs(String timestamp) {
+ return timestamp.substring(0, timestamp.lastIndexOf('.'));
+ }
+
+ public static String projectUrl(ImportProjectInfo info, String project) {
+ return ensureSlash(info.from())
+ + "#/admin/projects/"
+ + (info.name() != null ? info.name() : project);
+ }
+
+ private static String ensureSlash(String in) {
+ if (in != null && !in.endsWith("/")) {
+ return in + "/";
+ }
+ return in;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImportProjectScreen.java b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImportProjectScreen.java
index c24c2c3..f65ae5c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImportProjectScreen.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImportProjectScreen.java
@@ -14,25 +14,22 @@
package com.googlesource.gerrit.plugins.importer.client;
+import static com.googlesource.gerrit.plugins.importer.client.TextBoxUtil.addPasswordTextBox;
+import static com.googlesource.gerrit.plugins.importer.client.TextBoxUtil.addTextBox;
+import static com.googlesource.gerrit.plugins.importer.client.TextBoxUtil.getValue;
+
import com.google.gerrit.plugin.client.Plugin;
import com.google.gerrit.plugin.client.rpc.RestApi;
import com.google.gerrit.plugin.client.screen.Screen;
import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.Scheduler;
-import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
@@ -55,13 +52,13 @@
ImportProjectScreen() {
setStyleName("importer-import-panel");
- fromTxt = addTextBox("From*", "URL of the remote system from where the project should be imported");
- srcNameTxt = addTextBox("Project Name in Source*", "name of project in source system");
- targetNameTxt = addTextBox("Target Project Name", "name of project in target system"
+ fromTxt = addTextBox(this, "From*", "URL of the remote system from where the project should be imported");
+ srcNameTxt = addTextBox(this, "Project Name in Source*", "name of project in source system");
+ targetNameTxt = addTextBox(this, "Target Project Name", "name of project in target system"
+ " (if not specified it is assumed to be the same name as in the source system)");
- userTxt = addTextBox("Remote User*", "user on remote system");
- passTxt = addPasswordTextBox("Password*", "password of remote user");
- parentTxt = addTextBox("Parent", "name of parent project in target system"
+ userTxt = addTextBox(this, "Remote User*", "user on remote system");
+ passTxt = addPasswordTextBox(this, "Password*", "password of remote user");
+ parentTxt = addTextBox(this, "Parent", "name of parent project in target system"
+ "(if not specified it is assumed to be the same parent as in the source system)");
HorizontalPanel buttons = new HorizontalPanel();
@@ -83,77 +80,6 @@
importButton.setEnabled(false);
}
- private TextBox addTextBox(String label, String infoMsg) {
- return addTextBox(label, infoMsg, false);
- }
-
- private TextBox addPasswordTextBox(String label, String infoMsg) {
- return addTextBox(label, infoMsg, true);
- }
-
- private TextBox addTextBox(String label, String infoMsg, boolean isPassword) {
- HorizontalPanel hp = new HorizontalPanel();
- hp.add(new Label(label));
- Image info = new Image(ImporterPlugin.RESOURCES.info());
- info.setTitle(infoMsg);
- hp.add(info);
- hp.add(new Label(":"));
-
- Panel p = new VerticalPanel();
- p.add(hp);
- TextBox tb = createTextBox(isPassword);
- p.add(tb);
- add(p);
- return tb;
- }
-
- private static TextBox createTextBox(boolean isPassword) {
- TextBox tb;
- if (isPassword) {
- tb = new PasswordTextBox() {
- @Override
- public void onBrowserEvent(Event event) {
- super.onBrowserEvent(event);
- handlePaste(this, event);
- }
- };
- } else {
- tb = new TextBox() {
- @Override
- public void onBrowserEvent(Event event) {
- super.onBrowserEvent(event);
- handlePaste(this, event);
- }
- };
- }
- tb.addKeyPressHandler(new KeyPressHandler() {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- event.stopPropagation();
- }
- });
- tb.sinkEvents(Event.ONPASTE);
- tb.setVisibleLength(40);
- return tb;
- }
-
- private static void handlePaste(final TextBox tb, Event event) {
- if (event.getTypeInt() == Event.ONPASTE) {
- Scheduler.get().scheduleDeferred(new ScheduledCommand() {
- @Override
- public void execute() {
- if (getValue(tb).length() != 0) {
- tb.setEnabled(true);
- }
- }
- });
- }
- }
-
- private static String getValue(TextBox tb) {
- return tb.getValue().trim();
- }
-
private void doImport() {
final String targetName = getValue(targetNameTxt).length() == 0
? getValue(srcNameTxt) : getValue(targetNameTxt);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImporterPlugin.java b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImporterPlugin.java
index 88473d9..c1e9973 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImporterPlugin.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ImporterPlugin.java
@@ -25,5 +25,7 @@
@Override
public void onPluginLoad() {
Plugin.get().screen("project", new ImportProjectScreen.Factory());
+ Plugin.get().screen("list", new ImportProjectListScreen.Factory());
+ Plugin.get().screenRegex("projects/(.*)", new ProjectImportsScreen.Factory());
}
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/client/ProjectImportsScreen.java b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ProjectImportsScreen.java
new file mode 100644
index 0000000..874bb35
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ProjectImportsScreen.java
@@ -0,0 +1,119 @@
+// 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.client;
+
+import static com.googlesource.gerrit.plugins.importer.client.ImportProjectListScreen.projectUrl;
+import static com.googlesource.gerrit.plugins.importer.client.ImportProjectListScreen.removeNs;
+
+import com.google.gerrit.client.rpc.Natives;
+import com.google.gerrit.plugin.client.Plugin;
+import com.google.gerrit.plugin.client.rpc.RestApi;
+import com.google.gerrit.plugin.client.screen.Screen;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.FlexTable;
+import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+import java.util.Collections;
+import java.util.List;
+
+public class ProjectImportsScreen extends VerticalPanel {
+ static class Factory implements Screen.EntryPoint {
+ @Override
+ public void onLoad(Screen screen) {
+ screen.setPageTitle("Imports of " + screen.getToken(1));
+ screen.show(new ProjectImportsScreen(screen.getToken(1)));
+ }
+ }
+
+ ProjectImportsScreen(final String project) {
+ setStyleName("importer-import-panel");
+
+ new RestApi("config").id("server").view(Plugin.get().getPluginName(), "projects")
+ .id(project).get(new AsyncCallback<ImportProjectInfo>() {
+ @Override
+ public void onSuccess(ImportProjectInfo importProjectInfo) {
+ display(project, importProjectInfo);
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ // never invoked
+ }
+ });
+ }
+
+ private void display(final String project, ImportProjectInfo info) {
+ MyTable t = new MyTable();
+ t.setStyleName("importer-projectImportInfoTable");
+ t.addRow("Project Name", project);
+ String srcProjectUrl = projectUrl(info, project);
+ t.addRow("From", new Anchor(srcProjectUrl, srcProjectUrl));
+ t.addRow("Parent", info.parent());
+ t.addRow("Actions", new ImportActionPanel(project));
+ add(t);
+
+ add(new Label("Imports:"));
+ int columns = 3;
+ FlexTable importsTable = new FlexTable();
+ importsTable.setStyleName("importer-importProjectTable");
+ FlexCellFormatter fmt = importsTable.getFlexCellFormatter();
+ for (int c = 0; c < columns; c++) {
+ fmt.addStyleName(0, c, "dataHeader");
+ fmt.addStyleName(0, c, "topMostCell");
+ }
+ fmt.addStyleName(0, 0, "leftMostCell");
+
+ importsTable.setText(0, 0, "Timestamp");
+ importsTable.setText(0, 1, "User");
+ importsTable.setText(0, 2, "Remote User");
+ int row = 1;
+ List<ImportInfo> imports = Natives.asList(info.imports());
+ Collections.reverse(imports);
+ for (ImportInfo importInfo : imports) {
+ for (int c = 0; c < columns; c++) {
+ fmt.addStyleName(row, c, "dataCell");
+ fmt.addStyleName(row, 0, "leftMostCell");
+ }
+
+ importsTable.setText(row, 0, removeNs(importInfo.timestamp()));
+ importsTable.setText(row, 1, importInfo.user().username());
+ importsTable.setText(row, 2, importInfo.remoteUser());
+
+ row++;
+ }
+
+ add(importsTable);
+ }
+
+ private static class MyTable extends FlexTable {
+ private static int row = 0;
+
+ private void addRow(String label, String value) {
+ setWidget(row, 0, new Label(label + ":"));
+ setWidget(row, 1, new Label(value));
+ row++;
+ }
+
+ private void addRow(String label, Widget w) {
+ setWidget(row, 0, new Label(label + ":"));
+ setWidget(row, 1, w);
+ row++;
+ }
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/client/ResumeImportDialog.java b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ResumeImportDialog.java
new file mode 100644
index 0000000..73488f4
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ResumeImportDialog.java
@@ -0,0 +1,129 @@
+// 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.client;
+
+import static com.googlesource.gerrit.plugins.importer.client.TextBoxUtil.addPasswordTextBox;
+import static com.googlesource.gerrit.plugins.importer.client.TextBoxUtil.addTextBox;
+import static com.googlesource.gerrit.plugins.importer.client.TextBoxUtil.getValue;
+
+import com.google.gerrit.plugin.client.Plugin;
+import com.google.gerrit.plugin.client.rpc.RestApi;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.DialogBox;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwtexpui.globalkey.client.GlobalKey;
+import com.google.gwtexpui.user.client.AutoCenterDialogBox;
+
+public class ResumeImportDialog extends AutoCenterDialogBox {
+
+ private final Button cancelButton;
+ private final Button resumeButton;
+ private final TextBox userTxt;
+ private final TextBox passTxt;
+
+ public ResumeImportDialog(final String project) {
+ super(/* auto hide */false, /* modal */true);
+ setGlassEnabled(true);
+ setText("Resume Project Import");
+
+ FlowPanel buttons = new FlowPanel();
+
+ resumeButton = new Button();
+ resumeButton.setText("Resume");
+ resumeButton.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ hide();
+
+ ResumeImportProjectInput in = ResumeImportProjectInput.create();
+ in.user(getValue(userTxt));
+ in.pass(getValue(passTxt));
+
+ new RestApi("config").id("server")
+ .view(Plugin.get().getName(), "projects").id(project)
+ .view("resume").put(in, new AsyncCallback<JavaScriptObject>() {
+ @Override
+ public void onSuccess(JavaScriptObject result) {
+ Plugin.get().go("/admin/projects/" + project);
+
+ final DialogBox successDialog = new DialogBox();
+ successDialog.setText("Resume Project Import");
+ successDialog.setAnimationEnabled(true);
+
+ Panel p = new VerticalPanel();
+ p.setStyleName("importer-message-panel");
+ p.add(new Label("The project import was resumed."));
+ Button okButton = new Button("OK");
+ okButton.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ successDialog.hide();
+ }
+ });
+
+ p.add(okButton);
+ successDialog.add(p);
+
+ successDialog.center();
+ successDialog.show();
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ }
+ });
+ }
+ });
+ buttons.add(resumeButton);
+
+ cancelButton = new Button();
+ cancelButton.addStyleName("importer-cancel-button");
+ cancelButton.setText("Cancel");
+ cancelButton.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ hide();
+ }
+ });
+ buttons.add(cancelButton);
+
+ FlowPanel center = new FlowPanel();
+ Label msg = new Label("Resume import of project '" + project + "'");
+ msg.addStyleName("importer-resume-message");
+ center.add(msg);
+
+ userTxt = addTextBox(center, "Remote User*", "user on remote system");
+ passTxt = addPasswordTextBox(center, "Password*", "password of remote user");
+
+ center.add(buttons);
+ add(center);
+
+ setWidget(center);
+ }
+
+ @Override
+ public void center() {
+ super.center();
+ GlobalKey.dialog(this);
+ cancelButton.setFocus(true);
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/client/ResumeImportProjectInput.java b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ResumeImportProjectInput.java
new file mode 100644
index 0000000..1635882
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/client/ResumeImportProjectInput.java
@@ -0,0 +1,29 @@
+// 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.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+public class ResumeImportProjectInput extends JavaScriptObject {
+ final native void user(String u) /*-{ this.user = u; }-*/;
+ final native void pass(String p) /*-{ this.pass = p; }-*/;
+
+ static ResumeImportProjectInput create() {
+ return (ResumeImportProjectInput) createObject();
+ }
+
+ protected ResumeImportProjectInput() {
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/client/TextBoxUtil.java b/src/main/java/com/googlesource/gerrit/plugins/importer/client/TextBoxUtil.java
new file mode 100644
index 0000000..81d56c8
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/client/TextBoxUtil.java
@@ -0,0 +1,101 @@
+// 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.client;
+
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.PasswordTextBox;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.VerticalPanel;
+
+public class TextBoxUtil {
+ public static TextBox addTextBox(Panel p, String label, String infoMsg) {
+ return addTextBox(p, label, infoMsg, false);
+ }
+
+ public static TextBox addPasswordTextBox(Panel p, String label, String infoMsg) {
+ return addTextBox(p, label, infoMsg, true);
+ }
+
+ public static TextBox addTextBox(Panel p, String label, String infoMsg, boolean isPassword) {
+ HorizontalPanel hp = new HorizontalPanel();
+ hp.add(new Label(label));
+ Image info = new Image(ImporterPlugin.RESOURCES.info());
+ info.setTitle(infoMsg);
+ hp.add(info);
+ hp.add(new Label(":"));
+
+ Panel vp = new VerticalPanel();
+ vp.add(hp);
+ TextBox tb = createTextBox(isPassword);
+ vp.add(tb);
+ p.add(vp);
+ return tb;
+ }
+
+ private static TextBox createTextBox(boolean isPassword) {
+ TextBox tb;
+ if (isPassword) {
+ tb = new PasswordTextBox() {
+ @Override
+ public void onBrowserEvent(Event event) {
+ super.onBrowserEvent(event);
+ handlePaste(this, event);
+ }
+ };
+ } else {
+ tb = new TextBox() {
+ @Override
+ public void onBrowserEvent(Event event) {
+ super.onBrowserEvent(event);
+ handlePaste(this, event);
+ }
+ };
+ }
+ tb.addKeyPressHandler(new KeyPressHandler() {
+ @Override
+ public void onKeyPress(KeyPressEvent event) {
+ event.stopPropagation();
+ }
+ });
+ tb.sinkEvents(Event.ONPASTE);
+ tb.setVisibleLength(40);
+ return tb;
+ }
+
+ private static void handlePaste(final TextBox tb, Event event) {
+ if (event.getTypeInt() == Event.ONPASTE) {
+ Scheduler.get().scheduleDeferred(new ScheduledCommand() {
+ @Override
+ public void execute() {
+ if (getValue(tb).length() != 0) {
+ tb.setEnabled(true);
+ }
+ }
+ });
+ }
+ }
+
+ public static String getValue(TextBox tb) {
+ return tb.getValue().trim();
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/importer/public/importer.css b/src/main/java/com/googlesource/gerrit/plugins/importer/public/importer.css
index d3e79ea..5eef977 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/importer/public/importer.css
+++ b/src/main/java/com/googlesource/gerrit/plugins/importer/public/importer.css
@@ -13,10 +13,60 @@
* limitations under the License.
*/
-.importer-import-panel, .importer-message-panel {
+.importer-import-panel,
+.importer-message-panel,
+.importer-imports-panel {
border-spacing: 0px 5px;
}
.importer-importButton {
margin-left: 10px !important;
}
+
+.importer-importProjectTable {
+ border-collapse: separate;
+ border-spacing: 0;
+}
+
+.importer-importProjectTable .leftMostCell {
+ border-left: 1px solid #EEE;
+}
+
+.importer-importProjectTable .topMostCell {
+ border-top: 1px solid #EEE;
+}
+
+.importer-importProjectTable .dataHeader {
+ border: 1px solid #FFF;
+ padding: 2px 6px 1px;
+ background-color: #EEE;
+ font-style: italic;
+ white-space: nowrap;
+ color: textColor;
+}
+
+.importer-importProjectTable .dataCell {
+ padding-left: 5px;
+ padding-right: 5px;
+ border-right: 1px solid #EEE;
+ border-bottom: 1px solid #EEE;
+ vertical-align: middle;
+ height: 20px;
+}
+
+.importer-cancel-button {
+ margin-left: 150px;
+}
+
+.importer-resume-message,
+.importer-complete-message {
+ margin-bottom: 10px;
+}
+
+.importer-action-panel .gwt-Button {
+ margin: 2px;
+}
+
+.importer-projectImportInfoTable {
+ margin-bottom: 10px;
+}
diff --git a/src/main/resources/Documentation/cmd-resume-project.md b/src/main/resources/Documentation/cmd-resume-project.md
index 8221c5f..f5c7ebd 100644
--- a/src/main/resources/Documentation/cmd-resume-project.md
+++ b/src/main/resources/Documentation/cmd-resume-project.md
@@ -11,6 +11,7 @@
ssh -p @SSH_PORT@ @SSH_HOST@ @PLUGIN@ resume-project \
--user <USER> | -u <USER> \
--pass - | <PASS> \
+ [--force] \
[--quiet] \
<NAME>
```
@@ -38,6 +39,11 @@
`--user`
: User on remote system.
+`--force`
+: Whether the resume should be done forcefully. On resume with force
+ changes that have the same last modified timestamp in the source
+ and target system are resumed, otherwise they will be skipped.
+
`--quiet`
: Suppress progress messages.
diff --git a/src/main/resources/Documentation/rest-api-config.md b/src/main/resources/Documentation/rest-api-config.md
index 380e584..5655414 100644
--- a/src/main/resources/Documentation/rest-api-config.md
+++ b/src/main/resources/Documentation/rest-api-config.md
@@ -41,9 +41,6 @@
Lists the imported projects.
-As result a map is returned that maps the project name to
-[ImportProjectInfo](#import-project-info) entity.
-
Caller must be a member of a group that is granted the 'Import'
capability (provided by this plugin) or the 'Administrate Server'
capability.
@@ -58,6 +55,9 @@
GET /config/server/@PLUGIN@~projects/?match=my HTTP/1.0
```
+As result a map is returned that maps the project name to
+[ImportProjectInfo](#import-project-info) entity.
+
#### Response
```
@@ -196,7 +196,7 @@
}
```
-### <a id="complete-project-import"> Resume Project Import
+### <a id="complete-project-import"> Complete Project Import
_DELETE /config/server/@PLUGIN@~projects/[\{project-name\}](../../../Documentation/rest-api-projects.html#project-name)_
Mark a project import as completed.
@@ -272,6 +272,9 @@
* _user_: User on remote system.
* _pass_: Password of remote user.
+* _force_: Whether the resume should be done forcefully. On resume with
+force changes that have the same last modified timestamp in the source
+and target system are resumed, otherwise they will be skipped.
SEE ALSO
diff --git a/src/main/resources/static/resume-project-import.js b/src/main/resources/static/resume-project-import.js
index eaf752d..fdca63c 100644
--- a/src/main/resources/static/resume-project-import.js
+++ b/src/main/resources/static/resume-project-import.js
@@ -17,10 +17,11 @@
var u = c.textfield();
var p = c.textfield();
p.type = 'password';
+ var f = c.checkbox();
var b = c.button('Resume',
{onclick: function(){
c.call(
- {user: u.value, pass: p.value},
+ {user: u.value, pass: p.value, force: f.checked},
function(r) {
c.hide();
window.alert('Import for project "'
@@ -38,6 +39,8 @@
c.br(),
c.prependLabel('Password:', p),
c.br(),
+ c.label(f, 'Force Resume'),
+ c.br(),
c.br(),
b));
}