MergeExecute()   F
last analyzed

Complexity

Conditions 91
Paths > 20000

Size

Total Lines 758
Code Lines 374

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 91
eloc 374
nop 1
dl 0
loc 758
rs 0
c 0
b 0
f 0
nc 67190784

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
 * Handle merging and splitting of topics
5
 *
6
 * Simple Machines Forum (SMF)
7
 *
8
 * @package SMF
9
 * @author Simple Machines http://www.simplemachines.org
10
 * @copyright 2019 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 RC2
14
 *
15
 * Original module by Mach8 - We'll never forget you.
16
 */
17
18
if (!defined('SMF'))
19
	die('No direct access...');
20
21
/**
22
 * splits a topic into two topics.
23
 * delegates to the other functions (based on the URL parameter 'sa').
24
 * loads the SplitTopics template.
25
 * requires the split_any permission.
26
 * is accessed with ?action=splittopics.
27
 */
28
function SplitTopics()
29
{
30
	global $topic, $sourcedir;
31
32
	// And... which topic were you splitting, again?
33
	if (empty($topic))
34
		fatal_lang_error('numbers_one_to_nine', false);
35
36
	// Are you allowed to split topics?
37
	isAllowedTo('split_any');
38
39
	// Load up the "dependencies" - the template, getMsgMemberID(), and sendNotifications().
40
	if (!isset($_REQUEST['xml']))
41
		loadTemplate('SplitTopics');
42
	require_once($sourcedir . '/Subs-Boards.php');
43
	require_once($sourcedir . '/Subs-Post.php');
44
45
	$subActions = array(
46
		'selectTopics' => 'SplitSelectTopics',
47
		'execute' => 'SplitExecute',
48
		'index' => 'SplitIndex',
49
		'splitSelection' => 'SplitSelectionExecute',
50
	);
51
52
	// ?action=splittopics;sa=LETSBREAKIT won't work, sorry.
53
	if (empty($_REQUEST['sa']) || !isset($subActions[$_REQUEST['sa']]))
54
		SplitIndex();
55
56
	else
57
		call_helper($subActions[$_REQUEST['sa']]);
58
}
59
60
/**
61
 * screen shown before the actual split.
62
 * is accessed with ?action=splittopics;sa=index.
63
 * default sub action for ?action=splittopics.
64
 * uses 'ask' sub template of the SplitTopics template.
65
 * redirects to SplitSelectTopics if the message given turns out to be
66
 * the first message of a topic.
67
 * shows the user three ways to split the current topic.
68
 */
69
function SplitIndex()
70
{
71
	global $txt, $topic, $context, $smcFunc, $modSettings;
72
73
	// Validate "at".
74
	if (empty($_GET['at']))
75
		fatal_lang_error('numbers_one_to_nine', false);
76
	$_GET['at'] = (int) $_GET['at'];
77
78
	// Retrieve the subject and stuff of the specific topic/message.
79
	$request = $smcFunc['db_query']('', '
80
		SELECT m.subject, t.num_replies, t.unapproved_posts, t.id_first_msg, t.approved
81
		FROM {db_prefix}messages AS m
82
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
83
		WHERE m.id_msg = {int:split_at}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
84
			AND m.approved = 1') . '
85
			AND m.id_topic = {int:current_topic}
86
		LIMIT 1',
87
		array(
88
			'current_topic' => $topic,
89
			'split_at' => $_GET['at'],
90
		)
91
	);
92
	if ($smcFunc['db_num_rows']($request) == 0)
93
		fatal_lang_error('cant_find_messages');
94
95
	list ($_REQUEST['subname'], $num_replies, $unapproved_posts, $id_first_msg, $approved) = $smcFunc['db_fetch_row']($request);
96
	$smcFunc['db_free_result']($request);
97
98
	// If not approved validate they can see it.
99
	if ($modSettings['postmod_active'] && !$approved)
100
		isAllowedTo('approve_posts');
101
102
	// If this topic has unapproved posts, we need to count them too...
103
	if ($modSettings['postmod_active'] && allowedTo('approve_posts'))
104
		$num_replies += $unapproved_posts - ($approved ? 0 : 1);
105
106
	// Check if there is more than one message in the topic.  (there should be.)
107
	if ($num_replies < 1)
108
		fatal_lang_error('topic_one_post', false);
109
110
	// Check if this is the first message in the topic (if so, the first and second option won't be available)
111
	if ($id_first_msg == $_GET['at'])
112
		return SplitSelectTopics();
0 ignored issues
show
Bug introduced by
Are you sure the usage of SplitSelectTopics() 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...
113
114
	// Basic template information....
115
	$context['message'] = array(
116
		'id' => $_GET['at'],
117
		'subject' => $_REQUEST['subname']
118
	);
119
	$context['sub_template'] = 'ask';
120
	$context['page_title'] = $txt['split'];
121
}
122
123
/**
124
 * do the actual split.
125
 * is accessed with ?action=splittopics;sa=execute.
126
 * uses the main SplitTopics template.
127
 * supports three ways of splitting:
128
 * (1) only one message is split off.
129
 * (2) all messages after and including a given message are split off.
130
 * (3) select topics to split (redirects to SplitSelectTopics()).
131
 * uses splitTopic function to do the actual splitting.
132
 */
133
function SplitExecute()
134
{
135
	global $txt, $topic, $context, $smcFunc;
136
137
	// Check the session to make sure they meant to do this.
138
	checkSession();
139
140
	// Clean up the subject.
141
	if (!isset($_POST['subname']) || $_POST['subname'] == '')
142
		$_POST['subname'] = $txt['new_topic'];
143
144
	// Redirect to the selector if they chose selective.
145
	if ($_POST['step2'] == 'selective')
146
		redirectexit ('action=splittopics;sa=selectTopics;subname=' . $_POST['subname'] . ';topic=' . $topic . '.0;start2=0');
147
148
	$_POST['at'] = (int) $_POST['at'];
149
	$messagesToBeSplit = array();
150
151
	if ($_POST['step2'] == 'afterthis')
152
	{
153
		// Fetch the message IDs of the topic that are at or after the message.
154
		$request = $smcFunc['db_query']('', '
155
			SELECT id_msg
156
			FROM {db_prefix}messages
157
			WHERE id_topic = {int:current_topic}
158
				AND id_msg >= {int:split_at}',
159
			array(
160
				'current_topic' => $topic,
161
				'split_at' => $_POST['at'],
162
			)
163
		);
164
		while ($row = $smcFunc['db_fetch_assoc']($request))
165
			$messagesToBeSplit[] = $row['id_msg'];
166
167
		$smcFunc['db_free_result']($request);
168
	}
169
	// Only the selected message has to be split. That should be easy.
170
	elseif ($_POST['step2'] == 'onlythis')
171
		$messagesToBeSplit[] = $_POST['at'];
172
	// There's another action?!
173
	else
174
		fatal_lang_error('no_access', false);
175
176
	$context['old_topic'] = $topic;
177
	$context['new_topic'] = splitTopic($topic, $messagesToBeSplit, $_POST['subname']);
178
	$context['page_title'] = $txt['split'];
179
}
180
181
/**
182
 * allows the user to select the messages to be split.
183
 * is accessed with ?action=splittopics;sa=selectTopics.
184
 * uses 'select' sub template of the SplitTopics template or (for
185
 * XMLhttp) the 'split' sub template of the Xml template.
186
 * supports XMLhttp for adding/removing a message to the selection.
187
 * uses a session variable to store the selected topics.
188
 * shows two independent page indexes for both the selected and
189
 * not-selected messages (;topic=1.x;start2=y).
190
 */
191
function SplitSelectTopics()
192
{
193
	global $txt, $scripturl, $topic, $context, $modSettings, $original_msgs, $smcFunc, $options;
194
195
	$context['page_title'] = $txt['split'] . ' - ' . $txt['select_split_posts'];
196
197
	// Haven't selected anything have we?
198
	$_SESSION['split_selection'][$topic] = empty($_SESSION['split_selection'][$topic]) ? array() : $_SESSION['split_selection'][$topic];
199
200
	// This is a special case for split topics from quick-moderation checkboxes
201
	if (isset($_REQUEST['subname_enc']))
202
		$_REQUEST['subname'] = urldecode($_REQUEST['subname_enc']);
203
204
	$context['not_selected'] = array(
205
		'num_messages' => 0,
206
		'start' => empty($_REQUEST['start']) ? 0 : (int) $_REQUEST['start'],
207
		'messages' => array(),
208
	);
209
210
	$context['selected'] = array(
211
		'num_messages' => 0,
212
		'start' => empty($_REQUEST['start2']) ? 0 : (int) $_REQUEST['start2'],
213
		'messages' => array(),
214
	);
215
216
	$context['topic'] = array(
217
		'id' => $topic,
218
		'subject' => urlencode($_REQUEST['subname']),
219
	);
220
221
	// Some stuff for our favorite template.
222
	$context['new_subject'] = $_REQUEST['subname'];
223
224
	// Using the "select" sub template.
225
	$context['sub_template'] = isset($_REQUEST['xml']) ? 'split' : 'select';
226
227
	// Are we using a custom messages per page?
228
	$context['messages_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
229
230
	// Get the message ID's from before the move.
231
	if (isset($_REQUEST['xml']))
232
	{
233
		$original_msgs = array(
234
			'not_selected' => array(),
235
			'selected' => array(),
236
		);
237
		$request = $smcFunc['db_query']('', '
238
			SELECT id_msg
239
			FROM {db_prefix}messages
240
			WHERE id_topic = {int:current_topic}' . (empty($_SESSION['split_selection'][$topic]) ? '' : '
241
				AND id_msg NOT IN ({array_int:no_split_msgs})') . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
242
				AND approved = {int:is_approved}') . '
243
				' . (empty($options['view_newest_first']) ? '' : 'ORDER BY id_msg DESC') . '
244
				LIMIT {int:start}, {int:messages_per_page}',
245
			array(
246
				'current_topic' => $topic,
247
				'no_split_msgs' => empty($_SESSION['split_selection'][$topic]) ? array() : $_SESSION['split_selection'][$topic],
248
				'is_approved' => 1,
249
				'start' => $context['not_selected']['start'],
250
				'messages_per_page' => $context['messages_per_page'],
251
			)
252
		);
253
		// You can't split the last message off.
254
		if (empty($context['not_selected']['start']) && $smcFunc['db_num_rows']($request) <= 1 && $_REQUEST['move'] == 'down')
255
			$_REQUEST['move'] = '';
256
		while ($row = $smcFunc['db_fetch_assoc']($request))
257
			$original_msgs['not_selected'][] = $row['id_msg'];
258
		$smcFunc['db_free_result']($request);
259
		if (!empty($_SESSION['split_selection'][$topic]))
260
		{
261
			$request = $smcFunc['db_query']('', '
262
				SELECT id_msg
263
				FROM {db_prefix}messages
264
				WHERE id_topic = {int:current_topic}
265
					AND id_msg IN ({array_int:split_msgs})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
266
					AND approved = {int:is_approved}') . '
267
				' . (empty($options['view_newest_first']) ? '' : 'ORDER BY id_msg DESC') . '
268
				LIMIT {int:start}, {int:messages_per_page}',
269
				array(
270
					'current_topic' => $topic,
271
					'split_msgs' => $_SESSION['split_selection'][$topic],
272
					'is_approved' => 1,
273
					'start' => $context['selected']['start'],
274
					'messages_per_page' => $context['messages_per_page'],
275
				)
276
			);
277
			while ($row = $smcFunc['db_fetch_assoc']($request))
278
				$original_msgs['selected'][] = $row['id_msg'];
279
280
			$smcFunc['db_free_result']($request);
281
		}
282
	}
283
284
	// (De)select a message..
285
	if (!empty($_REQUEST['move']))
286
	{
287
		$_REQUEST['msg'] = (int) $_REQUEST['msg'];
288
289
		if ($_REQUEST['move'] == 'reset')
290
			$_SESSION['split_selection'][$topic] = array();
291
		elseif ($_REQUEST['move'] == 'up')
292
			$_SESSION['split_selection'][$topic] = array_diff($_SESSION['split_selection'][$topic], array($_REQUEST['msg']));
293
		else
294
			$_SESSION['split_selection'][$topic][] = $_REQUEST['msg'];
295
	}
296
297
	// Make sure the selection is still accurate.
298
	if (!empty($_SESSION['split_selection'][$topic]))
299
	{
300
		$request = $smcFunc['db_query']('', '
301
			SELECT id_msg
302
			FROM {db_prefix}messages
303
			WHERE id_topic = {int:current_topic}
304
				AND id_msg IN ({array_int:split_msgs})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
305
				AND approved = {int:is_approved}'),
306
			array(
307
				'current_topic' => $topic,
308
				'split_msgs' => $_SESSION['split_selection'][$topic],
309
				'is_approved' => 1,
310
			)
311
		);
312
		$_SESSION['split_selection'][$topic] = array();
313
314
		while ($row = $smcFunc['db_fetch_assoc']($request))
315
			$_SESSION['split_selection'][$topic][] = $row['id_msg'];
316
317
		$smcFunc['db_free_result']($request);
318
	}
319
320
	// Get the number of messages (not) selected to be split.
321
	$request = $smcFunc['db_query']('', '
322
		SELECT ' . (empty($_SESSION['split_selection'][$topic]) ? '0' : 'm.id_msg IN ({array_int:split_msgs})') . ' AS is_selected, COUNT(*) AS num_messages
323
		FROM {db_prefix}messages AS m
324
		WHERE m.id_topic = {int:current_topic}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
325
			AND approved = {int:is_approved}') . (empty($_SESSION['split_selection'][$topic]) ? '' : '
326
		GROUP BY is_selected'),
327
		array(
328
			'current_topic' => $topic,
329
			'split_msgs' => !empty($_SESSION['split_selection'][$topic]) ? $_SESSION['split_selection'][$topic] : array(),
330
			'is_approved' => 1,
331
		)
332
	);
333
	while ($row = $smcFunc['db_fetch_assoc']($request))
334
		$context[empty($row['is_selected']) || $row['is_selected'] == 'f' ? 'not_selected' : 'selected']['num_messages'] = $row['num_messages'];
335
	$smcFunc['db_free_result']($request);
336
337
	// Fix an oversized starting page (to make sure both pageindexes are properly set).
338
	if ($context['selected']['start'] >= $context['selected']['num_messages'])
339
		$context['selected']['start'] = $context['selected']['num_messages'] <= $context['messages_per_page'] ? 0 : ($context['selected']['num_messages'] - (($context['selected']['num_messages'] % $context['messages_per_page']) == 0 ? $context['messages_per_page'] : ($context['selected']['num_messages'] % $context['messages_per_page'])));
340
341
	// Build a page list of the not-selected topics...
342
	$context['not_selected']['page_index'] = constructPageIndex($scripturl . '?action=splittopics;sa=selectTopics;subname=' . strtr(urlencode($_REQUEST['subname']), array('%' => '%%')) . ';topic=' . $topic . '.%1$d;start2=' . $context['selected']['start'], $context['not_selected']['start'], $context['not_selected']['num_messages'], $context['messages_per_page'], true);
343
	// ...and one of the selected topics.
344
	$context['selected']['page_index'] = constructPageIndex($scripturl . '?action=splittopics;sa=selectTopics;subname=' . strtr(urlencode($_REQUEST['subname']), array('%' => '%%')) . ';topic=' . $topic . '.' . $context['not_selected']['start'] . ';start2=%1$d', $context['selected']['start'], $context['selected']['num_messages'], $context['messages_per_page'], true);
345
346
	// Get the messages and stick them into an array.
347
	$request = $smcFunc['db_query']('', '
348
		SELECT m.subject, COALESCE(mem.real_name, m.poster_name) AS real_name, m.poster_time, m.body, m.id_msg, m.smileys_enabled
349
		FROM {db_prefix}messages AS m
350
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
351
		WHERE m.id_topic = {int:current_topic}' . (empty($_SESSION['split_selection'][$topic]) ? '' : '
352
			AND id_msg NOT IN ({array_int:no_split_msgs})') . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
353
			AND approved = {int:is_approved}') . '
354
			' . (empty($options['view_newest_first']) ? '' : 'ORDER BY m.id_msg DESC') . '
355
			LIMIT {int:start}, {int:messages_per_page}',
356
		array(
357
			'current_topic' => $topic,
358
			'no_split_msgs' => !empty($_SESSION['split_selection'][$topic]) ? $_SESSION['split_selection'][$topic] : array(),
359
			'is_approved' => 1,
360
			'start' => $context['not_selected']['start'],
361
			'messages_per_page' => $context['messages_per_page'],
362
		)
363
	);
364
	$context['messages'] = array();
365
	for ($counter = 0; $row = $smcFunc['db_fetch_assoc']($request); $counter++)
366
	{
367
		censorText($row['subject']);
368
		censorText($row['body']);
369
370
		$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
371
372
		$context['not_selected']['messages'][$row['id_msg']] = array(
373
			'id' => $row['id_msg'],
374
			'subject' => $row['subject'],
375
			'time' => timeformat($row['poster_time']),
376
			'timestamp' => forum_time(true, $row['poster_time']),
377
			'body' => $row['body'],
378
			'poster' => $row['real_name'],
379
		);
380
	}
381
	$smcFunc['db_free_result']($request);
382
383
	// Now get the selected messages.
384
	if (!empty($_SESSION['split_selection'][$topic]))
385
	{
386
		// Get the messages and stick them into an array.
387
		$request = $smcFunc['db_query']('', '
388
			SELECT m.subject, COALESCE(mem.real_name, m.poster_name) AS real_name,  m.poster_time, m.body, m.id_msg, m.smileys_enabled
389
			FROM {db_prefix}messages AS m
390
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
391
			WHERE m.id_topic = {int:current_topic}
392
				AND m.id_msg IN ({array_int:split_msgs})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
393
				AND approved = {int:is_approved}') . '
394
			' . (empty($options['view_newest_first']) ? '' : 'ORDER BY m.id_msg DESC') . '
395
			LIMIT {int:start}, {int:messages_per_page}',
396
			array(
397
				'current_topic' => $topic,
398
				'split_msgs' => $_SESSION['split_selection'][$topic],
399
				'is_approved' => 1,
400
				'start' => $context['selected']['start'],
401
				'messages_per_page' => $context['messages_per_page'],
402
			)
403
		);
404
		$context['messages'] = array();
405
		for ($counter = 0; $row = $smcFunc['db_fetch_assoc']($request); $counter++)
406
		{
407
			censorText($row['subject']);
408
			censorText($row['body']);
409
410
			$row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
411
412
			$context['selected']['messages'][$row['id_msg']] = array(
413
				'id' => $row['id_msg'],
414
				'subject' => $row['subject'],
415
				'time' => timeformat($row['poster_time']),
416
				'timestamp' => forum_time(true, $row['poster_time']),
417
				'body' => $row['body'],
418
				'poster' => $row['real_name']
419
			);
420
		}
421
		$smcFunc['db_free_result']($request);
422
	}
423
424
	// The XMLhttp method only needs the stuff that changed, so let's compare.
425
	if (isset($_REQUEST['xml']))
426
	{
427
		$changes = array(
428
			'remove' => array(
429
				'not_selected' => array_diff($original_msgs['not_selected'], array_keys($context['not_selected']['messages'])),
430
				'selected' => array_diff($original_msgs['selected'], array_keys($context['selected']['messages'])),
431
			),
432
			'insert' => array(
433
				'not_selected' => array_diff(array_keys($context['not_selected']['messages']), $original_msgs['not_selected']),
434
				'selected' => array_diff(array_keys($context['selected']['messages']), $original_msgs['selected']),
435
			),
436
		);
437
438
		$context['changes'] = array();
439
		foreach ($changes as $change_type => $change_array)
440
			foreach ($change_array as $section => $msg_array)
441
			{
442
				if (empty($msg_array))
443
					continue;
444
445
				foreach ($msg_array as $id_msg)
446
				{
447
					$context['changes'][$change_type . $id_msg] = array(
448
						'id' => $id_msg,
449
						'type' => $change_type,
450
						'section' => $section,
451
					);
452
					if ($change_type == 'insert')
453
						$context['changes']['insert' . $id_msg]['insert_value'] = $context[$section]['messages'][$id_msg];
454
				}
455
			}
456
	}
457
}
458
459
/**
460
 * do the actual split of a selection of topics.
461
 * is accessed with ?action=splittopics;sa=splitSelection.
462
 * uses the main SplitTopics template.
463
 * uses splitTopic function to do the actual splitting.
464
 */
465
function SplitSelectionExecute()
466
{
467
	global $txt, $topic, $context;
468
469
	// Make sure the session id was passed with post.
470
	checkSession();
471
472
	// Default the subject in case it's blank.
473
	if (!isset($_POST['subname']) || $_POST['subname'] == '')
474
		$_POST['subname'] = $txt['new_topic'];
475
476
	// You must've selected some messages!  Can't split out none!
477
	if (empty($_SESSION['split_selection'][$topic]))
478
		fatal_lang_error('no_posts_selected', false);
479
480
	$context['old_topic'] = $topic;
481
	$context['new_topic'] = splitTopic($topic, $_SESSION['split_selection'][$topic], $_POST['subname']);
482
	$context['page_title'] = $txt['split'];
483
}
484
485
/**
486
 * general function to split off a topic.
487
 * creates a new topic and moves the messages with the IDs in
488
 * array messagesToBeSplit to the new topic.
489
 * the subject of the newly created topic is set to 'newSubject'.
490
 * marks the newly created message as read for the user splitting it.
491
 * updates the statistics to reflect a newly created topic.
492
 * logs the action in the moderation log.
493
 * a notification is sent to all users monitoring this topic.
494
 *
495
 * @param int $split1_ID_TOPIC The ID of the topic we're splitting
496
 * @param array $splitMessages The IDs of the messages being split
497
 * @param string $new_subject The subject of the new topic
498
 * @return int The ID of the new split topic.
499
 */
500
function splitTopic($split1_ID_TOPIC, $splitMessages, $new_subject)
501
{
502
	global $smcFunc, $txt, $sourcedir;
503
504
	// Nothing to split?
505
	if (empty($splitMessages))
506
		fatal_lang_error('no_posts_selected', false);
507
508
	// Get some board info.
509
	$request = $smcFunc['db_query']('', '
510
		SELECT id_board, approved
511
		FROM {db_prefix}topics
512
		WHERE id_topic = {int:id_topic}
513
		LIMIT 1',
514
		array(
515
			'id_topic' => $split1_ID_TOPIC,
516
		)
517
	);
518
	list ($id_board, $split1_approved) = $smcFunc['db_fetch_row']($request);
519
	$smcFunc['db_free_result']($request);
520
521
	// Find the new first and last not in the list. (old topic)
522
	$request = $smcFunc['db_query']('', '
523
		SELECT
524
			MIN(m.id_msg) AS myid_first_msg, MAX(m.id_msg) AS myid_last_msg, COUNT(*) AS message_count, m.approved
525
		FROM {db_prefix}messages AS m
526
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:id_topic})
527
		WHERE m.id_msg NOT IN ({array_int:no_msg_list})
528
			AND m.id_topic = {int:id_topic}
529
		GROUP BY m.approved
530
		ORDER BY m.approved DESC
531
		LIMIT 2',
532
		array(
533
			'id_topic' => $split1_ID_TOPIC,
534
			'no_msg_list' => $splitMessages,
535
		)
536
	);
537
	// You can't select ALL the messages!
538
	if ($smcFunc['db_num_rows']($request) == 0)
539
		fatal_lang_error('selected_all_posts', false);
540
541
	$split1_first_msg = null;
542
	$split1_last_msg = null;
543
544
	while ($row = $smcFunc['db_fetch_assoc']($request))
545
	{
546
		// Get the right first and last message dependant on approved state...
547
		if (empty($split1_first_msg) || $row['myid_first_msg'] < $split1_first_msg)
548
			$split1_first_msg = $row['myid_first_msg'];
549
		if (empty($split1_last_msg) || $row['approved'])
550
			$split1_last_msg = $row['myid_last_msg'];
551
552
		// Get the counts correct...
553
		if ($row['approved'])
554
		{
555
			$split1_replies = $row['message_count'] - 1;
556
			$split1_unapprovedposts = 0;
557
		}
558
		else
559
		{
560
			if (!isset($split1_replies))
561
				$split1_replies = 0;
562
			// If the topic isn't approved then num replies must go up by one... as first post wouldn't be counted.
563
			elseif (!$split1_approved)
564
				$split1_replies++;
565
566
			$split1_unapprovedposts = $row['message_count'];
567
		}
568
	}
569
	$smcFunc['db_free_result']($request);
570
	$split1_firstMem = getMsgMemberID($split1_first_msg);
571
	$split1_lastMem = getMsgMemberID($split1_last_msg);
572
573
	// Find the first and last in the list. (new topic)
574
	$request = $smcFunc['db_query']('', '
575
		SELECT MIN(id_msg) AS myid_first_msg, MAX(id_msg) AS myid_last_msg, COUNT(*) AS message_count, approved
576
		FROM {db_prefix}messages
577
		WHERE id_msg IN ({array_int:msg_list})
578
			AND id_topic = {int:id_topic}
579
		GROUP BY id_topic, approved
580
		ORDER BY approved DESC
581
		LIMIT 2',
582
		array(
583
			'msg_list' => $splitMessages,
584
			'id_topic' => $split1_ID_TOPIC,
585
		)
586
	);
587
	while ($row = $smcFunc['db_fetch_assoc']($request))
588
	{
589
		// As before get the right first and last message dependant on approved state...
590
		if (empty($split2_first_msg) || $row['myid_first_msg'] < $split2_first_msg)
591
			$split2_first_msg = $row['myid_first_msg'];
592
		if (empty($split2_last_msg) || $row['approved'])
593
			$split2_last_msg = $row['myid_last_msg'];
594
595
		// Then do the counts again...
596
		if ($row['approved'])
597
		{
598
			$split2_approved = true;
599
			$split2_replies = $row['message_count'] - 1;
600
			$split2_unapprovedposts = 0;
601
		}
602
		else
603
		{
604
			// Should this one be approved??
605
			if ($split2_first_msg == $row['myid_first_msg'])
606
				$split2_approved = false;
607
608
			if (!isset($split2_replies))
609
				$split2_replies = 0;
610
			// As before, fix number of replies.
611
			elseif (!$split2_approved)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $split2_approved does not seem to be defined for all execution paths leading up to this point.
Loading history...
612
				$split2_replies++;
613
614
			$split2_unapprovedposts = $row['message_count'];
615
		}
616
	}
617
	$smcFunc['db_free_result']($request);
618
	$split2_firstMem = getMsgMemberID($split2_first_msg);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $split2_first_msg does not seem to be defined for all execution paths leading up to this point.
Loading history...
619
	$split2_lastMem = getMsgMemberID($split2_last_msg);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $split2_last_msg does not seem to be defined for all execution paths leading up to this point.
Loading history...
620
621
	// No database changes yet, so let's double check to see if everything makes at least a little sense.
622
	if ($split1_first_msg <= 0 || $split1_last_msg <= 0 || $split2_first_msg <= 0 || $split2_last_msg <= 0 || $split1_replies < 0 || $split2_replies < 0 || $split1_unapprovedposts < 0 || $split2_unapprovedposts < 0 || !isset($split1_approved) || !isset($split2_approved))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $split2_replies does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $split1_replies does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $split2_unapprovedposts does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $split1_unapprovedposts does not seem to be defined for all execution paths leading up to this point.
Loading history...
623
		fatal_lang_error('cant_find_messages');
624
625
	// You cannot split off the first message of a topic.
626
	if ($split1_first_msg > $split2_first_msg)
627
		fatal_lang_error('split_first_post', false);
628
629
	// We're off to insert the new topic!  Use 0 for now to avoid UNIQUE errors.
630
	$split2_ID_TOPIC = $smcFunc['db_insert']('',
631
		'{db_prefix}topics',
632
		array(
633
			'id_board' => 'int',
634
			'id_member_started' => 'int',
635
			'id_member_updated' => 'int',
636
			'id_first_msg' => 'int',
637
			'id_last_msg' => 'int',
638
			'num_replies' => 'int',
639
			'unapproved_posts' => 'int',
640
			'approved' => 'int',
641
			'is_sticky' => 'int',
642
		),
643
		array(
644
			(int) $id_board, $split2_firstMem, $split2_lastMem, 0,
645
			0, $split2_replies, $split2_unapprovedposts, (int) $split2_approved, 0,
646
		),
647
		array('id_topic'),
648
		1
649
	);
650
	if ($split2_ID_TOPIC <= 0)
651
		fatal_lang_error('cant_insert_topic');
652
653
	// Move the messages over to the other topic.
654
	$new_subject = strtr($smcFunc['htmltrim']($smcFunc['htmlspecialchars']($new_subject)), array("\r" => '', "\n" => '', "\t" => ''));
655
	// Check the subject length.
656
	if ($smcFunc['strlen']($new_subject) > 100)
657
		$new_subject = $smcFunc['substr']($new_subject, 0, 100);
658
	// Valid subject?
659
	if ($new_subject != '')
660
	{
661
		$smcFunc['db_query']('', '
662
			UPDATE {db_prefix}messages
663
			SET
664
				id_topic = {int:id_topic},
665
				subject = CASE WHEN id_msg = {int:split_first_msg} THEN {string:new_subject} ELSE {string:new_subject_replies} END
666
			WHERE id_msg IN ({array_int:split_msgs})',
667
			array(
668
				'split_msgs' => $splitMessages,
669
				'id_topic' => $split2_ID_TOPIC,
670
				'new_subject' => $new_subject,
671
				'split_first_msg' => $split2_first_msg,
672
				'new_subject_replies' => $txt['response_prefix'] . $new_subject,
673
			)
674
		);
675
676
		// Cache the new topics subject... we can do it now as all the subjects are the same!
677
		updateStats('subject', $split2_ID_TOPIC, $new_subject);
678
	}
679
680
	// Any associated reported posts better follow...
681
	$smcFunc['db_query']('', '
682
		UPDATE {db_prefix}log_reported
683
		SET id_topic = {int:id_topic}
684
		WHERE id_msg IN ({array_int:split_msgs})',
685
		array(
686
			'split_msgs' => $splitMessages,
687
			'id_topic' => $split2_ID_TOPIC,
688
		)
689
	);
690
691
	// Mess with the old topic's first, last, and number of messages.
692
	$smcFunc['db_query']('', '
693
		UPDATE {db_prefix}topics
694
		SET
695
			num_replies = {int:num_replies},
696
			id_first_msg = {int:id_first_msg},
697
			id_last_msg = {int:id_last_msg},
698
			id_member_started = {int:id_member_started},
699
			id_member_updated = {int:id_member_updated},
700
			unapproved_posts = {int:unapproved_posts}
701
		WHERE id_topic = {int:id_topic}',
702
		array(
703
			'num_replies' => $split1_replies,
704
			'id_first_msg' => $split1_first_msg,
705
			'id_last_msg' => $split1_last_msg,
706
			'id_member_started' => $split1_firstMem,
707
			'id_member_updated' => $split1_lastMem,
708
			'unapproved_posts' => $split1_unapprovedposts,
709
			'id_topic' => $split1_ID_TOPIC,
710
		)
711
	);
712
713
	// Now, put the first/last message back to what they should be.
714
	$smcFunc['db_query']('', '
715
		UPDATE {db_prefix}topics
716
		SET
717
			id_first_msg = {int:id_first_msg},
718
			id_last_msg = {int:id_last_msg}
719
		WHERE id_topic = {int:id_topic}',
720
		array(
721
			'id_first_msg' => $split2_first_msg,
722
			'id_last_msg' => $split2_last_msg,
723
			'id_topic' => $split2_ID_TOPIC,
724
		)
725
	);
726
727
	// If the new topic isn't approved ensure the first message flags this just in case.
728
	if (!$split2_approved)
729
		$smcFunc['db_query']('', '
730
			UPDATE {db_prefix}messages
731
			SET approved = {int:approved}
732
			WHERE id_msg = {int:id_msg}
733
				AND id_topic = {int:id_topic}',
734
			array(
735
				'approved' => 0,
736
				'id_msg' => $split2_first_msg,
737
				'id_topic' => $split2_ID_TOPIC,
738
			)
739
		);
740
741
	// The board has more topics now (Or more unapproved ones!).
742
	$smcFunc['db_query']('', '
743
		UPDATE {db_prefix}boards
744
		SET ' . ($split2_approved ? '
745
			num_topics = num_topics + 1' : '
746
			unapproved_topics = unapproved_topics + 1') . '
747
		WHERE id_board = {int:id_board}',
748
		array(
749
			'id_board' => $id_board,
750
		)
751
	);
752
753
	// Copy log topic entries.
754
	// @todo This should really be chunked.
755
	$request = $smcFunc['db_query']('', '
756
		SELECT id_member, id_msg, unwatched
757
		FROM {db_prefix}log_topics
758
		WHERE id_topic = {int:id_topic}',
759
		array(
760
			'id_topic' => (int) $split1_ID_TOPIC,
761
		)
762
	);
763
	if ($smcFunc['db_num_rows']($request) > 0)
764
	{
765
		$replaceEntries = array();
766
		while ($row = $smcFunc['db_fetch_assoc']($request))
767
			$replaceEntries[] = array($row['id_member'], $split2_ID_TOPIC, $row['id_msg'], $row['unwatched']);
768
769
		$smcFunc['db_insert']('ignore',
770
			'{db_prefix}log_topics',
771
			array('id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'unwatched' => 'int'),
772
			$replaceEntries,
773
			array('id_member', 'id_topic')
774
		);
775
		unset($replaceEntries);
776
	}
777
	$smcFunc['db_free_result']($request);
778
779
	// Housekeeping.
780
	updateStats('topic');
781
	updateLastMessages($id_board);
782
783
	logAction('split', array('topic' => $split1_ID_TOPIC, 'new_topic' => $split2_ID_TOPIC, 'board' => $id_board));
784
785
	// Notify people that this topic has been split?
786
	sendNotifications($split1_ID_TOPIC, 'split');
0 ignored issues
show
Bug introduced by
$split1_ID_TOPIC of type integer is incompatible with the type array expected by parameter $topics of sendNotifications(). ( Ignorable by Annotation )

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

786
	sendNotifications(/** @scrutinizer ignore-type */ $split1_ID_TOPIC, 'split');
Loading history...
787
788
	// If there's a search index that needs updating, update it...
789
	require_once($sourcedir . '/Search.php');
790
	$searchAPI = findSearchAPI();
791
	if (is_callable(array($searchAPI, 'topicSplit')))
792
		$searchAPI->topicSplit($split2_ID_TOPIC, $splitMessages);
0 ignored issues
show
Bug introduced by
The method topicSplit() does not exist on search_api_interface. ( Ignorable by Annotation )

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

792
		$searchAPI->/** @scrutinizer ignore-call */ 
793
              topicSplit($split2_ID_TOPIC, $splitMessages);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
793
794
	// Maybe we want to let an external CMS know about this split
795
	$split1 = array(
796
		'num_replies' => $split1_replies,
797
		'id_first_msg' => $split1_first_msg,
798
		'id_last_msg' => $split1_last_msg,
799
		'id_member_started' => $split1_firstMem,
800
		'id_member_updated' => $split1_lastMem,
801
		'unapproved_posts' => $split1_unapprovedposts,
802
		'id_topic' => $split1_ID_TOPIC,
803
	);
804
	$split2 = array(
805
		'num_replies' => $split2_replies,
806
		'id_first_msg' => $split2_first_msg,
807
		'id_last_msg' => $split2_last_msg,
808
		'id_member_started' => $split2_firstMem,
809
		'id_member_updated' => $split2_lastMem,
810
		'unapproved_posts' => $split2_unapprovedposts,
811
		'id_topic' => $split2_ID_TOPIC,
812
	);
813
	call_integration_hook('integrate_split_topic', array($split1, $split2, $new_subject, $id_board));
814
815
	// Return the ID of the newly created topic.
816
	return $split2_ID_TOPIC;
817
}
818
819
/**
820
 * merges two or more topics into one topic.
821
 * delegates to the other functions (based on the URL parameter sa).
822
 * loads the SplitTopics template.
823
 * requires the merge_any permission.
824
 * is accessed with ?action=mergetopics.
825
 */
826
function MergeTopics()
827
{
828
	// Load the template....
829
	loadTemplate('MoveTopic');
830
831
	$subActions = array(
832
		'done' => 'MergeDone',
833
		'execute' => 'MergeExecute',
834
		'index' => 'MergeIndex',
835
		'options' => 'MergeExecute',
836
	);
837
838
	// ?action=mergetopics;sa=LETSBREAKIT won't work, sorry.
839
	if (empty($_REQUEST['sa']) || !isset($subActions[$_REQUEST['sa']]))
840
		MergeIndex();
841
842
	else
843
		call_helper($subActions[$_REQUEST['sa']]);
844
}
845
846
/**
847
 * allows to pick a topic to merge the current topic with.
848
 * is accessed with ?action=mergetopics;sa=index
849
 * default sub action for ?action=mergetopics.
850
 * uses 'merge' sub template of the MoveTopic template.
851
 * allows to set a different target board.
852
 */
853
function MergeIndex()
854
{
855
	global $txt, $board, $context, $smcFunc, $sourcedir;
856
	global $scripturl, $modSettings;
857
858
	if (!isset($_GET['from']))
859
		fatal_lang_error('no_access', false);
860
	$_GET['from'] = (int) $_GET['from'];
861
862
	$_REQUEST['targetboard'] = isset($_REQUEST['targetboard']) ? (int) $_REQUEST['targetboard'] : $board;
863
	$context['target_board'] = $_REQUEST['targetboard'];
864
865
	// Prepare a handy query bit for approval...
866
	if ($modSettings['postmod_active'])
867
	{
868
		$can_approve_boards = boardsAllowedTo('approve_posts');
869
		$onlyApproved = $can_approve_boards !== array(0) && !in_array($_REQUEST['targetboard'], $can_approve_boards);
870
	}
871
	else
872
		$onlyApproved = false;
873
874
	// How many topics are on this board?  (used for paging.)
875
	$request = $smcFunc['db_query']('', '
876
		SELECT COUNT(*)
877
		FROM {db_prefix}topics AS t
878
		WHERE t.id_board = {int:id_board}' . ($onlyApproved ? '
879
			AND t.approved = {int:is_approved}' : ''),
880
		array(
881
			'id_board' => $_REQUEST['targetboard'],
882
			'is_approved' => 1,
883
		)
884
	);
885
	list ($topiccount) = $smcFunc['db_fetch_row']($request);
886
	$smcFunc['db_free_result']($request);
887
888
	// Make the page list.
889
	$context['page_index'] = constructPageIndex($scripturl . '?action=mergetopics;from=' . $_GET['from'] . ';targetboard=' . $_REQUEST['targetboard'] . ';board=' . $board . '.%1$d', $_REQUEST['start'], $topiccount, $modSettings['defaultMaxTopics'], true);
890
891
	// Get the topic's subject.
892
	$request = $smcFunc['db_query']('', '
893
		SELECT m.subject
894
		FROM {db_prefix}topics AS t
895
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
896
		WHERE t.id_topic = {int:id_topic}
897
			AND t.id_board = {int:current_board}' . ($onlyApproved ? '
898
			AND t.approved = {int:is_approved}' : '') . '
899
		LIMIT 1',
900
		array(
901
			'current_board' => $board,
902
			'id_topic' => $_GET['from'],
903
			'is_approved' => 1,
904
		)
905
	);
906
	if ($smcFunc['db_num_rows']($request) == 0)
907
		fatal_lang_error('no_board');
908
	list ($subject) = $smcFunc['db_fetch_row']($request);
909
	$smcFunc['db_free_result']($request);
910
911
	// Tell the template a few things..
912
	$context['origin_topic'] = $_GET['from'];
913
	$context['origin_subject'] = $subject;
914
	$context['origin_js_subject'] = addcslashes(addslashes($subject), '/');
915
	$context['page_title'] = $txt['merge'];
916
917
	// Check which boards you have merge permissions on.
918
	$merge_boards = boardsAllowedTo('merge_any');
919
920
	if (empty($merge_boards))
921
		fatal_lang_error('cannot_merge_any', 'user');
922
923
	// No sense in loading this if you can only merge on this board
924
	if (count($merge_boards) > 1 || in_array(0, $merge_boards))
925
	{
926
		require_once($sourcedir . '/Subs-MessageIndex.php');
927
928
		// Set up a couple of options for our board list
929
		$options = array(
930
			'not_redirection' => true,
931
			'selected_board' => $context['target_board'],
932
		);
933
934
		// Only include these boards in the list (0 means you're an admin')
935
		if (!in_array(0, $merge_boards))
936
			$options['included_boards'] = $merge_boards;
937
938
		$context['merge_categories'] = getBoardList($options);
939
	}
940
941
	// Get some topics to merge it with.
942
	$request = $smcFunc['db_query']('', '
943
		SELECT t.id_topic, m.subject, m.id_member, COALESCE(mem.real_name, m.poster_name) AS poster_name
944
		FROM {db_prefix}topics AS t
945
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
946
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
947
		WHERE t.id_board = {int:id_board}
948
			AND t.id_topic != {int:id_topic}
949
			AND t.id_redirect_topic = {int:not_redirect}' . ($onlyApproved ? '
950
			AND t.approved = {int:is_approved}' : '') . '
951
		ORDER BY {raw:sort}
952
		LIMIT {int:offset}, {int:limit}',
953
		array(
954
			'id_board' => $_REQUEST['targetboard'],
955
			'id_topic' => $_GET['from'],
956
			'sort' => 't.is_sticky DESC, t.id_last_msg DESC',
957
			'offset' => $_REQUEST['start'],
958
			'limit' => $modSettings['defaultMaxTopics'],
959
			'is_approved' => 1,
960
			'not_redirect' => 0,
961
		)
962
	);
963
	$context['topics'] = array();
964
	while ($row = $smcFunc['db_fetch_assoc']($request))
965
	{
966
		censorText($row['subject']);
967
968
		$context['topics'][] = array(
969
			'id' => $row['id_topic'],
970
			'poster' => array(
971
				'id' => $row['id_member'],
972
				'name' => $row['poster_name'],
973
				'href' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member'],
974
				'link' => empty($row['id_member']) ? $row['poster_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '" target="_blank" rel="noopener">' . $row['poster_name'] . '</a>'
975
			),
976
			'subject' => $row['subject'],
977
			'js_subject' => addcslashes(addslashes($row['subject']), '/')
978
		);
979
	}
980
	$smcFunc['db_free_result']($request);
981
982
	if (empty($context['topics']) && count($merge_boards) <= 1 && !in_array(0, $merge_boards))
983
		fatal_lang_error('merge_need_more_topics');
984
985
	$context['sub_template'] = 'merge';
986
}
987
988
/**
989
 * set merge options and do the actual merge of two or more topics.
990
 *
991
 * the merge options screen:
992
 * * shows topics to be merged and allows to set some merge options.
993
 * * is accessed by ?action=mergetopics;sa=options.and can also internally be called by QuickModeration() (Subs-Boards.php).
994
 * * uses 'merge_extra_options' sub template of the MoveTopic template.
995
 *
996
 * the actual merge:
997
 * * is accessed with ?action=mergetopics;sa=execute.
998
 * * updates the statistics to reflect the merge.
999
 * * logs the action in the moderation log.
1000
 * * sends a notification is sent to all users monitoring this topic.
1001
 * * redirects to ?action=mergetopics;sa=done.
1002
 *
1003
 * @param array $topics The IDs of the topics to merge
1004
 */
1005
function MergeExecute($topics = array())
1006
{
1007
	global $user_info, $txt, $context, $scripturl, $sourcedir;
1008
	global $smcFunc, $language, $modSettings;
1009
1010
	// Check the session.
1011
	checkSession('request');
1012
1013
	// Handle URLs from MergeIndex.
1014
	if (!empty($_GET['from']) && !empty($_GET['to']))
1015
		$topics = array((int) $_GET['from'], (int) $_GET['to']);
1016
1017
	// If we came from a form, the topic IDs came by post.
1018
	if (!empty($_POST['topics']) && is_array($_POST['topics']))
1019
		$topics = $_POST['topics'];
1020
1021
	// There's nothing to merge with just one topic...
1022
	if (empty($topics) || !is_array($topics) || count($topics) == 1)
1023
		fatal_lang_error('merge_need_more_topics');
1024
1025
	// Make sure every topic is numeric, or some nasty things could be done with the DB.
1026
	foreach ($topics as $id => $topic)
1027
		$topics[$id] = (int) $topic;
1028
1029
	// Joy of all joys, make sure they're not messing about with unapproved topics they can't see :P
1030
	if ($modSettings['postmod_active'])
1031
		$can_approve_boards = boardsAllowedTo('approve_posts');
1032
1033
	// Get info about the topics and polls that will be merged.
1034
	$request = $smcFunc['db_query']('', '
1035
		SELECT
1036
			t.id_topic, t.id_board, t.id_poll, t.num_views, t.is_sticky, t.approved, t.num_replies, t.unapproved_posts, t.id_redirect_topic,
1037
			m1.subject, m1.poster_time AS time_started, COALESCE(mem1.id_member, 0) AS id_member_started, COALESCE(mem1.real_name, m1.poster_name) AS name_started,
1038
			m2.poster_time AS time_updated, COALESCE(mem2.id_member, 0) AS id_member_updated, COALESCE(mem2.real_name, m2.poster_name) AS name_updated
1039
		FROM {db_prefix}topics AS t
1040
			INNER JOIN {db_prefix}messages AS m1 ON (m1.id_msg = t.id_first_msg)
1041
			INNER JOIN {db_prefix}messages AS m2 ON (m2.id_msg = t.id_last_msg)
1042
			LEFT JOIN {db_prefix}members AS mem1 ON (mem1.id_member = m1.id_member)
1043
			LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = m2.id_member)
1044
		WHERE t.id_topic IN ({array_int:topic_list})
1045
		ORDER BY t.id_first_msg
1046
		LIMIT {int:limit}',
1047
		array(
1048
			'topic_list' => $topics,
1049
			'limit' => count($topics),
1050
		)
1051
	);
1052
	if ($smcFunc['db_num_rows']($request) < 2)
1053
		fatal_lang_error('no_topic_id');
1054
1055
	$num_views = 0;
1056
	$is_sticky = 0;
1057
	$boardTotals = array();
1058
	$boards = array();
1059
	$polls = array();
1060
	$firstTopic = 0;
1061
	$context['is_approved'] = 1;
1062
	$lowestTopicId = 0;
1063
	$lowestTopicBoard = 0;
1064
1065
	while ($row = $smcFunc['db_fetch_assoc']($request))
1066
	{
1067
		// Sorry, redirection topics can't be merged
1068
		if (!empty($row['id_redirect_topic']))
1069
			fatal_lang_error('cannot_merge_redirect', false);
1070
1071
		// Make a note for the board counts...
1072
		if (!isset($boardTotals[$row['id_board']]))
1073
			$boardTotals[$row['id_board']] = array(
1074
				'posts' => 0,
1075
				'topics' => 0,
1076
				'unapproved_posts' => 0,
1077
				'unapproved_topics' => 0
1078
			);
1079
1080
		// We can't see unapproved topics here?
1081
		if ($modSettings['postmod_active'] && !$row['approved'] && $can_approve_boards != array(0) && in_array($row['id_board'], $can_approve_boards))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $can_approve_boards does not seem to be defined for all execution paths leading up to this point.
Loading history...
1082
		{
1083
			unset($topics[$row['id_topic']]); // If we can't see it, we should not merge it and not adjust counts! Instead skip it.
1084
			continue;
1085
		}
1086
		elseif (!$row['approved'])
1087
			$boardTotals[$row['id_board']]['unapproved_topics']++;
1088
		else
1089
			$boardTotals[$row['id_board']]['topics']++;
1090
1091
		$boardTotals[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts'];
1092
		$boardTotals[$row['id_board']]['posts'] += $row['num_replies'] + ($row['approved'] ? 1 : 0);
1093
1094
		// In the case of making a redirect, the topic count goes up by one due to the redirect topic.
1095
		if (isset($_POST['postRedirect']))
1096
			$boardTotals[$row['id_board']]['topics']--;
1097
1098
		$topic_data[$row['id_topic']] = array(
1099
			'id' => $row['id_topic'],
1100
			'board' => $row['id_board'],
1101
			'poll' => $row['id_poll'],
1102
			'num_views' => $row['num_views'],
1103
			'subject' => $row['subject'],
1104
			'started' => array(
1105
				'time' => timeformat($row['time_started']),
1106
				'timestamp' => forum_time(true, $row['time_started']),
1107
				'href' => empty($row['id_member_started']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member_started'],
1108
				'link' => empty($row['id_member_started']) ? $row['name_started'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_started'] . '">' . $row['name_started'] . '</a>'
1109
			),
1110
			'updated' => array(
1111
				'time' => timeformat($row['time_updated']),
1112
				'timestamp' => forum_time(true, $row['time_updated']),
1113
				'href' => empty($row['id_member_updated']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member_updated'],
1114
				'link' => empty($row['id_member_updated']) ? $row['name_updated'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_updated'] . '">' . $row['name_updated'] . '</a>'
1115
			),
1116
			'approved' => $row['approved']
1117
		);
1118
		$num_views += $row['num_views'];
1119
		$boards[] = $row['id_board'];
1120
1121
		// If there's no poll, id_poll == 0...
1122
		if ($row['id_poll'] > 0)
1123
			$polls[] = $row['id_poll'];
1124
		// Store the id_topic with the lowest id_first_msg.
1125
		if (empty($firstTopic))
1126
			$firstTopic = $row['id_topic'];
1127
1128
		// Lowest topic id gets selected as surviving topic id. We need to store this board so we can adjust the topic count (This one will not have a redirect topic)
1129
		if ($row['id_topic'] < $lowestTopicId || empty($lowestTopicId))
1130
		{
1131
			$lowestTopicId = $row['id_topic'];
1132
			$lowestTopicBoard = $row['id_board'];
1133
		}
1134
1135
		$is_sticky = max($is_sticky, $row['is_sticky']);
1136
	}
1137
	$smcFunc['db_free_result']($request);
1138
1139
	// If we didn't get any topics then they've been messing with unapproved stuff.
1140
	if (empty($topic_data))
1141
		fatal_lang_error('no_topic_id');
1142
1143
	if (isset($_POST['postRedirect']) && !empty($lowestTopicBoard))
1144
		$boardTotals[$lowestTopicBoard]['topics']++;
1145
1146
	// Will this be approved?
1147
	$context['is_approved'] = $topic_data[$firstTopic]['approved'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $topic_data does not seem to be defined for all execution paths leading up to this point.
Loading history...
1148
1149
	$boards = array_values(array_unique($boards));
1150
1151
	// The parameters of MergeExecute were set, so this must've been an internal call.
1152
	if (!empty($topics))
1153
	{
1154
		isAllowedTo('merge_any', $boards);
1155
		loadTemplate('MoveTopic');
1156
	}
1157
1158
	// Get the boards a user is allowed to merge in.
1159
	$merge_boards = boardsAllowedTo('merge_any');
1160
	if (empty($merge_boards))
1161
		fatal_lang_error('cannot_merge_any', 'user');
1162
1163
	// Make sure they can see all boards....
1164
	$request = $smcFunc['db_query']('', '
1165
		SELECT b.id_board
1166
		FROM {db_prefix}boards AS b
1167
		WHERE b.id_board IN ({array_int:boards})
1168
			AND {query_see_board}' . (!in_array(0, $merge_boards) ? '
1169
			AND b.id_board IN ({array_int:merge_boards})' : '') . '
1170
		LIMIT {int:limit}',
1171
		array(
1172
			'boards' => $boards,
1173
			'merge_boards' => $merge_boards,
1174
			'limit' => count($boards),
1175
		)
1176
	);
1177
	// If the number of boards that's in the output isn't exactly the same as we've put in there, you're in trouble.
1178
	if ($smcFunc['db_num_rows']($request) != count($boards))
1179
		fatal_lang_error('no_board');
1180
	$smcFunc['db_free_result']($request);
1181
1182
	if (empty($_REQUEST['sa']) || $_REQUEST['sa'] == 'options')
1183
	{
1184
		if (count($polls) > 1)
1185
		{
1186
			$request = $smcFunc['db_query']('', '
1187
				SELECT t.id_topic, t.id_poll, m.subject, p.question
1188
				FROM {db_prefix}polls AS p
1189
					INNER JOIN {db_prefix}topics AS t ON (t.id_poll = p.id_poll)
1190
					INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
1191
				WHERE p.id_poll IN ({array_int:polls})
1192
				LIMIT {int:limit}',
1193
				array(
1194
					'polls' => $polls,
1195
					'limit' => count($polls),
1196
				)
1197
			);
1198
			while ($row = $smcFunc['db_fetch_assoc']($request))
1199
				$context['polls'][] = array(
1200
					'id' => $row['id_poll'],
1201
					'topic' => array(
1202
						'id' => $row['id_topic'],
1203
						'subject' => $row['subject']
1204
					),
1205
					'question' => $row['question'],
1206
					'selected' => $row['id_topic'] == $firstTopic
1207
				);
1208
			$smcFunc['db_free_result']($request);
1209
		}
1210
		if (count($boards) > 1)
1211
		{
1212
			$request = $smcFunc['db_query']('', '
1213
				SELECT id_board, name
1214
				FROM {db_prefix}boards
1215
				WHERE id_board IN ({array_int:boards})
1216
				ORDER BY name
1217
				LIMIT {int:limit}',
1218
				array(
1219
					'boards' => $boards,
1220
					'limit' => count($boards),
1221
				)
1222
			);
1223
			while ($row = $smcFunc['db_fetch_assoc']($request))
1224
				$context['boards'][] = array(
1225
					'id' => $row['id_board'],
1226
					'name' => $row['name'],
1227
					'selected' => $row['id_board'] == $topic_data[$firstTopic]['board']
1228
				);
1229
			$smcFunc['db_free_result']($request);
1230
		}
1231
1232
		$context['topics'] = $topic_data;
1233
		foreach ($topic_data as $id => $topic)
1234
			$context['topics'][$id]['selected'] = $topic['id'] == $firstTopic;
1235
1236
		$context['page_title'] = $txt['merge'];
1237
		$context['sub_template'] = 'merge_extra_options';
1238
		return;
1239
	}
1240
1241
	// Determine target board.
1242
	$target_board = count($boards) > 1 ? (int) $_REQUEST['board'] : $boards[0];
1243
	if (!in_array($target_board, $boards))
1244
		fatal_lang_error('no_board');
1245
1246
	// Determine which poll will survive and which polls won't.
1247
	$target_poll = count($polls) > 1 ? (int) $_POST['poll'] : (count($polls) == 1 ? $polls[0] : 0);
1248
	if ($target_poll > 0 && !in_array($target_poll, $polls))
1249
		fatal_lang_error('no_access', false);
1250
	$deleted_polls = empty($target_poll) ? $polls : array_diff($polls, array($target_poll));
1251
1252
	// Determine the subject of the newly merged topic - was a custom subject specified?
1253
	if (empty($_POST['subject']) && isset($_POST['custom_subject']) && $_POST['custom_subject'] != '')
1254
	{
1255
		$target_subject = strtr($smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['custom_subject'])), array("\r" => '', "\n" => '', "\t" => ''));
1256
		// Keep checking the length.
1257
		if ($smcFunc['strlen']($target_subject) > 100)
1258
			$target_subject = $smcFunc['substr']($target_subject, 0, 100);
1259
1260
		// Nothing left - odd but pick the first topics subject.
1261
		if ($target_subject == '')
1262
			$target_subject = $topic_data[$firstTopic]['subject'];
1263
	}
1264
	// A subject was selected from the list.
1265
	elseif (!empty($topic_data[(int) $_POST['subject']]['subject']))
1266
		$target_subject = $topic_data[(int) $_POST['subject']]['subject'];
1267
	// Nothing worked? Just take the subject of the first message.
1268
	else
1269
		$target_subject = $topic_data[$firstTopic]['subject'];
1270
1271
	// Get the first and last message and the number of messages....
1272
	$request = $smcFunc['db_query']('', '
1273
		SELECT approved, MIN(id_msg) AS first_msg, MAX(id_msg) AS last_msg, COUNT(*) AS message_count
1274
		FROM {db_prefix}messages
1275
		WHERE id_topic IN ({array_int:topics})
1276
		GROUP BY approved
1277
		ORDER BY approved DESC',
1278
		array(
1279
			'topics' => $topics,
1280
		)
1281
	);
1282
	$topic_approved = 1;
1283
	$first_msg = 0;
1284
	while ($row = $smcFunc['db_fetch_assoc']($request))
1285
	{
1286
		// If this is approved, or is fully unapproved.
1287
		if ($row['approved'] || !empty($first_msg))
1288
		{
1289
			$first_msg = $row['first_msg'];
1290
			$last_msg = $row['last_msg'];
1291
			if ($row['approved'])
1292
			{
1293
				$num_replies = $row['message_count'] - 1;
1294
				$num_unapproved = 0;
1295
			}
1296
			else
1297
			{
1298
				$topic_approved = 0;
1299
				$num_replies = 0;
1300
				$num_unapproved = $row['message_count'];
1301
			}
1302
		}
1303
		else
1304
		{
1305
			// If this has a lower first_msg then the first post is not approved and hence the number of replies was wrong!
1306
			if ($first_msg > $row['first_msg'])
1307
			{
1308
				$first_msg = $row['first_msg'];
1309
				$num_replies++;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $num_replies does not seem to be defined for all execution paths leading up to this point.
Loading history...
1310
				$topic_approved = 0;
1311
			}
1312
			$num_unapproved = $row['message_count'];
1313
		}
1314
	}
1315
	$smcFunc['db_free_result']($request);
1316
1317
	// Ensure we have a board stat for the target board.
1318
	if (!isset($boardTotals[$target_board]))
1319
	{
1320
		$boardTotals[$target_board] = array(
1321
			'posts' => 0,
1322
			'topics' => 0,
1323
			'unapproved_posts' => 0,
1324
			'unapproved_topics' => 0
1325
		);
1326
	}
1327
1328
	// Fix the topic count stuff depending on what the new one counts as.
1329
	$boardTotals[$target_board][(!$topic_approved) ? 'unapproved_topics' : 'topics']--;
1330
1331
	$boardTotals[$target_board]['unapproved_posts'] -= $num_unapproved;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $num_unapproved does not seem to be defined for all execution paths leading up to this point.
Loading history...
1332
	$boardTotals[$target_board]['posts'] -= $topic_approved ? $num_replies + 1 : $num_replies;
1333
1334
	// Get the member ID of the first and last message.
1335
	$request = $smcFunc['db_query']('', '
1336
		SELECT id_member
1337
		FROM {db_prefix}messages
1338
		WHERE id_msg IN ({int:first_msg}, {int:last_msg})
1339
		ORDER BY id_msg
1340
		LIMIT 2',
1341
		array(
1342
			'first_msg' => $first_msg,
1343
			'last_msg' => $last_msg,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $last_msg does not seem to be defined for all execution paths leading up to this point.
Loading history...
1344
		)
1345
	);
1346
	list ($member_started) = $smcFunc['db_fetch_row']($request);
1347
	list ($member_updated) = $smcFunc['db_fetch_row']($request);
1348
1349
	// First and last message are the same, so only row was returned.
1350
	if ($member_updated === null)
1351
		$member_updated = $member_started;
1352
1353
	$smcFunc['db_free_result']($request);
1354
1355
	// Obtain all the message ids we are going to affect.
1356
	$affected_msgs = array();
1357
	$request = $smcFunc['db_query']('', '
1358
		SELECT id_msg
1359
		FROM {db_prefix}messages
1360
		WHERE id_topic IN ({array_int:topic_list})',
1361
		array(
1362
			'topic_list' => $topics,
1363
		)
1364
	);
1365
	while ($row = $smcFunc['db_fetch_row']($request))
1366
		$affected_msgs[] = $row[0];
1367
	$smcFunc['db_free_result']($request);
1368
1369
	// Assign the first topic ID to be the merged topic.
1370
	$id_topic = min($topics);
1371
1372
	$deleted_topics = array_diff($topics, array($id_topic));
1373
	$updated_topics = array();
1374
1375
	// Create stub topics out of the remaining topics.
1376
	// We don't want the search index data though (For non-redirect merges).
1377
	if (!isset($_POST['postRedirect']))
1378
	{
1379
		$smcFunc['db_query']('', '
1380
			DELETE FROM {db_prefix}log_search_subjects
1381
			WHERE id_topic IN ({array_int:deleted_topics})',
1382
			array(
1383
				'deleted_topics' => $deleted_topics,
1384
			)
1385
		);
1386
	}
1387
1388
	require_once($sourcedir . '/Subs-Post.php');
1389
	$posterOptions = array(
1390
		'id' => $user_info['id'],
1391
		'update_post_count' => false,
1392
	);
1393
1394
	// We only need to do this if we're posting redirection topics...
1395
	if (isset($_POST['postRedirect']))
1396
	{
1397
		$_POST['reason'] = $smcFunc['htmlspecialchars']($_POST['reason'], ENT_QUOTES);
1398
		preparsecode($_POST['reason']);
1399
1400
		// Add a URL onto the message.
1401
		$reason = strtr($_POST['reason'], array(
1402
			$txt['movetopic_auto_topic'] => '[iurl=' . $scripturl . '?topic=' . $id_topic . '.0]' . $target_subject . '[/iurl]'
1403
		));
1404
1405
		// Automatically remove this MERGED redirection topic in the future?
1406
		$redirect_expires = !empty($_POST['redirect_expires']) ? ((int) ($_POST['redirect_expires'] * 60) + time()) : 0;
1407
1408
		// Redirect to the MERGED topic from topic list?
1409
		$redirect_topic = isset($_POST['redirect_topic']) ? $id_topic : 0;
1410
1411
		foreach ($deleted_topics as $this_old_topic)
1412
		{
1413
			$redirect_subject = sprintf($txt['merged_subject'], $topic_data[$this_old_topic]['subject']);
1414
1415
			$msgOptions = array(
1416
				'icon' => 'moved',
1417
				'subject' => $redirect_subject,
1418
				'body' => $reason,
1419
				'approved' => 1,
1420
			);
1421
			$topicOptions = array(
1422
				'id' => $this_old_topic,
1423
				'is_approved' => true,
1424
				'lock_mode' => 1,
1425
				'board' => $topic_data[$this_old_topic]['board'],
1426
				'mark_as_read' => true,
1427
			);
1428
1429
			// So we have to make the post. We need to do *this* here so we don't foul up indexes later
1430
			// and we have to fix them up later once everything else has happened.
1431
			if (createPost($msgOptions, $topicOptions, $posterOptions))
1432
			{
1433
				$updated_topics[$this_old_topic] = $msgOptions['id'];
1434
			}
1435
1436
			// Update subject search index
1437
			updateStats('subject', $this_old_topic, $redirect_subject);
1438
		}
1439
	}
1440
1441
	// Grab the response prefix (like 'Re: ') in the default forum language.
1442
	if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix')))
1443
	{
1444
		if ($language === $user_info['language'])
1445
			$context['response_prefix'] = $txt['response_prefix'];
1446
		else
1447
		{
1448
			loadLanguage('index', $language, false);
1449
			$context['response_prefix'] = $txt['response_prefix'];
1450
			loadLanguage('index');
1451
		}
1452
		cache_put_data('response_prefix', $context['response_prefix'], 600);
1453
	}
1454
1455
	// Change the topic IDs of all messages that will be merged.  Also adjust subjects if 'enforce subject' was checked.
1456
	$smcFunc['db_query']('', '
1457
		UPDATE {db_prefix}messages
1458
		SET
1459
			id_topic = {int:id_topic},
1460
			id_board = {int:target_board}' . (empty($_POST['enforce_subject']) ? '' : ',
1461
			subject = {string:subject}') . '
1462
		WHERE id_topic IN ({array_int:topic_list})' . (!empty($updated_topics) ? '
1463
			AND id_msg NOT IN ({array_int:merge_msg})' : ''),
1464
		array(
1465
			'topic_list' => $topics,
1466
			'id_topic' => $id_topic,
1467
			'merge_msg' => $updated_topics,
1468
			'target_board' => $target_board,
1469
			'subject' => $context['response_prefix'] . $target_subject,
1470
		)
1471
	);
1472
1473
	// Any reported posts should reflect the new board.
1474
	$smcFunc['db_query']('', '
1475
		UPDATE {db_prefix}log_reported
1476
		SET
1477
			id_topic = {int:id_topic},
1478
			id_board = {int:target_board}
1479
		WHERE id_topic IN ({array_int:topics_list})',
1480
		array(
1481
			'topics_list' => $topics,
1482
			'id_topic' => $id_topic,
1483
			'target_board' => $target_board,
1484
		)
1485
	);
1486
1487
	// Change the subject of the first message...
1488
	$smcFunc['db_query']('', '
1489
		UPDATE {db_prefix}messages
1490
		SET subject = {string:target_subject}
1491
		WHERE id_msg = {int:first_msg}',
1492
		array(
1493
			'first_msg' => $first_msg,
1494
			'target_subject' => $target_subject,
1495
		)
1496
	);
1497
1498
	// Adjust all calendar events to point to the new topic.
1499
	$smcFunc['db_query']('', '
1500
		UPDATE {db_prefix}calendar
1501
		SET
1502
			id_topic = {int:id_topic},
1503
			id_board = {int:target_board}
1504
		WHERE id_topic IN ({array_int:deleted_topics})',
1505
		array(
1506
			'deleted_topics' => $deleted_topics,
1507
			'id_topic' => $id_topic,
1508
			'target_board' => $target_board,
1509
		)
1510
	);
1511
1512
	// Merge log topic entries.
1513
	// The unwatch setting comes from the oldest topic
1514
	$request = $smcFunc['db_query']('', '
1515
		SELECT id_member, MIN(id_msg) AS new_id_msg, unwatched
1516
		FROM {db_prefix}log_topics
1517
		WHERE id_topic IN ({array_int:topics})
1518
		GROUP BY id_member, unwatched',
1519
		array(
1520
			'topics' => $topics,
1521
		)
1522
	);
1523
	if ($smcFunc['db_num_rows']($request) > 0)
1524
	{
1525
		$replaceEntries = array();
1526
		while ($row = $smcFunc['db_fetch_assoc']($request))
1527
			$replaceEntries[] = array($row['id_member'], $id_topic, $row['new_id_msg'], $row['unwatched']);
1528
1529
		$smcFunc['db_insert']('replace',
1530
			'{db_prefix}log_topics',
1531
			array('id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'unwatched' => 'int'),
1532
			$replaceEntries,
1533
			array('id_member', 'id_topic')
1534
		);
1535
		unset($replaceEntries);
1536
1537
		// Get rid of the old log entries.
1538
		$smcFunc['db_query']('', '
1539
			DELETE FROM {db_prefix}log_topics
1540
			WHERE id_topic IN ({array_int:deleted_topics})',
1541
			array(
1542
				'deleted_topics' => $deleted_topics,
1543
			)
1544
		);
1545
	}
1546
	$smcFunc['db_free_result']($request);
1547
1548
	// Merge topic notifications.
1549
	$notifications = isset($_POST['notifications']) && is_array($_POST['notifications']) ? array_intersect($topics, $_POST['notifications']) : array();
1550
	if (!empty($notifications))
1551
	{
1552
		$request = $smcFunc['db_query']('', '
1553
			SELECT id_member, MAX(sent) AS sent
1554
			FROM {db_prefix}log_notify
1555
			WHERE id_topic IN ({array_int:topics_list})
1556
			GROUP BY id_member',
1557
			array(
1558
				'topics_list' => $notifications,
1559
			)
1560
		);
1561
		if ($smcFunc['db_num_rows']($request) > 0)
1562
		{
1563
			$replaceEntries = array();
1564
			while ($row = $smcFunc['db_fetch_assoc']($request))
1565
				$replaceEntries[] = array($row['id_member'], $id_topic, 0, $row['sent']);
1566
1567
			$smcFunc['db_insert']('replace',
1568
				'{db_prefix}log_notify',
1569
				array('id_member' => 'int', 'id_topic' => 'int', 'id_board' => 'int', 'sent' => 'int'),
1570
				$replaceEntries,
1571
				array('id_member', 'id_topic', 'id_board')
1572
			);
1573
			unset($replaceEntries);
1574
1575
			$smcFunc['db_query']('', '
1576
				DELETE FROM {db_prefix}log_topics
1577
				WHERE id_topic IN ({array_int:deleted_topics})',
1578
				array(
1579
					'deleted_topics' => $deleted_topics,
1580
				)
1581
			);
1582
		}
1583
		$smcFunc['db_free_result']($request);
1584
	}
1585
1586
	// Get rid of the redundant polls.
1587
	if (!empty($deleted_polls))
1588
	{
1589
		$smcFunc['db_query']('', '
1590
			DELETE FROM {db_prefix}polls
1591
			WHERE id_poll IN ({array_int:deleted_polls})',
1592
			array(
1593
				'deleted_polls' => $deleted_polls,
1594
			)
1595
		);
1596
		$smcFunc['db_query']('', '
1597
			DELETE FROM {db_prefix}poll_choices
1598
			WHERE id_poll IN ({array_int:deleted_polls})',
1599
			array(
1600
				'deleted_polls' => $deleted_polls,
1601
			)
1602
		);
1603
		$smcFunc['db_query']('', '
1604
			DELETE FROM {db_prefix}log_polls
1605
			WHERE id_poll IN ({array_int:deleted_polls})',
1606
			array(
1607
				'deleted_polls' => $deleted_polls,
1608
			)
1609
		);
1610
	}
1611
1612
	// Cycle through each board...
1613
	foreach ($boardTotals as $id_board => $stats)
1614
	{
1615
		$smcFunc['db_query']('', '
1616
			UPDATE {db_prefix}boards
1617
			SET
1618
				num_topics = CASE WHEN {int:topics} > num_topics THEN 0 ELSE num_topics - {int:topics} END,
1619
				unapproved_topics = CASE WHEN {int:unapproved_topics} > unapproved_topics THEN 0 ELSE unapproved_topics - {int:unapproved_topics} END,
1620
				num_posts = CASE WHEN {int:posts} > num_posts THEN 0 ELSE num_posts - {int:posts} END,
1621
				unapproved_posts = CASE WHEN {int:unapproved_posts} > unapproved_posts THEN 0 ELSE unapproved_posts - {int:unapproved_posts} END
1622
			WHERE id_board = {int:id_board}',
1623
			array(
1624
				'id_board' => $id_board,
1625
				'topics' => $stats['topics'],
1626
				'unapproved_topics' => $stats['unapproved_topics'],
1627
				'posts' => $stats['posts'],
1628
				'unapproved_posts' => $stats['unapproved_posts'],
1629
			)
1630
		);
1631
	}
1632
1633
	// Determine the board the final topic resides in
1634
	$request = $smcFunc['db_query']('', '
1635
		SELECT id_board
1636
		FROM {db_prefix}topics
1637
		WHERE id_topic = {int:id_topic}
1638
		LIMIT 1',
1639
		array(
1640
			'id_topic' => $id_topic,
1641
		)
1642
	);
1643
	list($id_board) = $smcFunc['db_fetch_row']($request);
1644
	$smcFunc['db_free_result']($request);
1645
1646
	// Again, only do this if we're redirecting - otherwise delete
1647
	if (isset($_POST['postRedirect']))
1648
	{
1649
		// Having done all that, now make sure we fix the merge/redirect topics upp before we
1650
		// leave here. Specifically: that there are no replies, no unapproved stuff, that the first
1651
		// and last posts are the same and so on and so forth.
1652
		foreach ($updated_topics as $old_topic => $id_msg)
1653
		{
1654
			$smcFunc['db_query']('', '
1655
				UPDATE {db_prefix}topics
1656
				SET id_first_msg = id_last_msg,
1657
					id_member_started = {int:current_user},
1658
					id_member_updated = {int:current_user},
1659
					id_poll = 0,
1660
					approved = 1,
1661
					num_replies = 0,
1662
					unapproved_posts = 0,
1663
					id_redirect_topic = {int:redirect_topic},
1664
					redirect_expires = {int:redirect_expires}
1665
				WHERE id_topic = {int:old_topic}',
1666
				array(
1667
					'current_user' => $user_info['id'],
1668
					'old_topic' => $old_topic,
1669
					'redirect_topic' => $redirect_topic,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $redirect_topic does not seem to be defined for all execution paths leading up to this point.
Loading history...
1670
					'redirect_expires' => $redirect_expires
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $redirect_expires does not seem to be defined for all execution paths leading up to this point.
Loading history...
1671
				)
1672
			);
1673
		}
1674
	}
1675
1676
	// Ensure we don't accidentally delete the poll we want to keep...
1677
	$smcFunc['db_query']('', '
1678
		UPDATE {db_prefix}topics
1679
		SET id_poll = 0
1680
		WHERE id_topic IN ({array_int:deleted_topics})',
1681
		array(
1682
			'deleted_topics' => $deleted_topics
1683
		)
1684
	);
1685
1686
	// Delete any remaining data regarding these topics, this is done before changing the properties of the merged topic (else we get duplicate keys)...
1687
	if (!isset($_POST['postRedirect']))
1688
	{
1689
		// Remove any remaining info about these topics...
1690
		include_once($sourcedir . '/RemoveTopic.php');
1691
		// We do not need to remove the counts of the deleted topics, as we already removed these.
1692
		removeTopics($deleted_topics, false, true, false);
1693
	}
1694
1695
	// Asssign the properties of the newly merged topic.
1696
	$smcFunc['db_query']('', '
1697
		UPDATE {db_prefix}topics
1698
		SET
1699
			id_board = {int:id_board},
1700
			id_member_started = {int:id_member_started},
1701
			id_member_updated = {int:id_member_updated},
1702
			id_first_msg = {int:id_first_msg},
1703
			id_last_msg = {int:id_last_msg},
1704
			id_poll = {int:id_poll},
1705
			num_replies = {int:num_replies},
1706
			unapproved_posts = {int:unapproved_posts},
1707
			num_views = {int:num_views},
1708
			is_sticky = {int:is_sticky},
1709
			approved = {int:approved}
1710
		WHERE id_topic = {int:id_topic}',
1711
		array(
1712
			'id_board' => $target_board,
1713
			'is_sticky' => $is_sticky,
1714
			'approved' => $topic_approved,
1715
			'id_topic' => $id_topic,
1716
			'id_member_started' => $member_started,
1717
			'id_member_updated' => $member_updated,
1718
			'id_first_msg' => $first_msg,
1719
			'id_last_msg' => $last_msg,
1720
			'id_poll' => $target_poll,
1721
			'num_replies' => $num_replies,
1722
			'unapproved_posts' => $num_unapproved,
1723
			'num_views' => $num_views,
1724
		)
1725
	);
1726
1727
	// Update all the statistics.
1728
	updateStats('topic');
1729
	updateStats('subject', $id_topic, $target_subject);
1730
	updateLastMessages($boards);
1731
1732
	logAction('merge', array('topic' => $id_topic, 'board' => $id_board));
1733
1734
	// Notify people that these topics have been merged?
1735
	sendNotifications($id_topic, 'merge');
1736
1737
	// If there's a search index that needs updating, update it...
1738
	require_once($sourcedir . '/Search.php');
1739
	$searchAPI = findSearchAPI();
1740
	if (is_callable(array($searchAPI, 'topicMerge')))
1741
		$searchAPI->topicMerge($id_topic, $topics, $affected_msgs, empty($_POST['enforce_subject']) ? null : array($context['response_prefix'], $target_subject));
1742
1743
	// Merging is the sort of thing an external CMS might want to know about
1744
	$merged_topic = array(
1745
		'id_board' => $target_board,
1746
		'is_sticky' => $is_sticky,
1747
		'approved' => $topic_approved,
1748
		'id_topic' => $id_topic,
1749
		'id_member_started' => $member_started,
1750
		'id_member_updated' => $member_updated,
1751
		'id_first_msg' => $first_msg,
1752
		'id_last_msg' => $last_msg,
1753
		'id_poll' => $target_poll,
1754
		'num_replies' => $num_replies,
1755
		'unapproved_posts' => $num_unapproved,
1756
		'num_views' => $num_views,
1757
		'subject' => $target_subject,
1758
	);
1759
	call_integration_hook('integrate_merge_topic', array($merged_topic, $updated_topics, $deleted_topics, $deleted_polls));
1760
1761
	// Send them to the all done page.
1762
	redirectexit('action=mergetopics;sa=done;to=' . $id_topic . ';targetboard=' . $target_board);
1763
}
1764
1765
/**
1766
 * Shows a 'merge completed' screen.
1767
 * is accessed with ?action=mergetopics;sa=done.
1768
 * uses 'merge_done' sub template of the SplitTopics template.
1769
 */
1770
function MergeDone()
1771
{
1772
	global $txt, $context;
1773
1774
	// Make sure the template knows everything...
1775
	$context['target_board'] = (int) $_GET['targetboard'];
1776
	$context['target_topic'] = (int) $_GET['to'];
1777
1778
	$context['page_title'] = $txt['merge'];
1779
	$context['sub_template'] = 'merge_done';
1780
}
1781
1782
?>