// 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.httpd.template; import com.google.common.base.Strings; import com.google.gerrit.httpd.HtmlDomUtil; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; import com.google.inject.Inject; import com.google.inject.Singleton; import org.eclipse.jgit.lib.Config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import java.io.File; import java.io.IOException; @Singleton public class SiteHeaderFooter { private static final Logger log = LoggerFactory.getLogger(SiteHeaderFooter.class); private final boolean refreshHeaderFooter; private final SitePaths sitePaths; private volatile Template template; @Inject SiteHeaderFooter(@GerritServerConfig Config cfg, SitePaths sitePaths) { this.refreshHeaderFooter = cfg.getBoolean("site", "refreshHeaderFooter", true); this.sitePaths = sitePaths; Template t = new Template(sitePaths); try { t.load(); } catch (IOException e) { log.warn("Cannot load site header or footer", e); } template = t; } public Document parse(Class<?> clazz, String name) throws IOException { Template t = template; if (refreshHeaderFooter && t.isStale()) { t = new Template(sitePaths); try { t.load(); template = t; } catch (IOException e) { log.warn("Cannot refresh site header or footer", e); t = template; } } Document doc = HtmlDomUtil.parseFile(clazz, name); injectCss(doc, "gerrit_sitecss", t.css); injectXml(doc, "gerrit_header", t.header); injectXml(doc, "gerrit_footer", t.footer); return doc; } private void injectCss(Document doc, String id, String content) { Element e = HtmlDomUtil.find(doc, id); if (e != null) { if (!Strings.isNullOrEmpty(content)) { while (e.getFirstChild() != null) { e.removeChild(e.getFirstChild()); } e.removeAttribute("id"); e.appendChild(doc.createCDATASection("\n" + content + "\n")); } else { e.getParentNode().removeChild(e); } } } private void injectXml(Document doc, String id, Element d) { Element e = HtmlDomUtil.find(doc, id); if (e != null) { if (d != null) { while (e.getFirstChild() != null) { e.removeChild(e.getFirstChild()); } e.appendChild(doc.importNode(d, true)); } else { e.getParentNode().removeChild(e); } } } private static class Template { private final FileInfo cssFile; private final FileInfo headerFile; private final FileInfo footerFile; String css; Element header; Element footer; Template(SitePaths site) { cssFile = new FileInfo(site.site_css); headerFile = new FileInfo(site.site_header); footerFile = new FileInfo(site.site_footer); } void load() throws IOException { css = HtmlDomUtil.readFile( cssFile.path.getParentFile(), cssFile.path.getName()); header = readXml(headerFile); footer = readXml(footerFile); } boolean isStale() { return cssFile.isStale() || headerFile.isStale() || footerFile.isStale(); } private static Element readXml(FileInfo src) throws IOException { Document d = HtmlDomUtil.parseFile(src.path); return d != null ? d.getDocumentElement() : null; } } private static class FileInfo { final File path; final long time; FileInfo(File p) { path = p; time = path.lastModified(); } boolean isStale() { return time != path.lastModified(); } } }