Fossil SCM

/chat: add embedded view support for .wiki/.md/.pikchr file attachments, with the caveat that the rendering happens in an iframe and thus has some limitations/usability quirks compared to non-iframed content. Added based on feedback from a /chat session.

stephan 2022-11-29 20:59 trunk
Commit fd5298f027f473fb8f25859b97049ebd2315cc942c1216088a2c1625d4490b74
+62 -11
--- src/chat.c
+++ src/chat.c
@@ -135,13 +135,14 @@
135135
** If this setting is defined and is not an empty string, then
136136
** timeline events are posted to the chat as they arrive. The synthesized
137137
** chat messages appear to come from the user identified by this setting,
138138
** not the user on the timeline event.
139139
**
140
-** All chat messages that come from the chat-timeline-user are interpreted
141
-** as text/x-fossil-wiki instead of as text/markdown. For this reason,
142
-** the chat-timeline-user name should probably not be a real user.
140
+** All chat messages that come from the chat-timeline-user are
141
+** interpreted as text/x-fossil-wiki instead of as text/x-markdown.
142
+** For this reason, the chat-timeline-user name should probably not be
143
+** a real user.
143144
*/
144145
/*
145146
** WEBPAGE: chat loadavg-exempt
146147
**
147148
** Start up a browser-based chat session.
@@ -419,14 +420,14 @@
419420
}
420421
db_commit_transaction();
421422
}
422423
423424
/*
424
-** This routine receives raw (user-entered) message text and transforms
425
-** it into HTML that is safe to insert using innerHTML. As of 2021-09-19,
426
-** it does so by using markdown_to_html() to convert markdown-formatted
427
-** zMsg to HTML.
425
+** This routine receives raw (user-entered) message text and
426
+** transforms it into HTML that is safe to insert using innerHTML. As
427
+** of 2021-09-19, it does so by using wiki_convert() or
428
+** markdown_to_html() to convert wiki/markdown-formatted zMsg to HTML.
428429
**
429430
** Space to hold the returned string is obtained from fossil_malloc()
430431
** and must be freed by the caller.
431432
*/
432433
static char *chat_format_to_html(const char *zMsg, int isWiki){
@@ -438,11 +439,11 @@
438439
/* Used for chat-timeline-user. The zMsg is text/x-fossil-wiki. */
439440
Blob bIn;
440441
blob_init(&bIn, zMsg, (int)strlen(zMsg));
441442
wiki_convert(&bIn, &out, WIKI_INLINE);
442443
}else{
443
- /* The common case: zMsg is text/markdown */
444
+ /* The common case: zMsg is text/x-markdown */
444445
Blob bIn;
445446
blob_init(&bIn, zMsg, (int)strlen(zMsg));
446447
markdown_to_html(&bIn, NULL, &out);
447448
}
448449
return blob_str(&out);
@@ -781,17 +782,34 @@
781782
** WEBPAGE: chat-download hidden loadavg-exempt
782783
**
783784
** Download the CHAT.FILE attachment associated with a single chat
784785
** entry. The "name" query parameter begins with an integer that
785786
** identifies the particular chat message. The integer may be followed
786
-** by a / and a filename, which will indicate to the browser to use
787
-** the indicated name when saving the file.
787
+** by a / and a filename, which will (A) indicate to the browser to
788
+** use the indicated name when saving the file and (B) be used to
789
+** guess the mimetype in some particular cases involving the "render"
790
+** flag.
791
+**
792
+** If the "render" URL parameter is provided, the blob has a size
793
+** greater than zero, and blob meets one of the following conditions
794
+** then the fossil-rendered form of that content is returned, rather
795
+** than the original:
796
+**
797
+** - Mimetype is text/x-markdown or text/markdown: emit HTML.
798
+**
799
+** - Mimetype is text/x-fossil-wiki or P("name") ends with ".wiki":
800
+** emit HTML.
801
+**
802
+** - Mimetype is text/x-pikchr or P("name") ends with ".pikchr": emit
803
+** image/svg+xml if rendering succeeds or text/html if rendering
804
+** fails.
788805
*/
789806
void chat_download_webpage(void){
790807
int msgid;
791808
Blob r;
792809
const char *zMime;
810
+ const char *zName = PD("name","0");
793811
login_check_credentials();
794812
if( !g.perm.Chat ){
795813
style_header("Chat Not Authorized");
796814
@ <h1>Not Authorized</h1>
797815
@ <p>You do not have permission to use the chatroom on this
@@ -798,15 +816,48 @@
798816
@ repository.</p>
799817
style_finish_page();
800818
return;
801819
}
802820
chat_create_tables();
803
- msgid = atoi(PD("name","0"));
821
+ msgid = atoi(zName);
804822
blob_zero(&r);
805823
zMime = db_text(0, "SELECT fmime FROM chat wHERE msgid=%d", msgid);
806824
if( zMime==0 ) return;
807825
db_blob(&r, "SELECT file FROM chat WHERE msgid=%d", msgid);
826
+ if( r.nUsed>0 && P("render")!=0 ){
827
+ /* Maybe return fossil-rendered form of the content. */
828
+ Blob r2 = BLOB_INITIALIZER; /* output target for rendering */
829
+ const char * zMime2 = 0; /* adjusted response mimetype */
830
+ if(fossil_strcmp(zMime, "text/x-markdown")==0
831
+ /* Firefox uploads md files with the mimetype text/markdown */
832
+ || fossil_strcmp(zMime, "text/markdown")==0){
833
+ markdown_to_html(&r, 0, &r2);
834
+ safe_html(&r2);
835
+ zMime2 = "text/html";
836
+ }else if(fossil_strcmp(zMime, "text/x-fossil-wiki")==0
837
+ || sqlite3_strglob("*.wiki", zName)==0){
838
+ /* .wiki files get uploaded as application/octet-stream */
839
+ wiki_convert(&r, &r2, 0);
840
+ zMime2 = "text/html";
841
+ }else if(fossil_strcmp(zMime, "text/x-pikchr")==0
842
+ || sqlite3_strglob("*.pikchr",zName)==0){
843
+ /* .pikchr files get uploaded as application/octet-stream */
844
+ const char *zPikchr = blob_str(&r);
845
+ int w = 0, h = 0;
846
+ char *zOut = pikchr(zPikchr, "pikchr", 0, &w, &h);
847
+ if(zOut){
848
+ blob_append(&r2, zOut, -1);
849
+ }
850
+ zMime2 = w>0 ? "image/svg+xml" : "text/html";
851
+ free(zOut);
852
+ }
853
+ if(r2.aData!=0){
854
+ blob_swap(&r, &r2);
855
+ blob_reset(&r2);
856
+ zMime = zMime2;
857
+ }
858
+ }
808859
cgi_set_content_type(zMime);
809860
cgi_set_content(&r);
810861
}
811862
812863
813864
--- src/chat.c
+++ src/chat.c
@@ -135,13 +135,14 @@
135 ** If this setting is defined and is not an empty string, then
136 ** timeline events are posted to the chat as they arrive. The synthesized
137 ** chat messages appear to come from the user identified by this setting,
138 ** not the user on the timeline event.
139 **
140 ** All chat messages that come from the chat-timeline-user are interpreted
141 ** as text/x-fossil-wiki instead of as text/markdown. For this reason,
142 ** the chat-timeline-user name should probably not be a real user.
 
143 */
144 /*
145 ** WEBPAGE: chat loadavg-exempt
146 **
147 ** Start up a browser-based chat session.
@@ -419,14 +420,14 @@
419 }
420 db_commit_transaction();
421 }
422
423 /*
424 ** This routine receives raw (user-entered) message text and transforms
425 ** it into HTML that is safe to insert using innerHTML. As of 2021-09-19,
426 ** it does so by using markdown_to_html() to convert markdown-formatted
427 ** zMsg to HTML.
428 **
429 ** Space to hold the returned string is obtained from fossil_malloc()
430 ** and must be freed by the caller.
431 */
432 static char *chat_format_to_html(const char *zMsg, int isWiki){
@@ -438,11 +439,11 @@
438 /* Used for chat-timeline-user. The zMsg is text/x-fossil-wiki. */
439 Blob bIn;
440 blob_init(&bIn, zMsg, (int)strlen(zMsg));
441 wiki_convert(&bIn, &out, WIKI_INLINE);
442 }else{
443 /* The common case: zMsg is text/markdown */
444 Blob bIn;
445 blob_init(&bIn, zMsg, (int)strlen(zMsg));
446 markdown_to_html(&bIn, NULL, &out);
447 }
448 return blob_str(&out);
@@ -781,17 +782,34 @@
781 ** WEBPAGE: chat-download hidden loadavg-exempt
782 **
783 ** Download the CHAT.FILE attachment associated with a single chat
784 ** entry. The "name" query parameter begins with an integer that
785 ** identifies the particular chat message. The integer may be followed
786 ** by a / and a filename, which will indicate to the browser to use
787 ** the indicated name when saving the file.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
788 */
789 void chat_download_webpage(void){
790 int msgid;
791 Blob r;
792 const char *zMime;
 
793 login_check_credentials();
794 if( !g.perm.Chat ){
795 style_header("Chat Not Authorized");
796 @ <h1>Not Authorized</h1>
797 @ <p>You do not have permission to use the chatroom on this
@@ -798,15 +816,48 @@
798 @ repository.</p>
799 style_finish_page();
800 return;
801 }
802 chat_create_tables();
803 msgid = atoi(PD("name","0"));
804 blob_zero(&r);
805 zMime = db_text(0, "SELECT fmime FROM chat wHERE msgid=%d", msgid);
806 if( zMime==0 ) return;
807 db_blob(&r, "SELECT file FROM chat WHERE msgid=%d", msgid);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
808 cgi_set_content_type(zMime);
809 cgi_set_content(&r);
810 }
811
812
813
--- src/chat.c
+++ src/chat.c
@@ -135,13 +135,14 @@
135 ** If this setting is defined and is not an empty string, then
136 ** timeline events are posted to the chat as they arrive. The synthesized
137 ** chat messages appear to come from the user identified by this setting,
138 ** not the user on the timeline event.
139 **
140 ** All chat messages that come from the chat-timeline-user are
141 ** interpreted as text/x-fossil-wiki instead of as text/x-markdown.
142 ** For this reason, the chat-timeline-user name should probably not be
143 ** a real user.
144 */
145 /*
146 ** WEBPAGE: chat loadavg-exempt
147 **
148 ** Start up a browser-based chat session.
@@ -419,14 +420,14 @@
420 }
421 db_commit_transaction();
422 }
423
424 /*
425 ** This routine receives raw (user-entered) message text and
426 ** transforms it into HTML that is safe to insert using innerHTML. As
427 ** of 2021-09-19, it does so by using wiki_convert() or
428 ** markdown_to_html() to convert wiki/markdown-formatted zMsg to HTML.
429 **
430 ** Space to hold the returned string is obtained from fossil_malloc()
431 ** and must be freed by the caller.
432 */
433 static char *chat_format_to_html(const char *zMsg, int isWiki){
@@ -438,11 +439,11 @@
439 /* Used for chat-timeline-user. The zMsg is text/x-fossil-wiki. */
440 Blob bIn;
441 blob_init(&bIn, zMsg, (int)strlen(zMsg));
442 wiki_convert(&bIn, &out, WIKI_INLINE);
443 }else{
444 /* The common case: zMsg is text/x-markdown */
445 Blob bIn;
446 blob_init(&bIn, zMsg, (int)strlen(zMsg));
447 markdown_to_html(&bIn, NULL, &out);
448 }
449 return blob_str(&out);
@@ -781,17 +782,34 @@
782 ** WEBPAGE: chat-download hidden loadavg-exempt
783 **
784 ** Download the CHAT.FILE attachment associated with a single chat
785 ** entry. The "name" query parameter begins with an integer that
786 ** identifies the particular chat message. The integer may be followed
787 ** by a / and a filename, which will (A) indicate to the browser to
788 ** use the indicated name when saving the file and (B) be used to
789 ** guess the mimetype in some particular cases involving the "render"
790 ** flag.
791 **
792 ** If the "render" URL parameter is provided, the blob has a size
793 ** greater than zero, and blob meets one of the following conditions
794 ** then the fossil-rendered form of that content is returned, rather
795 ** than the original:
796 **
797 ** - Mimetype is text/x-markdown or text/markdown: emit HTML.
798 **
799 ** - Mimetype is text/x-fossil-wiki or P("name") ends with ".wiki":
800 ** emit HTML.
801 **
802 ** - Mimetype is text/x-pikchr or P("name") ends with ".pikchr": emit
803 ** image/svg+xml if rendering succeeds or text/html if rendering
804 ** fails.
805 */
806 void chat_download_webpage(void){
807 int msgid;
808 Blob r;
809 const char *zMime;
810 const char *zName = PD("name","0");
811 login_check_credentials();
812 if( !g.perm.Chat ){
813 style_header("Chat Not Authorized");
814 @ <h1>Not Authorized</h1>
815 @ <p>You do not have permission to use the chatroom on this
@@ -798,15 +816,48 @@
816 @ repository.</p>
817 style_finish_page();
818 return;
819 }
820 chat_create_tables();
821 msgid = atoi(zName);
822 blob_zero(&r);
823 zMime = db_text(0, "SELECT fmime FROM chat wHERE msgid=%d", msgid);
824 if( zMime==0 ) return;
825 db_blob(&r, "SELECT file FROM chat WHERE msgid=%d", msgid);
826 if( r.nUsed>0 && P("render")!=0 ){
827 /* Maybe return fossil-rendered form of the content. */
828 Blob r2 = BLOB_INITIALIZER; /* output target for rendering */
829 const char * zMime2 = 0; /* adjusted response mimetype */
830 if(fossil_strcmp(zMime, "text/x-markdown")==0
831 /* Firefox uploads md files with the mimetype text/markdown */
832 || fossil_strcmp(zMime, "text/markdown")==0){
833 markdown_to_html(&r, 0, &r2);
834 safe_html(&r2);
835 zMime2 = "text/html";
836 }else if(fossil_strcmp(zMime, "text/x-fossil-wiki")==0
837 || sqlite3_strglob("*.wiki", zName)==0){
838 /* .wiki files get uploaded as application/octet-stream */
839 wiki_convert(&r, &r2, 0);
840 zMime2 = "text/html";
841 }else if(fossil_strcmp(zMime, "text/x-pikchr")==0
842 || sqlite3_strglob("*.pikchr",zName)==0){
843 /* .pikchr files get uploaded as application/octet-stream */
844 const char *zPikchr = blob_str(&r);
845 int w = 0, h = 0;
846 char *zOut = pikchr(zPikchr, "pikchr", 0, &w, &h);
847 if(zOut){
848 blob_append(&r2, zOut, -1);
849 }
850 zMime2 = w>0 ? "image/svg+xml" : "text/html";
851 free(zOut);
852 }
853 if(r2.aData!=0){
854 blob_swap(&r, &r2);
855 blob_reset(&r2);
856 zMime = zMime2;
857 }
858 }
859 cgi_set_content_type(zMime);
860 cgi_set_content(&r);
861 }
862
863
864
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -914,23 +914,58 @@
914914
(d.getMinutes()+100).toString().slice(1,3),
915915
' ', dowMap[d.getDay()]
916916
].join('');
917917
};
918918
919
+ /**
920
+ Returns true if this page believes it can embed a view of the
921
+ file wrapped by the given message object, else returns false.
922
+ */
919923
const canEmbedFile = function f(msg){
920924
if(!f.$rx){
921
- f.$rx = /\.((html?)|(txt))$/i;
925
+ f.$rx = /\.((html?)|(txt)|(md)|(wiki)|(pikchr))$/i;
922926
f.$specificTypes = [
923927
'text/plain',
924
- 'text/html'
928
+ 'text/html',
929
+ 'text/x-markdown',
930
+ /* Firefox sends text/markdown when uploading .md files */
931
+ 'text/markdown',
932
+ 'text/x-pikchr',
933
+ 'text/x-fossil-wiki'
934
+ // add more as we discover which ones Firefox won't
935
+ // force the user to try to download.
936
+ ];
937
+ }
938
+ if(msg.fmime){
939
+ if(msg.fmime.startsWith("image/")
940
+ || f.$specificTypes.indexOf(msg.fmime)>=0){
941
+ return true;
942
+ }
943
+ }
944
+ return (msg.fname && f.$rx.test(msg.fname));
945
+ };
946
+
947
+ /**
948
+ Returns true if the given message object "should"
949
+ be embedded in fossil-rendered form instead of
950
+ raw content form. This is only intended to be passed
951
+ message objects for which canEmbedFile() returns true.
952
+ */
953
+ const shouldWikiRenderEmbed = function f(msg){
954
+ if(!f.$rx){
955
+ f.$rx = /\.((md)|(wiki)|(pikchr))$/i;
956
+ f.$specificTypes = [
957
+ 'text/x-markdown',
958
+ 'text/markdown' /* Firefox-uploaded md files */,
959
+ 'text/x-pikchr',
960
+ 'text/x-fossil-wiki'
925961
// add more as we discover which ones Firefox won't
926962
// force the user to try to download.
927963
];
928964
}
929965
if(msg.fmime){
930
- return (msg.fmime.startsWith("image/")
931
- || f.$specificTypes.indexOf(msg.fmime)>=0);
966
+ if(f.$specificTypes.indexOf(msg.fmime)>=0) return true;
932967
}
933968
return msg.fname && f.$rx.test(msg.fname);
934969
};
935970
936971
const adjustIFrameSize = function(msgObj){
@@ -1011,19 +1046,24 @@
10111046
// ^^^ add m.fname to URL to cause downloaded file to have that name.
10121047
"(" + m.fname + " " + m.fsize + " bytes)"
10131048
)
10141049
D.attr(a,'target','_blank');
10151050
D.append(w, a);
1051
+ console.warn("canEmbedFile(",m,") =",canEmbedFile(m));
10161052
if(canEmbedFile(m)){
10171053
/* Add an option to embed HTML attachments in an iframe. The primary
10181054
use case is attached diffs. */
1055
+ const shouldWikiRender = shouldWikiRenderEmbed(m);
1056
+ const downloadArgs = shouldWikiRender ? '?render' : '';
1057
+ console.warn("downloadArgs",downloadArgs,m);
10191058
D.addClass(contentTarget, 'wide');
10201059
const embedTarget = this.e.content;
10211060
const self = this;
10221061
const btnEmbed = D.attr(D.checkbox("1", false), 'id',
10231062
'embed-'+ds.msgid);
1024
- const btnLabel = D.label(btnEmbed, "Embed");
1063
+ const btnLabel = D.label(btnEmbed, shouldWikiRender
1064
+ ? "Embed (fossil-rendered)" : "Embed");
10251065
/* Maintenance reminder: do not disable the toggle
10261066
button while the content is loading because that will
10271067
cause it to get stuck in disabled mode if the browser
10281068
decides that loading the content should prompt the
10291069
user to download it, rather than embed it in the
@@ -1041,11 +1081,11 @@
10411081
D.append(embedTarget, iframe);
10421082
iframe.addEventListener('load', function(){
10431083
self.e.$iframeLoaded = true;
10441084
adjustIFrameSize(self);
10451085
});
1046
- iframe.setAttribute('src', downloadUri);
1086
+ iframe.setAttribute('src', downloadUri + downloadArgs);
10471087
});
10481088
D.append(w, btnEmbed, btnLabel);
10491089
}
10501090
contentTarget.appendChild(w);
10511091
}
10521092
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -914,23 +914,58 @@
914 (d.getMinutes()+100).toString().slice(1,3),
915 ' ', dowMap[d.getDay()]
916 ].join('');
917 };
918
 
 
 
 
919 const canEmbedFile = function f(msg){
920 if(!f.$rx){
921 f.$rx = /\.((html?)|(txt))$/i;
922 f.$specificTypes = [
923 'text/plain',
924 'text/html'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
925 // add more as we discover which ones Firefox won't
926 // force the user to try to download.
927 ];
928 }
929 if(msg.fmime){
930 return (msg.fmime.startsWith("image/")
931 || f.$specificTypes.indexOf(msg.fmime)>=0);
932 }
933 return msg.fname && f.$rx.test(msg.fname);
934 };
935
936 const adjustIFrameSize = function(msgObj){
@@ -1011,19 +1046,24 @@
1011 // ^^^ add m.fname to URL to cause downloaded file to have that name.
1012 "(" + m.fname + " " + m.fsize + " bytes)"
1013 )
1014 D.attr(a,'target','_blank');
1015 D.append(w, a);
 
1016 if(canEmbedFile(m)){
1017 /* Add an option to embed HTML attachments in an iframe. The primary
1018 use case is attached diffs. */
 
 
 
1019 D.addClass(contentTarget, 'wide');
1020 const embedTarget = this.e.content;
1021 const self = this;
1022 const btnEmbed = D.attr(D.checkbox("1", false), 'id',
1023 'embed-'+ds.msgid);
1024 const btnLabel = D.label(btnEmbed, "Embed");
 
1025 /* Maintenance reminder: do not disable the toggle
1026 button while the content is loading because that will
1027 cause it to get stuck in disabled mode if the browser
1028 decides that loading the content should prompt the
1029 user to download it, rather than embed it in the
@@ -1041,11 +1081,11 @@
1041 D.append(embedTarget, iframe);
1042 iframe.addEventListener('load', function(){
1043 self.e.$iframeLoaded = true;
1044 adjustIFrameSize(self);
1045 });
1046 iframe.setAttribute('src', downloadUri);
1047 });
1048 D.append(w, btnEmbed, btnLabel);
1049 }
1050 contentTarget.appendChild(w);
1051 }
1052
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -914,23 +914,58 @@
914 (d.getMinutes()+100).toString().slice(1,3),
915 ' ', dowMap[d.getDay()]
916 ].join('');
917 };
918
919 /**
920 Returns true if this page believes it can embed a view of the
921 file wrapped by the given message object, else returns false.
922 */
923 const canEmbedFile = function f(msg){
924 if(!f.$rx){
925 f.$rx = /\.((html?)|(txt)|(md)|(wiki)|(pikchr))$/i;
926 f.$specificTypes = [
927 'text/plain',
928 'text/html',
929 'text/x-markdown',
930 /* Firefox sends text/markdown when uploading .md files */
931 'text/markdown',
932 'text/x-pikchr',
933 'text/x-fossil-wiki'
934 // add more as we discover which ones Firefox won't
935 // force the user to try to download.
936 ];
937 }
938 if(msg.fmime){
939 if(msg.fmime.startsWith("image/")
940 || f.$specificTypes.indexOf(msg.fmime)>=0){
941 return true;
942 }
943 }
944 return (msg.fname && f.$rx.test(msg.fname));
945 };
946
947 /**
948 Returns true if the given message object "should"
949 be embedded in fossil-rendered form instead of
950 raw content form. This is only intended to be passed
951 message objects for which canEmbedFile() returns true.
952 */
953 const shouldWikiRenderEmbed = function f(msg){
954 if(!f.$rx){
955 f.$rx = /\.((md)|(wiki)|(pikchr))$/i;
956 f.$specificTypes = [
957 'text/x-markdown',
958 'text/markdown' /* Firefox-uploaded md files */,
959 'text/x-pikchr',
960 'text/x-fossil-wiki'
961 // add more as we discover which ones Firefox won't
962 // force the user to try to download.
963 ];
964 }
965 if(msg.fmime){
966 if(f.$specificTypes.indexOf(msg.fmime)>=0) return true;
 
967 }
968 return msg.fname && f.$rx.test(msg.fname);
969 };
970
971 const adjustIFrameSize = function(msgObj){
@@ -1011,19 +1046,24 @@
1046 // ^^^ add m.fname to URL to cause downloaded file to have that name.
1047 "(" + m.fname + " " + m.fsize + " bytes)"
1048 )
1049 D.attr(a,'target','_blank');
1050 D.append(w, a);
1051 console.warn("canEmbedFile(",m,") =",canEmbedFile(m));
1052 if(canEmbedFile(m)){
1053 /* Add an option to embed HTML attachments in an iframe. The primary
1054 use case is attached diffs. */
1055 const shouldWikiRender = shouldWikiRenderEmbed(m);
1056 const downloadArgs = shouldWikiRender ? '?render' : '';
1057 console.warn("downloadArgs",downloadArgs,m);
1058 D.addClass(contentTarget, 'wide');
1059 const embedTarget = this.e.content;
1060 const self = this;
1061 const btnEmbed = D.attr(D.checkbox("1", false), 'id',
1062 'embed-'+ds.msgid);
1063 const btnLabel = D.label(btnEmbed, shouldWikiRender
1064 ? "Embed (fossil-rendered)" : "Embed");
1065 /* Maintenance reminder: do not disable the toggle
1066 button while the content is loading because that will
1067 cause it to get stuck in disabled mode if the browser
1068 decides that loading the content should prompt the
1069 user to download it, rather than embed it in the
@@ -1041,11 +1081,11 @@
1081 D.append(embedTarget, iframe);
1082 iframe.addEventListener('load', function(){
1083 self.e.$iframeLoaded = true;
1084 adjustIFrameSize(self);
1085 });
1086 iframe.setAttribute('src', downloadUri + downloadArgs);
1087 });
1088 D.append(w, btnEmbed, btnLabel);
1089 }
1090 contentTarget.appendChild(w);
1091 }
1092
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,10 +1,14 @@
11
<title>Change Log</title>
22
33
<h2 id='v2_21'>Changes for version 2.21 (pending)</h2>
44
* Add the ability to put text descriptions on ticket report formats.
55
* Upgrade the test-find-pivot command to the [/help/merge-base|merge-base command].
6
+ * The [/help?cmd=/chat|/chat page] can now embed fossil-rendered
7
+ views of wiki/markdown/pikchr file attachments with the caveat that such
8
+ embedding happens in an iframe and thus does not inherit styles and such
9
+ from the containing browser window.
610
711
<h2 id='v2_20'>Changes for version 2.20 (2022-11-16)</h2>
812
* Added the [/help?cmd=chat-timeline-user|chat-timeline-user setting]. If
913
it is not an empty string, then any changes that would appear on the timeline
1014
are announced in [./chat.md|the chat room].
1115
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,10 +1,14 @@
1 <title>Change Log</title>
2
3 <h2 id='v2_21'>Changes for version 2.21 (pending)</h2>
4 * Add the ability to put text descriptions on ticket report formats.
5 * Upgrade the test-find-pivot command to the [/help/merge-base|merge-base command].
 
 
 
 
6
7 <h2 id='v2_20'>Changes for version 2.20 (2022-11-16)</h2>
8 * Added the [/help?cmd=chat-timeline-user|chat-timeline-user setting]. If
9 it is not an empty string, then any changes that would appear on the timeline
10 are announced in [./chat.md|the chat room].
11
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,10 +1,14 @@
1 <title>Change Log</title>
2
3 <h2 id='v2_21'>Changes for version 2.21 (pending)</h2>
4 * Add the ability to put text descriptions on ticket report formats.
5 * Upgrade the test-find-pivot command to the [/help/merge-base|merge-base command].
6 * The [/help?cmd=/chat|/chat page] can now embed fossil-rendered
7 views of wiki/markdown/pikchr file attachments with the caveat that such
8 embedding happens in an iframe and thus does not inherit styles and such
9 from the containing browser window.
10
11 <h2 id='v2_20'>Changes for version 2.20 (2022-11-16)</h2>
12 * Added the [/help?cmd=chat-timeline-user|chat-timeline-user setting]. If
13 it is not an empty string, then any changes that would appear on the timeline
14 are announced in [./chat.md|the chat room].
15

Keyboard Shortcuts

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