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;