Line data Source code
1 : // Copyright (C) 2013 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.httpd.template; 16 : 17 : import static com.google.gerrit.common.FileUtil.lastModified; 18 : 19 : import com.google.common.base.Strings; 20 : import com.google.common.flogger.FluentLogger; 21 : import com.google.gerrit.common.Nullable; 22 : import com.google.gerrit.httpd.HtmlDomUtil; 23 : import com.google.gerrit.server.config.GerritServerConfig; 24 : import com.google.gerrit.server.config.SitePaths; 25 : import com.google.inject.Inject; 26 : import com.google.inject.Singleton; 27 : import java.io.IOException; 28 : import java.nio.file.Path; 29 : import org.eclipse.jgit.lib.Config; 30 : import org.w3c.dom.Document; 31 : import org.w3c.dom.Element; 32 : 33 : @Singleton 34 : public class SiteHeaderFooter { 35 99 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 36 : 37 : private final boolean refreshHeaderFooter; 38 : private final SitePaths sitePaths; 39 : private volatile Template template; 40 : 41 : @Inject 42 99 : SiteHeaderFooter(@GerritServerConfig Config cfg, SitePaths sitePaths) { 43 99 : this.refreshHeaderFooter = cfg.getBoolean("site", "refreshHeaderFooter", true); 44 99 : this.sitePaths = sitePaths; 45 : 46 : try { 47 99 : Template t = new Template(sitePaths); 48 99 : t.load(); 49 99 : template = t; 50 0 : } catch (IOException e) { 51 0 : logger.atWarning().withCause(e).log("Cannot load site header or footer"); 52 99 : } 53 99 : } 54 : 55 : public Document parse(Class<?> clazz, String name) throws IOException { 56 0 : Template t = template; 57 0 : if (refreshHeaderFooter && t.isStale()) { 58 0 : t = new Template(sitePaths); 59 : try { 60 0 : t.load(); 61 0 : template = t; 62 0 : } catch (IOException e) { 63 0 : logger.atWarning().withCause(e).log("Cannot refresh site header or footer"); 64 0 : t = template; 65 0 : } 66 : } 67 : 68 0 : Document doc = HtmlDomUtil.parseFile(clazz, name); 69 0 : injectCss(doc, "gerrit_sitecss", t.css); 70 0 : injectXml(doc, "gerrit_header", t.header); 71 0 : injectXml(doc, "gerrit_footer", t.footer); 72 0 : return doc; 73 : } 74 : 75 : private void injectCss(Document doc, String id, String content) { 76 0 : Element e = HtmlDomUtil.find(doc, id); 77 0 : if (e != null) { 78 0 : if (!Strings.isNullOrEmpty(content)) { 79 0 : while (e.getFirstChild() != null) { 80 0 : e.removeChild(e.getFirstChild()); 81 : } 82 0 : e.removeAttribute("id"); 83 0 : e.appendChild(doc.createCDATASection("\n" + content + "\n")); 84 : } else { 85 0 : e.getParentNode().removeChild(e); 86 : } 87 : } 88 0 : } 89 : 90 : private void injectXml(Document doc, String id, Element d) { 91 0 : Element e = HtmlDomUtil.find(doc, id); 92 0 : if (e != null) { 93 0 : if (d != null) { 94 0 : while (e.getFirstChild() != null) { 95 0 : e.removeChild(e.getFirstChild()); 96 : } 97 0 : e.appendChild(doc.importNode(d, true)); 98 : } else { 99 0 : e.getParentNode().removeChild(e); 100 : } 101 : } 102 0 : } 103 : 104 : private static class Template { 105 : private final FileInfo cssFile; 106 : private final FileInfo headerFile; 107 : private final FileInfo footerFile; 108 : 109 : String css; 110 : Element header; 111 : Element footer; 112 : 113 99 : Template(SitePaths site) { 114 99 : cssFile = new FileInfo(site.site_css); 115 99 : headerFile = new FileInfo(site.site_header); 116 99 : footerFile = new FileInfo(site.site_footer); 117 99 : } 118 : 119 : void load() throws IOException { 120 99 : css = HtmlDomUtil.readFile(cssFile.path.getParent(), cssFile.path.getFileName().toString()); 121 99 : header = readXml(headerFile); 122 99 : footer = readXml(footerFile); 123 99 : } 124 : 125 : boolean isStale() { 126 0 : return cssFile.isStale() || headerFile.isStale() || footerFile.isStale(); 127 : } 128 : 129 : @Nullable 130 : private static Element readXml(FileInfo src) throws IOException { 131 99 : Document d = HtmlDomUtil.parseFile(src.path); 132 99 : return d != null ? d.getDocumentElement() : null; 133 : } 134 : } 135 : 136 : private static class FileInfo { 137 : final Path path; 138 : final long time; 139 : 140 99 : FileInfo(Path p) { 141 99 : path = p; 142 99 : time = lastModified(p); 143 99 : } 144 : 145 : boolean isStale() { 146 0 : return time != lastModified(path); 147 : } 148 : } 149 : }