comparison forum/Sources/SplitTopics.php @ 76:e3e11437ecea website

Add forum code
author Chris Cannam
date Sun, 07 Jul 2013 11:25:48 +0200
parents
children
comparison
equal deleted inserted replaced
75:72f59aa7e503 76:e3e11437ecea
1 <?php
2
3 /**
4 * Simple Machines Forum (SMF)
5 *
6 * @package SMF
7 * @author Simple Machines http://www.simplemachines.org
8 * @copyright 2011 Simple Machines
9 * @license http://www.simplemachines.org/about/smf/license.php BSD
10 *
11 * @version 2.0
12 */
13
14 // Original module by Mach8 - We'll never forget you.
15
16 if (!defined('SMF'))
17 die('Hacking attempt...');
18
19 /* This file handles merging and splitting topics... it does this with:
20
21 void SplitTopics()
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 void SplitIndex()
29 - screen shown before the actual split.
30 - is accessed with ?action=splittopics;sa=index.
31 - default sub action for ?action=splittopics.
32 - uses 'ask' sub template of the SplitTopics template.
33 - redirects to SplitSelectTopics if the message given turns out to be
34 the first message of a topic.
35 - shows the user three ways to split the current topic.
36
37 void SplitExecute()
38 - do the actual split.
39 - is accessed with ?action=splittopics;sa=execute.
40 - uses the main SplitTopics template.
41 - supports three ways of splitting:
42 (1) only one message is split off.
43 (2) all messages after and including a given message are split off.
44 (3) select topics to split (redirects to SplitSelectTopics()).
45 - uses splitTopic function to do the actual splitting.
46
47 void SplitSelectTopics()
48 - allows the user to select the messages to be split.
49 - is accessed with ?action=splittopics;sa=selectTopics.
50 - uses 'select' sub template of the SplitTopics template or (for
51 XMLhttp) the 'split' sub template of the Xml template.
52 - supports XMLhttp for adding/removing a message to the selection.
53 - uses a session variable to store the selected topics.
54 - shows two independent page indexes for both the selected and
55 not-selected messages (;topic=1.x;start2=y).
56
57 void SplitSelectionExecute()
58 - do the actual split of a selection of topics.
59 - is accessed with ?action=splittopics;sa=splitSelection.
60 - uses the main SplitTopics template.
61 - uses splitTopic function to do the actual splitting.
62
63 int splitTopic(int topicID, array messagesToBeSplit, string newSubject)
64 - general function to split off a topic.
65 - creates a new topic and moves the messages with the IDs in
66 array messagesToBeSplit to the new topic.
67 - the subject of the newly created topic is set to 'newSubject'.
68 - marks the newly created message as read for the user splitting it.
69 - updates the statistics to reflect a newly created topic.
70 - logs the action in the moderation log.
71 - a notification is sent to all users monitoring this topic.
72 - returns the topic ID of the new split topic.
73
74 void MergeTopics()
75 - merges two or more topics into one topic.
76 - delegates to the other functions (based on the URL parameter sa).
77 - loads the SplitTopics template.
78 - requires the merge_any permission.
79 - is accessed with ?action=mergetopics.
80
81 void MergeIndex()
82 - allows to pick a topic to merge the current topic with.
83 - is accessed with ?action=mergetopics;sa=index
84 - default sub action for ?action=mergetopics.
85 - uses 'merge' sub template of the SplitTopics template.
86 - allows to set a different target board.
87
88 void MergeExecute(array topics = request)
89 - set merge options and do the actual merge of two or more topics.
90 - the merge options screen:
91 - shows topics to be merged and allows to set some merge options.
92 - is accessed by ?action=mergetopics;sa=options.and can also
93 internally be called by QuickModeration() (Subs-Boards.php).
94 - uses 'merge_extra_options' sub template of the SplitTopics
95 template.
96 - the actual merge:
97 - is accessed with ?action=mergetopics;sa=execute.
98 - updates the statistics to reflect the merge.
99 - logs the action in the moderation log.
100 - sends a notification is sent to all users monitoring this topic.
101 - redirects to ?action=mergetopics;sa=done.
102
103 void MergeDone()
104 - shows a 'merge completed' screen.
105 - is accessed with ?action=mergetopics;sa=done.
106 - uses 'merge_done' sub template of the SplitTopics template.
107 */
108
109 // Split a topic into two separate topics... in case it got offtopic, etc.
110 function SplitTopics()
111 {
112 global $topic, $sourcedir;
113
114 // And... which topic were you splitting, again?
115 if (empty($topic))
116 fatal_lang_error('numbers_one_to_nine', false);
117
118 // Are you allowed to split topics?
119 isAllowedTo('split_any');
120
121 // Load up the "dependencies" - the template, getMsgMemberID(), and sendNotifications().
122 if (!isset($_REQUEST['xml']))
123 loadTemplate('SplitTopics');
124 require_once($sourcedir . '/Subs-Boards.php');
125 require_once($sourcedir . '/Subs-Post.php');
126
127 $subActions = array(
128 'selectTopics' => 'SplitSelectTopics',
129 'execute' => 'SplitExecute',
130 'index' => 'SplitIndex',
131 'splitSelection' => 'SplitSelectionExecute',
132 );
133
134 // ?action=splittopics;sa=LETSBREAKIT won't work, sorry.
135 if (empty($_REQUEST['sa']) || !isset($subActions[$_REQUEST['sa']]))
136 SplitIndex();
137 else
138 $subActions[$_REQUEST['sa']]();
139 }
140
141 // Part 1: General stuff.
142 function SplitIndex()
143 {
144 global $txt, $topic, $context, $smcFunc, $modSettings;
145
146 // Validate "at".
147 if (empty($_GET['at']))
148 fatal_lang_error('numbers_one_to_nine', false);
149 $_GET['at'] = (int) $_GET['at'];
150
151 // Retrieve the subject and stuff of the specific topic/message.
152 $request = $smcFunc['db_query']('', '
153 SELECT m.subject, t.num_replies, t.unapproved_posts, t.id_first_msg, t.approved
154 FROM {db_prefix}messages AS m
155 INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
156 WHERE m.id_msg = {int:split_at}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
157 AND m.approved = 1') . '
158 AND m.id_topic = {int:current_topic}
159 LIMIT 1',
160 array(
161 'current_topic' => $topic,
162 'split_at' => $_GET['at'],
163 )
164 );
165 if ($smcFunc['db_num_rows']($request) == 0)
166 fatal_lang_error('cant_find_messages');
167 list ($_REQUEST['subname'], $num_replies, $unapproved_posts, $id_first_msg, $approved) = $smcFunc['db_fetch_row']($request);
168 $smcFunc['db_free_result']($request);
169
170 // If not approved validate they can see it.
171 if ($modSettings['postmod_active'] && !$approved)
172 isAllowedTo('approve_posts');
173
174 // If this topic has unapproved posts, we need to count them too...
175 if ($modSettings['postmod_active'] && allowedTo('approve_posts'))
176 $num_replies += $unapproved_posts - ($approved ? 0 : 1);
177
178 // Check if there is more than one message in the topic. (there should be.)
179 if ($num_replies < 1)
180 fatal_lang_error('topic_one_post', false);
181
182 // Check if this is the first message in the topic (if so, the first and second option won't be available)
183 if ($id_first_msg == $_GET['at'])
184 return SplitSelectTopics();
185
186 // Basic template information....
187 $context['message'] = array(
188 'id' => $_GET['at'],
189 'subject' => $_REQUEST['subname']
190 );
191 $context['sub_template'] = 'ask';
192 $context['page_title'] = $txt['split'];
193 }
194
195 // Alright, you've decided what you want to do with it.... now to do it.
196 function SplitExecute()
197 {
198 global $txt, $board, $topic, $context, $user_info, $smcFunc, $modSettings;
199
200 // Check the session to make sure they meant to do this.
201 checkSession();
202
203 // Clean up the subject.
204 if (!isset($_POST['subname']) || $_POST['subname'] == '')
205 $_POST['subname'] = $txt['new_topic'];
206
207 // Redirect to the selector if they chose selective.
208 if ($_POST['step2'] == 'selective')
209 {
210 $_REQUEST['subname'] = $_POST['subname'];
211 return SplitSelectTopics();
212 }
213
214 $_POST['at'] = (int) $_POST['at'];
215 $messagesToBeSplit = array();
216
217 if ($_POST['step2'] == 'afterthis')
218 {
219 // Fetch the message IDs of the topic that are at or after the message.
220 $request = $smcFunc['db_query']('', '
221 SELECT id_msg
222 FROM {db_prefix}messages
223 WHERE id_topic = {int:current_topic}
224 AND id_msg >= {int:split_at}',
225 array(
226 'current_topic' => $topic,
227 'split_at' => $_POST['at'],
228 )
229 );
230 while ($row = $smcFunc['db_fetch_assoc']($request))
231 $messagesToBeSplit[] = $row['id_msg'];
232 $smcFunc['db_free_result']($request);
233 }
234 // Only the selected message has to be split. That should be easy.
235 elseif ($_POST['step2'] == 'onlythis')
236 $messagesToBeSplit[] = $_POST['at'];
237 // There's another action?!
238 else
239 fatal_lang_error('no_access', false);
240
241 $context['old_topic'] = $topic;
242 $context['new_topic'] = splitTopic($topic, $messagesToBeSplit, $_POST['subname']);
243 $context['page_title'] = $txt['split'];
244 }
245
246 // Get a selective list of topics...
247 function SplitSelectTopics()
248 {
249 global $txt, $scripturl, $topic, $context, $modSettings, $original_msgs, $smcFunc, $options;
250
251 $context['page_title'] = $txt['split'] . ' - ' . $txt['select_split_posts'];
252
253 // Haven't selected anything have we?
254 $_SESSION['split_selection'][$topic] = empty($_SESSION['split_selection'][$topic]) ? array() : $_SESSION['split_selection'][$topic];
255
256 $context['not_selected'] = array(
257 'num_messages' => 0,
258 'start' => empty($_REQUEST['start']) ? 0 : (int) $_REQUEST['start'],
259 'messages' => array(),
260 );
261
262 $context['selected'] = array(
263 'num_messages' => 0,
264 'start' => empty($_REQUEST['start2']) ? 0 : (int) $_REQUEST['start2'],
265 'messages' => array(),
266 );
267
268 $context['topic'] = array(
269 'id' => $topic,
270 'subject' => urlencode($_REQUEST['subname']),
271 );
272
273 // Some stuff for our favorite template.
274 $context['new_subject'] = $_REQUEST['subname'];
275
276 // Using the "select" sub template.
277 $context['sub_template'] = isset($_REQUEST['xml']) ? 'split' : 'select';
278
279 // Are we using a custom messages per page?
280 $context['messages_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
281
282 // Get the message ID's from before the move.
283 if (isset($_REQUEST['xml']))
284 {
285 $original_msgs = array(
286 'not_selected' => array(),
287 'selected' => array(),
288 );
289 $request = $smcFunc['db_query']('', '
290 SELECT id_msg
291 FROM {db_prefix}messages
292 WHERE id_topic = {int:current_topic}' . (empty($_SESSION['split_selection'][$topic]) ? '' : '
293 AND id_msg NOT IN ({array_int:no_split_msgs})') . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
294 AND approved = {int:is_approved}') . '
295 ORDER BY id_msg DESC
296 LIMIT {int:start}, {int:messages_per_page}',
297 array(
298 'current_topic' => $topic,
299 'no_split_msgs' => empty($_SESSION['split_selection'][$topic]) ? array() : $_SESSION['split_selection'][$topic],
300 'is_approved' => 1,
301 'start' => $context['not_selected']['start'],
302 'messages_per_page' => $context['messages_per_page'],
303 )
304 );
305 // You can't split the last message off.
306 if (empty($context['not_selected']['start']) && $smcFunc['db_num_rows']($request) <= 1 && $_REQUEST['move'] == 'down')
307 $_REQUEST['move'] = '';
308 while ($row = $smcFunc['db_fetch_assoc']($request))
309 $original_msgs['not_selected'][] = $row['id_msg'];
310 $smcFunc['db_free_result']($request);
311 if (!empty($_SESSION['split_selection'][$topic]))
312 {
313 $request = $smcFunc['db_query']('', '
314 SELECT id_msg
315 FROM {db_prefix}messages
316 WHERE id_topic = {int:current_topic}
317 AND id_msg IN ({array_int:split_msgs})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
318 AND approved = {int:is_approved}') . '
319 ORDER BY id_msg DESC
320 LIMIT {int:start}, {int:messages_per_page}',
321 array(
322 'current_topic' => $topic,
323 'split_msgs' => $_SESSION['split_selection'][$topic],
324 'is_approved' => 1,
325 'start' => $context['selected']['start'],
326 'messages_per_page' => $context['messages_per_page'],
327 )
328 );
329 while ($row = $smcFunc['db_fetch_assoc']($request))
330 $original_msgs['selected'][] = $row['id_msg'];
331 $smcFunc['db_free_result']($request);
332 }
333 }
334
335 // (De)select a message..
336 if (!empty($_REQUEST['move']))
337 {
338 $_REQUEST['msg'] = (int) $_REQUEST['msg'];
339
340 if ($_REQUEST['move'] == 'reset')
341 $_SESSION['split_selection'][$topic] = array();
342 elseif ($_REQUEST['move'] == 'up')
343 $_SESSION['split_selection'][$topic] = array_diff($_SESSION['split_selection'][$topic], array($_REQUEST['msg']));
344 else
345 $_SESSION['split_selection'][$topic][] = $_REQUEST['msg'];
346 }
347
348 // Make sure the selection is still accurate.
349 if (!empty($_SESSION['split_selection'][$topic]))
350 {
351 $request = $smcFunc['db_query']('', '
352 SELECT id_msg
353 FROM {db_prefix}messages
354 WHERE id_topic = {int:current_topic}
355 AND id_msg IN ({array_int:split_msgs})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
356 AND approved = {int:is_approved}'),
357 array(
358 'current_topic' => $topic,
359 'split_msgs' => $_SESSION['split_selection'][$topic],
360 'is_approved' => 1,
361 )
362 );
363 $_SESSION['split_selection'][$topic] = array();
364 while ($row = $smcFunc['db_fetch_assoc']($request))
365 $_SESSION['split_selection'][$topic][] = $row['id_msg'];
366 $smcFunc['db_free_result']($request);
367 }
368
369 // Get the number of messages (not) selected to be split.
370 $request = $smcFunc['db_query']('', '
371 SELECT ' . (empty($_SESSION['split_selection'][$topic]) ? '0' : 'm.id_msg IN ({array_int:split_msgs})') . ' AS is_selected, COUNT(*) AS num_messages
372 FROM {db_prefix}messages AS m
373 WHERE m.id_topic = {int:current_topic}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
374 AND approved = {int:is_approved}') . (empty($_SESSION['split_selection'][$topic]) ? '' : '
375 GROUP BY is_selected'),
376 array(
377 'current_topic' => $topic,
378 'split_msgs' => !empty($_SESSION['split_selection'][$topic]) ? $_SESSION['split_selection'][$topic] : array(),
379 'is_approved' => 1,
380 )
381 );
382 while ($row = $smcFunc['db_fetch_assoc']($request))
383 $context[empty($row['is_selected']) ? 'not_selected' : 'selected']['num_messages'] = $row['num_messages'];
384 $smcFunc['db_free_result']($request);
385
386 // Fix an oversized starting page (to make sure both pageindexes are properly set).
387 if ($context['selected']['start'] >= $context['selected']['num_messages'])
388 $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'])));
389
390 // Build a page list of the not-selected topics...
391 $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);
392 // ...and one of the selected topics.
393 $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);
394
395 // Get the messages and stick them into an array.
396 $request = $smcFunc['db_query']('', '
397 SELECT m.subject, IFNULL(mem.real_name, m.poster_name) AS real_name, m.poster_time, m.body, m.id_msg, m.smileys_enabled
398 FROM {db_prefix}messages AS m
399 LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
400 WHERE m.id_topic = {int:current_topic}' . (empty($_SESSION['split_selection'][$topic]) ? '' : '
401 AND id_msg NOT IN ({array_int:no_split_msgs})') . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
402 AND approved = {int:is_approved}') . '
403 ORDER BY m.id_msg DESC
404 LIMIT {int:start}, {int:messages_per_page}',
405 array(
406 'current_topic' => $topic,
407 'no_split_msgs' => !empty($_SESSION['split_selection'][$topic]) ? $_SESSION['split_selection'][$topic] : array(),
408 'is_approved' => 1,
409 'start' => $context['not_selected']['start'],
410 'messages_per_page' => $context['messages_per_page'],
411 )
412 );
413 $context['messages'] = array();
414 for ($counter = 0; $row = $smcFunc['db_fetch_assoc']($request); $counter ++)
415 {
416 censorText($row['subject']);
417 censorText($row['body']);
418
419 $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
420
421 $context['not_selected']['messages'][$row['id_msg']] = array(
422 'id' => $row['id_msg'],
423 'alternate' => $counter % 2,
424 'subject' => $row['subject'],
425 'time' => timeformat($row['poster_time']),
426 'timestamp' => forum_time(true, $row['poster_time']),
427 'body' => $row['body'],
428 'poster' => $row['real_name'],
429 );
430 }
431 $smcFunc['db_free_result']($request);
432
433 // Now get the selected messages.
434 if (!empty($_SESSION['split_selection'][$topic]))
435 {
436 // Get the messages and stick them into an array.
437 $request = $smcFunc['db_query']('', '
438 SELECT m.subject, IFNULL(mem.real_name, m.poster_name) AS real_name, m.poster_time, m.body, m.id_msg, m.smileys_enabled
439 FROM {db_prefix}messages AS m
440 LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
441 WHERE m.id_topic = {int:current_topic}
442 AND m.id_msg IN ({array_int:split_msgs})' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
443 AND approved = {int:is_approved}') . '
444 ORDER BY m.id_msg DESC
445 LIMIT {int:start}, {int:messages_per_page}',
446 array(
447 'current_topic' => $topic,
448 'split_msgs' => $_SESSION['split_selection'][$topic],
449 'is_approved' => 1,
450 'start' => $context['selected']['start'],
451 'messages_per_page' => $context['messages_per_page'],
452 )
453 );
454 $context['messages'] = array();
455 for ($counter = 0; $row = $smcFunc['db_fetch_assoc']($request); $counter ++)
456 {
457 censorText($row['subject']);
458 censorText($row['body']);
459
460 $row['body'] = parse_bbc($row['body'], $row['smileys_enabled'], $row['id_msg']);
461
462 $context['selected']['messages'][$row['id_msg']] = array(
463 'id' => $row['id_msg'],
464 'alternate' => $counter % 2,
465 'subject' => $row['subject'],
466 'time' => timeformat($row['poster_time']),
467 'timestamp' => forum_time(true, $row['poster_time']),
468 'body' => $row['body'],
469 'poster' => $row['real_name']
470 );
471 }
472 $smcFunc['db_free_result']($request);
473 }
474
475 // The XMLhttp method only needs the stuff that changed, so let's compare.
476 if (isset($_REQUEST['xml']))
477 {
478 $changes = array(
479 'remove' => array(
480 'not_selected' => array_diff($original_msgs['not_selected'], array_keys($context['not_selected']['messages'])),
481 'selected' => array_diff($original_msgs['selected'], array_keys($context['selected']['messages'])),
482 ),
483 'insert' => array(
484 'not_selected' => array_diff(array_keys($context['not_selected']['messages']), $original_msgs['not_selected']),
485 'selected' => array_diff(array_keys($context['selected']['messages']), $original_msgs['selected']),
486 ),
487 );
488
489 $context['changes'] = array();
490 foreach ($changes as $change_type => $change_array)
491 foreach ($change_array as $section => $msg_array)
492 {
493 if (empty($msg_array))
494 continue;
495
496 foreach ($msg_array as $id_msg)
497 {
498 $context['changes'][$change_type . $id_msg] = array(
499 'id' => $id_msg,
500 'type' => $change_type,
501 'section' => $section,
502 );
503 if ($change_type == 'insert')
504 $context['changes']['insert' . $id_msg]['insert_value'] = $context[$section]['messages'][$id_msg];
505 }
506 }
507 }
508 }
509
510 // Actually and selectively split the topics out.
511 function SplitSelectionExecute()
512 {
513 global $txt, $board, $topic, $context, $user_info;
514
515 // Make sure the session id was passed with post.
516 checkSession();
517
518 // Default the subject in case it's blank.
519 if (!isset($_POST['subname']) || $_POST['subname'] == '')
520 $_POST['subname'] = $txt['new_topic'];
521
522 // You must've selected some messages! Can't split out none!
523 if (empty($_SESSION['split_selection'][$topic]))
524 fatal_lang_error('no_posts_selected', false);
525
526 $context['old_topic'] = $topic;
527 $context['new_topic'] = splitTopic($topic, $_SESSION['split_selection'][$topic], $_POST['subname']);
528 $context['page_title'] = $txt['split'];
529 }
530
531 // Split a topic in two topics.
532 function splitTopic($split1_ID_TOPIC, $splitMessages, $new_subject)
533 {
534 global $user_info, $topic, $board, $modSettings, $smcFunc, $txt;
535
536 // Nothing to split?
537 if (empty($splitMessages))
538 fatal_lang_error('no_posts_selected', false);
539
540 // Get some board info.
541 $request = $smcFunc['db_query']('', '
542 SELECT id_board, approved
543 FROM {db_prefix}topics
544 WHERE id_topic = {int:id_topic}
545 LIMIT 1',
546 array(
547 'id_topic' => $split1_ID_TOPIC,
548 )
549 );
550 list ($id_board, $split1_approved) = $smcFunc['db_fetch_row']($request);
551 $smcFunc['db_free_result']($request);
552
553 // Find the new first and last not in the list. (old topic)
554 $request = $smcFunc['db_query']('', '
555 SELECT
556 MIN(m.id_msg) AS myid_first_msg, MAX(m.id_msg) AS myid_last_msg, COUNT(*) AS message_count, m.approved
557 FROM {db_prefix}messages AS m
558 INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:id_topic})
559 WHERE m.id_msg NOT IN ({array_int:no_msg_list})
560 AND m.id_topic = {int:id_topic}
561 GROUP BY m.approved
562 ORDER BY m.approved DESC
563 LIMIT 2',
564 array(
565 'id_topic' => $split1_ID_TOPIC,
566 'no_msg_list' => $splitMessages,
567 )
568 );
569 // You can't select ALL the messages!
570 if ($smcFunc['db_num_rows']($request) == 0)
571 fatal_lang_error('selected_all_posts', false);
572 while ($row = $smcFunc['db_fetch_assoc']($request))
573 {
574 // Get the right first and last message dependant on approved state...
575 if (empty($split1_first_msg) || $row['myid_first_msg'] < $split1_first_msg)
576 $split1_first_msg = $row['myid_first_msg'];
577 if (empty($split1_last_msg) || $row['approved'])
578 $split1_last_msg = $row['myid_last_msg'];
579
580 // Get the counts correct...
581 if ($row['approved'])
582 {
583 $split1_replies = $row['message_count'] - 1;
584 $split1_unapprovedposts = 0;
585 }
586 else
587 {
588 if (!isset($split1_replies))
589 $split1_replies = 0;
590 // If the topic isn't approved then num replies must go up by one... as first post wouldn't be counted.
591 elseif (!$split1_approved)
592 $split1_replies++;
593
594 $split1_unapprovedposts = $row['message_count'];
595 }
596 }
597 $smcFunc['db_free_result']($request);
598 $split1_firstMem = getMsgMemberID($split1_first_msg);
599 $split1_lastMem = getMsgMemberID($split1_last_msg);
600
601 // Find the first and last in the list. (new topic)
602 $request = $smcFunc['db_query']('', '
603 SELECT MIN(id_msg) AS myid_first_msg, MAX(id_msg) AS myid_last_msg, COUNT(*) AS message_count, approved
604 FROM {db_prefix}messages
605 WHERE id_msg IN ({array_int:msg_list})
606 AND id_topic = {int:id_topic}
607 GROUP BY id_topic, approved
608 ORDER BY approved DESC
609 LIMIT 2',
610 array(
611 'msg_list' => $splitMessages,
612 'id_topic' => $split1_ID_TOPIC,
613 )
614 );
615 while ($row = $smcFunc['db_fetch_assoc']($request))
616 {
617 // As before get the right first and last message dependant on approved state...
618 if (empty($split2_first_msg) || $row['myid_first_msg'] < $split2_first_msg)
619 $split2_first_msg = $row['myid_first_msg'];
620 if (empty($split2_last_msg) || $row['approved'])
621 $split2_last_msg = $row['myid_last_msg'];
622
623 // Then do the counts again...
624 if ($row['approved'])
625 {
626 $split2_approved = true;
627 $split2_replies = $row['message_count'] - 1;
628 $split2_unapprovedposts = 0;
629 }
630 else
631 {
632 // Should this one be approved??
633 if ($split2_first_msg == $row['myid_first_msg'])
634 $split2_approved = false;
635
636 if (!isset($split2_replies))
637 $split2_replies = 0;
638 // As before, fix number of replies.
639 elseif (!$split2_approved)
640 $split2_replies++;
641
642 $split2_unapprovedposts = $row['message_count'];
643 }
644 }
645 $smcFunc['db_free_result']($request);
646 $split2_firstMem = getMsgMemberID($split2_first_msg);
647 $split2_lastMem = getMsgMemberID($split2_last_msg);
648
649 // No database changes yet, so let's double check to see if everything makes at least a little sense.
650 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))
651 fatal_lang_error('cant_find_messages');
652
653 // You cannot split off the first message of a topic.
654 if ($split1_first_msg > $split2_first_msg)
655 fatal_lang_error('split_first_post', false);
656
657 // We're off to insert the new topic! Use 0 for now to avoid UNIQUE errors.
658 $smcFunc['db_insert']('',
659 '{db_prefix}topics',
660 array(
661 'id_board' => 'int',
662 'id_member_started' => 'int',
663 'id_member_updated' => 'int',
664 'id_first_msg' => 'int',
665 'id_last_msg' => 'int',
666 'num_replies' => 'int',
667 'unapproved_posts' => 'int',
668 'approved' => 'int',
669 'is_sticky' => 'int',
670 ),
671 array(
672 (int) $id_board, $split2_firstMem, $split2_lastMem, 0,
673 0, $split2_replies, $split2_unapprovedposts, (int) $split2_approved, 0,
674 ),
675 array('id_topic')
676 );
677 $split2_ID_TOPIC = $smcFunc['db_insert_id']('{db_prefix}topics', 'id_topic');
678 if ($split2_ID_TOPIC <= 0)
679 fatal_lang_error('cant_insert_topic');
680
681 // Move the messages over to the other topic.
682 $new_subject = strtr($smcFunc['htmltrim']($smcFunc['htmlspecialchars']($new_subject)), array("\r" => '', "\n" => '', "\t" => ''));
683 // Check the subject length.
684 if ($smcFunc['strlen']($new_subject) > 100)
685 $new_subject = $smcFunc['substr']($new_subject, 0, 100);
686 // Valid subject?
687 if ($new_subject != '')
688 {
689 $smcFunc['db_query']('', '
690 UPDATE {db_prefix}messages
691 SET
692 id_topic = {int:id_topic},
693 subject = CASE WHEN id_msg = {int:split_first_msg} THEN {string:new_subject} ELSE {string:new_subject_replies} END
694 WHERE id_msg IN ({array_int:split_msgs})',
695 array(
696 'split_msgs' => $splitMessages,
697 'id_topic' => $split2_ID_TOPIC,
698 'new_subject' => $new_subject,
699 'split_first_msg' => $split2_first_msg,
700 'new_subject_replies' => $txt['response_prefix'] . $new_subject,
701 )
702 );
703
704 // Cache the new topics subject... we can do it now as all the subjects are the same!
705 updateStats('subject', $split2_ID_TOPIC, $new_subject);
706 }
707
708 // Any associated reported posts better follow...
709 $smcFunc['db_query']('', '
710 UPDATE {db_prefix}log_reported
711 SET id_topic = {int:id_topic}
712 WHERE id_msg IN ({array_int:split_msgs})',
713 array(
714 'split_msgs' => $splitMessages,
715 'id_topic' => $split2_ID_TOPIC,
716 )
717 );
718
719 // Mess with the old topic's first, last, and number of messages.
720 $smcFunc['db_query']('', '
721 UPDATE {db_prefix}topics
722 SET
723 num_replies = {int:num_replies},
724 id_first_msg = {int:id_first_msg},
725 id_last_msg = {int:id_last_msg},
726 id_member_started = {int:id_member_started},
727 id_member_updated = {int:id_member_updated},
728 unapproved_posts = {int:unapproved_posts}
729 WHERE id_topic = {int:id_topic}',
730 array(
731 'num_replies' => $split1_replies,
732 'id_first_msg' => $split1_first_msg,
733 'id_last_msg' => $split1_last_msg,
734 'id_member_started' => $split1_firstMem,
735 'id_member_updated' => $split1_lastMem,
736 'unapproved_posts' => $split1_unapprovedposts,
737 'id_topic' => $split1_ID_TOPIC,
738 )
739 );
740
741 // Now, put the first/last message back to what they should be.
742 $smcFunc['db_query']('', '
743 UPDATE {db_prefix}topics
744 SET
745 id_first_msg = {int:id_first_msg},
746 id_last_msg = {int:id_last_msg}
747 WHERE id_topic = {int:id_topic}',
748 array(
749 'id_first_msg' => $split2_first_msg,
750 'id_last_msg' => $split2_last_msg,
751 'id_topic' => $split2_ID_TOPIC,
752 )
753 );
754
755 // If the new topic isn't approved ensure the first message flags this just in case.
756 if (!$split2_approved)
757 $smcFunc['db_query']('', '
758 UPDATE {db_prefix}messages
759 SET approved = {int:approved}
760 WHERE id_msg = {int:id_msg}
761 AND id_topic = {int:id_topic}',
762 array(
763 'approved' => 0,
764 'id_msg' => $split2_first_msg,
765 'id_topic' => $split2_ID_TOPIC,
766 )
767 );
768
769 // The board has more topics now (Or more unapproved ones!).
770 $smcFunc['db_query']('', '
771 UPDATE {db_prefix}boards
772 SET ' . ($split2_approved ? '
773 num_topics = num_topics + 1' : '
774 unapproved_topics = unapproved_topics + 1') . '
775 WHERE id_board = {int:id_board}',
776 array(
777 'id_board' => $id_board,
778 )
779 );
780
781 // Copy log topic entries.
782 // !!! This should really be chunked.
783 $request = $smcFunc['db_query']('', '
784 SELECT id_member, id_msg
785 FROM {db_prefix}log_topics
786 WHERE id_topic = {int:id_topic}',
787 array(
788 'id_topic' => (int) $split1_ID_TOPIC,
789 )
790 );
791 if ($smcFunc['db_num_rows']($request) > 0)
792 {
793 $replaceEntries = array();
794 while ($row = $smcFunc['db_fetch_assoc']($request))
795 $replaceEntries[] = array($row['id_member'], $split2_ID_TOPIC, $row['id_msg']);
796
797 $smcFunc['db_insert']('ignore',
798 '{db_prefix}log_topics',
799 array('id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int'),
800 $replaceEntries,
801 array('id_member', 'id_topic')
802 );
803 unset($replaceEntries);
804 }
805 $smcFunc['db_free_result']($request);
806
807 // Housekeeping.
808 updateStats('topic');
809 updateLastMessages($id_board);
810
811 logAction('split', array('topic' => $split1_ID_TOPIC, 'new_topic' => $split2_ID_TOPIC, 'board' => $id_board));
812
813 // Notify people that this topic has been split?
814 sendNotifications($split1_ID_TOPIC, 'split');
815
816 // Return the ID of the newly created topic.
817 return $split2_ID_TOPIC;
818 }
819
820 // Merge two topics into one topic... useful if they have the same basic subject.
821 function MergeTopics()
822 {
823 // Load the template....
824 loadTemplate('SplitTopics');
825
826 $subActions = array(
827 'done' => 'MergeDone',
828 'execute' => 'MergeExecute',
829 'index' => 'MergeIndex',
830 'options' => 'MergeExecute',
831 );
832
833 // ?action=mergetopics;sa=LETSBREAKIT won't work, sorry.
834 if (empty($_REQUEST['sa']) || !isset($subActions[$_REQUEST['sa']]))
835 MergeIndex();
836 else
837 $subActions[$_REQUEST['sa']]();
838 }
839
840 // Merge two topics together.
841 function MergeIndex()
842 {
843 global $txt, $board, $context, $smcFunc;
844 global $scripturl, $topic, $user_info, $modSettings;
845
846 if (!isset($_GET['from']))
847 fatal_lang_error('no_access', false);
848 $_GET['from'] = (int) $_GET['from'];
849
850 $_REQUEST['targetboard'] = isset($_REQUEST['targetboard']) ? (int) $_REQUEST['targetboard'] : $board;
851 $context['target_board'] = $_REQUEST['targetboard'];
852
853 // Prepare a handy query bit for approval...
854 if ($modSettings['postmod_active'])
855 {
856 $can_approve_boards = boardsAllowedTo('approve_posts');
857 $onlyApproved = $can_approve_boards !== array(0) && !in_array($_REQUEST['targetboard'], $can_approve_boards);
858 }
859 else
860 $onlyApproved = false;
861
862 // How many topics are on this board? (used for paging.)
863 $request = $smcFunc['db_query']('', '
864 SELECT COUNT(*)
865 FROM {db_prefix}topics AS t
866 WHERE t.id_board = {int:id_board}' . ($onlyApproved ? '
867 AND t.approved = {int:is_approved}' : ''),
868 array(
869 'id_board' => $_REQUEST['targetboard'],
870 'is_approved' => 1,
871 )
872 );
873 list ($topiccount) = $smcFunc['db_fetch_row']($request);
874 $smcFunc['db_free_result']($request);
875
876 // Make the page list.
877 $context['page_index'] = constructPageIndex($scripturl . '?action=mergetopics;from=' . $_GET['from'] . ';targetboard=' . $_REQUEST['targetboard'] . ';board=' . $board . '.%1$d', $_REQUEST['start'], $topiccount, $modSettings['defaultMaxTopics'], true);
878
879 // Get the topic's subject.
880 $request = $smcFunc['db_query']('', '
881 SELECT m.subject
882 FROM {db_prefix}topics AS t
883 INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
884 WHERE t.id_topic = {int:id_topic}
885 AND t.id_board = {int:current_board}' . ($onlyApproved ? '
886 AND t.approved = {int:is_approved}' : '') . '
887 LIMIT 1',
888 array(
889 'current_board' => $board,
890 'id_topic' => $_GET['from'],
891 'is_approved' => 1,
892 )
893 );
894 if ($smcFunc['db_num_rows']($request) == 0)
895 fatal_lang_error('no_board');
896 list ($subject) = $smcFunc['db_fetch_row']($request);
897 $smcFunc['db_free_result']($request);
898
899 // Tell the template a few things..
900 $context['origin_topic'] = $_GET['from'];
901 $context['origin_subject'] = $subject;
902 $context['origin_js_subject'] = addcslashes(addslashes($subject), '/');
903 $context['page_title'] = $txt['merge'];
904
905 // Check which boards you have merge permissions on.
906 $merge_boards = boardsAllowedTo('merge_any');
907
908 if (empty($merge_boards))
909 fatal_lang_error('cannot_merge_any', 'user');
910
911 // Get a list of boards they can navigate to to merge.
912 $request = $smcFunc['db_query']('order_by_board_order', '
913 SELECT b.id_board, b.name AS board_name, c.name AS cat_name
914 FROM {db_prefix}boards AS b
915 LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
916 WHERE {query_see_board}' . (!in_array(0, $merge_boards) ? '
917 AND b.id_board IN ({array_int:merge_boards})' : ''),
918 array(
919 'merge_boards' => $merge_boards,
920 )
921 );
922 $context['boards'] = array();
923 while ($row = $smcFunc['db_fetch_assoc']($request))
924 $context['boards'][] = array(
925 'id' => $row['id_board'],
926 'name' => $row['board_name'],
927 'category' => $row['cat_name']
928 );
929 $smcFunc['db_free_result']($request);
930
931 // Get some topics to merge it with.
932 $request = $smcFunc['db_query']('', '
933 SELECT t.id_topic, m.subject, m.id_member, IFNULL(mem.real_name, m.poster_name) AS poster_name
934 FROM {db_prefix}topics AS t
935 INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
936 LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
937 WHERE t.id_board = {int:id_board}
938 AND t.id_topic != {int:id_topic}' . ($onlyApproved ? '
939 AND t.approved = {int:is_approved}' : '') . '
940 ORDER BY {raw:sort}
941 LIMIT {int:offset}, {int:limit}',
942 array(
943 'id_board' => $_REQUEST['targetboard'],
944 'id_topic' => $_GET['from'],
945 'sort' => (!empty($modSettings['enableStickyTopics']) ? 't.is_sticky DESC, ' : '') . 't.id_last_msg DESC',
946 'offset' => $_REQUEST['start'],
947 'limit' => $modSettings['defaultMaxTopics'],
948 'is_approved' => 1,
949 )
950 );
951 $context['topics'] = array();
952 while ($row = $smcFunc['db_fetch_assoc']($request))
953 {
954 censorText($row['subject']);
955
956 $context['topics'][] = array(
957 'id' => $row['id_topic'],
958 'poster' => array(
959 'id' => $row['id_member'],
960 'name' => $row['poster_name'],
961 'href' => empty($row['id_member']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member'],
962 'link' => empty($row['id_member']) ? $row['poster_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '" target="_blank" class="new_win">' . $row['poster_name'] . '</a>'
963 ),
964 'subject' => $row['subject'],
965 'js_subject' => addcslashes(addslashes($row['subject']), '/')
966 );
967 }
968 $smcFunc['db_free_result']($request);
969
970 if (empty($context['topics']) && count($context['boards']) <= 1)
971 fatal_lang_error('merge_need_more_topics');
972
973 $context['sub_template'] = 'merge';
974 }
975
976 // Now that the topic IDs are known, do the proper merging.
977 function MergeExecute($topics = array())
978 {
979 global $user_info, $txt, $context, $scripturl, $sourcedir;
980 global $smcFunc, $language, $modSettings;
981
982 // Check the session.
983 checkSession('request');
984
985 // Handle URLs from MergeIndex.
986 if (!empty($_GET['from']) && !empty($_GET['to']))
987 $topics = array((int) $_GET['from'], (int) $_GET['to']);
988
989 // If we came from a form, the topic IDs came by post.
990 if (!empty($_POST['topics']) && is_array($_POST['topics']))
991 $topics = $_POST['topics'];
992
993 // There's nothing to merge with just one topic...
994 if (empty($topics) || !is_array($topics) || count($topics) == 1)
995 fatal_lang_error('merge_need_more_topics');
996
997 // Make sure every topic is numeric, or some nasty things could be done with the DB.
998 foreach ($topics as $id => $topic)
999 $topics[$id] = (int) $topic;
1000
1001 // Joy of all joys, make sure they're not pi**ing about with unapproved topics they can't see :P
1002 if ($modSettings['postmod_active'])
1003 $can_approve_boards = boardsAllowedTo('approve_posts');
1004
1005 // Get info about the topics and polls that will be merged.
1006 $request = $smcFunc['db_query']('', '
1007 SELECT
1008 t.id_topic, t.id_board, t.id_poll, t.num_views, t.is_sticky, t.approved, t.num_replies, t.unapproved_posts,
1009 m1.subject, m1.poster_time AS time_started, IFNULL(mem1.id_member, 0) AS id_member_started, IFNULL(mem1.real_name, m1.poster_name) AS name_started,
1010 m2.poster_time AS time_updated, IFNULL(mem2.id_member, 0) AS id_member_updated, IFNULL(mem2.real_name, m2.poster_name) AS name_updated
1011 FROM {db_prefix}topics AS t
1012 INNER JOIN {db_prefix}messages AS m1 ON (m1.id_msg = t.id_first_msg)
1013 INNER JOIN {db_prefix}messages AS m2 ON (m2.id_msg = t.id_last_msg)
1014 LEFT JOIN {db_prefix}members AS mem1 ON (mem1.id_member = m1.id_member)
1015 LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = m2.id_member)
1016 WHERE t.id_topic IN ({array_int:topic_list})
1017 ORDER BY t.id_first_msg
1018 LIMIT ' . count($topics),
1019 array(
1020 'topic_list' => $topics,
1021 )
1022 );
1023 if ($smcFunc['db_num_rows']($request) < 2)
1024 fatal_lang_error('no_topic_id');
1025 $num_views = 0;
1026 $is_sticky = 0;
1027 $boardTotals = array();
1028 $boards = array();
1029 $polls = array();
1030 while ($row = $smcFunc['db_fetch_assoc']($request))
1031 {
1032 // Make a note for the board counts...
1033 if (!isset($boardTotals[$row['id_board']]))
1034 $boardTotals[$row['id_board']] = array(
1035 'posts' => 0,
1036 'topics' => 0,
1037 'unapproved_posts' => 0,
1038 'unapproved_topics' => 0
1039 );
1040
1041 // We can't see unapproved topics here?
1042 if ($modSettings['postmod_active'] && !$row['approved'] && $can_approve_boards != array(0) && in_array($row['id_board'], $can_approve_boards))
1043 continue;
1044 elseif (!$row['approved'])
1045 $boardTotals[$row['id_board']]['unapproved_topics']++;
1046 else
1047 $boardTotals[$row['id_board']]['topics']++;
1048
1049 $boardTotals[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts'];
1050 $boardTotals[$row['id_board']]['posts'] += $row['num_replies'] + ($row['approved'] ? 1 : 0);
1051
1052 $topic_data[$row['id_topic']] = array(
1053 'id' => $row['id_topic'],
1054 'board' => $row['id_board'],
1055 'poll' => $row['id_poll'],
1056 'num_views' => $row['num_views'],
1057 'subject' => $row['subject'],
1058 'started' => array(
1059 'time' => timeformat($row['time_started']),
1060 'timestamp' => forum_time(true, $row['time_started']),
1061 'href' => empty($row['id_member_started']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member_started'],
1062 'link' => empty($row['id_member_started']) ? $row['name_started'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_started'] . '">' . $row['name_started'] . '</a>'
1063 ),
1064 'updated' => array(
1065 'time' => timeformat($row['time_updated']),
1066 'timestamp' => forum_time(true, $row['time_updated']),
1067 'href' => empty($row['id_member_updated']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member_updated'],
1068 'link' => empty($row['id_member_updated']) ? $row['name_updated'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_updated'] . '">' . $row['name_updated'] . '</a>'
1069 )
1070 );
1071 $num_views += $row['num_views'];
1072 $boards[] = $row['id_board'];
1073
1074 // If there's no poll, id_poll == 0...
1075 if ($row['id_poll'] > 0)
1076 $polls[] = $row['id_poll'];
1077 // Store the id_topic with the lowest id_first_msg.
1078 if (empty($firstTopic))
1079 $firstTopic = $row['id_topic'];
1080
1081 $is_sticky = max($is_sticky, $row['is_sticky']);
1082 }
1083 $smcFunc['db_free_result']($request);
1084
1085 // If we didn't get any topics then they've been messing with unapproved stuff.
1086 if (empty($topic_data))
1087 fatal_lang_error('no_topic_id');
1088
1089 $boards = array_values(array_unique($boards));
1090
1091 // The parameters of MergeExecute were set, so this must've been an internal call.
1092 if (!empty($topics))
1093 {
1094 isAllowedTo('merge_any', $boards);
1095 loadTemplate('SplitTopics');
1096 }
1097
1098 // Get the boards a user is allowed to merge in.
1099 $merge_boards = boardsAllowedTo('merge_any');
1100 if (empty($merge_boards))
1101 fatal_lang_error('cannot_merge_any', 'user');
1102
1103 // Make sure they can see all boards....
1104 $request = $smcFunc['db_query']('', '
1105 SELECT b.id_board
1106 FROM {db_prefix}boards AS b
1107 WHERE b.id_board IN ({array_int:boards})
1108 AND {query_see_board}' . (!in_array(0, $merge_boards) ? '
1109 AND b.id_board IN ({array_int:merge_boards})' : '') . '
1110 LIMIT ' . count($boards),
1111 array(
1112 'boards' => $boards,
1113 'merge_boards' => $merge_boards,
1114 )
1115 );
1116 // 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.
1117 if ($smcFunc['db_num_rows']($request) != count($boards))
1118 fatal_lang_error('no_board');
1119 $smcFunc['db_free_result']($request);
1120
1121 if (empty($_REQUEST['sa']) || $_REQUEST['sa'] == 'options')
1122 {
1123 if (count($polls) > 1)
1124 {
1125 $request = $smcFunc['db_query']('', '
1126 SELECT t.id_topic, t.id_poll, m.subject, p.question
1127 FROM {db_prefix}polls AS p
1128 INNER JOIN {db_prefix}topics AS t ON (t.id_poll = p.id_poll)
1129 INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
1130 WHERE p.id_poll IN ({array_int:polls})
1131 LIMIT ' . count($polls),
1132 array(
1133 'polls' => $polls,
1134 )
1135 );
1136 while ($row = $smcFunc['db_fetch_assoc']($request))
1137 $context['polls'][] = array(
1138 'id' => $row['id_poll'],
1139 'topic' => array(
1140 'id' => $row['id_topic'],
1141 'subject' => $row['subject']
1142 ),
1143 'question' => $row['question'],
1144 'selected' => $row['id_topic'] == $firstTopic
1145 );
1146 $smcFunc['db_free_result']($request);
1147 }
1148 if (count($boards) > 1)
1149 {
1150 $request = $smcFunc['db_query']('', '
1151 SELECT id_board, name
1152 FROM {db_prefix}boards
1153 WHERE id_board IN ({array_int:boards})
1154 ORDER BY name
1155 LIMIT ' . count($boards),
1156 array(
1157 'boards' => $boards,
1158 )
1159 );
1160 while ($row = $smcFunc['db_fetch_assoc']($request))
1161 $context['boards'][] = array(
1162 'id' => $row['id_board'],
1163 'name' => $row['name'],
1164 'selected' => $row['id_board'] == $topic_data[$firstTopic]['board']
1165 );
1166 $smcFunc['db_free_result']($request);
1167 }
1168
1169 $context['topics'] = $topic_data;
1170 foreach ($topic_data as $id => $topic)
1171 $context['topics'][$id]['selected'] = $topic['id'] == $firstTopic;
1172
1173 $context['page_title'] = $txt['merge'];
1174 $context['sub_template'] = 'merge_extra_options';
1175 return;
1176 }
1177
1178 // Determine target board.
1179 $target_board = count($boards) > 1 ? (int) $_REQUEST['board'] : $boards[0];
1180 if (!in_array($target_board, $boards))
1181 fatal_lang_error('no_board');
1182
1183 // Determine which poll will survive and which polls won't.
1184 $target_poll = count($polls) > 1 ? (int) $_POST['poll'] : (count($polls) == 1 ? $polls[0] : 0);
1185 if ($target_poll > 0 && !in_array($target_poll, $polls))
1186 fatal_lang_error('no_access', false);
1187 $deleted_polls = empty($target_poll) ? $polls : array_diff($polls, array($target_poll));
1188
1189 // Determine the subject of the newly merged topic - was a custom subject specified?
1190 if (empty($_POST['subject']) && isset($_POST['custom_subject']) && $_POST['custom_subject'] != '')
1191 {
1192 $target_subject = strtr($smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['custom_subject'])), array("\r" => '', "\n" => '', "\t" => ''));
1193 // Keep checking the length.
1194 if ($smcFunc['strlen']($target_subject) > 100)
1195 $target_subject = $smcFunc['substr']($target_subject, 0, 100);
1196
1197 // Nothing left - odd but pick the first topics subject.
1198 if ($target_subject == '')
1199 $target_subject = $topic_data[$firstTopic]['subject'];
1200 }
1201 // A subject was selected from the list.
1202 elseif (!empty($topic_data[(int) $_POST['subject']]['subject']))
1203 $target_subject = $topic_data[(int) $_POST['subject']]['subject'];
1204 // Nothing worked? Just take the subject of the first message.
1205 else
1206 $target_subject = $topic_data[$firstTopic]['subject'];
1207
1208 // Get the first and last message and the number of messages....
1209 $request = $smcFunc['db_query']('', '
1210 SELECT approved, MIN(id_msg) AS first_msg, MAX(id_msg) AS last_msg, COUNT(*) AS message_count
1211 FROM {db_prefix}messages
1212 WHERE id_topic IN ({array_int:topics})
1213 GROUP BY approved
1214 ORDER BY approved DESC',
1215 array(
1216 'topics' => $topics,
1217 )
1218 );
1219 $topic_approved = 1;
1220 while ($row = $smcFunc['db_fetch_assoc']($request))
1221 {
1222 // If this is approved, or is fully unapproved.
1223 if ($row['approved'] || !isset($first_msg))
1224 {
1225 $first_msg = $row['first_msg'];
1226 $last_msg = $row['last_msg'];
1227 if ($row['approved'])
1228 {
1229 $num_replies = $row['message_count'] - 1;
1230 $num_unapproved = 0;
1231 }
1232 else
1233 {
1234 $topic_approved = 0;
1235 $num_replies = 0;
1236 $num_unapproved = $row['message_count'];
1237 }
1238 }
1239 else
1240 {
1241 // If this has a lower first_msg then the first post is not approved and hence the number of replies was wrong!
1242 if ($first_msg > $row['first_msg'])
1243 {
1244 $first_msg = $row['first_msg'];
1245 $num_replies++;
1246 $topic_approved = 0;
1247 }
1248 $num_unapproved = $row['message_count'];
1249 }
1250 }
1251 $smcFunc['db_free_result']($request);
1252
1253 // Ensure we have a board stat for the target board.
1254 if (!isset($boardTotals[$target_board]))
1255 {
1256 $boardTotals[$target_board] = array(
1257 'posts' => 0,
1258 'topics' => 0,
1259 'unapproved_posts' => 0,
1260 'unapproved_topics' => 0
1261 );
1262 }
1263
1264 // Fix the topic count stuff depending on what the new one counts as.
1265 if ($topic_approved)
1266 $boardTotals[$target_board]['topics']--;
1267 else
1268 $boardTotals[$target_board]['unapproved_topics']--;
1269
1270 $boardTotals[$target_board]['unapproved_posts'] -= $num_unapproved;
1271 $boardTotals[$target_board]['posts'] -= $topic_approved ? $num_replies + 1 : $num_replies;
1272
1273 // Get the member ID of the first and last message.
1274 $request = $smcFunc['db_query']('', '
1275 SELECT id_member
1276 FROM {db_prefix}messages
1277 WHERE id_msg IN ({int:first_msg}, {int:last_msg})
1278 ORDER BY id_msg
1279 LIMIT 2',
1280 array(
1281 'first_msg' => $first_msg,
1282 'last_msg' => $last_msg,
1283 )
1284 );
1285 list ($member_started) = $smcFunc['db_fetch_row']($request);
1286 list ($member_updated) = $smcFunc['db_fetch_row']($request);
1287 // First and last message are the same, so only row was returned.
1288 if ($member_updated === NULL)
1289 $member_updated = $member_started;
1290
1291 $smcFunc['db_free_result']($request);
1292
1293 // Assign the first topic ID to be the merged topic.
1294 $id_topic = min($topics);
1295
1296 // Delete the remaining topics.
1297 $deleted_topics = array_diff($topics, array($id_topic));
1298 $smcFunc['db_query']('', '
1299 DELETE FROM {db_prefix}topics
1300 WHERE id_topic IN ({array_int:deleted_topics})',
1301 array(
1302 'deleted_topics' => $deleted_topics,
1303 )
1304 );
1305 $smcFunc['db_query']('', '
1306 DELETE FROM {db_prefix}log_search_subjects
1307 WHERE id_topic IN ({array_int:deleted_topics})',
1308 array(
1309 'deleted_topics' => $deleted_topics,
1310 )
1311 );
1312
1313 // Asssign the properties of the newly merged topic.
1314 $smcFunc['db_query']('', '
1315 UPDATE {db_prefix}topics
1316 SET
1317 id_board = {int:id_board},
1318 id_member_started = {int:id_member_started},
1319 id_member_updated = {int:id_member_updated},
1320 id_first_msg = {int:id_first_msg},
1321 id_last_msg = {int:id_last_msg},
1322 id_poll = {int:id_poll},
1323 num_replies = {int:num_replies},
1324 unapproved_posts = {int:unapproved_posts},
1325 num_views = {int:num_views},
1326 is_sticky = {int:is_sticky},
1327 approved = {int:approved}
1328 WHERE id_topic = {int:id_topic}',
1329 array(
1330 'id_board' => $target_board,
1331 'is_sticky' => $is_sticky,
1332 'approved' => $topic_approved,
1333 'id_topic' => $id_topic,
1334 'id_member_started' => $member_started,
1335 'id_member_updated' => $member_updated,
1336 'id_first_msg' => $first_msg,
1337 'id_last_msg' => $last_msg,
1338 'id_poll' => $target_poll,
1339 'num_replies' => $num_replies,
1340 'unapproved_posts' => $num_unapproved,
1341 'num_views' => $num_views,
1342 )
1343 );
1344
1345 // Grab the response prefix (like 'Re: ') in the default forum language.
1346 if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix')))
1347 {
1348 if ($language === $user_info['language'])
1349 $context['response_prefix'] = $txt['response_prefix'];
1350 else
1351 {
1352 loadLanguage('index', $language, false);
1353 $context['response_prefix'] = $txt['response_prefix'];
1354 loadLanguage('index');
1355 }
1356 cache_put_data('response_prefix', $context['response_prefix'], 600);
1357 }
1358
1359 // Change the topic IDs of all messages that will be merged. Also adjust subjects if 'enforce subject' was checked.
1360 $smcFunc['db_query']('', '
1361 UPDATE {db_prefix}messages
1362 SET
1363 id_topic = {int:id_topic},
1364 id_board = {int:target_board}' . (empty($_POST['enforce_subject']) ? '' : ',
1365 subject = {string:subject}') . '
1366 WHERE id_topic IN ({array_int:topic_list})',
1367 array(
1368 'topic_list' => $topics,
1369 'id_topic' => $id_topic,
1370 'target_board' => $target_board,
1371 'subject' => $context['response_prefix'] . $target_subject,
1372 )
1373 );
1374
1375 // Any reported posts should reflect the new board.
1376 $smcFunc['db_query']('', '
1377 UPDATE {db_prefix}log_reported
1378 SET
1379 id_topic = {int:id_topic},
1380 id_board = {int:target_board}
1381 WHERE id_topic IN ({array_int:topics_list})',
1382 array(
1383 'topics_list' => $topics,
1384 'id_topic' => $id_topic,
1385 'target_board' => $target_board,
1386 )
1387 );
1388
1389 // Change the subject of the first message...
1390 $smcFunc['db_query']('', '
1391 UPDATE {db_prefix}messages
1392 SET subject = {string:target_subject}
1393 WHERE id_msg = {int:first_msg}',
1394 array(
1395 'first_msg' => $first_msg,
1396 'target_subject' => $target_subject,
1397 )
1398 );
1399
1400 // Adjust all calendar events to point to the new topic.
1401 $smcFunc['db_query']('', '
1402 UPDATE {db_prefix}calendar
1403 SET
1404 id_topic = {int:id_topic},
1405 id_board = {int:target_board}
1406 WHERE id_topic IN ({array_int:deleted_topics})',
1407 array(
1408 'deleted_topics' => $deleted_topics,
1409 'id_topic' => $id_topic,
1410 'target_board' => $target_board,
1411 )
1412 );
1413
1414 // Merge log topic entries.
1415 $request = $smcFunc['db_query']('', '
1416 SELECT id_member, MIN(id_msg) AS new_id_msg
1417 FROM {db_prefix}log_topics
1418 WHERE id_topic IN ({array_int:topics})
1419 GROUP BY id_member',
1420 array(
1421 'topics' => $topics,
1422 )
1423 );
1424 if ($smcFunc['db_num_rows']($request) > 0)
1425 {
1426 $replaceEntries = array();
1427 while ($row = $smcFunc['db_fetch_assoc']($request))
1428 $replaceEntries[] = array($row['id_member'], $id_topic, $row['new_id_msg']);
1429
1430 $smcFunc['db_insert']('replace',
1431 '{db_prefix}log_topics',
1432 array('id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int'),
1433 $replaceEntries,
1434 array('id_member', 'id_topic')
1435 );
1436 unset($replaceEntries);
1437
1438 // Get rid of the old log entries.
1439 $smcFunc['db_query']('', '
1440 DELETE FROM {db_prefix}log_topics
1441 WHERE id_topic IN ({array_int:deleted_topics})',
1442 array(
1443 'deleted_topics' => $deleted_topics,
1444 )
1445 );
1446 }
1447 $smcFunc['db_free_result']($request);
1448
1449 // Merge topic notifications.
1450 $notifications = isset($_POST['notifications']) && is_array($_POST['notifications']) ? array_intersect($topics, $_POST['notifications']) : array();
1451 if (!empty($notifications))
1452 {
1453 $request = $smcFunc['db_query']('', '
1454 SELECT id_member, MAX(sent) AS sent
1455 FROM {db_prefix}log_notify
1456 WHERE id_topic IN ({array_int:topics_list})
1457 GROUP BY id_member',
1458 array(
1459 'topics_list' => $notifications,
1460 )
1461 );
1462 if ($smcFunc['db_num_rows']($request) > 0)
1463 {
1464 $replaceEntries = array();
1465 while ($row = $smcFunc['db_fetch_assoc']($request))
1466 $replaceEntries[] = array($row['id_member'], $id_topic, 0, $row['sent']);
1467
1468 $smcFunc['db_insert']('replace',
1469 '{db_prefix}log_notify',
1470 array('id_member' => 'int', 'id_topic' => 'int', 'id_board' => 'int', 'sent' => 'int'),
1471 $replaceEntries,
1472 array('id_member', 'id_topic', 'id_board')
1473 );
1474 unset($replaceEntries);
1475
1476 $smcFunc['db_query']('', '
1477 DELETE FROM {db_prefix}log_topics
1478 WHERE id_topic IN ({array_int:deleted_topics})',
1479 array(
1480 'deleted_topics' => $deleted_topics,
1481 )
1482 );
1483 }
1484 $smcFunc['db_free_result']($request);
1485 }
1486
1487 // Get rid of the redundant polls.
1488 if (!empty($deleted_polls))
1489 {
1490 $smcFunc['db_query']('', '
1491 DELETE FROM {db_prefix}polls
1492 WHERE id_poll IN ({array_int:deleted_polls})',
1493 array(
1494 'deleted_polls' => $deleted_polls,
1495 )
1496 );
1497 $smcFunc['db_query']('', '
1498 DELETE FROM {db_prefix}poll_choices
1499 WHERE id_poll IN ({array_int:deleted_polls})',
1500 array(
1501 'deleted_polls' => $deleted_polls,
1502 )
1503 );
1504 $smcFunc['db_query']('', '
1505 DELETE FROM {db_prefix}log_polls
1506 WHERE id_poll IN ({array_int:deleted_polls})',
1507 array(
1508 'deleted_polls' => $deleted_polls,
1509 )
1510 );
1511 }
1512
1513 // Cycle through each board...
1514 foreach ($boardTotals as $id_board => $stats)
1515 {
1516 $smcFunc['db_query']('', '
1517 UPDATE {db_prefix}boards
1518 SET
1519 num_topics = CASE WHEN {int:topics} > num_topics THEN 0 ELSE num_topics - {int:topics} END,
1520 unapproved_topics = CASE WHEN {int:unapproved_topics} > unapproved_topics THEN 0 ELSE unapproved_topics - {int:unapproved_topics} END,
1521 num_posts = CASE WHEN {int:posts} > num_posts THEN 0 ELSE num_posts - {int:posts} END,
1522 unapproved_posts = CASE WHEN {int:unapproved_posts} > unapproved_posts THEN 0 ELSE unapproved_posts - {int:unapproved_posts} END
1523 WHERE id_board = {int:id_board}',
1524 array(
1525 'id_board' => $id_board,
1526 'topics' => $stats['topics'],
1527 'unapproved_topics' => $stats['unapproved_topics'],
1528 'posts' => $stats['posts'],
1529 'unapproved_posts' => $stats['unapproved_posts'],
1530 )
1531 );
1532 }
1533
1534 // Determine the board the final topic resides in
1535 $request = $smcFunc['db_query']('', '
1536 SELECT id_board
1537 FROM {db_prefix}topics
1538 WHERE id_topic = {int:id_topic}
1539 LIMIT 1',
1540 array(
1541 'id_topic' => $id_topic,
1542 )
1543 );
1544 list($id_board) = $smcFunc['db_fetch_row']($request);
1545 $smcFunc['db_free_result']($request);
1546
1547 require_once($sourcedir . '/Subs-Post.php');
1548
1549 // Update all the statistics.
1550 updateStats('topic');
1551 updateStats('subject', $id_topic, $target_subject);
1552 updateLastMessages($boards);
1553
1554 logAction('merge', array('topic' => $id_topic, 'board' => $id_board));
1555
1556 // Notify people that these topics have been merged?
1557 sendNotifications($id_topic, 'merge');
1558
1559 // Send them to the all done page.
1560 redirectexit('action=mergetopics;sa=done;to=' . $id_topic . ';targetboard=' . $target_board);
1561 }
1562
1563 // Tell the user the move was done properly.
1564 function MergeDone()
1565 {
1566 global $txt, $context;
1567
1568 // Make sure the template knows everything...
1569 $context['target_board'] = (int) $_GET['targetboard'];
1570 $context['target_topic'] = (int) $_GET['to'];
1571
1572 $context['page_title'] = $txt['merge'];
1573 $context['sub_template'] = 'merge_done';
1574 }
1575
1576 ?>