75593a4e317eac40b781c83718961f0eb7f84f3a max Thu Aug 27 06:32:34 2026 -0700 Blue bar login and logout now return to the page they were clicked on #Preview2 week - bugs introduced now will need a build patch to fix The Login link in the blue bar sent everyone to the My Sessions page after they signed in, and the Sign out and account links in the logged-in dialog did the same, so a visitor reading a help page or looking at an item details page lost their place. Only hgTracks was handled, as a special case. Adds wikiLinkEncodeCurrentPageReturnUrl(), which builds a returnto for the page the CGI is currently serving, including its query string, since the track and item parameters of pages like hgTrackUi and hgc are not all kept in the cart. hgTracks keeps its old behaviour of returning to hgTracks?hgsid=, because its state is in the cart and its query string can hold a one-shot zoom or drag. hgMenubar does the same for the static pages it is included into, using the page path from the SSI environment. The two checks hgLogin runs on an incoming returnto (URL shape, and the optional login.approvedReturn host list) move to wikiLink.c so that the CGIs building a link apply the same rules before writing it. A URL hgLogin would refuse now becomes an ordinary login link instead of an error page: an over-long or oddly-quoted query string costs the query string, and a host that login.approvedReturn does not cover falls back to the old My Sessions target. Note that with login.approvedReturn set, static-page returns need the bare host added to the list. Also converts the fixed 2 kB buffers in the wikiLink URL builders to dyStrings, since a cgi-encoded return URL can nearly triple in length and safef would have aborted. refs #38192 diff --git src/hg/lib/web.c src/hg/lib/web.c index 223c5538348..4efdfd2e875 100644 --- src/hg/lib/web.c +++ src/hg/lib/web.c @@ -1656,82 +1656,97 @@ struct trackDb *tdb = hTrackDbForTrack(db, track); if (tdb) { struct trackDb *topLevel = trackDbTopLevelSelfOrParent(tdb); char *undupedTrack = dupTrackSkipToSourceName(topLevel->track); safef(hgTablesOptions, sizeof hgTablesOptions, "../cgi-bin/hgTables?hgta_doMainPage=1&hgta_group=%s&hgta_track=%s&hgta_table=%s&", topLevel->grp, undupedTrack, tdb->table); menuStr = replaceChars(menuStr, "../cgi-bin/hgTables?", hgTablesOptions); trackDbFree(&tdb); } } } // Fill in the top-right Login link (placeholder <!-- LOGIN_LINK --> in globalNavBar.inc, inside -// the #topRightLinks container). Logged out: a "Login" link to hgSession. Logged in: the +// the #topRightLinks container). Logged out: a "Login" link straight to hgLogin. Logged in: the // username, which opens an account dialog (handled in topLinks.js using the data-* attributes -// below). No login system: removed. +// below). No login system: removed. Login, logout and the account links all send the visitor +// back to the page they are on now. { char *loginLi = ""; if (loginSystemEnabled() || wikiLinkEnabled()) { char *userName = wikiLinkUserName(); struct dyString *dy = dyStringNew(512); + // Come back to the page the visitor was reading when they clicked, refs #38192. The + // return URL is NULL on the few pages there is no point returning to, and then these + // links fall back to their old hgSession target. + char *hgsid = cart ? cartSessionId(cart) : ""; + char *retEnc = wikiLinkEncodeCurrentPageReturnUrl(hgsid); if (userName == NULL) { // Link straight to the login page (same target hgSession's own Login link uses), // rather than bouncing the user through hgSession first. - char *loginUrl = wikiLinkUserLoginUrl(cart ? cartSessionId(cart) : ""); + char *loginUrl = retEnc ? wikiLinkUserLoginUrlReturning(hgsid, retEnc) + : wikiLinkUserLoginUrl(hgsid); dyStringPrintf(dy, "<a class='topRightLink' href='%s' id='loginLink' " "title='Log in to save and share sessions'>Login</a>", loginUrl); + freez(&loginUrl); } else { - char *hgsid = cart ? cartSessionId(cart) : NULL; - char *logoutUrl = wikiLinkUserLogoutUrl(hgsid); - char *changePwUrl = wikiLinkChangePasswordUrl(hgsid); - char *changeEmailUrl = wikiLinkChangeEmailUrl(hgsid); + char *logoutUrl = retEnc ? wikiLinkUserLogoutUrlReturning(hgsid, retEnc) + : wikiLinkUserLogoutUrl(hgsid); + char *changePwUrl = retEnc ? wikiLinkChangePasswordUrlReturning(hgsid, retEnc) + : wikiLinkChangePasswordUrl(hgsid); + char *changeEmailUrl = retEnc ? wikiLinkChangeEmailUrlReturning(hgsid, retEnc) + : wikiLinkChangeEmailUrl(hgsid); dyStringPrintf(dy, "<a class='topRightLink' href='#' id='loginLink' " "title='Account info and sign out' " "data-username=\"%s\" data-logouturl=\"%s\" data-changepwurl=\"%s\" " "data-changeemailurl=\"%s\">%s</a>", userName, logoutUrl, changePwUrl ? changePwUrl : "", changeEmailUrl ? changeEmailUrl : "", userName); } + freez(&retEnc); loginLi = dyStringCannibalize(&dy); } menuStr = replaceChars(menuStr, "<!-- LOGIN_LINK -->", loginLi); } // Fill in the top-right "Share a link" link (placeholder <!-- SHARE_LINK -->). // data-sharemode tells topLinks.js what to do: // "session" (hgTracks): save the current view as a session and show a short link; branches on // data-loggedin to either name a session (logged in) or make an anonymous link. // "url" (hgTrackUi): just show the current page URL with the hgsid stripped, for sharing. { char *shareLi = ""; boolean isHgTracks = (scriptName && endsWith(scriptName, "hgTracks")); boolean isHgTrackUi = (scriptName && endsWith(scriptName, "hgTrackUi")); if (isHgTracks || isHgTrackUi) { char *userName = (loginSystemEnabled() || wikiLinkEnabled()) ? wikiLinkUserName() : NULL; char *shareMode = isHgTrackUi ? "url" : "session"; struct dyString *dy = dyStringNew(256); + // data-shortlink lets topLinks.js build the exact share URL to preview in the dialog, before + // the session is actually created, matching addSessionLink()'s server-side choice. dyStringPrintf(dy, "<a class='topRightLink' href='#' id='shareLink' " "title='Get a link to this view to share with others' " - "data-sharemode='%s' data-loggedin='%d'", shareMode, (userName != NULL)); + "data-sharemode='%s' data-loggedin='%d' data-shortlink='%d'", + shareMode, (userName != NULL), + cfgOptionBooleanDefault("hgSession.shortLink", FALSE)); if (userName != NULL) dyStringPrintf(dy, " data-username=\"%s\"", userName); dyStringAppend(dy, ">Share a link</a>"); shareLi = dyStringCannibalize(&dy); } menuStr = replaceChars(menuStr, "<!-- SHARE_LINK -->", shareLi); } if(scriptName) { // Provide optional official mirror servers menu items char *geoMenu = geoMirrorMenu(); char *pattern = "<!-- OPTIONAL_MIRROR_MENU -->"; char *newMenuStr = replaceChars(menuStr, pattern, geoMenu); freez(&menuStr); menuStr = newMenuStr;