| ︙ | | | ︙ | |
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
@ suname TEXT, -- corresponding USER entry
@ sverified BOOLEAN DEFAULT true, -- email address verified
@ sdonotcall BOOLEAN, -- true for Do Not Call
@ sdigest BOOLEAN, -- true for daily digests only
@ ssub TEXT, -- baseline subscriptions
@ sctime INTDATE, -- When this entry was created. unixtime
@ mtime INTDATE, -- Last change. unixtime
@ smip TEXT -- IP address of last change
@ );
@ CREATE INDEX repository.subscriberUname
@ ON subscriber(suname) WHERE suname IS NOT NULL;
@
@ DROP TABLE IF EXISTS repository.pending_alert;
@ -- Email notifications that need to be sent.
@ --
@ -- The first character of the eventid determines the event type.
@ -- Remaining characters determine the specific event. For example,
@ -- 'c4413' means check-in with rid=4413.
@ --
@ CREATE TABLE repository.pending_alert(
@ eventid TEXT PRIMARY KEY, -- Object that changed
@ sentSep BOOLEAN DEFAULT false, -- individual alert sent
@ sentDigest BOOLEAN DEFAULT false, -- digest alert sent
@ sentMod BOOLEAN DEFAULT false -- pending moderation alert sent
@ ) WITHOUT ROWID;
@
@ DROP TABLE IF EXISTS repository.alert_bounce;
@ -- Record bounced emails. If too many bounces are received within
@ -- some defined time range, then cancel the subscription. Older
@ -- entries are periodically purged.
@ --
@ CREATE TABLE repository.alert_bounce(
@ subscriberId INTEGER, -- to whom the email was sent.
@ sendTime INTEGER, -- seconds since 1970 when email was sent
@ rcvdTime INTEGER -- seconds since 1970 when bounce was received
@ );
;
/*
** Return true if the email notification tables exist.
*/
int alert_tables_exist(void){
return db_table_exists("repository", "subscriber");
}
/*
** Make sure the table needed for email notification exist in the repository.
**
** If the bOnlyIfEnabled option is true, then tables are only created
** if the email-send-method is something other than "off".
*/
void alert_schema(int bOnlyIfEnabled){
if( !alert_tables_exist() ){
if( bOnlyIfEnabled
&& fossil_strcmp(db_get("email-send-method",0),"off")==0
){
return; /* Don't create table for disabled email */
}
db_exec_sql(zAlertInit);
}else if( !db_table_has_column("repository","pending_alert","sentMod") ){
db_multi_exec(
"ALTER TABLE repository.pending_alert"
" ADD COLUMN sentMod BOOLEAN DEFAULT false;"
);
}
}
/*
** Enable triggers that automatically populate the pending_alert
** table.
*/
void alert_create_trigger(void){
|
|
>
>
<
<
<
<
<
<
<
<
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
|
|
|
|
<
|
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
@ suname TEXT, -- corresponding USER entry
@ sverified BOOLEAN DEFAULT true, -- email address verified
@ sdonotcall BOOLEAN, -- true for Do Not Call
@ sdigest BOOLEAN, -- true for daily digests only
@ ssub TEXT, -- baseline subscriptions
@ sctime INTDATE, -- When this entry was created. unixtime
@ mtime INTDATE, -- Last change. unixtime
@ smip TEXT, -- IP address of last change
@ lastContact INT -- Last contact. days since 1970
@ );
@ CREATE INDEX repository.subscriberUname
@ ON subscriber(suname) WHERE suname IS NOT NULL;
@
@ DROP TABLE IF EXISTS repository.pending_alert;
@ -- Email notifications that need to be sent.
@ --
@ -- The first character of the eventid determines the event type.
@ -- Remaining characters determine the specific event. For example,
@ -- 'c4413' means check-in with rid=4413.
@ --
@ CREATE TABLE repository.pending_alert(
@ eventid TEXT PRIMARY KEY, -- Object that changed
@ sentSep BOOLEAN DEFAULT false, -- individual alert sent
@ sentDigest BOOLEAN DEFAULT false, -- digest alert sent
@ sentMod BOOLEAN DEFAULT false -- pending moderation alert sent
@ ) WITHOUT ROWID;
@
@ -- Obsolete table. No longer used.
@ DROP TABLE IF EXISTS repository.alert_bounce;
;
/*
** Return true if the email notification tables exist.
*/
int alert_tables_exist(void){
return db_table_exists("repository", "subscriber");
}
/*
** Record the fact that user zUser has made contact with the repository.
** This resets the subscription timeout on that user.
*/
void alert_user_contact(const char *zUser){
if( db_table_has_column("repository","subscriber","lastContact") ){
db_multi_exec(
"UPDATE subscriber SET lastContact=now()/86400 WHERE suname=%Q",
zUser
);
}
}
/*
** Make sure the table needed for email notification exist in the repository.
**
** If the bOnlyIfEnabled option is true, then tables are only created
** if the email-send-method is something other than "off".
*/
void alert_schema(int bOnlyIfEnabled){
if( !alert_tables_exist() ){
if( bOnlyIfEnabled
&& fossil_strcmp(db_get("email-send-method",0),"off")==0
){
return; /* Don't create table for disabled email */
}
db_exec_sql(zAlertInit);
return;
}
if( db_table_has_column("repository","subscriber","lastContact") ){
return;
}
db_multi_exec(
"DROP TABLE IF EXISTS repository.alert_bounde;\n"
"ALTER TABLE repository.subscriber ADD COLUMN lastContact INT;\n"
"UPDATE subscriber SET lastContact=mtime/86400;"
);
if( db_table_has_column("repository","pending_alert","sentMod") ){
return;
}
db_multi_exec(
"ALTER TABLE repository.pending_alert"
" ADD COLUMN sentMod BOOLEAN DEFAULT false;"
);
}
/*
** Enable triggers that automatically populate the pending_alert
** table.
*/
void alert_create_trigger(void){
|
| ︙ | | | ︙ | |
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
|
if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f';
if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't';
if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w';
if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x';
ssub[nsub] = 0;
zCode = db_text(0,
"INSERT INTO subscriber(semail,suname,"
" sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)"
"VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)"
"RETURNING hex(subscriberCode);",
/* semail */ zEAddr,
/* suname */ suname,
/* sverified */ needCaptcha==0,
/* sdigest */ PB("di"),
/* ssub */ ssub,
/* smip */ g.zIpAddr
|
|
|
|
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
|
if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f';
if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't';
if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w';
if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x';
ssub[nsub] = 0;
zCode = db_text(0,
"INSERT INTO subscriber(semail,suname,"
" sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip,lastContact)"
"VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q,now()/86400)"
"RETURNING hex(subscriberCode);",
/* semail */ zEAddr,
/* suname */ suname,
/* sverified */ needCaptcha==0,
/* sdigest */ PB("di"),
/* ssub */ ssub,
/* smip */ g.zIpAddr
|
| ︙ | | | ︙ | |
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
|
const char *mtime; /* */
const char *sctime; /* Time subscription created */
int eErr = 0; /* Type of error */
char *zErr = 0; /* Error message text */
int sid = 0; /* Subscriber ID */
int nName; /* Length of zName in bytes */
char *zHalfCode; /* prefix of subscriberCode */
db_begin_transaction();
if( alert_webpages_disabled() ){
db_commit_transaction();
return;
}
login_check_credentials();
|
>
|
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
|
const char *mtime; /* */
const char *sctime; /* Time subscription created */
int eErr = 0; /* Type of error */
char *zErr = 0; /* Error message text */
int sid = 0; /* Subscriber ID */
int nName; /* Length of zName in bytes */
char *zHalfCode; /* prefix of subscriberCode */
int keepAlive = 0; /* True to update the last contact time */
db_begin_transaction();
if( alert_webpages_disabled() ){
db_commit_transaction();
return;
}
login_check_credentials();
|
| ︙ | | | ︙ | |
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
|
}
if( sid==0 && nName>=32 ){
sid = db_int(0,
"SELECT CASE WHEN hex(subscriberCode) LIKE (%Q||'%%')"
" THEN subscriberId ELSE 0 END"
" FROM subscriber WHERE subscriberCode>=hextoblob(%Q)"
" LIMIT 1", zName, zName);
}
if( sid==0 && isLogin ){
sid = db_int(0, "SELECT subscriberId FROM subscriber"
" WHERE suname=%Q", g.zLogin);
}
if( sid==0 ){
db_commit_transaction();
|
>
|
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
|
}
if( sid==0 && nName>=32 ){
sid = db_int(0,
"SELECT CASE WHEN hex(subscriberCode) LIKE (%Q||'%%')"
" THEN subscriberId ELSE 0 END"
" FROM subscriber WHERE subscriberCode>=hextoblob(%Q)"
" LIMIT 1", zName, zName);
if( sid ) keepAlive = 1;
}
if( sid==0 && isLogin ){
sid = db_int(0, "SELECT subscriberId FROM subscriber"
" WHERE suname=%Q", g.zLogin);
}
if( sid==0 ){
db_commit_transaction();
|
| ︙ | | | ︙ | |
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
|
newSsub[nsub] = 0;
ssub = newSsub;
blob_init(&update, "UPDATE subscriber SET", -1);
blob_append_sql(&update,
" sdonotcall=%d,"
" sdigest=%d,"
" ssub=%Q,"
" mtime=strftime('%%s','now'),"
" smip=%Q",
sdonotcall,
sdigest,
ssub,
g.zIpAddr
);
if( g.perm.Admin ){
|
|
>
|
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
|
newSsub[nsub] = 0;
ssub = newSsub;
blob_init(&update, "UPDATE subscriber SET", -1);
blob_append_sql(&update,
" sdonotcall=%d,"
" sdigest=%d,"
" ssub=%Q,"
" mtime=now(),"
" lastContact=now()/86400,"
" smip=%Q",
sdonotcall,
sdigest,
ssub,
g.zIpAddr
);
if( g.perm.Admin ){
|
| ︙ | | | ︙ | |
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
|
}
blob_append_sql(&update," WHERE subscriberId=%d", sid);
if( eErr==0 ){
db_exec_sql(blob_str(&update));
ssub = 0;
}
blob_reset(&update);
}
if( P("delete")!=0 && cgi_csrf_safe(1) ){
if( !PB("dodelete") ){
eErr = 9;
zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to"
" unsubscribe");
}else{
|
>
>
>
>
>
|
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
|
}
blob_append_sql(&update," WHERE subscriberId=%d", sid);
if( eErr==0 ){
db_exec_sql(blob_str(&update));
ssub = 0;
}
blob_reset(&update);
}else if( keepAlive ){
db_multi_exec(
"UPDATE subscriber SET lastContact=now()/86400"
" WHERE subscriberId=%d", sid
);
}
if( P("delete")!=0 && cgi_csrf_safe(1) ){
if( !PB("dodelete") ){
eErr = 9;
zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to"
" unsubscribe");
}else{
|
| ︙ | | | ︙ | |
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
|
style_header("Subscriber List");
nTotal = db_int(0, "SELECT count(*) FROM subscriber");
nPending = db_int(0, "SELECT count(*) FROM subscriber WHERE NOT sverified");
if( nPending>0 && P("purge") && cgi_csrf_safe(0) ){
int nNewPending;
db_multi_exec(
"DELETE FROM subscriber"
" WHERE NOT sverified AND mtime<0+strftime('%%s','now','-1 day')"
);
nNewPending = db_int(0, "SELECT count(*) FROM subscriber"
" WHERE NOT sverified");
nDel = nPending - nNewPending;
nPending = nNewPending;
nTotal -= nDel;
}
if( nPending>0 ){
@ <h1>%,d(nTotal) Subscribers, %,d(nPending) Pending</h1>
if( nDel==0 && 0<db_int(0,"SELECT count(*) FROM subscriber"
" WHERE NOT sverified AND mtime<0+strftime('%%s','now','-1 day')")
){
style_submenu_element("Purge Pending","subscribers?purge");
}
}else{
@ <h1>%,d(nTotal) Subscribers</h1>
}
if( nDel>0 ){
@ <p>*** %d(nDel) pending subscriptions deleted ***</p>
}
blob_init(&sql, 0, 0);
blob_append_sql(&sql,
"SELECT subscriberId," /* 0 */
" semail," /* 1 */
" ssub," /* 2 */
" suname," /* 3 */
" sverified," /* 4 */
" sdigest," /* 5 */
" mtime," /* 6 */
" date(sctime,'unixepoch')," /* 7 */
" (SELECT uid FROM user WHERE login=subscriber.suname)" /* 8 */
" FROM subscriber"
);
if( P("only")!=0 ){
blob_append_sql(&sql, " WHERE ssub LIKE '%%%q%%'", P("only"));
style_submenu_element("Show All","%R/subscribers");
}
blob_append_sql(&sql," ORDER BY mtime DESC");
db_prepare_blob(&q, &sql);
iNow = time(0);
@ <table border='1' class='sortable' \
@ data-init-sort='6' data-column-types='tttttKt'>
@ <thead>
@ <tr>
@ <th>Email
@ <th>Events
@ <th>Digest-Only?
@ <th>User
@ <th>Verified?
@ <th>Last change
@ <th>Created
@ </tr>
@ </thead><tbody>
while( db_step(&q)==SQLITE_ROW ){
sqlite3_int64 iMtime = db_column_int64(&q, 6);
double rAge = (iNow - iMtime)/86400.0;
int uid = db_column_int(&q, 8);
const char *zUname = db_column_text(&q, 3);
@ <tr>
@ <td><a href='%R/alerts?sid=%d(db_column_int(&q,0))'>\
@ %h(db_column_text(&q,1))</a></td>
@ <td>%h(db_column_text(&q,2))</td>
@ <td>%s(db_column_int(&q,5)?"digest":"")</td>
if( uid ){
@ <td><a href='%R/setup_uedit?id=%d(uid)'>%h(zUname)</a>
}else{
@ <td>%h(zUname)</td>
}
@ <td>%s(db_column_int(&q,4)?"yes":"pending")</td>
@ <td data-sortkey='%010llx(iMtime)'>%z(human_readable_age(rAge))</td>
@ <td>%h(db_column_text(&q,7))</td>
@ </tr>
}
@ </tbody></table>
db_finalize(&q);
style_table_sorter();
style_finish_page();
|
|
|
|
>
|
>
>
>
>
|
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
|
style_header("Subscriber List");
nTotal = db_int(0, "SELECT count(*) FROM subscriber");
nPending = db_int(0, "SELECT count(*) FROM subscriber WHERE NOT sverified");
if( nPending>0 && P("purge") && cgi_csrf_safe(0) ){
int nNewPending;
db_multi_exec(
"DELETE FROM subscriber"
" WHERE NOT sverified AND mtime<now()-86400"
);
nNewPending = db_int(0, "SELECT count(*) FROM subscriber"
" WHERE NOT sverified");
nDel = nPending - nNewPending;
nPending = nNewPending;
nTotal -= nDel;
}
if( nPending>0 ){
@ <h1>%,d(nTotal) Subscribers, %,d(nPending) Pending</h1>
if( nDel==0 && 0<db_int(0,"SELECT count(*) FROM subscriber"
" WHERE NOT sverified AND mtime<now()-86400")
){
style_submenu_element("Purge Pending","subscribers?purge");
}
}else{
@ <h1>%,d(nTotal) Subscribers</h1>
}
if( nDel>0 ){
@ <p>*** %d(nDel) pending subscriptions deleted ***</p>
}
blob_init(&sql, 0, 0);
blob_append_sql(&sql,
"SELECT subscriberId," /* 0 */
" semail," /* 1 */
" ssub," /* 2 */
" suname," /* 3 */
" sverified," /* 4 */
" sdigest," /* 5 */
" mtime," /* 6 */
" date(sctime,'unixepoch')," /* 7 */
" (SELECT uid FROM user WHERE login=subscriber.suname)," /* 8 */
" coalesce(lastContact,mtime/86400)" /* 9 */
" FROM subscriber"
);
if( P("only")!=0 ){
blob_append_sql(&sql, " WHERE ssub LIKE '%%%q%%'", P("only"));
style_submenu_element("Show All","%R/subscribers");
}
blob_append_sql(&sql," ORDER BY mtime DESC");
db_prepare_blob(&q, &sql);
iNow = time(0);
@ <table border='1' class='sortable' \
@ data-init-sort='6' data-column-types='tttttKKt'>
@ <thead>
@ <tr>
@ <th>Email
@ <th>Events
@ <th>Digest-Only?
@ <th>User
@ <th>Verified?
@ <th>Last change
@ <th>Last contact
@ <th>Created
@ </tr>
@ </thead><tbody>
while( db_step(&q)==SQLITE_ROW ){
sqlite3_int64 iMtime = db_column_int64(&q, 6);
double rAge = (iNow - iMtime)/86400.0;
int uid = db_column_int(&q, 8);
const char *zUname = db_column_text(&q, 3);
sqlite3_int64 iContact = db_column_int64(&q, 9);
double rContact = (iNow/86400) - iContact;
@ <tr>
@ <td><a href='%R/alerts?sid=%d(db_column_int(&q,0))'>\
@ %h(db_column_text(&q,1))</a></td>
@ <td>%h(db_column_text(&q,2))</td>
@ <td>%s(db_column_int(&q,5)?"digest":"")</td>
if( uid ){
@ <td><a href='%R/setup_uedit?id=%d(uid)'>%h(zUname)</a>
}else{
@ <td>%h(zUname)</td>
}
@ <td>%s(db_column_int(&q,4)?"yes":"pending")</td>
@ <td data-sortkey='%010llx(iMtime)'>%z(human_readable_age(rAge))</td>
@ <td data-sortkey='%010llx(iContact)'>%z(human_readable_age(rContact))</td>
@ <td>%h(db_column_text(&q,7))</td>
@ </tr>
}
@ </tbody></table>
db_finalize(&q);
style_table_sorter();
style_finish_page();
|
| ︙ | | | ︙ | |