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