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

SplitTopics.php ➔ splitTopic()   F

Complexity

Conditions 39
Paths > 20000

Size

Total Lines 318

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 39
nc 429496.7295
nop 3
dl 0
loc 318
rs 0
c 0
b 0
f 0

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

783
	sendNotifications(/** @scrutinizer ignore-type */ $split1_ID_TOPIC, 'split');
Loading history...
784
785
	// If there's a search index that needs updating, update it...
786
	require_once($sourcedir . '/Search.php');
787
	$searchAPI = findSearchAPI();
788
	if (is_callable(array($searchAPI, 'topicSplit')))
789
		$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

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