Fossil SCM

Two new notification options: "n" means to be notified for new forum threads only and "r" means to be notified for forum posts that are a reply to a post made by the user.

drh 2023-08-05 21:18 trunk
Commit d4361f6a94725aa36f30f4d1444963da2732dcc86bd5261f26dd495021a5e2b1
1 file changed +94 -10
+94 -10
--- src/alerts.c
+++ src/alerts.c
@@ -47,12 +47,15 @@
4747
@ -- The ssub field is a string where each character indicates a particular
4848
@ -- type of event to subscribe to. Choices:
4949
@ -- a - Announcements
5050
@ -- c - Check-ins
5151
@ -- f - Forum posts
52
+@ -- n - New forum threads
53
+@ -- r - Replies to my own forum posts
5254
@ -- t - Ticket changes
5355
@ -- w - Wiki changes
56
+@ -- x - Edits to forum posts
5457
@ -- Probably different codes will be added in the future. In the future
5558
@ -- we might also add a separate table that allows subscribing to email
5659
@ -- notifications for specific branches or tags or tickets.
5760
@ --
5861
@ CREATE TABLE repository.subscriber(
@@ -1556,10 +1559,12 @@
15561559
if( suname==0 && needCaptcha==0 && !g.perm.Admin ) suname = g.zLogin;
15571560
if( suname && suname[0]==0 ) suname = 0;
15581561
if( PB("sa") ) ssub[nsub++] = 'a';
15591562
if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c';
15601563
if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f';
1564
+ if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n';
1565
+ if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r';
15611566
if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't';
15621567
if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w';
15631568
if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x';
15641569
ssub[nsub] = 0;
15651570
zCode = db_text(0,
@@ -1618,10 +1623,12 @@
16181623
** come from a prior Submit of the form) then default all of the
16191624
** subscription options to "on" */
16201625
cgi_set_parameter_nocopy("sa","1",1);
16211626
if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1);
16221627
if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1);
1628
+ if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1);
1629
+ if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1);
16231630
if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1);
16241631
if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1);
16251632
}
16261633
@ <p>To receive email notifications for changes to this
16271634
@ repository, fill out the form below and press the "Submit" button.</p>
@@ -1675,13 +1682,17 @@
16751682
@ <label><input type="checkbox" name="sc" %s(PCK("sc"))> \
16761683
@ Check-ins</label><br>
16771684
}
16781685
if( g.perm.RdForum ){
16791686
@ <label><input type="checkbox" name="sf" %s(PCK("sf"))> \
1680
- @ Forum Posts</label><br>
1687
+ @ All Forum Posts</label><br>
1688
+ @ <label><input type="checkbox" name="sn" %s(PCK("sn"))> \
1689
+ @ New Forum Threads</label><br>
1690
+ @ <label><input type="checkbox" name="sr" %s(PCK("sr"))> \
1691
+ @ Replies To My Forum Posts</label><br>
16811692
@ <label><input type="checkbox" name="sx" %s(PCK("sx"))> \
1682
- @ Forum Edits</label><br>
1693
+ @ Edits To Forum Posts</label><br>
16831694
}
16841695
if( g.perm.RdTkt ){
16851696
@ <label><input type="checkbox" name="st" %s(PCK("st"))> \
16861697
@ Ticket changes</label><br>
16871698
}
@@ -1799,10 +1810,11 @@
17991810
*/
18001811
void alert_page(void){
18011812
const char *zName = 0; /* Value of the name= query parameter */
18021813
Stmt q; /* For querying the database */
18031814
int sa, sc, sf, st, sw, sx; /* Types of notifications requested */
1815
+ int sn, sr;
18041816
int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */
18051817
int isLogin; /* True if logged in as an individual */
18061818
const char *ssub = 0; /* Subscription flags */
18071819
const char *semail = 0; /* Email address */
18081820
const char *smip; /* */
@@ -1855,10 +1867,12 @@
18551867
sdigest = PB("sdigest");
18561868
semail = P("semail");
18571869
if( PB("sa") ) newSsub[nsub++] = 'a';
18581870
if( g.perm.Read && PB("sc") ) newSsub[nsub++] = 'c';
18591871
if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f';
1872
+ if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n';
1873
+ if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r';
18601874
if( g.perm.RdTkt && PB("st") ) newSsub[nsub++] = 't';
18611875
if( g.perm.RdWiki && PB("sw") ) newSsub[nsub++] = 'w';
18621876
if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x';
18631877
newSsub[nsub] = 0;
18641878
ssub = newSsub;
@@ -1951,10 +1965,12 @@
19511965
sverified = db_column_int(&q, 1);
19521966
}
19531967
sa = strchr(ssub,'a')!=0;
19541968
sc = strchr(ssub,'c')!=0;
19551969
sf = strchr(ssub,'f')!=0;
1970
+ sn = strchr(ssub,'n')!=0;
1971
+ sr = strchr(ssub,'r')!=0;
19561972
st = strchr(ssub,'t')!=0;
19571973
sw = strchr(ssub,'w')!=0;
19581974
sx = strchr(ssub,'x')!=0;
19591975
smip = db_column_text(&q, 5);
19601976
mtime = db_column_text(&q, 7);
@@ -2056,13 +2072,17 @@
20562072
@ <label><input type="checkbox" name="sc" %s(sc?"checked":"")>\
20572073
@ Check-ins</label><br>
20582074
}
20592075
if( g.perm.RdForum ){
20602076
@ <label><input type="checkbox" name="sf" %s(sf?"checked":"")>\
2061
- @ Forum Posts</label><br>
2077
+ @ All Forum Posts</label><br>
2078
+ @ <label><input type="checkbox" name="sn" %s(sn?"checked":"")>\
2079
+ @ New Forum Threads</label><br>
2080
+ @ <label><input type="checkbox" name="sr" %s(sr?"checked":"")>\
2081
+ @ Replies To My Posts</label><br>
20622082
@ <label><input type="checkbox" name="sx" %s(sx?"checked":"")>\
2063
- @ Forum Edits</label><br>
2083
+ @ Edits To Forum Posts</label><br>
20642084
}
20652085
if( g.perm.RdTkt ){
20662086
@ <label><input type="checkbox" name="st" %s(st?"checked":"")>\
20672087
@ Ticket changes</label><br>
20682088
}
@@ -2484,20 +2504,24 @@
24842504
**
24852505
** type values:
24862506
**
24872507
** c A new check-in
24882508
** f An original forum post
2509
+** n New forum threads
2510
+** r Replies to my forum posts
24892511
** x An edit to a prior forum post
24902512
** t A new ticket or a change to an existing ticket
24912513
** w A change to a wiki page
2514
+** x Edits to forum posts
24922515
*/
24932516
struct EmailEvent {
2494
- int type; /* 'c', 'f', 't', 'w', 'x' */
2517
+ int type; /* 'c', 'f', 'n', 'r', 't', 'w', 'x' */
24952518
int needMod; /* Pending moderator approval */
24962519
Blob hdr; /* Header content, for forum entries */
24972520
Blob txt; /* Text description to appear in an alert */
24982521
char *zFromName; /* Human name of the sender */
2522
+ char *zPriors; /* Upthread sender IDs for forum posts */
24992523
EmailEvent *pNext; /* Next in chronological order */
25002524
};
25012525
#endif
25022526
25032527
/*
@@ -2507,14 +2531,44 @@
25072531
while( p ){
25082532
EmailEvent *pNext = p->pNext;
25092533
blob_reset(&p->txt);
25102534
blob_reset(&p->hdr);
25112535
fossil_free(p->zFromName);
2536
+ fossil_free(p->zPriors);
25122537
fossil_free(p);
25132538
p = pNext;
25142539
}
25152540
}
2541
+
2542
+/*
2543
+** Compute a string that is appropriate for the EmailEvent.zPriors field
2544
+** for a particular forum post.
2545
+**
2546
+** This string is an encode list of sender names and rids for all ancestors
2547
+** of the fpdi post - the post that fpid answer, the post that that parent
2548
+** post answers, and so forth back up to the root post. Duplicates sender
2549
+** names are omitted.
2550
+**
2551
+** The EmailEvent.zPriors field is used to screen events for people who
2552
+** only want to see replies to their own posts or to specific posts.
2553
+*/
2554
+static char *alert_compute_priors(int fpid){
2555
+ return db_text(0,
2556
+ "WITH priors(rid,who) AS ("
2557
+ " SELECT firt, coalesce(euser,user)"
2558
+ " FROM forumpost LEFT JOIN event ON fpid=objid"
2559
+ " WHERE fpid=%d"
2560
+ " UNION ALL"
2561
+ " SELECT firt, coalesce(euser,user)"
2562
+ " FROM priors, forumpost LEFT JOIN event ON fpid=objid"
2563
+ " WHERE fpid=rid"
2564
+ ")"
2565
+ "SELECT ','||group_concat(DISTINCT 'u'||who)||"
2566
+ "','||group_concat(rid) FROM priors;",
2567
+ fpid
2568
+ );
2569
+}
25162570
25172571
/*
25182572
** Compute and return a linked list of EmailEvent objects
25192573
** corresponding to the current content of the temp.wantalert
25202574
** table which should be defined as follows:
@@ -2644,11 +2698,12 @@
26442698
" ORDER BY event.mtime"
26452699
);
26462700
zFrom = db_get("email-self",0);
26472701
zSub = db_get("email-subname","");
26482702
while( db_step(&q)==SQLITE_ROW ){
2649
- Manifest *pPost = manifest_get(db_column_int(&q,0), CFTYPE_FORUM, 0);
2703
+ int fpid = db_column_int(&q,0);
2704
+ Manifest *pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
26502705
const char *zIrt;
26512706
const char *zUuid;
26522707
const char *zTitle;
26532708
const char *z;
26542709
if( pPost==0 ) continue;
@@ -2657,10 +2712,11 @@
26572712
pLast = p;
26582713
p->type = db_column_int(&q,7) ? 'f' : 'x';
26592714
p->needMod = db_column_int(&q, 5);
26602715
z = db_column_text(&q,6);
26612716
p->zFromName = z && z[0] ? fossil_strdup(z) : 0;
2717
+ p->zPriors = alert_compute_priors(fpid);
26622718
p->pNext = 0;
26632719
blob_init(&p->hdr, 0, 0);
26642720
zUuid = db_column_text(&q, 1);
26652721
zTitle = db_column_text(&q, 3);
26662722
if( p->needMod ){
@@ -2864,10 +2920,25 @@
28642920
"immediately, visit the following webpage:\n\n"
28652921
" %s/alerts/%s\n\n",
28662922
ALERT_RENEWAL_MSG_FREQUENCY, zUrl, zCode
28672923
);
28682924
}
2925
+
2926
+/*
2927
+** If zUser is a sender of one of the ancestors of a forum post
2928
+** (if zUser appears in zPriors) then return true.
2929
+*/
2930
+static int alert_in_priors(const char *zUser, const char *zPriors){
2931
+ int n = (int)strlen(zUser);
2932
+ char zBuf[200];
2933
+ if( n>195 ) return 0;
2934
+ if( zPriors==0 || zPriors[0]==0 ) return 0;
2935
+ zBuf[0] = ',';
2936
+ zBuf[1] = 'u';
2937
+ memcpy(zBuf+2, zUser, n+1);
2938
+ return strstr(zPriors, zBuf)!=0;
2939
+}
28692940
28702941
#if INTERFACE
28712942
/*
28722943
** Flags for alert_send_alerts()
28732944
*/
@@ -3012,11 +3083,12 @@
30123083
db_prepare(&q,
30133084
"SELECT"
30143085
" hex(subscriberCode)," /* 0 */
30153086
" semail," /* 1 */
30163087
" ssub," /* 2 */
3017
- " fullcap(user.cap)" /* 3 */
3088
+ " fullcap(user.cap)," /* 3 */
3089
+ " suname" /* 4 */
30183090
" FROM subscriber LEFT JOIN user ON (login=suname)"
30193091
" WHERE sverified"
30203092
" AND NOT sdonotcall"
30213093
" AND sdigest IS %s"
30223094
" AND coalesce(subscriber.lastContact,subscriber.mtime)>=%d",
@@ -3028,19 +3100,30 @@
30283100
const char *zSub = db_column_text(&q, 2);
30293101
const char *zEmail = db_column_text(&q, 1);
30303102
const char *zCap = db_column_text(&q, 3);
30313103
int nHit = 0;
30323104
for(p=pEvents; p; p=p->pNext){
3033
- if( strchr(zSub,p->type)==0 ) continue;
3105
+ if( strchr(zSub,p->type)==0 ){
3106
+ if( p->type!='f' ) continue;
3107
+ if( strchr(zSub,'n')!=0 && (p->zPriors==0 || p->zPriors[0]==0) ){
3108
+ /* New post: accepted */
3109
+ }else if( strchr(zSub,'r')!=0
3110
+ && alert_in_priors(db_column_text(&q,4), p->zPriors) ){
3111
+ /* A follow-up to a post written by the user: accept */
3112
+ }else{
3113
+ continue;
3114
+ }
3115
+ }
30343116
if( p->needMod ){
30353117
/* For events that require moderator approval, only send an alert
30363118
** if the recipient is a moderator for that type of event. Setup
30373119
** and Admin users always get notified. */
30383120
char xType = '*';
30393121
if( strpbrk(zCap,"as")==0 ){
30403122
switch( p->type ){
3041
- case 'x': case 'f': xType = '5'; break;
3123
+ case 'x': case 'f':
3124
+ case 'n': case 'r': xType = '5'; break;
30423125
case 't': xType = 'q'; break;
30433126
case 'w': xType = 'l'; break;
30443127
}
30453128
if( strchr(zCap,xType)==0 ) continue;
30463129
}
@@ -3051,11 +3134,12 @@
30513134
/* Other users only see the alert if they have sufficient
30523135
** privilege to view the event itself */
30533136
char xType = '*';
30543137
switch( p->type ){
30553138
case 'c': xType = 'o'; break;
3056
- case 'x': case 'f': xType = '2'; break;
3139
+ case 'x': case 'f':
3140
+ case 'n': case 'r': xType = '2'; break;
30573141
case 't': xType = 'r'; break;
30583142
case 'w': xType = 'j'; break;
30593143
}
30603144
if( strchr(zCap,xType)==0 ) continue;
30613145
}
30623146
--- src/alerts.c
+++ src/alerts.c
@@ -47,12 +47,15 @@
47 @ -- The ssub field is a string where each character indicates a particular
48 @ -- type of event to subscribe to. Choices:
49 @ -- a - Announcements
50 @ -- c - Check-ins
51 @ -- f - Forum posts
 
 
52 @ -- t - Ticket changes
53 @ -- w - Wiki changes
 
54 @ -- Probably different codes will be added in the future. In the future
55 @ -- we might also add a separate table that allows subscribing to email
56 @ -- notifications for specific branches or tags or tickets.
57 @ --
58 @ CREATE TABLE repository.subscriber(
@@ -1556,10 +1559,12 @@
1556 if( suname==0 && needCaptcha==0 && !g.perm.Admin ) suname = g.zLogin;
1557 if( suname && suname[0]==0 ) suname = 0;
1558 if( PB("sa") ) ssub[nsub++] = 'a';
1559 if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c';
1560 if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f';
 
 
1561 if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't';
1562 if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w';
1563 if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x';
1564 ssub[nsub] = 0;
1565 zCode = db_text(0,
@@ -1618,10 +1623,12 @@
1618 ** come from a prior Submit of the form) then default all of the
1619 ** subscription options to "on" */
1620 cgi_set_parameter_nocopy("sa","1",1);
1621 if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1);
1622 if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1);
 
 
1623 if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1);
1624 if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1);
1625 }
1626 @ <p>To receive email notifications for changes to this
1627 @ repository, fill out the form below and press the "Submit" button.</p>
@@ -1675,13 +1682,17 @@
1675 @ <label><input type="checkbox" name="sc" %s(PCK("sc"))> \
1676 @ Check-ins</label><br>
1677 }
1678 if( g.perm.RdForum ){
1679 @ <label><input type="checkbox" name="sf" %s(PCK("sf"))> \
1680 @ Forum Posts</label><br>
 
 
 
 
1681 @ <label><input type="checkbox" name="sx" %s(PCK("sx"))> \
1682 @ Forum Edits</label><br>
1683 }
1684 if( g.perm.RdTkt ){
1685 @ <label><input type="checkbox" name="st" %s(PCK("st"))> \
1686 @ Ticket changes</label><br>
1687 }
@@ -1799,10 +1810,11 @@
1799 */
1800 void alert_page(void){
1801 const char *zName = 0; /* Value of the name= query parameter */
1802 Stmt q; /* For querying the database */
1803 int sa, sc, sf, st, sw, sx; /* Types of notifications requested */
 
1804 int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */
1805 int isLogin; /* True if logged in as an individual */
1806 const char *ssub = 0; /* Subscription flags */
1807 const char *semail = 0; /* Email address */
1808 const char *smip; /* */
@@ -1855,10 +1867,12 @@
1855 sdigest = PB("sdigest");
1856 semail = P("semail");
1857 if( PB("sa") ) newSsub[nsub++] = 'a';
1858 if( g.perm.Read && PB("sc") ) newSsub[nsub++] = 'c';
1859 if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f';
 
 
1860 if( g.perm.RdTkt && PB("st") ) newSsub[nsub++] = 't';
1861 if( g.perm.RdWiki && PB("sw") ) newSsub[nsub++] = 'w';
1862 if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x';
1863 newSsub[nsub] = 0;
1864 ssub = newSsub;
@@ -1951,10 +1965,12 @@
1951 sverified = db_column_int(&q, 1);
1952 }
1953 sa = strchr(ssub,'a')!=0;
1954 sc = strchr(ssub,'c')!=0;
1955 sf = strchr(ssub,'f')!=0;
 
 
1956 st = strchr(ssub,'t')!=0;
1957 sw = strchr(ssub,'w')!=0;
1958 sx = strchr(ssub,'x')!=0;
1959 smip = db_column_text(&q, 5);
1960 mtime = db_column_text(&q, 7);
@@ -2056,13 +2072,17 @@
2056 @ <label><input type="checkbox" name="sc" %s(sc?"checked":"")>\
2057 @ Check-ins</label><br>
2058 }
2059 if( g.perm.RdForum ){
2060 @ <label><input type="checkbox" name="sf" %s(sf?"checked":"")>\
2061 @ Forum Posts</label><br>
 
 
 
 
2062 @ <label><input type="checkbox" name="sx" %s(sx?"checked":"")>\
2063 @ Forum Edits</label><br>
2064 }
2065 if( g.perm.RdTkt ){
2066 @ <label><input type="checkbox" name="st" %s(st?"checked":"")>\
2067 @ Ticket changes</label><br>
2068 }
@@ -2484,20 +2504,24 @@
2484 **
2485 ** type values:
2486 **
2487 ** c A new check-in
2488 ** f An original forum post
 
 
2489 ** x An edit to a prior forum post
2490 ** t A new ticket or a change to an existing ticket
2491 ** w A change to a wiki page
 
2492 */
2493 struct EmailEvent {
2494 int type; /* 'c', 'f', 't', 'w', 'x' */
2495 int needMod; /* Pending moderator approval */
2496 Blob hdr; /* Header content, for forum entries */
2497 Blob txt; /* Text description to appear in an alert */
2498 char *zFromName; /* Human name of the sender */
 
2499 EmailEvent *pNext; /* Next in chronological order */
2500 };
2501 #endif
2502
2503 /*
@@ -2507,14 +2531,44 @@
2507 while( p ){
2508 EmailEvent *pNext = p->pNext;
2509 blob_reset(&p->txt);
2510 blob_reset(&p->hdr);
2511 fossil_free(p->zFromName);
 
2512 fossil_free(p);
2513 p = pNext;
2514 }
2515 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2516
2517 /*
2518 ** Compute and return a linked list of EmailEvent objects
2519 ** corresponding to the current content of the temp.wantalert
2520 ** table which should be defined as follows:
@@ -2644,11 +2698,12 @@
2644 " ORDER BY event.mtime"
2645 );
2646 zFrom = db_get("email-self",0);
2647 zSub = db_get("email-subname","");
2648 while( db_step(&q)==SQLITE_ROW ){
2649 Manifest *pPost = manifest_get(db_column_int(&q,0), CFTYPE_FORUM, 0);
 
2650 const char *zIrt;
2651 const char *zUuid;
2652 const char *zTitle;
2653 const char *z;
2654 if( pPost==0 ) continue;
@@ -2657,10 +2712,11 @@
2657 pLast = p;
2658 p->type = db_column_int(&q,7) ? 'f' : 'x';
2659 p->needMod = db_column_int(&q, 5);
2660 z = db_column_text(&q,6);
2661 p->zFromName = z && z[0] ? fossil_strdup(z) : 0;
 
2662 p->pNext = 0;
2663 blob_init(&p->hdr, 0, 0);
2664 zUuid = db_column_text(&q, 1);
2665 zTitle = db_column_text(&q, 3);
2666 if( p->needMod ){
@@ -2864,10 +2920,25 @@
2864 "immediately, visit the following webpage:\n\n"
2865 " %s/alerts/%s\n\n",
2866 ALERT_RENEWAL_MSG_FREQUENCY, zUrl, zCode
2867 );
2868 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2869
2870 #if INTERFACE
2871 /*
2872 ** Flags for alert_send_alerts()
2873 */
@@ -3012,11 +3083,12 @@
3012 db_prepare(&q,
3013 "SELECT"
3014 " hex(subscriberCode)," /* 0 */
3015 " semail," /* 1 */
3016 " ssub," /* 2 */
3017 " fullcap(user.cap)" /* 3 */
 
3018 " FROM subscriber LEFT JOIN user ON (login=suname)"
3019 " WHERE sverified"
3020 " AND NOT sdonotcall"
3021 " AND sdigest IS %s"
3022 " AND coalesce(subscriber.lastContact,subscriber.mtime)>=%d",
@@ -3028,19 +3100,30 @@
3028 const char *zSub = db_column_text(&q, 2);
3029 const char *zEmail = db_column_text(&q, 1);
3030 const char *zCap = db_column_text(&q, 3);
3031 int nHit = 0;
3032 for(p=pEvents; p; p=p->pNext){
3033 if( strchr(zSub,p->type)==0 ) continue;
 
 
 
 
 
 
 
 
 
 
3034 if( p->needMod ){
3035 /* For events that require moderator approval, only send an alert
3036 ** if the recipient is a moderator for that type of event. Setup
3037 ** and Admin users always get notified. */
3038 char xType = '*';
3039 if( strpbrk(zCap,"as")==0 ){
3040 switch( p->type ){
3041 case 'x': case 'f': xType = '5'; break;
 
3042 case 't': xType = 'q'; break;
3043 case 'w': xType = 'l'; break;
3044 }
3045 if( strchr(zCap,xType)==0 ) continue;
3046 }
@@ -3051,11 +3134,12 @@
3051 /* Other users only see the alert if they have sufficient
3052 ** privilege to view the event itself */
3053 char xType = '*';
3054 switch( p->type ){
3055 case 'c': xType = 'o'; break;
3056 case 'x': case 'f': xType = '2'; break;
 
3057 case 't': xType = 'r'; break;
3058 case 'w': xType = 'j'; break;
3059 }
3060 if( strchr(zCap,xType)==0 ) continue;
3061 }
3062
--- src/alerts.c
+++ src/alerts.c
@@ -47,12 +47,15 @@
47 @ -- The ssub field is a string where each character indicates a particular
48 @ -- type of event to subscribe to. Choices:
49 @ -- a - Announcements
50 @ -- c - Check-ins
51 @ -- f - Forum posts
52 @ -- n - New forum threads
53 @ -- r - Replies to my own forum posts
54 @ -- t - Ticket changes
55 @ -- w - Wiki changes
56 @ -- x - Edits to forum posts
57 @ -- Probably different codes will be added in the future. In the future
58 @ -- we might also add a separate table that allows subscribing to email
59 @ -- notifications for specific branches or tags or tickets.
60 @ --
61 @ CREATE TABLE repository.subscriber(
@@ -1556,10 +1559,12 @@
1559 if( suname==0 && needCaptcha==0 && !g.perm.Admin ) suname = g.zLogin;
1560 if( suname && suname[0]==0 ) suname = 0;
1561 if( PB("sa") ) ssub[nsub++] = 'a';
1562 if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c';
1563 if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f';
1564 if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n';
1565 if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r';
1566 if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't';
1567 if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w';
1568 if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x';
1569 ssub[nsub] = 0;
1570 zCode = db_text(0,
@@ -1618,10 +1623,12 @@
1623 ** come from a prior Submit of the form) then default all of the
1624 ** subscription options to "on" */
1625 cgi_set_parameter_nocopy("sa","1",1);
1626 if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1);
1627 if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1);
1628 if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1);
1629 if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1);
1630 if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1);
1631 if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1);
1632 }
1633 @ <p>To receive email notifications for changes to this
1634 @ repository, fill out the form below and press the "Submit" button.</p>
@@ -1675,13 +1682,17 @@
1682 @ <label><input type="checkbox" name="sc" %s(PCK("sc"))> \
1683 @ Check-ins</label><br>
1684 }
1685 if( g.perm.RdForum ){
1686 @ <label><input type="checkbox" name="sf" %s(PCK("sf"))> \
1687 @ All Forum Posts</label><br>
1688 @ <label><input type="checkbox" name="sn" %s(PCK("sn"))> \
1689 @ New Forum Threads</label><br>
1690 @ <label><input type="checkbox" name="sr" %s(PCK("sr"))> \
1691 @ Replies To My Forum Posts</label><br>
1692 @ <label><input type="checkbox" name="sx" %s(PCK("sx"))> \
1693 @ Edits To Forum Posts</label><br>
1694 }
1695 if( g.perm.RdTkt ){
1696 @ <label><input type="checkbox" name="st" %s(PCK("st"))> \
1697 @ Ticket changes</label><br>
1698 }
@@ -1799,10 +1810,11 @@
1810 */
1811 void alert_page(void){
1812 const char *zName = 0; /* Value of the name= query parameter */
1813 Stmt q; /* For querying the database */
1814 int sa, sc, sf, st, sw, sx; /* Types of notifications requested */
1815 int sn, sr;
1816 int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */
1817 int isLogin; /* True if logged in as an individual */
1818 const char *ssub = 0; /* Subscription flags */
1819 const char *semail = 0; /* Email address */
1820 const char *smip; /* */
@@ -1855,10 +1867,12 @@
1867 sdigest = PB("sdigest");
1868 semail = P("semail");
1869 if( PB("sa") ) newSsub[nsub++] = 'a';
1870 if( g.perm.Read && PB("sc") ) newSsub[nsub++] = 'c';
1871 if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f';
1872 if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n';
1873 if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r';
1874 if( g.perm.RdTkt && PB("st") ) newSsub[nsub++] = 't';
1875 if( g.perm.RdWiki && PB("sw") ) newSsub[nsub++] = 'w';
1876 if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x';
1877 newSsub[nsub] = 0;
1878 ssub = newSsub;
@@ -1951,10 +1965,12 @@
1965 sverified = db_column_int(&q, 1);
1966 }
1967 sa = strchr(ssub,'a')!=0;
1968 sc = strchr(ssub,'c')!=0;
1969 sf = strchr(ssub,'f')!=0;
1970 sn = strchr(ssub,'n')!=0;
1971 sr = strchr(ssub,'r')!=0;
1972 st = strchr(ssub,'t')!=0;
1973 sw = strchr(ssub,'w')!=0;
1974 sx = strchr(ssub,'x')!=0;
1975 smip = db_column_text(&q, 5);
1976 mtime = db_column_text(&q, 7);
@@ -2056,13 +2072,17 @@
2072 @ <label><input type="checkbox" name="sc" %s(sc?"checked":"")>\
2073 @ Check-ins</label><br>
2074 }
2075 if( g.perm.RdForum ){
2076 @ <label><input type="checkbox" name="sf" %s(sf?"checked":"")>\
2077 @ All Forum Posts</label><br>
2078 @ <label><input type="checkbox" name="sn" %s(sn?"checked":"")>\
2079 @ New Forum Threads</label><br>
2080 @ <label><input type="checkbox" name="sr" %s(sr?"checked":"")>\
2081 @ Replies To My Posts</label><br>
2082 @ <label><input type="checkbox" name="sx" %s(sx?"checked":"")>\
2083 @ Edits To Forum Posts</label><br>
2084 }
2085 if( g.perm.RdTkt ){
2086 @ <label><input type="checkbox" name="st" %s(st?"checked":"")>\
2087 @ Ticket changes</label><br>
2088 }
@@ -2484,20 +2504,24 @@
2504 **
2505 ** type values:
2506 **
2507 ** c A new check-in
2508 ** f An original forum post
2509 ** n New forum threads
2510 ** r Replies to my forum posts
2511 ** x An edit to a prior forum post
2512 ** t A new ticket or a change to an existing ticket
2513 ** w A change to a wiki page
2514 ** x Edits to forum posts
2515 */
2516 struct EmailEvent {
2517 int type; /* 'c', 'f', 'n', 'r', 't', 'w', 'x' */
2518 int needMod; /* Pending moderator approval */
2519 Blob hdr; /* Header content, for forum entries */
2520 Blob txt; /* Text description to appear in an alert */
2521 char *zFromName; /* Human name of the sender */
2522 char *zPriors; /* Upthread sender IDs for forum posts */
2523 EmailEvent *pNext; /* Next in chronological order */
2524 };
2525 #endif
2526
2527 /*
@@ -2507,14 +2531,44 @@
2531 while( p ){
2532 EmailEvent *pNext = p->pNext;
2533 blob_reset(&p->txt);
2534 blob_reset(&p->hdr);
2535 fossil_free(p->zFromName);
2536 fossil_free(p->zPriors);
2537 fossil_free(p);
2538 p = pNext;
2539 }
2540 }
2541
2542 /*
2543 ** Compute a string that is appropriate for the EmailEvent.zPriors field
2544 ** for a particular forum post.
2545 **
2546 ** This string is an encode list of sender names and rids for all ancestors
2547 ** of the fpdi post - the post that fpid answer, the post that that parent
2548 ** post answers, and so forth back up to the root post. Duplicates sender
2549 ** names are omitted.
2550 **
2551 ** The EmailEvent.zPriors field is used to screen events for people who
2552 ** only want to see replies to their own posts or to specific posts.
2553 */
2554 static char *alert_compute_priors(int fpid){
2555 return db_text(0,
2556 "WITH priors(rid,who) AS ("
2557 " SELECT firt, coalesce(euser,user)"
2558 " FROM forumpost LEFT JOIN event ON fpid=objid"
2559 " WHERE fpid=%d"
2560 " UNION ALL"
2561 " SELECT firt, coalesce(euser,user)"
2562 " FROM priors, forumpost LEFT JOIN event ON fpid=objid"
2563 " WHERE fpid=rid"
2564 ")"
2565 "SELECT ','||group_concat(DISTINCT 'u'||who)||"
2566 "','||group_concat(rid) FROM priors;",
2567 fpid
2568 );
2569 }
2570
2571 /*
2572 ** Compute and return a linked list of EmailEvent objects
2573 ** corresponding to the current content of the temp.wantalert
2574 ** table which should be defined as follows:
@@ -2644,11 +2698,12 @@
2698 " ORDER BY event.mtime"
2699 );
2700 zFrom = db_get("email-self",0);
2701 zSub = db_get("email-subname","");
2702 while( db_step(&q)==SQLITE_ROW ){
2703 int fpid = db_column_int(&q,0);
2704 Manifest *pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
2705 const char *zIrt;
2706 const char *zUuid;
2707 const char *zTitle;
2708 const char *z;
2709 if( pPost==0 ) continue;
@@ -2657,10 +2712,11 @@
2712 pLast = p;
2713 p->type = db_column_int(&q,7) ? 'f' : 'x';
2714 p->needMod = db_column_int(&q, 5);
2715 z = db_column_text(&q,6);
2716 p->zFromName = z && z[0] ? fossil_strdup(z) : 0;
2717 p->zPriors = alert_compute_priors(fpid);
2718 p->pNext = 0;
2719 blob_init(&p->hdr, 0, 0);
2720 zUuid = db_column_text(&q, 1);
2721 zTitle = db_column_text(&q, 3);
2722 if( p->needMod ){
@@ -2864,10 +2920,25 @@
2920 "immediately, visit the following webpage:\n\n"
2921 " %s/alerts/%s\n\n",
2922 ALERT_RENEWAL_MSG_FREQUENCY, zUrl, zCode
2923 );
2924 }
2925
2926 /*
2927 ** If zUser is a sender of one of the ancestors of a forum post
2928 ** (if zUser appears in zPriors) then return true.
2929 */
2930 static int alert_in_priors(const char *zUser, const char *zPriors){
2931 int n = (int)strlen(zUser);
2932 char zBuf[200];
2933 if( n>195 ) return 0;
2934 if( zPriors==0 || zPriors[0]==0 ) return 0;
2935 zBuf[0] = ',';
2936 zBuf[1] = 'u';
2937 memcpy(zBuf+2, zUser, n+1);
2938 return strstr(zPriors, zBuf)!=0;
2939 }
2940
2941 #if INTERFACE
2942 /*
2943 ** Flags for alert_send_alerts()
2944 */
@@ -3012,11 +3083,12 @@
3083 db_prepare(&q,
3084 "SELECT"
3085 " hex(subscriberCode)," /* 0 */
3086 " semail," /* 1 */
3087 " ssub," /* 2 */
3088 " fullcap(user.cap)," /* 3 */
3089 " suname" /* 4 */
3090 " FROM subscriber LEFT JOIN user ON (login=suname)"
3091 " WHERE sverified"
3092 " AND NOT sdonotcall"
3093 " AND sdigest IS %s"
3094 " AND coalesce(subscriber.lastContact,subscriber.mtime)>=%d",
@@ -3028,19 +3100,30 @@
3100 const char *zSub = db_column_text(&q, 2);
3101 const char *zEmail = db_column_text(&q, 1);
3102 const char *zCap = db_column_text(&q, 3);
3103 int nHit = 0;
3104 for(p=pEvents; p; p=p->pNext){
3105 if( strchr(zSub,p->type)==0 ){
3106 if( p->type!='f' ) continue;
3107 if( strchr(zSub,'n')!=0 && (p->zPriors==0 || p->zPriors[0]==0) ){
3108 /* New post: accepted */
3109 }else if( strchr(zSub,'r')!=0
3110 && alert_in_priors(db_column_text(&q,4), p->zPriors) ){
3111 /* A follow-up to a post written by the user: accept */
3112 }else{
3113 continue;
3114 }
3115 }
3116 if( p->needMod ){
3117 /* For events that require moderator approval, only send an alert
3118 ** if the recipient is a moderator for that type of event. Setup
3119 ** and Admin users always get notified. */
3120 char xType = '*';
3121 if( strpbrk(zCap,"as")==0 ){
3122 switch( p->type ){
3123 case 'x': case 'f':
3124 case 'n': case 'r': xType = '5'; break;
3125 case 't': xType = 'q'; break;
3126 case 'w': xType = 'l'; break;
3127 }
3128 if( strchr(zCap,xType)==0 ) continue;
3129 }
@@ -3051,11 +3134,12 @@
3134 /* Other users only see the alert if they have sufficient
3135 ** privilege to view the event itself */
3136 char xType = '*';
3137 switch( p->type ){
3138 case 'c': xType = 'o'; break;
3139 case 'x': case 'f':
3140 case 'n': case 'r': xType = '2'; break;
3141 case 't': xType = 'r'; break;
3142 case 'w': xType = 'j'; break;
3143 }
3144 if( strchr(zCap,xType)==0 ) continue;
3145 }
3146

Keyboard Shortcuts

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