diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetDetail.java index 39f5cb0aed..788a354f08 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetDetail.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetDetail.java @@ -19,6 +19,7 @@ import com.google.gerrit.reviewdb.client.PatchSet; import com.google.gerrit.reviewdb.client.PatchSetInfo; import com.google.gerrit.reviewdb.client.Project; +import java.util.Collections; import java.util.List; public class PatchSetDetail { @@ -26,6 +27,7 @@ public class PatchSetDetail { protected PatchSetInfo info; protected List patches; protected Project.NameKey project; + protected List commands; public PatchSetDetail() { } @@ -61,4 +63,15 @@ public class PatchSetDetail { public void setProject(final Project.NameKey p) { project = p; } + + public List getCommands() { + if (commands != null) { + return commands; + } + return Collections.emptyList(); + } + + public void setCommands(List cmds) { + commands = cmds; + } } diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/UiCommandDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/UiCommandDetail.java new file mode 100644 index 0000000000..cd0118605f --- /dev/null +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/UiCommandDetail.java @@ -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; +} diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/UiCommand.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/UiCommand.java new file mode 100644 index 0000000000..b4466d113f --- /dev/null +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/UiCommand.java @@ -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 extends RestView { + 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); +} diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java index 0d7472f14d..23a07822f3 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java @@ -14,12 +14,15 @@ package com.google.gerrit.client.changes; import com.google.gerrit.client.Dispatcher; +import com.google.gerrit.client.ErrorDialog; import com.google.gerrit.client.FormatUtil; import com.google.gerrit.client.Gerrit; import com.google.gerrit.client.GitwebLink; import com.google.gerrit.client.download.DownloadPanel; import com.google.gerrit.client.patches.PatchUtil; 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.ActionDialog; 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.data.ChangeDetail; 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.AccountGeneralPreferences.DownloadCommand; 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.PatchSetInfo; 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.ClickHandler; import com.google.gwt.event.logical.shared.OpenEvent; 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.Button; import com.google.gwt.user.client.ui.DisclosurePanel; @@ -169,6 +176,7 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel if (changeDetail.isCurrentPatchSet(detail)) { populateActions(detail); } + populateCommands(detail); } if (detail.getPatchSet().isDraft()) { 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 cb = + new AsyncCallback() { + @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() { final Button b = new Button(Util.C.buttonReview()); b.addClickHandler(new ClickHandler() { diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java index 8e81dd377d..152673d198 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java @@ -14,20 +14,28 @@ 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.UiCommandDetail; 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.reviewdb.client.Account; 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.Patch; import com.google.gerrit.reviewdb.client.PatchLineComment; import com.google.gerrit.reviewdb.client.PatchSet; 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.server.CurrentUser; 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.PatchListCache; import com.google.gerrit.server.patch.PatchListKey; @@ -44,6 +52,8 @@ import org.eclipse.jgit.lib.ObjectId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -67,6 +77,7 @@ class PatchSetDetailFactory extends Handler { private final ReviewDb db; private final PatchListCache patchListCache; private final ChangeControl.Factory changeControlFactory; + private final Revisions revisions; private Project.NameKey projectKey; private final PatchSet.Id psIdBase; @@ -83,6 +94,7 @@ class PatchSetDetailFactory extends Handler { PatchSetDetailFactory(final PatchSetInfoFactory psif, final ReviewDb db, final PatchListCache patchListCache, final ChangeControl.Factory changeControlFactory, + final Revisions revisions, @Assisted("psIdBase") @Nullable final PatchSet.Id psIdBase, @Assisted("psIdNew") final PatchSet.Id psIdNew, @Assisted @Nullable final AccountDiffPreference diffPrefs) { @@ -90,6 +102,7 @@ class PatchSetDetailFactory extends Handler { this.db = db; this.patchListCache = patchListCache; this.changeControlFactory = changeControlFactory; + this.revisions = revisions; this.psIdBase = psIdBase; this.psIdNew = psIdNew; @@ -164,9 +177,57 @@ class PatchSetDetailFactory extends Handler { } } + RevisionResource rev = + new RevisionResource(new ChangeResource(control), patchSet); + detail.setCommands(buildCommands(rev)); return detail; } + private List buildCommands(RevisionResource rev) { + List all = Lists.newArrayList(); + for (DynamicMap.Entry> 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 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 cmd = (UiCommand) 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() { + @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, NoSuchEntityException { final PatchSet ps = db.patchSets().get(psId);