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:
@@ -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
|
administrators can map plugin screens into the Gerrit URL namespace or
|
||||||
even replace Gerrit screens by plugin screens.
|
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]]
|
[[settings-screen]]
|
||||||
== Plugin Settings Screen
|
== Plugin Settings Screen
|
||||||
|
|
||||||
|
@@ -1638,6 +1638,9 @@ Allowed values are `SIDE_BY_SIDE`, `UNIFIED_DIFF`.
|
|||||||
|`my` ||
|
|`my` ||
|
||||||
The menu items of the `MY` top menu as a list of
|
The menu items of the `MY` top menu as a list of
|
||||||
link:rest-api-config.html#top-menu-item-info[TopMenuItemInfo] entities.
|
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]]
|
[[preferences-input]]
|
||||||
@@ -1684,6 +1687,9 @@ Allowed values are `SIDE_BY_SIDE`, `UNIFIED_DIFF`.
|
|||||||
|`my` |optional|
|
|`my` |optional|
|
||||||
The menu items of the `MY` top menu as a list of
|
The menu items of the `MY` top menu as a list of
|
||||||
link:rest-api-config.html#top-menu-item-info[TopMenuItemInfo] entities.
|
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]]
|
[[query-limit-info]]
|
||||||
|
@@ -14,6 +14,9 @@
|
|||||||
|
|
||||||
package com.google.gerrit.client.info;
|
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;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DateFormat;
|
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DateFormat;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DiffView;
|
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.JavaScriptObject;
|
||||||
import com.google.gwt.core.client.JsArray;
|
import com.google.gwt.core.client.JsArray;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class AccountPreferencesInfo extends JavaScriptObject {
|
public class AccountPreferencesInfo extends JavaScriptObject {
|
||||||
public static AccountPreferencesInfo create() {
|
public static AccountPreferencesInfo create() {
|
||||||
@@ -197,6 +202,26 @@ public class AccountPreferencesInfo extends JavaScriptObject {
|
|||||||
final native void initMy() /*-{ this.my = []; }-*/;
|
final native void initMy() /*-{ this.my = []; }-*/;
|
||||||
final native void addMy(TopMenuItem m) /*-{ this.my.push(m); }-*/;
|
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() {
|
protected AccountPreferencesInfo() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -391,6 +391,7 @@ public class Gerrit implements EntryPoint {
|
|||||||
myAccount = AccountInfo.create(0, null, null, null);
|
myAccount = AccountInfo.create(0, null, null, null);
|
||||||
myAccountDiffPref = null;
|
myAccountDiffPref = null;
|
||||||
myPrefs = AccountPreferencesInfo.createDefault();
|
myPrefs = AccountPreferencesInfo.createDefault();
|
||||||
|
urlAliasMatcher.clearUserAliases();
|
||||||
xGerritAuth = null;
|
xGerritAuth = null;
|
||||||
refreshMenuBar();
|
refreshMenuBar();
|
||||||
|
|
||||||
@@ -880,6 +881,7 @@ public class Gerrit implements EntryPoint {
|
|||||||
siteFooter.setVisible(myPrefs.showSiteHeader());
|
siteFooter.setVisible(myPrefs.showSiteHeader());
|
||||||
}
|
}
|
||||||
FormatUtil.setPreferences(myPrefs);
|
FormatUtil.setPreferences(myPrefs);
|
||||||
|
urlAliasMatcher.updateUserAliases(myPrefs.urlAliases());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void getDocIndex(final AsyncCallback<DocInfo> cb) {
|
private static void getDocIndex(final AsyncCallback<DocInfo> cb) {
|
||||||
|
@@ -20,18 +20,41 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class UrlAliasMatcher {
|
public class UrlAliasMatcher {
|
||||||
|
private final Map<RegExp, String> userUrlAliases;
|
||||||
private final Map<RegExp, String> globalUrlAliases;
|
private final Map<RegExp, String> globalUrlAliases;
|
||||||
|
|
||||||
UrlAliasMatcher(Map<String, String> globalUrlAliases) {
|
UrlAliasMatcher(Map<String, String> globalUrlAliases) {
|
||||||
this.globalUrlAliases = new HashMap<>();
|
this.globalUrlAliases = compile(globalUrlAliases);
|
||||||
if (globalUrlAliases != null) {
|
this.userUrlAliases = new HashMap<>();
|
||||||
for (Map.Entry<String, String> e : globalUrlAliases.entrySet()) {
|
}
|
||||||
this.globalUrlAliases.put(RegExp.compile(e.getKey()), e.getValue());
|
|
||||||
|
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) {
|
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()) {
|
for (Map.Entry<RegExp, String> e : globalUrlAliases.entrySet()) {
|
||||||
RegExp pat = e.getKey();
|
RegExp pat = e.getKey();
|
||||||
if (pat.exec(token) != null) {
|
if (pat.exec(token) != null) {
|
||||||
|
@@ -44,7 +44,9 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class GetPreferences implements RestReadView<AccountResource> {
|
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_URL = "url";
|
||||||
public static final String KEY_TARGET = "target";
|
public static final String KEY_TARGET = "target";
|
||||||
public static final String KEY_ID = "id";
|
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<CurrentUser> self;
|
||||||
private final Provider<ReviewDb> db;
|
private final Provider<ReviewDb> db;
|
||||||
@@ -110,6 +115,7 @@ public class GetPreferences implements RestReadView<AccountResource> {
|
|||||||
ReviewCategoryStrategy reviewCategoryStrategy;
|
ReviewCategoryStrategy reviewCategoryStrategy;
|
||||||
DiffView diffView;
|
DiffView diffView;
|
||||||
List<TopMenu.MenuItem> my;
|
List<TopMenu.MenuItem> my;
|
||||||
|
Map<String, String> urlAliases;
|
||||||
|
|
||||||
public PreferenceInfo(AccountGeneralPreferences p,
|
public PreferenceInfo(AccountGeneralPreferences p,
|
||||||
VersionedAccountPreferences v, Repository allUsers) {
|
VersionedAccountPreferences v, Repository allUsers) {
|
||||||
@@ -129,12 +135,12 @@ public class GetPreferences implements RestReadView<AccountResource> {
|
|||||||
reviewCategoryStrategy = p.getReviewCategoryStrategy();
|
reviewCategoryStrategy = p.getReviewCategoryStrategy();
|
||||||
diffView = p.getDiffView();
|
diffView = p.getDiffView();
|
||||||
}
|
}
|
||||||
my = my(v, allUsers);
|
loadFromAllUsers(v, allUsers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<TopMenu.MenuItem> my(VersionedAccountPreferences v,
|
private void loadFromAllUsers(VersionedAccountPreferences v,
|
||||||
Repository allUsers) {
|
Repository allUsers) {
|
||||||
List<TopMenu.MenuItem> my = my(v);
|
my = my(v);
|
||||||
if (my.isEmpty() && !v.isDefaults()) {
|
if (my.isEmpty() && !v.isDefaults()) {
|
||||||
try {
|
try {
|
||||||
VersionedAccountPreferences d = VersionedAccountPreferences.forDefault();
|
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("Starred Changes", "#/q/is:starred", null));
|
||||||
my.add(new TopMenu.MenuItem("Groups", "#/groups/self", null));
|
my.add(new TopMenu.MenuItem("Groups", "#/groups/self", null));
|
||||||
}
|
}
|
||||||
return my;
|
|
||||||
|
urlAliases = urlAliases(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<TopMenu.MenuItem> my(VersionedAccountPreferences 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);
|
String val = cfg.getString(MY, subsection, key);
|
||||||
return !Strings.isNullOrEmpty(val) ? val : defaultValue;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,9 +15,12 @@
|
|||||||
package com.google.gerrit.server.account;
|
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_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_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.KEY_URL;
|
||||||
import static com.google.gerrit.server.account.GetPreferences.MY;
|
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.common.base.Strings;
|
||||||
import com.google.gerrit.extensions.restapi.AuthException;
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
@@ -48,6 +51,8 @@ import org.eclipse.jgit.lib.Config;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class SetPreferences implements RestModifyView<AccountResource, Input> {
|
public class SetPreferences implements RestModifyView<AccountResource, Input> {
|
||||||
@@ -67,6 +72,7 @@ public class SetPreferences implements RestModifyView<AccountResource, Input> {
|
|||||||
public ReviewCategoryStrategy reviewCategoryStrategy;
|
public ReviewCategoryStrategy reviewCategoryStrategy;
|
||||||
public DiffView diffView;
|
public DiffView diffView;
|
||||||
public List<TopMenu.MenuItem> my;
|
public List<TopMenu.MenuItem> my;
|
||||||
|
public Map<String, String> urlAliases;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Provider<CurrentUser> self;
|
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().accounts().update(Collections.singleton(a));
|
||||||
db.get().commit();
|
db.get().commit();
|
||||||
storeMyMenus(versionedPrefs, i.my);
|
storeMyMenus(versionedPrefs, i.my);
|
||||||
|
storeUrlAliases(versionedPrefs, i.urlAliases);
|
||||||
versionedPrefs.commit(md);
|
versionedPrefs.commit(md);
|
||||||
cache.evict(accountId);
|
cache.evict(accountId);
|
||||||
return new GetPreferences.PreferenceInfo(
|
return new GetPreferences.PreferenceInfo(
|
||||||
p, versionedPrefs,
|
p, versionedPrefs, md.getRepository());
|
||||||
md.getRepository());
|
|
||||||
} finally {
|
} finally {
|
||||||
md.close();
|
md.close();
|
||||||
db.get().rollback();
|
db.get().rollback();
|
||||||
@@ -202,4 +208,21 @@ public class SetPreferences implements RestModifyView<AccountResource, Input> {
|
|||||||
cfg.unsetSection(section, subsection);
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Submodule plugins/cookbook-plugin updated: e49010bbbe...d2f6bc3511
Reference in New Issue
Block a user