Add branch actions to Projects > Branches view

Enumerate any RestViews which implement UiAction for BranchResource
and generate buttons for these in the web UI.  This allows the Codenvy
plugin to contribute an Edit button next to each branch.

Change-Id: Iaa95ccf560036b928066684a4ca33b6b27b08ab6
This commit is contained in:
Shawn Pearce
2014-03-24 16:21:51 -07:00
parent a9e17d1799
commit d1cf9624c7
8 changed files with 89 additions and 15 deletions

View File

@@ -168,8 +168,9 @@ on a button associated with a server side `UiAction`.
self.onAction(type, view_name, callback);
----
* type: `'change'`, `'revision'` or `'project'`, indicating which type
of resource the `UiAction` was bound to in the server.
* type: `'change'`, `'revision'`, `'project'`, or `'branch'`
indicating which type of resource the `UiAction` was bound to
in the server.
* view_name: string appearing in URLs to name the view. This is the
second argument of the `get()`, `post()`, `put()`, and `delete()`
@@ -837,8 +838,8 @@ on a button associated with a server side `UiAction`.
Gerrit.onAction(type, view_name, callback);
----
* type: `'change'` or `'revision'`, indicating what sort of resource
the `UiAction` was bound to in the server.
* type: `'change'`, `'revision'`, `'project'` or `'branch'` indicating
what sort of resource the `UiAction` was bound to in the server.
* view_name: string appearing in URLs to name the view. This is the
second argument of the `get()`, `post()`, `put()`, and `delete()`

View File

@@ -20,6 +20,7 @@ import com.google.gerrit.client.api.ProjectGlue;
import com.google.gerrit.client.api.RevisionGlue;
import com.google.gerrit.client.changes.ChangeInfo;
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
import com.google.gerrit.client.projects.BranchInfo;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
@@ -28,13 +29,19 @@ import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
public class ActionButton extends Button implements ClickHandler {
private final Project.NameKey project;
private final BranchInfo branch;
private final ChangeInfo change;
private final RevisionInfo revision;
private final ActionInfo action;
private ActionContext ctx;
public ActionButton(Project.NameKey project, ActionInfo action) {
this(project, null, null, action);
this(project, null, null, null, action);
}
public ActionButton(Project.NameKey project, BranchInfo branch,
ActionInfo action) {
this(project, branch, null, null, action);
}
public ActionButton(ChangeInfo change, ActionInfo action) {
@@ -43,11 +50,11 @@ public class ActionButton extends Button implements ClickHandler {
public ActionButton(ChangeInfo change, RevisionInfo revision,
ActionInfo action) {
this(null, change, revision, action);
this(null, null, change, revision, action);
}
private ActionButton(Project.NameKey project, ChangeInfo change,
RevisionInfo revision, ActionInfo action) {
private ActionButton(Project.NameKey project, BranchInfo branch,
ChangeInfo change, RevisionInfo revision, ActionInfo action) {
super(new SafeHtmlBuilder()
.openDiv()
.append(action.label())
@@ -58,6 +65,7 @@ public class ActionButton extends Button implements ClickHandler {
addClickHandler(this);
this.project = project;
this.branch = branch;
this.change = change;
this.revision = revision;
this.action = action;
@@ -75,6 +83,8 @@ public class ActionButton extends Button implements ClickHandler {
RevisionGlue.onAction(change, revision, action, this);
} else if (change != null) {
ChangeGlue.onAction(change, action, this);
} else if (branch != null) {
ProjectGlue.onAction(project, branch, action, this);
} else if (project != null) {
ProjectGlue.onAction(project, action, this);
}

View File

@@ -22,6 +22,8 @@ import com.google.gerrit.client.GitwebLink;
import com.google.gerrit.client.VoidResult;
import com.google.gerrit.client.access.AccessMap;
import com.google.gerrit.client.access.ProjectAccessInfo;
import com.google.gerrit.client.actions.ActionButton;
import com.google.gerrit.client.actions.ActionInfo;
import com.google.gerrit.client.projects.BranchInfo;
import com.google.gerrit.client.projects.ProjectApi;
import com.google.gerrit.client.rpc.GerritCallback;
@@ -387,10 +389,18 @@ public class ProjectBranchesScreen extends ProjectScreen {
table.setText(row, 3, "");
}
FlowPanel actionsPanel = new FlowPanel();
if (c != null) {
table.setWidget(row, 4, new Anchor(c.getLinkName(), false,
actionsPanel.add(new Anchor(c.getLinkName(), false,
c.toBranch(new Branch.NameKey(getProjectKey(), k.ref()))));
}
if (k.actions() != null) {
k.actions().copyKeysIntoChildren("id");
for (ActionInfo a : Natives.asList(k.actions().values())) {
actionsPanel.add(new ActionButton(getProjectKey(), k, a));
}
}
table.setWidget(row, 4, actionsPanel);
final FlexCellFormatter fmt = table.getFlexCellFormatter();
String iconCellStyle = Gerrit.RESOURCES.css().iconCell();

View File

@@ -18,6 +18,7 @@ import com.google.gerrit.client.actions.ActionButton;
import com.google.gerrit.client.actions.ActionInfo;
import com.google.gerrit.client.changes.ChangeInfo;
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
import com.google.gerrit.client.projects.BranchInfo;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.NativeString;
import com.google.gerrit.client.rpc.RestApi;
@@ -136,6 +137,7 @@ public class ActionContext extends JavaScriptObject {
final native void set(ActionInfo a) /*-{ this.action=a; }-*/;
final native void set(ChangeInfo c) /*-{ this.change=c; }-*/;
final native void set(Project.NameKey p) /*-{ this.project=p; }-*/;
final native void set(BranchInfo b) /*-{ this.branch=b }-*/;
final native void set(RevisionInfo r) /*-{ this.revision=r; }-*/;
final native void button(ActionButton b) /*-{ this._b=b; }-*/;

View File

@@ -43,6 +43,7 @@ public class ApiGlue {
change_actions: {},
revision_actions: {},
project_actions: {},
branch_actions: {},
getPluginName: @com.google.gerrit.client.api.ApiGlue::getPluginName(),
injectCss: @com.google.gwt.dom.client.StyleInjector::inject(Ljava/lang/String;),
@@ -71,6 +72,7 @@ public class ApiGlue {
if ('change' == t) this.change_actions[i]=c;
else if ('revision' == t) this.revision_actions[i]=c;
else if ('project' == t) this.project_actions[i]=c;
else if ('branch' == t) this.branch_actions[i]=c;
else if ('screen' == t) _screen(p,t,c);
},
screen: function(r,c){this._screen(this.getPluginName(),r,c)},

View File

@@ -16,18 +16,40 @@ package com.google.gerrit.client.api;
import com.google.gerrit.client.actions.ActionButton;
import com.google.gerrit.client.actions.ActionInfo;
import com.google.gerrit.client.projects.BranchInfo;
import com.google.gerrit.client.projects.ProjectApi;
import com.google.gerrit.client.rpc.RestApi;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.core.client.JavaScriptObject;
public class ProjectGlue {
public static void onAction(
Project.NameKey project,
BranchInfo branch,
ActionInfo action,
ActionButton button) {
RestApi api = ProjectApi.project(project)
.view("branches").id(branch.ref())
.view(action.id());
JavaScriptObject f = branchAction(action.id());
if (f != null) {
ActionContext c = ActionContext.create(api);
c.set(action);
c.set(project);
c.set(branch);
c.button(button);
ApiGlue.invoke(f, c);
} else {
DefaultActions.invoke(project, action, api);
}
}
public static void onAction(
Project.NameKey project,
ActionInfo action,
ActionButton button) {
RestApi api = ProjectApi.project(project).view(action.id());
JavaScriptObject f = get(action.id());
JavaScriptObject f = projectAction(action.id());
if (f != null) {
ActionContext c = ActionContext.create(api);
c.set(action);
@@ -39,10 +61,14 @@ public class ProjectGlue {
}
}
private static final native JavaScriptObject get(String id) /*-{
private static final native JavaScriptObject projectAction(String id) /*-{
return $wnd.Gerrit.project_actions[id];
}-*/;
private static final native JavaScriptObject branchAction(String id) /*-{
return $wnd.Gerrit.branch_actions[id];
}-*/;
private ProjectGlue() {
}
}

View File

@@ -14,6 +14,8 @@
package com.google.gerrit.client.projects;
import com.google.gerrit.client.actions.ActionInfo;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gwt.core.client.JavaScriptObject;
@@ -27,6 +29,7 @@ public class BranchInfo extends JavaScriptObject {
public final native String ref() /*-{ return this.ref; }-*/;
public final native String revision() /*-{ return this.revision; }-*/;
public final native boolean canDelete() /*-{ return this['can_delete'] ? true : false; }-*/;
public final native NativeMap<ActionInfo> actions() /*-{ return this.actions }-*/;
protected BranchInfo() {
}

View File

@@ -16,11 +16,17 @@ package com.google.gerrit.server.project;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gerrit.extensions.common.ActionInfo;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.extensions.webui.UiActions;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
import com.google.inject.util.Providers;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Constants;
@@ -34,14 +40,17 @@ import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class ListBranches implements RestReadView<ProjectResource> {
private final GitRepositoryManager repoManager;
private final DynamicMap<RestView<BranchResource>> branchViews;
@Inject
public ListBranches(GitRepositoryManager repoManager) {
public ListBranches(GitRepositoryManager repoManager,
DynamicMap<RestView<BranchResource>> branchViews) {
this.repoManager = repoManager;
this.branchViews = branchViews;
}
@Override
@@ -136,17 +145,28 @@ public class ListBranches implements RestReadView<ProjectResource> {
return branches;
}
private static BranchInfo createBranchInfo(Ref ref, RefControl refControl,
private BranchInfo createBranchInfo(Ref ref, RefControl refControl,
Set<String> targets) {
return new BranchInfo(ref.getName(),
BranchInfo info = new BranchInfo(ref.getName(),
ref.getObjectId() != null ? ref.getObjectId().name() : null,
!targets.contains(ref.getName()) && refControl.canDelete());
for (UiAction.Description d : UiActions.from(
branchViews,
new BranchResource(refControl.getProjectControl(), info),
Providers.of(refControl.getCurrentUser()))) {
if (info.actions == null) {
info.actions = new TreeMap<>();
}
info.actions.put(d.getId(), new ActionInfo(d));
}
return info;
}
public static class BranchInfo {
public String ref;
public String revision;
public Boolean canDelete;
public Map<String, ActionInfo> actions;
public BranchInfo(String ref, String revision, boolean canDelete) {
this.ref = ref;