7be0d4bee5fc92c426382f480e06254e60f8bb9e max Sat Jul 11 20:15:05 2026 -0700 Cache-bust menu-bar CSS/JS on static pages, refs #10138 hgMenubar.c emitted nice_menu.css and topLinks.js as bare URLs with no version query, so browsers kept serving a stale cached nice_menu.css after commit c5a326f3 added the top-right links. The new #trToggle hamburger is hidden by a rule in that CSS, so on static pages (e.g. the homepage) it showed on first load and only disappeared after a manual force-reload. Append ?v=<file mtime> to those two URLs, the same idea CGIs get from webTimeStampedLinkToResource (which can't be reused here: it emits a ../-relative URL that is wrong for the arbitrary-depth static pages this menu bar is SSI-included into). Uses the existing fileModTime(), guarded by fileExists so a missing resource degrades to ?v=0 rather than aborting the menu bar. diff --git src/hg/hgMenubar/hgMenubar.c src/hg/hgMenubar/hgMenubar.c index f3fcc977176..2302170fca7 100644 --- src/hg/hgMenubar/hgMenubar.c +++ src/hg/hgMenubar/hgMenubar.c @@ -1,28 +1,29 @@ /* * This CGI is used by static html pages to show a menu bar. * On an Apache with activated SSI, a html statement like * <!--#include virtual="../cgi-bin/hgMenubar"--> * will include the menu bar into a static page. */ #include "common.h" #include "cheapcgi.h" #include "dystring.h" #include "filePath.h" #include "linefile.h" #include "jsHelper.h" #include "wikiLink.h" +#include "portable.h" #define CGI_NAME "cgi-bin/hgMenubar" #define NAVBAR_INC_PATH "/inc/globalNavBar.inc" #define OLD_HREF "href=\"../" char* errMessage; char *loginLinkHtml() /* Return HTML <li> for the top-right Login menu item, or "" if no login system is configured. * Static-page variant: the logged-out link uses href="../cgi-bin/hgSession" so the caller's * OLD_HREF substitution rewrites it to a page-relative path; logged-in account-dialog URLs come * from wikiLink and are absolute. topLinks.js turns the logged-in item into a dialog. */ { if (!(loginSystemEnabled() || wikiLinkEnabled())) return cloneString(""); @@ -43,56 +44,69 @@ dyStringPrintf(dy, "<a class='topRightLink' href='#' id='loginLink' " "title='Account info and sign out' " "data-username=\"%s\" data-logouturl=\"%s\" data-changepwurl=\"%s\">%s</a>", userName, logoutUrl, changePwUrl ? changePwUrl : "", userName); } return dyStringCannibalize(&dy); } char *incFilePath(char *cgiPath, char *filePath, char *docRoot) /* Replace CGI_NAME in cgiPath with docRoot/filePath. filePath must begin with "/" eg "/inc/..." */ { char *incPath = replaceChars(cgiPath, "/"CGI_NAME, filePath); return catTwoStrings(docRoot, incPath); } -void printIncludes(char* baseDir) +void printIncludes(char* baseDir, char *docRoot) { +// Cache-buster for the menu-bar CSS/JS: append ?v=<file mtime> so browsers refetch these when +// they change instead of serving a stale cached copy (the CGIs get this from +// webTimeStampedLinkToResource, but that emits its own "../"-relative URL which is wrong for the +// arbitrary-depth static pages this menu bar is included into, so we reuse just its mtime idea). +// fileExists guards fileModTime, which would otherwise abort the menu bar on every static page if +// a resource were missing. +char jsPath[PATH_LEN], cssPath[PATH_LEN]; +safef(jsPath, sizeof jsPath, "%s/js/topLinks.js", docRoot); +safef(cssPath, sizeof cssPath, "%s/style/nice_menu.css", docRoot); +long jsVer = fileExists(jsPath) ? (long)fileModTime(jsPath) : 0; +long cssVer = fileExists(cssPath) ? (long)fileModTime(cssPath) : 0; + printf ("<noscript><div class='noscript'><div class='noscript-inner'><p><b>JavaScript is disabled in your web browser</b></p><p>You must have JavaScript enabled in your web browser to use the Genome Browser</p></div></div></noscript>\n"); printf ("<script type='text/javascript' SRC='%sjs/jquery.js'></script>\n", baseDir); printf ("<script type='text/javascript' SRC='%sjs/jquery.plugins.js'></script>\n", baseDir); printf("<script type='text/javascript' SRC='%s/js/utils.js'></script>\n", baseDir); -printf("<script type='text/javascript' SRC='%s/js/topLinks.js'></script>\n", baseDir); -printf ("<LINK rel='STYLESHEET' href='%sstyle/nice_menu.css' TYPE='text/css'>\n", baseDir); +printf("<script type='text/javascript' SRC='%s/js/topLinks.js?v=%ld'></script>\n", baseDir, jsVer); +printf ("<LINK rel='STYLESHEET' href='%sstyle/nice_menu.css?v=%ld' TYPE='text/css'>\n", baseDir, + cssVer); } void printMenuBar(char *cgiPath, char *docRoot, char *pagePath, char *filePath) { char *navBarLoc = incFilePath(cgiPath, filePath, docRoot); struct lineFile *menuFile = lineFileOpen(navBarLoc, TRUE); char* oldLine = NULL; int lineSize = 0; char *cgiContainerPath = replaceChars(cgiPath, CGI_NAME, ""); char *newPath = makeRelativePath(pagePath, cgiContainerPath); char *newHref = catTwoStrings("href=\"", newPath); printf ("Content-type: text/html\r\n\r\n"); if (sameString(filePath, NAVBAR_INC_PATH)) - printIncludes(newPath); + printIncludes(newPath, docRoot); while (lineFileNext(menuFile, &oldLine, &lineSize)) { // Not quite as robust as perl search and replace - no variable whitespace handling // Also lots of memory leakage - every line is reallocated and forgotten char *line = oldLine; // Fill the top-right link placeholders. Login shows the user or a link to hgSession; // the Share-a-link button is browser-only, so it is dropped on static pages. if (stringIn("<!-- LOGIN_LINK -->", line)) line = replaceChars(line, "<!-- LOGIN_LINK -->", loginLinkHtml()); if (stringIn("<!-- SHARE_LINK -->", line)) line = replaceChars(line, "<!-- SHARE_LINK -->", ""); char *newLine = replaceChars(line, OLD_HREF, newHref); printf("%s\n", newLine); }