123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
** For maximum efficiency, it is best to choose the longest delay that
** does not cause timeouts in intermediate proxies or web server.
*/
/*
** SETTING: chat-alert-sound width=10
**
** This is the name of the builtin sound file to use for the alert tone.
** The value must be the name of one of a builtin WAV file.
*/
/*
** WEBPAGE: chat
**
** Start up a browser-based chat session.
**
** This is the main page that humans use to access the chatroom. Simply
|
|
|
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
** For maximum efficiency, it is best to choose the longest delay that
** does not cause timeouts in intermediate proxies or web server.
*/
/*
** SETTING: chat-alert-sound width=10
**
** This is the name of the builtin sound file to use for the alert tone.
** The value must be the name of a builtin WAV file.
*/
/*
** WEBPAGE: chat
**
** Start up a browser-based chat session.
**
** This is the main page that humans use to access the chatroom. Simply
|
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
zProjectName = db_get("project-name","Unnamed project");
style_set_current_feature("chat");
style_header("Chat");
@ <form accept-encoding="utf-8" id="chat-form" autocomplete="off">
@ <div id='chat-input-area'>
@ <div id='chat-input-line'>
@ <input type="text" name="msg" id="chat-input-single" \
@ placeholder="Type message for %h(zProjectName)." autocomplete="off">
@ <textarea rows="8" id="chat-input-multi" \
@ placeholder="Type message for %h(zProjectName). Ctrl-Enter sends it." \
@ class="hidden"></textarea>
@ <input type="submit" value="Send" id="chat-message-submit">
@ <span id="chat-settings-button" class="settings-icon" \
@ aria-label="Settings..." aria-haspopup="true" ></span>
@ </div>
@ <div id='chat-input-file-area'>
@ <div class='file-selection-wrapper'>
|
|
>
|
|
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
zProjectName = db_get("project-name","Unnamed project");
style_set_current_feature("chat");
style_header("Chat");
@ <form accept-encoding="utf-8" id="chat-form" autocomplete="off">
@ <div id='chat-input-area'>
@ <div id='chat-input-line'>
@ <input type="text" name="msg" id="chat-input-single" \
@ placeholder="Type markdown-formatted message for %h(zProjectName)." \
@ autocomplete="off">
@ <textarea rows="8" id="chat-input-multi" \
@ placeholder="Type markdown-formatted message for %h(zProjectName). Ctrl-Enter sends it." \
@ class="hidden"></textarea>
@ <input type="submit" value="Send" id="chat-message-submit">
@ <span id="chat-settings-button" class="settings-icon" \
@ aria-label="Settings..." aria-haspopup="true" ></span>
@ </div>
@ <div id='chat-input-file-area'>
@ <div class='file-selection-wrapper'>
|
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
|
}else{
CX("}");
}
fossil_free(zTime);
}
/*
** WEBPAGE: chat-send
**
** This page receives (via XHR) a new chat-message and/or a new file
** to be entered into the chat history.
**
** On success it responds with an empty response: the new message
** should be fetched via /chat-poll. On error, e.g. login expiry,
** it emits a JSON response in the same form as described for
|
|
|
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
|
}else{
CX("}");
}
fossil_free(zTime);
}
/*
** WEBPAGE: chat-send hidden
**
** This page receives (via XHR) a new chat-message and/or a new file
** to be entered into the chat history.
**
** On success it responds with an empty response: the new message
** should be fetched via /chat-poll. On error, e.g. login expiry,
** it emits a JSON response in the same form as described for
|
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
|
blob_reset(&b);
}
db_commit_transaction();
}
/*
** This routine receives raw (user-entered) message text and transforms
** it into HTML that is safe to insert using innerHTML.
**
** * HTML in the original text is escaped.
**
** * Hyperlinks are identified and tagged. Hyperlinks are:
**
** - Undelimited text of the form https:... or http:...
** - Any text enclosed within [...]
**
** Space to hold the returned string is obtained from fossil_malloc()
** and must be freed by the caller.
*/
static char *chat_format_to_html(const char *zMsg){
char *zSafe = mprintf("%h", zMsg);
int i, j, k;
Blob out;
char zClose[20];
blob_init(&out, 0, 0);
for(i=j=0; zSafe[i]; i++){
if( zSafe[i]=='[' ){
for(k=i+1; zSafe[k] && zSafe[k]!=']'; k++){}
if( zSafe[k]==']' ){
zSafe[k] = 0;
if( j<i ){
blob_append(&out, zSafe + j, i-j);
j = i;
}
blob_append_char(&out, '[');
wiki_resolve_hyperlink(&out,
WIKI_NOBADLINKS|WIKI_TARGET_BLANK|WIKI_NOBRACKET,
zSafe+i+1, zClose, sizeof(zClose), zSafe, 0);
zSafe[k] = ']';
j++;
blob_append(&out, zSafe + j, k - j);
blob_append(&out, zClose, -1);
blob_append_char(&out, ']');
i = k;
j = k+1;
continue;
}
}else if( zSafe[i]=='h'
&& (strncmp(zSafe+i,"http:",5)==0
|| strncmp(zSafe+i,"https:",6)==0) ){
for(k=i+1; zSafe[k] && !fossil_isspace(zSafe[k]); k++){}
if( k>i+7 ){
char c = zSafe[k];
if( !fossil_isalnum(zSafe[k-1]) && zSafe[k-1]!='/' ){
k--;
c = zSafe[k];
}
if( j<i ){
blob_append(&out, zSafe + j, i-j);
j = i;
}
zSafe[k] = 0;
wiki_resolve_hyperlink(&out, WIKI_NOBADLINKS|WIKI_TARGET_BLANK,
zSafe+i, zClose, sizeof(zClose), zSafe, 0);
zSafe[k] = c;
blob_append(&out, zSafe + j, k - j);
blob_append(&out, zClose, -1);
i = j = k;
continue;
}
}
}
if( j<i ){
blob_append(&out, zSafe+j, j-i);
}
fossil_free(zSafe);
return blob_str(&out);
}
/*
** COMMAND: test-chat-formatter
**
** Usage: %fossil test-chat-formatter STRING ...
|
|
|
<
|
<
<
<
<
<
<
<
|
<
<
<
<
<
|
<
<
<
|
<
<
<
<
<
<
|
<
<
<
<
>
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
|
blob_reset(&b);
}
db_commit_transaction();
}
/*
** This routine receives raw (user-entered) message text and transforms
** it into HTML that is safe to insert using innerHTML. As of 2021-09-19,
** it does so by using markdown_to_html() to convert markdown-formatted
** zMsg to HTML.
**
** Space to hold the returned string is obtained from fossil_malloc()
** and must be freed by the caller.
*/
static char *chat_format_to_html(const char *zMsg){
Blob out;
blob_init(&out, "", 0);
if(*zMsg){
Blob bIn;
blob_init(&bIn, zMsg, (int)strlen(zMsg));
markdown_to_html(&bIn, NULL, &out);
}
return blob_str(&out);
}
/*
** COMMAND: test-chat-formatter
**
** Usage: %fossil test-chat-formatter STRING ...
|
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
|
zOut = chat_format_to_html(g.argv[i]);
fossil_print("[%d]: %s\n", i, zOut);
fossil_free(zOut);
}
}
/*
** WEBPAGE: chat-poll
**
** The chat page generated by /chat using an XHR to this page to
** request new chat content. A typical invocation is:
**
** /chat-poll/N
** /chat-poll?name=N
**
|
|
|
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
|
zOut = chat_format_to_html(g.argv[i]);
fossil_print("[%d]: %s\n", i, zOut);
fossil_free(zOut);
}
}
/*
** WEBPAGE: chat-poll hidden
**
** The chat page generated by /chat using an XHR to this page to
** request new chat content. A typical invocation is:
**
** /chat-poll/N
** /chat-poll?name=N
**
|
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
|
db_finalize(&q1);
blob_append(&json, "\n]}", 3);
cgi_set_content(&json);
return;
}
/*
** WEBPAGE: chat-download
**
** Download the CHAT.FILE attachment associated with a single chat
** entry. The "name" query parameter begins with an integer that
** identifies the particular chat message. The integer may be followed
** by a / and a filename, which will indicate to the browser to use
** the indicated name when saving the file.
*/
|
|
|
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
|
db_finalize(&q1);
blob_append(&json, "\n]}", 3);
cgi_set_content(&json);
return;
}
/*
** WEBPAGE: chat-download hidden
**
** Download the CHAT.FILE attachment associated with a single chat
** entry. The "name" query parameter begins with an integer that
** identifies the particular chat message. The integer may be followed
** by a / and a filename, which will indicate to the browser to use
** the indicated name when saving the file.
*/
|
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
|
db_blob(&r, "SELECT file FROM chat WHERE msgid=%d", msgid);
cgi_set_content_type(zMime);
cgi_set_content(&r);
}
/*
** WEBPAGE: chat-delete
**
** Delete the chat entry identified by the name query parameter.
** Invoking fetch("chat-delete/"+msgid) from javascript in the client
** will delete a chat entry from the CHAT table.
**
** This routine both deletes the identified chat entry and also inserts
** a new entry with the current timestamp and with:
|
|
|
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
|
db_blob(&r, "SELECT file FROM chat WHERE msgid=%d", msgid);
cgi_set_content_type(zMime);
cgi_set_content(&r);
}
/*
** WEBPAGE: chat-delete hidden
**
** Delete the chat entry identified by the name query parameter.
** Invoking fetch("chat-delete/"+msgid) from javascript in the client
** will delete a chat entry from the CHAT table.
**
** This routine both deletes the identified chat entry and also inserts
** a new entry with the current timestamp and with:
|