5b8fdca0d6014f4b2f363724ee91f096ef62eae5 chmalee Thu Sep 30 12:21:57 2021 -0700 Get hub track selection box and hgTrackUi links working on track search diff --git src/hg/hgTracks/searchTracks.c src/hg/hgTracks/searchTracks.c index 8564780..1e5a2b3 100644 --- src/hg/hgTracks/searchTracks.c +++ src/hg/hgTracks/searchTracks.c @@ -34,30 +34,35 @@ #define TRACK_SEARCH_FORM "trackSearch" #define SEARCH_RESULTS_FORM "searchResults" #define TRACK_SEARCH_CURRENT_TAB "tsCurTab" #define TRACK_SEARCH_SIMPLE "tsSimple" #define TRACK_SEARCH_ON_NAME "tsName" #define TRACK_SEARCH_ON_TYPE "tsType" #define TRACK_SEARCH_ON_GROUP "tsGroup" #define TRACK_SEARCH_ON_DESCR "tsDescr" #define TRACK_SEARCH_SORT "tsSort" #define TRACK_SEARCH_ON_HUBS "tsIncludePublicHubs" // the list of found tracks struct slRef *tracks = NULL; +// for advanced search only: +// associates hub id's to hub urls that have search results, +// used to get huburls onto hgTrackUi links +struct hash *hubIdsToUrls = NULL; + static int gCmpGroup(const void *va, const void *vb) /* Compare groups based on label. */ { const struct group *a = *((struct group **)va); const struct group *b = *((struct group **)vb); return strcmp(a->label, b->label); } // Would like to do a radio button choice ofsorts enum sortBy { sbRelevance=0, sbAbc =1, sbHierarchy=2, }; @@ -296,71 +301,75 @@ pfd->done = TRUE; if (measureTiming) { pfd->searchTime = clock1000() - startTime;; measureTime("Finished finding tracks for hub '%s': ", pfd->hubName); } slRemoveEl(&pfdRunning, pfd); slAddHead(&pfdDone, pfd); pthread_mutex_unlock(&pfdMutex); } errCatchEnd(errCatch); //always return NULL for pthread_create() return NULL; } -static void hubSearchHashToPfdList(struct hash *searchResultsHash, struct hash *hubLookup, - struct sqlConnection *conn) +static void hubSearchHashToPfdList(char *descSearch, struct hash *searchResultsHash, + struct hash *hubLookup, struct sqlConnection *conn) /* getHubSearchResults() returned a hash of search hits to various hubs, convert that * into something we can work on in parallel */ { struct hubSearchTracks *ret = NULL; struct hashCookie cookie = hashFirst(searchResultsHash); struct hash *hubUrlsToTrackList = hashNew(0); struct hashEl *hel = NULL; struct dyString *trackName = dyStringNew(0); struct slName *connectedHubs = hubConnectHubsInCart(cart); while ((hel = hashNext(&cookie)) != NULL) { struct hubSearchText *hst = (struct hubSearchText *)hel->val; struct hubEntry *hubInfo = (struct hubEntry *) hashFindVal(hubLookup, hst->hubUrl); if (isNotEmpty(hubInfo->errorMessage)) continue; - // if we were already checking out this hub, then it's search hits + // if we were already connected to this hub, then it's search hits // were already taken care of by the regular search code char hubId[256]; safef(hubId, sizeof(hubId), "%d", hubInfo->id); if (slNameInList(connectedHubs, hubId)) continue; struct hubConnectStatus *hub = hubConnectStatusForId(conn, hubInfo->id); // the hubSearchText contains multiple entries per lookup in order to form // the hgHubConnect UI. We only need one type of hit here: struct hubSearchTracks *found = hashFindVal(hubUrlsToTrackList, hst->hubUrl); for (; hst != NULL; hst = hst->next) { // don't add results for matches to the hub descriptionUrl, only to track names/descs if (isNotEmpty(hst->track) && hst->textLength != hubSearchTextMeta) { + // hst->textLength=hubSearchTextLong denotes hits to track description + if (isNotEmpty(descSearch) && hst->textLength != hubSearchTextLong) + continue; if (!found) { AllocVar(found); found->hubUrl = hst->hubUrl; found->searchedTracks = NULL; found->hub = hub; found->hubId = hubInfo->id; slAddHead(&ret, found); hashAdd(hubUrlsToTrackList, hst->hubUrl, found); + hashAdd(hubIdsToUrls, hubId, hst->hubUrl); } dyStringPrintf(trackName, "%s%d_%s", hubTrackPrefix, hubInfo->id, hst->track); slNameStore(&found->searchedTracks, cloneString(trackName->string)); dyStringClear(trackName); } } } struct hubSearchTracks *t; for (t = ret; t != NULL; t = t->next) { struct paraFetchData *pfd; AllocVar(pfd); pfd->hubName = t->hubUrl; pfd->hst = t; slAddHead(&pfdList, pfd); @@ -441,60 +450,62 @@ { // Should we warn or something that results are still waiting? As of now // just silently return instead, and note that no unconnected hub data // will show up (we get connected hub results for free because of // trackDbCaching) if (measureTiming) measureTime("Timed out searching hubs"); } if (lockStatus == 0) pthread_mutex_unlock(&pfdMutex); } void addHubSearchResults(struct slName *nameList, char *descSearch) /* add public hubs to the track list */ { -// set to something large so we always use the udc cache struct sqlConnection *conn = hConnectCentral(); char *hubSearchTableName = hubSearchTextTableName(); char *publicTable = cfgOptionEnvDefault("HGDB_HUB_PUBLIC_TABLE", hubPublicTableConfVariable, defaultHubPublicTableName); char *statusTable = cfgOptionEnvDefault("HGDB_HUB_STATUS_TABLE", hubStatusTableConfVariable, defaultHubStatusTableName); struct dyString *extra = dyStringNew(0); if (nameList) { struct slName *tmp = NULL; + char escapedInput[512]; for (tmp = nameList; tmp != NULL; tmp = tmp->next) { - dyStringPrintf(extra, "label like '%%%s%%'", tmp->name); + // escape user input manually: + sqlSafefFrag(escapedInput, sizeof(escapedInput), "%s", tmp->name); + dyStringPrintf(extra, "label like '%%%s%%'", escapedInput); if (tmp->next) dyStringPrintf(extra, " and "); } } if (sqlTableExists(conn, hubSearchTableName)) { struct hash *searchResultsHash = hashNew(0); struct hash *pHash = hashNew(0); struct slName *hubsToPrint = NULL; addPublicHubsToHubStatus(cart, conn, publicTable, statusTable); struct hash *hubLookup = buildPublicLookupHash(conn, publicTable, statusTable, &pHash); char *db = cloneString(trackHubSkipHubName(database)); tolowers(db); - getHubSearchResults(conn, hubSearchTableName, descSearch, descSearch != NULL, db, hubLookup, &searchResultsHash, &hubsToPrint, extra->string); - hubSearchHashToPfdList(searchResultsHash, hubLookup, conn); + getHubSearchResults(conn, hubSearchTableName, descSearch, isNotEmpty(descSearch), db, hubLookup, &searchResultsHash, &hubsToPrint, dyStringCannibalize(&extra)); + hubSearchHashToPfdList(descSearch, searchResultsHash, hubLookup, conn); if (measureTiming) measureTime("after querying hubSearchText table and ready to start threads"); int ptMax = atoi(cfgOptionDefault("parallelFetch.threads", "20")); int pfdListCount = 0, pt; if (ptMax > 0) { pfdListCount = slCount(pfdList); pthread_t *threads = NULL; ptMax = min(ptMax, pfdListCount); if (ptMax > 0) { AllocArray(threads, ptMax); for (pt = 0; pt < ptMax; pt++) { int rc = pthread_create(&threads[pt], NULL, addUnconnectedHubSearchResults, &threads[pt]); @@ -813,115 +824,164 @@ cgiMakeOnEventRadioButtonWithClass(TRACK_SEARCH_SORT, "1", (sortBy == sbAbc), NULL,"click", "findTracks.sortNow(this);"); hPrintf("Alphabetically"); cgiMakeOnEventRadioButtonWithClass(TRACK_SEARCH_SORT, "2", (sortBy == sbHierarchy), NULL,"click", "findTracks.sortNow(this);"); hPrintf("by Hierarchy  \n"); } hPrintf("\n"); // Set up json for js functionality struct jsonElement *jsonTdbVars = newJsonObject(newHash(8)); int trackCount=0; boolean containerTrackCount = 0; struct slRef *ptr; + struct slName *connectedHubIds = hubConnectHubsInCart(cart); while((ptr = slPopHead(&tracks))) { if (++trackCount > MAX_FOUND_TRACKS) break; struct track *track = (struct track *) ptr->val; jsonTdbSettingsBuild(jsonTdbVars, track, FALSE); // FALSE: No config from track search + char *hubId = NULL; + if (isHubTrack(track->track)) + { + char *trackNameCopy = cloneString(track->track); + hubId = strchr(trackNameCopy, '_'); + hubId += 1; + char *ptr2 = strchr(hubId, '_'); + if (ptr2 == NULL) + errAbort("hub track '%s' not in correct format", track->track); + *ptr2 = '\0'; + char *hubUrl = hashFindVal(hubIdsToUrls, hubId); + if (hubUrl != NULL) + { + struct jsonElement *ele = jsonFindNamedField(jsonTdbVars, "", track->track); + jsonObjectAdd(ele, "hubUrl", newJsonString(hubUrl)); + } + } if (tdbIsFolder(track->tdb)) // supertrack hPrintf("\n"); else if (tdbIsContainer(track->tdb)) hPrintf("\n"); else hPrintf("\n"); hPrintf("\n"); // Determine visibility and checked state track->visibility = tdbVisLimitedByAncestors(cart, track->tdb, TRUE, TRUE); boolean checked = ( track->visibility != tvHide ); if (tdbIsContainerChild(track->tdb)) { // Don't need all 4 states here. Visible=checked&&enabled checked = fourStateVisible(subtrackFourStateChecked(track->tdb,cart)); // Checked is only if subtrack level vis is also set! checked = (checked && ( track->visibility != tvHide )); } + // if we haven't already connected to this hub, then by default + // we need to unselect every checkbox + if (isHubTrack(track->track) && !slNameInList(connectedHubIds, hubId)) + checked = FALSE; // Setup the check box #define CB_HIDDEN_VAR "" // subtracks and folder children get "_sel" var. ("_sel" var is temp on folder children) if (tdbIsContainerChild(track->tdb) || tdbIsFolderContent(track->tdb)) hPrintf(CB_HIDDEN_VAR,track->track,checked?"1":CART_VAR_EMPTY); #define CB_SEEN "" hPrintf(CB_SEEN,track->track,(checked ? " CHECKED" : "")); safef(id, sizeof id, "%s_sel_id", track->track); // XSS Filter? jsOnEventById("click", id, "findTracks.clickedOne(this,true);"); hPrintf("\n"); // Setup the visibility drop down #define VIS_HIDDEN_VAR "" hPrintf(VIS_HIDDEN_VAR,track->track,CART_VAR_EMPTY); // All tracks get vis hidden var safef(id, sizeof id, "%s_id", track->track); // XSS Filter? safef(javascript, sizeof javascript, "findTracks.changeVis(this);"); struct slPair *event = slPairNew("change", cloneString(javascript)); if (tdbIsFolder(track->tdb)) { hideShowDropDownWithClassAndExtra(track->track, id, (track->visibility != tvHide), "normalText visDD", event); } else { hTvDropDownClassWithJavascript(NULL, id, track->visibility,track->canPack, "normalText seenVis",event); } + char *hubId = NULL; + if (isHubTrack(track->track)) + { + char *trackNameCopy = cloneString(track->track); + hubId = strchr(trackNameCopy, '_'); + hubId += 1; + char *ptr2 = strchr(hubId, '_'); + if (ptr2 == NULL) + errAbort("hub track '%s' not in correct format", track->track); + *ptr2 = '\0'; + } // If this is a container track, allow configuring... if (tdbIsContainer(track->tdb) || tdbIsFolder(track->tdb)) { containerTrackCount++; // Using onclick ensures return to search tracks on submit hPrintf("  ", track->track); safef(id, sizeof id, "%s_confSet", track->track); // XSS Filter? - jsOnEventByIdF("click", id, "findTracks.configSet(\"%s\");", track->track); + char hubConfigUrl[4096]; + safef(hubConfigUrl, sizeof(hubConfigUrl), "%s", track->track); + if (isHubTrack(track->track)) + { + char *hubUrl = hashFindVal(hubIdsToUrls, hubId); + if (hubUrl != NULL) + safefcat(hubConfigUrl, sizeof(hubConfigUrl), "&hubUrl=%s", hubUrl); + } + jsOnEventByIdF("click", id, "findTracks.configSet(\"%s\");", hubConfigUrl); } //#define SHOW_PARENT_FOLDER #ifdef SHOW_PARENT_FOLDER else if (tdbIsContainerChild(track->tdb) || tdbIsFolderContent(track->tdb)) { struct trackDb *parentTdb = tdbIsContainerChild(track->tdb) ? tdbGetContainer(track->tdb) : tdbGetImmediateFolder(track->tdb); if (parentTdb != NULL) // Using href will not return to search tracks on submit hPrintf("  ", parentTdb->track); } #endif///def SHOW_PARENT_FOLDER hPrintf("\n"); // shortLabel has description popup and longLabel has "..." metadata + char hgTrackUiUrl[4096]; + safef(hgTrackUiUrl, sizeof(hgTrackUiUrl), "%s", trackUrl(track->track, NULL)); + if (isHubTrack(track->track)) + { + char *hubUrl = hashFindVal(hubIdsToUrls, hubId); + if (hubUrl != NULL) + safefcat(hgTrackUiUrl, sizeof(hgTrackUiUrl), "&hubUrl=%s", hubUrl); + } hPrintf("%s\n", - track->track, trackUrl(track->track, NULL), track->shortLabel); + track->track, hgTrackUiUrl, track->shortLabel); safef(id, sizeof id, "%s_dispFndTrk", track->track); jsOnEventByIdF("click", id, "popUp.hgTrackUi('%s',true); return false;", track->track); hPrintf("%s", track->longLabel); compositeMetadataToggle(database, track->tdb, NULL, TRUE, FALSE); hPrintf("\n"); } //hPrintf("\n"); // Closing view in browser button and foundTracks count hPrintf(""); hPrintf(""); hPrintf("    "); if (tracksFound >= ENOUGH_FOUND_TRACKS) { @@ -1136,30 +1196,32 @@ hPrintf("\n"); hPrintf("\n"); hPrintf("\n"); hPrintf(""); // Restricts to max-width:1000px; cgiDown(0.8); if (measureTiming) measureTime("Rendered tabs"); if (doSearch) { // Now search long startTime = clock1000(); + if (hubIdsToUrls == NULL) + hubIdsToUrls = hashNew(0); if (selectedTab==simpleTab && !isEmpty(simpleEntry)) simpleSearchForTracks(simpleEntry); else if (selectedTab==advancedTab) advancedSearchForTracks(conn,groupList,nameSearch,typeSearch,descSearch, groupSearch,mdbSelects, includeHubResults); if (measureTiming) fprintf(stdout, "Searched for tracks: %lu
", clock1000()-startTime); // Sort and Print results if (selectedTab!=filesTab) { enum sortBy sortBy = cartUsualInt(cart,TRACK_SEARCH_SORT,sbRelevance); tracksFound = slCount(tracks); if (tracksFound > 1)