77dde8a831004c9007488a90784605b6b6ee168e
chmalee
  Tue Apr 16 11:22:04 2019 -0700
First pass on validate hub tab for hgHubConnect, currently hg.conf controlled. Work in progress still, refs #18870

diff --git src/hg/hgHubConnect/hgHubConnect.c src/hg/hgHubConnect/hgHubConnect.c
index d2de99c..37ed605 100644
--- src/hg/hgHubConnect/hgHubConnect.c
+++ src/hg/hgHubConnect/hgHubConnect.c
@@ -369,30 +369,174 @@
 	ourPrintCell("");
 
 
     if (hub->trackHub != NULL)
 	printGenomes(hub->trackHub, count, !hubHasError);
     else
 	ourPrintCell("");
 
     puts("</tr>");
     }
 
 printf("</tbody></TABLE>\n");
 printf("</div>");
 }
 
+int doCheckTrackDb(struct dyString *errors, struct trackHub *hub,
+                    struct trackHubGenome *genome)
+/* Attempt to open a trackDb from a hub and report errors back.
+ * Eventually will check every stanza and not die if there's an error with the first one*/
+{
+int errorCount = 0;
+struct trackDb *tdb = NULL;
+struct errCatch *errCatch = errCatchNew();
+if (errCatchStart(errCatch))
+    {
+    tdb = trackHubTracksForGenome(hub, genome);
+    tdb = trackDbLinkUpGenerations(tdb);
+    tdb = trackDbPolishAfterLinkup(tdb, genome->name);
+    if (tdb != NULL)
+        {
+        dyStringPrintf(errors, "<ul>\n<li>%s</li>\n", tdb->track);
+        dyStringPrintf(errors, "<ul>\n<li>No errors found</li>\n</ul></ul>");
+        }
+    }
+errCatchEnd(errCatch);
+if (errCatch->gotError)
+    {
+    errorCount += 1;
+    dyStringPrintf(errors, "<ul><li><span class=\"hubError\">Error: %s</span></li></ul>\n",
+        errCatch->message->string);
+    }
+errCatchFree(&errCatch);
+return errorCount;
+}
+
+int doCheckGenomesFile(struct dyString *errors, struct trackHub *hub)
+/* Mostly just calls doCheckTrackDb() but applies correct styling to error messages */
+{
+struct trackHubGenome *genome = NULL;
+int errorCount = 0;
+int trackDbErrorCount = 0;
+struct dyString *trackDbErrorString = dyStringNew(0);
+for (genome = hub->genomeList; genome != NULL; genome = genome->next)
+    {
+    trackDbErrorCount = 0;
+    dyStringPrintf(errors, "<ul>\n<li>genome %s", genome->name);
+    // check each track for this genome
+    trackDbErrorCount = doCheckTrackDb(trackDbErrorString, hub, genome);
+    errorCount += trackDbErrorCount;
+    if (trackDbErrorCount > 0)
+        {
+        if (trackDbErrorCount == 1)
+            dyStringPrintf(errors, " (%d error)\n%s</ul>\n",  trackDbErrorCount, trackDbErrorString->string);
+        else
+            dyStringPrintf(errors, " (%d errors)\n%s</ul>\n",  trackDbErrorCount, trackDbErrorString->string);
+        }
+    else
+        dyStringPrintf(errors, "<ul><li>No trackDb errors</ul></ul>");
+    dyStringClear(trackDbErrorString);
+    }
+dyStringFree(&trackDbErrorString);
+return errorCount;
+}
+
+struct trackHub *trackHubCheckHubFile(struct dyString *errors, char *hubUrl)
+/* Wrap trackHubOpen in an errCatch, HTML style the error message */
+{
+struct trackHub *hub = NULL;
+struct errCatch *errCatch = errCatchNew();
+if (errCatchStart(errCatch))
+    hub = trackHubOpen(hubUrl, "");
+errCatchEnd(errCatch);
+if (errCatch->gotError)
+    dyStringPrintf(errors, "<span class=\"hubError\">Error: %s", errCatch->message->string);
+errCatchFree(&errCatch);
+return hub;
+}
+
+void doValidateNewHub(char *hubUrl)
+/* Open up a track hub and report errors to user. */
+{
+struct trackHub *hub = NULL;
+struct dyString *errors = dyStringNew(0); // string with HTML of all the errors
+udcSetCacheTimeout(1);
+printf("<tr><td>");
+hub = trackHubCheckHubFile(errors, hubUrl);
+if (hub)
+    {
+    // go through each genome and check each trackDb stanza, building up errors string
+    int errorCount = doCheckGenomesFile(errors, hub);
+    if (errorCount > 0)
+        {
+        printf("<div style=\"text-align: left\" class=\"hubTdbTree\">");
+        if (errorCount == 1)
+            printf("<ul>\n<li>Found %d error in %s<ul><li>hub name: \"%s\"\n", errorCount, hub->url, hub->shortLabel);
+        else
+            printf("<ul>\n<li>Found %d errors in %s<ul><li>hub name: \"%s\"\n", errorCount, hub->url, hub->shortLabel);
+        printf("%s", errors->string);
+        printf("</ul>\n");
+        }
+    else
+        printf("<div>No configuration errors for hub: %s</div>", hub->shortLabel);
+    }
+else
+    {
+    printf("<div>%s</div>", errors->string);
+    }
+dyStringFree(&errors);
+printf("</div></td></tr>");
+}
+
+void hgHubConnectValidateNewHub()
+{
+// put out the top of our page
+char *hubUrl = cartOptionalString(cart, "validateHubUrl");
+printf("<div id=\"validateHub\" class=\"hubList\"> \n"
+    "<table id=\"validateHub\"> \n"
+    "<thead><tr> \n");
+printf("<th colspan=\"6\" id=\"addHubBar\"><label for=\"validateHubUrl\">URL:</label> \n");
+if (hubUrl != NULL)
+    {
+	printf("<input name=\"validateText\" id=\"validateHubUrl\" class=\"hubField\" "
+	    "type=\"text\" size=\"65\" value=\"%s\"> \n", hubUrl);
+    }
+else
+    {
+    printf("<input name=\"validateText\" id=\"validateHubUrl\" class=\"hubField\" "
+        "type=\"text\" size=\"65\"> \n");
+    }
+printf("<input name=\"hubValidateButton\" id='hubValidateButton' "
+    "class=\"hubField\" type=\"button\" value=\"Validate Hub\">\n"
+    "</th> \n"
+    "</tr> \n");
+
+if (hubUrl == NULL)
+    printf("<tr><td>Enter URL to hub to check settings</td></tr> \n");
+else
+    doValidateNewHub(hubUrl);
+printf("</table>");
+
+jsOnEventById("click", "hubValidateButton",
+    "var validateText = document.getElementById('validateHubUrl');"
+    "validateText.value=$.trim(validateText.value);"
+    "if(validateUrl($('#validateHubUrl').val())) { "
+    " document.validateHubForm.elements['validateHubUrl'].value=validateText.value;"
+    " document.validateHubForm.submit(); return true; }"
+    "else { return false; }"
+    );
+}
 
 static void addPublicHubsToHubStatus(struct sqlConnection *conn, char *publicTable, char  *statusTable)
 /* Add urls in the hubPublic table to the hubStatus table if they aren't there already */
 {
 char query[1024];
 sqlSafef(query, sizeof(query),
         "select %s.hubUrl from %s left join %s on %s.hubUrl = %s.hubUrl where %s.hubUrl is NULL",
         publicTable, publicTable, statusTable, publicTable, statusTable, statusTable); 
 struct sqlResult *sr = sqlGetResult(conn, query);
 char **row;
 while ((row = sqlNextRow(sr)) != NULL)
     {
     char *errorMessage = NULL;
     char *url = row[0];
 
@@ -1463,42 +1607,56 @@
 
 // this is the form for the reset hub button
 printf("<FORM ACTION=\"%s\" NAME=\"resetHubForm\">\n",  "../cgi-bin/hgHubConnect");
 cgiMakeHiddenVar(hgHubCheckUrl, "");
 cartSaveSession(cart);
 puts("</FORM>");
 
 // this is the form for the search hub button
 printf("<FORM ACTION=\"%s\" NAME=\"searchHubForm\">\n",  "../cgi-bin/hgHubConnect");
 cgiMakeHiddenVar(hgHubSearchTerms, "");
 cgiMakeHiddenVar(hgHubDoSearch, "on");
 cgiMakeHiddenVar(hgHubDbFilter, "");
 cartSaveSession(cart);
 puts("</FORM>");
 
+// this is the form for the validate button
+boolean doValidate = cfgOptionBooleanDefault("hgHubConnect.validateHub", FALSE);
+if (doValidate)
+    {
+    printf("<FORM ACTION=\"%s\" NAME=\"validateHubForm\">\n",  "../cgi-bin/hgHubConnect");
+    cgiMakeHiddenVar("validateHubUrl", "");
+    cartSaveSession(cart);
+    puts("</FORM>");
+    }
+
 // ... and now the main form
 printf("<FORM ACTION=\"%s\" METHOD=\"POST\" NAME=\"mainForm\">\n", "../cgi-bin/hgGateway");
 cartSaveSession(cart);
 
 // we have two tabs for the public and unlisted hubs
 printf("<div id=\"tabs\">"
        "<ul> <li><a href=\"#publicHubs\">Public Hubs</a></li>"
-       "<li><a href=\"#unlistedHubs\">My Hubs</a></li> "
-       "</ul> ");
+       "<li><a href=\"#unlistedHubs\">My Hubs</a></li> ");
+if (doValidate) // put up the validate tab if hg.conf statement present
+    printf("<li><a href=\"#validateHub\">Validate Hub</a></li>");
+printf("</ul> ");
 
 struct hash *publicHash = hgHubConnectPublic();
 hgHubConnectUnlisted(hubList, publicHash);
+if (doValidate)
+    hgHubConnectValidateNewHub();
 printf("</div>");
 
 printf("<div class=\"tabFooter\">");
 
 printf("<span class=\"small\">"
     "Contact <a href=\"../contacts.html\">us</A> to add a public hub."
     "</span>\n");
 printf("</div>");
 
 cgiMakeHiddenVar(hgHubConnectRemakeTrackHub, "on");
 
 puts("</FORM>");
 printf("</div>\n");
 
 jsInline(