Fossil SCM

Doc additions and add sensible type-dependent redirect defaults for /attachadd.

stephan 2026-06-04 08:34 UTC attach-v2
Commit ae89edbc9340ec70addc33fadc5cb3014068e8ebc6232145d155c0b15f7bad0e
2 files changed +17 -11 +1 -1
+17 -11
--- src/attach.c
+++ src/attach.c
@@ -446,10 +446,16 @@
446446
attach_put(&manifest, rid, needModerator);
447447
assert( blob_is_reset(&manifest) );
448448
db_end_transaction(0);
449449
}
450450
451
+/*
452
+** Renders the "legacy" (static) /attachadd form. One of the first
453
+** four arguments must be non-NULL and the other three must be NULL.
454
+** zComment may be NULL, as may zFrom. See the call sites for more
455
+** context.
456
+*/
451457
static void attach_render_legacy_form(const char *zForumPost,
452458
const char *zTechNote,
453459
const char *zTicket,
454460
const char *zWikiPage,
455461
const char *zComment,
@@ -493,22 +499,22 @@
493499
**
494500
** Or the "version 2" interface:
495501
**
496502
** target=ATTACHMENT_TARGET
497503
**
498
-** Behaves exactly like attachaddV2_page().
504
+** Behaves as documented for attachaddV2_page().
499505
*/
500506
void attachadd_page(void){
501507
const char *zPage;
502508
const char *zForumPost;
503509
const char *zTkt;
504510
const char *zTechNote;
505
- const char *zFrom;
506511
const char *aContent;
507512
const char *zName;
508513
const char *zComment;
509514
const char *zTarget;
515
+ const char *zFrom; /* Origin page - redirect here after saving */
510516
char * zTo = 0; /* Optionally redirect here after saving */
511517
char *zTargetType = 0;
512518
char *zExtraFree = 0;
513519
int szContent;
514520
int goodCaptcha = 1;
@@ -549,25 +555,23 @@
549555
"forum posts.");
550556
}
551557
zTarget = zExtraFree = rid_to_uuid(fpid);
552558
zTargetType = mprintf("Forum post <a href=\"%R/forumpost/%S\">%h</a>",
553559
zTarget, zForumPost);
554
- zTo = 1
555
- ? mprintf("%R/forumpost/%S", zTarget)
556
- : mprintf("%R/attachview?forumpost=%T&file=%T",
557
- zTarget, zName);
560
+ zTo = zFrom ? 0 : mprintf("%R/forumpost/%S", zTarget);
558561
}else if( zPage ){
559562
if( g.perm.ApndWiki==0 || g.perm.Attach==0 ){
560563
login_needed(g.anon.ApndWiki && g.anon.Attach);
561564
return;
562565
}
563566
if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", zPage) ){
564567
fossil_redirect_home();
565568
}
566569
zTarget = zPage;
567
- zTargetType = mprintf("Wiki Page <a href=\"%R/wiki?name=%h\">%h</a>",
570
+ zTargetType = mprintf("Wiki Page <a href=\"%R/wiki?name=%t\">%h</a>",
568571
zPage, zPage);
572
+ zTo = zFrom ? 0 : mprintf("%R/wiki?name=%T", zTarget);
569573
}else if ( zTechNote ){
570574
if( g.perm.Write==0 || g.perm.ApndWiki==0 || g.perm.Attach==0 ){
571575
login_needed(g.anon.Write && g.anon.ApndWiki && g.anon.Attach);
572576
return;
573577
}
@@ -577,11 +581,11 @@
577581
if( zTechNote==0) fossil_redirect_home();
578582
}
579583
zTarget = zTechNote;
580584
zTargetType = mprintf("Tech Note <a href=\"%R/technote/%s\">%S</a>",
581585
zTechNote, zTechNote);
582
-
586
+ zTo = zFrom ? 0 : mprintf("%R/technote/%S", zTarget);
583587
}else{
584588
assert( zTkt );
585589
if( g.perm.ApndTkt==0 || g.perm.Attach==0 ){
586590
login_needed(g.anon.ApndTkt && g.anon.Attach);
587591
return;
@@ -590,12 +594,13 @@
590594
zTkt = db_text(0, "SELECT substr(tagname,5) FROM tag"
591595
" WHERE tagname GLOB 'tkt-%q*'", zTkt);
592596
if( zTkt==0 ) fossil_redirect_home();
593597
}
594598
zTarget = zTkt;
595
- zTargetType = mprintf("Ticket <a href=\"%R/tktview/%s\">%S</a>",
599
+ zTargetType = mprintf("Ticket <a href=\"%R/tktview/%S\">%S</a>",
596600
zTkt, zTkt);
601
+ zTo = zFrom ? 0 : mprintf("%R/tktview/%S", zTarget);
597602
}
598603
szLimit = db_get_int("attachment-size-limit", 0);
599604
if( szContent<0 || (szLimit && szContent>szLimit) ){
600605
/* This check must be done late so that zTargetType is set up. */
601606
@ <p class="generalError">Attachment %h(zName) is too large.
@@ -636,11 +641,12 @@
636641
** file1..fileN=FILE_OBJECTS
637642
** dryrun=0|1
638643
**
639644
** Each posted file in the set file1..fileN gets attached to the
640645
** given target, permissions permitting. If dryrun>0 then the change
641
-** is rolled back instead of committed.
646
+** is rolled back instead of committed. target=X must refer to a full
647
+** target ID, not a prefix.
642648
**
643649
** Responds with JSON: an empty object on success and
644650
** {error:"message"} on error. The on-success response structure is
645651
** subject to amendment.
646652
*/
@@ -898,11 +904,11 @@
898904
if( !goodCaptcha ){
899905
@ <p class="generalError">Error: Incorrect security code.</p>
900906
}
901907
@ <h2>Attachments for %s(zTargetType)</h2>
902908
if(1){
903
- /* noscript fallback is completely tested */
909
+ /* noscript fallback is completely untested */
904910
@ <noscript>
905911
attach_render_legacy_form(noJsArgs[0], noJsArgs[1], noJsArgs[2],
906912
noJsArgs[3], 0,
907913
zFrom ? zFrom : mprintf("%R/home"));
908914
@ </noscript>
909915
--- src/attach.c
+++ src/attach.c
@@ -446,10 +446,16 @@
446 attach_put(&manifest, rid, needModerator);
447 assert( blob_is_reset(&manifest) );
448 db_end_transaction(0);
449 }
450
 
 
 
 
 
 
451 static void attach_render_legacy_form(const char *zForumPost,
452 const char *zTechNote,
453 const char *zTicket,
454 const char *zWikiPage,
455 const char *zComment,
@@ -493,22 +499,22 @@
493 **
494 ** Or the "version 2" interface:
495 **
496 ** target=ATTACHMENT_TARGET
497 **
498 ** Behaves exactly like attachaddV2_page().
499 */
500 void attachadd_page(void){
501 const char *zPage;
502 const char *zForumPost;
503 const char *zTkt;
504 const char *zTechNote;
505 const char *zFrom;
506 const char *aContent;
507 const char *zName;
508 const char *zComment;
509 const char *zTarget;
 
510 char * zTo = 0; /* Optionally redirect here after saving */
511 char *zTargetType = 0;
512 char *zExtraFree = 0;
513 int szContent;
514 int goodCaptcha = 1;
@@ -549,25 +555,23 @@
549 "forum posts.");
550 }
551 zTarget = zExtraFree = rid_to_uuid(fpid);
552 zTargetType = mprintf("Forum post <a href=\"%R/forumpost/%S\">%h</a>",
553 zTarget, zForumPost);
554 zTo = 1
555 ? mprintf("%R/forumpost/%S", zTarget)
556 : mprintf("%R/attachview?forumpost=%T&file=%T",
557 zTarget, zName);
558 }else if( zPage ){
559 if( g.perm.ApndWiki==0 || g.perm.Attach==0 ){
560 login_needed(g.anon.ApndWiki && g.anon.Attach);
561 return;
562 }
563 if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", zPage) ){
564 fossil_redirect_home();
565 }
566 zTarget = zPage;
567 zTargetType = mprintf("Wiki Page <a href=\"%R/wiki?name=%h\">%h</a>",
568 zPage, zPage);
 
569 }else if ( zTechNote ){
570 if( g.perm.Write==0 || g.perm.ApndWiki==0 || g.perm.Attach==0 ){
571 login_needed(g.anon.Write && g.anon.ApndWiki && g.anon.Attach);
572 return;
573 }
@@ -577,11 +581,11 @@
577 if( zTechNote==0) fossil_redirect_home();
578 }
579 zTarget = zTechNote;
580 zTargetType = mprintf("Tech Note <a href=\"%R/technote/%s\">%S</a>",
581 zTechNote, zTechNote);
582
583 }else{
584 assert( zTkt );
585 if( g.perm.ApndTkt==0 || g.perm.Attach==0 ){
586 login_needed(g.anon.ApndTkt && g.anon.Attach);
587 return;
@@ -590,12 +594,13 @@
590 zTkt = db_text(0, "SELECT substr(tagname,5) FROM tag"
591 " WHERE tagname GLOB 'tkt-%q*'", zTkt);
592 if( zTkt==0 ) fossil_redirect_home();
593 }
594 zTarget = zTkt;
595 zTargetType = mprintf("Ticket <a href=\"%R/tktview/%s\">%S</a>",
596 zTkt, zTkt);
 
597 }
598 szLimit = db_get_int("attachment-size-limit", 0);
599 if( szContent<0 || (szLimit && szContent>szLimit) ){
600 /* This check must be done late so that zTargetType is set up. */
601 @ <p class="generalError">Attachment %h(zName) is too large.
@@ -636,11 +641,12 @@
636 ** file1..fileN=FILE_OBJECTS
637 ** dryrun=0|1
638 **
639 ** Each posted file in the set file1..fileN gets attached to the
640 ** given target, permissions permitting. If dryrun>0 then the change
641 ** is rolled back instead of committed.
 
642 **
643 ** Responds with JSON: an empty object on success and
644 ** {error:"message"} on error. The on-success response structure is
645 ** subject to amendment.
646 */
@@ -898,11 +904,11 @@
898 if( !goodCaptcha ){
899 @ <p class="generalError">Error: Incorrect security code.</p>
900 }
901 @ <h2>Attachments for %s(zTargetType)</h2>
902 if(1){
903 /* noscript fallback is completely tested */
904 @ <noscript>
905 attach_render_legacy_form(noJsArgs[0], noJsArgs[1], noJsArgs[2],
906 noJsArgs[3], 0,
907 zFrom ? zFrom : mprintf("%R/home"));
908 @ </noscript>
909
--- src/attach.c
+++ src/attach.c
@@ -446,10 +446,16 @@
446 attach_put(&manifest, rid, needModerator);
447 assert( blob_is_reset(&manifest) );
448 db_end_transaction(0);
449 }
450
451 /*
452 ** Renders the "legacy" (static) /attachadd form. One of the first
453 ** four arguments must be non-NULL and the other three must be NULL.
454 ** zComment may be NULL, as may zFrom. See the call sites for more
455 ** context.
456 */
457 static void attach_render_legacy_form(const char *zForumPost,
458 const char *zTechNote,
459 const char *zTicket,
460 const char *zWikiPage,
461 const char *zComment,
@@ -493,22 +499,22 @@
499 **
500 ** Or the "version 2" interface:
501 **
502 ** target=ATTACHMENT_TARGET
503 **
504 ** Behaves as documented for attachaddV2_page().
505 */
506 void attachadd_page(void){
507 const char *zPage;
508 const char *zForumPost;
509 const char *zTkt;
510 const char *zTechNote;
 
511 const char *aContent;
512 const char *zName;
513 const char *zComment;
514 const char *zTarget;
515 const char *zFrom; /* Origin page - redirect here after saving */
516 char * zTo = 0; /* Optionally redirect here after saving */
517 char *zTargetType = 0;
518 char *zExtraFree = 0;
519 int szContent;
520 int goodCaptcha = 1;
@@ -549,25 +555,23 @@
555 "forum posts.");
556 }
557 zTarget = zExtraFree = rid_to_uuid(fpid);
558 zTargetType = mprintf("Forum post <a href=\"%R/forumpost/%S\">%h</a>",
559 zTarget, zForumPost);
560 zTo = zFrom ? 0 : mprintf("%R/forumpost/%S", zTarget);
 
 
 
561 }else if( zPage ){
562 if( g.perm.ApndWiki==0 || g.perm.Attach==0 ){
563 login_needed(g.anon.ApndWiki && g.anon.Attach);
564 return;
565 }
566 if( !db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'", zPage) ){
567 fossil_redirect_home();
568 }
569 zTarget = zPage;
570 zTargetType = mprintf("Wiki Page <a href=\"%R/wiki?name=%t\">%h</a>",
571 zPage, zPage);
572 zTo = zFrom ? 0 : mprintf("%R/wiki?name=%T", zTarget);
573 }else if ( zTechNote ){
574 if( g.perm.Write==0 || g.perm.ApndWiki==0 || g.perm.Attach==0 ){
575 login_needed(g.anon.Write && g.anon.ApndWiki && g.anon.Attach);
576 return;
577 }
@@ -577,11 +581,11 @@
581 if( zTechNote==0) fossil_redirect_home();
582 }
583 zTarget = zTechNote;
584 zTargetType = mprintf("Tech Note <a href=\"%R/technote/%s\">%S</a>",
585 zTechNote, zTechNote);
586 zTo = zFrom ? 0 : mprintf("%R/technote/%S", zTarget);
587 }else{
588 assert( zTkt );
589 if( g.perm.ApndTkt==0 || g.perm.Attach==0 ){
590 login_needed(g.anon.ApndTkt && g.anon.Attach);
591 return;
@@ -590,12 +594,13 @@
594 zTkt = db_text(0, "SELECT substr(tagname,5) FROM tag"
595 " WHERE tagname GLOB 'tkt-%q*'", zTkt);
596 if( zTkt==0 ) fossil_redirect_home();
597 }
598 zTarget = zTkt;
599 zTargetType = mprintf("Ticket <a href=\"%R/tktview/%S\">%S</a>",
600 zTkt, zTkt);
601 zTo = zFrom ? 0 : mprintf("%R/tktview/%S", zTarget);
602 }
603 szLimit = db_get_int("attachment-size-limit", 0);
604 if( szContent<0 || (szLimit && szContent>szLimit) ){
605 /* This check must be done late so that zTargetType is set up. */
606 @ <p class="generalError">Attachment %h(zName) is too large.
@@ -636,11 +641,12 @@
641 ** file1..fileN=FILE_OBJECTS
642 ** dryrun=0|1
643 **
644 ** Each posted file in the set file1..fileN gets attached to the
645 ** given target, permissions permitting. If dryrun>0 then the change
646 ** is rolled back instead of committed. target=X must refer to a full
647 ** target ID, not a prefix.
648 **
649 ** Responds with JSON: an empty object on success and
650 ** {error:"message"} on error. The on-success response structure is
651 ** subject to amendment.
652 */
@@ -898,11 +904,11 @@
904 if( !goodCaptcha ){
905 @ <p class="generalError">Error: Incorrect security code.</p>
906 }
907 @ <h2>Attachments for %s(zTargetType)</h2>
908 if(1){
909 /* noscript fallback is completely untested */
910 @ <noscript>
911 attach_render_legacy_form(noJsArgs[0], noJsArgs[1], noJsArgs[2],
912 noJsArgs[3], 0,
913 zFrom ? zFrom : mprintf("%R/home"));
914 @ </noscript>
915
+1 -1
--- src/forum.c
+++ src/forum.c
@@ -1254,11 +1254,11 @@
12541254
@ <br><label><input type="checkbox" name="trust">
12551255
@ Trust user "%h(pManifest->zUser)" so that future posts by \
12561256
@ "%h(pManifest->zUser)" do not require moderation.
12571257
@ </label>
12581258
@ <input type="hidden" name="trustuser" value="%h(pManifest->zUser)">
1259
- bBrBeforeAttach = 1 /* slightly improve the layout */;
1259
+ bBrBeforeAttach = 1 /* slightly unmangle the layout */;
12601260
}
12611261
}else if( bSameUser ){
12621262
/* Allow users to delete (reject) their own pending posts. */
12631263
@ <input type="submit" name="reject" value="Delete">
12641264
}
12651265
--- src/forum.c
+++ src/forum.c
@@ -1254,11 +1254,11 @@
1254 @ <br><label><input type="checkbox" name="trust">
1255 @ Trust user "%h(pManifest->zUser)" so that future posts by \
1256 @ "%h(pManifest->zUser)" do not require moderation.
1257 @ </label>
1258 @ <input type="hidden" name="trustuser" value="%h(pManifest->zUser)">
1259 bBrBeforeAttach = 1 /* slightly improve the layout */;
1260 }
1261 }else if( bSameUser ){
1262 /* Allow users to delete (reject) their own pending posts. */
1263 @ <input type="submit" name="reject" value="Delete">
1264 }
1265
--- src/forum.c
+++ src/forum.c
@@ -1254,11 +1254,11 @@
1254 @ <br><label><input type="checkbox" name="trust">
1255 @ Trust user "%h(pManifest->zUser)" so that future posts by \
1256 @ "%h(pManifest->zUser)" do not require moderation.
1257 @ </label>
1258 @ <input type="hidden" name="trustuser" value="%h(pManifest->zUser)">
1259 bBrBeforeAttach = 1 /* slightly unmangle the layout */;
1260 }
1261 }else if( bSameUser ){
1262 /* Allow users to delete (reject) their own pending posts. */
1263 @ <input type="submit" name="reject" value="Delete">
1264 }
1265

Keyboard Shortcuts

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