removeDeleteConcurrence()   B
last analyzed

Complexity

Conditions 7
Paths 6

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 14
c 0
b 0
f 0
nop 0
dl 0
loc 23
rs 8.8333
nc 6
1
<?php
2
3
/**
4
 * The contents of this file handle the deletion of topics, posts, and related
5
 * paraphernalia.
6
 *
7
 * Simple Machines Forum (SMF)
8
 *
9
 * @package SMF
10
 * @author Simple Machines https://www.simplemachines.org
11
 * @copyright 2022 Simple Machines and individual contributors
12
 * @license https://www.simplemachines.org/about/smf/license.php BSD
13
 *
14
 * @version 2.1.0
15
 */
16
17
if (!defined('SMF'))
18
	die('No direct access...');
19
20
/*	The contents of this file handle the deletion of topics, posts, and related
21
	paraphernalia.  It has the following functions:
22
23
*/
24
25
/**
26
 * Completely remove an entire topic.
27
 * Redirects to the board when completed.
28
 */
29
function RemoveTopic2()
30
{
31
	global $user_info, $topic, $board, $sourcedir, $smcFunc, $modSettings;
32
33
	// Make sure they aren't being lead around by someone. (:@)
34
	checkSession('get');
35
36
	// This file needs to be included for sendNotifications().
37
	require_once($sourcedir . '/Subs-Post.php');
38
39
	// Trying to fool us around, are we?
40
	if (empty($topic))
41
		redirectexit();
42
43
	removeDeleteConcurrence();
44
45
	$request = $smcFunc['db_query']('', '
46
		SELECT t.id_member_started, ms.subject, t.approved, t.locked
47
		FROM {db_prefix}topics AS t
48
			INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)
49
		WHERE t.id_topic = {int:current_topic}
50
		LIMIT 1',
51
		array(
52
			'current_topic' => $topic,
53
		)
54
	);
55
	list ($starter, $subject, $approved, $locked) = $smcFunc['db_fetch_row']($request);
56
	$smcFunc['db_free_result']($request);
57
58
	if ($starter == $user_info['id'] && !allowedTo('remove_any'))
59
		isAllowedTo('remove_own');
60
	else
61
		isAllowedTo('remove_any');
62
63
	// Can they see the topic?
64
	if ($modSettings['postmod_active'] && !$approved && $starter != $user_info['id'])
65
		isAllowedTo('approve_posts');
66
67
	// Ok, we got that far, but is it locked?
68
	if ($locked)
69
	{
70
		if (!($locked == 1 && $starter == $user_info['id'] || allowedTo('lock_any')))
71
			fatal_lang_error('cannot_remove_locked', 'user');
72
	}
73
74
	// Notify people that this topic has been removed.
75
	sendNotifications($topic, 'remove');
76
77
	removeTopics($topic);
78
79
	// Note, only log topic ID in native form if it's not gone forever.
80
	if (allowedTo('remove_any') || (allowedTo('remove_own') && $starter == $user_info['id']))
81
		logAction('remove', array((empty($modSettings['recycle_enable']) || $modSettings['recycle_board'] != $board ? 'topic' : 'old_topic_id') => $topic, 'subject' => $subject, 'member' => $starter, 'board' => $board));
82
83
	redirectexit('board=' . $board . '.0');
84
}
85
86
/**
87
 * Remove just a single post.
88
 * On completion redirect to the topic or to the board.
89
 */
90
function DeleteMessage()
91
{
92
	global $user_info, $topic, $board, $modSettings, $smcFunc;
93
94
	checkSession('get');
95
96
	$_REQUEST['msg'] = (int) $_REQUEST['msg'];
97
98
	// Is $topic set?
99
	if (empty($topic) && isset($_REQUEST['topic']))
100
		$topic = (int) $_REQUEST['topic'];
101
102
	removeDeleteConcurrence();
103
104
	$request = $smcFunc['db_query']('', '
105
		SELECT t.id_member_started, m.id_member, m.subject, m.poster_time, m.approved
106
		FROM {db_prefix}topics AS t
107
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = {int:id_msg} AND m.id_topic = {int:current_topic})
108
		WHERE t.id_topic = {int:current_topic}
109
		LIMIT 1',
110
		array(
111
			'current_topic' => $topic,
112
			'id_msg' => $_REQUEST['msg'],
113
		)
114
	);
115
	list ($starter, $poster, $subject, $post_time, $approved) = $smcFunc['db_fetch_row']($request);
116
	$smcFunc['db_free_result']($request);
117
118
	// Verify they can see this!
119
	if ($modSettings['postmod_active'] && !$approved && !empty($poster) && $poster != $user_info['id'])
120
		isAllowedTo('approve_posts');
121
122
	if ($poster == $user_info['id'])
123
	{
124
		if (!allowedTo('delete_own'))
125
		{
126
			if ($starter == $user_info['id'] && !allowedTo('delete_any'))
127
				isAllowedTo('delete_replies');
128
			elseif (!allowedTo('delete_any'))
129
				isAllowedTo('delete_own');
130
		}
131
		elseif (!allowedTo('delete_any') && ($starter != $user_info['id'] || !allowedTo('delete_replies')) && !empty($modSettings['edit_disable_time']) && $post_time + $modSettings['edit_disable_time'] * 60 < time())
132
			fatal_lang_error('modify_post_time_passed', false);
133
	}
134
	elseif ($starter == $user_info['id'] && !allowedTo('delete_any'))
135
		isAllowedTo('delete_replies');
136
	else
137
		isAllowedTo('delete_any');
138
139
	// If the full topic was removed go back to the board.
140
	$full_topic = removeMessage($_REQUEST['msg']);
141
142
	if (allowedTo('delete_any') && (!allowedTo('delete_own') || $poster != $user_info['id']))
143
		logAction('delete', array('topic' => $topic, 'subject' => $subject, 'member' => $poster, 'board' => $board));
144
145
	// We want to redirect back to recent action.
146
	if (isset($_REQUEST['modcenter']))
147
		redirectexit('action=moderate;area=reportedposts;done');
148
	elseif (isset($_REQUEST['recent']))
149
		redirectexit('action=recent');
150
	elseif (isset($_REQUEST['profile'], $_REQUEST['start'], $_REQUEST['u']))
151
		redirectexit('action=profile;u=' . $_REQUEST['u'] . ';area=showposts;start=' . $_REQUEST['start']);
152
	elseif ($full_topic)
153
		redirectexit('board=' . $board . '.0');
154
	else
155
		redirectexit('topic=' . $topic . '.' . $_REQUEST['start']);
156
}
157
158
/**
159
 * So long as you are sure... all old posts will be gone.
160
 * Used in ManageMaintenance.php to prune old topics.
161
 */
162
function RemoveOldTopics2()
163
{
164
	global $smcFunc;
165
166
	isAllowedTo('admin_forum');
167
	checkSession('post', 'admin');
168
169
	// No boards at all?  Forget it then :/.
170
	if (empty($_POST['boards']))
171
		redirectexit('action=admin;area=maintain;sa=topics');
172
173
	// This should exist, but we can make sure.
174
	$_POST['delete_type'] = isset($_POST['delete_type']) ? $_POST['delete_type'] : 'nothing';
175
176
	// Custom conditions.
177
	$condition = '';
178
	$condition_params = array(
179
		'boards' => array_keys($_POST['boards']),
180
		'poster_time' => time() - 3600 * 24 * $_POST['maxdays'],
181
	);
182
183
	// Just moved notice topics?
184
	// Note that this ignores redirection topics unless it's a non-expiring one
185
	if ($_POST['delete_type'] == 'moved')
186
	{
187
		$condition .= '
188
			AND m.icon = {string:icon}
189
			AND t.locked = {int:locked}
190
			AND t.redirect_expires = {int:not_expiring}';
191
		$condition_params['icon'] = 'moved';
192
		$condition_params['locked'] = 1;
193
		$condition_params['not_expiring'] = 0;
194
	}
195
	// Otherwise, maybe locked topics only?
196
	elseif ($_POST['delete_type'] == 'locked')
197
	{
198
		// Exclude moved/merged notices since we have another option for those...
199
		$condition .= '
200
			AND t.icon != {string:icon}
201
			AND t.locked = {int:locked}';
202
		$condition_params['icon'] = 'moved';
203
		$condition_params['locked'] = 1;
204
	}
205
206
	// Exclude stickies?
207
	if (isset($_POST['delete_old_not_sticky']))
208
	{
209
		$condition .= '
210
			AND t.is_sticky = {int:is_sticky}';
211
		$condition_params['is_sticky'] = 0;
212
	}
213
214
	// All we're gonna do here is grab the id_topic's and send them to removeTopics().
215
	$request = $smcFunc['db_query']('', '
216
		SELECT t.id_topic
217
		FROM {db_prefix}topics AS t
218
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_last_msg)
219
		WHERE
220
			m.poster_time < {int:poster_time}' . $condition . '
221
			AND t.id_board IN ({array_int:boards})',
222
		$condition_params
223
	);
224
	$topics = array();
225
	while ($row = $smcFunc['db_fetch_assoc']($request))
226
		$topics[] = $row['id_topic'];
227
	$smcFunc['db_free_result']($request);
228
229
	removeTopics($topics, false, true);
230
231
	// Log an action into the moderation log.
232
	logAction('pruned', array('days' => $_POST['maxdays']));
233
234
	redirectexit('action=admin;area=maintain;sa=topics;done=purgeold');
235
}
236
237
/**
238
 * Removes the passed id_topic's. (permissions are NOT checked here!).
239
 *
240
 * @param array|int $topics The topics to remove (can be an id or an array of ids).
241
 * @param bool $decreasePostCount Whether to decrease the users' post counts
242
 * @param bool $ignoreRecycling Whether to ignore recycling board settings
243
 * @param bool $updateBoardCount Whether to adjust topic counts for the boards
244
 */
245
function removeTopics($topics, $decreasePostCount = true, $ignoreRecycling = false, $updateBoardCount = true)
246
{
247
	global $sourcedir, $modSettings, $smcFunc;
248
249
	// Nothing to do?
250
	if (empty($topics))
251
		return;
252
	// Only a single topic.
253
	if (is_numeric($topics))
254
		$topics = array($topics);
255
256
	$recycle_board = !empty($modSettings['recycle_enable']) && !empty($modSettings['recycle_board']) ? (int) $modSettings['recycle_board'] : 0;
257
258
	// Do something before?
259
	call_integration_hook('integrate_remove_topics_before', array($topics, $recycle_board));
260
261
	// Decrease the post counts.
262
	if ($decreasePostCount)
263
	{
264
		$requestMembers = $smcFunc['db_query']('', '
265
			SELECT m.id_member, COUNT(*) AS posts
266
			FROM {db_prefix}messages AS m
267
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
268
			WHERE m.id_topic IN ({array_int:topics})' . (!empty($recycle_board) ? '
269
				AND m.id_board != {int:recycled_board}' : '') . '
270
				AND b.count_posts = {int:do_count_posts}
271
				AND m.approved = {int:is_approved}
272
			GROUP BY m.id_member',
273
			array(
274
				'do_count_posts' => 0,
275
				'recycled_board' => $recycle_board,
276
				'topics' => $topics,
277
				'is_approved' => 1,
278
			)
279
		);
280
		if ($smcFunc['db_num_rows']($requestMembers) > 0)
281
		{
282
			while ($rowMembers = $smcFunc['db_fetch_assoc']($requestMembers))
283
				updateMemberData($rowMembers['id_member'], array('posts' => 'posts - ' . $rowMembers['posts']));
284
		}
285
		$smcFunc['db_free_result']($requestMembers);
286
	}
287
288
	// Recycle topics that aren't in the recycle board...
289
	if (!empty($recycle_board) && !$ignoreRecycling)
290
	{
291
		$request = $smcFunc['db_query']('', '
292
			SELECT id_topic, id_board, unapproved_posts, approved
293
			FROM {db_prefix}topics
294
			WHERE id_topic IN ({array_int:topics})
295
				AND id_board != {int:recycle_board}
296
			LIMIT {int:limit}',
297
			array(
298
				'recycle_board' => $recycle_board,
299
				'topics' => $topics,
300
				'limit' => count($topics),
301
			)
302
		);
303
		if ($smcFunc['db_num_rows']($request) > 0)
304
		{
305
			// Get topics that will be recycled.
306
			$recycleTopics = array();
307
			while ($row = $smcFunc['db_fetch_assoc']($request))
308
			{
309
				if (function_exists('apache_reset_timeout'))
310
					@apache_reset_timeout();
311
312
				$recycleTopics[] = $row['id_topic'];
313
314
				// Set the id_previous_board for this topic - and make it not sticky.
315
				$smcFunc['db_query']('', '
316
					UPDATE {db_prefix}topics
317
					SET id_previous_board = {int:id_previous_board}, is_sticky = {int:not_sticky}
318
					WHERE id_topic = {int:id_topic}',
319
					array(
320
						'id_previous_board' => $row['id_board'],
321
						'id_topic' => $row['id_topic'],
322
						'not_sticky' => 0,
323
					)
324
				);
325
			}
326
			$smcFunc['db_free_result']($request);
327
328
			// Move the topics to the recycle board.
329
			require_once($sourcedir . '/MoveTopic.php');
330
			moveTopics($recycleTopics, $modSettings['recycle_board']);
331
332
			// Close reports that are being recycled.
333
			require_once($sourcedir . '/ModerationCenter.php');
334
335
			$smcFunc['db_query']('', '
336
				UPDATE {db_prefix}log_reported
337
				SET closed = {int:is_closed}
338
				WHERE id_topic IN ({array_int:recycle_topics})',
339
				array(
340
					'recycle_topics' => $recycleTopics,
341
					'is_closed' => 1,
342
				)
343
			);
344
345
			updateSettings(array('last_mod_report_action' => time()));
346
347
			require_once($sourcedir . '/Subs-ReportedContent.php');
348
			recountOpenReports('posts');
349
350
			// Topics that were recycled don't need to be deleted, so subtract them.
351
			$topics = array_diff($topics, $recycleTopics);
352
		}
353
		else
354
			$smcFunc['db_free_result']($request);
355
	}
356
357
	// Still topics left to delete?
358
	if (empty($topics))
359
		return;
360
361
	// Callback for search APIs to do their thing
362
	require_once($sourcedir . '/Search.php');
363
	$searchAPI = findSearchAPI();
364
	if ($searchAPI->supportsMethod('topicsRemoved'))
365
		$searchAPI->topicsRemoved($topics);
366
367
	$adjustBoards = array();
368
369
	// Find out how many posts we are deleting.
370
	$request = $smcFunc['db_query']('', '
371
		SELECT id_board, approved, COUNT(*) AS num_topics, SUM(unapproved_posts) AS unapproved_posts,
372
			SUM(num_replies) AS num_replies
373
		FROM {db_prefix}topics
374
		WHERE id_topic IN ({array_int:topics})
375
		GROUP BY id_board, approved',
376
		array(
377
			'topics' => $topics,
378
		)
379
	);
380
	while ($row = $smcFunc['db_fetch_assoc']($request))
381
	{
382
		if (!isset($adjustBoards[$row['id_board']]['num_posts']))
383
		{
384
			$adjustBoards[$row['id_board']] = array(
385
				'num_posts' => 0,
386
				'num_topics' => 0,
387
				'unapproved_posts' => 0,
388
				'unapproved_topics' => 0,
389
				'id_board' => $row['id_board']
390
			);
391
		}
392
		// Posts = (num_replies + 1) for each approved topic.
393
		$adjustBoards[$row['id_board']]['num_posts'] += $row['num_replies'] + ($row['approved'] ? $row['num_topics'] : 0);
394
		$adjustBoards[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts'];
395
396
		// Add the topics to the right type.
397
		if ($row['approved'])
398
			$adjustBoards[$row['id_board']]['num_topics'] += $row['num_topics'];
399
		else
400
			$adjustBoards[$row['id_board']]['unapproved_topics'] += $row['num_topics'];
401
	}
402
	$smcFunc['db_free_result']($request);
403
404
	if ($updateBoardCount)
405
	{
406
		// Decrease the posts/topics...
407
		foreach ($adjustBoards as $stats)
408
		{
409
			if (function_exists('apache_reset_timeout'))
410
				@apache_reset_timeout();
411
412
			$smcFunc['db_query']('', '
413
				UPDATE {db_prefix}boards
414
				SET
415
					num_posts = CASE WHEN {int:num_posts} > num_posts THEN 0 ELSE num_posts - {int:num_posts} END,
416
					num_topics = CASE WHEN {int:num_topics} > num_topics THEN 0 ELSE num_topics - {int:num_topics} END,
417
					unapproved_posts = CASE WHEN {int:unapproved_posts} > unapproved_posts THEN 0 ELSE unapproved_posts - {int:unapproved_posts} END,
418
					unapproved_topics = CASE WHEN {int:unapproved_topics} > unapproved_topics THEN 0 ELSE unapproved_topics - {int:unapproved_topics} END
419
				WHERE id_board = {int:id_board}',
420
				array(
421
					'id_board' => $stats['id_board'],
422
					'num_posts' => $stats['num_posts'],
423
					'num_topics' => $stats['num_topics'],
424
					'unapproved_posts' => $stats['unapproved_posts'],
425
					'unapproved_topics' => $stats['unapproved_topics'],
426
				)
427
			);
428
		}
429
	}
430
	// Remove Polls.
431
	$request = $smcFunc['db_query']('', '
432
		SELECT id_poll
433
		FROM {db_prefix}topics
434
		WHERE id_topic IN ({array_int:topics})
435
			AND id_poll > {int:no_poll}
436
		LIMIT {int:limit}',
437
		array(
438
			'no_poll' => 0,
439
			'topics' => $topics,
440
			'limit' => count($topics),
441
		)
442
	);
443
	$polls = array();
444
	while ($row = $smcFunc['db_fetch_assoc']($request))
445
		$polls[] = $row['id_poll'];
446
	$smcFunc['db_free_result']($request);
447
448
	if (!empty($polls))
449
	{
450
		$smcFunc['db_query']('', '
451
			DELETE FROM {db_prefix}polls
452
			WHERE id_poll IN ({array_int:polls})',
453
			array(
454
				'polls' => $polls,
455
			)
456
		);
457
		$smcFunc['db_query']('', '
458
			DELETE FROM {db_prefix}poll_choices
459
			WHERE id_poll IN ({array_int:polls})',
460
			array(
461
				'polls' => $polls,
462
			)
463
		);
464
		$smcFunc['db_query']('', '
465
			DELETE FROM {db_prefix}log_polls
466
			WHERE id_poll IN ({array_int:polls})',
467
			array(
468
				'polls' => $polls,
469
			)
470
		);
471
	}
472
473
	// Get rid of the attachment, if it exists.
474
	require_once($sourcedir . '/ManageAttachments.php');
475
	$attachmentQuery = array(
476
		'attachment_type' => 0,
477
		'id_topic' => $topics,
478
	);
479
	removeAttachments($attachmentQuery, 'messages');
480
481
	// Delete possible search index entries.
482
	if (!empty($modSettings['search_custom_index_config']))
483
	{
484
		$customIndexSettings = $smcFunc['json_decode']($modSettings['search_custom_index_config'], true);
485
486
		$words = array();
487
		$messages = array();
488
		$request = $smcFunc['db_query']('', '
489
			SELECT id_msg, body
490
			FROM {db_prefix}messages
491
			WHERE id_topic IN ({array_int:topics})',
492
			array(
493
				'topics' => $topics,
494
			)
495
		);
496
		while ($row = $smcFunc['db_fetch_assoc']($request))
497
		{
498
			if (function_exists('apache_reset_timeout'))
499
				@apache_reset_timeout();
500
501
			$words = array_merge($words, text2words($row['body'], $customIndexSettings['bytes_per_word'], true));
502
			$messages[] = $row['id_msg'];
503
		}
504
		$smcFunc['db_free_result']($request);
505
		$words = array_unique($words);
506
507
		if (!empty($words) && !empty($messages))
508
			$smcFunc['db_query']('', '
509
				DELETE FROM {db_prefix}log_search_words
510
				WHERE id_word IN ({array_int:word_list})
511
					AND id_msg IN ({array_int:message_list})',
512
				array(
513
					'word_list' => $words,
514
					'message_list' => $messages,
515
				)
516
			);
517
	}
518
519
	// Delete anything related to the topic.
520
	$smcFunc['db_query']('', '
521
		DELETE FROM {db_prefix}messages
522
		WHERE id_topic IN ({array_int:topics})',
523
		array(
524
			'topics' => $topics,
525
		)
526
	);
527
	$smcFunc['db_query']('', '
528
		DELETE FROM {db_prefix}calendar
529
		WHERE id_topic IN ({array_int:topics})',
530
		array(
531
			'topics' => $topics,
532
		)
533
	);
534
	$smcFunc['db_query']('', '
535
		DELETE FROM {db_prefix}log_topics
536
		WHERE id_topic IN ({array_int:topics})',
537
		array(
538
			'topics' => $topics,
539
		)
540
	);
541
	$smcFunc['db_query']('', '
542
		DELETE FROM {db_prefix}log_notify
543
		WHERE id_topic IN ({array_int:topics})',
544
		array(
545
			'topics' => $topics,
546
		)
547
	);
548
	$smcFunc['db_query']('', '
549
		DELETE FROM {db_prefix}topics
550
		WHERE id_topic IN ({array_int:topics})',
551
		array(
552
			'topics' => $topics,
553
		)
554
	);
555
	$smcFunc['db_query']('', '
556
		DELETE FROM {db_prefix}log_search_subjects
557
		WHERE id_topic IN ({array_int:topics})',
558
		array(
559
			'topics' => $topics,
560
		)
561
	);
562
563
	// Maybe there's a mod that wants to delete topic related data of its own
564
	call_integration_hook('integrate_remove_topics', array($topics));
565
566
	// Update the totals...
567
	updateStats('message');
568
	updateStats('topic');
569
	updateSettings(array(
570
		'calendar_updated' => time(),
571
	));
572
573
	require_once($sourcedir . '/Subs-Post.php');
574
	$updates = array();
575
	foreach ($adjustBoards as $stats)
576
		$updates[] = $stats['id_board'];
577
	updateLastMessages($updates);
578
}
579
580
/**
581
 * Remove a specific message (including permission checks).
582
 *
583
 * @param int $message The message id
584
 * @param bool $decreasePostCount Whether to decrease users' post counts
585
 * @return bool Whether the operation succeeded
586
 */
587
function removeMessage($message, $decreasePostCount = true)
588
{
589
	global $board, $sourcedir, $modSettings, $user_info, $smcFunc;
590
591
	if (empty($message) || !is_numeric($message))
592
		return false;
593
594
	$recycle_board = !empty($modSettings['recycle_enable']) && !empty($modSettings['recycle_board']) ? (int) $modSettings['recycle_board'] : 0;
595
596
	$request = $smcFunc['db_query']('', '
597
		SELECT
598
			m.id_member, m.icon, m.poster_time, m.subject,' . (empty($modSettings['search_custom_index_config']) ? '' : ' m.body,') . '
599
			m.approved, t.id_topic, t.id_first_msg, t.id_last_msg, t.num_replies, t.id_board,
600
			t.id_member_started AS id_member_poster,
601
			b.count_posts
602
		FROM {db_prefix}messages AS m
603
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
604
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
605
		WHERE m.id_msg = {int:id_msg}
606
		LIMIT 1',
607
		array(
608
			'id_msg' => $message,
609
		)
610
	);
611
	if ($smcFunc['db_num_rows']($request) == 0)
612
		return false;
613
614
	$row = $smcFunc['db_fetch_assoc']($request);
615
	$smcFunc['db_free_result']($request);
616
617
	// Give mods a heads-up before we do anything.
618
	call_integration_hook('integrate_pre_remove_message', array($message, $decreasePostCount, $row));
619
620
	if (empty($board) || $row['id_board'] != $board)
621
	{
622
		$delete_any = boardsAllowedTo('delete_any');
623
624
		if (!in_array(0, $delete_any) && !in_array($row['id_board'], $delete_any))
625
		{
626
			$delete_own = boardsAllowedTo('delete_own');
627
			$delete_own = in_array(0, $delete_own) || in_array($row['id_board'], $delete_own);
0 ignored issues
show
Bug introduced by
$delete_own of type true is incompatible with the type array expected by parameter $haystack of in_array(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

627
			$delete_own = in_array(0, /** @scrutinizer ignore-type */ $delete_own) || in_array($row['id_board'], $delete_own);
Loading history...
628
			$delete_replies = boardsAllowedTo('delete_replies');
629
			$delete_replies = in_array(0, $delete_replies) || in_array($row['id_board'], $delete_replies);
630
631
			if ($row['id_member'] == $user_info['id'])
632
			{
633
				if (!$delete_own)
634
				{
635
					if ($row['id_member_poster'] == $user_info['id'])
636
					{
637
						if (!$delete_replies)
638
							fatal_lang_error('cannot_delete_replies', 'permission');
639
					}
640
					else
641
						fatal_lang_error('cannot_delete_own', 'permission');
642
				}
643
				elseif (($row['id_member_poster'] != $user_info['id'] || !$delete_replies) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + $modSettings['edit_disable_time'] * 60 < time())
644
					fatal_lang_error('modify_post_time_passed', false);
645
			}
646
			elseif ($row['id_member_poster'] == $user_info['id'])
647
			{
648
				if (!$delete_replies)
649
					fatal_lang_error('cannot_delete_replies', 'permission');
650
			}
651
			else
652
				fatal_lang_error('cannot_delete_any', 'permission');
653
		}
654
655
		// Can't delete an unapproved message, if you can't see it!
656
		if ($modSettings['postmod_active'] && !$row['approved'] && $row['id_member'] != $user_info['id'] && !(in_array(0, $delete_any) || in_array($row['id_board'], $delete_any)))
657
		{
658
			$approve_posts = boardsAllowedTo('approve_posts');
659
			if (!in_array(0, $approve_posts) && !in_array($row['id_board'], $approve_posts))
660
				return false;
661
		}
662
	}
663
	else
664
	{
665
		// Check permissions to delete this message.
666
		if ($row['id_member'] == $user_info['id'])
667
		{
668
			if (!allowedTo('delete_own'))
669
			{
670
				if ($row['id_member_poster'] == $user_info['id'] && !allowedTo('delete_any'))
671
					isAllowedTo('delete_replies');
672
				elseif (!allowedTo('delete_any'))
673
					isAllowedTo('delete_own');
674
			}
675
			elseif (!allowedTo('delete_any') && ($row['id_member_poster'] != $user_info['id'] || !allowedTo('delete_replies')) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + $modSettings['edit_disable_time'] * 60 < time())
676
				fatal_lang_error('modify_post_time_passed', false);
677
		}
678
		elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('delete_any'))
679
			isAllowedTo('delete_replies');
680
		else
681
			isAllowedTo('delete_any');
682
683
		if ($modSettings['postmod_active'] && !$row['approved'] && $row['id_member'] != $user_info['id'] && !allowedTo('delete_own'))
684
			isAllowedTo('approve_posts');
685
	}
686
687
	// Delete the *whole* topic, but only if the topic consists of one message.
688
	if ($row['id_first_msg'] == $message)
689
	{
690
		if (empty($board) || $row['id_board'] != $board)
691
		{
692
			$remove_any = boardsAllowedTo('remove_any');
693
			$remove_any = in_array(0, $remove_any) || in_array($row['id_board'], $remove_any);
694
			if (!$remove_any)
695
			{
696
				$remove_own = boardsAllowedTo('remove_own');
697
				$remove_own = in_array(0, $remove_own) || in_array($row['id_board'], $remove_own);
698
			}
699
700
			if ($row['id_member'] != $user_info['id'] && !$remove_any)
701
				fatal_lang_error('cannot_remove_any', 'permission');
702
			elseif (!$remove_any && !$remove_own)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $remove_own does not seem to be defined for all execution paths leading up to this point.
Loading history...
703
				fatal_lang_error('cannot_remove_own', 'permission');
704
		}
705
		else
706
		{
707
			// Check permissions to delete a whole topic.
708
			if ($row['id_member'] != $user_info['id'])
709
				isAllowedTo('remove_any');
710
			elseif (!allowedTo('remove_any'))
711
				isAllowedTo('remove_own');
712
		}
713
714
		// ...if there is only one post.
715
		if (!empty($row['num_replies']))
716
			fatal_lang_error('delFirstPost', false);
717
718
		removeTopics($row['id_topic']);
719
		return true;
720
	}
721
722
	// Deleting a recycled message can not lower anyone's post count.
723
	if (!empty($recycle_board) && $row['id_board'] == $recycle_board)
724
		$decreasePostCount = false;
725
726
	// This is the last post, update the last post on the board.
727
	if ($row['id_last_msg'] == $message)
728
	{
729
		// Find the last message, set it, and decrease the post count.
730
		$request = $smcFunc['db_query']('', '
731
			SELECT id_msg, id_member
732
			FROM {db_prefix}messages
733
			WHERE id_topic = {int:id_topic}
734
				AND id_msg != {int:id_msg}
735
			ORDER BY ' . ($modSettings['postmod_active'] ? 'approved DESC, ' : '') . 'id_msg DESC
736
			LIMIT 1',
737
			array(
738
				'id_topic' => $row['id_topic'],
739
				'id_msg' => $message,
740
			)
741
		);
742
		$row2 = $smcFunc['db_fetch_assoc']($request);
743
		$smcFunc['db_free_result']($request);
744
745
		$smcFunc['db_query']('', '
746
			UPDATE {db_prefix}topics
747
			SET
748
				id_last_msg = {int:id_last_msg},
749
				id_member_updated = {int:id_member_updated}' . (!$modSettings['postmod_active'] || $row['approved'] ? ',
750
				num_replies = CASE WHEN num_replies = {int:no_replies} THEN 0 ELSE num_replies - 1 END' : ',
751
				unapproved_posts = CASE WHEN unapproved_posts = {int:no_unapproved} THEN 0 ELSE unapproved_posts - 1 END') . '
752
			WHERE id_topic = {int:id_topic}',
753
			array(
754
				'id_last_msg' => $row2['id_msg'],
755
				'id_member_updated' => $row2['id_member'],
756
				'no_replies' => 0,
757
				'no_unapproved' => 0,
758
				'id_topic' => $row['id_topic'],
759
			)
760
		);
761
	}
762
	// Only decrease post counts.
763
	else
764
		$smcFunc['db_query']('', '
765
			UPDATE {db_prefix}topics
766
			SET ' . ($row['approved'] ? '
767
				num_replies = CASE WHEN num_replies = {int:no_replies} THEN 0 ELSE num_replies - 1 END' : '
768
				unapproved_posts = CASE WHEN unapproved_posts = {int:no_unapproved} THEN 0 ELSE unapproved_posts - 1 END') . '
769
			WHERE id_topic = {int:id_topic}',
770
			array(
771
				'no_replies' => 0,
772
				'no_unapproved' => 0,
773
				'id_topic' => $row['id_topic'],
774
			)
775
		);
776
777
	// Default recycle to false.
778
	$recycle = false;
779
780
	// If recycle topics has been set, make a copy of this message in the recycle board.
781
	// Make sure we're not recycling messages that are already on the recycle board.
782
	if (!empty($modSettings['recycle_enable']) && $row['id_board'] != $modSettings['recycle_board'] && $row['icon'] != 'recycled')
783
	{
784
		// Check if the recycle board exists and if so get the read status.
785
		$request = $smcFunc['db_query']('', '
786
			SELECT (COALESCE(lb.id_msg, 0) >= b.id_msg_updated) AS is_seen, id_last_msg
787
			FROM {db_prefix}boards AS b
788
				LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member})
789
			WHERE b.id_board = {int:recycle_board}',
790
			array(
791
				'current_member' => $user_info['id'],
792
				'recycle_board' => $modSettings['recycle_board'],
793
			)
794
		);
795
		if ($smcFunc['db_num_rows']($request) == 0)
796
			fatal_lang_error('recycle_no_valid_board');
797
		list ($isRead, $last_board_msg) = $smcFunc['db_fetch_row']($request);
798
		$smcFunc['db_free_result']($request);
799
800
		// Is there an existing topic in the recycle board to group this post with?
801
		$request = $smcFunc['db_query']('', '
802
			SELECT id_topic, id_first_msg, id_last_msg
803
			FROM {db_prefix}topics
804
			WHERE id_previous_topic = {int:id_previous_topic}
805
				AND id_board = {int:recycle_board}',
806
			array(
807
				'id_previous_topic' => $row['id_topic'],
808
				'recycle_board' => $modSettings['recycle_board'],
809
			)
810
		);
811
		list ($id_recycle_topic, $first_topic_msg, $last_topic_msg) = $smcFunc['db_fetch_row']($request);
812
		$smcFunc['db_free_result']($request);
813
814
		// Insert a new topic in the recycle board if $id_recycle_topic is empty.
815
		if (empty($id_recycle_topic))
816
			$id_topic = $smcFunc['db_insert']('',
817
				'{db_prefix}topics',
818
				array(
819
					'id_board' => 'int', 'id_member_started' => 'int', 'id_member_updated' => 'int', 'id_first_msg' => 'int',
820
					'id_last_msg' => 'int', 'unapproved_posts' => 'int', 'approved' => 'int', 'id_previous_topic' => 'int',
821
				),
822
				array(
823
					$modSettings['recycle_board'], $row['id_member'], $row['id_member'], $message,
824
					$message, 0, 1, $row['id_topic'],
825
				),
826
				array('id_topic'),
827
				1
828
			);
829
830
		// Capture the ID of the new topic...
831
		$topicID = empty($id_recycle_topic) ? $id_topic : $id_recycle_topic;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $id_topic does not seem to be defined for all execution paths leading up to this point.
Loading history...
832
833
		// If the topic creation went successful, move the message.
834
		if ($topicID > 0)
835
		{
836
			$smcFunc['db_query']('', '
837
				UPDATE {db_prefix}messages
838
				SET
839
					id_topic = {int:id_topic},
840
					id_board = {int:recycle_board},
841
					approved = {int:is_approved}
842
				WHERE id_msg = {int:id_msg}',
843
				array(
844
					'id_topic' => $topicID,
845
					'recycle_board' => $modSettings['recycle_board'],
846
					'id_msg' => $message,
847
					'is_approved' => 1,
848
				)
849
			);
850
851
			// Take any reported posts with us...
852
			$smcFunc['db_query']('', '
853
				UPDATE {db_prefix}log_reported
854
				SET
855
					id_topic = {int:id_topic},
856
					id_board = {int:recycle_board}
857
				WHERE id_msg = {int:id_msg}',
858
				array(
859
					'id_topic' => $topicID,
860
					'recycle_board' => $modSettings['recycle_board'],
861
					'id_msg' => $message,
862
				)
863
			);
864
865
			// Mark recycled topic as read.
866
			if (!$user_info['is_guest'])
867
				$smcFunc['db_insert']('replace',
868
					'{db_prefix}log_topics',
869
					array('id_topic' => 'int', 'id_member' => 'int', 'id_msg' => 'int', 'unwatched' => 'int'),
870
					array($topicID, $user_info['id'], $modSettings['maxMsgID'], 0),
871
					array('id_topic', 'id_member')
872
				);
873
874
			// Mark recycle board as seen, if it was marked as seen before.
875
			if (!empty($isRead) && !$user_info['is_guest'])
876
				$smcFunc['db_insert']('replace',
877
					'{db_prefix}log_boards',
878
					array('id_board' => 'int', 'id_member' => 'int', 'id_msg' => 'int'),
879
					array($modSettings['recycle_board'], $user_info['id'], $modSettings['maxMsgID']),
880
					array('id_board', 'id_member')
881
				);
882
883
			// Add one topic and post to the recycle bin board.
884
			$smcFunc['db_query']('', '
885
				UPDATE {db_prefix}boards
886
				SET
887
					num_topics = num_topics + {int:num_topics_inc},
888
					num_posts = num_posts + 1' .
889
						($message > $last_board_msg ? ', id_last_msg = {int:id_merged_msg}' : '') . '
890
				WHERE id_board = {int:recycle_board}',
891
				array(
892
					'num_topics_inc' => empty($id_recycle_topic) ? 1 : 0,
893
					'recycle_board' => $modSettings['recycle_board'],
894
					'id_merged_msg' => $message,
895
				)
896
			);
897
898
			// Lets increase the num_replies, and the first/last message ID as appropriate.
899
			if (!empty($id_recycle_topic))
900
				$smcFunc['db_query']('', '
901
					UPDATE {db_prefix}topics
902
					SET num_replies = num_replies + 1' .
903
						($message > $last_topic_msg ? ', id_last_msg = {int:id_merged_msg}' : '') .
904
						($message < $first_topic_msg ? ', id_first_msg = {int:id_merged_msg}' : '') . '
905
					WHERE id_topic = {int:id_recycle_topic}',
906
					array(
907
						'id_recycle_topic' => $id_recycle_topic,
908
						'id_merged_msg' => $message,
909
					)
910
				);
911
912
			// Make sure this message isn't getting deleted later on.
913
			$recycle = true;
914
915
			// Make sure we update the search subject index.
916
			updateStats('subject', $topicID, $row['subject']);
917
		}
918
919
		// If it wasn't approved don't keep it in the queue.
920
		if (!$row['approved'])
921
			$smcFunc['db_query']('', '
922
				DELETE FROM {db_prefix}approval_queue
923
				WHERE id_msg = {int:id_msg}
924
					AND id_attach = {int:id_attach}',
925
				array(
926
					'id_msg' => $message,
927
					'id_attach' => 0,
928
				)
929
			);
930
	}
931
932
	$smcFunc['db_query']('', '
933
		UPDATE {db_prefix}boards
934
		SET ' . ($row['approved'] ? '
935
			num_posts = CASE WHEN num_posts = {int:no_posts} THEN 0 ELSE num_posts - 1 END' : '
936
			unapproved_posts = CASE WHEN unapproved_posts = {int:no_unapproved} THEN 0 ELSE unapproved_posts - 1 END') . '
937
		WHERE id_board = {int:id_board}',
938
		array(
939
			'no_posts' => 0,
940
			'no_unapproved' => 0,
941
			'id_board' => $row['id_board'],
942
		)
943
	);
944
945
	// If the poster was registered and the board this message was on incremented
946
	// the member's posts when it was posted, decrease his or her post count.
947
	if (!empty($row['id_member']) && $decreasePostCount && empty($row['count_posts']) && $row['approved'])
948
		updateMemberData($row['id_member'], array('posts' => '-'));
949
950
	// Only remove posts if they're not recycled.
951
	if (!$recycle)
952
	{
953
		// Callback for search APIs to do their thing
954
		require_once($sourcedir . '/Search.php');
955
		$searchAPI = findSearchAPI();
956
		if ($searchAPI->supportsMethod('postRemoved'))
957
			$searchAPI->postRemoved($message);
958
959
		// Remove the message!
960
		$smcFunc['db_query']('', '
961
			DELETE FROM {db_prefix}messages
962
			WHERE id_msg = {int:id_msg}',
963
			array(
964
				'id_msg' => $message,
965
			)
966
		);
967
968
		if (!empty($modSettings['search_custom_index_config']))
969
		{
970
			$customIndexSettings = $smcFunc['json_decode']($modSettings['search_custom_index_config'], true);
971
			$words = text2words($row['body'], $customIndexSettings['bytes_per_word'], true);
972
			if (!empty($words))
973
				$smcFunc['db_query']('', '
974
					DELETE FROM {db_prefix}log_search_words
975
					WHERE id_word IN ({array_int:word_list})
976
						AND id_msg = {int:id_msg}',
977
					array(
978
						'word_list' => $words,
979
						'id_msg' => $message,
980
					)
981
				);
982
		}
983
984
		// Delete attachment(s) if they exist.
985
		require_once($sourcedir . '/ManageAttachments.php');
986
		$attachmentQuery = array(
987
			'attachment_type' => 0,
988
			'id_msg' => $message,
989
		);
990
		removeAttachments($attachmentQuery);
991
	}
992
993
	// Allow mods to remove message related data of their own (likes, maybe?)
994
	call_integration_hook('integrate_remove_message', array($message, $row, $recycle));
995
996
	// Update the pesky statistics.
997
	updateStats('message');
998
	updateStats('topic');
999
	updateSettings(array(
1000
		'calendar_updated' => time(),
1001
	));
1002
1003
	// And now to update the last message of each board we messed with.
1004
	require_once($sourcedir . '/Subs-Post.php');
1005
	if ($recycle)
1006
		updateLastMessages(array($row['id_board'], $modSettings['recycle_board']));
1007
	else
1008
		updateLastMessages($row['id_board']);
1009
1010
	// Close any moderation reports for this message.
1011
	$smcFunc['db_query']('', '
1012
		UPDATE {db_prefix}log_reported
1013
		SET closed = {int:is_closed}
1014
		WHERE id_msg = {int:id_msg}',
1015
		array(
1016
			'is_closed' => 1,
1017
			'id_msg' => $message,
1018
		)
1019
	);
1020
	if ($smcFunc['db_affected_rows']() != 0)
1021
	{
1022
		require_once($sourcedir . '/Subs-ReportedContent.php');
1023
		updateSettings(array('last_mod_report_action' => time()));
1024
		recountOpenReports('posts');
1025
	}
1026
1027
	return false;
1028
}
1029
1030
/**
1031
 * Move back a topic from the recycle board to its original board.
1032
 */
1033
function RestoreTopic()
1034
{
1035
	global $smcFunc, $modSettings, $sourcedir;
1036
1037
	// Check session.
1038
	checkSession('get');
1039
1040
	// Is recycled board enabled?
1041
	if (empty($modSettings['recycle_enable']))
1042
		fatal_lang_error('restored_disabled', 'critical');
1043
1044
	// Can we be in here?
1045
	isAllowedTo('move_any', $modSettings['recycle_board']);
1046
1047
	// We need this file.
1048
	require_once($sourcedir . '/MoveTopic.php');
1049
1050
	$unfound_messages = array();
1051
	$topics_to_restore = array();
1052
1053
	// Restoring messages?
1054
	if (!empty($_REQUEST['msgs']))
1055
	{
1056
		$msgs = explode(',', $_REQUEST['msgs']);
1057
		foreach ($msgs as $k => $msg)
1058
			$msgs[$k] = (int) $msg;
1059
1060
		// Get the id_previous_board and id_previous_topic.
1061
		$request = $smcFunc['db_query']('', '
1062
			SELECT m.id_topic, m.id_msg, m.id_board, m.subject, m.id_member, t.id_previous_board, t.id_previous_topic,
1063
				t.id_first_msg, b.count_posts, COALESCE(pt.id_board, 0) AS possible_prev_board
1064
			FROM {db_prefix}messages AS m
1065
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
1066
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
1067
				LEFT JOIN {db_prefix}topics AS pt ON (pt.id_topic = t.id_previous_topic)
1068
			WHERE m.id_msg IN ({array_int:messages})',
1069
			array(
1070
				'messages' => $msgs,
1071
			)
1072
		);
1073
1074
		$actioned_messages = array();
1075
		$previous_topics = array();
1076
		while ($row = $smcFunc['db_fetch_assoc']($request))
1077
		{
1078
			// Restoring the first post means topic.
1079
			if ($row['id_msg'] == $row['id_first_msg'] && $row['id_previous_topic'] == $row['id_topic'])
1080
			{
1081
				$topics_to_restore[] = $row['id_topic'];
1082
				continue;
1083
			}
1084
			// Don't know where it's going?
1085
			if (empty($row['id_previous_topic']))
1086
			{
1087
				$unfound_messages[$row['id_msg']] = $row['subject'];
1088
				continue;
1089
			}
1090
1091
			$previous_topics[] = $row['id_previous_topic'];
1092
			if (empty($actioned_messages[$row['id_previous_topic']]))
1093
				$actioned_messages[$row['id_previous_topic']] = array(
1094
					'msgs' => array(),
1095
					'count_posts' => $row['count_posts'],
1096
					'subject' => $row['subject'],
1097
					'previous_board' => $row['id_previous_board'],
1098
					'possible_prev_board' => $row['possible_prev_board'],
1099
					'current_topic' => $row['id_topic'],
1100
					'current_board' => $row['id_board'],
1101
					'members' => array(),
1102
				);
1103
1104
			$actioned_messages[$row['id_previous_topic']]['msgs'][$row['id_msg']] = $row['subject'];
1105
			if ($row['id_member'])
1106
				$actioned_messages[$row['id_previous_topic']]['members'][] = $row['id_member'];
1107
		}
1108
		$smcFunc['db_free_result']($request);
1109
1110
		// Check for topics we are going to fully restore.
1111
		foreach ($actioned_messages as $topic => $data)
1112
			if (in_array($topic, $topics_to_restore))
1113
				unset($actioned_messages[$topic]);
1114
1115
		// Load any previous topics to check they exist.
1116
		if (!empty($previous_topics))
1117
		{
1118
			$request = $smcFunc['db_query']('', '
1119
				SELECT t.id_topic, t.id_board, m.subject
1120
				FROM {db_prefix}topics AS t
1121
					INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
1122
				WHERE t.id_topic IN ({array_int:previous_topics})',
1123
				array(
1124
					'previous_topics' => $previous_topics,
1125
				)
1126
			);
1127
			$previous_topics = array();
1128
			while ($row = $smcFunc['db_fetch_assoc']($request))
1129
				$previous_topics[$row['id_topic']] = array(
1130
					'board' => $row['id_board'],
1131
					'subject' => $row['subject'],
1132
				);
1133
			$smcFunc['db_free_result']($request);
1134
		}
1135
1136
		// Restore each topic.
1137
		$messages = array();
1138
		foreach ($actioned_messages as $topic => $data)
1139
		{
1140
			// If we have topics we are going to restore the whole lot ignore them.
1141
			if (in_array($topic, $topics_to_restore))
1142
			{
1143
				unset($actioned_messages[$topic]);
1144
				continue;
1145
			}
1146
1147
			// Move the posts back then!
1148
			if (isset($previous_topics[$topic]))
1149
			{
1150
				mergePosts(array_keys($data['msgs']), $data['current_topic'], $topic);
1151
				// Log em.
1152
				logAction('restore_posts', array('topic' => $topic, 'subject' => $previous_topics[$topic]['subject'], 'board' => empty($data['previous_board']) ? $data['possible_prev_board'] : $data['previous_board']));
1153
				$messages = array_merge(array_keys($data['msgs']), $messages);
1154
			}
1155
			else
1156
			{
1157
				foreach ($data['msgs'] as $msg)
1158
					$unfound_messages[$msg['id']] = $msg['subject'];
1159
			}
1160
		}
1161
	}
1162
1163
	// Now any topics?
1164
	if (!empty($_REQUEST['topics']))
1165
	{
1166
		$topics = explode(',', $_REQUEST['topics']);
1167
		foreach ($topics as $id)
1168
			$topics_to_restore[] = (int) $id;
1169
	}
1170
1171
	if (!empty($topics_to_restore))
1172
	{
1173
		// Lets get the data for these topics.
1174
		$request = $smcFunc['db_query']('', '
1175
			SELECT t.id_topic, t.id_previous_board, t.id_board, t.id_first_msg, m.subject
1176
			FROM {db_prefix}topics AS t
1177
				INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
1178
			WHERE t.id_topic IN ({array_int:topics})',
1179
			array(
1180
				'topics' => $topics_to_restore,
1181
			)
1182
		);
1183
		while ($row = $smcFunc['db_fetch_assoc']($request))
1184
		{
1185
			// We can only restore if the previous board is set.
1186
			if (empty($row['id_previous_board']))
1187
			{
1188
				$unfound_messages[$row['id_first_msg']] = $row['subject'];
1189
				continue;
1190
			}
1191
1192
			// Ok we got here so me move them from here to there.
1193
			moveTopics($row['id_topic'], $row['id_previous_board']);
1194
1195
			// Lets see if the board that we are returning to has post count enabled.
1196
			$request2 = $smcFunc['db_query']('', '
1197
				SELECT count_posts
1198
				FROM {db_prefix}boards
1199
				WHERE id_board = {int:board}',
1200
				array(
1201
					'board' => $row['id_previous_board'],
1202
				)
1203
			);
1204
			list ($count_posts) = $smcFunc['db_fetch_row']($request2);
1205
			$smcFunc['db_free_result']($request2);
1206
1207
			if (empty($count_posts))
1208
			{
1209
				// Lets get the members that need their post count restored.
1210
				$request2 = $smcFunc['db_query']('', '
1211
					SELECT id_member, COUNT(*) AS post_count
1212
					FROM {db_prefix}messages
1213
					WHERE id_topic = {int:topic}
1214
						AND approved = {int:is_approved}
1215
					GROUP BY id_member',
1216
					array(
1217
						'topic' => $row['id_topic'],
1218
						'is_approved' => 1,
1219
					)
1220
				);
1221
1222
				while ($member = $smcFunc['db_fetch_assoc']($request2))
1223
					updateMemberData($member['id_member'], array('posts' => 'posts + ' . $member['post_count']));
1224
				$smcFunc['db_free_result']($request2);
1225
			}
1226
1227
			// Log it.
1228
			logAction('restore_topic', array('topic' => $row['id_topic'], 'board' => $row['id_board'], 'board_to' => $row['id_previous_board']));
1229
		}
1230
		$smcFunc['db_free_result']($request);
1231
	}
1232
1233
	// Didn't find some things?
1234
	if (!empty($unfound_messages))
1235
		fatal_lang_error('restore_not_found', false, array(implode('<br>', $unfound_messages)));
1236
1237
	// Just send them to the index if they get here.
1238
	redirectexit();
1239
}
1240
1241
/**
1242
 * Take a load of messages from one place and stick them in a topic
1243
 *
1244
 * @param array $msgs The IDs of the posts to merge
1245
 * @param integer $from_topic The ID of the topic the messages were originally in
1246
 * @param integer $target_topic The ID of the topic the messages are being merged into
1247
 */
1248
function mergePosts($msgs, $from_topic, $target_topic)
1249
{
1250
	global $smcFunc, $sourcedir;
1251
1252
	//!!! This really needs to be rewritten to take a load of messages from ANY topic, it's also inefficient.
1253
1254
	// Is it an array?
1255
	if (!is_array($msgs))
0 ignored issues
show
introduced by
The condition is_array($msgs) is always true.
Loading history...
1256
		$msgs = array($msgs);
1257
1258
	// Lets make sure they are int.
1259
	foreach ($msgs as $key => $msg)
1260
		$msgs[$key] = (int) $msg;
1261
1262
	// Get the source information.
1263
	$request = $smcFunc['db_query']('', '
1264
		SELECT t.id_board, t.id_first_msg, t.num_replies, t.unapproved_posts
1265
		FROM {db_prefix}topics AS t
1266
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
1267
		WHERE t.id_topic = {int:from_topic}',
1268
		array(
1269
			'from_topic' => $from_topic,
1270
		)
1271
	);
1272
	list ($from_board, $from_first_msg, $from_replies, $from_unapproved_posts) = $smcFunc['db_fetch_row']($request);
1273
	$smcFunc['db_free_result']($request);
1274
1275
	// Get some target topic and board stats.
1276
	$request = $smcFunc['db_query']('', '
1277
		SELECT t.id_board, t.id_first_msg, t.num_replies, t.unapproved_posts, b.count_posts
1278
		FROM {db_prefix}topics AS t
1279
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
1280
		WHERE t.id_topic = {int:target_topic}',
1281
		array(
1282
			'target_topic' => $target_topic,
1283
		)
1284
	);
1285
	list ($target_board, $target_first_msg, $target_replies, $target_unapproved_posts, $count_posts) = $smcFunc['db_fetch_row']($request);
1286
	$smcFunc['db_free_result']($request);
1287
1288
	// Lets see if the board that we are returning to has post count enabled.
1289
	if (empty($count_posts))
1290
	{
1291
		// Lets get the members that need their post count restored.
1292
		$request = $smcFunc['db_query']('', '
1293
			SELECT id_member
1294
			FROM {db_prefix}messages
1295
			WHERE id_msg IN ({array_int:messages})
1296
				AND approved = {int:is_approved}',
1297
			array(
1298
				'messages' => $msgs,
1299
				'is_approved' => 1,
1300
			)
1301
		);
1302
1303
		while ($row = $smcFunc['db_fetch_assoc']($request))
1304
			updateMemberData($row['id_member'], array('posts' => '+'));
1305
	}
1306
1307
	// Time to move the messages.
1308
	$smcFunc['db_query']('', '
1309
		UPDATE {db_prefix}messages
1310
		SET
1311
			id_topic = {int:target_topic},
1312
			id_board = {int:target_board}
1313
		WHERE id_msg IN({array_int:msgs})',
1314
		array(
1315
			'target_topic' => $target_topic,
1316
			'target_board' => $target_board,
1317
			'msgs' => $msgs,
1318
		)
1319
	);
1320
1321
	// Fix the id_first_msg and id_last_msg for the target topic.
1322
	$target_topic_data = array(
1323
		'num_replies' => 0,
1324
		'unapproved_posts' => 0,
1325
		'id_first_msg' => 9999999999,
1326
	);
1327
	$request = $smcFunc['db_query']('', '
1328
		SELECT MIN(id_msg) AS id_first_msg, MAX(id_msg) AS id_last_msg, COUNT(*) AS message_count, approved
1329
		FROM {db_prefix}messages
1330
		WHERE id_topic = {int:target_topic}
1331
		GROUP BY id_topic, approved
1332
		ORDER BY approved ASC
1333
		LIMIT 2',
1334
		array(
1335
			'target_topic' => $target_topic,
1336
		)
1337
	);
1338
	while ($row = $smcFunc['db_fetch_assoc']($request))
1339
	{
1340
		if ($row['id_first_msg'] < $target_topic_data['id_first_msg'])
1341
			$target_topic_data['id_first_msg'] = $row['id_first_msg'];
1342
		$target_topic_data['id_last_msg'] = $row['id_last_msg'];
1343
		if (!$row['approved'])
1344
			$target_topic_data['unapproved_posts'] = $row['message_count'];
1345
		else
1346
			$target_topic_data['num_replies'] = max(0, $row['message_count'] - 1);
1347
	}
1348
	$smcFunc['db_free_result']($request);
1349
1350
	// We have a new post count for the board.
1351
	$smcFunc['db_query']('', '
1352
		UPDATE {db_prefix}boards
1353
		SET
1354
			num_posts = num_posts + {int:diff_replies},
1355
			unapproved_posts = unapproved_posts + {int:diff_unapproved_posts}
1356
		WHERE id_board = {int:target_board}',
1357
		array(
1358
			'diff_replies' => $target_topic_data['num_replies'] - $target_replies, // Lets keep in mind that the first message in a topic counts towards num_replies in a board.
1359
			'diff_unapproved_posts' => $target_topic_data['unapproved_posts'] - $target_unapproved_posts,
1360
			'target_board' => $target_board,
1361
		)
1362
	);
1363
1364
	// In some cases we merged the only post in a topic so the topic data is left behind in the topic table.
1365
	$request = $smcFunc['db_query']('', '
1366
		SELECT id_topic
1367
		FROM {db_prefix}messages
1368
		WHERE id_topic = {int:from_topic}',
1369
		array(
1370
			'from_topic' => $from_topic,
1371
		)
1372
	);
1373
1374
	// Remove the topic if it doesn't have any messages.
1375
	$topic_exists = true;
1376
	if ($smcFunc['db_num_rows']($request) == 0)
1377
	{
1378
		removeTopics($from_topic, false, true);
1379
		$topic_exists = false;
1380
	}
1381
	$smcFunc['db_free_result']($request);
1382
1383
	// Recycled topic.
1384
	if ($topic_exists == 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...
1385
	{
1386
		// Fix the id_first_msg and id_last_msg for the source topic.
1387
		$source_topic_data = array(
1388
			'num_replies' => 0,
1389
			'unapproved_posts' => 0,
1390
			'id_first_msg' => 9999999999,
1391
		);
1392
		$request = $smcFunc['db_query']('', '
1393
			SELECT MIN(id_msg) AS id_first_msg, MAX(id_msg) AS id_last_msg, COUNT(*) AS message_count, approved, subject
1394
			FROM {db_prefix}messages
1395
			WHERE id_topic = {int:from_topic}
1396
			GROUP BY id_topic, approved
1397
			ORDER BY approved ASC
1398
			LIMIT 2',
1399
			array(
1400
				'from_topic' => $from_topic,
1401
			)
1402
		);
1403
		while ($row = $smcFunc['db_fetch_assoc']($request))
1404
		{
1405
			if ($row['id_first_msg'] < $source_topic_data['id_first_msg'])
1406
				$source_topic_data['id_first_msg'] = $row['id_first_msg'];
1407
			$source_topic_data['id_last_msg'] = $row['id_last_msg'];
1408
			if (!$row['approved'])
1409
				$source_topic_data['unapproved_posts'] = $row['message_count'];
1410
			else
1411
				$source_topic_data['num_replies'] = max(0, $row['message_count'] - 1);
1412
		}
1413
		$smcFunc['db_free_result']($request);
1414
1415
		// Update the topic details for the source topic.
1416
		$smcFunc['db_query']('', '
1417
			UPDATE {db_prefix}topics
1418
			SET
1419
				id_first_msg = {int:id_first_msg},
1420
				id_last_msg = {int:id_last_msg},
1421
				num_replies = {int:num_replies},
1422
				unapproved_posts = {int:unapproved_posts}
1423
			WHERE id_topic = {int:from_topic}',
1424
			array(
1425
				'id_first_msg' => $source_topic_data['id_first_msg'],
1426
				'id_last_msg' => $source_topic_data['id_last_msg'],
1427
				'num_replies' => $source_topic_data['num_replies'],
1428
				'unapproved_posts' => $source_topic_data['unapproved_posts'],
1429
				'from_topic' => $from_topic,
1430
			)
1431
		);
1432
1433
		// We have a new post count for the source board.
1434
		$smcFunc['db_query']('', '
1435
			UPDATE {db_prefix}boards
1436
			SET
1437
				num_posts = num_posts + {int:diff_replies},
1438
				unapproved_posts = unapproved_posts + {int:diff_unapproved_posts}
1439
			WHERE id_board = {int:from_board}',
1440
			array(
1441
				'diff_replies' => $source_topic_data['num_replies'] - $from_replies, // Lets keep in mind that the first message in a topic counts towards num_replies in a board.
1442
				'diff_unapproved_posts' => $source_topic_data['unapproved_posts'] - $from_unapproved_posts,
1443
				'from_board' => $from_board,
1444
			)
1445
		);
1446
	}
1447
1448
	// Finally get around to updating the destination topic, now all indexes etc on the source are fixed.
1449
	$smcFunc['db_query']('', '
1450
		UPDATE {db_prefix}topics
1451
		SET
1452
			id_first_msg = {int:id_first_msg},
1453
			id_last_msg = {int:id_last_msg},
1454
			num_replies = {int:num_replies},
1455
			unapproved_posts = {int:unapproved_posts}
1456
		WHERE id_topic = {int:target_topic}',
1457
		array(
1458
			'id_first_msg' => $target_topic_data['id_first_msg'],
1459
			'id_last_msg' => $target_topic_data['id_last_msg'],
1460
			'num_replies' => $target_topic_data['num_replies'],
1461
			'unapproved_posts' => $target_topic_data['unapproved_posts'],
1462
			'target_topic' => $target_topic,
1463
		)
1464
	);
1465
1466
	// Need it to update some stats.
1467
	require_once($sourcedir . '/Subs-Post.php');
1468
1469
	// Update stats.
1470
	updateStats('topic');
1471
	updateStats('message');
1472
1473
	// Subject cache?
1474
	$cache_updates = array();
1475
	if ($target_first_msg != $target_topic_data['id_first_msg'])
1476
		$cache_updates[] = $target_topic_data['id_first_msg'];
1477
	if (!empty($source_topic_data['id_first_msg']) && $from_first_msg != $source_topic_data['id_first_msg'])
1478
		$cache_updates[] = $source_topic_data['id_first_msg'];
1479
1480
	if (!empty($cache_updates))
1481
	{
1482
		$request = $smcFunc['db_query']('', '
1483
			SELECT id_topic, subject
1484
			FROM {db_prefix}messages
1485
			WHERE id_msg IN ({array_int:first_messages})',
1486
			array(
1487
				'first_messages' => $cache_updates,
1488
			)
1489
		);
1490
		while ($row = $smcFunc['db_fetch_assoc']($request))
1491
			updateStats('subject', $row['id_topic'], $row['subject']);
1492
		$smcFunc['db_free_result']($request);
1493
	}
1494
1495
	updateLastMessages(array($from_board, $target_board));
1496
}
1497
1498
/**
1499
 * Try to determine if the topic has already been deleted by another user.
1500
 *
1501
 * @return bool False if it can't be deleted (recycling not enabled or no recycling board set), true if we've confirmed it can be deleted. Dies with an error if it's already been deleted.
1502
 */
1503
function removeDeleteConcurrence()
1504
{
1505
	global $modSettings, $board, $scripturl, $context;
1506
1507
	// No recycle no need to go further
1508
	if (empty($modSettings['recycle_enable']) || empty($modSettings['recycle_board']))
1509
		return false;
1510
1511
	// If it's confirmed go on and delete (from recycle)
1512
	if (isset($_GET['confirm_delete']))
1513
		return true;
1514
1515
	if (empty($board))
1516
		return false;
1517
1518
	if ($modSettings['recycle_board'] != $board)
1519
		return true;
1520
	elseif (isset($_REQUEST['msg']))
1521
		$confirm_url = $scripturl . '?action=deletemsg;confirm_delete;topic=' . $context['current_topic'] . '.0;msg=' . $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'];
1522
	else
1523
		$confirm_url = $scripturl . '?action=removetopic2;confirm_delete;topic=' . $context['current_topic'] . '.0;' . $context['session_var'] . '=' . $context['session_id'];
1524
1525
	fatal_lang_error('post_already_deleted', false, array($confirm_url));
1526
}
1527
1528
?>