Merge topic 'inline-3'

* changes:
  InlineEdit: Expose UiAction to rebase change edit on latest patch set
  InlineEdit: Expose UiAction to publish change edit
  InlineEdit: Expose UiAction to delete change edit
  InlineEdit: Add UiActions support for change edits
  InlineEdit: Implement CS2 integration
  InlineEdit: GET change edit using /changes/{id}/edit REST endpoint
This commit is contained in:
Dave Borowitz 2014-09-09 11:24:41 +00:00 committed by Gerrit Code Review
commit d5836cf0f3
19 changed files with 433 additions and 27 deletions
Documentation
gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common
gerrit-gwtui/src/main/java/com/google/gerrit/client
gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client
gerrit-server/src/main/java/com/google/gerrit/server/edit

@ -168,7 +168,7 @@ on a button associated with a server side `UiAction`.
self.onAction(type, view_name, callback);
----
* type: `'change'`, `'revision'`, `'project'`, or `'branch'`
* type: `'change'`, `'edit'`, `'revision'`, `'project'`, or `'branch'`
indicating which type of resource the `UiAction` was bound to
in the server.
@ -838,8 +838,8 @@ on a button associated with a server side `UiAction`.
Gerrit.onAction(type, view_name, callback);
----
* type: `'change'`, `'revision'`, `'project'` or `'branch'` indicating
what sort of resource the `UiAction` was bound to in the server.
* type: `'change'`, `'edit'`, `'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()`

@ -3826,6 +3826,10 @@ The `EditInfo` entity contains information about a change edit.
|Field Name ||Description
|`commit` ||The commit of change edit as
link:#commit-info[CommitInfo] entity.
|`actions` ||
Actions the caller might be able to perform on this change edit. The
information is a map of view name to link:#action-info[ActionInfo]
entities.
|`files` |optional|
The files of the change edit as a map that maps the file names to
link:#file-info[FileInfo] entities.

@ -18,5 +18,6 @@ import java.util.Map;
public class EditInfo {
public CommitInfo commit;
public Map<String, ActionInfo> actions;
public Map<String, FileInfo> files;
}

@ -16,9 +16,11 @@ package com.google.gerrit.client.actions;
import com.google.gerrit.client.api.ActionContext;
import com.google.gerrit.client.api.ChangeGlue;
import com.google.gerrit.client.api.EditGlue;
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.EditInfo;
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
import com.google.gerrit.client.projects.BranchInfo;
import com.google.gerrit.reviewdb.client.Project;
@ -31,30 +33,37 @@ public class ActionButton extends Button implements ClickHandler {
private final Project.NameKey project;
private final BranchInfo branch;
private final ChangeInfo change;
private final EditInfo edit;
private final RevisionInfo revision;
private final ActionInfo action;
private ActionContext ctx;
public ActionButton(Project.NameKey project, ActionInfo action) {
this(project, null, null, null, action);
this(project, null, null, null, null, action);
}
public ActionButton(Project.NameKey project, BranchInfo branch,
ActionInfo action) {
this(project, branch, null, null, action);
this(project, branch, null, null, null, action);
}
public ActionButton(ChangeInfo change, ActionInfo action) {
this(change, null, action);
this(null, null, change, null, null, action);
}
public ActionButton(ChangeInfo change, RevisionInfo revision,
ActionInfo action) {
this(null, null, change, revision, action);
this(null, null, change, null, revision, action);
}
public ActionButton(ChangeInfo change, EditInfo edit,
ActionInfo action) {
this(null, null, change, edit, null, action);
}
private ActionButton(Project.NameKey project, BranchInfo branch,
ChangeInfo change, RevisionInfo revision, ActionInfo action) {
ChangeInfo change, EditInfo edit, RevisionInfo revision,
ActionInfo action) {
super(new SafeHtmlBuilder()
.openDiv()
.append(action.label())
@ -67,6 +76,7 @@ public class ActionButton extends Button implements ClickHandler {
this.project = project;
this.branch = branch;
this.change = change;
this.edit = edit;
this.revision = revision;
this.action = action;
}
@ -81,6 +91,8 @@ public class ActionButton extends Button implements ClickHandler {
if (revision != null) {
RevisionGlue.onAction(change, revision, action, this);
} else if (edit != null) {
EditGlue.onAction(change, edit, action, this);
} else if (change != null) {
ChangeGlue.onAction(change, action, this);
} else if (branch != null) {

@ -17,6 +17,7 @@ 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.changes.ChangeInfo;
import com.google.gerrit.client.changes.ChangeInfo.EditInfo;
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
import com.google.gerrit.client.projects.BranchInfo;
import com.google.gerrit.client.rpc.GerritCallback;
@ -137,6 +138,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(EditInfo e) /*-{ this.edit=e; }-*/;
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; }-*/;

@ -41,6 +41,7 @@ public class ApiGlue {
plugins: {},
screens: {},
change_actions: {},
edit_actions: {},
revision_actions: {},
project_actions: {},
branch_actions: {},
@ -71,6 +72,7 @@ public class ApiGlue {
_onAction: function (p,t,n,c) {
var i = p+'~'+n;
if ('change' == t) this.change_actions[i]=c;
else if ('edit' == t) this.edit_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;

@ -0,0 +1,54 @@
// Copyright (C) 2014 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.client.api;
import com.google.gerrit.client.actions.ActionButton;
import com.google.gerrit.client.actions.ActionInfo;
import com.google.gerrit.client.changes.ChangeApi;
import com.google.gerrit.client.changes.ChangeInfo;
import com.google.gerrit.client.changes.ChangeInfo.EditInfo;
import com.google.gerrit.client.rpc.RestApi;
import com.google.gwt.core.client.JavaScriptObject;
public class EditGlue {
public static void onAction(
ChangeInfo change,
EditInfo edit,
ActionInfo action,
ActionButton button) {
RestApi api = ChangeApi.edit(
change.legacy_id().get())
.view(action.id());
JavaScriptObject f = get(action.id());
if (f != null) {
ActionContext c = ActionContext.create(api);
c.set(action);
c.set(change);
c.set(edit);
c.button(button);
ApiGlue.invoke(f, c);
} else {
DefaultActions.invoke(change, action, api);
}
}
private static final native JavaScriptObject get(String id) /*-{
return $wnd.Gerrit.edit_actions[id];
}-*/;
private EditGlue() {
}
}

@ -19,6 +19,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.CommitInfo;
import com.google.gerrit.client.changes.ChangeInfo.EditInfo;
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.reviewdb.client.Change;
@ -46,6 +47,9 @@ class Actions extends Composite {
@UiField Button cherrypick;
@UiField Button deleteChange;
@UiField Button deleteRevision;
@UiField Button deleteEdit;
@UiField Button publishEdit;
@UiField Button rebaseEdit;
@UiField Button publish;
@UiField Button rebase;
@UiField Button revert;
@ -84,6 +88,7 @@ class Actions extends Composite {
initChangeActions(info, hasUser);
initRevisionActions(info, revInfo, hasUser);
initEditActions(info, info.edit(), hasUser);
}
private void initChangeActions(ChangeInfo info, boolean hasUser) {
@ -103,6 +108,26 @@ class Actions extends Composite {
}
}
private void initEditActions(ChangeInfo info, EditInfo editInfo,
boolean hasUser) {
if (!info.has_edit() || !info.current_revision().equals(editInfo.name())) {
return;
}
NativeMap<ActionInfo> actions = editInfo.has_actions()
? editInfo.actions()
: NativeMap.<ActionInfo> create();
actions.copyKeysIntoChildren("id");
if (hasUser) {
a2b(actions, "/", deleteEdit);
a2b(actions, "publish", publishEdit);
a2b(actions, "rebase", rebaseEdit);
for (String id : filterNonCore(actions)) {
add(new ActionButton(info, editInfo, actions.get(id)));
}
}
}
private void initRevisionActions(ChangeInfo info, RevisionInfo revInfo,
boolean hasUser) {
NativeMap<ActionInfo> actions = revInfo.has_actions()
@ -164,6 +189,21 @@ class Actions extends Composite {
DraftActions.publish(changeId, revision);
}
@UiHandler("deleteEdit")
void onDeleteEdit(ClickEvent e) {
EditActions.deleteEdit(changeId);
}
@UiHandler("publishEdit")
void onPublishEdit(ClickEvent e) {
EditActions.publishEdit(changeId);
}
@UiHandler("rebaseEdit")
void onRebaseEdit(ClickEvent e) {
EditActions.rebaseEdit(changeId);
}
@UiHandler("deleteRevision")
void onDeleteRevision(ClickEvent e) {
DraftActions.delete(changeId, revision);

@ -92,6 +92,15 @@ limitations under the License.
<g:Button ui:field='publish' styleName='' visible='false'>
<div><ui:msg>Publish</ui:msg></div>
</g:Button>
<g:Button ui:field='deleteEdit' styleName='' visible='false'>
<div><ui:msg>Delete Edit</ui:msg></div>
</g:Button>
<g:Button ui:field='publishEdit' styleName='' visible='false'>
<div><ui:msg>Publish Edit</ui:msg></div>
</g:Button>
<g:Button ui:field='rebaseEdit' styleName='' visible='false'>
<div><ui:msg>Rebase Edit</ui:msg></div>
</g:Button>
<g:Button ui:field='abandon' styleName='{style.red}' visible='false'>
<div><ui:msg>Abandon</ui:msg></div>

@ -17,7 +17,7 @@ package com.google.gerrit.client.change;
import com.google.gwt.i18n.client.Messages;
public interface ChangeMessages extends Messages {
String patchSets(int currentlyViewedPatchSet, int currentPatchSet);
String patchSets(String currentlyViewedPatchSet, String currentPatchSet);
String changeWithNoRevisions(int changeId);
String relatedChanges(int count);
String relatedChanges(String count);

@ -24,6 +24,7 @@ import com.google.gerrit.client.api.ChangeGlue;
import com.google.gerrit.client.changes.ChangeApi;
import com.google.gerrit.client.changes.ChangeInfo;
import com.google.gerrit.client.changes.ChangeInfo.CommitInfo;
import com.google.gerrit.client.changes.ChangeInfo.EditInfo;
import com.google.gerrit.client.changes.ChangeInfo.MessageInfo;
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
import com.google.gerrit.client.changes.ChangeList;
@ -125,6 +126,7 @@ public class ChangeScreen2 extends Screen {
private String revision;
private ChangeInfo changeInfo;
private CommentLinkProcessor commentLinkProcessor;
private EditInfo edit;
private KeyCommandSet keysNavigation;
private KeyCommandSet keysAction;
@ -196,13 +198,24 @@ public class ChangeScreen2 extends Screen {
@Override
protected void onLoad() {
super.onLoad();
loadChangeInfo(true, new GerritCallback<ChangeInfo>() {
@Override
public void onSuccess(ChangeInfo info) {
info.init();
loadConfigInfo(info, base);
}
});
CallbackGroup group = new CallbackGroup();
if (Gerrit.isSignedIn()) {
ChangeApi.editWithFiles(changeId.get(), group.add(
new GerritCallback<EditInfo>() {
@Override
public void onSuccess(EditInfo result) {
edit = result;
}
}));
}
loadChangeInfo(true, group.addFinal(
new GerritCallback<ChangeInfo>() {
@Override
public void onSuccess(ChangeInfo info) {
info.init();
loadConfigInfo(info, base);
}
}));
}
void loadChangeInfo(boolean fg, AsyncCallback<ChangeInfo> cb) {
@ -340,17 +353,17 @@ public class ChangeScreen2 extends Screen {
}
private void initRevisionsAction(ChangeInfo info, String revision) {
int currentPatchSet;
String currentPatchSet;
if (info.current_revision() != null
&& info.revisions().containsKey(info.current_revision())) {
currentPatchSet = info.revision(info.current_revision())._number();
currentPatchSet = info.revision(info.current_revision()).id();
} else {
JsArray<RevisionInfo> revList = info.revisions().values();
RevisionInfo.sortRevisionInfoByNumber(revList);
currentPatchSet = revList.get(revList.length() - 1)._number();
currentPatchSet = revList.get(revList.length() - 1).id();
}
int currentlyViewedPatchSet = info.revision(revision)._number();
String currentlyViewedPatchSet = info.revision(revision).id();
patchSetsText.setInnerText(Resources.M.patchSets(
currentlyViewedPatchSet, currentPatchSet));
patchSetsAction = new PatchSetsAction(
@ -551,11 +564,55 @@ public class ChangeScreen2 extends Screen {
private void loadConfigInfo(final ChangeInfo info, final String base) {
info.revisions().copyKeysIntoChildren("name");
if (edit != null) {
edit.set_name(edit.commit().commit());
info.set_edit(edit);
if (edit.has_files()) {
edit.files().copyKeysIntoChildren("path");
}
info.revisions().put(edit.name(), RevisionInfo.fromEdit(edit));
JsArray<RevisionInfo> list = info.revisions().values();
// Edit is converted to a regular revision (with number = 0) and
// added to the list of revisions. Additionally under certain
// circumstances change edit is assigned to be the current revision
// and is selected to be shown on the change screen.
// We have two different strategies to assign edit to the current ps:
// 1. revision == null: no revision is selected, so use the edit only
// if it is based on the latest patch set
// 2. edit was selected explicitly from ps drop down:
// use the edit regardless of which patch set it is based on
if (revision == null) {
RevisionInfo.sortRevisionInfoByNumber(list);
RevisionInfo rev = list.get(list.length() - 1);
if (rev.is_edit()) {
info.set_current_revision(rev.name());
}
} else if (revision.equals("edit") || revision.equals("0")) {
for (int i = 0; i < list.length(); i++) {
RevisionInfo r = list.get(i);
if (r.is_edit()) {
info.set_current_revision(r.name());
break;
}
}
}
}
final RevisionInfo rev = resolveRevisionToDisplay(info);
final RevisionInfo b = resolveRevisionOrPatchSetId(info, base, null);
CallbackGroup group = new CallbackGroup();
loadDiff(b, rev, myLastReply(info), group);
if (rev.is_edit()) {
NativeMap<JsArray<CommentInfo>> emptyComment = NativeMap.create();
files.setRevisions(
b != null ? new PatchSet.Id(changeId, b._number()) : null,
new PatchSet.Id(changeId, rev._number()));
files.setValue(info.edit().files(), myLastReply(info),
emptyComment,
emptyComment);
} else {
loadDiff(b, rev, myLastReply(info), group);
}
loadCommit(rev, group);
if (loaded) {
@ -671,6 +728,9 @@ public class ChangeScreen2 extends Screen {
}
private void loadCommit(final RevisionInfo rev, CallbackGroup group) {
if (rev.is_edit()) {
return;
}
ChangeApi.revision(changeId.get(), rev.name())
.view("commit")
.get(group.add(new AsyncCallback<CommitInfo>() {
@ -772,10 +832,14 @@ public class ChangeScreen2 extends Screen {
private void renderChangeInfo(ChangeInfo info) {
changeInfo = info;
lastDisplayedUpdate = info.updated();
RevisionInfo revisionInfo = info.revision(revision);
boolean current = info.status().isOpen()
&& revision.equals(info.current_revision());
&& revision.equals(info.current_revision())
&& !revisionInfo.is_edit();
if (!current && info.status() == Change.Status.NEW) {
if (revisionInfo.is_edit()) {
statusText.setInnerText(Util.C.changeEdit());
} else if (!current && info.status() == Change.Status.NEW) {
statusText.setInnerText(Util.C.notCurrent());
labels.setVisible(false);
} else {
@ -882,7 +946,7 @@ public class ChangeScreen2 extends Screen {
for (int i = list.length() - 1; i >= 0; i--) {
RevisionInfo r = list.get(i);
diffBase.addItem(
r._number() + ": " + r.name().substring(0, 6),
r.id() + ": " + r.name().substring(0, 6),
r.name());
if (r.name().equals(revision)) {
SelectElement.as(diffBase.getElement()).getOptions()

@ -0,0 +1,56 @@
// Copyright (C) 2014 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.client.change;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.ChangeApi;
import com.google.gerrit.client.changes.SubmitFailureDialog;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gwt.core.client.JavaScriptObject;
public class EditActions {
static void deleteEdit(Change.Id id) {
ChangeApi.deleteEdit(id.get(), cs(id));
}
static void publishEdit(Change.Id id) {
ChangeApi.publishEdit(id.get(), cs(id));
}
static void rebaseEdit(Change.Id id) {
ChangeApi.rebaseEdit(id.get(), cs(id));
}
public static GerritCallback<JavaScriptObject> cs(
final Change.Id id) {
return new GerritCallback<JavaScriptObject>() {
public void onSuccess(JavaScriptObject result) {
Gerrit.display(PageLinks.toChange(id));
}
public void onFailure(Throwable err) {
if (SubmitFailureDialog.isConflict(err)) {
new SubmitFailureDialog(err.getMessage()).center();
Gerrit.display(PageLinks.toChange(id));
} else {
super.onFailure(err);
}
}
};
}
}

@ -19,8 +19,11 @@ import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.ChangeApi;
import com.google.gerrit.client.changes.ChangeInfo;
import com.google.gerrit.client.changes.ChangeInfo.CommitInfo;
import com.google.gerrit.client.changes.ChangeInfo.EditInfo;
import com.google.gerrit.client.changes.ChangeInfo.RevisionInfo;
import com.google.gerrit.client.changes.ChangeList;
import com.google.gerrit.client.rpc.CallbackGroup;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.rpc.RestApi;
@ -57,6 +60,7 @@ class PatchSetsBox extends Composite {
private static final String OPEN;
private static final HyperlinkImpl link = GWT.create(HyperlinkImpl.class);
private EditInfo edit;
static {
OPEN = DOM.createUniqueId().replace('-', '_');
@ -116,14 +120,31 @@ class PatchSetsBox extends Composite {
@Override
protected void onLoad() {
if (!loaded) {
CallbackGroup group = new CallbackGroup();
if (Gerrit.isSignedIn()) {
// TODO(davido): It shouldn't be necessary to make this call.
// PatchSetsBox is constructed via PatchSetsAction which is
// only initialized by CS2 after loading the EditInfo in that path.
ChangeApi.edit(changeId.get(), group.add(
new GerritCallback<EditInfo>() {
@Override
public void onSuccess(EditInfo result) {
edit = result;
}
}));
}
RestApi call = ChangeApi.detail(changeId.get());
ChangeList.addOptions(call, EnumSet.of(
ListChangesOption.ALL_COMMITS,
ListChangesOption.ALL_REVISIONS,
ListChangesOption.DRAFT_COMMENTS));
call.get(new AsyncCallback<ChangeInfo>() {
call.get(group.addFinal(new AsyncCallback<ChangeInfo>() {
@Override
public void onSuccess(ChangeInfo result) {
if (edit != null) {
edit.set_name(edit.commit().commit());
result.revisions().put(edit.name(), RevisionInfo.fromEdit(edit));
}
render(result.revisions());
loaded = true;
}
@ -131,7 +152,7 @@ class PatchSetsBox extends Composite {
@Override
public void onFailure(Throwable caught) {
}
});
}));
}
}

@ -14,6 +14,7 @@
package com.google.gerrit.client.changes;
import com.google.gerrit.client.changes.ChangeInfo.EditInfo;
import com.google.gerrit.client.changes.ChangeInfo.IncludedInInfo;
import com.google.gerrit.client.rpc.NativeString;
import com.google.gerrit.client.rpc.RestApi;
@ -68,6 +69,18 @@ public class ChangeApi {
return call(id, "detail");
}
public static void edit(int id, AsyncCallback<EditInfo> cb) {
edit(id).get(cb);
}
public static void editWithFiles(int id, AsyncCallback<EditInfo> cb) {
edit(id).addParameter("list", true).get(cb);
}
public static RestApi edit(int id) {
return change(id).view("edit");
}
public static void includedIn(int id, AsyncCallback<IncludedInInfo> cb) {
call(id, "in").get(cb);
}
@ -142,6 +155,23 @@ public class ChangeApi {
revision(id, commit).delete(cb);
}
/** Delete change edit. */
public static void deleteEdit(int id, AsyncCallback<JavaScriptObject> cb) {
edit(id).delete(cb);
}
/** Publish change edit. */
public static void publishEdit(int id, AsyncCallback<JavaScriptObject> cb) {
JavaScriptObject in = JavaScriptObject.createObject();
change(id).view("publish_edit").post(in, cb);
}
/** Rebase change edit on latest patch set. */
public static void rebaseEdit(int id, AsyncCallback<JavaScriptObject> cb) {
JavaScriptObject in = JavaScriptObject.createObject();
change(id).view("rebase_edit").post(in, cb);
}
/** Rebase a revision onto the branch tip. */
public static void rebase(int id, String commit, AsyncCallback<ChangeInfo> cb) {
JavaScriptObject in = JavaScriptObject.createObject();

@ -25,6 +25,7 @@ public interface ChangeConstants extends Constants {
String readyToSubmit();
String mergeConflict();
String notCurrent();
String changeEdit();
String myDashboardTitle();
String unknownDashboardTitle();

@ -6,6 +6,7 @@ statusLongDraft = Draft
readyToSubmit = Ready to Submit
mergeConflict = Merge Conflict
notCurrent = Not Current
changeEdit = Change Edit
starredHeading = Starred Changes
watchedHeading = Open Changes of Watched Projects

@ -24,6 +24,7 @@ import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.common.data.LabelValue;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
@ -99,9 +100,13 @@ public class ChangeInfo extends JavaScriptObject {
public final native NativeMap<LabelInfo> all_labels() /*-{ return this.labels; }-*/;
public final native LabelInfo label(String n) /*-{ return this.labels[n]; }-*/;
public final native String current_revision() /*-{ return this.current_revision; }-*/;
public final native void set_current_revision(String r) /*-{ this.current_revision = r; }-*/;
public final native NativeMap<RevisionInfo> revisions() /*-{ return this.revisions; }-*/;
public final native RevisionInfo revision(String n) /*-{ return this.revisions[n]; }-*/;
public final native JsArray<MessageInfo> messages() /*-{ return this.messages; }-*/;
public final native void set_edit(EditInfo edit) /*-{ this.edit = edit; }-*/;
public final native EditInfo edit() /*-{ return this.edit; }-*/;
public final native boolean has_edit() /*-{ return this.hasOwnProperty('edit') }-*/;
public final native boolean has_permitted_labels()
/*-{ return this.hasOwnProperty('permitted_labels') }-*/;
@ -206,8 +211,12 @@ public class ChangeInfo extends JavaScriptObject {
public static class EditInfo extends JavaScriptObject {
public final native String name() /*-{ return this.name; }-*/;
public final native String set_name(String n) /*-{ this.name = n; }-*/;
public final native CommitInfo commit() /*-{ return this.commit; }-*/;
public final native boolean has_actions() /*-{ return this.hasOwnProperty('actions') }-*/;
public final native NativeMap<ActionInfo> actions() /*-{ return this.actions; }-*/;
public final native boolean has_files() /*-{ return this.hasOwnProperty('files') }-*/;
public final native NativeMap<FileInfo> files() /*-{ return this.files; }-*/;
@ -216,10 +225,21 @@ public class ChangeInfo extends JavaScriptObject {
}
public static class RevisionInfo extends JavaScriptObject {
public static RevisionInfo fromEdit(EditInfo edit) {
RevisionInfo revisionInfo = createObject().cast();
revisionInfo.takeFromEdit(edit);
return revisionInfo;
}
private final native void takeFromEdit(EditInfo edit) /*-{
this._number = 0;
this.name = edit.name;
this.commit = edit.commit;
}-*/;
public final native int _number() /*-{ return this._number; }-*/;
public final native String name() /*-{ return this.name; }-*/;
public final native boolean draft() /*-{ return this.draft || false; }-*/;
public final native boolean has_draft_comments() /*-{ return this.has_draft_comments || false; }-*/;
public final native boolean is_edit() /*-{ return this._number == 0; }-*/;
public final native CommitInfo commit() /*-{ return this.commit; }-*/;
public final native void set_commit(CommitInfo c) /*-{ this.commit = c; }-*/;
@ -234,14 +254,57 @@ public class ChangeInfo extends JavaScriptObject {
public final native JsArray<WebLinkInfo> web_links() /*-{ return this.web_links; }-*/;
public static void sortRevisionInfoByNumber(JsArray<RevisionInfo> list) {
final int parent_number = findEditParent(list);
Collections.sort(Natives.asList(list), new Comparator<RevisionInfo>() {
@Override
public int compare(RevisionInfo a, RevisionInfo b) {
return a._number() - b._number();
int a_number = a._number();
int b_number = b._number();
if (a_number == 0) {
if (b_number == parent_number + 1) {
a_number = parent_number;
} else {
a_number = parent_number + 1;
}
}
if (b_number == 0) {
if (a_number == parent_number + 1) {
b_number = parent_number;
} else {
b_number = parent_number + 1;
}
}
return a_number - b_number;
}
});
}
private static int findEditParent(JsArray<RevisionInfo> list) {
for (int i = 0; i < list.length(); i++) {
// edit under revisions?
RevisionInfo editInfo = list.get(i);
if (editInfo._number() == 0) {
// parent commit is parent patch set revision
CommitInfo commit = editInfo.commit().parents().get(0);
String parentRevision = commit.commit();
// find parent
for (int j = 0; j < list.length(); j++) {
RevisionInfo parentInfo = list.get(j);
String name = parentInfo.name();
if (name.equals(parentRevision)) {
// found parent pacth set number
return parentInfo._number();
}
}
}
}
return -1;
}
public final String id() {
return PatchSet.Id.toId(_number());
}
protected RevisionInfo () {
}
}

@ -112,6 +112,16 @@ public final class PatchSet {
}
return Integer.parseInt(ref.substring(ps));
}
public String getId() {
return toId(patchSetId);
}
public static String toId(int number) {
return number == 0
? "edit"
: String.valueOf(number);
}
}
@Column(id = 1, name = Column.NONE)

@ -15,20 +15,27 @@
package com.google.gerrit.server.edit;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gerrit.extensions.common.ActionInfo;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.common.EditInfo;
import com.google.gerrit.extensions.webui.PrivateInternals_UiActionDescription;
import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.CommonConverters;
import com.google.inject.Singleton;
import org.eclipse.jgit.revwalk.RevCommit;
import java.io.IOException;
import java.util.Map;
@Singleton
public class ChangeEditJson {
public EditInfo toEditInfo(ChangeEdit edit) throws IOException {
EditInfo out = new EditInfo();
out.commit = fillCommit(edit.getEditCommit());
out.actions = fillActions(edit);
return out;
}
@ -48,4 +55,33 @@ public class ChangeEditJson {
return commit;
}
private static Map<String, ActionInfo> fillActions(ChangeEdit edit) {
Map<String, ActionInfo> actions = Maps.newTreeMap();
UiAction.Description descr = new UiAction.Description();
PrivateInternals_UiActionDescription.setId(descr, "/");
PrivateInternals_UiActionDescription.setMethod(descr, "DELETE");
descr.setTitle("Delete edit");
actions.put(descr.getId(), new ActionInfo(descr));
// Only expose publish action when the edit is on top of current ps
PatchSet.Id current = edit.getChange().currentPatchSetId();
PatchSet basePs = edit.getBasePatchSet();
if (basePs.getId().equals(current)) {
descr = new UiAction.Description();
PrivateInternals_UiActionDescription.setId(descr, "publish");
PrivateInternals_UiActionDescription.setMethod(descr, "POST");
descr.setTitle("Publish edit");
actions.put(descr.getId(), new ActionInfo(descr));
} else {
descr = new UiAction.Description();
PrivateInternals_UiActionDescription.setId(descr, "rebase");
PrivateInternals_UiActionDescription.setMethod(descr, "POST");
descr.setTitle("Rebase edit");
actions.put(descr.getId(), new ActionInfo(descr));
}
return actions;
}
}