Fossil SCM

Update the "configure" command so that the "import", "export", and "merge" subcommands use the new config format.

drh 2011-04-26 18:36 UTC config-sync
Commit 42911838825ae9ce1d7f0f707f142323257166dc
+6 -6
--- src/clone.c
+++ src/clone.c
@@ -64,14 +64,14 @@
6464
file_copy(g.urlName, g.argv[3]);
6565
db_close(1);
6666
db_open_repository(g.argv[3]);
6767
db_record_repository_filename(g.argv[3]);
6868
db_multi_exec(
69
- "REPLACE INTO config(name,value)"
70
- " VALUES('server-code', lower(hex(randomblob(20))));"
71
- "REPLACE INTO config(name,value)"
72
- " VALUES('last-sync-url', '%q');",
69
+ "REPLACE INTO config(name,value,mtime)"
70
+ " VALUES('server-code', lower(hex(randomblob(20))),now());"
71
+ "REPLACE INTO config(name,value,mtime)"
72
+ " VALUES('last-sync-url', '%q',now());",
7373
g.urlCanonical
7474
);
7575
db_multi_exec(
7676
"DELETE FROM blob WHERE rid IN private;"
7777
"DELETE FROM delta wHERE rid IN private;"
@@ -92,12 +92,12 @@
9292
user_select();
9393
db_set("content-schema", CONTENT_SCHEMA, 0);
9494
db_set("aux-schema", AUX_SCHEMA, 0);
9595
db_set("last-sync-url", g.argv[2], 0);
9696
db_multi_exec(
97
- "REPLACE INTO config(name,value)"
98
- " VALUES('server-code', lower(hex(randomblob(20))));"
97
+ "REPLACE INTO config(name,value,mtime)"
98
+ " VALUES('server-code', lower(hex(randomblob(20))), now());"
9999
);
100100
url_enable_proxy(0);
101101
url_get_password_if_needed();
102102
g.xlinkClusterOnly = 1;
103103
nErr = client_sync(0,0,1,bPrivate,CONFIGSET_ALL,0);
104104
--- src/clone.c
+++ src/clone.c
@@ -64,14 +64,14 @@
64 file_copy(g.urlName, g.argv[3]);
65 db_close(1);
66 db_open_repository(g.argv[3]);
67 db_record_repository_filename(g.argv[3]);
68 db_multi_exec(
69 "REPLACE INTO config(name,value)"
70 " VALUES('server-code', lower(hex(randomblob(20))));"
71 "REPLACE INTO config(name,value)"
72 " VALUES('last-sync-url', '%q');",
73 g.urlCanonical
74 );
75 db_multi_exec(
76 "DELETE FROM blob WHERE rid IN private;"
77 "DELETE FROM delta wHERE rid IN private;"
@@ -92,12 +92,12 @@
92 user_select();
93 db_set("content-schema", CONTENT_SCHEMA, 0);
94 db_set("aux-schema", AUX_SCHEMA, 0);
95 db_set("last-sync-url", g.argv[2], 0);
96 db_multi_exec(
97 "REPLACE INTO config(name,value)"
98 " VALUES('server-code', lower(hex(randomblob(20))));"
99 );
100 url_enable_proxy(0);
101 url_get_password_if_needed();
102 g.xlinkClusterOnly = 1;
103 nErr = client_sync(0,0,1,bPrivate,CONFIGSET_ALL,0);
104
--- src/clone.c
+++ src/clone.c
@@ -64,14 +64,14 @@
64 file_copy(g.urlName, g.argv[3]);
65 db_close(1);
66 db_open_repository(g.argv[3]);
67 db_record_repository_filename(g.argv[3]);
68 db_multi_exec(
69 "REPLACE INTO config(name,value,mtime)"
70 " VALUES('server-code', lower(hex(randomblob(20))),now());"
71 "REPLACE INTO config(name,value,mtime)"
72 " VALUES('last-sync-url', '%q',now());",
73 g.urlCanonical
74 );
75 db_multi_exec(
76 "DELETE FROM blob WHERE rid IN private;"
77 "DELETE FROM delta wHERE rid IN private;"
@@ -92,12 +92,12 @@
92 user_select();
93 db_set("content-schema", CONTENT_SCHEMA, 0);
94 db_set("aux-schema", AUX_SCHEMA, 0);
95 db_set("last-sync-url", g.argv[2], 0);
96 db_multi_exec(
97 "REPLACE INTO config(name,value,mtime)"
98 " VALUES('server-code', lower(hex(randomblob(20))), now());"
99 );
100 url_enable_proxy(0);
101 url_get_password_if_needed();
102 g.xlinkClusterOnly = 1;
103 nErr = client_sync(0,0,1,bPrivate,CONFIGSET_ALL,0);
104
+118 -71
--- src/configure.c
+++ src/configure.c
@@ -27,18 +27,20 @@
2727
#if INTERFACE
2828
/*
2929
** Configuration transfers occur in groups. These are the allowed
3030
** groupings:
3131
*/
32
-#define CONFIGSET_SKIN 0x000001 /* WWW interface appearance */
33
-#define CONFIGSET_TKT 0x000002 /* Ticket configuration */
34
-#define CONFIGSET_PROJ 0x000004 /* Project name */
35
-#define CONFIGSET_SHUN 0x000008 /* Shun settings */
36
-#define CONFIGSET_USER 0x000010 /* The USER table */
37
-#define CONFIGSET_ADDR 0x000020 /* The CONCEALED table */
38
-
39
-#define CONFIGSET_ALL 0xffffff /* Everything */
32
+#define CONFIGSET_SKIN 0x000001 /* WWW interface appearance */
33
+#define CONFIGSET_TKT 0x000002 /* Ticket configuration */
34
+#define CONFIGSET_PROJ 0x000004 /* Project name */
35
+#define CONFIGSET_SHUN 0x000008 /* Shun settings */
36
+#define CONFIGSET_USER 0x000010 /* The USER table */
37
+#define CONFIGSET_ADDR 0x000020 /* The CONCEALED table */
38
+
39
+#define CONFIGSET_ALL 0x0000ff /* Everything */
40
+
41
+#define CONFIGSET_OVERWRITE 0x100000 /* Causes overwrite instead of merge */
4042
4143
#endif /* INTERFACE */
4244
4345
/*
4446
** Names of the configuration sets
@@ -127,12 +129,17 @@
127129
** login credentials and has sufficient capabilities to access the requested
128130
** information.
129131
*/
130132
int configure_is_exportable(const char *zName){
131133
int i;
134
+ int n = strlen(zName);
135
+ if( n>2 && zName[0]=='\'' && zName[n-1]=='\'' ){
136
+ zName++;
137
+ n -= 2;
138
+ }
132139
for(i=0; i<count(aConfig); i++){
133
- if( fossil_strcmp(zName, aConfig[i].zName)==0 ){
140
+ if( memcmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){
134141
int m = aConfig[i].groupMask;
135142
if( !g.okAdmin ){
136143
m &= ~CONFIGSET_USER;
137144
}
138145
if( !g.okRdAddr ){
@@ -200,38 +207,39 @@
200207
}
201208
202209
/*
203210
** Two SQL functions:
204211
**
205
-** flag_test(int)
206
-** flag_clear(int)
212
+** config_is_reset(int)
213
+** config_reset(int)
207214
**
208
-** The flag_test() function takes the integer valued argument and
209
-** ANDs it against the static variable "flag_value" below. The
210
-** function returns TRUE or false depending on the result. The
211
-** flag_clear() function masks off the bits from "flag_value" that
215
+** The config_is_reset() function takes the integer valued argument and
216
+** ANDs it against the static variable "configHasBeenReset" below. The
217
+** function returns TRUE or FALSE depending on the result depending on
218
+** whether or not the corresponding configuration table has been reset. The
219
+** config_reset() function adds the bits to "configHasBeenReset" that
212220
** are given in the argument.
213221
**
214222
** These functions are used below in the WHEN clause of a trigger to
215223
** get the trigger to fire exactly once.
216224
*/
217
-static int flag_value = 0xffff;
218
-static void flag_test_function(
225
+static int configHasBeenReset = 0;
226
+static void config_is_reset_function(
219227
sqlite3_context *context,
220228
int argc,
221229
sqlite3_value **argv
222230
){
223231
int m = sqlite3_value_int(argv[0]);
224
- sqlite3_result_int(context, (flag_value&m)!=0 );
232
+ sqlite3_result_int(context, (configHasBeenReset&m)!=0 );
225233
}
226
-static void flag_clear_function(
234
+static void config_reset_function(
227235
sqlite3_context *context,
228236
int argc,
229237
sqlite3_value **argv
230238
){
231239
int m = sqlite3_value_int(argv[0]);
232
- flag_value &= ~m;
240
+ configHasBeenReset |= m;
233241
}
234242
235243
/*
236244
** Create the temporary _xfer_reportfmt and _xfer_user tables that are
237245
** necessary in order to evalute the SQL text generated by the
@@ -273,30 +281,30 @@
273281
** existing data.
274282
*/
275283
if( replaceFlag ){
276284
static const char zSQL2[] =
277285
@ CREATE TRIGGER _xfer_r1 BEFORE INSERT ON _xfer_reportfmt
278
- @ WHEN flag_test(1) BEGIN
286
+ @ WHEN NOT config_is_reset(2) BEGIN
279287
@ DELETE FROM _xfer_reportfmt;
280
- @ SELECT flag_clear(1);
288
+ @ SELECT config_reset(2);
281289
@ END;
282290
@ CREATE TRIGGER _xfer_r2 BEFORE INSERT ON _xfer_user
283
- @ WHEN flag_test(2) BEGIN
291
+ @ WHEN NOT config_is_reset(16) BEGIN
284292
@ DELETE FROM _xfer_user;
285
- @ SELECT flag_clear(2);
293
+ @ SELECT config_reset(16);
286294
@ END;
287295
@ CREATE TEMP TRIGGER _xfer_r3 BEFORE INSERT ON shun
288
- @ WHEN flag_test(4) BEGIN
296
+ @ WHEN NOT config_is_reset(8) BEGIN
289297
@ DELETE FROM shun;
290
- @ SELECT flag_clear(4);
298
+ @ SELECT config_reset(8);
291299
@ END;
292300
;
293
- sqlite3_create_function(g.db, "flag_test", 1, SQLITE_UTF8, 0,
294
- flag_test_function, 0, 0);
295
- sqlite3_create_function(g.db, "flag_clear", 1, SQLITE_UTF8, 0,
296
- flag_clear_function, 0, 0);
297
- flag_value = 0xffff;
301
+ sqlite3_create_function(g.db, "config_is_reset", 1, SQLITE_UTF8, 0,
302
+ config_is_reset_function, 0, 0);
303
+ sqlite3_create_function(g.db, "config_reset", 1, SQLITE_UTF8, 0,
304
+ config_reset_function, 0, 0);
305
+ configHasBeenReset = 0;
298306
db_multi_exec(zSQL2);
299307
}
300308
}
301309
302310
/*
@@ -384,16 +392,17 @@
384392
** table like _fer_reportfmt or _xfer_user. Such tables must be created
385393
** ahead of time using configure_prepare_to_receive(). Then after multiple
386394
** calls to this routine, configure_finalize_receive() to transfer the
387395
** information received into the true target table.
388396
*/
389
-void configure_receive(const char *zName, Blob *pContent, int mask){
397
+void configure_receive(const char *zName, Blob *pContent, int groupMask){
390398
if( zName[0]=='/' ){
391399
/* The new format */
392400
char *azToken[12];
393401
int nToken = 0;
394402
int ii, jj;
403
+ int thisMask;
395404
Blob name, value, sql;
396405
static const struct receiveType {
397406
const char *zName;
398407
const char *zPrimKey;
399408
int nField;
@@ -425,29 +434,38 @@
425434
if( !safeSql(z) ) return;
426435
if( nToken>=count(azToken) ) break;
427436
}
428437
if( nToken<2 ) return;
429438
if( aType[ii].zName[0]=='/' ){
430
- if( (configure_is_exportable(azToken[1]) & mask)==0 ) return;
431
- }else{
432
- if( (configure_is_exportable(aType[ii].zName) & mask)==0 ) return;
433
- }
434
-
435
- blob_reset(&sql);
436
- blob_appendf(&sql, "INSERT OR IGNORE INTO %s(%s, mtime",
437
- &zName[1], aType[ii].zPrimKey);
439
+ thisMask = configure_is_exportable(azToken[1]);
440
+ }else{
441
+ thisMask = configure_is_exportable(aType[ii].zName);
442
+ }
443
+ if( (thisMask & groupMask)==0 ) return;
444
+
445
+ blob_zero(&sql);
446
+ if( groupMask & CONFIGSET_OVERWRITE ){
447
+ if( (thisMask & configHasBeenReset)==0 && aType[ii].zName[0]!='/' ){
448
+ db_multi_exec("DELETE FROM %s", &aType[ii].zName[1]);
449
+ configHasBeenReset |= thisMask;
450
+ }
451
+ blob_append(&sql, "REPLACE INTO ", -1);
452
+ }else{
453
+ blob_append(&sql, "INSERT OR IGNORE INTO ", -1);
454
+ }
455
+ blob_appendf(&sql, "%s(%s, mtime", &zName[1], aType[ii].zPrimKey);
438456
for(jj=2; jj<nToken; jj+=2){
439457
blob_appendf(&sql, ",%s", azToken[jj]);
440458
}
441459
blob_appendf(&sql,") VALUES(%s,%s", azToken[1], azToken[0]);
442460
for(jj=2; jj<nToken; jj+=2){
443461
blob_appendf(&sql, ",%s", azToken[jj+1]);
444462
}
445
- db_multi_exec("%s", blob_str(&sql));
463
+ db_multi_exec("%s)", blob_str(&sql));
446464
if( db_changes()==0 ){
447465
blob_reset(&sql);
448
- blob_appendf(&sql, "UPDATE %s SET mtime=%s,", &zName[1], azToken[0]);
466
+ blob_appendf(&sql, "UPDATE %s SET mtime=%s", &zName[1], azToken[0]);
449467
for(jj=2; jj<nToken; jj+=2){
450468
blob_appendf(&sql, ", %s=%s", azToken[jj], azToken[jj+1]);
451469
}
452470
blob_appendf(&sql, " WHERE %s=%s AND mtime<%s",
453471
aType[ii].zPrimKey, azToken[1], azToken[0]);
@@ -454,15 +472,15 @@
454472
db_multi_exec("%s", blob_str(&sql));
455473
}
456474
blob_reset(&sql);
457475
}else{
458476
/* Otherwise, the old format */
459
- if( (configure_is_exportable(zName) & mask)==0 ) return;
477
+ if( (configure_is_exportable(zName) & groupMask)==0 ) return;
460478
if( strcmp(zName, "logo-image")==0 ){
461479
Stmt ins;
462480
db_prepare(&ins,
463
- "REPLACE INTO config(name, value) VALUES(:name, :value)"
481
+ "REPLACE INTO config(name, value, mtime) VALUES(:name, :value, now())"
464482
);
465483
db_bind_text(&ins, ":name", zName);
466484
db_bind_blob(&ins, ":value", pContent);
467485
db_step(&ins);
468486
db_finalize(&ins);
@@ -473,17 +491,47 @@
473491
** point.
474492
*/
475493
db_multi_exec("%s", blob_str(pContent));
476494
}else{
477495
db_multi_exec(
478
- "REPLACE INTO config(name,value) VALUES(%Q,%Q)",
496
+ "REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now())",
479497
zName, blob_str(pContent)
480498
);
481499
}
482500
}
483501
}
484502
503
+/*
504
+** Process a file full of "config" cards.
505
+*/
506
+void configure_receive_all(Blob *pIn, int groupMask){
507
+ Blob line;
508
+ int nToken;
509
+ int size;
510
+ Blob aToken[4];
511
+
512
+ configHasBeenReset = 0;
513
+ while( blob_line(pIn, &line) ){
514
+ if( blob_buffer(&line)[0]=='#' ) continue;
515
+ nToken = blob_tokenize(&line, aToken, count(aToken));
516
+ if( blob_eq(&aToken[0],"config")
517
+ && nToken==3
518
+ && blob_is_int(&aToken[2], &size)
519
+ ){
520
+ const char *zName = blob_str(&aToken[1]);
521
+ Blob content;
522
+ blob_zero(&content);
523
+ blob_extract(pIn, size, &content);
524
+ g.okAdmin = g.okRdAddr = 1;
525
+ configure_receive(zName, &content, groupMask);
526
+ blob_reset(&content);
527
+ blob_seek(pIn, 1, BLOB_SEEK_CUR);
528
+ }
529
+ }
530
+}
531
+
532
+
485533
/*
486534
** Send "config" cards using the new format for all elements of a group
487535
** that have recently changed.
488536
**
489537
** Output goes into pOut. The groupMask identifies the group(s) to be sent.
@@ -648,40 +696,25 @@
648696
/*
649697
** Write SQL text into file zFilename that will restore the configuration
650698
** area identified by mask to its current state from any other state.
651699
*/
652700
static void export_config(
653
- int mask, /* Mask indicating which configuration to export */
701
+ int groupMask, /* Mask indicating which configuration to export */
654702
const char *zMask, /* Name of the configuration */
703
+ sqlite3_int64 iStart, /* Start date */
655704
const char *zFilename /* Write into this file */
656705
){
657
- int i;
658706
Blob out;
659707
blob_zero(&out);
660708
blob_appendf(&out,
661
- "-- The \"%s\" configuration exported from\n"
662
- "-- repository \"%s\"\n"
663
- "-- on %s\n",
709
+ "# The \"%s\" configuration exported from\n"
710
+ "# repository \"%s\"\n"
711
+ "# on %s\n",
664712
zMask, g.zRepositoryName,
665713
db_text(0, "SELECT datetime('now')")
666714
);
667
- for(i=0; i<count(aConfig); i++){
668
- if( (aConfig[i].groupMask & mask)!=0 ){
669
- const char *zName = aConfig[i].zName;
670
- if( zName[0]!='@' ){
671
- char *zValue = db_text(0,
672
- "SELECT quote(value) FROM config WHERE name=%Q", zName);
673
- if( zValue ){
674
- blob_appendf(&out,"REPLACE INTO config VALUES(%Q,%s);\n",
675
- zName, zValue);
676
- }
677
- free(zValue);
678
- }else{
679
- configure_render_special_name(zName, &out);
680
- }
681
- }
682
- }
715
+ configure_send_group(&out, groupMask, iStart);
683716
blob_write_to_file(&out, zFilename);
684717
blob_reset(&out);
685718
}
686719
687720
@@ -738,25 +771,39 @@
738771
db_find_and_open_repository(0, 0);
739772
zMethod = g.argv[2];
740773
n = strlen(zMethod);
741774
if( strncmp(zMethod, "export", n)==0 ){
742775
int mask;
776
+ const char *zSince = find_option("since",0,1);
777
+ sqlite3_int64 iStart;
743778
if( g.argc!=5 ){
744779
usage("export AREA FILENAME");
745780
}
746781
mask = find_area(g.argv[3]);
747
- export_config(mask, g.argv[3], g.argv[4]);
782
+ if( zSince ){
783
+ iStart = db_multi_exec(
784
+ "SELECT coalesce(strftime('%%s',%Q),strftime('%%s','now',%Q))+0",
785
+ zSince, zSince
786
+ );
787
+ }else{
788
+ iStart = 0;
789
+ }
790
+ export_config(mask, g.argv[3], iStart, g.argv[4]);
748791
}else
749792
if( strncmp(zMethod, "import", n)==0
750793
|| strncmp(zMethod, "merge", n)==0 ){
751794
Blob in;
795
+ int groupMask;
752796
if( g.argc!=4 ) usage(mprintf("%s FILENAME",zMethod));
753797
blob_read_from_file(&in, g.argv[3]);
754798
db_begin_transaction();
755
- configure_prepare_to_receive(zMethod[0]=='i');
756
- db_multi_exec("%s", blob_str(&in));
757
- configure_finalize_receive();
799
+ if( zMethod[0]=='i' ){
800
+ groupMask = CONFIGSET_ALL | CONFIGSET_OVERWRITE;
801
+ }else{
802
+ groupMask = CONFIGSET_ALL;
803
+ }
804
+ configure_receive_all(&in, groupMask);
758805
db_end_transaction(0);
759806
}else
760807
if( strncmp(zMethod, "pull", n)==0 || strncmp(zMethod, "push", n)==0 ){
761808
int mask;
762809
const char *zServer;
@@ -793,11 +840,11 @@
793840
if( g.argc!=4 ) usage("reset AREA");
794841
mask = find_area(g.argv[3]);
795842
zBackup = db_text(0,
796843
"SELECT strftime('config-backup-%%Y%%m%%d%%H%%M%%f','now')");
797844
db_begin_transaction();
798
- export_config(mask, g.argv[3], zBackup);
845
+ export_config(mask, g.argv[3], 0, zBackup);
799846
for(i=0; i<count(aConfig); i++){
800847
const char *zName = aConfig[i].zName;
801848
if( (aConfig[i].groupMask & mask)==0 ) continue;
802849
if( zName[0]!='@' ){
803850
db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
804851
--- src/configure.c
+++ src/configure.c
@@ -27,18 +27,20 @@
27 #if INTERFACE
28 /*
29 ** Configuration transfers occur in groups. These are the allowed
30 ** groupings:
31 */
32 #define CONFIGSET_SKIN 0x000001 /* WWW interface appearance */
33 #define CONFIGSET_TKT 0x000002 /* Ticket configuration */
34 #define CONFIGSET_PROJ 0x000004 /* Project name */
35 #define CONFIGSET_SHUN 0x000008 /* Shun settings */
36 #define CONFIGSET_USER 0x000010 /* The USER table */
37 #define CONFIGSET_ADDR 0x000020 /* The CONCEALED table */
38
39 #define CONFIGSET_ALL 0xffffff /* Everything */
 
 
40
41 #endif /* INTERFACE */
42
43 /*
44 ** Names of the configuration sets
@@ -127,12 +129,17 @@
127 ** login credentials and has sufficient capabilities to access the requested
128 ** information.
129 */
130 int configure_is_exportable(const char *zName){
131 int i;
 
 
 
 
 
132 for(i=0; i<count(aConfig); i++){
133 if( fossil_strcmp(zName, aConfig[i].zName)==0 ){
134 int m = aConfig[i].groupMask;
135 if( !g.okAdmin ){
136 m &= ~CONFIGSET_USER;
137 }
138 if( !g.okRdAddr ){
@@ -200,38 +207,39 @@
200 }
201
202 /*
203 ** Two SQL functions:
204 **
205 ** flag_test(int)
206 ** flag_clear(int)
207 **
208 ** The flag_test() function takes the integer valued argument and
209 ** ANDs it against the static variable "flag_value" below. The
210 ** function returns TRUE or false depending on the result. The
211 ** flag_clear() function masks off the bits from "flag_value" that
 
212 ** are given in the argument.
213 **
214 ** These functions are used below in the WHEN clause of a trigger to
215 ** get the trigger to fire exactly once.
216 */
217 static int flag_value = 0xffff;
218 static void flag_test_function(
219 sqlite3_context *context,
220 int argc,
221 sqlite3_value **argv
222 ){
223 int m = sqlite3_value_int(argv[0]);
224 sqlite3_result_int(context, (flag_value&m)!=0 );
225 }
226 static void flag_clear_function(
227 sqlite3_context *context,
228 int argc,
229 sqlite3_value **argv
230 ){
231 int m = sqlite3_value_int(argv[0]);
232 flag_value &= ~m;
233 }
234
235 /*
236 ** Create the temporary _xfer_reportfmt and _xfer_user tables that are
237 ** necessary in order to evalute the SQL text generated by the
@@ -273,30 +281,30 @@
273 ** existing data.
274 */
275 if( replaceFlag ){
276 static const char zSQL2[] =
277 @ CREATE TRIGGER _xfer_r1 BEFORE INSERT ON _xfer_reportfmt
278 @ WHEN flag_test(1) BEGIN
279 @ DELETE FROM _xfer_reportfmt;
280 @ SELECT flag_clear(1);
281 @ END;
282 @ CREATE TRIGGER _xfer_r2 BEFORE INSERT ON _xfer_user
283 @ WHEN flag_test(2) BEGIN
284 @ DELETE FROM _xfer_user;
285 @ SELECT flag_clear(2);
286 @ END;
287 @ CREATE TEMP TRIGGER _xfer_r3 BEFORE INSERT ON shun
288 @ WHEN flag_test(4) BEGIN
289 @ DELETE FROM shun;
290 @ SELECT flag_clear(4);
291 @ END;
292 ;
293 sqlite3_create_function(g.db, "flag_test", 1, SQLITE_UTF8, 0,
294 flag_test_function, 0, 0);
295 sqlite3_create_function(g.db, "flag_clear", 1, SQLITE_UTF8, 0,
296 flag_clear_function, 0, 0);
297 flag_value = 0xffff;
298 db_multi_exec(zSQL2);
299 }
300 }
301
302 /*
@@ -384,16 +392,17 @@
384 ** table like _fer_reportfmt or _xfer_user. Such tables must be created
385 ** ahead of time using configure_prepare_to_receive(). Then after multiple
386 ** calls to this routine, configure_finalize_receive() to transfer the
387 ** information received into the true target table.
388 */
389 void configure_receive(const char *zName, Blob *pContent, int mask){
390 if( zName[0]=='/' ){
391 /* The new format */
392 char *azToken[12];
393 int nToken = 0;
394 int ii, jj;
 
395 Blob name, value, sql;
396 static const struct receiveType {
397 const char *zName;
398 const char *zPrimKey;
399 int nField;
@@ -425,29 +434,38 @@
425 if( !safeSql(z) ) return;
426 if( nToken>=count(azToken) ) break;
427 }
428 if( nToken<2 ) return;
429 if( aType[ii].zName[0]=='/' ){
430 if( (configure_is_exportable(azToken[1]) & mask)==0 ) return;
431 }else{
432 if( (configure_is_exportable(aType[ii].zName) & mask)==0 ) return;
433 }
434
435 blob_reset(&sql);
436 blob_appendf(&sql, "INSERT OR IGNORE INTO %s(%s, mtime",
437 &zName[1], aType[ii].zPrimKey);
 
 
 
 
 
 
 
 
 
438 for(jj=2; jj<nToken; jj+=2){
439 blob_appendf(&sql, ",%s", azToken[jj]);
440 }
441 blob_appendf(&sql,") VALUES(%s,%s", azToken[1], azToken[0]);
442 for(jj=2; jj<nToken; jj+=2){
443 blob_appendf(&sql, ",%s", azToken[jj+1]);
444 }
445 db_multi_exec("%s", blob_str(&sql));
446 if( db_changes()==0 ){
447 blob_reset(&sql);
448 blob_appendf(&sql, "UPDATE %s SET mtime=%s,", &zName[1], azToken[0]);
449 for(jj=2; jj<nToken; jj+=2){
450 blob_appendf(&sql, ", %s=%s", azToken[jj], azToken[jj+1]);
451 }
452 blob_appendf(&sql, " WHERE %s=%s AND mtime<%s",
453 aType[ii].zPrimKey, azToken[1], azToken[0]);
@@ -454,15 +472,15 @@
454 db_multi_exec("%s", blob_str(&sql));
455 }
456 blob_reset(&sql);
457 }else{
458 /* Otherwise, the old format */
459 if( (configure_is_exportable(zName) & mask)==0 ) return;
460 if( strcmp(zName, "logo-image")==0 ){
461 Stmt ins;
462 db_prepare(&ins,
463 "REPLACE INTO config(name, value) VALUES(:name, :value)"
464 );
465 db_bind_text(&ins, ":name", zName);
466 db_bind_blob(&ins, ":value", pContent);
467 db_step(&ins);
468 db_finalize(&ins);
@@ -473,17 +491,47 @@
473 ** point.
474 */
475 db_multi_exec("%s", blob_str(pContent));
476 }else{
477 db_multi_exec(
478 "REPLACE INTO config(name,value) VALUES(%Q,%Q)",
479 zName, blob_str(pContent)
480 );
481 }
482 }
483 }
484
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
485 /*
486 ** Send "config" cards using the new format for all elements of a group
487 ** that have recently changed.
488 **
489 ** Output goes into pOut. The groupMask identifies the group(s) to be sent.
@@ -648,40 +696,25 @@
648 /*
649 ** Write SQL text into file zFilename that will restore the configuration
650 ** area identified by mask to its current state from any other state.
651 */
652 static void export_config(
653 int mask, /* Mask indicating which configuration to export */
654 const char *zMask, /* Name of the configuration */
 
655 const char *zFilename /* Write into this file */
656 ){
657 int i;
658 Blob out;
659 blob_zero(&out);
660 blob_appendf(&out,
661 "-- The \"%s\" configuration exported from\n"
662 "-- repository \"%s\"\n"
663 "-- on %s\n",
664 zMask, g.zRepositoryName,
665 db_text(0, "SELECT datetime('now')")
666 );
667 for(i=0; i<count(aConfig); i++){
668 if( (aConfig[i].groupMask & mask)!=0 ){
669 const char *zName = aConfig[i].zName;
670 if( zName[0]!='@' ){
671 char *zValue = db_text(0,
672 "SELECT quote(value) FROM config WHERE name=%Q", zName);
673 if( zValue ){
674 blob_appendf(&out,"REPLACE INTO config VALUES(%Q,%s);\n",
675 zName, zValue);
676 }
677 free(zValue);
678 }else{
679 configure_render_special_name(zName, &out);
680 }
681 }
682 }
683 blob_write_to_file(&out, zFilename);
684 blob_reset(&out);
685 }
686
687
@@ -738,25 +771,39 @@
738 db_find_and_open_repository(0, 0);
739 zMethod = g.argv[2];
740 n = strlen(zMethod);
741 if( strncmp(zMethod, "export", n)==0 ){
742 int mask;
 
 
743 if( g.argc!=5 ){
744 usage("export AREA FILENAME");
745 }
746 mask = find_area(g.argv[3]);
747 export_config(mask, g.argv[3], g.argv[4]);
 
 
 
 
 
 
 
 
748 }else
749 if( strncmp(zMethod, "import", n)==0
750 || strncmp(zMethod, "merge", n)==0 ){
751 Blob in;
 
752 if( g.argc!=4 ) usage(mprintf("%s FILENAME",zMethod));
753 blob_read_from_file(&in, g.argv[3]);
754 db_begin_transaction();
755 configure_prepare_to_receive(zMethod[0]=='i');
756 db_multi_exec("%s", blob_str(&in));
757 configure_finalize_receive();
 
 
 
758 db_end_transaction(0);
759 }else
760 if( strncmp(zMethod, "pull", n)==0 || strncmp(zMethod, "push", n)==0 ){
761 int mask;
762 const char *zServer;
@@ -793,11 +840,11 @@
793 if( g.argc!=4 ) usage("reset AREA");
794 mask = find_area(g.argv[3]);
795 zBackup = db_text(0,
796 "SELECT strftime('config-backup-%%Y%%m%%d%%H%%M%%f','now')");
797 db_begin_transaction();
798 export_config(mask, g.argv[3], zBackup);
799 for(i=0; i<count(aConfig); i++){
800 const char *zName = aConfig[i].zName;
801 if( (aConfig[i].groupMask & mask)==0 ) continue;
802 if( zName[0]!='@' ){
803 db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
804
--- src/configure.c
+++ src/configure.c
@@ -27,18 +27,20 @@
27 #if INTERFACE
28 /*
29 ** Configuration transfers occur in groups. These are the allowed
30 ** groupings:
31 */
32 #define CONFIGSET_SKIN 0x000001 /* WWW interface appearance */
33 #define CONFIGSET_TKT 0x000002 /* Ticket configuration */
34 #define CONFIGSET_PROJ 0x000004 /* Project name */
35 #define CONFIGSET_SHUN 0x000008 /* Shun settings */
36 #define CONFIGSET_USER 0x000010 /* The USER table */
37 #define CONFIGSET_ADDR 0x000020 /* The CONCEALED table */
38
39 #define CONFIGSET_ALL 0x0000ff /* Everything */
40
41 #define CONFIGSET_OVERWRITE 0x100000 /* Causes overwrite instead of merge */
42
43 #endif /* INTERFACE */
44
45 /*
46 ** Names of the configuration sets
@@ -127,12 +129,17 @@
129 ** login credentials and has sufficient capabilities to access the requested
130 ** information.
131 */
132 int configure_is_exportable(const char *zName){
133 int i;
134 int n = strlen(zName);
135 if( n>2 && zName[0]=='\'' && zName[n-1]=='\'' ){
136 zName++;
137 n -= 2;
138 }
139 for(i=0; i<count(aConfig); i++){
140 if( memcmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){
141 int m = aConfig[i].groupMask;
142 if( !g.okAdmin ){
143 m &= ~CONFIGSET_USER;
144 }
145 if( !g.okRdAddr ){
@@ -200,38 +207,39 @@
207 }
208
209 /*
210 ** Two SQL functions:
211 **
212 ** config_is_reset(int)
213 ** config_reset(int)
214 **
215 ** The config_is_reset() function takes the integer valued argument and
216 ** ANDs it against the static variable "configHasBeenReset" below. The
217 ** function returns TRUE or FALSE depending on the result depending on
218 ** whether or not the corresponding configuration table has been reset. The
219 ** config_reset() function adds the bits to "configHasBeenReset" that
220 ** are given in the argument.
221 **
222 ** These functions are used below in the WHEN clause of a trigger to
223 ** get the trigger to fire exactly once.
224 */
225 static int configHasBeenReset = 0;
226 static void config_is_reset_function(
227 sqlite3_context *context,
228 int argc,
229 sqlite3_value **argv
230 ){
231 int m = sqlite3_value_int(argv[0]);
232 sqlite3_result_int(context, (configHasBeenReset&m)!=0 );
233 }
234 static void config_reset_function(
235 sqlite3_context *context,
236 int argc,
237 sqlite3_value **argv
238 ){
239 int m = sqlite3_value_int(argv[0]);
240 configHasBeenReset |= m;
241 }
242
243 /*
244 ** Create the temporary _xfer_reportfmt and _xfer_user tables that are
245 ** necessary in order to evalute the SQL text generated by the
@@ -273,30 +281,30 @@
281 ** existing data.
282 */
283 if( replaceFlag ){
284 static const char zSQL2[] =
285 @ CREATE TRIGGER _xfer_r1 BEFORE INSERT ON _xfer_reportfmt
286 @ WHEN NOT config_is_reset(2) BEGIN
287 @ DELETE FROM _xfer_reportfmt;
288 @ SELECT config_reset(2);
289 @ END;
290 @ CREATE TRIGGER _xfer_r2 BEFORE INSERT ON _xfer_user
291 @ WHEN NOT config_is_reset(16) BEGIN
292 @ DELETE FROM _xfer_user;
293 @ SELECT config_reset(16);
294 @ END;
295 @ CREATE TEMP TRIGGER _xfer_r3 BEFORE INSERT ON shun
296 @ WHEN NOT config_is_reset(8) BEGIN
297 @ DELETE FROM shun;
298 @ SELECT config_reset(8);
299 @ END;
300 ;
301 sqlite3_create_function(g.db, "config_is_reset", 1, SQLITE_UTF8, 0,
302 config_is_reset_function, 0, 0);
303 sqlite3_create_function(g.db, "config_reset", 1, SQLITE_UTF8, 0,
304 config_reset_function, 0, 0);
305 configHasBeenReset = 0;
306 db_multi_exec(zSQL2);
307 }
308 }
309
310 /*
@@ -384,16 +392,17 @@
392 ** table like _fer_reportfmt or _xfer_user. Such tables must be created
393 ** ahead of time using configure_prepare_to_receive(). Then after multiple
394 ** calls to this routine, configure_finalize_receive() to transfer the
395 ** information received into the true target table.
396 */
397 void configure_receive(const char *zName, Blob *pContent, int groupMask){
398 if( zName[0]=='/' ){
399 /* The new format */
400 char *azToken[12];
401 int nToken = 0;
402 int ii, jj;
403 int thisMask;
404 Blob name, value, sql;
405 static const struct receiveType {
406 const char *zName;
407 const char *zPrimKey;
408 int nField;
@@ -425,29 +434,38 @@
434 if( !safeSql(z) ) return;
435 if( nToken>=count(azToken) ) break;
436 }
437 if( nToken<2 ) return;
438 if( aType[ii].zName[0]=='/' ){
439 thisMask = configure_is_exportable(azToken[1]);
440 }else{
441 thisMask = configure_is_exportable(aType[ii].zName);
442 }
443 if( (thisMask & groupMask)==0 ) return;
444
445 blob_zero(&sql);
446 if( groupMask & CONFIGSET_OVERWRITE ){
447 if( (thisMask & configHasBeenReset)==0 && aType[ii].zName[0]!='/' ){
448 db_multi_exec("DELETE FROM %s", &aType[ii].zName[1]);
449 configHasBeenReset |= thisMask;
450 }
451 blob_append(&sql, "REPLACE INTO ", -1);
452 }else{
453 blob_append(&sql, "INSERT OR IGNORE INTO ", -1);
454 }
455 blob_appendf(&sql, "%s(%s, mtime", &zName[1], aType[ii].zPrimKey);
456 for(jj=2; jj<nToken; jj+=2){
457 blob_appendf(&sql, ",%s", azToken[jj]);
458 }
459 blob_appendf(&sql,") VALUES(%s,%s", azToken[1], azToken[0]);
460 for(jj=2; jj<nToken; jj+=2){
461 blob_appendf(&sql, ",%s", azToken[jj+1]);
462 }
463 db_multi_exec("%s)", blob_str(&sql));
464 if( db_changes()==0 ){
465 blob_reset(&sql);
466 blob_appendf(&sql, "UPDATE %s SET mtime=%s", &zName[1], azToken[0]);
467 for(jj=2; jj<nToken; jj+=2){
468 blob_appendf(&sql, ", %s=%s", azToken[jj], azToken[jj+1]);
469 }
470 blob_appendf(&sql, " WHERE %s=%s AND mtime<%s",
471 aType[ii].zPrimKey, azToken[1], azToken[0]);
@@ -454,15 +472,15 @@
472 db_multi_exec("%s", blob_str(&sql));
473 }
474 blob_reset(&sql);
475 }else{
476 /* Otherwise, the old format */
477 if( (configure_is_exportable(zName) & groupMask)==0 ) return;
478 if( strcmp(zName, "logo-image")==0 ){
479 Stmt ins;
480 db_prepare(&ins,
481 "REPLACE INTO config(name, value, mtime) VALUES(:name, :value, now())"
482 );
483 db_bind_text(&ins, ":name", zName);
484 db_bind_blob(&ins, ":value", pContent);
485 db_step(&ins);
486 db_finalize(&ins);
@@ -473,17 +491,47 @@
491 ** point.
492 */
493 db_multi_exec("%s", blob_str(pContent));
494 }else{
495 db_multi_exec(
496 "REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now())",
497 zName, blob_str(pContent)
498 );
499 }
500 }
501 }
502
503 /*
504 ** Process a file full of "config" cards.
505 */
506 void configure_receive_all(Blob *pIn, int groupMask){
507 Blob line;
508 int nToken;
509 int size;
510 Blob aToken[4];
511
512 configHasBeenReset = 0;
513 while( blob_line(pIn, &line) ){
514 if( blob_buffer(&line)[0]=='#' ) continue;
515 nToken = blob_tokenize(&line, aToken, count(aToken));
516 if( blob_eq(&aToken[0],"config")
517 && nToken==3
518 && blob_is_int(&aToken[2], &size)
519 ){
520 const char *zName = blob_str(&aToken[1]);
521 Blob content;
522 blob_zero(&content);
523 blob_extract(pIn, size, &content);
524 g.okAdmin = g.okRdAddr = 1;
525 configure_receive(zName, &content, groupMask);
526 blob_reset(&content);
527 blob_seek(pIn, 1, BLOB_SEEK_CUR);
528 }
529 }
530 }
531
532
533 /*
534 ** Send "config" cards using the new format for all elements of a group
535 ** that have recently changed.
536 **
537 ** Output goes into pOut. The groupMask identifies the group(s) to be sent.
@@ -648,40 +696,25 @@
696 /*
697 ** Write SQL text into file zFilename that will restore the configuration
698 ** area identified by mask to its current state from any other state.
699 */
700 static void export_config(
701 int groupMask, /* Mask indicating which configuration to export */
702 const char *zMask, /* Name of the configuration */
703 sqlite3_int64 iStart, /* Start date */
704 const char *zFilename /* Write into this file */
705 ){
 
706 Blob out;
707 blob_zero(&out);
708 blob_appendf(&out,
709 "# The \"%s\" configuration exported from\n"
710 "# repository \"%s\"\n"
711 "# on %s\n",
712 zMask, g.zRepositoryName,
713 db_text(0, "SELECT datetime('now')")
714 );
715 configure_send_group(&out, groupMask, iStart);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
716 blob_write_to_file(&out, zFilename);
717 blob_reset(&out);
718 }
719
720
@@ -738,25 +771,39 @@
771 db_find_and_open_repository(0, 0);
772 zMethod = g.argv[2];
773 n = strlen(zMethod);
774 if( strncmp(zMethod, "export", n)==0 ){
775 int mask;
776 const char *zSince = find_option("since",0,1);
777 sqlite3_int64 iStart;
778 if( g.argc!=5 ){
779 usage("export AREA FILENAME");
780 }
781 mask = find_area(g.argv[3]);
782 if( zSince ){
783 iStart = db_multi_exec(
784 "SELECT coalesce(strftime('%%s',%Q),strftime('%%s','now',%Q))+0",
785 zSince, zSince
786 );
787 }else{
788 iStart = 0;
789 }
790 export_config(mask, g.argv[3], iStart, g.argv[4]);
791 }else
792 if( strncmp(zMethod, "import", n)==0
793 || strncmp(zMethod, "merge", n)==0 ){
794 Blob in;
795 int groupMask;
796 if( g.argc!=4 ) usage(mprintf("%s FILENAME",zMethod));
797 blob_read_from_file(&in, g.argv[3]);
798 db_begin_transaction();
799 if( zMethod[0]=='i' ){
800 groupMask = CONFIGSET_ALL | CONFIGSET_OVERWRITE;
801 }else{
802 groupMask = CONFIGSET_ALL;
803 }
804 configure_receive_all(&in, groupMask);
805 db_end_transaction(0);
806 }else
807 if( strncmp(zMethod, "pull", n)==0 || strncmp(zMethod, "push", n)==0 ){
808 int mask;
809 const char *zServer;
@@ -793,11 +840,11 @@
840 if( g.argc!=4 ) usage("reset AREA");
841 mask = find_area(g.argv[3]);
842 zBackup = db_text(0,
843 "SELECT strftime('config-backup-%%Y%%m%%d%%H%%M%%f','now')");
844 db_begin_transaction();
845 export_config(mask, g.argv[3], 0, zBackup);
846 for(i=0; i<count(aConfig); i++){
847 const char *zName = aConfig[i].zName;
848 if( (aConfig[i].groupMask & mask)==0 ) continue;
849 if( zName[0]!='@' ){
850 db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
851
+21 -6
--- src/db.c
+++ src/db.c
@@ -34,10 +34,11 @@
3434
#endif
3535
#include <sqlite3.h>
3636
#include <sys/types.h>
3737
#include <sys/stat.h>
3838
#include <unistd.h>
39
+#include <time.h>
3940
#include "db.h"
4041
4142
#if INTERFACE
4243
/*
4344
** An single SQL statement is represented as an instance of the following
@@ -606,10 +607,23 @@
606607
}
607608
va_end(ap);
608609
sqlite3_exec(db, "COMMIT", 0, 0, 0);
609610
sqlite3_close(db);
610611
}
612
+
613
+/*
614
+** Function to return the number of seconds since 1970. This is
615
+** the same as strftime('%s','now') but is more compact.
616
+*/
617
+static void db_now_function(
618
+ sqlite3_context *context,
619
+ int argc,
620
+ sqlite3_value **argv
621
+){
622
+ sqlite3_result_int64(context, time(0));
623
+}
624
+
611625
612626
/*
613627
** Open a database file. Return a pointer to the new database
614628
** connection. An error results in process abort.
615629
*/
@@ -630,10 +644,11 @@
630644
if( rc!=SQLITE_OK ){
631645
db_err(sqlite3_errmsg(db));
632646
}
633647
sqlite3_busy_timeout(db, 5000);
634648
sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */
649
+ sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0);
635650
return db;
636651
}
637652
638653
639654
/*
@@ -1105,14 +1120,14 @@
11051120
11061121
db_set("content-schema", CONTENT_SCHEMA, 0);
11071122
db_set("aux-schema", AUX_SCHEMA, 0);
11081123
if( makeServerCodes ){
11091124
db_multi_exec(
1110
- "INSERT INTO config(name,value)"
1111
- " VALUES('server-code', lower(hex(randomblob(20))));"
1112
- "INSERT INTO config(name,value)"
1113
- " VALUES('project-code', lower(hex(randomblob(20))));"
1125
+ "INSERT INTO config(name,value,mtime)"
1126
+ " VALUES('server-code', lower(hex(randomblob(20))),now());"
1127
+ "INSERT INTO config(name,value,mtime)"
1128
+ " VALUES('project-code', lower(hex(randomblob(20))),now());"
11141129
);
11151130
}
11161131
if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0);
11171132
if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0);
11181133
db_create_default_users(0, zDefaultUser);
@@ -1406,11 +1421,11 @@
14061421
db_swap_connections();
14071422
db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%Q)",
14081423
zName, zValue);
14091424
db_swap_connections();
14101425
}else{
1411
- db_multi_exec("REPLACE INTO config(name,value) VALUES(%Q,%Q)",
1426
+ db_multi_exec("REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now())",
14121427
zName, zValue);
14131428
}
14141429
if( globalFlag && g.repositoryOpen ){
14151430
db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
14161431
}
@@ -1465,11 +1480,11 @@
14651480
db_swap_connections();
14661481
db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%d)",
14671482
zName, value);
14681483
db_swap_connections();
14691484
}else{
1470
- db_multi_exec("REPLACE INTO config(name,value) VALUES(%Q,%d)",
1485
+ db_multi_exec("REPLACE INTO config(name,value,mtime) VALUES(%Q,%d,now())",
14711486
zName, value);
14721487
}
14731488
if( globalFlag && g.repositoryOpen ){
14741489
db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
14751490
}
14761491
--- src/db.c
+++ src/db.c
@@ -34,10 +34,11 @@
34 #endif
35 #include <sqlite3.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
 
39 #include "db.h"
40
41 #if INTERFACE
42 /*
43 ** An single SQL statement is represented as an instance of the following
@@ -606,10 +607,23 @@
606 }
607 va_end(ap);
608 sqlite3_exec(db, "COMMIT", 0, 0, 0);
609 sqlite3_close(db);
610 }
 
 
 
 
 
 
 
 
 
 
 
 
 
611
612 /*
613 ** Open a database file. Return a pointer to the new database
614 ** connection. An error results in process abort.
615 */
@@ -630,10 +644,11 @@
630 if( rc!=SQLITE_OK ){
631 db_err(sqlite3_errmsg(db));
632 }
633 sqlite3_busy_timeout(db, 5000);
634 sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */
 
635 return db;
636 }
637
638
639 /*
@@ -1105,14 +1120,14 @@
1105
1106 db_set("content-schema", CONTENT_SCHEMA, 0);
1107 db_set("aux-schema", AUX_SCHEMA, 0);
1108 if( makeServerCodes ){
1109 db_multi_exec(
1110 "INSERT INTO config(name,value)"
1111 " VALUES('server-code', lower(hex(randomblob(20))));"
1112 "INSERT INTO config(name,value)"
1113 " VALUES('project-code', lower(hex(randomblob(20))));"
1114 );
1115 }
1116 if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0);
1117 if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0);
1118 db_create_default_users(0, zDefaultUser);
@@ -1406,11 +1421,11 @@
1406 db_swap_connections();
1407 db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%Q)",
1408 zName, zValue);
1409 db_swap_connections();
1410 }else{
1411 db_multi_exec("REPLACE INTO config(name,value) VALUES(%Q,%Q)",
1412 zName, zValue);
1413 }
1414 if( globalFlag && g.repositoryOpen ){
1415 db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
1416 }
@@ -1465,11 +1480,11 @@
1465 db_swap_connections();
1466 db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%d)",
1467 zName, value);
1468 db_swap_connections();
1469 }else{
1470 db_multi_exec("REPLACE INTO config(name,value) VALUES(%Q,%d)",
1471 zName, value);
1472 }
1473 if( globalFlag && g.repositoryOpen ){
1474 db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
1475 }
1476
--- src/db.c
+++ src/db.c
@@ -34,10 +34,11 @@
34 #endif
35 #include <sqlite3.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39 #include <time.h>
40 #include "db.h"
41
42 #if INTERFACE
43 /*
44 ** An single SQL statement is represented as an instance of the following
@@ -606,10 +607,23 @@
607 }
608 va_end(ap);
609 sqlite3_exec(db, "COMMIT", 0, 0, 0);
610 sqlite3_close(db);
611 }
612
613 /*
614 ** Function to return the number of seconds since 1970. This is
615 ** the same as strftime('%s','now') but is more compact.
616 */
617 static void db_now_function(
618 sqlite3_context *context,
619 int argc,
620 sqlite3_value **argv
621 ){
622 sqlite3_result_int64(context, time(0));
623 }
624
625
626 /*
627 ** Open a database file. Return a pointer to the new database
628 ** connection. An error results in process abort.
629 */
@@ -630,10 +644,11 @@
644 if( rc!=SQLITE_OK ){
645 db_err(sqlite3_errmsg(db));
646 }
647 sqlite3_busy_timeout(db, 5000);
648 sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */
649 sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0);
650 return db;
651 }
652
653
654 /*
@@ -1105,14 +1120,14 @@
1120
1121 db_set("content-schema", CONTENT_SCHEMA, 0);
1122 db_set("aux-schema", AUX_SCHEMA, 0);
1123 if( makeServerCodes ){
1124 db_multi_exec(
1125 "INSERT INTO config(name,value,mtime)"
1126 " VALUES('server-code', lower(hex(randomblob(20))),now());"
1127 "INSERT INTO config(name,value,mtime)"
1128 " VALUES('project-code', lower(hex(randomblob(20))),now());"
1129 );
1130 }
1131 if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0);
1132 if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0);
1133 db_create_default_users(0, zDefaultUser);
@@ -1406,11 +1421,11 @@
1421 db_swap_connections();
1422 db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%Q)",
1423 zName, zValue);
1424 db_swap_connections();
1425 }else{
1426 db_multi_exec("REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now())",
1427 zName, zValue);
1428 }
1429 if( globalFlag && g.repositoryOpen ){
1430 db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
1431 }
@@ -1465,11 +1480,11 @@
1480 db_swap_connections();
1481 db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%d)",
1482 zName, value);
1483 db_swap_connections();
1484 }else{
1485 db_multi_exec("REPLACE INTO config(name,value,mtime) VALUES(%Q,%d,now())",
1486 zName, value);
1487 }
1488 if( globalFlag && g.repositoryOpen ){
1489 db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
1490 }
1491
+2 -2
--- src/login.c
+++ src/login.c
@@ -1257,12 +1257,12 @@
12571257
db_multi_exec("DETACH other");
12581258
12591259
/* Propagate the changes to all other members of the login-group */
12601260
zSql = mprintf(
12611261
"BEGIN;"
1262
- "REPLACE INTO config(name, value) VALUES('peer-name-%q', %Q);"
1263
- "REPLACE INTO config(name, value) VALUES('peer-repo-%q', %Q);"
1262
+ "REPLACE INTO config(name,value,mtime) VALUES('peer-name-%q',%Q,now());"
1263
+ "REPLACE INTO config(name,value,mtime) VALUES('peer-repo-%q',%Q,now());"
12641264
"COMMIT;",
12651265
zSelfProjCode, zSelfLabel, zSelfProjCode, zSelfRepo
12661266
);
12671267
login_group_sql(zSql, "<li> ", "</li>", pzErrMsg);
12681268
fossil_free(zSql);
12691269
--- src/login.c
+++ src/login.c
@@ -1257,12 +1257,12 @@
1257 db_multi_exec("DETACH other");
1258
1259 /* Propagate the changes to all other members of the login-group */
1260 zSql = mprintf(
1261 "BEGIN;"
1262 "REPLACE INTO config(name, value) VALUES('peer-name-%q', %Q);"
1263 "REPLACE INTO config(name, value) VALUES('peer-repo-%q', %Q);"
1264 "COMMIT;",
1265 zSelfProjCode, zSelfLabel, zSelfProjCode, zSelfRepo
1266 );
1267 login_group_sql(zSql, "<li> ", "</li>", pzErrMsg);
1268 fossil_free(zSql);
1269
--- src/login.c
+++ src/login.c
@@ -1257,12 +1257,12 @@
1257 db_multi_exec("DETACH other");
1258
1259 /* Propagate the changes to all other members of the login-group */
1260 zSql = mprintf(
1261 "BEGIN;"
1262 "REPLACE INTO config(name,value,mtime) VALUES('peer-name-%q',%Q,now());"
1263 "REPLACE INTO config(name,value,mtime) VALUES('peer-repo-%q',%Q,now());"
1264 "COMMIT;",
1265 zSelfProjCode, zSelfLabel, zSelfProjCode, zSelfRepo
1266 );
1267 login_group_sql(zSql, "<li> ", "</li>", pzErrMsg);
1268 fossil_free(zSql);
1269
+2 -2
--- src/rebuild.c
+++ src/rebuild.c
@@ -530,12 +530,12 @@
530530
}
531531
db_begin_transaction();
532532
ttyOutput = 1;
533533
errCnt = rebuild_db(randomizeFlag, 1, doClustering);
534534
db_multi_exec(
535
- "REPLACE INTO config(name,value) VALUES('content-schema','%s');"
536
- "REPLACE INTO config(name,value) VALUES('aux-schema','%s');",
535
+ "REPLACE INTO config(name,value,mtime) VALUES('content-schema','%s',now());"
536
+ "REPLACE INTO config(name,value,mtime) VALUES('aux-schema','%s',now());",
537537
CONTENT_SCHEMA, AUX_SCHEMA
538538
);
539539
if( errCnt && !forceFlag ){
540540
printf("%d errors. Rolling back changes. Use --force to force a commit.\n",
541541
errCnt);
542542
--- src/rebuild.c
+++ src/rebuild.c
@@ -530,12 +530,12 @@
530 }
531 db_begin_transaction();
532 ttyOutput = 1;
533 errCnt = rebuild_db(randomizeFlag, 1, doClustering);
534 db_multi_exec(
535 "REPLACE INTO config(name,value) VALUES('content-schema','%s');"
536 "REPLACE INTO config(name,value) VALUES('aux-schema','%s');",
537 CONTENT_SCHEMA, AUX_SCHEMA
538 );
539 if( errCnt && !forceFlag ){
540 printf("%d errors. Rolling back changes. Use --force to force a commit.\n",
541 errCnt);
542
--- src/rebuild.c
+++ src/rebuild.c
@@ -530,12 +530,12 @@
530 }
531 db_begin_transaction();
532 ttyOutput = 1;
533 errCnt = rebuild_db(randomizeFlag, 1, doClustering);
534 db_multi_exec(
535 "REPLACE INTO config(name,value,mtime) VALUES('content-schema','%s',now());"
536 "REPLACE INTO config(name,value,mtime) VALUES('aux-schema','%s',now());",
537 CONTENT_SCHEMA, AUX_SCHEMA
538 );
539 if( errCnt && !forceFlag ){
540 printf("%d errors. Rolling back changes. Use --force to force a commit.\n",
541 errCnt);
542
+3 -3
--- src/setup.c
+++ src/setup.c
@@ -1286,18 +1286,18 @@
12861286
if( P("set")!=0 && zMime && zMime[0] && szImg>0 ){
12871287
Blob img;
12881288
Stmt ins;
12891289
blob_init(&img, aImg, szImg);
12901290
db_prepare(&ins,
1291
- "REPLACE INTO config(name, value)"
1292
- " VALUES('logo-image',:bytes)"
1291
+ "REPLACE INTO config(name,value,mtime)"
1292
+ " VALUES('logo-image',:bytes,now())"
12931293
);
12941294
db_bind_blob(&ins, ":bytes", &img);
12951295
db_step(&ins);
12961296
db_finalize(&ins);
12971297
db_multi_exec(
1298
- "REPLACE INTO config(name, value) VALUES('logo-mimetype',%Q)",
1298
+ "REPLACE INTO config(name,value,mtime) VALUES('logo-mimetype',%Q,now())",
12991299
zMime
13001300
);
13011301
db_end_transaction(0);
13021302
cgi_redirect("setup_logo");
13031303
}else if( P("clr")!=0 ){
13041304
--- src/setup.c
+++ src/setup.c
@@ -1286,18 +1286,18 @@
1286 if( P("set")!=0 && zMime && zMime[0] && szImg>0 ){
1287 Blob img;
1288 Stmt ins;
1289 blob_init(&img, aImg, szImg);
1290 db_prepare(&ins,
1291 "REPLACE INTO config(name, value)"
1292 " VALUES('logo-image',:bytes)"
1293 );
1294 db_bind_blob(&ins, ":bytes", &img);
1295 db_step(&ins);
1296 db_finalize(&ins);
1297 db_multi_exec(
1298 "REPLACE INTO config(name, value) VALUES('logo-mimetype',%Q)",
1299 zMime
1300 );
1301 db_end_transaction(0);
1302 cgi_redirect("setup_logo");
1303 }else if( P("clr")!=0 ){
1304
--- src/setup.c
+++ src/setup.c
@@ -1286,18 +1286,18 @@
1286 if( P("set")!=0 && zMime && zMime[0] && szImg>0 ){
1287 Blob img;
1288 Stmt ins;
1289 blob_init(&img, aImg, szImg);
1290 db_prepare(&ins,
1291 "REPLACE INTO config(name,value,mtime)"
1292 " VALUES('logo-image',:bytes,now())"
1293 );
1294 db_bind_blob(&ins, ":bytes", &img);
1295 db_step(&ins);
1296 db_finalize(&ins);
1297 db_multi_exec(
1298 "REPLACE INTO config(name,value,mtime) VALUES('logo-mimetype',%Q,now())",
1299 zMime
1300 );
1301 db_end_transaction(0);
1302 cgi_redirect("setup_logo");
1303 }else if( P("clr")!=0 ){
1304
+27 -18
--- src/skins.c
+++ src/skins.c
@@ -25,11 +25,12 @@
2525
/*
2626
** A black-and-white theme with the project title in a bar across the top
2727
** and no logo image.
2828
*/
2929
static const char zBuiltinSkin1[] =
30
-@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
30
+@ REPLACE INTO config(name,mtime,value)
31
+@ VALUES('css',now(),'/* General settings for the entire page */
3132
@ body {
3233
@ margin: 0ex 1ex;
3334
@ padding: 0px;
3435
@ background-color: white;
3536
@ font-family: sans-serif;
@@ -152,11 +153,11 @@
152153
@ table.label-value th {
153154
@ vertical-align: top;
154155
@ text-align: right;
155156
@ padding: 0.2ex 2ex;
156157
@ }');
157
-@ REPLACE INTO config VALUES('header','<html>
158
+@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
158159
@ <head>
159160
@ <title>$<project_name>: $<title></title>
160161
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
161162
@ href="$home/timeline.rss">
162163
@ <link rel="stylesheet" href="$home/style.css?blackwhite" type="text/css"
@@ -204,11 +205,12 @@
204205
@ } else {
205206
@ html "<a href=''$home/login''>Login</a> "
206207
@ }
207208
@ </th1></div>
208209
@ ');
209
-@ REPLACE INTO config VALUES('footer','<div class="footer">
210
+@ REPLACE INTO config(name,mtime,value)
211
+@ VALUES('footer',now(),'<div class="footer">
210212
@ Fossil version $manifest_version $manifest_date
211213
@ </div>
212214
@ </body></html>
213215
@ ');
214216
;
@@ -216,11 +218,12 @@
216218
/*
217219
** A tan theme with the project title above the user identification
218220
** and no logo image.
219221
*/
220222
static const char zBuiltinSkin2[] =
221
-@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
223
+@ REPLACE INTO config(name,mtime,value)
224
+@ VALUES('css',now(),'/* General settings for the entire page */
222225
@ body {
223226
@ margin: 0ex 0ex;
224227
@ padding: 0px;
225228
@ background-color: #fef3bc;
226229
@ font-family: sans-serif;
@@ -354,11 +357,11 @@
354357
@ vertical-align: top;
355358
@ text-align: right;
356359
@ padding: 0.2ex 2ex;
357360
@ }
358361
@ ');
359
-@ REPLACE INTO config VALUES('header','<html>
362
+@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
360363
@ <head>
361364
@ <title>$<project_name>: $<title></title>
362365
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
363366
@ href="$home/timeline.rss">
364367
@ <link rel="stylesheet" href="$home/style.css?tan" type="text/css"
@@ -405,11 +408,12 @@
405408
@ } else {
406409
@ html "<a href=''$home/login''>Login</a> "
407410
@ }
408411
@ </th1></div>
409412
@ ');
410
-@ REPLACE INTO config VALUES('footer','<div class="footer">
413
+@ REPLACE INTO config(name,mtime,value)
414
+@ VALUES('footer',now(),'<div class="footer">
411415
@ Fossil version $manifest_version $manifest_date
412416
@ </div>
413417
@ </body></html>
414418
@ ');
415419
;
@@ -417,11 +421,12 @@
417421
/*
418422
** Black letters on a white or cream background with the main menu
419423
** stuck on the left-hand side.
420424
*/
421425
static const char zBuiltinSkin3[] =
422
-@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
426
+@ REPLACE INTO config(name,mtime,value)
427
+@ VALUES('css',now(),'/* General settings for the entire page */
423428
@ body {
424429
@ margin:0px 0px 0px 0px;
425430
@ padding:0px;
426431
@ font-family:verdana, arial, helvetica, "sans serif";
427432
@ color:#333;
@@ -586,11 +591,11 @@
586591
@ table.label-value th {
587592
@ vertical-align: top;
588593
@ text-align: right;
589594
@ padding: 0.2ex 2ex;
590595
@ }');
591
-@ REPLACE INTO config VALUES('header','<html>
596
+@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
592597
@ <head>
593598
@ <title>$<project_name>: $<title></title>
594599
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
595600
@ href="$home/timeline.rss">
596601
@ <link rel="stylesheet" href="$home/style.css?black2" type="text/css"
@@ -640,11 +645,11 @@
640645
@ html "<li><a href=''$home/login''>Login</a></li>"
641646
@ }
642647
@ </th1></ul></div>
643648
@ <div id="container">
644649
@ ');
645
-@ REPLACE INTO config VALUES('footer','</div>
650
+@ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div>
646651
@ <div class="footer">
647652
@ Fossil version $manifest_version $manifest_date
648653
@ </div>
649654
@ </body></html>
650655
@ ');
@@ -653,11 +658,12 @@
653658
654659
/*
655660
** Gradients and rounded corners.
656661
*/
657662
static const char zBuiltinSkin4[] =
658
-@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
663
+@ REPLACE INTO config(name,mtime,value)
664
+@ VALUES('css',now(),'/* General settings for the entire page */
659665
@ html {
660666
@ min-height: 100%;
661667
@ }
662668
@ body {
663669
@ margin: 0ex 1ex;
@@ -880,11 +886,11 @@
880886
@ }
881887
@
882888
@ textarea {
883889
@ font-size: 1em;
884890
@ }');
885
-@ REPLACE INTO config VALUES('header','<html>
891
+@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
886892
@ <head>
887893
@ <title>$<project_name>: $<title></title>
888894
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
889895
@ href="$home/timeline.rss">
890896
@ <link rel="stylesheet" href="$home/style.css?black2" type="text/css"
@@ -934,11 +940,11 @@
934940
@ html "<a href=''$home/login''>Login</a>"
935941
@ }
936942
@ </th1></ul></div>
937943
@ <div id="container">
938944
@ ');
939
-@ REPLACE INTO config VALUES('footer','</div>
945
+@ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div>
940946
@ <div class="footer">
941947
@ Fossil version $manifest_version $manifest_date
942948
@ </div>
943949
@ </body></html>
944950
@ ');
@@ -984,17 +990,20 @@
984990
** Memory to hold the returned string is obtained from malloc.
985991
*/
986992
static char *getSkin(int useDefault){
987993
Blob val;
988994
blob_zero(&val);
989
- blob_appendf(&val, "REPLACE INTO config VALUES('css',%Q);\n",
995
+ blob_appendf(&val,
996
+ "REPLACE INTO config(name,value,mtime) VALUES('css',%Q,now());\n",
990997
useDefault ? zDefaultCSS : db_get("css", (char*)zDefaultCSS)
991998
);
992
- blob_appendf(&val, "REPLACE INTO config VALUES('header',%Q);\n",
999
+ blob_appendf(&val,
1000
+ "REPLACE INTO config(name,value,mtime) VALUES('header',%Q,now());\n",
9931001
useDefault ? zDefaultHeader : db_get("header", (char*)zDefaultHeader)
9941002
);
995
- blob_appendf(&val, "REPLACE INTO config VALUES('footer',%Q);\n",
1003
+ blob_appendf(&val,
1004
+ "REPLACE INTO config(name,value,mtime) VALUES('footer',%Q,now());\n",
9961005
useDefault ? zDefaultFooter : db_get("footer", (char*)zDefaultFooter)
9971006
);
9981007
return blob_str(&val);
9991008
}
10001009
@@ -1048,11 +1057,11 @@
10481057
if( db_exists("SELECT 1 FROM config WHERE name=%Q", zName)
10491058
|| strcmp(zName, "Default")==0 ){
10501059
zErr = mprintf("Skin name \"%h\" already exists. "
10511060
"Choose a different name.", P("sn"));
10521061
}else{
1053
- db_multi_exec("INSERT INTO config VALUES(%Q,%Q)",
1062
+ db_multi_exec("INSERT INTO config(name,value,mtime) VALUES(%Q,%Q,now())",
10541063
zName, zCurrent
10551064
);
10561065
}
10571066
}
10581067
@@ -1069,13 +1078,13 @@
10691078
seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'"
10701079
" AND value=%Q", zCurrent);
10711080
}
10721081
if( !seen ){
10731082
db_multi_exec(
1074
- "INSERT INTO config VALUES("
1083
+ "INSERT INTO config(name,value,mtime) VALUES("
10751084
" strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S'),"
1076
- " %Q)", zCurrent
1085
+ " %Q,now())", zCurrent
10771086
);
10781087
}
10791088
seen = 0;
10801089
for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){
10811090
if( strcmp(aBuiltinSkin[i].zName, z)==0 ){
10821091
--- src/skins.c
+++ src/skins.c
@@ -25,11 +25,12 @@
25 /*
26 ** A black-and-white theme with the project title in a bar across the top
27 ** and no logo image.
28 */
29 static const char zBuiltinSkin1[] =
30 @ REPLACE INTO config VALUES('css','/* General settings for the entire page */
 
31 @ body {
32 @ margin: 0ex 1ex;
33 @ padding: 0px;
34 @ background-color: white;
35 @ font-family: sans-serif;
@@ -152,11 +153,11 @@
152 @ table.label-value th {
153 @ vertical-align: top;
154 @ text-align: right;
155 @ padding: 0.2ex 2ex;
156 @ }');
157 @ REPLACE INTO config VALUES('header','<html>
158 @ <head>
159 @ <title>$<project_name>: $<title></title>
160 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
161 @ href="$home/timeline.rss">
162 @ <link rel="stylesheet" href="$home/style.css?blackwhite" type="text/css"
@@ -204,11 +205,12 @@
204 @ } else {
205 @ html "<a href=''$home/login''>Login</a> "
206 @ }
207 @ </th1></div>
208 @ ');
209 @ REPLACE INTO config VALUES('footer','<div class="footer">
 
210 @ Fossil version $manifest_version $manifest_date
211 @ </div>
212 @ </body></html>
213 @ ');
214 ;
@@ -216,11 +218,12 @@
216 /*
217 ** A tan theme with the project title above the user identification
218 ** and no logo image.
219 */
220 static const char zBuiltinSkin2[] =
221 @ REPLACE INTO config VALUES('css','/* General settings for the entire page */
 
222 @ body {
223 @ margin: 0ex 0ex;
224 @ padding: 0px;
225 @ background-color: #fef3bc;
226 @ font-family: sans-serif;
@@ -354,11 +357,11 @@
354 @ vertical-align: top;
355 @ text-align: right;
356 @ padding: 0.2ex 2ex;
357 @ }
358 @ ');
359 @ REPLACE INTO config VALUES('header','<html>
360 @ <head>
361 @ <title>$<project_name>: $<title></title>
362 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
363 @ href="$home/timeline.rss">
364 @ <link rel="stylesheet" href="$home/style.css?tan" type="text/css"
@@ -405,11 +408,12 @@
405 @ } else {
406 @ html "<a href=''$home/login''>Login</a> "
407 @ }
408 @ </th1></div>
409 @ ');
410 @ REPLACE INTO config VALUES('footer','<div class="footer">
 
411 @ Fossil version $manifest_version $manifest_date
412 @ </div>
413 @ </body></html>
414 @ ');
415 ;
@@ -417,11 +421,12 @@
417 /*
418 ** Black letters on a white or cream background with the main menu
419 ** stuck on the left-hand side.
420 */
421 static const char zBuiltinSkin3[] =
422 @ REPLACE INTO config VALUES('css','/* General settings for the entire page */
 
423 @ body {
424 @ margin:0px 0px 0px 0px;
425 @ padding:0px;
426 @ font-family:verdana, arial, helvetica, "sans serif";
427 @ color:#333;
@@ -586,11 +591,11 @@
586 @ table.label-value th {
587 @ vertical-align: top;
588 @ text-align: right;
589 @ padding: 0.2ex 2ex;
590 @ }');
591 @ REPLACE INTO config VALUES('header','<html>
592 @ <head>
593 @ <title>$<project_name>: $<title></title>
594 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
595 @ href="$home/timeline.rss">
596 @ <link rel="stylesheet" href="$home/style.css?black2" type="text/css"
@@ -640,11 +645,11 @@
640 @ html "<li><a href=''$home/login''>Login</a></li>"
641 @ }
642 @ </th1></ul></div>
643 @ <div id="container">
644 @ ');
645 @ REPLACE INTO config VALUES('footer','</div>
646 @ <div class="footer">
647 @ Fossil version $manifest_version $manifest_date
648 @ </div>
649 @ </body></html>
650 @ ');
@@ -653,11 +658,12 @@
653
654 /*
655 ** Gradients and rounded corners.
656 */
657 static const char zBuiltinSkin4[] =
658 @ REPLACE INTO config VALUES('css','/* General settings for the entire page */
 
659 @ html {
660 @ min-height: 100%;
661 @ }
662 @ body {
663 @ margin: 0ex 1ex;
@@ -880,11 +886,11 @@
880 @ }
881 @
882 @ textarea {
883 @ font-size: 1em;
884 @ }');
885 @ REPLACE INTO config VALUES('header','<html>
886 @ <head>
887 @ <title>$<project_name>: $<title></title>
888 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
889 @ href="$home/timeline.rss">
890 @ <link rel="stylesheet" href="$home/style.css?black2" type="text/css"
@@ -934,11 +940,11 @@
934 @ html "<a href=''$home/login''>Login</a>"
935 @ }
936 @ </th1></ul></div>
937 @ <div id="container">
938 @ ');
939 @ REPLACE INTO config VALUES('footer','</div>
940 @ <div class="footer">
941 @ Fossil version $manifest_version $manifest_date
942 @ </div>
943 @ </body></html>
944 @ ');
@@ -984,17 +990,20 @@
984 ** Memory to hold the returned string is obtained from malloc.
985 */
986 static char *getSkin(int useDefault){
987 Blob val;
988 blob_zero(&val);
989 blob_appendf(&val, "REPLACE INTO config VALUES('css',%Q);\n",
 
990 useDefault ? zDefaultCSS : db_get("css", (char*)zDefaultCSS)
991 );
992 blob_appendf(&val, "REPLACE INTO config VALUES('header',%Q);\n",
 
993 useDefault ? zDefaultHeader : db_get("header", (char*)zDefaultHeader)
994 );
995 blob_appendf(&val, "REPLACE INTO config VALUES('footer',%Q);\n",
 
996 useDefault ? zDefaultFooter : db_get("footer", (char*)zDefaultFooter)
997 );
998 return blob_str(&val);
999 }
1000
@@ -1048,11 +1057,11 @@
1048 if( db_exists("SELECT 1 FROM config WHERE name=%Q", zName)
1049 || strcmp(zName, "Default")==0 ){
1050 zErr = mprintf("Skin name \"%h\" already exists. "
1051 "Choose a different name.", P("sn"));
1052 }else{
1053 db_multi_exec("INSERT INTO config VALUES(%Q,%Q)",
1054 zName, zCurrent
1055 );
1056 }
1057 }
1058
@@ -1069,13 +1078,13 @@
1069 seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'"
1070 " AND value=%Q", zCurrent);
1071 }
1072 if( !seen ){
1073 db_multi_exec(
1074 "INSERT INTO config VALUES("
1075 " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S'),"
1076 " %Q)", zCurrent
1077 );
1078 }
1079 seen = 0;
1080 for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){
1081 if( strcmp(aBuiltinSkin[i].zName, z)==0 ){
1082
--- src/skins.c
+++ src/skins.c
@@ -25,11 +25,12 @@
25 /*
26 ** A black-and-white theme with the project title in a bar across the top
27 ** and no logo image.
28 */
29 static const char zBuiltinSkin1[] =
30 @ REPLACE INTO config(name,mtime,value)
31 @ VALUES('css',now(),'/* General settings for the entire page */
32 @ body {
33 @ margin: 0ex 1ex;
34 @ padding: 0px;
35 @ background-color: white;
36 @ font-family: sans-serif;
@@ -152,11 +153,11 @@
153 @ table.label-value th {
154 @ vertical-align: top;
155 @ text-align: right;
156 @ padding: 0.2ex 2ex;
157 @ }');
158 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
159 @ <head>
160 @ <title>$<project_name>: $<title></title>
161 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
162 @ href="$home/timeline.rss">
163 @ <link rel="stylesheet" href="$home/style.css?blackwhite" type="text/css"
@@ -204,11 +205,12 @@
205 @ } else {
206 @ html "<a href=''$home/login''>Login</a> "
207 @ }
208 @ </th1></div>
209 @ ');
210 @ REPLACE INTO config(name,mtime,value)
211 @ VALUES('footer',now(),'<div class="footer">
212 @ Fossil version $manifest_version $manifest_date
213 @ </div>
214 @ </body></html>
215 @ ');
216 ;
@@ -216,11 +218,12 @@
218 /*
219 ** A tan theme with the project title above the user identification
220 ** and no logo image.
221 */
222 static const char zBuiltinSkin2[] =
223 @ REPLACE INTO config(name,mtime,value)
224 @ VALUES('css',now(),'/* General settings for the entire page */
225 @ body {
226 @ margin: 0ex 0ex;
227 @ padding: 0px;
228 @ background-color: #fef3bc;
229 @ font-family: sans-serif;
@@ -354,11 +357,11 @@
357 @ vertical-align: top;
358 @ text-align: right;
359 @ padding: 0.2ex 2ex;
360 @ }
361 @ ');
362 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
363 @ <head>
364 @ <title>$<project_name>: $<title></title>
365 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
366 @ href="$home/timeline.rss">
367 @ <link rel="stylesheet" href="$home/style.css?tan" type="text/css"
@@ -405,11 +408,12 @@
408 @ } else {
409 @ html "<a href=''$home/login''>Login</a> "
410 @ }
411 @ </th1></div>
412 @ ');
413 @ REPLACE INTO config(name,mtime,value)
414 @ VALUES('footer',now(),'<div class="footer">
415 @ Fossil version $manifest_version $manifest_date
416 @ </div>
417 @ </body></html>
418 @ ');
419 ;
@@ -417,11 +421,12 @@
421 /*
422 ** Black letters on a white or cream background with the main menu
423 ** stuck on the left-hand side.
424 */
425 static const char zBuiltinSkin3[] =
426 @ REPLACE INTO config(name,mtime,value)
427 @ VALUES('css',now(),'/* General settings for the entire page */
428 @ body {
429 @ margin:0px 0px 0px 0px;
430 @ padding:0px;
431 @ font-family:verdana, arial, helvetica, "sans serif";
432 @ color:#333;
@@ -586,11 +591,11 @@
591 @ table.label-value th {
592 @ vertical-align: top;
593 @ text-align: right;
594 @ padding: 0.2ex 2ex;
595 @ }');
596 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
597 @ <head>
598 @ <title>$<project_name>: $<title></title>
599 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
600 @ href="$home/timeline.rss">
601 @ <link rel="stylesheet" href="$home/style.css?black2" type="text/css"
@@ -640,11 +645,11 @@
645 @ html "<li><a href=''$home/login''>Login</a></li>"
646 @ }
647 @ </th1></ul></div>
648 @ <div id="container">
649 @ ');
650 @ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div>
651 @ <div class="footer">
652 @ Fossil version $manifest_version $manifest_date
653 @ </div>
654 @ </body></html>
655 @ ');
@@ -653,11 +658,12 @@
658
659 /*
660 ** Gradients and rounded corners.
661 */
662 static const char zBuiltinSkin4[] =
663 @ REPLACE INTO config(name,mtime,value)
664 @ VALUES('css',now(),'/* General settings for the entire page */
665 @ html {
666 @ min-height: 100%;
667 @ }
668 @ body {
669 @ margin: 0ex 1ex;
@@ -880,11 +886,11 @@
886 @ }
887 @
888 @ textarea {
889 @ font-size: 1em;
890 @ }');
891 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
892 @ <head>
893 @ <title>$<project_name>: $<title></title>
894 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
895 @ href="$home/timeline.rss">
896 @ <link rel="stylesheet" href="$home/style.css?black2" type="text/css"
@@ -934,11 +940,11 @@
940 @ html "<a href=''$home/login''>Login</a>"
941 @ }
942 @ </th1></ul></div>
943 @ <div id="container">
944 @ ');
945 @ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div>
946 @ <div class="footer">
947 @ Fossil version $manifest_version $manifest_date
948 @ </div>
949 @ </body></html>
950 @ ');
@@ -984,17 +990,20 @@
990 ** Memory to hold the returned string is obtained from malloc.
991 */
992 static char *getSkin(int useDefault){
993 Blob val;
994 blob_zero(&val);
995 blob_appendf(&val,
996 "REPLACE INTO config(name,value,mtime) VALUES('css',%Q,now());\n",
997 useDefault ? zDefaultCSS : db_get("css", (char*)zDefaultCSS)
998 );
999 blob_appendf(&val,
1000 "REPLACE INTO config(name,value,mtime) VALUES('header',%Q,now());\n",
1001 useDefault ? zDefaultHeader : db_get("header", (char*)zDefaultHeader)
1002 );
1003 blob_appendf(&val,
1004 "REPLACE INTO config(name,value,mtime) VALUES('footer',%Q,now());\n",
1005 useDefault ? zDefaultFooter : db_get("footer", (char*)zDefaultFooter)
1006 );
1007 return blob_str(&val);
1008 }
1009
@@ -1048,11 +1057,11 @@
1057 if( db_exists("SELECT 1 FROM config WHERE name=%Q", zName)
1058 || strcmp(zName, "Default")==0 ){
1059 zErr = mprintf("Skin name \"%h\" already exists. "
1060 "Choose a different name.", P("sn"));
1061 }else{
1062 db_multi_exec("INSERT INTO config(name,value,mtime) VALUES(%Q,%Q,now())",
1063 zName, zCurrent
1064 );
1065 }
1066 }
1067
@@ -1069,13 +1078,13 @@
1078 seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'"
1079 " AND value=%Q", zCurrent);
1080 }
1081 if( !seen ){
1082 db_multi_exec(
1083 "INSERT INTO config(name,value,mtime) VALUES("
1084 " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S'),"
1085 " %Q,now())", zCurrent
1086 );
1087 }
1088 seen = 0;
1089 for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){
1090 if( strcmp(aBuiltinSkin[i].zName, z)==0 ){
1091

Keyboard Shortcuts

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