Fossil SCM

Initial bits for the ajax-powered wiki editor. There's still much to do here, but most of the basics are in place.

stephan 2020-07-29 20:19 trunk
Commit 20636c936f865945083ba344d68c02ba36f43c14a44ae6cce1bcb089454dc883
+3 -1
--- src/ajax.c
+++ src/ajax.c
@@ -323,10 +323,11 @@
323323
if(zRenderMode!=0){
324324
cgi_printf_header("x-ajax-render-mode: %s\r\n", zRenderMode);
325325
}
326326
}
327327
328
+#if INTERFACE
328329
/*
329330
** Internal mapping of ajax sub-route names to various metadata.
330331
*/
331332
struct AjaxRoute {
332333
const char *zName; /* Name part of the route after "ajax/" */
@@ -334,16 +335,17 @@
334335
int bWriteMode; /* True if requires write mode */
335336
int bPost; /* True if requires POST (i.e. CSRF
336337
** verification) */
337338
};
338339
typedef struct AjaxRoute AjaxRoute;
340
+#endif /*INTERFACE*/
339341
340342
/*
341343
** Comparison function for bsearch() for searching an AjaxRoute
342344
** list for a matching name.
343345
*/
344
-static int cmp_ajax_route_name(const void *a, const void *b){
346
+int cmp_ajax_route_name(const void *a, const void *b){
345347
const AjaxRoute * rA = (const AjaxRoute*)a;
346348
const AjaxRoute * rB = (const AjaxRoute*)b;
347349
return fossil_strcmp(rA->zName, rB->zName);
348350
}
349351
350352
--- src/ajax.c
+++ src/ajax.c
@@ -323,10 +323,11 @@
323 if(zRenderMode!=0){
324 cgi_printf_header("x-ajax-render-mode: %s\r\n", zRenderMode);
325 }
326 }
327
 
328 /*
329 ** Internal mapping of ajax sub-route names to various metadata.
330 */
331 struct AjaxRoute {
332 const char *zName; /* Name part of the route after "ajax/" */
@@ -334,16 +335,17 @@
334 int bWriteMode; /* True if requires write mode */
335 int bPost; /* True if requires POST (i.e. CSRF
336 ** verification) */
337 };
338 typedef struct AjaxRoute AjaxRoute;
 
339
340 /*
341 ** Comparison function for bsearch() for searching an AjaxRoute
342 ** list for a matching name.
343 */
344 static int cmp_ajax_route_name(const void *a, const void *b){
345 const AjaxRoute * rA = (const AjaxRoute*)a;
346 const AjaxRoute * rB = (const AjaxRoute*)b;
347 return fossil_strcmp(rA->zName, rB->zName);
348 }
349
350
--- src/ajax.c
+++ src/ajax.c
@@ -323,10 +323,11 @@
323 if(zRenderMode!=0){
324 cgi_printf_header("x-ajax-render-mode: %s\r\n", zRenderMode);
325 }
326 }
327
328 #if INTERFACE
329 /*
330 ** Internal mapping of ajax sub-route names to various metadata.
331 */
332 struct AjaxRoute {
333 const char *zName; /* Name part of the route after "ajax/" */
@@ -334,16 +335,17 @@
335 int bWriteMode; /* True if requires write mode */
336 int bPost; /* True if requires POST (i.e. CSRF
337 ** verification) */
338 };
339 typedef struct AjaxRoute AjaxRoute;
340 #endif /*INTERFACE*/
341
342 /*
343 ** Comparison function for bsearch() for searching an AjaxRoute
344 ** list for a matching name.
345 */
346 int cmp_ajax_route_name(const void *a, const void *b){
347 const AjaxRoute * rA = (const AjaxRoute*)a;
348 const AjaxRoute * rB = (const AjaxRoute*)b;
349 return fossil_strcmp(rA->zName, rB->zName);
350 }
351
352
--- src/default.css
+++ src/default.css
@@ -919,5 +919,178 @@
919919
}
920920
img {
921921
max-width: 100%;
922922
height: auto;
923923
}
924
+
925
+/**
926
+ .tab-xxx: styles for fossil.tabs.js.
927
+*/
928
+.tab-container {
929
+ width: 100%;
930
+ display: flex;
931
+ flex-direction: column;
932
+ align-items: stretch;
933
+}
934
+.tab-container > #fossil-status-bar {
935
+ margin-top: 0;
936
+}
937
+.tab-container > .tabs {
938
+ padding: 0.25em;
939
+ margin: 0;
940
+ display: flex;
941
+ flex-direction: column;
942
+ border-width: 1px;
943
+ border-style: outset;
944
+ border-color: inherit;
945
+}
946
+.tab-container > .tabs > .tab-panel {
947
+ align-self: stretch;
948
+ flex: 10 1 auto;
949
+ display: block;
950
+}
951
+.tab-container > .tab-bar {
952
+ display: flex;
953
+ flex-direction: row;
954
+ flex: 1 10 auto;
955
+ align-self: stretch;
956
+ flex-wrap: wrap;
957
+}
958
+.tab-container > .tab-bar > .tab-button {
959
+ display: inline-block;
960
+ border-radius: 0.5em 0.5em 0 0;
961
+ margin: 0 0.1em;
962
+ padding: 0.25em 0.75em;
963
+ align-self: baseline;
964
+ border-color: inherit;
965
+ border-width: 1px;
966
+ border-bottom: none;
967
+ border-top-style: inset;
968
+ border-left-style: inset;
969
+ border-right-style: inset;
970
+ cursor: pointer;
971
+ opacity: 0.6;
972
+}
973
+.tab-container > .tab-bar > .tab-button.selected {
974
+ text-decoration: underline;
975
+ opacity: 1.0;
976
+ border-top-style: outset;
977
+ border-left-style: outset;
978
+ border-right-style: outset;
979
+}
980
+
981
+/**
982
+ The flex-xxx classes can be used to create basic flexbox layouts
983
+ through the application of classes to the containing/contained
984
+ objects.
985
+*/
986
+.flex-container {
987
+ display: flex;
988
+}
989
+.flex-container.flex-row {
990
+ flex-direction: row;
991
+ flex-wrap: wrap;
992
+ justify-content: center;
993
+ align-items: center;
994
+}
995
+.flex-container .flex-grow {
996
+ flex-grow: 10;
997
+ flex-shrink: 0;
998
+}
999
+.flex-container .flex-shrink {
1000
+ flex-grow: 0;
1001
+ flex-shrink: 10;
1002
+}
1003
+.flex-container.flex-row.stretch {
1004
+ flex-wrap: wrap;
1005
+ align-items: baseline;
1006
+ justify-content: stretch;
1007
+ margin: 0;
1008
+}
1009
+.flex-container.flex-column {
1010
+ flex-direction: column;
1011
+ flex-wrap: wrap;
1012
+ justify-content: center;
1013
+ align-items: center;
1014
+}
1015
+.flex-container.flex-column.stretch {
1016
+ align-items: stretch;
1017
+ margin: 0;
1018
+}
1019
+.flex-container.child-gap-small > * {
1020
+ margin: 0.25em;
1021
+}
1022
+#fossil-status-bar {
1023
+ display: block;
1024
+ font-family: monospace;
1025
+ border-width: 1px;
1026
+ border-style: inset;
1027
+ border-color: inherit;
1028
+ min-height: 1.5em;
1029
+ font-size: 1.2em;
1030
+ padding: 0.2em;
1031
+ margin: 0.25em 0;
1032
+ flex: 0 0 auto;
1033
+}
1034
+.font-size-100 {
1035
+ font-size: 100%;
1036
+}
1037
+.font-size-125 {
1038
+ font-size: 125%;
1039
+}
1040
+.font-size-150 {
1041
+ font-size: 150%;
1042
+}
1043
+.font-size-175 {
1044
+ font-size: 175%;
1045
+}
1046
+.font-size-200 {
1047
+ font-size: 200%;
1048
+}
1049
+
1050
+/**
1051
+ .input-with-label is intended to be a wrapper element which
1052
+ contain both a LABEL tag and an INPUT or SELECT control.
1053
+ The wrapper is "necessary", as opposed to placing the INPUT
1054
+ in the LABEL, so that we can include multiple INPUT
1055
+ elements (e.g. a set of radio buttons).
1056
+*/
1057
+.input-with-label {
1058
+ border: 1px inset #808080;
1059
+ border-radius: 0.5em;
1060
+ padding: 0.25em 0.4em;
1061
+ margin: 0 0.5em;
1062
+ display: inline-block;
1063
+ cursor: default;
1064
+}
1065
+.input-with-label > * {
1066
+ vertical-align: middle;
1067
+}
1068
+.input-with-label > label {
1069
+ display: inline; /* some skins set label display to block! */
1070
+}
1071
+.input-with-label > input {
1072
+ margin: 0;
1073
+}
1074
+.input-with-label > button {
1075
+ margin: 0;
1076
+}
1077
+.input-with-label > select {
1078
+ margin: 0;
1079
+}
1080
+.input-with-label > input[type=text] {
1081
+ margin: 0;
1082
+}
1083
+.input-with-label > textarea {
1084
+ margin: 0;
1085
+}
1086
+.input-with-label > input[type=checkbox] {
1087
+ vertical-align: sub;
1088
+}
1089
+.input-with-label > input[type=radio] {
1090
+ vertical-align: sub;
1091
+}
1092
+.input-with-label > label {
1093
+ font-weight: initial;
1094
+ margin: 0 0.25em 0 0.25em;
1095
+ vertical-align: middle;
1096
+}
9241097
--- src/default.css
+++ src/default.css
@@ -919,5 +919,178 @@
919 }
920 img {
921 max-width: 100%;
922 height: auto;
923 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
924
--- src/default.css
+++ src/default.css
@@ -919,5 +919,178 @@
919 }
920 img {
921 max-width: 100%;
922 height: auto;
923 }
924
925 /**
926 .tab-xxx: styles for fossil.tabs.js.
927 */
928 .tab-container {
929 width: 100%;
930 display: flex;
931 flex-direction: column;
932 align-items: stretch;
933 }
934 .tab-container > #fossil-status-bar {
935 margin-top: 0;
936 }
937 .tab-container > .tabs {
938 padding: 0.25em;
939 margin: 0;
940 display: flex;
941 flex-direction: column;
942 border-width: 1px;
943 border-style: outset;
944 border-color: inherit;
945 }
946 .tab-container > .tabs > .tab-panel {
947 align-self: stretch;
948 flex: 10 1 auto;
949 display: block;
950 }
951 .tab-container > .tab-bar {
952 display: flex;
953 flex-direction: row;
954 flex: 1 10 auto;
955 align-self: stretch;
956 flex-wrap: wrap;
957 }
958 .tab-container > .tab-bar > .tab-button {
959 display: inline-block;
960 border-radius: 0.5em 0.5em 0 0;
961 margin: 0 0.1em;
962 padding: 0.25em 0.75em;
963 align-self: baseline;
964 border-color: inherit;
965 border-width: 1px;
966 border-bottom: none;
967 border-top-style: inset;
968 border-left-style: inset;
969 border-right-style: inset;
970 cursor: pointer;
971 opacity: 0.6;
972 }
973 .tab-container > .tab-bar > .tab-button.selected {
974 text-decoration: underline;
975 opacity: 1.0;
976 border-top-style: outset;
977 border-left-style: outset;
978 border-right-style: outset;
979 }
980
981 /**
982 The flex-xxx classes can be used to create basic flexbox layouts
983 through the application of classes to the containing/contained
984 objects.
985 */
986 .flex-container {
987 display: flex;
988 }
989 .flex-container.flex-row {
990 flex-direction: row;
991 flex-wrap: wrap;
992 justify-content: center;
993 align-items: center;
994 }
995 .flex-container .flex-grow {
996 flex-grow: 10;
997 flex-shrink: 0;
998 }
999 .flex-container .flex-shrink {
1000 flex-grow: 0;
1001 flex-shrink: 10;
1002 }
1003 .flex-container.flex-row.stretch {
1004 flex-wrap: wrap;
1005 align-items: baseline;
1006 justify-content: stretch;
1007 margin: 0;
1008 }
1009 .flex-container.flex-column {
1010 flex-direction: column;
1011 flex-wrap: wrap;
1012 justify-content: center;
1013 align-items: center;
1014 }
1015 .flex-container.flex-column.stretch {
1016 align-items: stretch;
1017 margin: 0;
1018 }
1019 .flex-container.child-gap-small > * {
1020 margin: 0.25em;
1021 }
1022 #fossil-status-bar {
1023 display: block;
1024 font-family: monospace;
1025 border-width: 1px;
1026 border-style: inset;
1027 border-color: inherit;
1028 min-height: 1.5em;
1029 font-size: 1.2em;
1030 padding: 0.2em;
1031 margin: 0.25em 0;
1032 flex: 0 0 auto;
1033 }
1034 .font-size-100 {
1035 font-size: 100%;
1036 }
1037 .font-size-125 {
1038 font-size: 125%;
1039 }
1040 .font-size-150 {
1041 font-size: 150%;
1042 }
1043 .font-size-175 {
1044 font-size: 175%;
1045 }
1046 .font-size-200 {
1047 font-size: 200%;
1048 }
1049
1050 /**
1051 .input-with-label is intended to be a wrapper element which
1052 contain both a LABEL tag and an INPUT or SELECT control.
1053 The wrapper is "necessary", as opposed to placing the INPUT
1054 in the LABEL, so that we can include multiple INPUT
1055 elements (e.g. a set of radio buttons).
1056 */
1057 .input-with-label {
1058 border: 1px inset #808080;
1059 border-radius: 0.5em;
1060 padding: 0.25em 0.4em;
1061 margin: 0 0.5em;
1062 display: inline-block;
1063 cursor: default;
1064 }
1065 .input-with-label > * {
1066 vertical-align: middle;
1067 }
1068 .input-with-label > label {
1069 display: inline; /* some skins set label display to block! */
1070 }
1071 .input-with-label > input {
1072 margin: 0;
1073 }
1074 .input-with-label > button {
1075 margin: 0;
1076 }
1077 .input-with-label > select {
1078 margin: 0;
1079 }
1080 .input-with-label > input[type=text] {
1081 margin: 0;
1082 }
1083 .input-with-label > textarea {
1084 margin: 0;
1085 }
1086 .input-with-label > input[type=checkbox] {
1087 vertical-align: sub;
1088 }
1089 .input-with-label > input[type=radio] {
1090 vertical-align: sub;
1091 }
1092 .input-with-label > label {
1093 font-weight: initial;
1094 margin: 0 0.25em 0 0.25em;
1095 vertical-align: middle;
1096 }
1097
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -149,27 +149,12 @@
149149
/** Returns the index object, fetching it from the stash or creating
150150
it anew on the first call. */
151151
getIndex: function(){
152152
if(!this.index){
153153
this.index = F.storage.getJSON(
154
- this.keys.index, undefined
154
+ this.keys.index, {}
155155
);
156
- if(!this.index){
157
- /*check for and remove/replace older name. This whole block
158
- can be removed once the test phase is done (don't want to
159
- invalidate the testers' edits on the test server). When
160
- doing so, be sure to replace undefined in the above
161
- getJSON() call with {}. */
162
- const oldName = F.page.name+':index';
163
- this.index = F.storage.getJSON(oldName,undefined);
164
- if(this.index){
165
- F.storage.remove(oldName);
166
- this.storeIndex();
167
- }else{
168
- this.index = {};
169
- }
170
- }
171156
}
172157
return this.index;
173158
},
174159
_fireStashEvent: function(){
175160
if(this._disableNextEvent) delete this._disableNextEvent;
176161
177162
ADDED src/fossil.page.wikiedit.js
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -149,27 +149,12 @@
149 /** Returns the index object, fetching it from the stash or creating
150 it anew on the first call. */
151 getIndex: function(){
152 if(!this.index){
153 this.index = F.storage.getJSON(
154 this.keys.index, undefined
155 );
156 if(!this.index){
157 /*check for and remove/replace older name. This whole block
158 can be removed once the test phase is done (don't want to
159 invalidate the testers' edits on the test server). When
160 doing so, be sure to replace undefined in the above
161 getJSON() call with {}. */
162 const oldName = F.page.name+':index';
163 this.index = F.storage.getJSON(oldName,undefined);
164 if(this.index){
165 F.storage.remove(oldName);
166 this.storeIndex();
167 }else{
168 this.index = {};
169 }
170 }
171 }
172 return this.index;
173 },
174 _fireStashEvent: function(){
175 if(this._disableNextEvent) delete this._disableNextEvent;
176
177 DDED src/fossil.page.wikiedit.js
--- src/fossil.page.fileedit.js
+++ src/fossil.page.fileedit.js
@@ -149,27 +149,12 @@
149 /** Returns the index object, fetching it from the stash or creating
150 it anew on the first call. */
151 getIndex: function(){
152 if(!this.index){
153 this.index = F.storage.getJSON(
154 this.keys.index, {}
155 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156 }
157 return this.index;
158 },
159 _fireStashEvent: function(){
160 if(this._disableNextEvent) delete this._disableNextEvent;
161
162 DDED src/fossil.page.wikiedit.js
--- a/src/fossil.page.wikiedit.js
+++ b/src/fossil.page.wikiedit.js
@@ -0,0 +1,26 @@
1
+const ndx =
2
+function click(){
3
+ if(!click.sorticase){
4
+ click } onload:;
5
+ sel);
6
+ ey){ });
7
+ .sort(click.sorticase)}btn.clickthis.e.selec'option'
8
+ btn* = local edits exist")+ = new/unsaved page")nst ndx =
9
+function click(){
10
+ if(!click.sorticase){
11
+ click } onload:;
12
+ sel);
13
+ ey){ });
14
+ .sort(click.sorticase)}btn.click Floaded':) return;t ndx =
15
+function click(){
16
+ {
17
+ if(!click.sorticas ;
18
+if(Filecheckincontent: string=
19
+function click(){
20
+ const ndx =
21
+function click(){
22
+ edit,
23
+mimetype: winfo.mimetype,
24
+ i !!winfo.isSandboxH@56G,7x@1Vh,o@1cq,Xx@1eX,Tt@2Yd,N:#select-mimetype select4n@30q,6:contenK@35j,9_@36i,SC@3Ge,97@3lL,4:edit2K@3uS,Y:non-dry-run commit
25
+ 'wikiedit2C@3x0,96@44D,4:edit3M@4DJ,4:editRr@4Ge,d:edit-file-loaded' event, passing it
26
+ EH@4hz,8:!!r.typeL@4xB,M@63C,23@4yS,54@4~_,PR@55l,N:dispatchEvent('wikieditIT@5W4,S:filename',this.winfo.filenamK@5RU,R:heckin', this.winfo.checkinI@5nF,2n@5o8,H:ajax/wiki-diff',{7@Vx,R@5TS,CG@5rh,2j@63f,m@66a,1:}g@4Pz,2~@68d,Cs@6CD,3mLC5b;
--- a/src/fossil.page.wikiedit.js
+++ b/src/fossil.page.wikiedit.js
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/fossil.page.wikiedit.js
+++ b/src/fossil.page.wikiedit.js
@@ -0,0 +1,26 @@
1 const ndx =
2 function click(){
3 if(!click.sorticase){
4 click } onload:;
5 sel);
6 ey){ });
7 .sort(click.sorticase)}btn.clickthis.e.selec'option'
8 btn* = local edits exist")+ = new/unsaved page")nst ndx =
9 function click(){
10 if(!click.sorticase){
11 click } onload:;
12 sel);
13 ey){ });
14 .sort(click.sorticase)}btn.click Floaded':) return;t ndx =
15 function click(){
16 {
17 if(!click.sorticas ;
18 if(Filecheckincontent: string=
19 function click(){
20 const ndx =
21 function click(){
22 edit,
23 mimetype: winfo.mimetype,
24 i !!winfo.isSandboxH@56G,7x@1Vh,o@1cq,Xx@1eX,Tt@2Yd,N:#select-mimetype select4n@30q,6:contenK@35j,9_@36i,SC@3Ge,97@3lL,4:edit2K@3uS,Y:non-dry-run commit
25 'wikiedit2C@3x0,96@44D,4:edit3M@4DJ,4:editRr@4Ge,d:edit-file-loaded' event, passing it
26 EH@4hz,8:!!r.typeL@4xB,M@63C,23@4yS,54@4~_,PR@55l,N:dispatchEvent('wikieditIT@5W4,S:filename',this.winfo.filenamK@5RU,R:heckin', this.winfo.checkinI@5nF,2n@5o8,H:ajax/wiki-diff',{7@Vx,R@5TS,CG@5rh,2j@63f,m@66a,1:}g@4Pz,2~@68d,Cs@6CD,3mLC5b;
--- src/main.mk
+++ src/main.mk
@@ -228,10 +228,11 @@
228228
$(SRCDIR)/fossil.confirmer.js \
229229
$(SRCDIR)/fossil.dom.js \
230230
$(SRCDIR)/fossil.fetch.js \
231231
$(SRCDIR)/fossil.page.fileedit.js \
232232
$(SRCDIR)/fossil.page.forumpost.js \
233
+ $(SRCDIR)/fossil.page.wikiedit.js \
233234
$(SRCDIR)/fossil.storage.js \
234235
$(SRCDIR)/fossil.tabs.js \
235236
$(SRCDIR)/graph.js \
236237
$(SRCDIR)/href.js \
237238
$(SRCDIR)/login.js \
@@ -257,10 +258,11 @@
257258
$(SRCDIR)/sounds/d.wav \
258259
$(SRCDIR)/sounds/e.wav \
259260
$(SRCDIR)/sounds/f.wav \
260261
$(SRCDIR)/style.admin_log.css \
261262
$(SRCDIR)/style.fileedit.css \
263
+ $(SRCDIR)/style.wikiedit.css \
262264
$(SRCDIR)/tree.js \
263265
$(SRCDIR)/useredit.js \
264266
$(SRCDIR)/wiki.wiki
265267
266268
TRANS_SRC = \
267269
--- src/main.mk
+++ src/main.mk
@@ -228,10 +228,11 @@
228 $(SRCDIR)/fossil.confirmer.js \
229 $(SRCDIR)/fossil.dom.js \
230 $(SRCDIR)/fossil.fetch.js \
231 $(SRCDIR)/fossil.page.fileedit.js \
232 $(SRCDIR)/fossil.page.forumpost.js \
 
233 $(SRCDIR)/fossil.storage.js \
234 $(SRCDIR)/fossil.tabs.js \
235 $(SRCDIR)/graph.js \
236 $(SRCDIR)/href.js \
237 $(SRCDIR)/login.js \
@@ -257,10 +258,11 @@
257 $(SRCDIR)/sounds/d.wav \
258 $(SRCDIR)/sounds/e.wav \
259 $(SRCDIR)/sounds/f.wav \
260 $(SRCDIR)/style.admin_log.css \
261 $(SRCDIR)/style.fileedit.css \
 
262 $(SRCDIR)/tree.js \
263 $(SRCDIR)/useredit.js \
264 $(SRCDIR)/wiki.wiki
265
266 TRANS_SRC = \
267
--- src/main.mk
+++ src/main.mk
@@ -228,10 +228,11 @@
228 $(SRCDIR)/fossil.confirmer.js \
229 $(SRCDIR)/fossil.dom.js \
230 $(SRCDIR)/fossil.fetch.js \
231 $(SRCDIR)/fossil.page.fileedit.js \
232 $(SRCDIR)/fossil.page.forumpost.js \
233 $(SRCDIR)/fossil.page.wikiedit.js \
234 $(SRCDIR)/fossil.storage.js \
235 $(SRCDIR)/fossil.tabs.js \
236 $(SRCDIR)/graph.js \
237 $(SRCDIR)/href.js \
238 $(SRCDIR)/login.js \
@@ -257,10 +258,11 @@
258 $(SRCDIR)/sounds/d.wav \
259 $(SRCDIR)/sounds/e.wav \
260 $(SRCDIR)/sounds/f.wav \
261 $(SRCDIR)/style.admin_log.css \
262 $(SRCDIR)/style.fileedit.css \
263 $(SRCDIR)/style.wikiedit.css \
264 $(SRCDIR)/tree.js \
265 $(SRCDIR)/useredit.js \
266 $(SRCDIR)/wiki.wiki
267
268 TRANS_SRC = \
269
--- src/style.fileedit.css
+++ src/style.fileedit.css
@@ -183,186 +183,5 @@
183183
width: initial;
184184
}
185185
body.fileedit .sbsdiffcols div.difftxtcol pre {
186186
max-width: 44em;
187187
}
188
-
189
-/**
190
- Styles for fossil.tabs.js. As of this writing, currently
191
- only used by /fileedit, but it is anticipated that these
192
- will eventually need to migrate to default_css.txt for use
193
- in the wiki and/or forum pages when implementing tabbed
194
- ajax-based previews.
195
-*/
196
-.tab-container {
197
- width: 100%;
198
- display: flex;
199
- flex-direction: column;
200
- align-items: stretch;
201
-}
202
-.tab-container > #fossil-status-bar {
203
- margin-top: 0;
204
-}
205
-.tab-container > .tabs {
206
- padding: 0.25em;
207
- margin: 0;
208
- display: flex;
209
- flex-direction: column;
210
- border-width: 1px;
211
- border-style: outset;
212
- border-color: inherit;
213
-}
214
-.tab-container > .tabs > .tab-panel {
215
- align-self: stretch;
216
- flex: 10 1 auto;
217
- display: block;
218
-}
219
-.tab-container > .tab-bar {
220
- display: flex;
221
- flex-direction: row;
222
- flex: 1 10 auto;
223
- align-self: stretch;
224
- flex-wrap: wrap;
225
-}
226
-.tab-container > .tab-bar > .tab-button {
227
- display: inline-block;
228
- border-radius: 0.5em 0.5em 0 0;
229
- margin: 0 0.1em;
230
- padding: 0.25em 0.75em;
231
- align-self: baseline;
232
- border-color: inherit;
233
- border-width: 1px;
234
- border-bottom: none;
235
- border-top-style: inset;
236
- border-left-style: inset;
237
- border-right-style: inset;
238
- cursor: pointer;
239
- opacity: 0.6;
240
-}
241
-.tab-container > .tab-bar > .tab-button.selected {
242
- text-decoration: underline;
243
- opacity: 1.0;
244
- border-top-style: outset;
245
- border-left-style: outset;
246
- border-right-style: outset;
247
-}
248
-
249
-/**
250
- Styles developed for /fileedit but which have wider
251
- applicability...
252
-
253
- As of this writing, these are only used by /fileedit, but it is
254
- anticipated that they will eventually need to be migrated over to
255
- default_css.txt for use in other pages (specifically wiki and forum
256
- page/post editors).
257
-*/
258
-.flex-container {
259
- display: flex;
260
-}
261
-.flex-container.flex-row {
262
- flex-direction: row;
263
- flex-wrap: wrap;
264
- justify-content: center;
265
- align-items: center;
266
-}
267
-.flex-container .flex-grow {
268
- flex-grow: 10;
269
- flex-shrink: 0;
270
-}
271
-.flex-container .flex-shrink {
272
- flex-grow: 0;
273
- flex-shrink: 10;
274
-}
275
-.flex-container.flex-row.stretch {
276
- flex-wrap: wrap;
277
- align-items: baseline;
278
- justify-content: stretch;
279
- margin: 0;
280
-}
281
-.flex-container.flex-column {
282
- flex-direction: column;
283
- flex-wrap: wrap;
284
- justify-content: center;
285
- align-items: center;
286
-}
287
-.flex-container.flex-column.stretch {
288
- align-items: stretch;
289
- margin: 0;
290
-}
291
-.flex-container.child-gap-small > * {
292
- margin: 0.25em;
293
-}
294
-#fossil-status-bar {
295
- display: block;
296
- font-family: monospace;
297
- border-width: 1px;
298
- border-style: inset;
299
- border-color: inherit;
300
- min-height: 1.5em;
301
- font-size: 1.2em;
302
- padding: 0.2em;
303
- margin: 0.25em 0;
304
- flex: 0 0 auto;
305
-}
306
-.font-size-100 {
307
- font-size: 100%;
308
-}
309
-.font-size-125 {
310
- font-size: 125%;
311
-}
312
-.font-size-150 {
313
- font-size: 150%;
314
-}
315
-.font-size-175 {
316
- font-size: 175%;
317
-}
318
-.font-size-200 {
319
- font-size: 200%;
320
-}
321
-
322
-/**
323
- .input-with-label is intended to be a wrapper element which
324
- contain both a LABEL tag and an INPUT or SELECT control.
325
- The wrapper is "necessary", as opposed to placing the INPUT
326
- in the LABEL, so that we can include multiple INPUT
327
- elements (e.g. a set of radio buttons).
328
-*/
329
-.input-with-label {
330
- border: 1px inset #808080;
331
- border-radius: 0.5em;
332
- padding: 0.25em 0.4em;
333
- margin: 0 0.5em;
334
- display: inline-block;
335
- cursor: default;
336
-}
337
-.input-with-label > * {
338
- vertical-align: middle;
339
-}
340
-.input-with-label > label {
341
- display: inline; /* some skins set label display to block! */
342
-}
343
-.input-with-label > input {
344
- margin: 0;
345
-}
346
-.input-with-label > button {
347
- margin: 0;
348
-}
349
-.input-with-label > select {
350
- margin: 0;
351
-}
352
-.input-with-label > input[type=text] {
353
- margin: 0;
354
-}
355
-.input-with-label > textarea {
356
- margin: 0;
357
-}
358
-.input-with-label > input[type=checkbox] {
359
- vertical-align: sub;
360
-}
361
-.input-with-label > input[type=radio] {
362
- vertical-align: sub;
363
-}
364
-.input-with-label > label {
365
- font-weight: initial;
366
- margin: 0 0.25em 0 0.25em;
367
- vertical-align: middle;
368
-}
369188
370189
ADDED src/style.wikiedit.css
--- src/style.fileedit.css
+++ src/style.fileedit.css
@@ -183,186 +183,5 @@
183 width: initial;
184 }
185 body.fileedit .sbsdiffcols div.difftxtcol pre {
186 max-width: 44em;
187 }
188
189 /**
190 Styles for fossil.tabs.js. As of this writing, currently
191 only used by /fileedit, but it is anticipated that these
192 will eventually need to migrate to default_css.txt for use
193 in the wiki and/or forum pages when implementing tabbed
194 ajax-based previews.
195 */
196 .tab-container {
197 width: 100%;
198 display: flex;
199 flex-direction: column;
200 align-items: stretch;
201 }
202 .tab-container > #fossil-status-bar {
203 margin-top: 0;
204 }
205 .tab-container > .tabs {
206 padding: 0.25em;
207 margin: 0;
208 display: flex;
209 flex-direction: column;
210 border-width: 1px;
211 border-style: outset;
212 border-color: inherit;
213 }
214 .tab-container > .tabs > .tab-panel {
215 align-self: stretch;
216 flex: 10 1 auto;
217 display: block;
218 }
219 .tab-container > .tab-bar {
220 display: flex;
221 flex-direction: row;
222 flex: 1 10 auto;
223 align-self: stretch;
224 flex-wrap: wrap;
225 }
226 .tab-container > .tab-bar > .tab-button {
227 display: inline-block;
228 border-radius: 0.5em 0.5em 0 0;
229 margin: 0 0.1em;
230 padding: 0.25em 0.75em;
231 align-self: baseline;
232 border-color: inherit;
233 border-width: 1px;
234 border-bottom: none;
235 border-top-style: inset;
236 border-left-style: inset;
237 border-right-style: inset;
238 cursor: pointer;
239 opacity: 0.6;
240 }
241 .tab-container > .tab-bar > .tab-button.selected {
242 text-decoration: underline;
243 opacity: 1.0;
244 border-top-style: outset;
245 border-left-style: outset;
246 border-right-style: outset;
247 }
248
249 /**
250 Styles developed for /fileedit but which have wider
251 applicability...
252
253 As of this writing, these are only used by /fileedit, but it is
254 anticipated that they will eventually need to be migrated over to
255 default_css.txt for use in other pages (specifically wiki and forum
256 page/post editors).
257 */
258 .flex-container {
259 display: flex;
260 }
261 .flex-container.flex-row {
262 flex-direction: row;
263 flex-wrap: wrap;
264 justify-content: center;
265 align-items: center;
266 }
267 .flex-container .flex-grow {
268 flex-grow: 10;
269 flex-shrink: 0;
270 }
271 .flex-container .flex-shrink {
272 flex-grow: 0;
273 flex-shrink: 10;
274 }
275 .flex-container.flex-row.stretch {
276 flex-wrap: wrap;
277 align-items: baseline;
278 justify-content: stretch;
279 margin: 0;
280 }
281 .flex-container.flex-column {
282 flex-direction: column;
283 flex-wrap: wrap;
284 justify-content: center;
285 align-items: center;
286 }
287 .flex-container.flex-column.stretch {
288 align-items: stretch;
289 margin: 0;
290 }
291 .flex-container.child-gap-small > * {
292 margin: 0.25em;
293 }
294 #fossil-status-bar {
295 display: block;
296 font-family: monospace;
297 border-width: 1px;
298 border-style: inset;
299 border-color: inherit;
300 min-height: 1.5em;
301 font-size: 1.2em;
302 padding: 0.2em;
303 margin: 0.25em 0;
304 flex: 0 0 auto;
305 }
306 .font-size-100 {
307 font-size: 100%;
308 }
309 .font-size-125 {
310 font-size: 125%;
311 }
312 .font-size-150 {
313 font-size: 150%;
314 }
315 .font-size-175 {
316 font-size: 175%;
317 }
318 .font-size-200 {
319 font-size: 200%;
320 }
321
322 /**
323 .input-with-label is intended to be a wrapper element which
324 contain both a LABEL tag and an INPUT or SELECT control.
325 The wrapper is "necessary", as opposed to placing the INPUT
326 in the LABEL, so that we can include multiple INPUT
327 elements (e.g. a set of radio buttons).
328 */
329 .input-with-label {
330 border: 1px inset #808080;
331 border-radius: 0.5em;
332 padding: 0.25em 0.4em;
333 margin: 0 0.5em;
334 display: inline-block;
335 cursor: default;
336 }
337 .input-with-label > * {
338 vertical-align: middle;
339 }
340 .input-with-label > label {
341 display: inline; /* some skins set label display to block! */
342 }
343 .input-with-label > input {
344 margin: 0;
345 }
346 .input-with-label > button {
347 margin: 0;
348 }
349 .input-with-label > select {
350 margin: 0;
351 }
352 .input-with-label > input[type=text] {
353 margin: 0;
354 }
355 .input-with-label > textarea {
356 margin: 0;
357 }
358 .input-with-label > input[type=checkbox] {
359 vertical-align: sub;
360 }
361 .input-with-label > input[type=radio] {
362 vertical-align: sub;
363 }
364 .input-with-label > label {
365 font-weight: initial;
366 margin: 0 0.25em 0 0.25em;
367 vertical-align: middle;
368 }
369
370 DDED src/style.wikiedit.css
--- src/style.fileedit.css
+++ src/style.fileedit.css
@@ -183,186 +183,5 @@
183 width: initial;
184 }
185 body.fileedit .sbsdiffcols div.difftxtcol pre {
186 max-width: 44em;
187 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
189 DDED src/style.wikiedit.css
--- a/src/style.wikiedit.css
+++ b/src/style.wikiedit.css
@@ -0,0 +1,6 @@
1
+>}
2
+ height */.5emselect option.stashed::before {
3
+/* Maintenance reminder: the op:focusre changed here: see fossil.page.config.editStateMarkers */
4
+ content: "[*] "select option.strevertion.stashed/stashe0,2gWsetion.stashed/stashewikiinitial{*/.5emselect option.stash;
5
+}
6
+. i.e. they're not "really" cust* "+ "
--- a/src/style.wikiedit.css
+++ b/src/style.wikiedit.css
@@ -0,0 +1,6 @@
 
 
 
 
 
 
--- a/src/style.wikiedit.css
+++ b/src/style.wikiedit.css
@@ -0,0 +1,6 @@
1 >}
2 height */.5emselect option.stashed::before {
3 /* Maintenance reminder: the op:focusre changed here: see fossil.page.config.editStateMarkers */
4 content: "[*] "select option.strevertion.stashed/stashe0,2gWsetion.stashed/stashewikiinitial{*/.5emselect option.stash;
5 }
6 . i.e. they're not "really" cust* "+ "
+364
--- src/wiki.c
+++ src/wiki.c
@@ -398,10 +398,24 @@
398398
if( sqlite3_strglob("tag/*", zPageName)==0 ){
399399
return WIKITYPE_TAG;
400400
}
401401
return WIKITYPE_NORMAL;
402402
}
403
+
404
+/*
405
+** Returns a JSON-friendly string form of the integer value returned
406
+** by wiki_page_type(zPageName).
407
+*/
408
+const char * wiki_page_type_name(const char *zPageName){
409
+ switch(wiki_page_type(zPageName)){
410
+ case WIKITYPE_CHECKIN: return "checkin";
411
+ case WIKITYPE_BRANCH: return "branch";
412
+ case WIKITYPE_TAG: return "tag";
413
+ case WIKITYPE_NORMAL:
414
+ default: return "normal";
415
+ }
416
+}
403417
404418
/*
405419
** Add an appropriate style_header() for either the /wiki or /wikiedit page
406420
** for zPageName. zExtra is an empty string for /wiki but has the text
407421
** "Edit: " for /wikiedit.
@@ -620,10 +634,355 @@
620634
return azStyles[i+1];
621635
}
622636
}
623637
return azStyles[1];
624638
}
639
+
640
+/*
641
+ ** Tries to fetch a wiki page for the given name. If found, it
642
+ ** returns true, else false. If pRid is not NULL then if found *pRid is
643
+ ** set to its RID. If ppWiki is not NULL then if found *ppWiki is set
644
+ ** to the loaded wiki object, which the caller is responsible for
645
+ ** passing to manifest_destroy().
646
+ */
647
+int wiki_fetch_by_name( const char *zPageName, int * pRid,
648
+ Manifest **ppWiki ){
649
+ Manifest *pWiki = 0;
650
+ char *zTag = mprintf("wiki-%s", zPageName);
651
+ const int rid = db_int(0,
652
+ "SELECT rid FROM tagxref"
653
+ " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
654
+ " ORDER BY mtime DESC", zTag
655
+ );
656
+
657
+ fossil_free(zTag);
658
+ if( rid == 0 ){
659
+ return 0;
660
+ }
661
+ else if(pRid){
662
+ *pRid = rid;
663
+ }
664
+ if(ppWiki){
665
+ pWiki = manifest_get(rid, CFTYPE_WIKI, 0);
666
+ if( pWiki==0 ){
667
+ /* "Cannot happen." */
668
+ return 0;
669
+ }
670
+ *ppWiki = pWiki;
671
+ }
672
+ return 1;
673
+}
674
+
675
+/*
676
+** Ajax route handler for /wikiajax/fetch.
677
+**
678
+** URL params:
679
+**
680
+** page = the wiki page name
681
+**
682
+** Responds with JSON. On error, an object in the form documented by
683
+** ajax_route_error(). On success, an object in this form:
684
+**
685
+** { name: "page name",
686
+** type: "normal" | "tag" | "checkin" | "branch" | "sandbox",
687
+** mimetype: "mime type",
688
+** content: "page content"
689
+** }
690
+*/
691
+static void wiki_ajax_route_fetch(){
692
+ const char * zPageName = P("page");
693
+ int isSandbox;
694
+
695
+ if( zPageName==0 || zPageName[0]==0 ){
696
+ ajax_route_error(400,"Missing page name.");
697
+ return;
698
+ }
699
+ cgi_set_content_type("application/json");
700
+ isSandbox = is_sandbox(zPageName);
701
+ if( isSandbox ){
702
+ char * zMimetype =
703
+ db_get("sandbox-mimetype","text/x-fossil-wiki");
704
+ CX("{\"name\": %!j, \"type\": \"sandbox\", "
705
+ "\"mimetype\": %!j, \"content\": ""}",
706
+ zPageName, zMimetype);
707
+ fossil_free(zMimetype);
708
+ }else{
709
+ Manifest * pWiki = 0;
710
+ if( !wiki_fetch_by_name(zPageName, 0, &pWiki) ){
711
+ ajax_route_error(404, "Wiki page not found.");
712
+ return;
713
+ }
714
+ CX("{\"name\": %!j, \"type\": %!j, "
715
+ "\"mimetype\": %!j, \"content\": %!j}",
716
+ pWiki->zWikiTitle,
717
+ wiki_page_type_name(pWiki->zWikiTitle),
718
+ pWiki->zMimetype ? pWiki->zMimetype : "text/x-fossil-wiki",
719
+ pWiki->zWiki);
720
+ manifest_destroy(pWiki);
721
+ }
722
+}
723
+
724
+/*
725
+** Ajax route handler for /wikiajax/preview.
726
+**
727
+** URL params:
728
+**
729
+** mimetype = the wiki page mimetype (determines rendering style)
730
+** content = the wiki page content
731
+*/
732
+static void wiki_ajax_route_preview(){
733
+ Blob content = empty_blob;
734
+ const char * zMimetype = PD("mimetype","text/x-fossil-wiki");
735
+ const char * zContent = P("content");
736
+
737
+ if( zContent==0 ){
738
+ ajax_route_error(400,"Missing content to preview.");
739
+ return;
740
+ }
741
+ blob_init(&content, zContent, -1);
742
+ cgi_set_content_type("text/html");
743
+ wiki_render_by_mimetype(&content, zMimetype);
744
+ blob_reset(&content);
745
+}
746
+
747
+/*
748
+** WEBPAGE: wikiajax
749
+**
750
+** An internal dispatcher for wiki AJAX operations. Not for direct
751
+** client use.
752
+*/
753
+void wiki_ajax_page(void){
754
+ const char * zName = P("name");
755
+ AjaxRoute routeName = {0,0,0,0};
756
+ const AjaxRoute * pRoute = 0;
757
+ const AjaxRoute routes[] = {
758
+ /* Keep these sorted by zName (for bsearch()) */
759
+ {"fetch", wiki_ajax_route_fetch, 0, 0},
760
+ {"preview", wiki_ajax_route_preview, 1, 1}
761
+ /* /preview access mode: whether or not wiki-write mode is
762
+ needed really depends on multiple factors. e.g. the sandbox
763
+ page does not normally require more than anonymous access.
764
+ TODO: set its write-mode to false and do the check manually
765
+ in that route's handler.
766
+ */
767
+ };
768
+
769
+ if(zName==0 || zName[0]==0){
770
+ ajax_route_error(400,"Missing required [route] 'name' parameter.");
771
+ return;
772
+ }
773
+ routeName.zName = zName;
774
+ pRoute = (const AjaxRoute *)bsearch(&routeName, routes,
775
+ count(routes), sizeof routes[0],
776
+ cmp_ajax_route_name);
777
+ if(pRoute==0){
778
+ ajax_route_error(404,"Ajax route not found.");
779
+ return;
780
+ }
781
+ login_check_credentials();
782
+ if( pRoute->bWriteMode!=0 && g.perm.WrWiki==0 ){
783
+ ajax_route_error(403,"Write permissions required.");
784
+ return;
785
+ }else if(0==cgi_csrf_safe(pRoute->bPost)){
786
+ ajax_route_error(403,
787
+ "CSRF violation (make sure sending of HTTP "
788
+ "Referer headers is enabled for XHR "
789
+ "connections).");
790
+ return;
791
+ }
792
+ pRoute->xCallback();
793
+}
794
+
795
+/*
796
+** Main front-end for the Ajax-based wiki editor app.
797
+*/
798
+static void wikiedit_page_v2(void){
799
+ const char *zPageName;
800
+ Blob endScript = empty_blob; /* end-of-page JS code */;
801
+ int isSandbox;
802
+
803
+ login_check_credentials();
804
+ zPageName = PD("name","");
805
+ /* TODO: not require a page name, and instead offer a list
806
+ of pages. */
807
+ /*TODO: check name only for case of new page:
808
+ if( check_name(zPageName) ) return;*/
809
+ isSandbox = is_sandbox(zPageName);
810
+ if( isSandbox ){
811
+ if( !g.perm.WrWiki ){
812
+ login_needed(g.anon.WrWiki);
813
+ return;
814
+ }
815
+ }else if( zPageName!=0 ){
816
+ int rid = 0;
817
+ int found = 0;
818
+ if( !wiki_special_permission(zPageName) ){
819
+ login_needed(0);
820
+ return;
821
+ }
822
+ found = wiki_fetch_by_name(zPageName, &rid, 0);
823
+ if( !found ){
824
+ /* TODO: set up for a new page */
825
+ }
826
+ if( (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){
827
+ login_needed(rid ? g.anon.WrWiki : g.anon.NewWiki);
828
+ return;
829
+ }
830
+ }
831
+ style_header("Wiki Editor");
832
+
833
+ /* Status bar */
834
+ CX("<div id='fossil-status-bar' "
835
+ "title='Status message area. Double-click to clear them.'>"
836
+ "Status messages will go here.</div>\n"
837
+ /* will be moved into the tab container via JS */);
838
+
839
+ /* Main tab container... */
840
+ CX("<div id='wikiedit-tabs' class='tab-container'></div>");
841
+
842
+ /******* Page list *******/
843
+ {
844
+ CX("<div id='wikiedit-tab-pages' "
845
+ "data-tab-parent='wikiedit-tabs' "
846
+ "data-tab-label='Page List'"
847
+ ">");
848
+ CX("<div class='flex-container flex-row child-gap-small'>");
849
+ CX("TODO: page selection list.");
850
+ CX("</div>");
851
+ CX("</div>"/*#tab-file-content*/);
852
+ }
853
+
854
+ /******* Content tab *******/
855
+ {
856
+ CX("<div id='wikiedit-tab-content' "
857
+ "data-tab-parent='wikiedit-tabs' "
858
+ "data-tab-label='Page Editor' "
859
+ "data-tab-select='1'"
860
+ ">");
861
+ CX("<div class='flex-container flex-row child-gap-small'>");
862
+ style_select_list_str("select-mimetype",
863
+ "mimetype",
864
+ "Mimetype", 0,
865
+ "text/x-fossil-wiki",
866
+ "Fossil wiki", "text/x-fossil-wiki",
867
+ "Markdown", "text/x-markdown",
868
+ "Plain text", "text/plain",
869
+ NULL);
870
+ CX("<button class='wikiedit-content-reload confirmer' "
871
+ "title='Reload the file from the server, discarding "
872
+ "any local edits. To help avoid accidental loss of "
873
+ "edits, it requires confirmation (a second click) within "
874
+ "a few seconds or it will not reload.'"
875
+ ">Discard &amp; Reload</button>");
876
+ style_select_list_int("select-font-size",
877
+ "editor_font_size", "Editor font size",
878
+ NULL/*tooltip*/,
879
+ 100,
880
+ "100%", 100, "125%", 125,
881
+ "150%", 150, "175%", 175,
882
+ "200%", 200, NULL);
883
+ CX("</div>");
884
+ CX("<div class='flex-container flex-column stretch'>");
885
+ CX("<textarea name='content' id='wikiedit-content-editor' "
886
+ "class='wikiedit' "
887
+ "rows='20' cols='80'>");
888
+ CX("</textarea>");
889
+ CX("</div>"/*textarea wrapper*/);
890
+ CX("</div>"/*#tab-file-content*/);
891
+ }
892
+ /****** Preview tab ******/
893
+ {
894
+ CX("<div id='wikiedit-tab-preview' "
895
+ "data-tab-parent='wikiedit-tabs' "
896
+ "data-tab-label='Preview'"
897
+ ">");
898
+ CX("<div class='wikiedit-options flex-container flex-row'>");
899
+ CX("<button id='btn-preview-refresh' "
900
+ "data-f-preview-from='wikiContent' "
901
+ /* ^^^ fossil.page[methodName]() OR text source elem ID,
902
+ ** but we need a method in order to support clients swapping out
903
+ ** the text editor with their own. */
904
+ "data-f-preview-via='_postPreview' "
905
+ /* ^^^ fossil.page[methodName](content, callback) */
906
+ "data-f-preview-to='#wikiedit-tab-preview-wrapper' "
907
+ /* ^^^ dest elem ID */
908
+ ">Refresh</button>");
909
+ /* Toggle auto-update of preview when the Preview tab is selected. */
910
+ style_labeled_checkbox("cb-preview-autoupdate",
911
+ NULL,
912
+ "Auto-refresh?",
913
+ "1", 1,
914
+ "If on, the preview will automatically "
915
+ "refresh when this tab is selected.");
916
+ CX("</div>"/*.wikiedit-options*/);
917
+ CX("<div id='wikiedit-tab-preview-wrapper'></div>");
918
+ CX("</div>"/*#wikiedit-tab-preview*/);
919
+ }
920
+
921
+ /****** Diff tab ******/
922
+ {
923
+ CX("<div id='wikiedit-tab-diff' "
924
+ "data-tab-parent='wikiedit-tabs' "
925
+ "data-tab-label='Diff'"
926
+ ">");
927
+
928
+ CX("<div class='wikiedit-options flex-container flex-row' "
929
+ "id='wikiedit-tab-diff-buttons'>");
930
+ CX("<button class='sbs'>Side-by-side</button>"
931
+ "<button class='unified'>Unified</button>");
932
+ if(0){
933
+ /* For the time being let's just ignore all whitespace
934
+ ** changes, as files with Windows-style EOLs always show
935
+ ** more diffs than we want then they're submitted to
936
+ ** ?ajax=diff because JS normalizes them to Unix EOLs.
937
+ ** We can revisit this decision later. */
938
+ style_select_list_int("diff-ws-policy",
939
+ "diff_ws", "Whitespace",
940
+ "Whitespace handling policy.",
941
+ 2,
942
+ "Diff all whitespace", 0,
943
+ "Ignore EOL whitespace", 1,
944
+ "Ignore all whitespace", 2,
945
+ NULL);
946
+ }
947
+ CX("</div>");
948
+ CX("<div id='wikiedit-tab-diff-wrapper'>"
949
+ "Diffs will be shown here."
950
+ "</div>");
951
+ CX("</div>"/*#wikiedit-tab-diff*/);
952
+ }
953
+
954
+ if(zPageName && *zPageName){
955
+ /* Dynamically populate the editor... */
956
+ blob_appendf(&endScript, "fossil.onPageLoad(function(){");
957
+ blob_appendf(&endScript, "fossil.page.loadPage(%!j);",
958
+ zPageName);
959
+ blob_appendf(&endScript, "});\n");
960
+ }
961
+
962
+ style_emit_script_fossil_bootstrap(0);
963
+ append_diff_javascript(1);
964
+ style_emit_script_fetch(0);
965
+ style_emit_script_tabs(0)/*also emits fossil.dom*/;
966
+ style_emit_script_confirmer(0);
967
+ style_emit_script_builtin(0, "fossil.storage.js");
968
+ style_emit_script_builtin(0, "fossil.page.wikiedit.js");
969
+
970
+ if(blob_size(&endScript)>0){
971
+ style_emit_script_tag(0,0);
972
+ CX("\n(function(){\n");
973
+ CX("try{\n%b}\n"
974
+ "catch(e){"
975
+ "fossil.error(e); console.error('Exception:',e);"
976
+ "}\n",
977
+ &endScript);
978
+ CX("})();");
979
+ style_emit_script_tag(1,0);
980
+ }
981
+ blob_reset(&endScript);
982
+ style_footer();
983
+}
625984
626985
/*
627986
** WEBPAGE: wikiedit
628987
** URL: /wikiedit?name=PAGENAME
629988
**
@@ -643,10 +1002,15 @@
6431002
int isWysiwyg = P("wysiwyg")!=0;
6441003
int goodCaptcha = 1;
6451004
int eType = WIKITYPE_UNKNOWN;
6461005
int havePreview = 0;
6471006
1007
+ if(1){
1008
+ wikiedit_page_v2();
1009
+ return;
1010
+ }
1011
+
6481012
if( P("edit-wysiwyg")!=0 ){ isWysiwyg = 1; zBody = 0; }
6491013
if( P("edit-markup")!=0 ){ isWysiwyg = 0; zBody = 0; }
6501014
if( zBody ){
6511015
if( isWysiwyg ){
6521016
Blob body;
6531017
--- src/wiki.c
+++ src/wiki.c
@@ -398,10 +398,24 @@
398 if( sqlite3_strglob("tag/*", zPageName)==0 ){
399 return WIKITYPE_TAG;
400 }
401 return WIKITYPE_NORMAL;
402 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
403
404 /*
405 ** Add an appropriate style_header() for either the /wiki or /wikiedit page
406 ** for zPageName. zExtra is an empty string for /wiki but has the text
407 ** "Edit: " for /wikiedit.
@@ -620,10 +634,355 @@
620 return azStyles[i+1];
621 }
622 }
623 return azStyles[1];
624 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
625
626 /*
627 ** WEBPAGE: wikiedit
628 ** URL: /wikiedit?name=PAGENAME
629 **
@@ -643,10 +1002,15 @@
643 int isWysiwyg = P("wysiwyg")!=0;
644 int goodCaptcha = 1;
645 int eType = WIKITYPE_UNKNOWN;
646 int havePreview = 0;
647
 
 
 
 
 
648 if( P("edit-wysiwyg")!=0 ){ isWysiwyg = 1; zBody = 0; }
649 if( P("edit-markup")!=0 ){ isWysiwyg = 0; zBody = 0; }
650 if( zBody ){
651 if( isWysiwyg ){
652 Blob body;
653
--- src/wiki.c
+++ src/wiki.c
@@ -398,10 +398,24 @@
398 if( sqlite3_strglob("tag/*", zPageName)==0 ){
399 return WIKITYPE_TAG;
400 }
401 return WIKITYPE_NORMAL;
402 }
403
404 /*
405 ** Returns a JSON-friendly string form of the integer value returned
406 ** by wiki_page_type(zPageName).
407 */
408 const char * wiki_page_type_name(const char *zPageName){
409 switch(wiki_page_type(zPageName)){
410 case WIKITYPE_CHECKIN: return "checkin";
411 case WIKITYPE_BRANCH: return "branch";
412 case WIKITYPE_TAG: return "tag";
413 case WIKITYPE_NORMAL:
414 default: return "normal";
415 }
416 }
417
418 /*
419 ** Add an appropriate style_header() for either the /wiki or /wikiedit page
420 ** for zPageName. zExtra is an empty string for /wiki but has the text
421 ** "Edit: " for /wikiedit.
@@ -620,10 +634,355 @@
634 return azStyles[i+1];
635 }
636 }
637 return azStyles[1];
638 }
639
640 /*
641 ** Tries to fetch a wiki page for the given name. If found, it
642 ** returns true, else false. If pRid is not NULL then if found *pRid is
643 ** set to its RID. If ppWiki is not NULL then if found *ppWiki is set
644 ** to the loaded wiki object, which the caller is responsible for
645 ** passing to manifest_destroy().
646 */
647 int wiki_fetch_by_name( const char *zPageName, int * pRid,
648 Manifest **ppWiki ){
649 Manifest *pWiki = 0;
650 char *zTag = mprintf("wiki-%s", zPageName);
651 const int rid = db_int(0,
652 "SELECT rid FROM tagxref"
653 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
654 " ORDER BY mtime DESC", zTag
655 );
656
657 fossil_free(zTag);
658 if( rid == 0 ){
659 return 0;
660 }
661 else if(pRid){
662 *pRid = rid;
663 }
664 if(ppWiki){
665 pWiki = manifest_get(rid, CFTYPE_WIKI, 0);
666 if( pWiki==0 ){
667 /* "Cannot happen." */
668 return 0;
669 }
670 *ppWiki = pWiki;
671 }
672 return 1;
673 }
674
675 /*
676 ** Ajax route handler for /wikiajax/fetch.
677 **
678 ** URL params:
679 **
680 ** page = the wiki page name
681 **
682 ** Responds with JSON. On error, an object in the form documented by
683 ** ajax_route_error(). On success, an object in this form:
684 **
685 ** { name: "page name",
686 ** type: "normal" | "tag" | "checkin" | "branch" | "sandbox",
687 ** mimetype: "mime type",
688 ** content: "page content"
689 ** }
690 */
691 static void wiki_ajax_route_fetch(){
692 const char * zPageName = P("page");
693 int isSandbox;
694
695 if( zPageName==0 || zPageName[0]==0 ){
696 ajax_route_error(400,"Missing page name.");
697 return;
698 }
699 cgi_set_content_type("application/json");
700 isSandbox = is_sandbox(zPageName);
701 if( isSandbox ){
702 char * zMimetype =
703 db_get("sandbox-mimetype","text/x-fossil-wiki");
704 CX("{\"name\": %!j, \"type\": \"sandbox\", "
705 "\"mimetype\": %!j, \"content\": ""}",
706 zPageName, zMimetype);
707 fossil_free(zMimetype);
708 }else{
709 Manifest * pWiki = 0;
710 if( !wiki_fetch_by_name(zPageName, 0, &pWiki) ){
711 ajax_route_error(404, "Wiki page not found.");
712 return;
713 }
714 CX("{\"name\": %!j, \"type\": %!j, "
715 "\"mimetype\": %!j, \"content\": %!j}",
716 pWiki->zWikiTitle,
717 wiki_page_type_name(pWiki->zWikiTitle),
718 pWiki->zMimetype ? pWiki->zMimetype : "text/x-fossil-wiki",
719 pWiki->zWiki);
720 manifest_destroy(pWiki);
721 }
722 }
723
724 /*
725 ** Ajax route handler for /wikiajax/preview.
726 **
727 ** URL params:
728 **
729 ** mimetype = the wiki page mimetype (determines rendering style)
730 ** content = the wiki page content
731 */
732 static void wiki_ajax_route_preview(){
733 Blob content = empty_blob;
734 const char * zMimetype = PD("mimetype","text/x-fossil-wiki");
735 const char * zContent = P("content");
736
737 if( zContent==0 ){
738 ajax_route_error(400,"Missing content to preview.");
739 return;
740 }
741 blob_init(&content, zContent, -1);
742 cgi_set_content_type("text/html");
743 wiki_render_by_mimetype(&content, zMimetype);
744 blob_reset(&content);
745 }
746
747 /*
748 ** WEBPAGE: wikiajax
749 **
750 ** An internal dispatcher for wiki AJAX operations. Not for direct
751 ** client use.
752 */
753 void wiki_ajax_page(void){
754 const char * zName = P("name");
755 AjaxRoute routeName = {0,0,0,0};
756 const AjaxRoute * pRoute = 0;
757 const AjaxRoute routes[] = {
758 /* Keep these sorted by zName (for bsearch()) */
759 {"fetch", wiki_ajax_route_fetch, 0, 0},
760 {"preview", wiki_ajax_route_preview, 1, 1}
761 /* /preview access mode: whether or not wiki-write mode is
762 needed really depends on multiple factors. e.g. the sandbox
763 page does not normally require more than anonymous access.
764 TODO: set its write-mode to false and do the check manually
765 in that route's handler.
766 */
767 };
768
769 if(zName==0 || zName[0]==0){
770 ajax_route_error(400,"Missing required [route] 'name' parameter.");
771 return;
772 }
773 routeName.zName = zName;
774 pRoute = (const AjaxRoute *)bsearch(&routeName, routes,
775 count(routes), sizeof routes[0],
776 cmp_ajax_route_name);
777 if(pRoute==0){
778 ajax_route_error(404,"Ajax route not found.");
779 return;
780 }
781 login_check_credentials();
782 if( pRoute->bWriteMode!=0 && g.perm.WrWiki==0 ){
783 ajax_route_error(403,"Write permissions required.");
784 return;
785 }else if(0==cgi_csrf_safe(pRoute->bPost)){
786 ajax_route_error(403,
787 "CSRF violation (make sure sending of HTTP "
788 "Referer headers is enabled for XHR "
789 "connections).");
790 return;
791 }
792 pRoute->xCallback();
793 }
794
795 /*
796 ** Main front-end for the Ajax-based wiki editor app.
797 */
798 static void wikiedit_page_v2(void){
799 const char *zPageName;
800 Blob endScript = empty_blob; /* end-of-page JS code */;
801 int isSandbox;
802
803 login_check_credentials();
804 zPageName = PD("name","");
805 /* TODO: not require a page name, and instead offer a list
806 of pages. */
807 /*TODO: check name only for case of new page:
808 if( check_name(zPageName) ) return;*/
809 isSandbox = is_sandbox(zPageName);
810 if( isSandbox ){
811 if( !g.perm.WrWiki ){
812 login_needed(g.anon.WrWiki);
813 return;
814 }
815 }else if( zPageName!=0 ){
816 int rid = 0;
817 int found = 0;
818 if( !wiki_special_permission(zPageName) ){
819 login_needed(0);
820 return;
821 }
822 found = wiki_fetch_by_name(zPageName, &rid, 0);
823 if( !found ){
824 /* TODO: set up for a new page */
825 }
826 if( (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){
827 login_needed(rid ? g.anon.WrWiki : g.anon.NewWiki);
828 return;
829 }
830 }
831 style_header("Wiki Editor");
832
833 /* Status bar */
834 CX("<div id='fossil-status-bar' "
835 "title='Status message area. Double-click to clear them.'>"
836 "Status messages will go here.</div>\n"
837 /* will be moved into the tab container via JS */);
838
839 /* Main tab container... */
840 CX("<div id='wikiedit-tabs' class='tab-container'></div>");
841
842 /******* Page list *******/
843 {
844 CX("<div id='wikiedit-tab-pages' "
845 "data-tab-parent='wikiedit-tabs' "
846 "data-tab-label='Page List'"
847 ">");
848 CX("<div class='flex-container flex-row child-gap-small'>");
849 CX("TODO: page selection list.");
850 CX("</div>");
851 CX("</div>"/*#tab-file-content*/);
852 }
853
854 /******* Content tab *******/
855 {
856 CX("<div id='wikiedit-tab-content' "
857 "data-tab-parent='wikiedit-tabs' "
858 "data-tab-label='Page Editor' "
859 "data-tab-select='1'"
860 ">");
861 CX("<div class='flex-container flex-row child-gap-small'>");
862 style_select_list_str("select-mimetype",
863 "mimetype",
864 "Mimetype", 0,
865 "text/x-fossil-wiki",
866 "Fossil wiki", "text/x-fossil-wiki",
867 "Markdown", "text/x-markdown",
868 "Plain text", "text/plain",
869 NULL);
870 CX("<button class='wikiedit-content-reload confirmer' "
871 "title='Reload the file from the server, discarding "
872 "any local edits. To help avoid accidental loss of "
873 "edits, it requires confirmation (a second click) within "
874 "a few seconds or it will not reload.'"
875 ">Discard &amp; Reload</button>");
876 style_select_list_int("select-font-size",
877 "editor_font_size", "Editor font size",
878 NULL/*tooltip*/,
879 100,
880 "100%", 100, "125%", 125,
881 "150%", 150, "175%", 175,
882 "200%", 200, NULL);
883 CX("</div>");
884 CX("<div class='flex-container flex-column stretch'>");
885 CX("<textarea name='content' id='wikiedit-content-editor' "
886 "class='wikiedit' "
887 "rows='20' cols='80'>");
888 CX("</textarea>");
889 CX("</div>"/*textarea wrapper*/);
890 CX("</div>"/*#tab-file-content*/);
891 }
892 /****** Preview tab ******/
893 {
894 CX("<div id='wikiedit-tab-preview' "
895 "data-tab-parent='wikiedit-tabs' "
896 "data-tab-label='Preview'"
897 ">");
898 CX("<div class='wikiedit-options flex-container flex-row'>");
899 CX("<button id='btn-preview-refresh' "
900 "data-f-preview-from='wikiContent' "
901 /* ^^^ fossil.page[methodName]() OR text source elem ID,
902 ** but we need a method in order to support clients swapping out
903 ** the text editor with their own. */
904 "data-f-preview-via='_postPreview' "
905 /* ^^^ fossil.page[methodName](content, callback) */
906 "data-f-preview-to='#wikiedit-tab-preview-wrapper' "
907 /* ^^^ dest elem ID */
908 ">Refresh</button>");
909 /* Toggle auto-update of preview when the Preview tab is selected. */
910 style_labeled_checkbox("cb-preview-autoupdate",
911 NULL,
912 "Auto-refresh?",
913 "1", 1,
914 "If on, the preview will automatically "
915 "refresh when this tab is selected.");
916 CX("</div>"/*.wikiedit-options*/);
917 CX("<div id='wikiedit-tab-preview-wrapper'></div>");
918 CX("</div>"/*#wikiedit-tab-preview*/);
919 }
920
921 /****** Diff tab ******/
922 {
923 CX("<div id='wikiedit-tab-diff' "
924 "data-tab-parent='wikiedit-tabs' "
925 "data-tab-label='Diff'"
926 ">");
927
928 CX("<div class='wikiedit-options flex-container flex-row' "
929 "id='wikiedit-tab-diff-buttons'>");
930 CX("<button class='sbs'>Side-by-side</button>"
931 "<button class='unified'>Unified</button>");
932 if(0){
933 /* For the time being let's just ignore all whitespace
934 ** changes, as files with Windows-style EOLs always show
935 ** more diffs than we want then they're submitted to
936 ** ?ajax=diff because JS normalizes them to Unix EOLs.
937 ** We can revisit this decision later. */
938 style_select_list_int("diff-ws-policy",
939 "diff_ws", "Whitespace",
940 "Whitespace handling policy.",
941 2,
942 "Diff all whitespace", 0,
943 "Ignore EOL whitespace", 1,
944 "Ignore all whitespace", 2,
945 NULL);
946 }
947 CX("</div>");
948 CX("<div id='wikiedit-tab-diff-wrapper'>"
949 "Diffs will be shown here."
950 "</div>");
951 CX("</div>"/*#wikiedit-tab-diff*/);
952 }
953
954 if(zPageName && *zPageName){
955 /* Dynamically populate the editor... */
956 blob_appendf(&endScript, "fossil.onPageLoad(function(){");
957 blob_appendf(&endScript, "fossil.page.loadPage(%!j);",
958 zPageName);
959 blob_appendf(&endScript, "});\n");
960 }
961
962 style_emit_script_fossil_bootstrap(0);
963 append_diff_javascript(1);
964 style_emit_script_fetch(0);
965 style_emit_script_tabs(0)/*also emits fossil.dom*/;
966 style_emit_script_confirmer(0);
967 style_emit_script_builtin(0, "fossil.storage.js");
968 style_emit_script_builtin(0, "fossil.page.wikiedit.js");
969
970 if(blob_size(&endScript)>0){
971 style_emit_script_tag(0,0);
972 CX("\n(function(){\n");
973 CX("try{\n%b}\n"
974 "catch(e){"
975 "fossil.error(e); console.error('Exception:',e);"
976 "}\n",
977 &endScript);
978 CX("})();");
979 style_emit_script_tag(1,0);
980 }
981 blob_reset(&endScript);
982 style_footer();
983 }
984
985 /*
986 ** WEBPAGE: wikiedit
987 ** URL: /wikiedit?name=PAGENAME
988 **
@@ -643,10 +1002,15 @@
1002 int isWysiwyg = P("wysiwyg")!=0;
1003 int goodCaptcha = 1;
1004 int eType = WIKITYPE_UNKNOWN;
1005 int havePreview = 0;
1006
1007 if(1){
1008 wikiedit_page_v2();
1009 return;
1010 }
1011
1012 if( P("edit-wysiwyg")!=0 ){ isWysiwyg = 1; zBody = 0; }
1013 if( P("edit-markup")!=0 ){ isWysiwyg = 0; zBody = 0; }
1014 if( zBody ){
1015 if( isWysiwyg ){
1016 Blob body;
1017
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -640,10 +640,11 @@
640640
$(SRCDIR)/fossil.confirmer.js \
641641
$(SRCDIR)/fossil.dom.js \
642642
$(SRCDIR)/fossil.fetch.js \
643643
$(SRCDIR)/fossil.page.fileedit.js \
644644
$(SRCDIR)/fossil.page.forumpost.js \
645
+ $(SRCDIR)/fossil.page.wikiedit.js \
645646
$(SRCDIR)/fossil.storage.js \
646647
$(SRCDIR)/fossil.tabs.js \
647648
$(SRCDIR)/graph.js \
648649
$(SRCDIR)/href.js \
649650
$(SRCDIR)/login.js \
@@ -669,10 +670,11 @@
669670
$(SRCDIR)/sounds/d.wav \
670671
$(SRCDIR)/sounds/e.wav \
671672
$(SRCDIR)/sounds/f.wav \
672673
$(SRCDIR)/style.admin_log.css \
673674
$(SRCDIR)/style.fileedit.css \
675
+ $(SRCDIR)/style.wikiedit.css \
674676
$(SRCDIR)/tree.js \
675677
$(SRCDIR)/useredit.js \
676678
$(SRCDIR)/wiki.wiki
677679
678680
TRANS_SRC = \
679681
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -640,10 +640,11 @@
640 $(SRCDIR)/fossil.confirmer.js \
641 $(SRCDIR)/fossil.dom.js \
642 $(SRCDIR)/fossil.fetch.js \
643 $(SRCDIR)/fossil.page.fileedit.js \
644 $(SRCDIR)/fossil.page.forumpost.js \
 
645 $(SRCDIR)/fossil.storage.js \
646 $(SRCDIR)/fossil.tabs.js \
647 $(SRCDIR)/graph.js \
648 $(SRCDIR)/href.js \
649 $(SRCDIR)/login.js \
@@ -669,10 +670,11 @@
669 $(SRCDIR)/sounds/d.wav \
670 $(SRCDIR)/sounds/e.wav \
671 $(SRCDIR)/sounds/f.wav \
672 $(SRCDIR)/style.admin_log.css \
673 $(SRCDIR)/style.fileedit.css \
 
674 $(SRCDIR)/tree.js \
675 $(SRCDIR)/useredit.js \
676 $(SRCDIR)/wiki.wiki
677
678 TRANS_SRC = \
679
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -640,10 +640,11 @@
640 $(SRCDIR)/fossil.confirmer.js \
641 $(SRCDIR)/fossil.dom.js \
642 $(SRCDIR)/fossil.fetch.js \
643 $(SRCDIR)/fossil.page.fileedit.js \
644 $(SRCDIR)/fossil.page.forumpost.js \
645 $(SRCDIR)/fossil.page.wikiedit.js \
646 $(SRCDIR)/fossil.storage.js \
647 $(SRCDIR)/fossil.tabs.js \
648 $(SRCDIR)/graph.js \
649 $(SRCDIR)/href.js \
650 $(SRCDIR)/login.js \
@@ -669,10 +670,11 @@
670 $(SRCDIR)/sounds/d.wav \
671 $(SRCDIR)/sounds/e.wav \
672 $(SRCDIR)/sounds/f.wav \
673 $(SRCDIR)/style.admin_log.css \
674 $(SRCDIR)/style.fileedit.css \
675 $(SRCDIR)/style.wikiedit.css \
676 $(SRCDIR)/tree.js \
677 $(SRCDIR)/useredit.js \
678 $(SRCDIR)/wiki.wiki
679
680 TRANS_SRC = \
681
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -561,10 +561,11 @@
561561
"$(SRCDIR)\fossil.confirmer.js" \
562562
"$(SRCDIR)\fossil.dom.js" \
563563
"$(SRCDIR)\fossil.fetch.js" \
564564
"$(SRCDIR)\fossil.page.fileedit.js" \
565565
"$(SRCDIR)\fossil.page.forumpost.js" \
566
+ "$(SRCDIR)\fossil.page.wikiedit.js" \
566567
"$(SRCDIR)\fossil.storage.js" \
567568
"$(SRCDIR)\fossil.tabs.js" \
568569
"$(SRCDIR)\graph.js" \
569570
"$(SRCDIR)\href.js" \
570571
"$(SRCDIR)\login.js" \
@@ -590,10 +591,11 @@
590591
"$(SRCDIR)\sounds\d.wav" \
591592
"$(SRCDIR)\sounds\e.wav" \
592593
"$(SRCDIR)\sounds\f.wav" \
593594
"$(SRCDIR)\style.admin_log.css" \
594595
"$(SRCDIR)\style.fileedit.css" \
596
+ "$(SRCDIR)\style.wikiedit.css" \
595597
"$(SRCDIR)\tree.js" \
596598
"$(SRCDIR)\useredit.js" \
597599
"$(SRCDIR)\wiki.wiki"
598600
599601
OBJ = "$(OX)\add$O" \
@@ -1152,10 +1154,11 @@
11521154
echo "$(SRCDIR)\fossil.confirmer.js" >> $@
11531155
echo "$(SRCDIR)\fossil.dom.js" >> $@
11541156
echo "$(SRCDIR)\fossil.fetch.js" >> $@
11551157
echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@
11561158
echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@
1159
+ echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@
11571160
echo "$(SRCDIR)\fossil.storage.js" >> $@
11581161
echo "$(SRCDIR)\fossil.tabs.js" >> $@
11591162
echo "$(SRCDIR)\graph.js" >> $@
11601163
echo "$(SRCDIR)\href.js" >> $@
11611164
echo "$(SRCDIR)\login.js" >> $@
@@ -1181,10 +1184,11 @@
11811184
echo "$(SRCDIR)\sounds/d.wav" >> $@
11821185
echo "$(SRCDIR)\sounds/e.wav" >> $@
11831186
echo "$(SRCDIR)\sounds/f.wav" >> $@
11841187
echo "$(SRCDIR)\style.admin_log.css" >> $@
11851188
echo "$(SRCDIR)\style.fileedit.css" >> $@
1189
+ echo "$(SRCDIR)\style.wikiedit.css" >> $@
11861190
echo "$(SRCDIR)\tree.js" >> $@
11871191
echo "$(SRCDIR)\useredit.js" >> $@
11881192
echo "$(SRCDIR)\wiki.wiki" >> $@
11891193
11901194
"$(OX)\add$O" : "$(OX)\add_.c" "$(OX)\add.h"
11911195
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -561,10 +561,11 @@
561 "$(SRCDIR)\fossil.confirmer.js" \
562 "$(SRCDIR)\fossil.dom.js" \
563 "$(SRCDIR)\fossil.fetch.js" \
564 "$(SRCDIR)\fossil.page.fileedit.js" \
565 "$(SRCDIR)\fossil.page.forumpost.js" \
 
566 "$(SRCDIR)\fossil.storage.js" \
567 "$(SRCDIR)\fossil.tabs.js" \
568 "$(SRCDIR)\graph.js" \
569 "$(SRCDIR)\href.js" \
570 "$(SRCDIR)\login.js" \
@@ -590,10 +591,11 @@
590 "$(SRCDIR)\sounds\d.wav" \
591 "$(SRCDIR)\sounds\e.wav" \
592 "$(SRCDIR)\sounds\f.wav" \
593 "$(SRCDIR)\style.admin_log.css" \
594 "$(SRCDIR)\style.fileedit.css" \
 
595 "$(SRCDIR)\tree.js" \
596 "$(SRCDIR)\useredit.js" \
597 "$(SRCDIR)\wiki.wiki"
598
599 OBJ = "$(OX)\add$O" \
@@ -1152,10 +1154,11 @@
1152 echo "$(SRCDIR)\fossil.confirmer.js" >> $@
1153 echo "$(SRCDIR)\fossil.dom.js" >> $@
1154 echo "$(SRCDIR)\fossil.fetch.js" >> $@
1155 echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@
1156 echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@
 
1157 echo "$(SRCDIR)\fossil.storage.js" >> $@
1158 echo "$(SRCDIR)\fossil.tabs.js" >> $@
1159 echo "$(SRCDIR)\graph.js" >> $@
1160 echo "$(SRCDIR)\href.js" >> $@
1161 echo "$(SRCDIR)\login.js" >> $@
@@ -1181,10 +1184,11 @@
1181 echo "$(SRCDIR)\sounds/d.wav" >> $@
1182 echo "$(SRCDIR)\sounds/e.wav" >> $@
1183 echo "$(SRCDIR)\sounds/f.wav" >> $@
1184 echo "$(SRCDIR)\style.admin_log.css" >> $@
1185 echo "$(SRCDIR)\style.fileedit.css" >> $@
 
1186 echo "$(SRCDIR)\tree.js" >> $@
1187 echo "$(SRCDIR)\useredit.js" >> $@
1188 echo "$(SRCDIR)\wiki.wiki" >> $@
1189
1190 "$(OX)\add$O" : "$(OX)\add_.c" "$(OX)\add.h"
1191
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -561,10 +561,11 @@
561 "$(SRCDIR)\fossil.confirmer.js" \
562 "$(SRCDIR)\fossil.dom.js" \
563 "$(SRCDIR)\fossil.fetch.js" \
564 "$(SRCDIR)\fossil.page.fileedit.js" \
565 "$(SRCDIR)\fossil.page.forumpost.js" \
566 "$(SRCDIR)\fossil.page.wikiedit.js" \
567 "$(SRCDIR)\fossil.storage.js" \
568 "$(SRCDIR)\fossil.tabs.js" \
569 "$(SRCDIR)\graph.js" \
570 "$(SRCDIR)\href.js" \
571 "$(SRCDIR)\login.js" \
@@ -590,10 +591,11 @@
591 "$(SRCDIR)\sounds\d.wav" \
592 "$(SRCDIR)\sounds\e.wav" \
593 "$(SRCDIR)\sounds\f.wav" \
594 "$(SRCDIR)\style.admin_log.css" \
595 "$(SRCDIR)\style.fileedit.css" \
596 "$(SRCDIR)\style.wikiedit.css" \
597 "$(SRCDIR)\tree.js" \
598 "$(SRCDIR)\useredit.js" \
599 "$(SRCDIR)\wiki.wiki"
600
601 OBJ = "$(OX)\add$O" \
@@ -1152,10 +1154,11 @@
1154 echo "$(SRCDIR)\fossil.confirmer.js" >> $@
1155 echo "$(SRCDIR)\fossil.dom.js" >> $@
1156 echo "$(SRCDIR)\fossil.fetch.js" >> $@
1157 echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@
1158 echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@
1159 echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@
1160 echo "$(SRCDIR)\fossil.storage.js" >> $@
1161 echo "$(SRCDIR)\fossil.tabs.js" >> $@
1162 echo "$(SRCDIR)\graph.js" >> $@
1163 echo "$(SRCDIR)\href.js" >> $@
1164 echo "$(SRCDIR)\login.js" >> $@
@@ -1181,10 +1184,11 @@
1184 echo "$(SRCDIR)\sounds/d.wav" >> $@
1185 echo "$(SRCDIR)\sounds/e.wav" >> $@
1186 echo "$(SRCDIR)\sounds/f.wav" >> $@
1187 echo "$(SRCDIR)\style.admin_log.css" >> $@
1188 echo "$(SRCDIR)\style.fileedit.css" >> $@
1189 echo "$(SRCDIR)\style.wikiedit.css" >> $@
1190 echo "$(SRCDIR)\tree.js" >> $@
1191 echo "$(SRCDIR)\useredit.js" >> $@
1192 echo "$(SRCDIR)\wiki.wiki" >> $@
1193
1194 "$(OX)\add$O" : "$(OX)\add_.c" "$(OX)\add.h"
1195

Keyboard Shortcuts

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