Allow RestApiView<RevisionResource> to add buttons on patch sets

RestApiViews that accept a RevisionResource may now also describe
buttons that should appear on a patch set. Description is given to
Gerrit by implementing the UiCommand interface and returning useful
information from the getter methods.

Change-Id: I2b9445e73448968f1e3234812cc28b57e9ec7250
This commit is contained in:
Shawn Pearce 2013-05-10 14:18:35 -07:00
parent 878b5a5544
commit b313bfe17f
5 changed files with 176 additions and 1 deletions

View File

@ -19,6 +19,7 @@ import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetInfo; import com.google.gerrit.reviewdb.client.PatchSetInfo;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import java.util.Collections;
import java.util.List; import java.util.List;
public class PatchSetDetail { public class PatchSetDetail {
@ -26,6 +27,7 @@ public class PatchSetDetail {
protected PatchSetInfo info; protected PatchSetInfo info;
protected List<Patch> patches; protected List<Patch> patches;
protected Project.NameKey project; protected Project.NameKey project;
protected List<UiCommandDetail> commands;
public PatchSetDetail() { public PatchSetDetail() {
} }
@ -61,4 +63,15 @@ public class PatchSetDetail {
public void setProject(final Project.NameKey p) { public void setProject(final Project.NameKey p) {
project = p; project = p;
} }
public List<UiCommandDetail> getCommands() {
if (commands != null) {
return commands;
}
return Collections.emptyList();
}
public void setCommands(List<UiCommandDetail> cmds) {
commands = cmds;
}
} }

View File

@ -0,0 +1,24 @@
// Copyright (C) 2013 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.google.gerrit.common.data;
/** Detail necessary to display an action. */
public class UiCommandDetail {
public String id;
public String method;
public String label;
public String title;
public boolean enabled;
}

View File

@ -0,0 +1,30 @@
// Copyright (C) 2013 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.google.gerrit.extensions.webui;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestView;
public interface UiCommand<R extends RestResource> extends RestView<R> {
public static enum Place {
PATCHSET_ACTION_PANEL;
};
Place getPlace();
String getLabel(R resource);
String getTitle(R resource);
boolean isVisible(R resource);
boolean isEnabled(R resource);
}

View File

@ -14,12 +14,15 @@
package com.google.gerrit.client.changes; package com.google.gerrit.client.changes;
import com.google.gerrit.client.Dispatcher; import com.google.gerrit.client.Dispatcher;
import com.google.gerrit.client.ErrorDialog;
import com.google.gerrit.client.FormatUtil; import com.google.gerrit.client.FormatUtil;
import com.google.gerrit.client.Gerrit; import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.GitwebLink; import com.google.gerrit.client.GitwebLink;
import com.google.gerrit.client.download.DownloadPanel; import com.google.gerrit.client.download.DownloadPanel;
import com.google.gerrit.client.patches.PatchUtil; import com.google.gerrit.client.patches.PatchUtil;
import com.google.gerrit.client.rpc.GerritCallback; import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.NativeString;
import com.google.gerrit.client.rpc.RestApi;
import com.google.gerrit.client.ui.AccountLinkPanel; import com.google.gerrit.client.ui.AccountLinkPanel;
import com.google.gerrit.client.ui.ActionDialog; import com.google.gerrit.client.ui.ActionDialog;
import com.google.gerrit.client.ui.CherryPickDialog; import com.google.gerrit.client.ui.CherryPickDialog;
@ -28,6 +31,7 @@ import com.google.gerrit.client.ui.ListenableAccountDiffPreference;
import com.google.gerrit.common.PageLinks; import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.data.ChangeDetail; import com.google.gerrit.common.data.ChangeDetail;
import com.google.gerrit.common.data.PatchSetDetail; import com.google.gerrit.common.data.PatchSetDetail;
import com.google.gerrit.common.data.UiCommandDetail;
import com.google.gerrit.reviewdb.client.AccountDiffPreference; import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand; import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadScheme; import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadScheme;
@ -35,10 +39,13 @@ import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetInfo; import com.google.gerrit.reviewdb.client.PatchSetInfo;
import com.google.gerrit.reviewdb.client.UserIdentity; import com.google.gerrit.reviewdb.client.UserIdentity;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.OpenEvent; import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.event.logical.shared.OpenHandler; import com.google.gwt.event.logical.shared.OpenHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DisclosurePanel; import com.google.gwt.user.client.ui.DisclosurePanel;
@ -169,6 +176,7 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel
if (changeDetail.isCurrentPatchSet(detail)) { if (changeDetail.isCurrentPatchSet(detail)) {
populateActions(detail); populateActions(detail);
} }
populateCommands(detail);
} }
if (detail.getPatchSet().isDraft()) { if (detail.getPatchSet().isDraft()) {
if (changeDetail.canPublish()) { if (changeDetail.canPublish()) {
@ -535,6 +543,45 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel
} }
} }
private void populateCommands(final PatchSetDetail detail) {
for (final UiCommandDetail cmd : detail.getCommands()) {
final Button b = new Button(cmd.label);
b.setEnabled(cmd.enabled);
b.setTitle(cmd.title);
b.addClickHandler(new ClickHandler() {
@Override
public void onClick(final ClickEvent event) {
b.setEnabled(false);
AsyncCallback<NativeString> cb =
new AsyncCallback<NativeString>() {
@Override
public void onFailure(Throwable caught) {
b.setEnabled(true);
new ErrorDialog(caught).center();
}
@Override
public void onSuccess(NativeString msg) {
b.setEnabled(true);
if (msg != null) {
Window.alert(msg.asString());
}
}
};
RestApi api = ChangeApi.revision(patchSet.getId()).view(cmd.id);
if ("PUT".equalsIgnoreCase(cmd.method)) {
api.put(JavaScriptObject.createObject(), cb);
} else if ("DELETE".equalsIgnoreCase(cmd.method)) {
api.delete(cb);
} else {
api.post(JavaScriptObject.createObject(), cb);
}
}
});
actionsPanel.add(b);
}
}
private void populateReviewAction() { private void populateReviewAction() {
final Button b = new Button(Util.C.buttonReview()); final Button b = new Button(Util.C.buttonReview());
b.addClickHandler(new ClickHandler() { b.addClickHandler(new ClickHandler() {

View File

@ -14,20 +14,28 @@
package com.google.gerrit.httpd.rpc.changedetail; package com.google.gerrit.httpd.rpc.changedetail;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.PatchSetDetail; import com.google.gerrit.common.data.PatchSetDetail;
import com.google.gerrit.common.data.UiCommandDetail;
import com.google.gerrit.common.errors.NoSuchEntityException; import com.google.gerrit.common.errors.NoSuchEntityException;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.webui.UiCommand;
import com.google.gerrit.httpd.rpc.Handler; import com.google.gerrit.httpd.rpc.Handler;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountDiffPreference; import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
import com.google.gerrit.reviewdb.client.AccountPatchReview; import com.google.gerrit.reviewdb.client.AccountPatchReview;
import com.google.gerrit.reviewdb.client.Patch; import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchLineComment; import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.change.Revisions;
import com.google.gerrit.server.patch.PatchList; import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListCache; import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListKey; import com.google.gerrit.server.patch.PatchListKey;
@ -44,6 +52,8 @@ import org.eclipse.jgit.lib.ObjectId;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -67,6 +77,7 @@ class PatchSetDetailFactory extends Handler<PatchSetDetail> {
private final ReviewDb db; private final ReviewDb db;
private final PatchListCache patchListCache; private final PatchListCache patchListCache;
private final ChangeControl.Factory changeControlFactory; private final ChangeControl.Factory changeControlFactory;
private final Revisions revisions;
private Project.NameKey projectKey; private Project.NameKey projectKey;
private final PatchSet.Id psIdBase; private final PatchSet.Id psIdBase;
@ -83,6 +94,7 @@ class PatchSetDetailFactory extends Handler<PatchSetDetail> {
PatchSetDetailFactory(final PatchSetInfoFactory psif, final ReviewDb db, PatchSetDetailFactory(final PatchSetInfoFactory psif, final ReviewDb db,
final PatchListCache patchListCache, final PatchListCache patchListCache,
final ChangeControl.Factory changeControlFactory, final ChangeControl.Factory changeControlFactory,
final Revisions revisions,
@Assisted("psIdBase") @Nullable final PatchSet.Id psIdBase, @Assisted("psIdBase") @Nullable final PatchSet.Id psIdBase,
@Assisted("psIdNew") final PatchSet.Id psIdNew, @Assisted("psIdNew") final PatchSet.Id psIdNew,
@Assisted @Nullable final AccountDiffPreference diffPrefs) { @Assisted @Nullable final AccountDiffPreference diffPrefs) {
@ -90,6 +102,7 @@ class PatchSetDetailFactory extends Handler<PatchSetDetail> {
this.db = db; this.db = db;
this.patchListCache = patchListCache; this.patchListCache = patchListCache;
this.changeControlFactory = changeControlFactory; this.changeControlFactory = changeControlFactory;
this.revisions = revisions;
this.psIdBase = psIdBase; this.psIdBase = psIdBase;
this.psIdNew = psIdNew; this.psIdNew = psIdNew;
@ -164,9 +177,57 @@ class PatchSetDetailFactory extends Handler<PatchSetDetail> {
} }
} }
RevisionResource rev =
new RevisionResource(new ChangeResource(control), patchSet);
detail.setCommands(buildCommands(rev));
return detail; return detail;
} }
private List<UiCommandDetail> buildCommands(RevisionResource rev) {
List<UiCommandDetail> all = Lists.newArrayList();
for (DynamicMap.Entry<RestView<RevisionResource>> e : revisions.views()) {
int d = e.getExportName().indexOf('.');
if (d < 0) {
continue;
}
String method = e.getExportName().substring(0, d);
String name = e.getExportName().substring(d + 1);
RestView<RevisionResource> view;
try {
view = e.getProvider().get();
} catch (RuntimeException err) {
log.error(String.format(
"error in view %s.%s",
e.getPluginName(), e.getExportName()), err);
continue;
}
if (!(view instanceof UiCommand)) {
continue;
}
UiCommand<RevisionResource> cmd = (UiCommand<RevisionResource>) view;
if (cmd.getPlace() != UiCommand.Place.PATCHSET_ACTION_PANEL
|| !cmd.isVisible(rev)) {
continue;
}
UiCommandDetail dsc = new UiCommandDetail();
dsc.id = e.getPluginName() + '~' + name;
dsc.method = method;
dsc.label = cmd.getLabel(rev);
dsc.title = cmd.getTitle(rev);
dsc.enabled = cmd.isEnabled(rev);
all.add(dsc);
}
Collections.sort(all, new Comparator<UiCommandDetail>() {
@Override
public int compare(UiCommandDetail a, UiCommandDetail b) {
return a.id.compareTo(b.id);
}
});
return all.isEmpty() ? null : all;
}
private ObjectId toObjectId(final PatchSet.Id psId) throws OrmException, private ObjectId toObjectId(final PatchSet.Id psId) throws OrmException,
NoSuchEntityException { NoSuchEntityException {
final PatchSet ps = db.patchSets().get(psId); final PatchSet ps = db.patchSets().get(psId);