1a2011f25ac0bd4ce02ed0eb093ff169724f9047 angie Mon May 2 16:39:23 2016 -0700 Added trackDb setting "tableBrowser noGenome", which allows tables to appear in TB and DI menus, unlike "tableBrowser off", but does not allow genome-wide queries on the tables. The new "noGenome" setting is used for OMIM tracks -- OMIM gave the OK for non-genome-wide queries on their tables. See #4458#note-53 for details of UI & functional changes to the TB and DI for noGenome tracks. hg/lib/cartTrackDb.c handles access control based on trackDb tableBrowser settings (and the seldom-used tableAccessControl database tables that restrict tables to be viewed only by certain hosts). As before, 'tableBrowser off' means that the track is removed from the trackList so the TB & DI don't even know that it exists. 'tableBrowser noGenome' tracks are included in the trackList, but the tracks and any tables listed after 'noGenome' are hashed for later use by cartTrackDbIsNoGenome(). The hash used to contain permitted-host lists, but now contains structs that combine the permitted-host lists with a noGenome flag. hgIntegrator.c now includes a 'noGenome' flag in the JSONified groupedTrackDb, which the JS code uses to identify noGenome tracks. When executing a query, if the query region is genome but hgi_querySpec has noGenome related tables left over from past position-only queries, those related table settings are removed from the parsed JSON querySpec->config->relatedTables that is passed down into annoStreamDb. If one of the dataSources is noGenome but the region is genome (should be possible only by URL-tweaking), the region is forced to position. In hgTables, when region is genome, correlation and intersection track menu options for noGenome tracks are disabled. On the main page, JS code controls whether options are disabled depending on the current region. When listing related tables for filtering or selected fields output, if region is genome then the checkboxes for noGenome tables are disabled. If the cart has noGenome related table or intersection settings left over from past searches, they are ignored. If a noGenome query URL is tweaked to have hgta_region=genome, the TB errors out. hgIntegratorModel.js handles disabling of menu options and related table field settings for noGenome tracks when region is genome. refs #4458 diff --git src/hg/lib/jsHelper.c src/hg/lib/jsHelper.c index 9348b02..a1a6980 100644 --- src/hg/lib/jsHelper.c +++ src/hg/lib/jsHelper.c @@ -1,571 +1,581 @@ // jsHelper.c - helper routines for interface between CGIs and client-side javascript /* Copyright (C) 2014 The Regents of the University of California * See README in this or parent directory for licensing information. */ #include "common.h" #include #include "dystring.h" #include "cheapcgi.h" #include "cart.h" #include "hPrint.h" #include "hash.h" #include "jsHelper.h" #include "web.h" #include "hui.h" #include "hgConfig.h" #include "portable.h" static boolean jsInited = FALSE; /* mainForm/hiddenForm code supports the following: when the user selects * something from a pull-down, it will go hit the server to * figure out how to reload other control options based on the choice. * (For instance if they change the group, which items in the track * drop-down need to change). * * We accomplish this by maintaining two forms - a mainForm and a * hiddenForm. The hiddenForm maintains echo's of all the variables * in the main form, which get updated onChange of controls that need * to 'ripple' to other controls. The onChange also submits the * control. */ void jsInit() /* If this is the first call, set window.onload to the operations * performed upon loading a page and print supporting javascript. * Currently this just sets the page vertical position if specified on * CGI, and includes jsHelper.js. * Subsequent calls do nothing, so this can be called many times. */ { if (! jsInited) { // jsh_pageVertPos trick taken from // http://www.softcomplex.com/docs/get_window_size_and_scrollbar_position.html puts(""); int pos = cgiOptionalInt("jsh_pageVertPos", 0); if (pos > 0) printf("\n", pos); jsInited = TRUE; jsIncludeFile("jsHelper.js", NULL); } } struct dyString *jsOnChangeStart() /* Start up an onChange string */ { struct dyString *dy = dyStringNew(1024); dyStringAppend(dy, "onChange=\""); return dy; } char *jsOnChangeEnd(struct dyString **pDy) /* Finish up javascript onChange command. */ { dyStringAppend(*pDy, "document.hiddenForm.submit();\""); return dyStringCannibalize(pDy); } void jsDropDownCarryOver(struct dyString *dy, char *var) /* Add statement to carry-over drop-down item to dy. */ { dyStringPrintf(dy, "document.hiddenForm.%s.value=", var); dyStringPrintf(dy, "document.mainForm.%s.options", var); dyStringPrintf(dy, "[document.mainForm.%s.selectedIndex].value; ", var); } void jsTextCarryOver(struct dyString *dy, char *var) /* Add statement to carry-over text item to dy. */ { dyStringPrintf(dy, "document.hiddenForm.%s.value=document.mainForm.%s.value; ", var, var); } void jsTrackingVar(char *jsVar, char *val) /* Emit a little Javascript to keep track of a variable. * This helps especially with radio buttons. */ { hPrintf("\n"); } -void jsMakeTrackingRadioButton(char *cgiVar, char *jsVar, - char *val, char *selVal) -/* Make a radio button that also sets tracking variable +void jsMakeTrackingRadioButtonExtraHtml(char *cgiVar, char *jsVar, + char *val, char *selVal, char *extraHtml) +/* Make a radio button with extra HTML attributes that also sets tracking variable * in javascript. */ { hPrintf(""); } +void jsMakeTrackingRadioButton(char *cgiVar, char *jsVar, + char *val, char *selVal) +/* Make a radio button that also sets tracking variable + * in javascript. */ +{ +jsMakeTrackingRadioButtonExtraHtml(cgiVar, jsVar, val, selVal, NULL); +} + void jsMakeTrackingCheckBox(struct cart *cart, char *cgiVar, char *jsVar, boolean usualVal) /* Make a check box filling in with existing value and * putting a javascript tracking variable on it. */ { char buf[256]; boolean oldVal = cartUsualBoolean(cart, cgiVar, usualVal); hPrintf("\n", jsVar, oldVal); hPrintf(""); safef(buf, sizeof(buf), "%s%s", cgiBooleanShadowPrefix(), cgiVar); cgiMakeHiddenVar(buf, "0"); } void jsTrackedVarCarryOver(struct dyString *dy, char *cgiVar, char *jsVar) /* Carry over tracked variable (radio button?) to hidden form. */ { dyStringPrintf(dy, "document.hiddenForm.%s.value=%s; ", cgiVar, jsVar); } char *jsRadioUpdate(char *cgiVar, char *jsVar, char *val) /* Make a little javascript to check and uncheck radio buttons * according to new value. To use this you must have called * jsInit somewhere, and also must use jsMakeTrackingRadioButton * to make the buttons. */ { static char buf[256]; safef(buf, sizeof(buf), "setRadioCheck('%s', '%s'); %s='%s'", cgiVar, val, jsVar, val); return buf; } void jsCreateHiddenForm(struct cart *cart, char *scriptName, char **vars, int varCount) /* Create a hidden form with the given variables */ { int i; hPrintf( "
\n", scriptName); cartSaveSession(cart); for (i=0; i\n", vars[i]); puts(""); } char *jsSetVerticalPosition(char *form) /* Returns a javascript statement for storing the vertical position of the * page; typically this would go just before a document submit. * jsInit must be called first. * Do not free return value! */ { if (! jsInited) errAbort("jsSetVerticalPosition: jsInit must be called first."); static char vertPosSet[2048]; safef(vertPosSet, sizeof(vertPosSet), "document.%s.jsh_pageVertPos.value = f_scrollTop(); ", form); return vertPosSet; } void jsMakeCheckboxGroupSetClearButton(char *buttonVar, boolean isSet) /* Make a button for setting or clearing a set of checkboxes with the same name. * Uses only javascript to change the checkboxes, no resubmit. */ { char javascript[256]; safef(javascript, sizeof(javascript), "var list = document.getElementsByName('%s'); " "for (var ix = 0; ix < list.length; ix++) {list[ix].checked = %s}", buttonVar, isSet ? "true" : "false"); cgiMakeOnClickButton(javascript, isSet ? JS_SET_ALL_BUTTON_LABEL : JS_CLEAR_ALL_BUTTON_LABEL); } void jsMakeSetClearContainer() /* Begin a wrapper div with class setClearContainer, plus 'Set all' and 'Clear all' buttons. * This should be followed by a bunch of checkboxes, and then a call to jsEndContainer. */ { puts("
\n" "\n" "\n" "
" ); } void jsEndContainer() /* End a wrapper div. */ { puts("
"); } char *jsPressOnEnter(char *button) /* Returns a javascript statement that clicks button when the Enter key * has been pressed; typically this would go in a text input. * jsInit must be called first. * Do not free return value! */ { if (! jsInited) errAbort("jsPressOnEnter: jsInit must be called first."); static char poe[2048]; safef(poe, sizeof(poe), "return pressOnEnter(event, %s);", button); return poe; } void jsIncludeFile(char *fileName, char *noScriptMsg) { /* Prints out html to include given javascript file from the js directory; suppresses redundant * "); puts(""); puts(""); puts(""); puts(""); puts(""); puts(""); puts(""); puts(""); puts(""); puts(""); puts(""); puts(""); puts(""); puts(""); puts(""); puts(""); puts(""); } char *jsCheckAllOnClickHandler(char *idPrefix, boolean state) /* Returns javascript for use as an onclick attribute value to check all/uncheck all * all checkboxes with given idPrefix. * state parameter determines whether to "check all" or "uncheck all" (TRUE means "check all"). */ { static char buf[512]; jsIncludeFile("utils.js", NULL); safef(buf, sizeof(buf), "setCheckBoxesWithPrefix(this, '%s', %s); return false", idPrefix, state ? "true" : "false"); return buf; } /* cgiMakeCheckAllSubmitButton really belongs in cheapcgi.c, but that is compiled without access to jsHelper.h */ void cgiMakeCheckAllSubmitButton(char *name, char *value, char *id, char *idPrefix, boolean state) /* Make submit button which uses javascript to apply check all or uncheck all to all * checkboxes with given idPrefix. * state parameter determines whether to "check all" or "uncheck all" (TRUE means "check all"). * id parameter may be NULL */ { cgiMakeOnClickSubmitButton(jsCheckAllOnClickHandler(idPrefix, state), name, value); } char *stripRegEx(char *str, char *regEx, int flags) { /* Strip out text matching regEx from str. flags is passed through to regcomp as the cflags argument. Returned string should be free'ed after use. */ return replaceRegEx(str, NULL, regEx, flags); } char *replaceRegEx(char *str, char *replace, char *regEx, int flags) { /* Replace text matching regEx in str with replace string. flags is passed through to regcomp as the cflags argument. Returned string should be free'ed after use. */ regex_t re; regmatch_t match[1]; int err = regcomp(&re, regEx, flags); if(err) errAbort("regcomp failed; err: %d", err); struct dyString *dy = newDyString(0); size_t len = strlen(str); size_t offset = 0; while(offset < len && !regexec(&re, str + offset, 1, match, 0)) { dyStringAppendN(dy, str + offset, match[0].rm_so); if(replace != NULL) dyStringAppend(dy, replace); offset += match[0].rm_eo; } if(offset < len) { dyStringAppend(dy, str + offset); } regfree(&re); return dyStringCannibalize(&dy); } char *jsStripJavascript(char *str) /* Strip out anything that looks like javascript in html string. This function is designed to cleanup user input (e.g. to avoid XSS attacks). In reality, we cannot remove javascript with 100% accuracy, b/c there are many browser specific ways of embedding javascript; see http://ha.ckers.org/xss.html for many, many examples. Returned string should be free'ed after use. */ { char *regExs[] = {"[^<]*", "]*>" // handles case where they have an un-closed script tag with a src attribute }; int i; str = cloneString(str); for(i=0;i