Fossil SCM

Initial work on /fileedit page. First dry-run save was just performed.

stephan 2020-05-01 14:22 UTC checkin-without-checkout
Commit fbd31f2049740cd4506526835c8590af5f15a07f127645c4e881a894f371f380
2 files changed +212 -99 +21 -1
+212 -99
--- src/checkin.c
+++ src/checkin.c
@@ -2706,11 +2706,11 @@
27062706
** CheckinMiniInfo instance and is freed by CheckinMiniInfo_cleanup().
27072707
*/
27082708
struct CheckinMiniInfo {
27092709
Manifest * pParent; /* parent checkin. Memory is owned by this
27102710
object. */
2711
- char *zParentUuid; /* UUID of pParent */
2711
+ char *zParentUuid; /* Full UUID of pParent */
27122712
char *zFilename; /* Name of single file to commit. Must be
27132713
relative to the top of the repo. */
27142714
Blob fileContent; /* Content of file referred to by zFilename. */
27152715
Blob fileHash; /* Hash of this->fileContent, using the repo's
27162716
preferred hash method. */
@@ -3054,21 +3054,25 @@
30543054
** save a manifest for that change. Ownership of pCI and its contents
30553055
** are unchanged.
30563056
**
30573057
** This function may may modify pCI as follows:
30583058
**
3059
-** - If Manifest pCI->pParent is NULL then it will be loaded
3060
-** using pCI->zParentUuid. pCI->zParentUuid may not be NULL.
3059
+** - If one of Manifest pCI->pParent or pCI->zParentUuid are NULL,
3060
+** then the other will be assigned based on its counterpart. Both
3061
+** may not be NULL.
30613062
**
30623063
** - pCI->zDate is normalized to/replaced with a valid date/time
30633064
** string. If its original value cannot be validated then
30643065
** this function fails. If pCI->zDate is NULL, the current time
30653066
** is used.
30663067
**
3067
-** - If pCI->fileContent is not binary and its line-ending style
3068
-** differs from its previous version, it is converted to the same
3069
-** EOL style. If this is done, the pCI->fileHash is re-computed.
3068
+** - If the CIMINI_CONVERT_EOL flag is set, pCI->fileContent appears
3069
+** to be plain text, and its line-ending style differs from its
3070
+** previous version, it is converted to the same EOL style as the
3071
+** previous version. If this is done, the pCI->fileHash is
3072
+** re-computed. Note that only pCI->fileContent, not the original
3073
+** file, is affected by the conversion.
30703074
**
30713075
** - If pCI->fileHash is empty, this routine populates it with the
30723076
** repository's preferred hash algorithm.
30733077
**
30743078
** pCI's ownership is not modified.
@@ -3108,15 +3112,21 @@
31083112
}
31093113
fossil_free(zProjCode);
31103114
}
31113115
db_begin_transaction();
31123116
3113
- if(pCI->pParent==0){
3117
+ if(pCI->pParent==0 && pCI->zParentUuid){
3118
+ ci_err((pErr, "Cannot determine parent version."));
3119
+ }
3120
+ else if(pCI->pParent==0){
31143121
pCI->pParent = manifest_get_by_name(pCI->zParentUuid, 0);
31153122
if(pCI->pParent==0){
31163123
ci_err((pErr,"Cannot load manifest for [%S].", pCI->zParentUuid));
31173124
}
3125
+ }else if(pCI->zParentUuid==0){
3126
+ pCI->zParentUuid = rid_to_uuid(pCI->pParent->rid);
3127
+ assert(pCI->zParentUuid);
31183128
}
31193129
31203130
assert(pCI->pParent->rid>0);
31213131
if(leaf_is_closed(pCI->pParent->rid)){
31223132
ci_err((pErr,"Cannot commit to a closed leaf."));
@@ -3361,11 +3371,11 @@
33613371
**
33623372
** %fossil test-ci-mini -R REPO -m ... -r foo --as src/myfile.c myfile.c
33633373
**
33643374
*/
33653375
void test_ci_mini_cmd(){
3366
- CheckinMiniInfo cinf; /* checkin state */
3376
+ CheckinMiniInfo cimi; /* checkin state */
33673377
int newRid = 0; /* RID of new version */
33683378
const char * zFilename; /* argv[2] */
33693379
const char * zComment; /* -m comment */
33703380
const char * zCommentFile; /* -M FILE */
33713381
const char * zAsFilename; /* --as filename */
@@ -3378,45 +3388,45 @@
33783388
** needs in order to fully/properly populate the CheckinMiniInfo and
33793389
** then pass it on to checkin_mini() to do most of the validation
33803390
** and work. The point of this is to avoid duplicate code when a web
33813391
** front-end is added for checkin_mini().
33823392
*/
3383
- CheckinMiniInfo_init(&cinf);
3393
+ CheckinMiniInfo_init(&cimi);
33843394
zComment = find_option("comment","m",1);
33853395
zCommentFile = find_option("comment-file","M",1);
33863396
zAsFilename = find_option("as",0,1);
33873397
zRevision = find_option("revision","r",1);
33883398
zUser = find_option("user-override",0,1);
33893399
zDate = find_option("date-override",0,1);
33903400
zManifestFile = find_option("save-manifest",0,1);
33913401
if(find_option("wet-run",0,0)==0){
3392
- cinf.flags |= CIMINI_DRY_RUN;
3402
+ cimi.flags |= CIMINI_DRY_RUN;
33933403
}
33943404
if(find_option("allow-fork",0,0)!=0){
3395
- cinf.flags |= CIMINI_ALLOW_FORK;
3405
+ cimi.flags |= CIMINI_ALLOW_FORK;
33963406
}
33973407
if(find_option("dump-manifest","d",0)!=0){
3398
- cinf.flags |= CIMINI_DUMP_MANIFEST;
3408
+ cimi.flags |= CIMINI_DUMP_MANIFEST;
33993409
}
34003410
if(find_option("allow-merge-conflict",0,0)!=0){
3401
- cinf.flags |= CIMINI_ALLOW_MERGE_MARKER;
3411
+ cimi.flags |= CIMINI_ALLOW_MERGE_MARKER;
34023412
}
34033413
if(find_option("allow-older",0,0)!=0){
3404
- cinf.flags |= CIMINI_ALLOW_OLDER;
3414
+ cimi.flags |= CIMINI_ALLOW_OLDER;
34053415
}
34063416
if(find_option("convert-eol",0,0)!=0){
3407
- cinf.flags |= CIMINI_CONVERT_EOL;
3417
+ cimi.flags |= CIMINI_CONVERT_EOL;
34083418
}
34093419
if(find_option("delta",0,0)!=0){
3410
- cinf.flags |= CIMINI_PREFER_DELTA;
3420
+ cimi.flags |= CIMINI_PREFER_DELTA;
34113421
}
34123422
if(find_option("delta2",0,0)!=0){
34133423
/* Undocumented. For testing only. */
3414
- cinf.flags |= CIMINI_PREFER_DELTA | CIMINI_STRONGLY_PREFER_DELTA;
3424
+ cimi.flags |= CIMINI_PREFER_DELTA | CIMINI_STRONGLY_PREFER_DELTA;
34153425
}
34163426
if(find_option("allow-new-file",0,0)!=0){
3417
- cinf.flags |= CIMINI_ALLOW_NEW_FILE;
3427
+ cimi.flags |= CIMINI_ALLOW_NEW_FILE;
34183428
}
34193429
db_find_and_open_repository(0, 0);
34203430
verify_all_options();
34213431
user_select();
34223432
if(g.argc!=3){
@@ -3424,46 +3434,46 @@
34243434
}
34253435
if(zComment && zCommentFile){
34263436
fossil_fatal("Only one of -m or -M, not both, may be used.");
34273437
}else{
34283438
if(zCommentFile && *zCommentFile){
3429
- blob_read_from_file(&cinf.comment, zCommentFile, ExtFILE);
3439
+ blob_read_from_file(&cimi.comment, zCommentFile, ExtFILE);
34303440
}else if(zComment && *zComment){
3431
- blob_append(&cinf.comment, zComment, -1);
3441
+ blob_append(&cimi.comment, zComment, -1);
34323442
}
3433
- if(!blob_size(&cinf.comment)){
3443
+ if(!blob_size(&cimi.comment)){
34343444
fossil_fatal("Non-empty checkin comment is required.");
34353445
}
34363446
}
34373447
db_begin_transaction();
34383448
zFilename = g.argv[2];
3439
- cinf.zFilename = mprintf("%/", zAsFilename ? zAsFilename : zFilename);
3440
- cinf.filePerm = file_perm(zFilename, ExtFILE);
3441
- cinf.zUser = mprintf("%s", zUser ? zUser : login_name());
3449
+ cimi.zFilename = mprintf("%/", zAsFilename ? zAsFilename : zFilename);
3450
+ cimi.filePerm = file_perm(zFilename, ExtFILE);
3451
+ cimi.zUser = mprintf("%s", zUser ? zUser : login_name());
34423452
if(zDate){
3443
- cinf.zDate = mprintf("%s", zDate);
3453
+ cimi.zDate = mprintf("%s", zDate);
34443454
}
34453455
if(zRevision==0 || zRevision[0]==0){
34463456
if(g.localOpen/*checkout*/){
34473457
zRevision = db_lget("checkout-hash", 0)/*leak*/;
34483458
}else{
34493459
zRevision = "trunk";
34503460
}
34513461
}
3452
- name_to_uuid2(zRevision, "ci", &cinf.zParentUuid);
3453
- if(cinf.zParentUuid==0){
3462
+ name_to_uuid2(zRevision, "ci", &cimi.zParentUuid);
3463
+ if(cimi.zParentUuid==0){
34543464
fossil_fatal("Cannot determine version to commit to.");
34553465
}
3456
- blob_read_from_file(&cinf.fileContent, zFilename, ExtFILE);
3466
+ blob_read_from_file(&cimi.fileContent, zFilename, ExtFILE);
34573467
{
34583468
Blob theManifest = empty_blob; /* --save-manifest target */
34593469
Blob errMsg = empty_blob;
34603470
int rc;
34613471
if(zManifestFile){
3462
- cinf.pMfOut = &theManifest;
3472
+ cimi.pMfOut = &theManifest;
34633473
}
3464
- rc = checkin_mini(&cinf, &newRid, &errMsg);
3474
+ rc = checkin_mini(&cimi, &newRid, &errMsg);
34653475
if(rc){
34663476
assert(blob_size(&errMsg)==0);
34673477
}else{
34683478
assert(blob_size(&errMsg));
34693479
fossil_fatal("%b", &errMsg);
@@ -3475,23 +3485,23 @@
34753485
blob_reset(&theManifest);
34763486
}
34773487
}
34783488
if(newRid!=0){
34793489
fossil_print("New version%s: %z\n",
3480
- (cinf.flags & CIMINI_DRY_RUN) ? " (dry run)" : "",
3490
+ (cimi.flags & CIMINI_DRY_RUN) ? " (dry run)" : "",
34813491
rid_to_uuid(newRid));
34823492
}
34833493
db_end_transaction(0/*checkin_mini() will have triggered it to roll
34843494
** back in dry-run mode, but we need access to
34853495
** the transaction-written db state in this
34863496
** routine.*/);
3487
- if(!(cinf.flags & CIMINI_DRY_RUN) && newRid!=0 && g.localOpen!=0){
3497
+ if(!(cimi.flags & CIMINI_DRY_RUN) && newRid!=0 && g.localOpen!=0){
34883498
fossil_warning("The checkout state is now out of sync "
34893499
"with regards to this commit. It needs to be "
34903500
"'update'd or 'close'd and re-'open'ed.");
34913501
}
3492
- CheckinMiniInfo_cleanup(&cinf);
3502
+ CheckinMiniInfo_cleanup(&cimi);
34933503
}
34943504
34953505
34963506
/*
34973507
** Returns true if the given filename qualified for online editing
@@ -3522,105 +3532,208 @@
35223532
** Parameters intended to be passed in only via the editor's own form:
35233533
**
35243534
** diff If true, show diff from prev version.
35253535
** preview If true, preview (how depends on mimetype).
35263536
** content File content.
3537
+** comment Checkin comment.
3538
+** n Optional comment mimetype ("n" as in N-card).
35273539
**
35283540
**
35293541
*/
35303542
void fileedit_page(){
35313543
const char * zFilename = PD("file",P("name")); /* filename */
3532
- const char * zRev = P("r"); /* checkin version */
3533
- const char * zContent = P("content"); /* file content */
3534
- const char * zComment = P("comment"); /* checkin comment */
3535
- char * zRevResolved = 0; /* Resolved zRev */
3536
- int vid, frid; /* checkin/file rids */
3537
- char * zFileUuid = 0;
3538
- Blob content = empty_blob;
3544
+ const char * zRev = P("r"); /* checkin version */
3545
+ const char * zContent = P("content"); /* file content */
3546
+ const char * zComment = P("comment"); /* checkin comment */
3547
+ CheckinMiniInfo cimi; /* Checkin state */
3548
+ int submitMode = 0; /* See mapping below */
3549
+ char * zRevResolved = 0; /* Resolved zRev */
3550
+ int vid; /* checkin rid */
3551
+ char * zFileUuid = 0; /* File content UUID */
3552
+ Blob err = empty_blob; /* Error report */
3553
+ const char * zFlagCheck = 0; /* Temp url flag holder */
3554
+ Stmt stmt = empty_Stmt;
3555
+#define fail(EXPR) blob_appendf EXPR; goto end_footer
35393556
35403557
login_check_credentials();
35413558
if( !g.perm.Write ){
35423559
login_needed(g.anon.Write);
35433560
return;
35443561
}
3562
+ CheckinMiniInfo_init(&cimi);
3563
+ zFlagCheck = P("comment");
3564
+ if(zFlagCheck){
3565
+ cimi.zMimetype = mprintf("%s",zFlagCheck);
3566
+ zFlagCheck = 0;
3567
+ }
3568
+ cimi.zUser = mprintf("%s",g.zLogin);
35453569
/*
35463570
** TODOs include, but are not limited to:
35473571
**
3548
- ** - On initial hit, fetch file content and stuff it in a textarea.
3549
- **
35503572
** - Preview button + view
35513573
**
35523574
** - Diff button + view
35533575
**
3554
- ** - Checkbox options: allow fork, dry-run, convert EOL,
3555
- ** allow merge conflict, allow older (just in case server time
3556
- ** is messed up or someone checked something in w/ a future
3557
- ** timestamp)
3558
- **
35593576
*/
35603577
if(!zRev || !*zRev || !zFilename || !*zFilename){
3561
- webpage_error("Missing required URL parameters.");
3578
+ fail((&err,"Missing required URL parameters."));
35623579
}
35633580
vid = symbolic_name_to_rid(zRev, "ci");
35643581
if(0==vid){
3565
- webpage_error("Could not resolve checkin version.");
3566
- }
3567
- zRevResolved = rid_to_uuid(vid);
3568
- zFileUuid = db_text(0,"SELECT uuid FROM files_of_checkin WHERE "
3569
- "filename=%Q %s AND checkinID=%d",
3570
- zFilename, filename_collation(), vid);
3571
- if(!zFileUuid){
3572
- webpage_error("Checkin [%S] does not contain file: %T",
3573
- zRevResolved, zFilename);
3574
- }
3575
-
3576
- frid = fast_uuid_to_rid(zFileUuid);
3577
- assert(frid);
3578
- if(zContent==0){
3579
- content_get(frid, &content);
3580
- zContent = blob_size(&content) ? blob_str(&content) : NULL;
3581
- }else{
3582
- blob_init(&content,zContent,-1);
3583
- }
3584
- if(looks_like_binary(&content)){
3585
- webpage_error("File appears to be binary. Cannot edit: %T",
3586
- zFilename);
3587
- }
3588
-
3582
+ fail((&err,"Could not resolve checkin version."));
3583
+ }
3584
+
3585
+ /* Find the repo-side file entry or fail... */
3586
+ zRevResolved = rid_to_uuid(vid);
3587
+ db_prepare(&stmt, "SELECT uuid, perm FROM files_of_checkin "
3588
+ "WHERE filename=%Q %s AND checkinID=%d",
3589
+ zFilename, filename_collation(), vid);
3590
+ if(SQLITE_ROW==db_step(&stmt)){
3591
+ const char * zPerm = db_column_text(&stmt, 1);
3592
+ const int isLink = zPerm ? strstr(zPerm,"l")!=0 : 0;
3593
+ if(isLink){
3594
+ fail((&err,"Editing symlinks is not permitted."));
3595
+ }
3596
+ zFileUuid = mprintf("%s",db_column_text(&stmt, 0));
3597
+ }
3598
+ db_finalize(&stmt);
3599
+ if(!zFileUuid){
3600
+ fail((&err,"Checkin [%S] does not contain file: %h",
3601
+ zRevResolved, zFilename));
3602
+ }
3603
+
3604
+ /* Read file content from submit request or repo... */
3605
+ if(zContent==0){
3606
+ const int frid = fast_uuid_to_rid(zFileUuid);
3607
+ assert(frid);
3608
+ content_get(frid, &cimi.fileContent);
3609
+ zContent = blob_size(&cimi.fileContent)
3610
+ ? blob_str(&cimi.fileContent) : NULL;
3611
+ }else{
3612
+ blob_init(&cimi.fileContent,zContent,-1);
3613
+ }
3614
+ if(looks_like_binary(&cimi.fileContent)){
3615
+ fail((&err,"File appears to be binary. Cannot edit: %h",
3616
+ zFilename));
3617
+ }
3618
+
3619
+ /* All set. Here we go... */
35893620
style_header("File Editor");
35903621
#define fp fossil_print
35913622
/* ^^^ Appologies, Richard, but the @ form plays havoc with emacs */
35923623
3593
- fp("<h1>Editing: %T</h1>",zFilename);
3594
- fp("<p>This page is <em>far from complete</em>.</p>");
3595
-
3596
- fp("<form action=\"%R/fileedit\" method=\"POST\" class=\"fileedit-form\">");
3597
- fp("<input type=\"hidden\" name=\"r\" value=\"%s\">", zRevResolved);
3598
- fp("<input type=\"hidden\" name=\"file\" value=\"%T\">",
3599
- zFilename);
3600
- fp("<h3>Comment</h3>");
3601
- fp("<textarea name=\"comment\" rows=\"3\" cols=\"80\">");
3624
+ fp("<h1>Editing: %h</h1>",zFilename);
3625
+ fp("<p class='hint'>Permalink: "
3626
+ "<a href='%R/fileedit?file=%T&r=%s'>"
3627
+ "%R/fileedit?file=%T&r=%s</a></p>",
3628
+ zFilename, zRev, zFilename, zRev);
3629
+ fp("<p>This page is <em>far from complete</em>.</p>\n");
3630
+
3631
+ fp("<form action='%R/fileedit' method='POST' "
3632
+ "class='fileedit-form'>\n");
3633
+
3634
+ /******* Hidden fields *******/
3635
+ fp("<input type='hidden' name='r' value='%s'>", zRevResolved);
3636
+ fp("<input type='hidden' name='file' value='%T'>",
3637
+ zFilename);
3638
+
3639
+ /******* Comment *******/
3640
+ fp("<h3>Comment</h3>\n");
3641
+ fp("<textarea name='comment' rows='3' cols='80'>");
36023642
if(zComment && *zComment){
3603
- fp("%T"/*%T?*/, zComment);
3604
- }
3605
- fp("</textarea>");
3606
- fp("<h3>Content</h3>");
3607
- fp("<textarea name=\"content\" rows=\"20\" cols=\"80\">");
3608
- if(zContent && *zContent){
3609
- fp("%s", zContent);
3610
- }
3611
- fp("</textarea>");
3612
-
3613
- fp("<div class=\"fileedit-options\">");
3614
- /* Put checkboxes here... */
3615
- fp("Many buttons and checkboxes to put here");
3616
- fp("</div>");
3617
-
3618
- fp("</form>");
3619
-
3620
- blob_reset(&content);
3643
+ fp("%h"/*%h? %s?*/, zComment);
3644
+ }
3645
+ fp("</textarea>\n");
3646
+ fp("<div class='hint'>Comments use the Fossil wiki markup "
3647
+ "syntax.</div>"/*TODO: radiobuttons for fossil/me/plain text*/);
3648
+
3649
+ /******* Content *******/
3650
+ fp("<h3>Content</h3>\n");
3651
+ fp("<textarea name='content' rows='20' cols='80'>");
3652
+ if(zContent && *zContent){
3653
+ fp("%h", zContent);
3654
+ }
3655
+ fp("</textarea>\n");
3656
+
3657
+ /******* Flags/options *******/
3658
+ fp("<fieldset class='fileedit-options'>"
3659
+ "<legend>Many checkboxes are TODO</legend><div>"
3660
+ /* Chrome does not sanely lay out multiple
3661
+ ** fieldset children after the <legend>, so
3662
+ ** a containing div is necessary. */);
3663
+ /*
3664
+ ** TODO: Put checkboxes here...
3665
+ **
3666
+ ** allow-fork, dry-run, convert-eol, allow-merge-conflict,
3667
+ ** set-exec-bit, date-override, allow-older (in case server time is
3668
+ ** messed up or someone checked something in w/ a future timestamp),
3669
+ ** prefer-delta, strongly-prefer-delta (undocumented - for
3670
+ ** development/admin use only).
3671
+ */
3672
+ fp("</div></fieldset>");
3673
+
3674
+ /******* Buttons *******/
3675
+ fp("<fieldset class='fileedit-options'>"
3676
+ "<legend>Several buttons are TODO</legend><div>");
3677
+ fp("<button type='submit' name='submit' value='1'>"
3678
+ "Submit (dry-run)</button>");
3679
+ fp("<button type='submit' name='submit' value='2'>"
3680
+ "Preview (TODO)</button>");
3681
+ fp("<button type='submit' name='submit' value='3'>"
3682
+ "Diff (TODO)</button>");
3683
+ fp("</div></fieldset>");
3684
+
3685
+ /******* End of form *******/
3686
+ fp("</form>\n");
3687
+ zContent = 0;
36213688
fossil_free(zRevResolved);
36223689
fossil_free(zFileUuid);
36233690
3691
+ submitMode = atoi(PD("submit","0"))
3692
+ /*
3693
+ ** Submit modes: 1=submit (save), 2=preview, 3=diff
3694
+ */;
3695
+ if(1==submitMode/*save*/){
3696
+ Blob manifest = empty_blob;
3697
+ int rc;
3698
+
3699
+ /* TODO: pull these flags from P() */
3700
+ cimi.flags |= CIMINI_DRY_RUN;
3701
+ cimi.flags |= CIMINI_CONVERT_EOL;
3702
+ cimi.flags |= CIMINI_PREFER_DELTA;
3703
+ /*cimi.flags |= CIMINI_STRONGLY_PREFER_DELTA;*/
3704
+
3705
+ db_begin_transaction();
3706
+ cimi.pParent = manifest_get(vid, CFTYPE_MANIFEST, 0);
3707
+ assert(cimi.pParent && "We know vid is valid.");
3708
+ cimi.zFilename = mprintf("%s",zFilename);
3709
+ cimi.pMfOut = &manifest;
3710
+ cimi.filePerm = PERM_REG;
3711
+ if(zComment && *zComment){
3712
+ blob_append(&cimi.comment, zComment, -1);
3713
+ }
3714
+ rc = checkin_mini(&cimi, 0, &err);
3715
+ if(rc!=0){
3716
+ fp("<h3>Manifest</h3><pre><code>%h</code></pre>",
3717
+ blob_str(&manifest));
3718
+ }
3719
+ blob_reset(&manifest);
3720
+ db_end_transaction(rc ? 0 : 1);
3721
+ }else if(2==submitMode/*preview*/){
3722
+ /* TODO */
3723
+ }else if(3==submitMode/*diff*/){
3724
+ /* TODO */
3725
+ }else{
3726
+ /* Ignore invalid submitMode value */
3727
+ goto end_footer;
3728
+ }
3729
+
3730
+end_footer:
3731
+ if(blob_size(&err)){
3732
+ fp("<div class='fileedit-error-report'>%h</div>",
3733
+ blob_str(&err));
3734
+ }
3735
+ blob_reset(&err);
3736
+ CheckinMiniInfo_cleanup(&cimi);
36243737
style_footer();
36253738
#undef fp
36263739
}
36273740
--- src/checkin.c
+++ src/checkin.c
@@ -2706,11 +2706,11 @@
2706 ** CheckinMiniInfo instance and is freed by CheckinMiniInfo_cleanup().
2707 */
2708 struct CheckinMiniInfo {
2709 Manifest * pParent; /* parent checkin. Memory is owned by this
2710 object. */
2711 char *zParentUuid; /* UUID of pParent */
2712 char *zFilename; /* Name of single file to commit. Must be
2713 relative to the top of the repo. */
2714 Blob fileContent; /* Content of file referred to by zFilename. */
2715 Blob fileHash; /* Hash of this->fileContent, using the repo's
2716 preferred hash method. */
@@ -3054,21 +3054,25 @@
3054 ** save a manifest for that change. Ownership of pCI and its contents
3055 ** are unchanged.
3056 **
3057 ** This function may may modify pCI as follows:
3058 **
3059 ** - If Manifest pCI->pParent is NULL then it will be loaded
3060 ** using pCI->zParentUuid. pCI->zParentUuid may not be NULL.
 
3061 **
3062 ** - pCI->zDate is normalized to/replaced with a valid date/time
3063 ** string. If its original value cannot be validated then
3064 ** this function fails. If pCI->zDate is NULL, the current time
3065 ** is used.
3066 **
3067 ** - If pCI->fileContent is not binary and its line-ending style
3068 ** differs from its previous version, it is converted to the same
3069 ** EOL style. If this is done, the pCI->fileHash is re-computed.
 
 
 
3070 **
3071 ** - If pCI->fileHash is empty, this routine populates it with the
3072 ** repository's preferred hash algorithm.
3073 **
3074 ** pCI's ownership is not modified.
@@ -3108,15 +3112,21 @@
3108 }
3109 fossil_free(zProjCode);
3110 }
3111 db_begin_transaction();
3112
3113 if(pCI->pParent==0){
 
 
 
3114 pCI->pParent = manifest_get_by_name(pCI->zParentUuid, 0);
3115 if(pCI->pParent==0){
3116 ci_err((pErr,"Cannot load manifest for [%S].", pCI->zParentUuid));
3117 }
 
 
 
3118 }
3119
3120 assert(pCI->pParent->rid>0);
3121 if(leaf_is_closed(pCI->pParent->rid)){
3122 ci_err((pErr,"Cannot commit to a closed leaf."));
@@ -3361,11 +3371,11 @@
3361 **
3362 ** %fossil test-ci-mini -R REPO -m ... -r foo --as src/myfile.c myfile.c
3363 **
3364 */
3365 void test_ci_mini_cmd(){
3366 CheckinMiniInfo cinf; /* checkin state */
3367 int newRid = 0; /* RID of new version */
3368 const char * zFilename; /* argv[2] */
3369 const char * zComment; /* -m comment */
3370 const char * zCommentFile; /* -M FILE */
3371 const char * zAsFilename; /* --as filename */
@@ -3378,45 +3388,45 @@
3378 ** needs in order to fully/properly populate the CheckinMiniInfo and
3379 ** then pass it on to checkin_mini() to do most of the validation
3380 ** and work. The point of this is to avoid duplicate code when a web
3381 ** front-end is added for checkin_mini().
3382 */
3383 CheckinMiniInfo_init(&cinf);
3384 zComment = find_option("comment","m",1);
3385 zCommentFile = find_option("comment-file","M",1);
3386 zAsFilename = find_option("as",0,1);
3387 zRevision = find_option("revision","r",1);
3388 zUser = find_option("user-override",0,1);
3389 zDate = find_option("date-override",0,1);
3390 zManifestFile = find_option("save-manifest",0,1);
3391 if(find_option("wet-run",0,0)==0){
3392 cinf.flags |= CIMINI_DRY_RUN;
3393 }
3394 if(find_option("allow-fork",0,0)!=0){
3395 cinf.flags |= CIMINI_ALLOW_FORK;
3396 }
3397 if(find_option("dump-manifest","d",0)!=0){
3398 cinf.flags |= CIMINI_DUMP_MANIFEST;
3399 }
3400 if(find_option("allow-merge-conflict",0,0)!=0){
3401 cinf.flags |= CIMINI_ALLOW_MERGE_MARKER;
3402 }
3403 if(find_option("allow-older",0,0)!=0){
3404 cinf.flags |= CIMINI_ALLOW_OLDER;
3405 }
3406 if(find_option("convert-eol",0,0)!=0){
3407 cinf.flags |= CIMINI_CONVERT_EOL;
3408 }
3409 if(find_option("delta",0,0)!=0){
3410 cinf.flags |= CIMINI_PREFER_DELTA;
3411 }
3412 if(find_option("delta2",0,0)!=0){
3413 /* Undocumented. For testing only. */
3414 cinf.flags |= CIMINI_PREFER_DELTA | CIMINI_STRONGLY_PREFER_DELTA;
3415 }
3416 if(find_option("allow-new-file",0,0)!=0){
3417 cinf.flags |= CIMINI_ALLOW_NEW_FILE;
3418 }
3419 db_find_and_open_repository(0, 0);
3420 verify_all_options();
3421 user_select();
3422 if(g.argc!=3){
@@ -3424,46 +3434,46 @@
3424 }
3425 if(zComment && zCommentFile){
3426 fossil_fatal("Only one of -m or -M, not both, may be used.");
3427 }else{
3428 if(zCommentFile && *zCommentFile){
3429 blob_read_from_file(&cinf.comment, zCommentFile, ExtFILE);
3430 }else if(zComment && *zComment){
3431 blob_append(&cinf.comment, zComment, -1);
3432 }
3433 if(!blob_size(&cinf.comment)){
3434 fossil_fatal("Non-empty checkin comment is required.");
3435 }
3436 }
3437 db_begin_transaction();
3438 zFilename = g.argv[2];
3439 cinf.zFilename = mprintf("%/", zAsFilename ? zAsFilename : zFilename);
3440 cinf.filePerm = file_perm(zFilename, ExtFILE);
3441 cinf.zUser = mprintf("%s", zUser ? zUser : login_name());
3442 if(zDate){
3443 cinf.zDate = mprintf("%s", zDate);
3444 }
3445 if(zRevision==0 || zRevision[0]==0){
3446 if(g.localOpen/*checkout*/){
3447 zRevision = db_lget("checkout-hash", 0)/*leak*/;
3448 }else{
3449 zRevision = "trunk";
3450 }
3451 }
3452 name_to_uuid2(zRevision, "ci", &cinf.zParentUuid);
3453 if(cinf.zParentUuid==0){
3454 fossil_fatal("Cannot determine version to commit to.");
3455 }
3456 blob_read_from_file(&cinf.fileContent, zFilename, ExtFILE);
3457 {
3458 Blob theManifest = empty_blob; /* --save-manifest target */
3459 Blob errMsg = empty_blob;
3460 int rc;
3461 if(zManifestFile){
3462 cinf.pMfOut = &theManifest;
3463 }
3464 rc = checkin_mini(&cinf, &newRid, &errMsg);
3465 if(rc){
3466 assert(blob_size(&errMsg)==0);
3467 }else{
3468 assert(blob_size(&errMsg));
3469 fossil_fatal("%b", &errMsg);
@@ -3475,23 +3485,23 @@
3475 blob_reset(&theManifest);
3476 }
3477 }
3478 if(newRid!=0){
3479 fossil_print("New version%s: %z\n",
3480 (cinf.flags & CIMINI_DRY_RUN) ? " (dry run)" : "",
3481 rid_to_uuid(newRid));
3482 }
3483 db_end_transaction(0/*checkin_mini() will have triggered it to roll
3484 ** back in dry-run mode, but we need access to
3485 ** the transaction-written db state in this
3486 ** routine.*/);
3487 if(!(cinf.flags & CIMINI_DRY_RUN) && newRid!=0 && g.localOpen!=0){
3488 fossil_warning("The checkout state is now out of sync "
3489 "with regards to this commit. It needs to be "
3490 "'update'd or 'close'd and re-'open'ed.");
3491 }
3492 CheckinMiniInfo_cleanup(&cinf);
3493 }
3494
3495
3496 /*
3497 ** Returns true if the given filename qualified for online editing
@@ -3522,105 +3532,208 @@
3522 ** Parameters intended to be passed in only via the editor's own form:
3523 **
3524 ** diff If true, show diff from prev version.
3525 ** preview If true, preview (how depends on mimetype).
3526 ** content File content.
 
 
3527 **
3528 **
3529 */
3530 void fileedit_page(){
3531 const char * zFilename = PD("file",P("name")); /* filename */
3532 const char * zRev = P("r"); /* checkin version */
3533 const char * zContent = P("content"); /* file content */
3534 const char * zComment = P("comment"); /* checkin comment */
3535 char * zRevResolved = 0; /* Resolved zRev */
3536 int vid, frid; /* checkin/file rids */
3537 char * zFileUuid = 0;
3538 Blob content = empty_blob;
 
 
 
 
 
3539
3540 login_check_credentials();
3541 if( !g.perm.Write ){
3542 login_needed(g.anon.Write);
3543 return;
3544 }
 
 
 
 
 
 
 
3545 /*
3546 ** TODOs include, but are not limited to:
3547 **
3548 ** - On initial hit, fetch file content and stuff it in a textarea.
3549 **
3550 ** - Preview button + view
3551 **
3552 ** - Diff button + view
3553 **
3554 ** - Checkbox options: allow fork, dry-run, convert EOL,
3555 ** allow merge conflict, allow older (just in case server time
3556 ** is messed up or someone checked something in w/ a future
3557 ** timestamp)
3558 **
3559 */
3560 if(!zRev || !*zRev || !zFilename || !*zFilename){
3561 webpage_error("Missing required URL parameters.");
3562 }
3563 vid = symbolic_name_to_rid(zRev, "ci");
3564 if(0==vid){
3565 webpage_error("Could not resolve checkin version.");
3566 }
3567 zRevResolved = rid_to_uuid(vid);
3568 zFileUuid = db_text(0,"SELECT uuid FROM files_of_checkin WHERE "
3569 "filename=%Q %s AND checkinID=%d",
3570 zFilename, filename_collation(), vid);
3571 if(!zFileUuid){
3572 webpage_error("Checkin [%S] does not contain file: %T",
3573 zRevResolved, zFilename);
3574 }
3575
3576 frid = fast_uuid_to_rid(zFileUuid);
3577 assert(frid);
3578 if(zContent==0){
3579 content_get(frid, &content);
3580 zContent = blob_size(&content) ? blob_str(&content) : NULL;
3581 }else{
3582 blob_init(&content,zContent,-1);
3583 }
3584 if(looks_like_binary(&content)){
3585 webpage_error("File appears to be binary. Cannot edit: %T",
3586 zFilename);
3587 }
3588
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3589 style_header("File Editor");
3590 #define fp fossil_print
3591 /* ^^^ Appologies, Richard, but the @ form plays havoc with emacs */
3592
3593 fp("<h1>Editing: %T</h1>",zFilename);
3594 fp("<p>This page is <em>far from complete</em>.</p>");
3595
3596 fp("<form action=\"%R/fileedit\" method=\"POST\" class=\"fileedit-form\">");
3597 fp("<input type=\"hidden\" name=\"r\" value=\"%s\">", zRevResolved);
3598 fp("<input type=\"hidden\" name=\"file\" value=\"%T\">",
3599 zFilename);
3600 fp("<h3>Comment</h3>");
3601 fp("<textarea name=\"comment\" rows=\"3\" cols=\"80\">");
 
 
 
 
 
 
 
 
 
3602 if(zComment && *zComment){
3603 fp("%T"/*%T?*/, zComment);
3604 }
3605 fp("</textarea>");
3606 fp("<h3>Content</h3>");
3607 fp("<textarea name=\"content\" rows=\"20\" cols=\"80\">");
3608 if(zContent && *zContent){
3609 fp("%s", zContent);
3610 }
3611 fp("</textarea>");
3612
3613 fp("<div class=\"fileedit-options\">");
3614 /* Put checkboxes here... */
3615 fp("Many buttons and checkboxes to put here");
3616 fp("</div>");
3617
3618 fp("</form>");
3619
3620 blob_reset(&content);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3621 fossil_free(zRevResolved);
3622 fossil_free(zFileUuid);
3623
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3624 style_footer();
3625 #undef fp
3626 }
3627
--- src/checkin.c
+++ src/checkin.c
@@ -2706,11 +2706,11 @@
2706 ** CheckinMiniInfo instance and is freed by CheckinMiniInfo_cleanup().
2707 */
2708 struct CheckinMiniInfo {
2709 Manifest * pParent; /* parent checkin. Memory is owned by this
2710 object. */
2711 char *zParentUuid; /* Full UUID of pParent */
2712 char *zFilename; /* Name of single file to commit. Must be
2713 relative to the top of the repo. */
2714 Blob fileContent; /* Content of file referred to by zFilename. */
2715 Blob fileHash; /* Hash of this->fileContent, using the repo's
2716 preferred hash method. */
@@ -3054,21 +3054,25 @@
3054 ** save a manifest for that change. Ownership of pCI and its contents
3055 ** are unchanged.
3056 **
3057 ** This function may may modify pCI as follows:
3058 **
3059 ** - If one of Manifest pCI->pParent or pCI->zParentUuid are NULL,
3060 ** then the other will be assigned based on its counterpart. Both
3061 ** may not be NULL.
3062 **
3063 ** - pCI->zDate is normalized to/replaced with a valid date/time
3064 ** string. If its original value cannot be validated then
3065 ** this function fails. If pCI->zDate is NULL, the current time
3066 ** is used.
3067 **
3068 ** - If the CIMINI_CONVERT_EOL flag is set, pCI->fileContent appears
3069 ** to be plain text, and its line-ending style differs from its
3070 ** previous version, it is converted to the same EOL style as the
3071 ** previous version. If this is done, the pCI->fileHash is
3072 ** re-computed. Note that only pCI->fileContent, not the original
3073 ** file, is affected by the conversion.
3074 **
3075 ** - If pCI->fileHash is empty, this routine populates it with the
3076 ** repository's preferred hash algorithm.
3077 **
3078 ** pCI's ownership is not modified.
@@ -3108,15 +3112,21 @@
3112 }
3113 fossil_free(zProjCode);
3114 }
3115 db_begin_transaction();
3116
3117 if(pCI->pParent==0 && pCI->zParentUuid){
3118 ci_err((pErr, "Cannot determine parent version."));
3119 }
3120 else if(pCI->pParent==0){
3121 pCI->pParent = manifest_get_by_name(pCI->zParentUuid, 0);
3122 if(pCI->pParent==0){
3123 ci_err((pErr,"Cannot load manifest for [%S].", pCI->zParentUuid));
3124 }
3125 }else if(pCI->zParentUuid==0){
3126 pCI->zParentUuid = rid_to_uuid(pCI->pParent->rid);
3127 assert(pCI->zParentUuid);
3128 }
3129
3130 assert(pCI->pParent->rid>0);
3131 if(leaf_is_closed(pCI->pParent->rid)){
3132 ci_err((pErr,"Cannot commit to a closed leaf."));
@@ -3361,11 +3371,11 @@
3371 **
3372 ** %fossil test-ci-mini -R REPO -m ... -r foo --as src/myfile.c myfile.c
3373 **
3374 */
3375 void test_ci_mini_cmd(){
3376 CheckinMiniInfo cimi; /* checkin state */
3377 int newRid = 0; /* RID of new version */
3378 const char * zFilename; /* argv[2] */
3379 const char * zComment; /* -m comment */
3380 const char * zCommentFile; /* -M FILE */
3381 const char * zAsFilename; /* --as filename */
@@ -3378,45 +3388,45 @@
3388 ** needs in order to fully/properly populate the CheckinMiniInfo and
3389 ** then pass it on to checkin_mini() to do most of the validation
3390 ** and work. The point of this is to avoid duplicate code when a web
3391 ** front-end is added for checkin_mini().
3392 */
3393 CheckinMiniInfo_init(&cimi);
3394 zComment = find_option("comment","m",1);
3395 zCommentFile = find_option("comment-file","M",1);
3396 zAsFilename = find_option("as",0,1);
3397 zRevision = find_option("revision","r",1);
3398 zUser = find_option("user-override",0,1);
3399 zDate = find_option("date-override",0,1);
3400 zManifestFile = find_option("save-manifest",0,1);
3401 if(find_option("wet-run",0,0)==0){
3402 cimi.flags |= CIMINI_DRY_RUN;
3403 }
3404 if(find_option("allow-fork",0,0)!=0){
3405 cimi.flags |= CIMINI_ALLOW_FORK;
3406 }
3407 if(find_option("dump-manifest","d",0)!=0){
3408 cimi.flags |= CIMINI_DUMP_MANIFEST;
3409 }
3410 if(find_option("allow-merge-conflict",0,0)!=0){
3411 cimi.flags |= CIMINI_ALLOW_MERGE_MARKER;
3412 }
3413 if(find_option("allow-older",0,0)!=0){
3414 cimi.flags |= CIMINI_ALLOW_OLDER;
3415 }
3416 if(find_option("convert-eol",0,0)!=0){
3417 cimi.flags |= CIMINI_CONVERT_EOL;
3418 }
3419 if(find_option("delta",0,0)!=0){
3420 cimi.flags |= CIMINI_PREFER_DELTA;
3421 }
3422 if(find_option("delta2",0,0)!=0){
3423 /* Undocumented. For testing only. */
3424 cimi.flags |= CIMINI_PREFER_DELTA | CIMINI_STRONGLY_PREFER_DELTA;
3425 }
3426 if(find_option("allow-new-file",0,0)!=0){
3427 cimi.flags |= CIMINI_ALLOW_NEW_FILE;
3428 }
3429 db_find_and_open_repository(0, 0);
3430 verify_all_options();
3431 user_select();
3432 if(g.argc!=3){
@@ -3424,46 +3434,46 @@
3434 }
3435 if(zComment && zCommentFile){
3436 fossil_fatal("Only one of -m or -M, not both, may be used.");
3437 }else{
3438 if(zCommentFile && *zCommentFile){
3439 blob_read_from_file(&cimi.comment, zCommentFile, ExtFILE);
3440 }else if(zComment && *zComment){
3441 blob_append(&cimi.comment, zComment, -1);
3442 }
3443 if(!blob_size(&cimi.comment)){
3444 fossil_fatal("Non-empty checkin comment is required.");
3445 }
3446 }
3447 db_begin_transaction();
3448 zFilename = g.argv[2];
3449 cimi.zFilename = mprintf("%/", zAsFilename ? zAsFilename : zFilename);
3450 cimi.filePerm = file_perm(zFilename, ExtFILE);
3451 cimi.zUser = mprintf("%s", zUser ? zUser : login_name());
3452 if(zDate){
3453 cimi.zDate = mprintf("%s", zDate);
3454 }
3455 if(zRevision==0 || zRevision[0]==0){
3456 if(g.localOpen/*checkout*/){
3457 zRevision = db_lget("checkout-hash", 0)/*leak*/;
3458 }else{
3459 zRevision = "trunk";
3460 }
3461 }
3462 name_to_uuid2(zRevision, "ci", &cimi.zParentUuid);
3463 if(cimi.zParentUuid==0){
3464 fossil_fatal("Cannot determine version to commit to.");
3465 }
3466 blob_read_from_file(&cimi.fileContent, zFilename, ExtFILE);
3467 {
3468 Blob theManifest = empty_blob; /* --save-manifest target */
3469 Blob errMsg = empty_blob;
3470 int rc;
3471 if(zManifestFile){
3472 cimi.pMfOut = &theManifest;
3473 }
3474 rc = checkin_mini(&cimi, &newRid, &errMsg);
3475 if(rc){
3476 assert(blob_size(&errMsg)==0);
3477 }else{
3478 assert(blob_size(&errMsg));
3479 fossil_fatal("%b", &errMsg);
@@ -3475,23 +3485,23 @@
3485 blob_reset(&theManifest);
3486 }
3487 }
3488 if(newRid!=0){
3489 fossil_print("New version%s: %z\n",
3490 (cimi.flags & CIMINI_DRY_RUN) ? " (dry run)" : "",
3491 rid_to_uuid(newRid));
3492 }
3493 db_end_transaction(0/*checkin_mini() will have triggered it to roll
3494 ** back in dry-run mode, but we need access to
3495 ** the transaction-written db state in this
3496 ** routine.*/);
3497 if(!(cimi.flags & CIMINI_DRY_RUN) && newRid!=0 && g.localOpen!=0){
3498 fossil_warning("The checkout state is now out of sync "
3499 "with regards to this commit. It needs to be "
3500 "'update'd or 'close'd and re-'open'ed.");
3501 }
3502 CheckinMiniInfo_cleanup(&cimi);
3503 }
3504
3505
3506 /*
3507 ** Returns true if the given filename qualified for online editing
@@ -3522,105 +3532,208 @@
3532 ** Parameters intended to be passed in only via the editor's own form:
3533 **
3534 ** diff If true, show diff from prev version.
3535 ** preview If true, preview (how depends on mimetype).
3536 ** content File content.
3537 ** comment Checkin comment.
3538 ** n Optional comment mimetype ("n" as in N-card).
3539 **
3540 **
3541 */
3542 void fileedit_page(){
3543 const char * zFilename = PD("file",P("name")); /* filename */
3544 const char * zRev = P("r"); /* checkin version */
3545 const char * zContent = P("content"); /* file content */
3546 const char * zComment = P("comment"); /* checkin comment */
3547 CheckinMiniInfo cimi; /* Checkin state */
3548 int submitMode = 0; /* See mapping below */
3549 char * zRevResolved = 0; /* Resolved zRev */
3550 int vid; /* checkin rid */
3551 char * zFileUuid = 0; /* File content UUID */
3552 Blob err = empty_blob; /* Error report */
3553 const char * zFlagCheck = 0; /* Temp url flag holder */
3554 Stmt stmt = empty_Stmt;
3555 #define fail(EXPR) blob_appendf EXPR; goto end_footer
3556
3557 login_check_credentials();
3558 if( !g.perm.Write ){
3559 login_needed(g.anon.Write);
3560 return;
3561 }
3562 CheckinMiniInfo_init(&cimi);
3563 zFlagCheck = P("comment");
3564 if(zFlagCheck){
3565 cimi.zMimetype = mprintf("%s",zFlagCheck);
3566 zFlagCheck = 0;
3567 }
3568 cimi.zUser = mprintf("%s",g.zLogin);
3569 /*
3570 ** TODOs include, but are not limited to:
3571 **
 
 
3572 ** - Preview button + view
3573 **
3574 ** - Diff button + view
3575 **
 
 
 
 
 
3576 */
3577 if(!zRev || !*zRev || !zFilename || !*zFilename){
3578 fail((&err,"Missing required URL parameters."));
3579 }
3580 vid = symbolic_name_to_rid(zRev, "ci");
3581 if(0==vid){
3582 fail((&err,"Could not resolve checkin version."));
3583 }
3584
3585 /* Find the repo-side file entry or fail... */
3586 zRevResolved = rid_to_uuid(vid);
3587 db_prepare(&stmt, "SELECT uuid, perm FROM files_of_checkin "
3588 "WHERE filename=%Q %s AND checkinID=%d",
3589 zFilename, filename_collation(), vid);
3590 if(SQLITE_ROW==db_step(&stmt)){
3591 const char * zPerm = db_column_text(&stmt, 1);
3592 const int isLink = zPerm ? strstr(zPerm,"l")!=0 : 0;
3593 if(isLink){
3594 fail((&err,"Editing symlinks is not permitted."));
3595 }
3596 zFileUuid = mprintf("%s",db_column_text(&stmt, 0));
3597 }
3598 db_finalize(&stmt);
3599 if(!zFileUuid){
3600 fail((&err,"Checkin [%S] does not contain file: %h",
3601 zRevResolved, zFilename));
3602 }
3603
3604 /* Read file content from submit request or repo... */
3605 if(zContent==0){
3606 const int frid = fast_uuid_to_rid(zFileUuid);
3607 assert(frid);
3608 content_get(frid, &cimi.fileContent);
3609 zContent = blob_size(&cimi.fileContent)
3610 ? blob_str(&cimi.fileContent) : NULL;
3611 }else{
3612 blob_init(&cimi.fileContent,zContent,-1);
3613 }
3614 if(looks_like_binary(&cimi.fileContent)){
3615 fail((&err,"File appears to be binary. Cannot edit: %h",
3616 zFilename));
3617 }
3618
3619 /* All set. Here we go... */
3620 style_header("File Editor");
3621 #define fp fossil_print
3622 /* ^^^ Appologies, Richard, but the @ form plays havoc with emacs */
3623
3624 fp("<h1>Editing: %h</h1>",zFilename);
3625 fp("<p class='hint'>Permalink: "
3626 "<a href='%R/fileedit?file=%T&r=%s'>"
3627 "%R/fileedit?file=%T&r=%s</a></p>",
3628 zFilename, zRev, zFilename, zRev);
3629 fp("<p>This page is <em>far from complete</em>.</p>\n");
3630
3631 fp("<form action='%R/fileedit' method='POST' "
3632 "class='fileedit-form'>\n");
3633
3634 /******* Hidden fields *******/
3635 fp("<input type='hidden' name='r' value='%s'>", zRevResolved);
3636 fp("<input type='hidden' name='file' value='%T'>",
3637 zFilename);
3638
3639 /******* Comment *******/
3640 fp("<h3>Comment</h3>\n");
3641 fp("<textarea name='comment' rows='3' cols='80'>");
3642 if(zComment && *zComment){
3643 fp("%h"/*%h? %s?*/, zComment);
3644 }
3645 fp("</textarea>\n");
3646 fp("<div class='hint'>Comments use the Fossil wiki markup "
3647 "syntax.</div>"/*TODO: radiobuttons for fossil/me/plain text*/);
3648
3649 /******* Content *******/
3650 fp("<h3>Content</h3>\n");
3651 fp("<textarea name='content' rows='20' cols='80'>");
3652 if(zContent && *zContent){
3653 fp("%h", zContent);
3654 }
3655 fp("</textarea>\n");
3656
3657 /******* Flags/options *******/
3658 fp("<fieldset class='fileedit-options'>"
3659 "<legend>Many checkboxes are TODO</legend><div>"
3660 /* Chrome does not sanely lay out multiple
3661 ** fieldset children after the <legend>, so
3662 ** a containing div is necessary. */);
3663 /*
3664 ** TODO: Put checkboxes here...
3665 **
3666 ** allow-fork, dry-run, convert-eol, allow-merge-conflict,
3667 ** set-exec-bit, date-override, allow-older (in case server time is
3668 ** messed up or someone checked something in w/ a future timestamp),
3669 ** prefer-delta, strongly-prefer-delta (undocumented - for
3670 ** development/admin use only).
3671 */
3672 fp("</div></fieldset>");
3673
3674 /******* Buttons *******/
3675 fp("<fieldset class='fileedit-options'>"
3676 "<legend>Several buttons are TODO</legend><div>");
3677 fp("<button type='submit' name='submit' value='1'>"
3678 "Submit (dry-run)</button>");
3679 fp("<button type='submit' name='submit' value='2'>"
3680 "Preview (TODO)</button>");
3681 fp("<button type='submit' name='submit' value='3'>"
3682 "Diff (TODO)</button>");
3683 fp("</div></fieldset>");
3684
3685 /******* End of form *******/
3686 fp("</form>\n");
3687 zContent = 0;
3688 fossil_free(zRevResolved);
3689 fossil_free(zFileUuid);
3690
3691 submitMode = atoi(PD("submit","0"))
3692 /*
3693 ** Submit modes: 1=submit (save), 2=preview, 3=diff
3694 */;
3695 if(1==submitMode/*save*/){
3696 Blob manifest = empty_blob;
3697 int rc;
3698
3699 /* TODO: pull these flags from P() */
3700 cimi.flags |= CIMINI_DRY_RUN;
3701 cimi.flags |= CIMINI_CONVERT_EOL;
3702 cimi.flags |= CIMINI_PREFER_DELTA;
3703 /*cimi.flags |= CIMINI_STRONGLY_PREFER_DELTA;*/
3704
3705 db_begin_transaction();
3706 cimi.pParent = manifest_get(vid, CFTYPE_MANIFEST, 0);
3707 assert(cimi.pParent && "We know vid is valid.");
3708 cimi.zFilename = mprintf("%s",zFilename);
3709 cimi.pMfOut = &manifest;
3710 cimi.filePerm = PERM_REG;
3711 if(zComment && *zComment){
3712 blob_append(&cimi.comment, zComment, -1);
3713 }
3714 rc = checkin_mini(&cimi, 0, &err);
3715 if(rc!=0){
3716 fp("<h3>Manifest</h3><pre><code>%h</code></pre>",
3717 blob_str(&manifest));
3718 }
3719 blob_reset(&manifest);
3720 db_end_transaction(rc ? 0 : 1);
3721 }else if(2==submitMode/*preview*/){
3722 /* TODO */
3723 }else if(3==submitMode/*diff*/){
3724 /* TODO */
3725 }else{
3726 /* Ignore invalid submitMode value */
3727 goto end_footer;
3728 }
3729
3730 end_footer:
3731 if(blob_size(&err)){
3732 fp("<div class='fileedit-error-report'>%h</div>",
3733 blob_str(&err));
3734 }
3735 blob_reset(&err);
3736 CheckinMiniInfo_cleanup(&cimi);
3737 style_footer();
3738 #undef fp
3739 }
3740
--- src/default_css.txt
+++ src/default_css.txt
@@ -863,11 +863,31 @@
863863
// overflow: auto;
864864
// }
865865
// .fileedit-XXX => /fileedit page
866866
.fileedit-form textarea {
867867
width: 100%;
868
+}
869
+.fileedit-form fieldset {
870
+ border-radius: 0.5em;
868871
}
869872
.fileedit-form .fileedit-options {
870873
padding: 0.5em;
871874
margin: 1em 0 0 0;
872
- border: 2px inset #a0a0a0;
875
+}
876
+.fileedit-form .fileedit-buttons > div > * {
877
+ margin: 0 0.25em 0.25em 0.25em;
878
+}
879
+.fileedit-form .fileedit-options > div > * {
880
+ margin: 0 0.25em 0.25em 0.25em;
881
+}
882
+.fileedit-form .hint {
883
+ font-size: 80%;
884
+ opacity: 0.75;
885
+}
886
+
887
+.fileedit-error-report {
888
+ background: yellow;
889
+ color: darkred;
890
+ margin: 1em 0;
891
+ padding: 0.5em;
892
+ border-radius: 0.5em;
873893
}
874894
--- src/default_css.txt
+++ src/default_css.txt
@@ -863,11 +863,31 @@
863 // overflow: auto;
864 // }
865 // .fileedit-XXX => /fileedit page
866 .fileedit-form textarea {
867 width: 100%;
 
 
 
868 }
869 .fileedit-form .fileedit-options {
870 padding: 0.5em;
871 margin: 1em 0 0 0;
872 border: 2px inset #a0a0a0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
873 }
874
--- src/default_css.txt
+++ src/default_css.txt
@@ -863,11 +863,31 @@
863 // overflow: auto;
864 // }
865 // .fileedit-XXX => /fileedit page
866 .fileedit-form textarea {
867 width: 100%;
868 }
869 .fileedit-form fieldset {
870 border-radius: 0.5em;
871 }
872 .fileedit-form .fileedit-options {
873 padding: 0.5em;
874 margin: 1em 0 0 0;
875 }
876 .fileedit-form .fileedit-buttons > div > * {
877 margin: 0 0.25em 0.25em 0.25em;
878 }
879 .fileedit-form .fileedit-options > div > * {
880 margin: 0 0.25em 0.25em 0.25em;
881 }
882 .fileedit-form .hint {
883 font-size: 80%;
884 opacity: 0.75;
885 }
886
887 .fileedit-error-report {
888 background: yellow;
889 color: darkred;
890 margin: 1em 0;
891 padding: 0.5em;
892 border-radius: 0.5em;
893 }
894

Keyboard Shortcuts

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