15724fd4a6d6bc917c1ed86367a39cdf40404852 max Mon Jun 13 08:44:30 2022 -0700 improving the "my hubs" tab of hgHubConnect, refs #29583 diff --git src/hg/hgHubConnect/hgHubConnect.c src/hg/hgHubConnect/hgHubConnect.c index 3de1860..7ebf7d2 100644 --- src/hg/hgHubConnect/hgHubConnect.c +++ src/hg/hgHubConnect/hgHubConnect.c @@ -109,55 +109,64 @@ static char *removeLastComma(char *string) { if (string != NULL) { int len = strlen(string); if ( string[len - 1] == ',') string[len - 1] = 0; else if (len > 2 && endsWith(string,", ")) string[len - 2] = 0; } return string; } #define GENLISTWIDTH 40 -static void printGenomeList(char *hubUrl, struct slName *genomes, int row, boolean withLink) +static void printGenomeList(char *hubUrl, struct slName *genomes, int row, boolean withLink, boolean withPaste) /* print supported assembly names from sl list */ { struct dyString *dyHtml = dyStringNew(1024); struct dyString *dyShortHtml = dyStringNew(1024); // create two strings: one shortened to GENLISTWIDTH characters // and another one with all genomes int charCount = 0; struct slName *genome = genomes; for(; genome; genome = genome->next) { char *trimmedName = trackHubSkipHubName(genome->name); char *shortName = cloneString(trimmedName); // If even the first element is too long, truncate its short name. if (genome==genomes && strlen(trimmedName) > GENLISTWIDTH) shortName[GENLISTWIDTH] = 0; // append to dyShortHtml if necessary if (charCount == 0 || (charCount+strlen(trimmedName)<=GENLISTWIDTH)) { if (withLink) + { dyStringPrintf(dyShortHtml,"<a title='Connect hub and open the %s assembly' href='hgTracks?hubUrl=%s&genome=%s&position=lastDbPos'>%s</a>" , genome->name, hubUrl, genome->name, shortName); + // https://hgdownload-test.gi.ucsc.edu/hubs/GCA/009/914/755/GCA_009914755.4/hub.txt + if (withPaste) + { + dyStringPrintf(dyShortHtml,"<input type='hidden' value='%s'>" , hubUrl); + dyStringAppend(dyShortHtml, "<svg title='click to copy genome browser hub connection URL to clipboard, for sharing with others' class='pasteIcon' style='margin-left: 6px; cursor: pointer; vertical-align:baseline; width:0.8em' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'><!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d='M128 184c0-30.879 25.122-56 56-56h136V56c0-13.255-10.745-24-24-24h-80.61C204.306 12.89 183.637 0 160 0s-44.306 12.89-55.39 32H24C10.745 32 0 42.745 0 56v336c0 13.255 10.745 24 24 24h104V184zm32-144c13.255 0 24 10.745 24 24s-10.745 24-24 24-24-10.745-24-24 10.745-24 24-24zm184 248h104v200c0 13.255-10.745 24-24 24H184c-13.255 0-24-10.745-24-24V184c0-13.255 10.745-24 24-24h136v104c0 13.2 10.8 24 24 24zm104-38.059V256h-96v-96h6.059a24 24 0 0 1 16.97 7.029l65.941 65.941a24.002 24.002 0 0 1 7.03 16.971z'/></svg>"); + } + } else dyStringPrintf(dyShortHtml,"%s" , shortName); + dyStringPrintf(dyShortHtml,", "); } freeMem(shortName); charCount += strlen(trimmedName); // always append to dyHtml if (withLink) dyStringPrintf(dyHtml,"<a title='Connect hub and open the %s assembly' href='hgTracks?hubUrl=%s&genome=%s&position=lastDbPos'>%s</a>" , genome->name, hubUrl, genome->name, trimmedName); else dyStringPrintf(dyHtml,"%s" , trimmedName); if (genome->next) { dyStringPrintf(dyHtml,", "); @@ -202,31 +211,31 @@ } static void printGenomes(struct trackHub *thub, int row, boolean withLink) /* print supported assembly names from trackHub */ { /* List of associated genomes. */ struct trackHubGenome *genomes = thub->genomeList; struct slName *list = NULL, *el; for(; genomes; genomes = genomes->next) { el = slNameNew(genomes->name); slAddHead(&list, el); } slReverse(&list); -printGenomeList(thub->url, list, row, withLink); +printGenomeList(thub->url, list, row, withLink, TRUE); } static void hgHubConnectUnlisted(struct hubConnectStatus *hubList, struct hash *publicHash) /* Put up the list of unlisted hubs and other controls for the page. */ /* uses publicHash to distingusih public hubs from unlisted ones */ /* NOTE: Destroys hubList */ { // put out the top of our page puts("<div id=\"unlistedHubs\" class=\"hubList\"> \n" "<div class='tabSection' style='border-bottom:none'>"); printf("<FORM ACTION=\"%s\" id='unlistedHubForm' NAME=\"unlistedHubForm\">\n", "../cgi-bin/hgGateway"); cgiMakeHiddenVar(hgHubConnectRemakeTrackHub, "on"); @@ -246,37 +255,33 @@ "hubText.value=$.trim(hubText.value);" "if(validateUrl($('#hubUrl').val())) { " " document.addHubForm.elements['hubUrl'].value=hubText.value;" " document.addHubForm.submit(); return true; } " "else { return false; }" ); // count up the number of unlisted hubs we currently have int unlistedHubCount = 0; struct hubConnectStatus *unlistedHubList = NULL; struct hubConnectStatus *hub, *nextHub; for(hub = hubList; hub; hub = nextHub) { nextHub = hub->next; - // if url is not in publicHash, it's unlisted */ - if (!((publicHash != NULL) && hashLookup(publicHash, hub->hubUrl))) - { unlistedHubCount++; slAddHead(&unlistedHubList, hub); } - } hubList = NULL; // hubList no longer valid puts("<p>Enter hub URLs below to connect hubs. Hubs connected this way are not accessible to " "other users by default.</p><p>If you wish to share your hub you can create a " "<a href=\"/goldenPath/help/hgSessionHelp.html\" style='color:#121E9A' target=_blank>session link</a>. " "First, connect the hub and configure the tracks image as desired, then navigate to " "<a href=\"/cgi-bin/hgSession\" style='color:#121E9A' target=_blank>My Sessions</a> (<b>My Data</b> > <b>My Sessions</b>). " "The resulting stable link can be added to publications and shared freely. You, as the author, " "also have the power to update the session contents freely. " "Alternatively, you may <a href=\"/goldenPath/help/hgTrackHubHelp.html#Sharing\" " "style='color:#121E9A' target=_blank>build a link with the hub URL</a> to allow users to retain their browser " "configuration, connected hubs, and custom tracks.</p>" "</p>" "<p><a href=\"../contacts.html\" style='color:#121E9A'>Contact us</A> if you wish to submit a hub to the list of public hubs.</p>\n" @@ -286,31 +291,31 @@ { // nothing to see here printf("<tr><td>No Unlisted Track Hubs</td></tr>"); printf("</thead></table>"); puts("</FORM>"); // return from within DIV and FROM is probably not a good idea puts("</div></div>"); // tabSection and .unlistedHubs return; } // time to output the big table. First the header puts( "<tr> " "<th>Display</th> " "<th>Hub Name</th> " "<th>Description</th> " - "<th>Assemblies<span class='assemblyClickNote'>Click to connect and browse directly</span></th> " + "<th>Assemblies<span class='assemblyClickNote'>Click to connect and browse directly. Click copy icon to copy URL to clipboard for sharing.</span></th> " "</tr>\n" "</thead>\n"); // start first row printf("<tbody>"); char id[256]; int count = 0; for(hub = unlistedHubList; hub; hub = hub->next) { char hubName[64]; safef(hubName, sizeof(hubName), "%s%u", hgHubConnectHubVarPrefix, hub->id); if (!cartUsualBoolean(cart, hubName, FALSE)) continue; @@ -632,31 +637,31 @@ ourCellStart(); printf("<span class=\"hubError\">ERROR: %s </span>" "<a href=\"../goldenPath/help/hgTrackHubHelp.html#Debug\">Debug Help</a>", hubInfo->errorMessage); safef(jsId, sizeof jsId, "hubClearButton%d", count); printf( "<input name=\"hubClearButton\" id='%s' " "class=\"hubButton\" type=\"button\" value=\"Retry Hub\">" , jsId); jsOnEventByIdF("click", jsId, "document.resetHubForm.elements['hubCheckUrl'].value='%s';" "document.resetHubForm.submit();return true;", hubInfo->hubUrl); ourCellEnd(); } -printGenomeList(hubInfo->hubUrl, dbListNames, count, hubHasNoError); +printGenomeList(hubInfo->hubUrl, dbListNames, count, hubHasNoError, FALSE); printf("</tr>\n"); } struct trackHub *fetchTrackHub(struct hubEntry *hubInfo) /* Fetch the hub structure for a public hub, trapping the error * if the hub cannot be reached */ { struct errCatch *errCatch = errCatchNew(); struct trackHub *hub = NULL; if (errCatchStart(errCatch)) { char hubName[4096]; safef(hubName, sizeof(hubName), "hub_%d", hubInfo->id); hub = trackHubOpen(hubInfo->hubUrl, hubName); } @@ -1624,31 +1629,31 @@ cartSaveSession(cart); puts("</FORM>"); // this is the invisible form for the reset hub button - it's submitted via javascript printf("<FORM ACTION=\"%s\" NAME=\"resetHubForm\">\n", "../cgi-bin/hgHubConnect"); cgiMakeHiddenVar(hgHubCheckUrl, ""); cartSaveSession(cart); puts("</FORM>"); // ... and now the main form // we have three tabs for the public and unlisted hubs and hub development printf("<div id=\"tabs\">" "<ul> <li><a class=\"defaultDesc\" href=\"#publicHubs\">Public Hubs</a></li>" - "<li><a class=\"defaultDesc\" href=\"#unlistedHubs\">My Hubs</a></li> "); + "<li><a class=\"defaultDesc\" href=\"#unlistedHubs\">Connected Hubs</a></li> "); if (cfgOptionBooleanDefault("hgHubConnect.validateHub", TRUE)) printf("<li><a class=\"hubDeveloperDesc\" href=\"#hubDeveloper\">Hub Development</a></li>"); printf("</ul> "); // The public hubs table is getting big and takes a while to download. // Jquery UI's tabs() command will layout the page, but because of // jsInlining, it will only be called at the end of the page. This can lead to the page "jumping". // To make the inline code run now, let's flush JS inlines. // I'm not sure that this makes a visible difference, but it doesn't do any harm either jsInlineFinish(); jsInlineReset(); struct hash *publicHash = hgHubConnectPublic(); hgHubConnectUnlisted(hubList, publicHash);