Index: src/branch.c ================================================================== --- src/branch.c +++ src/branch.c @@ -683,11 +683,10 @@ style_set_current_feature("branch"); style_header("Branches"); style_submenu_element("List", "brlist"); login_anonymous_available(); timeline_ss_submenu(); - cookie_render(); @

The initial check-in for each branch:

blob_append(&sql, timeline_query_for_www(), -1); blob_append_sql(&sql, "AND blob.rid IN (SELECT rid FROM tagxref" " WHERE tagtype>0 AND tagid=%d AND srcid!=0)", TAG_BRANCH); Index: src/cgi.c ================================================================== --- src/cgi.c +++ src/cgi.c @@ -1159,16 +1159,28 @@ #endif z = (char*)P("HTTP_COOKIE"); if( z ){ z = fossil_strdup(z); add_param_list(z, ';'); + z = (char*)cookie_value("skin",0); + if(z){ + skin_use_alternative(z, 2); + } } z = (char*)P("QUERY_STRING"); if( z ){ z = fossil_strdup(z); add_param_list(z, '&'); + z = (char*)P("skin"); + if(z){ + char *zErr = skin_use_alternative(z, 2); + if(!zErr){ + cookie_write_parameter("skin","skin",z); + } + fossil_free(zErr); + } } z = (char*)P("REMOTE_ADDR"); if( z ){ g.zIpAddr = fossil_strdup(z); Index: src/cookies.c ================================================================== --- src/cookies.c +++ src/cookies.c @@ -173,15 +173,16 @@ const char *zDflt /* Default value for the parameter */ ){ cookie_readwrite(zQP, zPName, zDflt, COOKIE_READ|COOKIE_WRITE); } -/* Update the user preferences cookie, if necessary, and shut down this -** module +/* Update the user preferences cookie, if necessary, and shut down +** this module. The cookie is only emitted if its value has actually +** changed since the request started. */ void cookie_render(void){ - if( cookies.bChanged && P("udc")!=0 ){ + if( cookies.bChanged ){ Blob new; int i; blob_init(&new, 0, 0); for(i=0;i0 ) blob_append(&new, ",", 1); Index: src/descendants.c ================================================================== --- src/descendants.c +++ src/descendants.c @@ -565,11 +565,10 @@ url_reset(&url); style_set_current_feature("leaves"); style_header("Leaves"); login_anonymous_available(); timeline_ss_submenu(); - cookie_render(); #if 0 style_sidebox_begin("Nomenclature:", "33%"); @
    @
  1. A
    leaf
    @ is a check-in with no descendants in the same branch.
  2. Index: src/finfo.c ================================================================== --- src/finfo.c +++ src/finfo.c @@ -364,11 +364,10 @@ url_initialize(&url, "finfo"); if( brBg ) url_add_parameter(&url, "brbg", 0); if( uBg ) url_add_parameter(&url, "ubg", 0); ridFrom = name_to_rid_www("from"); zPrevDate[0] = 0; - cookie_render(); if( fnid==0 ){ @ No such file: %h(zFilename) style_finish_page(); return; } Index: src/info.c ================================================================== --- src/info.c +++ src/info.c @@ -939,11 +939,10 @@ append_file_change_line(zUuid, zName, zOld, zNew, zOldName, diffFlags,pRe,mperm); } db_finalize(&q3); append_diff_javascript(diffType==2); - cookie_render(); style_finish_page(); } /* ** WEBPAGE: winfo @@ -1185,11 +1184,10 @@ login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } login_anonymous_available(); load_control(); diffType = preferred_diff_type(); - cookie_render(); zRe = P("regex"); if( zRe ) re_compile(&pRe, zRe, 0); zBranch = P("branch"); if( zBranch && zBranch[0]==0 ) zBranch = 0; if( zBranch ){ @@ -1706,11 +1704,10 @@ int verbose = PB("verbose"); login_check_credentials(); if( !g.perm.Read ){ login_needed(g.anon.Read); return; } diffType = preferred_diff_type(); - cookie_render(); if( P("from") && P("to") ){ v1 = artifact_from_ci_and_filename("from"); v2 = artifact_from_ci_and_filename("to"); }else{ Stmt q; Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -1838,33 +1838,11 @@ zPathInfo += 7; g.nExtraURL += 7; cgi_replace_parameter("PATH_INFO", zPathInfo); cgi_replace_parameter("SCRIPT_NAME", zNewScript); etag_cancel(); - }else if( zPathInfo && strncmp(zPathInfo, "/skn_", 5)==0 ){ - int i; - char *zAlt; - char *zErr; - char *z; - while( (z = strstr(zPathInfo+1,"/skn_"))!=0 ) zPathInfo = z; - for(i=5; zPathInfo[i] && zPathInfo[i]!='/'; i++){} - zAlt = mprintf("%.*s", i-5, zPathInfo+5); - zErr = skin_use_alternative(zAlt); - if( zErr ){ - fossil_free(zErr); - }else{ - char *zNewScript; - zNewScript = mprintf("%T/skn_%s", P("SCRIPT_NAME"), zAlt); - if( g.zTop ) g.zTop = mprintf("%R/skn_%s", zAlt); - if( g.zBaseURL ) g.zBaseURL = mprintf("%s/skn_%s", g.zBaseURL, zAlt); - zPathInfo += i; - g.nExtraURL += i; - cgi_replace_parameter("PATH_INFO", zPathInfo); - cgi_replace_parameter("SCRIPT_NAME", zNewScript); - } - fossil_free(zAlt); - } + } /* If the content type is application/x-fossil or ** application/x-fossil-debug, then a sync/push/pull/clone is ** desired, so default the PATH_INFO to /xfer */ @@ -2378,11 +2356,11 @@ ** Use one of the built-in skins defined by LABEL. LABEL is the ** name of the subdirectory under the skins/ directory that holds ** the elements of the built-in skin. If LABEL does not match, ** this directive is a silent no-op. */ - skin_use_alternative(blob_str(&value)); + fossil_free(skin_use_alternative(blob_str(&value), 1)); blob_reset(&value); continue; } if( blob_eq(&key, "jsmode:") && blob_token(&line, &value) ){ /* jsmode: MODE Index: src/skins.c ================================================================== --- src/skins.c +++ src/skins.c @@ -104,21 +104,61 @@ ** ** If the alternative skin name contains one or more '/' characters, then ** it is assumed to be a directory on disk that holds override css.txt, ** footer.txt, and header.txt. This mode can be used for interactive ** development of new skins. +** +** The 2nd parameter is a ranking of how important this alternative +** skin declaration is, and lower values trump higher ones. If a call +** to this function passes a higher-valued rank than a previous call, +** the subsequent call becomes a no-op. Only calls with the same or +** lower rank (i.e. higher priority) will overwrite a previous +** setting. This approach is used because the CGI/server-time +** initialization happens in an order which is incompatible with our +** preferred ranking, making it otherwise more invasive to tell the +** internals "the --skin flag ranks higher than a URL parameter" (the +** former gets initialized before both URL parameters and the /draft +** path determination). +** +** The rankings were initially defined in +** https://fossil-scm.org/forum/forumpost/caf8c9a8bb +** and are: +** +** 0) A skin name matching the glob draft[1-9] trumps everything else. +** +** 1) The --skin flag or skin: CGI config setting. +** +** 2) The "skin" display setting cookie or URL argument, in that +** order. If the "skin" URL argument is provided and refers to a legal +** skin then that will update the display cookie. If the skin name is +** illegal it is silently ignored. +** +** 3) Skin properties from the CONFIG db table +** +** 4) Default skin. +** +** As a special case, the name "_repo" resets zAltSkinDir and +** pAltSkin to 0 to indicate that the current config-side skin should +** be used (rank 3, above), then returns 0. */ -char *skin_use_alternative(const char *zName){ +char *skin_use_alternative(const char *zName, int rank){ + static int currentRank = 5; int i; Blob err = BLOB_INITIALIZER; - if( strchr(zName, '/')!=0 ){ + if(rank > currentRank) return 0; + if( 1==rank && strchr(zName, '/')!=0 ){ zAltSkinDir = fossil_strdup(zName); return 0; } if( sqlite3_strglob("draft[1-9]", zName)==0 ){ skin_use_draft(zName[5] - '0'); return 0; + } + if(zName && 0==strcmp("_repo",zName)){ + pAltSkin = 0; + zAltSkinDir = 0; + return 0; } for(i=0; iWarning: this fossil instance was started with + @ a hard-coded skin value which trumps any option selected below. + @ A skins selected below will be recorded in your prefere cookie + @ but will not be used until/unless the site administrator + @ configures the site to run without a forced hard-coded skin. + @

    + } @

    The following skins are available for this repository:

    @
      if( pAltSkin==0 && zAltSkinDir==0 && iDraftSkin==0 ){ @
    • Standard skin for this repository ← Currently in use }else{ - @
    • %z(href("%s/skins",zBase))Standard skin for this repository + @
    • %z(href("%R/skins?skin=_repo"))Standard skin for this repository } for(i=0; i %h(aBuiltinSkin[i].zDesc) ← Currently in use }else{ - char *zUrl = href("%s/skn_%s/skins", zBase, aBuiltinSkin[i].zLabel); + char *zUrl = href("%R/skins?skin=%T", aBuiltinSkin[i].zLabel); @
    • %z(zUrl)%h(aBuiltinSkin[i].zDesc) } } @
    style_finish_page(); fossil_free(zBase); } Index: src/style.c ================================================================== --- src/style.c +++ src/style.c @@ -1011,10 +1011,14 @@ if( sqlite3_strlike("%%", zFooter, 0)!=0 ){ style_load_all_js_files(); @ @ } + /* Update the user display prefs cookie if it was modified during + ** this request. + */ + cookie_render(); } /* ** Begin a side-box on the right-hand side of a page. The title and ** the width of the box are given as arguments. The width is usually Index: src/tag.c ================================================================== --- src/tag.c +++ src/tag.c @@ -755,11 +755,10 @@ style_header("Tagged Check-ins"); style_submenu_element("List", "taglist"); login_anonymous_available(); timeline_ss_submenu(); - cookie_render(); @

    Check-ins with non-propagating tags:

    blob_append(&sql, timeline_query_for_www(), -1); blob_append_sql(&sql, "AND blob.rid IN (SELECT rid FROM tagxref" " WHERE tagtype=1 AND srcid>0" Index: src/timeline.c ================================================================== --- src/timeline.c +++ src/timeline.c @@ -1754,11 +1754,10 @@ cgi_set_parameter("y", zType); } if( zType[0]=='a' || zType[0]=='c' ){ cookie_write_parameter("y","y",zType); } - cookie_render(); /* Convert the cf=FILEHASH query parameter into a c=CHECKINHASH value */ if( P("cf")!=0 ){ zCirca = db_text(0, "SELECT (SELECT uuid FROM blob WHERE rid=mlink.mid)" Index: www/changes.wiki ================================================================== --- www/changes.wiki +++ www/changes.wiki @@ -21,24 +21,25 @@ check-in. * The "pikchr-background" settings is now available in "detail.txt" skin files, for better control of Pikchr colors in inverted color schemes. * The hamburger menu is now available on the built-in - "[/skn_ardoise/doc/trunk/www/changes.wiki#v2_15|ardoise]" and - "[/skn_plain_gray/doc/trunk/www/changes.wiki#v2_15|plain_gray]" + "[/doc/trunk/www/changes.wiki?skin=ardoise#v2_15|ardoise]" and + "[/doc/trunk/www/changes.wiki?skin=plain_gray#v2_15|plain_gray]" skins. - * Add the --list option to the + * Add the --list option to the [/help?cmd=tarball|tarball], [/help?cmd=zip|zip], and [/help?cmd=sqlar|sqlar] commands. * The javascript used to implement the hamburger menu on the default built-in skin has been made generic so that it is usable by a variety of skins, and promoted to an ordinary built-in javascript file. * Any built-in skin named "X" can be used instead of the standard - repository skin by including the term "skn_X" at the beginning - of the URL path. + repository skin by adding the URL parameter skin=X to the + request, and that selection is persistent using a cookie. The + [/skins] page may be used to select a skin. * New TH1 commands: "builtin_request_js", "capexpr", "foreach", "string match"

    Changes for Version 2.14 (2021-01-20)