dccbd05b94e1a9323a61a14df17ac93b25aee9d3
gperez2
  Thu Sep 17 09:48:20 2026 -0700
Updating hgLogin's recovery-email wording (heading, menu link, description, and confirmation mail) for clarity and to match Change password/Change email, refs #38197

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

diff --git src/hg/hgLogin/hgLogin.c src/hg/hgLogin/hgLogin.c
index a51106c0598..0fdc857f89f 100644
--- src/hg/hgLogin/hgLogin.c
+++ src/hg/hgLogin/hgLogin.c
@@ -1257,31 +1257,32 @@
 char expStr[32];
 safef(expStr, sizeof(expStr), "%ld", clock1() + 7*24*3600);   // link good for a week
 char *sig = recovEmailSig(user, recovEmail, curRecov, curVerified, expStr);
 char url[1024];
 safef(url, sizeof(url),
     "%s?hgLogin.do.confirmRecovEmail=1&user=%s&recovEmail=%s&exp=%s&sig=%s",
     hgLoginUrl, cgiEncode(user), cgiEncode(recovEmail), expStr, sig);
 char subject[256];
 safef(subject, sizeof(subject), "Confirm your %s recovery email address", brwName);
 char *remoteAddr = getenv("REMOTE_ADDR");
 char message[4096];
 safef(message, sizeof(message),
     "Someone (probably you, from IP address %s) gave this address as the recovery email address "
     "for the %s account \"%s\".\nTo confirm that this mailbox is yours, open this link in your "
     "browser:\n\n%s\n\nThe link works once and expires in seven days.  Until it is opened, this "
-    "address cannot be used to sign in to that account and will not receive a password reset.\n\n"
+    "email address cannot be used to sign in to that account and will not receive a "
+    "password-reset email.\n\n"
     "If this is *not* you, do not open the link: someone typed your address by mistake, and "
     "ignoring this message is all it takes to keep them from using it.\n\n%s\n%s",
     emptyForNull(remoteAddr), brwName, user, url, signature, returnAddr);
 /* Not sendActMailOut(): that exits the CGI when the address will not take mail, which would
  * end the signup response after the account has already been created and its activation mail
  * sent.  A recovery address is optional and easy to mistype, so a bad one must not derail
  * signing up -- the address simply stays unconfirmed, which is the safe state. */
 if (mailViaPipeBounce(recovEmail, subject, message, returnAddr) == -1)
     fprintf(stderr, "hgLogin: could not mail recovery-address confirmation to %s for account "
         "%s\n", recovEmail, user);
 freeMem(sig);
 }
 
 static void sendRecovEmailChangeAlertMail(char *email, char *user, char *newRecov)
 /* Tell the account's main address that its recovery address just changed, so its owner finds
@@ -1516,31 +1517,31 @@
 sqlFreeResult(&sr);
 char *expected = recovEmailSig(user, recovEmail, emptyForNull(curRecov),
                                emptyForNull(curVerified), expStr);
 boolean sigOk = isNotEmpty(sig) && sameString(sig, expected);
 freeMem(expected);
 if (!sigOk || isEmpty(user) || spc_email_isvalid(recovEmail) == 0)
     {
     freez(&errMsg);
     errMsg = cloneString("This confirmation link is not valid or has already been used.");
     displayLoginPage(conn);
     return;
     }
 if (clock1() > atol(expStr))
     {
     freez(&errMsg);
-    errMsg = cloneString("This confirmation link has expired.");
+    errMsg = cloneString("This confirmation link has expired. Please request a new one.");
     displayLoginPage(conn);
     return;
     }
 /* Set the address as well as the flag: for a signup this rewrites the same value, and for a
  * change this is the point at which the new address takes effect. */
 sqlSafef(query, sizeof(query),
     "UPDATE gbMembers SET recovEmail='%s', recovEmailVerified='Y', lastUse=NOW() "
     "WHERE userName='%s'", recovEmail, user);
 sqlUpdate(conn, query);
 /* A change, not a signup confirmation: tell the main address, so a hijack gets noticed. */
 if (isNotEmpty(curRecov) && differentWord(curRecov, recovEmail))
     {
     sqlSafef(query, sizeof(query), "SELECT email FROM gbMembers WHERE userName='%s'", user);
     char *email = sqlQuickString(conn, query);
     if (isNotEmpty(email))
@@ -1578,40 +1579,43 @@
 char query[512];
 sqlSafef(query, sizeof(query),
     "SELECT recovEmail, recovEmailVerified FROM gbMembers WHERE userName='%s'", user);
 struct sqlResult *sr = sqlGetResult(conn, query);
 char **row = sqlNextRow(sr);
 char *curRecov = (row != NULL) ? cloneString(emptyForNull(row[0])) : cloneString("");
 boolean curConfirmed = (row != NULL) && sameWord(emptyForNull(row[1]), "Y");
 sqlFreeResult(&sr);
 sqlSafef(query, sizeof(query), "SELECT password FROM gbMembers WHERE userName='%s'", user);
 boolean hasPassword = isNotEmpty(sqlQuickString(conn, query));
 char *encUser = htmlEncode(user);
 char *encCurRecov = htmlEncode(isNotEmpty(curRecov) ? curRecov : "(none)");
 
 hPrintf("<div id=\"changeRecovEmailBox\" class=\"centeredContainer formBox\">"
     "<h2>%s</h2>", brwName);
-hPrintf("<h3>Recovery Email</h3>");
+hPrintf("<h3>Change recovery email</h3>");
 hPrintf("<p><span style='color:red;'>%s</span></p>", errMsg ? errMsg : "");
 hPrintf("<form method=\"post\" action=\"%s\" name=\"changeRecovEmailForm\">", hgLoginUrl);
 hPrintf("<p>Signed in as <b>%s</b>.<br>Current recovery email address: <b>%s</b>%s</p>",
     encUser, encCurRecov,
     (isNotEmpty(curRecov) && !curConfirmed) ? " (waiting to be confirmed)" : "");
-hPrintf("<p style=\"font-size:0.9em\">A second address you can use to get back into your "
-    "account: it can sign you in, including with the Google and ORCID buttons, and it receives "
-    "a copy of a password reset. We email it a link to confirm it, and it does nothing until "
-    "you open that link. Your current address keeps working until then.</p>");
+hPrintf("<p style=\"font-size:0.9em\">You can add a second email address to get back into "
+    "your account. Once confirmed, it can sign you in, including through the Google and "
+    "ORCID buttons, and it will get a copy of the password-reset email whenever one is sent "
+    "for this account. We will email a confirmation link to the new address. Until that "
+    "link is opened, the email address cannot be used to sign in and will not receive a "
+    "password-reset email.%s</p>",
+    isNotEmpty(curRecov) ? " Your current recovery email address keeps working until then." : "");
 freeMem(encUser);
 freeMem(encCurRecov);
 if (hasPassword)
     hPrintf("<div class=\"inputGroup\">"
         "<label for=\"curPassword\">Current password</label>"
         "<input type=\"password\" name=\"hgLogin_curPassword\" value=\"\" size=\"30\" "
         "id=\"curPassword\">"
         "</div>");
 hPrintf("<div class=\"inputGroup\">"
     "<label for=\"newRecovEmail1\">New recovery email address</label>"
     "<input type=\"text\" name=\"hgLogin_newRecovEmail1\" value=\"\" size=\"30\" "
     "id=\"newRecovEmail1\">"
     "</div>");
 hPrintf("<div class=\"inputGroup\">"
     "<label for=\"newRecovEmail2\">Re-enter new recovery email address</label>"
@@ -1696,31 +1700,31 @@
     changeRecovEmailPage(conn);
     return;
     }
 /* Do not store the address yet: mail a one-time confirmation link and put it on the account
  * only when that link is opened (see confirmRecovEmail).  Until then the address the user has
  * now keeps working, so a typo here costs them nothing. */
 sendRecovEmailConfirmMail(recov1, user, curRecov, curVerified);
 cartRemove(cart, "hgLogin_newRecovEmail1");
 cartRemove(cart, "hgLogin_newRecovEmail2");
 cartRemove(cart, "hgLogin_curPassword");
 char *encRecov = htmlEncode(recov1);
 hPrintf("<div class=\"centeredContainer formBox\"><h2>%s</h2>", brwName);
 hPrintf("<h3>Almost done. Please check your email</h3>");
 hPrintf("<p>We sent a confirmation link to <b>%s</b>. Open the link in that message to finish "
     "setting your recovery email address. The link works once and expires in seven days. Until "
-    "then nothing about your account changes.</p></div>", encRecov);
+    "that link is opened, nothing about your account changes.</p></div>", encRecov);
 freeMem(encRecov);
 returnToURL(3000);
 }
 
 void signupPage(struct sqlConnection *conn)
 /* draw the signup page */
 {
 hPrintf("<div id=\"signUpBox\" class=\"centeredContainer formBox\">"
     "<h2>%s</h2>", brwName);
 hPrintf(
     "<p>Signing up enables you to save multiple sessions, share your sessions with others via short and stable session links and manage previously uploaded custom tracks and track hubs.</p>"
     "\n");
 hPrintf("<p>Already have an account? "
     "<a href=\"%s?hgLogin.do.displayLoginPage=1\">Go to the login page</a>.</p>", hgLoginUrl);
 printSocialButtons(FALSE, TRUE, "Sign up");