Passed
Push — release-2.1 ( 0c2197...207d2d )
by Jeremy
05:47
created

prepareMessageContext()   F

Complexity

Conditions 38
Paths > 20000

Size

Total Lines 134
Code Lines 83

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 38
eloc 83
c 1
b 0
f 0
nop 2
dl 0
loc 134
rs 0
nc 25134

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
 * This file is mainly meant for controlling the actions related to personal
5
 * messages. It allows viewing, sending, deleting, and marking personal
6
 * messages. For compatibility reasons, they are often called "instant messages".
7
 *
8
 * Simple Machines Forum (SMF)
9
 *
10
 * @package SMF
11
 * @author Simple Machines http://www.simplemachines.org
12
 * @copyright 2018 Simple Machines and individual contributors
13
 * @license http://www.simplemachines.org/about/smf/license.php BSD
14
 *
15
 * @version 2.1 Beta 4
16
 */
17
18
if (!defined('SMF'))
19
	die('No direct access...');
20
21
/**
22
 * This helps organize things...
23
 * @todo this should be a simple dispatcher....
24
 */
25
function MessageMain()
26
{
27
	global $txt, $scripturl, $sourcedir, $context, $user_info, $user_settings, $smcFunc, $modSettings;
28
29
	// No guests!
30
	is_not_guest();
31
32
	// You're not supposed to be here at all, if you can't even read PMs.
33
	isAllowedTo('pm_read');
34
35
	// This file contains the basic functions for sending a PM.
36
	require_once($sourcedir . '/Subs-Post.php');
37
38
	loadLanguage('PersonalMessage+Drafts');
39
40
	if (!isset($_REQUEST['xml']))
41
		loadTemplate('PersonalMessage');
42
43
	// Load up the members maximum message capacity.
44
	if ($user_info['is_admin'])
45
		$context['message_limit'] = 0;
46
	elseif (($context['message_limit'] = cache_get_data('msgLimit:' . $user_info['id'], 360)) === null)
0 ignored issues
show
introduced by
The condition $context['message_limit'...fo['id'], 360) === null is always false.
Loading history...
47
	{
48
		// @todo Why do we do this?  It seems like if they have any limit we should use it.
49
		$request = $smcFunc['db_query']('', '
50
			SELECT MAX(max_messages) AS top_limit, MIN(max_messages) AS bottom_limit
51
			FROM {db_prefix}membergroups
52
			WHERE id_group IN ({array_int:users_groups})',
53
			array(
54
				'users_groups' => $user_info['groups'],
55
			)
56
		);
57
		list ($maxMessage, $minMessage) = $smcFunc['db_fetch_row']($request);
58
		$smcFunc['db_free_result']($request);
59
60
		$context['message_limit'] = $minMessage == 0 ? 0 : $maxMessage;
61
62
		// Save us doing it again!
63
		cache_put_data('msgLimit:' . $user_info['id'], $context['message_limit'], 360);
64
	}
65
66
	// Prepare the context for the capacity bar.
67
	if (!empty($context['message_limit']))
68
	{
69
		$bar = ($user_info['messages'] * 100) / $context['message_limit'];
70
71
		$context['limit_bar'] = array(
72
			'messages' => $user_info['messages'],
73
			'allowed' => $context['message_limit'],
74
			'percent' => $bar,
75
			'bar' => min(100, (int) $bar),
76
			'text' => sprintf($txt['pm_currently_using'], $user_info['messages'], round($bar, 1)),
77
		);
78
	}
79
80
	// a previous message was sent successfully? show a small indication.
81
	if (isset($_GET['done']) && ($_GET['done'] == 'sent'))
82
		$context['pm_sent'] = true;
83
84
	$context['labels'] = array();
85
86
	// Load the label data.
87
	if ($user_settings['new_pm'] || ($context['labels'] = cache_get_data('labelCounts:' . $user_info['id'], 720)) === null)
88
	{
89
		// Looks like we need to reseek!
90
91
		// Inbox "label"
92
		$context['labels'][-1] = array(
93
			'id' => -1,
94
			'name' => $txt['pm_msg_label_inbox'],
95
			'messages' => 0,
96
			'unread_messages' => 0,
97
		);
98
99
		// First get the inbox counts
100
		// The CASE WHEN here is because is_read is set to 3 when you reply to a message
101
		$result = $smcFunc['db_query']('', '
102
			SELECT COUNT(*) AS total, SUM(is_read & 1) AS num_read
103
			FROM {db_prefix}pm_recipients
104
			WHERE id_member = {int:current_member}
105
				AND in_inbox = {int:in_inbox}
106
				AND deleted = {int:not_deleted}',
107
			array(
108
				'current_member' => $user_info['id'],
109
				'in_inbox' => 1,
110
				'not_deleted' => 0,
111
			)
112
		);
113
114
		while ($row = $smcFunc['db_fetch_assoc']($result))
115
		{
116
			$context['labels'][-1]['messages'] = $row['total'];
117
			$context['labels'][-1]['unread_messages'] = $row['total'] - $row['num_read'];
118
		}
119
120
		$smcFunc['db_free_result']($result);
121
122
		// Now load info about all the other labels
123
		$result = $smcFunc['db_query']('', '
124
			SELECT l.id_label, l.name, COALESCE(SUM(pr.is_read & 1), 0) AS num_read, COALESCE(COUNT(pr.id_pm), 0) AS total
125
			FROM {db_prefix}pm_labels AS l
126
				LEFT JOIN {db_prefix}pm_labeled_messages AS pl ON (pl.id_label = l.id_label)
127
				LEFT JOIN {db_prefix}pm_recipients AS pr ON (pr.id_pm = pl.id_pm)
128
			WHERE l.id_member = {int:current_member}
129
			GROUP BY l.id_label, l.name',
130
			array(
131
				'current_member' => $user_info['id'],
132
			)
133
		);
134
135
		while ($row = $smcFunc['db_fetch_assoc']($result))
136
		{
137
			$context['labels'][$row['id_label']] = array(
138
				'id' => $row['id_label'],
139
				'name' => $row['name'],
140
				'messages' => $row['total'],
141
				'unread_messages' => $row['total'] - $row['num_read']
142
			);
143
		}
144
145
		$smcFunc['db_free_result']($result);
146
147
		// Store it please!
148
		cache_put_data('labelCounts:' . $user_info['id'], $context['labels'], 720);
149
	}
150
151
	// Now we have the labels, and assuming we have unsorted mail, apply our rules!
152
	if ($user_settings['new_pm'])
153
	{
154
		ApplyRules();
155
		updateMemberData($user_info['id'], array('new_pm' => 0));
156
		$smcFunc['db_query']('', '
157
			UPDATE {db_prefix}pm_recipients
158
			SET is_new = {int:not_new}
159
			WHERE id_member = {int:current_member}',
160
			array(
161
				'current_member' => $user_info['id'],
162
				'not_new' => 0,
163
			)
164
		);
165
	}
166
167
	// This determines if we have more labels than just the standard inbox.
168
	$context['currently_using_labels'] = count($context['labels']) > 1 ? 1 : 0;
169
170
	// Some stuff for the labels...
171
	$context['current_label_id'] = isset($_REQUEST['l']) && isset($context['labels'][$_REQUEST['l']]) ? (int) $_REQUEST['l'] : -1;
172
	$context['current_label'] = &$context['labels'][$context['current_label_id']]['name'];
173
	$context['folder'] = !isset($_REQUEST['f']) || $_REQUEST['f'] != 'sent' ? 'inbox' : 'sent';
174
175
	// This is convenient.  Do you know how annoying it is to do this every time?!
176
	$context['current_label_redirect'] = 'action=pm;f=' . $context['folder'] . (isset($_GET['start']) ? ';start=' . $_GET['start'] : '') . (isset($_REQUEST['l']) ? ';l=' . $_REQUEST['l'] : '');
177
	$context['can_issue_warning'] = allowedTo('issue_warning') && $modSettings['warning_settings'][0] == 1;
178
179
	// Are PM drafts enabled?
180
	$context['drafts_pm_save'] = !empty($modSettings['drafts_pm_enabled']) && allowedTo('pm_draft');
181
	$context['drafts_autosave'] = !empty($context['drafts_pm_save']) && !empty($modSettings['drafts_autosave_enabled']);
182
183
	// Build the linktree for all the actions...
184
	$context['linktree'][] = array(
185
		'url' => $scripturl . '?action=pm',
186
		'name' => $txt['personal_messages']
187
	);
188
189
	// Preferences...
190
	$context['display_mode'] = $user_settings['pm_prefs'] & 3;
191
192
	$subActions = array(
193
		'popup' => 'MessagePopup',
194
		'manlabels' => 'ManageLabels',
195
		'manrules' => 'ManageRules',
196
		'pmactions' => 'MessageActionsApply',
197
		'prune' => 'MessagePrune',
198
		'removeall' => 'MessageKillAllQuery',
199
		'removeall2' => 'MessageKillAll',
200
		'report' => 'ReportMessage',
201
		'search' => 'MessageSearch',
202
		'search2' => 'MessageSearch2',
203
		'send' => 'MessagePost',
204
		'send2' => 'MessagePost2',
205
		'settings' => 'MessageSettings',
206
		'showpmdrafts' => 'MessageDrafts',
207
	);
208
209
	if (!isset($_REQUEST['sa']) || !isset($subActions[$_REQUEST['sa']]))
210
	{
211
		$_REQUEST['sa'] = '';
212
		MessageFolder();
213
	}
214
	else
215
	{
216
		if (!isset($_REQUEST['xml']) && $_REQUEST['sa'] != 'popup')
217
			messageIndexBar($_REQUEST['sa']);
218
219
		call_helper($subActions[$_REQUEST['sa']]);
220
	}
221
}
222
223
/**
224
 * A menu to easily access different areas of the PM section
225
 *
226
 * @param string $area The area we're currently in
227
 */
228
function messageIndexBar($area)
229
{
230
	global $txt, $context, $scripturl, $sourcedir, $modSettings, $user_info;
231
232
	$pm_areas = array(
233
		'folders' => array(
234
			'title' => $txt['pm_messages'],
235
			'areas' => array(
236
				'inbox' => array(
237
					'label' => $txt['inbox'],
238
					'custom_url' => $scripturl . '?action=pm',
239
					'amt' => 0,
240
				),
241
				'send' => array(
242
					'label' => $txt['new_message'],
243
					'custom_url' => $scripturl . '?action=pm;sa=send',
244
					'permission' => 'pm_send',
245
					'amt' => 0,
246
				),
247
				'sent' => array(
248
					'label' => $txt['sent_items'],
249
					'custom_url' => $scripturl . '?action=pm;f=sent',
250
					'amt' => 0,
251
				),
252
				'drafts' => array(
253
					'label' => $txt['drafts_show'],
254
					'custom_url' => $scripturl . '?action=pm;sa=showpmdrafts',
255
					'permission' => 'pm_draft',
256
					'enabled' => !empty($modSettings['drafts_pm_enabled']),
257
					'amt' => 0,
258
				),
259
			),
260
			'amt' => 0,
261
		),
262
		'labels' => array(
263
			'title' => $txt['pm_labels'],
264
			'areas' => array(),
265
			'amt' => 0,
266
		),
267
		'actions' => array(
268
			'title' => $txt['pm_actions'],
269
			'areas' => array(
270
				'search' => array(
271
					'label' => $txt['pm_search_bar_title'],
272
					'custom_url' => $scripturl . '?action=pm;sa=search',
273
				),
274
				'prune' => array(
275
					'label' => $txt['pm_prune'],
276
					'custom_url' => $scripturl . '?action=pm;sa=prune'
277
				),
278
			),
279
		),
280
		'pref' => array(
281
			'title' => $txt['pm_preferences'],
282
			'areas' => array(
283
				'manlabels' => array(
284
					'label' => $txt['pm_manage_labels'],
285
					'custom_url' => $scripturl . '?action=pm;sa=manlabels',
286
				),
287
				'manrules' => array(
288
					'label' => $txt['pm_manage_rules'],
289
					'custom_url' => $scripturl . '?action=pm;sa=manrules',
290
				),
291
				'settings' => array(
292
					'label' => $txt['pm_settings'],
293
					'custom_url' => $scripturl . '?action=pm;sa=settings',
294
				),
295
			),
296
		),
297
	);
298
299
	// Handle labels.
300
	if (empty($context['currently_using_labels']))
301
		unset($pm_areas['labels']);
302
	else
303
	{
304
		// Note we send labels by id as it will have less problems in the querystring.
305
		foreach ($context['labels'] as $label)
306
		{
307
			if ($label['id'] == -1)
308
				continue;
309
310
			// Count the amount of unread items in labels.
311
			$pm_areas['labels']['amt'] += $label['unread_messages'];
312
313
			// Add the label to the menu.
314
			$pm_areas['labels']['areas']['label' . $label['id']] = array(
315
				'label' => $label['name'],
316
				'custom_url' => $scripturl . '?action=pm;l=' . $label['id'],
317
				'amt' => $label['unread_messages'],
318
				'unread_messages' => $label['unread_messages'],
319
				'messages' => $label['messages'],
320
			);
321
		}
322
	}
323
324
	$pm_areas['folders']['areas']['inbox']['unread_messages'] = &$context['labels'][-1]['unread_messages'];
325
	$pm_areas['folders']['areas']['inbox']['messages'] = &$context['labels'][-1]['messages'];
326
	if (!empty($context['labels'][-1]['unread_messages']))
327
	{
328
		$pm_areas['folders']['areas']['inbox']['amt'] = $context['labels'][-1]['unread_messages'];
329
		$pm_areas['folders']['amt'] = $context['labels'][-1]['unread_messages'];
330
	}
331
332
	// Do we have a limit on the amount of messages we can keep?
333
	if (!empty($context['message_limit']))
334
	{
335
		$bar = round(($user_info['messages'] * 100) / $context['message_limit'], 1);
336
337
		$context['limit_bar'] = array(
338
			'messages' => $user_info['messages'],
339
			'allowed' => $context['message_limit'],
340
			'percent' => $bar,
341
			'bar' => $bar > 100 ? 100 : (int) $bar,
342
			'text' => sprintf($txt['pm_currently_using'], $user_info['messages'], $bar)
343
		);
344
	}
345
346
	require_once($sourcedir . '/Subs-Menu.php');
347
348
	// Set a few options for the menu.
349
	$menuOptions = array(
350
		'current_area' => $area,
351
		'disable_url_session_check' => true,
352
	);
353
354
	// Actually create the menu!
355
	$pm_include_data = createMenu($pm_areas, $menuOptions);
356
	unset($pm_areas);
357
358
	// No menu means no access.
359
	if (!$pm_include_data && (!$user_info['is_guest'] || validateSession()))
0 ignored issues
show
introduced by
The condition $pm_include_data is always false.
Loading history...
360
		fatal_lang_error('no_access', false);
361
362
	// Make a note of the Unique ID for this menu.
363
	$context['pm_menu_id'] = $context['max_menu_id'];
364
	$context['pm_menu_name'] = 'menu_data_' . $context['pm_menu_id'];
365
366
	// Set the selected item.
367
	$current_area = $pm_include_data['current_area'];
368
	$context['menu_item_selected'] = $current_area;
369
370
	// Set the template for this area and add the profile layer.
371
	if (!isset($_REQUEST['xml']))
372
		$context['template_layers'][] = 'pm';
373
}
374
375
/**
376
 * The popup for when we ask for the popup from the user.
377
 */
378
function MessagePopup()
379
{
380
	global $context, $modSettings, $smcFunc, $memberContext, $scripturl, $user_settings, $db_show_debug;
381
382
	// We do not want to output debug information here.
383
	$db_show_debug = false;
384
385
	// We only want to output our little layer here.
386
	$context['template_layers'] = array();
387
	$context['sub_template'] = 'pm_popup';
388
389
	$context['can_send_pm'] = allowedTo('pm_send');
390
	$context['can_draft'] = allowedTo('pm_draft') && !empty($modSettings['drafts_pm_enabled']);
391
392
	// So are we loading stuff?
393
	$request = $smcFunc['db_query']('', '
394
		SELECT id_pm
395
		FROM {db_prefix}pm_recipients AS pmr
396
		WHERE pmr.id_member = {int:current_member}
397
			AND is_read = {int:not_read}
398
		ORDER BY id_pm',
399
		array(
400
			'current_member' => $context['user']['id'],
401
			'not_read' => 0,
402
		)
403
	);
404
	$pms = array();
405
	while ($row = $smcFunc['db_fetch_row']($request))
406
		$pms[] = $row[0];
407
	$smcFunc['db_free_result']($request);
408
409
	if (!empty($pms))
410
	{
411
		// Just quickly, it's possible that the number of PMs can get out of sync.
412
		$count_unread = count($pms);
413
		if ($count_unread != $user_settings['unread_messages'])
414
		{
415
			updateMemberData($context['user']['id'], array('unread_messages' => $count_unread));
416
			$context['user']['unread_messages'] = count($pms);
417
		}
418
419
		// Now, actually fetch me some PMs. Make sure we track the senders, got some work to do for them.
420
		$senders = array();
421
422
		$request = $smcFunc['db_query']('', '
423
			SELECT pm.id_pm, pm.id_pm_head, COALESCE(mem.id_member, pm.id_member_from) AS id_member_from,
424
				COALESCE(mem.real_name, pm.from_name) AS member_from, pm.msgtime AS timestamp, pm.subject
425
			FROM {db_prefix}personal_messages AS pm
426
				LEFT JOIN {db_prefix}members AS mem ON (pm.id_member_from = mem.id_member)
427
			WHERE pm.id_pm IN ({array_int:id_pms})',
428
			array(
429
				'id_pms' => $pms,
430
			)
431
		);
432
		while ($row = $smcFunc['db_fetch_assoc']($request))
433
		{
434
			if (!empty($row['id_member_from']))
435
				$senders[] = $row['id_member_from'];
436
437
			$row['replied_to_you'] = $row['id_pm'] != $row['id_pm_head'];
438
			$row['time'] = timeformat($row['timestamp']);
439
			$row['pm_link'] = '<a href="' . $scripturl . '?action=pm;f=inbox;pmsg=' . $row['id_pm'] . '">' . $row['subject'] . '</a>';
440
			$context['unread_pms'][$row['id_pm']] = $row;
441
		}
442
		$smcFunc['db_free_result']($request);
443
444
		$senders = loadMemberData($senders);
445
		foreach ($senders as $member)
446
			loadMemberContext($member);
447
448
		// Having loaded everyone, attach them to the PMs.
449
		foreach ($context['unread_pms'] as $id_pm => $details)
450
			if (!empty($memberContext[$details['id_member_from']]))
451
				$context['unread_pms'][$id_pm]['member'] = &$memberContext[$details['id_member_from']];
452
	}
453
}
454
455
/**
456
 * A folder, ie. inbox/sent etc.
457
 */
458
function MessageFolder()
459
{
460
	global $txt, $scripturl, $modSettings, $context, $subjects_request;
461
	global $messages_request, $user_info, $recipients, $options, $smcFunc, $user_settings;
462
463
	// Changing view?
464
	if (isset($_GET['view']))
465
	{
466
		$context['display_mode'] = $context['display_mode'] > 1 ? 0 : $context['display_mode'] + 1;
467
		updateMemberData($user_info['id'], array('pm_prefs' => ($user_settings['pm_prefs'] & 252) | $context['display_mode']));
468
	}
469
470
	// Make sure the starting location is valid.
471
	if (isset($_GET['start']) && $_GET['start'] != 'new')
472
		$_GET['start'] = (int) $_GET['start'];
473
	elseif (!isset($_GET['start']) && !empty($options['view_newest_pm_first']))
474
		$_GET['start'] = 0;
475
	else
476
		$_GET['start'] = 'new';
477
478
	// Set up some basic theme stuff.
479
	$context['from_or_to'] = $context['folder'] != 'sent' ? 'from' : 'to';
480
	$context['get_pmessage'] = 'prepareMessageContext';
481
	$context['signature_enabled'] = substr($modSettings['signature_settings'], 0, 1) == 1;
482
	$context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : array();
483
484
	// Prevent signature images from going outside the box.
485
	if ($context['signature_enabled'])
486
	{
487
		list ($sig_limits, $sig_bbc) = explode(':', $modSettings['signature_settings']);
488
		$sig_limits = explode(',', $sig_limits);
489
490
		if (!empty($sig_limits[5]) || !empty($sig_limits[6]))
491
			addInlineCss('
492
	.signature img { ' . (!empty($sig_limits[5]) ? 'max-width: ' . (int) $sig_limits[5] . 'px; ' : '') . (!empty($sig_limits[6]) ? 'max-height: ' . (int) $sig_limits[6] . 'px; ' : '') . '}');
493
	}
494
495
	$labelJoin = '';
496
	$labelQuery = '';
497
	$labelQuery2 = '';
498
499
	// SMF logic: If you're viewing a label, it's still the inbox
500
	if ($context['folder'] == 'inbox' && $context['current_label_id'] == -1)
501
	{
502
		$labelQuery = '
503
			AND pmr.in_inbox = 1';
504
	}
505
	elseif ($context['folder'] != 'sent')
506
	{
507
		$labelJoin = '
508
			INNER JOIN {db_prefix}pm_labeled_messages AS pl ON (pl.id_pm = pmr.id_pm)';
509
510
		$labelQuery2 = '
511
			AND pl.id_label = ' . $context['current_label_id'];
512
	}
513
514
	// Set the index bar correct!
515
	messageIndexBar($context['current_label_id'] == -1 ? $context['folder'] : 'label' . $context['current_label_id']);
516
517
	// Sorting the folder.
518
	$sort_methods = array(
519
		'date' => 'pm.id_pm',
520
		'name' => 'COALESCE(mem.real_name, \'\')',
521
		'subject' => 'pm.subject',
522
	);
523
524
	// They didn't pick one, use the forum default.
525
	if (!isset($_GET['sort']) || !isset($sort_methods[$_GET['sort']]))
526
	{
527
		$context['sort_by'] = 'date';
528
		$_GET['sort'] = 'pm.id_pm';
529
		// An overriding setting?
530
		$descending = !empty($options['view_newest_pm_first']);
531
	}
532
	// Otherwise use the defaults: ascending, by date.
533
	else
534
	{
535
		$context['sort_by'] = $_GET['sort'];
536
		$_GET['sort'] = $sort_methods[$_GET['sort']];
537
		$descending = isset($_GET['desc']);
538
	}
539
540
	$context['sort_direction'] = $descending ? 'down' : 'up';
541
542
	// Set the text to resemble the current folder.
543
	$pmbox = $context['folder'] != 'sent' ? $txt['inbox'] : $txt['sent_items'];
544
	$txt['delete_all'] = str_replace('PMBOX', $pmbox, $txt['delete_all']);
545
546
	// Now, build the link tree!
547
	if ($context['current_label_id'] == -1)
548
		$context['linktree'][] = array(
549
			'url' => $scripturl . '?action=pm;f=' . $context['folder'],
550
			'name' => $pmbox
551
		);
552
553
	// Build it further for a label.
554
	if ($context['current_label_id'] != -1)
555
		$context['linktree'][] = array(
556
			'url' => $scripturl . '?action=pm;f=' . $context['folder'] . ';l=' . $context['current_label_id'],
557
			'name' => $txt['pm_current_label'] . ': ' . $context['current_label']
558
		);
559
560
	// Figure out how many messages there are.
561
	if ($context['folder'] == 'sent')
562
		$request = $smcFunc['db_query']('', '
563
			SELECT COUNT(' . ($context['display_mode'] == 2 ? 'DISTINCT pm.id_pm_head' : '*') . ')
564
			FROM {db_prefix}personal_messages AS pm
565
			WHERE pm.id_member_from = {int:current_member}
566
				AND pm.deleted_by_sender = {int:not_deleted}',
567
			array(
568
				'current_member' => $user_info['id'],
569
				'not_deleted' => 0,
570
			)
571
		);
572
	else
573
		$request = $smcFunc['db_query']('', '
574
			SELECT COUNT(' . ($context['display_mode'] == 2 ? 'DISTINCT pm.id_pm_head' : '*') . ')
575
			FROM {db_prefix}pm_recipients AS pmr' . ($context['display_mode'] == 2 ? '
576
				INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)' : '') . $labelJoin . '
577
			WHERE pmr.id_member = {int:current_member}
578
				AND pmr.deleted = {int:not_deleted}' . $labelQuery . $labelQuery2,
579
			array(
580
				'current_member' => $user_info['id'],
581
				'not_deleted' => 0,
582
			)
583
		);
584
	list ($max_messages) = $smcFunc['db_fetch_row']($request);
585
	$smcFunc['db_free_result']($request);
586
587
	// Only show the button if there are messages to delete.
588
	$context['show_delete'] = $max_messages > 0;
589
	$maxPerPage = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
590
591
	// Start on the last page.
592
	if (!is_numeric($_GET['start']) || $_GET['start'] >= $max_messages)
593
		$_GET['start'] = ($max_messages - 1) - (($max_messages - 1) % $maxPerPage);
594
	elseif ($_GET['start'] < 0)
595
		$_GET['start'] = 0;
596
597
	// ... but wait - what if we want to start from a specific message?
598
	if (isset($_GET['pmid']))
599
	{
600
		$pmID = (int) $_GET['pmid'];
601
602
		// Make sure you have access to this PM.
603
		if (!isAccessiblePM($pmID, $context['folder'] == 'sent' ? 'outbox' : 'inbox'))
604
			fatal_lang_error('no_access', false);
605
606
		$context['current_pm'] = $pmID;
607
608
		// With only one page of PM's we're gonna want page 1.
609
		if ($max_messages <= $maxPerPage)
610
			$_GET['start'] = 0;
611
		// If we pass kstart we assume we're in the right place.
612
		elseif (!isset($_GET['kstart']))
613
		{
614
			if ($context['folder'] == 'sent')
615
				$request = $smcFunc['db_query']('', '
616
					SELECT COUNT(' . ($context['display_mode'] == 2 ? 'DISTINCT pm.id_pm_head' : '*') . ')
617
					FROM {db_prefix}personal_messages
618
					WHERE id_member_from = {int:current_member}
619
						AND deleted_by_sender = {int:not_deleted}
620
						AND id_pm ' . ($descending ? '>' : '<') . ' {int:id_pm}',
621
					array(
622
						'current_member' => $user_info['id'],
623
						'not_deleted' => 0,
624
						'id_pm' => $pmID,
625
					)
626
				);
627
			else
628
				$request = $smcFunc['db_query']('', '
629
					SELECT COUNT(' . ($context['display_mode'] == 2 ? 'DISTINCT pm.id_pm_head' : '*') . ')
630
					FROM {db_prefix}pm_recipients AS pmr' . ($context['display_mode'] == 2 ? '
631
						INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)' : '') . $labelJoin . '
632
					WHERE pmr.id_member = {int:current_member}
633
						AND pmr.deleted = {int:not_deleted}' . $labelQuery . $labelQuery2 . '
634
						AND pmr.id_pm ' . ($descending ? '>' : '<') . ' {int:id_pm}',
635
					array(
636
						'current_member' => $user_info['id'],
637
						'not_deleted' => 0,
638
						'id_pm' => $pmID,
639
					)
640
				);
641
642
			list ($_GET['start']) = $smcFunc['db_fetch_row']($request);
643
			$smcFunc['db_free_result']($request);
644
645
			// To stop the page index's being abnormal, start the page on the page the message would normally be located on...
646
			$_GET['start'] = $maxPerPage * (int) ($_GET['start'] / $maxPerPage);
647
		}
648
	}
649
650
	// Sanitize and validate pmsg variable if set.
651
	if (isset($_GET['pmsg']))
652
	{
653
		$pmsg = (int) $_GET['pmsg'];
654
655
		if (!isAccessiblePM($pmsg, $context['folder'] == 'sent' ? 'outbox' : 'inbox'))
656
			fatal_lang_error('no_access', false);
657
	}
658
659
	// Set up the page index.
660
	$context['page_index'] = constructPageIndex($scripturl . '?action=pm;f=' . $context['folder'] . (isset($_REQUEST['l']) ? ';l=' . (int) $_REQUEST['l'] : '') . ';sort=' . $context['sort_by'] . ($descending ? ';desc' : ''), $_GET['start'], $max_messages, $maxPerPage);
661
	$context['start'] = $_GET['start'];
662
663
	// Determine the navigation context.
664
	$context['links'] = array(
665
		'first' => $_GET['start'] >= $maxPerPage ? $scripturl . '?action=pm;start=0' : '',
666
		'prev' => $_GET['start'] >= $maxPerPage ? $scripturl . '?action=pm;start=' . ($_GET['start'] - $maxPerPage) : '',
667
		'next' => $_GET['start'] + $maxPerPage < $max_messages ? $scripturl . '?action=pm;start=' . ($_GET['start'] + $maxPerPage) : '',
668
		'last' => $_GET['start'] + $maxPerPage < $max_messages ? $scripturl . '?action=pm;start=' . (floor(($max_messages - 1) / $maxPerPage) * $maxPerPage) : '',
669
		'up' => $scripturl,
670
	);
671
	$context['page_info'] = array(
672
		'current_page' => $_GET['start'] / $maxPerPage + 1,
673
		'num_pages' => floor(($max_messages - 1) / $maxPerPage) + 1
674
	);
675
676
	// First work out what messages we need to see - if grouped is a little trickier...
677
	if ($context['display_mode'] == 2)
678
	{
679
		if ($context['folder'] != 'sent' && $context['folder'] != 'inbox')
680
		{
681
			$labelJoin = '
682
				INNER JOIN {db_prefix}pm_labeled_messages AS pl ON (pl.id_pm = pm.id_pm)';
683
684
			$labelQuery = '';
685
			$labelQuery2 = '
686
				AND pl.id_label = ' . $context['current_label_id'];
687
		}
688
689
		$request = $smcFunc['db_query']('', '
690
				SELECT MAX(pm.id_pm) AS id_pm, pm.id_pm_head
691
				FROM {db_prefix}personal_messages AS pm' . ($context['folder'] == 'sent' ? ($context['sort_by'] == 'name' ? '
692
				LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)' : '') : '
693
				INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm
694
					AND pmr.id_member = {int:current_member}
695
					AND pmr.deleted = {int:deleted_by}
696
					' . $labelQuery . ')') . $labelJoin . ($context['sort_by'] == 'name' ? ('
697
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = {raw:pm_member})') : '') . '
698
				WHERE ' . ($context['folder'] == 'sent' ? 'pm.id_member_from = {int:current_member}
699
					AND pm.deleted_by_sender = {int:deleted_by}' : '1=1') . (empty($pmsg) ? '' : '
700
					AND pm.id_pm = {int:pmsg}') . $labelQuery2 . '
701
				GROUP BY pm.id_pm_head'.($_GET['sort'] != 'pm.id_pm' ? ',' . $_GET['sort'] : '') . '
702
				ORDER BY ' . ($_GET['sort'] == 'pm.id_pm' ? 'id_pm' : '{raw:sort}') . ($descending ? ' DESC' : ' ASC') . (empty($_GET['pmsg']) ? '
703
				LIMIT ' . $_GET['start'] . ', ' . $maxPerPage : ''),
704
				array(
705
					'current_member' => $user_info['id'],
706
					'deleted_by' => 0,
707
					'sort' => $_GET['sort'],
708
					'pm_member' => $context['folder'] == 'sent' ? 'pmr.id_member' : 'pm.id_member_from',
709
					'pmsg' => isset($pmsg) ? (int) $pmsg : 0,
710
				)
711
		);
712
	}
713
	// This is kinda simple!
714
	else
715
	{
716
		// @todo SLOW This query uses a filesort. (inbox only.)
717
		$request = $smcFunc['db_query']('', '
718
			SELECT pm.id_pm, pm.id_pm_head, pm.id_member_from
719
			FROM {db_prefix}personal_messages AS pm' . ($context['folder'] == 'sent' ? '' . ($context['sort_by'] == 'name' ? '
720
				LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)' : '') : '
721
				INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm
722
					AND pmr.id_member = {int:current_member}
723
					AND pmr.deleted = {int:is_deleted}
724
					' . $labelQuery . ')') . $labelJoin . ($context['sort_by'] == 'name' ? ('
725
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = {raw:pm_member})') : '') . '
726
			WHERE ' . ($context['folder'] == 'sent' ? 'pm.id_member_from = {int:current_member}
727
				AND pm.deleted_by_sender = {int:is_deleted}' : '1=1') . (empty($pmsg) ? '' : '
728
				AND pm.id_pm = {int:pmsg}') . $labelQuery2 . '
729
			ORDER BY ' . ($_GET['sort'] == 'pm.id_pm' && $context['folder'] != 'sent' ? 'pmr.id_pm' : '{raw:sort}') . ($descending ? ' DESC' : ' ASC') . (empty($pmsg) ? '
730
			LIMIT ' . $_GET['start'] . ', ' . $maxPerPage : ''),
731
			array(
732
				'current_member' => $user_info['id'],
733
				'is_deleted' => 0,
734
				'sort' => $_GET['sort'],
735
				'pm_member' => $context['folder'] == 'sent' ? 'pmr.id_member' : 'pm.id_member_from',
736
				'pmsg' => isset($pmsg) ? (int) $pmsg : 0,
737
			)
738
		);
739
	}
740
	// Load the id_pms and initialize recipients.
741
	$pms = array();
742
	$lastData = array();
743
	$posters = $context['folder'] == 'sent' ? array($user_info['id']) : array();
744
	$recipients = array();
745
746
	while ($row = $smcFunc['db_fetch_assoc']($request))
747
	{
748
		if (!isset($recipients[$row['id_pm']]))
749
		{
750
			if (isset($row['id_member_from']))
751
				$posters[$row['id_pm']] = $row['id_member_from'];
752
			$pms[$row['id_pm']] = $row['id_pm'];
753
			$recipients[$row['id_pm']] = array(
754
				'to' => array(),
755
				'bcc' => array()
756
			);
757
		}
758
759
		// Keep track of the last message so we know what the head is without another query!
760
		if ((empty($pmID) && (empty($options['view_newest_pm_first']) || !isset($lastData))) || empty($lastData) || (!empty($pmID) && $pmID == $row['id_pm']))
761
			$lastData = array(
762
				'id' => $row['id_pm'],
763
				'head' => $row['id_pm_head'],
764
			);
765
	}
766
	$smcFunc['db_free_result']($request);
767
768
	// Make sure that we have been given a correct head pm id!
769
	if ($context['display_mode'] == 2 && !empty($pmID) && $pmID != $lastData['id'])
770
		fatal_lang_error('no_access', false);
771
772
	if (!empty($pms))
773
	{
774
		// Select the correct current message.
775
		if (empty($pmID))
776
			$context['current_pm'] = $lastData['id'];
777
778
		// This is a list of the pm's that are used for "full" display.
779
		if ($context['display_mode'] == 0)
780
			$display_pms = $pms;
781
		else
782
			$display_pms = array($context['current_pm']);
783
784
		// At this point we know the main id_pm's. But - if we are looking at conversations we need the others!
785
		if ($context['display_mode'] == 2)
786
		{
787
			$request = $smcFunc['db_query']('', '
788
				SELECT pm.id_pm, pm.id_member_from, pm.deleted_by_sender, pmr.id_member, pmr.deleted
789
				FROM {db_prefix}personal_messages AS pm
790
					INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)
791
				WHERE pm.id_pm_head = {int:id_pm_head}
792
					AND ((pm.id_member_from = {int:current_member} AND pm.deleted_by_sender = {int:not_deleted})
793
						OR (pmr.id_member = {int:current_member} AND pmr.deleted = {int:not_deleted}))
794
				ORDER BY pm.id_pm',
795
				array(
796
					'current_member' => $user_info['id'],
797
					'id_pm_head' => $lastData['head'],
798
					'not_deleted' => 0,
799
				)
800
			);
801
			while ($row = $smcFunc['db_fetch_assoc']($request))
802
			{
803
				// This is, frankly, a joke. We will put in a workaround for people sending to themselves - yawn!
804
				if ($context['folder'] == 'sent' && $row['id_member_from'] == $user_info['id'] && $row['deleted_by_sender'] == 1)
805
					continue;
806
				elseif ($row['id_member'] == $user_info['id'] & $row['deleted'] == 1)
0 ignored issues
show
Bug introduced by
Are you sure you want to use the bitwise & or did you mean &&?
Loading history...
807
					continue;
808
809
				if (!isset($recipients[$row['id_pm']]))
810
					$recipients[$row['id_pm']] = array(
811
						'to' => array(),
812
						'bcc' => array()
813
					);
814
				$display_pms[] = $row['id_pm'];
815
				$posters[$row['id_pm']] = $row['id_member_from'];
816
			}
817
			$smcFunc['db_free_result']($request);
818
		}
819
820
		// This is pretty much EVERY pm!
821
		$all_pms = array_merge($pms, $display_pms);
822
		$all_pms = array_unique($all_pms);
823
824
		// Get recipients (don't include bcc-recipients for your inbox, you're not supposed to know :P).
825
		$request = $smcFunc['db_query']('', '
826
			SELECT pmr.id_pm, mem_to.id_member AS id_member_to, mem_to.real_name AS to_name, pmr.bcc, pmr.in_inbox, pmr.is_read
827
			FROM {db_prefix}pm_recipients AS pmr
828
				LEFT JOIN {db_prefix}members AS mem_to ON (mem_to.id_member = pmr.id_member)
829
			WHERE pmr.id_pm IN ({array_int:pm_list})',
830
			array(
831
				'pm_list' => $all_pms,
832
			)
833
		);
834
		$context['message_labels'] = array();
835
		$context['message_replied'] = array();
836
		$context['message_unread'] = array();
837
		while ($row = $smcFunc['db_fetch_assoc']($request))
838
		{
839
			if ($context['folder'] == 'sent' || empty($row['bcc']))
840
			{
841
				$recipients[$row['id_pm']][empty($row['bcc']) ? 'to' : 'bcc'][] = empty($row['id_member_to']) ? $txt['guest_title'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_to'] . '">' . $row['to_name'] . '</a>';
842
843
				$context['folder'] == 'sent' && $context['display_mode'] != 2 ? $context['message_replied'][$row['id_pm']] = $row['is_read'] & 2 : '';
844
			}
845
846
			if ($row['id_member_to'] == $user_info['id'] && $context['folder'] != 'sent')
847
			{
848
				$context['message_replied'][$row['id_pm']] = $row['is_read'] & 2;
849
				$context['message_unread'][$row['id_pm']] = $row['is_read'] == 0;
850
851
				// Get the labels for this PM
852
				$request2 = $smcFunc['db_query']('', '
853
					SELECT id_label
854
					FROM {db_prefix}pm_labeled_messages
855
					WHERE id_pm = {int:current_pm}',
856
					array(
857
						'current_pm' => $row['id_pm'],
858
					)
859
				);
860
861
				while ($row2 = $smcFunc['db_fetch_assoc']($request2))
862
				{
863
					$l_id = $row2['id_label'];
864
					if (isset($context['labels'][$l_id]))
865
						$context['message_labels'][$row['id_pm']][$l_id] = array('id' => $l_id, 'name' => $context['labels'][$l_id]['name']);
866
				}
867
868
				$smcFunc['db_free_result']($request2);
869
870
				// Is this in the inbox as well?
871
				if ($row['in_inbox'] == 1)
872
				{
873
					$context['message_labels'][$row['id_pm']][-1] = array('id' => -1, 'name' => $context['labels'][-1]['name']);
874
				}
875
			}
876
		}
877
		$smcFunc['db_free_result']($request);
878
879
		// Make sure we don't load unnecessary data.
880
		if ($context['display_mode'] == 1)
881
		{
882
			foreach ($posters as $k => $v)
883
				if (!in_array($k, $display_pms))
884
					unset($posters[$k]);
885
		}
886
887
		// Load any users....
888
		loadMemberData($posters);
889
890
		// If we're on grouped/restricted view get a restricted list of messages.
891
		if ($context['display_mode'] != 0)
892
		{
893
			// Get the order right.
894
			$orderBy = array();
895
			foreach (array_reverse($pms) as $pm)
896
				$orderBy[] = 'pm.id_pm = ' . $pm;
897
898
			// Seperate query for these bits!
899
			$subjects_request = $smcFunc['db_query']('', '
900
				SELECT pm.id_pm, pm.subject, COALESCE(pm.id_member_from, 0) AS id_member_from, pm.msgtime, COALESCE(mem.real_name, pm.from_name) AS from_name,
901
					mem.id_member
902
				FROM {db_prefix}personal_messages AS pm
903
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from)
904
				WHERE pm.id_pm IN ({array_int:pm_list})
905
				ORDER BY ' . implode(', ', $orderBy) . '
906
				LIMIT {int:limit}',
907
				array(
908
					'pm_list' => $pms,
909
					'limit' => count($pms),
910
				)
911
			);
912
		}
913
914
		// Execute the query!
915
		$messages_request = $smcFunc['db_query']('', '
916
			SELECT pm.id_pm, pm.subject, pm.id_member_from, pm.body, pm.msgtime, pm.from_name
917
			FROM {db_prefix}personal_messages AS pm' . ($context['folder'] == 'sent' ? '
918
				LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)' : '') . ($context['sort_by'] == 'name' ? '
919
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = {raw:id_member})' : '') . '
920
			WHERE pm.id_pm IN ({array_int:display_pms})' . ($context['folder'] == 'sent' ? '
921
			GROUP BY pm.id_pm, pm.subject, pm.id_member_from, pm.body, pm.msgtime, pm.from_name' : '') . '
922
			ORDER BY ' . ($context['display_mode'] == 2 ? 'pm.id_pm' : '{raw:sort}') . ($descending ? ' DESC' : ' ASC') . '
923
			LIMIT {int:limit}',
924
			array(
925
				'display_pms' => $display_pms,
926
				'id_member' => $context['folder'] == 'sent' ? 'pmr.id_member' : 'pm.id_member_from',
927
				'limit' => count($display_pms),
928
				'sort' => $_GET['sort'],
929
			)
930
		);
931
932
		// Build the conversation button array.
933
		if ($context['display_mode'] == 2)
934
		{
935
			$context['conversation_buttons'] = array(
936
				'delete' => array('text' => 'delete_conversation', 'image' => 'delete.png', 'url' => $scripturl . '?action=pm;sa=pmactions;pm_actions[' . $context['current_pm'] . ']=delete;conversation;f=' . $context['folder'] . ';start=' . $context['start'] . ($context['current_label_id'] != -1 ? ';l=' . $context['current_label_id'] : '') . ';' . $context['session_var'] . '=' . $context['session_id'], 'custom' => 'data-confirm="' . $txt['remove_conversation'] . '"', 'class' => 'you_sure'),
937
			);
938
939
			// Allow mods to add additional buttons here
940
			call_integration_hook('integrate_conversation_buttons');
941
		}
942
	}
943
	else
944
		$messages_request = false;
945
946
	$context['can_send_pm'] = allowedTo('pm_send');
947
	$context['can_send_email'] = allowedTo('moderate_forum');
948
	$context['sub_template'] = 'folder';
949
	$context['page_title'] = $txt['pm_inbox'];
950
951
	// Finally mark the relevant messages as read.
952
	if ($context['folder'] != 'sent' && !empty($context['labels'][(int) $context['current_label_id']]['unread_messages']))
953
	{
954
		// If the display mode is "old sk00l" do them all...
955
		if ($context['display_mode'] == 0)
956
			markMessages(null, $context['current_label_id']);
957
		// Otherwise do just the current one!
958
		elseif (!empty($context['current_pm']))
959
			markMessages($display_pms, $context['current_label_id']);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $display_pms does not seem to be defined for all execution paths leading up to this point.
Loading history...
960
	}
961
}
962
963
/**
964
 * Get a personal message for the theme.  (used to save memory.)
965
 *
966
 * @param string $type The type of message
967
 * @param bool $reset Whether to reset the internal pointer
968
 * @return bool|array False on failure, otherwise an array of info
969
 */
970
function prepareMessageContext($type = 'subject', $reset = false)
971
{
972
	global $txt, $scripturl, $modSettings, $context, $messages_request, $memberContext, $recipients, $smcFunc;
973
	global $user_info, $subjects_request;
974
975
	// Count the current message number....
976
	static $counter = null;
977
	if ($counter === null || $reset)
978
		$counter = $context['start'];
979
980
	static $temp_pm_selected = null;
981
	if ($temp_pm_selected === null)
982
	{
983
		$temp_pm_selected = isset($_SESSION['pm_selected']) ? $_SESSION['pm_selected'] : array();
984
		$_SESSION['pm_selected'] = array();
985
	}
986
987
	// If we're in non-boring view do something exciting!
988
	if ($context['display_mode'] != 0 && $subjects_request && $type == 'subject')
989
	{
990
		$subject = $smcFunc['db_fetch_assoc']($subjects_request);
991
		if (!$subject)
992
		{
993
			$smcFunc['db_free_result']($subjects_request);
994
			return false;
995
		}
996
997
		$subject['subject'] = $subject['subject'] == '' ? $txt['no_subject'] : $subject['subject'];
998
		censorText($subject['subject']);
999
1000
		$output = array(
1001
			'id' => $subject['id_pm'],
1002
			'member' => array(
1003
				'id' => $subject['id_member_from'],
1004
				'name' => $subject['from_name'],
1005
				'link' => ($subject['id_member_from'] != 0) ? '<a href="' . $scripturl . '?action=profile;u=' . $subject['id_member_from'] . '">' . $subject['from_name'] . '</a>' : $subject['from_name'],
1006
			),
1007
			'recipients' => &$recipients[$subject['id_pm']],
1008
			'subject' => $subject['subject'],
1009
			'time' => timeformat($subject['msgtime']),
1010
			'timestamp' => forum_time(true, $subject['msgtime']),
1011
			'number_recipients' => count($recipients[$subject['id_pm']]['to']),
1012
			'labels' => &$context['message_labels'][$subject['id_pm']],
1013
			'fully_labeled' => count(isset($context['message_labels'][$subject['id_pm']]) ? $context['message_labels'][$subject['id_pm']] : array()) == count($context['labels']),
1014
			'is_replied_to' => &$context['message_replied'][$subject['id_pm']],
1015
			'is_unread' => &$context['message_unread'][$subject['id_pm']],
1016
			'is_selected' => !empty($temp_pm_selected) && in_array($subject['id_pm'], $temp_pm_selected),
1017
		);
1018
1019
		return $output;
1020
	}
1021
1022
	// Bail if it's false, ie. no messages.
1023
	if ($messages_request == false)
1024
		return false;
1025
1026
	// Reset the data?
1027
	if ($reset == true)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
1028
		return @$smcFunc['db_data_seek']($messages_request, 0);
1029
1030
	// Get the next one... bail if anything goes wrong.
1031
	$message = $smcFunc['db_fetch_assoc']($messages_request);
1032
	if (!$message)
1033
	{
1034
		if ($type != 'subject')
1035
			$smcFunc['db_free_result']($messages_request);
1036
1037
		return false;
1038
	}
1039
1040
	// Use '(no subject)' if none was specified.
1041
	$message['subject'] = $message['subject'] == '' ? $txt['no_subject'] : $message['subject'];
1042
1043
	// Load the message's information - if it's not there, load the guest information.
1044
	if (!loadMemberContext($message['id_member_from'], true))
1045
	{
1046
		$memberContext[$message['id_member_from']]['name'] = $message['from_name'];
1047
		$memberContext[$message['id_member_from']]['id'] = 0;
1048
1049
		// Sometimes the forum sends messages itself (Warnings are an example) - in this case don't label it from a guest.
1050
		$memberContext[$message['id_member_from']]['group'] = $message['from_name'] == $context['forum_name_html_safe'] ? '' : $txt['guest_title'];
1051
		$memberContext[$message['id_member_from']]['link'] = $message['from_name'];
1052
		$memberContext[$message['id_member_from']]['email'] = '';
1053
		$memberContext[$message['id_member_from']]['show_email'] = false;
1054
		$memberContext[$message['id_member_from']]['is_guest'] = true;
1055
	}
1056
	else
1057
	{
1058
		$memberContext[$message['id_member_from']]['can_view_profile'] = allowedTo('profile_view') || ($message['id_member_from'] == $user_info['id'] && !$user_info['is_guest']);
1059
		$memberContext[$message['id_member_from']]['can_see_warning'] = !isset($context['disabled_fields']['warning_status']) && $memberContext[$message['id_member_from']]['warning_status'] && ($context['user']['can_mod'] || (!empty($modSettings['warning_show']) && ($modSettings['warning_show'] > 1 || $message['id_member_from'] == $user_info['id'])));
1060
		// Show the email if it's your own PM
1061
		$memberContext[$message['id_member_from']]['show_email'] |= $message['id_member_from'] == $user_info['id'];
1062
	}
1063
1064
	$memberContext[$message['id_member_from']]['show_profile_buttons'] = $modSettings['show_profile_buttons'] && (!empty($memberContext[$message['id_member_from']]['can_view_profile']) || (!empty($memberContext[$message['id_member_from']]['website']['url']) && !isset($context['disabled_fields']['website'])) || $memberContext[$message['id_member_from']]['show_email'] || $context['can_send_pm']);
1065
1066
	// Censor all the important text...
1067
	censorText($message['body']);
1068
	censorText($message['subject']);
1069
1070
	// Run UBBC interpreter on the message.
1071
	$message['body'] = parse_bbc($message['body'], true, 'pm' . $message['id_pm']);
1072
1073
	// Send the array.
1074
	$output = array(
1075
		'id' => $message['id_pm'],
1076
		'member' => &$memberContext[$message['id_member_from']],
1077
		'subject' => $message['subject'],
1078
		'time' => timeformat($message['msgtime']),
1079
		'timestamp' => forum_time(true, $message['msgtime']),
1080
		'counter' => $counter,
1081
		'body' => $message['body'],
1082
		'recipients' => &$recipients[$message['id_pm']],
1083
		'number_recipients' => count($recipients[$message['id_pm']]['to']),
1084
		'labels' => &$context['message_labels'][$message['id_pm']],
1085
		'fully_labeled' => count(isset($context['message_labels'][$message['id_pm']]) ? $context['message_labels'][$message['id_pm']] : array()) == count($context['labels']),
1086
		'is_replied_to' => &$context['message_replied'][$message['id_pm']],
1087
		'is_unread' => &$context['message_unread'][$message['id_pm']],
1088
		'is_selected' => !empty($temp_pm_selected) && in_array($message['id_pm'], $temp_pm_selected),
1089
		'is_message_author' => $message['id_member_from'] == $user_info['id'],
1090
		'can_report' => !empty($modSettings['enableReportPM']),
1091
		'can_see_ip' => allowedTo('moderate_forum') || ($message['id_member_from'] == $user_info['id'] && !empty($user_info['id'])),
1092
	);
1093
1094
	$counter++;
1095
1096
	// Any custom profile fields?
1097
	if (!empty($memberContext[$message['id_member_from']]['custom_fields']))
1098
		foreach ($memberContext[$message['id_member_from']]['custom_fields'] as $custom)
1099
			$output['custom_fields'][$context['cust_profile_fields_placement'][$custom['placement']]][] = $custom;
1100
1101
	call_integration_hook('integrate_prepare_pm_context', array(&$output, &$message, $counter));
1102
1103
	return $output;
1104
}
1105
1106
/**
1107
 * Allows searching through personal messages.
1108
 */
1109
function MessageSearch()
1110
{
1111
	global $context, $txt, $scripturl, $smcFunc;
1112
1113
	if (isset($_REQUEST['params']))
1114
	{
1115
		$temp_params = explode('|"|', base64_decode(strtr($_REQUEST['params'], array(' ' => '+'))));
1116
		$context['search_params'] = array();
1117
		foreach ($temp_params as $i => $data)
1118
		{
1119
			@list ($k, $v) = explode('|\'|', $data);
1120
			$context['search_params'][$k] = $v;
1121
		}
1122
	}
1123
	if (isset($_REQUEST['search']))
1124
		$context['search_params']['search'] = un_htmlspecialchars($_REQUEST['search']);
1125
1126
	if (isset($context['search_params']['search']))
1127
		$context['search_params']['search'] = $smcFunc['htmlspecialchars']($context['search_params']['search']);
1128
	if (isset($context['search_params']['userspec']))
1129
		$context['search_params']['userspec'] = $smcFunc['htmlspecialchars']($context['search_params']['userspec']);
1130
1131
	if (!empty($context['search_params']['searchtype']))
1132
		$context['search_params']['searchtype'] = 2;
1133
1134
	if (!empty($context['search_params']['minage']))
1135
		$context['search_params']['minage'] = (int) $context['search_params']['minage'];
1136
1137
	if (!empty($context['search_params']['maxage']))
1138
		$context['search_params']['maxage'] = (int) $context['search_params']['maxage'];
1139
1140
	$context['search_params']['subject_only'] = !empty($context['search_params']['subject_only']);
1141
	$context['search_params']['show_complete'] = !empty($context['search_params']['show_complete']);
1142
1143
	// Create the array of labels to be searched.
1144
	$context['search_labels'] = array();
1145
	$searchedLabels = isset($context['search_params']['labels']) && $context['search_params']['labels'] != '' ? explode(',', $context['search_params']['labels']) : array();
1146
	foreach ($context['labels'] as $label)
1147
	{
1148
		$context['search_labels'][] = array(
1149
			'id' => $label['id'],
1150
			'name' => $label['name'],
1151
			'checked' => !empty($searchedLabels) ? in_array($label['id'], $searchedLabels) : true,
1152
		);
1153
	}
1154
1155
	// Are all the labels checked?
1156
	$context['check_all'] = empty($searchedLabels) || count($context['search_labels']) == count($searchedLabels);
1157
1158
	// Load the error text strings if there were errors in the search.
1159
	if (!empty($context['search_errors']))
1160
	{
1161
		loadLanguage('Errors');
1162
		$context['search_errors']['messages'] = array();
1163
		foreach ($context['search_errors'] as $search_error => $dummy)
1164
		{
1165
			if ($search_error == 'messages')
1166
				continue;
1167
1168
			$context['search_errors']['messages'][] = $txt['error_' . $search_error];
1169
		}
1170
	}
1171
1172
	$context['page_title'] = $txt['pm_search_title'];
1173
	$context['sub_template'] = 'search';
1174
	$context['linktree'][] = array(
1175
		'url' => $scripturl . '?action=pm;sa=search',
1176
		'name' => $txt['pm_search_bar_title'],
1177
	);
1178
}
1179
1180
/**
1181
 * Actually do the search of personal messages.
1182
 */
1183
function MessageSearch2()
1184
{
1185
	global $scripturl, $modSettings, $user_info, $context, $txt;
1186
	global $memberContext, $smcFunc;
1187
1188
	if (!empty($context['load_average']) && !empty($modSettings['loadavg_search']) && $context['load_average'] >= $modSettings['loadavg_search'])
1189
		fatal_lang_error('loadavg_search_disabled', false);
1190
1191
	/**
1192
	 * @todo For the moment force the folder to the inbox.
1193
	 * @todo Maybe set the inbox based on a cookie or theme setting?
1194
	 */
1195
	$context['folder'] = 'inbox';
1196
1197
	// Some useful general permissions.
1198
	$context['can_send_pm'] = allowedTo('pm_send');
1199
1200
	// Some hardcoded veriables that can be tweaked if required.
1201
	$maxMembersToSearch = 500;
1202
1203
	// Extract all the search parameters.
1204
	$search_params = array();
1205
	if (isset($_REQUEST['params']))
1206
	{
1207
		$temp_params = explode('|"|', base64_decode(strtr($_REQUEST['params'], array(' ' => '+'))));
1208
		foreach ($temp_params as $i => $data)
1209
		{
1210
			@list ($k, $v) = explode('|\'|', $data);
1211
			$search_params[$k] = $v;
1212
		}
1213
	}
1214
1215
	$context['start'] = isset($_GET['start']) ? (int) $_GET['start'] : 0;
1216
1217
	// Store whether simple search was used (needed if the user wants to do another query).
1218
	if (!isset($search_params['advanced']))
1219
		$search_params['advanced'] = empty($_REQUEST['advanced']) ? 0 : 1;
1220
1221
	// 1 => 'allwords' (default, don't set as param) / 2 => 'anywords'.
1222
	if (!empty($search_params['searchtype']) || (!empty($_REQUEST['searchtype']) && $_REQUEST['searchtype'] == 2))
1223
		$search_params['searchtype'] = 2;
1224
1225
	// Minimum age of messages. Default to zero (don't set param in that case).
1226
	if (!empty($search_params['minage']) || (!empty($_REQUEST['minage']) && $_REQUEST['minage'] > 0))
1227
		$search_params['minage'] = !empty($search_params['minage']) ? (int) $search_params['minage'] : (int) $_REQUEST['minage'];
1228
1229
	// Maximum age of messages. Default to infinite (9999 days: param not set).
1230
	if (!empty($search_params['maxage']) || (!empty($_REQUEST['maxage']) && $_REQUEST['maxage'] != 9999))
1231
		$search_params['maxage'] = !empty($search_params['maxage']) ? (int) $search_params['maxage'] : (int) $_REQUEST['maxage'];
1232
1233
	$search_params['subject_only'] = !empty($search_params['subject_only']) || !empty($_REQUEST['subject_only']);
1234
	$search_params['show_complete'] = !empty($search_params['show_complete']) || !empty($_REQUEST['show_complete']);
1235
1236
	// Default the user name to a wildcard matching every user (*).
1237
	if (!empty($search_params['user_spec']) || (!empty($_REQUEST['userspec']) && $_REQUEST['userspec'] != '*'))
1238
		$search_params['userspec'] = isset($search_params['userspec']) ? $search_params['userspec'] : $_REQUEST['userspec'];
1239
1240
	// This will be full of all kinds of parameters!
1241
	$searchq_parameters = array();
1242
1243
	// If there's no specific user, then don't mention it in the main query.
1244
	if (empty($search_params['userspec']))
1245
		$userQuery = '';
1246
	else
1247
	{
1248
		$userString = strtr($smcFunc['htmlspecialchars']($search_params['userspec'], ENT_QUOTES), array('&quot;' => '"'));
1249
		$userString = strtr($userString, array('%' => '\%', '_' => '\_', '*' => '%', '?' => '_'));
1250
1251
		preg_match_all('~"([^"]+)"~', $userString, $matches);
1252
		$possible_users = array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $userString)));
1253
1254
		for ($k = 0, $n = count($possible_users); $k < $n; $k++)
1255
		{
1256
			$possible_users[$k] = trim($possible_users[$k]);
1257
1258
			if (strlen($possible_users[$k]) == 0)
1259
				unset($possible_users[$k]);
1260
		}
1261
1262
		if (!empty($possible_users))
1263
		{
1264
			// We need to bring this into the query and do it nice and cleanly.
1265
			$where_params = array();
1266
			$where_clause = array();
1267
			foreach ($possible_users as $k => $v)
1268
			{
1269
				$where_params['name_' . $k] = $v;
1270
				$where_clause[] = '{raw:real_name} LIKE {string:name_' . $k . '}';
1271
				if (!isset($where_params['real_name']))
1272
					$where_params['real_name'] = $smcFunc['db_case_sensitive'] ? 'LOWER(real_name)' : 'real_name';
1273
			}
1274
1275
			// Who matches those criteria?
1276
			// @todo This doesn't support sent item searching.
1277
			$request = $smcFunc['db_query']('', '
1278
				SELECT id_member
1279
				FROM {db_prefix}members
1280
				WHERE ' . implode(' OR ', $where_clause),
1281
				$where_params
1282
			);
1283
1284
			// Simply do nothing if there're too many members matching the criteria.
1285
			if ($smcFunc['db_num_rows']($request) > $maxMembersToSearch)
1286
				$userQuery = '';
1287
			elseif ($smcFunc['db_num_rows']($request) == 0)
1288
			{
1289
				$userQuery = 'AND pm.id_member_from = 0 AND ({raw:pm_from_name} LIKE {raw:guest_user_name_implode})';
1290
				$searchq_parameters['guest_user_name_implode'] = '\'' . implode('\' OR ' . ($smcFunc['db_case_sensitive'] ? 'LOWER(pm.from_name)' : 'pm.from_name') . ' LIKE \'', $possible_users) . '\'';
1291
				$searchq_parameters['pm_from_name'] = $smcFunc['db_case_sensitive'] ? 'LOWER(pm.from_name)' : 'pm.from_name';
1292
			}
1293
			else
1294
			{
1295
				$memberlist = array();
1296
				while ($row = $smcFunc['db_fetch_assoc']($request))
1297
					$memberlist[] = $row['id_member'];
1298
				$userQuery = 'AND (pm.id_member_from IN ({array_int:member_list}) OR (pm.id_member_from = 0 AND ({raw:pm_from_name} LIKE {raw:guest_user_name_implode})))';
1299
				$searchq_parameters['guest_user_name_implode'] = '\'' . implode('\' OR ' . ($smcFunc['db_case_sensitive'] ? 'LOWER(pm.from_name)' : 'pm.from_name') . ' LIKE \'', $possible_users) . '\'';
1300
				$searchq_parameters['member_list'] = $memberlist;
1301
				$searchq_parameters['pm_from_name'] = $smcFunc['db_case_sensitive'] ? 'LOWER(pm.from_name)' : 'pm.from_name';
1302
			}
1303
			$smcFunc['db_free_result']($request);
1304
		}
1305
		else
1306
			$userQuery = '';
1307
	}
1308
1309
	// Setup the sorting variables...
1310
	// @todo Add more in here!
1311
	$sort_columns = array(
1312
		'pm.id_pm',
1313
	);
1314
	if (empty($search_params['sort']) && !empty($_REQUEST['sort']))
1315
		list ($search_params['sort'], $search_params['sort_dir']) = array_pad(explode('|', $_REQUEST['sort']), 2, '');
1316
	$search_params['sort'] = !empty($search_params['sort']) && in_array($search_params['sort'], $sort_columns) ? $search_params['sort'] : 'pm.id_pm';
1317
	$search_params['sort_dir'] = !empty($search_params['sort_dir']) && $search_params['sort_dir'] == 'asc' ? 'asc' : 'desc';
1318
1319
	// Sort out any labels we may be searching by.
1320
	$labelQuery = '';
1321
	$labelJoin = '';
1322
	if ($context['folder'] == 'inbox' && !empty($search_params['advanced']) && $context['currently_using_labels'])
1323
	{
1324
		// Came here from pagination?  Put them back into $_REQUEST for sanitization.
1325
		if (isset($search_params['labels']))
1326
			$_REQUEST['searchlabel'] = explode(',', $search_params['labels']);
1327
1328
		// Assuming we have some labels - make them all integers.
1329
		if (!empty($_REQUEST['searchlabel']) && is_array($_REQUEST['searchlabel']))
1330
		{
1331
			foreach ($_REQUEST['searchlabel'] as $key => $id)
1332
				$_REQUEST['searchlabel'][$key] = (int) $id;
1333
		}
1334
		else
1335
			$_REQUEST['searchlabel'] = array();
1336
1337
		// Now that everything is cleaned up a bit, make the labels a param.
1338
		$search_params['labels'] = implode(',', $_REQUEST['searchlabel']);
1339
1340
		// No labels selected? That must be an error!
1341
		if (empty($_REQUEST['searchlabel']))
1342
			$context['search_errors']['no_labels_selected'] = true;
1343
		// Otherwise prepare the query!
1344
		elseif (count($_REQUEST['searchlabel']) != count($context['labels']))
1345
		{
1346
			// Special case here... "inbox" isn't a real label anymore...
1347
			if (in_array(-1, $_REQUEST['searchlabel']))
1348
			{
1349
				$labelQuery = '	AND pmr.in_inbox = {int:in_inbox}';
1350
				$searchq_parameters['in_inbox'] = 1;
1351
1352
				// Now we get rid of that...
1353
				$temp = array_diff($_REQUEST['searchlabel'], array(-1));
1354
				$_REQUEST['searchlabel'] = $temp;
1355
			}
1356
1357
			// Still have something?
1358
			if (!empty($_REQUEST['searchlabel']))
1359
			{
1360
				if ($labelQuery == '')
1361
				{
1362
					// Not searching the inbox - PM must be labeled
1363
					$labelQuery = ' AND pml.id_label IN ({array_int:labels})';
1364
					$labelJoin = ' INNER JOIN {db_prefix}pm_labeled_messages AS pml ON (pml.id_pm = pmr.id_pm)';
1365
				}
1366
				else
1367
				{
1368
					// Searching the inbox - PM doesn't have to be labeled
1369
					$labelQuery = ' AND (' . substr($labelQuery, 5) . ' OR pml.id_label IN ({array_int:labels}))';
1370
					$labelJoin = ' LEFT JOIN {db_prefix}pm_labeled_messages AS pml ON (pml.id_pm = pmr.id_pm)';
1371
				}
1372
1373
				$searchq_parameters['labels'] = $_REQUEST['searchlabel'];
1374
			}
1375
		}
1376
	}
1377
1378
	// What are we actually searching for?
1379
	$search_params['search'] = !empty($search_params['search']) ? $search_params['search'] : (isset($_REQUEST['search']) ? $_REQUEST['search'] : '');
1380
	// If we ain't got nothing - we should error!
1381
	if (!isset($search_params['search']) || $search_params['search'] == '')
1382
		$context['search_errors']['invalid_search_string'] = true;
1383
1384
	// Extract phrase parts first (e.g. some words "this is a phrase" some more words.)
1385
	preg_match_all('~(?:^|\s)([-]?)"([^"]+)"(?:$|\s)~' . ($context['utf8'] ? 'u' : ''), $search_params['search'], $matches, PREG_PATTERN_ORDER);
1386
	$searchArray = $matches[2];
1387
1388
	// Remove the phrase parts and extract the words.
1389
	$tempSearch = explode(' ', preg_replace('~(?:^|\s)(?:[-]?)"(?:[^"]+)"(?:$|\s)~' . ($context['utf8'] ? 'u' : ''), ' ', $search_params['search']));
1390
1391
	// A minus sign in front of a word excludes the word.... so...
1392
	$excludedWords = array();
1393
1394
	// .. first, we check for things like -"some words", but not "-some words".
1395
	foreach ($matches[1] as $index => $word)
1396
		if ($word == '-')
1397
		{
1398
			$word = $smcFunc['strtolower'](trim($searchArray[$index]));
1399
			if (strlen($word) > 0)
1400
				$excludedWords[] = $word;
1401
			unset($searchArray[$index]);
1402
		}
1403
1404
	// Now we look for -test, etc.... normaller.
1405
	foreach ($tempSearch as $index => $word)
1406
	{
1407
		if (strpos(trim($word), '-') === 0)
1408
		{
1409
			$word = substr($smcFunc['strtolower']($word), 1);
1410
			if (strlen($word) > 0)
1411
				$excludedWords[] = $word;
1412
			unset($tempSearch[$index]);
1413
		}
1414
	}
1415
1416
	$searchArray = array_merge($searchArray, $tempSearch);
1417
1418
	// Trim everything and make sure there are no words that are the same.
1419
	foreach ($searchArray as $index => $value)
1420
	{
1421
		$searchArray[$index] = $smcFunc['strtolower'](trim($value));
1422
		if ($searchArray[$index] == '')
1423
			unset($searchArray[$index]);
1424
		else
1425
		{
1426
			// Sort out entities first.
1427
			$searchArray[$index] = $smcFunc['htmlspecialchars']($searchArray[$index]);
1428
		}
1429
	}
1430
	$searchArray = array_unique($searchArray);
1431
1432
	// Create an array of replacements for highlighting.
1433
	$context['mark'] = array();
1434
	foreach ($searchArray as $word)
1435
		$context['mark'][$word] = '<strong class="highlight">' . $word . '</strong>';
1436
1437
	// This contains *everything*
1438
	$searchWords = array_merge($searchArray, $excludedWords);
1439
1440
	// Make sure at least one word is being searched for.
1441
	if (empty($searchArray))
1442
		$context['search_errors']['invalid_search_string'] = true;
1443
1444
	// Sort out the search query so the user can edit it - if they want.
1445
	$context['search_params'] = $search_params;
1446
	if (isset($context['search_params']['search']))
1447
		$context['search_params']['search'] = $smcFunc['htmlspecialchars']($context['search_params']['search']);
1448
	if (isset($context['search_params']['userspec']))
1449
		$context['search_params']['userspec'] = $smcFunc['htmlspecialchars']($context['search_params']['userspec']);
1450
1451
	// Now we have all the parameters, combine them together for pagination and the like...
1452
	$context['params'] = array();
1453
	foreach ($search_params as $k => $v)
1454
		$context['params'][] = $k . '|\'|' . $v;
1455
	$context['params'] = base64_encode(implode('|"|', $context['params']));
1456
1457
	// Compile the subject query part.
1458
	$andQueryParts = array();
1459
1460
	foreach ($searchWords as $index => $word)
1461
	{
1462
		if ($word == '')
1463
			continue;
1464
1465
		if ($search_params['subject_only'])
1466
			$andQueryParts[] = 'pm.subject' . (in_array($word, $excludedWords) ? ' NOT' : '') . ' LIKE {string:search_' . $index . '}';
1467
		else
1468
			$andQueryParts[] = '(pm.subject' . (in_array($word, $excludedWords) ? ' NOT' : '') . ' LIKE {string:search_' . $index . '} ' . (in_array($word, $excludedWords) ? 'AND pm.body NOT' : 'OR pm.body') . ' LIKE {string:search_' . $index . '})';
1469
		$searchq_parameters['search_' . $index] = '%' . strtr($word, array('_' => '\\_', '%' => '\\%')) . '%';
1470
	}
1471
1472
	$searchQuery = ' 1=1';
1473
	if (!empty($andQueryParts))
1474
		$searchQuery = implode(!empty($search_params['searchtype']) && $search_params['searchtype'] == 2 ? ' OR ' : ' AND ', $andQueryParts);
1475
1476
	// Age limits?
1477
	$timeQuery = '';
1478
	if (!empty($search_params['minage']))
1479
		$timeQuery .= ' AND pm.msgtime < ' . (time() - $search_params['minage'] * 86400);
1480
	if (!empty($search_params['maxage']))
1481
		$timeQuery .= ' AND pm.msgtime > ' . (time() - $search_params['maxage'] * 86400);
1482
1483
	// If we have errors - return back to the first screen...
1484
	if (!empty($context['search_errors']))
1485
	{
1486
		$_REQUEST['params'] = $context['params'];
1487
		return MessageSearch();
0 ignored issues
show
Bug introduced by
Are you sure the usage of MessageSearch() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1488
	}
1489
1490
	// Get the amount of results.
1491
	$request = $smcFunc['db_query']('', '
1492
		SELECT COUNT(*)
1493
		FROM {db_prefix}pm_recipients AS pmr
1494
			INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)
1495
			' . $labelJoin . '
1496
		WHERE ' . ($context['folder'] == 'inbox' ? '
1497
			pmr.id_member = {int:current_member}
1498
			AND pmr.deleted = {int:not_deleted}' : '
1499
			pm.id_member_from = {int:current_member}
1500
			AND pm.deleted_by_sender = {int:not_deleted}') . '
1501
			' . $userQuery . $labelQuery . $timeQuery . '
1502
			AND (' . $searchQuery . ')',
1503
		array_merge($searchq_parameters, array(
1504
			'current_member' => $user_info['id'],
1505
			'not_deleted' => 0,
1506
		))
1507
	);
1508
	list ($numResults) = $smcFunc['db_fetch_row']($request);
1509
	$smcFunc['db_free_result']($request);
1510
1511
	// Get all the matching messages... using standard search only (No caching and the like!)
1512
	// @todo This doesn't support sent item searching yet.
1513
	$request = $smcFunc['db_query']('', '
1514
		SELECT pm.id_pm, pm.id_pm_head, pm.id_member_from
1515
		FROM {db_prefix}pm_recipients AS pmr
1516
			INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)
1517
			' . $labelJoin . '
1518
		WHERE ' . ($context['folder'] == 'inbox' ? '
1519
			pmr.id_member = {int:current_member}
1520
			AND pmr.deleted = {int:not_deleted}' : '
1521
			pm.id_member_from = {int:current_member}
1522
			AND pm.deleted_by_sender = {int:not_deleted}') . '
1523
			' . $userQuery . $labelQuery . $timeQuery . '
1524
			AND (' . $searchQuery . ')
1525
		ORDER BY {raw:sort} {raw:sort_dir}
1526
		LIMIT {int:start}, {int:max}',
1527
		array_merge($searchq_parameters, array(
1528
			'current_member' => $user_info['id'],
1529
			'not_deleted' => 0,
1530
			'sort' => $search_params['sort'],
1531
			'sort_dir' => $search_params['sort_dir'],
1532
			'start' => $context['start'],
1533
			'max' => $modSettings['search_results_per_page'],
1534
		))
1535
	);
1536
	$foundMessages = array();
1537
	$posters = array();
1538
	$head_pms = array();
1539
	while ($row = $smcFunc['db_fetch_assoc']($request))
1540
	{
1541
		$foundMessages[] = $row['id_pm'];
1542
		$posters[] = $row['id_member_from'];
1543
		$head_pms[$row['id_pm']] = $row['id_pm_head'];
1544
	}
1545
	$smcFunc['db_free_result']($request);
1546
1547
	// Find the real head pms!
1548
	if ($context['display_mode'] == 2 && !empty($head_pms))
1549
	{
1550
		$request = $smcFunc['db_query']('', '
1551
			SELECT MAX(pm.id_pm) AS id_pm, pm.id_pm_head
1552
			FROM {db_prefix}personal_messages AS pm
1553
				INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)
1554
			WHERE pm.id_pm_head IN ({array_int:head_pms})
1555
				AND pmr.id_member = {int:current_member}
1556
				AND pmr.deleted = {int:not_deleted}
1557
			GROUP BY pm.id_pm_head
1558
			LIMIT {int:limit}',
1559
			array(
1560
				'head_pms' => array_unique($head_pms),
1561
				'current_member' => $user_info['id'],
1562
				'not_deleted' => 0,
1563
				'limit' => count($head_pms),
1564
			)
1565
		);
1566
		$real_pm_ids = array();
1567
		while ($row = $smcFunc['db_fetch_assoc']($request))
1568
			$real_pm_ids[$row['id_pm_head']] = $row['id_pm'];
1569
		$smcFunc['db_free_result']($request);
1570
	}
1571
1572
	// Load the users...
1573
	loadMemberData($posters);
1574
1575
	// Sort out the page index.
1576
	$context['page_index'] = constructPageIndex($scripturl . '?action=pm;sa=search2;params=' . $context['params'], $_GET['start'], $numResults, $modSettings['search_results_per_page'], false);
1577
1578
	$context['message_labels'] = array();
1579
	$context['message_replied'] = array();
1580
	$context['personal_messages'] = array();
1581
1582
	if (!empty($foundMessages))
1583
	{
1584
		// Now get recipients (but don't include bcc-recipients for your inbox, you're not supposed to know :P!)
1585
		$request = $smcFunc['db_query']('', '
1586
			SELECT
1587
				pmr.id_pm, mem_to.id_member AS id_member_to, mem_to.real_name AS to_name,
1588
				pmr.bcc, pmr.in_inbox, pmr.is_read
1589
			FROM {db_prefix}pm_recipients AS pmr
1590
				LEFT JOIN {db_prefix}members AS mem_to ON (mem_to.id_member = pmr.id_member)
1591
			WHERE pmr.id_pm IN ({array_int:message_list})',
1592
			array(
1593
				'message_list' => $foundMessages,
1594
			)
1595
		);
1596
		while ($row = $smcFunc['db_fetch_assoc']($request))
1597
		{
1598
			if ($context['folder'] == 'sent' || empty($row['bcc']))
1599
				$recipients[$row['id_pm']][empty($row['bcc']) ? 'to' : 'bcc'][] = empty($row['id_member_to']) ? $txt['guest_title'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_to'] . '">' . $row['to_name'] . '</a>';
1600
1601
			if ($row['id_member_to'] == $user_info['id'] && $context['folder'] != 'sent')
1602
			{
1603
				$context['message_replied'][$row['id_pm']] = $row['is_read'] & 2;
1604
1605
				$row['labels'] = '';
1606
1607
				// Get the labels for this PM
1608
				$request2 = $smcFunc['db_query']('', '
1609
					SELECT id_label
1610
					FROM {db_prefix}pm_labeled_messages
1611
					WHERE id_pm = {int:current_pm}',
1612
					array(
1613
						'current_pm' => $row['id_pm'],
1614
					)
1615
				);
1616
1617
				while ($row2 = $smcFunc['db_fetch_assoc']($request2))
1618
				{
1619
					$l_id = $row2['id_label'];
1620
					if (isset($context['labels'][$l_id]))
1621
						$context['message_labels'][$row['id_pm']][$l_id] = array('id' => $l_id, 'name' => $context['labels'][$l_id]['name']);
1622
1623
					// Here we find the first label on a message - for linking to posts in results
1624
					if (!isset($context['first_label'][$row['id_pm']]) && $row['in_inbox'] != 1)
1625
						$context['first_label'][$row['id_pm']] = $l_id;
1626
				}
1627
1628
				$smcFunc['db_free_result']($request2);
1629
1630
				// Is this in the inbox as well?
1631
				if ($row['in_inbox'] == 1)
1632
				{
1633
					$context['message_labels'][$row['id_pm']][-1] = array('id' => -1, 'name' => $context['labels'][-1]['name']);
1634
				}
1635
1636
				$row['labels'] = $row['labels'] == '' ? array() : explode(',', $row['labels']);
1637
			}
1638
		}
1639
1640
		// Prepare the query for the callback!
1641
		$request = $smcFunc['db_query']('', '
1642
			SELECT pm.id_pm, pm.subject, pm.id_member_from, pm.body, pm.msgtime, pm.from_name
1643
			FROM {db_prefix}personal_messages AS pm
1644
			WHERE pm.id_pm IN ({array_int:message_list})
1645
			ORDER BY {raw:sort} {raw:sort_dir}
1646
			LIMIT {int:limit}',
1647
			array(
1648
				'message_list' => $foundMessages,
1649
				'limit' => count($foundMessages),
1650
				'sort' => $search_params['sort'],
1651
				'sort_dir' => $search_params['sort_dir'],
1652
			)
1653
		);
1654
		$counter = 0;
1655
		while ($row = $smcFunc['db_fetch_assoc']($request))
1656
		{
1657
			// If there's no message subject, use the default.
1658
			$row['subject'] = $row['subject'] == '' ? $txt['no_subject'] : $row['subject'];
1659
1660
			// Load this posters context info, if it ain't there then fill in the essentials...
1661
			if (!loadMemberContext($row['id_member_from'], true))
1662
			{
1663
				$memberContext[$row['id_member_from']]['name'] = $row['from_name'];
1664
				$memberContext[$row['id_member_from']]['id'] = 0;
1665
				$memberContext[$row['id_member_from']]['group'] = $txt['guest_title'];
1666
				$memberContext[$row['id_member_from']]['link'] = $row['from_name'];
1667
				$memberContext[$row['id_member_from']]['email'] = '';
1668
				$memberContext[$row['id_member_from']]['is_guest'] = true;
1669
			}
1670
1671
			// Censor anything we don't want to see...
1672
			censorText($row['body']);
1673
			censorText($row['subject']);
1674
1675
			// Parse out any BBC...
1676
			$row['body'] = parse_bbc($row['body'], true, 'pm' . $row['id_pm']);
1677
1678
			$href = $scripturl . '?action=pm;f=' . $context['folder'] . (isset($context['first_label'][$row['id_pm']]) ? ';l=' . $context['first_label'][$row['id_pm']] : '') . ';pmid=' . ($context['display_mode'] == 2 && isset($real_pm_ids[$head_pms[$row['id_pm']]]) ? $real_pm_ids[$head_pms[$row['id_pm']]] : $row['id_pm']) . '#msg' . $row['id_pm'];
1679
			$context['personal_messages'][] = array(
1680
				'id' => $row['id_pm'],
1681
				'member' => &$memberContext[$row['id_member_from']],
1682
				'subject' => $row['subject'],
1683
				'body' => $row['body'],
1684
				'time' => timeformat($row['msgtime']),
1685
				'recipients' => &$recipients[$row['id_pm']],
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $recipients does not seem to be defined for all execution paths leading up to this point.
Loading history...
1686
				'labels' => &$context['message_labels'][$row['id_pm']],
1687
				'fully_labeled' => count($context['message_labels'][$row['id_pm']]) == count($context['labels']),
1688
				'is_replied_to' => &$context['message_replied'][$row['id_pm']],
1689
				'href' => $href,
1690
				'link' => '<a href="' . $href . '">' . $row['subject'] . '</a>',
1691
				'counter' => ++$counter,
1692
			);
1693
		}
1694
		$smcFunc['db_free_result']($request);
1695
	}
1696
1697
	call_integration_hook('integrate_search_pm_context');
1698
1699
	// Finish off the context.
1700
	$context['page_title'] = $txt['pm_search_title'];
1701
	$context['sub_template'] = 'search_results';
1702
	$context['menu_data_' . $context['pm_menu_id']]['current_area'] = 'search';
1703
	$context['linktree'][] = array(
1704
		'url' => $scripturl . '?action=pm;sa=search',
1705
		'name' => $txt['pm_search_bar_title'],
1706
	);
1707
}
1708
1709
/**
1710
 * Send a new message?
1711
 */
1712
function MessagePost()
1713
{
1714
	global $txt, $sourcedir, $scripturl, $modSettings;
1715
	global $context, $smcFunc, $language, $user_info;
1716
1717
	isAllowedTo('pm_send');
1718
1719
	loadLanguage('PersonalMessage');
1720
	// Just in case it was loaded from somewhere else.
1721
	loadTemplate('PersonalMessage');
1722
	loadJavaScriptFile('PersonalMessage.js', array('defer' => false, 'minimize' => true), 'smf_pms');
1723
	loadJavaScriptFile('suggest.js', array('defer' => false, 'minimize' => true), 'smf_suggest');
1724
	$context['sub_template'] = 'send';
1725
1726
	// Extract out the spam settings - cause it's neat.
1727
	list ($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']);
1728
1729
	// Set the title...
1730
	$context['page_title'] = $txt['send_message'];
1731
1732
	$context['reply'] = isset($_REQUEST['pmsg']) || isset($_REQUEST['quote']);
1733
1734
	// Check whether we've gone over the limit of messages we can send per hour.
1735
	if (!empty($modSettings['pm_posts_per_hour']) && !allowedTo(array('admin_forum', 'moderate_forum', 'send_mail')) && $user_info['mod_cache']['bq'] == '0=1' && $user_info['mod_cache']['gq'] == '0=1')
1736
	{
1737
		// How many messages have they sent this last hour?
1738
		$request = $smcFunc['db_query']('', '
1739
			SELECT COUNT(pr.id_pm) AS post_count
1740
			FROM {db_prefix}personal_messages AS pm
1741
				INNER JOIN {db_prefix}pm_recipients AS pr ON (pr.id_pm = pm.id_pm)
1742
			WHERE pm.id_member_from = {int:current_member}
1743
				AND pm.msgtime > {int:msgtime}',
1744
			array(
1745
				'current_member' => $user_info['id'],
1746
				'msgtime' => time() - 3600,
1747
			)
1748
		);
1749
		list ($postCount) = $smcFunc['db_fetch_row']($request);
1750
		$smcFunc['db_free_result']($request);
1751
1752
		if (!empty($postCount) && $postCount >= $modSettings['pm_posts_per_hour'])
1753
			fatal_lang_error('pm_too_many_per_hour', true, array($modSettings['pm_posts_per_hour']));
1754
	}
1755
1756
	// Quoting/Replying to a message?
1757
	if (!empty($_REQUEST['pmsg']))
1758
	{
1759
		$pmsg = (int) $_REQUEST['pmsg'];
1760
1761
		// Make sure this is yours.
1762
		if (!isAccessiblePM($pmsg))
1763
			fatal_lang_error('no_access', false);
1764
1765
		// Work out whether this is one you've received?
1766
		$request = $smcFunc['db_query']('', '
1767
			SELECT
1768
				id_pm
1769
			FROM {db_prefix}pm_recipients
1770
			WHERE id_pm = {int:id_pm}
1771
				AND id_member = {int:current_member}
1772
			LIMIT 1',
1773
			array(
1774
				'current_member' => $user_info['id'],
1775
				'id_pm' => $pmsg,
1776
			)
1777
		);
1778
		$isReceived = $smcFunc['db_num_rows']($request) != 0;
1779
		$smcFunc['db_free_result']($request);
1780
1781
		// Get the quoted message (and make sure you're allowed to see this quote!).
1782
		$request = $smcFunc['db_query']('', '
1783
			SELECT
1784
				pm.id_pm, CASE WHEN pm.id_pm_head = {int:id_pm_head_empty} THEN pm.id_pm ELSE pm.id_pm_head END AS pm_head,
1785
				pm.body, pm.subject, pm.msgtime, mem.member_name, COALESCE(mem.id_member, 0) AS id_member,
1786
				COALESCE(mem.real_name, pm.from_name) AS real_name
1787
			FROM {db_prefix}personal_messages AS pm' . (!$isReceived ? '' : '
1788
				INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = {int:id_pm})') . '
1789
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from)
1790
			WHERE pm.id_pm = {int:id_pm}' . (!$isReceived ? '
1791
				AND pm.id_member_from = {int:current_member}' : '
1792
				AND pmr.id_member = {int:current_member}') . '
1793
			LIMIT 1',
1794
			array(
1795
				'current_member' => $user_info['id'],
1796
				'id_pm_head_empty' => 0,
1797
				'id_pm' => $pmsg,
1798
			)
1799
		);
1800
		if ($smcFunc['db_num_rows']($request) == 0)
1801
			fatal_lang_error('pm_not_yours', false);
1802
		$row_quoted = $smcFunc['db_fetch_assoc']($request);
1803
		$smcFunc['db_free_result']($request);
1804
1805
		// Censor the message.
1806
		censorText($row_quoted['subject']);
1807
		censorText($row_quoted['body']);
1808
1809
		// Add 'Re: ' to it....
1810
		if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix')))
1811
		{
1812
			if ($language === $user_info['language'])
1813
				$context['response_prefix'] = $txt['response_prefix'];
1814
			else
1815
			{
1816
				loadLanguage('index', $language, false);
1817
				$context['response_prefix'] = $txt['response_prefix'];
1818
				loadLanguage('index');
1819
			}
1820
			cache_put_data('response_prefix', $context['response_prefix'], 600);
1821
		}
1822
		$form_subject = $row_quoted['subject'];
1823
		if ($context['reply'] && trim($context['response_prefix']) != '' && $smcFunc['strpos']($form_subject, trim($context['response_prefix'])) !== 0)
1824
			$form_subject = $context['response_prefix'] . $form_subject;
1825
1826
		if (isset($_REQUEST['quote']))
1827
		{
1828
			// Remove any nested quotes and <br>...
1829
			$form_message = preg_replace('~<br ?/?' . '>~i', "\n", $row_quoted['body']);
1830
			if (!empty($modSettings['removeNestedQuotes']))
1831
				$form_message = preg_replace(array('~\n?\[quote.*?\].+?\[/quote\]\n?~is', '~^\n~', '~\[/quote\]~'), '', $form_message);
1832
			if (empty($row_quoted['id_member']))
1833
				$form_message = '[quote author=&quot;' . $row_quoted['real_name'] . '&quot;]' . "\n" . $form_message . "\n" . '[/quote]';
1834
			else
1835
				$form_message = '[quote author=' . $row_quoted['real_name'] . ' link=action=profile;u=' . $row_quoted['id_member'] . ' date=' . $row_quoted['msgtime'] . ']' . "\n" . $form_message . "\n" . '[/quote]';
1836
		}
1837
		else
1838
			$form_message = '';
1839
1840
		// Do the BBC thang on the message.
1841
		$row_quoted['body'] = parse_bbc($row_quoted['body'], true, 'pm' . $row_quoted['id_pm']);
1842
1843
		// Set up the quoted message array.
1844
		$context['quoted_message'] = array(
1845
			'id' => $row_quoted['id_pm'],
1846
			'pm_head' => $row_quoted['pm_head'],
1847
			'member' => array(
1848
				'name' => $row_quoted['real_name'],
1849
				'username' => $row_quoted['member_name'],
1850
				'id' => $row_quoted['id_member'],
1851
				'href' => !empty($row_quoted['id_member']) ? $scripturl . '?action=profile;u=' . $row_quoted['id_member'] : '',
1852
				'link' => !empty($row_quoted['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row_quoted['id_member'] . '">' . $row_quoted['real_name'] . '</a>' : $row_quoted['real_name'],
1853
			),
1854
			'subject' => $row_quoted['subject'],
1855
			'time' => timeformat($row_quoted['msgtime']),
1856
			'timestamp' => forum_time(true, $row_quoted['msgtime']),
1857
			'body' => $row_quoted['body']
1858
		);
1859
	}
1860
	else
1861
	{
1862
		$context['quoted_message'] = false;
1863
		$form_subject = '';
1864
		$form_message = '';
1865
	}
1866
1867
	$context['recipients'] = array(
1868
		'to' => array(),
1869
		'bcc' => array(),
1870
	);
1871
1872
	// Sending by ID?  Replying to all?  Fetch the real_name(s).
1873
	if (isset($_REQUEST['u']))
1874
	{
1875
		// If the user is replying to all, get all the other members this was sent to..
1876
		if ($_REQUEST['u'] == 'all' && isset($row_quoted))
1877
		{
1878
			// Firstly, to reply to all we clearly already have $row_quoted - so have the original member from.
1879
			if ($row_quoted['id_member'] != $user_info['id'])
1880
				$context['recipients']['to'][] = array(
1881
					'id' => $row_quoted['id_member'],
1882
					'name' => $smcFunc['htmlspecialchars']($row_quoted['real_name']),
1883
				);
1884
1885
			// Now to get the others.
1886
			$request = $smcFunc['db_query']('', '
1887
				SELECT mem.id_member, mem.real_name
1888
				FROM {db_prefix}pm_recipients AS pmr
1889
					INNER JOIN {db_prefix}members AS mem ON (mem.id_member = pmr.id_member)
1890
				WHERE pmr.id_pm = {int:id_pm}
1891
					AND pmr.id_member != {int:current_member}
1892
					AND pmr.bcc = {int:not_bcc}',
1893
				array(
1894
					'current_member' => $user_info['id'],
1895
					'id_pm' => $pmsg,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $pmsg does not seem to be defined for all execution paths leading up to this point.
Loading history...
1896
					'not_bcc' => 0,
1897
				)
1898
			);
1899
			while ($row = $smcFunc['db_fetch_assoc']($request))
1900
				$context['recipients']['to'][] = array(
1901
					'id' => $row['id_member'],
1902
					'name' => $row['real_name'],
1903
				);
1904
			$smcFunc['db_free_result']($request);
1905
		}
1906
		else
1907
		{
1908
			$_REQUEST['u'] = explode(',', $_REQUEST['u']);
1909
			foreach ($_REQUEST['u'] as $key => $uID)
1910
				$_REQUEST['u'][$key] = (int) $uID;
1911
1912
			$_REQUEST['u'] = array_unique($_REQUEST['u']);
1913
1914
			$request = $smcFunc['db_query']('', '
1915
				SELECT id_member, real_name
1916
				FROM {db_prefix}members
1917
				WHERE id_member IN ({array_int:member_list})
1918
				LIMIT {int:limit}',
1919
				array(
1920
					'member_list' => $_REQUEST['u'],
1921
					'limit' => count($_REQUEST['u']),
1922
				)
1923
			);
1924
			while ($row = $smcFunc['db_fetch_assoc']($request))
1925
				$context['recipients']['to'][] = array(
1926
					'id' => $row['id_member'],
1927
					'name' => $row['real_name'],
1928
				);
1929
			$smcFunc['db_free_result']($request);
1930
		}
1931
1932
		// Get a literal name list in case the user has JavaScript disabled.
1933
		$names = array();
1934
		foreach ($context['recipients']['to'] as $to)
1935
			$names[] = $to['name'];
1936
		$context['to_value'] = empty($names) ? '' : '&quot;' . implode('&quot;, &quot;', $names) . '&quot;';
1937
	}
1938
	else
1939
		$context['to_value'] = '';
1940
1941
	// Set the defaults...
1942
	$context['subject'] = $form_subject;
1943
	$context['message'] = str_replace(array('"', '<', '>', '&nbsp;'), array('&quot;', '&lt;', '&gt;', ' '), $form_message);
1944
	$context['post_error'] = array();
1945
1946
	// And build the link tree.
1947
	$context['linktree'][] = array(
1948
		'url' => $scripturl . '?action=pm;sa=send',
1949
		'name' => $txt['new_message']
1950
	);
1951
1952
	$modSettings['disable_wysiwyg'] = !empty($modSettings['disable_wysiwyg']) || empty($modSettings['enableBBC']);
1953
1954
	// Generate a list of drafts that they can load in to the editor
1955
	if (!empty($context['drafts_pm_save']))
1956
	{
1957
		require_once($sourcedir . '/Drafts.php');
1958
		$pm_seed = isset($_REQUEST['pmsg']) ? $_REQUEST['pmsg'] : (isset($_REQUEST['quote']) ? $_REQUEST['quote'] : 0);
1959
		ShowDrafts($user_info['id'], $pm_seed, 1);
1960
	}
1961
1962
	// Needed for the WYSIWYG editor.
1963
	require_once($sourcedir . '/Subs-Editor.php');
1964
1965
	// Now create the editor.
1966
	$editorOptions = array(
1967
		'id' => 'message',
1968
		'value' => $context['message'],
1969
		'height' => '250px',
1970
		'width' => '100%',
1971
		'labels' => array(
1972
			'post_button' => $txt['send_message'],
1973
		),
1974
		'preview_type' => 2,
1975
		'required' => true,
1976
	);
1977
	create_control_richedit($editorOptions);
1978
1979
	// Store the ID for old compatibility.
1980
	$context['post_box_name'] = $editorOptions['id'];
1981
1982
	$context['bcc_value'] = '';
1983
1984
	$context['require_verification'] = !$user_info['is_admin'] && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification'];
1985
	if ($context['require_verification'])
1986
	{
1987
		$verificationOptions = array(
1988
			'id' => 'pm',
1989
		);
1990
		$context['require_verification'] = create_control_verification($verificationOptions);
1991
		$context['visual_verification_id'] = $verificationOptions['id'];
1992
	}
1993
1994
	call_integration_hook('integrate_pm_post');
1995
1996
	// Register this form and get a sequence number in $context.
1997
	checkSubmitOnce('register');
1998
}
1999
2000
/**
2001
 * This function allows the user to view their PM drafts
2002
 */
2003
function MessageDrafts()
2004
{
2005
	global $sourcedir, $user_info;
2006
2007
	// validate with loadMemberData()
2008
	$memberResult = loadMemberData($user_info['id'], false);
2009
	if (!$memberResult)
0 ignored issues
show
Bug Best Practice introduced by
The expression $memberResult of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2010
		fatal_lang_error('not_a_user', false);
2011
	list ($memID) = $memberResult;
2012
2013
	// drafts is where the functions reside
2014
	require_once($sourcedir . '/Drafts.php');
2015
	showPMDrafts($memID);
2016
}
2017
2018
/**
2019
 * An error in the message...
2020
 *
2021
 * @param array $error_types An array of strings indicating which type of errors occurred
2022
 * @param array $named_recipients
2023
 * @param $recipient_ids
2024
 */
2025
function messagePostError($error_types, $named_recipients, $recipient_ids = array())
2026
{
2027
	global $txt, $context, $scripturl, $modSettings;
2028
	global $smcFunc, $user_info, $sourcedir;
2029
2030
	if (!isset($_REQUEST['xml']))
2031
	{
2032
		$context['menu_data_' . $context['pm_menu_id']]['current_area'] = 'send';
2033
		$context['sub_template'] = 'send';
2034
		loadJavaScriptFile('PersonalMessage.js', array('defer' => false, 'minimize' => true), 'smf_pms');
2035
		loadJavaScriptFile('suggest.js', array('defer' => false, 'minimize' => true), 'smf_suggest');
2036
	}
2037
	else
2038
		$context['sub_template'] = 'pm';
2039
2040
	$context['page_title'] = $txt['send_message'];
2041
2042
	// Got some known members?
2043
	$context['recipients'] = array(
2044
		'to' => array(),
2045
		'bcc' => array(),
2046
	);
2047
	if (!empty($recipient_ids['to']) || !empty($recipient_ids['bcc']))
2048
	{
2049
		$allRecipients = array_merge($recipient_ids['to'], $recipient_ids['bcc']);
2050
2051
		$request = $smcFunc['db_query']('', '
2052
			SELECT id_member, real_name
2053
			FROM {db_prefix}members
2054
			WHERE id_member IN ({array_int:member_list})',
2055
			array(
2056
				'member_list' => $allRecipients,
2057
			)
2058
		);
2059
		while ($row = $smcFunc['db_fetch_assoc']($request))
2060
		{
2061
			$recipientType = in_array($row['id_member'], $recipient_ids['bcc']) ? 'bcc' : 'to';
2062
			$context['recipients'][$recipientType][] = array(
2063
				'id' => $row['id_member'],
2064
				'name' => $row['real_name'],
2065
			);
2066
		}
2067
		$smcFunc['db_free_result']($request);
2068
	}
2069
2070
	// Set everything up like before....
2071
	$context['subject'] = isset($_REQUEST['subject']) ? $smcFunc['htmlspecialchars']($_REQUEST['subject']) : '';
2072
	$context['message'] = isset($_REQUEST['message']) ? str_replace(array('  '), array('&nbsp; '), $smcFunc['htmlspecialchars']($_REQUEST['message'])) : '';
2073
	$context['reply'] = !empty($_REQUEST['replied_to']);
2074
2075
	if ($context['reply'])
2076
	{
2077
		$_REQUEST['replied_to'] = (int) $_REQUEST['replied_to'];
2078
2079
		$request = $smcFunc['db_query']('', '
2080
			SELECT
2081
				pm.id_pm, CASE WHEN pm.id_pm_head = {int:no_id_pm_head} THEN pm.id_pm ELSE pm.id_pm_head END AS pm_head,
2082
				pm.body, pm.subject, pm.msgtime, mem.member_name, COALESCE(mem.id_member, 0) AS id_member,
2083
				COALESCE(mem.real_name, pm.from_name) AS real_name
2084
			FROM {db_prefix}personal_messages AS pm' . ($context['folder'] == 'sent' ? '' : '
2085
				INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = {int:replied_to})') . '
2086
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from)
2087
			WHERE pm.id_pm = {int:replied_to}' . ($context['folder'] == 'sent' ? '
2088
				AND pm.id_member_from = {int:current_member}' : '
2089
				AND pmr.id_member = {int:current_member}') . '
2090
			LIMIT 1',
2091
			array(
2092
				'current_member' => $user_info['id'],
2093
				'no_id_pm_head' => 0,
2094
				'replied_to' => $_REQUEST['replied_to'],
2095
			)
2096
		);
2097
		if ($smcFunc['db_num_rows']($request) == 0)
2098
		{
2099
			if (!isset($_REQUEST['xml']))
2100
				fatal_lang_error('pm_not_yours', false);
2101
			else
2102
				$error_types[] = 'pm_not_yours';
2103
		}
2104
		$row_quoted = $smcFunc['db_fetch_assoc']($request);
2105
		$smcFunc['db_free_result']($request);
2106
2107
		censorText($row_quoted['subject']);
2108
		censorText($row_quoted['body']);
2109
2110
		$context['quoted_message'] = array(
2111
			'id' => $row_quoted['id_pm'],
2112
			'pm_head' => $row_quoted['pm_head'],
2113
			'member' => array(
2114
				'name' => $row_quoted['real_name'],
2115
				'username' => $row_quoted['member_name'],
2116
				'id' => $row_quoted['id_member'],
2117
				'href' => !empty($row_quoted['id_member']) ? $scripturl . '?action=profile;u=' . $row_quoted['id_member'] : '',
2118
				'link' => !empty($row_quoted['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row_quoted['id_member'] . '">' . $row_quoted['real_name'] . '</a>' : $row_quoted['real_name'],
2119
			),
2120
			'subject' => $row_quoted['subject'],
2121
			'time' => timeformat($row_quoted['msgtime']),
2122
			'timestamp' => forum_time(true, $row_quoted['msgtime']),
2123
			'body' => parse_bbc($row_quoted['body'], true, 'pm' . $row_quoted['id_pm']),
2124
		);
2125
	}
2126
2127
	// Build the link tree....
2128
	$context['linktree'][] = array(
2129
		'url' => $scripturl . '?action=pm;sa=send',
2130
		'name' => $txt['new_message']
2131
	);
2132
2133
	// Set each of the errors for the template.
2134
	loadLanguage('Errors');
2135
2136
	$context['error_type'] = 'minor';
2137
2138
	$context['post_error'] = array(
2139
		'messages' => array(),
2140
		// @todo error handling: maybe fatal errors can be error_type => serious
2141
		'error_type' => '',
2142
	);
2143
2144
	foreach ($error_types as $error_type)
2145
	{
2146
		$context['post_error'][$error_type] = true;
2147
		if (isset($txt['error_' . $error_type]))
2148
		{
2149
			if ($error_type == 'long_message')
2150
				$txt['error_' . $error_type] = sprintf($txt['error_' . $error_type], $modSettings['max_messageLength']);
2151
			$context['post_error']['messages'][] = $txt['error_' . $error_type];
2152
		}
2153
2154
		// If it's not a minor error flag it as such.
2155
		if (!in_array($error_type, array('new_reply', 'not_approved', 'new_replies', 'old_topic', 'need_qr_verification', 'no_subject')))
2156
			$context['error_type'] = 'serious';
2157
	}
2158
2159
	// We need to load the editor once more.
2160
	require_once($sourcedir . '/Subs-Editor.php');
2161
2162
	// Create it...
2163
	$editorOptions = array(
2164
		'id' => 'message',
2165
		'value' => $context['message'],
2166
		'width' => '90%',
2167
		'height' => '250px',
2168
		'labels' => array(
2169
			'post_button' => $txt['send_message'],
2170
		),
2171
		'preview_type' => 2,
2172
	);
2173
	create_control_richedit($editorOptions);
2174
2175
	// ... and store the ID again...
2176
	$context['post_box_name'] = $editorOptions['id'];
2177
2178
	// Check whether we need to show the code again.
2179
	$context['require_verification'] = !$user_info['is_admin'] && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification'];
2180
	if ($context['require_verification'] && !isset($_REQUEST['xml']))
2181
	{
2182
		require_once($sourcedir . '/Subs-Editor.php');
2183
		$verificationOptions = array(
2184
			'id' => 'pm',
2185
		);
2186
		$context['require_verification'] = create_control_verification($verificationOptions);
2187
		$context['visual_verification_id'] = $verificationOptions['id'];
2188
	}
2189
2190
	$context['to_value'] = empty($named_recipients['to']) ? '' : '&quot;' . implode('&quot;, &quot;', $named_recipients['to']) . '&quot;';
2191
	$context['bcc_value'] = empty($named_recipients['bcc']) ? '' : '&quot;' . implode('&quot;, &quot;', $named_recipients['bcc']) . '&quot;';
2192
2193
	call_integration_hook('integrate_pm_error');
2194
2195
	// No check for the previous submission is needed.
2196
	checkSubmitOnce('free');
2197
2198
	// Acquire a new form sequence number.
2199
	checkSubmitOnce('register');
2200
}
2201
2202
/**
2203
 * Send it!
2204
 */
2205
function MessagePost2()
2206
{
2207
	global $txt, $context, $sourcedir;
2208
	global $user_info, $modSettings, $smcFunc;
2209
2210
	isAllowedTo('pm_send');
2211
	require_once($sourcedir . '/Subs-Auth.php');
2212
2213
	// PM Drafts enabled and needed?
2214
	if ($context['drafts_pm_save'] && (isset($_POST['save_draft']) || isset($_POST['id_pm_draft'])))
2215
		require_once($sourcedir . '/Drafts.php');
2216
2217
	loadLanguage('PersonalMessage', '', false);
2218
2219
	// Extract out the spam settings - it saves database space!
2220
	list ($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']);
2221
2222
	// Initialize the errors we're about to make.
2223
	$post_errors = array();
2224
2225
	// Check whether we've gone over the limit of messages we can send per hour - fatal error if fails!
2226
	if (!empty($modSettings['pm_posts_per_hour']) && !allowedTo(array('admin_forum', 'moderate_forum', 'send_mail')) && $user_info['mod_cache']['bq'] == '0=1' && $user_info['mod_cache']['gq'] == '0=1')
2227
	{
2228
		// How many have they sent this last hour?
2229
		$request = $smcFunc['db_query']('', '
2230
			SELECT COUNT(pr.id_pm) AS post_count
2231
			FROM {db_prefix}personal_messages AS pm
2232
				INNER JOIN {db_prefix}pm_recipients AS pr ON (pr.id_pm = pm.id_pm)
2233
			WHERE pm.id_member_from = {int:current_member}
2234
				AND pm.msgtime > {int:msgtime}',
2235
			array(
2236
				'current_member' => $user_info['id'],
2237
				'msgtime' => time() - 3600,
2238
			)
2239
		);
2240
		list ($postCount) = $smcFunc['db_fetch_row']($request);
2241
		$smcFunc['db_free_result']($request);
2242
2243
		if (!empty($postCount) && $postCount >= $modSettings['pm_posts_per_hour'])
2244
		{
2245
			if (!isset($_REQUEST['xml']))
2246
				fatal_lang_error('pm_too_many_per_hour', true, array($modSettings['pm_posts_per_hour']));
2247
			else
2248
				$post_errors[] = 'pm_too_many_per_hour';
2249
		}
2250
	}
2251
2252
	// If your session timed out, show an error, but do allow to re-submit.
2253
	if (!isset($_REQUEST['xml']) && checkSession('post', '', false) != '')
2254
		$post_errors[] = 'session_timeout';
2255
2256
	$_REQUEST['subject'] = isset($_REQUEST['subject']) ? trim($_REQUEST['subject']) : '';
2257
	$_REQUEST['to'] = empty($_POST['to']) ? (empty($_GET['to']) ? '' : $_GET['to']) : $_POST['to'];
2258
	$_REQUEST['bcc'] = empty($_POST['bcc']) ? (empty($_GET['bcc']) ? '' : $_GET['bcc']) : $_POST['bcc'];
2259
2260
	// Route the input from the 'u' parameter to the 'to'-list.
2261
	if (!empty($_POST['u']))
2262
		$_POST['recipient_to'] = explode(',', $_POST['u']);
2263
2264
	// Construct the list of recipients.
2265
	$recipientList = array();
2266
	$namedRecipientList = array();
2267
	$namesNotFound = array();
2268
	foreach (array('to', 'bcc') as $recipientType)
2269
	{
2270
		// First, let's see if there's user ID's given.
2271
		$recipientList[$recipientType] = array();
2272
		if (!empty($_POST['recipient_' . $recipientType]) && is_array($_POST['recipient_' . $recipientType]))
2273
		{
2274
			foreach ($_POST['recipient_' . $recipientType] as $recipient)
2275
				$recipientList[$recipientType][] = (int) $recipient;
2276
		}
2277
2278
		// Are there also literal names set?
2279
		if (!empty($_REQUEST[$recipientType]))
2280
		{
2281
			// We're going to take out the "s anyway ;).
2282
			$recipientString = strtr($_REQUEST[$recipientType], array('\\"' => '"'));
2283
2284
			preg_match_all('~"([^"]+)"~', $recipientString, $matches);
2285
			$namedRecipientList[$recipientType] = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $recipientString))));
2286
2287
			foreach ($namedRecipientList[$recipientType] as $index => $recipient)
2288
			{
2289
				if (strlen(trim($recipient)) > 0)
2290
					$namedRecipientList[$recipientType][$index] = $smcFunc['htmlspecialchars']($smcFunc['strtolower'](trim($recipient)));
2291
				else
2292
					unset($namedRecipientList[$recipientType][$index]);
2293
			}
2294
2295
			if (!empty($namedRecipientList[$recipientType]))
2296
			{
2297
				$foundMembers = findMembers($namedRecipientList[$recipientType]);
2298
2299
				// Assume all are not found, until proven otherwise.
2300
				$namesNotFound[$recipientType] = $namedRecipientList[$recipientType];
2301
2302
				foreach ($foundMembers as $member)
2303
				{
2304
					$testNames = array(
2305
						$smcFunc['strtolower']($member['username']),
2306
						$smcFunc['strtolower']($member['name']),
2307
						$smcFunc['strtolower']($member['email']),
2308
					);
2309
2310
					if (count(array_intersect($testNames, $namedRecipientList[$recipientType])) !== 0)
2311
					{
2312
						$recipientList[$recipientType][] = $member['id'];
2313
2314
						// Get rid of this username, since we found it.
2315
						$namesNotFound[$recipientType] = array_diff($namesNotFound[$recipientType], $testNames);
2316
					}
2317
				}
2318
			}
2319
		}
2320
2321
		// Selected a recipient to be deleted? Remove them now.
2322
		if (!empty($_POST['delete_recipient']))
2323
			$recipientList[$recipientType] = array_diff($recipientList[$recipientType], array((int) $_POST['delete_recipient']));
2324
2325
		// Make sure we don't include the same name twice
2326
		$recipientList[$recipientType] = array_unique($recipientList[$recipientType]);
2327
	}
2328
2329
	// Are we changing the recipients some how?
2330
	$is_recipient_change = !empty($_POST['delete_recipient']) || !empty($_POST['to_submit']) || !empty($_POST['bcc_submit']);
2331
2332
	// Check if there's at least one recipient.
2333
	if (empty($recipientList['to']) && empty($recipientList['bcc']))
2334
		$post_errors[] = 'no_to';
2335
2336
	// Make sure that we remove the members who did get it from the screen.
2337
	if (!$is_recipient_change)
2338
	{
2339
		foreach ($recipientList as $recipientType => $dummy)
2340
		{
2341
			if (!empty($namesNotFound[$recipientType]))
2342
			{
2343
				$post_errors[] = 'bad_' . $recipientType;
2344
2345
				// Since we already have a post error, remove the previous one.
2346
				$post_errors = array_diff($post_errors, array('no_to'));
2347
2348
				foreach ($namesNotFound[$recipientType] as $name)
2349
					$context['send_log']['failed'][] = sprintf($txt['pm_error_user_not_found'], $name);
2350
			}
2351
		}
2352
	}
2353
2354
	// Did they make any mistakes?
2355
	if ($_REQUEST['subject'] == '')
2356
		$post_errors[] = 'no_subject';
2357
	if (!isset($_REQUEST['message']) || $_REQUEST['message'] == '')
2358
		$post_errors[] = 'no_message';
2359
	elseif (!empty($modSettings['max_messageLength']) && $smcFunc['strlen']($_REQUEST['message']) > $modSettings['max_messageLength'])
2360
		$post_errors[] = 'long_message';
2361
	else
2362
	{
2363
		// Preparse the message.
2364
		$message = $_REQUEST['message'];
2365
		preparsecode($message);
2366
2367
		// Make sure there's still some content left without the tags.
2368
		if ($smcFunc['htmltrim'](strip_tags(parse_bbc($smcFunc['htmlspecialchars']($message, ENT_QUOTES), false), '<img>')) === '' && (!allowedTo('admin_forum') || strpos($message, '[html]') === false))
2369
			$post_errors[] = 'no_message';
2370
	}
2371
2372
	// Wrong verification code?
2373
	if (!$user_info['is_admin'] && !isset($_REQUEST['xml']) && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification'])
2374
	{
2375
		require_once($sourcedir . '/Subs-Editor.php');
2376
		$verificationOptions = array(
2377
			'id' => 'pm',
2378
		);
2379
		$context['require_verification'] = create_control_verification($verificationOptions, true);
2380
2381
		if (is_array($context['require_verification']))
2382
			$post_errors = array_merge($post_errors, $context['require_verification']);
2383
	}
2384
2385
	// If they did, give a chance to make ammends.
2386
	if (!empty($post_errors) && !$is_recipient_change && !isset($_REQUEST['preview']) && !isset($_REQUEST['xml']))
2387
		return messagePostError($post_errors, $namedRecipientList, $recipientList);
0 ignored issues
show
Bug introduced by
Are you sure the usage of messagePostError($post_e...ntList, $recipientList) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
2388
2389
	// Want to take a second glance before you send?
2390
	if (isset($_REQUEST['preview']))
2391
	{
2392
		// Set everything up to be displayed.
2393
		$context['preview_subject'] = $smcFunc['htmlspecialchars']($_REQUEST['subject']);
2394
		$context['preview_message'] = $smcFunc['htmlspecialchars']($_REQUEST['message'], ENT_QUOTES);
2395
		preparsecode($context['preview_message'], true);
2396
2397
		// Parse out the BBC if it is enabled.
2398
		$context['preview_message'] = parse_bbc($context['preview_message']);
2399
2400
		// Censor, as always.
2401
		censorText($context['preview_subject']);
2402
		censorText($context['preview_message']);
2403
2404
		// Set a descriptive title.
2405
		$context['page_title'] = $txt['preview'] . ' - ' . $context['preview_subject'];
2406
2407
		// Pretend they messed up but don't ignore if they really did :P.
2408
		return messagePostError($post_errors, $namedRecipientList, $recipientList);
0 ignored issues
show
Bug introduced by
Are you sure the usage of messagePostError($post_e...ntList, $recipientList) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
2409
	}
2410
2411
	// Adding a recipient cause javascript ain't working?
2412
	elseif ($is_recipient_change)
2413
	{
2414
		// Maybe we couldn't find one?
2415
		foreach ($namesNotFound as $recipientType => $names)
2416
		{
2417
			$post_errors[] = 'bad_' . $recipientType;
2418
			foreach ($names as $name)
2419
				$context['send_log']['failed'][] = sprintf($txt['pm_error_user_not_found'], $name);
2420
		}
2421
2422
		return messagePostError(array(), $namedRecipientList, $recipientList);
0 ignored issues
show
Bug introduced by
Are you sure the usage of messagePostError(array()...ntList, $recipientList) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
2423
	}
2424
2425
	// Want to save this as a draft and think about it some more?
2426
	if ($context['drafts_pm_save'] && isset($_POST['save_draft']))
2427
	{
2428
		SavePMDraft($post_errors, $recipientList);
2429
		return messagePostError($post_errors, $namedRecipientList, $recipientList);
0 ignored issues
show
Bug introduced by
Are you sure the usage of messagePostError($post_e...ntList, $recipientList) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
2430
	}
2431
2432
	// Before we send the PM, let's make sure we don't have an abuse of numbers.
2433
	elseif (!empty($modSettings['max_pm_recipients']) && count($recipientList['to']) + count($recipientList['bcc']) > $modSettings['max_pm_recipients'] && !allowedTo(array('moderate_forum', 'send_mail', 'admin_forum')))
2434
	{
2435
		$context['send_log'] = array(
2436
			'sent' => array(),
2437
			'failed' => array(sprintf($txt['pm_too_many_recipients'], $modSettings['max_pm_recipients'])),
2438
		);
2439
		return messagePostError($post_errors, $namedRecipientList, $recipientList);
0 ignored issues
show
Bug introduced by
Are you sure the usage of messagePostError($post_e...ntList, $recipientList) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
2440
	}
2441
2442
	// Protect from message spamming.
2443
	spamProtection('pm');
2444
2445
	// Prevent double submission of this form.
2446
	checkSubmitOnce('check');
2447
2448
	// Do the actual sending of the PM.
2449
	if (!empty($recipientList['to']) || !empty($recipientList['bcc']))
2450
		$context['send_log'] = sendpm($recipientList, $_REQUEST['subject'], $_REQUEST['message'], true, null, !empty($_REQUEST['pm_head']) ? (int) $_REQUEST['pm_head'] : 0);
2451
	else
2452
		$context['send_log'] = array(
2453
			'sent' => array(),
2454
			'failed' => array()
2455
		);
2456
2457
	// Mark the message as "replied to".
2458
	if (!empty($context['send_log']['sent']) && !empty($_REQUEST['replied_to']) && isset($_REQUEST['f']) && $_REQUEST['f'] == 'inbox')
2459
	{
2460
		$smcFunc['db_query']('', '
2461
			UPDATE {db_prefix}pm_recipients
2462
			SET is_read = is_read | 2
2463
			WHERE id_pm = {int:replied_to}
2464
				AND id_member = {int:current_member}',
2465
			array(
2466
				'current_member' => $user_info['id'],
2467
				'replied_to' => (int) $_REQUEST['replied_to'],
2468
			)
2469
		);
2470
	}
2471
2472
	// If one or more of the recipient were invalid, go back to the post screen with the failed usernames.
2473
	if (!empty($context['send_log']['failed']))
2474
		return messagePostError($post_errors, $namesNotFound, array(
0 ignored issues
show
Bug introduced by
Are you sure the usage of messagePostError($post_e...send_log']['failed']))) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
2475
			'to' => array_intersect($recipientList['to'], $context['send_log']['failed']),
2476
			'bcc' => array_intersect($recipientList['bcc'], $context['send_log']['failed'])
2477
		));
2478
2479
	// Message sent successfully?
2480
	if (!empty($context['send_log']) && empty($context['send_log']['failed']))
2481
	{
2482
		$context['current_label_redirect'] = $context['current_label_redirect'] . ';done=sent';
2483
2484
		// If we had a PM draft for this one, then its time to remove it since it was just sent
2485
		if ($context['drafts_pm_save'] && !empty($_POST['id_pm_draft']))
2486
			DeleteDraft($_POST['id_pm_draft']);
2487
	}
2488
2489
	// Go back to the where they sent from, if possible...
2490
	redirectexit($context['current_label_redirect']);
2491
}
2492
/**
2493
 * This function performs all additional stuff...
2494
 */
2495
function MessageActionsApply()
2496
{
2497
	global $context, $user_info, $options, $smcFunc;
2498
2499
	checkSession('request');
2500
2501
	if (isset($_REQUEST['del_selected']))
2502
		$_REQUEST['pm_action'] = 'delete';
2503
2504
	if (isset($_REQUEST['pm_action']) && $_REQUEST['pm_action'] != '' && !empty($_REQUEST['pms']) && is_array($_REQUEST['pms']))
2505
	{
2506
		foreach ($_REQUEST['pms'] as $pm)
2507
			$_REQUEST['pm_actions'][(int) $pm] = $_REQUEST['pm_action'];
2508
	}
2509
2510
	if (empty($_REQUEST['pm_actions']))
2511
		redirectexit($context['current_label_redirect']);
2512
2513
	// If we are in conversation, we may need to apply this to every message in the conversation.
2514
	if ($context['display_mode'] == 2 && isset($_REQUEST['conversation']))
2515
	{
2516
		$id_pms = array();
2517
		foreach ($_REQUEST['pm_actions'] as $pm => $dummy)
2518
			$id_pms[] = (int) $pm;
2519
2520
		$request = $smcFunc['db_query']('', '
2521
			SELECT id_pm_head, id_pm
2522
			FROM {db_prefix}personal_messages
2523
			WHERE id_pm IN ({array_int:id_pms})',
2524
			array(
2525
				'id_pms' => $id_pms,
2526
			)
2527
		);
2528
		$pm_heads = array();
2529
		while ($row = $smcFunc['db_fetch_assoc']($request))
2530
			$pm_heads[$row['id_pm_head']] = $row['id_pm'];
2531
		$smcFunc['db_free_result']($request);
2532
2533
		$request = $smcFunc['db_query']('', '
2534
			SELECT id_pm, id_pm_head
2535
			FROM {db_prefix}personal_messages
2536
			WHERE id_pm_head IN ({array_int:pm_heads})',
2537
			array(
2538
				'pm_heads' => array_keys($pm_heads),
2539
			)
2540
		);
2541
		// Copy the action from the single to PM to the others.
2542
		while ($row = $smcFunc['db_fetch_assoc']($request))
2543
		{
2544
			if (isset($pm_heads[$row['id_pm_head']]) && isset($_REQUEST['pm_actions'][$pm_heads[$row['id_pm_head']]]))
2545
				$_REQUEST['pm_actions'][$row['id_pm']] = $_REQUEST['pm_actions'][$pm_heads[$row['id_pm_head']]];
2546
		}
2547
		$smcFunc['db_free_result']($request);
2548
	}
2549
2550
	$to_delete = array();
2551
	$to_label = array();
2552
	$label_type = array();
2553
	$labels = array();
2554
	foreach ($_REQUEST['pm_actions'] as $pm => $action)
2555
	{
2556
		if ($action === 'delete')
2557
			$to_delete[] = (int) $pm;
2558
		else
2559
		{
2560
			if (substr($action, 0, 4) == 'add_')
2561
			{
2562
				$type = 'add';
2563
				$action = substr($action, 4);
2564
			}
2565
			elseif (substr($action, 0, 4) == 'rem_')
2566
			{
2567
				$type = 'rem';
2568
				$action = substr($action, 4);
2569
			}
2570
			else
2571
				$type = 'unk';
2572
2573
			if ($action == '-1' || (int) $action > 0)
2574
			{
2575
				$to_label[(int) $pm] = (int) $action;
2576
				$label_type[(int) $pm] = $type;
2577
			}
2578
		}
2579
	}
2580
2581
	// Deleting, it looks like?
2582
	if (!empty($to_delete))
2583
		deleteMessages($to_delete, $context['display_mode'] == 2 ? null : $context['folder']);
2584
2585
	// Are we labeling anything?
2586
	if (!empty($to_label) && $context['folder'] == 'inbox')
2587
	{
2588
		// Are we dealing with conversation view? If so, get all the messages in each conversation
2589
		if ($context['display_mode'] == 2)
2590
		{
2591
			$get_pms = $smcFunc['db_query']('', '
2592
				SELECT pm.id_pm_head, pm.id_pm
2593
				FROM {db_prefix}personal_messages AS pm
2594
					INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)
2595
				WHERE pm.id_pm_head IN ({array_int:head_pms})
2596
					AND pm.id_pm NOT IN ({array_int:head_pms})
2597
					AND pmr.id_member = {int:current_member}',
2598
				array(
2599
					'head_pms' => array_keys($to_label),
2600
					'current_member' => $user_info['id'],
2601
				)
2602
			);
2603
2604
			while ($other_pms = $smcFunc['db_fetch_assoc']($get_pms))
2605
			{
2606
				$to_label[$other_pms['id_pm']] = $to_label[$other_pms['id_pm_head']];
2607
			}
2608
2609
			$smcFunc['db_free_result']($get_pms);
2610
		}
2611
2612
		// Get information about each message...
2613
		$request = $smcFunc['db_query']('', '
2614
			SELECT id_pm, in_inbox
2615
			FROM {db_prefix}pm_recipients
2616
			WHERE id_member = {int:current_member}
2617
				AND id_pm IN ({array_int:to_label})
2618
			LIMIT ' . count($to_label),
2619
			array(
2620
				'current_member' => $user_info['id'],
2621
				'to_label' => array_keys($to_label),
2622
			)
2623
		);
2624
2625
		while ($row = $smcFunc['db_fetch_assoc']($request))
2626
		{
2627
			// Get the labels as well, but only if we're not dealing with the inbox
2628
			if ($to_label[$row['id_pm']] != '-1')
2629
			{
2630
				// The JOIN here ensures we only get labels that this user has applied to this PM
2631
				$request2 = $smcFunc['db_query']('', '
2632
					SELECT l.id_label, pml.id_pm
2633
					FROM {db_prefix}pm_labels AS l
2634
						INNER JOIN {db_prefix}pm_labeled_messages AS pml ON (pml.id_label = l.id_label)
2635
					WHERE l.id_member = {int:current_member}
2636
						AND pml.id_pm = {int:current_pm}',
2637
					array(
2638
						'current_member' => $user_info['id'],
2639
						'current_pm' => $row['id_pm'],
2640
					)
2641
				);
2642
2643
				while ($row2 = $smcFunc['db_fetch_assoc']($request2))
2644
				{
2645
					$labels[$row2['id_label']] = $row2['id_label'];
2646
				}
2647
2648
				$smcFunc['db_free_result']($request2);
2649
			}
2650
			elseif ($type == 'rem')
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $type seems to be defined by a foreach iteration on line 2554. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
2651
			{
2652
				// If we're removing from the inbox, see if we have at least one other label.
2653
				// This query is faster than the one above
2654
				$request2 = $smcFunc['db_query']('', '
2655
					SELECT COUNT(l.id_label)
2656
					FROM {db_prefix}pm_labels AS l
2657
						INNER JOIN {db_prefix}pm_labeled_messages AS pml ON (pml.id_label = l.id_label)
2658
					WHERE l.id_member = {int:current_member}
2659
						AND pml.id_pm = {int:current_pm}',
2660
					array(
2661
						'current_member' => $user_info['id'],
2662
						'current_pm' => $row['id_pm'],
2663
					)
2664
				);
2665
2666
				// How many labels do you have?
2667
				list ($num_labels) = $smcFunc['db_fetch_assoc']($request2);
2668
2669
				if ($num_labels > 0);
2670
					$context['can_remove_inbox'] = true;
2671
2672
				$smcFunc['db_free_result']($request2);
2673
			}
2674
2675
			// Use this to determine what to do later on...
2676
			$original_labels = $labels;
2677
2678
			// Ignore inbox for now - we'll deal with it later
2679
			if ($to_label[$row['id_pm']] != '-1')
2680
			{
2681
				// If this label is in the list and we're not adding it, remove it
2682
				if (array_key_exists($to_label[$row['id_pm']], $labels) && $type !== 'add')
2683
					unset($labels[$to_label[$row['id_pm']]]);
2684
				else if ($type !== 'rem')
2685
					$labels[$to_label[$row['id_pm']]] = $to_label[$row['id_pm']];
2686
			}
2687
2688
			// Removing all labels or just removing the inbox label
2689
			if ($type == 'rem' && empty($labels))
2690
				$in_inbox = (empty($context['can_remove_inbox']) ? 1 : 0);
2691
			// Adding new labels, but removing inbox and applying new ones
2692
			elseif ($type == 'add' && !empty($options['pm_remove_inbox_label']) && !empty($labels))
2693
				$in_inbox = 0;
2694
			// Just adding it to the inbox
2695
			else
2696
				$in_inbox = 1;
2697
2698
			// Are we adding it to or removing it from the inbox?
2699
			if ($in_inbox != $row['in_inbox'])
2700
			{
2701
				$smcFunc['db_query']('', '
2702
					UPDATE {db_prefix}pm_recipients
2703
					SET in_inbox = {int:in_inbox}
2704
					WHERE id_pm = {int:id_pm}
2705
						AND id_member = {int:current_member}',
2706
					array(
2707
						'current_member' => $user_info['id'],
2708
						'id_pm' => $row['id_pm'],
2709
						'in_inbox' => $in_inbox,
2710
					)
2711
				);
2712
			}
2713
2714
			// Which labels do we not want now?
2715
			$labels_to_remove = array_diff($original_labels, $labels);
2716
2717
			// Don't apply it if it's already applied
2718
			$labels_to_apply = array_diff($labels, $original_labels);
2719
2720
			// Remove labels
2721
			if (!empty($labels_to_remove))
2722
			{
2723
				$smcFunc['db_query']('', '
2724
					DELETE FROM {db_prefix}pm_labeled_messages
2725
					WHERE id_pm = {int:current_pm}
2726
						AND id_label IN ({array_int:labels_to_remove})',
2727
					array(
2728
						'current_pm' => $row['id_pm'],
2729
						'labels_to_remove' => $labels_to_remove,
2730
					)
2731
				);
2732
			}
2733
2734
			// Add new ones
2735
			if (!empty($labels_to_apply))
2736
			{
2737
				$inserts = array();
2738
				foreach ($labels_to_apply as $label)
2739
					$inserts[] = array($row['id_pm'], $label);
2740
2741
				$smcFunc['db_insert']('',
2742
					'{db_prefix}pm_labeled_messages',
2743
					array('id_pm' => 'int', 'id_label' => 'int'),
2744
					$inserts,
2745
					array()
2746
				);
2747
			}
2748
		}
2749
		$smcFunc['db_free_result']($request);
2750
	}
2751
2752
	// Back to the folder.
2753
	$_SESSION['pm_selected'] = array_keys($to_label);
2754
	redirectexit($context['current_label_redirect'] . (count($to_label) == 1 ? '#msg' . $_SESSION['pm_selected'][0] : ''), count($to_label) == 1 && isBrowser('ie'));
2755
}
2756
2757
/**
2758
 * Are you sure you want to PERMANENTLY (mostly) delete ALL your messages?
2759
 */
2760
function MessageKillAllQuery()
2761
{
2762
	global $txt, $context;
2763
2764
	// Only have to set up the template....
2765
	$context['sub_template'] = 'ask_delete';
2766
	$context['page_title'] = $txt['delete_all'];
2767
	$context['delete_all'] = $_REQUEST['f'] == 'all';
2768
2769
	// And set the folder name...
2770
	$txt['delete_all'] = str_replace('PMBOX', $context['folder'] != 'sent' ? $txt['inbox'] : $txt['sent_items'], $txt['delete_all']);
2771
}
2772
2773
/**
2774
 * Delete ALL the messages!
2775
 */
2776
function MessageKillAll()
2777
{
2778
	global $context;
2779
2780
	checkSession('get');
2781
2782
	// If all then delete all messages the user has.
2783
	if ($_REQUEST['f'] == 'all')
2784
		deleteMessages(null, null);
2785
	// Otherwise just the selected folder.
2786
	else
2787
		deleteMessages(null, $_REQUEST['f'] != 'sent' ? 'inbox' : 'sent');
2788
2789
	// Done... all gone.
2790
	redirectexit($context['current_label_redirect']);
2791
}
2792
2793
/**
2794
 * This function allows the user to delete all messages older than so many days.
2795
 */
2796
function MessagePrune()
2797
{
2798
	global $txt, $context, $user_info, $scripturl, $smcFunc;
2799
2800
	// Actually delete the messages.
2801
	if (isset($_REQUEST['age']))
2802
	{
2803
		checkSession();
2804
2805
		// Calculate the time to delete before.
2806
		$deleteTime = max(0, time() - (86400 * (int) $_REQUEST['age']));
2807
2808
		// Array to store the IDs in.
2809
		$toDelete = array();
2810
2811
		// Select all the messages they have sent older than $deleteTime.
2812
		$request = $smcFunc['db_query']('', '
2813
			SELECT id_pm
2814
			FROM {db_prefix}personal_messages
2815
			WHERE deleted_by_sender = {int:not_deleted}
2816
				AND id_member_from = {int:current_member}
2817
				AND msgtime < {int:msgtime}',
2818
			array(
2819
				'current_member' => $user_info['id'],
2820
				'not_deleted' => 0,
2821
				'msgtime' => $deleteTime,
2822
			)
2823
		);
2824
		while ($row = $smcFunc['db_fetch_row']($request))
2825
			$toDelete[] = $row[0];
2826
		$smcFunc['db_free_result']($request);
2827
2828
		// Select all messages in their inbox older than $deleteTime.
2829
		$request = $smcFunc['db_query']('', '
2830
			SELECT pmr.id_pm
2831
			FROM {db_prefix}pm_recipients AS pmr
2832
				INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)
2833
			WHERE pmr.deleted = {int:not_deleted}
2834
				AND pmr.id_member = {int:current_member}
2835
				AND pm.msgtime < {int:msgtime}',
2836
			array(
2837
				'current_member' => $user_info['id'],
2838
				'not_deleted' => 0,
2839
				'msgtime' => $deleteTime,
2840
			)
2841
		);
2842
		while ($row = $smcFunc['db_fetch_assoc']($request))
2843
			$toDelete[] = $row['id_pm'];
2844
		$smcFunc['db_free_result']($request);
2845
2846
		// Delete the actual messages.
2847
		deleteMessages($toDelete);
2848
2849
		// Go back to their inbox.
2850
		redirectexit($context['current_label_redirect']);
2851
	}
2852
2853
	// Build the link tree elements.
2854
	$context['linktree'][] = array(
2855
		'url' => $scripturl . '?action=pm;sa=prune',
2856
		'name' => $txt['pm_prune']
2857
	);
2858
2859
	$context['sub_template'] = 'prune';
2860
	$context['page_title'] = $txt['pm_prune'];
2861
}
2862
2863
/**
2864
 * Delete the specified personal messages.
2865
 *
2866
 * @param array|null $personal_messages An array containing the IDs of PMs to delete or null to delete all of them
2867
 * @param string|null $folder Which "folder" to delete PMs from - 'sent' to delete them from the outbox, null or anything else to delete from the inbox
2868
 * @param array|int|null $owner An array of IDs of users whose PMs are being deleted, the ID of a single user or null to use the current user's ID
2869
 */
2870
function deleteMessages($personal_messages, $folder = null, $owner = null)
2871
{
2872
	global $user_info, $smcFunc;
2873
2874
	if ($owner === null)
2875
		$owner = array($user_info['id']);
2876
	elseif (empty($owner))
2877
		return;
2878
	elseif (!is_array($owner))
2879
		$owner = array($owner);
2880
2881
	if ($personal_messages !== null)
2882
	{
2883
		if (empty($personal_messages) || !is_array($personal_messages))
2884
			return;
2885
2886
		foreach ($personal_messages as $index => $delete_id)
2887
			$personal_messages[$index] = (int) $delete_id;
2888
2889
		$where = '
2890
				AND id_pm IN ({array_int:pm_list})';
2891
	}
2892
	else
2893
		$where = '';
2894
2895
	if ($folder == 'sent' || $folder === null)
2896
	{
2897
		$smcFunc['db_query']('', '
2898
			UPDATE {db_prefix}personal_messages
2899
			SET deleted_by_sender = {int:is_deleted}
2900
			WHERE id_member_from IN ({array_int:member_list})
2901
				AND deleted_by_sender = {int:not_deleted}' . $where,
2902
			array(
2903
				'member_list' => $owner,
2904
				'is_deleted' => 1,
2905
				'not_deleted' => 0,
2906
				'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(),
2907
			)
2908
		);
2909
	}
2910
	if ($folder != 'sent' || $folder === null)
2911
	{
2912
		// Calculate the number of messages each member's gonna lose...
2913
		$request = $smcFunc['db_query']('', '
2914
			SELECT id_member, COUNT(*) AS num_deleted_messages, CASE WHEN is_read & 1 >= 1 THEN 1 ELSE 0 END AS is_read
2915
			FROM {db_prefix}pm_recipients
2916
			WHERE id_member IN ({array_int:member_list})
2917
				AND deleted = {int:not_deleted}' . $where . '
2918
			GROUP BY id_member, is_read',
2919
			array(
2920
				'member_list' => $owner,
2921
				'not_deleted' => 0,
2922
				'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(),
2923
			)
2924
		);
2925
		// ...And update the statistics accordingly - now including unread messages!.
2926
		while ($row = $smcFunc['db_fetch_assoc']($request))
2927
		{
2928
			if ($row['is_read'])
2929
				updateMemberData($row['id_member'], array('instant_messages' => $where == '' ? 0 : 'instant_messages - ' . $row['num_deleted_messages']));
2930
			else
2931
				updateMemberData($row['id_member'], array('instant_messages' => $where == '' ? 0 : 'instant_messages - ' . $row['num_deleted_messages'], 'unread_messages' => $where == '' ? 0 : 'unread_messages - ' . $row['num_deleted_messages']));
2932
2933
			// If this is the current member we need to make their message count correct.
2934
			if ($user_info['id'] == $row['id_member'])
2935
			{
2936
				$user_info['messages'] -= $row['num_deleted_messages'];
2937
				if (!($row['is_read']))
2938
					$user_info['unread_messages'] -= $row['num_deleted_messages'];
2939
			}
2940
		}
2941
		$smcFunc['db_free_result']($request);
2942
2943
		// Do the actual deletion.
2944
		$smcFunc['db_query']('', '
2945
			UPDATE {db_prefix}pm_recipients
2946
			SET deleted = {int:is_deleted}
2947
			WHERE id_member IN ({array_int:member_list})
2948
				AND deleted = {int:not_deleted}' . $where,
2949
			array(
2950
				'member_list' => $owner,
2951
				'is_deleted' => 1,
2952
				'not_deleted' => 0,
2953
				'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(),
2954
			)
2955
		);
2956
2957
		$labels = array();
2958
2959
		// Get any labels that the owner may have applied to this PM
2960
		// The join is here to ensure we only get labels applied by the specified member(s)
2961
		$get_labels = $smcFunc['db_query']('', '
2962
			SELECT pml.id_label
2963
			FROM {db_prefix}pm_labels AS l
2964
			INNER JOIN {db_prefix}pm_labeled_messages AS pml ON (pml.id_label = l.id_label)
2965
			WHERE l.id_member IN ({array_int:member_list})' . $where,
2966
			array(
2967
				'member_list' => $owner,
2968
				'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(),
2969
			)
2970
		);
2971
2972
		while ($row = $smcFunc['db_fetch_assoc']($get_labels))
2973
		{
2974
			$labels[] = $row['id_label'];
2975
		}
2976
2977
		$smcFunc['db_free_result']($get_labels);
2978
2979
		if (!empty($labels))
2980
		{
2981
			$smcFunc['db_query']('', '
2982
				DELETE FROM {db_prefix}pm_labeled_messages
2983
				WHERE id_label IN ({array_int:labels})' . $where,
2984
				array(
2985
					'labels' => $labels,
2986
					'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(),
2987
				)
2988
			);
2989
		}
2990
	}
2991
2992
	// If sender and recipients all have deleted their message, it can be removed.
2993
	$request = $smcFunc['db_query']('', '
2994
		SELECT pm.id_pm AS sender, pmr.id_pm
2995
		FROM {db_prefix}personal_messages AS pm
2996
			LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm AND pmr.deleted = {int:not_deleted})
2997
		WHERE pm.deleted_by_sender = {int:is_deleted} AND pmr.id_pm is null
2998
			' . str_replace('id_pm', 'pm.id_pm', $where),
2999
		array(
3000
			'not_deleted' => 0,
3001
			'is_deleted' => 1,
3002
			'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(),
3003
		)
3004
	);
3005
	$remove_pms = array();
3006
	while ($row = $smcFunc['db_fetch_assoc']($request))
3007
		$remove_pms[] = $row['sender'];
3008
	$smcFunc['db_free_result']($request);
3009
3010
	if (!empty($remove_pms))
3011
	{
3012
		$smcFunc['db_query']('', '
3013
			DELETE FROM {db_prefix}personal_messages
3014
			WHERE id_pm IN ({array_int:pm_list})',
3015
			array(
3016
				'pm_list' => $remove_pms,
3017
			)
3018
		);
3019
3020
		$smcFunc['db_query']('', '
3021
			DELETE FROM {db_prefix}pm_recipients
3022
			WHERE id_pm IN ({array_int:pm_list})',
3023
			array(
3024
				'pm_list' => $remove_pms,
3025
			)
3026
		);
3027
3028
		$smcFunc['db_query']('', '
3029
			DELETE FROM {db_prefix}pm_labeled_messages
3030
			WHERE id_pm IN ({array_int:pm_list})',
3031
			array(
3032
				'pm_list' => $remove_pms,
3033
			)
3034
		);
3035
	}
3036
3037
	// Any cached numbers may be wrong now.
3038
	cache_put_data('labelCounts:' . $user_info['id'], null, 720);
3039
}
3040
3041
/**
3042
 * Mark the specified personal messages read.
3043
 *
3044
 * @param array|null $personal_messages An array of PM IDs to mark or null to mark all
3045
 * @param int|null $label The ID of a label. If set, only messages with this label will be marked.
3046
 * @param int|null $owner If owner is set, marks messages owned by that member id
3047
 */
3048
function markMessages($personal_messages = null, $label = null, $owner = null)
3049
{
3050
	global $user_info, $context, $smcFunc;
3051
3052
	if ($owner === null)
3053
		$owner = $user_info['id'];
3054
3055
	$in_inbox = '';
3056
3057
	// Marking all messages with a specific label as read?
3058
	// If we know which PMs we're marking read, then we don't need label info
3059
	if ($personal_messages === null && $label !== null && $label != '-1')
3060
	{
3061
		$personal_messages = array();
3062
		$get_messages = $smcFunc['db_query']('', '
3063
			SELECT id_pm
3064
			FROM {db_prefix}pm_labeled_messages
3065
			WHERE id_label = {int:current_label}',
3066
			array(
3067
				'current_label' => $label,
3068
			)
3069
		);
3070
3071
		while ($row = $smcFunc['db_fetch_assoc']($get_messages))
3072
		{
3073
			$personal_messages[] = $row['id_pm'];
3074
		}
3075
3076
		$smcFunc['db_free_result']($get_messages);
3077
	}
3078
	elseif ($label = '-1')
0 ignored issues
show
Unused Code introduced by
The assignment to $label is dead and can be removed.
Loading history...
3079
	{
3080
		// Marking all PMs in your inbox read
3081
		$in_inbox = '
3082
			AND in_inbox = {int:in_inbox}';
3083
	}
3084
3085
	$smcFunc['db_query']('', '
3086
		UPDATE {db_prefix}pm_recipients
3087
		SET is_read = is_read | 1
3088
		WHERE id_member = {int:id_member}
3089
			AND NOT (is_read & 1 >= 1)' . ($personal_messages !== null ? '
3090
			AND id_pm IN ({array_int:personal_messages})' : '') . $in_inbox,
3091
		array(
3092
			'personal_messages' => $personal_messages,
3093
			'id_member' => $owner,
3094
			'in_inbox' => 1,
3095
		)
3096
	);
3097
3098
	// If something wasn't marked as read, get the number of unread messages remaining.
3099
	if ($smcFunc['db_affected_rows']() > 0)
3100
	{
3101
		if ($owner == $user_info['id'])
3102
		{
3103
			foreach ($context['labels'] as $label)
3104
				$context['labels'][(int) $label['id']]['unread_messages'] = 0;
3105
		}
3106
3107
		$result = $smcFunc['db_query']('', '
3108
			SELECT id_pm, in_inbox, COUNT(*) AS num
3109
			FROM {db_prefix}pm_recipients
3110
			WHERE id_member = {int:id_member}
3111
				AND NOT (is_read & 1 >= 1)
3112
				AND deleted = {int:is_not_deleted}
3113
			GROUP BY id_pm, in_inbox',
3114
			array(
3115
				'id_member' => $owner,
3116
				'is_not_deleted' => 0,
3117
			)
3118
		);
3119
		$total_unread = 0;
3120
		while ($row = $smcFunc['db_fetch_assoc']($result))
3121
		{
3122
			$total_unread += $row['num'];
3123
3124
			if ($owner != $user_info['id'] || empty($row['id_pm']))
3125
				continue;
3126
3127
			$this_labels = array();
3128
3129
			// Get all the labels
3130
			$result2 = $smcFunc['db_query']('', '
3131
				SELECT pml.id_label
3132
				FROM {db_prefix}pm_labels AS l
3133
					INNER JOIN {db_prefix}pm_labeled_messages AS pml ON (pml.id_label = l.id_label)
3134
				WHERE l.id_member = {int:id_member}
3135
					AND pml.id_pm = {int:current_pm}',
3136
				array(
3137
					'id_member' => $owner,
3138
					'current_pm' => $row['id_pm'],
3139
				)
3140
			);
3141
3142
			while ($row2 = $smcFunc['db_fetch_assoc']($result2))
3143
			{
3144
				$this_labels[] = $row2['id_label'];
3145
			}
3146
3147
			$smcFunc['db_free_result']($result2);
3148
3149
			foreach ($this_labels as $this_label)
3150
				$context['labels'][$this_label]['unread_messages'] += $row['num'];
3151
3152
			if ($row['in_inbox'] == 1)
3153
				$context['labels'][-1]['unread_messages'] += $row['num'];
3154
		}
3155
		$smcFunc['db_free_result']($result);
3156
3157
		// Need to store all this.
3158
		cache_put_data('labelCounts:' . $owner, $context['labels'], 720);
3159
		updateMemberData($owner, array('unread_messages' => $total_unread));
3160
3161
		// If it was for the current member, reflect this in the $user_info array too.
3162
		if ($owner == $user_info['id'])
3163
			$user_info['unread_messages'] = $total_unread;
3164
	}
3165
}
3166
3167
/**
3168
 * This function handles adding, deleting and editing labels on messages.
3169
 */
3170
function ManageLabels()
3171
{
3172
	global $txt, $context, $user_info, $scripturl, $smcFunc;
3173
3174
	// Build the link tree elements...
3175
	$context['linktree'][] = array(
3176
		'url' => $scripturl . '?action=pm;sa=manlabels',
3177
		'name' => $txt['pm_manage_labels']
3178
	);
3179
3180
	$context['page_title'] = $txt['pm_manage_labels'];
3181
	$context['sub_template'] = 'labels';
3182
3183
	$the_labels = array();
3184
	$labels_to_add = array();
3185
	$labels_to_remove = array();
3186
	$label_updates = array();
3187
3188
	// Add all existing labels to the array to save, slashing them as necessary...
3189
	foreach ($context['labels'] as $label)
3190
	{
3191
		if ($label['id'] != -1)
3192
			$the_labels[$label['id']] = $label['name'];
3193
	}
3194
3195
	if (isset($_POST[$context['session_var']]))
3196
	{
3197
		checkSession();
3198
3199
		// This will be for updating messages.
3200
		$message_changes = array();
3201
		$rule_changes = array();
3202
3203
		// Will most likely need this.
3204
		LoadRules();
3205
3206
		// Adding a new label?
3207
		if (isset($_POST['add']))
3208
		{
3209
			$_POST['label'] = strtr($smcFunc['htmlspecialchars'](trim($_POST['label'])), array(',' => '&#044;'));
3210
3211
			if ($smcFunc['strlen']($_POST['label']) > 30)
3212
				$_POST['label'] = $smcFunc['substr']($_POST['label'], 0, 30);
3213
			if ($_POST['label'] != '')
3214
			{
3215
				$the_labels[] = $_POST['label'];
3216
				$labels_to_add[] = $_POST['label'];
3217
			}
3218
		}
3219
		// Deleting an existing label?
3220
		elseif (isset($_POST['delete'], $_POST['delete_label']))
3221
		{
3222
			foreach ($_POST['delete_label'] AS $label => $dummy)
3223
			{
3224
				unset($the_labels[$label]);
3225
				$labels_to_remove[] = $label;
3226
			}
3227
		}
3228
		// The hardest one to deal with... changes.
3229
		elseif (isset($_POST['save']) && !empty($_POST['label_name']))
3230
		{
3231
			foreach ($the_labels as $id => $name)
3232
			{
3233
				if ($id == -1)
3234
					continue;
3235
				elseif (isset($_POST['label_name'][$id]))
3236
				{
3237
					$_POST['label_name'][$id] = trim(strtr($smcFunc['htmlspecialchars']($_POST['label_name'][$id]), array(',' => '&#044;')));
3238
3239
					if ($smcFunc['strlen']($_POST['label_name'][$id]) > 30)
3240
						$_POST['label_name'][$id] = $smcFunc['substr']($_POST['label_name'][$id], 0, 30);
3241
					if ($_POST['label_name'][$id] != '')
3242
					{
3243
						// Changing the name of this label?
3244
						if ($the_labels[$id] != $_POST['label_name'][$id])
3245
							$label_updates[$id] = $_POST['label_name'][$id];
3246
3247
						$the_labels[(int) $id] = $_POST['label_name'][$id];
3248
					}
3249
					else
3250
					{
3251
						unset($the_labels[(int) $id]);
3252
						$labels_to_remove[] = $id;
3253
						$message_changes[(int) $id] = true;
3254
					}
3255
				}
3256
			}
3257
		}
3258
3259
		// Save any new labels
3260
		if (!empty($labels_to_add))
3261
		{
3262
			$inserts = array();
3263
			foreach ($labels_to_add AS $label)
3264
				$inserts[] = array($user_info['id'], $label);
3265
3266
			$smcFunc['db_insert']('', '{db_prefix}pm_labels', array('id_member' => 'int', 'name' => 'string-30'), $inserts, array());
3267
		}
3268
3269
		// Update existing labels as needed
3270
		if (!empty($label_upates))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $label_upates does not exist. Did you maybe mean $label_updates?
Loading history...
3271
		{
3272
			foreach ($label_updates AS $id => $name)
3273
			{
3274
				$smcFunc['db_query']('', '
3275
					UPDATE {db_prefix}labels
3276
					SET name = {string:name}
3277
					WHERE id_label = {int:id_label}',
3278
					array(
3279
						'name' => $name,
3280
						'id' => $id
3281
					)
3282
				);
3283
			}
3284
		}
3285
3286
		// Now the fun part... Deleting labels.
3287
		if (!empty($labels_to_remove))
3288
		{
3289
			// First delete the labels
3290
			$smcFunc['db_query']('', '
3291
				DELETE FROM {db_prefix}pm_labels
3292
				WHERE id_label IN ({array_int:labels_to_delete})',
3293
				array(
3294
					'labels_to_delete' => $labels_to_remove,
3295
				)
3296
			);
3297
3298
			// Now remove the now-deleted labels from any PMs...
3299
			$smcFunc['db_query']('', '
3300
				DELETE FROM {db_prefix}pm_labeled_messages
3301
				WHERE id_label IN ({array_int:labels_to_delete})',
3302
				array(
3303
					'labels_to_delete' => $labels_to_remove,
3304
				)
3305
			);
3306
3307
			// Get any PMs with no labels which aren't in the inbox
3308
			$get_stranded_pms = $smcFunc['db_query']('', '
3309
				SELECT pmr.id_pm
3310
				FROM {db_prefix}pm_recipients AS pmr
3311
					LEFT JOIN {db_prefix}pm_labeled_messages AS pml ON (pml.id_pm = pmr.id_pm)
3312
				WHERE pml.id_label IS NULL
3313
					AND pmr.in_inbox = {int:not_in_inbox}
3314
					AND pmr.deleted = {int:not_deleted}
3315
					AND pmr.id_member = {int:current_member}',
3316
				array(
3317
					'not_in_inbox' => 0,
3318
					'not_deleted' => 0,
3319
					'current_member' => $user_info['id'],
3320
				)
3321
			);
3322
3323
			$stranded_messages = array();
3324
			while ($row = $smcFunc['db_fetch_assoc']($get_stranded_pms))
3325
			{
3326
				$stranded_messages[] = $row['id_pm'];
3327
			}
3328
3329
			$smcFunc['db_free_result']($get_stranded_pms);
3330
3331
			// Move these back to the inbox if necessary
3332
			if (!empty($stranded_messages))
3333
			{
3334
				// We now have more messages in the inbox
3335
				$context['labels'][-1]['messages'] += count($stranded_messages);
3336
				$smcFunc['db_query']('', '
3337
					UPDATE {db_prefix}pm_recipients
3338
					SET in_inbox = {int:in_inbox}
3339
					WHERE id_pm IN ({array_int:stranded_messages})
3340
						AND id_member = {int:current_member}',
3341
					array(
3342
						'stranded_messages' => $stranded_messages,
3343
						'in_inbox' => 1,
3344
					)
3345
				);
3346
			}
3347
3348
			// Now do the same the rules - check through each rule.
3349
			foreach ($context['rules'] as $k => $rule)
3350
			{
3351
				// Each action...
3352
				foreach ($rule['actions'] as $k2 => $action)
3353
				{
3354
					if ($action['t'] != 'lab' || !in_array($action['v'], $labels_to_remove))
3355
						continue;
3356
3357
					$rule_changes[] = $rule['id'];
3358
3359
					// Can't apply this label anymore if it doesn't exist
3360
					unset($context['rules'][$k]['actions'][$k2]);
3361
				}
3362
			}
3363
		}
3364
3365
		// If we have rules to change do so now.
3366
		if (!empty($rule_changes))
3367
		{
3368
			$rule_changes = array_unique($rule_changes);
3369
			// Update/delete as appropriate.
3370
			foreach ($rule_changes as $k => $id)
3371
				if (!empty($context['rules'][$id]['actions']))
3372
				{
3373
					$smcFunc['db_query']('', '
3374
						UPDATE {db_prefix}pm_rules
3375
						SET actions = {string:actions}
3376
						WHERE id_rule = {int:id_rule}
3377
							AND id_member = {int:current_member}',
3378
						array(
3379
							'current_member' => $user_info['id'],
3380
							'id_rule' => $id,
3381
							'actions' => $smcFunc['json_encode']($context['rules'][$id]['actions']),
3382
						)
3383
					);
3384
					unset($rule_changes[$k]);
3385
				}
3386
3387
			// Anything left here means it's lost all actions...
3388
			if (!empty($rule_changes))
3389
				$smcFunc['db_query']('', '
3390
					DELETE FROM {db_prefix}pm_rules
3391
					WHERE id_rule IN ({array_int:rule_list})
3392
							AND id_member = {int:current_member}',
3393
					array(
3394
						'current_member' => $user_info['id'],
3395
						'rule_list' => $rule_changes,
3396
					)
3397
				);
3398
		}
3399
3400
		// Make sure we're not caching this!
3401
		cache_put_data('labelCounts:' . $user_info['id'], null, 720);
3402
3403
		// To make the changes appear right away, redirect.
3404
		redirectexit('action=pm;sa=manlabels');
3405
	}
3406
}
3407
3408
/**
3409
 * Allows to edit Personal Message Settings.
3410
 *
3411
 * @uses Profile.php
3412
 * @uses Profile-Modify.php
3413
 * @uses Profile template.
3414
 * @uses Profile language file.
3415
 */
3416
function MessageSettings()
3417
{
3418
	global $txt, $user_info, $context, $sourcedir;
3419
	global $scripturl, $profile_vars, $cur_profile, $user_profile;
3420
3421
	// Need this for the display.
3422
	require_once($sourcedir . '/Profile.php');
3423
	require_once($sourcedir . '/Profile-Modify.php');
3424
3425
	// We want them to submit back to here.
3426
	$context['profile_custom_submit_url'] = $scripturl . '?action=pm;sa=settings;save';
3427
3428
	loadMemberData($user_info['id'], false, 'profile');
3429
	$cur_profile = $user_profile[$user_info['id']];
3430
3431
	loadLanguage('Profile');
3432
	loadTemplate('Profile');
3433
3434
	// Since this is internally handled with the profile code because that's how it was done ages ago
3435
	// we have to set everything up for handling this...
3436
	$context['page_title'] = $txt['pm_settings'];
3437
	$context['user']['is_owner'] = true;
3438
	$context['id_member'] = $user_info['id'];
3439
	$context['require_password'] = false;
3440
	$context['menu_item_selected'] = 'settings';
3441
	$context['submit_button_text'] = $txt['pm_settings'];
3442
	$context['profile_header_text'] = $txt['personal_messages'];
3443
	$context['sub_template'] = 'edit_options';
3444
	$context['page_desc'] = $txt['pm_settings_desc'];
3445
3446
	loadThemeOptions($user_info['id']);
3447
	loadCustomFields($user_info['id'], 'pmprefs');
3448
3449
	// Add our position to the linktree.
3450
	$context['linktree'][] = array(
3451
		'url' => $scripturl . '?action=pm;sa=settings',
3452
		'name' => $txt['pm_settings']
3453
	);
3454
3455
	// Are they saving?
3456
	if (isset($_REQUEST['save']))
3457
	{
3458
		checkSession();
3459
3460
		// Mimic what profile would do.
3461
		$_POST = htmltrim__recursive($_POST);
3462
		$_POST = htmlspecialchars__recursive($_POST);
3463
3464
		// Save the fields.
3465
		saveProfileFields();
3466
3467
		if (!empty($profile_vars))
3468
			updateMemberData($user_info['id'], $profile_vars);
3469
	}
3470
3471
	setupProfileContext(
3472
		array(
3473
			'pm_prefs',
3474
		)
3475
	);
3476
}
3477
3478
/**
3479
 * Allows the user to report a personal message to an administrator.
3480
 *
3481
 * - In the first instance requires that the ID of the message to report is passed through $_GET.
3482
 * - It allows the user to report to either a particular administrator - or the whole admin team.
3483
 * - It will forward on a copy of the original message without allowing the reporter to make changes.
3484
 *
3485
 * @uses report_message sub-template.
3486
 */
3487
function ReportMessage()
3488
{
3489
	global $txt, $context, $scripturl;
3490
	global $user_info, $language, $modSettings, $smcFunc;
3491
3492
	// Check that this feature is even enabled!
3493
	if (empty($modSettings['enableReportPM']) || empty($_REQUEST['pmsg']))
3494
		fatal_lang_error('no_access', false);
3495
3496
	$pmsg = (int) $_REQUEST['pmsg'];
3497
3498
	if (!isAccessiblePM($pmsg, 'inbox'))
3499
		fatal_lang_error('no_access', false);
3500
3501
	$context['pm_id'] = $pmsg;
3502
	$context['page_title'] = $txt['pm_report_title'];
3503
3504
	// If we're here, just send the user to the template, with a few useful context bits.
3505
	if (!isset($_POST['report']))
3506
	{
3507
		$context['sub_template'] = 'report_message';
3508
3509
		// @todo I don't like being able to pick who to send it to.  Favoritism, etc. sucks.
3510
		// Now, get all the administrators.
3511
		$request = $smcFunc['db_query']('', '
3512
			SELECT id_member, real_name
3513
			FROM {db_prefix}members
3514
			WHERE id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0
3515
			ORDER BY real_name',
3516
			array(
3517
				'admin_group' => 1,
3518
			)
3519
		);
3520
		$context['admins'] = array();
3521
		while ($row = $smcFunc['db_fetch_assoc']($request))
3522
			$context['admins'][$row['id_member']] = $row['real_name'];
3523
		$smcFunc['db_free_result']($request);
3524
3525
		// How many admins in total?
3526
		$context['admin_count'] = count($context['admins']);
3527
	}
3528
	// Otherwise, let's get down to the sending stuff.
3529
	else
3530
	{
3531
		// Check the session before proceeding any further!
3532
		checkSession();
3533
3534
		// First, pull out the message contents, and verify it actually went to them!
3535
		$request = $smcFunc['db_query']('', '
3536
			SELECT pm.subject, pm.body, pm.msgtime, pm.id_member_from, COALESCE(m.real_name, pm.from_name) AS sender_name
3537
			FROM {db_prefix}personal_messages AS pm
3538
				INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)
3539
				LEFT JOIN {db_prefix}members AS m ON (m.id_member = pm.id_member_from)
3540
			WHERE pm.id_pm = {int:id_pm}
3541
				AND pmr.id_member = {int:current_member}
3542
				AND pmr.deleted = {int:not_deleted}
3543
			LIMIT 1',
3544
			array(
3545
				'current_member' => $user_info['id'],
3546
				'id_pm' => $context['pm_id'],
3547
				'not_deleted' => 0,
3548
			)
3549
		);
3550
		// Can only be a hacker here!
3551
		if ($smcFunc['db_num_rows']($request) == 0)
3552
			fatal_lang_error('no_access', false);
3553
		list ($subject, $body, $time, $memberFromID, $memberFromName) = $smcFunc['db_fetch_row']($request);
3554
		$smcFunc['db_free_result']($request);
3555
3556
		// Remove the line breaks...
3557
		$body = preg_replace('~<br ?/?' . '>~i', "\n", $body);
3558
3559
		// Get any other recipients of the email.
3560
		$request = $smcFunc['db_query']('', '
3561
			SELECT mem_to.id_member AS id_member_to, mem_to.real_name AS to_name, pmr.bcc
3562
			FROM {db_prefix}pm_recipients AS pmr
3563
				LEFT JOIN {db_prefix}members AS mem_to ON (mem_to.id_member = pmr.id_member)
3564
			WHERE pmr.id_pm = {int:id_pm}
3565
				AND pmr.id_member != {int:current_member}',
3566
			array(
3567
				'current_member' => $user_info['id'],
3568
				'id_pm' => $context['pm_id'],
3569
			)
3570
		);
3571
		$recipients = array();
3572
		$hidden_recipients = 0;
3573
		while ($row = $smcFunc['db_fetch_assoc']($request))
3574
		{
3575
			// If it's hidden still don't reveal their names - privacy after all ;)
3576
			if ($row['bcc'])
3577
				$hidden_recipients++;
3578
			else
3579
				$recipients[] = '[url=' . $scripturl . '?action=profile;u=' . $row['id_member_to'] . ']' . $row['to_name'] . '[/url]';
3580
		}
3581
		$smcFunc['db_free_result']($request);
3582
3583
		if ($hidden_recipients)
3584
			$recipients[] = sprintf($txt['pm_report_pm_hidden'], $hidden_recipients);
3585
3586
		// Now let's get out and loop through the admins.
3587
		$request = $smcFunc['db_query']('', '
3588
			SELECT id_member, real_name, lngfile
3589
			FROM {db_prefix}members
3590
			WHERE (id_group = {int:admin_id} OR FIND_IN_SET({int:admin_id}, additional_groups) != 0)
3591
				' . (empty($_POST['id_admin']) ? '' : 'AND id_member = {int:specific_admin}') . '
3592
			ORDER BY lngfile',
3593
			array(
3594
				'admin_id' => 1,
3595
				'specific_admin' => isset($_POST['id_admin']) ? (int) $_POST['id_admin'] : 0,
3596
			)
3597
		);
3598
3599
		// Maybe we shouldn't advertise this?
3600
		if ($smcFunc['db_num_rows']($request) == 0)
3601
			fatal_lang_error('no_access', false);
3602
3603
		$memberFromName = un_htmlspecialchars($memberFromName);
3604
3605
		// Prepare the message storage array.
3606
		$messagesToSend = array();
3607
		// Loop through each admin, and add them to the right language pile...
3608
		while ($row = $smcFunc['db_fetch_assoc']($request))
3609
		{
3610
			// Need to send in the correct language!
3611
			$cur_language = empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile'];
3612
3613
			if (!isset($messagesToSend[$cur_language]))
3614
			{
3615
				loadLanguage('PersonalMessage', $cur_language, false);
3616
3617
				// Make the body.
3618
				$report_body = str_replace(array('{REPORTER}', '{SENDER}'), array(un_htmlspecialchars($user_info['name']), $memberFromName), $txt['pm_report_pm_user_sent']);
3619
				$report_body .= "\n" . '[b]' . $_POST['reason'] . '[/b]' . "\n\n";
3620
				if (!empty($recipients))
3621
					$report_body .= $txt['pm_report_pm_other_recipients'] . ' ' . implode(', ', $recipients) . "\n\n";
3622
				$report_body .= $txt['pm_report_pm_unedited_below'] . "\n" . '[quote author=' . (empty($memberFromID) ? '"' . $memberFromName . '"' : $memberFromName . ' link=action=profile;u=' . $memberFromID . ' date=' . $time) . ']' . "\n" . un_htmlspecialchars($body) . '[/quote]';
3623
3624
				// Plonk it in the array ;)
3625
				$messagesToSend[$cur_language] = array(
3626
					'subject' => ($smcFunc['strpos']($subject, $txt['pm_report_pm_subject']) === false ? $txt['pm_report_pm_subject'] : '') . un_htmlspecialchars($subject),
3627
					'body' => $report_body,
3628
					'recipients' => array(
3629
						'to' => array(),
3630
						'bcc' => array()
3631
					),
3632
				);
3633
			}
3634
3635
			// Add them to the list.
3636
			$messagesToSend[$cur_language]['recipients']['to'][$row['id_member']] = $row['id_member'];
3637
		}
3638
		$smcFunc['db_free_result']($request);
3639
3640
		// Send a different email for each language.
3641
		foreach ($messagesToSend as $lang => $message)
3642
			sendpm($message['recipients'], $message['subject'], $message['body']);
3643
3644
		// Give the user their own language back!
3645
		if (!empty($modSettings['userLanguage']))
3646
			loadLanguage('PersonalMessage', '', false);
3647
3648
		// Leave them with a template.
3649
		$context['sub_template'] = 'report_message_complete';
3650
	}
3651
}
3652
3653
/**
3654
 * List all rules, and allow adding/entering etc...
3655
 */
3656
function ManageRules()
3657
{
3658
	global $txt, $context, $user_info, $scripturl, $smcFunc;
3659
3660
	// The link tree - gotta have this :o
3661
	$context['linktree'][] = array(
3662
		'url' => $scripturl . '?action=pm;sa=manrules',
3663
		'name' => $txt['pm_manage_rules']
3664
	);
3665
3666
	$context['page_title'] = $txt['pm_manage_rules'];
3667
	$context['sub_template'] = 'rules';
3668
3669
	// Load them... load them!!
3670
	LoadRules();
3671
3672
	// Likely to need all the groups!
3673
	$request = $smcFunc['db_query']('', '
3674
		SELECT mg.id_group, mg.group_name, COALESCE(gm.id_member, 0) AS can_moderate, mg.hidden
3675
		FROM {db_prefix}membergroups AS mg
3676
			LEFT JOIN {db_prefix}group_moderators AS gm ON (gm.id_group = mg.id_group AND gm.id_member = {int:current_member})
3677
		WHERE mg.min_posts = {int:min_posts}
3678
			AND mg.id_group != {int:moderator_group}
3679
			AND mg.hidden = {int:not_hidden}
3680
		ORDER BY mg.group_name',
3681
		array(
3682
			'current_member' => $user_info['id'],
3683
			'min_posts' => -1,
3684
			'moderator_group' => 3,
3685
			'not_hidden' => 0,
3686
		)
3687
	);
3688
	$context['groups'] = array();
3689
	while ($row = $smcFunc['db_fetch_assoc']($request))
3690
	{
3691
		// Hide hidden groups!
3692
		if ($row['hidden'] && !$row['can_moderate'] && !allowedTo('manage_membergroups'))
3693
			continue;
3694
3695
		$context['groups'][$row['id_group']] = $row['group_name'];
3696
	}
3697
	$smcFunc['db_free_result']($request);
3698
3699
	// Applying all rules?
3700
	if (isset($_GET['apply']))
3701
	{
3702
		checkSession('get');
3703
3704
		ApplyRules(true);
3705
		redirectexit('action=pm;sa=manrules');
3706
	}
3707
	// Editing a specific one?
3708
	if (isset($_GET['add']))
3709
	{
3710
		$context['rid'] = isset($_GET['rid']) && isset($context['rules'][$_GET['rid']]) ? (int) $_GET['rid'] : 0;
3711
		$context['sub_template'] = 'add_rule';
3712
3713
		// Current rule information...
3714
		if ($context['rid'])
3715
		{
3716
			$context['rule'] = $context['rules'][$context['rid']];
3717
			$members = array();
3718
			// Need to get member names!
3719
			foreach ($context['rule']['criteria'] as $k => $criteria)
3720
				if ($criteria['t'] == 'mid' && !empty($criteria['v']))
3721
					$members[(int) $criteria['v']] = $k;
3722
3723
			if (!empty($members))
3724
			{
3725
				$request = $smcFunc['db_query']('', '
3726
					SELECT id_member, member_name
3727
					FROM {db_prefix}members
3728
					WHERE id_member IN ({array_int:member_list})',
3729
					array(
3730
						'member_list' => array_keys($members),
3731
					)
3732
				);
3733
				while ($row = $smcFunc['db_fetch_assoc']($request))
3734
					$context['rule']['criteria'][$members[$row['id_member']]]['v'] = $row['member_name'];
3735
				$smcFunc['db_free_result']($request);
3736
			}
3737
		}
3738
		else
3739
			$context['rule'] = array(
3740
				'id' => '',
3741
				'name' => '',
3742
				'criteria' => array(),
3743
				'actions' => array(),
3744
				'logic' => 'and',
3745
			);
3746
	}
3747
	// Saving?
3748
	elseif (isset($_GET['save']))
3749
	{
3750
		checkSession();
3751
		$context['rid'] = isset($_GET['rid']) && isset($context['rules'][$_GET['rid']]) ? (int) $_GET['rid'] : 0;
3752
3753
		// Name is easy!
3754
		$ruleName = $smcFunc['htmlspecialchars'](trim($_POST['rule_name']));
3755
		if (empty($ruleName))
3756
			fatal_lang_error('pm_rule_no_name', false);
3757
3758
		// Sanity check...
3759
		if (empty($_POST['ruletype']) || empty($_POST['acttype']))
3760
			fatal_lang_error('pm_rule_no_criteria', false);
3761
3762
		// Let's do the criteria first - it's also hardest!
3763
		$criteria = array();
3764
		foreach ($_POST['ruletype'] as $ind => $type)
3765
		{
3766
			// Check everything is here...
3767
			if ($type == 'gid' && (!isset($_POST['ruledefgroup'][$ind]) || !isset($context['groups'][$_POST['ruledefgroup'][$ind]])))
3768
				continue;
3769
			elseif ($type != 'bud' && !isset($_POST['ruledef'][$ind]))
3770
				continue;
3771
3772
			// Members need to be found.
3773
			if ($type == 'mid')
3774
			{
3775
				$name = trim($_POST['ruledef'][$ind]);
3776
				$request = $smcFunc['db_query']('', '
3777
					SELECT id_member
3778
					FROM {db_prefix}members
3779
					WHERE real_name = {string:member_name}
3780
						OR member_name = {string:member_name}',
3781
					array(
3782
						'member_name' => $name,
3783
					)
3784
				);
3785
				if ($smcFunc['db_num_rows']($request) == 0)
3786
				{
3787
					loadLanguage('Errors');
3788
					fatal_lang_error('invalid_username', false);
3789
				}
3790
				list ($memID) = $smcFunc['db_fetch_row']($request);
3791
				$smcFunc['db_free_result']($request);
3792
3793
				$criteria[] = array('t' => 'mid', 'v' => $memID);
3794
			}
3795
			elseif ($type == 'bud')
3796
				$criteria[] = array('t' => 'bud', 'v' => 1);
3797
			elseif ($type == 'gid')
3798
				$criteria[] = array('t' => 'gid', 'v' => (int) $_POST['ruledefgroup'][$ind]);
3799
			elseif (in_array($type, array('sub', 'msg')) && trim($_POST['ruledef'][$ind]) != '')
3800
				$criteria[] = array('t' => $type, 'v' => $smcFunc['htmlspecialchars'](trim($_POST['ruledef'][$ind])));
3801
		}
3802
3803
		// Also do the actions!
3804
		$actions = array();
3805
		$doDelete = 0;
3806
		$isOr = $_POST['rule_logic'] == 'or' ? 1 : 0;
3807
		foreach ($_POST['acttype'] as $ind => $type)
3808
		{
3809
			// Picking a valid label?
3810
			if ($type == 'lab' && (!isset($_POST['labdef'][$ind]) || !isset($context['labels'][$_POST['labdef'][$ind]])))
3811
				continue;
3812
3813
			// Record what we're doing.
3814
			if ($type == 'del')
3815
				$doDelete = 1;
3816
			elseif ($type == 'lab')
3817
				$actions[] = array('t' => 'lab', 'v' => (int) $_POST['labdef'][$ind]);
3818
		}
3819
3820
		if (empty($criteria) || (empty($actions) && !$doDelete))
3821
			fatal_lang_error('pm_rule_no_criteria', false);
3822
3823
		// What are we storing?
3824
		$criteria = $smcFunc['json_encode']($criteria);
3825
		$actions = $smcFunc['json_encode']($actions);
3826
3827
		// Create the rule?
3828
		if (empty($context['rid']))
3829
			$smcFunc['db_insert']('',
3830
				'{db_prefix}pm_rules',
3831
				array(
3832
					'id_member' => 'int', 'rule_name' => 'string', 'criteria' => 'string', 'actions' => 'string',
3833
					'delete_pm' => 'int', 'is_or' => 'int',
3834
				),
3835
				array(
3836
					$user_info['id'], $ruleName, $criteria, $actions, $doDelete, $isOr,
3837
				),
3838
				array('id_rule')
3839
			);
3840
		else
3841
			$smcFunc['db_query']('', '
3842
				UPDATE {db_prefix}pm_rules
3843
				SET rule_name = {string:rule_name}, criteria = {string:criteria}, actions = {string:actions},
3844
					delete_pm = {int:delete_pm}, is_or = {int:is_or}
3845
				WHERE id_rule = {int:id_rule}
3846
					AND id_member = {int:current_member}',
3847
				array(
3848
					'current_member' => $user_info['id'],
3849
					'delete_pm' => $doDelete,
3850
					'is_or' => $isOr,
3851
					'id_rule' => $context['rid'],
3852
					'rule_name' => $ruleName,
3853
					'criteria' => $criteria,
3854
					'actions' => $actions,
3855
				)
3856
			);
3857
3858
		redirectexit('action=pm;sa=manrules');
3859
	}
3860
	// Deleting?
3861
	elseif (isset($_POST['delselected']) && !empty($_POST['delrule']))
3862
	{
3863
		checkSession();
3864
		$toDelete = array();
3865
		foreach ($_POST['delrule'] as $k => $v)
3866
			$toDelete[] = (int) $k;
3867
3868
		if (!empty($toDelete))
3869
			$smcFunc['db_query']('', '
3870
				DELETE FROM {db_prefix}pm_rules
3871
				WHERE id_rule IN ({array_int:delete_list})
3872
					AND id_member = {int:current_member}',
3873
				array(
3874
					'current_member' => $user_info['id'],
3875
					'delete_list' => $toDelete,
3876
				)
3877
			);
3878
3879
		redirectexit('action=pm;sa=manrules');
3880
	}
3881
}
3882
3883
/**
3884
 * This will apply rules to all unread messages. If all_messages is set will, clearly, do it to all!
3885
 *
3886
 * @param bool $all_messages Whether to apply this to all messages or just unread ones
3887
 */
3888
function ApplyRules($all_messages = false)
3889
{
3890
	global $user_info, $smcFunc, $context, $options;
3891
3892
	// Want this - duh!
3893
	loadRules();
3894
3895
	// No rules?
3896
	if (empty($context['rules']))
3897
		return;
3898
3899
	// Just unread ones?
3900
	$ruleQuery = $all_messages ? '' : ' AND pmr.is_new = 1';
3901
3902
	// @todo Apply all should have timeout protection!
3903
	// Get all the messages that match this.
3904
	$request = $smcFunc['db_query']('', '
3905
		SELECT
3906
			pmr.id_pm, pm.id_member_from, pm.subject, pm.body, mem.id_group
3907
		FROM {db_prefix}pm_recipients AS pmr
3908
			INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)
3909
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from)
3910
		WHERE pmr.id_member = {int:current_member}
3911
			AND pmr.deleted = {int:not_deleted}
3912
			' . $ruleQuery,
3913
		array(
3914
			'current_member' => $user_info['id'],
3915
			'not_deleted' => 0,
3916
		)
3917
	);
3918
	$actions = array();
3919
	while ($row = $smcFunc['db_fetch_assoc']($request))
3920
	{
3921
		foreach ($context['rules'] as $rule)
3922
		{
3923
			$match = false;
3924
			// Loop through all the criteria hoping to make a match.
3925
			foreach ($rule['criteria'] as $criterium)
3926
			{
3927
				if (($criterium['t'] == 'mid' && $criterium['v'] == $row['id_member_from']) || ($criterium['t'] == 'gid' && $criterium['v'] == $row['id_group']) || ($criterium['t'] == 'sub' && strpos($row['subject'], $criterium['v']) !== false) || ($criterium['t'] == 'msg' && strpos($row['body'], $criterium['v']) !== false))
3928
					$match = true;
3929
				// If we're adding and one criteria don't match then we stop!
3930
				elseif ($rule['logic'] == 'and')
3931
				{
3932
					$match = false;
3933
					break;
3934
				}
3935
			}
3936
3937
			// If we have a match the rule must be true - act!
3938
			if ($match)
3939
			{
3940
				if ($rule['delete'])
3941
					$actions['deletes'][] = $row['id_pm'];
3942
				else
3943
				{
3944
					foreach ($rule['actions'] as $ruleAction)
3945
					{
3946
						if ($ruleAction['t'] == 'lab')
3947
						{
3948
							// Get a basic pot started!
3949
							if (!isset($actions['labels'][$row['id_pm']]))
3950
								$actions['labels'][$row['id_pm']] = array();
3951
							$actions['labels'][$row['id_pm']][] = $ruleAction['v'];
3952
						}
3953
					}
3954
				}
3955
			}
3956
		}
3957
	}
3958
	$smcFunc['db_free_result']($request);
3959
3960
	// Deletes are easy!
3961
	if (!empty($actions['deletes']))
3962
		deleteMessages($actions['deletes']);
3963
3964
	// Relabel?
3965
	if (!empty($actions['labels']))
3966
	{
3967
		foreach ($actions['labels'] as $pm => $labels)
3968
		{
3969
			// Quickly check each label is valid!
3970
			$realLabels = array();
3971
			foreach ($context['labels'] as $label)
3972
			{
3973
				if (in_array($label['id'], $labels) && $label['id'] != -1 || empty($options['pm_remove_inbox_label']))
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (in_array($label['id'], ...m_remove_inbox_label']), Probably Intended Meaning: in_array($label['id'], $..._remove_inbox_label']))
Loading history...
3974
				{
3975
					// Make sure this stays in the inbox
3976
					if ($label['id'] == '-1')
3977
					{
3978
						$smcFunc['db_query']('', '
3979
							UPDATE {db_prefix}pm_recipients
3980
							SET in_inbox = {int:in_inbox}
3981
							WHERE id_pm = {int:id_pm}
3982
								AND id_member = {int:current_member}',
3983
							array(
3984
								'in_inbox' => 1,
3985
								'id_pm' => $pm,
3986
								'current_member' => $user_info['id'],
3987
							)
3988
						);
3989
					}
3990
					else
3991
					{
3992
						$realLabels[] = $label['id'];
3993
					}
3994
				}
3995
			}
3996
3997
			$inserts = array();
3998
			// Now we insert the label info
3999
			foreach ($realLabels as $a_label)
4000
				$inserts[] = array($pm, $a_label);
4001
4002
			$smcFunc['db_insert']('ignore',
4003
				'{db_prefix}pm_labeled_messages',
4004
				array('id_pm' => 'int', 'id_label' => 'int'),
4005
				$inserts,
4006
				array()
4007
			);
4008
		}
4009
	}
4010
}
4011
4012
/**
4013
 * Load up all the rules for the current user.
4014
 *
4015
 * @param bool $reload Whether or not to reload all the rules from the database if $context['rules'] is set
4016
 */
4017
function LoadRules($reload = false)
4018
{
4019
	global $user_info, $context, $smcFunc;
4020
4021
	if (isset($context['rules']) && !$reload)
4022
		return;
4023
4024
	$request = $smcFunc['db_query']('', '
4025
		SELECT
4026
			id_rule, rule_name, criteria, actions, delete_pm, is_or
4027
		FROM {db_prefix}pm_rules
4028
		WHERE id_member = {int:current_member}',
4029
		array(
4030
			'current_member' => $user_info['id'],
4031
		)
4032
	);
4033
	$context['rules'] = array();
4034
	// Simply fill in the data!
4035
	while ($row = $smcFunc['db_fetch_assoc']($request))
4036
	{
4037
		$context['rules'][$row['id_rule']] = array(
4038
			'id' => $row['id_rule'],
4039
			'name' => $row['rule_name'],
4040
			'criteria' => $smcFunc['json_decode']($row['criteria'], true),
4041
			'actions' => $smcFunc['json_decode']($row['actions'], true),
4042
			'delete' => $row['delete_pm'],
4043
			'logic' => $row['is_or'] ? 'or' : 'and',
4044
		);
4045
4046
		if ($row['delete_pm'])
4047
			$context['rules'][$row['id_rule']]['actions'][] = array('t' => 'del', 'v' => 1);
4048
	}
4049
	$smcFunc['db_free_result']($request);
4050
}
4051
4052
/**
4053
 * Check if the PM is available to the current user.
4054
 *
4055
 * @param int $pmID The ID of the PM
4056
 * @param string $validFor Which folders this is valud for - can be 'inbox', 'outbox' or 'in_or_outbox'
4057
 * @return boolean Whether the PM is accessible in that folder for the current user
4058
 */
4059
function isAccessiblePM($pmID, $validFor = 'in_or_outbox')
4060
{
4061
	global $user_info, $smcFunc;
4062
4063
	$request = $smcFunc['db_query']('', '
4064
		SELECT
4065
			pm.id_member_from = {int:id_current_member} AND pm.deleted_by_sender = {int:not_deleted} AS valid_for_outbox,
4066
			pmr.id_pm IS NOT NULL AS valid_for_inbox
4067
		FROM {db_prefix}personal_messages AS pm
4068
			LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm AND pmr.id_member = {int:id_current_member} AND pmr.deleted = {int:not_deleted})
4069
		WHERE pm.id_pm = {int:id_pm}
4070
			AND ((pm.id_member_from = {int:id_current_member} AND pm.deleted_by_sender = {int:not_deleted}) OR pmr.id_pm IS NOT NULL)',
4071
		array(
4072
			'id_pm' => $pmID,
4073
			'id_current_member' => $user_info['id'],
4074
			'not_deleted' => 0,
4075
		)
4076
	);
4077
4078
	if ($smcFunc['db_num_rows']($request) === 0)
4079
	{
4080
		$smcFunc['db_free_result']($request);
4081
		return false;
4082
	}
4083
4084
	$validationResult = $smcFunc['db_fetch_assoc']($request);
4085
	$smcFunc['db_free_result']($request);
4086
4087
	switch ($validFor)
4088
	{
4089
		case 'inbox':
4090
			return !empty($validationResult['valid_for_inbox']);
4091
		break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
4092
4093
		case 'outbox':
4094
			return !empty($validationResult['valid_for_outbox']);
4095
		break;
4096
4097
		case 'in_or_outbox':
4098
			return !empty($validationResult['valid_for_inbox']) || !empty($validationResult['valid_for_outbox']);
4099
		break;
4100
4101
		default:
4102
			trigger_error('Undefined validation type given', E_USER_ERROR);
4103
		break;
4104
	}
4105
}
4106
4107
?>