Fossil SCM

Add chat search for message by ID using search term #NNNN.

stephan 2024-07-02 10:33 fts5-chat-search
Commit 3449350042fa7fb74de4cb0b1829fc62f63b83c2ec204264f7d776eaa3a55865
+19 -8
--- src/chat.c
+++ src/chat.c
@@ -755,16 +755,15 @@
755755
/*
756756
** WEBPAGE: chat-query hidden loadavg-exempt
757757
*/
758758
void chat_query_webpage(void){
759759
Blob json; /* The json to be constructed and returned */
760
- int nLimit = atoi(PD("n","500"));
761
- const char *zQuery = PD("q", "");
762
- int iFirst = atoi(PD("i","0"));
763
-
764760
Blob sql = empty_blob;
765761
Stmt q1;
762
+ int nLimit = atoi(PD("n","500"));
763
+ int iFirst = atoi(PD("i","0"));
764
+ const char *zQuery = PD("q", "");
766765
i64 iMin = 0;
767766
i64 iMax = 0;
768767
769768
login_check_credentials();
770769
if( !g.perm.Chat ) {
@@ -775,19 +774,31 @@
775774
cgi_set_content_type("application/json");
776775
777776
if( zQuery[0] ){
778777
iMax = db_int64(0, "SELECT max(msgid) FROM chat");
779778
iMin = db_int64(0, "SELECT min(msgid) FROM chat");
780
- blob_append_sql(&sql,
779
+ if( '#'==zQuery[0] ){
780
+ /* Assume we're looking for an exact msgid match. */
781
+ ++zQuery;
782
+ blob_append_sql(&sql,
783
+ "SELECT msgid, datetime(mtime), xfrom, "
784
+ " xmsg, octet_length(file), fname, fmime, mdel, lmtime "
785
+ " FROM chat WHERE msgid=+%Q",
786
+ zQuery
787
+ );
788
+ }else{
789
+ blob_append_sql(&sql,
781790
"SELECT * FROM ("
782791
"SELECT c.msgid, datetime(c.mtime), c.xfrom, "
783792
" highlight(chatfts1, 0, '<span class=\"match\">', '</span>'), "
784
- " octet_length(c.file), c.fname, c.fmime, c.mdel, c.lmtime"
785
- " FROM chatfts1(%Q) f, chat c WHERE f.rowid=c.msgid "
793
+ " octet_length(c.file), c.fname, c.fmime, c.mdel, c.lmtime "
794
+ " FROM chatfts1(%Q) f, chat c "
795
+ " WHERE f.rowid=c.msgid"
786796
" ORDER BY f.rowid DESC LIMIT %d"
787797
") ORDER BY 1 ASC", zQuery, nLimit
788
- );
798
+ );
799
+ }
789800
}else{
790801
blob_append_sql(&sql,
791802
"SELECT msgid, datetime(mtime), xfrom, "
792803
" xmsg, octet_length(file), fname, fmime, mdel, lmtime"
793804
" FROM chat WHERE msgid>=%d LIMIT %d",
794805
--- src/chat.c
+++ src/chat.c
@@ -755,16 +755,15 @@
755 /*
756 ** WEBPAGE: chat-query hidden loadavg-exempt
757 */
758 void chat_query_webpage(void){
759 Blob json; /* The json to be constructed and returned */
760 int nLimit = atoi(PD("n","500"));
761 const char *zQuery = PD("q", "");
762 int iFirst = atoi(PD("i","0"));
763
764 Blob sql = empty_blob;
765 Stmt q1;
 
 
 
766 i64 iMin = 0;
767 i64 iMax = 0;
768
769 login_check_credentials();
770 if( !g.perm.Chat ) {
@@ -775,19 +774,31 @@
775 cgi_set_content_type("application/json");
776
777 if( zQuery[0] ){
778 iMax = db_int64(0, "SELECT max(msgid) FROM chat");
779 iMin = db_int64(0, "SELECT min(msgid) FROM chat");
780 blob_append_sql(&sql,
 
 
 
 
 
 
 
 
 
 
781 "SELECT * FROM ("
782 "SELECT c.msgid, datetime(c.mtime), c.xfrom, "
783 " highlight(chatfts1, 0, '<span class=\"match\">', '</span>'), "
784 " octet_length(c.file), c.fname, c.fmime, c.mdel, c.lmtime"
785 " FROM chatfts1(%Q) f, chat c WHERE f.rowid=c.msgid "
 
786 " ORDER BY f.rowid DESC LIMIT %d"
787 ") ORDER BY 1 ASC", zQuery, nLimit
788 );
 
789 }else{
790 blob_append_sql(&sql,
791 "SELECT msgid, datetime(mtime), xfrom, "
792 " xmsg, octet_length(file), fname, fmime, mdel, lmtime"
793 " FROM chat WHERE msgid>=%d LIMIT %d",
794
--- src/chat.c
+++ src/chat.c
@@ -755,16 +755,15 @@
755 /*
756 ** WEBPAGE: chat-query hidden loadavg-exempt
757 */
758 void chat_query_webpage(void){
759 Blob json; /* The json to be constructed and returned */
 
 
 
 
760 Blob sql = empty_blob;
761 Stmt q1;
762 int nLimit = atoi(PD("n","500"));
763 int iFirst = atoi(PD("i","0"));
764 const char *zQuery = PD("q", "");
765 i64 iMin = 0;
766 i64 iMax = 0;
767
768 login_check_credentials();
769 if( !g.perm.Chat ) {
@@ -775,19 +774,31 @@
774 cgi_set_content_type("application/json");
775
776 if( zQuery[0] ){
777 iMax = db_int64(0, "SELECT max(msgid) FROM chat");
778 iMin = db_int64(0, "SELECT min(msgid) FROM chat");
779 if( '#'==zQuery[0] ){
780 /* Assume we're looking for an exact msgid match. */
781 ++zQuery;
782 blob_append_sql(&sql,
783 "SELECT msgid, datetime(mtime), xfrom, "
784 " xmsg, octet_length(file), fname, fmime, mdel, lmtime "
785 " FROM chat WHERE msgid=+%Q",
786 zQuery
787 );
788 }else{
789 blob_append_sql(&sql,
790 "SELECT * FROM ("
791 "SELECT c.msgid, datetime(c.mtime), c.xfrom, "
792 " highlight(chatfts1, 0, '<span class=\"match\">', '</span>'), "
793 " octet_length(c.file), c.fname, c.fmime, c.mdel, c.lmtime "
794 " FROM chatfts1(%Q) f, chat c "
795 " WHERE f.rowid=c.msgid"
796 " ORDER BY f.rowid DESC LIMIT %d"
797 ") ORDER BY 1 ASC", zQuery, nLimit
798 );
799 }
800 }else{
801 blob_append_sql(&sql,
802 "SELECT msgid, datetime(mtime), xfrom, "
803 " xmsg, octet_length(file), fname, fmime, mdel, lmtime"
804 " FROM chat WHERE msgid>=%d LIMIT %d",
805
--- src/fossil.fetch.js
+++ src/fossil.fetch.js
@@ -234,11 +234,11 @@
234234
urlTransform() must refer to a function which accepts a relative path
235235
to the same site as fetch() is served from and an optional set of
236236
URL parameters to pass with it (in the form a of a string
237237
("a=b&c=d...") or an object of key/value pairs (which it converts
238238
to such a string), and returns the resulting URL or URI as a string.
239
-*/
239
+*/
240240
fossil.fetch.urlTransform = (u,p)=>fossil.repoUrl(u,p);
241241
fossil.fetch.beforesend = function(){};
242242
fossil.fetch.aftersend = function(){};
243243
fossil.fetch.timeout = 15000/* Default timeout, in ms. */;
244244
})(window.fossil);
245245
--- src/fossil.fetch.js
+++ src/fossil.fetch.js
@@ -234,11 +234,11 @@
234 urlTransform() must refer to a function which accepts a relative path
235 to the same site as fetch() is served from and an optional set of
236 URL parameters to pass with it (in the form a of a string
237 ("a=b&c=d...") or an object of key/value pairs (which it converts
238 to such a string), and returns the resulting URL or URI as a string.
239 */
240 fossil.fetch.urlTransform = (u,p)=>fossil.repoUrl(u,p);
241 fossil.fetch.beforesend = function(){};
242 fossil.fetch.aftersend = function(){};
243 fossil.fetch.timeout = 15000/* Default timeout, in ms. */;
244 })(window.fossil);
245
--- src/fossil.fetch.js
+++ src/fossil.fetch.js
@@ -234,11 +234,11 @@
234 urlTransform() must refer to a function which accepts a relative path
235 to the same site as fetch() is served from and an optional set of
236 URL parameters to pass with it (in the form a of a string
237 ("a=b&c=d...") or an object of key/value pairs (which it converts
238 to such a string), and returns the resulting URL or URI as a string.
239 */
240 fossil.fetch.urlTransform = (u,p)=>fossil.repoUrl(u,p);
241 fossil.fetch.beforesend = function(){};
242 fossil.fetch.aftersend = function(){};
243 fossil.fetch.timeout = 15000/* Default timeout, in ms. */;
244 })(window.fossil);
245
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -176,16 +176,18 @@
176176
activeUser: undefined,
177177
match: function(uname){
178178
return this.activeUser===uname || !this.activeUser;
179179
}
180180
},
181
- /** Gets (no args) or sets (1 arg) the current input text field
182
- value, taking into account single- vs multi-line input. The
183
- getter returns a string and the setter returns this
184
- object. As a special case, if arguments[0] is a boolean
185
- value, it behaves like a getter and, if arguments[0]===true
186
- it clears the input field before returning. */
181
+ /**
182
+ Gets (no args) or sets (1 arg) the current input text field
183
+ value, taking into account single- vs multi-line input. The
184
+ getter returns a trim()'d string and the setter returns this
185
+ object. As a special case, if arguments[0] is a boolean
186
+ value, it behaves like a getter and, if arguments[0]===true
187
+ it clears the input field before returning.
188
+ */
187189
inputValue: function(/*string newValue | bool clearInputField*/){
188190
const e = this.inputElement();
189191
if(arguments.length && 'boolean'!==typeof arguments[0]){
190192
if(e.isContentEditable) e.innerText = arguments[0];
191193
else e.value = arguments[0];
@@ -194,11 +196,11 @@
194196
const rc = e.isContentEditable ? e.innerText : e.value;
195197
if( true===arguments[0] ){
196198
if(e.isContentEditable) e.innerText = '';
197199
else e.value = '';
198200
}
199
- return rc;
201
+ return rc && rc.trim();
200202
},
201203
/** Asks the current user input field to take focus. Returns this. */
202204
inputFocus: function(){
203205
this.inputElement().focus();
204206
return this;
@@ -2378,11 +2380,12 @@
23782380
result elements.
23792381
*/
23802382
Chat.clearSearch = function(addInstructions=false){
23812383
const e = D.clearElement( this.e.searchContent );
23822384
if(addInstructions){
2383
- D.append(e, "Enter search terms in the message field.");
2385
+ D.append(e, "Enter search terms in the message field. "+
2386
+ "Use #NNNNN to search for the message with ID NNNNN.");
23842387
}
23852388
return e;
23862389
};
23872390
Chat.clearSearch(true);
23882391
/**
@@ -2392,13 +2395,15 @@
23922395
Chat.submitSearch = function(){
23932396
const term = this.inputValue(true);
23942397
const eMsgTgt = this.clearSearch(true);
23952398
if( !term ) return;
23962399
D.append( eMsgTgt, "Searching for ",term," ...");
2400
+ const fd = new FormData();
2401
+ fd.set('q', term);
23972402
F.fetch(
23982403
"chat-query", {
2399
- urlParams: {q: term},
2404
+ payload: fd,
24002405
responseType: 'json',
24012406
onload:function(jx){
24022407
let previd = 0;
24032408
D.clearElement(eMsgTgt);
24042409
jx.msgs.forEach((m)=>{
24052410
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -176,16 +176,18 @@
176 activeUser: undefined,
177 match: function(uname){
178 return this.activeUser===uname || !this.activeUser;
179 }
180 },
181 /** Gets (no args) or sets (1 arg) the current input text field
182 value, taking into account single- vs multi-line input. The
183 getter returns a string and the setter returns this
184 object. As a special case, if arguments[0] is a boolean
185 value, it behaves like a getter and, if arguments[0]===true
186 it clears the input field before returning. */
 
 
187 inputValue: function(/*string newValue | bool clearInputField*/){
188 const e = this.inputElement();
189 if(arguments.length && 'boolean'!==typeof arguments[0]){
190 if(e.isContentEditable) e.innerText = arguments[0];
191 else e.value = arguments[0];
@@ -194,11 +196,11 @@
194 const rc = e.isContentEditable ? e.innerText : e.value;
195 if( true===arguments[0] ){
196 if(e.isContentEditable) e.innerText = '';
197 else e.value = '';
198 }
199 return rc;
200 },
201 /** Asks the current user input field to take focus. Returns this. */
202 inputFocus: function(){
203 this.inputElement().focus();
204 return this;
@@ -2378,11 +2380,12 @@
2378 result elements.
2379 */
2380 Chat.clearSearch = function(addInstructions=false){
2381 const e = D.clearElement( this.e.searchContent );
2382 if(addInstructions){
2383 D.append(e, "Enter search terms in the message field.");
 
2384 }
2385 return e;
2386 };
2387 Chat.clearSearch(true);
2388 /**
@@ -2392,13 +2395,15 @@
2392 Chat.submitSearch = function(){
2393 const term = this.inputValue(true);
2394 const eMsgTgt = this.clearSearch(true);
2395 if( !term ) return;
2396 D.append( eMsgTgt, "Searching for ",term," ...");
 
 
2397 F.fetch(
2398 "chat-query", {
2399 urlParams: {q: term},
2400 responseType: 'json',
2401 onload:function(jx){
2402 let previd = 0;
2403 D.clearElement(eMsgTgt);
2404 jx.msgs.forEach((m)=>{
2405
--- src/fossil.page.chat.js
+++ src/fossil.page.chat.js
@@ -176,16 +176,18 @@
176 activeUser: undefined,
177 match: function(uname){
178 return this.activeUser===uname || !this.activeUser;
179 }
180 },
181 /**
182 Gets (no args) or sets (1 arg) the current input text field
183 value, taking into account single- vs multi-line input. The
184 getter returns a trim()'d string and the setter returns this
185 object. As a special case, if arguments[0] is a boolean
186 value, it behaves like a getter and, if arguments[0]===true
187 it clears the input field before returning.
188 */
189 inputValue: function(/*string newValue | bool clearInputField*/){
190 const e = this.inputElement();
191 if(arguments.length && 'boolean'!==typeof arguments[0]){
192 if(e.isContentEditable) e.innerText = arguments[0];
193 else e.value = arguments[0];
@@ -194,11 +196,11 @@
196 const rc = e.isContentEditable ? e.innerText : e.value;
197 if( true===arguments[0] ){
198 if(e.isContentEditable) e.innerText = '';
199 else e.value = '';
200 }
201 return rc && rc.trim();
202 },
203 /** Asks the current user input field to take focus. Returns this. */
204 inputFocus: function(){
205 this.inputElement().focus();
206 return this;
@@ -2378,11 +2380,12 @@
2380 result elements.
2381 */
2382 Chat.clearSearch = function(addInstructions=false){
2383 const e = D.clearElement( this.e.searchContent );
2384 if(addInstructions){
2385 D.append(e, "Enter search terms in the message field. "+
2386 "Use #NNNNN to search for the message with ID NNNNN.");
2387 }
2388 return e;
2389 };
2390 Chat.clearSearch(true);
2391 /**
@@ -2392,13 +2395,15 @@
2395 Chat.submitSearch = function(){
2396 const term = this.inputValue(true);
2397 const eMsgTgt = this.clearSearch(true);
2398 if( !term ) return;
2399 D.append( eMsgTgt, "Searching for ",term," ...");
2400 const fd = new FormData();
2401 fd.set('q', term);
2402 F.fetch(
2403 "chat-query", {
2404 payload: fd,
2405 responseType: 'json',
2406 onload:function(jx){
2407 let previd = 0;
2408 D.clearElement(eMsgTgt);
2409 jx.msgs.forEach((m)=>{
2410

Keyboard Shortcuts

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