Fossil SCM

The URL parser now understands the /draftN/ prefix. Draft skins can now be initialized from built-ins. Another incremental check-in.

drh 2017-12-02 16:22 UTC skin-setup-refactor
Commit 0cba37ec1aae0f52c49b354589df6f0b1d41fd574fa1e8829265ede1a943d7c5
3 files changed +34 +27 -2 +97 -8
+34
--- src/db.c
+++ src/db.c
@@ -2472,10 +2472,44 @@
24722472
return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName);
24732473
}
24742474
void db_lset_int(const char *zName, int value){
24752475
db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
24762476
}
2477
+
2478
+/* Va-args versions of db_get(), db_set(), and db_unset()
2479
+*/
2480
+char *db_get_mprintf(const char *zFormat, const char *zDefault, ...){
2481
+ va_list ap;
2482
+ char *zName;
2483
+ char *zResult;
2484
+ va_start(ap, zDefault);
2485
+ zName = vmprintf(zFormat, ap);
2486
+ va_end(ap);
2487
+ zResult = db_get(zName, zDefault);
2488
+ fossil_free(zName);
2489
+ return zResult;
2490
+}
2491
+void db_set_mprintf(const char *zFormat, const char *zNew, int iGlobal, ...){
2492
+ va_list ap;
2493
+ char *zName;
2494
+ va_start(ap, iGlobal);
2495
+ zName = vmprintf(zFormat, ap);
2496
+ va_end(ap);
2497
+ db_set(zName, zNew, iGlobal);
2498
+ fossil_free(zName);
2499
+}
2500
+void db_unset_mprintf(const char *zFormat, int iGlobal, ...){
2501
+ va_list ap;
2502
+ char *zName;
2503
+ va_start(ap, iGlobal);
2504
+ zName = vmprintf(zFormat, ap);
2505
+ va_end(ap);
2506
+ db_unset(zName, iGlobal);
2507
+ fossil_free(zName);
2508
+}
2509
+
2510
+
24772511
24782512
#if INTERFACE
24792513
/* Manifest generation flags */
24802514
#define MFESTFLG_RAW 0x01
24812515
#define MFESTFLG_UUID 0x02
24822516
--- src/db.c
+++ src/db.c
@@ -2472,10 +2472,44 @@
2472 return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName);
2473 }
2474 void db_lset_int(const char *zName, int value){
2475 db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
2476 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2477
2478 #if INTERFACE
2479 /* Manifest generation flags */
2480 #define MFESTFLG_RAW 0x01
2481 #define MFESTFLG_UUID 0x02
2482
--- src/db.c
+++ src/db.c
@@ -2472,10 +2472,44 @@
2472 return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName);
2473 }
2474 void db_lset_int(const char *zName, int value){
2475 db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
2476 }
2477
2478 /* Va-args versions of db_get(), db_set(), and db_unset()
2479 */
2480 char *db_get_mprintf(const char *zFormat, const char *zDefault, ...){
2481 va_list ap;
2482 char *zName;
2483 char *zResult;
2484 va_start(ap, zDefault);
2485 zName = vmprintf(zFormat, ap);
2486 va_end(ap);
2487 zResult = db_get(zName, zDefault);
2488 fossil_free(zName);
2489 return zResult;
2490 }
2491 void db_set_mprintf(const char *zFormat, const char *zNew, int iGlobal, ...){
2492 va_list ap;
2493 char *zName;
2494 va_start(ap, iGlobal);
2495 zName = vmprintf(zFormat, ap);
2496 va_end(ap);
2497 db_set(zName, zNew, iGlobal);
2498 fossil_free(zName);
2499 }
2500 void db_unset_mprintf(const char *zFormat, int iGlobal, ...){
2501 va_list ap;
2502 char *zName;
2503 va_start(ap, iGlobal);
2504 zName = vmprintf(zFormat, ap);
2505 va_end(ap);
2506 db_unset(zName, iGlobal);
2507 fossil_free(zName);
2508 }
2509
2510
2511
2512 #if INTERFACE
2513 /* Manifest generation flags */
2514 #define MFESTFLG_RAW 0x01
2515 #define MFESTFLG_UUID 0x02
2516
+27 -2
--- src/main.c
+++ src/main.c
@@ -1542,20 +1542,45 @@
15421542
}
15431543
}
15441544
}
15451545
15461546
/* At this point, the appropriate repository database file will have
1547
- ** been opened. Use the first element of PATH_INFO as the page name
1548
- ** and deliver the appropriate page back to the user.
1547
+ ** been opened.
1548
+ **
1549
+ ** Check to see if the the PATH_INFO begins with "draft[1-9]" and if
1550
+ ** so activate the special handling for draft skins
1551
+ */
1552
+ if( zPathInfo && strncmp(zPathInfo,"/draft",6)==0
1553
+ && zPathInfo[6]>='1' && zPathInfo[6]<='9'
1554
+ && (zPathInfo[7]=='/' || zPathInfo[7]==0)
1555
+ ){
1556
+ int iSkin = zPathInfo[6] - '0';
1557
+ char *zNewScript;
1558
+ skin_use_draft(iSkin);
1559
+ zNewScript = mprintf("%s/draft%d", P("SCRIPT_NAME"), iSkin);
1560
+ if( g.zTop ) g.zTop = mprintf("%s/draft%d", g.zTop, iSkin);
1561
+ if( g.zBaseURL ) g.zBaseURL = mprintf("%s/draft%d", g.zBaseURL, iSkin);
1562
+ zPathInfo += 7;
1563
+ cgi_replace_parameter("PATH_INFO", zPathInfo);
1564
+ cgi_replace_parameter("SCRIPT_NAME", zNewScript);
1565
+ }
1566
+
1567
+ /* If the content type is application/x-fossil or
1568
+ ** application/x-fossil-debug, then a sync/push/pull/clone is
1569
+ ** desired, so default the PATH_INFO to /xfer
15491570
*/
15501571
if( g.zContentType &&
15511572
strncmp(g.zContentType, "application/x-fossil", 20)==0 ){
15521573
/* Special case: If the content mimetype shows that it is "fossil sync"
15531574
** payload, then pretend that the PATH_INFO is /xfer so that we always
15541575
** invoke the sync page. */
15551576
zPathInfo = "/xfer";
15561577
}
1578
+
1579
+ /* Use the first element of PATH_INFO as the page name
1580
+ ** and deliver the appropriate page back to the user.
1581
+ */
15571582
set_base_url(0);
15581583
if( zPathInfo==0 || zPathInfo[0]==0
15591584
|| (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
15601585
/* Second special case: If the PATH_INFO is blank, issue a redirect to
15611586
** the home page identified by the "index-page" setting in the repository
15621587
--- src/main.c
+++ src/main.c
@@ -1542,20 +1542,45 @@
1542 }
1543 }
1544 }
1545
1546 /* At this point, the appropriate repository database file will have
1547 ** been opened. Use the first element of PATH_INFO as the page name
1548 ** and deliver the appropriate page back to the user.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1549 */
1550 if( g.zContentType &&
1551 strncmp(g.zContentType, "application/x-fossil", 20)==0 ){
1552 /* Special case: If the content mimetype shows that it is "fossil sync"
1553 ** payload, then pretend that the PATH_INFO is /xfer so that we always
1554 ** invoke the sync page. */
1555 zPathInfo = "/xfer";
1556 }
 
 
 
 
1557 set_base_url(0);
1558 if( zPathInfo==0 || zPathInfo[0]==0
1559 || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
1560 /* Second special case: If the PATH_INFO is blank, issue a redirect to
1561 ** the home page identified by the "index-page" setting in the repository
1562
--- src/main.c
+++ src/main.c
@@ -1542,20 +1542,45 @@
1542 }
1543 }
1544 }
1545
1546 /* At this point, the appropriate repository database file will have
1547 ** been opened.
1548 **
1549 ** Check to see if the the PATH_INFO begins with "draft[1-9]" and if
1550 ** so activate the special handling for draft skins
1551 */
1552 if( zPathInfo && strncmp(zPathInfo,"/draft",6)==0
1553 && zPathInfo[6]>='1' && zPathInfo[6]<='9'
1554 && (zPathInfo[7]=='/' || zPathInfo[7]==0)
1555 ){
1556 int iSkin = zPathInfo[6] - '0';
1557 char *zNewScript;
1558 skin_use_draft(iSkin);
1559 zNewScript = mprintf("%s/draft%d", P("SCRIPT_NAME"), iSkin);
1560 if( g.zTop ) g.zTop = mprintf("%s/draft%d", g.zTop, iSkin);
1561 if( g.zBaseURL ) g.zBaseURL = mprintf("%s/draft%d", g.zBaseURL, iSkin);
1562 zPathInfo += 7;
1563 cgi_replace_parameter("PATH_INFO", zPathInfo);
1564 cgi_replace_parameter("SCRIPT_NAME", zNewScript);
1565 }
1566
1567 /* If the content type is application/x-fossil or
1568 ** application/x-fossil-debug, then a sync/push/pull/clone is
1569 ** desired, so default the PATH_INFO to /xfer
1570 */
1571 if( g.zContentType &&
1572 strncmp(g.zContentType, "application/x-fossil", 20)==0 ){
1573 /* Special case: If the content mimetype shows that it is "fossil sync"
1574 ** payload, then pretend that the PATH_INFO is /xfer so that we always
1575 ** invoke the sync page. */
1576 zPathInfo = "/xfer";
1577 }
1578
1579 /* Use the first element of PATH_INFO as the page name
1580 ** and deliver the appropriate page back to the user.
1581 */
1582 set_base_url(0);
1583 if( zPathInfo==0 || zPathInfo[0]==0
1584 || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
1585 /* Second special case: If the PATH_INFO is blank, issue a redirect to
1586 ** the home page identified by the "index-page" setting in the repository
1587
+97 -8
--- src/skins.c
+++ src/skins.c
@@ -64,10 +64,11 @@
6464
** The following static variable holds the name of the alternative skin,
6565
** or NULL if the skin should be as configured.
6666
*/
6767
static struct BuiltinSkin *pAltSkin = 0;
6868
static char *zAltSkinDir = 0;
69
+static int iDraftSkin = 0;
6970
7071
/*
7172
** Skin details are a set of key/value pairs that define display
7273
** attributes of the skin that cannot be easily specified using CSS
7374
** or that need to be known on the server-side.
@@ -100,10 +101,14 @@
100101
int i;
101102
Blob err = BLOB_INITIALIZER;
102103
if( strchr(zName, '/')!=0 ){
103104
zAltSkinDir = fossil_strdup(zName);
104105
return 0;
106
+ }
107
+ if( sqlite3_strglob("draft[1-9]", zName)==0 ){
108
+ skin_use_draft(zName[5] - '0');
109
+ return 0;
105110
}
106111
for(i=0; i<count(aBuiltinSkin); i++){
107112
if( fossil_strcmp(aBuiltinSkin[i].zLabel, zName)==0 ){
108113
pAltSkin = &aBuiltinSkin[i];
109114
return 0;
@@ -126,18 +131,33 @@
126131
if( zSkin ){
127132
char *zErr = skin_use_alternative(zSkin);
128133
if( zErr ) fossil_fatal("%s", zErr);
129134
}
130135
}
136
+
137
+/*
138
+** Use one of the draft skins.
139
+*/
140
+void skin_use_draft(int i){
141
+ iDraftSkin = i;
142
+}
131143
132144
/*
133145
** The following routines return the various components of the skin
134146
** that should be used for the current run.
147
+**
148
+** zWhat is one of: "css", "header", "footer", "details".
135149
*/
136150
const char *skin_get(const char *zWhat){
137151
const char *zOut;
138152
char *z;
153
+ if( iDraftSkin ){
154
+ z = mprintf("draft%d-%s", iDraftSkin, zWhat);
155
+ zOut = db_get(z, 0);
156
+ fossil_free(z);
157
+ if( zOut ) return zOut;
158
+ }
139159
if( zAltSkinDir ){
140160
char *z = mprintf("%s/%s.txt", zAltSkinDir, zWhat);
141161
if( file_isfile(z) ){
142162
Blob x;
143163
blob_read_from_file(&x, z);
@@ -668,39 +688,83 @@
668688
}
669689
@ </div></form>
670690
style_footer();
671691
db_end_transaction(0);
672692
}
693
+
694
+/*
695
+** Try to initialize draft skin iSkin to the built-in or preexisting
696
+** skin named by zTemplate.
697
+*/
698
+static void skin_initialize_draft(int iSkin, const char *zTemplate){
699
+ int i;
700
+ const char *azWhat[] = { "css", "header", "footer", "detail" };
701
+ if( zTemplate==0 ) return;
702
+ if( strcmp(zTemplate, "current")==0 ){
703
+ for(i=0; i<count(azWhat); i++){
704
+ db_unset_mprintf("draft%d-%s", 0, iSkin, azWhat[i]);
705
+ }
706
+ }else{
707
+ for(i=0; i<count(aBuiltinSkin); i++){
708
+ if( strcmp(zTemplate, aBuiltinSkin[i].zLabel)==0 ){
709
+ for(i=0; i<count(azWhat); i++){
710
+ char *zKey = mprintf("skins/%s/%s.txt", zTemplate, azWhat[i]);
711
+ db_set_mprintf("draft%d-%s", builtin_text(zKey), 0, iSkin, azWhat[i]);
712
+ }
713
+ break;
714
+ }
715
+ }
716
+ }
717
+}
673718
674719
/*
675720
** WEBPAGE: setup_skin
676721
**
677722
** Generate a page showing the steps needed to customize a skin.
678723
*/
679724
void setup_skin(void){
680725
int i; /* Loop counter */
681726
int iSkin; /* Which draft skin is being edited */
727
+ int isAdmin; /* True for an administrator */
728
+ int isEditor; /* Others authorized to make edits */
682729
static const char *azTestPages[] = {
683730
"home",
684731
"timeline",
685732
"dir?ci=tip",
686733
"dir?ci=tip&type=tree",
687734
"brlist",
688735
"info/trunk",
689736
};
690737
691
-
738
+ /* Figure out which skin we are editing */
692739
iSkin = atoi(PD("sk","1"));
693740
if( iSkin<1 || iSkin>9 ) iSkin = 1;
741
+
742
+ /* Figure out if the current user is allowed to make administrative
743
+ ** changes and/or edits
744
+ */
694745
login_check_credentials();
695
- style_header("Customize Skin");
746
+ if( g.perm.Admin ){
747
+ isAdmin = isEditor = 1;
748
+ }else{
749
+ char *zAllowedEditors;
750
+ Glob *pAllowedEditors;
751
+ isAdmin = isEditor = 0;
752
+ zAllowedEditors = db_get_mprintf("draft%d-users", 0, iSkin);
753
+ if( zAllowedEditors ){
754
+ pAllowedEditors = glob_create(zAllowedEditors);
755
+ isEditor = glob_match(pAllowedEditors, zAllowedEditors);
756
+ glob_free(pAllowedEditors);
757
+ }
758
+ }
759
+
760
+ /* Initialize the skin, if requested and authorized. */
761
+ if( P("init3")!=0 && isEditor ){
762
+ skin_initialize_draft(iSkin, P("initskin"));
763
+ }
696764
697
-#if 0
698
- @ <p>
699
- cgi_print_all(0);
700
- @ </p>
701
-#endif
765
+ style_header("Customize Skin");
702766
703767
@ <p>Customize the look of this Fossil repository by making changes
704768
@ to the CSS, Header, Footer, and Detail Settings in one of nine "draft"
705769
@ configurations. Then, after verifying that all is working correctly,
706770
@ publish the draft to become the new main Skin.<p>
@@ -729,10 +793,31 @@
729793
@ <a name='step2'></a>
730794
@ <h1>Step 2: Authenticate
731795
@
732796
@ <a name='step3'></a>
733797
@ <h1>Step 3: Initialize The Draft</h1>
798
+ @
799
+ if( !isEditor ){
800
+ @ <p>You are not allowed to initialize draft%(iSkin). Contact
801
+ @ the administrator for this repository for more information.
802
+ }else{
803
+ @ <p>Initialize the draft%d(iSkin) skin to one of the built-in skins
804
+ @ or a preexisting skin, to use as a baseline.</p>
805
+ @
806
+ @ <p><form method='POST' action='%R/setup_skin#stop4' id='f03'>
807
+ @ <input type='hidden' name='sk' value='%d(iSkin)'>
808
+ @ Initialize <b>draft%d(iSkin)</b> to
809
+ @ <select size='1' name='initskin'>
810
+ @ <option value='current'>Currently In Use</option>
811
+ for(i=0; i<count(aBuiltinSkin); i++){
812
+ @ <option value='%s(aBuiltinSkin[i].zLabel)'>\
813
+ @ %h(aBuiltinSkin[i].zDesc) (built-in)</option>
814
+ }
815
+ @ </select>
816
+ @ <input type='submit' name='init3' value='Go'>
817
+ @ </p>
818
+ }
734819
@
735820
@ <a name='step4'></a>
736821
@ <h1>Step 4: Make Edits</h1>
737822
@
738823
@ <a name='step5'></a>
@@ -739,15 +824,19 @@
739824
@ <h1>Step 5: Verify The Draft Skin</h1>
740825
@
741826
@ <p>To test this draft skin, insert text "/draft%d(iSkin)/" just before the
742827
@ operation name in the URL. Here are a few links to try:
743828
@ <ul>
744
- for(i=0; i<sizeof(azTestPages)/sizeof(azTestPages[0]); i++){
829
+ for(i=0; i<count(azTestPages); i++){
745830
@ <li><a href='%s(g.zBaseURL)/draft%d(iSkin)/%s(azTestPages[i])'>\
746831
@ %s(g.zBaseURL)/draft%d(iSkin)/%s(azTestPages[i])</a>
747832
}
748833
@ </ul>
834
+ @
835
+ @ <p><b>Important:</b> After CSS changes, you will probably need to
836
+ @ press the "Reload" button on your browser for those changes
837
+ @ to take effect.</p>
749838
@
750839
@ <a name='step6'></a>
751840
@ <h1>Step 6: Publish The Draft</h1>
752841
if( !g.perm.Setup ){
753842
@ <p>Only administrators are allowed to publish draft skins. Contact
754843
--- src/skins.c
+++ src/skins.c
@@ -64,10 +64,11 @@
64 ** The following static variable holds the name of the alternative skin,
65 ** or NULL if the skin should be as configured.
66 */
67 static struct BuiltinSkin *pAltSkin = 0;
68 static char *zAltSkinDir = 0;
 
69
70 /*
71 ** Skin details are a set of key/value pairs that define display
72 ** attributes of the skin that cannot be easily specified using CSS
73 ** or that need to be known on the server-side.
@@ -100,10 +101,14 @@
100 int i;
101 Blob err = BLOB_INITIALIZER;
102 if( strchr(zName, '/')!=0 ){
103 zAltSkinDir = fossil_strdup(zName);
104 return 0;
 
 
 
 
105 }
106 for(i=0; i<count(aBuiltinSkin); i++){
107 if( fossil_strcmp(aBuiltinSkin[i].zLabel, zName)==0 ){
108 pAltSkin = &aBuiltinSkin[i];
109 return 0;
@@ -126,18 +131,33 @@
126 if( zSkin ){
127 char *zErr = skin_use_alternative(zSkin);
128 if( zErr ) fossil_fatal("%s", zErr);
129 }
130 }
 
 
 
 
 
 
 
131
132 /*
133 ** The following routines return the various components of the skin
134 ** that should be used for the current run.
 
 
135 */
136 const char *skin_get(const char *zWhat){
137 const char *zOut;
138 char *z;
 
 
 
 
 
 
139 if( zAltSkinDir ){
140 char *z = mprintf("%s/%s.txt", zAltSkinDir, zWhat);
141 if( file_isfile(z) ){
142 Blob x;
143 blob_read_from_file(&x, z);
@@ -668,39 +688,83 @@
668 }
669 @ </div></form>
670 style_footer();
671 db_end_transaction(0);
672 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
673
674 /*
675 ** WEBPAGE: setup_skin
676 **
677 ** Generate a page showing the steps needed to customize a skin.
678 */
679 void setup_skin(void){
680 int i; /* Loop counter */
681 int iSkin; /* Which draft skin is being edited */
 
 
682 static const char *azTestPages[] = {
683 "home",
684 "timeline",
685 "dir?ci=tip",
686 "dir?ci=tip&type=tree",
687 "brlist",
688 "info/trunk",
689 };
690
691
692 iSkin = atoi(PD("sk","1"));
693 if( iSkin<1 || iSkin>9 ) iSkin = 1;
 
 
 
 
694 login_check_credentials();
695 style_header("Customize Skin");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
696
697 #if 0
698 @ <p>
699 cgi_print_all(0);
700 @ </p>
701 #endif
702
703 @ <p>Customize the look of this Fossil repository by making changes
704 @ to the CSS, Header, Footer, and Detail Settings in one of nine "draft"
705 @ configurations. Then, after verifying that all is working correctly,
706 @ publish the draft to become the new main Skin.<p>
@@ -729,10 +793,31 @@
729 @ <a name='step2'></a>
730 @ <h1>Step 2: Authenticate
731 @
732 @ <a name='step3'></a>
733 @ <h1>Step 3: Initialize The Draft</h1>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
734 @
735 @ <a name='step4'></a>
736 @ <h1>Step 4: Make Edits</h1>
737 @
738 @ <a name='step5'></a>
@@ -739,15 +824,19 @@
739 @ <h1>Step 5: Verify The Draft Skin</h1>
740 @
741 @ <p>To test this draft skin, insert text "/draft%d(iSkin)/" just before the
742 @ operation name in the URL. Here are a few links to try:
743 @ <ul>
744 for(i=0; i<sizeof(azTestPages)/sizeof(azTestPages[0]); i++){
745 @ <li><a href='%s(g.zBaseURL)/draft%d(iSkin)/%s(azTestPages[i])'>\
746 @ %s(g.zBaseURL)/draft%d(iSkin)/%s(azTestPages[i])</a>
747 }
748 @ </ul>
 
 
 
 
749 @
750 @ <a name='step6'></a>
751 @ <h1>Step 6: Publish The Draft</h1>
752 if( !g.perm.Setup ){
753 @ <p>Only administrators are allowed to publish draft skins. Contact
754
--- src/skins.c
+++ src/skins.c
@@ -64,10 +64,11 @@
64 ** The following static variable holds the name of the alternative skin,
65 ** or NULL if the skin should be as configured.
66 */
67 static struct BuiltinSkin *pAltSkin = 0;
68 static char *zAltSkinDir = 0;
69 static int iDraftSkin = 0;
70
71 /*
72 ** Skin details are a set of key/value pairs that define display
73 ** attributes of the skin that cannot be easily specified using CSS
74 ** or that need to be known on the server-side.
@@ -100,10 +101,14 @@
101 int i;
102 Blob err = BLOB_INITIALIZER;
103 if( strchr(zName, '/')!=0 ){
104 zAltSkinDir = fossil_strdup(zName);
105 return 0;
106 }
107 if( sqlite3_strglob("draft[1-9]", zName)==0 ){
108 skin_use_draft(zName[5] - '0');
109 return 0;
110 }
111 for(i=0; i<count(aBuiltinSkin); i++){
112 if( fossil_strcmp(aBuiltinSkin[i].zLabel, zName)==0 ){
113 pAltSkin = &aBuiltinSkin[i];
114 return 0;
@@ -126,18 +131,33 @@
131 if( zSkin ){
132 char *zErr = skin_use_alternative(zSkin);
133 if( zErr ) fossil_fatal("%s", zErr);
134 }
135 }
136
137 /*
138 ** Use one of the draft skins.
139 */
140 void skin_use_draft(int i){
141 iDraftSkin = i;
142 }
143
144 /*
145 ** The following routines return the various components of the skin
146 ** that should be used for the current run.
147 **
148 ** zWhat is one of: "css", "header", "footer", "details".
149 */
150 const char *skin_get(const char *zWhat){
151 const char *zOut;
152 char *z;
153 if( iDraftSkin ){
154 z = mprintf("draft%d-%s", iDraftSkin, zWhat);
155 zOut = db_get(z, 0);
156 fossil_free(z);
157 if( zOut ) return zOut;
158 }
159 if( zAltSkinDir ){
160 char *z = mprintf("%s/%s.txt", zAltSkinDir, zWhat);
161 if( file_isfile(z) ){
162 Blob x;
163 blob_read_from_file(&x, z);
@@ -668,39 +688,83 @@
688 }
689 @ </div></form>
690 style_footer();
691 db_end_transaction(0);
692 }
693
694 /*
695 ** Try to initialize draft skin iSkin to the built-in or preexisting
696 ** skin named by zTemplate.
697 */
698 static void skin_initialize_draft(int iSkin, const char *zTemplate){
699 int i;
700 const char *azWhat[] = { "css", "header", "footer", "detail" };
701 if( zTemplate==0 ) return;
702 if( strcmp(zTemplate, "current")==0 ){
703 for(i=0; i<count(azWhat); i++){
704 db_unset_mprintf("draft%d-%s", 0, iSkin, azWhat[i]);
705 }
706 }else{
707 for(i=0; i<count(aBuiltinSkin); i++){
708 if( strcmp(zTemplate, aBuiltinSkin[i].zLabel)==0 ){
709 for(i=0; i<count(azWhat); i++){
710 char *zKey = mprintf("skins/%s/%s.txt", zTemplate, azWhat[i]);
711 db_set_mprintf("draft%d-%s", builtin_text(zKey), 0, iSkin, azWhat[i]);
712 }
713 break;
714 }
715 }
716 }
717 }
718
719 /*
720 ** WEBPAGE: setup_skin
721 **
722 ** Generate a page showing the steps needed to customize a skin.
723 */
724 void setup_skin(void){
725 int i; /* Loop counter */
726 int iSkin; /* Which draft skin is being edited */
727 int isAdmin; /* True for an administrator */
728 int isEditor; /* Others authorized to make edits */
729 static const char *azTestPages[] = {
730 "home",
731 "timeline",
732 "dir?ci=tip",
733 "dir?ci=tip&type=tree",
734 "brlist",
735 "info/trunk",
736 };
737
738 /* Figure out which skin we are editing */
739 iSkin = atoi(PD("sk","1"));
740 if( iSkin<1 || iSkin>9 ) iSkin = 1;
741
742 /* Figure out if the current user is allowed to make administrative
743 ** changes and/or edits
744 */
745 login_check_credentials();
746 if( g.perm.Admin ){
747 isAdmin = isEditor = 1;
748 }else{
749 char *zAllowedEditors;
750 Glob *pAllowedEditors;
751 isAdmin = isEditor = 0;
752 zAllowedEditors = db_get_mprintf("draft%d-users", 0, iSkin);
753 if( zAllowedEditors ){
754 pAllowedEditors = glob_create(zAllowedEditors);
755 isEditor = glob_match(pAllowedEditors, zAllowedEditors);
756 glob_free(pAllowedEditors);
757 }
758 }
759
760 /* Initialize the skin, if requested and authorized. */
761 if( P("init3")!=0 && isEditor ){
762 skin_initialize_draft(iSkin, P("initskin"));
763 }
764
765 style_header("Customize Skin");
 
 
 
 
766
767 @ <p>Customize the look of this Fossil repository by making changes
768 @ to the CSS, Header, Footer, and Detail Settings in one of nine "draft"
769 @ configurations. Then, after verifying that all is working correctly,
770 @ publish the draft to become the new main Skin.<p>
@@ -729,10 +793,31 @@
793 @ <a name='step2'></a>
794 @ <h1>Step 2: Authenticate
795 @
796 @ <a name='step3'></a>
797 @ <h1>Step 3: Initialize The Draft</h1>
798 @
799 if( !isEditor ){
800 @ <p>You are not allowed to initialize draft%(iSkin). Contact
801 @ the administrator for this repository for more information.
802 }else{
803 @ <p>Initialize the draft%d(iSkin) skin to one of the built-in skins
804 @ or a preexisting skin, to use as a baseline.</p>
805 @
806 @ <p><form method='POST' action='%R/setup_skin#stop4' id='f03'>
807 @ <input type='hidden' name='sk' value='%d(iSkin)'>
808 @ Initialize <b>draft%d(iSkin)</b> to
809 @ <select size='1' name='initskin'>
810 @ <option value='current'>Currently In Use</option>
811 for(i=0; i<count(aBuiltinSkin); i++){
812 @ <option value='%s(aBuiltinSkin[i].zLabel)'>\
813 @ %h(aBuiltinSkin[i].zDesc) (built-in)</option>
814 }
815 @ </select>
816 @ <input type='submit' name='init3' value='Go'>
817 @ </p>
818 }
819 @
820 @ <a name='step4'></a>
821 @ <h1>Step 4: Make Edits</h1>
822 @
823 @ <a name='step5'></a>
@@ -739,15 +824,19 @@
824 @ <h1>Step 5: Verify The Draft Skin</h1>
825 @
826 @ <p>To test this draft skin, insert text "/draft%d(iSkin)/" just before the
827 @ operation name in the URL. Here are a few links to try:
828 @ <ul>
829 for(i=0; i<count(azTestPages); i++){
830 @ <li><a href='%s(g.zBaseURL)/draft%d(iSkin)/%s(azTestPages[i])'>\
831 @ %s(g.zBaseURL)/draft%d(iSkin)/%s(azTestPages[i])</a>
832 }
833 @ </ul>
834 @
835 @ <p><b>Important:</b> After CSS changes, you will probably need to
836 @ press the "Reload" button on your browser for those changes
837 @ to take effect.</p>
838 @
839 @ <a name='step6'></a>
840 @ <h1>Step 6: Publish The Draft</h1>
841 if( !g.perm.Setup ){
842 @ <p>Only administrators are allowed to publish draft skins. Contact
843

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button