Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Add defenses against cross-site request forgery attacks. |
|---|---|
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA1: |
0be54823ba2b5afe0b058e77376feaa1 |
| User & Date: | drh 2008-10-18 12:55:44.000 |
Context
|
2008-10-18
| ||
| 13:03 | Use sqlite3_snprintf() instead of snprintf() since the latter is not available on all platforms. check-in: 9e80dc66cf user: drh tags: trunk | |
| 12:55 | Add defenses against cross-site request forgery attacks. check-in: 0be54823ba user: drh tags: trunk | |
| 02:27 | Improvements to how ticket changes are displayed in the UI. The hyperlink is show with strick-through if the ticket is closed. The title is shown after the ticket hyperlink. SQL to determine the closed condition and the title can be set in the ticket setup screens. check-in: ac3f1f2ba7 user: drh tags: trunk | |
Changes
Changes to src/admin.c.
| ︙ | ︙ | |||
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
}
admin_prepare_submenu();
style_header("Admin SQL");
@ <h2>SQL:</h2>
@ You can enter only SELECT statements here, and some SQL-side functions
@ are also restricted.<br/>
@ <form action='' method='post'>
@ <textarea style='border:2px solid black' name='sql'
@ cols='80' rows='5'>%h(zSql)</textarea>
@ <br/><input type='submit' name='sql_submit'/> <input type='reset'/>
@ </form>
if( zSql[0] ){
sqlite3_set_authorizer(g.db, selectOnly, 0);
db_generic_query_view(zSql, 0);
sqlite3_set_authorizer(g.db, 0, 0);
}
style_footer();
}
| > > | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
}
admin_prepare_submenu();
style_header("Admin SQL");
@ <h2>SQL:</h2>
@ You can enter only SELECT statements here, and some SQL-side functions
@ are also restricted.<br/>
@ <form action='' method='post'>
login_insert_csrf_secret();
@ <textarea style='border:2px solid black' name='sql'
@ cols='80' rows='5'>%h(zSql)</textarea>
@ <br/><input type='submit' name='sql_submit'/> <input type='reset'/>
@ </form>
if( zSql[0] ){
login_verify_csrf_secret();
sqlite3_set_authorizer(g.db, selectOnly, 0);
db_generic_query_view(zSql, 0);
sqlite3_set_authorizer(g.db, 0, 0);
}
style_footer();
}
|
| ︙ | ︙ |
Changes to src/info.c.
| ︙ | ︙ | |||
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 |
cgi_redirectf("vinfo?name=%d", rid);
}
if( P("apply") ){
Blob ctrl;
char *zDate;
int nChng = 0;
blob_zero(&ctrl);
zDate = db_text(0, "SELECT datetime('now')");
zDate[10] = 'T';
blob_appendf(&ctrl, "D %s\n", zDate);
if( strcmp(zComment,zNewComment)!=0 ){
nChng++;
blob_appendf(&ctrl, "T +comment %s %F\n", zUuid, zNewComment);
| > | 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 |
cgi_redirectf("vinfo?name=%d", rid);
}
if( P("apply") ){
Blob ctrl;
char *zDate;
int nChng = 0;
login_verify_csrf_secret();
blob_zero(&ctrl);
zDate = db_text(0, "SELECT datetime('now')");
zDate[10] = 'T';
blob_appendf(&ctrl, "D %s\n", zDate);
if( strcmp(zComment,zNewComment)!=0 ){
nChng++;
blob_appendf(&ctrl, "T +comment %s %F\n", zUuid, zNewComment);
|
| ︙ | ︙ | |||
1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 |
blob_append(&comment, zNewComment, -1);
zUuid[10] = 0;
style_header("Edit Baseline [%s]", zUuid);
@ <p>Make changes to the User and Comment for baseline
@ [<a href="vinfo?name=%d(rid)">%s(zUuid)</a>] then press the
@ "Apply Changes" button.</p>
@ <form action="%s(g.zBaseURL)/vedit" method="POST">
@ <input type="hidden" name="r" value="%d(rid)">
@ <p>
@ <b>User:</b> <input type="text" name="u" size="20" value="%h(zNewUser)">
@ </p>
@ <p><b>Comment:</b></b><br />
wiki_convert(&comment, 0, WIKI_INLINE);
@ <br /><textarea name="c" rows="10" cols="80">%h(zNewComment)</textarea></p>
| > | 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 |
blob_append(&comment, zNewComment, -1);
zUuid[10] = 0;
style_header("Edit Baseline [%s]", zUuid);
@ <p>Make changes to the User and Comment for baseline
@ [<a href="vinfo?name=%d(rid)">%s(zUuid)</a>] then press the
@ "Apply Changes" button.</p>
@ <form action="%s(g.zBaseURL)/vedit" method="POST">
login_insert_csrf_secret();
@ <input type="hidden" name="r" value="%d(rid)">
@ <p>
@ <b>User:</b> <input type="text" name="u" size="20" value="%h(zNewUser)">
@ </p>
@ <p><b>Comment:</b></b><br />
wiki_convert(&comment, 0, WIKI_INLINE);
@ <br /><textarea name="c" rows="10" cols="80">%h(zNewComment)</textarea></p>
|
| ︙ | ︙ |
Changes to src/login.c.
| ︙ | ︙ | |||
232 233 234 235 236 237 238 |
/*
** This routine examines the login cookie to see if it exists and
** and is valid. If the login cookie checks out, it then sets
** g.zUserUuid appropriately.
**
*/
void login_check_credentials(void){
| | | | | | | > > > | 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 |
/*
** This routine examines the login cookie to see if it exists and
** and is valid. If the login cookie checks out, it then sets
** g.zUserUuid appropriately.
**
*/
void login_check_credentials(void){
int uid = 0; /* User id */
const char *zCookie; /* Text of the login cookie */
const char *zRemoteAddr; /* IP address of the requestor */
const char *zCap = 0; /* Capability string */
const char *zNcap; /* Capabilities of user "nobody" */
const char *zAcap; /* Capabllities of user "anonymous" */
/* Only run this check once. */
if( g.userUid!=0 ) return;
/* If the HTTP connection is coming over 127.0.0.1 and if
** local login is disabled, then there is no need to check
** user credentials.
*/
zRemoteAddr = PD("REMOTE_ADDR","nil");
if( strcmp(zRemoteAddr, "127.0.0.1")==0 && db_get_int("localauth",0)==0 ){
uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
zCap = "s";
g.noPswd = 1;
strcpy(g.zCsrfToken, "localhost");
}
/* Check the login cookie to see if it matches a known valid user.
*/
if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){
if( isdigit(zCookie[0]) ){
uid = db_int(0,
"SELECT uid FROM user"
" WHERE uid=%d"
" AND cookie=%Q"
" AND ipaddr=%Q"
" AND cexpire>julianday('now')",
atoi(zCookie), zCookie, zRemoteAddr
);
}else if( zCookie[0]=='a' ){
uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'");
}
snprintf(g.zCsrfToken, sizeof(g.zCsrfToken), "%.10s", zCookie);
}
if( uid==0 ){
uid = db_int(0, "SELECT uid FROM user WHERE login='nobody'");
if( uid==0 ){
uid = -1;
zCap = "";
}
strcpy(g.zCsrfToken, "none");
}
if( zCap==0 ){
if( uid ){
Stmt s;
db_prepare(&s, "SELECT login, cap FROM user WHERE uid=%d", uid);
if( db_step(&s)==SQLITE_ROW ){
g.zLogin = db_column_malloc(&s, 0);
|
| ︙ | ︙ | |||
430 431 432 433 434 435 436 |
" AND cap LIKE '%%h%%'") ){
const char *zUrl = PD("REQUEST_URI", "index");
@ <p>Many <font color="red">hyperlinks are disabled.</font><br />
@ Use <a href="%s(g.zTop)/login?anon=1&g=%T(zUrl)">anonymous login</a>
@ to enable hyperlinks.</p>
}
}
| > > > > > > > > > > > > > > > > > > > > > > > > | 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 |
" AND cap LIKE '%%h%%'") ){
const char *zUrl = PD("REQUEST_URI", "index");
@ <p>Many <font color="red">hyperlinks are disabled.</font><br />
@ Use <a href="%s(g.zTop)/login?anon=1&g=%T(zUrl)">anonymous login</a>
@ to enable hyperlinks.</p>
}
}
/*
** While rendering a form, call this routine to add the Anti-CSRF token
** as a hidden element of the form.
*/
void login_insert_csrf_secret(void){
@ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)">
}
/*
** Before using the results of a form, first call this routine to verify
** that ths Anti-CSRF token is present and is valid. If the Anti-CSRF token
** is missing or is incorrect, then this emits and error message and never
** returns.
*/
void login_verify_csrf_secret(void){
const char *zCsrf; /* The CSRF secret */
if( g.okCsrf ) return;
if( (zCsrf = P("csrf"))!=0 && strcmp(zCsrf, g.zCsrfToken)==0 ){
g.okCsrf = 1;
return;
}
fossil_fatal("Cross-site request forgery attempt");
}
|
Changes to src/main.c.
| ︙ | ︙ | |||
117 118 119 120 121 122 123 124 125 126 127 128 129 130 | int okNewTkt; /* n: create new tickets */ int okApndTkt; /* c: append to tickets via the web */ int okWrTkt; /* w: make changes to tickets via web */ int okTktFmt; /* t: create new ticket report formats */ int okRdAddr; /* e: read email addresses or other private data */ int okZip; /* z: download zipped artifact via /zip URL */ FILE *fDebug; /* Write debug information here, if the file exists */ /* Storage for the aux() and/or option() SQL function arguments */ int nAux; /* Number of distinct aux() or option() values */ const char *azAuxName[MX_AUX]; /* Name of each aux() or option() value */ char *azAuxParam[MX_AUX]; /* Param of each aux() or option() value */ const char *azAuxVal[MX_AUX]; /* Value of each aux() or option() value */ | > > > > | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | int okNewTkt; /* n: create new tickets */ int okApndTkt; /* c: append to tickets via the web */ int okWrTkt; /* w: make changes to tickets via web */ int okTktFmt; /* t: create new ticket report formats */ int okRdAddr; /* e: read email addresses or other private data */ int okZip; /* z: download zipped artifact via /zip URL */ /* For defense against Cross-site Request Forgery attacks */ char zCsrfToken[12]; /* Value of the anti-CSRF token */ int okCsrf; /* Anti-CSRF token is present and valid */ FILE *fDebug; /* Write debug information here, if the file exists */ /* Storage for the aux() and/or option() SQL function arguments */ int nAux; /* Number of distinct aux() or option() values */ const char *azAuxName[MX_AUX]; /* Name of each aux() or option() value */ char *azAuxParam[MX_AUX]; /* Param of each aux() or option() value */ const char *azAuxVal[MX_AUX]; /* Value of each aux() or option() value */ |
| ︙ | ︙ |
Changes to src/report.c.
| ︙ | ︙ | |||
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 |
rn = atoi(PD("rn","0"));
zTitle = P("t");
zOwner = PD("w",g.zLogin);
z = P("s");
zSQL = z ? trim_string(z) : 0;
zClrKey = trim_string(PD("k",""));
if( rn>0 && P("del2") ){
db_multi_exec("DELETE FROM reportfmt WHERE rn=%d", rn);
cgi_redirect("reportlist");
return;
}else if( rn>0 && P("del1") ){
zTitle = db_text(0, "SELECT title FROM reportfmt "
"WHERE rn=%d", rn);
if( zTitle==0 ) cgi_redirect("reportlist");
style_header("Are You Sure?");
@ <form action="rptedit" method="POST">
@ <p>You are about to delete all traces of the report
@ <strong>%h(zTitle)</strong> from
@ the database. This is an irreversible operation. All records
@ related to this report will be removed and cannot be recovered.</p>
@
@ <input type="hidden" name="rn" value="%d(rn)">
@ <input type="submit" name="del2" value="Delete The Report">
@ <input type="submit" name="can" value="Cancel">
@ </form>
style_footer();
return;
}else if( P("can") ){
/* user cancelled */
cgi_redirect("reportlist");
return;
}
if( zTitle && zSQL ){
if( zSQL[0]==0 ){
zErr = "Please supply an SQL query statement";
}else if( (zTitle = trim_string(zTitle))[0]==0 ){
zErr = "Please supply a title";
}else{
zErr = verify_sql_statement(zSQL);
}
if( zErr==0 ){
if( rn>0 ){
db_multi_exec("UPDATE reportfmt SET title=%Q, sqlcode=%Q,"
" owner=%Q, cols=%Q WHERE rn=%d",
zTitle, zSQL, zOwner, zClrKey, rn);
}else{
db_multi_exec("INSERT INTO reportfmt(title,sqlcode,owner,cols) "
"VALUES(%Q,%Q,%Q,%Q)",
| > > > | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
rn = atoi(PD("rn","0"));
zTitle = P("t");
zOwner = PD("w",g.zLogin);
z = P("s");
zSQL = z ? trim_string(z) : 0;
zClrKey = trim_string(PD("k",""));
if( rn>0 && P("del2") ){
login_verify_csrf_secret();
db_multi_exec("DELETE FROM reportfmt WHERE rn=%d", rn);
cgi_redirect("reportlist");
return;
}else if( rn>0 && P("del1") ){
zTitle = db_text(0, "SELECT title FROM reportfmt "
"WHERE rn=%d", rn);
if( zTitle==0 ) cgi_redirect("reportlist");
style_header("Are You Sure?");
@ <form action="rptedit" method="POST">
@ <p>You are about to delete all traces of the report
@ <strong>%h(zTitle)</strong> from
@ the database. This is an irreversible operation. All records
@ related to this report will be removed and cannot be recovered.</p>
@
@ <input type="hidden" name="rn" value="%d(rn)">
login_insert_csrf_secret();
@ <input type="submit" name="del2" value="Delete The Report">
@ <input type="submit" name="can" value="Cancel">
@ </form>
style_footer();
return;
}else if( P("can") ){
/* user cancelled */
cgi_redirect("reportlist");
return;
}
if( zTitle && zSQL ){
if( zSQL[0]==0 ){
zErr = "Please supply an SQL query statement";
}else if( (zTitle = trim_string(zTitle))[0]==0 ){
zErr = "Please supply a title";
}else{
zErr = verify_sql_statement(zSQL);
}
if( zErr==0 ){
login_verify_csrf_secret();
if( rn>0 ){
db_multi_exec("UPDATE reportfmt SET title=%Q, sqlcode=%Q,"
" owner=%Q, cols=%Q WHERE rn=%d",
zTitle, zSQL, zOwner, zClrKey, rn);
}else{
db_multi_exec("INSERT INTO reportfmt(title,sqlcode,owner,cols) "
"VALUES(%Q,%Q,%Q,%Q)",
|
| ︙ | ︙ | |||
402 403 404 405 406 407 408 409 410 411 412 413 414 415 |
@ <form action="rptedit" method="POST">
@ <input type="hidden" name="rn" value="%d(rn)">
@ <p>Report Title:<br>
@ <input type="text" name="t" value="%h(zTitle)" size="60"></p>
@ <p>Enter a complete SQL query statement against the "TICKET" table:<br>
@ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea>
@ </p>
if( g.okAdmin ){
@ <p>Report owner:
@ <input type="text" name="w" size="20" value="%h(zOwner)">
@ </p>
} else {
@ <input type="hidden" name="w" value="%h(zOwner)">
}
| > | 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 |
@ <form action="rptedit" method="POST">
@ <input type="hidden" name="rn" value="%d(rn)">
@ <p>Report Title:<br>
@ <input type="text" name="t" value="%h(zTitle)" size="60"></p>
@ <p>Enter a complete SQL query statement against the "TICKET" table:<br>
@ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea>
@ </p>
login_insert_csrf_secret();
if( g.okAdmin ){
@ <p>Report owner:
@ <input type="text" name="w" size="20" value="%h(zOwner)">
@ </p>
} else {
@ <input type="hidden" name="w" value="%h(zOwner)">
}
|
| ︙ | ︙ |
Changes to src/setup.c.
| ︙ | ︙ | |||
284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
@ <font color="red">Login "%h(zLogin)" is already used by a different
@ user.</font>
@
@ <p><a href="setup_uedit?id=%d(uid))>[Bummer]</a></p>
style_footer();
return;
}
db_multi_exec(
"REPLACE INTO user(uid,login,info,pw,cap) "
"VALUES(nullif(%d,0),%Q,%Q,%Q,'%s')",
uid, P("login"), P("info"), zPw, zCap
);
cgi_redirect("setup_ulist");
return;
| > | 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
@ <font color="red">Login "%h(zLogin)" is already used by a different
@ user.</font>
@
@ <p><a href="setup_uedit?id=%d(uid))>[Bummer]</a></p>
style_footer();
return;
}
login_verify_csrf_secret();
db_multi_exec(
"REPLACE INTO user(uid,login,info,pw,cap) "
"VALUES(nullif(%d,0),%Q,%Q,%Q,'%s')",
uid, P("login"), P("info"), zPw, zCap
);
cgi_redirect("setup_ulist");
return;
|
| ︙ | ︙ | |||
338 339 340 341 342 343 344 345 346 347 348 349 350 351 |
if( uid ){
style_header(mprintf("Edit User %h", zLogin));
}else{
style_header("Add A New User");
}
@ <table align="left" hspace="20" vspace="10"><tr><td>
@ <form action="%s(g.zPath)" method="POST">
@ <table>
@ <tr>
@ <td align="right"><nobr>User ID:</nobr></td>
if( uid ){
@ <td>%d(uid) <input type="hidden" name="id" value="%d(uid)"></td>
}else{
@ <td>(new user)<input type="hidden" name="id" value=0></td>
| > | 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 |
if( uid ){
style_header(mprintf("Edit User %h", zLogin));
}else{
style_header("Add A New User");
}
@ <table align="left" hspace="20" vspace="10"><tr><td>
@ <form action="%s(g.zPath)" method="POST">
login_insert_csrf_secret();
@ <table>
@ <tr>
@ <td align="right"><nobr>User ID:</nobr></td>
if( uid ){
@ <td>%d(uid) <input type="hidden" name="id" value="%d(uid)"></td>
}else{
@ <td>(new user)<input type="hidden" name="id" value=0></td>
|
| ︙ | ︙ | |||
541 542 543 544 545 546 547 548 549 550 551 552 553 554 |
}
if( zQ==0 && P("submit") ){
zQ = "off";
}
if( zQ ){
int iQ = strcmp(zQ,"on")==0 || atoi(zQ);
if( iQ!=iVal ){
db_set(zVar, iQ ? "1" : "0", 0);
iVal = iQ;
}
}
if( iVal ){
@ <input type="checkbox" name="%s(zQParm)" checked><b>%s(zLabel)</b></input>
}else{
| > | 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 |
}
if( zQ==0 && P("submit") ){
zQ = "off";
}
if( zQ ){
int iQ = strcmp(zQ,"on")==0 || atoi(zQ);
if( iQ!=iVal ){
login_verify_csrf_secret();
db_set(zVar, iQ ? "1" : "0", 0);
iVal = iQ;
}
}
if( iVal ){
@ <input type="checkbox" name="%s(zQParm)" checked><b>%s(zLabel)</b></input>
}else{
|
| ︙ | ︙ | |||
565 566 567 568 569 570 571 572 573 574 575 576 577 578 |
const char *zVar, /* The corresponding row in the VAR table */
const char *zQParm, /* The query parameter */
char *zDflt /* Default value if VAR table entry does not exist */
){
const char *zVal = db_get(zVar, zDflt);
const char *zQ = P(zQParm);
if( zQ && strcmp(zQ,zVal)!=0 ){
db_set(zVar, zQ, 0);
zVal = zQ;
}
@ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)">
@ <b>%s(zLabel)</b>
}
| > | 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 |
const char *zVar, /* The corresponding row in the VAR table */
const char *zQParm, /* The query parameter */
char *zDflt /* Default value if VAR table entry does not exist */
){
const char *zVal = db_get(zVar, zDflt);
const char *zQ = P(zQParm);
if( zQ && strcmp(zQ,zVal)!=0 ){
login_verify_csrf_secret();
db_set(zVar, zQ, 0);
zVal = zQ;
}
@ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)">
@ <b>%s(zLabel)</b>
}
|
| ︙ | ︙ | |||
586 587 588 589 590 591 592 593 594 595 596 597 598 599 |
const char *zVar, /* The corresponding row in the VAR table */
const char *zQP, /* The query parameter */
const char *zDflt /* Default value if VAR table entry does not exist */
){
const char *z = db_get(zVar, (char*)zDflt);
const char *zQ = P(zQP);
if( zQ && strcmp(zQ,z)!=0 ){
db_set(zVar, zQ, 0);
z = zQ;
}
if( rows>0 && cols>0 ){
@ <textarea name="%s(zQP)" rows="%d(rows)" cols="%d(cols)">%h(z)</textarea>
@ <b>%s(zLabel)</b>
}
| > | 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 |
const char *zVar, /* The corresponding row in the VAR table */
const char *zQP, /* The query parameter */
const char *zDflt /* Default value if VAR table entry does not exist */
){
const char *z = db_get(zVar, (char*)zDflt);
const char *zQ = P(zQP);
if( zQ && strcmp(zQ,z)!=0 ){
login_verify_csrf_secret();
db_set(zVar, zQ, 0);
z = zQ;
}
if( rows>0 && cols>0 ){
@ <textarea name="%s(zQP)" rows="%d(rows)" cols="%d(cols)">%h(z)</textarea>
@ <b>%s(zLabel)</b>
}
|
| ︙ | ︙ | |||
608 609 610 611 612 613 614 |
if( !g.okSetup ){
login_needed();
}
style_header("Access Control Settings");
db_begin_transaction();
@ <form action="%s(g.zBaseURL)/setup_access" method="POST">
| | | 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 |
if( !g.okSetup ){
login_needed();
}
style_header("Access Control Settings");
db_begin_transaction();
@ <form action="%s(g.zBaseURL)/setup_access" method="POST">
login_insert_csrf_secret();
@ <hr>
onoff_attribute("Require password for local access",
"localauth", "localauth", 1);
@ <p>When enabled, the password sign-in is required for
@ web access coming from 127.0.0.1. When disabled, web access
@ from 127.0.0.1 is allows without any login - the user id is selected
@ from the ~/.fossil database. Password login is always required
|
| ︙ | ︙ | |||
659 660 661 662 663 664 665 666 667 668 669 670 671 672 |
if( !g.okSetup ){
login_needed();
}
style_header("Timeline Display Preferences");
db_begin_transaction();
@ <form action="%s(g.zBaseURL)/setup_timeline" method="POST">
@ <hr>
onoff_attribute("Block markup in timeline",
"timeline-block-markup", "tbm", 0);
@ <p>In timeline displays, check-in comments can be displayed with or
@ without block markup (paragraphs, tables, etc.)</p>
| > | 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 |
if( !g.okSetup ){
login_needed();
}
style_header("Timeline Display Preferences");
db_begin_transaction();
@ <form action="%s(g.zBaseURL)/setup_timeline" method="POST">
login_insert_csrf_secret();
@ <hr>
onoff_attribute("Block markup in timeline",
"timeline-block-markup", "tbm", 0);
@ <p>In timeline displays, check-in comments can be displayed with or
@ without block markup (paragraphs, tables, etc.)</p>
|
| ︙ | ︙ | |||
691 692 693 694 695 696 697 698 699 700 701 702 703 704 |
if( !g.okSetup ){
login_needed();
}
style_header("WWW Configuration");
db_begin_transaction();
@ <form action="%s(g.zBaseURL)/setup_config" method="POST">
@ <hr />
entry_attribute("Project Name", 60, "project-name", "pn", "");
@ <p>Give your project a name so visitors know what this site is about.
@ The project name will also be used as the RSS feed title.</p>
@ <hr />
textarea_attribute("Project Description", 5, 60,
"project-description", "pd", "");
| > | 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 |
if( !g.okSetup ){
login_needed();
}
style_header("WWW Configuration");
db_begin_transaction();
@ <form action="%s(g.zBaseURL)/setup_config" method="POST">
login_insert_csrf_secret();
@ <hr />
entry_attribute("Project Name", 60, "project-name", "pn", "");
@ <p>Give your project a name so visitors know what this site is about.
@ The project name will also be used as the RSS feed title.</p>
@ <hr />
textarea_attribute("Project Description", 5, 60,
"project-description", "pd", "");
|
| ︙ | ︙ | |||
733 734 735 736 737 738 739 740 741 742 743 744 745 746 |
void setup_editcss(void){
login_check_credentials();
if( !g.okSetup ){
login_needed();
}
style_header("Edit CSS");
@ <form action="%s(g.zBaseURL)/setup_editcss" method="POST">
@ Edit the CSS:<br />
textarea_attribute("", 40, 80, "css", "css", zDefaultCSS);
@ <br />
@ <input type="submit" name="submit" value="Apply Changes">
@ </form>
@ <hr>
@ Here is the default CSS:
| > | 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 |
void setup_editcss(void){
login_check_credentials();
if( !g.okSetup ){
login_needed();
}
style_header("Edit CSS");
@ <form action="%s(g.zBaseURL)/setup_editcss" method="POST">
login_insert_csrf_secret();
@ Edit the CSS:<br />
textarea_attribute("", 40, 80, "css", "css", zDefaultCSS);
@ <br />
@ <input type="submit" name="submit" value="Apply Changes">
@ </form>
@ <hr>
@ Here is the default CSS:
|
| ︙ | ︙ | |||
763 764 765 766 767 768 769 770 771 772 773 774 775 776 |
db_multi_exec("DELETE FROM config WHERE name='header'");
cgi_replace_parameter("header", zDefaultHeader);
}else{
textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader);
}
style_header("Edit Page Header");
@ <form action="%s(g.zBaseURL)/setup_header" method="POST">
@ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to
@ generate the beginning of every page through start of the main
@ menu.</p>
textarea_attribute("", 40, 80, "header", "header", zDefaultHeader);
@ <br />
@ <input type="submit" name="submit" value="Apply Changes">
@ <input type="submit" name="clear" value="Revert To Default">
| > | 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 |
db_multi_exec("DELETE FROM config WHERE name='header'");
cgi_replace_parameter("header", zDefaultHeader);
}else{
textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader);
}
style_header("Edit Page Header");
@ <form action="%s(g.zBaseURL)/setup_header" method="POST">
login_insert_csrf_secret();
@ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to
@ generate the beginning of every page through start of the main
@ menu.</p>
textarea_attribute("", 40, 80, "header", "header", zDefaultHeader);
@ <br />
@ <input type="submit" name="submit" value="Apply Changes">
@ <input type="submit" name="clear" value="Revert To Default">
|
| ︙ | ︙ | |||
797 798 799 800 801 802 803 804 805 806 807 808 809 810 |
db_multi_exec("DELETE FROM config WHERE name='footer'");
cgi_replace_parameter("footer", zDefaultFooter);
}else{
textarea_attribute(0, 0, 0, "footer", "footer", zDefaultFooter);
}
style_header("Edit Page Footer");
@ <form action="%s(g.zBaseURL)/setup_footer" method="POST">
@ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to
@ generate the end of every page.</p>
textarea_attribute("", 20, 80, "footer", "footer", zDefaultFooter);
@ <br />
@ <input type="submit" name="submit" value="Apply Changes">
@ <input type="submit" name="clear" value="Revert To Default">
@ </form>
| > | 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 |
db_multi_exec("DELETE FROM config WHERE name='footer'");
cgi_replace_parameter("footer", zDefaultFooter);
}else{
textarea_attribute(0, 0, 0, "footer", "footer", zDefaultFooter);
}
style_header("Edit Page Footer");
@ <form action="%s(g.zBaseURL)/setup_footer" method="POST">
login_insert_csrf_secret();
@ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to
@ generate the end of every page.</p>
textarea_attribute("", 20, 80, "footer", "footer", zDefaultFooter);
@ <br />
@ <input type="submit" name="submit" value="Apply Changes">
@ <input type="submit" name="clear" value="Revert To Default">
@ </form>
|
| ︙ | ︙ |
Changes to src/shun.c.
| ︙ | ︙ | |||
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
memcpy(zCanonical, zUuid, UUID_SIZE+1);
canonical16(zCanonical, UUID_SIZE);
zUuid = zCanonical;
}
}
style_header("Shunned Artifacts");
if( zUuid && P("sub") ){
db_multi_exec("DELETE FROM shun WHERE uuid='%s'", zUuid);
if( db_exists("SELECT 1 FROM blob WHERE uuid='%s'", zUuid) ){
@ <p><font color="blue">Artifact
@ <a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a> is no
@ longer being shunned.</font></p>
}else{
@ <p><font color="blue">Artifact %s(zUuid)</a> will no longer
@ be shunned. But it does not exist in the repository. It
@ may be necessary to rebuild the repository using the
@ <b>fossil rebuild</b> command-line before the artifact content
@ can pulled in from other respositories.</font></p>
}
}
if( zUuid && P("add") ){
db_multi_exec("INSERT OR IGNORE INTO shun VALUES('%s')", zUuid);
@ <p><font color="blue">Artifact
@ <a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a> has been
@ shunned. It will no longer be pushed.
@ It will be removed from the repository the next time the respository
@ is rebuilt using the <b>fossil rebuild</b> command-line</font></p>
}
| > > | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
memcpy(zCanonical, zUuid, UUID_SIZE+1);
canonical16(zCanonical, UUID_SIZE);
zUuid = zCanonical;
}
}
style_header("Shunned Artifacts");
if( zUuid && P("sub") ){
login_verify_csrf_secret();
db_multi_exec("DELETE FROM shun WHERE uuid='%s'", zUuid);
if( db_exists("SELECT 1 FROM blob WHERE uuid='%s'", zUuid) ){
@ <p><font color="blue">Artifact
@ <a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a> is no
@ longer being shunned.</font></p>
}else{
@ <p><font color="blue">Artifact %s(zUuid)</a> will no longer
@ be shunned. But it does not exist in the repository. It
@ may be necessary to rebuild the repository using the
@ <b>fossil rebuild</b> command-line before the artifact content
@ can pulled in from other respositories.</font></p>
}
}
if( zUuid && P("add") ){
login_verify_csrf_secret();
db_multi_exec("INSERT OR IGNORE INTO shun VALUES('%s')", zUuid);
@ <p><font color="blue">Artifact
@ <a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a> has been
@ shunned. It will no longer be pushed.
@ It will be removed from the repository the next time the respository
@ is rebuilt using the <b>fossil rebuild</b> command-line</font></p>
}
|
| ︙ | ︙ | |||
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
@ spam added to Wiki, files that violate copyright or patent agreements,
@ or artifacts that by design or accident interfere with the processing
@ of the repository. Do not shun artifacts merely to remove them from
@ sight - set the "hidden" tag on such artifacts instead.</p>
@
@ <blockquote>
@ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
@ <input type="text" name="uuid" value="%h(PD("shun",""))" size="50">
@ <input type="submit" name="add" value="Shun">
@ </form>
@ </blockquote>
@
@ <p>Enter the UUID of a previous shunned artifact to cause it to be
@ accepted again in the repository. The artifact content is not
@ restored because the content is unknown. The only change is that
@ the formerly shunned artifact will be accepted on subsequent sync
@ operations.</p>
@
@ <blockquote>
@ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
@ <input type="text" name="uuid" size="50">
@ <input type="submit" name="sub" value="Accept">
@ </form>
@ </blockquote>
style_footer();
}
| > > | 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
@ spam added to Wiki, files that violate copyright or patent agreements,
@ or artifacts that by design or accident interfere with the processing
@ of the repository. Do not shun artifacts merely to remove them from
@ sight - set the "hidden" tag on such artifacts instead.</p>
@
@ <blockquote>
@ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
login_insert_csrf_secret();
@ <input type="text" name="uuid" value="%h(PD("shun",""))" size="50">
@ <input type="submit" name="add" value="Shun">
@ </form>
@ </blockquote>
@
@ <p>Enter the UUID of a previous shunned artifact to cause it to be
@ accepted again in the repository. The artifact content is not
@ restored because the content is unknown. The only change is that
@ the formerly shunned artifact will be accepted on subsequent sync
@ operations.</p>
@
@ <blockquote>
@ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
login_insert_csrf_secret();
@ <input type="text" name="uuid" size="50">
@ <input type="submit" name="sub" value="Accept">
@ </form>
@ </blockquote>
style_footer();
}
|
| ︙ | ︙ |
Changes to src/tkt.c.
| ︙ | ︙ | |||
382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
){
char *zDate;
const char *zUuid;
int i;
int rid;
Blob tktchng, cksum;
zUuid = (const char *)pUuid;
blob_zero(&tktchng);
zDate = db_text(0, "SELECT datetime('now')");
zDate[10] = 'T';
blob_appendf(&tktchng, "D %s\n", zDate);
free(zDate);
for(i=0; i<nField; i++){
| > | 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 |
){
char *zDate;
const char *zUuid;
int i;
int rid;
Blob tktchng, cksum;
login_verify_csrf_secret();
zUuid = (const char *)pUuid;
blob_zero(&tktchng);
zDate = db_text(0, "SELECT datetime('now')");
zDate[10] = 'T';
blob_appendf(&tktchng, "D %s\n", zDate);
free(zDate);
for(i=0; i<nField; i++){
|
| ︙ | ︙ | |||
465 466 467 468 469 470 471 472 473 474 475 476 477 478 |
}
style_header("New Ticket");
ticket_init();
getAllTicketFields();
initializeVariablesFromDb();
initializeVariablesFromCGI();
@ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
zScript = ticket_newpage_code();
Th_Store("login", g.zLogin);
Th_Store("date", db_text(0, "SELECT datetime('now')"));
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
(void*)&zNewUuid, 0);
if( Th_Render(zScript)==TH_RETURN && zNewUuid ){
cgi_redirect(mprintf("%s/tktview/%s", g.zBaseURL, zNewUuid));
| > | 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 |
}
style_header("New Ticket");
ticket_init();
getAllTicketFields();
initializeVariablesFromDb();
initializeVariablesFromCGI();
@ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
login_insert_csrf_secret();
zScript = ticket_newpage_code();
Th_Store("login", g.zLogin);
Th_Store("date", db_text(0, "SELECT datetime('now')"));
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
(void*)&zNewUuid, 0);
if( Th_Render(zScript)==TH_RETURN && zNewUuid ){
cgi_redirect(mprintf("%s/tktview/%s", g.zBaseURL, zNewUuid));
|
| ︙ | ︙ | |||
526 527 528 529 530 531 532 533 534 535 536 537 538 539 |
}
ticket_init();
getAllTicketFields();
initializeVariablesFromCGI();
initializeVariablesFromDb();
@ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
@ <input type="hidden" name="name" value="%s(zName)">
zScript = ticket_editpage_code();
Th_Store("login", g.zLogin);
Th_Store("date", db_text(0, "SELECT datetime('now')"));
Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
if( Th_Render(zScript)==TH_RETURN && zName ){
cgi_redirect(mprintf("%s/tktview/%s", g.zBaseURL, zName));
| > | 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 |
}
ticket_init();
getAllTicketFields();
initializeVariablesFromCGI();
initializeVariablesFromDb();
@ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
@ <input type="hidden" name="name" value="%s(zName)">
login_insert_csrf_secret();
zScript = ticket_editpage_code();
Th_Store("login", g.zLogin);
Th_Store("date", db_text(0, "SELECT datetime('now')"));
Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
if( Th_Render(zScript)==TH_RETURN && zName ){
cgi_redirect(mprintf("%s/tktview/%s", g.zBaseURL, zName));
|
| ︙ | ︙ |
Changes to src/tktsetup.c.
| ︙ | ︙ | |||
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
isSubmit = P("submit")!=0;
z = P("x");
if( z==0 ){
z = db_get(zDbField, (char*)zDfltValue);
}
style_header("Edit %s", zTitle);
if( P("clear")!=0 ){
db_unset(zDbField, 0);
if( xRebuild ) xRebuild();
z = zDfltValue;
}else if( isSubmit ){
char *zErr = 0;
if( xText && (zErr = xText(z))!=0 ){
@ <p><font color="red"><b>ERROR: %h(zErr)</b></font></p>
}else{
db_set(zDbField, z, 0);
if( xRebuild ) xRebuild();
cgi_redirect("tktsetup");
}
}
@ <form action="%s(g.zBaseURL)/%s(g.zPath)" method="POST">
@ <p>%s(zDesc)</p>
@ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea>
@ <blockquote>
@ <input type="submit" name="submit" value="Apply Changes">
@ <input type="submit" name="clear" value="Revert To Default">
@ <input type="submit" name="setup" value="Cancel">
@ </blockquote>
| > > > | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
isSubmit = P("submit")!=0;
z = P("x");
if( z==0 ){
z = db_get(zDbField, (char*)zDfltValue);
}
style_header("Edit %s", zTitle);
if( P("clear")!=0 ){
login_verify_csrf_secret();
db_unset(zDbField, 0);
if( xRebuild ) xRebuild();
z = zDfltValue;
}else if( isSubmit ){
char *zErr = 0;
login_verify_csrf_secret();
if( xText && (zErr = xText(z))!=0 ){
@ <p><font color="red"><b>ERROR: %h(zErr)</b></font></p>
}else{
db_set(zDbField, z, 0);
if( xRebuild ) xRebuild();
cgi_redirect("tktsetup");
}
}
@ <form action="%s(g.zBaseURL)/%s(g.zPath)" method="POST">
login_insert_csrf_secret();
@ <p>%s(zDesc)</p>
@ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea>
@ <blockquote>
@ <input type="submit" name="submit" value="Apply Changes">
@ <input type="submit" name="clear" value="Revert To Default">
@ <input type="submit" name="setup" value="Cancel">
@ </blockquote>
|
| ︙ | ︙ | |||
632 633 634 635 636 637 638 639 640 641 642 643 644 645 |
if( P("setup") ){
cgi_redirect("tktsetup");
}
style_header("Ticket Display On Timelines");
db_begin_transaction();
@ <form action="%s(g.zBaseURL)/tktsetup_timeline" method="POST">
@ <hr>
entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", "title");
@ <p>An SQL expression in a query against the TICKET table that will
@ return the title of the ticket for display purposes after hyperlinks to
@ that ticket</p>
| > | 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 |
if( P("setup") ){
cgi_redirect("tktsetup");
}
style_header("Ticket Display On Timelines");
db_begin_transaction();
@ <form action="%s(g.zBaseURL)/tktsetup_timeline" method="POST">
login_insert_csrf_secret();
@ <hr>
entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", "title");
@ <p>An SQL expression in a query against the TICKET table that will
@ return the title of the ticket for display purposes after hyperlinks to
@ that ticket</p>
|
| ︙ | ︙ |
Changes to src/wiki.c.
| ︙ | ︙ | |||
249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
Blob cksum;
int nrid;
blob_zero(&wiki);
db_begin_transaction();
if( isSandbox ){
db_set("sandbox",zBody,0);
}else{
zDate = db_text(0, "SELECT datetime('now')");
zDate[10] = 'T';
blob_appendf(&wiki, "D %s\n", zDate);
free(zDate);
blob_appendf(&wiki, "L %F\n", zPageName);
if( rid ){
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
| > | 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
Blob cksum;
int nrid;
blob_zero(&wiki);
db_begin_transaction();
if( isSandbox ){
db_set("sandbox",zBody,0);
}else{
login_verify_csrf_secret();
zDate = db_text(0, "SELECT datetime('now')");
zDate[10] = 'T';
blob_appendf(&wiki, "D %s\n", zDate);
free(zDate);
blob_appendf(&wiki, "L %F\n", zPageName);
if( rid ){
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
|
| ︙ | ︙ | |||
298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
}
for(n=2, z=zBody; z[0]; z++){
if( z[0]=='\n' ) n++;
}
if( n<20 ) n = 20;
if( n>40 ) n = 40;
@ <form method="POST" action="%s(g.zBaseURL)/wikiedit">
@ <input type="hidden" name="name" value="%h(zPageName)">
@ <textarea name="w" class="wikiedit" cols="80"
@ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
@ <br>
@ <input type="submit" name="preview" value="Preview Your Changes">
@ <input type="submit" name="submit" value="Apply These Changes">
@ <input type="submit" name="cancel" value="Cancel">
| > | 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
}
for(n=2, z=zBody; z[0]; z++){
if( z[0]=='\n' ) n++;
}
if( n<20 ) n = 20;
if( n>40 ) n = 40;
@ <form method="POST" action="%s(g.zBaseURL)/wikiedit">
login_insert_csrf_secret();
@ <input type="hidden" name="name" value="%h(zPageName)">
@ <textarea name="w" class="wikiedit" cols="80"
@ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
@ <br>
@ <input type="submit" name="preview" value="Preview Your Changes">
@ <input type="submit" name="submit" value="Apply These Changes">
@ <input type="submit" name="cancel" value="Cancel">
|
| ︙ | ︙ | |||
382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
blob_zero(&body);
if( isSandbox ){
blob_appendf(&body, db_get("sandbox",""));
appendRemark(&body);
db_set("sandbox", blob_str(&body), 0);
}else{
content_get(rid, &content);
manifest_parse(&m, &content);
if( m.type==CFTYPE_WIKI ){
blob_append(&body, m.zWiki, -1);
}
manifest_clear(&m);
blob_zero(&wiki);
| > | 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 |
blob_zero(&body);
if( isSandbox ){
blob_appendf(&body, db_get("sandbox",""));
appendRemark(&body);
db_set("sandbox", blob_str(&body), 0);
}else{
login_verify_csrf_secret();
content_get(rid, &content);
manifest_parse(&m, &content);
if( m.type==CFTYPE_WIKI ){
blob_append(&body, m.zWiki, -1);
}
manifest_clear(&m);
blob_zero(&wiki);
|
| ︙ | ︙ | |||
433 434 435 436 437 438 439 440 441 442 443 444 445 446 |
@ Preview:<hr>
wiki_convert(&preview, 0, 0);
@ <hr>
blob_reset(&preview);
}
zUser = PD("u", g.zLogin);
@ <form method="POST" action="%s(g.zBaseURL)/wikiappend">
@ <input type="hidden" name="name" value="%h(zPageName)">
@ Your Name:
@ <input type="text" name="u" size="20" value="%h(zUser)"><br>
@ Comment to append:<br>
@ <textarea name="r" class="wikiedit" cols="80"
@ rows="10" wrap="virtual">%h(PD("r",""))</textarea>
@ <br>
| > | 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 |
@ Preview:<hr>
wiki_convert(&preview, 0, 0);
@ <hr>
blob_reset(&preview);
}
zUser = PD("u", g.zLogin);
@ <form method="POST" action="%s(g.zBaseURL)/wikiappend">
login_insert_csrf_secret();
@ <input type="hidden" name="name" value="%h(zPageName)">
@ Your Name:
@ <input type="text" name="u" size="20" value="%h(zUser)"><br>
@ Comment to append:<br>
@ <textarea name="r" class="wikiedit" cols="80"
@ rows="10" wrap="virtual">%h(PD("r",""))</textarea>
@ <br>
|
| ︙ | ︙ |