1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Functions to support the sending of notifications (new posts, replys, topics) |
5
|
|
|
* |
6
|
|
|
* @package ElkArte Forum |
7
|
|
|
* @copyright ElkArte Forum contributors |
8
|
|
|
* @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file) |
9
|
|
|
* |
10
|
|
|
* @version 2.0 dev |
11
|
|
|
* |
12
|
|
|
*/ |
13
|
|
|
|
14
|
|
|
use ElkArte\Helper\TokenHash; |
15
|
|
|
use ElkArte\Helper\Util; |
16
|
|
|
use ElkArte\Languages\Loader; |
17
|
|
|
use ElkArte\Notifications\Notifications; |
18
|
|
|
use ElkArte\Notifications\PostNotifications; |
19
|
|
|
use ElkArte\User; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* Sends a notification to members who have elected to receive emails |
23
|
|
|
* |
24
|
|
|
* @param int[]|int $topics - represents the topics the action is happening to. |
25
|
|
|
* @param string $type - can be any of reply, sticky, lock, unlock, remove, |
26
|
|
|
* move, merge, and split. An appropriate message will be sent for each. |
27
|
|
|
* @param int[]|int $exclude = array() - members in exclude array will not be |
28
|
|
|
* processed for the topic with the same key. |
29
|
|
|
* @param int[]|int $members_only = array() - are the only ones that will be sent the notification if they have it on. |
30
|
|
|
* @param array $pbe = array() - array containing user_info if this is being run as a result of an email posting |
31
|
|
|
*/ |
32
|
|
|
function sendNotifications($topics, $type, $exclude = [], $members_only = [], $pbe = []) |
33
|
|
|
{ |
34
|
|
|
// Simply redirects to PostNotifications class sendNotifications method |
35
|
2 |
|
// @todo I could find no use of $exclude in any calls to this function, that is why its missing :/ |
36
|
|
|
(new PostNotifications())->sendNotifications($topics, $type, $members_only, $pbe); |
37
|
2 |
|
} |
38
|
|
|
|
39
|
|
|
/** |
40
|
2 |
|
* Notifies members who have requested notification for new topics posted on a board of said posts. |
41
|
2 |
|
* |
42
|
|
|
* @param array $topicData |
43
|
|
|
*/ |
44
|
2 |
|
function sendBoardNotifications(&$topicData) |
45
|
|
|
{ |
46
|
|
|
// Redirects to PostNotifications class sendBoardNotifications method |
47
|
|
|
(new PostNotifications())->sendBoardNotifications($topicData); |
48
|
|
|
} |
49
|
|
|
|
50
|
2 |
|
/** |
51
|
|
|
* A special function for handling the hell which is sending approval notifications. |
52
|
2 |
|
* |
53
|
|
|
* @param array $topicData |
54
|
|
|
*/ |
55
|
|
|
function sendApprovalNotifications(&$topicData) |
56
|
2 |
|
{ |
57
|
2 |
|
(new PostNotifications())->sendApprovalNotifications($topicData); |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* This simple function gets a list of all administrators and emails them |
62
|
|
|
* to let them know a new member has joined. |
63
|
2 |
|
* Called by registerMember() function in subs/Members.subs.php. |
64
|
2 |
|
* Email is sent to all groups that have the moderate_forum permission. |
65
|
|
|
* The language set by each member is being used (if available). |
66
|
|
|
* |
67
|
2 |
|
* @param string $type types supported are 'approval', 'activation', and 'standard'. |
68
|
2 |
|
* @param int $memberID |
69
|
2 |
|
* @param string|null $member_name = null |
70
|
|
|
* @uses the Login language file. |
71
|
|
|
*/ |
72
|
|
|
function sendAdminNotifications($type, $memberID, $member_name = null) |
73
|
|
|
{ |
74
|
|
|
global $modSettings, $language; |
75
|
|
|
|
76
|
|
|
$db = database(); |
77
|
|
|
|
78
|
|
|
// If the setting isn't enabled then just exit. |
79
|
|
|
if (empty($modSettings['notify_new_registration'])) |
80
|
|
|
{ |
81
|
2 |
|
return; |
82
|
2 |
|
} |
83
|
|
|
|
84
|
2 |
|
// Needed to notify admins, or anyone |
85
|
|
|
require_once(SUBSDIR . '/Mail.subs.php'); |
86
|
|
|
|
87
|
2 |
|
if ($member_name === null) |
88
|
|
|
{ |
89
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
90
|
2 |
|
|
91
|
|
|
// Get the new user's name.... |
92
|
|
|
$member_info = getBasicMemberData($memberID); |
93
|
2 |
|
$member_name = $member_info['real_name']; |
94
|
2 |
|
} |
95
|
2 |
|
|
96
|
2 |
|
// All membergroups who can approve members. |
97
|
2 |
|
$groups = []; |
98
|
2 |
|
$db->fetchQuery(' |
99
|
2 |
|
SELECT |
100
|
2 |
|
id_group |
101
|
2 |
|
FROM {db_prefix}permissions |
102
|
2 |
|
WHERE permission = {string:moderate_forum} |
103
|
|
|
AND add_deny = {int:add_deny} |
104
|
2 |
|
AND id_group != {int:id_group}', |
105
|
|
|
[ |
106
|
|
|
'add_deny' => 1, |
107
|
|
|
'id_group' => 0, |
108
|
2 |
|
'moderate_forum' => 'moderate_forum', |
109
|
|
|
] |
110
|
2 |
|
)->fetch_callback( |
111
|
|
|
function ($row) use (&$groups) { |
112
|
1 |
|
$groups[] = $row['id_group']; |
113
|
|
|
} |
114
|
|
|
); |
115
|
|
|
|
116
|
|
|
// Add administrators too... |
117
|
2 |
|
$groups[] = 1; |
118
|
|
|
$groups = array_unique($groups); |
119
|
|
|
|
120
|
|
|
// Get a list of all members who have ability to approve accounts - these are the people who we inform. |
121
|
|
|
$current_language = User::$info->language; |
|
|
|
|
122
|
2 |
|
$db->query('', ' |
123
|
|
|
SELECT |
124
|
|
|
id_member, lngfile, email_address |
125
|
2 |
|
FROM {db_prefix}members |
126
|
|
|
WHERE (id_group IN ({array_int:group_list}) OR FIND_IN_SET({raw:group_array_implode}, additional_groups) != 0) |
127
|
|
|
AND notify_types != {int:notify_types} |
128
|
|
|
ORDER BY lngfile', |
129
|
|
|
[ |
130
|
|
|
'group_list' => $groups, |
131
|
2 |
|
'notify_types' => 4, |
132
|
2 |
|
'group_array_implode' => implode(', additional_groups) != 0 OR FIND_IN_SET(', $groups), |
133
|
|
|
] |
134
|
2 |
|
)->fetch_callback( |
135
|
|
|
function ($row) use ($type, $member_name, $memberID, $language) { |
136
|
|
|
global $scripturl, $modSettings; |
137
|
2 |
|
|
138
|
2 |
|
$replacements = [ |
139
|
|
|
'USERNAME' => $member_name, |
140
|
2 |
|
'PROFILELINK' => $scripturl . '?action=profile;u=' . $memberID |
141
|
|
|
]; |
142
|
1 |
|
$emailtype = 'admin_notify'; |
143
|
2 |
|
|
144
|
|
|
// If they need to be approved add more info... |
145
|
|
|
if ($type === 'approval') |
146
|
|
|
{ |
147
|
2 |
|
$replacements['APPROVALLINK'] = $scripturl . '?action=admin;area=viewmembers;sa=browse;type=approve'; |
148
|
|
|
$emailtype .= '_approval'; |
149
|
|
|
} |
150
|
2 |
|
|
151
|
|
|
$emaildata = loadEmailTemplate($emailtype, $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']); |
152
|
|
|
|
153
|
|
|
// And do the actual sending... |
154
|
|
|
sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 0); |
155
|
|
|
} |
156
|
|
|
); |
157
|
|
|
|
158
|
|
|
if (isset($current_language) && $current_language !== User::$info->language) |
159
|
|
|
{ |
160
|
|
|
$lang_loader = new Loader(null, $txt, database()); |
161
|
|
|
$lang_loader->load('Login', false); |
162
|
|
|
} |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* Checks if a user has the correct access to get notifications |
167
|
|
|
* - validates they have proper group access to a board |
168
|
|
|
* - if using the maillist, checks if they should get a reply-able message |
169
|
|
|
* - not muted |
170
|
|
|
* - has postby_email permission on the board |
171
|
|
|
* |
172
|
|
|
* Returns false if they do not have the proper group access to a board |
173
|
|
|
* Sets email_perm to false if they should not get a reply-able message |
174
|
|
|
* |
175
|
|
|
* @param array $row |
176
|
|
|
* @param bool $maillist |
177
|
|
|
* @param bool $email_perm |
178
|
|
|
* |
179
|
|
|
* @return bool |
180
|
|
|
*/ |
181
|
|
|
function validateNotificationAccess($row, $maillist, &$email_perm = true) |
182
|
|
|
{ |
183
|
|
|
global $modSettings; |
184
|
|
|
|
185
|
|
|
static $board_profile = []; |
186
|
|
|
|
187
|
|
|
$member_in_groups = array_merge([$row['id_group'], $row['id_post_group']], (empty($row['additional_groups']) ? [] : explode(',', $row['additional_groups']))); |
188
|
|
|
$board_allowed_groups = explode(',', $row['member_groups']); |
189
|
|
|
|
190
|
|
|
// Standardize the data |
191
|
|
|
$member_in_groups = array_map('intval', $member_in_groups); |
192
|
|
|
$board_allowed_groups = array_map('intval', $board_allowed_groups); |
193
|
|
|
|
194
|
|
|
// No need to check for you ;) |
195
|
|
|
if (!in_array(1, $member_in_groups, true)) |
196
|
|
|
{ |
197
|
|
|
$email_perm = true; |
198
|
|
|
|
199
|
|
|
return true; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
// They do have access to this board? |
203
|
|
|
if (count(array_intersect($member_in_groups, $board_allowed_groups)) === 0) |
204
|
|
|
{ |
205
|
|
|
$email_perm = false; |
206
|
|
|
|
207
|
|
|
return false; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
// If using maillist, see if they should get a reply-able message |
211
|
|
|
if ($email_perm && $maillist) |
212
|
|
|
{ |
213
|
|
|
// Perhaps they don't require or deserve a security key in the message |
214
|
|
|
if (!empty($modSettings['postmod_active']) |
215
|
|
|
&& !empty($modSettings['warning_mute']) && $modSettings['warning_mute'] <= $row['warning']) |
216
|
|
|
{ |
217
|
|
|
$email_perm = false; |
218
|
|
|
|
219
|
|
|
return false; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
if (!isset($board_profile[$row['id_board']])) |
223
|
|
|
{ |
224
|
|
|
require_once(SUBSDIR . '/Members.subs.php'); |
225
|
|
|
$board_profile[$row['id_board']] = groupsAllowedTo('postby_email', $row['id_board']); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
// In a group that has email posting permissions on this board |
229
|
|
|
if (count(array_intersect($board_profile[$row['id_board']]['allowed'], $member_in_groups)) === 0) |
230
|
|
|
{ |
231
|
|
|
$email_perm = false; |
232
|
|
|
|
233
|
|
|
return false; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
// And not specifically denied? |
237
|
|
|
if ($email_perm && !empty($modSettings['permission_enable_deny']) |
238
|
|
|
&& count(array_intersect($member_in_groups, $board_profile[$row['id_board']]['denied'])) !== 0) |
239
|
|
|
{ |
240
|
|
|
$email_perm = false; |
241
|
|
|
} |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
return $email_perm; |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Queries the database for notification preferences of a set of members. |
249
|
|
|
* |
250
|
|
|
* @param string[]|string $notification_types |
251
|
|
|
* @param int[]|int $members |
252
|
|
|
* |
253
|
|
|
* @return array |
254
|
|
|
*/ |
255
|
|
|
function getUsersNotificationsPreferences($notification_types, $members) |
256
|
|
|
{ |
257
|
|
|
$db = database(); |
258
|
|
|
|
259
|
|
|
$notification_types = (array) $notification_types; |
260
|
|
|
$query_members = (array) $members; |
261
|
|
|
$defaults = []; |
262
|
|
|
foreach (getConfiguredNotificationMethods('*') as $notification => $methods) |
263
|
|
|
{ |
264
|
|
|
$return = []; |
265
|
|
|
foreach ($methods as $k => $level) |
266
|
|
|
{ |
267
|
|
|
if ($level == Notifications::DEFAULT_LEVEL) |
268
|
|
|
{ |
269
|
|
|
$return[] = $k; |
270
|
|
|
} |
271
|
|
|
} |
272
|
|
|
$defaults[$notification] = $return; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
$results = []; |
276
|
|
|
$db->fetchQuery(' |
277
|
|
|
SELECT |
278
|
|
|
id_member, notification_type, mention_type |
279
|
|
|
FROM {db_prefix}notifications_pref |
280
|
|
|
WHERE id_member IN ({array_int:members_to}) |
281
|
|
|
AND mention_type IN ({array_string:mention_types})', |
282
|
|
|
[ |
283
|
|
|
'members_to' => $query_members, |
284
|
|
|
'mention_types' => $notification_types, |
285
|
|
|
] |
286
|
|
|
)->fetch_callback( |
287
|
2 |
|
function ($row) use (&$results) { |
288
|
|
|
if (!isset($results[$row['id_member']])) |
289
|
|
|
{ |
290
|
|
|
$results[$row['id_member']] = []; |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
$results[$row['id_member']][$row['mention_type']] = json_decode($row['notification_type']); |
294
|
|
|
} |
295
|
|
|
); |
296
|
|
|
|
297
|
|
|
// Set the defaults |
298
|
|
|
foreach ($query_members as $member) |
299
|
|
|
{ |
300
|
|
|
foreach ($notification_types as $type) |
301
|
|
|
{ |
302
|
2 |
|
if (empty($results[$member]) && !empty($defaults[$type])) |
303
|
|
|
{ |
304
|
|
|
if (!isset($results[$member])) |
305
|
2 |
|
{ |
306
|
2 |
|
$results[$member] = []; |
307
|
2 |
|
} |
308
|
2 |
|
|
309
|
2 |
|
if (!isset($results[$member][$type])) |
310
|
2 |
|
{ |
311
|
|
|
$results[$member][$type] = []; |
312
|
|
|
} |
313
|
2 |
|
|
314
|
|
|
$results[$member][$type] = $defaults[$type]; |
315
|
|
|
} |
316
|
|
|
} |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
return $results; |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
/** |
323
|
|
|
* Saves into the database the notification preferences of a certain member. |
324
|
|
|
* |
325
|
|
|
* @param int $member The member id |
326
|
|
|
* @param array $notification_data The array of notifications ('type' => ['level']) |
327
|
|
|
*/ |
328
|
|
|
function saveUserNotificationsPreferences($member, $notification_data) |
329
|
|
|
{ |
330
|
|
|
$db = database(); |
331
|
|
|
|
332
|
|
|
$inserts = []; |
333
|
|
|
|
334
|
|
|
// First drop the existing settings |
335
|
|
|
$db->query('', ' |
336
|
|
|
DELETE FROM {db_prefix}notifications_pref |
337
|
|
|
WHERE id_member = {int:member} |
338
|
|
|
AND mention_type IN ({array_string:mention_types})', |
339
|
|
|
[ |
340
|
|
|
'member' => $member, |
341
|
|
|
'mention_types' => array_keys($notification_data), |
342
|
|
|
] |
343
|
|
|
); |
344
|
|
|
|
345
|
|
|
foreach ($notification_data as $type => $level) |
346
|
|
|
{ |
347
|
|
|
// used to skip values that are here only to remove the default |
348
|
|
|
if (empty($level)) |
349
|
|
|
{ |
350
|
|
|
continue; |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
// If they have any site notifications enabled, set a flag to request Push.Permissions |
354
|
|
|
if (in_array('notification', $level)) |
355
|
|
|
{ |
356
|
|
|
$_SESSION['push_enabled'] = true; |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
$inserts[] = [ |
360
|
|
|
$member, |
361
|
|
|
$type, |
362
|
|
|
json_encode($level), |
363
|
|
|
]; |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
if (empty($inserts)) |
367
|
|
|
{ |
368
|
|
|
return; |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
$db->insert('', |
372
|
|
|
'{db_prefix}notifications_pref', |
373
|
|
|
[ |
374
|
|
|
'id_member' => 'int', |
375
|
|
|
'mention_type' => 'string-12', |
376
|
|
|
'notification_type' => 'string', |
377
|
|
|
], |
378
|
|
|
$inserts, |
379
|
|
|
['id_member', 'mention_type'] |
380
|
|
|
); |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
/** |
384
|
|
|
* From the list of all possible notification methods available, only those |
385
|
|
|
* enabled are returned. |
386
|
|
|
* |
387
|
|
|
* @param string[] $possible_methods The array of notifications ('type' => 'level') |
388
|
|
|
* @param string $type The type of notification (mentionmem, likemsg, etc.) |
389
|
|
|
* |
390
|
|
|
* @return array |
391
|
|
|
*/ |
392
|
|
|
function filterNotificationMethods($possible_methods, $type) |
393
|
|
|
{ |
394
|
2 |
|
$unserialized = getConfiguredNotificationMethods($type); |
395
|
|
|
|
396
|
2 |
|
if (empty($unserialized)) |
397
|
|
|
{ |
398
|
|
|
return []; |
399
|
|
|
} |
400
|
|
|
|
401
|
|
|
$allowed = []; |
402
|
2 |
|
foreach ($possible_methods as $class) |
403
|
|
|
{ |
404
|
|
|
$class = strtolower($class); |
405
|
|
|
if (!empty($unserialized[$class])) |
406
|
|
|
{ |
407
|
|
|
$allowed[] = $class; |
408
|
|
|
} |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
return $allowed; |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
/** |
415
|
|
|
* Returns all the enabled methods of notification for a specific |
416
|
|
|
* type of notification. |
417
|
|
|
* |
418
|
|
|
* @param string $type The type of notification (mentionmem, likemsg, etc.) |
419
|
2 |
|
* |
420
|
|
|
* @return array |
421
|
|
|
*/ |
422
|
|
|
function getConfiguredNotificationMethods($type = '*') |
423
|
|
|
{ |
424
|
|
|
global $modSettings; |
425
|
|
|
|
426
|
|
|
$unserialized = Util::unserialize($modSettings['notification_methods']); |
427
|
|
|
|
428
|
|
|
if (isset($unserialized[$type])) |
429
|
|
|
{ |
430
|
|
|
return $unserialized[$type]; |
431
|
|
|
} |
432
|
|
|
|
433
|
|
|
if ($type === '*') |
434
|
|
|
{ |
435
|
|
|
return $unserialized; |
436
|
|
|
} |
437
|
|
|
|
438
|
|
|
return []; |
439
|
|
|
} |
440
|
2 |
|
|
441
|
|
|
/** |
442
|
|
|
* Creates a hash code using the notification details and our secret key |
443
|
|
|
* |
444
|
|
|
* - If no salt (secret key) has been set, creates a random one and saves it |
445
|
|
|
* in modSettings for future use |
446
|
|
|
* |
447
|
|
|
* @param string $memID member id |
448
|
|
|
* @param string $memEmail member email address |
449
|
|
|
* @param string $memSalt member salt |
450
|
|
|
* @param string $area area to unsubscribe |
451
|
|
|
* @param string $extra area specific data such as topic id or liked msg |
452
|
|
|
* @return string the token for the unsubscribe link |
453
|
|
|
*/ |
454
|
2 |
|
function getNotifierToken($memID, $memEmail, $memSalt, $area, $extra) |
455
|
|
|
{ |
456
|
2 |
|
global $modSettings; |
457
|
|
|
|
458
|
2 |
|
// We need a site salt to keep things moving |
459
|
2 |
|
if (empty($modSettings['unsubscribe_site_salt'])) |
460
|
|
|
{ |
461
|
|
|
$tokenizer = new TokenHash(); |
462
|
2 |
|
|
463
|
|
|
// Extra digits of salt |
464
|
2 |
|
$unsubscribe_site_salt = $tokenizer->generate_hash(22); |
465
|
|
|
updateSettings(['unsubscribe_site_salt' => $unsubscribe_site_salt]); |
466
|
|
|
} |
467
|
|
|
|
468
|
2 |
|
// Generate a code suitable for Blowfish crypt. |
469
|
|
|
$blowfish_salt = '$2a$07$' . $memSalt . $modSettings['unsubscribe_site_salt'] . '$'; |
470
|
|
|
$now = time(); |
471
|
2 |
|
$hash = crypt($area . $extra . $now . $memEmail . $memSalt, $blowfish_salt); |
472
|
2 |
|
|
473
|
|
|
// Return just the hash, drop the salt |
474
|
2 |
|
return urlencode(implode('_', |
475
|
|
|
[ |
476
|
2 |
|
$memID, |
477
|
|
|
substr($hash, 28), |
478
|
|
|
$area, |
479
|
|
|
$extra, |
480
|
|
|
$now |
481
|
|
|
] |
482
|
|
|
)); |
483
|
|
|
} |
484
|
|
|
|
485
|
2 |
|
/** |
486
|
|
|
* Validates a hash code using the notification details and our secret key |
487
|
|
|
* |
488
|
|
|
* - If no site salt (secret key) has been set, simply fails |
489
|
2 |
|
* |
490
|
2 |
|
* @param string $memEmail member email address |
491
|
|
|
* @param string $memSalt member salt |
492
|
|
|
* @param string $area data to validate = area + extra + time from link |
493
|
|
|
* @param string $hash the hash from the link |
494
|
|
|
* @return bool |
495
|
|
|
*/ |
496
|
2 |
|
function validateNotifierToken($memEmail, $memSalt, $area, $hash) |
497
|
2 |
|
{ |
498
|
|
|
global $modSettings; |
499
|
|
|
|
500
|
2 |
|
if (empty($modSettings['unsubscribe_site_salt'])) |
501
|
2 |
|
{ |
502
|
|
|
return false; |
503
|
2 |
|
} |
504
|
|
|
|
505
|
2 |
|
$blowfish_salt = '$2a$07$' . $memSalt . $modSettings['unsubscribe_site_salt']. '$'; |
506
|
2 |
|
$expected = substr($blowfish_salt, 0, 28) . $hash; |
507
|
|
|
$check = crypt($area . $memEmail . $memSalt, $blowfish_salt); |
508
|
2 |
|
|
509
|
|
|
// Basic safe compare |
510
|
1 |
|
return hash_equals($expected, $check); |
511
|
2 |
|
} |
512
|
|
|
|
513
|
|
|
/** |
514
|
|
|
* Fetches a set of data for a topic that will then be used in creating/building notification emails. |
515
|
2 |
|
* |
516
|
|
|
* @param int[] $topics |
517
|
|
|
* @param string $type |
518
|
|
|
* @return array[] A board array and the topic info array. Board array used to search for board subscriptions. |
519
|
|
|
*/ |
520
|
|
|
function getTopicInfos($topics, $type) |
521
|
|
|
{ |
522
|
|
|
$db = database(); |
523
|
|
|
|
524
|
|
|
$topicData = []; |
525
|
|
|
$boards_index = []; |
526
|
|
|
|
527
|
|
|
$db->fetchQuery(' |
528
|
|
|
SELECT |
529
|
|
|
mf.subject, ml.body, ml.id_member, t.id_last_msg, t.id_topic, t.id_board, t.id_member_started, |
530
|
2 |
|
mem.signature, COALESCE(mem.real_name, ml.poster_name) AS poster_name, COUNT(a.id_attach) as num_attach |
531
|
2 |
|
FROM {db_prefix}topics AS t |
532
|
2 |
|
INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg) |
533
|
2 |
|
INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg) |
534
|
2 |
|
LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = ml.id_member) |
535
|
|
|
LEFT JOIN {db_prefix}attachments AS a ON(a.attachment_type = {int:attachment_type} AND a.id_msg = t.id_last_msg) |
536
|
|
|
WHERE t.id_topic IN ({array_int:topic_list}) |
537
|
|
|
GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9', |
538
|
2 |
|
[ |
539
|
|
|
'topic_list' => $topics, |
540
|
|
|
'attachment_type' => 0, |
541
|
|
|
] |
542
|
|
|
)->fetch_callback( |
543
|
|
|
function ($row) use (&$topicData, &$boards_index, $type) { |
544
|
|
|
// all the boards for these topics, used to find all the members to be notified |
545
|
|
|
$boards_index[] = $row['id_board']; |
546
|
|
|
|
547
|
|
|
// And the information we are going to tell them about |
548
|
|
|
$topicData[$row['id_topic']] = [ |
549
|
|
|
'subject' => $row['subject'], |
550
|
|
|
'body' => $row['body'], |
551
|
|
|
'last_id' => (int) $row['id_last_msg'], |
552
|
|
|
'topic' => (int) $row['id_topic'], |
553
|
|
|
'board' => (int) $row['id_board'], |
554
|
|
|
'id_member_started' => (int) $row['id_member_started'], |
555
|
|
|
'name' => $type === 'reply' ? $row['poster_name'] : User::$info->name, |
|
|
|
|
556
|
|
|
'exclude' => '', |
557
|
|
|
'signature' => $row['signature'], |
558
|
|
|
'attachments' => (int) $row['num_attach'], |
559
|
|
|
]; |
560
|
|
|
} |
561
|
|
|
); |
562
|
|
|
|
563
|
|
|
return [$boards_index, $topicData]; |
564
|
|
|
} |
565
|
|
|
|
566
|
|
|
/** |
567
|
|
|
* Keeps the log_digest up to date for members who want weekly/daily updates |
568
|
|
|
* |
569
|
|
|
* @param array $digest_insert |
570
|
|
|
* @return void |
571
|
|
|
*/ |
572
|
|
|
function insertLogDigestQueue($digest_insert) |
573
|
|
|
{ |
574
|
|
|
$db = database(); |
575
|
|
|
|
576
|
|
|
$db->insert('', |
577
|
|
|
'{db_prefix}log_digest', |
578
|
|
|
[ |
579
|
|
|
'id_topic' => 'int', 'id_msg' => 'int', 'note_type' => 'string', 'exclude' => 'int', |
580
|
|
|
], |
581
|
|
|
$digest_insert, |
582
|
|
|
[] |
583
|
|
|
); |
584
|
|
|
} |
585
|
|
|
|
586
|
|
|
/** |
587
|
|
|
* Find the members with *board* notifications on. |
588
|
|
|
* |
589
|
|
|
* What it does: |
590
|
|
|
* Finds board notifications that meet: |
591
|
|
|
* - Member has watch notifications on for the board |
592
|
|
|
* - The notification type of reply and/or moderation notices |
593
|
|
|
* - Notification regularity of instantly or first unread |
594
|
|
|
* - Member is activated |
595
|
|
|
* - Member has access to the board where the topic resides |
596
|
|
|
* |
597
|
|
|
* @param int $user_id the id of the poster as they do not get a notification of their own post |
598
|
|
|
* @param int[] $boards_index list of boards to check |
599
|
|
|
* @param string $type type of activity, like reply or lock |
600
|
|
|
* @param int|int[] $members_only returns data only for a list of members |
601
|
|
|
* @param string $regularity type of notification 'email' or 'onsite' |
602
|
|
|
* @return array |
603
|
|
|
*/ |
604
|
|
|
function fetchBoardNotifications($user_id, $boards_index, $type, $members_only, $regularity = 'email') |
605
|
|
|
{ |
606
|
|
|
$db = database(); |
607
|
|
|
|
608
|
|
|
$boardNotifyData = []; |
609
|
|
|
|
610
|
|
|
$notify_types = $type === 'reply' ? 'mem.notify_types < 4' : 'mem.notify_types < 3'; |
611
|
|
|
$notify_regularity = $regularity === 'email' ? 'mem.notify_regularity < 2' : 'mem.notify_regularity = 4'; |
612
|
|
|
|
613
|
|
|
// Find the members (excluding the poster) that have board notification enabled. |
614
|
|
|
$db->fetchQuery(' |
615
|
2 |
|
SELECT |
616
|
|
|
mem.id_member, mem.email_address, mem.notify_regularity, mem.notify_types, mem.notify_send_body, |
617
|
2 |
|
mem.lngfile, mem.warning, mem.id_group, mem.additional_groups, mem.id_post_group, mem.password_salt, |
618
|
|
|
b.member_groups, b.name, b.id_profile, |
619
|
|
|
ln.id_board, ln.sent |
620
|
2 |
|
FROM {db_prefix}log_notify AS ln |
621
|
|
|
INNER JOIN {db_prefix}boards AS b ON (b.id_board = ln.id_board) |
622
|
|
|
INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member) |
623
|
|
|
WHERE ln.id_board IN ({array_int:board_list}) |
624
|
|
|
AND {raw:notify_types} |
625
|
|
|
AND {raw:notify_regularity} |
626
|
|
|
AND mem.is_activated = {int:is_activated} |
627
|
2 |
|
AND ln.id_member != {int:current_member}' . |
628
|
2 |
|
(empty($members_only) ? '' : ' AND ln.id_member IN ({array_int:members_only})') . ' |
629
|
2 |
|
ORDER BY mem.lngfile', |
630
|
|
|
[ |
631
|
|
|
'current_member' => $user_id, |
632
|
2 |
|
'board_list' => $boards_index, |
633
|
|
|
'notify_types' => $notify_types, |
634
|
|
|
'notify_regularity' => $notify_regularity, |
635
|
|
|
'is_activated' => 1, |
636
|
|
|
'members_only' => is_array($members_only) ? $members_only : [$members_only], |
637
|
|
|
] |
638
|
|
|
)->fetch_callback( |
639
|
|
|
function ($row) use (&$boardNotifyData) { |
640
|
|
|
// The board subscription information for the member |
641
|
|
|
$clean = [ |
642
|
|
|
'id_member' => (int) $row['id_member'], |
643
|
|
|
'email_address' => $row['email_address'], |
644
|
|
|
'notify_regularity' => (int) $row['notify_regularity'], |
645
|
|
|
'notify_types' => (int) $row['notify_types'], |
646
|
|
|
'notify_send_body' => (int) $row['notify_send_body'], |
647
|
|
|
'lngfile' => $row['lngfile'], |
648
|
|
|
'warning' => $row['warning'], |
649
|
|
|
'id_group' => (int) $row['id_group'], |
650
|
|
|
'additional_groups' => $row['additional_groups'], |
651
|
|
|
'id_post_group' => (int) $row['id_post_group'], |
652
|
|
|
'password_salt' => $row['password_salt'], |
653
|
|
|
'member_groups' => $row['member_groups'], |
654
|
|
|
'board_name' => $row['name'], |
655
|
|
|
'id_profile_board' => (int) $row['id_profile'], |
656
|
|
|
'id_board' => (int) $row['id_board'], |
657
|
|
|
'sent' => (int) $row['sent'], |
658
|
|
|
]; |
659
|
|
|
|
660
|
|
|
if (validateNotificationAccess($clean, false)) |
661
|
|
|
{ |
662
|
|
|
$boardNotifyData[] = $clean; |
663
|
|
|
} |
664
|
|
|
} |
665
|
|
|
); |
666
|
|
|
|
667
|
|
|
return $boardNotifyData; |
668
|
|
|
} |
669
|
|
|
|
670
|
|
|
/** |
671
|
|
|
* Finds members who have topic notification on for a topic where the type is one that they want to be |
672
|
|
|
* kept in the loop. |
673
|
|
|
* |
674
|
|
|
* What it does: |
675
|
|
|
* Finds notifications that meet: |
676
|
|
|
* - Member has watch notifications on for these topics |
677
|
|
|
* - Member has set notification type of reply and/or moderation notices |
678
|
|
|
* - ALL_MESSAGES = 1; |
679
|
|
|
* - MODERATION_ONLY_IF_STARTED = 2; |
680
|
|
|
* - ONLY_REPLIES = 3; |
681
|
|
|
* - NOTHING_AT_ALL = 4; |
682
|
|
|
* - Member has set notification regularity to 'email' (instantly or first unread) or 'onsite' for onsite |
683
|
|
|
* - NOTHING = 99; |
684
|
|
|
* - EMAIL INSTANTLY = 0; |
685
|
|
|
* - EMAIL FIRST_UNREAD_MSG = 1; |
686
|
|
|
* - DAILY_DIGEST = 2; |
687
|
|
|
* - WEEKLY_DIGEST = 3; |
688
|
|
|
* - ONSITE FIRST_UNREAD_MSG = 4; |
689
|
|
|
* - Member is activated |
690
|
|
|
* - Member has access to the board where the topic resides |
691
|
|
|
* |
692
|
|
|
* @param int $user_id some goon, e.g. the originator of the action who **WILL NOT** get a notification |
693
|
|
|
* @param int[] $topics array of topic id's that have updates |
694
|
|
|
* @param string $type type of notification like reply, lock, sticky |
695
|
|
|
* @param int|int[] $members_only if not empty, only send notices to these members |
696
|
|
|
* @param string $regularity type of notification 'email' or 'onsite' |
697
|
|
|
* @return array |
698
|
|
|
*/ |
699
|
|
|
function fetchTopicNotifications($user_id, $topics, $type, $members_only, $regularity = 'email') |
700
|
|
|
{ |
701
|
|
|
$db = database(); |
702
|
|
|
|
703
|
|
|
$topicNotifyData = []; |
704
|
|
|
|
705
|
|
|
$notify_types = $type === 'reply' ? 'mem.notify_types < 4' : 'mem.notify_types < 3'; |
706
|
|
|
$notify_regularity = $regularity === 'email' ? 'mem.notify_regularity < 2' : 'mem.notify_regularity = 4'; |
707
|
|
|
|
708
|
|
|
// Find the members with notification on for this topic. |
709
|
|
|
$db->fetchQuery(' |
710
|
|
|
SELECT |
711
|
|
|
mem.id_member, mem.email_address, mem.notify_regularity, mem.notify_types, mem.notify_send_body, mem.notify_from, |
712
|
|
|
mem.lngfile, mem.warning, mem.id_group, mem.additional_groups, mem.id_post_group, mem.password_salt, |
713
|
|
|
t.id_member_started, t.id_last_msg, |
714
|
|
|
b.member_groups, b.name, b.id_profile, b.id_board, |
715
|
|
|
ln.id_topic, ln.sent |
716
|
|
|
FROM {db_prefix}log_notify AS ln |
717
|
|
|
INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member) |
718
|
|
|
INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ln.id_topic) |
719
|
|
|
INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) |
720
|
|
|
WHERE ln.id_topic IN ({array_int:topic_list}) |
721
|
|
|
AND {raw:notify_types} |
722
|
|
|
AND {raw:notify_regularity} |
723
|
|
|
AND mem.is_activated = {int:is_activated} |
724
|
|
|
AND ln.id_member != {int:current_member}' . |
725
|
|
|
(empty($members_only) ? '' : ' AND ln.id_member IN ({array_int:members_only})') . ' |
726
|
|
|
ORDER BY mem.lngfile', |
727
|
|
|
[ |
728
|
|
|
'current_member' => $user_id, |
729
|
|
|
'topic_list' => $topics, |
730
|
|
|
'notify_types' => $notify_types, |
731
|
|
|
'notify_regularity' => $notify_regularity, |
732
|
|
|
'is_activated' => 1, |
733
|
|
|
'members_only' => is_array($members_only) ? $members_only : [$members_only], |
734
|
|
|
] |
735
|
|
|
)->fetch_callback( |
736
|
|
|
function ($row) use (&$topicNotifyData) { |
737
|
|
|
// The topic subscription information for the member |
738
|
|
|
$clean = [ |
739
|
|
|
'id_member' => (int) $row['id_member'], |
740
|
|
|
'email_address' => $row['email_address'], |
741
|
|
|
'notify_regularity' => (int) $row['notify_regularity'], |
742
|
|
|
'notify_types' => (int) $row['notify_types'], |
743
|
|
|
'notify_send_body' => (int) $row['notify_send_body'], |
744
|
|
|
'notify_from' => (int) $row['notify_from'], |
745
|
|
|
'lngfile' => $row['lngfile'], |
746
|
|
|
'warning' => $row['warning'], |
747
|
|
|
'id_group' => (int) $row['id_group'], |
748
|
|
|
'additional_groups' => $row['additional_groups'], |
749
|
|
|
'id_post_group' => (int) $row['id_post_group'], |
750
|
|
|
'password_salt' => $row['password_salt'], |
751
|
|
|
'id_member_started' => (int) $row['id_member_started'], |
752
|
|
|
'member_groups' => $row['member_groups'], |
753
|
|
|
'board_name' => $row['name'], |
754
|
|
|
'id_profile_board' => (int) $row['id_profile'], |
755
|
|
|
'id_board' => (int) $row['id_board'], |
756
|
|
|
'id_topic' => (int) $row['id_topic'], |
757
|
|
|
'last_id' => (int) $row['id_last_msg'], |
758
|
|
|
'sent' => (int) $row['sent'], |
759
|
|
|
]; |
760
|
|
|
|
761
|
|
|
if (validateNotificationAccess($clean, false)) |
762
|
|
|
{ |
763
|
|
|
$topicNotifyData[$clean['id_topic']] = $clean; |
764
|
|
|
} |
765
|
|
|
} |
766
|
|
|
); |
767
|
|
|
|
768
|
|
|
return $topicNotifyData; |
769
|
|
|
} |
770
|
|
|
|
771
|
|
|
/** |
772
|
|
|
* Updates the log_notify table for all members that have received notifications |
773
|
|
|
* |
774
|
|
|
* @param int $user_id |
775
|
|
|
* @param array $data |
776
|
|
|
* @param boolean $board if true updates a boards log notify, else topic |
777
|
|
|
* @return void |
778
|
|
|
*/ |
779
|
|
|
function updateLogNotify($user_id, $data, $board = false) |
780
|
|
|
{ |
781
|
|
|
$db = database(); |
782
|
|
|
|
783
|
|
|
$db->query('', ' |
784
|
|
|
UPDATE {db_prefix}log_notify |
785
|
|
|
SET |
786
|
|
|
sent = {int:is_sent} |
787
|
|
|
WHERE ' . ($board ? 'id_board' : 'id_topic') . ' IN ({array_int:data_list}) |
788
|
|
|
AND id_member != {int:current_member}', |
789
|
|
|
[ |
790
|
|
|
'current_member' => $user_id, |
791
|
|
|
'data_list' => $data, |
792
|
|
|
'is_sent' => 1, |
793
|
|
|
] |
794
|
|
|
); |
795
|
|
|
} |
796
|
|
|
|
797
|
|
|
/** |
798
|
|
|
* Finds members who have topic notification on for a topic where the type is one that they want to be |
799
|
|
|
* kept in the loop. |
800
|
|
|
* |
801
|
|
|
* What it does: |
802
|
|
|
* Finds notifications that meet: |
803
|
|
|
* - Members has watch notifications enabled for these topics |
804
|
|
|
* - The notification type is not none/off (all, replies+moderation, moderation only) |
805
|
2 |
|
* - Notification regularity of instantly or first unread |
806
|
|
|
* - Member is activated |
807
|
2 |
|
* - Member has access to the board where the topic resides |
808
|
|
|
* |
809
|
|
|
* @param int[] $topics array of topic id's that have updates |
810
|
2 |
|
* @return array |
811
|
|
|
*/ |
812
|
2 |
|
function fetchApprovalNotifications($topics) |
813
|
|
|
{ |
814
|
|
|
$db = database(); |
815
|
|
|
|
816
|
|
|
$approvalNotifyData = []; |
817
|
|
|
|
818
|
|
|
// Find everyone who needs to know about this. |
819
|
|
|
$db->fetchQuery(' |
820
|
|
|
SELECT |
821
|
|
|
DISTINCT mem.id_member, mem.email_address, mem.notify_regularity, mem.notify_types, mem.notify_send_body, mem.notify_from, |
822
|
|
|
mem.lngfile, mem.warning, mem.id_group, mem.additional_groups, mem.id_post_group, mem.password_salt, |
823
|
|
|
t.id_member_started, |
824
|
|
|
b.member_groups, b.name, b.id_profile, b.id_board, |
825
|
|
|
ln.id_topic, ln.sent |
826
|
|
|
FROM {db_prefix}log_notify AS ln |
827
|
|
|
INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member) |
828
|
|
|
INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ln.id_topic) |
829
|
|
|
INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board) |
830
|
|
|
WHERE ln.id_topic IN ({array_int:topic_list}) |
831
|
|
|
AND mem.is_activated = {int:is_activated} |
832
|
|
|
AND mem.notify_types < {int:notify_types} |
833
|
|
|
AND mem.notify_regularity < {int:notify_regularity} |
834
|
|
|
ORDER BY mem.lngfile', |
835
|
|
|
[ |
836
|
|
|
'topic_list' => $topics, |
837
|
|
|
'is_activated' => 1, |
838
|
|
|
'notify_types' => 4, |
839
|
|
|
'notify_regularity' => 2, |
840
|
|
|
] |
841
|
|
|
)->fetch_callback( |
842
|
|
|
function ($row) use (&$approvalNotifyData) { |
843
|
|
|
$clean = [ |
844
|
|
|
'id_member' => (int) $row['id_member'], |
845
|
|
|
'email_address' => $row['email_address'], |
846
|
|
|
'notify_regularity' => (int) $row['notify_regularity'], |
847
|
|
|
'notify_types' => (int) $row['notify_types'], |
848
|
|
|
'notify_send_body' => (int) $row['notify_send_body'], |
849
|
|
|
'notify_from' => (int) $row['notify_from'], |
850
|
|
|
'lngfile' => $row['lngfile'], |
851
|
|
|
'warning' => $row['warning'], |
852
|
|
|
'id_group' => (int) $row['id_group'], |
853
|
|
|
'additional_groups' => $row['additional_groups'], |
854
|
|
|
'id_post_group' => (int) $row['id_post_group'], |
855
|
|
|
'password_salt' => $row['password_salt'], |
856
|
|
|
'id_member_started' => (int) $row['id_member_started'], |
857
|
|
|
'member_groups' => $row['member_groups'], |
858
|
|
|
'board_name' => $row['name'], |
859
|
|
|
'id_profile_board' => (int) $row['id_profile'], |
860
|
|
|
'id_board' => (int) $row['id_board'], |
861
|
|
|
'id_topic' => (int) $row['id_topic'], |
862
|
|
|
'sent' => (int) $row['sent'], |
863
|
|
|
]; |
864
|
|
|
|
865
|
|
|
if (validateNotificationAccess($clean, false)) |
866
|
|
|
{ |
867
|
|
|
$approvalNotifyData[] = $clean; |
868
|
|
|
} |
869
|
|
|
} |
870
|
|
|
); |
871
|
|
|
|
872
|
|
|
return $approvalNotifyData; |
873
|
|
|
} |
874
|
|
|
|