Add support for user-specific URL aliases

User-specific URL aliases overwrite global URL aliases.

Plugins may use user-specific URL aliases to replace certain screens
for certain users. The cookbook plugin was adapted to demonstrate
this.

Change-Id: I9b4d89e9b0fcedbeceb7298b91506167d464a070
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
Edwin Kempin 2015-07-22 15:36:56 +02:00
parent 107b0ad53f
commit b1e6a3a69c
8 changed files with 112 additions and 11 deletions

View File

@ -1792,6 +1792,11 @@ By defining an link:config-gerrit.html#urlAlias[urlAlias] Gerrit
administrators can map plugin screens into the Gerrit URL namespace or
even replace Gerrit screens by plugin screens.
Plugins may also programatically add URL aliases in the preferences of
of a user. This way certain screens can be replaced for certain users.
E.g. the plugin may offer a user preferences setting for choosing a
screen that then sets/unsets a URL alias for the user.
[[settings-screen]]
== Plugin Settings Screen

View File

@ -1638,6 +1638,9 @@ Allowed values are `SIDE_BY_SIDE`, `UNIFIED_DIFF`.
|`my` ||
The menu items of the `MY` top menu as a list of
link:rest-api-config.html#top-menu-item-info[TopMenuItemInfo] entities.
|`url_aliases` |optional|
A map of URL path pairs, where the first URL path is an alias for the
second URL path.
|============================================
[[preferences-input]]
@ -1684,6 +1687,9 @@ Allowed values are `SIDE_BY_SIDE`, `UNIFIED_DIFF`.
|`my` |optional|
The menu items of the `MY` top menu as a list of
link:rest-api-config.html#top-menu-item-info[TopMenuItemInfo] entities.
|`url_aliases` |optional|
A map of URL path pairs, where the first URL path is an alias for the
second URL path.
|============================================
[[query-limit-info]]

View File

@ -14,6 +14,9 @@
package com.google.gerrit.client.info;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.NativeString;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DateFormat;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DiffView;
@ -24,7 +27,9 @@ import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.TimeFormat;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class AccountPreferencesInfo extends JavaScriptObject {
public static AccountPreferencesInfo create() {
@ -197,6 +202,26 @@ public class AccountPreferencesInfo extends JavaScriptObject {
final native void initMy() /*-{ this.my = []; }-*/;
final native void addMy(TopMenuItem m) /*-{ this.my.push(m); }-*/;
public final Map<String, String> urlAliases() {
Map<String, String> urlAliases = new HashMap<>();
for (String k : Natives.keys(_urlAliases())) {
urlAliases.put(k, urlAliasToken(k));
}
return urlAliases;
}
private final native String urlAliasToken(String m) /*-{ return this.url_aliases[m]; }-*/;
private final native NativeMap<NativeString> _urlAliases() /*-{ return this.url_aliases; }-*/;
public final void setUrlAliases(Map<String, String> urlAliases) {
initUrlAliases();
for (Map.Entry<String, String> e : urlAliases.entrySet()) {
putUrlAlias(e.getKey(), e.getValue());
}
}
private final native void putUrlAlias(String m, String t) /*-{ this.url_aliases[m] = t; }-*/;
private final native void initUrlAliases() /*-{ this.url_aliases = {}; }-*/;
protected AccountPreferencesInfo() {
}
}

View File

@ -391,6 +391,7 @@ public class Gerrit implements EntryPoint {
myAccount = AccountInfo.create(0, null, null, null);
myAccountDiffPref = null;
myPrefs = AccountPreferencesInfo.createDefault();
urlAliasMatcher.clearUserAliases();
xGerritAuth = null;
refreshMenuBar();
@ -880,6 +881,7 @@ public class Gerrit implements EntryPoint {
siteFooter.setVisible(myPrefs.showSiteHeader());
}
FormatUtil.setPreferences(myPrefs);
urlAliasMatcher.updateUserAliases(myPrefs.urlAliases());
}
private static void getDocIndex(final AsyncCallback<DocInfo> cb) {

View File

@ -20,18 +20,41 @@ import java.util.HashMap;
import java.util.Map;
public class UrlAliasMatcher {
private final Map<RegExp, String> userUrlAliases;
private final Map<RegExp, String> globalUrlAliases;
UrlAliasMatcher(Map<String, String> globalUrlAliases) {
this.globalUrlAliases = new HashMap<>();
if (globalUrlAliases != null) {
for (Map.Entry<String, String> e : globalUrlAliases.entrySet()) {
this.globalUrlAliases.put(RegExp.compile(e.getKey()), e.getValue());
this.globalUrlAliases = compile(globalUrlAliases);
this.userUrlAliases = new HashMap<>();
}
private static Map<RegExp, String> compile(Map<String, String> urlAliases) {
Map<RegExp, String> compiledUrlAliases = new HashMap<>();
if (urlAliases != null) {
for (Map.Entry<String, String> e : urlAliases.entrySet()) {
compiledUrlAliases.put(RegExp.compile(e.getKey()), e.getValue());
}
}
return compiledUrlAliases;
}
void clearUserAliases() {
this.userUrlAliases.clear();
}
void updateUserAliases(Map<String, String> userUrlAliases) {
clearUserAliases();
this.userUrlAliases.putAll(compile(userUrlAliases));
}
public String replace(String token) {
for (Map.Entry<RegExp, String> e : userUrlAliases.entrySet()) {
RegExp pat = e.getKey();
if (pat.exec(token) != null) {
return pat.replace(token, e.getValue());
}
}
for (Map.Entry<RegExp, String> e : globalUrlAliases.entrySet()) {
RegExp pat = e.getKey();
if (pat.exec(token) != null) {

View File

@ -44,7 +44,9 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Singleton
public class GetPreferences implements RestReadView<AccountResource> {
@ -54,6 +56,9 @@ public class GetPreferences implements RestReadView<AccountResource> {
public static final String KEY_URL = "url";
public static final String KEY_TARGET = "target";
public static final String KEY_ID = "id";
public static final String URL_ALIAS = "urlAlias";
public static final String KEY_MATCH = "match";
public static final String KEY_TOKEN = "token";
private final Provider<CurrentUser> self;
private final Provider<ReviewDb> db;
@ -110,6 +115,7 @@ public class GetPreferences implements RestReadView<AccountResource> {
ReviewCategoryStrategy reviewCategoryStrategy;
DiffView diffView;
List<TopMenu.MenuItem> my;
Map<String, String> urlAliases;
public PreferenceInfo(AccountGeneralPreferences p,
VersionedAccountPreferences v, Repository allUsers) {
@ -129,12 +135,12 @@ public class GetPreferences implements RestReadView<AccountResource> {
reviewCategoryStrategy = p.getReviewCategoryStrategy();
diffView = p.getDiffView();
}
my = my(v, allUsers);
loadFromAllUsers(v, allUsers);
}
private List<TopMenu.MenuItem> my(VersionedAccountPreferences v,
private void loadFromAllUsers(VersionedAccountPreferences v,
Repository allUsers) {
List<TopMenu.MenuItem> my = my(v);
my = my(v);
if (my.isEmpty() && !v.isDefaults()) {
try {
VersionedAccountPreferences d = VersionedAccountPreferences.forDefault();
@ -153,7 +159,8 @@ public class GetPreferences implements RestReadView<AccountResource> {
my.add(new TopMenu.MenuItem("Starred Changes", "#/q/is:starred", null));
my.add(new TopMenu.MenuItem("Groups", "#/groups/self", null));
}
return my;
urlAliases = urlAliases(v);
}
private List<TopMenu.MenuItem> my(VersionedAccountPreferences v) {
@ -175,5 +182,15 @@ public class GetPreferences implements RestReadView<AccountResource> {
String val = cfg.getString(MY, subsection, key);
return !Strings.isNullOrEmpty(val) ? val : defaultValue;
}
private static Map<String, String> urlAliases(VersionedAccountPreferences v) {
HashMap<String, String> urlAliases = new HashMap<>();
Config cfg = v.getConfig();
for (String subsection : cfg.getSubsections(URL_ALIAS)) {
urlAliases.put(cfg.getString(URL_ALIAS, subsection, KEY_MATCH),
cfg.getString(URL_ALIAS, subsection, KEY_TOKEN));
}
return !urlAliases.isEmpty() ? urlAliases : null;
}
}
}

View File

@ -15,9 +15,12 @@
package com.google.gerrit.server.account;
import static com.google.gerrit.server.account.GetPreferences.KEY_ID;
import static com.google.gerrit.server.account.GetPreferences.KEY_MATCH;
import static com.google.gerrit.server.account.GetPreferences.KEY_TARGET;
import static com.google.gerrit.server.account.GetPreferences.KEY_TOKEN;
import static com.google.gerrit.server.account.GetPreferences.KEY_URL;
import static com.google.gerrit.server.account.GetPreferences.MY;
import static com.google.gerrit.server.account.GetPreferences.URL_ALIAS;
import com.google.common.base.Strings;
import com.google.gerrit.extensions.restapi.AuthException;
@ -48,6 +51,8 @@ import org.eclipse.jgit.lib.Config;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@Singleton
public class SetPreferences implements RestModifyView<AccountResource, Input> {
@ -67,6 +72,7 @@ public class SetPreferences implements RestModifyView<AccountResource, Input> {
public ReviewCategoryStrategy reviewCategoryStrategy;
public DiffView diffView;
public List<TopMenu.MenuItem> my;
public Map<String, String> urlAliases;
}
private final Provider<CurrentUser> self;
@ -164,11 +170,11 @@ public class SetPreferences implements RestModifyView<AccountResource, Input> {
db.get().accounts().update(Collections.singleton(a));
db.get().commit();
storeMyMenus(versionedPrefs, i.my);
storeUrlAliases(versionedPrefs, i.urlAliases);
versionedPrefs.commit(md);
cache.evict(accountId);
return new GetPreferences.PreferenceInfo(
p, versionedPrefs,
md.getRepository());
p, versionedPrefs, md.getRepository());
} finally {
md.close();
db.get().rollback();
@ -202,4 +208,21 @@ public class SetPreferences implements RestModifyView<AccountResource, Input> {
cfg.unsetSection(section, subsection);
}
}
public static void storeUrlAliases(VersionedAccountPreferences prefs,
Map<String, String> urlAliases) {
if (urlAliases != null) {
Config cfg = prefs.getConfig();
for (String subsection : cfg.getSubsections(URL_ALIAS)) {
cfg.unsetSection(URL_ALIAS, subsection);
}
int i = 1;
for (Entry<String, String> e : urlAliases.entrySet()) {
cfg.setString(URL_ALIAS, URL_ALIAS + i, KEY_MATCH, e.getKey());
cfg.setString(URL_ALIAS, URL_ALIAS + i, KEY_TOKEN, e.getValue());
i++;
}
}
}
}

@ -1 +1 @@
Subproject commit e49010bbbed9d941c35a9f1eed1178cd909c7e34
Subproject commit d2f6bc3511185729d3ecc3b3df25b1e9cebe2b2d