c5a326f3cc42beca0b59c284a2819763548c166a
max
Wed Jul 8 05:31:13 2026 -0700
Add top-right "Share a link" and "Login" links to the menu bar, refs #10138
New js/topLinks.js drives two links at the top-right of the menu bar on all pages:
- Login: links to the login page when logged out; when logged in it shows the
username and opens an account dialog (My Sessions / My Custom Tracks /
My Track Hubs, change password, sign out).
- Share a link: on hgTracks, one click saves the current view as a session and
shows a copyable short link, with an optional "Specify name" rename step; works
logged-in or anonymously (reserved user "l"). On hgTrackUi and the hgc/hgGene
item-details popup it instead shares the page URL with the hgsid stripped and
the db argument kept.
hgSession gains two JSON endpoints (hgS_doSaveSessionJson, hgS_doRenameSessionJson)
that reuse saveCartAsSession() and addSessionLink(). The menu bar is patched in
lib/web.c (CGI pages) and hgMenubar.c (static pages) via comment placeholders in
globalNavBar.inc. On narrow/phone screens the links collapse into a menu icon with
a dropdown so they no longer overlap the menu.
diff --git src/hg/hgMenubar/hgMenubar.c src/hg/hgMenubar/hgMenubar.c
index 2ae5fca7093..f3fcc977176 100644
--- src/hg/hgMenubar/hgMenubar.c
+++ src/hg/hgMenubar/hgMenubar.c
@@ -1,111 +1,150 @@
/*
* This CGI is used by static html pages to show a menu bar.
* On an Apache with activated SSI, a html statement like
*
* 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"
#define CGI_NAME "cgi-bin/hgMenubar"
#define NAVBAR_INC_PATH "/inc/globalNavBar.inc"
#define OLD_HREF "href=\"../"
char* errMessage;
+char *loginLinkHtml()
+/* Return HTML
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("");
+struct dyString *dy = dyStringNew(512);
+char *userName = wikiLinkUserName();
+if (userName == NULL)
+ {
+ // Link straight to the login page (absolute URL from wikiLink), not through hgSession.
+ char *loginUrl = wikiLinkUserLoginUrl("");
+ dyStringPrintf(dy, "Login", loginUrl);
+ }
+else
+ {
+ // No hgsid available on static pages; the logout return URL simply omits it.
+ char *logoutUrl = wikiLinkUserLogoutUrl("");
+ char *changePwUrl = wikiLinkChangePasswordUrl("");
+ dyStringPrintf(dy, "%s",
+ 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)
{
printf ("\n");
printf ("\n", baseDir);
printf ("\n", baseDir);
printf("\n", baseDir);
+printf("\n", baseDir);
printf ("\n", baseDir);
}
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);
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 *newLine = replaceChars(oldLine, OLD_HREF, newHref);
+ 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("", line))
+ line = replaceChars(line, "", loginLinkHtml());
+ if (stringIn("", line))
+ line = replaceChars(line, "", "");
+ char *newLine = replaceChars(line, OLD_HREF, newHref);
printf("%s\n", newLine);
}
lineFileClose(&menuFile);
// links to hgTracks need to use the web browser width and set the hgTracks image
// size in pixels correctly to match the hgGateway "GO" button
jsInline("$(\"#tools1 ul li a\").each( function (a) {\n"
" if (this.href && this.href.indexOf(\"hgTracks\") !== -1) {\n"
" var obj = this;\n"
" obj.onclick = function(e) {\n"
" var pix = calculateHgTracksWidth();\n"
" e.currentTarget.href += \"&pix=\" + pix;\n"
" }\n"
" }\n"
"});\n");
// if the user has previously searched for assemblies, add them to the "Genomes" menu heading,
// above the "other" assemblies link
jsInline("addRecentGenomesToMenuBar();\n");
jsInlineFinish();
}
void parseEnvOrDie (char **cgiPath, char** docRoot, char** pagePath)
{
*cgiPath = getenv("SCRIPT_NAME");
*docRoot = getenv("DOCUMENT_ROOT");
*pagePath = getenv("REDIRECT_URL");
if (*pagePath == NULL)
*pagePath = getenv("DOCUMENT_URI");
if (*pagePath == NULL)
{
*pagePath = cloneString("/inc/");
errMessage = "Error: hgMenubar was run without the REDIRECT_URL or DOCUMENT_URI variable set. Looks like it wasn't run from an SSI statement. Defaulting to the 'inc/' directory, avoids errors in the Apache error log.";
}
if ( (*cgiPath == NULL) || (*docRoot == NULL) || (*pagePath == NULL) )
{
fprintf (stderr, "Error: bad invocation of menubar\n");
exit (1);
}
}
int main(int argc, char *argv[])
/* Process command line. */
{
char *cgiPath, *docRoot, *pagePath;
parseEnvOrDie(&cgiPath, &docRoot, &pagePath);
cgiSpoof(&argc, argv);
char *incFile = cgiUsualString("incFile", NAVBAR_INC_PATH);
printMenuBar(cgiPath, docRoot, pagePath, incFile);
if (errMessage)
puts(errMessage);
return 0;
}