Render per-project themes on the client side

Always include the relevant <style>/<div> tags in the host page, even
if they are empty. Have Gerrit store the sitewide values, and swap
them in and out when loading project-specific screens.

Change-Id: Iab6d2c91bf636ef361e2031dd39904e0c7e9bf41
This commit is contained in:
Dave Borowitz
2013-04-09 14:48:42 -07:00
parent 1e49e143ec
commit de117a33fb
12 changed files with 183 additions and 36 deletions

View File

@@ -21,4 +21,13 @@
<when-property-is name="user.agent" value="ie8"/>
</any>
</replace-with>
<replace-with class="com.google.gerrit.client.Themer.ThemerIE">
<when-type-is class="com.google.gerrit.client.Themer" />
<any>
<when-property-is name="user.agent" value="ie6"/>
<when-property-is name="user.agent" value="ie8"/>
<when-property-is name="user.agent" value="ie9"/>
</any>
</replace-with>
</module>

View File

@@ -95,6 +95,7 @@ public class Gerrit implements EntryPoint {
GWT.create(GerritResources.class);
public static final SystemInfoService SYSTEM_SVC;
public static final EventBus EVENT_BUS = GWT.create(SimpleEventBus.class);
public static Themer THEMER = GWT.create(Themer.class);
private static String myHost;
private static GerritConfig myConfig;
@@ -552,9 +553,17 @@ public class Gerrit implements EntryPoint {
if (signInAnchor != null) {
signInAnchor.setHref(loginRedirect(token));
}
saveDefaultTheme();
loadPlugins(hpd, token);
}
private void saveDefaultTheme() {
THEMER.init(Document.get().getElementById("gerrit_sitecss"),
Document.get().getElementById("gerrit_header"),
Document.get().getElementById("gerrit_footer"));
}
private void loadPlugins(HostPageData hpd, final String token) {
if (hpd.plugins != null) {
for (final String url : hpd.plugins) {

View File

@@ -0,0 +1,80 @@
// 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.git;
package com.google.gerrit.client;
import com.google.gerrit.client.projects.ThemeInfo;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.StyleElement;
public class Themer {
public static class ThemerIE extends Themer {
protected ThemerIE() {
}
@Override
protected String getCssText(StyleElement el) {
return el.getCssText();
}
@Override
protected void setCssText(StyleElement el, String css) {
el.setCssText(css);
}
}
protected StyleElement cssElement;
protected Element headerElement;
protected Element footerElement;
protected String cssText;
protected String headerHtml;
protected String footerHtml;
protected Themer() {
}
public void set(ThemeInfo theme) {
set(theme.css() != null ? theme.css() : cssText,
theme.header() != null ? theme.header() : headerHtml,
theme.footer() != null ? theme.footer() : footerHtml);
}
public void clear() {
set(cssText, headerHtml, footerHtml);
}
void init(Element css, Element header, Element footer) {
cssElement = StyleElement.as(css);
headerElement = header;
footerElement = footer;
cssText = getCssText(this.cssElement);
headerHtml = header.getInnerHTML();
footerHtml = footer.getInnerHTML();
}
protected String getCssText(StyleElement el) {
return el.getInnerHTML();
}
protected void setCssText(StyleElement el, String css) {
el.setInnerHTML(css);
}
private void set(String css, String header, String footer) {
setCssText(cssElement, css);
headerElement.setInnerHTML(header);
footerElement.setInnerHTML(footer);
}
}

View File

@@ -275,6 +275,7 @@ public class ChangeScreen extends Screen
@Override
public void onSuccess(ConfigInfoCache.Entry result) {
commentLinkProcessor = result.getCommentLinkProcessor();
setTheme(result.getTheme());
}
@Override

View File

@@ -185,6 +185,7 @@ public class PublishCommentScreen extends AccountScreen implements
@Override
public void onSuccess(ConfigInfoCache.Entry result) {
commentLinkProcessor = result.getCommentLinkProcessor();
setTheme(result.getTheme());
display(pubDetail);
}

View File

@@ -371,44 +371,39 @@ public abstract class PatchScreen extends Screen implements
fileList.movePointerTo(patchKey);
}
com.google.gwtjsonrpc.common.AsyncCallback<PatchScript> pscb =
new ScreenLoadCallback<PatchScript>(this) {
CallbackGroup cb = new CallbackGroup();
ConfigInfoCache.get(patchSetDetail.getProject(),
cb.add(new AsyncCallback<ConfigInfoCache.Entry>() {
@Override
protected void preDisplay(final PatchScript result) {
if (rpcSequence == rpcseq) {
onResult(result, isFirst);
}
public void onSuccess(ConfigInfoCache.Entry result) {
commentLinkProcessor = result.getCommentLinkProcessor();
contentTable.setCommentLinkProcessor(commentLinkProcessor);
setTheme(result.getTheme());
}
@Override
public void onFailure(final Throwable caught) {
if (rpcSequence == rpcseq) {
settingsPanel.setEnabled(true);
super.onFailure(caught);
}
public void onFailure(Throwable caught) {
// Handled by ScreenLoadCallback.onFailure.
}
};
if (commentLinkProcessor == null) {
// Fetch config in parallel if we haven't previously.
CallbackGroup cb = new CallbackGroup();
ConfigInfoCache.get(patchSetDetail.getProject(),
cb.add(new AsyncCallback<ConfigInfoCache.Entry>() {
@Override
public void onSuccess(ConfigInfoCache.Entry result) {
commentLinkProcessor = result.getCommentLinkProcessor();
contentTable.setCommentLinkProcessor(commentLinkProcessor);
}
}));
PatchUtil.DETAIL_SVC.patchScript(patchKey, idSideA, idSideB,
settingsPanel.getValue(), cb.addGwtjsonrpc(
new ScreenLoadCallback<PatchScript>(this) {
@Override
protected void preDisplay(final PatchScript result) {
if (rpcSequence == rpcseq) {
onResult(result, isFirst);
}
}
@Override
public void onFailure(Throwable caught) {
// Handled by ScreenLoadCallback.onFailure.
}
}));
pscb = cb.addGwtjsonrpc(pscb);
}
PatchUtil.DETAIL_SVC.patchScript(patchKey, idSideA, idSideB, //
settingsPanel.getValue(), pscb);
@Override
public void onFailure(final Throwable caught) {
if (rpcSequence == rpcseq) {
settingsPanel.setEnabled(true);
super.onFailure(caught);
}
}
}));
}
private void onResult(final PatchScript script, final boolean isFirst) {

View File

@@ -64,6 +64,8 @@ public class ConfigInfo extends JavaScriptObject {
return commentLinks;
}
final native ThemeInfo theme() /*-{ return this.theme; }-*/;
protected ConfigInfo() {
}

View File

@@ -42,6 +42,10 @@ public class ConfigInfoCache {
}
return commentLinkProcessor;
}
public ThemeInfo getTheme() {
return info.theme();
}
}
public static void get(Project.NameKey name, AsyncCallback<Entry> cb) {

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.server.git;
package com.google.gerrit.client.projects;
import com.google.gwt.core.client.JavaScriptObject;
public class ThemeInfo extends JavaScriptObject {
public final native String css() /*-{ return this.css; }-*/;
public final native String header() /*-{ return this.header; }-*/;
public final native String footer() /*-{ return this.footer; }-*/;
protected ThemeInfo() {
}
}

View File

@@ -15,6 +15,7 @@
package com.google.gerrit.client.ui;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.projects.ThemeInfo;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
@@ -41,6 +42,9 @@ public abstract class Screen extends View {
private String windowTitle;
private Widget titleWidget;
private ThemeInfo theme;
private boolean setTheme;
protected Screen() {
initWidget(new FlowPanel());
setStyleName(Gerrit.RESOURCES.css().screen());
@@ -54,6 +58,14 @@ public abstract class Screen extends View {
}
}
@Override
protected void onUnload() {
super.onUnload();
if (setTheme) {
Gerrit.THEMER.set(null);
}
}
public void registerKeys() {
}
@@ -124,6 +136,10 @@ public abstract class Screen extends View {
body.add(w);
}
protected void setTheme(final ThemeInfo t) {
theme = t;
}
/** Get the history token for this screen. */
public String getToken() {
return token;
@@ -167,5 +183,12 @@ public abstract class Screen extends View {
Gerrit.EVENT_BUS.fireEvent(new ScreenLoadEvent(this));
Gerrit.setQueryString(null);
registerKeys();
if (theme != null) {
Gerrit.THEMER.set(theme);
setTheme = true;
} else {
Gerrit.THEMER.clear();
}
}
}

View File

@@ -352,11 +352,9 @@ public class HostPageServlet extends HttpServlet {
String css = HtmlDomUtil.readFile(src.getParentFile(), src.getName());
if (css == null) {
banner.getParentNode().removeChild(banner);
return info;
}
banner.removeAttribute("id");
banner.appendChild(hostDoc.createCDATASection("\n" + css + "\n"));
return info;
}
@@ -375,7 +373,6 @@ public class HostPageServlet extends HttpServlet {
Document html = HtmlDomUtil.parseFile(src);
if (html == null) {
banner.getParentNode().removeChild(banner);
return info;
}

View File

@@ -31,7 +31,7 @@
})();
</script>
<script id="gerrit_hostpagedata"></script>
<style id="gerrit_sitecss" type="text/css"></style>
<style id="gerrit_sitecss" type="text/css"></style>
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>