Merge branch 'stable-2.9'

* stable-2.9:
  Enable automatic close changes on 'refs/meta/config'
  Implement pagination in group list screen
  Add option 'n' and 'S' to groups REST API to limit group results.
  Do not refresh group list if filter did not change
  Don't show the submit button for draft patch sets
  Prevent draft changes from being abandoned
  Allow plugins to refresh Gerrit's menu bar

Change-Id: Iedfdea0105829119dfe5e2d729bb53b55b233d74
This commit is contained in:
David Pursehouse 2014-04-17 14:31:33 +09:00
commit b2eb1260bb
14 changed files with 171 additions and 19 deletions

View File

@ -873,6 +873,10 @@ Gerrit.screen(pattern, callback);
=== Gerrit.refresh()
Redisplays the current web UI view, refreshing all information.
[[Gerrit_refreshMenuBar]]
=== Gerrit.refreshMenuBar()
Refreshes Gerrit's menu bar.
[[Gerrit_url]]
=== Gerrit.url()
Returns the URL of the Gerrit Code Review server. If invoked with

View File

@ -161,6 +161,24 @@ returned.
}
----
[[group-limit]]
==== Group Limit
The `/groups/` URL also accepts a limit integer in the `n` parameter.
This limits the results to show `n` groups.
Query the first 25 groups in group list.
----
GET /groups/?n=25 HTTP/1.0
----
The `/groups/` URL also accepts a start integer in the `S` parameter.
The results will skip `S` groups from group list.
Query 25 groups starting from index 50.
----
GET /groups/?n=25&S=50 HTTP/1.0
----
[[get-group]]
=== Get Group
--

View File

@ -137,4 +137,7 @@ public interface AdminConstants extends Constants {
String pagedProjectListPrev();
String pagedProjectListNext();
String pagedGroupListPrev();
String pagedGroupListNext();
}

View File

@ -102,6 +102,9 @@ errorNoGitRepository = No Git Repository
pagedProjectListPrev = ⇦Prev
pagedProjectListNext = Next⇨
pagedGroupListPrev = ⇦Prev
pagedGroupListNext = Next⇨
addPermission = Add Permission ...
# Permission Names

View File

@ -21,8 +21,10 @@ import com.google.gerrit.client.groups.GroupMap;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.ui.AccountScreen;
import com.google.gerrit.client.ui.FilteredUserInterface;
import com.google.gerrit.client.ui.Hyperlink;
import com.google.gerrit.client.ui.IgnoreOutdatedFilterResultsCallbackWrapper;
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
@ -32,11 +34,16 @@ import com.google.gwt.user.client.ui.Label;
import com.google.gwtexpui.globalkey.client.NpTextBox;
public class GroupListScreen extends AccountScreen implements FilteredUserInterface {
private Hyperlink prev;
private Hyperlink next;
private GroupTable groups;
private NpTextBox filterTxt;
private String subname;
private String subname = "";
private int startPosition;
private int pageSize;
public GroupListScreen() {
configurePageSize();
}
public GroupListScreen(String params) {
@ -49,6 +56,22 @@ public class GroupListScreen extends AccountScreen implements FilteredUserInterf
if ("filter".equals(kv[0])) {
subname = URL.decodeQueryString(kv[1]);
}
if ("skip".equals(kv[0]) && URL.decodeQueryString(kv[1]).matches("^[\\d]+")) {
startPosition = Integer.parseInt(URL.decodeQueryString(kv[1]));
}
}
configurePageSize();
}
private void configurePageSize() {
if (Gerrit.isSignedIn()) {
final AccountGeneralPreferences p =
Gerrit.getUserAccount().getGeneralPreferences();
final short m = p.getMaximumPageSize();
pageSize = 0 < m ? m : AccountGeneralPreferences.DEFAULT_PAGESIZE;
} else {
pageSize = AccountGeneralPreferences.DEFAULT_PAGESIZE;
}
}
@ -56,13 +79,17 @@ public class GroupListScreen extends AccountScreen implements FilteredUserInterf
protected void onLoad() {
super.onLoad();
display();
refresh(false);
refresh(false, false);
}
private void refresh(final boolean open) {
setToken(subname == null || "".equals(subname) ? ADMIN_GROUPS
: ADMIN_GROUPS + "?filter=" + URL.encodeQueryString(subname));
GroupMap.match(subname,
private void refresh(final boolean open, final boolean filterModified) {
if (filterModified){
startPosition = 0;
}
setToken(getTokenForScreen(subname, startPosition));
// Retrieve one more group than page size to determine if there are more
// groups to display
GroupMap.match(subname, pageSize + 1, startPosition,
new IgnoreOutdatedFilterResultsCallbackWrapper<GroupMap>(this,
new GerritCallback<GroupMap>() {
@Override
@ -71,13 +98,45 @@ public class GroupListScreen extends AccountScreen implements FilteredUserInterf
Gerrit.display(PageLinks.toGroup(
result.values().get(0).getGroupUUID()));
} else {
groups.display(result, subname);
if (result.size() <= pageSize) {
groups.display(result, subname);
next.setVisible(false);
} else {
groups.displaySubset(result, 0, result.size() - 1, subname);
setupNavigationLink(next, subname, startPosition + pageSize);
}
if (startPosition > 0) {
setupNavigationLink(prev, subname, startPosition - pageSize);
} else {
prev.setVisible(false);
}
groups.finishDisplay();
}
}
}));
}
private void setupNavigationLink(Hyperlink link, String filter, int skip) {
link.setTargetHistoryToken(getTokenForScreen(filter, skip));
link.setVisible(true);
}
private String getTokenForScreen(String filter, int skip) {
String token = ADMIN_GROUPS;
if (filter != null && !filter.isEmpty()) {
token += "?filter=" + URL.encodeQueryString(filter);
}
if (skip > 0) {
if (token.contains("?filter=")) {
token += ",";
} else {
token += "?";
}
token += "skip=" + skip;
}
return token;
}
@Override
public String getCurrentFilter() {
return subname;
@ -89,8 +148,20 @@ public class GroupListScreen extends AccountScreen implements FilteredUserInterf
setPageTitle(Util.C.groupListTitle());
initPageHeader();
prev = new Hyperlink(Util.C.pagedGroupListPrev(), true, "");
prev.setVisible(false);
next = new Hyperlink(Util.C.pagedGroupListNext(), true, "");
next.setVisible(false);
groups = new GroupTable(PageLinks.ADMIN_GROUPS);
add(groups);
final HorizontalPanel buttons = new HorizontalPanel();
buttons.setStyleName(Gerrit.RESOURCES.css().changeTablePrevNextLinks());
buttons.add(prev);
buttons.add(next);
add(buttons);
}
private void initPageHeader() {
@ -104,8 +175,13 @@ public class GroupListScreen extends AccountScreen implements FilteredUserInterf
filterTxt.addKeyUpHandler(new KeyUpHandler() {
@Override
public void onKeyUp(KeyUpEvent event) {
subname = filterTxt.getValue();
refresh(event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER);
boolean enterPressed =
event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER;
boolean filterModified = !filterTxt.getValue().equals(subname);
if (enterPressed || filterModified) {
subname = filterTxt.getValue();
refresh(enterPressed, filterModified);
}
}
});
hp.add(filterTxt);

View File

@ -95,6 +95,14 @@ public class GroupTable extends NavigationTable<GroupInfo> {
}
public void display(List<GroupInfo> list, String toHighlight) {
displaySubset(list, toHighlight, 0, list.size());
}
public void displaySubset(GroupMap groups, int fromIndex, int toIndex, String toHighlight) {
displaySubset(Natives.asList(groups.values()), toHighlight, fromIndex, toIndex);
}
public void displaySubset(List<GroupInfo> list, String toHighlight, int fromIndex, int toIndex) {
while (1 < table.getRowCount())
table.removeRow(table.getRowCount() - 1);
@ -104,7 +112,7 @@ public class GroupTable extends NavigationTable<GroupInfo> {
return a.name().compareTo(b.name());
}
});
for(GroupInfo group : list) {
for(GroupInfo group : list.subList(fromIndex, toIndex)) {
final int row = table.getRowCount();
table.insertRow(row);
applyDataRowStyle(row);

View File

@ -38,6 +38,7 @@ public class ActionContext extends JavaScriptObject {
Gerrit.ActionContext.prototype = {
go: Gerrit.go,
refresh: Gerrit.refresh,
refreshMenuBar: Gerrit.refreshMenuBar,
showError: Gerrit.showError,
br: function(){return doc.createElement('br')},

View File

@ -63,6 +63,7 @@ public class ApiGlue {
go: @com.google.gerrit.client.api.ApiGlue::go(Ljava/lang/String;),
refresh: @com.google.gerrit.client.api.ApiGlue::refresh(),
refreshMenuBar: @com.google.gerrit.client.api.ApiGlue::refreshMenuBar(),
showError: @com.google.gerrit.client.api.ApiGlue::showError(Ljava/lang/String;),
on: function (e,f){(this.events[e] || (this.events[e]=[])).push(f)},
@ -191,6 +192,10 @@ public class ApiGlue {
Gerrit.display(History.getToken());
}
private static final void refreshMenuBar() {
Gerrit.refreshMenuBar();
}
private static final void showError(String message) {
new ErrorDialog(message).center();
}

View File

@ -52,6 +52,7 @@ final class Plugin extends JavaScriptObject {
getPluginName: function(){return this.name},
go: @com.google.gerrit.client.api.ApiGlue::go(Ljava/lang/String;),
refresh: @com.google.gerrit.client.api.ApiGlue::refresh(),
refreshMenuBar: @com.google.gerrit.client.api.ApiGlue::refreshMenuBar(),
showError: @com.google.gerrit.client.api.ApiGlue::showError(Ljava/lang/String;),
on: function(e,f){G.on(e,f)},
onAction: function(t,n,c){G._onAction(this.name,t,n,c)},

View File

@ -24,12 +24,18 @@ public class GroupMap extends NativeMap<GroupInfo> {
groups().get(NativeMap.copyKeysIntoChildren(callback));
}
public static void match(String match, AsyncCallback<GroupMap> cb) {
if (match == null || "".equals(match)) {
all(cb);
} else {
groups().addParameter("m", match).get(NativeMap.copyKeysIntoChildren(cb));
public static void match(String match, int limit, int start, AsyncCallback<GroupMap> cb) {
RestApi call = groups();
if (match != null) {
call.addParameter("m", match);
}
if (limit > 0) {
call.addParameter("n", limit);
}
if (start > 0) {
call.addParameter("S", start);
}
call.get(NativeMap.copyKeysIntoChildren(cb));
}
public static void myOwned(AsyncCallback<GroupMap> cb) {

View File

@ -50,6 +50,10 @@ public final class Plugin extends JavaScriptObject {
public final native void refresh()
/*-{ return this.refresh() }-*/;
/** Refresh Gerrit's menu bar. */
public final native void refreshMenuBar()
/*-{ return this.refreshMenuBar() }-*/;
/** Show message in Gerrit's ErrorDialog. */
public final native void showError(String message)
/*-{ return this.showError(message) }-*/;

View File

@ -188,7 +188,8 @@ public class Submit implements RestModifyView<RevisionResource, SubmitInput>,
"Merge patch set %d into %s",
resource.getPatchSet().getPatchSetId(),
resource.getChange().getDest().getShortName()))
.setVisible(resource.getChange().getStatus().isOpen()
.setVisible(!resource.getPatchSet().isDraft()
&& resource.getChange().getStatus().isOpen()
&& resource.getPatchSet().getId().equals(current)
&& resource.getControl().canSubmit());
}

View File

@ -574,7 +574,7 @@ public class ReceiveCommits {
if (c.getResult() == OK) {
switch (c.getType()) {
case CREATE:
if (isHead(c)) {
if (isHead(c) || isConfig(c)) {
autoCloseChanges(c);
}
break;
@ -584,13 +584,13 @@ public class ReceiveCommits {
c.getRefName(),
c.getOldId(),
c.getNewId());
if (isHead(c)) {
if (isHead(c) || isConfig(c)) {
autoCloseChanges(c);
}
break;
case UPDATE_NONFASTFORWARD:
if (isHead(c)) {
if (isHead(c) || isConfig(c)) {
autoCloseChanges(c);
}
break;

View File

@ -87,6 +87,12 @@ public class ListGroups implements RestReadView<TopLevelResource> {
groupsToInspect.add(id);
}
@Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT", usage = "maximum number of groups to list")
private int limit;
@Option(name = "-S", metaVar = "CNT", usage = "number of groups to skip")
private int start;
@Option(name = "-m", metaVar = "MATCH", usage = "match group substring")
private String matchSubstring;
@ -168,7 +174,15 @@ public class ListGroups implements RestReadView<TopLevelResource> {
groupList = filterGroups(groupCache.all());
}
groupInfos = Lists.newArrayListWithCapacity(groupList.size());
int found = 0;
int foundIndex = 0;
for (AccountGroup group : groupList) {
if (foundIndex++ < start) {
continue;
}
if (limit > 0 && ++found > limit) {
break;
}
groupInfos.add(json.addOptions(options).format(
GroupDescriptions.forAccountGroup(group)));
}
@ -180,11 +194,19 @@ public class ListGroups implements RestReadView<TopLevelResource> {
private List<GroupInfo> getGroupsOwnedBy(IdentifiedUser user)
throws OrmException {
List<GroupInfo> groups = Lists.newArrayList();
int found = 0;
int foundIndex = 0;
for (AccountGroup g : filterGroups(groupCache.all())) {
GroupControl ctl = groupControlFactory.controlFor(g);
try {
if (genericGroupControlFactory.controlFor(user, g.getGroupUUID())
.isOwner()) {
if (foundIndex++ < start) {
continue;
}
if (limit > 0 && ++found > limit) {
break;
}
groups.add(json.addOptions(options).format(ctl.getGroup()));
}
} catch (NoSuchGroupException e) {