Fossil SCM

Add the ability to specify a description for each ticket report format. The user and reportfmt tables are updated with a new jx column containing JSON that describes the new features. (The user.jx table is currently not used but it was convenient to add it at the same time.)

drh 2022-11-18 19:28 trunk merge
Commit fcf17b28a902c01bccc41f044381073b0d4fbed2652e07adc897ff066252efcc
+56 -28
--- src/configure.c
+++ src/configure.c
@@ -357,11 +357,11 @@
357357
** NAME CONTENT
358358
** ------- -----------------------------------------------------------
359359
** /config $MTIME $NAME value $VALUE
360360
** /user $MTIME $LOGIN pw $VALUE cap $VALUE info $VALUE photo $VALUE
361361
** /shun $MTIME $UUID scom $VALUE
362
-** /reportfmt $MTIME $TITLE owner $VALUE cols $VALUE sqlcode $VALUE
362
+** /reportfmt $MTIME $TITLE owner $VALUE cols $VALUE sqlcode $VALUE jx $JSON
363363
** /concealed $MTIME $HASH content $VALUE
364364
** /subscriber $SMTIME $SEMAIL suname $V ...
365365
*/
366366
void configure_receive(const char *zName, Blob *pContent, int groupMask){
367367
int checkMask; /* Masks for which we must first check existance of tables */
@@ -378,17 +378,17 @@
378378
const char *zName; /* Configuration key for this table */
379379
const char *zPrimKey; /* Primary key column */
380380
int nField; /* Number of data fields */
381381
const char *azField[6]; /* Names of the data fields */
382382
} aType[] = {
383
- { "/config", "name", 1, { "value", 0,0,0,0,0 } },
384
- { "@user", "login", 4, { "pw","cap","info","photo",0,0} },
385
- { "@shun", "uuid", 1, { "scom", 0,0,0,0,0} },
386
- { "@reportfmt", "title", 3, { "owner","cols","sqlcode",0,0,0}},
387
- { "@concealed", "hash", 1, { "content", 0,0,0,0,0 } },
383
+ { "/config", "name", 1, { "value", 0,0,0,0,0 } },
384
+ { "@user", "login", 5, { "pw","cap","info","photo","jx",0} },
385
+ { "@shun", "uuid", 1, { "scom", 0,0,0,0,0} },
386
+ { "@reportfmt", "title", 4, { "owner","cols","sqlcode","jx",0,0}},
387
+ { "@concealed", "hash", 1, { "content", 0,0,0,0,0 } },
388388
{ "@subscriber","semail",6,
389
- { "suname","sdigest","sdonotcall","ssub","sctime","smip"} },
389
+ { "suname","sdigest","sdonotcall","ssub","sctime","smip"} },
390390
};
391391
392392
/* Locate the receiveType in aType[ii] */
393393
for(ii=0; ii<count(aType); ii++){
394394
if( fossil_strcmp(&aType[ii].zName[1],&zName[1])==0 ) break;
@@ -445,11 +445,20 @@
445445
azToken[1] /*safe-for-%s*/, azToken[0]/*safe-for-%s*/);
446446
for(jj=2; jj<nToken; jj+=2){
447447
blob_append_sql(&sql, ",%s", azToken[jj+1] /*safe-for-%s*/);
448448
}
449449
db_protect_only(PROTECT_SENSITIVE);
450
+
451
+ /* Make sure tables have the "jx" column */
452
+ if( strcmp(&zName[1],"user")==0 ){
453
+ user_update_user_table();
454
+ }else if( strcmp(&zName[1],"reportfmt")==0 ){
455
+ report_update_reportfmt_table();
456
+ }
457
+
450458
db_multi_exec("%s)", blob_sql_text(&sql));
459
+
451460
if( db_changes()==0 ){
452461
blob_reset(&sql);
453462
blob_append_sql(&sql, "UPDATE \"%w\" SET mtime=%s",
454463
&zName[1], azToken[0]/*safe-for-%s*/);
455464
for(jj=2; jj<nToken; jj+=2){
@@ -532,41 +541,60 @@
532541
blob_reset(&rec);
533542
}
534543
db_finalize(&q);
535544
}
536545
if( groupMask & CONFIGSET_USER ){
537
- db_prepare(&q, "SELECT mtime, quote(login), quote(pw), quote(cap),"
538
- " quote(info), quote(photo) FROM user"
539
- " WHERE mtime>=%lld", iStart);
546
+ if( db_table_has_column("repository","user","jx") ){
547
+ db_prepare(&q, "SELECT mtime, quote(login), quote(pw), quote(cap),"
548
+ " quote(info), quote(photo), quote(jx) FROM user"
549
+ " WHERE mtime>=%lld", iStart);
550
+ }else{
551
+ db_prepare(&q, "SELECT mtime, quote(login), quote(pw), quote(cap),"
552
+ " quote(info), quote(photo), 'NULL' FROM user"
553
+ " WHERE mtime>=%lld", iStart);
554
+ }
540555
while( db_step(&q)==SQLITE_ROW ){
541
- blob_appendf(&rec,"%s %s pw %s cap %s info %s photo %s",
542
- db_column_text(&q, 0),
543
- db_column_text(&q, 1),
544
- db_column_text(&q, 2),
545
- db_column_text(&q, 3),
546
- db_column_text(&q, 4),
547
- db_column_text(&q, 5)
548
- );
556
+ const char *z;
557
+ blob_appendf(&rec,"%s %s", db_column_text(&q,0), db_column_text(&q,1));
558
+ z = db_column_text(&q,2);
559
+ if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," pw %s", z);
560
+ z = db_column_text(&q,3);
561
+ if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," cap %s", z);
562
+ z = db_column_text(&q,4);
563
+ if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," info %s", z);
564
+ z = db_column_text(&q,5);
565
+ if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," photo %s", z);
566
+ z = db_column_text(&q,6);
567
+ if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," jx %s", z);
549568
blob_appendf(pOut, "config /user %d\n%s\n",
550569
blob_size(&rec), blob_str(&rec));
551570
nCard++;
552571
blob_reset(&rec);
553572
}
554573
db_finalize(&q);
555574
}
556575
if( groupMask & CONFIGSET_TKT ){
557
- db_prepare(&q, "SELECT mtime, quote(title), quote(owner), quote(cols),"
558
- " quote(sqlcode) FROM reportfmt"
559
- " WHERE mtime>=%lld", iStart);
576
+ if( db_table_has_column("repository","reportfmt","jx") ){
577
+ db_prepare(&q, "SELECT mtime, quote(title), quote(owner), quote(cols),"
578
+ " quote(sqlcode), quote(jx) FROM reportfmt"
579
+ " WHERE mtime>=%lld", iStart);
580
+ }else{
581
+ db_prepare(&q, "SELECT mtime, quote(title), quote(owner), quote(cols),"
582
+ " quote(sqlcode), 'NULL' FROM reportfmt"
583
+ " WHERE mtime>=%lld", iStart);
584
+ }
560585
while( db_step(&q)==SQLITE_ROW ){
561
- blob_appendf(&rec,"%s %s owner %s cols %s sqlcode %s",
562
- db_column_text(&q, 0),
563
- db_column_text(&q, 1),
564
- db_column_text(&q, 2),
565
- db_column_text(&q, 3),
566
- db_column_text(&q, 4)
567
- );
586
+ const char *z;
587
+ blob_appendf(&rec,"%s %s", db_column_text(&q,0), db_column_text(&q,1));
588
+ z = db_column_text(&q,2);
589
+ if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," owner %s", z);
590
+ z = db_column_text(&q,3);
591
+ if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," cols %s", z);
592
+ z = db_column_text(&q,4);
593
+ if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," sqlcode %s", z);
594
+ z = db_column_text(&q,5);
595
+ if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," jx %s", z);
568596
blob_appendf(pOut, "config /reportfmt %d\n%s\n",
569597
blob_size(&rec), blob_str(&rec));
570598
nCard++;
571599
blob_reset(&rec);
572600
}
573601
--- src/configure.c
+++ src/configure.c
@@ -357,11 +357,11 @@
357 ** NAME CONTENT
358 ** ------- -----------------------------------------------------------
359 ** /config $MTIME $NAME value $VALUE
360 ** /user $MTIME $LOGIN pw $VALUE cap $VALUE info $VALUE photo $VALUE
361 ** /shun $MTIME $UUID scom $VALUE
362 ** /reportfmt $MTIME $TITLE owner $VALUE cols $VALUE sqlcode $VALUE
363 ** /concealed $MTIME $HASH content $VALUE
364 ** /subscriber $SMTIME $SEMAIL suname $V ...
365 */
366 void configure_receive(const char *zName, Blob *pContent, int groupMask){
367 int checkMask; /* Masks for which we must first check existance of tables */
@@ -378,17 +378,17 @@
378 const char *zName; /* Configuration key for this table */
379 const char *zPrimKey; /* Primary key column */
380 int nField; /* Number of data fields */
381 const char *azField[6]; /* Names of the data fields */
382 } aType[] = {
383 { "/config", "name", 1, { "value", 0,0,0,0,0 } },
384 { "@user", "login", 4, { "pw","cap","info","photo",0,0} },
385 { "@shun", "uuid", 1, { "scom", 0,0,0,0,0} },
386 { "@reportfmt", "title", 3, { "owner","cols","sqlcode",0,0,0}},
387 { "@concealed", "hash", 1, { "content", 0,0,0,0,0 } },
388 { "@subscriber","semail",6,
389 { "suname","sdigest","sdonotcall","ssub","sctime","smip"} },
390 };
391
392 /* Locate the receiveType in aType[ii] */
393 for(ii=0; ii<count(aType); ii++){
394 if( fossil_strcmp(&aType[ii].zName[1],&zName[1])==0 ) break;
@@ -445,11 +445,20 @@
445 azToken[1] /*safe-for-%s*/, azToken[0]/*safe-for-%s*/);
446 for(jj=2; jj<nToken; jj+=2){
447 blob_append_sql(&sql, ",%s", azToken[jj+1] /*safe-for-%s*/);
448 }
449 db_protect_only(PROTECT_SENSITIVE);
 
 
 
 
 
 
 
 
450 db_multi_exec("%s)", blob_sql_text(&sql));
 
451 if( db_changes()==0 ){
452 blob_reset(&sql);
453 blob_append_sql(&sql, "UPDATE \"%w\" SET mtime=%s",
454 &zName[1], azToken[0]/*safe-for-%s*/);
455 for(jj=2; jj<nToken; jj+=2){
@@ -532,41 +541,60 @@
532 blob_reset(&rec);
533 }
534 db_finalize(&q);
535 }
536 if( groupMask & CONFIGSET_USER ){
537 db_prepare(&q, "SELECT mtime, quote(login), quote(pw), quote(cap),"
538 " quote(info), quote(photo) FROM user"
539 " WHERE mtime>=%lld", iStart);
 
 
 
 
 
 
540 while( db_step(&q)==SQLITE_ROW ){
541 blob_appendf(&rec,"%s %s pw %s cap %s info %s photo %s",
542 db_column_text(&q, 0),
543 db_column_text(&q, 1),
544 db_column_text(&q, 2),
545 db_column_text(&q, 3),
546 db_column_text(&q, 4),
547 db_column_text(&q, 5)
548 );
 
 
 
 
549 blob_appendf(pOut, "config /user %d\n%s\n",
550 blob_size(&rec), blob_str(&rec));
551 nCard++;
552 blob_reset(&rec);
553 }
554 db_finalize(&q);
555 }
556 if( groupMask & CONFIGSET_TKT ){
557 db_prepare(&q, "SELECT mtime, quote(title), quote(owner), quote(cols),"
558 " quote(sqlcode) FROM reportfmt"
559 " WHERE mtime>=%lld", iStart);
 
 
 
 
 
 
560 while( db_step(&q)==SQLITE_ROW ){
561 blob_appendf(&rec,"%s %s owner %s cols %s sqlcode %s",
562 db_column_text(&q, 0),
563 db_column_text(&q, 1),
564 db_column_text(&q, 2),
565 db_column_text(&q, 3),
566 db_column_text(&q, 4)
567 );
 
 
 
568 blob_appendf(pOut, "config /reportfmt %d\n%s\n",
569 blob_size(&rec), blob_str(&rec));
570 nCard++;
571 blob_reset(&rec);
572 }
573
--- src/configure.c
+++ src/configure.c
@@ -357,11 +357,11 @@
357 ** NAME CONTENT
358 ** ------- -----------------------------------------------------------
359 ** /config $MTIME $NAME value $VALUE
360 ** /user $MTIME $LOGIN pw $VALUE cap $VALUE info $VALUE photo $VALUE
361 ** /shun $MTIME $UUID scom $VALUE
362 ** /reportfmt $MTIME $TITLE owner $VALUE cols $VALUE sqlcode $VALUE jx $JSON
363 ** /concealed $MTIME $HASH content $VALUE
364 ** /subscriber $SMTIME $SEMAIL suname $V ...
365 */
366 void configure_receive(const char *zName, Blob *pContent, int groupMask){
367 int checkMask; /* Masks for which we must first check existance of tables */
@@ -378,17 +378,17 @@
378 const char *zName; /* Configuration key for this table */
379 const char *zPrimKey; /* Primary key column */
380 int nField; /* Number of data fields */
381 const char *azField[6]; /* Names of the data fields */
382 } aType[] = {
383 { "/config", "name", 1, { "value", 0,0,0,0,0 } },
384 { "@user", "login", 5, { "pw","cap","info","photo","jx",0} },
385 { "@shun", "uuid", 1, { "scom", 0,0,0,0,0} },
386 { "@reportfmt", "title", 4, { "owner","cols","sqlcode","jx",0,0}},
387 { "@concealed", "hash", 1, { "content", 0,0,0,0,0 } },
388 { "@subscriber","semail",6,
389 { "suname","sdigest","sdonotcall","ssub","sctime","smip"} },
390 };
391
392 /* Locate the receiveType in aType[ii] */
393 for(ii=0; ii<count(aType); ii++){
394 if( fossil_strcmp(&aType[ii].zName[1],&zName[1])==0 ) break;
@@ -445,11 +445,20 @@
445 azToken[1] /*safe-for-%s*/, azToken[0]/*safe-for-%s*/);
446 for(jj=2; jj<nToken; jj+=2){
447 blob_append_sql(&sql, ",%s", azToken[jj+1] /*safe-for-%s*/);
448 }
449 db_protect_only(PROTECT_SENSITIVE);
450
451 /* Make sure tables have the "jx" column */
452 if( strcmp(&zName[1],"user")==0 ){
453 user_update_user_table();
454 }else if( strcmp(&zName[1],"reportfmt")==0 ){
455 report_update_reportfmt_table();
456 }
457
458 db_multi_exec("%s)", blob_sql_text(&sql));
459
460 if( db_changes()==0 ){
461 blob_reset(&sql);
462 blob_append_sql(&sql, "UPDATE \"%w\" SET mtime=%s",
463 &zName[1], azToken[0]/*safe-for-%s*/);
464 for(jj=2; jj<nToken; jj+=2){
@@ -532,41 +541,60 @@
541 blob_reset(&rec);
542 }
543 db_finalize(&q);
544 }
545 if( groupMask & CONFIGSET_USER ){
546 if( db_table_has_column("repository","user","jx") ){
547 db_prepare(&q, "SELECT mtime, quote(login), quote(pw), quote(cap),"
548 " quote(info), quote(photo), quote(jx) FROM user"
549 " WHERE mtime>=%lld", iStart);
550 }else{
551 db_prepare(&q, "SELECT mtime, quote(login), quote(pw), quote(cap),"
552 " quote(info), quote(photo), 'NULL' FROM user"
553 " WHERE mtime>=%lld", iStart);
554 }
555 while( db_step(&q)==SQLITE_ROW ){
556 const char *z;
557 blob_appendf(&rec,"%s %s", db_column_text(&q,0), db_column_text(&q,1));
558 z = db_column_text(&q,2);
559 if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," pw %s", z);
560 z = db_column_text(&q,3);
561 if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," cap %s", z);
562 z = db_column_text(&q,4);
563 if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," info %s", z);
564 z = db_column_text(&q,5);
565 if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," photo %s", z);
566 z = db_column_text(&q,6);
567 if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," jx %s", z);
568 blob_appendf(pOut, "config /user %d\n%s\n",
569 blob_size(&rec), blob_str(&rec));
570 nCard++;
571 blob_reset(&rec);
572 }
573 db_finalize(&q);
574 }
575 if( groupMask & CONFIGSET_TKT ){
576 if( db_table_has_column("repository","reportfmt","jx") ){
577 db_prepare(&q, "SELECT mtime, quote(title), quote(owner), quote(cols),"
578 " quote(sqlcode), quote(jx) FROM reportfmt"
579 " WHERE mtime>=%lld", iStart);
580 }else{
581 db_prepare(&q, "SELECT mtime, quote(title), quote(owner), quote(cols),"
582 " quote(sqlcode), 'NULL' FROM reportfmt"
583 " WHERE mtime>=%lld", iStart);
584 }
585 while( db_step(&q)==SQLITE_ROW ){
586 const char *z;
587 blob_appendf(&rec,"%s %s", db_column_text(&q,0), db_column_text(&q,1));
588 z = db_column_text(&q,2);
589 if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," owner %s", z);
590 z = db_column_text(&q,3);
591 if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," cols %s", z);
592 z = db_column_text(&q,4);
593 if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," sqlcode %s", z);
594 z = db_column_text(&q,5);
595 if( strcmp(z,"NULL")!=0 ) blob_appendf(&rec," jx %s", z);
596 blob_appendf(pOut, "config /reportfmt %d\n%s\n",
597 blob_size(&rec), blob_str(&rec));
598 nCard++;
599 blob_reset(&rec);
600 }
601
+1 -1
--- src/event.c
+++ src/event.c
@@ -552,11 +552,11 @@
552552
@ </td></tr>
553553
554554
@ <tr><th align="right" valign="top">\
555555
@ %z(href("%R/markup_help"))Markup Style</a>:</th>
556556
@ <td valign="top">
557
- mimetype_option_menu(zMimetype);
557
+ mimetype_option_menu(zMimetype, "mimetype");
558558
@ </td></tr>
559559
560560
@ <tr><th align="right" valign="top">Page&nbsp;Content:</th>
561561
@ <td valign="top">
562562
@ <textarea name="w" class="technoteedit" cols="80"
563563
--- src/event.c
+++ src/event.c
@@ -552,11 +552,11 @@
552 @ </td></tr>
553
554 @ <tr><th align="right" valign="top">\
555 @ %z(href("%R/markup_help"))Markup Style</a>:</th>
556 @ <td valign="top">
557 mimetype_option_menu(zMimetype);
558 @ </td></tr>
559
560 @ <tr><th align="right" valign="top">Page&nbsp;Content:</th>
561 @ <td valign="top">
562 @ <textarea name="w" class="technoteedit" cols="80"
563
--- src/event.c
+++ src/event.c
@@ -552,11 +552,11 @@
552 @ </td></tr>
553
554 @ <tr><th align="right" valign="top">\
555 @ %z(href("%R/markup_help"))Markup Style</a>:</th>
556 @ <td valign="top">
557 mimetype_option_menu(zMimetype, "mimetype");
558 @ </td></tr>
559
560 @ <tr><th align="right" valign="top">Page&nbsp;Content:</th>
561 @ <td valign="top">
562 @ <textarea name="w" class="technoteedit" cols="80"
563
+1 -1
--- src/forum.c
+++ src/forum.c
@@ -1059,11 +1059,11 @@
10591059
if( zTitle ){
10601060
@ Title: <input type="input" name="title" value="%h(zTitle)" size="50"
10611061
@ maxlength="125"><br>
10621062
}
10631063
@ %z(href("%R/markup_help"))Markup style</a>:
1064
- mimetype_option_menu(zMimetype);
1064
+ mimetype_option_menu(zMimetype, "mimetype");
10651065
@ <br><textarea aria-label="Content:" name="content" class="wikiedit" \
10661066
@ cols="80" rows="25" wrap="virtual">%h(zContent)</textarea><br>
10671067
}
10681068
10691069
/*
10701070
--- src/forum.c
+++ src/forum.c
@@ -1059,11 +1059,11 @@
1059 if( zTitle ){
1060 @ Title: <input type="input" name="title" value="%h(zTitle)" size="50"
1061 @ maxlength="125"><br>
1062 }
1063 @ %z(href("%R/markup_help"))Markup style</a>:
1064 mimetype_option_menu(zMimetype);
1065 @ <br><textarea aria-label="Content:" name="content" class="wikiedit" \
1066 @ cols="80" rows="25" wrap="virtual">%h(zContent)</textarea><br>
1067 }
1068
1069 /*
1070
--- src/forum.c
+++ src/forum.c
@@ -1059,11 +1059,11 @@
1059 if( zTitle ){
1060 @ Title: <input type="input" name="title" value="%h(zTitle)" size="50"
1061 @ maxlength="125"><br>
1062 }
1063 @ %z(href("%R/markup_help"))Markup style</a>:
1064 mimetype_option_menu(zMimetype, "mimetype");
1065 @ <br><textarea aria-label="Content:" name="content" class="wikiedit" \
1066 @ cols="80" rows="25" wrap="virtual">%h(zContent)</textarea><br>
1067 }
1068
1069 /*
1070
+145 -32
--- src/report.c
+++ src/report.c
@@ -75,29 +75,29 @@
7575
cnt++;
7676
blob_appendf(&ril, "<li>");
7777
if( zTitle[0] == '_' ){
7878
blob_appendf(&ril, "%s", zTitle);
7979
} else {
80
- blob_appendf(&ril, "%z%h</a>", href("%R/rptview?rn=%d", rn), zTitle);
80
+ blob_appendf(&ril, "%z%h</a>", href("%R/rptview/%d", rn), zTitle);
8181
}
8282
blob_appendf(&ril, "&nbsp;&nbsp;&nbsp;");
8383
if( g.perm.Write && zOwner && zOwner[0] ){
8484
blob_appendf(&ril, "(by <i>%h</i>) ", zOwner);
8585
}
8686
if( g.perm.TktFmt ){
8787
blob_appendf(&ril, "[%zcopy</a>] ",
88
- href("%R/rptedit?rn=%d&copy=1", rn));
88
+ href("%R/rptedit/%d?copy=1", rn));
8989
}
9090
if( g.perm.Admin
9191
|| (g.perm.WrTkt && zOwner && fossil_strcmp(g.zLogin,zOwner)==0)
9292
){
9393
blob_appendf(&ril, "[%zedit</a>]",
94
- href("%R/rptedit?rn=%d", rn));
94
+ href("%R/rptedit/%d", rn));
9595
}
9696
if( g.perm.TktFmt ){
9797
blob_appendf(&ril, "[%zsql</a>]",
98
- href("%R/rptsql?rn=%d", rn));
98
+ href("%R/rptsql/%d", rn));
9999
}
100100
if( fossil_strcmp(zTitle, defaultReport)==0 ){
101101
blob_appendf(&ril, "&nbsp;← default");
102102
}
103103
blob_appendf(&ril, "</li>\n");
@@ -253,10 +253,24 @@
253253
break;
254254
}
255255
}
256256
return rc;
257257
}
258
+
259
+/*
260
+** Make sure the reportfmt table is up-to-date. It should contain
261
+** the "jx" column (as of version 2.21). If it does not, add it.
262
+**
263
+** The "jx" column is intended to hold a JSON object containing optional
264
+** key-value pairs.
265
+*/
266
+void report_update_reportfmt_table(void){
267
+ if( db_table_has_column("repository","reportfmt","jx")==0 ){
268
+ db_multi_exec("ALTER TABLE repository.reportfmt"
269
+ " ADD COLUMN jx TEXT DEFAULT '{}';");
270
+ }
271
+}
258272
259273
/*
260274
** Activate the ticket report query authorizer. Must be followed by an
261275
** eventual call to report_unrestrict_sql().
262276
*/
@@ -321,17 +335,53 @@
321335
sqlite3_finalize(pStmt);
322336
}
323337
report_unrestrict_sql();
324338
return zErr;
325339
}
340
+
341
+/*
342
+** Get a report number from query parameters. This can be done in various
343
+** ways:
344
+**
345
+** (1) (legacy) rn=NNN where NNN is the reportfmt.rn integer primary key.
346
+**
347
+** (2) name=NNN where NNN is the rn.
348
+**
349
+** (3) name=TAG where TAG matches reportfmt.jx->>tag
350
+**
351
+** Regardless of how the report is specified, return the primary key, rn.
352
+** Return 0 if not found.
353
+*/
354
+static int report_number(void){
355
+ int rn;
356
+ const char *zName;
357
+ char *zEnd;
358
+
359
+ /* Case (1) */
360
+ rn = atoi(PD("rn","0"));
361
+ if( rn>0 ) return rn;
362
+
363
+ zName = P("name");
364
+ if( zName==0 || zName[0]==0 ) return 0;
365
+ if( fossil_isdigit(zName[0])
366
+ && (rn = strtol(zName, &zEnd, 10))>0
367
+ && zEnd[0]==0
368
+ ){
369
+ /* Case 2 */
370
+ return rn;
371
+ }
372
+
373
+ rn = db_int(0, "SELECT rn FROM reportfmt WHERE jx->>'tag'==%Q", zName);
374
+ return rn;
375
+}
326376
327377
/*
328378
** WEBPAGE: rptsql
329
-** URL: /rptsql?rn=N
379
+** URL: /rptsql/N
330380
**
331
-** Display the SQL query used to generate a ticket report. The rn=N
332
-** query parameter identifies the specific report number to be displayed.
381
+** Display the SQL query used to generate a ticket report. The N value
382
+** is either the report number of a report tag.
333383
*/
334384
void view_see_sql(void){
335385
int rn;
336386
const char *zTitle;
337387
const char *zSQL;
@@ -342,11 +392,11 @@
342392
login_check_credentials();
343393
if( !g.perm.TktFmt ){
344394
login_needed(g.anon.TktFmt);
345395
return;
346396
}
347
- rn = atoi(PD("rn","0"));
397
+ rn = report_number();
348398
db_prepare(&q, "SELECT title, sqlcode, owner, cols "
349399
"FROM reportfmt WHERE rn=%d",rn);
350400
style_set_current_feature("report");
351401
style_header("SQL For Report Format Number %d", rn);
352402
if( db_step(&q)!=SQLITE_ROW ){
@@ -382,39 +432,51 @@
382432
** WEBPAGE: rptedit
383433
**
384434
** Create (/rptnew) or edit (/rptedit) a ticket report format.
385435
** Query parameters:
386436
**
387
-** rn=N Ticket report number. (required)
437
+** name=N Ticket report number or tag.
438
+** rn=N Ticket report number (legacy).
439
+** ^^^-- one of the two previous is required.
388440
** t=TITLE Title of the report format
389441
** w=USER Owner of the report format
390442
** s=SQL SQL text used to implement the report
391443
** k=KEY Color key
444
+** d=DESC Optional descriptive text
445
+** m=MIMETYPE Mimetype for DESC
446
+** x=TAG Symbolic name for the report
392447
*/
393448
void view_edit(void){
394449
int rn;
395
- const char *zTitle;
450
+ const char *zTitle; /* Title of the report */
396451
const char *z;
397
- const char *zOwner;
398
- const char *zClrKey;
399
- char *zSQL;
400
- char *zErr = 0;
452
+ const char *zOwner; /* Owner of the report */
453
+ const char *zClrKey; /* Color key - used to add colors to lines */
454
+ char *zSQL; /* The SQL text that gnerates the report */
455
+ char *zErr = 0; /* An error message */
456
+ const char *zDesc; /* Extra descriptive text about the report */
457
+ const char *zMimetype; /* Mimetype for zDesc */
458
+ const char *zTag; /* Symbolic name for this report */
401459
int dflt = P("dflt") ? 1 : 0;
402460
403461
login_check_credentials();
404462
if( !g.perm.TktFmt ){
405463
login_needed(g.anon.TktFmt);
406464
return;
407465
}
408466
style_set_current_feature("report");
409467
/*view_add_functions(0);*/
410
- rn = atoi(PD("rn","0"));
468
+ rn = report_number();
411469
zTitle = P("t");
412470
zOwner = PD("w",g.zLogin);
413471
z = P("s");
414472
zSQL = z ? trim_string(z) : 0;
415473
zClrKey = trim_string(PD("k",""));
474
+ zDesc = trim_string(PD("d",""));
475
+ zMimetype = P("m");
476
+ zTag = P("x");
477
+ report_update_reportfmt_table();
416478
if( rn>0 && P("del2") ){
417479
login_verify_csrf_secret();
418480
db_multi_exec("DELETE FROM reportfmt WHERE rn=%d", rn);
419481
cgi_redirect("reportlist");
420482
return;
@@ -456,18 +518,26 @@
456518
){
457519
zErr = mprintf("There is already another report named \"%h\"", zTitle);
458520
}
459521
if( zErr==0 ){
460522
login_verify_csrf_secret();
523
+ if( zTag && zTag[0]==0 ) zTag = 0;
524
+ if( zDesc && zDesc[0]==0 ){ zDesc = 0; zMimetype = 0; }
525
+ if( zMimetype && zMimetype[0]==0 ){ zDesc = 0; zMimetype = 0; }
461526
if( rn>0 ){
462
- db_multi_exec("UPDATE reportfmt SET title=%Q, sqlcode=%Q,"
463
- " owner=%Q, cols=%Q, mtime=now() WHERE rn=%d",
464
- zTitle, zSQL, zOwner, zClrKey, rn);
527
+ db_multi_exec(
528
+ "UPDATE reportfmt SET title=%Q, sqlcode=%Q,"
529
+ " owner=%Q, cols=%Q, mtime=now(), "
530
+ " jx=json_patch(jx,json_object('desc',%Q,'descmt',%Q,'tag',%Q))"
531
+ " WHERE rn=%d",
532
+ zTitle, zSQL, zOwner, zClrKey, zDesc, zMimetype, zTag, rn);
465533
}else{
466
- db_multi_exec("INSERT INTO reportfmt(title,sqlcode,owner,cols,mtime) "
467
- "VALUES(%Q,%Q,%Q,%Q,now())",
468
- zTitle, zSQL, zOwner, zClrKey);
534
+ db_multi_exec(
535
+ "INSERT INTO reportfmt(title,sqlcode,owner,cols,mtime,jx) "
536
+ "VALUES(%Q,%Q,%Q,%Q,now(),"
537
+ "json_object('desc',%Q,'descmt',%Q,'tag',%Q))",
538
+ zTitle, zSQL, zOwner, zClrKey, zDesc, zMimetype, zTag);
469539
rn = db_last_insert_rowid();
470540
}
471541
if( dflt ){
472542
db_set("ticket-default-report", zTitle, 0);
473543
}else{
@@ -474,40 +544,55 @@
474544
char *defaultReport = db_get("ticket-default-report", 0);
475545
if( fossil_strcmp(zTitle, defaultReport)==0 ){
476546
db_set("ticket-default-report", "", 0);
477547
}
478548
}
479
- cgi_redirect(mprintf("rptview?rn=%d", rn));
549
+ cgi_redirect(mprintf("rptview/%d", rn));
480550
return;
481551
}
482552
}else if( rn==0 ){
483553
zTitle = "";
484554
zSQL = ticket_report_template();
485555
zClrKey = ticket_key_template();
486556
}else{
487557
Stmt q;
488
- db_prepare(&q, "SELECT title, sqlcode, owner, cols "
558
+ int hasJx;
559
+ zDesc = 0;
560
+ zMimetype = 0;
561
+ zTag = 0;
562
+ db_prepare(&q, "SELECT title, sqlcode, owner, cols, json_valid(jx) "
489563
"FROM reportfmt WHERE rn=%d",rn);
490564
if( db_step(&q)==SQLITE_ROW ){
491565
char *defaultReport = db_get("ticket-default-report", 0);
492566
zTitle = db_column_malloc(&q, 0);
493567
zSQL = db_column_malloc(&q, 1);
494568
zOwner = db_column_malloc(&q, 2);
495569
zClrKey = db_column_malloc(&q, 3);
496570
dflt = fossil_strcmp(zTitle, defaultReport)==0;
571
+ hasJx = db_column_int(&q, 4);
497572
}
498573
db_finalize(&q);
574
+ if( hasJx ){
575
+ db_prepare(&q, "SELECT jx->>'desc', jx->>'descmt', jx->>'tag'"
576
+ " FROM reportfmt WHERE rn=%d", rn);
577
+ if( db_step(&q)==SQLITE_ROW ){
578
+ zDesc = db_column_malloc(&q, 0);
579
+ zMimetype = db_column_malloc(&q, 1);
580
+ zTag = db_column_malloc(&q, 2);
581
+ }
582
+ db_finalize(&q);
583
+ }
499584
if( P("copy") ){
500585
rn = 0;
501586
zTitle = mprintf("Copy Of %s", zTitle);
502587
zOwner = g.zLogin;
503588
}
504589
}
505590
if( zOwner==0 ) zOwner = g.zLogin;
506591
style_submenu_element("Cancel", "reportlist");
507592
if( rn>0 ){
508
- style_submenu_element("Delete", "rptedit?rn=%d&del1=1", rn);
593
+ style_submenu_element("Delete", "rptedit/%d?del1=1", rn);
509594
}
510595
style_header("%s", rn>0 ? "Edit Report Format":"Create New Report Format");
511596
if( zErr ){
512597
@ <blockquote class="reportError">%h(zErr)</blockquote>
513598
}
@@ -521,19 +606,33 @@
521606
login_insert_csrf_secret();
522607
if( g.perm.Admin ){
523608
@ <p>Report owner:
524609
@ <input type="text" name="w" size="20" value="%h(zOwner)" />
525610
@ </p>
611
+ @ <p>Tag:
612
+ @ <input type="text" name="x" size="20" value="%h(zTag?zTag:"")" />
613
+ @ </p>
526614
} else {
527615
@ <input type="hidden" name="w" value="%h(zOwner)" />
616
+ if( zTag && zTag[0] ){
617
+ @ <input type="hidden" name="x" value="%h(zTag)" />
618
+ }
528619
}
529620
@ <p>Enter an optional color key in the following box. (If blank, no
530621
@ color key is displayed.) Each line contains the text for a single
531622
@ entry in the key. The first token of each line is the background
532623
@ color for that line.<br />
533624
@ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
534625
@ </p>
626
+
627
+ @ <p>Optional human-readable description for this report<br />
628
+ @ %z(href("%R/markup_help"))Markup style</a>:
629
+ mimetype_option_menu(zMimetype, "m");
630
+ @ <br><textarea aria-label="Description:" name="d" class="wikiedit" \
631
+ @ cols="80" rows="15" wrap="virtual">%h(zDesc)</textarea>
632
+ @ </p>
633
+
535634
@ <p><label><input type="checkbox" name="dflt" %s(dflt?"checked":"")> \
536635
@ Make this the default report</label></p>
537636
if( !g.perm.Admin && fossil_strcmp(zOwner,g.zLogin)!=0 ){
538637
@ <p>This report format is owned by %h(zOwner). You are not allowed
539638
@ to change it.</p>
@@ -753,13 +852,13 @@
753852
pState->zWikiEnd = "";
754853
if( P("plaintext") ){
755854
pState->wikiFlags |= WIKI_LINKSONLY;
756855
pState->zWikiStart = "<pre class='verbatim'>";
757856
pState->zWikiEnd = "</pre>";
758
- style_submenu_element("Formatted", "%R/rptview?rn=%d", pState->rn);
857
+ style_submenu_element("Formatted", "%R/rptview/%d", pState->rn);
759858
}else{
760
- style_submenu_element("Plaintext", "%R/rptview?rn=%d&plaintext",
859
+ style_submenu_element("Plaintext", "%R/rptview/%d?plaintext",
761860
pState->rn);
762861
}
763862
}else{
764863
pState->nCol++;
765864
}
@@ -1025,29 +1124,34 @@
10251124
int rn, rc;
10261125
char *zSql;
10271126
char *zTitle;
10281127
char *zOwner;
10291128
char *zClrKey;
1129
+ char *zDesc;
1130
+ char *zMimetype;
10301131
int tabs;
10311132
Stmt q;
10321133
char *zErr1 = 0;
10331134
char *zErr2 = 0;
1034
-
1135
+
10351136
login_check_credentials();
10361137
if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
1138
+ report_update_reportfmt_table();
1139
+ rn = report_number();
10371140
tabs = P("tablist")!=0;
10381141
db_prepare(&q,
1039
- "SELECT title, sqlcode, owner, cols, rn FROM reportfmt WHERE rn=%d",
1040
- atoi(PD("rn","0")));
1142
+ "SELECT title, sqlcode, owner, cols, rn, jx->>'desc', jx->>'descmt'"
1143
+ " FROM reportfmt WHERE rn=%d", rn);
10411144
rc = db_step(&q);
10421145
if( rc!=SQLITE_ROW ){
10431146
const char *titleSearch =
10441147
defaultTitleSearch==0 || trim_string(defaultTitleSearch)[0]==0 ?
10451148
P("title") : defaultTitleSearch;
10461149
db_finalize(&q);
10471150
db_prepare(&q,
1048
- "SELECT title, sqlcode, owner, cols, rn FROM reportfmt WHERE title GLOB %Q",
1151
+ "SELECT title, sqlcode, owner, cols, rn, jx->>'desc', jx->>'descmt'"
1152
+ " FROM reportfmt WHERE title GLOB %Q",
10491153
titleSearch);
10501154
rc = db_step(&q);
10511155
}
10521156
if( rc!=SQLITE_ROW ){
10531157
db_finalize(&q);
@@ -1059,10 +1163,12 @@
10591163
zTitle = db_column_malloc(&q, 0);
10601164
zSql = db_column_malloc(&q, 1);
10611165
zOwner = db_column_malloc(&q, 2);
10621166
zClrKey = db_column_malloc(&q, 3);
10631167
rn = db_column_int(&q,4);
1168
+ zDesc = db_column_malloc(&q, 5);
1169
+ zMimetype = db_column_malloc(&q, 6);
10641170
db_finalize(&q);
10651171
10661172
if( P("order_by") ){
10671173
/*
10681174
** If the user wants to do a column sort, wrap the query into a sub
@@ -1094,19 +1200,26 @@
10941200
style_submenu_element("Raw","%R/%s?tablist=1",g.zPath);
10951201
style_submenu_element("Reports","%R/reportlist");
10961202
}
10971203
if( g.perm.Admin
10981204
|| (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){
1099
- style_submenu_element("Edit", "rptedit?rn=%d", rn);
1205
+ style_submenu_element("Edit", "rptedit/%d", rn);
11001206
}
11011207
if( g.perm.TktFmt ){
1102
- style_submenu_element("SQL", "rptsql?rn=%d",rn);
1208
+ style_submenu_element("SQL", "rptsql/%d",rn);
11031209
}
11041210
if( g.perm.NewTkt ){
11051211
style_submenu_element("New Ticket", "%R/tktnew");
11061212
}
11071213
style_header("%s", zTitle);
1214
+ }
1215
+ if( zDesc && zMimetype ){
1216
+ Blob src;
1217
+ blob_init(&src, zDesc, -1);
1218
+ wiki_render_by_mimetype(&src, zMimetype);
1219
+ blob_reset(&src);
1220
+ @ <p>
11081221
}
11091222
output_color_key(zClrKey, 1,
11101223
"border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\"");
11111224
@ <table border="1" cellpadding="2" cellspacing="0" class="report sortable"
11121225
@ data-column-types='' data-init-sort='0'>
11131226
--- src/report.c
+++ src/report.c
@@ -75,29 +75,29 @@
75 cnt++;
76 blob_appendf(&ril, "<li>");
77 if( zTitle[0] == '_' ){
78 blob_appendf(&ril, "%s", zTitle);
79 } else {
80 blob_appendf(&ril, "%z%h</a>", href("%R/rptview?rn=%d", rn), zTitle);
81 }
82 blob_appendf(&ril, "&nbsp;&nbsp;&nbsp;");
83 if( g.perm.Write && zOwner && zOwner[0] ){
84 blob_appendf(&ril, "(by <i>%h</i>) ", zOwner);
85 }
86 if( g.perm.TktFmt ){
87 blob_appendf(&ril, "[%zcopy</a>] ",
88 href("%R/rptedit?rn=%d&copy=1", rn));
89 }
90 if( g.perm.Admin
91 || (g.perm.WrTkt && zOwner && fossil_strcmp(g.zLogin,zOwner)==0)
92 ){
93 blob_appendf(&ril, "[%zedit</a>]",
94 href("%R/rptedit?rn=%d", rn));
95 }
96 if( g.perm.TktFmt ){
97 blob_appendf(&ril, "[%zsql</a>]",
98 href("%R/rptsql?rn=%d", rn));
99 }
100 if( fossil_strcmp(zTitle, defaultReport)==0 ){
101 blob_appendf(&ril, "&nbsp;← default");
102 }
103 blob_appendf(&ril, "</li>\n");
@@ -253,10 +253,24 @@
253 break;
254 }
255 }
256 return rc;
257 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
259 /*
260 ** Activate the ticket report query authorizer. Must be followed by an
261 ** eventual call to report_unrestrict_sql().
262 */
@@ -321,17 +335,53 @@
321 sqlite3_finalize(pStmt);
322 }
323 report_unrestrict_sql();
324 return zErr;
325 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
327 /*
328 ** WEBPAGE: rptsql
329 ** URL: /rptsql?rn=N
330 **
331 ** Display the SQL query used to generate a ticket report. The rn=N
332 ** query parameter identifies the specific report number to be displayed.
333 */
334 void view_see_sql(void){
335 int rn;
336 const char *zTitle;
337 const char *zSQL;
@@ -342,11 +392,11 @@
342 login_check_credentials();
343 if( !g.perm.TktFmt ){
344 login_needed(g.anon.TktFmt);
345 return;
346 }
347 rn = atoi(PD("rn","0"));
348 db_prepare(&q, "SELECT title, sqlcode, owner, cols "
349 "FROM reportfmt WHERE rn=%d",rn);
350 style_set_current_feature("report");
351 style_header("SQL For Report Format Number %d", rn);
352 if( db_step(&q)!=SQLITE_ROW ){
@@ -382,39 +432,51 @@
382 ** WEBPAGE: rptedit
383 **
384 ** Create (/rptnew) or edit (/rptedit) a ticket report format.
385 ** Query parameters:
386 **
387 ** rn=N Ticket report number. (required)
 
 
388 ** t=TITLE Title of the report format
389 ** w=USER Owner of the report format
390 ** s=SQL SQL text used to implement the report
391 ** k=KEY Color key
 
 
 
392 */
393 void view_edit(void){
394 int rn;
395 const char *zTitle;
396 const char *z;
397 const char *zOwner;
398 const char *zClrKey;
399 char *zSQL;
400 char *zErr = 0;
 
 
 
401 int dflt = P("dflt") ? 1 : 0;
402
403 login_check_credentials();
404 if( !g.perm.TktFmt ){
405 login_needed(g.anon.TktFmt);
406 return;
407 }
408 style_set_current_feature("report");
409 /*view_add_functions(0);*/
410 rn = atoi(PD("rn","0"));
411 zTitle = P("t");
412 zOwner = PD("w",g.zLogin);
413 z = P("s");
414 zSQL = z ? trim_string(z) : 0;
415 zClrKey = trim_string(PD("k",""));
 
 
 
 
416 if( rn>0 && P("del2") ){
417 login_verify_csrf_secret();
418 db_multi_exec("DELETE FROM reportfmt WHERE rn=%d", rn);
419 cgi_redirect("reportlist");
420 return;
@@ -456,18 +518,26 @@
456 ){
457 zErr = mprintf("There is already another report named \"%h\"", zTitle);
458 }
459 if( zErr==0 ){
460 login_verify_csrf_secret();
 
 
 
461 if( rn>0 ){
462 db_multi_exec("UPDATE reportfmt SET title=%Q, sqlcode=%Q,"
463 " owner=%Q, cols=%Q, mtime=now() WHERE rn=%d",
464 zTitle, zSQL, zOwner, zClrKey, rn);
 
 
 
465 }else{
466 db_multi_exec("INSERT INTO reportfmt(title,sqlcode,owner,cols,mtime) "
467 "VALUES(%Q,%Q,%Q,%Q,now())",
468 zTitle, zSQL, zOwner, zClrKey);
 
 
469 rn = db_last_insert_rowid();
470 }
471 if( dflt ){
472 db_set("ticket-default-report", zTitle, 0);
473 }else{
@@ -474,40 +544,55 @@
474 char *defaultReport = db_get("ticket-default-report", 0);
475 if( fossil_strcmp(zTitle, defaultReport)==0 ){
476 db_set("ticket-default-report", "", 0);
477 }
478 }
479 cgi_redirect(mprintf("rptview?rn=%d", rn));
480 return;
481 }
482 }else if( rn==0 ){
483 zTitle = "";
484 zSQL = ticket_report_template();
485 zClrKey = ticket_key_template();
486 }else{
487 Stmt q;
488 db_prepare(&q, "SELECT title, sqlcode, owner, cols "
 
 
 
 
489 "FROM reportfmt WHERE rn=%d",rn);
490 if( db_step(&q)==SQLITE_ROW ){
491 char *defaultReport = db_get("ticket-default-report", 0);
492 zTitle = db_column_malloc(&q, 0);
493 zSQL = db_column_malloc(&q, 1);
494 zOwner = db_column_malloc(&q, 2);
495 zClrKey = db_column_malloc(&q, 3);
496 dflt = fossil_strcmp(zTitle, defaultReport)==0;
 
497 }
498 db_finalize(&q);
 
 
 
 
 
 
 
 
 
 
499 if( P("copy") ){
500 rn = 0;
501 zTitle = mprintf("Copy Of %s", zTitle);
502 zOwner = g.zLogin;
503 }
504 }
505 if( zOwner==0 ) zOwner = g.zLogin;
506 style_submenu_element("Cancel", "reportlist");
507 if( rn>0 ){
508 style_submenu_element("Delete", "rptedit?rn=%d&del1=1", rn);
509 }
510 style_header("%s", rn>0 ? "Edit Report Format":"Create New Report Format");
511 if( zErr ){
512 @ <blockquote class="reportError">%h(zErr)</blockquote>
513 }
@@ -521,19 +606,33 @@
521 login_insert_csrf_secret();
522 if( g.perm.Admin ){
523 @ <p>Report owner:
524 @ <input type="text" name="w" size="20" value="%h(zOwner)" />
525 @ </p>
 
 
 
526 } else {
527 @ <input type="hidden" name="w" value="%h(zOwner)" />
 
 
 
528 }
529 @ <p>Enter an optional color key in the following box. (If blank, no
530 @ color key is displayed.) Each line contains the text for a single
531 @ entry in the key. The first token of each line is the background
532 @ color for that line.<br />
533 @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
534 @ </p>
 
 
 
 
 
 
 
 
535 @ <p><label><input type="checkbox" name="dflt" %s(dflt?"checked":"")> \
536 @ Make this the default report</label></p>
537 if( !g.perm.Admin && fossil_strcmp(zOwner,g.zLogin)!=0 ){
538 @ <p>This report format is owned by %h(zOwner). You are not allowed
539 @ to change it.</p>
@@ -753,13 +852,13 @@
753 pState->zWikiEnd = "";
754 if( P("plaintext") ){
755 pState->wikiFlags |= WIKI_LINKSONLY;
756 pState->zWikiStart = "<pre class='verbatim'>";
757 pState->zWikiEnd = "</pre>";
758 style_submenu_element("Formatted", "%R/rptview?rn=%d", pState->rn);
759 }else{
760 style_submenu_element("Plaintext", "%R/rptview?rn=%d&plaintext",
761 pState->rn);
762 }
763 }else{
764 pState->nCol++;
765 }
@@ -1025,29 +1124,34 @@
1025 int rn, rc;
1026 char *zSql;
1027 char *zTitle;
1028 char *zOwner;
1029 char *zClrKey;
 
 
1030 int tabs;
1031 Stmt q;
1032 char *zErr1 = 0;
1033 char *zErr2 = 0;
1034
1035 login_check_credentials();
1036 if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
 
 
1037 tabs = P("tablist")!=0;
1038 db_prepare(&q,
1039 "SELECT title, sqlcode, owner, cols, rn FROM reportfmt WHERE rn=%d",
1040 atoi(PD("rn","0")));
1041 rc = db_step(&q);
1042 if( rc!=SQLITE_ROW ){
1043 const char *titleSearch =
1044 defaultTitleSearch==0 || trim_string(defaultTitleSearch)[0]==0 ?
1045 P("title") : defaultTitleSearch;
1046 db_finalize(&q);
1047 db_prepare(&q,
1048 "SELECT title, sqlcode, owner, cols, rn FROM reportfmt WHERE title GLOB %Q",
 
1049 titleSearch);
1050 rc = db_step(&q);
1051 }
1052 if( rc!=SQLITE_ROW ){
1053 db_finalize(&q);
@@ -1059,10 +1163,12 @@
1059 zTitle = db_column_malloc(&q, 0);
1060 zSql = db_column_malloc(&q, 1);
1061 zOwner = db_column_malloc(&q, 2);
1062 zClrKey = db_column_malloc(&q, 3);
1063 rn = db_column_int(&q,4);
 
 
1064 db_finalize(&q);
1065
1066 if( P("order_by") ){
1067 /*
1068 ** If the user wants to do a column sort, wrap the query into a sub
@@ -1094,19 +1200,26 @@
1094 style_submenu_element("Raw","%R/%s?tablist=1",g.zPath);
1095 style_submenu_element("Reports","%R/reportlist");
1096 }
1097 if( g.perm.Admin
1098 || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){
1099 style_submenu_element("Edit", "rptedit?rn=%d", rn);
1100 }
1101 if( g.perm.TktFmt ){
1102 style_submenu_element("SQL", "rptsql?rn=%d",rn);
1103 }
1104 if( g.perm.NewTkt ){
1105 style_submenu_element("New Ticket", "%R/tktnew");
1106 }
1107 style_header("%s", zTitle);
 
 
 
 
 
 
 
1108 }
1109 output_color_key(zClrKey, 1,
1110 "border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\"");
1111 @ <table border="1" cellpadding="2" cellspacing="0" class="report sortable"
1112 @ data-column-types='' data-init-sort='0'>
1113
--- src/report.c
+++ src/report.c
@@ -75,29 +75,29 @@
75 cnt++;
76 blob_appendf(&ril, "<li>");
77 if( zTitle[0] == '_' ){
78 blob_appendf(&ril, "%s", zTitle);
79 } else {
80 blob_appendf(&ril, "%z%h</a>", href("%R/rptview/%d", rn), zTitle);
81 }
82 blob_appendf(&ril, "&nbsp;&nbsp;&nbsp;");
83 if( g.perm.Write && zOwner && zOwner[0] ){
84 blob_appendf(&ril, "(by <i>%h</i>) ", zOwner);
85 }
86 if( g.perm.TktFmt ){
87 blob_appendf(&ril, "[%zcopy</a>] ",
88 href("%R/rptedit/%d?copy=1", rn));
89 }
90 if( g.perm.Admin
91 || (g.perm.WrTkt && zOwner && fossil_strcmp(g.zLogin,zOwner)==0)
92 ){
93 blob_appendf(&ril, "[%zedit</a>]",
94 href("%R/rptedit/%d", rn));
95 }
96 if( g.perm.TktFmt ){
97 blob_appendf(&ril, "[%zsql</a>]",
98 href("%R/rptsql/%d", rn));
99 }
100 if( fossil_strcmp(zTitle, defaultReport)==0 ){
101 blob_appendf(&ril, "&nbsp;← default");
102 }
103 blob_appendf(&ril, "</li>\n");
@@ -253,10 +253,24 @@
253 break;
254 }
255 }
256 return rc;
257 }
258
259 /*
260 ** Make sure the reportfmt table is up-to-date. It should contain
261 ** the "jx" column (as of version 2.21). If it does not, add it.
262 **
263 ** The "jx" column is intended to hold a JSON object containing optional
264 ** key-value pairs.
265 */
266 void report_update_reportfmt_table(void){
267 if( db_table_has_column("repository","reportfmt","jx")==0 ){
268 db_multi_exec("ALTER TABLE repository.reportfmt"
269 " ADD COLUMN jx TEXT DEFAULT '{}';");
270 }
271 }
272
273 /*
274 ** Activate the ticket report query authorizer. Must be followed by an
275 ** eventual call to report_unrestrict_sql().
276 */
@@ -321,17 +335,53 @@
335 sqlite3_finalize(pStmt);
336 }
337 report_unrestrict_sql();
338 return zErr;
339 }
340
341 /*
342 ** Get a report number from query parameters. This can be done in various
343 ** ways:
344 **
345 ** (1) (legacy) rn=NNN where NNN is the reportfmt.rn integer primary key.
346 **
347 ** (2) name=NNN where NNN is the rn.
348 **
349 ** (3) name=TAG where TAG matches reportfmt.jx->>tag
350 **
351 ** Regardless of how the report is specified, return the primary key, rn.
352 ** Return 0 if not found.
353 */
354 static int report_number(void){
355 int rn;
356 const char *zName;
357 char *zEnd;
358
359 /* Case (1) */
360 rn = atoi(PD("rn","0"));
361 if( rn>0 ) return rn;
362
363 zName = P("name");
364 if( zName==0 || zName[0]==0 ) return 0;
365 if( fossil_isdigit(zName[0])
366 && (rn = strtol(zName, &zEnd, 10))>0
367 && zEnd[0]==0
368 ){
369 /* Case 2 */
370 return rn;
371 }
372
373 rn = db_int(0, "SELECT rn FROM reportfmt WHERE jx->>'tag'==%Q", zName);
374 return rn;
375 }
376
377 /*
378 ** WEBPAGE: rptsql
379 ** URL: /rptsql/N
380 **
381 ** Display the SQL query used to generate a ticket report. The N value
382 ** is either the report number of a report tag.
383 */
384 void view_see_sql(void){
385 int rn;
386 const char *zTitle;
387 const char *zSQL;
@@ -342,11 +392,11 @@
392 login_check_credentials();
393 if( !g.perm.TktFmt ){
394 login_needed(g.anon.TktFmt);
395 return;
396 }
397 rn = report_number();
398 db_prepare(&q, "SELECT title, sqlcode, owner, cols "
399 "FROM reportfmt WHERE rn=%d",rn);
400 style_set_current_feature("report");
401 style_header("SQL For Report Format Number %d", rn);
402 if( db_step(&q)!=SQLITE_ROW ){
@@ -382,39 +432,51 @@
432 ** WEBPAGE: rptedit
433 **
434 ** Create (/rptnew) or edit (/rptedit) a ticket report format.
435 ** Query parameters:
436 **
437 ** name=N Ticket report number or tag.
438 ** rn=N Ticket report number (legacy).
439 ** ^^^-- one of the two previous is required.
440 ** t=TITLE Title of the report format
441 ** w=USER Owner of the report format
442 ** s=SQL SQL text used to implement the report
443 ** k=KEY Color key
444 ** d=DESC Optional descriptive text
445 ** m=MIMETYPE Mimetype for DESC
446 ** x=TAG Symbolic name for the report
447 */
448 void view_edit(void){
449 int rn;
450 const char *zTitle; /* Title of the report */
451 const char *z;
452 const char *zOwner; /* Owner of the report */
453 const char *zClrKey; /* Color key - used to add colors to lines */
454 char *zSQL; /* The SQL text that gnerates the report */
455 char *zErr = 0; /* An error message */
456 const char *zDesc; /* Extra descriptive text about the report */
457 const char *zMimetype; /* Mimetype for zDesc */
458 const char *zTag; /* Symbolic name for this report */
459 int dflt = P("dflt") ? 1 : 0;
460
461 login_check_credentials();
462 if( !g.perm.TktFmt ){
463 login_needed(g.anon.TktFmt);
464 return;
465 }
466 style_set_current_feature("report");
467 /*view_add_functions(0);*/
468 rn = report_number();
469 zTitle = P("t");
470 zOwner = PD("w",g.zLogin);
471 z = P("s");
472 zSQL = z ? trim_string(z) : 0;
473 zClrKey = trim_string(PD("k",""));
474 zDesc = trim_string(PD("d",""));
475 zMimetype = P("m");
476 zTag = P("x");
477 report_update_reportfmt_table();
478 if( rn>0 && P("del2") ){
479 login_verify_csrf_secret();
480 db_multi_exec("DELETE FROM reportfmt WHERE rn=%d", rn);
481 cgi_redirect("reportlist");
482 return;
@@ -456,18 +518,26 @@
518 ){
519 zErr = mprintf("There is already another report named \"%h\"", zTitle);
520 }
521 if( zErr==0 ){
522 login_verify_csrf_secret();
523 if( zTag && zTag[0]==0 ) zTag = 0;
524 if( zDesc && zDesc[0]==0 ){ zDesc = 0; zMimetype = 0; }
525 if( zMimetype && zMimetype[0]==0 ){ zDesc = 0; zMimetype = 0; }
526 if( rn>0 ){
527 db_multi_exec(
528 "UPDATE reportfmt SET title=%Q, sqlcode=%Q,"
529 " owner=%Q, cols=%Q, mtime=now(), "
530 " jx=json_patch(jx,json_object('desc',%Q,'descmt',%Q,'tag',%Q))"
531 " WHERE rn=%d",
532 zTitle, zSQL, zOwner, zClrKey, zDesc, zMimetype, zTag, rn);
533 }else{
534 db_multi_exec(
535 "INSERT INTO reportfmt(title,sqlcode,owner,cols,mtime,jx) "
536 "VALUES(%Q,%Q,%Q,%Q,now(),"
537 "json_object('desc',%Q,'descmt',%Q,'tag',%Q))",
538 zTitle, zSQL, zOwner, zClrKey, zDesc, zMimetype, zTag);
539 rn = db_last_insert_rowid();
540 }
541 if( dflt ){
542 db_set("ticket-default-report", zTitle, 0);
543 }else{
@@ -474,40 +544,55 @@
544 char *defaultReport = db_get("ticket-default-report", 0);
545 if( fossil_strcmp(zTitle, defaultReport)==0 ){
546 db_set("ticket-default-report", "", 0);
547 }
548 }
549 cgi_redirect(mprintf("rptview/%d", rn));
550 return;
551 }
552 }else if( rn==0 ){
553 zTitle = "";
554 zSQL = ticket_report_template();
555 zClrKey = ticket_key_template();
556 }else{
557 Stmt q;
558 int hasJx;
559 zDesc = 0;
560 zMimetype = 0;
561 zTag = 0;
562 db_prepare(&q, "SELECT title, sqlcode, owner, cols, json_valid(jx) "
563 "FROM reportfmt WHERE rn=%d",rn);
564 if( db_step(&q)==SQLITE_ROW ){
565 char *defaultReport = db_get("ticket-default-report", 0);
566 zTitle = db_column_malloc(&q, 0);
567 zSQL = db_column_malloc(&q, 1);
568 zOwner = db_column_malloc(&q, 2);
569 zClrKey = db_column_malloc(&q, 3);
570 dflt = fossil_strcmp(zTitle, defaultReport)==0;
571 hasJx = db_column_int(&q, 4);
572 }
573 db_finalize(&q);
574 if( hasJx ){
575 db_prepare(&q, "SELECT jx->>'desc', jx->>'descmt', jx->>'tag'"
576 " FROM reportfmt WHERE rn=%d", rn);
577 if( db_step(&q)==SQLITE_ROW ){
578 zDesc = db_column_malloc(&q, 0);
579 zMimetype = db_column_malloc(&q, 1);
580 zTag = db_column_malloc(&q, 2);
581 }
582 db_finalize(&q);
583 }
584 if( P("copy") ){
585 rn = 0;
586 zTitle = mprintf("Copy Of %s", zTitle);
587 zOwner = g.zLogin;
588 }
589 }
590 if( zOwner==0 ) zOwner = g.zLogin;
591 style_submenu_element("Cancel", "reportlist");
592 if( rn>0 ){
593 style_submenu_element("Delete", "rptedit/%d?del1=1", rn);
594 }
595 style_header("%s", rn>0 ? "Edit Report Format":"Create New Report Format");
596 if( zErr ){
597 @ <blockquote class="reportError">%h(zErr)</blockquote>
598 }
@@ -521,19 +606,33 @@
606 login_insert_csrf_secret();
607 if( g.perm.Admin ){
608 @ <p>Report owner:
609 @ <input type="text" name="w" size="20" value="%h(zOwner)" />
610 @ </p>
611 @ <p>Tag:
612 @ <input type="text" name="x" size="20" value="%h(zTag?zTag:"")" />
613 @ </p>
614 } else {
615 @ <input type="hidden" name="w" value="%h(zOwner)" />
616 if( zTag && zTag[0] ){
617 @ <input type="hidden" name="x" value="%h(zTag)" />
618 }
619 }
620 @ <p>Enter an optional color key in the following box. (If blank, no
621 @ color key is displayed.) Each line contains the text for a single
622 @ entry in the key. The first token of each line is the background
623 @ color for that line.<br />
624 @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
625 @ </p>
626
627 @ <p>Optional human-readable description for this report<br />
628 @ %z(href("%R/markup_help"))Markup style</a>:
629 mimetype_option_menu(zMimetype, "m");
630 @ <br><textarea aria-label="Description:" name="d" class="wikiedit" \
631 @ cols="80" rows="15" wrap="virtual">%h(zDesc)</textarea>
632 @ </p>
633
634 @ <p><label><input type="checkbox" name="dflt" %s(dflt?"checked":"")> \
635 @ Make this the default report</label></p>
636 if( !g.perm.Admin && fossil_strcmp(zOwner,g.zLogin)!=0 ){
637 @ <p>This report format is owned by %h(zOwner). You are not allowed
638 @ to change it.</p>
@@ -753,13 +852,13 @@
852 pState->zWikiEnd = "";
853 if( P("plaintext") ){
854 pState->wikiFlags |= WIKI_LINKSONLY;
855 pState->zWikiStart = "<pre class='verbatim'>";
856 pState->zWikiEnd = "</pre>";
857 style_submenu_element("Formatted", "%R/rptview/%d", pState->rn);
858 }else{
859 style_submenu_element("Plaintext", "%R/rptview/%d?plaintext",
860 pState->rn);
861 }
862 }else{
863 pState->nCol++;
864 }
@@ -1025,29 +1124,34 @@
1124 int rn, rc;
1125 char *zSql;
1126 char *zTitle;
1127 char *zOwner;
1128 char *zClrKey;
1129 char *zDesc;
1130 char *zMimetype;
1131 int tabs;
1132 Stmt q;
1133 char *zErr1 = 0;
1134 char *zErr2 = 0;
1135
1136 login_check_credentials();
1137 if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
1138 report_update_reportfmt_table();
1139 rn = report_number();
1140 tabs = P("tablist")!=0;
1141 db_prepare(&q,
1142 "SELECT title, sqlcode, owner, cols, rn, jx->>'desc', jx->>'descmt'"
1143 " FROM reportfmt WHERE rn=%d", rn);
1144 rc = db_step(&q);
1145 if( rc!=SQLITE_ROW ){
1146 const char *titleSearch =
1147 defaultTitleSearch==0 || trim_string(defaultTitleSearch)[0]==0 ?
1148 P("title") : defaultTitleSearch;
1149 db_finalize(&q);
1150 db_prepare(&q,
1151 "SELECT title, sqlcode, owner, cols, rn, jx->>'desc', jx->>'descmt'"
1152 " FROM reportfmt WHERE title GLOB %Q",
1153 titleSearch);
1154 rc = db_step(&q);
1155 }
1156 if( rc!=SQLITE_ROW ){
1157 db_finalize(&q);
@@ -1059,10 +1163,12 @@
1163 zTitle = db_column_malloc(&q, 0);
1164 zSql = db_column_malloc(&q, 1);
1165 zOwner = db_column_malloc(&q, 2);
1166 zClrKey = db_column_malloc(&q, 3);
1167 rn = db_column_int(&q,4);
1168 zDesc = db_column_malloc(&q, 5);
1169 zMimetype = db_column_malloc(&q, 6);
1170 db_finalize(&q);
1171
1172 if( P("order_by") ){
1173 /*
1174 ** If the user wants to do a column sort, wrap the query into a sub
@@ -1094,19 +1200,26 @@
1200 style_submenu_element("Raw","%R/%s?tablist=1",g.zPath);
1201 style_submenu_element("Reports","%R/reportlist");
1202 }
1203 if( g.perm.Admin
1204 || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){
1205 style_submenu_element("Edit", "rptedit/%d", rn);
1206 }
1207 if( g.perm.TktFmt ){
1208 style_submenu_element("SQL", "rptsql/%d",rn);
1209 }
1210 if( g.perm.NewTkt ){
1211 style_submenu_element("New Ticket", "%R/tktnew");
1212 }
1213 style_header("%s", zTitle);
1214 }
1215 if( zDesc && zMimetype ){
1216 Blob src;
1217 blob_init(&src, zDesc, -1);
1218 wiki_render_by_mimetype(&src, zMimetype);
1219 blob_reset(&src);
1220 @ <p>
1221 }
1222 output_color_key(zClrKey, 1,
1223 "border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\"");
1224 @ <table border="1" cellpadding="2" cellspacing="0" class="report sortable"
1225 @ data-column-types='' data-init-sort='0'>
1226
+4 -2
--- src/schema.c
+++ src/schema.c
@@ -126,11 +126,12 @@
126126
@ cookie TEXT, -- WWW login cookie
127127
@ ipaddr TEXT, -- IP address for which cookie is valid
128128
@ cexpire DATETIME, -- Time when cookie expires
129129
@ info TEXT, -- contact information
130130
@ mtime DATE, -- last change. seconds since 1970
131
-@ photo BLOB -- JPEG image of this user
131
+@ photo BLOB, -- JPEG image of this user
132
+@ jx TEXT DEFAULT '{}' -- Extra fields in JSON
132133
@ );
133134
@
134135
@ -- The config table holds miscellanous information about the repository.
135136
@ -- in the form of name-value pairs.
136137
@ --
@@ -176,11 +177,12 @@
176177
@ rn INTEGER PRIMARY KEY, -- Report number
177178
@ owner TEXT, -- Owner of this report format (not used)
178179
@ title TEXT UNIQUE, -- Title of this report
179180
@ mtime DATE, -- Last modified. seconds since 1970
180181
@ cols TEXT, -- A color-key specification
181
-@ sqlcode TEXT -- An SQL SELECT statement for this report
182
+@ sqlcode TEXT, -- An SQL SELECT statement for this report
183
+@ jx TEXT DEFAULT '{}' -- Additional fields encoded as JSON
182184
@ );
183185
@
184186
@ -- Some ticket content (such as the originators email address or contact
185187
@ -- information) needs to be obscured to protect privacy. This is achieved
186188
@ -- by storing an SHA1 hash of the content. For display, the hash is
187189
--- src/schema.c
+++ src/schema.c
@@ -126,11 +126,12 @@
126 @ cookie TEXT, -- WWW login cookie
127 @ ipaddr TEXT, -- IP address for which cookie is valid
128 @ cexpire DATETIME, -- Time when cookie expires
129 @ info TEXT, -- contact information
130 @ mtime DATE, -- last change. seconds since 1970
131 @ photo BLOB -- JPEG image of this user
 
132 @ );
133 @
134 @ -- The config table holds miscellanous information about the repository.
135 @ -- in the form of name-value pairs.
136 @ --
@@ -176,11 +177,12 @@
176 @ rn INTEGER PRIMARY KEY, -- Report number
177 @ owner TEXT, -- Owner of this report format (not used)
178 @ title TEXT UNIQUE, -- Title of this report
179 @ mtime DATE, -- Last modified. seconds since 1970
180 @ cols TEXT, -- A color-key specification
181 @ sqlcode TEXT -- An SQL SELECT statement for this report
 
182 @ );
183 @
184 @ -- Some ticket content (such as the originators email address or contact
185 @ -- information) needs to be obscured to protect privacy. This is achieved
186 @ -- by storing an SHA1 hash of the content. For display, the hash is
187
--- src/schema.c
+++ src/schema.c
@@ -126,11 +126,12 @@
126 @ cookie TEXT, -- WWW login cookie
127 @ ipaddr TEXT, -- IP address for which cookie is valid
128 @ cexpire DATETIME, -- Time when cookie expires
129 @ info TEXT, -- contact information
130 @ mtime DATE, -- last change. seconds since 1970
131 @ photo BLOB, -- JPEG image of this user
132 @ jx TEXT DEFAULT '{}' -- Extra fields in JSON
133 @ );
134 @
135 @ -- The config table holds miscellanous information about the repository.
136 @ -- in the form of name-value pairs.
137 @ --
@@ -176,11 +177,12 @@
177 @ rn INTEGER PRIMARY KEY, -- Report number
178 @ owner TEXT, -- Owner of this report format (not used)
179 @ title TEXT UNIQUE, -- Title of this report
180 @ mtime DATE, -- Last modified. seconds since 1970
181 @ cols TEXT, -- A color-key specification
182 @ sqlcode TEXT, -- An SQL SELECT statement for this report
183 @ jx TEXT DEFAULT '{}' -- Additional fields encoded as JSON
184 @ );
185 @
186 @ -- Some ticket content (such as the originators email address or contact
187 @ -- information) needs to be obscured to protect privacy. This is achieved
188 @ -- by storing an SHA1 hash of the content. For display, the hash is
189
+14
--- src/user.c
+++ src/user.c
@@ -573,10 +573,24 @@
573573
user_select();
574574
fossil_print("Final g.zLogin: %s\n", g.zLogin);
575575
fossil_print("Final g.userUid: %d\n", g.userUid);
576576
}
577577
578
+
579
+/*
580
+** Make sure the USER table is up-to-date. It should contain
581
+** the "JX" column (as of version 2.21). If it does not, add it.
582
+**
583
+** The "JX" column is intended to hold a JSON object containing optional
584
+** key-value pairs.
585
+*/
586
+void user_update_user_table(void){
587
+ if( db_table_has_column("repository","user","jx")==0 ){
588
+ db_multi_exec("ALTER TABLE repository.user"
589
+ " ADD COLUMN jx TEXT DEFAULT '{}';");
590
+ }
591
+}
578592
579593
/*
580594
** COMMAND: test-hash-passwords
581595
**
582596
** Usage: %fossil test-hash-passwords REPOSITORY
583597
--- src/user.c
+++ src/user.c
@@ -573,10 +573,24 @@
573 user_select();
574 fossil_print("Final g.zLogin: %s\n", g.zLogin);
575 fossil_print("Final g.userUid: %d\n", g.userUid);
576 }
577
 
 
 
 
 
 
 
 
 
 
 
 
 
 
578
579 /*
580 ** COMMAND: test-hash-passwords
581 **
582 ** Usage: %fossil test-hash-passwords REPOSITORY
583
--- src/user.c
+++ src/user.c
@@ -573,10 +573,24 @@
573 user_select();
574 fossil_print("Final g.zLogin: %s\n", g.zLogin);
575 fossil_print("Final g.userUid: %d\n", g.userUid);
576 }
577
578
579 /*
580 ** Make sure the USER table is up-to-date. It should contain
581 ** the "JX" column (as of version 2.21). If it does not, add it.
582 **
583 ** The "JX" column is intended to hold a JSON object containing optional
584 ** key-value pairs.
585 */
586 void user_update_user_table(void){
587 if( db_table_has_column("repository","user","jx")==0 ){
588 db_multi_exec("ALTER TABLE repository.user"
589 " ADD COLUMN jx TEXT DEFAULT '{}';");
590 }
591 }
592
593 /*
594 ** COMMAND: test-hash-passwords
595 **
596 ** Usage: %fossil test-hash-passwords REPOSITORY
597
+8 -5
--- src/wiki.c
+++ src/wiki.c
@@ -646,15 +646,18 @@
646646
return nrid;
647647
}
648648
649649
/*
650650
** Output a selection box from which the user can select the
651
-** wiki mimetype.
651
+** wiki mimetype. Arguments:
652
+**
653
+** zMimetype - The current value of the query parameter
654
+** zParam - The name of the query parameter
652655
*/
653
-void mimetype_option_menu(const char *zMimetype){
656
+void mimetype_option_menu(const char *zMimetype, const char *zParam){
654657
unsigned i;
655
- @ <select name="mimetype" size="1">
658
+ @ <select name="%s(zParam)" size="1">
656659
for(i=0; i<count(azStyles); i+=3){
657660
if( fossil_strcmp(zMimetype,azStyles[i])==0 ){
658661
@ <option value="%s(azStyles[i])" selected>%s(azStyles[i+1])</option>
659662
}else{
660663
@ <option value="%s(azStyles[i])">%s(azStyles[i+1])</option>
@@ -1334,11 +1337,11 @@
13341337
">");
13351338
CX("<div class='"
13361339
"wikiedit-options flex-container flex-row child-gap-small'>");
13371340
CX("<div class='input-with-label'>"
13381341
"<label>Mime type</label>");
1339
- mimetype_option_menu("text/x-markdown");
1342
+ mimetype_option_menu("text/x-markdown", "mimetype");
13401343
CX("</div>");
13411344
style_select_list_int("select-font-size",
13421345
"editor_font_size", "Editor font size",
13431346
NULL/*tooltip*/,
13441347
100,
@@ -1535,11 +1538,11 @@
15351538
well_formed_wiki_name_rules();
15361539
form_begin(0, "%R/wikinew");
15371540
@ <p>Name of new wiki page:
15381541
@ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br />
15391542
@ %z(href("%R/markup_help"))Markup style</a>:
1540
- mimetype_option_menu("text/x-markdown");
1543
+ mimetype_option_menu("text/x-markdown", "mimetype");
15411544
@ <br /><input type="submit" value="Create" />
15421545
@ </p></form>
15431546
if( zName[0] ){
15441547
@ <p><span class="wikiError">
15451548
@ "%h(zName)" is not a valid wiki page name!</span></p>
15461549
--- src/wiki.c
+++ src/wiki.c
@@ -646,15 +646,18 @@
646 return nrid;
647 }
648
649 /*
650 ** Output a selection box from which the user can select the
651 ** wiki mimetype.
 
 
 
652 */
653 void mimetype_option_menu(const char *zMimetype){
654 unsigned i;
655 @ <select name="mimetype" size="1">
656 for(i=0; i<count(azStyles); i+=3){
657 if( fossil_strcmp(zMimetype,azStyles[i])==0 ){
658 @ <option value="%s(azStyles[i])" selected>%s(azStyles[i+1])</option>
659 }else{
660 @ <option value="%s(azStyles[i])">%s(azStyles[i+1])</option>
@@ -1334,11 +1337,11 @@
1334 ">");
1335 CX("<div class='"
1336 "wikiedit-options flex-container flex-row child-gap-small'>");
1337 CX("<div class='input-with-label'>"
1338 "<label>Mime type</label>");
1339 mimetype_option_menu("text/x-markdown");
1340 CX("</div>");
1341 style_select_list_int("select-font-size",
1342 "editor_font_size", "Editor font size",
1343 NULL/*tooltip*/,
1344 100,
@@ -1535,11 +1538,11 @@
1535 well_formed_wiki_name_rules();
1536 form_begin(0, "%R/wikinew");
1537 @ <p>Name of new wiki page:
1538 @ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br />
1539 @ %z(href("%R/markup_help"))Markup style</a>:
1540 mimetype_option_menu("text/x-markdown");
1541 @ <br /><input type="submit" value="Create" />
1542 @ </p></form>
1543 if( zName[0] ){
1544 @ <p><span class="wikiError">
1545 @ "%h(zName)" is not a valid wiki page name!</span></p>
1546
--- src/wiki.c
+++ src/wiki.c
@@ -646,15 +646,18 @@
646 return nrid;
647 }
648
649 /*
650 ** Output a selection box from which the user can select the
651 ** wiki mimetype. Arguments:
652 **
653 ** zMimetype - The current value of the query parameter
654 ** zParam - The name of the query parameter
655 */
656 void mimetype_option_menu(const char *zMimetype, const char *zParam){
657 unsigned i;
658 @ <select name="%s(zParam)" size="1">
659 for(i=0; i<count(azStyles); i+=3){
660 if( fossil_strcmp(zMimetype,azStyles[i])==0 ){
661 @ <option value="%s(azStyles[i])" selected>%s(azStyles[i+1])</option>
662 }else{
663 @ <option value="%s(azStyles[i])">%s(azStyles[i+1])</option>
@@ -1334,11 +1337,11 @@
1337 ">");
1338 CX("<div class='"
1339 "wikiedit-options flex-container flex-row child-gap-small'>");
1340 CX("<div class='input-with-label'>"
1341 "<label>Mime type</label>");
1342 mimetype_option_menu("text/x-markdown", "mimetype");
1343 CX("</div>");
1344 style_select_list_int("select-font-size",
1345 "editor_font_size", "Editor font size",
1346 NULL/*tooltip*/,
1347 100,
@@ -1535,11 +1538,11 @@
1538 well_formed_wiki_name_rules();
1539 form_begin(0, "%R/wikinew");
1540 @ <p>Name of new wiki page:
1541 @ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br />
1542 @ %z(href("%R/markup_help"))Markup style</a>:
1543 mimetype_option_menu("text/x-markdown", "mimetype");
1544 @ <br /><input type="submit" value="Create" />
1545 @ </p></form>
1546 if( zName[0] ){
1547 @ <p><span class="wikiError">
1548 @ "%h(zName)" is not a valid wiki page name!</span></p>
1549

Keyboard Shortcuts

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