Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Instead of injecting multiple mini-scripts, queue up script code and emit it in one anonymous function at the end. Update the version number and permalink after saving so that we're basing on the new version. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | checkin-without-checkout |
| Files: | files | file ages | folders |
| SHA3-256: |
6407b6ca37902e2c07ada139f63830c9 |
| User & Date: | stephan 2020-05-02 16:43:00.314 |
Context
|
2020-05-02
| ||
| 16:48 | Minor error message improvements. ... (check-in: 80345fd14e user: stephan tags: checkin-without-checkout) | |
| 16:43 | Instead of injecting multiple mini-scripts, queue up script code and emit it in one anonymous function at the end. Update the version number and permalink after saving so that we're basing on the new version. ... (check-in: 6407b6ca37 user: stephan tags: checkin-without-checkout) | |
| 15:03 | Added fileedit-glob setting (/fileedit whitelist) and honor it in /fileedit. Added mini-checkin options to specifically convert EOLs to Unix or Windows styles. /fileedit saving now more or less works, but still needs to update the 'r' value after editing or else we're stuck at the old parent version. ... (check-in: 33861414ae user: stephan tags: checkin-without-checkout) | |
Changes
Changes to src/checkin.c.
| ︙ | ︙ | |||
3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 |
static void fileedit_emit_script(int phase){
if(0==phase){
fossil_print("<script nonce='%s'>", style_nonce());
}else{
fossil_print("</script>\n");
}
}
static void fileedit_emit_script_fetch(){
#define fp fossil_print
fileedit_emit_script(0);
fp("window.fossilFetch = function(path,opt){\n");
fp(" if('function'===typeof opt){\n");
fp(" opt={onload:opt};\n");
fp(" }else{\n");
| > > > > > > > | 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 |
static void fileedit_emit_script(int phase){
if(0==phase){
fossil_print("<script nonce='%s'>", style_nonce());
}else{
fossil_print("</script>\n");
}
}
#if 0
/*
** This function is for potential TODO features for /fileedit.
** It's been tested with that code but is not currently used
** by it.
*/
static void fileedit_emit_script_fetch(){
#define fp fossil_print
fileedit_emit_script(0);
fp("window.fossilFetch = function(path,opt){\n");
fp(" if('function'===typeof opt){\n");
fp(" opt={onload:opt};\n");
fp(" }else{\n");
|
| ︙ | ︙ | |||
3610 3611 3612 3613 3614 3615 3616 3617 |
fp(" }\n");
fp(" }\n");
fp(" x.send();");
fp("};\n");
fileedit_emit_script(1);
#undef fp
};
| > > > > > > > > > > > > | | | | | | 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 |
fp(" }\n");
fp(" }\n");
fp(" x.send();");
fp("};\n");
fileedit_emit_script(1);
#undef fp
};
#endif /* fileedit_emit_script_fetch() */
/*
** Outputs a labeled checkbox element:
**
** <span class='input-with-label' title={{zTip}}>
** <input type='checkbox' name={{zFieldName}} value={{zValue}}
** {{isChecked ? " checked : ""}}/>
** <span>{{zLabel}}</span>
** </span>
**
** zFieldName, zLabel, and zValue are required. zTip is optional.
*/
static void style_labeled_checkbox(const char *zFieldName,
const char * zLabel,
const char * zValue,
const char * zTip,
int isChecked){
fossil_print("<span class='input-with-label'");
if(zTip && *zTip){
fossil_print(" title='%h'", zTip);
}
fossil_print("><input type='checkbox' name='%s' value='%T'%s/>",
zFieldName,
zValue ? zValue : "", isChecked ? " checked" : "");
|
| ︙ | ︙ | |||
3649 3650 3651 3652 3653 3654 3655 |
** comment Checkin comment.
** n Optional comment mimetype ("n" as in N-card).
**
**
*/
void fileedit_page(){
const char * zFilename = PD("file",P("name")); /* filename */
| | | | | | | | | | | | > > > > > > > | 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 |
** comment Checkin comment.
** n Optional comment mimetype ("n" as in N-card).
**
**
*/
void fileedit_page(){
const char * zFilename = PD("file",P("name")); /* filename */
const char * zRev = P("r"); /* checkin version */
const char * zContent = P("content"); /* file content */
const char * zComment = P("comment"); /* checkin comment */
CheckinMiniInfo cimi; /* Checkin state */
int submitMode = 0; /* See mapping below */
char * zRevResolved = 0; /* Resolved zRev */
int vid, newVid = 0; /* checkin rid */
char * zFileUuid = 0; /* File content UUID */
int frid = 0; /* File content rid */
Blob err = empty_blob; /* Error report */
const char * zFlagCheck = 0; /* Temp url flag holder */
Blob endScript = empty_blob; /* Script code to run at the
end. This content will be
combined into a single JS
function call, thus each
entry must end with a
semicolon. */
Stmt stmt = empty_Stmt;
#define fail(EXPR) blob_appendf EXPR; goto end_footer
login_check_credentials();
if( !g.perm.Write ){
login_needed(g.anon.Write);
return;
}
db_begin_transaction();
CheckinMiniInfo_init(&cimi);
submitMode = atoi(PD("submit","0"))
/* Submit modes: 0=initial request,
** 1=submit (save), 2=preview, 3=diff */;
zFlagCheck = P("comment");
if(zFlagCheck){
cimi.zMimetype = mprintf("%s",zFlagCheck);
|
| ︙ | ︙ | |||
3737 3738 3739 3740 3741 3742 3743 |
zFilename));
}
/* All set. Here we go... */
#define fp fossil_print
/* ^^^ Appologies, Richard, but the @ form plays havoc with emacs */
| < < | | > > > > | | > > | > | | | | | < < < < < | | | | | | > > > > > > > | | | | | | | > | | > > > > > > > > > > > | > | | | | | | | | | < < | > > | | < < < < < < | < < > > > > | < < < < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > < > > > > > > > > > > > > > | 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 |
zFilename));
}
/* All set. Here we go... */
#define fp fossil_print
/* ^^^ Appologies, Richard, but the @ form plays havoc with emacs */
fp("<h1>Editing:</h1>");
fp("<p class='hint'>");
fp("File: <code>%h</code><br>"
"Version: <code id='r-label'>%s</code><br>",
zFilename, zRevResolved);
fp("Permalink: <code>"
"<a id='permalink' href='%R/fileedit?file=%T&r=%!S'>"
"/fileedit?file=%T&r=%!S</a></code><br>"
"(Clicking the permalink will reload the page and discard "
"all edits!)",
zFilename, zRevResolved, zFilename, zRevResolved);
fp("</p>");
fp("<p>This page is <em>far from complete</em>.</p>\n");
fp("<form action='%R/fileedit' method='POST' "
"class='fileedit-form'>\n");
/******* Hidden fields *******/
fp("<input type='hidden' name='r' value='%s'>", zRevResolved);
fp("<input type='hidden' name='file' value='%T'>",
zFilename);
/******* Comment *******/
fp("<h3>Checkin Comment</h3>\n");
fp("<textarea name='comment' rows='3' cols='80'>");
if(zComment && *zComment){
fp("%h"/*%h? %s?*/, zComment);
}
fp("</textarea>\n");
fp("<div class='hint'>Comments use the Fossil wiki markup "
"syntax.</div>"/*TODO: radiobuttons for fossil/me/plain text*/);
/******* Content *******/
fp("<h3>File Content</h3>\n");
fp("<textarea name='content' id='fileedit-content' "
"rows='20' cols='80'>");
fp("Loading...");
fp("</textarea>\n");
/******* Flags/options *******/
fp("<fieldset class='fileedit-options'>"
"<legend>Options</legend><div>"
/* Chrome does not sanely lay out multiple
** fieldset children after the <legend>, so
** a containing div is necessary. */);
/*
** TODO?: date-override date selection field. Maybe use
** an input[type=datetime-local].
*/
if(0==submitMode || P("dry_run")!=0){
cimi.flags |= CIMINI_DRY_RUN;
}
style_labeled_checkbox("dry_run", "Dry-run?", "1",
"In dry-run mode, do not really save.",
cimi.flags & CIMINI_DRY_RUN);
if(P("allow_fork")!=0){
cimi.flags |= CIMINI_ALLOW_FORK;
}
style_labeled_checkbox("allow_fork", "Allow fork?", "1",
"Allow saving to create a fork?",
cimi.flags & CIMINI_ALLOW_FORK);
if(P("allow_older")!=0){
cimi.flags |= CIMINI_ALLOW_OLDER;
}
style_labeled_checkbox("allow_older", "Allow older?", "1",
"Allow saving to a version which has "
"a newer timestamp?",
cimi.flags & CIMINI_ALLOW_OLDER);
if(P("exec_bit")!=0){
cimi.filePerm = PERM_EXE;
}
style_labeled_checkbox("exec_bit", "Executable?", "1",
"Set executable bit?",
PERM_EXE==cimi.filePerm);
if(P("allow_merge_conflict")!=0){
cimi.flags |= CIMINI_ALLOW_MERGE_MARKER;
}
style_labeled_checkbox("allow_merge_conflict",
"Allow merge conflict markers?", "1",
"Allow saving even if the content contains "
"what appear to be fossil merge conflict "
"markers?",
cimi.flags & CIMINI_ALLOW_MERGE_MARKER);
{/* EOL conversion policy... */
const int eolMode = submitMode==0 ? 0 : atoi(PD("eol","0"));
switch(eolMode){
case 1: cimi.flags |= CIMINI_CONVERT_EOL_UNIX; break;
case 2: cimi.flags |= CIMINI_CONVERT_EOL_WINDOWS; break;
default: cimi.flags |= CIMINI_CONVERT_EOL_INHERIT; break;
}
fp("<select name='eol' "
"title='EOL conversion policy, noting that form-processing "
"may implicitly change the line endings of the input.'>");
fp("<option value='0'%s>Inherit EOLs</option>",
(eolMode!=1 && eolMode!=2) ? " selected" : "");
fp("<option value='1'%s/>Unix EOLs</option>",
eolMode==1 ? " selected" : "");
fp("<option value='2'%s>Windows EOLs</option>",
eolMode==2 ? " selected" : "");
fp("</select>");
}
if(P("prefer_delta")!=0){
cimi.flags |= CIMINI_PREFER_DELTA;
}
style_labeled_checkbox("prefer_delta",
"Prefer delta manifest?", "1",
"Will create a delta manifest, instead of "
"baseline, if conditions are favorable to do "
"so. This option is only a suggestion.",
cimi.flags & CIMINI_PREFER_DELTA);
fp("</div></fieldset>") /* end of checkboxes */;
/******* Buttons *******/
fp("<fieldset class='fileedit-options'>"
"<legend>Several buttons are TODO</legend><div>");
fp("<button type='submit' name='submit' value='1'>"
"Submit</button>");
fp("<button type='submit' name='submit' value='2'>"
"Preview (TODO)</button>");
fp("<button type='submit' name='submit' value='3'>"
"Diff (TODO)</button>");
fp("</div></fieldset>");
/******* End of form *******/
fp("</form>\n");
{
/* Populate the editor...
**
** To avoid all escaping-related issues, we have to do this one of
** two ways:
**
** 1) Fetch the content via AJAX. That only works if the content
** is already in the db, but not for edited versions.
**
** 2) Store the content as JSON and feed it into the textarea
** using JavaScript.
*/
char const * zQuoted = 0;
if(blob_size(&cimi.fileContent)>0){
db_prepare(&stmt, "SELECT json_quote(%B)", &cimi.fileContent);
db_step(&stmt);
zQuoted = db_column_text(&stmt,0);
}
blob_appendf(&endScript,
"/* populate editor form */\n"
"document.getElementById('fileedit-content')"
".value=%s;", zQuoted ? zQuoted : "'';\n");
if(stmt.pStmt){
db_finalize(&stmt);
}
}
if(1==submitMode/*save*/){
Blob manifest = empty_blob;
char * zNewUuid = 0;
/*cimi.flags |= CIMINI_STRONGLY_PREFER_DELTA;*/
if(zComment && *zComment){
blob_append(&cimi.comment, zComment, -1);
}else{
fail((&err,"Empty comment is not permitted."));
}
cimi.pParent = manifest_get(vid, CFTYPE_MANIFEST, 0);
assert(cimi.pParent && "We know vid is valid.");
cimi.zFilename = mprintf("%s",zFilename);
cimi.pMfOut = &manifest;
checkin_mini(&cimi, &newVid, &err);
if(newVid!=0){
zNewUuid = rid_to_uuid(newVid);
fp("<h3>Manifest%s: %S</h3><pre>"
"<code class='fileedit-manifest'>%h</code>"
"</pre>",
(cimi.flags & CIMINI_DRY_RUN) ? " (dry run)" : "",
zNewUuid, blob_str(&manifest));
if(!(CIMINI_DRY_RUN & cimi.flags)){
/* We need to update certain form fields and UI elements so
** they're not left pointing to the previous version. While
** we're at it, we'll re-enable dry-run mode for sanity's
** sake.
*/
blob_appendf(&endScript,
"/* Toggle dry-run back on */\n"
"document.querySelector('input[type=checkbox]"
"[name=dry_run]').checked=true;\n");
blob_appendf(&endScript,
"/* Update version number */\n"
"document.querySelector('input[name=r]')"
".value=%Q;\n"
"document.querySelector('#r-label')"
".innerText=%Q;\n",
zNewUuid, zNewUuid);
blob_appendf(&endScript,
"/* Update permalink */\n"
"const urlFull='%R/fileedit?file=%T&r=%!S';\n"
"const urlShort='/fileedit?file=%T&r=%!S';\n"
"let link=document.querySelector('#permalink');\n"
"link.innerText=urlShort;\n"
"link.setAttribute('href',urlFull);\n",
zFilename, zNewUuid, zFilename, zNewUuid);
}
fossil_free(zNewUuid);
}
/* On error, the error message is in the err blob and will
** be emitted below. */
blob_reset(&manifest);
}else if(2==submitMode/*preview*/){
/* TODO */
fail((&err,"Preview mode is still TODO."));
}else if(3==submitMode/*diff*/){
fail((&err,"Diff mode is still TODO."));
}else{
/* Ignore invalid submitMode value */
goto end_footer;
}
end_footer:
zContent = 0;
fossil_free(zRevResolved);
fossil_free(zFileUuid);
if(stmt.pStmt){
db_finalize(&stmt);
}
if(blob_size(&err)){
fp("<div class='fileedit-error-report'>%h</div>",
blob_str(&err));
}
blob_reset(&err);
CheckinMiniInfo_cleanup(&cimi);
if(blob_size(&endScript)>0){
fileedit_emit_script(0);
fp("(function(){\n");
fp("try{\n%b\n}catch(e){console.error('Exception:',e)}\n",
&endScript);
fp("})();");
fileedit_emit_script(1);
}
db_end_transaction(0/*noting that dry-run mode will have already
** set this to rollback mode. */);
style_footer();
#undef fp
}
|
Changes to src/default_css.txt.
| ︙ | ︙ | |||
896 897 898 899 900 901 902 903 904 905 906 907 908 909 |
}
.input-with-label {
border: 1px inset #808080;
border-radius: 0.5em;
padding: 0.25em 0.4em;
margin: 0 0.5em;
display: inline-block;
}
.input-with-label > input {
margin: 0;
}
.input-with-label > input[type=checkbox] {
vertical-align: sub;
}
| > | 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 |
}
.input-with-label {
border: 1px inset #808080;
border-radius: 0.5em;
padding: 0.25em 0.4em;
margin: 0 0.5em;
display: inline-block;
cursor: pointer;
}
.input-with-label > input {
margin: 0;
}
.input-with-label > input[type=checkbox] {
vertical-align: sub;
}
|
| ︙ | ︙ |