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
	 Edwin Kempin
					Edwin Kempin