Completed
Branch development (176841)
by Elk
06:59
created

Notification.subs.php ➔ sendApprovalNotifications()   F

Complexity

Conditions 22
Paths 1753

Size

Total Lines 136

Duplication

Lines 19
Ratio 13.97 %

Code Coverage

Tests 0
CRAP Score 506

Importance

Changes 0
Metric Value
cc 22
nc 1753
nop 1
dl 19
loc 136
rs 0
c 0
b 0
f 0
ccs 0
cts 62
cp 0
crap 506

How to fix   Long Method    Complexity   

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
/**
15
 * Sends a notification to members who have elected to receive emails
16
 * when things happen to a topic, such as replies are posted.
17
 * The function automatically finds the subject and its board, and
18
 * checks permissions for each member who is "signed up" for notifications.
19
 * It will not send 'reply' notifications more than once in a row.
20
 *
21
 * @param int[]|int $topics - represents the topics the action is happening to.
22
 * @param string $type - can be any of reply, sticky, lock, unlock, remove,
23
 *                       move, merge, and split.  An appropriate message will be sent for each.
24
 * @param int[]|int $exclude = array() - members in the exclude array will not be
25
 *                                   processed for the topic with the same key.
26
 * @param int[]|int $members_only = array() - are the only ones that will be sent the notification if they have it on.
27
 * @param mixed[] $pbe = array() - array containing user_info if this is being run as a result of an email posting
28
 * @uses Post language file
29
 * @throws \ElkArte\Exceptions\Exception
30
 */
31
function sendNotifications($topics, $type, $exclude = array(), $members_only = array(), $pbe = array())
32
{
33
	global $txt, $scripturl, $language, $webmaster_email, $mbname, $modSettings;
34
35
	$db = database();
36
37
	// Coming in from emailpost or emailtopic, if so pbe values will be set to the credentials of the emailer
38
	$user_id = (!empty($pbe['user_info']['id']) && !empty($modSettings['maillist_enabled'])) ? $pbe['user_info']['id'] : User::$info->id;
39
	$user_language = (!empty($pbe['user_info']['language']) && !empty($modSettings['maillist_enabled'])) ? $pbe['user_info']['language'] : User::$info->language;
40
41
	// Can't do it if there's no topics.
42
	if (empty($topics))
43
		return;
44
45
	// It must be an array - it must!
46
	if (!is_array($topics))
47
		$topics = array($topics);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $topics. This often makes code more readable.
Loading history...
48
49
	// I hope we are not sending one of those silly moderation notices
50
	$maillist = !empty($modSettings['maillist_enabled']) && !empty($modSettings['pbe_post_enabled']);
51
	if ($type !== 'reply' && !empty($maillist) && !empty($modSettings['pbe_no_mod_notices']))
52
		return;
53
54
	// Load in our dependencies
55
	require_once(SUBSDIR . '/Emailpost.subs.php');
56
	require_once(SUBSDIR . '/Mail.subs.php');
57
58
	// Get the subject, body and basic poster details, number of attachments if any
59
	$result = $db->query('', '
60
		SELECT mf.subject, ml.body, ml.id_member, t.id_last_msg, t.id_topic, t.id_board, mem.signature,
61
			COALESCE(mem.real_name, ml.poster_name) AS poster_name, COUNT(a.id_attach) as num_attach
62
		FROM {db_prefix}topics AS t
63
			INNER JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
64
			INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
65
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = ml.id_member)
66
			LEFT JOIN {db_prefix}attachments AS a ON(a.attachment_type = {int:attachment_type} AND a.id_msg = t.id_last_msg)
67
		WHERE t.id_topic IN ({array_int:topic_list})
68
		GROUP BY t.id_topic, mf.subject, ml.body, ml.id_member, mem.signature, mem.real_name, ml.poster_name',
69
		array(
70
			'topic_list' => $topics,
71
			'attachment_type' => 0,
72
		)
73
	);
74
	$topicData = array();
75
	$boards_index = array();
76
	while ($row = $db->fetch_assoc($result))
77
	{
78
		// Convert to markdown e.g. text ;) and clean it up
79
		pbe_prepare_text($row['body'], $row['subject'], $row['signature']);
80
81
		// all the boards for these topics, used to find all the members to be notified
82
		$boards_index[] = $row['id_board'];
83
84
		// And the information we are going to tell them about
85
		$topicData[$row['id_topic']] = array(
86
			'subject' => $row['subject'],
87
			'body' => $row['body'],
88
			'last_id' => $row['id_last_msg'],
89
			'topic' => $row['id_topic'],
90
			'board' => $row['id_board'],
91
			'name' => $type === 'reply' ? $row['poster_name'] : User::$info->name,
92
			'exclude' => '',
93
			'signature' => $row['signature'],
94
			'attachments' => $row['num_attach'],
95
		);
96
	}
97
	$db->free_result($result);
98
99
	// Work out any exclusions...
100
	foreach ($topics as $key => $id)
101
		if (isset($topicData[$id]) && !empty($exclude[$key]))
102
			$topicData[$id]['exclude'] = (int) $exclude[$key];
103
104
	// Nada?
105
	if (empty($topicData))
106
		trigger_error('sendNotifications(): topics not found', E_USER_NOTICE);
107
108
	$topics = array_keys($topicData);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $topics. This often makes code more readable.
Loading history...
109
110
	// Just in case they've gone walkies.
111
	if (empty($topics))
112
		return;
113
114
	// Insert all of these items into the digest log for those who want notifications later.
115
	$digest_insert = array();
116 View Code Duplication
	foreach ($topicData as $id => $data)
117
		$digest_insert[] = array($data['topic'], $data['last_id'], $type, (int) $data['exclude']);
118
119
	$db->insert('',
120
		'{db_prefix}log_digest',
121
		array(
122
			'id_topic' => 'int', 'id_msg' => 'int', 'note_type' => 'string', 'exclude' => 'int',
123
		),
124
		$digest_insert,
125
		array()
126
	);
127
128
	// Are we doing anything here?
129
	$sent = 0;
130
131
	// Using the posting email function in either group or list mode
132
	if ($maillist)
133
	{
134
		// Find the members with *board* notifications on.
135
		$members = $db->query('', '
136
			SELECT
137
				mem.id_member, mem.email_address, mem.notify_regularity, mem.notify_types, mem.notify_send_body, mem.lngfile, mem.warning,
138
				ln.sent, mem.id_group, mem.additional_groups, b.member_groups, mem.id_post_group, b.name, b.id_profile,
139
				ln.id_board
140
			FROM {db_prefix}log_notify AS ln
141
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = ln.id_board)
142
				INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member)
143
			WHERE ln.id_board IN ({array_int:board_list})
144
				AND mem.notify_types != {int:notify_types}
145
				AND mem.notify_regularity < {int:notify_regularity}
146
				AND mem.is_activated = {int:is_activated}
147
				AND ln.id_member != {int:current_member}' .
148
				(empty($members_only) ? '' : ' AND ln.id_member IN ({array_int:members_only})') . '
149
			ORDER BY mem.lngfile',
150
			array(
151
				'current_member' => $user_id,
152
				'board_list' => $boards_index,
153
				'notify_types' => $type === 'reply' ? 4 : 3,
154
				'notify_regularity' => 2,
155
				'is_activated' => 1,
156
				'members_only' => is_array($members_only) ? $members_only : array($members_only),
157
			)
158
		);
159
		$boards = array();
160
		while ($row = $db->fetch_assoc($members))
161
		{
162
			// If they are not the poster do they want to know?
163
			// @todo maybe if they posted via email?
164
			if ($type !== 'reply' && $row['notify_types'] == 2)
165
				continue;
166
167
			// for this member/board, loop through the topics and see if we should send it
168
			foreach ($topicData as $id => $data)
169
			{
170
				// Don't send it if its not from the right board
171
				if ($data['board'] !== $row['id_board'])
172
					continue;
173
				else
174
					$data['board_name'] = $row['name'];
175
176
				// Don't do the excluded...
177
				if ($data['exclude'] === $row['id_member'])
178
					continue;
179
180
				$email_perm = true;
181
				if (validateNotificationAccess($row, $maillist, $email_perm) === false)
182
					continue;
183
184
				$needed_language = empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile'];
185 View Code Duplication
				if (empty($current_language) || $current_language != $needed_language)
186
					$current_language = theme()->getTemplates()->loadLanguageFile('Post', $needed_language, false);
187
188
				$message_type = 'notification_' . $type;
189
				$replacements = array(
190
					'TOPICSUBJECT' => $data['subject'],
191
					'POSTERNAME' => un_htmlspecialchars($data['name']),
192
					'TOPICLINKNEW' => $scripturl . '?topic=' . $id . '.new;topicseen#new',
193
					'TOPICLINK' => $scripturl . '?topic=' . $id . '.msg' . $data['last_id'] . '#msg' . $data['last_id'],
194
					'UNSUBSCRIBELINK' => $scripturl . '?action=notifyboard;board=' . $data['board'] . '.0',
195
					'SIGNATURE' => $data['signature'],
196
					'BOARDNAME' => $data['board_name'],
197
				);
198
199
				if ($type === 'remove')
200
					unset($replacements['TOPICLINK'], $replacements['UNSUBSCRIBELINK']);
201
202
				// Do they want the body of the message sent too?
203
				if (!empty($row['notify_send_body']) && $type === 'reply')
204
				{
205
					$message_type .= '_body';
206
					$replacements['MESSAGE'] = $data['body'];
207
208
					// Any attachments? if so lets make a big deal about them!
209
					if ($data['attachments'] != 0)
210
						$replacements['MESSAGE'] .= "\n\n" . sprintf($txt['message_attachments'], $data['attachments'], $replacements['TOPICLINK']);
211
				}
212
213
				if (!empty($row['notify_regularity']) && $type === 'reply')
214
					$message_type .= '_once';
215
216
				// Give them a way to add in their own replacements
217
				call_integration_hook('integrate_notification_replacements', array(&$replacements, $row, $type, $current_language));
218
219
				// Send only if once is off or it's on and it hasn't been sent.
220
				if ($type !== 'reply' || empty($row['notify_regularity']) || empty($row['sent']))
221
				{
222
					$emaildata = loadEmailTemplate((($maillist && $email_perm && $type === 'reply' && !empty($row['notify_send_body'])) ? 'pbe_' : '') . $message_type, $replacements, $needed_language);
223
224
					// If using the maillist functions, we adjust who this is coming from
225
					if ($maillist && $email_perm && $type === 'reply' && !empty($row['notify_send_body']))
226
					{
227
						// In group mode like google group or yahoo group, the mail is from the poster
228
						// Otherwise in maillist mode, it is from the site
229
						$emailfrom = !empty($modSettings['maillist_group_mode']) ? un_htmlspecialchars($data['name']) : (!empty($modSettings['maillist_sitename']) ? un_htmlspecialchars($modSettings['maillist_sitename']) : $mbname);
230
231
						// The email address of the sender, irrespective of the envelope name above
232
						$from_wrapper = !empty($modSettings['maillist_mail_from']) ? $modSettings['maillist_mail_from'] : (empty($modSettings['maillist_sitename_address']) ? $webmaster_email : $modSettings['maillist_sitename_address']);
233
						sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], $emailfrom, 'm' . $data['last_id'], false, 3, null, false, $from_wrapper, $id);
234
					}
235 View Code Duplication
					else
236
						sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, 'm' . $data['last_id']);
237
238
					$sent++;
239
240
					// Make a note that this member was sent this topic
241
					$boards[$row['id_member']][$id] = 1;
242
				}
243
			}
244
		}
245
		$db->free_result($members);
246
	}
247
248
	// Find the members with notification on for this topic.
249
	$members = $db->query('', '
250
		SELECT
251
			mem.id_member, mem.email_address, mem.notify_regularity, mem.notify_types, mem.warning,
252
			mem.notify_send_body, mem.lngfile, mem.id_group, mem.additional_groups,mem.id_post_group,
253
			t.id_member_started, b.member_groups, b.name, b.id_profile, b.id_board,
254
			ln.id_topic, ln.sent
255
		FROM {db_prefix}log_notify AS ln
256
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member)
257
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ln.id_topic)
258
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
259
		WHERE ln.id_topic IN ({array_int:topic_list})
260
			AND mem.notify_types < {int:notify_types}
261
			AND mem.notify_regularity < {int:notify_regularity}
262
			AND mem.is_activated = {int:is_activated}
263
			AND ln.id_member != {int:current_member}' .
264
			(empty($members_only) ? '' : ' AND ln.id_member IN ({array_int:members_only})') . '
265
		ORDER BY mem.lngfile',
266
		array(
267
			'current_member' => $user_id,
268
			'topic_list' => $topics,
269
			'notify_types' => $type == 'reply' ? 4 : 3,
270
			'notify_regularity' => 2,
271
			'is_activated' => 1,
272
			'members_only' => is_array($members_only) ? $members_only : array($members_only),
273
		)
274
	);
275
	while ($row = $db->fetch_assoc($members))
276
	{
277
		// Don't do the excluded...
278
		if ($topicData[$row['id_topic']]['exclude'] == $row['id_member'])
279
			continue;
280
281
		// Don't do the ones that were sent via board notification, you only get one notice
282
		if (isset($boards[$row['id_member']][$row['id_topic']]))
283
			continue;
284
285
		// Easier to check this here... if they aren't the topic poster do they really want to know?
286
		// @todo perhaps just if they posted by email?
287
		if ($type != 'reply' && $row['notify_types'] == 2 && $row['id_member'] != $row['id_member_started'])
288
			continue;
289
290
		$email_perm = true;
291
		if (validateNotificationAccess($row, $maillist, $email_perm) === false)
292
			continue;
293
294
		$needed_language = empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile'];
295 View Code Duplication
		if (empty($current_language) || $current_language != $needed_language)
296
			$current_language = theme()->getTemplates()->loadLanguageFile('Post', $needed_language, false);
297
298
		$message_type = 'notification_' . $type;
299
		$replacements = array(
300
			'TOPICSUBJECT' => $topicData[$row['id_topic']]['subject'],
301
			'POSTERNAME' => un_htmlspecialchars($topicData[$row['id_topic']]['name']),
302
			'TOPICLINKNEW' => $scripturl . '?topic=' . $row['id_topic'] . '.new;topicseen#new',
303
			'TOPICLINK' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $data['last_id'] . '#msg' . $data['last_id'],
0 ignored issues
show
Bug introduced by
The variable $data does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
304
			'UNSUBSCRIBELINK' => $scripturl . '?action=notify;topic=' . $row['id_topic'] . '.0',
305
			'SIGNATURE' => $topicData[$row['id_topic']]['signature'],
306
			'BOARDNAME' => $row['name'],
307
		);
308
309
		if ($type == 'remove')
310
			unset($replacements['TOPICLINK'], $replacements['UNSUBSCRIBELINK']);
311
312
		// Do they want the body of the message sent too?
313 View Code Duplication
		if (!empty($row['notify_send_body']) && $type == 'reply')
314
		{
315
			$message_type .= '_body';
316
			$replacements['MESSAGE'] = $topicData[$row['id_topic']]['body'];
317
		}
318
		if (!empty($row['notify_regularity']) && $type == 'reply')
319
			$message_type .= '_once';
320
321
		// Send only if once is off or it's on and it hasn't been sent.
322
		if ($type != 'reply' || empty($row['notify_regularity']) || empty($row['sent']))
323
		{
324
			$emaildata = loadEmailTemplate((($maillist && $email_perm && $type === 'reply' && !empty($row['notify_send_body'])) ? 'pbe_' : '') . $message_type, $replacements, $needed_language);
325
326
			// Using the maillist functions? Then adjust the from wrapper
327
			if ($maillist && $email_perm && $type === 'reply' && !empty($row['notify_send_body']))
328
			{
329
				// Set the from name base on group or maillist mode
330
				$emailfrom = !empty($modSettings['maillist_group_mode']) ? un_htmlspecialchars($topicData[$row['id_topic']]['name']) : un_htmlspecialchars($modSettings['maillist_sitename']);
331
				$from_wrapper = !empty($modSettings['maillist_mail_from']) ? $modSettings['maillist_mail_from'] : (empty($modSettings['maillist_sitename_address']) ? $webmaster_email : $modSettings['maillist_sitename_address']);
332
				sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], $emailfrom, 'm' . $data['last_id'], false, 3, null, false, $from_wrapper, $row['id_topic']);
333
			}
334 View Code Duplication
			else
335
				sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, 'm' . $topicData[$row['id_topic']]['last_id']);
336
337
			$sent++;
338
		}
339
	}
340
	$db->free_result($members);
341
342
	if (isset($current_language) && $current_language != $user_language)
343
		theme()->getTemplates()->loadLanguageFile('Post');
344
345
	// Sent!
346
	if ($type == 'reply' && !empty($sent))
347
		$db->query('', '
348
			UPDATE {db_prefix}log_notify
349
			SET sent = {int:is_sent}
350
			WHERE id_topic IN ({array_int:topic_list})
351
				AND id_member != {int:current_member}',
352
			array(
353
				'current_member' => $user_id,
354
				'topic_list' => $topics,
355
				'is_sent' => 1,
356
			)
357
		);
358
359
	// For approvals we need to unsend the exclusions (This *is* the quickest way!)
360
	if (!empty($sent) && !empty($exclude))
361
	{
362
		foreach ($topicData as $id => $data)
363
		{
364
			if ($data['exclude'])
365
			{
366
				$db->query('', '
367
					UPDATE {db_prefix}log_notify
368
					SET sent = {int:not_sent}
369
					WHERE id_topic = {int:id_topic}
370
						AND id_member = {int:id_member}',
371
					array(
372
						'not_sent' => 0,
373
						'id_topic' => $id,
374
						'id_member' => $data['exclude'],
375
					)
376
				);
377
			}
378
		}
379
	}
380
}
381
382
/**
383
 * Notifies members who have requested notification for new topics posted on a board of said posts.
384
 *
385
 * receives data on the topics to send out notifications to by the passed in array.
386
 * only sends notifications to those who can *currently* see the topic (it doesn't matter if they could when they requested notification.)
387
 * loads the Post language file multiple times for each language if the userLanguage setting is set.
388
 *
389
 * @param mixed[] $topicData
390
 * @throws \ElkArte\Exceptions\Exception
391
 */
392
function sendBoardNotifications(&$topicData)
393
{
394
	global $scripturl, $language, $modSettings, $webmaster_email;
395
396
	$db = database();
397
398
	require_once(SUBSDIR . '/Mail.subs.php');
399
	require_once(SUBSDIR . '/Emailpost.subs.php');
400
401
	// Do we have one or lots of topics?
402
	if (isset($topicData['body']))
403
		$topicData = array($topicData);
404
405
	// Using the post to email functions?
406
	$maillist = !empty($modSettings['maillist_enabled']) && !empty($modSettings['pbe_post_enabled']);
407
408
	// Find out what boards we have... and clear out any rubbish!
409
	$boards = array();
410
	foreach ($topicData as $key => $topic)
411
	{
412
		if (!empty($topic['board']))
413
			$boards[$topic['board']][] = $key;
414
		else
415
		{
416
			unset($topic[$key]);
417
			continue;
418
		}
419
420
		// Convert to markdown markup e.g. styled plain text, while doing the censoring
421
		pbe_prepare_text($topicData[$key]['body'], $topicData[$key]['subject'], $topicData[$key]['signature']);
422
	}
423
424
	// Just the board numbers.
425
	$board_index = array_unique(array_keys($boards));
426
	if (empty($board_index))
427
		return;
428
429
	// Load the actual board names
430
	require_once(SUBSDIR . '/Boards.subs.php');
431
	$board_names = fetchBoardsInfo(array('boards' => $board_index, 'override_permissions' => true));
432
433
	// Yea, we need to add this to the digest queue.
434
	$digest_insert = array();
435 View Code Duplication
	foreach ($topicData as $id => $data)
436
		$digest_insert[] = array($data['topic'], $data['msg'], 'topic', User::$info->id);
437
	$db->insert('',
438
		'{db_prefix}log_digest',
439
		array(
440
			'id_topic' => 'int', 'id_msg' => 'int', 'note_type' => 'string', 'exclude' => 'int',
441
		),
442
		$digest_insert,
443
		array()
444
	);
445
446
	// Find the members with notification on for these boards.
447
	$members = $db->query('', '
448
		SELECT
449
			mem.id_member, mem.email_address, mem.notify_regularity, mem.notify_send_body, mem.lngfile, mem.warning,
450
			ln.sent, ln.id_board, mem.id_group, mem.additional_groups, b.member_groups, b.id_profile,
451
			mem.id_post_group
452
		FROM {db_prefix}log_notify AS ln
453
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = ln.id_board)
454
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member)
455
		WHERE ln.id_board IN ({array_int:board_list})
456
			AND mem.id_member != {int:current_member}
457
			AND mem.is_activated = {int:is_activated}
458
			AND mem.notify_types != {int:notify_types}
459
			AND mem.notify_regularity < {int:notify_regularity}
460
		ORDER BY mem.lngfile',
461
		array(
462
			'current_member' => User::$info->id,
463
			'board_list' => $board_index,
464
			'is_activated' => 1,
465
			'notify_types' => 4,
466
			'notify_regularity' => 2,
467
		)
468
	);
469
	// While we have members with board notifications
470
	while ($rowmember = $db->fetch_assoc($members))
471
	{
472
		$email_perm = true;
473
		if (validateNotificationAccess($rowmember, $maillist, $email_perm) === false)
474
			continue;
475
476
		$langloaded = theme()->getTemplates()->loadLanguageFile('index', empty($rowmember['lngfile']) || empty($modSettings['userLanguage']) ? $language : $rowmember['lngfile'], false);
477
478
		// Now loop through all the notifications to send for this board.
479
		if (empty($boards[$rowmember['id_board']]))
480
			continue;
481
482
		$sentOnceAlready = 0;
483
484
		// For each message we need to send (from this board to this member)
485
		foreach ($boards[$rowmember['id_board']] as $key)
486
		{
487
			// Don't notify the guy who started the topic!
488
			// @todo In this case actually send them a "it's approved hooray" email :P
489
			if ($topicData[$key]['poster'] == $rowmember['id_member'])
490
				continue;
491
492
			// Setup the string for adding the body to the message, if a user wants it.
493
			$send_body = $maillist || (empty($modSettings['disallow_sendBody']) && !empty($rowmember['notify_send_body']));
494
495
			$replacements = array(
496
				'TOPICSUBJECT' => $topicData[$key]['subject'],
497
				'POSTERNAME' => un_htmlspecialchars($topicData[$key]['name']),
498
				'TOPICLINK' => $scripturl . '?topic=' . $topicData[$key]['topic'] . '.new#new',
499
				'TOPICLINKNEW' => $scripturl . '?topic=' . $topicData[$key]['topic'] . '.new#new',
500
				'MESSAGE' => $send_body ? $topicData[$key]['body'] : '',
501
				'UNSUBSCRIBELINK' => $scripturl . '?action=notifyboard;board=' . $topicData[$key]['board'] . '.0',
502
				'SIGNATURE' => !empty($topicData[$key]['signature']) ? $topicData[$key]['signature'] : '',
503
				'BOARDNAME' => $board_names[$topicData[$key]['board']]['name'],
504
			);
505
506
			// Figure out which email to send
507
			$emailtype = '';
508
509
			// Send only if once is off or it's on and it hasn't been sent.
510
			if (!empty($rowmember['notify_regularity']) && !$sentOnceAlready && empty($rowmember['sent']))
511
				$emailtype = 'notify_boards_once';
512
			elseif (empty($rowmember['notify_regularity']))
513
				$emailtype = 'notify_boards';
514
515
			if (!empty($emailtype))
516
			{
517
				$emailtype .= $send_body ? '_body' : '';
518
				$emaildata = loadEmailTemplate((($maillist && $email_perm && $send_body) ? 'pbe_' : '') . $emailtype, $replacements, $langloaded);
519
				$emailname = (!empty($topicData[$key]['name'])) ? un_htmlspecialchars($topicData[$key]['name']) : null;
520
521
				// Maillist style?
522
				if ($maillist && $email_perm && $send_body)
523
				{
524
					// Add in the from wrapper and trigger sendmail to add in a security key
525
					$from_wrapper = !empty($modSettings['maillist_mail_from']) ? $modSettings['maillist_mail_from'] : (empty($modSettings['maillist_sitename_address']) ? $webmaster_email : $modSettings['maillist_sitename_address']);
526
					sendmail($rowmember['email_address'], $emaildata['subject'], $emaildata['body'], $emailname, 't' . $topicData[$key]['topic'], false, 3, null, false, $from_wrapper, $topicData[$key]['topic']);
527
				}
528
				else
529
					sendmail($rowmember['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 3);
530
			}
531
532
			$sentOnceAlready = 1;
533
		}
534
	}
535
	$db->free_result($members);
536
537
	theme()->getTemplates()->loadLanguageFile('index', User::$info->language);
538
539
	// Sent!
540
	$db->query('', '
541
		UPDATE {db_prefix}log_notify
542
		SET sent = {int:is_sent}
543
		WHERE id_board IN ({array_int:board_list})
544
			AND id_member != {int:current_member}',
545
		array(
546
			'current_member' => User::$info->id,
547
			'board_list' => $board_index,
548
			'is_sent' => 1,
549
		)
550
	);
551
}
552
553
/**
554
 * A special function for handling the hell which is sending approval notifications.
555
 *
556
 * @param mixed[] $topicData
557
 * @throws \ElkArte\Exceptions\Exception
558
 */
559
function sendApprovalNotifications(&$topicData)
560
{
561
	global $scripturl, $language, $modSettings;
562
563
	$db = database();
564
565
	// Clean up the data...
566
	if (!is_array($topicData) || empty($topicData))
567
		return;
568
569
	// Email ahoy
570
	require_once(SUBSDIR . '/Mail.subs.php');
571
	require_once(SUBSDIR . '/Emailpost.subs.php');
572
573
	$topics = array();
574
	$digest_insert = array();
575
	foreach ($topicData as $topic => $msgs)
576
	{
577
		foreach ($msgs as $msgKey => $msg)
578
		{
579
			// Convert it to markdown for sending, censor is done as well
580
			pbe_prepare_text($topicData[$topic][$msgKey]['body'], $topicData[$topic][$msgKey]['subject']);
581
582
			$topics[] = $msg['id'];
583
			$digest_insert[] = array($msg['topic'], $msg['id'], 'reply', User::$info->id);
584
		}
585
	}
586
587
	// These need to go into the digest too...
588
	$db->insert('',
589
		'{db_prefix}log_digest',
590
		array(
591
			'id_topic' => 'int', 'id_msg' => 'int', 'note_type' => 'string', 'exclude' => 'int',
592
		),
593
		$digest_insert,
594
		array()
595
	);
596
597
	// Find everyone who needs to know about this.
598
	$members = $db->query('', '
599
		SELECT
600
			mem.id_member, mem.email_address, mem.notify_regularity, mem.notify_types, mem.notify_send_body, mem.lngfile,
601
			ln.sent, mem.id_group, mem.additional_groups, b.member_groups, mem.id_post_group, t.id_member_started,
602
			ln.id_topic
603
		FROM {db_prefix}log_notify AS ln
604
			INNER JOIN {db_prefix}members AS mem ON (mem.id_member = ln.id_member)
605
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ln.id_topic)
606
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
607
		WHERE ln.id_topic IN ({array_int:topic_list})
608
			AND mem.is_activated = {int:is_activated}
609
			AND mem.notify_types < {int:notify_types}
610
			AND mem.notify_regularity < {int:notify_regularity}
611
		GROUP BY mem.id_member, ln.id_topic, mem.email_address, mem.notify_regularity, mem.notify_types, mem.notify_send_body, mem.lngfile, ln.sent, mem.id_group, mem.additional_groups, b.member_groups, mem.id_post_group, t.id_member_started
612
		ORDER BY mem.lngfile',
613
		array(
614
			'topic_list' => $topics,
615
			'is_activated' => 1,
616
			'notify_types' => 4,
617
			'notify_regularity' => 2,
618
		)
619
	);
620
	$sent = 0;
621
	$current_language = User::$info->language;
622
	while ($row = $db->fetch_assoc($members))
623
	{
624
		if ($row['id_group'] != 1)
625
		{
626
			$allowed = explode(',', $row['member_groups']);
627
			$row['additional_groups'] = explode(',', $row['additional_groups']);
628
			$row['additional_groups'][] = $row['id_group'];
629
			$row['additional_groups'][] = $row['id_post_group'];
630
631
			if (count(array_intersect($allowed, $row['additional_groups'])) == 0)
632
				continue;
633
		}
634
635
		$needed_language = empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile'];
636 View Code Duplication
		if (empty($current_language) || $current_language != $needed_language)
637
			$current_language = theme()->getTemplates()->loadLanguageFile('Post', $needed_language, false);
638
639
		$sent_this_time = false;
640
		$replacements = array(
641
			'TOPICLINK' => $scripturl . '?topic=' . $row['id_topic'] . '.new;topicseen#new',
642
			'UNSUBSCRIBELINK' => $scripturl . '?action=notify;topic=' . $row['id_topic'] . '.0',
643
		);
644
645
		// Now loop through all the messages to send.
646
		foreach ($topicData[$row['id_topic']] as $msg)
647
		{
648
			$replacements += array(
649
				'TOPICSUBJECT' => $msg['subject'],
650
				'POSTERNAME' => un_htmlspecialchars($msg['name']),
651
			);
652
653
			$message_type = 'notification_reply';
654
655
			// Do they want the body of the message sent too?
656 View Code Duplication
			if (!empty($row['notify_send_body']) && empty($modSettings['disallow_sendBody']))
657
			{
658
				$message_type .= '_body';
659
				$replacements['MESSAGE'] = $msg['body'];
660
			}
661
662
			if (!empty($row['notify_regularity']))
663
				$message_type .= '_once';
664
665
			// Send only if once is off or it's on and it hasn't been sent.
666
			if (empty($row['notify_regularity']) || (empty($row['sent']) && !$sent_this_time))
667
			{
668
				$emaildata = loadEmailTemplate($message_type, $replacements, $needed_language);
669
				sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, 'm' . $msg['last_id']);
670
				$sent++;
671
			}
672
673
			$sent_this_time = true;
674
		}
675
	}
676
	$db->free_result($members);
677
678
	if (isset($current_language) && $current_language != User::$info->language)
679
		theme()->getTemplates()->loadLanguageFile('Post');
680
681
	// Sent!
682 View Code Duplication
	if (!empty($sent))
683
		$db->query('', '
684
			UPDATE {db_prefix}log_notify
685
			SET sent = {int:is_sent}
686
			WHERE id_topic IN ({array_int:topic_list})
687
				AND id_member != {int:current_member}',
688
			array(
689
				'current_member' => User::$info->id,
690
				'topic_list' => $topics,
691
				'is_sent' => 1,
692
			)
693
		);
694
}
695
696
/**
697
 * This simple function gets a list of all administrators and sends them an email
698
 * to let them know a new member has joined.
699
 * Called by registerMember() function in subs/Members.subs.php.
700
 * Email is sent to all groups that have the moderate_forum permission.
701
 * The language set by each member is being used (if available).
702
 *
703
 * @param string $type types supported are 'approval', 'activation', and 'standard'.
704
 * @param int $memberID
705
 * @param string|null $member_name = null
706
 * @uses the Login language file.
707
 * @throws \ElkArte\Exceptions\Exception
708
 */
709
function sendAdminNotifications($type, $memberID, $member_name = null)
710
{
711 2
	global $modSettings, $language, $scripturl;
712
713 2
	$db = database();
714
715
	// If the setting isn't enabled then just exit.
716 2
	if (empty($modSettings['notify_new_registration']))
717 2
		return;
718
719
	// Needed to notify admins, or anyone
720
	require_once(SUBSDIR . '/Mail.subs.php');
721
722
	if ($member_name === null)
723
	{
724
		require_once(SUBSDIR . '/Members.subs.php');
725
726
		// Get the new user's name....
727
		$member_info = getBasicMemberData($memberID);
728
		$member_name = $member_info['real_name'];
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $member_name. This often makes code more readable.
Loading history...
729
	}
730
731
	$groups = array();
732
733
	// All membergroups who can approve members.
734
	$request = $db->query('', '
735
		SELECT id_group
736
		FROM {db_prefix}permissions
737
		WHERE permission = {string:moderate_forum}
738
			AND add_deny = {int:add_deny}
739
			AND id_group != {int:id_group}',
740
		array(
741
			'add_deny' => 1,
742
			'id_group' => 0,
743
			'moderate_forum' => 'moderate_forum',
744
		)
745
	);
746
	while ($row = $db->fetch_assoc($request))
747
		$groups[] = $row['id_group'];
748
	$db->free_result($request);
749
750
	// Add administrators too...
751
	$groups[] = 1;
752
	$groups = array_unique($groups);
753
754
	// Get a list of all members who have ability to approve accounts - these are the people who we inform.
755
	$request = $db->query('', '
756
		SELECT id_member, lngfile, email_address
757
		FROM {db_prefix}members
758
		WHERE (id_group IN ({array_int:group_list}) OR FIND_IN_SET({raw:group_array_implode}, additional_groups) != 0)
759
			AND notify_types != {int:notify_types}
760
		ORDER BY lngfile',
761
		array(
762
			'group_list' => $groups,
763
			'notify_types' => 4,
764
			'group_array_implode' => implode(', additional_groups) != 0 OR FIND_IN_SET(', $groups),
765
		)
766
	);
767
768
	$current_language = User::$info->language;
769
	while ($row = $db->fetch_assoc($request))
770
	{
771
		$replacements = array(
772
			'USERNAME' => $member_name,
773
			'PROFILELINK' => $scripturl . '?action=profile;u=' . $memberID
774
		);
775
		$emailtype = 'admin_notify';
776
777
		// If they need to be approved add more info...
778
		if ($type == 'approval')
779
		{
780
			$replacements['APPROVALLINK'] = $scripturl . '?action=admin;area=viewmembers;sa=browse;type=approve';
781
			$emailtype .= '_approval';
782
		}
783
784
		$emaildata = loadEmailTemplate($emailtype, $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']);
785
786
		// And do the actual sending...
787
		sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 0);
788
	}
789
	$db->free_result($request);
790
791
	if (isset($current_language) && $current_language != User::$info->language)
792
		theme()->getTemplates()->loadLanguageFile('Login');
793
}
794
795
/**
796
 * Checks if a user has the correct access to get notifications
797
 * - validates they have proper group access to a board
798
 * - if using the maillist, checks if they should get a reply-able message
799
 *     - not muted
800
 *     - has postby_email permission on the board
801
 *
802
 * Returns false if they do not have the proper group access to a board
803
 * Sets email_perm to false if they should not get a reply-able message
804
 *
805
 * @param mixed[] $row
806
 * @param boolean $maillist
807
 * @param boolean $email_perm
808
 *
809
 * @return bool
810
 * @throws \ElkArte\Exceptions\Exception
811
 */
812
function validateNotificationAccess($row, $maillist, &$email_perm = true)
813
{
814
	global $modSettings;
815
816
	static $board_profile = array();
817
818
	$allowed = explode(',', $row['member_groups']);
819
	$row['additional_groups'] = !empty($row['additional_groups']) ? explode(',', $row['additional_groups']) : array();
820
	$row['additional_groups'][] = $row['id_group'];
821
	$row['additional_groups'][] = $row['id_post_group'];
822
823
	// No need to check for you ;)
824
	if ($row['id_group'] == 1 || in_array('1', $row['additional_groups']))
825
		return $email_perm;
826
827
	// They do have access to this board?
828
	if (count(array_intersect($allowed, $row['additional_groups'])) === 0)
829
		return false;
830
831
	// If using maillist, see if they should get a reply-able message
832
	if ($maillist)
833
	{
834
		// Perhaps they don't require a security key in the message
835
		if (!empty($modSettings['postmod_active']) && !empty($modSettings['warning_mute']) && $modSettings['warning_mute'] <= $row['warning'])
836
			$email_perm = false;
837
		else
838
		{
839
			if (!isset($board_profile[$row['id_board']]))
840
			{
841
				require_once(SUBSDIR . '/Members.subs.php');
842
				$board_profile[$row['id_board']] = groupsAllowedTo('postby_email', $row['id_board']);
843
			}
844
845
			// In a group that has email posting permissions on this board
846
			if (count(array_intersect($board_profile[$row['id_board']]['allowed'], $row['additional_groups'])) === 0)
847
				$email_perm = false;
848
849
			// And not specifically denied?
850
			if ($email_perm && !empty($modSettings['permission_enable_deny'])
851
				&& count(array_intersect($row['additional_groups'], $board_profile[$row['id_board']]['denied'])) !== 0)
852
				$email_perm = false;
853
		}
854
	}
855
856
	return $email_perm;
857
}
858
859
/**
860
 * Queries the database for notification preferences of a set of members.
861
 *
862
 * @param string[]|string $notification_types
863
 * @param int[]|int $members
864
 *
865
 * @return array
866
 */
867
function getUsersNotificationsPreferences($notification_types, $members)
868
{
869 4
	$db = database();
870
871 4
	$notification_types = (array) $notification_types;
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $notification_types. This often makes code more readable.
Loading history...
872 4
	$query_members = (array) $members;
873 4
	$query_members[] = 0;
874
875 4
	$request = $db->query('', '
876
		SELECT id_member, notification_level, mention_type
877
		FROM {db_prefix}notifications_pref
878
		WHERE id_member IN ({array_int:members_to})
879
			AND mention_type IN ({array_string:mention_types})',
880
		array(
881 4
			'members_to' => $query_members,
882 4
			'mention_types' => $notification_types,
883
		)
884
	);
885 4
	$results = array();
886
887 4 View Code Duplication
	while ($row = $db->fetch_assoc($request))
888
	{
889 4
		if (!isset($results[$row['id_member']]))
890 4
			$results[$row['id_member']] = array();
891
892 4
		$results[$row['id_member']][$row['mention_type']] = (int) $row['notification_level'];
893
	}
894
895 4
	$db->free_result($request);
896
897 4
	$defaults = array();
898 4
	foreach ($notification_types as $val)
899
	{
900 4
		$defaults[$val] = 0;
901
	}
902 4
	if (isset($results[0]))
903
	{
904 4
		$defaults = array_merge($defaults, $results[0]);
905
	}
906
907 4
	$preferences = array();
908 4
	foreach ((array) $members as $member)
909
	{
910 4
		$preferences[$member] = $defaults;
911 4
		if (isset($results[$member]))
912 4
		$preferences[$member] = array_merge($preferences[$member], $results[$member]);
913
	}
914
915 4
	return $preferences;
916
}
917
918
/**
919
 * Saves into the database the notification preferences of a certain member.
920
 *
921
 * @param int $member The member id
922
 * @param int[] $notification_data The array of notifications ('type' => 'level')
923
 */
924
function saveUserNotificationsPreferences($member, $notification_data)
925
{
926
	$db = database();
927
928
	// First drop the existing settings
929
	$db->query('', '
930
		DELETE FROM {db_prefix}notifications_pref
931
		WHERE id_member = {int:member}
932
			AND mention_type IN ({array_string:mention_types})',
933
		array(
934
			'member' => $member,
935
			'mention_types' => array_keys($notification_data),
936
		)
937
	);
938
939
	$inserts = array();
940
	foreach ($notification_data as $type => $level)
941
	{
942
		$inserts[] = array(
943
			$member,
944
			$type,
945
			$level,
946
		);
947
	}
948
949
	if (empty($inserts))
950
		return;
951
952
	$db->insert('',
953
		'{db_prefix}notifications_pref',
954
		array(
955
			'id_member' => 'int',
956
			'mention_type' => 'string-12',
957
			'notification_level' => 'int',
958
		),
959
		$inserts,
960
		array('id_member', 'mention_type')
961
	);
962
}
963
964
/**
965
 * From the list of all possible notification methods available, only those
966
 * enabled are returned.
967
 *
968
 * @param string[] $possible_methods The array of notifications ('type' => 'level')
969
 * @param string $type The type of notification (mentionmem, likemsg, etc.)
970
 *
971
 * @return array
972
 */
973
function filterNotificationMethods($possible_methods, $type)
974
{
975 4
	$unserialized = getConfiguredNotificationMethods($type);
976
977 4
	if (empty($unserialized))
978
		return array();
979
980 4
	$allowed = array();
981 4
	foreach ($possible_methods as $key => $val)
982
	{
983 4
		if (isset($unserialized[$val]))
984 4
			$allowed[$key] = $val;
985
	}
986
987 4
	return $allowed;
988
}
989
990
/**
991
 * Returns all the enabled methods of notification for a specific
992
 * type of notification.
993
 *
994
 * @param string $type The type of notification (mentionmem, likemsg, etc.)
995
 *
996
 * @return array
997
 */
998
function getConfiguredNotificationMethods($type)
999
{
1000 4
	global $modSettings;
1001 4
	static $unserialized = null;
1002
1003 4
	if ($unserialized === null)
1004 2
		$unserialized = unserialize($modSettings['notification_methods']);
1005
1006 4
	if (isset($unserialized[$type]))
1007
	{
1008 4
		return $unserialized[$type];
1009
	}
1010
	else
1011
	{
1012
		return array();
1013
	}
1014
}
1015