Fossil SCM

Integrate the captcha in the new editor form. Start hooking in to /forumedit, but it's incomplete.

stephan 2026-06-06 20:11 UTC forum-editor-2026
Commit c7a84df415a01956f38aa9aa873ba5696ec522201dabc6f141e77202fda8bf1b
+16
--- src/ajax.c
+++ src/ajax.c
@@ -205,10 +205,26 @@
205205
blob_reset(&msg);
206206
cgi_set_content(&content);
207207
cgi_set_status(httpCode>0 ? httpCode : 500, "Error");
208208
cgi_set_content_type("application/json");
209209
}
210
+
211
+void ajax_route_error_forbidden(){
212
+ ajax_route_error(403, "Permission denied.");
213
+}
214
+
215
+void ajax_route_error_csrf(){
216
+ ajax_route_error(403, "Invalid CSRF signature.");
217
+}
218
+
219
+int ajax_check_csrf(int level){
220
+ if( 0==cgi_csrf_safe(level) ){
221
+ ajax_route_error_csrf();
222
+ return 0;
223
+ }
224
+ return 1;
225
+}
210226
211227
/*
212228
** Performs bootstrapping common to the /ajax/xyz AJAX routes, such as
213229
** logging in the user.
214230
**
215231
--- src/ajax.c
+++ src/ajax.c
@@ -205,10 +205,26 @@
205 blob_reset(&msg);
206 cgi_set_content(&content);
207 cgi_set_status(httpCode>0 ? httpCode : 500, "Error");
208 cgi_set_content_type("application/json");
209 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
211 /*
212 ** Performs bootstrapping common to the /ajax/xyz AJAX routes, such as
213 ** logging in the user.
214 **
215
--- src/ajax.c
+++ src/ajax.c
@@ -205,10 +205,26 @@
205 blob_reset(&msg);
206 cgi_set_content(&content);
207 cgi_set_status(httpCode>0 ? httpCode : 500, "Error");
208 cgi_set_content_type("application/json");
209 }
210
211 void ajax_route_error_forbidden(){
212 ajax_route_error(403, "Permission denied.");
213 }
214
215 void ajax_route_error_csrf(){
216 ajax_route_error(403, "Invalid CSRF signature.");
217 }
218
219 int ajax_check_csrf(int level){
220 if( 0==cgi_csrf_safe(level) ){
221 ajax_route_error_csrf();
222 return 0;
223 }
224 return 1;
225 }
226
227 /*
228 ** Performs bootstrapping common to the /ajax/xyz AJAX routes, such as
229 ** logging in the user.
230 **
231
+2 -3
--- src/attach.c
+++ src/attach.c
@@ -787,12 +787,11 @@
787787
788788
if( ! ajax_route_bootstrap(0, 1) ){
789789
return;
790790
}else if( !(goodCaptcha = captcha_is_correct(0)) ){
791791
goto ajax_post_403;
792
- }else if( !cgi_csrf_safe(2) ){
793
- ajax_route_error(403, "Invalid CSRF signature.");
792
+ }else if( !ajax_check_csrf(2) ){
794793
return;
795794
}
796795
db_begin_transaction();
797796
zTarget = P("target");
798797
eTgtType = attachment_target_type(zTarget, 1);
@@ -874,11 +873,11 @@
874873
fossil_free(zExtraFree);
875874
db_end_transaction(bRollback);
876875
return;
877876
ajax_post_403:
878877
db_rollback_transaction();
879
- ajax_route_error(403, "Permission denied.");
878
+ ajax_route_error_forbidden();
880879
return;
881880
ajax_post_404:
882881
db_rollback_transaction();
883882
ajax_route_error(404, "Target not found.");
884883
return;
885884
--- src/attach.c
+++ src/attach.c
@@ -787,12 +787,11 @@
787
788 if( ! ajax_route_bootstrap(0, 1) ){
789 return;
790 }else if( !(goodCaptcha = captcha_is_correct(0)) ){
791 goto ajax_post_403;
792 }else if( !cgi_csrf_safe(2) ){
793 ajax_route_error(403, "Invalid CSRF signature.");
794 return;
795 }
796 db_begin_transaction();
797 zTarget = P("target");
798 eTgtType = attachment_target_type(zTarget, 1);
@@ -874,11 +873,11 @@
874 fossil_free(zExtraFree);
875 db_end_transaction(bRollback);
876 return;
877 ajax_post_403:
878 db_rollback_transaction();
879 ajax_route_error(403, "Permission denied.");
880 return;
881 ajax_post_404:
882 db_rollback_transaction();
883 ajax_route_error(404, "Target not found.");
884 return;
885
--- src/attach.c
+++ src/attach.c
@@ -787,12 +787,11 @@
787
788 if( ! ajax_route_bootstrap(0, 1) ){
789 return;
790 }else if( !(goodCaptcha = captcha_is_correct(0)) ){
791 goto ajax_post_403;
792 }else if( !ajax_check_csrf(2) ){
 
793 return;
794 }
795 db_begin_transaction();
796 zTarget = P("target");
797 eTgtType = attachment_target_type(zTarget, 1);
@@ -874,11 +873,11 @@
873 fossil_free(zExtraFree);
874 db_end_transaction(bRollback);
875 return;
876 ajax_post_403:
877 db_rollback_transaction();
878 ajax_route_error_forbidden();
879 return;
880 ajax_post_404:
881 db_rollback_transaction();
882 ajax_route_error(404, "Target not found.");
883 return;
884
--- src/captcha.c
+++ src/captcha.c
@@ -712,10 +712,28 @@
712712
}
713713
@ <br/>\
714714
captcha_speakit_button(uSeed, 0);
715715
@ </td></tr></table></div>
716716
}
717
+
718
+/*
719
+** Like captcha_generate() but emits it in a different form, intended
720
+** for consumption by JS.
721
+*/
722
+void captcha_generate_for_js(int mFlags){
723
+ unsigned int uSeed;
724
+ const char *zDecoded;
725
+ char *zCaptcha;
726
+
727
+ if( !captcha_needed() && (mFlags & 0x02)==0 ) return;
728
+ uSeed = captcha_seed();
729
+ zDecoded = captcha_decode(uSeed, 0);
730
+ zCaptcha = captcha_render(zDecoded);
731
+ @ <div class='hidden captcha-for-js'>%h(zCaptcha)</div>
732
+ @ <input type="hidden" name="captchaseed" value="%u(uSeed)">
733
+}
734
+
717735
718736
/*
719737
** Add a "Speak the captcha" button.
720738
*/
721739
void captcha_speakit_button(unsigned int uSeed, const char *zMsg){
722740
--- src/captcha.c
+++ src/captcha.c
@@ -712,10 +712,28 @@
712 }
713 @ <br/>\
714 captcha_speakit_button(uSeed, 0);
715 @ </td></tr></table></div>
716 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
717
718 /*
719 ** Add a "Speak the captcha" button.
720 */
721 void captcha_speakit_button(unsigned int uSeed, const char *zMsg){
722
--- src/captcha.c
+++ src/captcha.c
@@ -712,10 +712,28 @@
712 }
713 @ <br/>\
714 captcha_speakit_button(uSeed, 0);
715 @ </td></tr></table></div>
716 }
717
718 /*
719 ** Like captcha_generate() but emits it in a different form, intended
720 ** for consumption by JS.
721 */
722 void captcha_generate_for_js(int mFlags){
723 unsigned int uSeed;
724 const char *zDecoded;
725 char *zCaptcha;
726
727 if( !captcha_needed() && (mFlags & 0x02)==0 ) return;
728 uSeed = captcha_seed();
729 zDecoded = captcha_decode(uSeed, 0);
730 zCaptcha = captcha_render(zDecoded);
731 @ <div class='hidden captcha-for-js'>%h(zCaptcha)</div>
732 @ <input type="hidden" name="captchaseed" value="%u(uSeed)">
733 }
734
735
736 /*
737 ** Add a "Speak the captcha" button.
738 */
739 void captcha_speakit_button(unsigned int uSeed, const char *zMsg){
740
+38
--- src/forum.c
+++ src/forum.c
@@ -1985,10 +1985,11 @@
19851985
void forumnew_page(void){
19861986
const char *zTitle = PDT("title","");
19871987
const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE);
19881988
const char *zContent = PDT("content","");
19891989
const int bLegacy = PB("legacy"); /* True for legacy HTML form */
1990
+
19901991
login_check_credentials();
19911992
if( !g.perm.WrForum ){
19921993
login_needed(g.anon.WrForum);
19931994
return;
19941995
}
@@ -2027,10 +2028,11 @@
20272028
When it's enabled, the above won't do anything and
20282029
JS will render the editor form in the next element. */
20292030
@ <div hidden id='forumnew-placeholder'>
20302031
@ <input type='hidden' name='title' value='%h(zTitle)'>
20312032
login_insert_csrf_secret();
2033
+ captcha_generate_for_js(0x02);
20322034
@ </div>
20332035
}
20342036
forum_emit_js();
20352037
style_finish_page();
20362038
}
@@ -2051,17 +2053,23 @@
20512053
const char *zMimetype = 0;
20522054
const char *zContent = 0;
20532055
const char *zTitle = 0;
20542056
char *zDate = 0;
20552057
const char *zFpid = PD("fpid","");
2058
+ const int bLegacy = PB("legacy"); /* True for legacy HTML form */
20562059
int isCsrfSafe;
20572060
int isDelete = 0;
20582061
int iClosed = 0;
20592062
int bSameUser; /* True if author is also the reader */
20602063
int bPreview; /* True in preview mode. */
20612064
int bPrivate; /* True if post is private (not yet moderated) */
20622065
int bReply; /* True if replying to a post */
2066
+
2067
+ if( !bLegacy ){
2068
+ forumedit_page_v2();
2069
+ return;
2070
+ }
20632071
20642072
login_check_credentials();
20652073
if( !g.perm.WrForum ){
20662074
login_needed(g.anon.WrForum);
20672075
return;
@@ -2215,10 +2223,11 @@
22152223
forum_post_widget(0, zMimetype, zContent);
22162224
}
22172225
if( !isDelete ){
22182226
@ <input type="submit" name="preview" value="Preview">
22192227
}
2228
+ @ <input type="hidden" name="legacy" value="1">
22202229
@ <input type="submit" name="cancel" value="Cancel">
22212230
if( (bPreview && !whitespace_only(zContent)) || isDelete ){
22222231
if( !iClosed || g.perm.Admin ) {
22232232
@ <input type="submit" name="submit" value="Submit">
22242233
}
@@ -2230,10 +2239,39 @@
22302239
forum_render_attachment_list(rid_to_uuid(fpid));
22312240
}
22322241
forum_render_attachment_notice();
22332242
forum_emit_js();
22342243
style_finish_page();
2244
+}
2245
+
2246
+/*
2247
+** WEBPAGE: forume2_v2 hidden
2248
+**
2249
+** A work in progress.
2250
+*/
2251
+void forumedit_page_v2(void){
2252
+ const char *zFpid = PD("fpid","");
2253
+
2254
+ login_check_credentials();
2255
+ if( !g.perm.WrForum ){
2256
+ login_needed(g.anon.WrForum);
2257
+ return;
2258
+ }
2259
+ style_set_current_feature("forum");
2260
+ style_header("Edit Forum Post");
2261
+ (void)zFpid;
2262
+ @ Much to do here.
2263
+ @ <div hidden id='forumedit-placeholder'>
2264
+#if 0
2265
+ @ <input type='hidden' name='title' value='%h(zTitle)'>
2266
+#endif
2267
+ login_insert_csrf_secret();
2268
+ captcha_generate_for_js(0);
2269
+ @ </div>
2270
+ forum_emit_js();
2271
+ style_finish_page();
2272
+
22352273
}
22362274
22372275
/*
22382276
** SETTING: forum-close-policy boolean default=off
22392277
** If true, forum moderators may close/re-open forum posts, and reply
22402278
--- src/forum.c
+++ src/forum.c
@@ -1985,10 +1985,11 @@
1985 void forumnew_page(void){
1986 const char *zTitle = PDT("title","");
1987 const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE);
1988 const char *zContent = PDT("content","");
1989 const int bLegacy = PB("legacy"); /* True for legacy HTML form */
 
1990 login_check_credentials();
1991 if( !g.perm.WrForum ){
1992 login_needed(g.anon.WrForum);
1993 return;
1994 }
@@ -2027,10 +2028,11 @@
2027 When it's enabled, the above won't do anything and
2028 JS will render the editor form in the next element. */
2029 @ <div hidden id='forumnew-placeholder'>
2030 @ <input type='hidden' name='title' value='%h(zTitle)'>
2031 login_insert_csrf_secret();
 
2032 @ </div>
2033 }
2034 forum_emit_js();
2035 style_finish_page();
2036 }
@@ -2051,17 +2053,23 @@
2051 const char *zMimetype = 0;
2052 const char *zContent = 0;
2053 const char *zTitle = 0;
2054 char *zDate = 0;
2055 const char *zFpid = PD("fpid","");
 
2056 int isCsrfSafe;
2057 int isDelete = 0;
2058 int iClosed = 0;
2059 int bSameUser; /* True if author is also the reader */
2060 int bPreview; /* True in preview mode. */
2061 int bPrivate; /* True if post is private (not yet moderated) */
2062 int bReply; /* True if replying to a post */
 
 
 
 
 
2063
2064 login_check_credentials();
2065 if( !g.perm.WrForum ){
2066 login_needed(g.anon.WrForum);
2067 return;
@@ -2215,10 +2223,11 @@
2215 forum_post_widget(0, zMimetype, zContent);
2216 }
2217 if( !isDelete ){
2218 @ <input type="submit" name="preview" value="Preview">
2219 }
 
2220 @ <input type="submit" name="cancel" value="Cancel">
2221 if( (bPreview && !whitespace_only(zContent)) || isDelete ){
2222 if( !iClosed || g.perm.Admin ) {
2223 @ <input type="submit" name="submit" value="Submit">
2224 }
@@ -2230,10 +2239,39 @@
2230 forum_render_attachment_list(rid_to_uuid(fpid));
2231 }
2232 forum_render_attachment_notice();
2233 forum_emit_js();
2234 style_finish_page();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2235 }
2236
2237 /*
2238 ** SETTING: forum-close-policy boolean default=off
2239 ** If true, forum moderators may close/re-open forum posts, and reply
2240
--- src/forum.c
+++ src/forum.c
@@ -1985,10 +1985,11 @@
1985 void forumnew_page(void){
1986 const char *zTitle = PDT("title","");
1987 const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE);
1988 const char *zContent = PDT("content","");
1989 const int bLegacy = PB("legacy"); /* True for legacy HTML form */
1990
1991 login_check_credentials();
1992 if( !g.perm.WrForum ){
1993 login_needed(g.anon.WrForum);
1994 return;
1995 }
@@ -2027,10 +2028,11 @@
2028 When it's enabled, the above won't do anything and
2029 JS will render the editor form in the next element. */
2030 @ <div hidden id='forumnew-placeholder'>
2031 @ <input type='hidden' name='title' value='%h(zTitle)'>
2032 login_insert_csrf_secret();
2033 captcha_generate_for_js(0x02);
2034 @ </div>
2035 }
2036 forum_emit_js();
2037 style_finish_page();
2038 }
@@ -2051,17 +2053,23 @@
2053 const char *zMimetype = 0;
2054 const char *zContent = 0;
2055 const char *zTitle = 0;
2056 char *zDate = 0;
2057 const char *zFpid = PD("fpid","");
2058 const int bLegacy = PB("legacy"); /* True for legacy HTML form */
2059 int isCsrfSafe;
2060 int isDelete = 0;
2061 int iClosed = 0;
2062 int bSameUser; /* True if author is also the reader */
2063 int bPreview; /* True in preview mode. */
2064 int bPrivate; /* True if post is private (not yet moderated) */
2065 int bReply; /* True if replying to a post */
2066
2067 if( !bLegacy ){
2068 forumedit_page_v2();
2069 return;
2070 }
2071
2072 login_check_credentials();
2073 if( !g.perm.WrForum ){
2074 login_needed(g.anon.WrForum);
2075 return;
@@ -2215,10 +2223,11 @@
2223 forum_post_widget(0, zMimetype, zContent);
2224 }
2225 if( !isDelete ){
2226 @ <input type="submit" name="preview" value="Preview">
2227 }
2228 @ <input type="hidden" name="legacy" value="1">
2229 @ <input type="submit" name="cancel" value="Cancel">
2230 if( (bPreview && !whitespace_only(zContent)) || isDelete ){
2231 if( !iClosed || g.perm.Admin ) {
2232 @ <input type="submit" name="submit" value="Submit">
2233 }
@@ -2230,10 +2239,39 @@
2239 forum_render_attachment_list(rid_to_uuid(fpid));
2240 }
2241 forum_render_attachment_notice();
2242 forum_emit_js();
2243 style_finish_page();
2244 }
2245
2246 /*
2247 ** WEBPAGE: forume2_v2 hidden
2248 **
2249 ** A work in progress.
2250 */
2251 void forumedit_page_v2(void){
2252 const char *zFpid = PD("fpid","");
2253
2254 login_check_credentials();
2255 if( !g.perm.WrForum ){
2256 login_needed(g.anon.WrForum);
2257 return;
2258 }
2259 style_set_current_feature("forum");
2260 style_header("Edit Forum Post");
2261 (void)zFpid;
2262 @ Much to do here.
2263 @ <div hidden id='forumedit-placeholder'>
2264 #if 0
2265 @ <input type='hidden' name='title' value='%h(zTitle)'>
2266 #endif
2267 login_insert_csrf_secret();
2268 captcha_generate_for_js(0);
2269 @ </div>
2270 forum_emit_js();
2271 style_finish_page();
2272
2273 }
2274
2275 /*
2276 ** SETTING: forum-close-policy boolean default=off
2277 ** If true, forum moderators may close/re-open forum posts, and reply
2278
--- src/fossil.page.forumpost.js
+++ src/fossil.page.forumpost.js
@@ -119,10 +119,25 @@
119119
120120
e.error = D.addClass(D.div(), 'error', 'hidden');
121121
wrapper.append(e.error);
122122
e.error.addEventListener('dblclick',()=>this.reportError());
123123
}
124
+
125
+ if( opt.captcha ){
126
+ const eCap = opt.captcha;
127
+ const w = D.div();
128
+ w.style.display = 'flex';
129
+ w.style.flexDirection = 'row';
130
+ w.style.gap = '1em';
131
+ eCap.style.fontFamily = 'monospace';
132
+ eCap.style.whiteSpace = 'pre';
133
+ eCap.style.fontSize = '50%';
134
+ e.captcha = D.attr(D.input('text'), 'size', 8);
135
+ w.append("Enter captcha value:", e.captcha);
136
+ wrapper.append(eCap, w);
137
+ eCap.classList.remove('hidden');
138
+ }
124139
125140
const idPrefix = 'FormPostEditor'+(++idCounter)/* TabManager requires IDs */;
126141
{ /* Main tabs... */
127142
e.tabs = D.attr(
128143
D.addClass(D.div(), 'tab-container'),
@@ -389,10 +404,13 @@
389404
fd.append(f.name, f.value);
390405
}
391406
fd.append('mimetype', this.mimetype);
392407
fd.append('title', this.title.trim());
393408
fd.append('content', addThisContent || this.editorContent.trim());
409
+ if( this.#e.captcha ){
410
+ fd.append('captcha', this.#e.captcha.value);
411
+ }
394412
return fd;
395413
}
396414
397415
async #fetchPreview(content){
398416
/* TODO: fetch preview */
@@ -455,10 +473,14 @@
455473
D.enable(this.#toDisable);
456474
});
457475
}
458476
459477
#validate(tgt){
478
+ if( this.#e.captcha && 8!==this.#e.captcha.value.length ){
479
+ this.reportError("Enter the captcha value.");
480
+ return;
481
+ }
460482
if( this.#opt.isNewThread ){
461483
let v = this.#e.title.value.trim();
462484
if( !v ){
463485
this.reportError("A non-empty title is required.");
464486
return;
@@ -684,14 +706,15 @@
684706
: null;
685707
if( eForumNew ){
686708
/* /forumnew */
687709
const fpe = new fossil.ForumPostEditor({
688710
draftKey: 'forumnew',
689
- hiddenFields: eForumNew.querySelectorAll('input[type=hidden]')
711
+ hiddenFields: eForumNew.querySelectorAll('input[type=hidden]'),
712
+ captcha: eForumNew.querySelector('.captcha-for-js')
690713
//mimetype: 'text/plain'
691714
});
692715
eForumNew.parentElement.insertBefore(fpe.widget, eForumNew);
693716
eForumNew.remove();
694717
fossil.page.fpe = fpe /* for testing via the console */;
695718
}/*eForumNew*/
696719
})/*F.onPageLoad callback*/;
697720
})(window.fossil);
698721
--- src/fossil.page.forumpost.js
+++ src/fossil.page.forumpost.js
@@ -119,10 +119,25 @@
119
120 e.error = D.addClass(D.div(), 'error', 'hidden');
121 wrapper.append(e.error);
122 e.error.addEventListener('dblclick',()=>this.reportError());
123 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
125 const idPrefix = 'FormPostEditor'+(++idCounter)/* TabManager requires IDs */;
126 { /* Main tabs... */
127 e.tabs = D.attr(
128 D.addClass(D.div(), 'tab-container'),
@@ -389,10 +404,13 @@
389 fd.append(f.name, f.value);
390 }
391 fd.append('mimetype', this.mimetype);
392 fd.append('title', this.title.trim());
393 fd.append('content', addThisContent || this.editorContent.trim());
 
 
 
394 return fd;
395 }
396
397 async #fetchPreview(content){
398 /* TODO: fetch preview */
@@ -455,10 +473,14 @@
455 D.enable(this.#toDisable);
456 });
457 }
458
459 #validate(tgt){
 
 
 
 
460 if( this.#opt.isNewThread ){
461 let v = this.#e.title.value.trim();
462 if( !v ){
463 this.reportError("A non-empty title is required.");
464 return;
@@ -684,14 +706,15 @@
684 : null;
685 if( eForumNew ){
686 /* /forumnew */
687 const fpe = new fossil.ForumPostEditor({
688 draftKey: 'forumnew',
689 hiddenFields: eForumNew.querySelectorAll('input[type=hidden]')
 
690 //mimetype: 'text/plain'
691 });
692 eForumNew.parentElement.insertBefore(fpe.widget, eForumNew);
693 eForumNew.remove();
694 fossil.page.fpe = fpe /* for testing via the console */;
695 }/*eForumNew*/
696 })/*F.onPageLoad callback*/;
697 })(window.fossil);
698
--- src/fossil.page.forumpost.js
+++ src/fossil.page.forumpost.js
@@ -119,10 +119,25 @@
119
120 e.error = D.addClass(D.div(), 'error', 'hidden');
121 wrapper.append(e.error);
122 e.error.addEventListener('dblclick',()=>this.reportError());
123 }
124
125 if( opt.captcha ){
126 const eCap = opt.captcha;
127 const w = D.div();
128 w.style.display = 'flex';
129 w.style.flexDirection = 'row';
130 w.style.gap = '1em';
131 eCap.style.fontFamily = 'monospace';
132 eCap.style.whiteSpace = 'pre';
133 eCap.style.fontSize = '50%';
134 e.captcha = D.attr(D.input('text'), 'size', 8);
135 w.append("Enter captcha value:", e.captcha);
136 wrapper.append(eCap, w);
137 eCap.classList.remove('hidden');
138 }
139
140 const idPrefix = 'FormPostEditor'+(++idCounter)/* TabManager requires IDs */;
141 { /* Main tabs... */
142 e.tabs = D.attr(
143 D.addClass(D.div(), 'tab-container'),
@@ -389,10 +404,13 @@
404 fd.append(f.name, f.value);
405 }
406 fd.append('mimetype', this.mimetype);
407 fd.append('title', this.title.trim());
408 fd.append('content', addThisContent || this.editorContent.trim());
409 if( this.#e.captcha ){
410 fd.append('captcha', this.#e.captcha.value);
411 }
412 return fd;
413 }
414
415 async #fetchPreview(content){
416 /* TODO: fetch preview */
@@ -455,10 +473,14 @@
473 D.enable(this.#toDisable);
474 });
475 }
476
477 #validate(tgt){
478 if( this.#e.captcha && 8!==this.#e.captcha.value.length ){
479 this.reportError("Enter the captcha value.");
480 return;
481 }
482 if( this.#opt.isNewThread ){
483 let v = this.#e.title.value.trim();
484 if( !v ){
485 this.reportError("A non-empty title is required.");
486 return;
@@ -684,14 +706,15 @@
706 : null;
707 if( eForumNew ){
708 /* /forumnew */
709 const fpe = new fossil.ForumPostEditor({
710 draftKey: 'forumnew',
711 hiddenFields: eForumNew.querySelectorAll('input[type=hidden]'),
712 captcha: eForumNew.querySelector('.captcha-for-js')
713 //mimetype: 'text/plain'
714 });
715 eForumNew.parentElement.insertBefore(fpe.widget, eForumNew);
716 eForumNew.remove();
717 fossil.page.fpe = fpe /* for testing via the console */;
718 }/*eForumNew*/
719 })/*F.onPageLoad callback*/;
720 })(window.fossil);
721

Keyboard Shortcuts

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