d2487acc53819ca9b3a5f6df519408c3e4293e93 tdreszer Wed Oct 6 14:22:33 2010 -0700 Make cgi references to style sheets (css files) actually point to timestamped link. Common code is also used for js files diff --git src/hg/lib/web.c src/hg/lib/web.c index 97c254f..8dd0080 100644 --- src/hg/lib/web.c +++ src/hg/lib/web.c @@ -1,10 +1,12 @@ #include "common.h" +#include #include "hCommon.h" #include "obscure.h" #include "dnautil.h" #include "errabort.h" #include "htmshell.h" #include "web.h" +#include "hPrint.h" #include "hdb.h" #include "hui.h" #include "hgConfig.h" @@ -147,9 +149,8 @@ } *ptr1 = 0; htmlTextOut(newString); - puts( - " " "\n" - " " "\n"); + printf(" \n "); + webIncludeResourceFile("HGStyle.css"); if (extraStyle != NULL) puts(extraStyle); printf("" "\n" @@ -1201,3 +1202,129 @@ finishPartialTable(rowIx, itemPos, maxPerRow, webPrintLinkCellStart); } +char *webTimeStampedLinkToResource(char *fileName, boolean wrapInHtml) +// Returns full path of timestamped link to the requested resource file (js, or css). +// If wrapInHtml, then returns link embedded in style or script html. Free after use. +{ +char baseName[PATH_LEN]; +char extension[FILEEXT_LEN]; +splitPath(fileName, NULL, baseName, extension); +boolean js = sameString(".js",extension); +boolean style = !js && sameString(".css",extension); +boolean image = !js && !style && (sameString(".png",extension) || sameString(".jpg",extension) || sameString(".gif",extension)); +if(!js && !style && !image) + errAbort("webTimeStampedLinkToResource: unknown resource type for %s.\n", fileName); + +// Build and verify directory +char *dirName = NULL; +if (js) + dirName = cfgOptionDefault("browser.javaScriptDir", "js"); +else if (style) + dirName = "style"; +else + dirName = "style/images"; +struct dyString *fullDirName = NULL; +char *docRoot = hDocumentRoot(); +if(docRoot != NULL) // tolerate missing docRoot (i.e. when running from command line) + fullDirName = dyStringCreate("%s/%s", docRoot, dirName); +else + fullDirName = dyStringCreate("%s", dirName); +if(!fileExists(dyStringContents(fullDirName))) + errAbort("webTimeStampedLinkToResource: dir: %s doesn't exist.\n", dyStringContents(fullDirName)); + +// build and verify real path to file +struct dyString *realFileName = dyStringCreate("%s/%s", dyStringContents(fullDirName), fileName); +if(!fileExists(dyStringContents(realFileName))) + errAbort("webTimeStampedLinkToResource: file: %s doesn't exist.\n", dyStringContents(realFileName)); + +// build and verify link path including timestamp in the form of dir/baseName-timeStamp.ext +long mtime = fileModTime(dyStringContents(realFileName)); // We add mtime to create a pseudo-version; this forces browsers to reload css/js file when it changes +struct dyString *linkWithTimestamp = dyStringCreate("%s/%s-%ld%s", dyStringContents(fullDirName), baseName, mtime, extension); + +// If link does not exist, then create it !! +if(!fileExists(dyStringContents(linkWithTimestamp))) + { + // The versioned copy should be created by the install process; however, mirrors may fail + // to preserve mtime's when copying over the javascript/css files, in which case the + // versioned softlinks won't match the real file; in that case, we try to create + // the versioned links on the fly (which requires write access to the javascript or style directory!). + + // Remove older links + struct dyString *pattern = dyStringCreate("%s-[0-9]+\\%s", baseName, extension); + struct slName *file, *files = listDirRegEx(dyStringContents(fullDirName), dyStringContents(pattern), REG_EXTENDED); + struct dyString *oldLink = dyStringNew(256); + for (file = files; file != NULL; file = file->next) + { + dyStringClear(oldLink); + dyStringPrintf(oldLink, "%s/%s", dyStringContents(fullDirName), file->name); + unlink(dyStringContents(oldLink)); + } + dyStringFree(&oldLink); + slFreeList(&files); + dyStringFree(&pattern); + + // Create new link + if(symlink(dyStringContents(realFileName), dyStringContents(linkWithTimestamp))) + { + int err = errno; + errAbort("webTimeStampedLinkToResource: symlink failed: errno: %d (%s); the directory '%s' must be writeable by user '%s'; alternatively, the installation process must create the versioned files\n", + err, strerror(err), dyStringContents(fullDirName), getUser()); + } + } +// Free up all that extra memory +dyStringFree(&realFileName); +dyStringFree(&fullDirName); +char *linkFull = dyStringCannibalize(&linkWithTimestamp); +char *link = linkFull; +if (docRoot != NULL) + link = cloneString(linkFull + strlen(docRoot) + 1); +freeMem(linkFull); + +if (wrapInHtml) // wrapped for christmas + { + struct dyString *wrapped = dyStringNew(0); + if (js) + dyStringPrintf(wrapped,"\n", link); + else if (style) + dyStringPrintf(wrapped,"\n", link); + else // assume image! + dyStringPrintf(wrapped,"\n", link); // NOTE: perhaps it is better to errAbort! + freeMem(link); + link = dyStringCannibalize(&wrapped); + } + +return link; +} + +char *webTimeStampedLinkToResourceOnFirstCall(char *fileName, boolean wrapInHtml) +// If this is the first call, will +// Return full path of timestamped link to the requested resource file (js, or css). Free after use. +// else returns NULL. Useful to ensure multiple references to the same resource file are not made +{ +static struct hash *includedResourceFiles = NULL; +if(!includedResourceFiles) + includedResourceFiles = newHash(0); + +if(hashLookup(includedResourceFiles, fileName)) + return NULL; + +char * link = webTimeStampedLinkToResource(fileName,wrapInHtml); +if (link) + hashAdd(includedResourceFiles, fileName, link); +return link; +} + +boolean webIncludeResourceFile(char *fileName) +// Converts fileName to web Resource link and hPrintfs the html reference +// This only prints and returns TRUE on first call for this resource. +// The reference will be to a link with timestamp. +{ +char *link = webTimeStampedLinkToResourceOnFirstCall(fileName,TRUE); +if (link) + { + hPrintf("%s",link); + freeMem(link); + return TRUE; + } +return FALSE; +}