Passed
Push — development ( e156b5...b889cb )
by Spuds
01:09 queued 28s
created

fetchTopicNotifications()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 70
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 38
dl 0
loc 70
rs 8.6897
c 1
b 0
f 0
cc 6
nc 4
nop 5
ccs 0
cts 34
cp 0
crap 42

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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;
0 ignored issues
show
Bug Best Practice introduced by
The property language does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
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,
0 ignored issues
show
Bug Best Practice introduced by
The property name does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
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