/changes/: Support custom dashboards

A custom dashboard can now be shown in a layout similar to the
per-user dashboard, but with the sections entirely configured from
the URL. This makes custom dashboards stateless on the server side.
Users or projects can simply trade URLs using an external system
like a project wiki, or site administrators can put the links into
the site's GerritHeader.html or GerritFooter.html.

Dashboards are available such as:

  /#/dashboard/?title=Custom+View&To+Review=reviewer:sop@google.com&Pending+In+repo=project:tools/repo+is:open

This opens a view showing the title "Custom View" with two sections,
"To Review" and "Pending in repo":

  Custom View

  To Review

    Results of `reviewer:sop@google.com`

  Pending In repo

    Results of `project:tools/repo is:open`

The dashboard URLs are pretty trivial to configure. All keys and
values are URL query parameter encoded. Set the page and window
title using an optional "title=Text" parameter.

Each section's title is defined by the parameter name, section
display order is defined by the order the parameters appear in the
URL, and the query results are defined by the parameter value.
To limit the number of rows in a query use "limit:N", otherwise
the entire result set will be shown (up to the user's query limit).

Parameters may be separted from each other using any of the following
characters, as some users may find one more readable than another:

  & or ; or ,

Change-Id: Ie36fe5726d2ae2f7bf22d470f8a5608a576ff593
This commit is contained in:
Shawn O. Pearce
2012-04-07 17:15:56 -07:00
parent 2a912fba44
commit f93b3eecec
2 changed files with 125 additions and 2 deletions

View File

@@ -58,6 +58,7 @@ import com.google.gerrit.client.auth.openid.OpenIdSignInDialog;
import com.google.gerrit.client.auth.userpass.UserPassSignInDialog;
import com.google.gerrit.client.changes.AccountDashboardScreen;
import com.google.gerrit.client.changes.ChangeScreen;
import com.google.gerrit.client.changes.CustomDashboardScreen;
import com.google.gerrit.client.changes.PatchTable;
import com.google.gerrit.client.changes.PublishCommentScreen;
import com.google.gerrit.client.changes.QueryScreen;
@@ -361,8 +362,18 @@ public class Dispatcher {
}
private static void dashboard(final String token) {
Gerrit.display(token, //
new AccountDashboardScreen(Account.Id.parse(skip(token))));
String rest = skip(token);
if (rest.matches("[0-9]+")) {
Gerrit.display(token, new AccountDashboardScreen(Account.Id.parse(rest)));
return;
}
if (rest.startsWith("?")) {
Gerrit.display(token, new CustomDashboardScreen(rest.substring(1)));
return;
}
Gerrit.display(token, new NotFoundScreen());
}
private static void change(final String token) {

View File

@@ -0,0 +1,112 @@
// Copyright (C) 2012 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.changes;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.rpc.NativeList;
import com.google.gerrit.client.rpc.ScreenLoadCallback;
import com.google.gerrit.client.ui.Screen;
import com.google.gwt.http.client.URL;
import java.util.ArrayList;
import java.util.List;
public class CustomDashboardScreen extends Screen implements ChangeListScreen {
private String title;
private List<String> titles;
private List<String> queries;
private ChangeTable2 table;
private List<ChangeTable2.Section> sections;
public CustomDashboardScreen(String params) {
titles = new ArrayList<String>();
queries = new ArrayList<String>();
for (String kvPair : params.split("[,;&]")) {
String[] kv = kvPair.split("=", 2);
if (kv.length != 2 || kv[0].isEmpty()) {
continue;
}
if ("title".equals(kv[0])) {
title = URL.decodeQueryString(kv[1]);
} else {
titles.add(URL.decodeQueryString(kv[0]));
queries.add(URL.decodeQueryString(kv[1]));
}
}
}
@Override
protected void onInitUI() {
super.onInitUI();
if (title != null) {
setWindowTitle(title);
setPageTitle(title);
}
table = new ChangeTable2();
table.addStyleName(Gerrit.RESOURCES.css().accountDashboard());
sections = new ArrayList<ChangeTable2.Section>();
for (String title : titles) {
ChangeTable2.Section s = new ChangeTable2.Section();
s.setTitleText(title);
table.addSection(s);
sections.add(s);
}
add(table);
}
@Override
protected void onLoad() {
super.onLoad();
if (queries.isEmpty()) {
display();
} else if (queries.size() == 1) {
ChangeList.next(queries.get(0),
0, PagedSingleListScreen.MAX_SORTKEY,
new ScreenLoadCallback<ChangeList>(this) {
@Override
protected void preDisplay(ChangeList result) {
table.updateColumnsForLabels(result);
sections.get(0).display(result);
table.finishDisplay();
}
});
} else {
ChangeList.query(
new ScreenLoadCallback<NativeList<ChangeList>>(this) {
@Override
protected void preDisplay(NativeList<ChangeList> result) {
table.updateColumnsForLabels(
result.asList().toArray(new ChangeList[result.size()]));
for (int i = 0; i < result.size(); i++) {
sections.get(i).display(result.get(i));
}
table.finishDisplay();
}
},
queries.toArray(new String[queries.size()]));
}
}
@Override
public void registerKeys() {
super.registerKeys();
table.setRegisterKeys(true);
}
}