| | @@ -1220,10 +1220,206 @@ |
| 1220 | 1220 | fileedit_render_diff(&content, frid, zRevUuid, isSbs); |
| 1221 | 1221 | fossil_free(zRevUuid); |
| 1222 | 1222 | blob_reset(&content); |
| 1223 | 1223 | } |
| 1224 | 1224 | |
| 1225 | +/* |
| 1226 | +** Sets up and validates must, but not all, of p's checkin-related |
| 1227 | +** state from the CGI environment. Returns 0 on success or a suggested |
| 1228 | +** HTTP result code on error, in which case a message will have been |
| 1229 | +** written to pErr. |
| 1230 | +** |
| 1231 | +** It always fails if it cannot completely resolve the 'file' and 'r' |
| 1232 | +** parameters, including verifying that the refer to a real |
| 1233 | +** file/version combination. Most others are optional. |
| 1234 | +** |
| 1235 | +** Intended to be used only by /filepage and /filepage_commit. |
| 1236 | +*/ |
| 1237 | +static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr){ |
| 1238 | + char * zFileUuid = 0; |
| 1239 | + const char * zFlag; |
| 1240 | + int rc = 0, vid = 0, frid = 0; |
| 1241 | + |
| 1242 | +#define fail(EXPR) blob_appendf EXPR; goto end_fail |
| 1243 | + zFlag = PD("file",P("name")); |
| 1244 | + if(zFlag==0 || !*zFlag){ |
| 1245 | + rc = 400; |
| 1246 | + fail((pErr,"Missing required 'file' parameter.")); |
| 1247 | + } |
| 1248 | + p->zFilename = mprintf("%s",zFlag); |
| 1249 | + |
| 1250 | + if(0==fileedit_is_editable(p->zFilename)){ |
| 1251 | + rc = 403; |
| 1252 | + fail((pErr,"Filename [%h] is disallowed " |
| 1253 | + "by the [fileedit-glob] repository " |
| 1254 | + "setting.", |
| 1255 | + p->zFilename)); |
| 1256 | + } |
| 1257 | + |
| 1258 | + zFlag = P("r"); |
| 1259 | + if(!zFlag){ |
| 1260 | + rc = 400; |
| 1261 | + fail((pErr,"Missing required 'r' parameter.")); |
| 1262 | + } |
| 1263 | + vid = symbolic_name_to_rid(zFlag, "ci"); |
| 1264 | + if(0==vid){ |
| 1265 | + rc = 404; |
| 1266 | + fail((pErr,"Could not resolve checkin version.")); |
| 1267 | + } |
| 1268 | + p->zParentUuid = rid_to_uuid(vid)/*fully expand it*/; |
| 1269 | + |
| 1270 | + zFileUuid = fileedit_file_uuid(p->zFilename, vid, &p->filePerm); |
| 1271 | + if(!zFileUuid){ |
| 1272 | + rc = 404; |
| 1273 | + fail((pErr,"Checkin [%S] does not contain file: " |
| 1274 | + "[%h]", p->zParentUuid, p->zFilename)); |
| 1275 | + }else if(PERM_LNK==p->filePerm){ |
| 1276 | + rc = 400; |
| 1277 | + fail((pErr,"Editing symlinks is not permitted.")); |
| 1278 | + } |
| 1279 | + |
| 1280 | + /* Find the repo-side file entry or fail... */ |
| 1281 | + frid = fast_uuid_to_rid(zFileUuid); |
| 1282 | + assert(frid); |
| 1283 | + |
| 1284 | + /* Read file content from submit request or repo... */ |
| 1285 | + zFlag = P("content"); |
| 1286 | + if(zFlag==0){ |
| 1287 | + content_get(frid, &p->fileContent); |
| 1288 | + }else{ |
| 1289 | + blob_init(&p->fileContent,zFlag,-1); |
| 1290 | + } |
| 1291 | + if(looks_like_binary(&p->fileContent)){ |
| 1292 | + rc = 400; |
| 1293 | + fail((pErr,"File appears to be binary. Cannot edit: " |
| 1294 | + "[%h]",p->zFilename)); |
| 1295 | + } |
| 1296 | + |
| 1297 | + zFlag = PT("comment"); |
| 1298 | + if(zFlag!=0 && *zFlag!=0){ |
| 1299 | + blob_append(&p->comment, zFlag, -1); |
| 1300 | + } |
| 1301 | + zFlag = P("comment_mimetype"); |
| 1302 | + if(zFlag){ |
| 1303 | + p->zCommentMimetype = mprintf("%s",zFlag); |
| 1304 | + zFlag = 0; |
| 1305 | + } |
| 1306 | + p->zUser = mprintf("%s",g.zLogin); |
| 1307 | +#define p_int(K) atoi(PD(K,"0")) |
| 1308 | + if(p_int("dry_run")!=0){ |
| 1309 | + p->flags |= CIMINI_DRY_RUN; |
| 1310 | + } |
| 1311 | + if(p_int("allow_fork")!=0){ |
| 1312 | + p->flags |= CIMINI_ALLOW_FORK; |
| 1313 | + } |
| 1314 | + if(p_int("allow_older")!=0){ |
| 1315 | + p->flags |= CIMINI_ALLOW_OLDER; |
| 1316 | + } |
| 1317 | + if(p_int("exec_bit")!=0){ |
| 1318 | + p->filePerm = PERM_EXE; |
| 1319 | + } |
| 1320 | + if(p_int("allow_merge_conflict")!=0){ |
| 1321 | + p->flags |= CIMINI_ALLOW_MERGE_MARKER; |
| 1322 | + } |
| 1323 | + if(p_int("prefer_delta")!=0){ |
| 1324 | + p->flags |= CIMINI_PREFER_DELTA; |
| 1325 | + } |
| 1326 | + |
| 1327 | + /* EOL conversion policy... */ |
| 1328 | + { |
| 1329 | + const int eolMode = p_int("eol"); |
| 1330 | + switch(eolMode){ |
| 1331 | + case 1: p->flags |= CIMINI_CONVERT_EOL_UNIX; break; |
| 1332 | + case 2: p->flags |= CIMINI_CONVERT_EOL_WINDOWS; break; |
| 1333 | + default: p->flags |= CIMINI_CONVERT_EOL_INHERIT; break; |
| 1334 | + } |
| 1335 | + } |
| 1336 | +#undef p_int |
| 1337 | + /* |
| 1338 | + ** TODO?: date-override date selection field. Maybe use |
| 1339 | + ** an input[type=datetime-local]. |
| 1340 | + */ |
| 1341 | + return 0; |
| 1342 | +end_fail: |
| 1343 | +#undef fail |
| 1344 | + fossil_free(zFileUuid); |
| 1345 | + return rc ? rc : 500; |
| 1346 | +} |
| 1347 | + |
| 1348 | +/* |
| 1349 | +** WEBPAGE: fileedit_commit |
| 1350 | +** |
| 1351 | +** Required query parameters: |
| 1352 | +** |
| 1353 | +** file=FILENAME |
| 1354 | +** r=Parent checkin UUID |
| 1355 | +** content=text |
| 1356 | +** comment=text |
| 1357 | +** |
| 1358 | +** Optional query parameters: |
| 1359 | +** |
| 1360 | +** comment_mimetype=text |
| 1361 | +** dry_run=int (1 or 0) |
| 1362 | +** |
| 1363 | +** |
| 1364 | +** User must have Write access to use this page. |
| 1365 | +** |
| 1366 | +** Responds with JSON: |
| 1367 | +** |
| 1368 | +** {uuid: newUUID, |
| 1369 | +** manifest: text of manifest, |
| 1370 | +** dryRun: bool |
| 1371 | +** } |
| 1372 | +** |
| 1373 | +** On error it produces a JSON response as documented for |
| 1374 | +** fileedit_ajax_error(). |
| 1375 | +*/ |
| 1376 | +void fileedit_ajax_commit(){ |
| 1377 | + int newVid = 0; |
| 1378 | + Blob err = empty_blob; |
| 1379 | + Blob manifest = empty_blob; |
| 1380 | + CheckinMiniInfo cimi; |
| 1381 | + int rc; |
| 1382 | + char * zNewUuid = 0; |
| 1383 | + |
| 1384 | + if(!fileedit_ajax_boostrap()){ |
| 1385 | + goto end_cleanup; |
| 1386 | + } |
| 1387 | + db_begin_transaction(); |
| 1388 | + CheckinMiniInfo_init(&cimi); |
| 1389 | + rc = fileedit_setup_cimi_from_p(&cimi,&err); |
| 1390 | + if(0!=rc){ |
| 1391 | + fileedit_ajax_error(rc,"%b",&err); |
| 1392 | + goto end_cleanup; |
| 1393 | + } |
| 1394 | + if(blob_size(&cimi.comment)==0){ |
| 1395 | + fileedit_ajax_error(400,"Empty checkin comment is not permitted."); |
| 1396 | + goto end_cleanup; |
| 1397 | + } |
| 1398 | + cimi.pMfOut = &manifest; |
| 1399 | + checkin_mini(&cimi, &newVid, &err); |
| 1400 | + if(blob_size(&err)){ |
| 1401 | + fileedit_ajax_error(500,"%b",&err); |
| 1402 | + goto end_cleanup; |
| 1403 | + } |
| 1404 | + assert(newVid>0); |
| 1405 | + zNewUuid = rid_to_uuid(newVid); |
| 1406 | + cgi_set_content_type("application/json"); |
| 1407 | + CX("{"); |
| 1408 | + CX("\"uuid\":\"%j\",", zNewUuid); |
| 1409 | + CX("\"dryRun\": %s,", |
| 1410 | + (CIMINI_DRY_RUN & cimi.flags) ? "true" : "false"); |
| 1411 | + CX("\"manifest\": \"%j\"", blob_str(&manifest)); |
| 1412 | + CX("}"); |
| 1413 | + db_end_transaction(0); |
| 1414 | +end_cleanup: |
| 1415 | + fossil_free(zNewUuid); |
| 1416 | + blob_reset(&err); |
| 1417 | + blob_reset(&manifest); |
| 1418 | + CheckinMiniInfo_cleanup(&cimi); |
| 1419 | +} |
| 1420 | + |
| 1225 | 1421 | |
| 1226 | 1422 | /* |
| 1227 | 1423 | ** Emits utility script code specific to the /fileedit page. |
| 1228 | 1424 | */ |
| 1229 | 1425 | static void fileedit_emit_page_script(){ |
| | @@ -1247,34 +1443,21 @@ |
| 1247 | 1443 | ** All other parameters are for internal use only, submitted via the |
| 1248 | 1444 | ** form-submission process, and may change with any given revision of |
| 1249 | 1445 | ** this code. |
| 1250 | 1446 | */ |
| 1251 | 1447 | void fileedit_page(){ |
| 1252 | | - enum submit_modes { |
| 1253 | | - SUBMIT_NONE = 0, SUBMIT_SAVE, SUBMIT_PREVIEW, |
| 1254 | | - SUBMIT_DIFF_SBS, SUBMIT_DIFF_UNIFIED, |
| 1255 | | - SUBMIT_end /* sentinel for range validation */ |
| 1256 | | - }; |
| 1257 | | - const char * zFilename = PD("file",P("name")); |
| 1258 | | - /* filename. We'll accept 'name' |
| 1448 | + const char * zFilename; /* filename. We'll accept 'name' |
| 1259 | 1449 | because that param is handled |
| 1260 | 1450 | specially by the core. */ |
| 1261 | | - const char * zRev = P("r"); /* checkin version */ |
| 1262 | | - const char * zContent = P("content"); /* file content */ |
| 1263 | | - const char * zComment = P("comment"); /* checkin comment */ |
| 1451 | + const char * zRev; /* checkin version */ |
| 1264 | 1452 | const char * zFileMime = 0; /* File mime type guess */ |
| 1265 | 1453 | CheckinMiniInfo cimi; /* Checkin state */ |
| 1266 | | - int submitMode = SUBMIT_NONE; /* See mapping below */ |
| 1267 | | - int vid, newVid = 0; /* checkin rid */ |
| 1268 | | - int frid = 0; /* File content rid */ |
| 1269 | | - int previewLn = P("preview_ln")!=0; /* Line number mode */ |
| 1270 | 1454 | int previewHtmlHeight = 0; /* iframe height (EMs) */ |
| 1271 | 1455 | int previewRenderMode = FE_RENDER_GUESS; /* preview mode */ |
| 1272 | 1456 | char * zFileUuid = 0; /* File content UUID */ |
| 1273 | 1457 | Blob err = empty_blob; /* Error report */ |
| 1274 | 1458 | Blob submitResult = empty_blob; /* Error report */ |
| 1275 | | - const char * zFlagCheck = 0; /* Temp url flag holder */ |
| 1276 | 1459 | Blob endScript = empty_blob; /* Script code to run at the |
| 1277 | 1460 | end. This content will be |
| 1278 | 1461 | combined into a single JS |
| 1279 | 1462 | function call, thus each |
| 1280 | 1463 | entry must end with a |
| | @@ -1287,103 +1470,26 @@ |
| 1287 | 1470 | login_needed(g.anon.Write); |
| 1288 | 1471 | return; |
| 1289 | 1472 | } |
| 1290 | 1473 | db_begin_transaction(); |
| 1291 | 1474 | CheckinMiniInfo_init(&cimi); |
| 1292 | | - submitMode = atoi(PD("submit","0")); |
| 1293 | | - if(submitMode < SUBMIT_NONE || submitMode >= SUBMIT_end){ |
| 1294 | | - submitMode = 0; |
| 1295 | | - } |
| 1296 | | - zFlagCheck = P("comment_mimetype"); |
| 1297 | | - if(zFlagCheck){ |
| 1298 | | - cimi.zCommentMimetype = mprintf("%s",zFlagCheck); |
| 1299 | | - zFlagCheck = 0; |
| 1300 | | - } |
| 1301 | | - cimi.zUser = mprintf("%s",g.zLogin); |
| 1302 | 1475 | |
| 1303 | 1476 | style_header("File Editor"); |
| 1304 | 1477 | /* As of this point, don't use return or fossil_fatal(), use |
| 1305 | 1478 | ** fail((&err,...)) instead so that we can be sure to do any |
| 1306 | 1479 | ** cleanup and end the transaction cleanly. |
| 1307 | 1480 | */ |
| 1308 | | - if(!zRev || !*zRev || !zFilename || !*zFilename){ |
| 1309 | | - fail((&err,"Missing required URL parameters: " |
| 1310 | | - "file=FILE and r=CHECKIN")); |
| 1311 | | - } |
| 1312 | | - if(0==fileedit_is_editable(zFilename)){ |
| 1313 | | - fail((&err,"Filename <code>%h</code> is disallowed " |
| 1314 | | - "by the <code>fileedit-glob</code> repository " |
| 1315 | | - "setting.", |
| 1316 | | - zFilename)); |
| 1317 | | - } |
| 1318 | | - vid = symbolic_name_to_rid(zRev, "ci"); |
| 1319 | | - if(0==vid){ |
| 1320 | | - fail((&err,"Could not resolve checkin version.")); |
| 1321 | | - } |
| 1322 | | - cimi.zFilename = mprintf("%s",zFilename); |
| 1323 | | - zFileMime = mimetype_from_name(zFilename); |
| 1324 | | - |
| 1325 | | - /* Find the repo-side file entry or fail... */ |
| 1326 | | - cimi.zParentUuid = rid_to_uuid(vid); |
| 1327 | | - zFileUuid = fileedit_file_uuid(zFilename, vid, &cimi.filePerm); |
| 1328 | | - if(!zFileUuid){ |
| 1329 | | - fail((&err,"Checkin [%S] does not contain file: " |
| 1330 | | - "<code>%h</code>", |
| 1331 | | - cimi.zParentUuid, zFilename)); |
| 1332 | | - } |
| 1333 | | - else if(PERM_LNK==cimi.filePerm){ |
| 1334 | | - fail((&err,"Editing symlinks is not permitted.")); |
| 1335 | | - } |
| 1336 | | - frid = fast_uuid_to_rid(zFileUuid); |
| 1337 | | - assert(frid); |
| 1338 | | - |
| 1339 | | - /* Read file content from submit request or repo... */ |
| 1340 | | - if(zContent==0){ |
| 1341 | | - content_get(frid, &cimi.fileContent); |
| 1342 | | - zContent = blob_size(&cimi.fileContent) |
| 1343 | | - ? blob_str(&cimi.fileContent) : NULL; |
| 1344 | | - }else{ |
| 1345 | | - blob_init(&cimi.fileContent,zContent,-1); |
| 1346 | | - } |
| 1347 | | - if(looks_like_binary(&cimi.fileContent)){ |
| 1348 | | - fail((&err,"File appears to be binary. Cannot edit: " |
| 1349 | | - "<code>%h</code>",zFilename)); |
| 1350 | | - } |
| 1351 | | - |
| 1352 | | - /* |
| 1353 | | - ** TODO?: date-override date selection field. Maybe use |
| 1354 | | - ** an input[type=datetime-local]. |
| 1355 | | - */ |
| 1356 | | - if(SUBMIT_NONE==submitMode || P("dry_run")!=0){ |
| 1357 | | - cimi.flags |= CIMINI_DRY_RUN; |
| 1358 | | - } |
| 1359 | | - if(P("allow_fork")!=0){ |
| 1360 | | - cimi.flags |= CIMINI_ALLOW_FORK; |
| 1361 | | - } |
| 1362 | | - if(P("allow_older")!=0){ |
| 1363 | | - cimi.flags |= CIMINI_ALLOW_OLDER; |
| 1364 | | - } |
| 1365 | | - if(P("exec_bit")!=0){ |
| 1366 | | - cimi.filePerm = PERM_EXE; |
| 1367 | | - } |
| 1368 | | - if(P("allow_merge_conflict")!=0){ |
| 1369 | | - cimi.flags |= CIMINI_ALLOW_MERGE_MARKER; |
| 1370 | | - } |
| 1371 | | - if(P("prefer_delta")!=0){ |
| 1372 | | - cimi.flags |= CIMINI_PREFER_DELTA; |
| 1373 | | - } |
| 1374 | | - /* EOL conversion policy... */ |
| 1375 | | - { |
| 1376 | | - const int eolMode = submitMode==SUBMIT_NONE |
| 1377 | | - ? 0 : atoi(PD("eol","0")); |
| 1378 | | - switch(eolMode){ |
| 1379 | | - case 1: cimi.flags |= CIMINI_CONVERT_EOL_UNIX; break; |
| 1380 | | - case 2: cimi.flags |= CIMINI_CONVERT_EOL_WINDOWS; break; |
| 1381 | | - default: cimi.flags |= CIMINI_CONVERT_EOL_INHERIT; break; |
| 1382 | | - } |
| 1383 | | - } |
| 1384 | | - |
| 1481 | + if(fileedit_setup_cimi_from_p(&cimi, &err)!=0){ |
| 1482 | + goto end_footer; |
| 1483 | + } |
| 1484 | + zFilename = cimi.zFilename; |
| 1485 | + zRev = cimi.zParentUuid; |
| 1486 | + |
| 1487 | + assert(zRev); |
| 1488 | + assert(zFilename); |
| 1489 | + zFileMime = mimetype_from_name(cimi.zFilename); |
| 1490 | + |
| 1385 | 1491 | /******************************************************************** |
| 1386 | 1492 | ** All errors which "could" have happened up to this point are of a |
| 1387 | 1493 | ** degree which keep us from rendering the rest of the page, and |
| 1388 | 1494 | ** thus fail() has already skipped to the end of the page to render |
| 1389 | 1495 | ** the errors. Any up-coming errors, barring malloc failure or |
| | @@ -1396,54 +1502,10 @@ |
| 1396 | 1502 | ** SAVE, we can capture all of the output, and thus can perform that |
| 1397 | 1503 | ** work before rendering, which is important so that we have the |
| 1398 | 1504 | ** proper version information when rendering the rest of the page. |
| 1399 | 1505 | ********************************************************************/ |
| 1400 | 1506 | #undef fail |
| 1401 | | - while(SUBMIT_SAVE==submitMode){ |
| 1402 | | - Blob manifest = empty_blob; |
| 1403 | | - /*cimi.flags |= CIMINI_STRONGLY_PREFER_DELTA;*/ |
| 1404 | | - if(zComment && *zComment){ |
| 1405 | | - blob_append(&cimi.comment, zComment, -1); |
| 1406 | | - }else{ |
| 1407 | | - blob_append(&err,"Empty checkin comment is not permitted.",-1); |
| 1408 | | - break; |
| 1409 | | - } |
| 1410 | | - cimi.pMfOut = &manifest; |
| 1411 | | - checkin_mini(&cimi, &newVid, &err); |
| 1412 | | - if(newVid!=0){ |
| 1413 | | - char * zNewUuid = rid_to_uuid(newVid); |
| 1414 | | - blob_appendf(&submitResult, |
| 1415 | | - "<h3>Manifest%s: %S</h3><pre>" |
| 1416 | | - "<code class='fileedit-manifest'>%h</code>" |
| 1417 | | - "</pre>", |
| 1418 | | - (cimi.flags & CIMINI_DRY_RUN) ? " (dry run)" : "", |
| 1419 | | - zNewUuid, blob_str(&manifest)); |
| 1420 | | - if(CIMINI_DRY_RUN & cimi.flags){ |
| 1421 | | - fossil_free(zNewUuid); |
| 1422 | | - }else{ |
| 1423 | | - /* Update cimi version info... */ |
| 1424 | | - assert(cimi.pParent); |
| 1425 | | - assert(cimi.zParentUuid); |
| 1426 | | - fossil_free(zFileUuid); |
| 1427 | | - zFileUuid = fileedit_file_uuid(cimi.zFilename, newVid, 0); |
| 1428 | | - manifest_destroy(cimi.pParent); |
| 1429 | | - cimi.pParent = 0; |
| 1430 | | - fossil_free(cimi.zParentUuid); |
| 1431 | | - cimi.zParentUuid = zNewUuid; |
| 1432 | | - zComment = 0; |
| 1433 | | - cimi.flags |= CIMINI_DRY_RUN /* for sanity's sake */; |
| 1434 | | - } |
| 1435 | | - } |
| 1436 | | - /* On error, the error message is in the err blob and will |
| 1437 | | - ** be emitted at the end. */ |
| 1438 | | - cimi.pMfOut = 0; |
| 1439 | | - blob_reset(&manifest); |
| 1440 | | - break; |
| 1441 | | - } |
| 1442 | | - |
| 1443 | | - CX("<div id='fossil-status-bar'>Async. status messages will go " |
| 1444 | | - "here.</div>\n"); |
| 1445 | 1507 | CX("<h1>Editing:</h1>"); |
| 1446 | 1508 | CX("<p class='fileedit-hint'>"); |
| 1447 | 1509 | CX("File: " |
| 1448 | 1510 | "[<a id='finfo-link' href='#'>info</a>] " |
| 1449 | 1511 | /* %R/finfo?name=%T&m=%!S */ |
| | @@ -1480,45 +1542,56 @@ |
| 1480 | 1542 | CX("<h3>File Content</h3>\n"); |
| 1481 | 1543 | CX("<textarea name='content' id='fileedit-content' " |
| 1482 | 1544 | "rows='20' cols='80'>"); |
| 1483 | 1545 | CX("Loading..."); |
| 1484 | 1546 | CX("</textarea>\n"); |
| 1547 | + |
| 1548 | + CX("<div id='fossil-status-bar'>Async. status messages will go " |
| 1549 | + "here.</div>\n"); |
| 1550 | + |
| 1485 | 1551 | /******* Flags/options *******/ |
| 1486 | 1552 | CX("<fieldset class='fileedit-options' id='options'>" |
| 1487 | 1553 | "<legend>Options</legend><div>" |
| 1488 | 1554 | /* Chrome does not sanely lay out multiple |
| 1489 | 1555 | ** fieldset children after the <legend>, so |
| 1490 | 1556 | ** a containing div is necessary. */); |
| 1491 | | - style_labeled_checkbox("dry_run", "Dry-run?", "1", |
| 1557 | + style_labeled_checkbox("cb-dry-run", |
| 1558 | + "dry_run", "Dry-run?", "1", |
| 1492 | 1559 | "In dry-run mode, the Save button performs " |
| 1493 | 1560 | "all work needed for saving but then rolls " |
| 1494 | 1561 | "back the transaction, and thus does not " |
| 1495 | 1562 | "really save.", |
| 1496 | | - cimi.flags & CIMINI_DRY_RUN); |
| 1497 | | - style_labeled_checkbox("allow_fork", "Allow fork?", "1", |
| 1563 | + 1); |
| 1564 | + style_labeled_checkbox("cb-allow-fork", |
| 1565 | + "allow_fork", "Allow fork?", "1", |
| 1498 | 1566 | "Allow saving to create a fork?", |
| 1499 | 1567 | cimi.flags & CIMINI_ALLOW_FORK); |
| 1500 | | - style_labeled_checkbox("allow_older", "Allow older?", "1", |
| 1568 | + style_labeled_checkbox("cb-allow-older", |
| 1569 | + "allow_older", "Allow older?", "1", |
| 1501 | 1570 | "Allow saving against a parent version " |
| 1502 | 1571 | "which has a newer timestamp?", |
| 1503 | 1572 | cimi.flags & CIMINI_ALLOW_OLDER); |
| 1504 | | - style_labeled_checkbox("exec_bit", "Executable?", "1", |
| 1573 | + style_labeled_checkbox("cb-exec-bit", |
| 1574 | + "exec_bit", "Executable?", "1", |
| 1505 | 1575 | "Set the executable bit?", |
| 1506 | 1576 | PERM_EXE==cimi.filePerm); |
| 1507 | | - style_labeled_checkbox("allow_merge_conflict", |
| 1577 | + style_labeled_checkbox("cb-allow-merge-conflict", |
| 1578 | + "allow_merge_conflict", |
| 1508 | 1579 | "Allow merge conflict markers?", "1", |
| 1509 | 1580 | "Allow saving even if the content contains " |
| 1510 | 1581 | "what appear to be fossil merge conflict " |
| 1511 | 1582 | "markers?", |
| 1512 | 1583 | cimi.flags & CIMINI_ALLOW_MERGE_MARKER); |
| 1513 | | - style_labeled_checkbox("prefer_delta", |
| 1584 | + style_labeled_checkbox("cb-prefer-delta", |
| 1585 | + "prefer_delta", |
| 1514 | 1586 | "Prefer delta manifest?", "1", |
| 1515 | 1587 | "Will create a delta manifest, instead of " |
| 1516 | 1588 | "baseline, if conditions are favorable to do " |
| 1517 | 1589 | "so. This option is only a suggestion.", |
| 1518 | 1590 | cimi.flags & CIMINI_PREFER_DELTA); |
| 1519 | | - style_select_list_int("eol", "EOL Style", |
| 1591 | + style_select_list_int("select-eol-style", |
| 1592 | + "eol", "EOL Style", |
| 1520 | 1593 | "EOL conversion policy, noting that " |
| 1521 | 1594 | "form-processing may implicitly change the " |
| 1522 | 1595 | "line endings of the input.", |
| 1523 | 1596 | (cimi.flags & CIMINI_CONVERT_EOL_UNIX) |
| 1524 | 1597 | ? 1 : (cimi.flags & CIMINI_CONVERT_EOL_WINDOWS |
| | @@ -1534,12 +1607,12 @@ |
| 1534 | 1607 | CX("<a id='comment'></a>"); |
| 1535 | 1608 | CX("<fieldset><legend>Commit message</legend><div>"); |
| 1536 | 1609 | CX("<textarea name='comment' rows='3' cols='80'>"); |
| 1537 | 1610 | /* ^^^ adding the 'required' attribute means we cannot even submit |
| 1538 | 1611 | ** for PREVIEW mode if it's empty :/. */ |
| 1539 | | - if(zComment && *zComment){ |
| 1540 | | - CX("%h", zComment); |
| 1612 | + if(blob_size(&cimi.comment)){ |
| 1613 | + CX("%h", blob_str(&cimi.comment)); |
| 1541 | 1614 | } |
| 1542 | 1615 | CX("</textarea>\n"); |
| 1543 | 1616 | CX("<div class='fileedit-hint'>Comments use the Fossil wiki markup " |
| 1544 | 1617 | "syntax.</div>\n"/*TODO: select for fossil/md/plain text*/); |
| 1545 | 1618 | CX("</div></fieldset>\n"); |
| | @@ -1547,61 +1620,74 @@ |
| 1547 | 1620 | /******* Buttons *******/ |
| 1548 | 1621 | CX("<a id='buttons'></a>"); |
| 1549 | 1622 | CX("<fieldset class='fileedit-options'>" |
| 1550 | 1623 | "<legend>Ask the server to...</legend><div>"); |
| 1551 | 1624 | CX("<button id='fileedit-btn-commit'>Commit</button>"); |
| 1552 | | - CX("<button id='fileedit-btn-preview'>Preview</button>"); |
| 1553 | | - { |
| 1554 | | - /* Preview rendering mode selection... */ |
| 1555 | | - previewRenderMode = atoi(PD("preview_render_mode","0")); |
| 1556 | | - previewRenderMode = fileedit_render_mode_for_mimetype(zFileMime); |
| 1557 | | - style_select_list_int("preview_render_mode", |
| 1558 | | - "Preview Mode", |
| 1559 | | - "Preview mode format.", |
| 1560 | | - previewRenderMode, |
| 1561 | | - "Guess", FE_RENDER_GUESS, |
| 1562 | | - "Wiki/Markdown", FE_RENDER_WIKI, |
| 1563 | | - "HTML (iframe)", FE_RENDER_HTML, |
| 1564 | | - "Plain Text", FE_RENDER_PLAIN_TEXT, |
| 1565 | | - NULL); |
| 1566 | | - previewHtmlHeight = atoi(PD("preview_html_ems","0")); |
| 1567 | | - if(!previewHtmlHeight){ |
| 1568 | | - previewHtmlHeight = 40; |
| 1569 | | - } |
| 1570 | | - /* Allow selection of HTML preview iframe height */ |
| 1571 | | - style_select_list_int("preview_html_ems", |
| 1572 | | - "HTML Preview IFrame Height (EMs)", |
| 1573 | | - "Height (in EMs) of the iframe used for " |
| 1574 | | - "HTML preview", |
| 1575 | | - previewHtmlHeight, |
| 1576 | | - "", 20, "", 40, |
| 1577 | | - "", 60, "", 80, |
| 1578 | | - "", 100, NULL); |
| 1579 | | - style_labeled_checkbox("preview_ln", |
| 1580 | | - "Add line numbers to plain-text previews?", |
| 1581 | | - "1", |
| 1582 | | - "If on, plain-text files (only) will get " |
| 1583 | | - "line numbers added to the preview.", |
| 1584 | | - previewLn); |
| 1585 | | - } |
| 1586 | 1625 | CX("<button id='fileedit-btn-diffsbs'>Diff (SBS)</button>"); |
| 1587 | 1626 | CX("<button id='fileedit-btn-diffu'>Diff (Unified)</button>"); |
| 1627 | + CX("<button id='fileedit-btn-preview'>Preview</button>"); |
| 1628 | + /* Default preview rendering mode selection... */ |
| 1629 | + previewRenderMode = fileedit_render_mode_for_mimetype(zFileMime); |
| 1630 | + style_select_list_int("select-preview-mode", |
| 1631 | + "preview_render_mode", |
| 1632 | + "Preview Mode", |
| 1633 | + "Preview mode format.", |
| 1634 | + previewRenderMode, |
| 1635 | + "Guess", FE_RENDER_GUESS, |
| 1636 | + "Wiki/Markdown", FE_RENDER_WIKI, |
| 1637 | + "HTML (iframe)", FE_RENDER_HTML, |
| 1638 | + "Plain Text", FE_RENDER_PLAIN_TEXT, |
| 1639 | + NULL); |
| 1640 | + /* |
| 1641 | + ** Set up a JS-side mapping of the FE_RENDER_xyz values |
| 1642 | + */ |
| 1643 | + blob_appendf(&endScript, "fossil.page.previewModes={" |
| 1644 | + "guess: %d, %d: 'guess', wiki: %d, %d: 'wiki'," |
| 1645 | + "html: %d, %d: 'html', text: %d, %d: 'text'" |
| 1646 | + "};\n", |
| 1647 | + FE_RENDER_GUESS, FE_RENDER_GUESS, |
| 1648 | + FE_RENDER_WIKI, FE_RENDER_WIKI, |
| 1649 | + FE_RENDER_HTML, FE_RENDER_HTML, |
| 1650 | + FE_RENDER_PLAIN_TEXT, FE_RENDER_PLAIN_TEXT); |
| 1651 | + |
| 1652 | + /* Allow selection of HTML preview iframe height */ |
| 1653 | + previewHtmlHeight = atoi(PD("preview_html_ems","0")); |
| 1654 | + if(!previewHtmlHeight){ |
| 1655 | + previewHtmlHeight = 40; |
| 1656 | + } |
| 1657 | + style_select_list_int("select-preview-html-ems", |
| 1658 | + "preview_html_ems", |
| 1659 | + "HTML Preview IFrame Height (EMs)", |
| 1660 | + "Height (in EMs) of the iframe used for " |
| 1661 | + "HTML preview", |
| 1662 | + previewHtmlHeight, |
| 1663 | + "", 20, "", 40, |
| 1664 | + "", 60, "", 80, |
| 1665 | + "", 100, NULL); |
| 1666 | + /* Selection of line numbers for text preview */ |
| 1667 | + style_labeled_checkbox("cb-line-numbers", |
| 1668 | + "preview_ln", |
| 1669 | + "Add line numbers to plain-text previews?", |
| 1670 | + "1", |
| 1671 | + "If on, plain-text files (only) will get " |
| 1672 | + "line numbers added to the preview.", |
| 1673 | + P("preview_ln")!=0); |
| 1674 | + |
| 1588 | 1675 | CX("</div></fieldset>"); |
| 1589 | 1676 | |
| 1590 | 1677 | /******* End of form *******/ |
| 1591 | 1678 | CX("</form>\n"); |
| 1592 | 1679 | |
| 1593 | | - CX("<div id='ajax-target'></div>" |
| 1680 | + CX("<div id='ajax-target'>%s</div>" |
| 1594 | 1681 | /* this is where preview/diff go */); |
| 1595 | 1682 | |
| 1596 | 1683 | /* Dynamically populate the editor... */ |
| 1597 | 1684 | blob_appendf(&endScript, |
| 1598 | 1685 | "fossil.page.loadFile('%j','%j');", |
| 1599 | 1686 | zFilename, cimi.zParentUuid); |
| 1600 | 1687 | |
| 1601 | 1688 | end_footer: |
| 1602 | | - zContent = 0; |
| 1603 | 1689 | fossil_free(zFileUuid); |
| 1604 | 1690 | if(stmt.pStmt){ |
| 1605 | 1691 | db_finalize(&stmt); |
| 1606 | 1692 | } |
| 1607 | 1693 | if(blob_size(&err)){ |
| 1608 | 1694 | |