Merge "Add top menu extension point"

This commit is contained in:
Edwin Kempin
2013-09-30 14:04:23 +00:00
committed by Gerrit Code Review
13 changed files with 393 additions and 1 deletions

View File

@@ -774,6 +774,53 @@ Gerrit.install(function(self) {
});
----
[[top-menu-extensions]]
Top Menu Extensions
-------------------
Plugins can contribute items to Gerrit's top menu.
A single top menu extension can have multiple elements and will be put as
the last element in Gerrit's top menu.
Plugins define the top menu entries by implementing `TopMenu` interface:
[source,java]
----
public class MyTopMenuExtension implements TopMenu {
@Override
public List<MenuEntry> getEntries() {
return Lists.newArrayList(
new MenuEntry("Top Menu Entry", Lists.newArrayList(
new MenuItem("Gerrit", "http://gerrit.googlecode.com/"))));
}
}
----
If no Guice modules are declared in the manifest, the top menu extension may use
auto-registration by providing an `@Listen` annotation:
[source,java]
----
@Listen
public class MyTopMenuExtension implements TopMenu {
...
}
----
Otherwise the top menu extension must be bound in a plugin module:
[source,java]
----
public class HelloWorldModule extends AbstractModule {
@Override
protected void configure() {
DynamicSet.bind(binder(), TopMenu.class).to(MyTopMenuExtension.class);
}
}
----
[[http]]
HTTP Servlets
-------------

View File

@@ -135,6 +135,47 @@ The entries in the map are sorted by capability ID.
}
----
[[get-top-menus]]
Get Top Menus
~~~~~~~~~~~~~
[verse]
'GET /config/server/top-menus'
Returns the list of additional top menu entries.
.Request
----
GET /config/server/top-menus HTTP/1.0
----
As response a list of the additional top menu entries as
link:#top-menu-entry-info[TopMenuEntryInfo] entities is returned.
.Response
----
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
)]}'
[
{
"name": "Top Menu Entry",
"items": [
{
"url": "http://gerrit.googlecode.com/",
"name": "Gerrit",
"target": "_blank"
}
]
}
]
----
[[json-entities]]
JSON Entities
-------------
[[capability-info]]
CapabilityInfo
~~~~~~~~~~~~~~
@@ -148,6 +189,32 @@ The `CapabilityInfo` entity contains information about a capability.
|`name` |capability name
|=================================
[[top-menu-entry-info]]
TopMenuEntryInfo
~~~~~~~~~~~~~~~~
The `TopMenuEntryInfo` entity contains information about a top menu
entry.
[options="header",width="50%",cols="1,5"]
|=================================
|Field Name |Description
|`name` |Name of the top menu entry.
|`items` |List of link:#top-menu-item-info[menu items].
|=================================
[[top-menu-item-info]]
TopMenuItemInfo
~~~~~~~~~~~~~~~
The `TopMenuItemInfo` entity contains information about a menu item in
a top menu entry.
[options="header",width="50%",cols="1,5"]
|=================================
|Field Name |Description
|`url` |The URL of the menu item link.
|`name` |The name of the menu item.
|`target` |Target attribute of the menu item link.
|=================================
GERRIT
------

View File

@@ -0,0 +1,51 @@
// Copyright (C) 2013 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.extensions.webui;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
import java.util.List;
@ExtensionPoint
public interface TopMenu {
public class MenuEntry {
public final String name;
public final List<MenuItem> items;
public MenuEntry(String name, List<MenuItem> items) {
this.name = name;
this.items = items;
}
}
public class MenuItem {
public final String url;
public final String name;
public final String target;
public MenuItem(String name, String url) {
this(name, url, "_blank");
}
public MenuItem(String name, String url, String target) {
this.url = url;
this.name = name;
this.target = target;
}
}
List<MenuEntry> getEntries();
}

View File

@@ -24,8 +24,13 @@ import com.google.gerrit.client.admin.ProjectScreen;
import com.google.gerrit.client.api.ApiGlue;
import com.google.gerrit.client.changes.ChangeConstants;
import com.google.gerrit.client.changes.ChangeListScreen;
import com.google.gerrit.client.config.ConfigServerApi;
import com.google.gerrit.client.extensions.TopMenu;
import com.google.gerrit.client.extensions.TopMenuItem;
import com.google.gerrit.client.extensions.TopMenuList;
import com.google.gerrit.client.patches.PatchScreen;
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.ui.LinkMenuBar;
import com.google.gerrit.client.ui.LinkMenuItem;
import com.google.gerrit.client.ui.MorphingTabPanel;
@@ -87,6 +92,7 @@ import com.google.gwtjsonrpc.common.AsyncCallback;
import com.google.gwtorm.client.KeyUtil;
import java.util.ArrayList;
import java.util.List;
public class Gerrit implements EntryPoint {
public static final GerritConstants C = GWT.create(GerritConstants.class);
@@ -789,6 +795,18 @@ public class Gerrit implements EntryPoint {
break;
}
}
ConfigServerApi.topMenus(new GerritCallback<TopMenuList>() {
public void onSuccess(TopMenuList result) {
List<TopMenu> topMenuExtensions = Natives.asList(result);
for (TopMenu menu : topMenuExtensions) {
LinkMenuBar bar = new LinkMenuBar();
for (TopMenuItem item : Natives.asList(menu.getItems())) {
addExtensionLink(bar, item);
}
menuLeft.add(bar, menu.getName());
}
};
});
}
public static void applyUserPreferences() {
@@ -921,4 +939,10 @@ public class Gerrit implements EntryPoint {
atag.setTarget("_blank");
m.add(atag);
}
private static void addExtensionLink(final LinkMenuBar m, final TopMenuItem item) {
final Anchor atag = anchor(item.getName(), item.getUrl());
atag.setTarget(item.getTarget());
m.add(atag);
}
}

View File

@@ -14,6 +14,7 @@
package com.google.gerrit.client.config;
import com.google.gerrit.client.extensions.TopMenuList;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.RestApi;
import com.google.gwt.user.client.rpc.AsyncCallback;
@@ -27,4 +28,8 @@ public class ConfigServerApi {
public static void capabilities(AsyncCallback<NativeMap<CapabilityInfo>> cb) {
new RestApi("/config/server/capabilities/").get(cb);
}
public static void topMenus(AsyncCallback<TopMenuList> cb) {
new RestApi("/config/server/top-menus").get(cb);
}
}

View File

@@ -0,0 +1,28 @@
// Copyright (C) 2013 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.extensions;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
public class TopMenu extends JavaScriptObject {
protected TopMenu() {
}
public final native String getName() /*-{ return this.name; }-*/;
public final native JsArray<TopMenuItem> getItems() /*-{ return this.items; }-*/;
}

View File

@@ -0,0 +1,26 @@
// Copyright (C) 2013 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.extensions;
import com.google.gwt.core.client.JavaScriptObject;
public class TopMenuItem extends JavaScriptObject {
public final native String getName() /*-{ return this.name; }-*/;
public final native String getUrl() /*-{ return this.url; }-*/;
public final native String getTarget() /*-{ return this.target; }-*/;
protected TopMenuItem() {
}
}

View File

@@ -0,0 +1,23 @@
// Copyright (C) 2013 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.extensions;
import com.google.gwt.core.client.JsArray;
public class TopMenuList extends JsArray<TopMenu> {
protected TopMenuList() {
}
}

View File

@@ -27,6 +27,7 @@ import com.google.gerrit.extensions.events.ProjectDeletedListener;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.webui.TopMenu;
import com.google.gerrit.rules.PrologModule;
import com.google.gerrit.rules.RulesCache;
import com.google.gerrit.server.AnonymousUser;
@@ -239,6 +240,7 @@ public class GerritGlobalModule extends FactoryModule {
DynamicSet.setOf(binder(), MergeValidationListener.class);
DynamicItem.itemOf(binder(), AvatarProvider.class);
DynamicSet.setOf(binder(), LifecycleListener.class);
DynamicSet.setOf(binder(), TopMenu.class);
bind(AnonymousUser.class);

View File

@@ -0,0 +1,41 @@
// Copyright (C) 2013 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.server.config;
import com.google.common.collect.Lists;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.webui.TopMenu;
import com.google.inject.Inject;
import java.util.List;
class ListTopMenus implements RestReadView<ConfigResource> {
private final DynamicSet<TopMenu> extensions;
@Inject
ListTopMenus(DynamicSet<TopMenu> extensions) {
this.extensions = extensions;
}
@Override
public Object apply(ConfigResource resource) {
List<TopMenu.MenuEntry> entries = Lists.newArrayList();
for (TopMenu extension : extensions) {
entries.addAll(extension.getEntries());
}
return entries;
}
}

View File

@@ -14,8 +14,9 @@
package com.google.gerrit.server.config;
import static com.google.gerrit.server.config.ConfigResource.CONFIG_KIND;
import static com.google.gerrit.server.config.CapabilityResource.CAPABILITY_KIND;
import static com.google.gerrit.server.config.ConfigResource.CONFIG_KIND;
import static com.google.gerrit.server.config.TopMenuResource.TOP_MENU_KIND;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.RestApiModule;
@@ -24,8 +25,10 @@ public class Module extends RestApiModule {
@Override
protected void configure() {
DynamicMap.mapOf(binder(), CONFIG_KIND);
DynamicMap.mapOf(binder(), TOP_MENU_KIND);
DynamicMap.mapOf(binder(), CAPABILITY_KIND);
child(CONFIG_KIND, "capabilities").to(CapabilitiesCollection.class);
child(CONFIG_KIND, "top-menus").to(TopMenuCollection.class);
get(CONFIG_KIND, "version").to(GetVersion.class);
}
}

View File

@@ -0,0 +1,52 @@
// Copyright (C) 2013 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.server.config;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.inject.Inject;
import com.google.inject.Provider;
class TopMenuCollection implements
ChildCollection<ConfigResource, TopMenuResource> {
private final DynamicMap<RestView<TopMenuResource>> views;
private final Provider<ListTopMenus> list;
@Inject
TopMenuCollection(DynamicMap<RestView<TopMenuResource>> views,
Provider<ListTopMenus> list) {
this.views = views;
this.list = list;
}
@Override
public RestView<ConfigResource> list() throws ResourceNotFoundException {
return list.get();
}
@Override
public TopMenuResource parse(ConfigResource parent, IdString id)
throws ResourceNotFoundException {
throw new ResourceNotFoundException(id);
}
@Override
public DynamicMap<RestView<TopMenuResource>> views() {
return views;
}
}

View File

@@ -0,0 +1,23 @@
// Copyright (C) 2013 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.server.config;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.inject.TypeLiteral;
public class TopMenuResource extends ConfigResource {
public static final TypeLiteral<RestView<TopMenuResource>> TOP_MENU_KIND =
new TypeLiteral<RestView<TopMenuResource>>() {};
}