Fossil SCM

Various minor cleanups.

stephan 2026-06-03 14:13 UTC attach-v2
Commit f850be30ff91896b7c0daed8b801b8d24bbcde56735e74d50033992b21f8be16
+48 -4
--- src/attach.c
+++ src/attach.c
@@ -55,10 +55,48 @@
5555
rc = db_column_int(&q, 0);
5656
}
5757
db_reset(&q);
5858
return rc;
5959
}
60
+
61
+/*
62
+** For a given aritfact ID and type, returns true if the current user
63
+** could hypothetically attach something to it, else returns 0.
64
+**
65
+** The rid is currently only relevant when iArtifactType is
66
+** CFTYPE_FORUM. For forum posts, it checks precisely the rid given,
67
+** not the head RID, to keep non-admins from attaching files to
68
+** threads which have since been taken over by another user (this
69
+** happens when an admin edits another user's post).
70
+*/
71
+int attach_user_may(int rid, int iArtifactType){
72
+ if( g.perm.Admin ) return 1;
73
+ if( !login_is_individual() ) return 0;
74
+ switch(iArtifactType){
75
+ case CFTYPE_FORUM:
76
+ return forumpost_is_owner(rid, 0);
77
+ case CFTYPE_WIKI:
78
+ return g.perm.ApndWiki && g.perm.Attach==0;
79
+ case CFTYPE_TICKET:
80
+ return g.perm.ApndTkt && g.perm.Attach==0;
81
+ case CFTYPE_EVENT:
82
+ return g.perm.Write && g.perm.ApndWiki && g.perm.Attach;
83
+ default:
84
+ return 0;
85
+ }
86
+}
87
+
88
+/*
89
+** Emits a single-button FORM which invokes
90
+** /attachadd?target=$zTarget.
91
+*/
92
+void attach_emit_attachadd_button(const char *zTarget){
93
+ @ <form method="post" action="%R/attachadd">\
94
+ @ <input type="hidden" name="target" value="%T(zTarget)">\
95
+ @ <input type="submit" value="Attach...">
96
+ @ </form>\
97
+}
6098
6199
/*
62100
** WEBPAGE: attachlist
63101
** List attachments.
64102
**
@@ -519,11 +557,14 @@
519557
/* This check must be done late so that zTargetType is set up. */
520558
@ <p class="generalError">Attachment %h(zName) is too large.
521559
@ <a href="%R/help/attachment-size-limit">Limit</a> is
522560
@ %d(szLimit ? szLimit : 0x7fffffff) bytes</p>
523561
/* Fall through and render form. */
524
- }else if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct(0)) ){
562
+ }else if( P("ok")
563
+ && cgi_csrf_safe(2)
564
+ && szContent>0
565
+ && (goodCaptcha = captcha_is_correct(0)) ){
525566
int needModerator = (zForumPost!=0 && forum_need_moderation()) ||
526567
(zTkt!=0 && ticket_need_moderation(0)) ||
527568
(zPage!=0 && wiki_need_moderation(0));
528569
attach_commit(zName, zTarget, aContent, szContent, needModerator, zComment);
529570
cgi_redirect(zTo ? zTo : zFrom);
@@ -554,10 +595,11 @@
554595
@ <input type="hidden" name="from" value="%h(zFrom)">
555596
@ <input type="submit" name="ok" value="Add Attachment">
556597
@ <input type="submit" name="cancel" value="Cancel">
557598
@ </div>
558599
captcha_generate(0);
600
+ login_insert_csrf_secret();
559601
@ </form>
560602
builtin_fossil_js_bundle_or("attach", NULL);
561603
style_finish_page();
562604
fossil_free(zTargetType);
563605
fossil_free(zExtraFree);
@@ -742,11 +784,10 @@
742784
const char *zTarget = P("target");
743785
char *zTo = 0;
744786
char *zTargetType = 0;
745787
char *zExtraFree = 0;
746788
int iTgtType = 0;
747
- int szContent = 0;
748789
int goodCaptcha = 1;
749790
750791
if( zFrom==0 ) zFrom = mprintf("%R/home");
751792
if( P("cancel") ) cgi_redirect(zFrom);
752793
if( 0==zTarget ){
@@ -828,14 +869,16 @@
828869
@ <p class="generalError">Error: Incorrect security code.</p>
829870
}
830871
@ <h2>Attachments for %s(zTargetType)</h2>
831872
attachment_list(zTarget, NULL,
832873
ATTACHLIST_SIZE | ATTACHLIST_HIDE_UNAPPROVED);
833
- /* Form gets fleshed out and activate from fossil.attach.js. */
834874
@ <div id='attachadd-form-wrapper'>
875
+ /* JS code imports these hidden fields into a form it generates. */
835876
@ <input type="hidden" name="target" value="%h(zTarget)">
836
- @ <input type="hidden" name="from" value="%h(zFrom)">
877
+ if( zFrom ){
878
+ @ <input type="hidden" name="from" value="%h(zFrom)">
879
+ }
837880
if( zTo ){
838881
@ <input type="hidden" name="to" value="%h(zTo)">
839882
}
840883
captcha_generate(0);
841884
login_insert_csrf_secret();
@@ -1340,5 +1383,6 @@
13401383
}
13411384
db_reset(&q);
13421385
}
13431386
db_finalize(&q);
13441387
}
1388
+
13451389
--- src/attach.c
+++ src/attach.c
@@ -55,10 +55,48 @@
55 rc = db_column_int(&q, 0);
56 }
57 db_reset(&q);
58 return rc;
59 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
61 /*
62 ** WEBPAGE: attachlist
63 ** List attachments.
64 **
@@ -519,11 +557,14 @@
519 /* This check must be done late so that zTargetType is set up. */
520 @ <p class="generalError">Attachment %h(zName) is too large.
521 @ <a href="%R/help/attachment-size-limit">Limit</a> is
522 @ %d(szLimit ? szLimit : 0x7fffffff) bytes</p>
523 /* Fall through and render form. */
524 }else if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct(0)) ){
 
 
 
525 int needModerator = (zForumPost!=0 && forum_need_moderation()) ||
526 (zTkt!=0 && ticket_need_moderation(0)) ||
527 (zPage!=0 && wiki_need_moderation(0));
528 attach_commit(zName, zTarget, aContent, szContent, needModerator, zComment);
529 cgi_redirect(zTo ? zTo : zFrom);
@@ -554,10 +595,11 @@
554 @ <input type="hidden" name="from" value="%h(zFrom)">
555 @ <input type="submit" name="ok" value="Add Attachment">
556 @ <input type="submit" name="cancel" value="Cancel">
557 @ </div>
558 captcha_generate(0);
 
559 @ </form>
560 builtin_fossil_js_bundle_or("attach", NULL);
561 style_finish_page();
562 fossil_free(zTargetType);
563 fossil_free(zExtraFree);
@@ -742,11 +784,10 @@
742 const char *zTarget = P("target");
743 char *zTo = 0;
744 char *zTargetType = 0;
745 char *zExtraFree = 0;
746 int iTgtType = 0;
747 int szContent = 0;
748 int goodCaptcha = 1;
749
750 if( zFrom==0 ) zFrom = mprintf("%R/home");
751 if( P("cancel") ) cgi_redirect(zFrom);
752 if( 0==zTarget ){
@@ -828,14 +869,16 @@
828 @ <p class="generalError">Error: Incorrect security code.</p>
829 }
830 @ <h2>Attachments for %s(zTargetType)</h2>
831 attachment_list(zTarget, NULL,
832 ATTACHLIST_SIZE | ATTACHLIST_HIDE_UNAPPROVED);
833 /* Form gets fleshed out and activate from fossil.attach.js. */
834 @ <div id='attachadd-form-wrapper'>
 
835 @ <input type="hidden" name="target" value="%h(zTarget)">
836 @ <input type="hidden" name="from" value="%h(zFrom)">
 
 
837 if( zTo ){
838 @ <input type="hidden" name="to" value="%h(zTo)">
839 }
840 captcha_generate(0);
841 login_insert_csrf_secret();
@@ -1340,5 +1383,6 @@
1340 }
1341 db_reset(&q);
1342 }
1343 db_finalize(&q);
1344 }
 
1345
--- src/attach.c
+++ src/attach.c
@@ -55,10 +55,48 @@
55 rc = db_column_int(&q, 0);
56 }
57 db_reset(&q);
58 return rc;
59 }
60
61 /*
62 ** For a given aritfact ID and type, returns true if the current user
63 ** could hypothetically attach something to it, else returns 0.
64 **
65 ** The rid is currently only relevant when iArtifactType is
66 ** CFTYPE_FORUM. For forum posts, it checks precisely the rid given,
67 ** not the head RID, to keep non-admins from attaching files to
68 ** threads which have since been taken over by another user (this
69 ** happens when an admin edits another user's post).
70 */
71 int attach_user_may(int rid, int iArtifactType){
72 if( g.perm.Admin ) return 1;
73 if( !login_is_individual() ) return 0;
74 switch(iArtifactType){
75 case CFTYPE_FORUM:
76 return forumpost_is_owner(rid, 0);
77 case CFTYPE_WIKI:
78 return g.perm.ApndWiki && g.perm.Attach==0;
79 case CFTYPE_TICKET:
80 return g.perm.ApndTkt && g.perm.Attach==0;
81 case CFTYPE_EVENT:
82 return g.perm.Write && g.perm.ApndWiki && g.perm.Attach;
83 default:
84 return 0;
85 }
86 }
87
88 /*
89 ** Emits a single-button FORM which invokes
90 ** /attachadd?target=$zTarget.
91 */
92 void attach_emit_attachadd_button(const char *zTarget){
93 @ <form method="post" action="%R/attachadd">\
94 @ <input type="hidden" name="target" value="%T(zTarget)">\
95 @ <input type="submit" value="Attach...">
96 @ </form>\
97 }
98
99 /*
100 ** WEBPAGE: attachlist
101 ** List attachments.
102 **
@@ -519,11 +557,14 @@
557 /* This check must be done late so that zTargetType is set up. */
558 @ <p class="generalError">Attachment %h(zName) is too large.
559 @ <a href="%R/help/attachment-size-limit">Limit</a> is
560 @ %d(szLimit ? szLimit : 0x7fffffff) bytes</p>
561 /* Fall through and render form. */
562 }else if( P("ok")
563 && cgi_csrf_safe(2)
564 && szContent>0
565 && (goodCaptcha = captcha_is_correct(0)) ){
566 int needModerator = (zForumPost!=0 && forum_need_moderation()) ||
567 (zTkt!=0 && ticket_need_moderation(0)) ||
568 (zPage!=0 && wiki_need_moderation(0));
569 attach_commit(zName, zTarget, aContent, szContent, needModerator, zComment);
570 cgi_redirect(zTo ? zTo : zFrom);
@@ -554,10 +595,11 @@
595 @ <input type="hidden" name="from" value="%h(zFrom)">
596 @ <input type="submit" name="ok" value="Add Attachment">
597 @ <input type="submit" name="cancel" value="Cancel">
598 @ </div>
599 captcha_generate(0);
600 login_insert_csrf_secret();
601 @ </form>
602 builtin_fossil_js_bundle_or("attach", NULL);
603 style_finish_page();
604 fossil_free(zTargetType);
605 fossil_free(zExtraFree);
@@ -742,11 +784,10 @@
784 const char *zTarget = P("target");
785 char *zTo = 0;
786 char *zTargetType = 0;
787 char *zExtraFree = 0;
788 int iTgtType = 0;
 
789 int goodCaptcha = 1;
790
791 if( zFrom==0 ) zFrom = mprintf("%R/home");
792 if( P("cancel") ) cgi_redirect(zFrom);
793 if( 0==zTarget ){
@@ -828,14 +869,16 @@
869 @ <p class="generalError">Error: Incorrect security code.</p>
870 }
871 @ <h2>Attachments for %s(zTargetType)</h2>
872 attachment_list(zTarget, NULL,
873 ATTACHLIST_SIZE | ATTACHLIST_HIDE_UNAPPROVED);
 
874 @ <div id='attachadd-form-wrapper'>
875 /* JS code imports these hidden fields into a form it generates. */
876 @ <input type="hidden" name="target" value="%h(zTarget)">
877 if( zFrom ){
878 @ <input type="hidden" name="from" value="%h(zFrom)">
879 }
880 if( zTo ){
881 @ <input type="hidden" name="to" value="%h(zTo)">
882 }
883 captcha_generate(0);
884 login_insert_csrf_secret();
@@ -1340,5 +1383,6 @@
1383 }
1384 db_reset(&q);
1385 }
1386 db_finalize(&q);
1387 }
1388
1389
+5 -10
--- src/forum.c
+++ src/forum.c
@@ -865,11 +865,13 @@
865865
/*
866866
** Returns true if the current user is authorized to set forum post
867867
** fpid's status.
868868
*/
869869
static int forum_may_set_status(int fpid){
870
- return g.perm.Admin
870
+ if( moderation_pending(fpid) ) return 0;
871
+ return
872
+ g.perm.Admin
871873
|| g.perm.ModForum
872874
|| (login_is_individual()
873875
&& forumpost_is_owner(fpid, 0));
874876
}
875877
@@ -1272,24 +1274,17 @@
12721274
@ %s(iClosed ? "action-reopen" : "action-close")'/>
12731275
/* ^^^ activated by fossil.page.forumpost.js */
12741276
}
12751277
@ </form>
12761278
}
1277
- if( g.perm.Admin ||
1278
- (login_is_individual()
1279
- && forumpost_is_owner(p/*not pHead*/->fpid, 0)) ){
1279
+ if( attach_user_may(p/*not pHead*/->fpid, CFTYPE_FORUM) ){
12801280
/* When an admin edits someone else's post, the admin
12811281
** effectively takes over ownership of it (and we currently
12821282
** have no way of passing it back). Because of this, we
12831283
** check the ownership of `p` instead of `pHead`. */
1284
- @ <form method="post" action="%R/attachadd" \
1285
- @ class='file-attach'>\
1286
- @ <input type="hidden" name="target" value="%T(pHead->zUuid)">
1287
- @ <input type="submit" value="Attach...">
1288
- login_insert_csrf_secret();
1284
+ attach_emit_attachadd_button(pHead->zUuid);
12891285
moderation_pending_www(p->fpid);
1290
- @ </form>
12911286
}
12921287
}
12931288
@ </div>
12941289
}
12951290
if( !p->pIrt && (flags & FDISPLAY_SELECTED)){
12961291
--- src/forum.c
+++ src/forum.c
@@ -865,11 +865,13 @@
865 /*
866 ** Returns true if the current user is authorized to set forum post
867 ** fpid's status.
868 */
869 static int forum_may_set_status(int fpid){
870 return g.perm.Admin
 
 
871 || g.perm.ModForum
872 || (login_is_individual()
873 && forumpost_is_owner(fpid, 0));
874 }
875
@@ -1272,24 +1274,17 @@
1272 @ %s(iClosed ? "action-reopen" : "action-close")'/>
1273 /* ^^^ activated by fossil.page.forumpost.js */
1274 }
1275 @ </form>
1276 }
1277 if( g.perm.Admin ||
1278 (login_is_individual()
1279 && forumpost_is_owner(p/*not pHead*/->fpid, 0)) ){
1280 /* When an admin edits someone else's post, the admin
1281 ** effectively takes over ownership of it (and we currently
1282 ** have no way of passing it back). Because of this, we
1283 ** check the ownership of `p` instead of `pHead`. */
1284 @ <form method="post" action="%R/attachadd" \
1285 @ class='file-attach'>\
1286 @ <input type="hidden" name="target" value="%T(pHead->zUuid)">
1287 @ <input type="submit" value="Attach...">
1288 login_insert_csrf_secret();
1289 moderation_pending_www(p->fpid);
1290 @ </form>
1291 }
1292 }
1293 @ </div>
1294 }
1295 if( !p->pIrt && (flags & FDISPLAY_SELECTED)){
1296
--- src/forum.c
+++ src/forum.c
@@ -865,11 +865,13 @@
865 /*
866 ** Returns true if the current user is authorized to set forum post
867 ** fpid's status.
868 */
869 static int forum_may_set_status(int fpid){
870 if( moderation_pending(fpid) ) return 0;
871 return
872 g.perm.Admin
873 || g.perm.ModForum
874 || (login_is_individual()
875 && forumpost_is_owner(fpid, 0));
876 }
877
@@ -1272,24 +1274,17 @@
1274 @ %s(iClosed ? "action-reopen" : "action-close")'/>
1275 /* ^^^ activated by fossil.page.forumpost.js */
1276 }
1277 @ </form>
1278 }
1279 if( attach_user_may(p/*not pHead*/->fpid, CFTYPE_FORUM) ){
 
 
1280 /* When an admin edits someone else's post, the admin
1281 ** effectively takes over ownership of it (and we currently
1282 ** have no way of passing it back). Because of this, we
1283 ** check the ownership of `p` instead of `pHead`. */
1284 attach_emit_attachadd_button(pHead->zUuid);
 
 
 
 
1285 moderation_pending_www(p->fpid);
 
1286 }
1287 }
1288 @ </div>
1289 }
1290 if( !p->pIrt && (flags & FDISPLAY_SELECTED)){
1291
--- src/fossil.attach.js
+++ src/fossil.attach.js
@@ -480,12 +480,12 @@
480480
return i;
481481
}
482482
}/*Attacher*/;
483483
F.Attacher = Attacher;
484484
485
- const eFormDiv = document.querySelector('#attachadd-form-wrapper');
486
- if( eFormDiv ){
485
+ const eFormWrapper = document.querySelector('#attachadd-form-wrapper');
486
+ if( eFormWrapper ){
487487
/* Inject a file-attachment form. */
488488
const urlArgs = new URLSearchParams(window.location.search);
489489
let zTarget = urlArgs.get('target');
490490
let zTo = urlArgs.get('to') || urlArgs.get('from');
491491
const eBtnSubmit = D.button("Submit");
@@ -500,11 +500,11 @@
500500
const cbAttacherChange = (ev)=>{
501501
const a = ev.detail.attacher;
502502
updateBtnSubmit(a);
503503
};
504504
const att = new Attacher({
505
- container: eFormDiv,
505
+ container: eFormWrapper,
506506
startWith: 1,
507507
listener: cbAttacherChange,
508508
controls: [eBtnSubmit],
509509
description: false
510510
});
@@ -520,11 +520,14 @@
520520
for(const row of li){
521521
++i;
522522
fd.append('file'+i, row.content);
523523
if( row.description ) fd.append('file'+i+'_desc', row.description);
524524
}
525
- for( const eIn of eFormDiv.querySelectorAll(':scope > input[type="hidden"]') ){
525
+ for( const eIn of eFormWrapper.querySelectorAll(
526
+ ':scope > input[type="hidden"]'
527
+ ) ){
528
+ /* Copy over hidden input fields emitted by the server. */
526529
if( eIn.name==='target' ){
527530
zTarget = eIn.value;
528531
}else if( eIn.name==='to' || (eIn.name==='from' && !zTo) ){
529532
zTo = eIn.value;
530533
}
@@ -541,11 +544,11 @@
541544
err = e;
542545
});
543546
D.enable(eBtnSubmit);
544547
delete eBtnSubmit.dataset.submitted;
545548
const jr = err ? undefined : await resp.json().catch(()=>{});
546
- if( jr?.error || !resp.ok ){
549
+ if( err || jr?.error || !resp.ok ){
547550
const msg = err ? err.message : (jr?.error || resp.statusText);
548551
att.reportError("Attaching failed: ", msg);
549552
}else{
550553
att.clear();
551554
let to = zTo || jr?.redirect;
@@ -552,15 +555,15 @@
552555
if( to ){
553556
if( '/'!==to[0] ){
554557
to = F.repoUrl(to);
555558
}
556559
window.location = to;
557
- }else{
560
+ }else if( target ){
558561
window.location = '?target='+zTarget+'&'+Date.now();
559562
}
560563
}
561564
})/*submit handler*/;
562565
updateBtnSubmit(att);
563566
F.page.attacher = att /* only for testing via dev console */;
564567
}/* /attachaddV2 */
565568
566569
})(window.fossil);
567570
--- src/fossil.attach.js
+++ src/fossil.attach.js
@@ -480,12 +480,12 @@
480 return i;
481 }
482 }/*Attacher*/;
483 F.Attacher = Attacher;
484
485 const eFormDiv = document.querySelector('#attachadd-form-wrapper');
486 if( eFormDiv ){
487 /* Inject a file-attachment form. */
488 const urlArgs = new URLSearchParams(window.location.search);
489 let zTarget = urlArgs.get('target');
490 let zTo = urlArgs.get('to') || urlArgs.get('from');
491 const eBtnSubmit = D.button("Submit");
@@ -500,11 +500,11 @@
500 const cbAttacherChange = (ev)=>{
501 const a = ev.detail.attacher;
502 updateBtnSubmit(a);
503 };
504 const att = new Attacher({
505 container: eFormDiv,
506 startWith: 1,
507 listener: cbAttacherChange,
508 controls: [eBtnSubmit],
509 description: false
510 });
@@ -520,11 +520,14 @@
520 for(const row of li){
521 ++i;
522 fd.append('file'+i, row.content);
523 if( row.description ) fd.append('file'+i+'_desc', row.description);
524 }
525 for( const eIn of eFormDiv.querySelectorAll(':scope > input[type="hidden"]') ){
 
 
 
526 if( eIn.name==='target' ){
527 zTarget = eIn.value;
528 }else if( eIn.name==='to' || (eIn.name==='from' && !zTo) ){
529 zTo = eIn.value;
530 }
@@ -541,11 +544,11 @@
541 err = e;
542 });
543 D.enable(eBtnSubmit);
544 delete eBtnSubmit.dataset.submitted;
545 const jr = err ? undefined : await resp.json().catch(()=>{});
546 if( jr?.error || !resp.ok ){
547 const msg = err ? err.message : (jr?.error || resp.statusText);
548 att.reportError("Attaching failed: ", msg);
549 }else{
550 att.clear();
551 let to = zTo || jr?.redirect;
@@ -552,15 +555,15 @@
552 if( to ){
553 if( '/'!==to[0] ){
554 to = F.repoUrl(to);
555 }
556 window.location = to;
557 }else{
558 window.location = '?target='+zTarget+'&'+Date.now();
559 }
560 }
561 })/*submit handler*/;
562 updateBtnSubmit(att);
563 F.page.attacher = att /* only for testing via dev console */;
564 }/* /attachaddV2 */
565
566 })(window.fossil);
567
--- src/fossil.attach.js
+++ src/fossil.attach.js
@@ -480,12 +480,12 @@
480 return i;
481 }
482 }/*Attacher*/;
483 F.Attacher = Attacher;
484
485 const eFormWrapper = document.querySelector('#attachadd-form-wrapper');
486 if( eFormWrapper ){
487 /* Inject a file-attachment form. */
488 const urlArgs = new URLSearchParams(window.location.search);
489 let zTarget = urlArgs.get('target');
490 let zTo = urlArgs.get('to') || urlArgs.get('from');
491 const eBtnSubmit = D.button("Submit");
@@ -500,11 +500,11 @@
500 const cbAttacherChange = (ev)=>{
501 const a = ev.detail.attacher;
502 updateBtnSubmit(a);
503 };
504 const att = new Attacher({
505 container: eFormWrapper,
506 startWith: 1,
507 listener: cbAttacherChange,
508 controls: [eBtnSubmit],
509 description: false
510 });
@@ -520,11 +520,14 @@
520 for(const row of li){
521 ++i;
522 fd.append('file'+i, row.content);
523 if( row.description ) fd.append('file'+i+'_desc', row.description);
524 }
525 for( const eIn of eFormWrapper.querySelectorAll(
526 ':scope > input[type="hidden"]'
527 ) ){
528 /* Copy over hidden input fields emitted by the server. */
529 if( eIn.name==='target' ){
530 zTarget = eIn.value;
531 }else if( eIn.name==='to' || (eIn.name==='from' && !zTo) ){
532 zTo = eIn.value;
533 }
@@ -541,11 +544,11 @@
544 err = e;
545 });
546 D.enable(eBtnSubmit);
547 delete eBtnSubmit.dataset.submitted;
548 const jr = err ? undefined : await resp.json().catch(()=>{});
549 if( err || jr?.error || !resp.ok ){
550 const msg = err ? err.message : (jr?.error || resp.statusText);
551 att.reportError("Attaching failed: ", msg);
552 }else{
553 att.clear();
554 let to = zTo || jr?.redirect;
@@ -552,15 +555,15 @@
555 if( to ){
556 if( '/'!==to[0] ){
557 to = F.repoUrl(to);
558 }
559 window.location = to;
560 }else if( target ){
561 window.location = '?target='+zTarget+'&'+Date.now();
562 }
563 }
564 })/*submit handler*/;
565 updateBtnSubmit(att);
566 F.page.attacher = att /* only for testing via dev console */;
567 }/* /attachaddV2 */
568
569 })(window.fossil);
570

Keyboard Shortcuts

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