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);