diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index f07ac1887f..54349cdab9 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -3767,6 +3767,46 @@ on the server. One or more groups can be set. If no groups are added, any user will be allowed to execute 'upload-pack' on the server. +[[urlAlias]] +=== Section urlAlias + +URL aliases define regular expressions for URL tokens that are mapped +to target URL tokens. + +Each URL alias must be specified in its own subsection. The subsection +name should be a descriptive name. It must be unique, but is not +interpreted in any way. + +The URL aliases are applied in no particular order. The first matching +URL alias is used and further matches are ignored. + +URL aliases can be used to map plugin screens into the Gerrit URL +namespace, or to replace Gerrit screens by plugin screens. + +Example: + +---- +[urlAlias "MyPluginScreen"] + match = /myscreen/(.*) + token = /x/myplugin/myscreen/$1 +[urlAlias "MyChangeScreen"] + match = /c/(.*) + token = /x/myplugin/c/$1 +---- + +[[urlAlias.match]]urlAlias.match:: ++ +A regular expression for a URL token. ++ +The matched URL token is replaced by `urlAlias.token`. + +[[urlAlias.token]]urlAlias.token:: ++ +The target URL token. ++ +It can contain placeholders for the groups matched by the +`urlAlias.match` regular expression: `$1` for the first matched group, +`$2` for the second matched group, etc. [[submodule]] === Section submodule diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt index aaf3752956..24f34af58d 100644 --- a/Documentation/dev-plugins.txt +++ b/Documentation/dev-plugins.txt @@ -1788,6 +1788,10 @@ public class MyPlugin extends PluginEntryPoint { } ---- +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. + [[settings-screen]] == Plugin Settings Screen diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt index 516ae51cee..9ba8264102 100644 --- a/Documentation/rest-api-config.txt +++ b/Documentation/rest-api-config.txt @@ -1352,6 +1352,11 @@ entity. Not set if SSHD is disabled. Information about the configuration from the link:config-gerrit.html#suggest[suggest] section as link:#suggest-info[ SuggestInfo] entity. +|`url_aliases` |optional| +A map of URL aliases, where a regular expression for an URL token is +mapped to a target URL token. The target URL token can contain +placeholders for the groups matched by the regular expression: `$1` for +the first matched group, `$2` for the second matched group, etc. |`user` || Information about the configuration from the link:config-gerrit.html#user[user] section as link:#user-config-info[ diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java index 1318a1d34f..4c63a6410e 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java @@ -96,6 +96,8 @@ import com.google.gerrit.reviewdb.client.Project; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.RunAsyncCallback; import com.google.gwt.http.client.URL; +import com.google.gwt.regexp.shared.MatchResult; +import com.google.gwt.regexp.shared.RegExp; import com.google.gwt.user.client.Window; import com.google.gwtorm.client.KeyUtil; @@ -204,7 +206,9 @@ public class Dispatcher { } } - private static void select(final String token) { + private static void select(String token) { + token = Gerrit.getUrlAliasMatcher().replace(token); + if (matchPrefix(QUERY, token)) { query(token); diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java index 8497ceed26..e39ae68b04 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java @@ -112,6 +112,7 @@ public class Gerrit implements EntryPoint { private static String myHost; private static ServerInfo myServerInfo; private static AccountPreferencesInfo myPrefs; + private static UrlAliasMatcher urlAliasMatcher; private static boolean hasDocumentation; private static String docUrl; private static HostPageData.Theme myTheme; @@ -294,6 +295,10 @@ public class Gerrit implements EntryPoint { return myServerInfo; } + public static UrlAliasMatcher getUrlAliasMatcher() { + return urlAliasMatcher; + } + /** Site theme information (site specific colors)/ */ public static HostPageData.Theme getTheme() { return myTheme; @@ -446,6 +451,7 @@ public class Gerrit implements EntryPoint { @Override public void onSuccess(ServerInfo info) { myServerInfo = info; + urlAliasMatcher = new UrlAliasMatcher(info.urlAliases()); String du = info.gerrit().docUrl(); if (du != null && !du.isEmpty()) { hasDocumentation = true; diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/UrlAliasMatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/UrlAliasMatcher.java new file mode 100644 index 0000000000..adaee55256 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/UrlAliasMatcher.java @@ -0,0 +1,43 @@ +// Copyright (C) 2015 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; + +import com.google.gwt.regexp.shared.RegExp; + +import java.util.HashMap; +import java.util.Map; + +public class UrlAliasMatcher { + private final Map globalUrlAliases; + + UrlAliasMatcher(Map globalUrlAliases) { + this.globalUrlAliases = new HashMap<>(); + if (globalUrlAliases != null) { + for (Map.Entry e : globalUrlAliases.entrySet()) { + this.globalUrlAliases.put(RegExp.compile(e.getKey()), e.getValue()); + } + } + } + + public String replace(String token) { + for (Map.Entry e : globalUrlAliases.entrySet()) { + RegExp pat = e.getKey(); + if (pat.exec(token) != null) { + return pat.replace(token, e.getValue()); + } + } + return token; + } +} diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ServerInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ServerInfo.java index 1820afbce1..4980ec9af5 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ServerInfo.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/config/ServerInfo.java @@ -14,8 +14,15 @@ package com.google.gerrit.client.config; +import com.google.gerrit.client.rpc.NativeMap; +import com.google.gerrit.client.rpc.NativeString; +import com.google.gerrit.client.rpc.Natives; import com.google.gwt.core.client.JavaScriptObject; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + public class ServerInfo extends JavaScriptObject { public final native AuthInfo auth() /*-{ return this.auth; }-*/; public final native ChangeConfigInfo change() /*-{ return this.change; }-*/; @@ -29,6 +36,18 @@ public class ServerInfo extends JavaScriptObject { public final native UserConfigInfo user() /*-{ return this.user; }-*/; public final native ReceiveInfo receive() /*-{ return this.receive; }-*/; + public final Map urlAliases() { + Map urlAliases = new HashMap<>(); + for (String k : Natives.keys(_urlAliases())) { + urlAliases.put(k, urlAliasToken(k)); + } + return urlAliases; + } + + public final native String urlAliasToken(String n) /*-{ return this.url_aliases[n]; }-*/; + private final native NativeMap _urlAliases() /*-{ return this.url_aliases; }-*/; + + public final boolean hasContactStore() { return contactStore() != null; } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java index 8c9b862665..2ebdadf1f2 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java @@ -47,6 +47,10 @@ import java.util.Map; import java.util.concurrent.TimeUnit; public class GetServerInfo implements RestReadView { + private final static String URL_ALIAS = "urlAlias"; + private final static String KEY_MATCH = "match"; + private final static String KEY_TOKEN = "token"; + private final Config config; private final AuthConfig authConfig; private final Realm realm; @@ -102,6 +106,10 @@ public class GetServerInfo implements RestReadView { info.plugin = getPluginInfo(); info.sshd = getSshdInfo(config); info.suggest = getSuggestInfo(config); + + Map urlAliases = getUrlAliasesInfo(config); + info.urlAliases = !urlAliases.isEmpty() ? urlAliases : null; + info.user = getUserInfo(anonymousCowardName); info.receive = getReceiveInfo(config); return info; @@ -267,6 +275,15 @@ public class GetServerInfo implements RestReadView { return info; } + private Map getUrlAliasesInfo(Config cfg) { + Map urlAliases = new HashMap<>(); + 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; + } + private SshdInfo getSshdInfo(Config cfg) { String[] addr = cfg.getStringList("sshd", null, "listenAddress"); if (addr.length == 1 && isOff(addr[0])) { @@ -313,6 +330,7 @@ public class GetServerInfo implements RestReadView { public PluginConfigInfo plugin; public SshdInfo sshd; public SuggestInfo suggest; + public Map urlAliases; public UserConfigInfo user; public ReceiveInfo receive; }