Enhance plugin extension panels to allow ordering per a config
Enhance plugin extension panels to allow ordering to be specified in AllProjects projects config so that ordering can be defined rather than determined by the order in which plugins are loaded. The interface change is backwards compatible at runtime because the extension panel implementation will correctly handle the null value for the panel name. Change-Id: I51f3ac5a18b504daa5bf55d92cc33621d2247082
This commit is contained in:
@@ -1209,6 +1209,7 @@ public class MyPlugin extends PluginEntryPoint {
|
||||
@Override
|
||||
public void onPluginLoad() {
|
||||
Plugin.get().panel(GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK,
|
||||
"my_panel_name",
|
||||
new Panel.EntryPoint() {
|
||||
@Override
|
||||
public void onLoad(Panel panel) {
|
||||
@@ -1220,6 +1221,23 @@ public class MyPlugin extends PluginEntryPoint {
|
||||
}
|
||||
----
|
||||
|
||||
Change Screen panel ordering may be specified in the
|
||||
project config. Values may be either "plugin name" or
|
||||
"plugin name"."panel name".
|
||||
Panels not specified in the config will be added
|
||||
to the end in load order. Panels specified in the config that
|
||||
are not found will be ignored.
|
||||
|
||||
Example config:
|
||||
----
|
||||
[extension-panels "CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK"]
|
||||
panel = helloworld.change_id
|
||||
panel = myotherplugin
|
||||
panel = myplugin.my_panel_name
|
||||
----
|
||||
|
||||
|
||||
|
||||
[[actions]]
|
||||
=== Actions
|
||||
|
||||
|
@@ -41,6 +41,8 @@ public class ConfigInfo {
|
||||
public Map<String, CommentLinkInfo> commentlinks;
|
||||
public ThemeInfo theme;
|
||||
|
||||
public Map<String, List<String>> extensionPanelNames;
|
||||
|
||||
public static class InheritedBooleanInfo {
|
||||
public Boolean value;
|
||||
public InheritableBoolean configuredValue;
|
||||
|
@@ -100,9 +100,9 @@ public class ApiGlue {
|
||||
var s = new SettingsScreenDefinition(p,m,c);
|
||||
(this.settingsScreens[n] || (this.settingsScreens[n]=[])).push(s);
|
||||
},
|
||||
panel: function(i,c){this._panel(this.getPluginName(),i,c)},
|
||||
_panel: function(n,i,c){
|
||||
var p = new PanelDefinition(n,c);
|
||||
panel: function(i,c,n){this._panel(this.getPluginName(),i,c,n)},
|
||||
_panel: function(n,i,c,x){
|
||||
var p = new PanelDefinition(n,c,x);
|
||||
(this.panels[i] || (this.panels[i]=[])).push(p);
|
||||
},
|
||||
|
||||
|
@@ -22,7 +22,10 @@ import com.google.gwt.dom.client.Element;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.SimplePanel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -32,13 +35,18 @@ public class ExtensionPanel extends FlowPanel {
|
||||
private final List<Context> contexts;
|
||||
|
||||
public ExtensionPanel(GerritUiExtensionPoint extensionPoint) {
|
||||
this.extensionPoint = extensionPoint;
|
||||
this.contexts = create();
|
||||
this(extensionPoint, new ArrayList<String>());
|
||||
}
|
||||
|
||||
private List<Context> create() {
|
||||
List<Context> contexts = new ArrayList<>();
|
||||
for (Definition def : Natives.asList(Definition.get(extensionPoint.name()))) {
|
||||
public ExtensionPanel(GerritUiExtensionPoint extensionPoint,
|
||||
List<String> panelNames) {
|
||||
this.extensionPoint = extensionPoint;
|
||||
this.contexts = create(panelNames);
|
||||
}
|
||||
|
||||
private List<Context> create(List<String> panelNames) {
|
||||
List<Context> contexts = new ArrayList<Context>();
|
||||
for (Definition def : getOrderedDefs(panelNames)) {
|
||||
SimplePanel p = new SimplePanel();
|
||||
add(p);
|
||||
contexts.add(Context.create(def, p));
|
||||
@@ -46,6 +54,45 @@ public class ExtensionPanel extends FlowPanel {
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private List<Definition> getOrderedDefs(List<String> panelNames) {
|
||||
if (panelNames == null) {
|
||||
panelNames = Collections.emptyList();
|
||||
}
|
||||
Map<String, List<Definition>> defsOrderedByName =
|
||||
new LinkedHashMap<String, List<Definition>>();
|
||||
for (String name : panelNames) {
|
||||
defsOrderedByName.put(name, new ArrayList<Definition>());
|
||||
}
|
||||
for (Definition def : Natives.asList(
|
||||
Definition.get(extensionPoint.name()))) {
|
||||
addDef(def, defsOrderedByName);
|
||||
}
|
||||
List<Definition> orderedDefs = new ArrayList<Definition>();
|
||||
for (List<Definition> defList : defsOrderedByName.values()) {
|
||||
orderedDefs.addAll(defList);
|
||||
}
|
||||
return orderedDefs;
|
||||
}
|
||||
|
||||
private static void addDef(Definition def,
|
||||
Map<String, List<Definition>> defsOrderedByName) {
|
||||
String panelName = def.getPanelName();
|
||||
if (panelName.equals(def.getPluginName() + ".undefined")) {
|
||||
/* Handle a partially undefined panel name from the
|
||||
javascript layer by generating a random panel name.
|
||||
This maintains support for panels that do not provide a name. */
|
||||
panelName = def.getPluginName() + "." + Long.toHexString(
|
||||
Double.doubleToLongBits(Math.random()));
|
||||
}
|
||||
if (defsOrderedByName.containsKey(panelName)) {
|
||||
defsOrderedByName.get(panelName).add(def);
|
||||
} else if (defsOrderedByName.containsKey(def.getPluginName())) {
|
||||
defsOrderedByName.get(def.getPluginName()).add(def);
|
||||
} else {
|
||||
defsOrderedByName.put(panelName, Collections.singletonList(def));
|
||||
}
|
||||
}
|
||||
|
||||
public void put(GerritUiExtensionPoint.Key key, String value) {
|
||||
for (Context ctx : contexts) {
|
||||
ctx.put(key.name(), value);
|
||||
@@ -103,9 +150,10 @@ public class ExtensionPanel extends FlowPanel {
|
||||
static final JavaScriptObject TYPE = init();
|
||||
|
||||
private static native JavaScriptObject init() /*-{
|
||||
function PanelDefinition(n, c) {
|
||||
function PanelDefinition(n, c, x) {
|
||||
this.pluginName = n;
|
||||
this.onLoad = c;
|
||||
this.name = x;
|
||||
};
|
||||
return PanelDefinition;
|
||||
}-*/;
|
||||
@@ -113,6 +161,9 @@ public class ExtensionPanel extends FlowPanel {
|
||||
static native JsArray<Definition> get(String i) /*-{ return $wnd.Gerrit.panels[i] || [] }-*/;
|
||||
|
||||
protected Definition() {}
|
||||
|
||||
public final native String getPanelName() /*-{ return this.pluginName + "." + this.name; }-*/;
|
||||
public final native String getPluginName() /*-{ return this.pluginName; }-*/;
|
||||
}
|
||||
|
||||
static class Context extends JavaScriptObject {
|
||||
|
@@ -68,7 +68,7 @@ final class Plugin extends JavaScriptObject {
|
||||
onAction: function(t,n,c){G._onAction(this.name,t,n,c)},
|
||||
screen: function(p,c){G._screen(this.name,p,c)},
|
||||
settingsScreen: function(p,m,c){G._settingsScreen(this.name,p,m,c)},
|
||||
panel: function(i,c){G._panel(this.name,i,c)},
|
||||
panel: function(i,c,n){G._panel(this.name,i,c,n)},
|
||||
|
||||
url: function (u){return G.url(this._url(u))},
|
||||
get: function(u,b){@com.google.gerrit.client.api.ActionContext::get(
|
||||
|
@@ -309,8 +309,7 @@ public class ChangeScreen extends Screen {
|
||||
@Override
|
||||
public void onSuccess(final ChangeInfo info) {
|
||||
info.init();
|
||||
addExtensionPoints(info, initCurrentRevision(info));
|
||||
|
||||
initCurrentRevision(info);
|
||||
final RevisionInfo rev = info.revision(revision);
|
||||
CallbackGroup group = new CallbackGroup();
|
||||
loadCommit(rev, group);
|
||||
@@ -379,7 +378,8 @@ public class ChangeScreen extends Screen {
|
||||
return resolveRevisionToDisplay(info);
|
||||
}
|
||||
|
||||
private void addExtensionPoints(ChangeInfo change, RevisionInfo rev) {
|
||||
private void addExtensionPoints(ChangeInfo change, RevisionInfo rev,
|
||||
Entry result) {
|
||||
addExtensionPoint(GerritUiExtensionPoint.CHANGE_SCREEN_HEADER, headerExtension, change, rev);
|
||||
addExtensionPoint(
|
||||
GerritUiExtensionPoint.CHANGE_SCREEN_HEADER_RIGHT_OF_BUTTONS,
|
||||
@@ -392,7 +392,10 @@ public class ChangeScreen extends Screen {
|
||||
change,
|
||||
rev);
|
||||
addExtensionPoint(
|
||||
GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK, changeExtension, change, rev);
|
||||
GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK,
|
||||
changeExtension, change, rev,
|
||||
result.getExtensionPanelNames(
|
||||
GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK.toString()));
|
||||
addExtensionPoint(
|
||||
GerritUiExtensionPoint.CHANGE_SCREEN_BELOW_RELATED_INFO_BLOCK,
|
||||
relatedExtension,
|
||||
@@ -408,13 +411,19 @@ public class ChangeScreen extends Screen {
|
||||
}
|
||||
|
||||
private void addExtensionPoint(
|
||||
GerritUiExtensionPoint extensionPoint, Panel p, ChangeInfo change, RevisionInfo rev) {
|
||||
ExtensionPanel extensionPanel = new ExtensionPanel(extensionPoint);
|
||||
GerritUiExtensionPoint extensionPoint, Panel p, ChangeInfo change,
|
||||
RevisionInfo rev, List<String> panelNames) {
|
||||
ExtensionPanel extensionPanel = new ExtensionPanel(extensionPoint, panelNames);
|
||||
extensionPanel.putObject(GerritUiExtensionPoint.Key.CHANGE_INFO, change);
|
||||
extensionPanel.putObject(GerritUiExtensionPoint.Key.REVISION_INFO, rev);
|
||||
p.add(extensionPanel);
|
||||
}
|
||||
|
||||
private void addExtensionPoint(
|
||||
GerritUiExtensionPoint extensionPoint, Panel p, ChangeInfo change, RevisionInfo rev) {
|
||||
addExtensionPoint(extensionPoint, p, change, rev, Collections.emptyList());
|
||||
}
|
||||
|
||||
private boolean enableSignedPush() {
|
||||
return Gerrit.info().receive().enableSignedPush();
|
||||
}
|
||||
@@ -1031,6 +1040,14 @@ public class ChangeScreen extends Screen {
|
||||
loadRevisionInfo();
|
||||
}
|
||||
});
|
||||
ConfigInfoCache.get(
|
||||
info.projectNameKey(),
|
||||
new GerritCallback<Entry>() {
|
||||
@Override
|
||||
public void onSuccess(Entry entry) {
|
||||
addExtensionPoints(info, rev, entry);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateToken(ChangeInfo info, DiffObject base, RevisionInfo rev) {
|
||||
|
@@ -116,6 +116,8 @@ public class ConfigInfo extends JavaScriptObject {
|
||||
|
||||
final native ThemeInfo theme() /*-{ return this.theme; }-*/;
|
||||
|
||||
final native NativeMap<JsArrayString> extensionPanelNames() /*-{ return this.extension_panel_names; }-*/;
|
||||
|
||||
protected ConfigInfo() {}
|
||||
|
||||
static class CommentLinkInfo extends JavaScriptObject {
|
||||
|
@@ -16,12 +16,16 @@ package com.google.gerrit.client.projects;
|
||||
|
||||
import com.google.gerrit.client.changes.ChangeApi;
|
||||
import com.google.gerrit.client.info.ChangeInfo;
|
||||
import com.google.gerrit.client.rpc.Natives;
|
||||
import com.google.gerrit.client.ui.CommentLinkProcessor;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.Project;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** Cache of {@link ConfigInfo} objects by project name. */
|
||||
@@ -48,6 +52,10 @@ public class ConfigInfoCache {
|
||||
public ThemeInfo getTheme() {
|
||||
return info.theme();
|
||||
}
|
||||
|
||||
public List<String> getExtensionPanelNames(String extensionPoint) {
|
||||
return Natives.asList(info.extensionPanelNames().get(extensionPoint));
|
||||
}
|
||||
}
|
||||
|
||||
public static void get(Project.NameKey name, AsyncCallback<Entry> cb) {
|
||||
|
@@ -121,12 +121,16 @@ public final class Plugin extends JavaScriptObject {
|
||||
*
|
||||
* @param extensionPoint the UI extension point for which the panel should be registered.
|
||||
* @param entry callback function invoked to create the panel widgets.
|
||||
* @param name the name of the panel which can be used to specify panel
|
||||
* ordering via project config
|
||||
*/
|
||||
public void panel(GerritUiExtensionPoint extensionPoint, Panel.EntryPoint entry) {
|
||||
panel(extensionPoint.name(), wrap(entry));
|
||||
public final void panel(GerritUiExtensionPoint extensionPoint,
|
||||
Panel.EntryPoint entry, String name) {
|
||||
panel(extensionPoint.name(), wrap(entry), name);
|
||||
}
|
||||
|
||||
private native void panel(String i, JavaScriptObject e) /*-{ this.panel(i, e) }-*/;
|
||||
private native void panel(String i, JavaScriptObject e, String n)
|
||||
/*-{ this.panel(i, e, n) }-*/;
|
||||
|
||||
protected Plugin() {}
|
||||
|
||||
|
@@ -166,6 +166,9 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
private static final SubmitType DEFAULT_SUBMIT_ACTION = SubmitType.MERGE_IF_NECESSARY;
|
||||
private static final ProjectState DEFAULT_STATE_VALUE = ProjectState.ACTIVE;
|
||||
|
||||
private static final String EXTENSION_PANELS = "extension-panels";
|
||||
private static final String KEY_PANEL = "panel";
|
||||
|
||||
private Project.NameKey projectName;
|
||||
private Project project;
|
||||
private AccountsSection accountsSection;
|
||||
@@ -186,6 +189,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
private Set<String> sectionsWithUnknownPermissions;
|
||||
private boolean hasLegacyPermissions;
|
||||
private boolean enableReviewerByEmail;
|
||||
private Map<String, List<String>> extensionPanelSections;
|
||||
|
||||
public static ProjectConfig read(MetaDataUpdate update)
|
||||
throws IOException, ConfigInvalidException {
|
||||
@@ -201,6 +205,10 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
return r;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getExtensionPanelSections() {
|
||||
return extensionPanelSections;
|
||||
}
|
||||
|
||||
public static CommentLinkInfoImpl buildCommentLink(Config cfg, String name, boolean allowRaw)
|
||||
throws IllegalArgumentException {
|
||||
String match = cfg.getString(COMMENTLINK, name, KEY_MATCH);
|
||||
@@ -541,6 +549,7 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
loadPluginSections(rc);
|
||||
loadReceiveSection(rc);
|
||||
loadReviewerSection(rc);
|
||||
loadExtensionPanelSections(rc);
|
||||
}
|
||||
|
||||
private void loadAccountsSection(Config rc, Map<String, GroupReference> groupsByName) {
|
||||
@@ -549,6 +558,23 @@ public class ProjectConfig extends VersionedMetaData implements ValidationError.
|
||||
loadPermissionRules(rc, ACCOUNTS, null, KEY_SAME_GROUP_VISIBILITY, groupsByName, false));
|
||||
}
|
||||
|
||||
private void loadExtensionPanelSections(Config rc) throws IOException {
|
||||
Map<String, String> lowerNames = Maps.newHashMapWithExpectedSize(2);
|
||||
extensionPanelSections = Maps.newLinkedHashMap();
|
||||
for (String name : rc.getSubsections(EXTENSION_PANELS)) {
|
||||
String lower = name.toLowerCase();
|
||||
if (lowerNames.containsKey(lower)) {
|
||||
error(new ValidationError(PROJECT_CONFIG, String.format(
|
||||
"Extension Panels \"%s\" conflicts with \"%s\"",
|
||||
name, lowerNames.get(lower))));
|
||||
}
|
||||
lowerNames.put(lower, name);
|
||||
extensionPanelSections.put(name,
|
||||
new ArrayList<String>(Arrays.asList(
|
||||
rc.getStringList(EXTENSION_PANELS, name, KEY_PANEL))));
|
||||
}
|
||||
}
|
||||
|
||||
private void loadContributorAgreements(Config rc, Map<String, GroupReference> groupsByName) {
|
||||
contributorAgreements = new HashMap<>();
|
||||
for (String name : rc.getSubsections(CONTRIBUTOR_AGREEMENT)) {
|
||||
|
@@ -131,6 +131,8 @@ public class ConfigInfoImpl extends ConfigInfo {
|
||||
actions.put(d.getId(), new ActionInfo(d));
|
||||
}
|
||||
this.theme = projectState.getTheme();
|
||||
|
||||
this.extensionPanelNames = projectState.getConfig().getExtensionPanelSections();
|
||||
}
|
||||
|
||||
private Map<String, Map<String, ConfigParameterInfo>> getPluginConfig(
|
||||
|
Reference in New Issue
Block a user