Fossil SCM

Permissions checks improvements. Added a couple TODOs, notably for how to integrate handling of new/as-yet-unsaved pages into the UI.

stephan 2020-07-30 01:45 ajax-wiki-editor
Commit 044e2b55a57cc68fb702fd5fd8239efb78de33a57b6730a6f4e077eb48aae122
2 files changed +3 -2 +121 -41
--- src/fossil.page.wikiedit.js
+++ src/fossil.page.wikiedit.js
@@ -534,11 +534,11 @@
534534
For use when installing a custom editor widget. Pass it the
535535
getter and setter callbacks to fetch resp. set the content of the
536536
custom widget. They will be triggered via
537537
P.wikiContent(). Returns this object.
538538
*/
539
- P.setFileContentMethods = function(getter, setter){
539
+ P.setContentMethods = function(getter, setter){
540540
this.wikiContent.get = getter;
541541
this.wikiContent.set = setter;
542542
return this;
543543
};
544544
@@ -678,10 +678,11 @@
678678
callback(content);
679679
return this;
680680
}
681681
const fd = new FormData();
682682
const mimetype = this.e.selectMimetype.value;
683
+ fd.append('page', this.winfo.name);
683684
fd.append('mimetype',mimetype);
684685
fd.append('content',content || '');
685686
F.message(
686687
"Fetching preview..."
687688
).fetch('wikiajax/preview',{
@@ -743,11 +744,11 @@
743744
).fetch('wikiajax/diff',{
744745
payload: fd,
745746
onload: function(c){
746747
target.innerHTML = [
747748
"<div>Diff <code>[",
748
- self.winfo.checkin,
749
+ self.winfo.name,
749750
"]</code> &rarr; Local Edits</div>",
750751
c||'No changes.'
751752
].join('');
752753
if(sbs) P.tweakSbsDiffs2();
753754
F.message('Updated diff.');
754755
--- src/fossil.page.wikiedit.js
+++ src/fossil.page.wikiedit.js
@@ -534,11 +534,11 @@
534 For use when installing a custom editor widget. Pass it the
535 getter and setter callbacks to fetch resp. set the content of the
536 custom widget. They will be triggered via
537 P.wikiContent(). Returns this object.
538 */
539 P.setFileContentMethods = function(getter, setter){
540 this.wikiContent.get = getter;
541 this.wikiContent.set = setter;
542 return this;
543 };
544
@@ -678,10 +678,11 @@
678 callback(content);
679 return this;
680 }
681 const fd = new FormData();
682 const mimetype = this.e.selectMimetype.value;
 
683 fd.append('mimetype',mimetype);
684 fd.append('content',content || '');
685 F.message(
686 "Fetching preview..."
687 ).fetch('wikiajax/preview',{
@@ -743,11 +744,11 @@
743 ).fetch('wikiajax/diff',{
744 payload: fd,
745 onload: function(c){
746 target.innerHTML = [
747 "<div>Diff <code>[",
748 self.winfo.checkin,
749 "]</code> &rarr; Local Edits</div>",
750 c||'No changes.'
751 ].join('');
752 if(sbs) P.tweakSbsDiffs2();
753 F.message('Updated diff.');
754
--- src/fossil.page.wikiedit.js
+++ src/fossil.page.wikiedit.js
@@ -534,11 +534,11 @@
534 For use when installing a custom editor widget. Pass it the
535 getter and setter callbacks to fetch resp. set the content of the
536 custom widget. They will be triggered via
537 P.wikiContent(). Returns this object.
538 */
539 P.setContentMethods = function(getter, setter){
540 this.wikiContent.get = getter;
541 this.wikiContent.set = setter;
542 return this;
543 };
544
@@ -678,10 +678,11 @@
678 callback(content);
679 return this;
680 }
681 const fd = new FormData();
682 const mimetype = this.e.selectMimetype.value;
683 fd.append('page', this.winfo.name);
684 fd.append('mimetype',mimetype);
685 fd.append('content',content || '');
686 F.message(
687 "Fetching preview..."
688 ).fetch('wikiajax/preview',{
@@ -743,11 +744,11 @@
744 ).fetch('wikiajax/diff',{
745 payload: fd,
746 onload: function(c){
747 target.innerHTML = [
748 "<div>Diff <code>[",
749 self.winfo.name,
750 "]</code> &rarr; Local Edits</div>",
751 c||'No changes.'
752 ].join('');
753 if(sbs) P.tweakSbsDiffs2();
754 F.message('Updated diff.');
755
+121 -41
--- src/wiki.c
+++ src/wiki.c
@@ -680,10 +680,49 @@
680680
}
681681
*ppWiki = pWiki;
682682
}
683683
return 1;
684684
}
685
+
686
+/*
687
+** Determines whether the wiki page with the given name can be edited
688
+** by the current user. If not, an AJAX error is queued and false is
689
+** returned, else true is returned. A NULL, empty, or malformed name
690
+** is considered non-writable.
691
+**
692
+** If pRid is not NULL then this function writes the page's rid to
693
+** *pRid (whether or not access is granted).
694
+**
695
+** Note that the sandbox is a special case: it is a pseudo-page with
696
+** no rid and this API does not allow anyone to actually save the
697
+** sandbox page, but it is reported as writable here (with rid 0).
698
+*/
699
+static int wiki_ajax_can_write(const char *zPageName, int * pRid){
700
+ int rid;
701
+ const char * zMsg = 0;
702
+
703
+ if(pRid) *pRid = 0;
704
+ if(!zPageName || !*zPageName
705
+ || !wiki_name_is_wellformed((unsigned const char *)zPageName)){
706
+ return 0;
707
+ }
708
+ if(is_sandbox(zPageName)) return 1;
709
+ wiki_fetch_by_name(zPageName, 0, &rid, 0);
710
+ if(pRid) *pRid = rid;
711
+ if(!wiki_special_permission(zPageName)) return 0;
712
+ if( (rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki) ){
713
+ return 3;
714
+ }else if(rid && !g.perm.WrWiki){
715
+ zMsg = "Requires wiki-write permissions.";
716
+ }else if(!rid && !g.perm.NewWiki){
717
+ zMsg = "Requires new-wiki permissions.";
718
+ }else{
719
+ assert(!"Can't happen?");
720
+ }
721
+ ajax_route_error(403, "%s", zMsg);
722
+ return 0;
723
+}
685724
686725
/*
687726
** Ajax route handler for /wikiajax/fetch.
688727
**
689728
** URL params:
@@ -695,11 +734,11 @@
695734
**
696735
** { name: "page name",
697736
** type: "normal" | "tag" | "checkin" | "branch" | "sandbox",
698737
** mimetype: "mime type",
699738
** version: UUID string or null for a sandbox page,
700
-** parent: "parent uuid" or null,
739
+** parent: "parent uuid" or null if no parent,
701740
** content: "page content"
702741
** }
703742
*/
704743
static void wiki_ajax_route_fetch(void){
705744
const char * zPageName = P("page");
@@ -751,10 +790,13 @@
751790
**
752791
** URL params:
753792
**
754793
** page = the wiki page name
755794
** content = the new/edited wiki page content
795
+**
796
+** Requires that the user have write access solely to avoid some
797
+** potential abuse cases. It does not actually write anything.
756798
*/
757799
static void wiki_ajax_route_diff(void){
758800
const char * zPageName = P("page");
759801
Blob contentNew = empty_blob, contentOrig = empty_blob;
760802
Manifest * pParent = 0;
@@ -761,10 +803,12 @@
761803
const char * zContent = P("content");
762804
u64 diffFlags = DIFF_HTML | DIFF_NOTTOOBIG | DIFF_STRIP_EOLCR;
763805
764806
if( zPageName==0 || zPageName[0]==0 ){
765807
ajax_route_error(400,"Missing page name.");
808
+ return;
809
+ }else if(!wiki_ajax_can_write(zPageName, 0)){
766810
return;
767811
}
768812
switch(atoi(PD("sbs","0"))){
769813
case 0: diffFlags |= DIFF_LINENO; break;
770814
default: diffFlags |= DIFF_SIDEBYSIDE;
@@ -792,26 +836,33 @@
792836
/*
793837
** Ajax route handler for /wikiajax/preview.
794838
**
795839
** URL params:
796840
**
841
+** page = wiki page name. This is only needed for authorization
842
+** checking.
797843
** mimetype = the wiki page mimetype (determines rendering style)
798844
** content = the wiki page content
799845
*/
800846
static void wiki_ajax_route_preview(void){
801
- Blob content = empty_blob;
802
- const char * zMimetype = PD("mimetype","text/x-fossil-wiki");
847
+ const char * zPageName = P("page");
803848
const char * zContent = P("content");
804849
805
- if( zContent==0 ){
850
+ if(!wiki_ajax_can_write(zPageName, 0)){
851
+ return;
852
+ }else if( zContent==0 ){
806853
ajax_route_error(400,"Missing content to preview.");
807854
return;
855
+ }else{
856
+ Blob content = empty_blob;
857
+ const char * zMimetype = PD("mimetype","text/x-fossil-wiki");
858
+
859
+ blob_init(&content, zContent, -1);
860
+ cgi_set_content_type("text/html");
861
+ wiki_render_by_mimetype(&content, zMimetype);
862
+ blob_reset(&content);
808863
}
809
- blob_init(&content, zContent, -1);
810
- cgi_set_content_type("text/html");
811
- wiki_render_by_mimetype(&content, zMimetype);
812
- blob_reset(&content);
813864
}
814865
815866
/*
816867
** Ajax route handler for /wikiajax/list.
817868
**
@@ -855,16 +906,16 @@
855906
const AjaxRoute routes[] = {
856907
/* Keep these sorted by zName (for bsearch()) */
857908
{"diff", wiki_ajax_route_diff, 1, 1},
858909
{"fetch", wiki_ajax_route_fetch, 0, 0},
859910
{"list", wiki_ajax_route_list, 0, 0},
860
- {"preview", wiki_ajax_route_preview, 1, 1}
861
- /* /preview access mode: whether or not wiki-write mode is
862
- needed really depends on multiple factors. e.g. the sandbox
863
- page does not normally require more than anonymous access.
864
- TODO: set its write-mode to false and do the check manually
865
- in that route's handler.
911
+ {"preview", wiki_ajax_route_preview, 0, 1}
912
+ /* /preview access mode: whether or not wiki-write mode is needed
913
+ really depends on multiple factors. e.g. the sandbox page does
914
+ not normally require more than anonymous access. We set its
915
+ write-mode to false and do those checks manually in that route's
916
+ handler.
866917
*/
867918
};
868919
869920
if(zName==0 || zName[0]==0){
870921
ajax_route_error(400,"Missing required [route] 'name' parameter.");
@@ -892,39 +943,44 @@
892943
pRoute->xCallback();
893944
}
894945
895946
/*
896947
** Main front-end for the Ajax-based wiki editor app.
948
+**
949
+** Optional URL arguments:
950
+**
951
+** name = wiki page name. Note that Ajax-based "v2" APIs use "page"
952
+** instead because "name" has a special meaning for fossil.
953
+**
954
+** mimetype=fossil-standard mimetype. This is typically only passed in
955
+** via the new-page process.
897956
*/
898957
static void wikiedit_page_v2(void){
899958
const char *zPageName;
900
- Blob endScript = empty_blob; /* end-of-page JS code */;
959
+ const char * zMimetype = P("mimetype");
901960
int isSandbox;
961
+ int found = 0;
902962
903963
login_check_credentials();
904964
zPageName = PD("name","");
905
- /* TODO: not require a page name, and instead offer a list
906
- of pages. */
907
- /*TODO: check name only for case of new page:
908
- if( check_name(zPageName) ) return;*/
965
+ if(zPageName && *zPageName){
966
+ if( check_name(zPageName) ) return;
967
+ }
909968
isSandbox = is_sandbox(zPageName);
910969
if( isSandbox ){
911970
if( !g.perm.WrWiki ){
912971
login_needed(g.anon.WrWiki);
913972
return;
914973
}
974
+ found = 1;
915975
}else if( zPageName!=0 ){
916976
int rid = 0;
917
- int found = 0;
918977
if( !wiki_special_permission(zPageName) ){
919978
login_needed(0);
920979
return;
921980
}
922981
found = wiki_fetch_by_name(zPageName, 0, &rid, 0);
923
- if( !found ){
924
- /* TODO: set up for a new page */
925
- }
926982
if( (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){
927983
login_needed(rid ? g.anon.WrWiki : g.anon.NewWiki);
928984
return;
929985
}
930986
}
@@ -1039,16 +1095,23 @@
10391095
"Diffs will be shown here."
10401096
"</div>");
10411097
CX("</div>"/*#wikiedit-tab-diff*/);
10421098
}
10431099
1044
- if(zPageName && *zPageName){
1045
- /* Dynamically populate the editor... */
1046
- blob_appendf(&endScript, "fossil.onPageLoad(function(){");
1047
- blob_appendf(&endScript, "fossil.page.loadPage(%!j);",
1048
- zPageName);
1049
- blob_appendf(&endScript, "});\n");
1100
+ /****** TODOs (remove before merging to trunk) ******/
1101
+ {
1102
+ CX("<div id='wikiedit-tab-todos' "
1103
+ "data-tab-parent='wikiedit-tabs' "
1104
+ "data-tab-label='TODOs'"
1105
+ ">");
1106
+ CX("TODOs, in no particular order:<ul>");
1107
+ CX("<li>Saving, obviously.</li>");
1108
+ CX("<li>Figure out how to integrate new/unusaved files into "
1109
+ "the UI</li>");
1110
+ /*CX("<li></li>");*/
1111
+ CX("</ul>");
1112
+ CX("</div>");
10501113
}
10511114
10521115
style_emit_script_fossil_bootstrap(0);
10531116
append_diff_javascript(1);
10541117
style_emit_script_fetch(0);
@@ -1055,22 +1118,39 @@
10551118
style_emit_script_tabs(0)/*also emits fossil.dom*/;
10561119
style_emit_script_confirmer(0);
10571120
style_emit_script_builtin(0, "fossil.storage.js");
10581121
style_emit_script_builtin(0, "fossil.page.wikiedit.js");
10591122
1060
- if(blob_size(&endScript)>0){
1061
- style_emit_script_tag(0,0);
1062
- CX("\n(function(){\n");
1063
- CX("try{\n%b}\n"
1064
- "catch(e){"
1065
- "fossil.error(e); console.error('Exception:',e);"
1066
- "}\n",
1067
- &endScript);
1068
- CX("})();");
1069
- style_emit_script_tag(1,0);
1070
- }
1071
- blob_reset(&endScript);
1123
+ /* Dynamically populate the editor... */
1124
+ style_emit_script_tag(0,0);
1125
+ CX("\nfossil.onPageLoad(function(){\n");
1126
+ CX("try{\n");
1127
+ if(zMimetype && *zMimetype){
1128
+ CX("fossil.page.selectMimetype(%!j);\n",
1129
+ zMimetype);
1130
+ }
1131
+ if(found){
1132
+ CX("fossil.page.loadPage(%!j);\n", zPageName);
1133
+ }else if(zPageName && *zPageName){
1134
+ /*
1135
+ FIXME: we don't yet have a good way to integrate a
1136
+ not-yet-existing/new/unsaved page into the UI.
1137
+ */
1138
+ CX("fossil.page.config.newPage = {"
1139
+ "\"name\": %!j, \"mimetype\": %!j"
1140
+ "};\n",
1141
+ zPageName,
1142
+ zMimetype ? zMimetype : "text/x-fossil-wiki");
1143
+ CX("fossil.error('You are editing a new, "
1144
+ "unsaved page:',%!j);\n",
1145
+ zPageName);
1146
+ }
1147
+ CX("}catch(e){"
1148
+ "fossil.error(e); console.error('Exception:',e);"
1149
+ "}\n");
1150
+ CX("});\n"/*fossil.onPageLoad()*/);
1151
+ style_emit_script_tag(1,0);
10721152
style_footer();
10731153
}
10741154
10751155
/*
10761156
** WEBPAGE: wikiedit
10771157
--- src/wiki.c
+++ src/wiki.c
@@ -680,10 +680,49 @@
680 }
681 *ppWiki = pWiki;
682 }
683 return 1;
684 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
685
686 /*
687 ** Ajax route handler for /wikiajax/fetch.
688 **
689 ** URL params:
@@ -695,11 +734,11 @@
695 **
696 ** { name: "page name",
697 ** type: "normal" | "tag" | "checkin" | "branch" | "sandbox",
698 ** mimetype: "mime type",
699 ** version: UUID string or null for a sandbox page,
700 ** parent: "parent uuid" or null,
701 ** content: "page content"
702 ** }
703 */
704 static void wiki_ajax_route_fetch(void){
705 const char * zPageName = P("page");
@@ -751,10 +790,13 @@
751 **
752 ** URL params:
753 **
754 ** page = the wiki page name
755 ** content = the new/edited wiki page content
 
 
 
756 */
757 static void wiki_ajax_route_diff(void){
758 const char * zPageName = P("page");
759 Blob contentNew = empty_blob, contentOrig = empty_blob;
760 Manifest * pParent = 0;
@@ -761,10 +803,12 @@
761 const char * zContent = P("content");
762 u64 diffFlags = DIFF_HTML | DIFF_NOTTOOBIG | DIFF_STRIP_EOLCR;
763
764 if( zPageName==0 || zPageName[0]==0 ){
765 ajax_route_error(400,"Missing page name.");
 
 
766 return;
767 }
768 switch(atoi(PD("sbs","0"))){
769 case 0: diffFlags |= DIFF_LINENO; break;
770 default: diffFlags |= DIFF_SIDEBYSIDE;
@@ -792,26 +836,33 @@
792 /*
793 ** Ajax route handler for /wikiajax/preview.
794 **
795 ** URL params:
796 **
 
 
797 ** mimetype = the wiki page mimetype (determines rendering style)
798 ** content = the wiki page content
799 */
800 static void wiki_ajax_route_preview(void){
801 Blob content = empty_blob;
802 const char * zMimetype = PD("mimetype","text/x-fossil-wiki");
803 const char * zContent = P("content");
804
805 if( zContent==0 ){
 
 
806 ajax_route_error(400,"Missing content to preview.");
807 return;
 
 
 
 
 
 
 
 
808 }
809 blob_init(&content, zContent, -1);
810 cgi_set_content_type("text/html");
811 wiki_render_by_mimetype(&content, zMimetype);
812 blob_reset(&content);
813 }
814
815 /*
816 ** Ajax route handler for /wikiajax/list.
817 **
@@ -855,16 +906,16 @@
855 const AjaxRoute routes[] = {
856 /* Keep these sorted by zName (for bsearch()) */
857 {"diff", wiki_ajax_route_diff, 1, 1},
858 {"fetch", wiki_ajax_route_fetch, 0, 0},
859 {"list", wiki_ajax_route_list, 0, 0},
860 {"preview", wiki_ajax_route_preview, 1, 1}
861 /* /preview access mode: whether or not wiki-write mode is
862 needed really depends on multiple factors. e.g. the sandbox
863 page does not normally require more than anonymous access.
864 TODO: set its write-mode to false and do the check manually
865 in that route's handler.
866 */
867 };
868
869 if(zName==0 || zName[0]==0){
870 ajax_route_error(400,"Missing required [route] 'name' parameter.");
@@ -892,39 +943,44 @@
892 pRoute->xCallback();
893 }
894
895 /*
896 ** Main front-end for the Ajax-based wiki editor app.
 
 
 
 
 
 
 
 
897 */
898 static void wikiedit_page_v2(void){
899 const char *zPageName;
900 Blob endScript = empty_blob; /* end-of-page JS code */;
901 int isSandbox;
 
902
903 login_check_credentials();
904 zPageName = PD("name","");
905 /* TODO: not require a page name, and instead offer a list
906 of pages. */
907 /*TODO: check name only for case of new page:
908 if( check_name(zPageName) ) return;*/
909 isSandbox = is_sandbox(zPageName);
910 if( isSandbox ){
911 if( !g.perm.WrWiki ){
912 login_needed(g.anon.WrWiki);
913 return;
914 }
 
915 }else if( zPageName!=0 ){
916 int rid = 0;
917 int found = 0;
918 if( !wiki_special_permission(zPageName) ){
919 login_needed(0);
920 return;
921 }
922 found = wiki_fetch_by_name(zPageName, 0, &rid, 0);
923 if( !found ){
924 /* TODO: set up for a new page */
925 }
926 if( (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){
927 login_needed(rid ? g.anon.WrWiki : g.anon.NewWiki);
928 return;
929 }
930 }
@@ -1039,16 +1095,23 @@
1039 "Diffs will be shown here."
1040 "</div>");
1041 CX("</div>"/*#wikiedit-tab-diff*/);
1042 }
1043
1044 if(zPageName && *zPageName){
1045 /* Dynamically populate the editor... */
1046 blob_appendf(&endScript, "fossil.onPageLoad(function(){");
1047 blob_appendf(&endScript, "fossil.page.loadPage(%!j);",
1048 zPageName);
1049 blob_appendf(&endScript, "});\n");
 
 
 
 
 
 
 
1050 }
1051
1052 style_emit_script_fossil_bootstrap(0);
1053 append_diff_javascript(1);
1054 style_emit_script_fetch(0);
@@ -1055,22 +1118,39 @@
1055 style_emit_script_tabs(0)/*also emits fossil.dom*/;
1056 style_emit_script_confirmer(0);
1057 style_emit_script_builtin(0, "fossil.storage.js");
1058 style_emit_script_builtin(0, "fossil.page.wikiedit.js");
1059
1060 if(blob_size(&endScript)>0){
1061 style_emit_script_tag(0,0);
1062 CX("\n(function(){\n");
1063 CX("try{\n%b}\n"
1064 "catch(e){"
1065 "fossil.error(e); console.error('Exception:',e);"
1066 "}\n",
1067 &endScript);
1068 CX("})();");
1069 style_emit_script_tag(1,0);
1070 }
1071 blob_reset(&endScript);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1072 style_footer();
1073 }
1074
1075 /*
1076 ** WEBPAGE: wikiedit
1077
--- src/wiki.c
+++ src/wiki.c
@@ -680,10 +680,49 @@
680 }
681 *ppWiki = pWiki;
682 }
683 return 1;
684 }
685
686 /*
687 ** Determines whether the wiki page with the given name can be edited
688 ** by the current user. If not, an AJAX error is queued and false is
689 ** returned, else true is returned. A NULL, empty, or malformed name
690 ** is considered non-writable.
691 **
692 ** If pRid is not NULL then this function writes the page's rid to
693 ** *pRid (whether or not access is granted).
694 **
695 ** Note that the sandbox is a special case: it is a pseudo-page with
696 ** no rid and this API does not allow anyone to actually save the
697 ** sandbox page, but it is reported as writable here (with rid 0).
698 */
699 static int wiki_ajax_can_write(const char *zPageName, int * pRid){
700 int rid;
701 const char * zMsg = 0;
702
703 if(pRid) *pRid = 0;
704 if(!zPageName || !*zPageName
705 || !wiki_name_is_wellformed((unsigned const char *)zPageName)){
706 return 0;
707 }
708 if(is_sandbox(zPageName)) return 1;
709 wiki_fetch_by_name(zPageName, 0, &rid, 0);
710 if(pRid) *pRid = rid;
711 if(!wiki_special_permission(zPageName)) return 0;
712 if( (rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki) ){
713 return 3;
714 }else if(rid && !g.perm.WrWiki){
715 zMsg = "Requires wiki-write permissions.";
716 }else if(!rid && !g.perm.NewWiki){
717 zMsg = "Requires new-wiki permissions.";
718 }else{
719 assert(!"Can't happen?");
720 }
721 ajax_route_error(403, "%s", zMsg);
722 return 0;
723 }
724
725 /*
726 ** Ajax route handler for /wikiajax/fetch.
727 **
728 ** URL params:
@@ -695,11 +734,11 @@
734 **
735 ** { name: "page name",
736 ** type: "normal" | "tag" | "checkin" | "branch" | "sandbox",
737 ** mimetype: "mime type",
738 ** version: UUID string or null for a sandbox page,
739 ** parent: "parent uuid" or null if no parent,
740 ** content: "page content"
741 ** }
742 */
743 static void wiki_ajax_route_fetch(void){
744 const char * zPageName = P("page");
@@ -751,10 +790,13 @@
790 **
791 ** URL params:
792 **
793 ** page = the wiki page name
794 ** content = the new/edited wiki page content
795 **
796 ** Requires that the user have write access solely to avoid some
797 ** potential abuse cases. It does not actually write anything.
798 */
799 static void wiki_ajax_route_diff(void){
800 const char * zPageName = P("page");
801 Blob contentNew = empty_blob, contentOrig = empty_blob;
802 Manifest * pParent = 0;
@@ -761,10 +803,12 @@
803 const char * zContent = P("content");
804 u64 diffFlags = DIFF_HTML | DIFF_NOTTOOBIG | DIFF_STRIP_EOLCR;
805
806 if( zPageName==0 || zPageName[0]==0 ){
807 ajax_route_error(400,"Missing page name.");
808 return;
809 }else if(!wiki_ajax_can_write(zPageName, 0)){
810 return;
811 }
812 switch(atoi(PD("sbs","0"))){
813 case 0: diffFlags |= DIFF_LINENO; break;
814 default: diffFlags |= DIFF_SIDEBYSIDE;
@@ -792,26 +836,33 @@
836 /*
837 ** Ajax route handler for /wikiajax/preview.
838 **
839 ** URL params:
840 **
841 ** page = wiki page name. This is only needed for authorization
842 ** checking.
843 ** mimetype = the wiki page mimetype (determines rendering style)
844 ** content = the wiki page content
845 */
846 static void wiki_ajax_route_preview(void){
847 const char * zPageName = P("page");
 
848 const char * zContent = P("content");
849
850 if(!wiki_ajax_can_write(zPageName, 0)){
851 return;
852 }else if( zContent==0 ){
853 ajax_route_error(400,"Missing content to preview.");
854 return;
855 }else{
856 Blob content = empty_blob;
857 const char * zMimetype = PD("mimetype","text/x-fossil-wiki");
858
859 blob_init(&content, zContent, -1);
860 cgi_set_content_type("text/html");
861 wiki_render_by_mimetype(&content, zMimetype);
862 blob_reset(&content);
863 }
 
 
 
 
864 }
865
866 /*
867 ** Ajax route handler for /wikiajax/list.
868 **
@@ -855,16 +906,16 @@
906 const AjaxRoute routes[] = {
907 /* Keep these sorted by zName (for bsearch()) */
908 {"diff", wiki_ajax_route_diff, 1, 1},
909 {"fetch", wiki_ajax_route_fetch, 0, 0},
910 {"list", wiki_ajax_route_list, 0, 0},
911 {"preview", wiki_ajax_route_preview, 0, 1}
912 /* /preview access mode: whether or not wiki-write mode is needed
913 really depends on multiple factors. e.g. the sandbox page does
914 not normally require more than anonymous access. We set its
915 write-mode to false and do those checks manually in that route's
916 handler.
917 */
918 };
919
920 if(zName==0 || zName[0]==0){
921 ajax_route_error(400,"Missing required [route] 'name' parameter.");
@@ -892,39 +943,44 @@
943 pRoute->xCallback();
944 }
945
946 /*
947 ** Main front-end for the Ajax-based wiki editor app.
948 **
949 ** Optional URL arguments:
950 **
951 ** name = wiki page name. Note that Ajax-based "v2" APIs use "page"
952 ** instead because "name" has a special meaning for fossil.
953 **
954 ** mimetype=fossil-standard mimetype. This is typically only passed in
955 ** via the new-page process.
956 */
957 static void wikiedit_page_v2(void){
958 const char *zPageName;
959 const char * zMimetype = P("mimetype");
960 int isSandbox;
961 int found = 0;
962
963 login_check_credentials();
964 zPageName = PD("name","");
965 if(zPageName && *zPageName){
966 if( check_name(zPageName) ) return;
967 }
 
968 isSandbox = is_sandbox(zPageName);
969 if( isSandbox ){
970 if( !g.perm.WrWiki ){
971 login_needed(g.anon.WrWiki);
972 return;
973 }
974 found = 1;
975 }else if( zPageName!=0 ){
976 int rid = 0;
 
977 if( !wiki_special_permission(zPageName) ){
978 login_needed(0);
979 return;
980 }
981 found = wiki_fetch_by_name(zPageName, 0, &rid, 0);
 
 
 
982 if( (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){
983 login_needed(rid ? g.anon.WrWiki : g.anon.NewWiki);
984 return;
985 }
986 }
@@ -1039,16 +1095,23 @@
1095 "Diffs will be shown here."
1096 "</div>");
1097 CX("</div>"/*#wikiedit-tab-diff*/);
1098 }
1099
1100 /****** TODOs (remove before merging to trunk) ******/
1101 {
1102 CX("<div id='wikiedit-tab-todos' "
1103 "data-tab-parent='wikiedit-tabs' "
1104 "data-tab-label='TODOs'"
1105 ">");
1106 CX("TODOs, in no particular order:<ul>");
1107 CX("<li>Saving, obviously.</li>");
1108 CX("<li>Figure out how to integrate new/unusaved files into "
1109 "the UI</li>");
1110 /*CX("<li></li>");*/
1111 CX("</ul>");
1112 CX("</div>");
1113 }
1114
1115 style_emit_script_fossil_bootstrap(0);
1116 append_diff_javascript(1);
1117 style_emit_script_fetch(0);
@@ -1055,22 +1118,39 @@
1118 style_emit_script_tabs(0)/*also emits fossil.dom*/;
1119 style_emit_script_confirmer(0);
1120 style_emit_script_builtin(0, "fossil.storage.js");
1121 style_emit_script_builtin(0, "fossil.page.wikiedit.js");
1122
1123 /* Dynamically populate the editor... */
1124 style_emit_script_tag(0,0);
1125 CX("\nfossil.onPageLoad(function(){\n");
1126 CX("try{\n");
1127 if(zMimetype && *zMimetype){
1128 CX("fossil.page.selectMimetype(%!j);\n",
1129 zMimetype);
1130 }
1131 if(found){
1132 CX("fossil.page.loadPage(%!j);\n", zPageName);
1133 }else if(zPageName && *zPageName){
1134 /*
1135 FIXME: we don't yet have a good way to integrate a
1136 not-yet-existing/new/unsaved page into the UI.
1137 */
1138 CX("fossil.page.config.newPage = {"
1139 "\"name\": %!j, \"mimetype\": %!j"
1140 "};\n",
1141 zPageName,
1142 zMimetype ? zMimetype : "text/x-fossil-wiki");
1143 CX("fossil.error('You are editing a new, "
1144 "unsaved page:',%!j);\n",
1145 zPageName);
1146 }
1147 CX("}catch(e){"
1148 "fossil.error(e); console.error('Exception:',e);"
1149 "}\n");
1150 CX("});\n"/*fossil.onPageLoad()*/);
1151 style_emit_script_tag(1,0);
1152 style_footer();
1153 }
1154
1155 /*
1156 ** WEBPAGE: wikiedit
1157

Keyboard Shortcuts

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