sendAdminNotifications()   B
last analyzed

Complexity

Conditions 8
Paths 5

Size

Total Lines 90
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 8.0877

Importance

Changes 0
Metric Value
cc 8
eloc 39
c 0
b 0
f 0
nc 5
nop 3
dl 0
loc 90
rs 8.0515
ccs 32
cts 36
cp 0.8889
crap 8.0877

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
 * Returns the enabled notifications from the modSettings.
249
 *
250
 * @return array
251
 * @global array $modSettings
252
 *
253
 */
254
function getEnabledNotifications()
255
{
256
	global $modSettings;
257
258
	if (empty($modSettings['enabled_mentions']))
259
	{
260
		return [];
261
	}
262
263
	$enabled = array_filter(array_unique(explode(',', $modSettings['enabled_mentions'])));
264
	sort($enabled);
265
266
	return $enabled;
267
}
268
269
/**
270
 * Queries the database for notification preferences of a set of members.
271
 *
272
 *
273
 * @param string[]|string $notification_types
274
 * @param int[]|int $members
275
 *
276
 * @return array
277
 */
278
function getUsersNotificationsPreferences($notification_types, $members)
279
{
280
	$db = database();
281
282
	$notification_types = (array) $notification_types;
283
	$query_members = (array) $members;
284
	$defaults = [];
285
	foreach (getConfiguredNotificationMethods('*') as $notification => $methods)
286
	{
287 2
		$return = [];
288
		foreach ($methods as $k => $level)
289
		{
290
			if ($level == Notifications::DEFAULT_LEVEL)
291
			{
292
				$return[] = $k;
293
			}
294
		}
295
		$defaults[$notification] = $return;
296
	}
297
298
	$results = [];
299
	$db->fetchQuery('
300
		SELECT 
301
			id_member, notification_type, mention_type
302 2
		FROM {db_prefix}notifications_pref
303
		WHERE id_member IN ({array_int:members_to})
304
			AND mention_type IN ({array_string:mention_types})',
305 2
		[
306 2
			'members_to' => $query_members,
307 2
			'mention_types' => $notification_types,
308 2
		]
309 2
	)->fetch_callback(
310 2
		function ($row) use (&$results) {
311
			if (!isset($results[$row['id_member']]))
312
			{
313 2
				$results[$row['id_member']] = [];
314
			}
315
316
			$results[$row['id_member']][$row['mention_type']] = json_decode($row['notification_type']);
317
		}
318
	);
319
320
	// Set the defaults
321
	foreach ($query_members as $member)
322
	{
323
		foreach ($notification_types as $type)
324
		{
325
			if (empty($results[$member]) && !empty($defaults[$type]))
326
			{
327
				if (!isset($results[$member]))
328
				{
329
					$results[$member] = [];
330
				}
331
332
				if (!isset($results[$member][$type]))
333
				{
334
					$results[$member][$type] = [];
335
				}
336
337
				$results[$member][$type] = $defaults[$type];
338
			}
339
		}
340
	}
341
342
	return $results;
343
}
344
345
/**
346
 * Saves into the database the notification preferences of a certain member.
347
 *
348
 * @param int $member The member id
349
 * @param array $notification_data The array of notifications ('type' => ['level'])
350
 */
351
function saveUserNotificationsPreferences($member, $notification_data)
352
{
353
	$db = database();
354
355
	$inserts = [];
356
357
	// First drop the existing settings
358
	$db->query('', '
359
		DELETE FROM {db_prefix}notifications_pref
360
		WHERE id_member = {int:member}
361
			AND mention_type IN ({array_string:mention_types})',
362
		[
363
			'member' => $member,
364
			'mention_types' => array_keys($notification_data),
365
		]
366
	);
367
368
	foreach ($notification_data as $type => $level)
369
	{
370
		// used to skip values that are here only to remove the default
371
		if (empty($level))
372
		{
373
			continue;
374
		}
375
376
		// If they have any site notifications enabled, set a flag to request Push.Permissions
377
		if (in_array('notification', $level))
378
		{
379
			$_SESSION['push_enabled'] = true;
380
		}
381
382
		$inserts[] = [
383
			$member,
384
			$type,
385
			json_encode($level),
386
		];
387
	}
388
389
	if (empty($inserts))
390
	{
391
		return;
392
	}
393
394 2
	$db->insert('',
395
		'{db_prefix}notifications_pref',
396 2
		[
397
			'id_member' => 'int',
398
			'mention_type' => 'string-12',
399
			'notification_type' => 'string',
400
		],
401
		$inserts,
402 2
		['id_member', 'mention_type']
403
	);
404
}
405
406
/**
407
 * From the list of all possible notification methods available, only those
408
 * enabled are returned.
409
 *
410
 * @param string[] $possible_methods The array of notifications ('type' => 'level')
411
 * @param string $type The type of notification (mentionmem, likemsg, etc.)
412
 *
413
 * @return array
414
 */
415
function filterNotificationMethods($possible_methods, $type)
416
{
417
	$unserialized = getConfiguredNotificationMethods($type);
418
419 2
	if (empty($unserialized))
420
	{
421
		return [];
422
	}
423
424
	$allowed = [];
425
	foreach ($possible_methods as $class)
426
	{
427
		$class = strtolower($class);
428
		if (!empty($unserialized[$class]))
429
		{
430
			$allowed[] = $class;
431
		}
432
	}
433
434
	return $allowed;
435
}
436
437
/**
438
 * Returns all the enabled methods of notification for a specific
439
 * type of notification.
440 2
 *
441
 * @param string $type The type of notification (mentionmem, likemsg, etc.)
442
 *
443
 * @return array
444
 */
445
function getConfiguredNotificationMethods($type = '*')
446
{
447
	global $modSettings;
448
449
	$unserialized = Util::unserialize($modSettings['notification_methods']);
450
451
	if (isset($unserialized[$type]))
452
	{
453
		return $unserialized[$type];
454 2
	}
455
456 2
	if ($type === '*')
457
	{
458 2
		return $unserialized;
459 2
	}
460
461
	return [];
462 2
}
463
464 2
/**
465
 * Creates a hash code using the notification details and our secret key
466
 *
467
 * - If no salt (secret key) has been set, creates a random one and saves it
468 2
 * in modSettings for future use
469
 *
470
 * @param string $memID member id
471 2
 * @param string $memEmail member email address
472 2
 * @param string $memSalt member salt
473
 * @param string $area area to unsubscribe
474 2
 * @param string $extra area specific data such as topic id or liked msg
475
 * @return string the token for the unsubscribe link
476 2
 */
477
function getNotifierToken($memID, $memEmail, $memSalt, $area, $extra)
478
{
479
	global $modSettings;
480
481
	// We need a site salt to keep things moving
482
	if (empty($modSettings['unsubscribe_site_salt']))
483
	{
484
		$tokenizer = new TokenHash();
485 2
486
		// Extra digits of salt
487
		$unsubscribe_site_salt = $tokenizer->generate_hash(22);
488
		updateSettings(['unsubscribe_site_salt' => $unsubscribe_site_salt]);
489 2
	}
490 2
491
	// Generate a code suitable for Blowfish crypt.
492
	$blowfish_salt = '$2a$07$' . $memSalt . $modSettings['unsubscribe_site_salt'] . '$';
493
	$now = time();
494
	$hash = crypt($area . $extra . $now . $memEmail . $memSalt, $blowfish_salt);
495
496 2
	// Return just the hash, drop the salt
497 2
	return urlencode(implode('_',
498
		[
499
			$memID,
500 2
			substr($hash, 28),
501 2
			$area,
502
			$extra,
503 2
			$now
504
		]
505 2
	));
506 2
}
507
508 2
/**
509
 * Validates a hash code using the notification details and our secret key
510 1
 *
511 2
 * - If no site salt (secret key) has been set, simply fails
512
 *
513
 * @param string $memEmail member email address
514
 * @param string $memSalt member salt
515 2
 * @param string $area data to validate = area + extra + time from link
516
 * @param string $hash the hash from the link
517
 * @return bool
518
 */
519
function validateNotifierToken($memEmail, $memSalt, $area, $hash)
520
{
521
	global $modSettings;
522
523
	if (empty($modSettings['unsubscribe_site_salt']))
524
	{
525
		return false;
526
	}
527
528
	$blowfish_salt = '$2a$07$' . $memSalt . $modSettings['unsubscribe_site_salt']. '$';
529
	$expected = substr($blowfish_salt, 0, 28) . $hash;
530 2
	$check = crypt($area . $memEmail . $memSalt, $blowfish_salt);
531 2
532 2
	// Basic safe compare
533 2
	return hash_equals($expected, $check);
534 2
}
535
536
/**
537
 * Fetches a set of data for a topic that will then be used in creating/building notification emails.
538 2
 *
539
 * @param int[] $topics
540
 * @param string $type
541
 * @return array[] A board array and the topic info array.  Board array used to search for board subscriptions.
542
 */
543
function getTopicInfos($topics, $type)
544
{
545
	$db = database();
546
547
	$topicData = [];
548
	$boards_index = [];
549
550
	$db->fetchQuery('
551
		SELECT 
552
			mf.subject, ml.body, ml.id_member, t.id_last_msg, t.id_topic, t.id_board, t.id_member_started,
553
			mem.signature, COALESCE(mem.real_name, ml.poster_name) AS poster_name, COUNT(a.id_attach) as num_attach
554
		FROM {db_prefix}topics AS t
555
			INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
556
			INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
557
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = ml.id_member)
558
			LEFT JOIN {db_prefix}attachments AS a ON(a.attachment_type = {int:attachment_type} AND a.id_msg = t.id_last_msg)
559
		WHERE t.id_topic IN ({array_int:topic_list})
560
		GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9',
561
		[
562
			'topic_list' => $topics,
563
			'attachment_type' => 0,
564
		]
565
	)->fetch_callback(
566
		function ($row) use (&$topicData, &$boards_index, $type) {
567
			// all the boards for these topics, used to find all the members to be notified
568
			$boards_index[] = $row['id_board'];
569
570
			// And the information we are going to tell them about
571
			$topicData[$row['id_topic']] = [
572
				'subject' => $row['subject'],
573
				'body' => $row['body'],
574
				'last_id' => (int) $row['id_last_msg'],
575
				'topic' => (int) $row['id_topic'],
576
				'board' => (int) $row['id_board'],
577
				'id_member_started' => (int) $row['id_member_started'],
578
				'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...
579
				'exclude' => '',
580
				'signature' => $row['signature'],
581
				'attachments' => (int) $row['num_attach'],
582
			];
583
		}
584
	);
585
586
	return [$boards_index, $topicData];
587
}
588
589
/**
590
 * Keeps the log_digest up to date for members who want weekly/daily updates
591
 *
592
 * @param array $digest_insert
593
 * @return void
594
 */
595
function insertLogDigestQueue($digest_insert)
596
{
597
	$db = database();
598
599
	$db->insert('',
600
		'{db_prefix}log_digest',
601
		[
602
			'id_topic' => 'int', 'id_msg' => 'int', 'note_type' => 'string', 'exclude' => 'int',
603
		],
604
		$digest_insert,
605
		[]
606
	);
607
}
608
609
/**
610
 * Find the members with *board* notifications on.
611
 *
612
 * What it does:
613
 * Finds board notifications that meet:
614
 * 	- Member has watch notifications on for the board
615 2
 *  - The notification type of reply and/or moderation notices
616
 *  - Notification regularity of instantly or first unread
617 2
 *  - Member is activated
618
 *  - Member has access to the board where the topic resides
619
 *
620 2
 * @param int $user_id the id of the poster as they do not get a notification of their own post
621
 * @param int[] $boards_index list of boards to check
622
 * @param string $type type of activity, like reply or lock
623
 * @param int|int[] $members_only returns data only for a list of members
624
 * @param string $regularity type of notification 'email' or 'onsite'
625
 * @return array
626
 */
627 2
function fetchBoardNotifications($user_id, $boards_index, $type, $members_only, $regularity = 'email')
628 2
{
629 2
	$db = database();
630
631
	$boardNotifyData = [];
632 2
633
	$notify_types = $type === 'reply' ? 'mem.notify_types < 4' : 'mem.notify_types < 3';
634
	$notify_regularity = $regularity === 'email' ? 'mem.notify_regularity < 2' : 'mem.notify_regularity = 4';
635
636
	// Find the members (excluding the poster) that have board notification enabled.
637
	$db->fetchQuery('
638
		SELECT
639
			mem.id_member, mem.email_address, mem.notify_regularity, mem.notify_types, mem.notify_send_body, 
640
			mem.lngfile, mem.warning, mem.id_group, mem.additional_groups, mem.id_post_group, mem.password_salt,
641
			b.member_groups, b.name, b.id_profile,
642
			ln.id_board, ln.sent
643
		FROM {db_prefix}log_notify AS ln
644
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = ln.id_board)
645
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member)
646
		WHERE ln.id_board IN ({array_int:board_list})
647
			AND {raw:notify_types}
648
			AND {raw:notify_regularity}
649
			AND mem.is_activated = {int:is_activated}
650
			AND ln.id_member != {int:current_member}' .
651
		(empty($members_only) ? '' : ' AND ln.id_member IN ({array_int:members_only})') . '
652
		ORDER BY mem.lngfile',
653
		[
654
			'current_member' => $user_id,
655
			'board_list' => $boards_index,
656
			'notify_types' => $notify_types,
657
			'notify_regularity' => $notify_regularity,
658
			'is_activated' => 1,
659
			'members_only' => is_array($members_only) ? $members_only : [$members_only],
660
		]
661
	)->fetch_callback(
662
		function ($row) use (&$boardNotifyData) {
663
			// The board subscription information for the member
664
			$clean = [
665
				'id_member' => (int) $row['id_member'],
666
				'email_address' => $row['email_address'],
667
				'notify_regularity' => (int) $row['notify_regularity'],
668
				'notify_types' => (int) $row['notify_types'],
669
				'notify_send_body' => (int) $row['notify_send_body'],
670
				'lngfile' => $row['lngfile'],
671
				'warning' => $row['warning'],
672
				'id_group' => (int) $row['id_group'],
673
				'additional_groups' => $row['additional_groups'],
674
				'id_post_group' => (int) $row['id_post_group'],
675
				'password_salt' => $row['password_salt'],
676
				'member_groups' => $row['member_groups'],
677
				'board_name' => $row['name'],
678
				'id_profile_board' => (int) $row['id_profile'],
679
				'id_board' => (int) $row['id_board'],
680
				'sent' => (int) $row['sent'],
681
			];
682
683
			if (validateNotificationAccess($clean, false))
684
			{
685
				$boardNotifyData[] = $clean;
686
			}
687
		}
688
	);
689
690
	return $boardNotifyData;
691
}
692
693
/**
694
 * Finds members who have topic notification on for a topic where the type is one that they want to be
695
 * kept in the loop.
696
 *
697
 * What it does:
698
 * Finds notifications that meet:
699
 * 	- Member has watch notifications on for these topics
700
 *  - Member has set notification type of reply and/or moderation notices
701
 *      - ALL_MESSAGES = 1;
702
 *      - MODERATION_ONLY_IF_STARTED = 2;
703
 *      - ONLY_REPLIES = 3;
704
 *      - NOTHING_AT_ALL = 4;
705
 *  - Member has set notification regularity to 'email' (instantly or first unread) or 'onsite' for onsite
706
 *      - NOTHING = 99;
707
 *      - EMAIL INSTANTLY = 0;
708
 *      - EMAIL FIRST_UNREAD_MSG = 1;
709
 *      - DAILY_DIGEST = 2;
710
 *      - WEEKLY_DIGEST = 3;
711
 *      - ONSITE FIRST_UNREAD_MSG = 4;
712
 *  - Member is activated
713
 *  - Member has access to the board where the topic resides
714
 *
715
 * @param int $user_id some goon, e.g. the originator of the action who **WILL NOT** get a notification
716
 * @param int[] $topics array of topic id's that have updates
717
 * @param string $type type of notification like reply, lock, sticky
718
 * @param int|int[] $members_only if not empty, only send notices to these members
719
 * @param string $regularity type of notification 'email' or 'onsite'
720
 * @return array
721
 */
722
function fetchTopicNotifications($user_id, $topics, $type, $members_only, $regularity = 'email')
723
{
724
	$db = database();
725
726
	$topicNotifyData = [];
727
728
	$notify_types = $type === 'reply' ? 'mem.notify_types < 4' : 'mem.notify_types < 3';
729
	$notify_regularity = $regularity === 'email' ? 'mem.notify_regularity < 2' : 'mem.notify_regularity = 4';
730
731
	// Find the members with notification on for this topic.
732
	$db->fetchQuery('
733
		SELECT
734
			mem.id_member, mem.email_address, mem.notify_regularity, mem.notify_types, mem.notify_send_body, mem.notify_from,
735
			mem.lngfile, mem.warning, mem.id_group, mem.additional_groups, mem.id_post_group, mem.password_salt,
736
			t.id_member_started, t.id_last_msg,
737
			b.member_groups, b.name, b.id_profile, b.id_board,
738
			ln.id_topic, ln.sent
739
		FROM {db_prefix}log_notify AS ln
740
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member)
741
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ln.id_topic)
742
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
743
		WHERE ln.id_topic IN ({array_int:topic_list})
744
			AND {raw:notify_types}
745
			AND {raw:notify_regularity}
746
			AND mem.is_activated = {int:is_activated}
747
			AND ln.id_member != {int:current_member}' .
748
		(empty($members_only) ? '' : ' AND ln.id_member IN ({array_int:members_only})') . '
749
		ORDER BY mem.lngfile',
750
		[
751
			'current_member' => $user_id,
752
			'topic_list' => $topics,
753
			'notify_types' => $notify_types,
754
			'notify_regularity' => $notify_regularity,
755
			'is_activated' => 1,
756
			'members_only' => is_array($members_only) ? $members_only : [$members_only],
757
		]
758
	)->fetch_callback(
759
		function ($row) use (&$topicNotifyData) {
760
			// The topic subscription information for the member
761
			$clean = [
762
				'id_member' => (int) $row['id_member'],
763
				'email_address' => $row['email_address'],
764
				'notify_regularity' => (int) $row['notify_regularity'],
765
				'notify_types' => (int) $row['notify_types'],
766
				'notify_send_body' => (int) $row['notify_send_body'],
767
				'notify_from' => (int) $row['notify_from'],
768
				'lngfile' => $row['lngfile'],
769
				'warning' => $row['warning'],
770
				'id_group' => (int) $row['id_group'],
771
				'additional_groups' => $row['additional_groups'],
772
				'id_post_group' => (int) $row['id_post_group'],
773
				'password_salt' => $row['password_salt'],
774
				'id_member_started' => (int) $row['id_member_started'],
775
				'member_groups' => $row['member_groups'],
776
				'board_name' => $row['name'],
777
				'id_profile_board' => (int) $row['id_profile'],
778
				'id_board' =>  (int) $row['id_board'],
779
				'id_topic' => (int) $row['id_topic'],
780
				'last_id' => (int) $row['id_last_msg'],
781
				'sent' => (int) $row['sent'],
782
			];
783
784
			if (validateNotificationAccess($clean, false))
785
			{
786
				$topicNotifyData[$clean['id_topic']] = $clean;
787
			}
788
		}
789
	);
790
791
	return $topicNotifyData;
792
}
793
794
/**
795
 * Updates the log_notify table for all members that have received notifications
796
 *
797
 * @param int $user_id
798
 * @param array $data
799
 * @param boolean $board if true updates a boards log notify, else topic
800
 * @return void
801
 */
802
function updateLogNotify($user_id, $data, $board = false)
803
{
804
	$db = database();
805 2
806
	$db->query('', '
807 2
		UPDATE {db_prefix}log_notify
808
		SET 
809
			sent = {int:is_sent}
810 2
		WHERE ' . ($board ? 'id_board' : 'id_topic') . ' IN ({array_int:data_list})
811
			AND id_member != {int:current_member}',
812 2
		[
813
			'current_member' => $user_id,
814
			'data_list' => $data,
815
			'is_sent' => 1,
816
		]
817
	);
818
}
819
820
/**
821
 * Finds members who have topic notification on for a topic where the type is one that they want to be
822
 * kept in the loop.
823
 *
824
 * What it does:
825
 * Finds notifications that meet:
826
 * 	- Members has watch notifications enabled for these topics
827
 *  - The notification type is not none/off (all, replies+moderation, moderation only)
828
 *  - Notification regularity of instantly or first unread
829
 *  - Member is activated
830
 *  - Member has access to the board where the topic resides
831
 *
832
 * @param int[] $topics array of topic id's that have updates
833
 * @return array
834
 */
835
function fetchApprovalNotifications($topics)
836
{
837
	$db = database();
838
839
	$approvalNotifyData = [];
840
841
	// Find everyone who needs to know about this.
842
	$db->fetchQuery('
843
		SELECT
844
			DISTINCT mem.id_member, mem.email_address, mem.notify_regularity, mem.notify_types, mem.notify_send_body, mem.notify_from,
845
			mem.lngfile, mem.warning, mem.id_group, mem.additional_groups, mem.id_post_group, mem.password_salt,
846
			t.id_member_started,
847
			b.member_groups, b.name, b.id_profile, b.id_board,
848
			ln.id_topic, ln.sent
849
		FROM {db_prefix}log_notify AS ln
850
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member)
851
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ln.id_topic)
852
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
853
		WHERE ln.id_topic IN ({array_int:topic_list})
854
			AND mem.is_activated = {int:is_activated}
855
			AND mem.notify_types < {int:notify_types}
856
			AND mem.notify_regularity < {int:notify_regularity}
857
		ORDER BY mem.lngfile',
858
		[
859
			'topic_list' => $topics,
860
			'is_activated' => 1,
861
			'notify_types' => 4,
862
			'notify_regularity' => 2,
863
		]
864
	)->fetch_callback(
865
		function ($row) use (&$approvalNotifyData) {
866
			$clean = [
867
				'id_member' => (int) $row['id_member'],
868
				'email_address' => $row['email_address'],
869
				'notify_regularity' => (int) $row['notify_regularity'],
870
				'notify_types' => (int) $row['notify_types'],
871
				'notify_send_body' => (int) $row['notify_send_body'],
872
				'notify_from' => (int) $row['notify_from'],
873
				'lngfile' => $row['lngfile'],
874
				'warning' => $row['warning'],
875
				'id_group' => (int) $row['id_group'],
876
				'additional_groups' => $row['additional_groups'],
877
				'id_post_group' => (int) $row['id_post_group'],
878
				'password_salt' => $row['password_salt'],
879
				'id_member_started' => (int) $row['id_member_started'],
880
				'member_groups' => $row['member_groups'],
881
				'board_name' => $row['name'],
882
				'id_profile_board' => (int) $row['id_profile'],
883
				'id_board' =>  (int) $row['id_board'],
884
				'id_topic' => (int) $row['id_topic'],
885
				'sent' => (int) $row['sent'],
886
			];
887
888
			if (validateNotificationAccess($clean, false))
889
			{
890
				$approvalNotifyData[] = $clean;
891
			}
892
		}
893
	);
894
895
	return $approvalNotifyData;
896
}
897