ca23d9f5054fc863beb18d7ef4919b550b2f8d9c
chmalee
  Mon Jun 1 10:45:37 2026 -0700
Allow dot in genome names in hubspace uploads, refs #37665

diff --git src/hg/js/hgMyData.js src/hg/js/hgMyData.js
index 86e84d8f27d..ab94bbd7b77 100644
--- src/hg/js/hgMyData.js
+++ src/hg/js/hgMyData.js
@@ -1027,35 +1027,37 @@
         "bigInteract": [".biginteract"],
         "bigPsl": [".bigpsl"],
         "bigChain": [".bigchain"],
         "bamIndex": [".bam.bai", ".bai"],
         "tabixIndex": [".vcf.gz.tbi", "vcf.bgz.tbi"],
         "hub.txt": ["hub.txt"],
         "2bit": [".2bit"],
         "text": [".txt", ".text"],
     };
 
     function getDefaultHubName() {
         return uiState.currentHub.length > 0 ? uiState.currentHub : uiState.hubNameDefault;
     }
 
     function sanitizeGenomeName(name) {
-        // Strip .2bit, replace non-alphanumeric/_/- with _, drop hub_ prefix.
+        // Strip .2bit, replace non-alphanumeric/_/-/. with _, drop hub_ prefix.
         // Returns empty string if nothing usable is left.
+        // The allowed character class [A-Za-z0-9._-] must match the
+        // server-side check in src/hg/hgHubConnect/hooks/pre-finish.c.
         if (!name) return "";
         let stem = name.replace(/\.2bit$/i, "");
-        stem = stem.replace(/[^A-Za-z0-9_-]/g, "_");
+        stem = stem.replace(/[^A-Za-z0-9._-]/g, "_");
         stem = stem.replace(/^hub_/, "");
         return stem;
     }
 
     function hubTxtPathForHub(hubName) {
         // Return the fullPath of the hub.txt file inside hubName as recorded in
         // hubSpace, falling back to "hubName/hub.txt" if there's no row yet.
         // The user may have uploaded their own "araTha1.hub.txt" - use its
         // actual filename rather than assuming "hub.txt".
         let dir = uiState.filesHash[hubName];
         if (dir && dir.children) {
             for (let child of dir.children) {
                 if (child.fileType === "hub.txt") return child.fullPath;
             }
         }