b53a97d26633a1f174a7031559a69d37e393379f chmalee Fri May 1 14:19:19 2026 -0700 Allow myVariants tracks to be shared similar to google docs, read only and read+edit access by username or for all. Last write wins. Currently no history of changes, leaving that for the next version of the feature diff --git src/hg/hgTrackUi/hgTrackUi.c src/hg/hgTrackUi/hgTrackUi.c index 844673f117d..d82c9429a94 100644 --- src/hg/hgTrackUi/hgTrackUi.c +++ src/hg/hgTrackUi/hgTrackUi.c @@ -18,30 +18,32 @@ #include "snpUi.h" #include "snp125Ui.h" #include "snp125.h" #include "sample.h" #include "wiggle.h" #include "hgMaf.h" #include "obscure.h" #include "chainCart.h" #include "chainDb.h" #include "gvUi.h" #include "grp.h" #include "oregannoUi.h" #include "chromGraph.h" #include "hgConfig.h" #include "customTrack.h" +#include "myVariants.h" +#include "wikiLink.h" #include "dupTrack.h" #include "dbRIP.h" #include "tfbsConsSites.h" #include "hapmapSnps.h" #include "nonCodingUi.h" #include "expRecord.h" #include "wikiTrack.h" #include "hubConnect.h" #include "trackHub.h" #include "pcrResult.h" #include "dgv.h" #include "transMapStuff.h" #include "vcfUi.h" #include "bbiFile.h" #include "ensFace.h" @@ -3282,30 +3284,86 @@ jsIncludeFile("dataTables-2.2.2.min.js", NULL); jsIncludeFile("dataTables.select-3.0.0.min.js", NULL); jsIncludeFile("facetedComposite.js", NULL); webIncludeResourceFile("dataTables-2.2.2.min.css"); webIncludeResourceFile("dataTables.select-3.0.0.min.css"); webIncludeResourceFile("facetedComposite.css"); // cleanup slPairFreeValsAndList(&dataTypes); hashFree(&defaultOn); } +static void myVariantsShareUi(struct trackDb *tdb) +/* Render the inline share management section on hgTrackUi for a user's own + * myVariants track. Emits HTML + calls into hui.js via jsInline to wire up + * the create/list/revoke API calls. */ +{ +char *userName = getUserName(); +if (userName == NULL) + { + printf("

Please log in to manage shares.

\n"); + return; + } + +printf("

Share this track

\n"); +printf("
\n"); +printf("

Create share link

\n"); +printf("\n"); + +/* Project dropdown */ +printf("\n"); + +/* Permission radios */ +printf("\n"); +printf("" + "\n"); +printf("" + "\n"); +printf("
Project:
Permission:" + " " + "" + "
Share with:
Label:
\n"); +printf("\n"); +printf("
" + "Share URL: " + "" + "
\n"); +printf("
\n"); + +printf("
\n"); +printf("
\n"); +printf("

Active shares

\n"); +printf("
Loading...
\n"); +printf("
\n"); + +jsInline("if (typeof myVariantsShareInit === 'function') myVariantsShareInit();\n"); +} + void specificUi(struct trackDb *tdb, struct trackDb *tdbList, struct customTrack *ct, boolean ajax) /* Draw track specific parts of UI. */ { char *track = tdb->track; char *db = database; char *liftDb = cloneString(trackDbSetting(tdb, "quickLiftDb")); if (liftDb != NULL) db = liftDb; // Ideally check cfgTypeFromTdb()/cfgByCfgType() first, but with all these special cases already in // place, lets be cautious at this time. // NOTE: Developer, please try to use cfgTypeFromTdb()/cfgByCfgType(). boolean boxed = trackDbSettingClosestToHomeOn(tdb, "boxedCfg"); boolean isGencode3 = trackDbSettingOn(tdb, "isGencode3"); // UI precedence: @@ -3489,30 +3547,37 @@ else if (startsWith("chromGraph", tdb->type)) chromGraphUi(tdb); else if (startsWith("sample", tdb->type)) genericWiggleUi(tdb,7); else if (startsWithWord("array",tdb->type)) // not quite the same as "expRatio" (custom tracks) expRatioCtUi(tdb); else if (startsWithWord("factorSource",tdb->type)) factorSourceUi(db,tdb); else if (startsWithWord("bigBed",tdb->type)) labelCfgUi(db, cart, tdb, tdb->track); } if (tdbSupportsColorOverride(tdb)) colorTrackOption(cart, tdb->track, tdb); +/* myVariants own track: render inline share management. Skip shared tracks + * (myVariants_shared_*) - you can't re-share someone else's data. */ +if (cfgOptionBooleanDefault("doMyVariants", FALSE) + && startsWith("myVariants_", tdb->track) + && !startsWith("myVariants_shared_", tdb->track)) + myVariantsShareUi(tdb); + if (!ajax) // ajax asks for a simple cfg dialog for right-click popup or hgTrackUi subtrack cfg { // Composites *might* have had their top level controls just printed, // but almost certainly have additional controls boolean isLogo = (trackDbSetting(tdb, "logo") != NULL); // It'd be nice to handle faceted composites as a separate container type, but practically so much // of the display features we want are identical to composites - it's easier to special case the UI. if (tdbIsComposite(tdb) && !isLogo) // for the moment generalizing this to include other containers... if (!sameOk(trackDbLocalSetting(tdb, "compositeTrack"), "faceted")) // but not faceted containers ... hCompositeUi(db, cart, tdb, NULL, NULL, MAIN_FORM); // Additional special case navigation links may be added extraUiLinks(db, tdb, cart); } } @@ -4371,41 +4436,71 @@ } if (dupTdb != NULL) slAddHead(&tdbList, dupTdb); } } if (sameWord(track, WIKI_TRACK_TABLE)) tdb = trackDbForWikiTrack(); else if (sameWord(track, RULER_TRACK_NAME)) /* special handling -- it's not a full-fledged track */ tdb = trackDbForRuler(); else if (sameWord(track, OLIGO_MATCH_TRACK_NAME)) tdb = trackDbForOligoMatch(); else if (sameWord(track, CUTTERS_TRACK_NAME)) tdb = trackDbForPseudoTrack(CUTTERS_TRACK_NAME, CUTTERS_TRACK_LABEL, CUTTERS_TRACK_LONGLABEL, tvHide, TRUE); -else if (isCustomTrack(track)) +else if (isCustomTrack(track) + || (cfgOptionBooleanDefault("doMyVariants", FALSE) && startsWith("myVariants_", track))) { + /* myVariants tracks (own and shared) are built dynamically and live in + * the CT list rather than the SQL trackDb table, but their names don't + * carry the ct_ prefix, so we need to look them up alongside regular + * custom tracks. */ ctList = customTracksParseCart(database, cart, NULL, NULL); for (ct = ctList; ct != NULL; ct = ct->next) { if (sameString(track, ct->tdb->track)) { tdb = ct->tdb; break; } } + /* Fallback for direct hgTrackUi navigation without hgTracks: regenerate + * the myVariants CT file and re-parse. Normally the CT file was written + * during the preceding hgTracks visit, so this branch only fires for + * bookmarked URLs or direct links. */ + if (tdb == NULL && startsWith("myVariants_", track)) + { + char *userName = getUserName(); + char *ctFile = myVariantsWriteCtFile(userName, database, cart); + if (isNotEmpty(ctFile)) + { + char mvVarName[256]; + safef(mvVarName, sizeof mvVarName, CT_FILE_VAR_PREFIX "%s", database); + cartSetString(cart, mvVarName, ctFile); + freeMem(ctFile); + ctList = customTracksParseCart(database, cart, NULL, NULL); + for (ct = ctList; ct != NULL; ct = ct->next) + { + if (sameString(track, ct->tdb->track)) + { + tdb = ct->tdb; + break; + } + } + } + } } else if (isHubTrack(track)) { tdb = hubConnectAddHubForTrackAndFindTdb(database, track, &tdbList, trackHash); } else if (sameString(track, "hgPcrResult")) tdb = pcrResultFakeTdb(); else { tdb = tdbForTrack(database, track,&tdbList); } if (tdb == NULL) { errAbort("Can't find %s in track database %s chromosome %s", track, database, chromosome);