\n");
}
else
printf("genome: %s assembly: %s [%s]\n",
organism, hFreezeDate(database), database);
if (measureTiming && (loadTime > 0))
printf("\n load time: %ld ms \n", loadTime);
/* place for warning messages to appear */
if (isNotEmpty(warn))
{
char *encoded = htmlEncode(warn);
printf("
%s", encoded);
freeMem(encoded);
}
/* count up number of custom tracks for this genome */
int numCts = slCount(ctList);
cgiSimpleTableStart();
cgiSimpleTableRowStart();
if (numCts)
{
puts("
");
puts("");
/* determine if there's a navigation position for this screen */
char *pos = NULL;
if (ctList)
{
pos = ctInitialPosition(ctList);
if (!pos)
pos = ctFirstItemPos(ctList);
}
/* button for GB navigation */
puts("
");
printf("
\n",
hgTracksName());
cartSaveSession(cart);
printf("",
"go to genome browser", buttonWidth);
if (pos)
cgiMakeHiddenVar("position", pos);
puts("
");
puts("
");
/* button for TB navigation */
puts("
");
printf("
\n",
hgTablesName());
cartSaveSession(cart);
printf("",
"go to table browser", buttonWidth);
puts("
");
puts("
");
puts("
");
puts("
");
cgiTableRowEnd();
cgiTableEnd();
cartSetString(cart, "hgta_group", "user");
}
void helpCustom()
/* display documentation */
{
webNewSection("Loading Custom Tracks");
char *browserVersion;
if (btIE == cgiClientBrowser(&browserVersion, NULL, NULL) && *browserVersion < '8')
puts("");
else
puts("");
webIncludeHelpFile("customTrackLoad", FALSE);
puts("");
}
void doBrowserLines(struct slName *browserLines, char **retErr)
/* parse variables from browser lines into the cart */
{
char *err = NULL;
struct slName *bl;
for (bl = browserLines; bl != NULL; bl = bl->next)
{
char *words[96];
int wordCount;
wordCount = chopLine(bl->name, words);
if (wordCount > 1)
{
char *command = words[1];
if (sameString(command, "hide")
|| sameString(command, "dense")
|| sameString(command, "pack")
|| sameString(command, "squish")
|| sameString(command, "full"))
{
if (wordCount > 2)
{
int i;
for (i=2; i hChromSize(database, chrom))
{
err ="Invalid browser position (use chrN:123-456 format)";
break;
}
cartSetString(cart, "position", words[2]);
}
}
}
if (retErr)
*retErr = err;
}
void doAddCustom(char *err)
/* display form for adding custom tracks.
* Include error message, if any */
{
cartWebStart(cart, database, "Add Custom Tracks");
addCustomForm(NULL, err);
helpCustom();
cartWebEnd(cart);
}
void doUpdateCustom(struct customTrack *ct, char *err)
/* display form for adding custom tracks.
* Include error message, if any */
{
char *longLabel = htmlEncode(ct->tdb->longLabel);
cartWebStart(cart, database, "Update Custom Track: %s [%s]",
longLabel, database);
freeMem(longLabel);
cartSetString(cart, hgCtDocText, ct->tdb->html);
addCustomForm(ct, err);
helpCustom();
cartWebEnd(cart);
}
static void doManageCustom(char *warn)
/* display form for deleting & updating custom tracks.
* Include warning message, if any */
{
cartWebStart(cart, database, "Manage Custom Tracks");
manageCustomForm(warn);
webNewSection("Managing Custom Tracks");
webIncludeHelpFile("customTrackManage", FALSE);
cartWebEnd(cart);
}
char *fixNewData(struct cart *cart)
/* append a newline to incoming data, to keep custom preprocessor happy */
{
char *customText = cartUsualString(cart, hgCtDataText, "");
if (isNotEmpty(customText))
{
struct dyString *ds = dyStringNew(0);
dyStringPrintf(ds, "%s\n", customText);
customText = dyStringCannibalize(&ds);
cartSetString(cart, hgCtDataText, customText);
}
return customText;
}
char *replacedTracksMsg(struct customTrack *replacedCts)
/* make warning message listing replaced tracks */
{
struct customTrack *ct;
if (!slCount(replacedCts))
return NULL;
struct dyString *dsWarn = dyStringNew(0);
dyStringAppend(dsWarn, "Replaced: ");
for (ct = replacedCts; ct != NULL; ct = ct->next)
{
if (ct != replacedCts)
/* not the first */
dyStringAppend(dsWarn, ", ");
dyStringAppend(dsWarn, ct->tdb->shortLabel);
}
return dyStringCannibalize(&dsWarn);
}
void doDeleteCustom()
/* remove custom tracks from list based on cart variables */
{
struct customTrack *ct;
for (ct = ctList; ct != NULL; ct = ct->next)
{
char var[256];
safef(var, sizeof var, "%s_%s", hgCtDeletePrefix, ct->tdb->track);
if (cartBoolean(cart, var))
slRemoveEl(&ctList, ct);
}
}
void doRefreshCustom(char **warn)
/* reparse custom tracks from URLs based on cart variables */
{
struct customTrack *ct;
struct customTrack *replacedCts = NULL;
struct customTrack *refreshCts = NULL;
for (ct = ctList; ct != NULL; ct = ct->next)
{
char var[256];
safef(var, sizeof var, "%s_%s", hgCtRefreshPrefix, ct->tdb->track);
if (cartUsualBoolean(cart, var, FALSE))
{
struct customTrack *nextCt = NULL, *urlCt = NULL;
struct customTrack *urlCts =
customFactoryParse(database, ctDataUrl(ct), FALSE, NULL);
for (urlCt = urlCts; urlCt != NULL; urlCt = nextCt)
{
nextCt = urlCt->next;
if (sameString(ct->tdb->track, urlCt->tdb->track))
slAddHead(&refreshCts, urlCt);
}
}
}
ctList = customTrackAddToList(ctList, refreshCts, &replacedCts, FALSE);
if (warn)
*warn = replacedTracksMsg(replacedCts);
customTrackHandleLift(database, ctList);
}
void addWarning(struct dyString *ds, char *msg)
/* build up a warning message from parts */
{
if (!msg)
return;
if (isNotEmpty(ds->string))
dyStringAppend(ds, ". ");
dyStringAppend(ds, msg);
}
char *saveLines(char *text, int max)
/* save lines from input, up to 'max'.
* Prepend with comment, if truncated */
{
if (!text)
return NULL;
char buf[128];
int count = 0;
char *line;
boolean truncated = FALSE;
struct dyString *ds = dyStringNew(0);
safef(buf, sizeof buf, "# Displaying first %d lines of data", max);
struct lineFile *lf = lineFileOnString("saved custom text", TRUE, text);
while (lineFileNext(lf, &line, NULL))
{
if (startsWith(buf, line))
continue;
if (++count > max)
{
truncated = TRUE;
break;
}
dyStringAppend(ds, line);
dyStringAppend(ds, "\n");
}
if (truncated)
{
struct dyString *dsNew = dyStringNew(0);
dyStringPrintf(dsNew, "%s\n%s", buf, dyStringCannibalize(&ds));
return dyStringCannibalize(&dsNew);
}
return (dyStringCannibalize(&ds));
}
struct customTrack *ctFromList(struct customTrack *ctList, char *track)
/* return custom track from list */
{
struct customTrack *ct = NULL;
for (ct = ctList; ct != NULL; ct = ct->next)
if (sameString(track, ct->tdb->track))
return ct;
return NULL;
}
boolean customTrackHasConfig(char *text)
/* determine if there are track or browser lines in text */
{
text = skipLeadingSpaces(text);
return startsWith("track ", text) || startsWith("browser ", text);
}
void doMiddle(struct cart *theCart)
/* create web page */
{
char *ctFileName = NULL;
struct slName *browserLines = NULL;
struct customTrack *replacedCts = NULL;
char *err = NULL, *warn = NULL;
char *selectedTable = NULL;
struct customTrack *ct = NULL;
boolean ctUpdated = FALSE;
char *initialDb = NULL;
long thisTime = clock1000();
cart = theCart;
measureTiming = isNotEmpty(cartOptionalString(cart, "measureTiming"));
initialDb = cloneString(cartString(cart, "db"));
getDbAndGenome(cart, &database, &organism, oldVars);
setUdcCacheDir();
customFactoryEnableExtraChecking(TRUE);
#if (defined USE_BAM && defined KNETFILE_HOOKS)
knetUdcInstall();
if (udcCacheTimeout() < 300)
udcSetCacheTimeout(300);
#endif//def USE_BAM && KNETFILE_HOOKS
if (sameString(initialDb, "0"))
{
/* when an organism is selected from the custom track management page,
* set the database to be the default only if it has custom tracks.
* Otherwise, pick an assembly for that organism that does have custom tracks. */
struct dbDb *dbDb, *dbDbs = getCustomTrackDatabases();
char *dbWithCts = NULL;
for (dbDb = dbDbs; dbDb != NULL; dbDb = dbDb->next)
{
if (sameString(database, dbDb->name))
break;
if (sameString(organism, dbDb->organism))
{
if (!dbWithCts)
dbWithCts = cloneString(dbDb->name);
}
}
if (dbWithCts)
/* set the database for the selected organism to an assembly that
* has custom tracks */
{
database = dbWithCts;
cartSetString(cart, "db", database);
}
}
if (cartVarExists(cart, hgCtDoAdd))
doAddCustom(NULL);
else if (cartVarExists(cart, hgCtTable))
{
/* update track */
/* need to clone the hgCtTable value, as the ParseCart will remove
the variable */
selectedTable = cloneString(cartString(cart, hgCtTable));
if (isNotEmpty(selectedTable))
{
ctList = customTracksParseCart(database, cart, NULL, NULL);
ct = ctFromList(ctList, selectedTable);
}
if (ct)
doUpdateCustom(ct, NULL);
else
doAddCustom(NULL);
}
else
{
/* get new and existing custom tracks from cart and decide what to do */
char *customText = fixNewData(cart);
/* save input so we can display if there's an error */
char *savedCustomText = saveLines(cloneString(customText),
SAVED_LINE_COUNT);
char *trackConfig = cartOptionalString(cart, hgCtConfigLines);
char *savedConfig = cloneString(trackConfig);
struct dyString *dsWarn = dyStringNew(0);
char *fileName = cartOptionalString(cart, hgCtDataFileName);
boolean hasData = (isNotEmpty(customText) || isNotEmpty(fileName));
if (cartVarExists(cart, hgCtUpdatedTrack) && hasData)
{
/* from 'update' screen */
/* prepend config to data for parser */
struct dyString *dsTrack = dyStringNew(0);
if (!trackConfig)
trackConfig = cartOptionalString(cart, hgCtUpdatedTrack);
char *fileContents = NULL;
if (isNotEmpty(fileName))
{
if (customTrackIsCompressed(fileName))
fileContents = "Compressed files not supported for data update";
else
fileContents = cartOptionalString(cart, hgCtDataFile);
customText = fileContents;
}
/* check for duplicate track config in config and data entry */
if (customTrackHasConfig(trackConfig) &&
customTrackHasConfig(customText))
{
if (startsWith(trackConfig, customText))
trackConfig = "";
else
customText = "Duplicate track configuration found - remove track and browser lines from Configuration box or from Data";
}
dyStringPrintf(dsTrack, "%s\n%s\n", trackConfig, customText);
customText = dyStringCannibalize(&dsTrack);
cartSetString(cart, hgCtDataText, customText);
if (isNotEmpty(fileContents))
{
/* already handled file */
cartRemove(cart, hgCtDataFile);
cartRemove(cart, hgCtDataFileName);
}
}
boolean ctParseError = FALSE;
struct errCatch *catch = errCatchNew();
if (errCatchStart(catch))
ctList = customTracksParseCartDetailed(database, cart, &browserLines, &ctFileName,
&replacedCts, NULL, &err);
errCatchEnd(catch);
if (catch->gotError)
{
addWarning(dsWarn, err);
addWarning(dsWarn, catch->message->string);
ctParseError = TRUE;
}
errCatchFree(&catch);
/* exclude special setting used by table browser to indicate
* db assembly for error-handling purposes only */
char *db = NULL;
if (trackConfig && (db = stringIn("db=", trackConfig)) != NULL)
{
db += 3;
char *nextTok = skipToSpaces(db);
if (!nextTok)
nextTok = strchr(db, 0);
db = cloneStringZ(db,nextTok-db);
if (!sameString(db,database))
err = "Invalid configuration found - remove db= or return it to it's original value";
}
if (cartVarExists(cart, hgCtUpdatedTrack) && !hasData)
{
/* update custom track config and doc, but not data*/
selectedTable = cartOptionalString(cart, hgCtUpdatedTable);
if (selectedTable)
{
ct = ctFromList(ctList, selectedTable);
if (ct)
{
struct errCatch *catch = errCatchNew();
if (errCatchStart(catch))
{
customTrackUpdateFromConfig(ct, database, trackConfig, &browserLines);
ctUpdated = TRUE;
}
errCatchEnd(catch);
if (catch->gotError)
addWarning(dsWarn, catch->message->string);
errCatchFree(&catch);
}
}
}
addWarning(dsWarn, replacedTracksMsg(replacedCts));
doBrowserLines(browserLines, &warn);
addWarning(dsWarn, warn);
if (err)
{
char *selectedTable = NULL;
cartSetString(cart, hgCtDataText, savedCustomText);
cartSetString(cart, hgCtConfigLines, savedConfig);
if ((selectedTable= cartOptionalString(cart, hgCtUpdatedTable)) != NULL)
{
ct = ctFromList(ctList, selectedTable);
doUpdateCustom(ct, err);
}
else
doAddCustom(err);
cartRemovePrefix(cart, hgCt);
return;
}
if (cartVarExists(cart, hgCtDoDelete))
{
doDeleteCustom();
ctUpdated = TRUE;
}
if (cartVarExists(cart, hgCtDoRefresh))
{
doRefreshCustom(&warn);
addWarning(dsWarn, warn);
ctUpdated = TRUE;
}
if (ctUpdated || ctConfigUpdate(ctFileName))
{
customTracksSaveCart(database, cart, ctList);
/* refresh ctList again to pickup remote resource error state */
struct errCatch *catch = errCatchNew();
if (errCatchStart(catch))
ctList = customTracksParseCartDetailed(database, cart, &browserLines, &ctFileName,
&replacedCts, NULL, &err);
errCatchEnd(catch);
if (catch->gotError)
{
addWarning(dsWarn, err);
addWarning(dsWarn, catch->message->string);
ctParseError = TRUE;
}
errCatchFree(&catch);
}
warn = dyStringCannibalize(&dsWarn);
if (measureTiming)
{
long lastTime = clock1000();
loadTime = lastTime - thisTime;
}
if (!initialDb || ctList || cartVarExists(cart, hgCtDoDelete))
doManageCustom(warn);
else if (ctParseError)
doAddCustom(warn);
else
doAddCustom(NULL);
}
cartRemovePrefix(cart, hgCt);
cartRemove(cart, CT_CUSTOM_TEXT_VAR);
}
int main(int argc, char *argv[])
/* Process command line. */
{
htmlPushEarlyHandlers();
oldVars = hashNew(10);
cgiSpoof(&argc, argv);
cartEmptyShell(doMiddle, hUserCookie(), excludeVars, oldVars);
return 0;
}