46a7db400ddc1280555e0bc32ed09311e19efc65 chmalee Thu May 7 10:53:58 2026 -0700 Reword some hubSpace assembly hub error message. Make the duration of some error message notifications longer, change some text around input about why certain inputs are disabled in certain situations, refs #37411 diff --git src/hg/hgHubConnect/trackHubWizard.c src/hg/hgHubConnect/trackHubWizard.c index 546d15e9fc0..33aced2287c 100644 --- src/hg/hgHubConnect/trackHubWizard.c +++ src/hg/hgHubConnect/trackHubWizard.c @@ -1,265 +1,266 @@ /* trackHubWizard -- a user interface for creating a track hubs configuration files */ /* Copyright (C) 2019 The Regents of the University of California * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #include "common.h" #include "cart.h" #include "cheapcgi.h" #include "hdb.h" #include "hgConfig.h" #include "md5.h" #include "trashDir.h" #include "hgHubConnect.h" #include "jsHelper.h" #include "web.h" #include "wikiLink.h" #include "customTrack.h" #include "userdata.h" #include "jsonWrite.h" #include "cartJson.h" #include "hubSpace.h" #include "hubSpaceKeys.h" #include "hubConnect.h" #include "trackHub.h" #include "htmshell.h" #include <limits.h> #include "errCatch.h" void removeOneFile(char *userName, char *cgiFileName, char *fullPath, char *db, char *fileType) /* Remove one single file for userName */ { // prefixUserFile returns a canonicalized path, or NULL if the // canonicalized path does not begin with the hg.conf specified userDataDir // TODO: make the debug information from stderr go to stdout so the user // can know there is a mistake somewhere, and only print the debug // information in the event that the filename actually begins with the // userDataDir so we don't tell hackers what files do and do not exist char *fileName = prefixUserFile(userName, fullPath, NULL); if (fileName) { if (fileExists(fileName)) { fprintf(stderr, "deleting file: '%s'\n", fileName); removeFileForUser(fileName, userName); fflush(stderr); } else { fprintf(stderr, "file '%s' does not exist\n", fileName); fflush(stderr); } } } int pathDepth(char *path) { // replace multiple occurences of '/' with just a single one to get a canonical path // as path///to/file and path/to/file are the same path on Linux char *deduped = replaceChars(path, "//", "/"); return countChars(deduped, '/'); } int sortByFullPathCmp(const void *va, const void *vb) /* Compare two fullPaths */ { struct jsonElement *a = (struct jsonElement *)(*(struct slRef **)va)->val; struct jsonElement *b = (struct jsonElement *)(*(struct slRef **)vb)->val; char *aFullpath = jsonStringField(a, "fullPath"); char *bFullpath = jsonStringField(b, "fullPath"); int aDepth = pathDepth(aFullpath); int bDepth = pathDepth(bFullpath); // ensure subdirectories order before their parents: if (aDepth != bDepth) return bDepth - aDepth; // if equal depth than lexicographic sort is fine return strcmp(jsonStringField(a,"fullPath"), jsonStringField(b, "fullPath")); } void sortByFullPath(struct jsonElement *listJson) { slSort(&(listJson->val.jeList), sortByFullPathCmp); } void doRemoveFile(struct cartJson *cj, struct hash *paramHash) /* Process the request to remove a file */ { char *userName = getUserName(); if (userName) { // our array of objects, each object represents a track file struct jsonElement *deleteJson = hashFindVal(paramHash, "fileList"); struct slRef *copy, *f, *fileList = deleteJson->val.jeList; struct jsonElement *dirListJsonEle = newJsonList(NULL); jsonWriteListStart(cj->jw, "deletedList"); for (f = fileList; f != NULL; ) { struct jsonElement *fileObj = (struct jsonElement *)f->val; char *fileName = jsonStringField(fileObj, "fileName"); char *fileType = jsonStringField(fileObj, "fileType"); char *db = jsonStringField(fileObj, "genome"); char *fullPath = jsonStringField(fileObj, "fullPath"); copy = f->next; if (sameString(fileType, "dir")) { f->next = NULL; jsonListAdd(dirListJsonEle, fileObj); } else { if (sameString(fileType, "hub.txt")) { // disconnect this hub from the cart if it exists char *hubUrl = urlForFile(userName, fullPath); char *hubId = hubNameFromUrl(hubUrl); if (hubId) { /* remove the cart variable */ hubId += 4; // skip past the hub_ part char buffer[1024]; safef(buffer, sizeof buffer, "hgHubConnect.hub.%s", hubId); cartRemove(cj->cart, buffer); } } removeOneFile(userName, fileName, fullPath, db, fileType); // write out the fullPath so the DataTable can remove the correct row: jsonWriteString(cj->jw, NULL, fullPath); } f = copy; } // now attempt to delete any requested directories, but don't die if they still have contents sortByFullPath(dirListJsonEle); struct slRef *dir = NULL; for (dir = dirListJsonEle->val.jeList; dir != NULL; dir = dir->next) { struct jsonElement *fileObj = (struct jsonElement *)dir->val; char *fileName = jsonStringField(fileObj, "fileName"); char *fileType = jsonStringField(fileObj, "fileType"); char *db = jsonStringField(fileObj, "genome"); char *fullPath = jsonStringField(fileObj, "fullPath"); removeOneFile(userName, fileName, fullPath, db, fileType); // write out the fullPath so the DataTable can remove the correct row: jsonWriteString(cj->jw, NULL, fullPath); } jsonWriteListEnd(cj->jw); } } void doMoveFile(struct cartJson *cj, struct hash *paramHash) /* Move a file to a new hub */ { } static void outUiDataForUser(struct jsonWrite *jw) /* List out the currently stored files for the user as well as other data * needed to create the hubSpace table */ { char *userName = getUserName(); jsonWriteObjectStart(jw, "userFiles"); if (userName) { // the url for this user: jsonWriteString(jw, "userUrl", webDataDir(userName)); jsonWriteListStart(jw, "fileList"); struct hubSpace *file, *fileList = listFilesForUser(userName); for (file = fileList; file != NULL; file = file->next) { jsonWriteObjectStart(jw, NULL); jsonWriteString(jw, "fileName", file->fileName); jsonWriteNumber(jw, "fileSize", file->fileSize); jsonWriteString(jw, "fileType", file->fileType); jsonWriteString(jw, "parentDir", file->parentDir); jsonWriteString(jw, "genome", file->db); jsonWriteString(jw, "lastModified", file->lastModified); jsonWriteString(jw, "uploadTime", file->creationTime); jsonWriteString(jw, "fullPath", stripDataDir(file->location, userName)); jsonWriteString(jw, "md5sum", file->md5sum); jsonWriteString(jw, "hubType", file->hubType ? file->hubType : "trackHub"); jsonWriteObjectEnd(jw); } jsonWriteListEnd(jw); } jsonWriteBoolean(jw, "isLoggedIn", getUserName() ? TRUE : FALSE); jsonWriteString(jw, "hubNameDefault", defaultHubNameForUser(getUserName())); // if the user is not logged, the 0 for the quota is ignored jsonWriteNumber(jw, "userQuota", getUserName() ? checkUserQuota(getUserName()) : 0); jsonWriteNumber(jw, "maxQuota", getUserName() ? getMaxUserQuota(getUserName()) : HUB_SPACE_DEFAULT_QUOTA); jsonWriteObjectEnd(jw); } void getHubSpaceUIState(struct cartJson *cj, struct hash *paramHash) /* Get all the data we need to make a users hubSpace UI table. The cartJson library * deals with printing the json */ { outUiDataForUser(cj->jw); } void cjRevokeApiKey(struct cartJson *cj, struct hash *paramHash) /* Wrapper for cartJson to call lib function revokeApiKey, removes any api keys for the user */ { struct errCatch *errCatch = errCatchNew(); if (errCatchStart(errCatch)) { char *userName = getUserName(); hubSpaceRevokeApiKey(userName); } errCatchEnd(errCatch); if (!(errCatch->gotError)) jsonWriteString(cj->jw, "revoke", "true"); else jsonWriteStringf(cj->jw, "error", "revokeApiKey() error: '%s'", errCatch->message->string); errCatchFree(&errCatch); } void cjGenerateApiKey(struct cartJson *cj, struct hash *paramHash) /* Wrapper for cartJson to call lib function generateApiKey, makes a random (but not crypto-secure api key for use of hubtools to upload to hubspace, or for skipping cloudflare */ { struct errCatch *errCatch = errCatchNew(); char *apiKey = NULL; if (errCatchStart(errCatch)) { char *userName = getUserName(); apiKey = hubSpaceGenerateApiKey(userName); } errCatchEnd(errCatch); if (apiKey) jsonWriteString(cj->jw, "apiKey", apiKey); else if (errCatch->gotError) jsonWriteStringf(cj->jw, "error", "generateApiKey() error: '%s'", errCatch->message->string); errCatchFree(&errCatch); } void doTrackHubWizard(char *database) /* Offer an upload form so users can upload all their hub files */ { jsIncludeFile("utils.js", NULL); jsIncludeFile("ajax.js", NULL); jsIncludeFile("lodash.3.10.0.compat.min.js", NULL); jsIncludeFile("cart.js", NULL); jsIncludeFile("autocompleteCat.js",NULL); webIncludeResourceFile("font-awesome.min.css"); webIncludeResourceFile("dataTables-2.2.2.min.css"); jsIncludeFile("dataTables-2.2.2.min.js", NULL); webIncludeResourceFile("dataTables.buttons-3.2.2.min.css"); jsIncludeFile("dataTables.buttons-3.2.2.min.js", NULL); webIncludeResourceFile("dataTables.select-3.0.0.min.css"); jsIncludeFile("dataTables.select-3.0.0.min.js", NULL); puts("<link href=\"https://releases.transloadit.com/uppy/v4.5.0/uppy.min.css\" rel=\"stylesheet\">"); puts("<script type=\"text/javascript\" src=\"https://releases.transloadit.com/uppy/v4.5.0/uppy.min.js\"></script>"); jsIncludeFile("hgMyData.js", NULL); // the skeleton HTML: webIncludeFile("inc/hgMyData.html"); webIncludeResourceFile("hgMyData.css"); jsInlineF("\nvar isLoggedIn = %s;\n", getUserName() ? "true" : "false"); jsInlineF("\nvar cartDb=\"%s %s\";\n", trackHubSkipHubName(hGenome(database)), database); jsInlineF("\nvar tusdEndpoint=\"%s\";\n", cfgOptionDefault("hubSpaceTusdEndpoint", NULL)); jsInlineF("\nvar fileListEndpoint=\"%shgHubConnect\";\n", hLoginHostCgiBinUrl()); jsInlineF("\nvar loginHost=\"http%s://%s\";\n", loginUseHttps() ? "s" : "", wikiLinkHost()); +jsInlineF("\nvar hubGenomeCollisionErrFrag=\"%s\";\n", HUB_GENOME_COLLISION_ERR_FRAG); jsInline("$(document).ready(function() {\nhubCreate.init();\n})"); puts("</div>"); }