Fossil SCM
Extended style.css to optionally be able to load page-specific CSS if the 'page' URL param is provided and a builtin file named style.{{page}}.css is found. The stylesheet_url TH1 var now adds URL param page=g.zPath if builtin file style.{{g.zPath}}.css is available.
Commit
d661c12cbadef066fcd747c7526cd64d5f27fa340021f3a3fa4d98e0af3970e7
Parent
b5944d4ede11319…
1 file changed
+58
-6
+58
-6
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -370,15 +370,31 @@ | ||
| 370 | 370 | const char *zVarPrefix, |
| 371 | 371 | const char *zConfigName, |
| 372 | 372 | const char *zPageName |
| 373 | 373 | ){ |
| 374 | 374 | char *zVarName = mprintf("%s_url", zVarPrefix); |
| 375 | - char *zUrl = mprintf("%R/%s?id=%x", zPageName, | |
| 376 | - skin_id(zConfigName)); | |
| 375 | + char *zExtra = 0; | |
| 376 | + char *zUrl = 0; | |
| 377 | + if(0==strcmp("css",zConfigName)){ | |
| 378 | + /* Account for page-specific CSS, appending a page=NAME url flag | |
| 379 | + ** only if we have a corresponding built-in page-specific CSS | |
| 380 | + ** file. Do not append it to all pages because we would | |
| 381 | + ** effectively cache-bust all pages which do not have | |
| 382 | + ** page-specific CSS. */ | |
| 383 | + char * zBuiltin = mprintf("style.%s.css", g.zPath); | |
| 384 | + if(builtin_file(zBuiltin,0)!=0){ | |
| 385 | + zExtra = mprintf("&page=%s", g.zPath); | |
| 386 | + } | |
| 387 | + fossil_free(zBuiltin); | |
| 388 | + } | |
| 389 | + zUrl = mprintf("%R/%s?id=%x%s", zPageName, | |
| 390 | + skin_id(zConfigName), | |
| 391 | + zExtra ? zExtra : ""); | |
| 377 | 392 | Th_Store(zVarName, zUrl); |
| 378 | - free(zUrl); | |
| 379 | - free(zVarName); | |
| 393 | + fossil_free(zExtra); | |
| 394 | + fossil_free(zUrl); | |
| 395 | + fossil_free(zVarName); | |
| 380 | 396 | } |
| 381 | 397 | |
| 382 | 398 | /* |
| 383 | 399 | ** Create a TH1 variable containing the URL for the specified config image. |
| 384 | 400 | ** The resulting variable name will be of the form $[zImageName]_image_url. |
| @@ -1063,16 +1079,52 @@ | ||
| 1063 | 1079 | ** WEBPAGE: style.css |
| 1064 | 1080 | ** |
| 1065 | 1081 | ** Return the style sheet. |
| 1066 | 1082 | */ |
| 1067 | 1083 | void page_style_css(void){ |
| 1068 | - Blob css; | |
| 1084 | + Blob css = empty_blob; | |
| 1069 | 1085 | int i; |
| 1070 | 1086 | int isInit = 0; |
| 1087 | + const char * zPage = P("page"); | |
| 1071 | 1088 | |
| 1072 | 1089 | cgi_set_content_type("text/css"); |
| 1073 | - blob_init(&css,skin_get("css"),-1); | |
| 1090 | + if(zPage!=0 && zPage[0]!=0 | |
| 1091 | + && strlen(zPage)<30/*marginal safety measure vs malicious input*/){ | |
| 1092 | + /* Check for page-specific CSS. The placement of this CSS is kinda | |
| 1093 | + ** tricky. It "very probably needs" to come before any | |
| 1094 | + ** skin-supplied CSS, but if it does then a system-level skin | |
| 1095 | + ** which does silly things like set *all* textareas to the same | |
| 1096 | + ** 32px tall (Ardoise) can effectively ruin page-specific | |
| 1097 | + ** layout. If the page-specific CSS is emitted after the skin, | |
| 1098 | + ** then the page-specific CSS will potentially override any user | |
| 1099 | + ** edits made to the skin, leaving the user with no way to | |
| 1100 | + ** override them except to import a separate CSS file from their | |
| 1101 | + ** custom skin, after this one. Thus the page CSS needs to come | |
| 1102 | + ** first, but it also needs "unusually specific" | |
| 1103 | + ** (i.e. strongly-binding) CSS classes for any style which "needs" | |
| 1104 | + ** to override the *default* skin CSS, but which is nonetheless | |
| 1105 | + ** overridable by client-side edits by using CSS selectors of | |
| 1106 | + ** equal or higher specificity. | |
| 1107 | + ** | |
| 1108 | + ** The alternative to this approach is that we pack all | |
| 1109 | + ** page-specific CSS into default_css.txt, which can explode it | |
| 1110 | + ** tremendously. e.g. /fileedit itself includes 330-ish lines of | |
| 1111 | + ** CSS. | |
| 1112 | + */ | |
| 1113 | + int nLen = 0; | |
| 1114 | + char * zPageCss = mprintf("style.%s.css",zPage); | |
| 1115 | + const char * zBuiltin = (const char *)builtin_file(zPageCss, &nLen); | |
| 1116 | + fossil_free(zPageCss); | |
| 1117 | + if(nLen>0){ | |
| 1118 | + blob_append(&css, zBuiltin, nLen); | |
| 1119 | + } | |
| 1120 | + } | |
| 1121 | + if(blob_size(&css)>0){ | |
| 1122 | + blob_append(&css,skin_get("css"),-1); | |
| 1123 | + }else{ | |
| 1124 | + blob_init(&css,skin_get("css"),-1); | |
| 1125 | + } | |
| 1074 | 1126 | |
| 1075 | 1127 | /* add special missing definitions */ |
| 1076 | 1128 | for(i=1; cssDefaultList[i].elementClass; i++){ |
| 1077 | 1129 | char *z = blob_str(&css); |
| 1078 | 1130 | if( !containsSelector(z, cssDefaultList[i].elementClass) ){ |
| 1079 | 1131 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -370,15 +370,31 @@ | |
| 370 | const char *zVarPrefix, |
| 371 | const char *zConfigName, |
| 372 | const char *zPageName |
| 373 | ){ |
| 374 | char *zVarName = mprintf("%s_url", zVarPrefix); |
| 375 | char *zUrl = mprintf("%R/%s?id=%x", zPageName, |
| 376 | skin_id(zConfigName)); |
| 377 | Th_Store(zVarName, zUrl); |
| 378 | free(zUrl); |
| 379 | free(zVarName); |
| 380 | } |
| 381 | |
| 382 | /* |
| 383 | ** Create a TH1 variable containing the URL for the specified config image. |
| 384 | ** The resulting variable name will be of the form $[zImageName]_image_url. |
| @@ -1063,16 +1079,52 @@ | |
| 1063 | ** WEBPAGE: style.css |
| 1064 | ** |
| 1065 | ** Return the style sheet. |
| 1066 | */ |
| 1067 | void page_style_css(void){ |
| 1068 | Blob css; |
| 1069 | int i; |
| 1070 | int isInit = 0; |
| 1071 | |
| 1072 | cgi_set_content_type("text/css"); |
| 1073 | blob_init(&css,skin_get("css"),-1); |
| 1074 | |
| 1075 | /* add special missing definitions */ |
| 1076 | for(i=1; cssDefaultList[i].elementClass; i++){ |
| 1077 | char *z = blob_str(&css); |
| 1078 | if( !containsSelector(z, cssDefaultList[i].elementClass) ){ |
| 1079 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -370,15 +370,31 @@ | |
| 370 | const char *zVarPrefix, |
| 371 | const char *zConfigName, |
| 372 | const char *zPageName |
| 373 | ){ |
| 374 | char *zVarName = mprintf("%s_url", zVarPrefix); |
| 375 | char *zExtra = 0; |
| 376 | char *zUrl = 0; |
| 377 | if(0==strcmp("css",zConfigName)){ |
| 378 | /* Account for page-specific CSS, appending a page=NAME url flag |
| 379 | ** only if we have a corresponding built-in page-specific CSS |
| 380 | ** file. Do not append it to all pages because we would |
| 381 | ** effectively cache-bust all pages which do not have |
| 382 | ** page-specific CSS. */ |
| 383 | char * zBuiltin = mprintf("style.%s.css", g.zPath); |
| 384 | if(builtin_file(zBuiltin,0)!=0){ |
| 385 | zExtra = mprintf("&page=%s", g.zPath); |
| 386 | } |
| 387 | fossil_free(zBuiltin); |
| 388 | } |
| 389 | zUrl = mprintf("%R/%s?id=%x%s", zPageName, |
| 390 | skin_id(zConfigName), |
| 391 | zExtra ? zExtra : ""); |
| 392 | Th_Store(zVarName, zUrl); |
| 393 | fossil_free(zExtra); |
| 394 | fossil_free(zUrl); |
| 395 | fossil_free(zVarName); |
| 396 | } |
| 397 | |
| 398 | /* |
| 399 | ** Create a TH1 variable containing the URL for the specified config image. |
| 400 | ** The resulting variable name will be of the form $[zImageName]_image_url. |
| @@ -1063,16 +1079,52 @@ | |
| 1079 | ** WEBPAGE: style.css |
| 1080 | ** |
| 1081 | ** Return the style sheet. |
| 1082 | */ |
| 1083 | void page_style_css(void){ |
| 1084 | Blob css = empty_blob; |
| 1085 | int i; |
| 1086 | int isInit = 0; |
| 1087 | const char * zPage = P("page"); |
| 1088 | |
| 1089 | cgi_set_content_type("text/css"); |
| 1090 | if(zPage!=0 && zPage[0]!=0 |
| 1091 | && strlen(zPage)<30/*marginal safety measure vs malicious input*/){ |
| 1092 | /* Check for page-specific CSS. The placement of this CSS is kinda |
| 1093 | ** tricky. It "very probably needs" to come before any |
| 1094 | ** skin-supplied CSS, but if it does then a system-level skin |
| 1095 | ** which does silly things like set *all* textareas to the same |
| 1096 | ** 32px tall (Ardoise) can effectively ruin page-specific |
| 1097 | ** layout. If the page-specific CSS is emitted after the skin, |
| 1098 | ** then the page-specific CSS will potentially override any user |
| 1099 | ** edits made to the skin, leaving the user with no way to |
| 1100 | ** override them except to import a separate CSS file from their |
| 1101 | ** custom skin, after this one. Thus the page CSS needs to come |
| 1102 | ** first, but it also needs "unusually specific" |
| 1103 | ** (i.e. strongly-binding) CSS classes for any style which "needs" |
| 1104 | ** to override the *default* skin CSS, but which is nonetheless |
| 1105 | ** overridable by client-side edits by using CSS selectors of |
| 1106 | ** equal or higher specificity. |
| 1107 | ** |
| 1108 | ** The alternative to this approach is that we pack all |
| 1109 | ** page-specific CSS into default_css.txt, which can explode it |
| 1110 | ** tremendously. e.g. /fileedit itself includes 330-ish lines of |
| 1111 | ** CSS. |
| 1112 | */ |
| 1113 | int nLen = 0; |
| 1114 | char * zPageCss = mprintf("style.%s.css",zPage); |
| 1115 | const char * zBuiltin = (const char *)builtin_file(zPageCss, &nLen); |
| 1116 | fossil_free(zPageCss); |
| 1117 | if(nLen>0){ |
| 1118 | blob_append(&css, zBuiltin, nLen); |
| 1119 | } |
| 1120 | } |
| 1121 | if(blob_size(&css)>0){ |
| 1122 | blob_append(&css,skin_get("css"),-1); |
| 1123 | }else{ |
| 1124 | blob_init(&css,skin_get("css"),-1); |
| 1125 | } |
| 1126 | |
| 1127 | /* add special missing definitions */ |
| 1128 | for(i=1; cssDefaultList[i].elementClass; i++){ |
| 1129 | char *z = blob_str(&css); |
| 1130 | if( !containsSelector(z, cssDefaultList[i].elementClass) ){ |
| 1131 |