bbe2055111b0bababdc240a64233b994f6b5de91
chmalee
Tue Mar 4 11:13:56 2025 -0800
Bump up DataTables javascript library versions to fix layout bug when initializing a table but not displaying it yet, like we do in the hub upload tab on hgHubConnect, refs #31058
diff --git src/hg/hgHubConnect/trackHubWizard.c src/hg/hgHubConnect/trackHubWizard.c
index 316199d6975..bd1eb5d2176 100644
--- src/hg/hgHubConnect/trackHubWizard.c
+++ src/hg/hgHubConnect/trackHubWizard.c
@@ -1,257 +1,257 @@
/* 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"
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)
{
return countChars(path, '/');
}
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);
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 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);
puts("\n");
puts("\n");
+ "href=\"https://cdn.datatables.net/2.2.2/css/dataTables.dataTables.min.css\">\n");
puts("");
+ "src=\"https://cdn.datatables.net/2.2.2/js/dataTables.min.js\">");
puts("\n");
+ "href=\"https://cdn.datatables.net/buttons/3.2.2/css/buttons.dataTables.min.css\">\n");
puts("");
+ "src=\"https://cdn.datatables.net/buttons/3.2.2/js/dataTables.buttons.min.js\">");
puts("\n");
+ "href=\"https://cdn.datatables.net/select/3.0.0/css/select.dataTables.min.css\">\n");
puts("");
+ "src=\"https://cdn.datatables.net/select/3.0.0/js/dataTables.select.min.js\">");
puts("");
puts("");
jsIncludeFile("hgMyData.js", NULL);
// the skeleton HTML:
webIncludeFile("inc/hgMyData.html");
webIncludeResourceFile("hgMyData.css");
// get the current files and vars stored for this user
struct jsonWrite *jw = jsonWriteNew();
outUiDataForUser(jw);
jsInlineF("\nvar uiData = {%s}\n", jw->dy->string);
jsonWriteFree(&jw);
jsInlineF("\nvar cartDb=\"%s %s\";\n", trackHubSkipHubName(hGenome(database)), database);
jsInlineF("\nvar tusdEndpoint=\"%s\";\n", cfgOptionDefault("hubSpaceTusdEndpoint", NULL));
jsInline("$(document).ready(function() {\nhubCreate.init();\n})");
puts("");
}
void revokeApiKey(struct cartJson *cj, struct hash *paramHash)
/* Remove any api keys for the user */
{
char *userName = getUserName();
struct sqlConnection *conn = hConnectCentral();
struct dyString *query = sqlDyStringCreate("delete from %s where userName='%s'", HUBSPACE_AUTH_TABLE, userName);
sqlUpdate(conn, dyStringCannibalize(&query));
hDisconnectCentral(&conn);
jsonWriteString(cj->jw, "revoke", "true");
}
void generateApiKey(struct cartJson *cj, struct hash *paramHash)
/* Make a random (but not crypto-secure api key for use of hubtools to upload to hubspace */
{
char *userName = getUserName();
if (!userName)
{
jsonWriteString(cj->jw, "error", "generateApiKey: not logged in");
return;
}
char *apiKey = makeRandomKey(256); // just needs some arbitrary length
// save this key to the database for this user, the 'on duplicate' part automatically revokes old keys
struct sqlConnection *conn = hConnectCentral();
struct dyString *query = sqlDyStringCreate("insert into %s values ('%s', '%s') on duplicate key update apiKey='%s'", HUBSPACE_AUTH_TABLE, userName, apiKey, apiKey);
sqlUpdate(conn, dyStringCannibalize(&query));
jsonWriteString(cj->jw, "apiKey", apiKey);
hDisconnectCentral(&conn);
}