Fossil SCM

/fileedit now squelches errors related to 'missing' URL args, as they are now optional. It also now displays an informative warning at the top of the page if fileedit-glob is not set or is empty. Added a hint about hyperlink in the preview page. Added a Help tab with useful(?) tips.

stephan 2020-05-12 10:24 fileedit-ajaxify
Commit c145140d3ee52d25d09a3f114d3496577dc90e52f7846a3853021638f8c1a5a7
--- src/default_css.txt
+++ src/default_css.txt
@@ -881,13 +881,16 @@
881881
font-size: 1.2em;
882882
padding: 0.2em;
883883
margin: 0.25em 0;
884884
flex: 0 0 auto;
885885
}
886
-#fossil-status-bar.error {
886
+.error {
887887
color: darkred;
888888
background: yellow;
889
+}
890
+body.fileedit .error {
891
+ padding: 0.25em;
889892
}
890893
//////////////////////////////////
891894
// Styles for fossil.tabs.js:
892895
.tab-container {
893896
width: 100%;
894897
--- src/default_css.txt
+++ src/default_css.txt
@@ -881,13 +881,16 @@
881 font-size: 1.2em;
882 padding: 0.2em;
883 margin: 0.25em 0;
884 flex: 0 0 auto;
885 }
886 #fossil-status-bar.error {
887 color: darkred;
888 background: yellow;
 
 
 
889 }
890 //////////////////////////////////
891 // Styles for fossil.tabs.js:
892 .tab-container {
893 width: 100%;
894
--- src/default_css.txt
+++ src/default_css.txt
@@ -881,13 +881,16 @@
881 font-size: 1.2em;
882 padding: 0.2em;
883 margin: 0.25em 0;
884 flex: 0 0 auto;
885 }
886 .error {
887 color: darkred;
888 background: yellow;
889 }
890 body.fileedit .error {
891 padding: 0.25em;
892 }
893 //////////////////////////////////
894 // Styles for fossil.tabs.js:
895 .tab-container {
896 width: 100%;
897
+100 -33
--- src/fileedit.c
+++ src/fileedit.c
@@ -873,10 +873,27 @@
873873
"'update'd or 'close'd and re-'open'ed.");
874874
}
875875
CheckinMiniInfo_cleanup(&cimi);
876876
}
877877
878
+/*
879
+** If the fileedit-glob setting has a value, this returns its Glob
880
+** object (in memory owned by this function), else it returns NULL.
881
+*/
882
+static Glob * fileedit_glob(void){
883
+ static Glob * pGlobs = 0;
884
+ static int once = 0;
885
+ if(0==pGlobs && once==0){
886
+ char * zGlobs = db_get("fileedit-glob",0);
887
+ once = 1;
888
+ if(0!=zGlobs && 0!=*zGlobs){
889
+ pGlobs = glob_create(zGlobs);
890
+ }
891
+ fossil_free(zGlobs);
892
+ }
893
+ return pGlobs;
894
+}
878895
879896
/*
880897
** Returns true if the given filename qualifies for online editing by
881898
** the current user, else returns false.
882899
**
@@ -883,25 +900,17 @@
883900
** Editing requires that the user have the Write permission and that
884901
** the filename match the glob defined by the fileedit-glob setting.
885902
** A missing or empty value for that glob disables all editing.
886903
*/
887904
int fileedit_is_editable(const char *zFilename){
888
- static Glob * pGlobs = 0;
889
- static int once = 0;
890
- if(0==g.perm.Write || zFilename==0 || *zFilename==0
891
- || (once!=0 && pGlobs==0)){
905
+ Glob * pGlobs = fileedit_glob();
906
+ if(pGlobs!=0 && zFilename!=0 && *zFilename!=0 && 0!=g.perm.Write){
907
+ return glob_match(pGlobs, zFilename);
908
+ }else{
892909
return 0;
893
- }else if(0==pGlobs){
894
- char * zGlobs = db_get("fileedit-glob",0);
895
- once = 1;
896
- if(0==zGlobs) return 0;
897
- pGlobs = glob_create(zGlobs);
898
- fossil_free(zGlobs);
899
- }
900
- return glob_match(pGlobs, zFilename);
901
-}
902
-
910
+ }
911
+}
903912
904913
enum fileedit_render_preview_flags {
905914
FE_PREVIEW_LINE_NUMBERS = 1
906915
};
907916
enum fileedit_render_modes {
@@ -1303,22 +1312,31 @@
13031312
** It always fails if it cannot completely resolve the 'file' and 'r'
13041313
** parameters, including verifying that the refer to a real
13051314
** file/version combination and editable by the current user. All
13061315
** others are optional (at this level, anyway, but upstream code might
13071316
** require them).
1317
+**
1318
+** If the 3rd argument is not NULL and an error is related to a
1319
+** missing arg then *bIsMissingArg is set to true. This is
1320
+** intended to allow /fileedit to squelch certain initialization
1321
+** errors.
13081322
**
13091323
** Intended to be used only by /filepage and /filepage_commit.
13101324
*/
1311
-static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr){
1325
+static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr,
1326
+ int * bIsMissingArg){
13121327
char * zFileUuid = 0; /* UUID of file content */
13131328
const char * zFlag; /* generic flag */
13141329
int rc = 0, vid = 0, frid = 0; /* result code, checkin/file rids */
13151330
13161331
#define fail(EXPR) blob_appendf EXPR; goto end_fail
13171332
zFlag = PD("filename",P("fn"));
13181333
if(zFlag==0 || !*zFlag){
13191334
rc = 400;
1335
+ if(bIsMissingArg){
1336
+ *bIsMissingArg = 1;
1337
+ }
13201338
fail((pErr,"Missing required 'filename' parameter."));
13211339
}
13221340
p->zFilename = mprintf("%s",zFlag);
13231341
13241342
if(0==fileedit_is_editable(p->zFilename)){
@@ -1330,10 +1348,13 @@
13301348
}
13311349
13321350
zFlag = PD("checkin",P("ci"));
13331351
if(!zFlag){
13341352
rc = 400;
1353
+ if(bIsMissingArg){
1354
+ *bIsMissingArg = 1;
1355
+ }
13351356
fail((pErr,"Missing required 'checkin' parameter."));
13361357
}
13371358
vid = symbolic_name_to_rid(zFlag, "ci");
13381359
if(0==vid){
13391360
rc = 404;
@@ -1547,11 +1568,11 @@
15471568
if(!fileedit_ajax_boostrap()){
15481569
return;
15491570
}
15501571
db_begin_transaction();
15511572
CheckinMiniInfo_init(&cimi);
1552
- rc = fileedit_setup_cimi_from_p(&cimi, &err);
1573
+ rc = fileedit_setup_cimi_from_p(&cimi, &err, 0);
15531574
if(0!=rc){
15541575
fileedit_ajax_error(rc,"%b",&err);
15551576
goto end_cleanup;
15561577
}
15571578
if(blob_size(&cimi.comment)==0){
@@ -1643,16 +1664,23 @@
16431664
style_header("File Editor");
16441665
/* As of this point, don't use return or fossil_fatal(). Write any
16451666
** error in (&err) and goto end_footer instead so that we can be
16461667
** sure to do any cleanup and end the transaction cleanly.
16471668
*/
1648
- if(fileedit_setup_cimi_from_p(&cimi, &err)==0){
1649
- zFilename = cimi.zFilename;
1650
- zRev = cimi.zParentUuid;
1651
- assert(zRev);
1652
- assert(zFilename);
1653
- zFileMime = mimetype_from_name(cimi.zFilename);
1669
+ {
1670
+ int isMissingArg = 0;
1671
+ if(fileedit_setup_cimi_from_p(&cimi, &err, &isMissingArg)==0){
1672
+ zFilename = cimi.zFilename;
1673
+ zRev = cimi.zParentUuid;
1674
+ assert(zRev);
1675
+ assert(zFilename);
1676
+ zFileMime = mimetype_from_name(cimi.zFilename);
1677
+ }else if(isMissingArg!=0){
1678
+ /* Squelch these startup warnings - they're non-fatal now but
1679
+ ** used to be. */
1680
+ blob_reset(&err);
1681
+ }
16541682
}
16551683
16561684
/********************************************************************
16571685
** All errors which "could" have happened up to this point are of a
16581686
** degree which keep us from rendering the rest of the page, and
@@ -1672,10 +1700,18 @@
16721700
style_emit_script_tag(0,0);
16731701
CX("document.body.classList.add('fileedit');\n");
16741702
style_emit_script_tag(1,0);
16751703
}
16761704
1705
+ if(fileedit_glob()==0){
1706
+ CX("<div class='error'>To enable online editing, the "
1707
+ "<code>fileedit-glob</code> repository setting must be set to a "
1708
+ "comma- or newine-delimited list of glob values matching files "
1709
+ "which may be edited online."
1710
+ "</div>");
1711
+ }
1712
+
16771713
/* Status bar */
16781714
CX("<div id='fossil-status-bar' "
16791715
"title='Status message area. Double-click to clear them.'>"
16801716
"Status messages will go here.</div>\n"
16811717
/* will be moved into the tab container via JS */);
@@ -1751,11 +1787,12 @@
17511787
CX("<div id='fileedit-tab-preview' "
17521788
"data-tab-parent='fileedit-tabs' "
17531789
"data-tab-label='Preview'"
17541790
">");
17551791
1756
- CX("<div class='fileedit-options flex-container flex-row'>");
1792
+ CX("<div class='fileedit-options flex-container flex-column'>");
1793
+ CX("<div class='flex-container flex-row'>");
17571794
CX("<button id='btn-preview-refresh' "
17581795
"data-f-preview-from='fileedit-content-editor' "
17591796
/* ^^^ text source elem ID*/
17601797
"data-f-preview-via='_postPreview' "
17611798
/* ^^^ fossil.page[methodName](content, callback) */
@@ -1816,10 +1853,16 @@
18161853
"preview_ln",
18171854
"Add line numbers to plain-text previews?",
18181855
"1", P("preview_ln")!=0,
18191856
"If on, plain-text files (only) will get "
18201857
"line numbers added to the preview.");
1858
+ CX("</div>"/*.flex-container.flex-row (buttons/options)*/);
1859
+ CX("<div class='fileedit-hint'>"
1860
+ "Note that hyperlinks in previewed HTML are relative to "
1861
+ "<em>this</em> page, and therefore not correct. Clicking "
1862
+ "them will leave this page, losing any edits."
1863
+ "</div>");
18211864
CX("</div>"/*.fileedit-options*/);
18221865
CX("<div id='fileedit-tab-preview-wrapper'></div>");
18231866
CX("</div>"/*#fileedit-tab-preview*/);
18241867
}
18251868
@@ -1842,14 +1885,12 @@
18421885
}
18431886
18441887
/****** Commit ******/
18451888
CX("<div id='fileedit-tab-commit' "
18461889
"data-tab-parent='fileedit-tabs' "
1847
- "data-tab-select='1' "
18481890
"data-tab-label='Commit'"
18491891
">");
1850
-
18511892
{
18521893
/******* Commit flags/options *******/
18531894
CX("<div class='fileedit-options flex-container flex-row'>");
18541895
style_labeled_checkbox("cb-dry-run",
18551896
"dry_run", "Dry-run?", "1", 1,
@@ -1948,16 +1989,44 @@
19481989
"<button id='fileedit-btn-commit'>Commit</button>"
19491990
"</div>\n");
19501991
CX("<div id='fileedit-manifest'></div>\n"
19511992
/* Manifest gets rendered here after a commit. */);
19521993
}
1953
-
19541994
CX("</div>"/*#fileedit-tab-commit*/);
19551995
1996
+ /****** Help/Tips ******/
1997
+ CX("<div id='fileedit-tab-help' "
1998
+ "data-tab-parent='fileedit-tabs' "
1999
+ "data-tab-label='Help'"
2000
+ ">");
2001
+ {
2002
+ CX("<h1>Help &amp; Tips</h1>");
2003
+ CX("<ul>");
2004
+ CX("<li><strong>Only files matching the <code>fileedit-glob</code> "
2005
+ "</strong> repository setting can be edited online. That setting "
2006
+ "must be a comma- or newline-delimited list of glob patterns "
2007
+ "for files which may be edited online.</li>");
2008
+ CX("<li><strong>Clicking any links</strong> on this page will "
2009
+ "leave the page, <strong>losing any edits</strong>.</li>");
2010
+ CX("<li>Saving edits creates a new commit with a single modified "
2011
+ "file.</li>");
2012
+ CX("<li>\"Delta manifests\" (see the checkbox on the Commit tab) "
2013
+ "make for smaller commit records, especially in repositories "
2014
+ "with many files.</li>");
2015
+ CX("<li>The file selector allows, for usability's sake, only files "
2016
+ "in leaf checkins to be selected, but files may be edited via "
2017
+ "non-leaf checkins by passing them as the <code>filename</code> "
2018
+ "and <code>checkin</code> URL arguments to this page.</li>");
2019
+ CX("</ul>");
2020
+ }
2021
+ CX("</div>"/*#fileedit-tab-help*/);
2022
+
2023
+
19562024
{
1957
- /* Dynamically populate the editor or display a warning
1958
- ** about having no file loaded... */
2025
+ /* Dynamically populate the editor, display a any error
2026
+ ** in the err blob, and/or switch to tab #0, where the file
2027
+ ** selector lives... */
19592028
blob_appendf(&endScript,
19602029
"window.addEventListener('load',");
19612030
if(zRev && zFilename){
19622031
assert(0==blob_size(&err));
19632032
blob_appendf(&endScript,
@@ -1965,17 +2034,15 @@
19652034
zFilename, cimi.zParentUuid);
19662035
}else{
19672036
blob_appendf(&endScript,"function(){");
19682037
if(blob_size(&err)>0){
19692038
blob_appendf(&endScript,
1970
- "fossil.error(\"%j\");\n"
1971
- "fossil.page.tabs.switchToTab(0);\n",
2039
+ "fossil.error(\"%j\");\n",
19722040
blob_str(&err));
1973
- }else{
1974
- blob_appendf(&endScript,
1975
- "fossil.error('No file/version selected.')");
19762041
}
2042
+ blob_appendf(&endScript,
2043
+ "fossil.page.tabs.switchToTab(0);\n");
19772044
blob_appendf(&endScript,"}");
19782045
}
19792046
blob_appendf(&endScript,", false);\n");
19802047
}
19812048
19822049
--- src/fileedit.c
+++ src/fileedit.c
@@ -873,10 +873,27 @@
873 "'update'd or 'close'd and re-'open'ed.");
874 }
875 CheckinMiniInfo_cleanup(&cimi);
876 }
877
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
878
879 /*
880 ** Returns true if the given filename qualifies for online editing by
881 ** the current user, else returns false.
882 **
@@ -883,25 +900,17 @@
883 ** Editing requires that the user have the Write permission and that
884 ** the filename match the glob defined by the fileedit-glob setting.
885 ** A missing or empty value for that glob disables all editing.
886 */
887 int fileedit_is_editable(const char *zFilename){
888 static Glob * pGlobs = 0;
889 static int once = 0;
890 if(0==g.perm.Write || zFilename==0 || *zFilename==0
891 || (once!=0 && pGlobs==0)){
892 return 0;
893 }else if(0==pGlobs){
894 char * zGlobs = db_get("fileedit-glob",0);
895 once = 1;
896 if(0==zGlobs) return 0;
897 pGlobs = glob_create(zGlobs);
898 fossil_free(zGlobs);
899 }
900 return glob_match(pGlobs, zFilename);
901 }
902
903
904 enum fileedit_render_preview_flags {
905 FE_PREVIEW_LINE_NUMBERS = 1
906 };
907 enum fileedit_render_modes {
@@ -1303,22 +1312,31 @@
1303 ** It always fails if it cannot completely resolve the 'file' and 'r'
1304 ** parameters, including verifying that the refer to a real
1305 ** file/version combination and editable by the current user. All
1306 ** others are optional (at this level, anyway, but upstream code might
1307 ** require them).
 
 
 
 
 
1308 **
1309 ** Intended to be used only by /filepage and /filepage_commit.
1310 */
1311 static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr){
 
1312 char * zFileUuid = 0; /* UUID of file content */
1313 const char * zFlag; /* generic flag */
1314 int rc = 0, vid = 0, frid = 0; /* result code, checkin/file rids */
1315
1316 #define fail(EXPR) blob_appendf EXPR; goto end_fail
1317 zFlag = PD("filename",P("fn"));
1318 if(zFlag==0 || !*zFlag){
1319 rc = 400;
 
 
 
1320 fail((pErr,"Missing required 'filename' parameter."));
1321 }
1322 p->zFilename = mprintf("%s",zFlag);
1323
1324 if(0==fileedit_is_editable(p->zFilename)){
@@ -1330,10 +1348,13 @@
1330 }
1331
1332 zFlag = PD("checkin",P("ci"));
1333 if(!zFlag){
1334 rc = 400;
 
 
 
1335 fail((pErr,"Missing required 'checkin' parameter."));
1336 }
1337 vid = symbolic_name_to_rid(zFlag, "ci");
1338 if(0==vid){
1339 rc = 404;
@@ -1547,11 +1568,11 @@
1547 if(!fileedit_ajax_boostrap()){
1548 return;
1549 }
1550 db_begin_transaction();
1551 CheckinMiniInfo_init(&cimi);
1552 rc = fileedit_setup_cimi_from_p(&cimi, &err);
1553 if(0!=rc){
1554 fileedit_ajax_error(rc,"%b",&err);
1555 goto end_cleanup;
1556 }
1557 if(blob_size(&cimi.comment)==0){
@@ -1643,16 +1664,23 @@
1643 style_header("File Editor");
1644 /* As of this point, don't use return or fossil_fatal(). Write any
1645 ** error in (&err) and goto end_footer instead so that we can be
1646 ** sure to do any cleanup and end the transaction cleanly.
1647 */
1648 if(fileedit_setup_cimi_from_p(&cimi, &err)==0){
1649 zFilename = cimi.zFilename;
1650 zRev = cimi.zParentUuid;
1651 assert(zRev);
1652 assert(zFilename);
1653 zFileMime = mimetype_from_name(cimi.zFilename);
 
 
 
 
 
 
 
1654 }
1655
1656 /********************************************************************
1657 ** All errors which "could" have happened up to this point are of a
1658 ** degree which keep us from rendering the rest of the page, and
@@ -1672,10 +1700,18 @@
1672 style_emit_script_tag(0,0);
1673 CX("document.body.classList.add('fileedit');\n");
1674 style_emit_script_tag(1,0);
1675 }
1676
 
 
 
 
 
 
 
 
1677 /* Status bar */
1678 CX("<div id='fossil-status-bar' "
1679 "title='Status message area. Double-click to clear them.'>"
1680 "Status messages will go here.</div>\n"
1681 /* will be moved into the tab container via JS */);
@@ -1751,11 +1787,12 @@
1751 CX("<div id='fileedit-tab-preview' "
1752 "data-tab-parent='fileedit-tabs' "
1753 "data-tab-label='Preview'"
1754 ">");
1755
1756 CX("<div class='fileedit-options flex-container flex-row'>");
 
1757 CX("<button id='btn-preview-refresh' "
1758 "data-f-preview-from='fileedit-content-editor' "
1759 /* ^^^ text source elem ID*/
1760 "data-f-preview-via='_postPreview' "
1761 /* ^^^ fossil.page[methodName](content, callback) */
@@ -1816,10 +1853,16 @@
1816 "preview_ln",
1817 "Add line numbers to plain-text previews?",
1818 "1", P("preview_ln")!=0,
1819 "If on, plain-text files (only) will get "
1820 "line numbers added to the preview.");
 
 
 
 
 
 
1821 CX("</div>"/*.fileedit-options*/);
1822 CX("<div id='fileedit-tab-preview-wrapper'></div>");
1823 CX("</div>"/*#fileedit-tab-preview*/);
1824 }
1825
@@ -1842,14 +1885,12 @@
1842 }
1843
1844 /****** Commit ******/
1845 CX("<div id='fileedit-tab-commit' "
1846 "data-tab-parent='fileedit-tabs' "
1847 "data-tab-select='1' "
1848 "data-tab-label='Commit'"
1849 ">");
1850
1851 {
1852 /******* Commit flags/options *******/
1853 CX("<div class='fileedit-options flex-container flex-row'>");
1854 style_labeled_checkbox("cb-dry-run",
1855 "dry_run", "Dry-run?", "1", 1,
@@ -1948,16 +1989,44 @@
1948 "<button id='fileedit-btn-commit'>Commit</button>"
1949 "</div>\n");
1950 CX("<div id='fileedit-manifest'></div>\n"
1951 /* Manifest gets rendered here after a commit. */);
1952 }
1953
1954 CX("</div>"/*#fileedit-tab-commit*/);
1955
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1956 {
1957 /* Dynamically populate the editor or display a warning
1958 ** about having no file loaded... */
 
1959 blob_appendf(&endScript,
1960 "window.addEventListener('load',");
1961 if(zRev && zFilename){
1962 assert(0==blob_size(&err));
1963 blob_appendf(&endScript,
@@ -1965,17 +2034,15 @@
1965 zFilename, cimi.zParentUuid);
1966 }else{
1967 blob_appendf(&endScript,"function(){");
1968 if(blob_size(&err)>0){
1969 blob_appendf(&endScript,
1970 "fossil.error(\"%j\");\n"
1971 "fossil.page.tabs.switchToTab(0);\n",
1972 blob_str(&err));
1973 }else{
1974 blob_appendf(&endScript,
1975 "fossil.error('No file/version selected.')");
1976 }
 
 
1977 blob_appendf(&endScript,"}");
1978 }
1979 blob_appendf(&endScript,", false);\n");
1980 }
1981
1982
--- src/fileedit.c
+++ src/fileedit.c
@@ -873,10 +873,27 @@
873 "'update'd or 'close'd and re-'open'ed.");
874 }
875 CheckinMiniInfo_cleanup(&cimi);
876 }
877
878 /*
879 ** If the fileedit-glob setting has a value, this returns its Glob
880 ** object (in memory owned by this function), else it returns NULL.
881 */
882 static Glob * fileedit_glob(void){
883 static Glob * pGlobs = 0;
884 static int once = 0;
885 if(0==pGlobs && once==0){
886 char * zGlobs = db_get("fileedit-glob",0);
887 once = 1;
888 if(0!=zGlobs && 0!=*zGlobs){
889 pGlobs = glob_create(zGlobs);
890 }
891 fossil_free(zGlobs);
892 }
893 return pGlobs;
894 }
895
896 /*
897 ** Returns true if the given filename qualifies for online editing by
898 ** the current user, else returns false.
899 **
@@ -883,25 +900,17 @@
900 ** Editing requires that the user have the Write permission and that
901 ** the filename match the glob defined by the fileedit-glob setting.
902 ** A missing or empty value for that glob disables all editing.
903 */
904 int fileedit_is_editable(const char *zFilename){
905 Glob * pGlobs = fileedit_glob();
906 if(pGlobs!=0 && zFilename!=0 && *zFilename!=0 && 0!=g.perm.Write){
907 return glob_match(pGlobs, zFilename);
908 }else{
909 return 0;
910 }
911 }
 
 
 
 
 
 
 
 
912
913 enum fileedit_render_preview_flags {
914 FE_PREVIEW_LINE_NUMBERS = 1
915 };
916 enum fileedit_render_modes {
@@ -1303,22 +1312,31 @@
1312 ** It always fails if it cannot completely resolve the 'file' and 'r'
1313 ** parameters, including verifying that the refer to a real
1314 ** file/version combination and editable by the current user. All
1315 ** others are optional (at this level, anyway, but upstream code might
1316 ** require them).
1317 **
1318 ** If the 3rd argument is not NULL and an error is related to a
1319 ** missing arg then *bIsMissingArg is set to true. This is
1320 ** intended to allow /fileedit to squelch certain initialization
1321 ** errors.
1322 **
1323 ** Intended to be used only by /filepage and /filepage_commit.
1324 */
1325 static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr,
1326 int * bIsMissingArg){
1327 char * zFileUuid = 0; /* UUID of file content */
1328 const char * zFlag; /* generic flag */
1329 int rc = 0, vid = 0, frid = 0; /* result code, checkin/file rids */
1330
1331 #define fail(EXPR) blob_appendf EXPR; goto end_fail
1332 zFlag = PD("filename",P("fn"));
1333 if(zFlag==0 || !*zFlag){
1334 rc = 400;
1335 if(bIsMissingArg){
1336 *bIsMissingArg = 1;
1337 }
1338 fail((pErr,"Missing required 'filename' parameter."));
1339 }
1340 p->zFilename = mprintf("%s",zFlag);
1341
1342 if(0==fileedit_is_editable(p->zFilename)){
@@ -1330,10 +1348,13 @@
1348 }
1349
1350 zFlag = PD("checkin",P("ci"));
1351 if(!zFlag){
1352 rc = 400;
1353 if(bIsMissingArg){
1354 *bIsMissingArg = 1;
1355 }
1356 fail((pErr,"Missing required 'checkin' parameter."));
1357 }
1358 vid = symbolic_name_to_rid(zFlag, "ci");
1359 if(0==vid){
1360 rc = 404;
@@ -1547,11 +1568,11 @@
1568 if(!fileedit_ajax_boostrap()){
1569 return;
1570 }
1571 db_begin_transaction();
1572 CheckinMiniInfo_init(&cimi);
1573 rc = fileedit_setup_cimi_from_p(&cimi, &err, 0);
1574 if(0!=rc){
1575 fileedit_ajax_error(rc,"%b",&err);
1576 goto end_cleanup;
1577 }
1578 if(blob_size(&cimi.comment)==0){
@@ -1643,16 +1664,23 @@
1664 style_header("File Editor");
1665 /* As of this point, don't use return or fossil_fatal(). Write any
1666 ** error in (&err) and goto end_footer instead so that we can be
1667 ** sure to do any cleanup and end the transaction cleanly.
1668 */
1669 {
1670 int isMissingArg = 0;
1671 if(fileedit_setup_cimi_from_p(&cimi, &err, &isMissingArg)==0){
1672 zFilename = cimi.zFilename;
1673 zRev = cimi.zParentUuid;
1674 assert(zRev);
1675 assert(zFilename);
1676 zFileMime = mimetype_from_name(cimi.zFilename);
1677 }else if(isMissingArg!=0){
1678 /* Squelch these startup warnings - they're non-fatal now but
1679 ** used to be. */
1680 blob_reset(&err);
1681 }
1682 }
1683
1684 /********************************************************************
1685 ** All errors which "could" have happened up to this point are of a
1686 ** degree which keep us from rendering the rest of the page, and
@@ -1672,10 +1700,18 @@
1700 style_emit_script_tag(0,0);
1701 CX("document.body.classList.add('fileedit');\n");
1702 style_emit_script_tag(1,0);
1703 }
1704
1705 if(fileedit_glob()==0){
1706 CX("<div class='error'>To enable online editing, the "
1707 "<code>fileedit-glob</code> repository setting must be set to a "
1708 "comma- or newine-delimited list of glob values matching files "
1709 "which may be edited online."
1710 "</div>");
1711 }
1712
1713 /* Status bar */
1714 CX("<div id='fossil-status-bar' "
1715 "title='Status message area. Double-click to clear them.'>"
1716 "Status messages will go here.</div>\n"
1717 /* will be moved into the tab container via JS */);
@@ -1751,11 +1787,12 @@
1787 CX("<div id='fileedit-tab-preview' "
1788 "data-tab-parent='fileedit-tabs' "
1789 "data-tab-label='Preview'"
1790 ">");
1791
1792 CX("<div class='fileedit-options flex-container flex-column'>");
1793 CX("<div class='flex-container flex-row'>");
1794 CX("<button id='btn-preview-refresh' "
1795 "data-f-preview-from='fileedit-content-editor' "
1796 /* ^^^ text source elem ID*/
1797 "data-f-preview-via='_postPreview' "
1798 /* ^^^ fossil.page[methodName](content, callback) */
@@ -1816,10 +1853,16 @@
1853 "preview_ln",
1854 "Add line numbers to plain-text previews?",
1855 "1", P("preview_ln")!=0,
1856 "If on, plain-text files (only) will get "
1857 "line numbers added to the preview.");
1858 CX("</div>"/*.flex-container.flex-row (buttons/options)*/);
1859 CX("<div class='fileedit-hint'>"
1860 "Note that hyperlinks in previewed HTML are relative to "
1861 "<em>this</em> page, and therefore not correct. Clicking "
1862 "them will leave this page, losing any edits."
1863 "</div>");
1864 CX("</div>"/*.fileedit-options*/);
1865 CX("<div id='fileedit-tab-preview-wrapper'></div>");
1866 CX("</div>"/*#fileedit-tab-preview*/);
1867 }
1868
@@ -1842,14 +1885,12 @@
1885 }
1886
1887 /****** Commit ******/
1888 CX("<div id='fileedit-tab-commit' "
1889 "data-tab-parent='fileedit-tabs' "
 
1890 "data-tab-label='Commit'"
1891 ">");
 
1892 {
1893 /******* Commit flags/options *******/
1894 CX("<div class='fileedit-options flex-container flex-row'>");
1895 style_labeled_checkbox("cb-dry-run",
1896 "dry_run", "Dry-run?", "1", 1,
@@ -1948,16 +1989,44 @@
1989 "<button id='fileedit-btn-commit'>Commit</button>"
1990 "</div>\n");
1991 CX("<div id='fileedit-manifest'></div>\n"
1992 /* Manifest gets rendered here after a commit. */);
1993 }
 
1994 CX("</div>"/*#fileedit-tab-commit*/);
1995
1996 /****** Help/Tips ******/
1997 CX("<div id='fileedit-tab-help' "
1998 "data-tab-parent='fileedit-tabs' "
1999 "data-tab-label='Help'"
2000 ">");
2001 {
2002 CX("<h1>Help &amp; Tips</h1>");
2003 CX("<ul>");
2004 CX("<li><strong>Only files matching the <code>fileedit-glob</code> "
2005 "</strong> repository setting can be edited online. That setting "
2006 "must be a comma- or newline-delimited list of glob patterns "
2007 "for files which may be edited online.</li>");
2008 CX("<li><strong>Clicking any links</strong> on this page will "
2009 "leave the page, <strong>losing any edits</strong>.</li>");
2010 CX("<li>Saving edits creates a new commit with a single modified "
2011 "file.</li>");
2012 CX("<li>\"Delta manifests\" (see the checkbox on the Commit tab) "
2013 "make for smaller commit records, especially in repositories "
2014 "with many files.</li>");
2015 CX("<li>The file selector allows, for usability's sake, only files "
2016 "in leaf checkins to be selected, but files may be edited via "
2017 "non-leaf checkins by passing them as the <code>filename</code> "
2018 "and <code>checkin</code> URL arguments to this page.</li>");
2019 CX("</ul>");
2020 }
2021 CX("</div>"/*#fileedit-tab-help*/);
2022
2023
2024 {
2025 /* Dynamically populate the editor, display a any error
2026 ** in the err blob, and/or switch to tab #0, where the file
2027 ** selector lives... */
2028 blob_appendf(&endScript,
2029 "window.addEventListener('load',");
2030 if(zRev && zFilename){
2031 assert(0==blob_size(&err));
2032 blob_appendf(&endScript,
@@ -1965,17 +2034,15 @@
2034 zFilename, cimi.zParentUuid);
2035 }else{
2036 blob_appendf(&endScript,"function(){");
2037 if(blob_size(&err)>0){
2038 blob_appendf(&endScript,
2039 "fossil.error(\"%j\");\n",
 
2040 blob_str(&err));
 
 
 
2041 }
2042 blob_appendf(&endScript,
2043 "fossil.page.tabs.switchToTab(0);\n");
2044 blob_appendf(&endScript,"}");
2045 }
2046 blob_appendf(&endScript,", false);\n");
2047 }
2048
2049
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -57,10 +57,12 @@
5757
return str ? str.split(f.rx) : [str];
5858
};
5959
6060
dom.div = dom.createElemFactory('div');
6161
dom.p = dom.createElemFactory('p');
62
+ dom.code = dom.createElemFactory('code');
63
+ dom.pre = dom.createElemFactory('pre');
6264
dom.header = dom.createElemFactory('header');
6365
dom.footer = dom.createElemFactory('footer');
6466
dom.section = dom.createElemFactory('section');
6567
dom.span = dom.createElemFactory('span');
6668
dom.strong = dom.createElemFactory('strong');
6769
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -57,10 +57,12 @@
57 return str ? str.split(f.rx) : [str];
58 };
59
60 dom.div = dom.createElemFactory('div');
61 dom.p = dom.createElemFactory('p');
 
 
62 dom.header = dom.createElemFactory('header');
63 dom.footer = dom.createElemFactory('footer');
64 dom.section = dom.createElemFactory('section');
65 dom.span = dom.createElemFactory('span');
66 dom.strong = dom.createElemFactory('strong');
67
--- src/fossil.dom.js
+++ src/fossil.dom.js
@@ -57,10 +57,12 @@
57 return str ? str.split(f.rx) : [str];
58 };
59
60 dom.div = dom.createElemFactory('div');
61 dom.p = dom.createElemFactory('p');
62 dom.code = dom.createElemFactory('code');
63 dom.pre = dom.createElemFactory('pre');
64 dom.header = dom.createElemFactory('header');
65 dom.footer = dom.createElemFactory('footer');
66 dom.section = dom.createElemFactory('section');
67 dom.span = dom.createElemFactory('span');
68 dom.strong = dom.createElemFactory('strong');
69
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -63,23 +63,25 @@
6363
if(!ciUuid){
6464
D.clearElement(D.disable(selFiles, this.e.btnLoadFile));
6565
return this;
6666
}
6767
const onload = (response)=>{
68
- D.clearElement(D.enable(selFiles, this.e.btnLoadFile));
68
+ D.clearElement(selFiles, this.e.btnLoadFile);
6969
D.append(
7070
D.clearElement(this.e.fileListLabel),
7171
"Editable files for ",
7272
D.a(F.repoUrl('timeline',{
7373
c: ciUuid
74
- }), F.hashDigits(ciUuid)),
75
- ':'
74
+ }), F.hashDigits(ciUuid))
7675
);
7776
this.cache.files[response.checkin] = response;
78
- response.editableFiles.forEach(function(fn){
77
+ response.editableFiles.forEach(function(fn,n){
7978
D.option(selFiles, fn);
8079
});
80
+ if(selFiles.options.length){
81
+ D.enable(selFiles, this.e.btnLoadFile);
82
+ }
8183
};
8284
const got = this.cache.files[ciUuid];
8385
if(got){
8486
onload(got);
8587
return this;
8688
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -63,23 +63,25 @@
63 if(!ciUuid){
64 D.clearElement(D.disable(selFiles, this.e.btnLoadFile));
65 return this;
66 }
67 const onload = (response)=>{
68 D.clearElement(D.enable(selFiles, this.e.btnLoadFile));
69 D.append(
70 D.clearElement(this.e.fileListLabel),
71 "Editable files for ",
72 D.a(F.repoUrl('timeline',{
73 c: ciUuid
74 }), F.hashDigits(ciUuid)),
75 ':'
76 );
77 this.cache.files[response.checkin] = response;
78 response.editableFiles.forEach(function(fn){
79 D.option(selFiles, fn);
80 });
 
 
 
81 };
82 const got = this.cache.files[ciUuid];
83 if(got){
84 onload(got);
85 return this;
86
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -63,23 +63,25 @@
63 if(!ciUuid){
64 D.clearElement(D.disable(selFiles, this.e.btnLoadFile));
65 return this;
66 }
67 const onload = (response)=>{
68 D.clearElement(selFiles, this.e.btnLoadFile);
69 D.append(
70 D.clearElement(this.e.fileListLabel),
71 "Editable files for ",
72 D.a(F.repoUrl('timeline',{
73 c: ciUuid
74 }), F.hashDigits(ciUuid))
 
75 );
76 this.cache.files[response.checkin] = response;
77 response.editableFiles.forEach(function(fn,n){
78 D.option(selFiles, fn);
79 });
80 if(selFiles.options.length){
81 D.enable(selFiles, this.e.btnLoadFile);
82 }
83 };
84 const got = this.cache.files[ciUuid];
85 if(got){
86 onload(got);
87 return this;
88
+1 -1
--- src/style.c
+++ src/style.c
@@ -1525,11 +1525,11 @@
15251525
** fossil.page holds info about the current page. This is also
15261526
** where the current page "should" store any of its own
15271527
** page-specific state, and it is reserved for that purpose.
15281528
*/
15291529
CX("window.fossil.page = {"
1530
- "page:\"%T\""
1530
+ "name:\"%T\""
15311531
"};\n", g.zPath);
15321532
CX("})();\n");
15331533
/* The remaining fossil object bootstrap code is not dependent on
15341534
** C-runtime state... */
15351535
if(asInline){
15361536
--- src/style.c
+++ src/style.c
@@ -1525,11 +1525,11 @@
1525 ** fossil.page holds info about the current page. This is also
1526 ** where the current page "should" store any of its own
1527 ** page-specific state, and it is reserved for that purpose.
1528 */
1529 CX("window.fossil.page = {"
1530 "page:\"%T\""
1531 "};\n", g.zPath);
1532 CX("})();\n");
1533 /* The remaining fossil object bootstrap code is not dependent on
1534 ** C-runtime state... */
1535 if(asInline){
1536
--- src/style.c
+++ src/style.c
@@ -1525,11 +1525,11 @@
1525 ** fossil.page holds info about the current page. This is also
1526 ** where the current page "should" store any of its own
1527 ** page-specific state, and it is reserved for that purpose.
1528 */
1529 CX("window.fossil.page = {"
1530 "name:\"%T\""
1531 "};\n", g.zPath);
1532 CX("})();\n");
1533 /* The remaining fossil object bootstrap code is not dependent on
1534 ** C-runtime state... */
1535 if(asInline){
1536

Keyboard Shortcuts

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