Fossil SCM
Filter notifications to reduce multiple alerts if forum-posts are edited.
Commit
1c84fa5569640b937af9241d66e0953756d6a5e771d820af659c8c8bff4a1ffe
Parent
bc7c27cb85a3cba…
1 file changed
+120
-11
+120
-11
| --- src/alerts.c | ||
| +++ src/alerts.c | ||
| @@ -303,10 +303,36 @@ | ||
| 303 | 303 | "eadmin", "", 0); |
| 304 | 304 | @ <p>This is the email for the human administrator for the system. |
| 305 | 305 | @ Abuse and trouble reports are send here. |
| 306 | 306 | @ (Property: "email-admin")</p> |
| 307 | 307 | @ <hr> |
| 308 | + | |
| 309 | + onoff_attribute("Omit alerts for edited forum posts", | |
| 310 | + "alert-omit-edited", "alert-omit-edited", 1, 0 ) ; | |
| 311 | + | |
| 312 | + @ <p>If enabled, notifications will only be sent for the latest | |
| 313 | + @ versions of edited forum posts. If disabled, notifications will | |
| 314 | + @ be sent for all versions. Applies to both individual and digest | |
| 315 | + @ notifications.</p> | |
| 316 | + | |
| 317 | + entry_attribute("Grace period in minutes allowed for editing", 5, | |
| 318 | + "alert-edit-window", "alert-edit-window", "5", 0 ) ; | |
| 319 | + | |
| 320 | + @ <p>Number of minutes grace allowed for just-posted forum topics | |
| 321 | + @ during which notifications will be sent. This is to reduce the | |
| 322 | + @ number of alerts if someone edits a post shortly after posting. | |
| 323 | + @ Set to zero to disable this feature.</p> | |
| 324 | + | |
| 325 | + onoff_attribute("Notify immediately if replied-to", | |
| 326 | + "alert-notify-on-reply", "alert-notify-on-reply", 1, 0 ) ; | |
| 327 | + | |
| 328 | + @ <p>If enabled, and a forum post has been replied-to, send | |
| 329 | + @ notifications immediately, even if the "grace period" has not | |
| 330 | + @ expired. If disabled, notifications for replied-to posts will | |
| 331 | + @ not be sent until the grace-period has expired. Does not affect | |
| 332 | + @ the handling of the reply itself.</p> | |
| 333 | + @ <hr> | |
| 308 | 334 | |
| 309 | 335 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 310 | 336 | @ </div></form> |
| 311 | 337 | db_end_transaction(0); |
| 312 | 338 | style_footer(); |
| @@ -1967,10 +1993,11 @@ | ||
| 1967 | 1993 | int type; /* 'c', 'f', 'm', 't', 'w' */ |
| 1968 | 1994 | int needMod; /* Pending moderator approval */ |
| 1969 | 1995 | Blob hdr; /* Header content, for forum entries */ |
| 1970 | 1996 | Blob txt; /* Text description to appear in an alert */ |
| 1971 | 1997 | char *zFromName; /* Human name of the sender */ |
| 1998 | + int edited; /* Forum post has been edited */ | |
| 1972 | 1999 | EmailEvent *pNext; /* Next in chronological order */ |
| 1973 | 2000 | }; |
| 1974 | 2001 | #endif |
| 1975 | 2002 | |
| 1976 | 2003 | /* |
| @@ -2000,10 +2027,11 @@ | ||
| 2000 | 2027 | EmailEvent anchor; |
| 2001 | 2028 | EmailEvent *pLast; |
| 2002 | 2029 | const char *zUrl = db_get("email-url","http://localhost:8080"); |
| 2003 | 2030 | const char *zFrom; |
| 2004 | 2031 | const char *zSub; |
| 2032 | + const int alert_omit_edited = db_get_int("alert-omit-edited",0); | |
| 2005 | 2033 | |
| 2006 | 2034 | |
| 2007 | 2035 | /* First do non-forum post events */ |
| 2008 | 2036 | db_prepare(&q, |
| 2009 | 2037 | "SELECT" |
| @@ -2024,12 +2052,14 @@ | ||
| 2024 | 2052 | " wantalert.needMod" /* 4 */ |
| 2025 | 2053 | " FROM temp.wantalert, event, blob" |
| 2026 | 2054 | " WHERE blob.rid=event.objid" |
| 2027 | 2055 | " AND event.objid=substr(wantalert.eventId,2)+0" |
| 2028 | 2056 | " AND (%d OR eventId NOT GLOB 'f*')" |
| 2057 | + " AND (%d OR wantalert.edited=false)" | |
| 2029 | 2058 | " ORDER BY event.mtime", |
| 2030 | - doDigest | |
| 2059 | + doDigest, | |
| 2060 | + !alert_omit_edited | |
| 2031 | 2061 | ); |
| 2032 | 2062 | memset(&anchor, 0, sizeof(anchor)); |
| 2033 | 2063 | pLast = &anchor; |
| 2034 | 2064 | *pnEvent = 0; |
| 2035 | 2065 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -2037,10 +2067,15 @@ | ||
| 2037 | 2067 | p = fossil_malloc( sizeof(EmailEvent) ); |
| 2038 | 2068 | pLast->pNext = p; |
| 2039 | 2069 | pLast = p; |
| 2040 | 2070 | p->type = db_column_text(&q, 3)[0]; |
| 2041 | 2071 | p->needMod = db_column_int(&q, 4); |
| 2072 | + /* For forum posts in a digest, the "edited=false" above will have prevented | |
| 2073 | + ** them being included in the result-set. For non-forum posts, set p->edited | |
| 2074 | + ** to 0 so they are processed as normal. | |
| 2075 | + */ | |
| 2076 | + p->edited = 0; | |
| 2042 | 2077 | p->zFromName = 0; |
| 2043 | 2078 | p->pNext = 0; |
| 2044 | 2079 | switch( p->type ){ |
| 2045 | 2080 | case 'c': zType = "Check-In"; break; |
| 2046 | 2081 | case 'f': zType = "Forum post"; break; |
| @@ -2086,11 +2121,12 @@ | ||
| 2086 | 2121 | " (SELECT uuid FROM blob WHERE rid=forumpost.fpid)," /* 1 */ |
| 2087 | 2122 | " datetime(event.mtime)," /* 2 */ |
| 2088 | 2123 | " substr(comment,instr(comment,':')+2)," /* 3 */ |
| 2089 | 2124 | " (SELECT uuid FROM blob WHERE rid=forumpost.firt)," /* 4 */ |
| 2090 | 2125 | " wantalert.needMod," /* 5 */ |
| 2091 | - " coalesce(trim(substr(info,1,instr(info,'<')-1)),euser,user)" /* 6 */ | |
| 2126 | + " coalesce(trim(substr(info,1,instr(info,'<')-1)),euser,user)," /* 6 */ | |
| 2127 | + " wantalert.edited" /* 7 */ | |
| 2092 | 2128 | " FROM temp.wantalert, event, forumpost" |
| 2093 | 2129 | " LEFT JOIN user ON (login=coalesce(euser,user))" |
| 2094 | 2130 | " WHERE event.objid=substr(wantalert.eventId,2)+0" |
| 2095 | 2131 | " AND eventId GLOB 'f*'" |
| 2096 | 2132 | " AND forumpost.fpid=event.objid" |
| @@ -2109,10 +2145,11 @@ | ||
| 2109 | 2145 | pLast->pNext = p; |
| 2110 | 2146 | pLast = p; |
| 2111 | 2147 | p->type = 'f'; |
| 2112 | 2148 | p->needMod = db_column_int(&q, 5); |
| 2113 | 2149 | z = db_column_text(&q,6); |
| 2150 | + p->edited = db_column_int(&q, 7); | |
| 2114 | 2151 | p->zFromName = z && z[0] ? fossil_strdup(z) : 0; |
| 2115 | 2152 | p->pNext = 0; |
| 2116 | 2153 | blob_init(&p->hdr, 0, 0); |
| 2117 | 2154 | zUuid = db_column_text(&q, 1); |
| 2118 | 2155 | zTitle = db_column_text(&q, 3); |
| @@ -2198,19 +2235,21 @@ | ||
| 2198 | 2235 | needMod = find_option("needmod",0,0)!=0; |
| 2199 | 2236 | db_find_and_open_repository(0, 0); |
| 2200 | 2237 | verify_all_options(); |
| 2201 | 2238 | db_begin_transaction(); |
| 2202 | 2239 | alert_schema(0); |
| 2203 | - db_multi_exec("CREATE TEMP TABLE wantalert(eventid TEXT, needMod BOOLEAN)"); | |
| 2240 | + db_multi_exec("CREATE TEMP TABLE wantalert(eventid TEXT, needMod BOOLEAN," | |
| 2241 | + "edited BOOLEAN)"); | |
| 2204 | 2242 | if( g.argc==2 ){ |
| 2205 | 2243 | db_multi_exec( |
| 2206 | - "INSERT INTO wantalert(eventId,needMod)" | |
| 2207 | - " SELECT eventid, %d FROM pending_alert", needMod); | |
| 2244 | + "INSERT INTO wantalert(eventId,needMod,edited)" | |
| 2245 | + " SELECT eventid, %d, 0 FROM pending_alert", needMod); | |
| 2208 | 2246 | }else{ |
| 2209 | 2247 | int i; |
| 2210 | 2248 | for(i=2; i<g.argc; i++){ |
| 2211 | - db_multi_exec("INSERT INTO wantalert(eventId,needMod) VALUES(%Q,%d)", | |
| 2249 | + db_multi_exec("INSERT INTO wantalert(eventId,needMod,edited)" | |
| 2250 | + " VALUES(%Q,%d,0)", | |
| 2212 | 2251 | g.argv[i], needMod); |
| 2213 | 2252 | } |
| 2214 | 2253 | } |
| 2215 | 2254 | blob_init(&out, 0, 0); |
| 2216 | 2255 | email_header(&out); |
| @@ -2303,10 +2342,18 @@ | ||
| 2303 | 2342 | ** (1) Create a TEMP table wantalert(eventId,needMod) and fill it with |
| 2304 | 2343 | ** all the events that we want to send alerts about. The needMod |
| 2305 | 2344 | ** flags is set if and only if the event is still awaiting |
| 2306 | 2345 | ** moderator approval. Events with the needMod flag are only |
| 2307 | 2346 | ** shown to users that have moderator privileges. |
| 2347 | +** | |
| 2348 | +** (1b) For forum posts, mark those that have been edited so we can | |
| 2349 | +** avoid notifications for out-of-date posts, if so configured. | |
| 2350 | +** | |
| 2351 | +** (1c) Purge forum-post entries from wantalert if not enough time | |
| 2352 | +** has passed since creation (and, optionally, if they have not | |
| 2353 | +** been replied to). This gives an opportunity for "oops I made | |
| 2354 | +** a typo" to be corrected before people are notified. | |
| 2308 | 2355 | ** |
| 2309 | 2356 | ** (2) Call alert_compute_event_text() to compute a list of EmailEvent |
| 2310 | 2357 | ** objects that describe all events about which we want to send |
| 2311 | 2358 | ** alerts. |
| 2312 | 2359 | ** |
| @@ -2335,10 +2382,13 @@ | ||
| 2335 | 2382 | const char *zRepoName; |
| 2336 | 2383 | const char *zFrom; |
| 2337 | 2384 | const char *zDest = (flags & SENDALERT_STDOUT) ? "stdout" : 0; |
| 2338 | 2385 | AlertSender *pSender = 0; |
| 2339 | 2386 | u32 senderFlags = 0; |
| 2387 | + const int alert_omit_edited = db_get_int("alert-omit-edited", 0); | |
| 2388 | + const int alert_edit_window = db_get_int("alert-edit-window", 0); | |
| 2389 | + const int alert_notify_on_reply = db_get_int("alert-notify-on-reply", 0); | |
| 2340 | 2390 | |
| 2341 | 2391 | if( g.fSqlTrace ) fossil_trace("-- BEGIN alert_send_alerts(%u)\n", flags); |
| 2342 | 2392 | alert_schema(0); |
| 2343 | 2393 | if( !alert_enabled() ) goto send_alert_done; |
| 2344 | 2394 | zUrl = db_get("email-url",0); |
| @@ -2354,35 +2404,88 @@ | ||
| 2354 | 2404 | |
| 2355 | 2405 | /* Step (1): Compute the alerts that need sending |
| 2356 | 2406 | */ |
| 2357 | 2407 | db_multi_exec( |
| 2358 | 2408 | "DROP TABLE IF EXISTS temp.wantalert;" |
| 2359 | - "CREATE TEMP TABLE wantalert(eventId TEXT, needMod BOOLEAN, sentMod);" | |
| 2409 | + "CREATE TEMP TABLE wantalert(eventId TEXT, needMod BOOLEAN, sentMod," | |
| 2410 | + " edited BOOLEAN);" | |
| 2360 | 2411 | ); |
| 2361 | 2412 | if( flags & SENDALERT_DIGEST ){ |
| 2362 | 2413 | /* Unmoderated changes are never sent as part of a digest */ |
| 2363 | 2414 | db_multi_exec( |
| 2364 | - "INSERT INTO wantalert(eventId,needMod)" | |
| 2365 | - " SELECT eventid, 0" | |
| 2415 | + "INSERT INTO wantalert(eventId,needMod,edited)" | |
| 2416 | + " SELECT eventid, 0, 0" | |
| 2366 | 2417 | " FROM pending_alert" |
| 2367 | 2418 | " WHERE sentDigest IS FALSE" |
| 2368 | 2419 | " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=substr(eventid,2));" |
| 2369 | 2420 | ); |
| 2370 | 2421 | zDigest = "true"; |
| 2371 | 2422 | }else{ |
| 2372 | 2423 | /* Immediate alerts might include events that are subject to |
| 2373 | 2424 | ** moderator approval */ |
| 2374 | 2425 | db_multi_exec( |
| 2375 | - "INSERT INTO wantalert(eventId,needMod,sentMod)" | |
| 2426 | + "INSERT INTO wantalert(eventId,needMod,sentMod,edited)" | |
| 2376 | 2427 | " SELECT eventid," |
| 2377 | 2428 | " EXISTS(SELECT 1 FROM private WHERE rid=substr(eventid,2))," |
| 2378 | - " sentMod" | |
| 2429 | + " sentMod,0" | |
| 2379 | 2430 | " FROM pending_alert" |
| 2380 | 2431 | " WHERE sentSep IS FALSE;" |
| 2381 | 2432 | "DELETE FROM wantalert WHERE needMod AND sentMod;" |
| 2382 | 2433 | ); |
| 2383 | 2434 | } |
| 2435 | + | |
| 2436 | + /* Step 1b | |
| 2437 | + ** Mark all entries as "edited" if a forum-post ("f...") and the id (after | |
| 2438 | + ** the "f") appears in the 'fprev' field of a 'forumpost' record. If | |
| 2439 | + ** 'alert-omit-edited' is enabled, notifications for such entries will NOT be | |
| 2440 | + ** generated, but the entries WILL be marked as 'sentSep' or 'sepDigest'. | |
| 2441 | + ** | |
| 2442 | + ** Step 1c | |
| 2443 | + ** Delete any entry that matchs all the following: | |
| 2444 | + ** a. Is a forum-post and the 'fmtime' entry from 'forumpost' is less than | |
| 2445 | + ** 'alert-edit-window' minutes in the past. | |
| 2446 | + ** b. Either 'alert-notift-on-reply' is turned off OR there is no reply to | |
| 2447 | + ** this post. | |
| 2448 | + ** c. Does not require approval. | |
| 2449 | + ** Entries matching all the above are removed from 'wantalert' so the original | |
| 2450 | + ** entries in 'pending_alert' will NOT be marked as sent/deleted. | |
| 2451 | + */ | |
| 2452 | + db_multi_exec( | |
| 2453 | + "UPDATE wantalert SET edited=1" | |
| 2454 | + " WHERE EXISTS (" | |
| 2455 | + " SELECT 1" | |
| 2456 | + " FROM forumpost" | |
| 2457 | + " WHERE substr(eventid,1,1)='f' AND fprev=substr(eventid,2)" | |
| 2458 | + " );" | |
| 2459 | + "DELETE FROM wantalert" | |
| 2460 | + " WHERE ( SELECT fmtime > julianday('now', '-%d minutes')" | |
| 2461 | + " FROM forumpost" | |
| 2462 | + " WHERE substr(eventid,1,1)='f' AND fpid=substr(eventid,2)" | |
| 2463 | + " )" | |
| 2464 | + " AND ( %d OR NOT EXISTS (" | |
| 2465 | + " SELECT 1" | |
| 2466 | + " FROM forumpost" | |
| 2467 | + " WHERE substr(eventid,1,1)='f' AND firt=substr(eventid,2)" | |
| 2468 | + " )" | |
| 2469 | + " )" | |
| 2470 | + " AND NOT needMod;", | |
| 2471 | + alert_edit_window, | |
| 2472 | + 1-alert_notify_on_reply | |
| 2473 | + ); | |
| 2474 | + | |
| 2475 | + if( g.fSqlTrace ) { | |
| 2476 | + db_prepare(&q, "SELECT eventid, needMod, sentMod, edited FROM wantalert" ) ; | |
| 2477 | + fossil_trace( "%10s %10s %10s %10s\n", "EventID", "NeedMod", "SentMod", "Edited" ); | |
| 2478 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 2479 | + const char* eventid = db_column_text( &q, 0 ) ; | |
| 2480 | + const char* needMod = db_column_text( &q, 1 ) ; | |
| 2481 | + const char* sentMod = db_column_text( &q, 2 ) ; | |
| 2482 | + const char* edited = db_column_text( &q, 3 ) ; | |
| 2483 | + fossil_trace( "%10s %10s %10s %10s\n", eventid, needMod, sentMod, edited ); | |
| 2484 | + } | |
| 2485 | + db_finalize(&q); | |
| 2486 | + } | |
| 2384 | 2487 | |
| 2385 | 2488 | /* Step 2: compute EmailEvent objects for every notification that |
| 2386 | 2489 | ** needs sending. |
| 2387 | 2490 | */ |
| 2388 | 2491 | pEvents = alert_compute_event_text(&nEvent, (flags & SENDALERT_DIGEST)!=0); |
| @@ -2459,10 +2562,16 @@ | ||
| 2459 | 2562 | case 't': xType = 'r'; break; |
| 2460 | 2563 | case 'w': xType = 'j'; break; |
| 2461 | 2564 | } |
| 2462 | 2565 | if( strchr(zCap,xType)==0 ) continue; |
| 2463 | 2566 | } |
| 2567 | + /* If 'alert-omit-edited' is enabled, and the current entry | |
| 2568 | + ** has been edited, skip it. This potentially could be made | |
| 2569 | + ** the subject of a user-specific setting. | |
| 2570 | + */ | |
| 2571 | + if( alert_omit_edited && p->edited ) continue; | |
| 2572 | + | |
| 2464 | 2573 | if( blob_size(&p->hdr)>0 ){ |
| 2465 | 2574 | /* This alert should be sent as a separate email */ |
| 2466 | 2575 | Blob fhdr, fbody; |
| 2467 | 2576 | blob_init(&fhdr, 0, 0); |
| 2468 | 2577 | blob_appendf(&fhdr, "To: <%s>\r\n", zEmail); |
| 2469 | 2578 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -303,10 +303,36 @@ | |
| 303 | "eadmin", "", 0); |
| 304 | @ <p>This is the email for the human administrator for the system. |
| 305 | @ Abuse and trouble reports are send here. |
| 306 | @ (Property: "email-admin")</p> |
| 307 | @ <hr> |
| 308 | |
| 309 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 310 | @ </div></form> |
| 311 | db_end_transaction(0); |
| 312 | style_footer(); |
| @@ -1967,10 +1993,11 @@ | |
| 1967 | int type; /* 'c', 'f', 'm', 't', 'w' */ |
| 1968 | int needMod; /* Pending moderator approval */ |
| 1969 | Blob hdr; /* Header content, for forum entries */ |
| 1970 | Blob txt; /* Text description to appear in an alert */ |
| 1971 | char *zFromName; /* Human name of the sender */ |
| 1972 | EmailEvent *pNext; /* Next in chronological order */ |
| 1973 | }; |
| 1974 | #endif |
| 1975 | |
| 1976 | /* |
| @@ -2000,10 +2027,11 @@ | |
| 2000 | EmailEvent anchor; |
| 2001 | EmailEvent *pLast; |
| 2002 | const char *zUrl = db_get("email-url","http://localhost:8080"); |
| 2003 | const char *zFrom; |
| 2004 | const char *zSub; |
| 2005 | |
| 2006 | |
| 2007 | /* First do non-forum post events */ |
| 2008 | db_prepare(&q, |
| 2009 | "SELECT" |
| @@ -2024,12 +2052,14 @@ | |
| 2024 | " wantalert.needMod" /* 4 */ |
| 2025 | " FROM temp.wantalert, event, blob" |
| 2026 | " WHERE blob.rid=event.objid" |
| 2027 | " AND event.objid=substr(wantalert.eventId,2)+0" |
| 2028 | " AND (%d OR eventId NOT GLOB 'f*')" |
| 2029 | " ORDER BY event.mtime", |
| 2030 | doDigest |
| 2031 | ); |
| 2032 | memset(&anchor, 0, sizeof(anchor)); |
| 2033 | pLast = &anchor; |
| 2034 | *pnEvent = 0; |
| 2035 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -2037,10 +2067,15 @@ | |
| 2037 | p = fossil_malloc( sizeof(EmailEvent) ); |
| 2038 | pLast->pNext = p; |
| 2039 | pLast = p; |
| 2040 | p->type = db_column_text(&q, 3)[0]; |
| 2041 | p->needMod = db_column_int(&q, 4); |
| 2042 | p->zFromName = 0; |
| 2043 | p->pNext = 0; |
| 2044 | switch( p->type ){ |
| 2045 | case 'c': zType = "Check-In"; break; |
| 2046 | case 'f': zType = "Forum post"; break; |
| @@ -2086,11 +2121,12 @@ | |
| 2086 | " (SELECT uuid FROM blob WHERE rid=forumpost.fpid)," /* 1 */ |
| 2087 | " datetime(event.mtime)," /* 2 */ |
| 2088 | " substr(comment,instr(comment,':')+2)," /* 3 */ |
| 2089 | " (SELECT uuid FROM blob WHERE rid=forumpost.firt)," /* 4 */ |
| 2090 | " wantalert.needMod," /* 5 */ |
| 2091 | " coalesce(trim(substr(info,1,instr(info,'<')-1)),euser,user)" /* 6 */ |
| 2092 | " FROM temp.wantalert, event, forumpost" |
| 2093 | " LEFT JOIN user ON (login=coalesce(euser,user))" |
| 2094 | " WHERE event.objid=substr(wantalert.eventId,2)+0" |
| 2095 | " AND eventId GLOB 'f*'" |
| 2096 | " AND forumpost.fpid=event.objid" |
| @@ -2109,10 +2145,11 @@ | |
| 2109 | pLast->pNext = p; |
| 2110 | pLast = p; |
| 2111 | p->type = 'f'; |
| 2112 | p->needMod = db_column_int(&q, 5); |
| 2113 | z = db_column_text(&q,6); |
| 2114 | p->zFromName = z && z[0] ? fossil_strdup(z) : 0; |
| 2115 | p->pNext = 0; |
| 2116 | blob_init(&p->hdr, 0, 0); |
| 2117 | zUuid = db_column_text(&q, 1); |
| 2118 | zTitle = db_column_text(&q, 3); |
| @@ -2198,19 +2235,21 @@ | |
| 2198 | needMod = find_option("needmod",0,0)!=0; |
| 2199 | db_find_and_open_repository(0, 0); |
| 2200 | verify_all_options(); |
| 2201 | db_begin_transaction(); |
| 2202 | alert_schema(0); |
| 2203 | db_multi_exec("CREATE TEMP TABLE wantalert(eventid TEXT, needMod BOOLEAN)"); |
| 2204 | if( g.argc==2 ){ |
| 2205 | db_multi_exec( |
| 2206 | "INSERT INTO wantalert(eventId,needMod)" |
| 2207 | " SELECT eventid, %d FROM pending_alert", needMod); |
| 2208 | }else{ |
| 2209 | int i; |
| 2210 | for(i=2; i<g.argc; i++){ |
| 2211 | db_multi_exec("INSERT INTO wantalert(eventId,needMod) VALUES(%Q,%d)", |
| 2212 | g.argv[i], needMod); |
| 2213 | } |
| 2214 | } |
| 2215 | blob_init(&out, 0, 0); |
| 2216 | email_header(&out); |
| @@ -2303,10 +2342,18 @@ | |
| 2303 | ** (1) Create a TEMP table wantalert(eventId,needMod) and fill it with |
| 2304 | ** all the events that we want to send alerts about. The needMod |
| 2305 | ** flags is set if and only if the event is still awaiting |
| 2306 | ** moderator approval. Events with the needMod flag are only |
| 2307 | ** shown to users that have moderator privileges. |
| 2308 | ** |
| 2309 | ** (2) Call alert_compute_event_text() to compute a list of EmailEvent |
| 2310 | ** objects that describe all events about which we want to send |
| 2311 | ** alerts. |
| 2312 | ** |
| @@ -2335,10 +2382,13 @@ | |
| 2335 | const char *zRepoName; |
| 2336 | const char *zFrom; |
| 2337 | const char *zDest = (flags & SENDALERT_STDOUT) ? "stdout" : 0; |
| 2338 | AlertSender *pSender = 0; |
| 2339 | u32 senderFlags = 0; |
| 2340 | |
| 2341 | if( g.fSqlTrace ) fossil_trace("-- BEGIN alert_send_alerts(%u)\n", flags); |
| 2342 | alert_schema(0); |
| 2343 | if( !alert_enabled() ) goto send_alert_done; |
| 2344 | zUrl = db_get("email-url",0); |
| @@ -2354,35 +2404,88 @@ | |
| 2354 | |
| 2355 | /* Step (1): Compute the alerts that need sending |
| 2356 | */ |
| 2357 | db_multi_exec( |
| 2358 | "DROP TABLE IF EXISTS temp.wantalert;" |
| 2359 | "CREATE TEMP TABLE wantalert(eventId TEXT, needMod BOOLEAN, sentMod);" |
| 2360 | ); |
| 2361 | if( flags & SENDALERT_DIGEST ){ |
| 2362 | /* Unmoderated changes are never sent as part of a digest */ |
| 2363 | db_multi_exec( |
| 2364 | "INSERT INTO wantalert(eventId,needMod)" |
| 2365 | " SELECT eventid, 0" |
| 2366 | " FROM pending_alert" |
| 2367 | " WHERE sentDigest IS FALSE" |
| 2368 | " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=substr(eventid,2));" |
| 2369 | ); |
| 2370 | zDigest = "true"; |
| 2371 | }else{ |
| 2372 | /* Immediate alerts might include events that are subject to |
| 2373 | ** moderator approval */ |
| 2374 | db_multi_exec( |
| 2375 | "INSERT INTO wantalert(eventId,needMod,sentMod)" |
| 2376 | " SELECT eventid," |
| 2377 | " EXISTS(SELECT 1 FROM private WHERE rid=substr(eventid,2))," |
| 2378 | " sentMod" |
| 2379 | " FROM pending_alert" |
| 2380 | " WHERE sentSep IS FALSE;" |
| 2381 | "DELETE FROM wantalert WHERE needMod AND sentMod;" |
| 2382 | ); |
| 2383 | } |
| 2384 | |
| 2385 | /* Step 2: compute EmailEvent objects for every notification that |
| 2386 | ** needs sending. |
| 2387 | */ |
| 2388 | pEvents = alert_compute_event_text(&nEvent, (flags & SENDALERT_DIGEST)!=0); |
| @@ -2459,10 +2562,16 @@ | |
| 2459 | case 't': xType = 'r'; break; |
| 2460 | case 'w': xType = 'j'; break; |
| 2461 | } |
| 2462 | if( strchr(zCap,xType)==0 ) continue; |
| 2463 | } |
| 2464 | if( blob_size(&p->hdr)>0 ){ |
| 2465 | /* This alert should be sent as a separate email */ |
| 2466 | Blob fhdr, fbody; |
| 2467 | blob_init(&fhdr, 0, 0); |
| 2468 | blob_appendf(&fhdr, "To: <%s>\r\n", zEmail); |
| 2469 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -303,10 +303,36 @@ | |
| 303 | "eadmin", "", 0); |
| 304 | @ <p>This is the email for the human administrator for the system. |
| 305 | @ Abuse and trouble reports are send here. |
| 306 | @ (Property: "email-admin")</p> |
| 307 | @ <hr> |
| 308 | |
| 309 | onoff_attribute("Omit alerts for edited forum posts", |
| 310 | "alert-omit-edited", "alert-omit-edited", 1, 0 ) ; |
| 311 | |
| 312 | @ <p>If enabled, notifications will only be sent for the latest |
| 313 | @ versions of edited forum posts. If disabled, notifications will |
| 314 | @ be sent for all versions. Applies to both individual and digest |
| 315 | @ notifications.</p> |
| 316 | |
| 317 | entry_attribute("Grace period in minutes allowed for editing", 5, |
| 318 | "alert-edit-window", "alert-edit-window", "5", 0 ) ; |
| 319 | |
| 320 | @ <p>Number of minutes grace allowed for just-posted forum topics |
| 321 | @ during which notifications will be sent. This is to reduce the |
| 322 | @ number of alerts if someone edits a post shortly after posting. |
| 323 | @ Set to zero to disable this feature.</p> |
| 324 | |
| 325 | onoff_attribute("Notify immediately if replied-to", |
| 326 | "alert-notify-on-reply", "alert-notify-on-reply", 1, 0 ) ; |
| 327 | |
| 328 | @ <p>If enabled, and a forum post has been replied-to, send |
| 329 | @ notifications immediately, even if the "grace period" has not |
| 330 | @ expired. If disabled, notifications for replied-to posts will |
| 331 | @ not be sent until the grace-period has expired. Does not affect |
| 332 | @ the handling of the reply itself.</p> |
| 333 | @ <hr> |
| 334 | |
| 335 | @ <p><input type="submit" name="submit" value="Apply Changes" /></p> |
| 336 | @ </div></form> |
| 337 | db_end_transaction(0); |
| 338 | style_footer(); |
| @@ -1967,10 +1993,11 @@ | |
| 1993 | int type; /* 'c', 'f', 'm', 't', 'w' */ |
| 1994 | int needMod; /* Pending moderator approval */ |
| 1995 | Blob hdr; /* Header content, for forum entries */ |
| 1996 | Blob txt; /* Text description to appear in an alert */ |
| 1997 | char *zFromName; /* Human name of the sender */ |
| 1998 | int edited; /* Forum post has been edited */ |
| 1999 | EmailEvent *pNext; /* Next in chronological order */ |
| 2000 | }; |
| 2001 | #endif |
| 2002 | |
| 2003 | /* |
| @@ -2000,10 +2027,11 @@ | |
| 2027 | EmailEvent anchor; |
| 2028 | EmailEvent *pLast; |
| 2029 | const char *zUrl = db_get("email-url","http://localhost:8080"); |
| 2030 | const char *zFrom; |
| 2031 | const char *zSub; |
| 2032 | const int alert_omit_edited = db_get_int("alert-omit-edited",0); |
| 2033 | |
| 2034 | |
| 2035 | /* First do non-forum post events */ |
| 2036 | db_prepare(&q, |
| 2037 | "SELECT" |
| @@ -2024,12 +2052,14 @@ | |
| 2052 | " wantalert.needMod" /* 4 */ |
| 2053 | " FROM temp.wantalert, event, blob" |
| 2054 | " WHERE blob.rid=event.objid" |
| 2055 | " AND event.objid=substr(wantalert.eventId,2)+0" |
| 2056 | " AND (%d OR eventId NOT GLOB 'f*')" |
| 2057 | " AND (%d OR wantalert.edited=false)" |
| 2058 | " ORDER BY event.mtime", |
| 2059 | doDigest, |
| 2060 | !alert_omit_edited |
| 2061 | ); |
| 2062 | memset(&anchor, 0, sizeof(anchor)); |
| 2063 | pLast = &anchor; |
| 2064 | *pnEvent = 0; |
| 2065 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -2037,10 +2067,15 @@ | |
| 2067 | p = fossil_malloc( sizeof(EmailEvent) ); |
| 2068 | pLast->pNext = p; |
| 2069 | pLast = p; |
| 2070 | p->type = db_column_text(&q, 3)[0]; |
| 2071 | p->needMod = db_column_int(&q, 4); |
| 2072 | /* For forum posts in a digest, the "edited=false" above will have prevented |
| 2073 | ** them being included in the result-set. For non-forum posts, set p->edited |
| 2074 | ** to 0 so they are processed as normal. |
| 2075 | */ |
| 2076 | p->edited = 0; |
| 2077 | p->zFromName = 0; |
| 2078 | p->pNext = 0; |
| 2079 | switch( p->type ){ |
| 2080 | case 'c': zType = "Check-In"; break; |
| 2081 | case 'f': zType = "Forum post"; break; |
| @@ -2086,11 +2121,12 @@ | |
| 2121 | " (SELECT uuid FROM blob WHERE rid=forumpost.fpid)," /* 1 */ |
| 2122 | " datetime(event.mtime)," /* 2 */ |
| 2123 | " substr(comment,instr(comment,':')+2)," /* 3 */ |
| 2124 | " (SELECT uuid FROM blob WHERE rid=forumpost.firt)," /* 4 */ |
| 2125 | " wantalert.needMod," /* 5 */ |
| 2126 | " coalesce(trim(substr(info,1,instr(info,'<')-1)),euser,user)," /* 6 */ |
| 2127 | " wantalert.edited" /* 7 */ |
| 2128 | " FROM temp.wantalert, event, forumpost" |
| 2129 | " LEFT JOIN user ON (login=coalesce(euser,user))" |
| 2130 | " WHERE event.objid=substr(wantalert.eventId,2)+0" |
| 2131 | " AND eventId GLOB 'f*'" |
| 2132 | " AND forumpost.fpid=event.objid" |
| @@ -2109,10 +2145,11 @@ | |
| 2145 | pLast->pNext = p; |
| 2146 | pLast = p; |
| 2147 | p->type = 'f'; |
| 2148 | p->needMod = db_column_int(&q, 5); |
| 2149 | z = db_column_text(&q,6); |
| 2150 | p->edited = db_column_int(&q, 7); |
| 2151 | p->zFromName = z && z[0] ? fossil_strdup(z) : 0; |
| 2152 | p->pNext = 0; |
| 2153 | blob_init(&p->hdr, 0, 0); |
| 2154 | zUuid = db_column_text(&q, 1); |
| 2155 | zTitle = db_column_text(&q, 3); |
| @@ -2198,19 +2235,21 @@ | |
| 2235 | needMod = find_option("needmod",0,0)!=0; |
| 2236 | db_find_and_open_repository(0, 0); |
| 2237 | verify_all_options(); |
| 2238 | db_begin_transaction(); |
| 2239 | alert_schema(0); |
| 2240 | db_multi_exec("CREATE TEMP TABLE wantalert(eventid TEXT, needMod BOOLEAN," |
| 2241 | "edited BOOLEAN)"); |
| 2242 | if( g.argc==2 ){ |
| 2243 | db_multi_exec( |
| 2244 | "INSERT INTO wantalert(eventId,needMod,edited)" |
| 2245 | " SELECT eventid, %d, 0 FROM pending_alert", needMod); |
| 2246 | }else{ |
| 2247 | int i; |
| 2248 | for(i=2; i<g.argc; i++){ |
| 2249 | db_multi_exec("INSERT INTO wantalert(eventId,needMod,edited)" |
| 2250 | " VALUES(%Q,%d,0)", |
| 2251 | g.argv[i], needMod); |
| 2252 | } |
| 2253 | } |
| 2254 | blob_init(&out, 0, 0); |
| 2255 | email_header(&out); |
| @@ -2303,10 +2342,18 @@ | |
| 2342 | ** (1) Create a TEMP table wantalert(eventId,needMod) and fill it with |
| 2343 | ** all the events that we want to send alerts about. The needMod |
| 2344 | ** flags is set if and only if the event is still awaiting |
| 2345 | ** moderator approval. Events with the needMod flag are only |
| 2346 | ** shown to users that have moderator privileges. |
| 2347 | ** |
| 2348 | ** (1b) For forum posts, mark those that have been edited so we can |
| 2349 | ** avoid notifications for out-of-date posts, if so configured. |
| 2350 | ** |
| 2351 | ** (1c) Purge forum-post entries from wantalert if not enough time |
| 2352 | ** has passed since creation (and, optionally, if they have not |
| 2353 | ** been replied to). This gives an opportunity for "oops I made |
| 2354 | ** a typo" to be corrected before people are notified. |
| 2355 | ** |
| 2356 | ** (2) Call alert_compute_event_text() to compute a list of EmailEvent |
| 2357 | ** objects that describe all events about which we want to send |
| 2358 | ** alerts. |
| 2359 | ** |
| @@ -2335,10 +2382,13 @@ | |
| 2382 | const char *zRepoName; |
| 2383 | const char *zFrom; |
| 2384 | const char *zDest = (flags & SENDALERT_STDOUT) ? "stdout" : 0; |
| 2385 | AlertSender *pSender = 0; |
| 2386 | u32 senderFlags = 0; |
| 2387 | const int alert_omit_edited = db_get_int("alert-omit-edited", 0); |
| 2388 | const int alert_edit_window = db_get_int("alert-edit-window", 0); |
| 2389 | const int alert_notify_on_reply = db_get_int("alert-notify-on-reply", 0); |
| 2390 | |
| 2391 | if( g.fSqlTrace ) fossil_trace("-- BEGIN alert_send_alerts(%u)\n", flags); |
| 2392 | alert_schema(0); |
| 2393 | if( !alert_enabled() ) goto send_alert_done; |
| 2394 | zUrl = db_get("email-url",0); |
| @@ -2354,35 +2404,88 @@ | |
| 2404 | |
| 2405 | /* Step (1): Compute the alerts that need sending |
| 2406 | */ |
| 2407 | db_multi_exec( |
| 2408 | "DROP TABLE IF EXISTS temp.wantalert;" |
| 2409 | "CREATE TEMP TABLE wantalert(eventId TEXT, needMod BOOLEAN, sentMod," |
| 2410 | " edited BOOLEAN);" |
| 2411 | ); |
| 2412 | if( flags & SENDALERT_DIGEST ){ |
| 2413 | /* Unmoderated changes are never sent as part of a digest */ |
| 2414 | db_multi_exec( |
| 2415 | "INSERT INTO wantalert(eventId,needMod,edited)" |
| 2416 | " SELECT eventid, 0, 0" |
| 2417 | " FROM pending_alert" |
| 2418 | " WHERE sentDigest IS FALSE" |
| 2419 | " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=substr(eventid,2));" |
| 2420 | ); |
| 2421 | zDigest = "true"; |
| 2422 | }else{ |
| 2423 | /* Immediate alerts might include events that are subject to |
| 2424 | ** moderator approval */ |
| 2425 | db_multi_exec( |
| 2426 | "INSERT INTO wantalert(eventId,needMod,sentMod,edited)" |
| 2427 | " SELECT eventid," |
| 2428 | " EXISTS(SELECT 1 FROM private WHERE rid=substr(eventid,2))," |
| 2429 | " sentMod,0" |
| 2430 | " FROM pending_alert" |
| 2431 | " WHERE sentSep IS FALSE;" |
| 2432 | "DELETE FROM wantalert WHERE needMod AND sentMod;" |
| 2433 | ); |
| 2434 | } |
| 2435 | |
| 2436 | /* Step 1b |
| 2437 | ** Mark all entries as "edited" if a forum-post ("f...") and the id (after |
| 2438 | ** the "f") appears in the 'fprev' field of a 'forumpost' record. If |
| 2439 | ** 'alert-omit-edited' is enabled, notifications for such entries will NOT be |
| 2440 | ** generated, but the entries WILL be marked as 'sentSep' or 'sepDigest'. |
| 2441 | ** |
| 2442 | ** Step 1c |
| 2443 | ** Delete any entry that matchs all the following: |
| 2444 | ** a. Is a forum-post and the 'fmtime' entry from 'forumpost' is less than |
| 2445 | ** 'alert-edit-window' minutes in the past. |
| 2446 | ** b. Either 'alert-notift-on-reply' is turned off OR there is no reply to |
| 2447 | ** this post. |
| 2448 | ** c. Does not require approval. |
| 2449 | ** Entries matching all the above are removed from 'wantalert' so the original |
| 2450 | ** entries in 'pending_alert' will NOT be marked as sent/deleted. |
| 2451 | */ |
| 2452 | db_multi_exec( |
| 2453 | "UPDATE wantalert SET edited=1" |
| 2454 | " WHERE EXISTS (" |
| 2455 | " SELECT 1" |
| 2456 | " FROM forumpost" |
| 2457 | " WHERE substr(eventid,1,1)='f' AND fprev=substr(eventid,2)" |
| 2458 | " );" |
| 2459 | "DELETE FROM wantalert" |
| 2460 | " WHERE ( SELECT fmtime > julianday('now', '-%d minutes')" |
| 2461 | " FROM forumpost" |
| 2462 | " WHERE substr(eventid,1,1)='f' AND fpid=substr(eventid,2)" |
| 2463 | " )" |
| 2464 | " AND ( %d OR NOT EXISTS (" |
| 2465 | " SELECT 1" |
| 2466 | " FROM forumpost" |
| 2467 | " WHERE substr(eventid,1,1)='f' AND firt=substr(eventid,2)" |
| 2468 | " )" |
| 2469 | " )" |
| 2470 | " AND NOT needMod;", |
| 2471 | alert_edit_window, |
| 2472 | 1-alert_notify_on_reply |
| 2473 | ); |
| 2474 | |
| 2475 | if( g.fSqlTrace ) { |
| 2476 | db_prepare(&q, "SELECT eventid, needMod, sentMod, edited FROM wantalert" ) ; |
| 2477 | fossil_trace( "%10s %10s %10s %10s\n", "EventID", "NeedMod", "SentMod", "Edited" ); |
| 2478 | while( db_step(&q)==SQLITE_ROW ){ |
| 2479 | const char* eventid = db_column_text( &q, 0 ) ; |
| 2480 | const char* needMod = db_column_text( &q, 1 ) ; |
| 2481 | const char* sentMod = db_column_text( &q, 2 ) ; |
| 2482 | const char* edited = db_column_text( &q, 3 ) ; |
| 2483 | fossil_trace( "%10s %10s %10s %10s\n", eventid, needMod, sentMod, edited ); |
| 2484 | } |
| 2485 | db_finalize(&q); |
| 2486 | } |
| 2487 | |
| 2488 | /* Step 2: compute EmailEvent objects for every notification that |
| 2489 | ** needs sending. |
| 2490 | */ |
| 2491 | pEvents = alert_compute_event_text(&nEvent, (flags & SENDALERT_DIGEST)!=0); |
| @@ -2459,10 +2562,16 @@ | |
| 2562 | case 't': xType = 'r'; break; |
| 2563 | case 'w': xType = 'j'; break; |
| 2564 | } |
| 2565 | if( strchr(zCap,xType)==0 ) continue; |
| 2566 | } |
| 2567 | /* If 'alert-omit-edited' is enabled, and the current entry |
| 2568 | ** has been edited, skip it. This potentially could be made |
| 2569 | ** the subject of a user-specific setting. |
| 2570 | */ |
| 2571 | if( alert_omit_edited && p->edited ) continue; |
| 2572 | |
| 2573 | if( blob_size(&p->hdr)>0 ){ |
| 2574 | /* This alert should be sent as a separate email */ |
| 2575 | Blob fhdr, fbody; |
| 2576 | blob_init(&fhdr, 0, 0); |
| 2577 | blob_appendf(&fhdr, "To: <%s>\r\n", zEmail); |
| 2578 |