Fossil SCM

Implemented HTML-in-iframe preview. Related internal cleanups.

stephan 2020-05-04 12:46 checkin-without-checkout
Commit ac309281e61e03a5cd38e81bc019d1102080c5db8bb8193bd8b27be81e66ffd4
1 file changed +88 -58
+88 -58
--- src/fileedit.c
+++ src/fileedit.c
@@ -992,64 +992,57 @@
992992
}
993993
994994
enum fileedit_render_preview_flags {
995995
FE_PREVIEW_LINE_NUMBERS = 1
996996
};
997
+enum fileedit_render_modes {
998
+FE_RENDER_PLAIN_TEXT = 0,
999
+FE_RENDER_HTML,
1000
+FE_RENDER_WIKI
1001
+};
1002
+
1003
+static int fileedit_render_mode_for_mimetype(const char * zMimetype){
1004
+ int rc = FE_RENDER_PLAIN_TEXT;
1005
+ if( zMimetype ){
1006
+ if( fossil_strcmp(zMimetype, "text/html")==0 ){
1007
+ rc = FE_RENDER_HTML;
1008
+ }else if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0
1009
+ || fossil_strcmp(zMimetype, "text/x-markdown")==0 ){
1010
+ rc = FE_RENDER_WIKI;
1011
+ }
1012
+ }
1013
+ return rc;
1014
+}
9971015
9981016
/*
9991017
** Performs the PREVIEW mode for /filepage.
10001018
*/
10011019
static void fileedit_render_preview(Blob * pContent,
10021020
const char *zFilename,
1003
- int flags){
1021
+ int flags,
1022
+ int nIframeHeightEm){
10041023
const char * zMime;
1005
- enum render_modes {PLAIN_TEXT = 0, HTML, WIKI};
1006
- int renderMode = PLAIN_TEXT;
1024
+ int renderMode = FE_RENDER_PLAIN_TEXT;
10071025
zMime = mimetype_from_name(zFilename);
1008
- if( zMime ){
1009
- if( fossil_strcmp(zMime, "text/html")==0 ){
1010
- renderMode = HTML;
1011
- }else if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0
1012
- || fossil_strcmp(zMime, "text/x-markdown")==0 ){
1013
- renderMode = WIKI;
1014
- }
1015
- }
1026
+ renderMode = fileedit_render_mode_for_mimetype(zMime);
10161027
CX("<div class='fileedit-preview'>");
10171028
CX("<div>Preview</div>");
10181029
switch(renderMode){
1019
- case HTML:{
1030
+ case FE_RENDER_HTML:{
1031
+ char * z64 = encode64(blob_str(pContent), blob_size(pContent));
10201032
CX("<iframe width='100%%' frameborder='0' marginwidth='0' "
1033
+ "style='height:%dem' "
10211034
"marginheight='0' sandbox='allow-same-origin' id='ifm1' "
1022
- "srcdoc='Not yet working: not sure how to "
1023
- "populate the iframe.'"
1024
- "></iframe>");
1025
-#if 0
1026
- fileedit_emit_script(0);
1027
- CX("document.getElementById('ifm1').addEventListener('load',"
1028
- "function(){\n"
1029
- "console.debug('iframe=',this);\n"
1030
- "this.height=this.contentDocument.documentElement."
1031
- "scrollHeight + 75;\n"
1032
- "this.contentDocument.body.innerHTML=`%h`;\n"
1033
- "});\n",
1034
- blob_str(pContent));
1035
- /* Potential TODO: use iframe.srcdoc:
1036
- **
1037
- ** https://caniuse.com/#search=srcdoc
1038
- ** https://stackoverflow.com/questions/22381216/escape-quotes-in-an-iframe-srcdoc-value
1039
- **
1040
- ** Doing so would require escaping the quote characters which match
1041
- ** the srcdoc='xyz' quotes.
1042
- */
1043
- fileedit_emit_script(1);
1044
-#endif
1035
+ "src='data:text/html;base64,%z'"
1036
+ "></iframe>", nIframeHeightEm ? nIframeHeightEm : 40,
1037
+ z64);
10451038
break;
10461039
}
1047
- case WIKI:
1040
+ case FE_RENDER_WIKI:
10481041
wiki_render_by_mimetype(pContent, zMime);
10491042
break;
1050
- case PLAIN_TEXT:
1043
+ case FE_RENDER_PLAIN_TEXT:
10511044
default:{
10521045
const char *zExt = strrchr(zFilename,'.');
10531046
const char *zContent = blob_str(pContent);
10541047
if(FE_PREVIEW_LINE_NUMBERS & flags){
10551048
output_text_with_line_numbers(zContent, "on");
@@ -1080,22 +1073,28 @@
10801073
** All other parameters are for internal use only, submitted via the
10811074
** form-submission process, and may change with any given revision of
10821075
** this code.
10831076
*/
10841077
void fileedit_page(){
1078
+ enum submit_modes {
1079
+ SUBMIT_NONE = 0, SUBMIT_SAVE = 1, SUBMIT_PREVIEW = 2,
1080
+ SUBMIT_DIFF = 3
1081
+ };
10851082
const char * zFilename = PD("file",P("name"));
10861083
/* filename. We'll accept 'name'
10871084
because that param is handled
10881085
specially by the core. */
10891086
const char * zRev = P("r"); /* checkin version */
10901087
const char * zContent = P("content"); /* file content */
10911088
const char * zComment = P("comment"); /* checkin comment */
1089
+ const char * zFileMime = 0; /* File mime type guess */
10921090
CheckinMiniInfo cimi; /* Checkin state */
1093
- int submitMode = 0; /* See mapping below */
1091
+ int submitMode = SUBMIT_NONE; /* See mapping below */
10941092
int vid, newVid = 0; /* checkin rid */
10951093
int frid = 0; /* File content rid */
10961094
int previewLn = P("preview_ln")!=0; /* Line number mode */
1095
+ int previewHtmlHeight = 0; /* iframe height (EMs) */
10971096
char * zFileUuid = 0; /* File content UUID */
10981097
Blob err = empty_blob; /* Error report */
10991098
const char * zFlagCheck = 0; /* Temp url flag holder */
11001099
Blob endScript = empty_blob; /* Script code to run at the
11011100
end. This content will be
@@ -1132,13 +1131,14 @@
11321131
login_needed(g.anon.Write);
11331132
return;
11341133
}
11351134
db_begin_transaction();
11361135
CheckinMiniInfo_init(&cimi);
1137
- submitMode = atoi(PD("submit","0"))
1138
- /* Submit modes: 0=initial request,
1139
- ** 1=submit (save), 2=preview, 3=diff */;
1136
+ submitMode = atoi(PD("submit","0"));
1137
+ if(submitMode < SUBMIT_NONE || submitMode > SUBMIT_DIFF){
1138
+ submitMode = 0;
1139
+ }
11401140
zFlagCheck = P("comment_mimetype");
11411141
if(zFlagCheck){
11421142
cimi.zMimetype = mprintf("%s",zFlagCheck);
11431143
zFlagCheck = 0;
11441144
}
@@ -1162,10 +1162,11 @@
11621162
vid = symbolic_name_to_rid(zRev, "ci");
11631163
if(0==vid){
11641164
fail((&err,"Could not resolve checkin version."));
11651165
}
11661166
cimi.zFilename = mprintf("%s",zFilename);
1167
+ zFileMime = mimetype_from_name(zFilename);
11671168
11681169
/* Find the repo-side file entry or fail... */
11691170
cimi.zParentUuid = rid_to_uuid(vid);
11701171
db_prepare(&stmt, "SELECT uuid, perm FROM files_of_checkin "
11711172
"WHERE filename=%Q %s AND checkinID=%d",
@@ -1222,13 +1223,12 @@
12221223
CX("</p>");
12231224
CX("<p>This page is <em>far from complete</em> and may still have "
12241225
"significant bugs. USE AT YOUR OWN RISK, preferably on a test "
12251226
"repo.</p>\n");
12261227
1227
- CX("<form action='%R/fileedit%s' method='POST' "
1228
- "class='fileedit-form'>\n",
1229
- submitMode>0 ? "#options" : "");
1228
+ CX("<form action='%R/fileedit#options' method='POST' "
1229
+ "class='fileedit-form'>\n");
12301230
12311231
/******* Hidden fields *******/
12321232
CX("<input type='hidden' name='r' value='%s'>",
12331233
cimi.zParentUuid);
12341234
CX("<input type='hidden' name='file' value='%T'>",
@@ -1263,11 +1263,11 @@
12631263
** a containing div is necessary. */);
12641264
/*
12651265
** TODO?: date-override date selection field. Maybe use
12661266
** an input[type=datetime-local].
12671267
*/
1268
- if(0==submitMode || P("dry_run")!=0){
1268
+ if(SUBMIT_NONE==submitMode || P("dry_run")!=0){
12691269
cimi.flags |= CIMINI_DRY_RUN;
12701270
}
12711271
style_labeled_checkbox("dry_run", "Dry-run?", "1",
12721272
"In dry-run mode, the Save button performs "
12731273
"all work needed for saving but then rolls "
@@ -1310,11 +1310,12 @@
13101310
"Will create a delta manifest, instead of "
13111311
"baseline, if conditions are favorable to do "
13121312
"so. This option is only a suggestion.",
13131313
cimi.flags & CIMINI_PREFER_DELTA);
13141314
{/* EOL conversion policy... */
1315
- const int eolMode = submitMode==0 ? 0 : atoi(PD("eol","0"));
1315
+ const int eolMode = submitMode==SUBMIT_NONE
1316
+ ? 0 : atoi(PD("eol","0"));
13161317
switch(eolMode){
13171318
case 1: cimi.flags |= CIMINI_CONVERT_EOL_UNIX; break;
13181319
case 2: cimi.flags |= CIMINI_CONVERT_EOL_WINDOWS; break;
13191320
default: cimi.flags |= CIMINI_CONVERT_EOL_INHERIT; break;
13201321
}
@@ -1340,23 +1341,51 @@
13401341
"Save</button>");
13411342
CX("<button type='submit' name='submit' value='2'>"
13421343
"Preview</button>");
13431344
CX("<button type='submit' name='submit' value='3'>"
13441345
"Diff (TODO)</button>");
1345
- CX("<br>");
1346
- style_labeled_checkbox("preview_ln",
1347
- "Add line numbers to plain-text previews?", "1",
1348
- "If on, plain-text files (only) will get "
1349
- "line numbers added to the preview.",
1350
- previewLn);
1346
+ {
1347
+ /* Options which depend on the current submitMode or
1348
+ the file's preview rendering mode. */
1349
+ const int renderMode = fileedit_render_mode_for_mimetype(zFileMime);
1350
+ CX("<br>");
1351
+ if(FE_RENDER_HTML==renderMode){
1352
+ /* HTML preview mode iframe height... */
1353
+ int i;
1354
+ if(submitMode==SUBMIT_PREVIEW){
1355
+ previewHtmlHeight = atoi(PD("preview_html_ems","0"));
1356
+ }else{
1357
+ previewHtmlHeight = 40;
1358
+ }
1359
+ /* Allow selection of HTML preview iframe height */
1360
+ CX("<select name='preview_html_ems' "
1361
+ "title='Height (in EMs) of the iframe used for HTML "
1362
+ "preview.'>\n");
1363
+ CX("<option disabled value='40'>HTML Preview Height (EMs)"
1364
+ "</option>\n");
1365
+ for( i = 20; i <= 100; i+=20 ){
1366
+ CX("<option value='%d'%s>%d</option>\n",
1367
+ i, (previewHtmlHeight==i) ? " selected" : "", i);
1368
+ }
1369
+ CX("</select>\n");
1370
+ }
1371
+ else if(FE_RENDER_PLAIN_TEXT == renderMode){
1372
+ style_labeled_checkbox("preview_ln",
1373
+ "Add line numbers to plain-text previews?",
1374
+ "1",
1375
+ "If on, plain-text files (only) will get "
1376
+ "line numbers added to the preview.",
1377
+ previewLn);
1378
+ }
1379
+ }
13511380
CX("</div></fieldset>");
13521381
13531382
/******* End of form *******/
13541383
CX("</form>\n");
13551384
13561385
/* Dynamically populate the editor... */
1357
- if(1==loadMode || (2==loadMode && submitMode>0)){
1386
+ if(1==loadMode || (2==loadMode && submitMode>SUBMIT_NONE)){
13581387
char const * zQuoted = 0;
13591388
if(blob_size(&cimi.fileContent)>0){
13601389
db_prepare(&stmt, "SELECT json_quote(%B)", &cimi.fileContent);
13611390
db_step(&stmt);
13621391
zQuoted = db_column_text(&stmt,0);
@@ -1367,11 +1396,11 @@
13671396
".value=%s;", zQuoted ? zQuoted : "'';\n");
13681397
if(stmt.pStmt){
13691398
db_finalize(&stmt);
13701399
}
13711400
}else if(2==loadMode){
1372
- assert(submitMode==0);
1401
+ assert(submitMode==SUBMIT_NONE);
13731402
fileedit_emit_script_fetch();
13741403
blob_appendf(&endScript,
13751404
"window.fossilFetch('raw/%s',{"
13761405
"onload: (r)=>document.getElementById('fileedit-content')"
13771406
".value=r,"
@@ -1379,11 +1408,11 @@
13791408
".value="
13801409
"'Error loading content'"
13811410
"});\n", zFileUuid);
13821411
}
13831412
1384
- if(1==submitMode/*save*/){
1413
+ if(SUBMIT_SAVE==submitMode){
13851414
Blob manifest = empty_blob;
13861415
char * zNewUuid = 0;
13871416
/*cimi.flags |= CIMINI_STRONGLY_PREFER_DELTA;*/
13881417
if(zComment && *zComment){
13891418
blob_append(&cimi.comment, zComment, -1);
@@ -1442,15 +1471,16 @@
14421471
}
14431472
/* On error, the error message is in the err blob and will
14441473
** be emitted below. */
14451474
cimi.pMfOut = 0;
14461475
blob_reset(&manifest);
1447
- }else if(2==submitMode/*preview*/){
1476
+ }else if(SUBMIT_PREVIEW==submitMode){
14481477
int pflags = 0;
14491478
if(previewLn) pflags |= FE_PREVIEW_LINE_NUMBERS;
1450
- fileedit_render_preview(&cimi.fileContent, cimi.zFilename, pflags);
1451
- }else if(3==submitMode/*diff*/){
1479
+ fileedit_render_preview(&cimi.fileContent, cimi.zFilename, pflags,
1480
+ previewHtmlHeight);
1481
+ }else if(SUBMIT_DIFF==submitMode/*diff*/){
14521482
fail((&err,"Diff mode is still TODO."));
14531483
}else{
14541484
/* Ignore invalid submitMode value */
14551485
goto end_footer;
14561486
}
14571487
--- src/fileedit.c
+++ src/fileedit.c
@@ -992,64 +992,57 @@
992 }
993
994 enum fileedit_render_preview_flags {
995 FE_PREVIEW_LINE_NUMBERS = 1
996 };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
997
998 /*
999 ** Performs the PREVIEW mode for /filepage.
1000 */
1001 static void fileedit_render_preview(Blob * pContent,
1002 const char *zFilename,
1003 int flags){
 
1004 const char * zMime;
1005 enum render_modes {PLAIN_TEXT = 0, HTML, WIKI};
1006 int renderMode = PLAIN_TEXT;
1007 zMime = mimetype_from_name(zFilename);
1008 if( zMime ){
1009 if( fossil_strcmp(zMime, "text/html")==0 ){
1010 renderMode = HTML;
1011 }else if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0
1012 || fossil_strcmp(zMime, "text/x-markdown")==0 ){
1013 renderMode = WIKI;
1014 }
1015 }
1016 CX("<div class='fileedit-preview'>");
1017 CX("<div>Preview</div>");
1018 switch(renderMode){
1019 case HTML:{
 
1020 CX("<iframe width='100%%' frameborder='0' marginwidth='0' "
 
1021 "marginheight='0' sandbox='allow-same-origin' id='ifm1' "
1022 "srcdoc='Not yet working: not sure how to "
1023 "populate the iframe.'"
1024 "></iframe>");
1025 #if 0
1026 fileedit_emit_script(0);
1027 CX("document.getElementById('ifm1').addEventListener('load',"
1028 "function(){\n"
1029 "console.debug('iframe=',this);\n"
1030 "this.height=this.contentDocument.documentElement."
1031 "scrollHeight + 75;\n"
1032 "this.contentDocument.body.innerHTML=`%h`;\n"
1033 "});\n",
1034 blob_str(pContent));
1035 /* Potential TODO: use iframe.srcdoc:
1036 **
1037 ** https://caniuse.com/#search=srcdoc
1038 ** https://stackoverflow.com/questions/22381216/escape-quotes-in-an-iframe-srcdoc-value
1039 **
1040 ** Doing so would require escaping the quote characters which match
1041 ** the srcdoc='xyz' quotes.
1042 */
1043 fileedit_emit_script(1);
1044 #endif
1045 break;
1046 }
1047 case WIKI:
1048 wiki_render_by_mimetype(pContent, zMime);
1049 break;
1050 case PLAIN_TEXT:
1051 default:{
1052 const char *zExt = strrchr(zFilename,'.');
1053 const char *zContent = blob_str(pContent);
1054 if(FE_PREVIEW_LINE_NUMBERS & flags){
1055 output_text_with_line_numbers(zContent, "on");
@@ -1080,22 +1073,28 @@
1080 ** All other parameters are for internal use only, submitted via the
1081 ** form-submission process, and may change with any given revision of
1082 ** this code.
1083 */
1084 void fileedit_page(){
 
 
 
 
1085 const char * zFilename = PD("file",P("name"));
1086 /* filename. We'll accept 'name'
1087 because that param is handled
1088 specially by the core. */
1089 const char * zRev = P("r"); /* checkin version */
1090 const char * zContent = P("content"); /* file content */
1091 const char * zComment = P("comment"); /* checkin comment */
 
1092 CheckinMiniInfo cimi; /* Checkin state */
1093 int submitMode = 0; /* See mapping below */
1094 int vid, newVid = 0; /* checkin rid */
1095 int frid = 0; /* File content rid */
1096 int previewLn = P("preview_ln")!=0; /* Line number mode */
 
1097 char * zFileUuid = 0; /* File content UUID */
1098 Blob err = empty_blob; /* Error report */
1099 const char * zFlagCheck = 0; /* Temp url flag holder */
1100 Blob endScript = empty_blob; /* Script code to run at the
1101 end. This content will be
@@ -1132,13 +1131,14 @@
1132 login_needed(g.anon.Write);
1133 return;
1134 }
1135 db_begin_transaction();
1136 CheckinMiniInfo_init(&cimi);
1137 submitMode = atoi(PD("submit","0"))
1138 /* Submit modes: 0=initial request,
1139 ** 1=submit (save), 2=preview, 3=diff */;
 
1140 zFlagCheck = P("comment_mimetype");
1141 if(zFlagCheck){
1142 cimi.zMimetype = mprintf("%s",zFlagCheck);
1143 zFlagCheck = 0;
1144 }
@@ -1162,10 +1162,11 @@
1162 vid = symbolic_name_to_rid(zRev, "ci");
1163 if(0==vid){
1164 fail((&err,"Could not resolve checkin version."));
1165 }
1166 cimi.zFilename = mprintf("%s",zFilename);
 
1167
1168 /* Find the repo-side file entry or fail... */
1169 cimi.zParentUuid = rid_to_uuid(vid);
1170 db_prepare(&stmt, "SELECT uuid, perm FROM files_of_checkin "
1171 "WHERE filename=%Q %s AND checkinID=%d",
@@ -1222,13 +1223,12 @@
1222 CX("</p>");
1223 CX("<p>This page is <em>far from complete</em> and may still have "
1224 "significant bugs. USE AT YOUR OWN RISK, preferably on a test "
1225 "repo.</p>\n");
1226
1227 CX("<form action='%R/fileedit%s' method='POST' "
1228 "class='fileedit-form'>\n",
1229 submitMode>0 ? "#options" : "");
1230
1231 /******* Hidden fields *******/
1232 CX("<input type='hidden' name='r' value='%s'>",
1233 cimi.zParentUuid);
1234 CX("<input type='hidden' name='file' value='%T'>",
@@ -1263,11 +1263,11 @@
1263 ** a containing div is necessary. */);
1264 /*
1265 ** TODO?: date-override date selection field. Maybe use
1266 ** an input[type=datetime-local].
1267 */
1268 if(0==submitMode || P("dry_run")!=0){
1269 cimi.flags |= CIMINI_DRY_RUN;
1270 }
1271 style_labeled_checkbox("dry_run", "Dry-run?", "1",
1272 "In dry-run mode, the Save button performs "
1273 "all work needed for saving but then rolls "
@@ -1310,11 +1310,12 @@
1310 "Will create a delta manifest, instead of "
1311 "baseline, if conditions are favorable to do "
1312 "so. This option is only a suggestion.",
1313 cimi.flags & CIMINI_PREFER_DELTA);
1314 {/* EOL conversion policy... */
1315 const int eolMode = submitMode==0 ? 0 : atoi(PD("eol","0"));
 
1316 switch(eolMode){
1317 case 1: cimi.flags |= CIMINI_CONVERT_EOL_UNIX; break;
1318 case 2: cimi.flags |= CIMINI_CONVERT_EOL_WINDOWS; break;
1319 default: cimi.flags |= CIMINI_CONVERT_EOL_INHERIT; break;
1320 }
@@ -1340,23 +1341,51 @@
1340 "Save</button>");
1341 CX("<button type='submit' name='submit' value='2'>"
1342 "Preview</button>");
1343 CX("<button type='submit' name='submit' value='3'>"
1344 "Diff (TODO)</button>");
1345 CX("<br>");
1346 style_labeled_checkbox("preview_ln",
1347 "Add line numbers to plain-text previews?", "1",
1348 "If on, plain-text files (only) will get "
1349 "line numbers added to the preview.",
1350 previewLn);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1351 CX("</div></fieldset>");
1352
1353 /******* End of form *******/
1354 CX("</form>\n");
1355
1356 /* Dynamically populate the editor... */
1357 if(1==loadMode || (2==loadMode && submitMode>0)){
1358 char const * zQuoted = 0;
1359 if(blob_size(&cimi.fileContent)>0){
1360 db_prepare(&stmt, "SELECT json_quote(%B)", &cimi.fileContent);
1361 db_step(&stmt);
1362 zQuoted = db_column_text(&stmt,0);
@@ -1367,11 +1396,11 @@
1367 ".value=%s;", zQuoted ? zQuoted : "'';\n");
1368 if(stmt.pStmt){
1369 db_finalize(&stmt);
1370 }
1371 }else if(2==loadMode){
1372 assert(submitMode==0);
1373 fileedit_emit_script_fetch();
1374 blob_appendf(&endScript,
1375 "window.fossilFetch('raw/%s',{"
1376 "onload: (r)=>document.getElementById('fileedit-content')"
1377 ".value=r,"
@@ -1379,11 +1408,11 @@
1379 ".value="
1380 "'Error loading content'"
1381 "});\n", zFileUuid);
1382 }
1383
1384 if(1==submitMode/*save*/){
1385 Blob manifest = empty_blob;
1386 char * zNewUuid = 0;
1387 /*cimi.flags |= CIMINI_STRONGLY_PREFER_DELTA;*/
1388 if(zComment && *zComment){
1389 blob_append(&cimi.comment, zComment, -1);
@@ -1442,15 +1471,16 @@
1442 }
1443 /* On error, the error message is in the err blob and will
1444 ** be emitted below. */
1445 cimi.pMfOut = 0;
1446 blob_reset(&manifest);
1447 }else if(2==submitMode/*preview*/){
1448 int pflags = 0;
1449 if(previewLn) pflags |= FE_PREVIEW_LINE_NUMBERS;
1450 fileedit_render_preview(&cimi.fileContent, cimi.zFilename, pflags);
1451 }else if(3==submitMode/*diff*/){
 
1452 fail((&err,"Diff mode is still TODO."));
1453 }else{
1454 /* Ignore invalid submitMode value */
1455 goto end_footer;
1456 }
1457
--- src/fileedit.c
+++ src/fileedit.c
@@ -992,64 +992,57 @@
992 }
993
994 enum fileedit_render_preview_flags {
995 FE_PREVIEW_LINE_NUMBERS = 1
996 };
997 enum fileedit_render_modes {
998 FE_RENDER_PLAIN_TEXT = 0,
999 FE_RENDER_HTML,
1000 FE_RENDER_WIKI
1001 };
1002
1003 static int fileedit_render_mode_for_mimetype(const char * zMimetype){
1004 int rc = FE_RENDER_PLAIN_TEXT;
1005 if( zMimetype ){
1006 if( fossil_strcmp(zMimetype, "text/html")==0 ){
1007 rc = FE_RENDER_HTML;
1008 }else if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0
1009 || fossil_strcmp(zMimetype, "text/x-markdown")==0 ){
1010 rc = FE_RENDER_WIKI;
1011 }
1012 }
1013 return rc;
1014 }
1015
1016 /*
1017 ** Performs the PREVIEW mode for /filepage.
1018 */
1019 static void fileedit_render_preview(Blob * pContent,
1020 const char *zFilename,
1021 int flags,
1022 int nIframeHeightEm){
1023 const char * zMime;
1024 int renderMode = FE_RENDER_PLAIN_TEXT;
 
1025 zMime = mimetype_from_name(zFilename);
1026 renderMode = fileedit_render_mode_for_mimetype(zMime);
 
 
 
 
 
 
 
1027 CX("<div class='fileedit-preview'>");
1028 CX("<div>Preview</div>");
1029 switch(renderMode){
1030 case FE_RENDER_HTML:{
1031 char * z64 = encode64(blob_str(pContent), blob_size(pContent));
1032 CX("<iframe width='100%%' frameborder='0' marginwidth='0' "
1033 "style='height:%dem' "
1034 "marginheight='0' sandbox='allow-same-origin' id='ifm1' "
1035 "src='data:text/html;base64,%z'"
1036 "></iframe>", nIframeHeightEm ? nIframeHeightEm : 40,
1037 z64);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1038 break;
1039 }
1040 case FE_RENDER_WIKI:
1041 wiki_render_by_mimetype(pContent, zMime);
1042 break;
1043 case FE_RENDER_PLAIN_TEXT:
1044 default:{
1045 const char *zExt = strrchr(zFilename,'.');
1046 const char *zContent = blob_str(pContent);
1047 if(FE_PREVIEW_LINE_NUMBERS & flags){
1048 output_text_with_line_numbers(zContent, "on");
@@ -1080,22 +1073,28 @@
1073 ** All other parameters are for internal use only, submitted via the
1074 ** form-submission process, and may change with any given revision of
1075 ** this code.
1076 */
1077 void fileedit_page(){
1078 enum submit_modes {
1079 SUBMIT_NONE = 0, SUBMIT_SAVE = 1, SUBMIT_PREVIEW = 2,
1080 SUBMIT_DIFF = 3
1081 };
1082 const char * zFilename = PD("file",P("name"));
1083 /* filename. We'll accept 'name'
1084 because that param is handled
1085 specially by the core. */
1086 const char * zRev = P("r"); /* checkin version */
1087 const char * zContent = P("content"); /* file content */
1088 const char * zComment = P("comment"); /* checkin comment */
1089 const char * zFileMime = 0; /* File mime type guess */
1090 CheckinMiniInfo cimi; /* Checkin state */
1091 int submitMode = SUBMIT_NONE; /* See mapping below */
1092 int vid, newVid = 0; /* checkin rid */
1093 int frid = 0; /* File content rid */
1094 int previewLn = P("preview_ln")!=0; /* Line number mode */
1095 int previewHtmlHeight = 0; /* iframe height (EMs) */
1096 char * zFileUuid = 0; /* File content UUID */
1097 Blob err = empty_blob; /* Error report */
1098 const char * zFlagCheck = 0; /* Temp url flag holder */
1099 Blob endScript = empty_blob; /* Script code to run at the
1100 end. This content will be
@@ -1132,13 +1131,14 @@
1131 login_needed(g.anon.Write);
1132 return;
1133 }
1134 db_begin_transaction();
1135 CheckinMiniInfo_init(&cimi);
1136 submitMode = atoi(PD("submit","0"));
1137 if(submitMode < SUBMIT_NONE || submitMode > SUBMIT_DIFF){
1138 submitMode = 0;
1139 }
1140 zFlagCheck = P("comment_mimetype");
1141 if(zFlagCheck){
1142 cimi.zMimetype = mprintf("%s",zFlagCheck);
1143 zFlagCheck = 0;
1144 }
@@ -1162,10 +1162,11 @@
1162 vid = symbolic_name_to_rid(zRev, "ci");
1163 if(0==vid){
1164 fail((&err,"Could not resolve checkin version."));
1165 }
1166 cimi.zFilename = mprintf("%s",zFilename);
1167 zFileMime = mimetype_from_name(zFilename);
1168
1169 /* Find the repo-side file entry or fail... */
1170 cimi.zParentUuid = rid_to_uuid(vid);
1171 db_prepare(&stmt, "SELECT uuid, perm FROM files_of_checkin "
1172 "WHERE filename=%Q %s AND checkinID=%d",
@@ -1222,13 +1223,12 @@
1223 CX("</p>");
1224 CX("<p>This page is <em>far from complete</em> and may still have "
1225 "significant bugs. USE AT YOUR OWN RISK, preferably on a test "
1226 "repo.</p>\n");
1227
1228 CX("<form action='%R/fileedit#options' method='POST' "
1229 "class='fileedit-form'>\n");
 
1230
1231 /******* Hidden fields *******/
1232 CX("<input type='hidden' name='r' value='%s'>",
1233 cimi.zParentUuid);
1234 CX("<input type='hidden' name='file' value='%T'>",
@@ -1263,11 +1263,11 @@
1263 ** a containing div is necessary. */);
1264 /*
1265 ** TODO?: date-override date selection field. Maybe use
1266 ** an input[type=datetime-local].
1267 */
1268 if(SUBMIT_NONE==submitMode || P("dry_run")!=0){
1269 cimi.flags |= CIMINI_DRY_RUN;
1270 }
1271 style_labeled_checkbox("dry_run", "Dry-run?", "1",
1272 "In dry-run mode, the Save button performs "
1273 "all work needed for saving but then rolls "
@@ -1310,11 +1310,12 @@
1310 "Will create a delta manifest, instead of "
1311 "baseline, if conditions are favorable to do "
1312 "so. This option is only a suggestion.",
1313 cimi.flags & CIMINI_PREFER_DELTA);
1314 {/* EOL conversion policy... */
1315 const int eolMode = submitMode==SUBMIT_NONE
1316 ? 0 : atoi(PD("eol","0"));
1317 switch(eolMode){
1318 case 1: cimi.flags |= CIMINI_CONVERT_EOL_UNIX; break;
1319 case 2: cimi.flags |= CIMINI_CONVERT_EOL_WINDOWS; break;
1320 default: cimi.flags |= CIMINI_CONVERT_EOL_INHERIT; break;
1321 }
@@ -1340,23 +1341,51 @@
1341 "Save</button>");
1342 CX("<button type='submit' name='submit' value='2'>"
1343 "Preview</button>");
1344 CX("<button type='submit' name='submit' value='3'>"
1345 "Diff (TODO)</button>");
1346 {
1347 /* Options which depend on the current submitMode or
1348 the file's preview rendering mode. */
1349 const int renderMode = fileedit_render_mode_for_mimetype(zFileMime);
1350 CX("<br>");
1351 if(FE_RENDER_HTML==renderMode){
1352 /* HTML preview mode iframe height... */
1353 int i;
1354 if(submitMode==SUBMIT_PREVIEW){
1355 previewHtmlHeight = atoi(PD("preview_html_ems","0"));
1356 }else{
1357 previewHtmlHeight = 40;
1358 }
1359 /* Allow selection of HTML preview iframe height */
1360 CX("<select name='preview_html_ems' "
1361 "title='Height (in EMs) of the iframe used for HTML "
1362 "preview.'>\n");
1363 CX("<option disabled value='40'>HTML Preview Height (EMs)"
1364 "</option>\n");
1365 for( i = 20; i <= 100; i+=20 ){
1366 CX("<option value='%d'%s>%d</option>\n",
1367 i, (previewHtmlHeight==i) ? " selected" : "", i);
1368 }
1369 CX("</select>\n");
1370 }
1371 else if(FE_RENDER_PLAIN_TEXT == renderMode){
1372 style_labeled_checkbox("preview_ln",
1373 "Add line numbers to plain-text previews?",
1374 "1",
1375 "If on, plain-text files (only) will get "
1376 "line numbers added to the preview.",
1377 previewLn);
1378 }
1379 }
1380 CX("</div></fieldset>");
1381
1382 /******* End of form *******/
1383 CX("</form>\n");
1384
1385 /* Dynamically populate the editor... */
1386 if(1==loadMode || (2==loadMode && submitMode>SUBMIT_NONE)){
1387 char const * zQuoted = 0;
1388 if(blob_size(&cimi.fileContent)>0){
1389 db_prepare(&stmt, "SELECT json_quote(%B)", &cimi.fileContent);
1390 db_step(&stmt);
1391 zQuoted = db_column_text(&stmt,0);
@@ -1367,11 +1396,11 @@
1396 ".value=%s;", zQuoted ? zQuoted : "'';\n");
1397 if(stmt.pStmt){
1398 db_finalize(&stmt);
1399 }
1400 }else if(2==loadMode){
1401 assert(submitMode==SUBMIT_NONE);
1402 fileedit_emit_script_fetch();
1403 blob_appendf(&endScript,
1404 "window.fossilFetch('raw/%s',{"
1405 "onload: (r)=>document.getElementById('fileedit-content')"
1406 ".value=r,"
@@ -1379,11 +1408,11 @@
1408 ".value="
1409 "'Error loading content'"
1410 "});\n", zFileUuid);
1411 }
1412
1413 if(SUBMIT_SAVE==submitMode){
1414 Blob manifest = empty_blob;
1415 char * zNewUuid = 0;
1416 /*cimi.flags |= CIMINI_STRONGLY_PREFER_DELTA;*/
1417 if(zComment && *zComment){
1418 blob_append(&cimi.comment, zComment, -1);
@@ -1442,15 +1471,16 @@
1471 }
1472 /* On error, the error message is in the err blob and will
1473 ** be emitted below. */
1474 cimi.pMfOut = 0;
1475 blob_reset(&manifest);
1476 }else if(SUBMIT_PREVIEW==submitMode){
1477 int pflags = 0;
1478 if(previewLn) pflags |= FE_PREVIEW_LINE_NUMBERS;
1479 fileedit_render_preview(&cimi.fileContent, cimi.zFilename, pflags,
1480 previewHtmlHeight);
1481 }else if(SUBMIT_DIFF==submitMode/*diff*/){
1482 fail((&err,"Diff mode is still TODO."));
1483 }else{
1484 /* Ignore invalid submitMode value */
1485 goto end_footer;
1486 }
1487

Keyboard Shortcuts

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