Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 963
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
lock_unlock
0.00% covered (danger)
0.00%
0 / 52
0.00% covered (danger)
0.00%
0 / 1
342
change_topic_type
0.00% covered (danger)
0.00%
0 / 70
0.00% covered (danger)
0.00%
0 / 1
306
mcp_move_topic
0.00% covered (danger)
0.00%
0 / 151
0.00% covered (danger)
0.00%
0 / 1
1406
mcp_restore_topic
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
110
mcp_delete_topic
0.00% covered (danger)
0.00%
0 / 109
0.00% covered (danger)
0.00%
0 / 1
462
mcp_delete_post
0.00% covered (danger)
0.00%
0 / 131
0.00% covered (danger)
0.00%
0 / 1
1560
mcp_fork_topic
0.00% covered (danger)
0.00%
0 / 276
0.00% covered (danger)
0.00%
0 / 1
2256
mcp_main
0.00% covered (danger)
0.00%
0 / 132
0.00% covered (danger)
0.00%
0 / 2
4160
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 main
0.00% covered (danger)
0.00%
0 / 131
0.00% covered (danger)
0.00%
0 / 1
4032
1<?php
2/**
3*
4* This file is part of the phpBB Forum Software package.
5*
6* @copyright (c) phpBB Limited <https://www.phpbb.com>
7* @license GNU General Public License, version 2 (GPL-2.0)
8*
9* For full copyright and license information, please see
10* the docs/CREDITS.txt file.
11*
12*/
13
14/**
15* @ignore
16*/
17if (!defined('IN_PHPBB'))
18{
19    exit;
20}
21
22/**
23* mcp_main
24* Handling mcp actions
25*/
26class mcp_main
27{
28    var $p_master;
29    var $u_action;
30
31    function __construct($p_master)
32    {
33        $this->p_master = $p_master;
34    }
35
36    function main($id, $mode)
37    {
38        global $auth, $user, $action;
39        global $phpbb_root_path, $phpEx, $request;
40        global $phpbb_dispatcher;
41
42        $quickmod = ($mode == 'quickmod') ? true : false;
43
44        /**
45        * Event to perform additional actions before an MCP action is executed.
46        *
47        * @event core.mcp_main_before
48        * @var    string    action                The action that is about to be performed
49        * @var    string    mode                The mode in which the MCP is accessed, e.g. front, forum_view, topic_view, post_details, quickmod
50        * @var    boolean    quickmod            Whether or not the action is performed via QuickMod
51        * @since 3.2.8-RC1
52        */
53        $vars = [
54            'action',
55            'mode',
56            'quickmod',
57        ];
58        extract($phpbb_dispatcher->trigger_event('core.mcp_main_before', compact($vars)));
59
60        switch ($action)
61        {
62            case 'lock':
63            case 'unlock':
64                $topic_ids = (!$quickmod) ? $request->variable('topic_id_list', array(0)) : array($request->variable('t', 0));
65
66                if (!count($topic_ids))
67                {
68                    trigger_error('NO_TOPIC_SELECTED');
69                }
70
71                lock_unlock($action, $topic_ids);
72            break;
73
74            case 'lock_post':
75            case 'unlock_post':
76
77                $post_ids = (!$quickmod) ? $request->variable('post_id_list', array(0)) : array($request->variable('p', 0));
78
79                if (!count($post_ids))
80                {
81                    trigger_error('NO_POST_SELECTED');
82                }
83
84                lock_unlock($action, $post_ids);
85            break;
86
87            case 'make_announce':
88            case 'make_sticky':
89            case 'make_global':
90            case 'make_normal':
91
92                $topic_ids = (!$quickmod) ? $request->variable('topic_id_list', array(0)) : array($request->variable('t', 0));
93
94                if (!count($topic_ids))
95                {
96                    trigger_error('NO_TOPIC_SELECTED');
97                }
98
99                change_topic_type($action, $topic_ids);
100            break;
101
102            case 'move':
103                $user->add_lang('viewtopic');
104
105                $topic_ids = (!$quickmod) ? $request->variable('topic_id_list', array(0)) : array($request->variable('t', 0));
106
107                if (!count($topic_ids))
108                {
109                    trigger_error('NO_TOPIC_SELECTED');
110                }
111
112                mcp_move_topic($topic_ids);
113            break;
114
115            case 'fork':
116                $user->add_lang('viewtopic');
117
118                $topic_ids = (!$quickmod) ? $request->variable('topic_id_list', array(0)) : array($request->variable('t', 0));
119
120                if (!count($topic_ids))
121                {
122                    trigger_error('NO_TOPIC_SELECTED');
123                }
124
125                mcp_fork_topic($topic_ids);
126            break;
127
128            case 'delete_topic':
129                $user->add_lang('viewtopic');
130
131                // f parameter is not reliable for permission usage, however we just use it to decide
132                // which permission we will check later on. So if it is manipulated, we will still catch it later on.
133                $forum_id = $request->variable('f', 0);
134                $topic_ids = (!$quickmod) ? $request->variable('topic_id_list', array(0)) : array($request->variable('t', 0));
135                $soft_delete = (($request->is_set_post('confirm') && !$request->is_set_post('delete_permanent')) || !$auth->acl_get('m_delete', $forum_id)) ? true : false;
136
137                if (!count($topic_ids))
138                {
139                    trigger_error('NO_TOPIC_SELECTED');
140                }
141
142                mcp_delete_topic($topic_ids, $soft_delete, $request->variable('delete_reason', '', true));
143            break;
144
145            case 'delete_post':
146                $user->add_lang('posting');
147
148                // f parameter is not reliable for permission usage, however we just use it to decide
149                // which permission we will check later on. So if it is manipulated, we will still catch it later on.
150                $forum_id = $request->variable('f', 0);
151                $post_ids = (!$quickmod) ? $request->variable('post_id_list', array(0)) : array($request->variable('p', 0));
152                $soft_delete = (($request->is_set_post('confirm') && !$request->is_set_post('delete_permanent')) || !$auth->acl_get('m_delete', $forum_id)) ? true : false;
153
154                if (!count($post_ids))
155                {
156                    trigger_error('NO_POST_SELECTED');
157                }
158
159                mcp_delete_post($post_ids, $soft_delete, $request->variable('delete_reason', '', true));
160            break;
161
162            case 'restore_topic':
163                $user->add_lang('posting');
164
165                $topic_ids = (!$quickmod) ? $request->variable('topic_id_list', array(0)) : array($request->variable('t', 0));
166
167                if (!count($topic_ids))
168                {
169                    trigger_error('NO_TOPIC_SELECTED');
170                }
171
172                mcp_restore_topic($topic_ids);
173            break;
174
175            default:
176                /**
177                * This event allows you to handle custom quickmod options
178                *
179                * @event core.modify_quickmod_actions
180                * @var    string    action        Topic quick moderation action name
181                * @var    bool    quickmod    Flag indicating whether MCP is in quick moderation mode
182                * @since 3.1.0-a4
183                * @changed 3.1.0-RC4 Added variables: action, quickmod
184                */
185                $vars = array('action', 'quickmod');
186                extract($phpbb_dispatcher->trigger_event('core.modify_quickmod_actions', compact($vars)));
187            break;
188        }
189
190        switch ($mode)
191        {
192            case 'front':
193                if (!function_exists('mcp_front_view'))
194                {
195                    include($phpbb_root_path . 'includes/mcp/mcp_front.' . $phpEx);
196                }
197
198                $user->add_lang('acp/common');
199
200                mcp_front_view($id, $mode, $action);
201
202                $this->tpl_name = 'mcp_front';
203                $this->page_title = 'MCP_MAIN';
204            break;
205
206            case 'forum_view':
207                if (!function_exists('mcp_forum_view'))
208                {
209                    include($phpbb_root_path . 'includes/mcp/mcp_forum.' . $phpEx);
210                }
211
212                $user->add_lang('viewforum');
213
214                $forum_id = $request->variable('f', 0);
215
216                $forum_info = phpbb_get_forum_data($forum_id, 'm_', true);
217
218                if (!count($forum_info))
219                {
220                    $this->main('main', 'front');
221                    return;
222                }
223
224                $forum_info = $forum_info[$forum_id];
225
226                mcp_forum_view($id, $mode, $action, $forum_info);
227
228                $this->tpl_name = 'mcp_forum';
229                $this->page_title = 'MCP_MAIN_FORUM_VIEW';
230            break;
231
232            case 'topic_view':
233                if (!function_exists('mcp_topic_view'))
234                {
235                    include($phpbb_root_path . 'includes/mcp/mcp_topic.' . $phpEx);
236                }
237
238                mcp_topic_view($id, $mode, $action);
239
240                $this->tpl_name = 'mcp_topic';
241                $this->page_title = 'MCP_MAIN_TOPIC_VIEW';
242            break;
243
244            case 'post_details':
245                if (!function_exists('mcp_post_details'))
246                {
247                    include($phpbb_root_path . 'includes/mcp/mcp_post.' . $phpEx);
248                }
249
250                mcp_post_details($id, $mode, $action);
251
252                $this->tpl_name = ($action == 'whois') ? 'mcp_whois' : 'mcp_post';
253                $this->page_title = 'MCP_MAIN_POST_DETAILS';
254            break;
255
256            default:
257                if ($quickmod)
258                {
259                    switch ($action)
260                    {
261                        case 'lock':
262                        case 'unlock':
263                        case 'make_announce':
264                        case 'make_sticky':
265                        case 'make_global':
266                        case 'make_normal':
267                        case 'make_onindex':
268                        case 'move':
269                        case 'fork':
270                        case 'delete_topic':
271                            trigger_error('TOPIC_NOT_EXIST');
272                        break;
273
274                        case 'lock_post':
275                        case 'unlock_post':
276                        case 'delete_post':
277                            trigger_error('POST_NOT_EXIST');
278                        break;
279                    }
280                }
281
282                trigger_error('NO_MODE', E_USER_ERROR);
283            break;
284        }
285    }
286}
287
288/**
289* Lock/Unlock Topic/Post
290*/
291function lock_unlock($action, $ids)
292{
293    global $user, $db, $request, $phpbb_log, $phpbb_dispatcher;
294
295    if ($action == 'lock' || $action == 'unlock')
296    {
297        $table = TOPICS_TABLE;
298        $sql_id = 'topic_id';
299        $set_id = 'topic_status';
300        $l_prefix = 'TOPIC';
301    }
302    else
303    {
304        $table = POSTS_TABLE;
305        $sql_id = 'post_id';
306        $set_id = 'post_edit_locked';
307        $l_prefix = 'POST';
308    }
309
310    $orig_ids = $ids;
311
312    if (!phpbb_check_ids($ids, $table, $sql_id, array('m_lock')))
313    {
314        // Make sure that for f_user_lock only the lock action is triggered.
315        if ($action != 'lock')
316        {
317            return;
318        }
319
320        $ids = $orig_ids;
321
322        if (!phpbb_check_ids($ids, $table, $sql_id, array('f_user_lock')))
323        {
324            return;
325        }
326    }
327    unset($orig_ids);
328
329    $redirect = $request->variable('redirect', build_url(array('action', 'quickmod')));
330    $redirect = reapply_sid($redirect);
331
332    $s_hidden_fields = build_hidden_fields(array(
333        $sql_id . '_list'    => $ids,
334        'action'            => $action,
335        'redirect'            => $redirect)
336    );
337
338    if (confirm_box(true))
339    {
340        $sql = "UPDATE $table
341            SET $set_id = " . (($action == 'lock' || $action == 'lock_post') ? ITEM_LOCKED : ITEM_UNLOCKED) . '
342            WHERE ' . $db->sql_in_set($sql_id, $ids);
343        $db->sql_query($sql);
344
345        $data = ($action == 'lock' || $action == 'unlock') ? phpbb_get_topic_data($ids) : phpbb_get_post_data($ids);
346
347        foreach ($data as $id => $row)
348        {
349            $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_' . strtoupper($action), false, array(
350                'forum_id' => $row['forum_id'],
351                'topic_id' => $row['topic_id'],
352                'post_id'  => isset($row['post_id']) ? $row['post_id'] : 0,
353                $row['topic_title']
354            ));
355        }
356
357        /**
358         * Perform additional actions after locking/unlocking posts/topics
359         *
360         * @event core.mcp_lock_unlock_after
361         * @var    string    action                Variable containing the action we perform on the posts/topics ('lock', 'unlock', 'lock_post' or 'unlock_post')
362         * @var    array    ids                    Array containing the post/topic IDs that have been locked/unlocked
363         * @var    array    data                Array containing posts/topics data
364         * @since 3.1.7-RC1
365         */
366        $vars = array(
367            'action',
368            'ids',
369            'data',
370        );
371        extract($phpbb_dispatcher->trigger_event('core.mcp_lock_unlock_after', compact($vars)));
372
373        $success_msg = $l_prefix . ((count($ids) == 1) ? '' : 'S') . '_' . (($action == 'lock' || $action == 'lock_post') ? 'LOCKED' : 'UNLOCKED') . '_SUCCESS';
374
375        meta_refresh(2, $redirect);
376        $message = $user->lang[$success_msg];
377
378        if (!$request->is_ajax())
379        {
380            $message .= '<br /><br />' . $user->lang('RETURN_PAGE', '<a href="' . $redirect . '">', '</a>');
381        }
382        trigger_error($message);
383    }
384    else
385    {
386        confirm_box(false, strtoupper($action) . '_' . $l_prefix . ((count($ids) == 1) ? '' : 'S'), $s_hidden_fields);
387    }
388
389    redirect($redirect);
390}
391
392/**
393* Change Topic Type
394*/
395function change_topic_type($action, $topic_ids)
396{
397    global $user, $db, $request, $phpbb_log, $phpbb_dispatcher;
398
399    switch ($action)
400    {
401        case 'make_announce':
402            $new_topic_type = POST_ANNOUNCE;
403            $check_acl = 'f_announce';
404            $l_new_type = (count($topic_ids) == 1) ? 'MCP_MAKE_ANNOUNCEMENT' : 'MCP_MAKE_ANNOUNCEMENTS';
405        break;
406
407        case 'make_global':
408            $new_topic_type = POST_GLOBAL;
409            $check_acl = 'f_announce_global';
410            $l_new_type = (count($topic_ids) == 1) ? 'MCP_MAKE_GLOBAL' : 'MCP_MAKE_GLOBALS';
411        break;
412
413        case 'make_sticky':
414            $new_topic_type = POST_STICKY;
415            $check_acl = 'f_sticky';
416            $l_new_type = (count($topic_ids) == 1) ? 'MCP_MAKE_STICKY' : 'MCP_MAKE_STICKIES';
417        break;
418
419        default:
420            $new_topic_type = POST_NORMAL;
421            $check_acl = false;
422            $l_new_type = (count($topic_ids) == 1) ? 'MCP_MAKE_NORMAL' : 'MCP_MAKE_NORMALS';
423        break;
424    }
425
426    $forum_id = phpbb_check_ids($topic_ids, TOPICS_TABLE, 'topic_id', $check_acl, true);
427
428    if ($forum_id === false)
429    {
430        return;
431    }
432
433    $redirect = $request->variable('redirect', build_url(array('action', 'quickmod')));
434    $redirect = reapply_sid($redirect);
435
436    $s_hidden_fields = array(
437        'topic_id_list'    => $topic_ids,
438        'f'                => $forum_id,
439        'action'        => $action,
440        'redirect'        => $redirect,
441    );
442
443    if (confirm_box(true))
444    {
445
446        /**
447         * Perform additional actions before changing topic(s) type
448         *
449         * @event core.mcp_change_topic_type_before
450         * @var    int        new_topic_type        The candidated topic type.
451         * @var    int        forum_id            The forum ID for the topic ID(s).
452         * @var    array    topic_ids            Array containing the topic ID(s) that will be changed
453         * @since 3.2.6-RC1
454         */
455        $vars = array(
456            'new_topic_type',
457            'forum_id',
458            'topic_ids',
459        );
460        extract($phpbb_dispatcher->trigger_event('core.mcp_change_topic_type_before', compact($vars)));
461
462        $db->sql_transaction('begin');
463
464        $sql = 'UPDATE ' . TOPICS_TABLE . "
465            SET topic_type = $new_topic_type
466            WHERE " . $db->sql_in_set('topic_id', $topic_ids);
467        $db->sql_query($sql);
468
469        if (($new_topic_type == POST_GLOBAL) && count($topic_ids))
470        {
471            // Delete topic shadows for global announcements
472            $sql = 'DELETE FROM ' . TOPICS_TABLE . '
473                WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids);
474            $db->sql_query($sql);
475        }
476
477        $db->sql_transaction('commit');
478
479        $success_msg = (count($topic_ids) == 1) ? 'TOPIC_TYPE_CHANGED' : 'TOPICS_TYPE_CHANGED';
480
481        if (count($topic_ids))
482        {
483            $data = phpbb_get_topic_data($topic_ids);
484
485            foreach ($data as $topic_id => $row)
486            {
487                $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_TOPIC_TYPE_CHANGED', false, array(
488                    'forum_id' => $forum_id,
489                    'topic_id' => $topic_id,
490                    $row['topic_title']
491                ));
492            }
493        }
494
495        /**
496         * Perform additional actions after changing topic types
497         *
498         * @event core.mcp_change_topic_type_after
499         * @var    int        new_topic_type        The newly changed topic type.
500         * @var    int        forum_id            The forum ID where the newly changed topic type belongs to.
501         * @var    array    topic_ids            Array containing the topic IDs that have been changed
502         * @since 3.2.6-RC1
503         */
504        $vars = array(
505            'new_topic_type',
506            'forum_id',
507            'topic_ids',
508        );
509        extract($phpbb_dispatcher->trigger_event('core.mcp_change_topic_type_after', compact($vars)));
510
511        meta_refresh(2, $redirect);
512        $message = $user->lang[$success_msg];
513
514        if (!$request->is_ajax())
515        {
516            $message .= '<br /><br />' . $user->lang('RETURN_PAGE', '<a href="' . $redirect . '">', '</a>');
517        }
518        trigger_error($message);
519    }
520    else
521    {
522        confirm_box(false, $l_new_type, build_hidden_fields($s_hidden_fields));
523    }
524
525    redirect($redirect);
526}
527
528/**
529* Move Topic
530*/
531function mcp_move_topic($topic_ids)
532{
533    global $auth, $user, $db, $template, $phpbb_log, $request, $phpbb_dispatcher;
534    global $phpEx, $phpbb_root_path;
535
536    // Here we limit the operation to one forum only
537    $forum_id = phpbb_check_ids($topic_ids, TOPICS_TABLE, 'topic_id', array('m_move'), true);
538
539    if ($forum_id === false)
540    {
541        return;
542    }
543
544    $to_forum_id = $request->variable('to_forum_id', 0);
545    $redirect = $request->variable('redirect', build_url(array('action', 'quickmod')));
546    $additional_msg = $success_msg = '';
547
548    $s_hidden_fields = build_hidden_fields(array(
549        'topic_id_list'    => $topic_ids,
550        'f'                => $forum_id,
551        'action'        => 'move',
552        'redirect'        => $redirect)
553    );
554
555    if ($to_forum_id)
556    {
557        $forum_data = phpbb_get_forum_data($to_forum_id, 'f_post');
558
559        if (!count($forum_data))
560        {
561            $additional_msg = $user->lang['FORUM_NOT_EXIST'];
562        }
563        else
564        {
565            $forum_data = $forum_data[$to_forum_id];
566
567            if ($forum_data['forum_type'] != FORUM_POST)
568            {
569                $additional_msg = $user->lang['FORUM_NOT_POSTABLE'];
570            }
571            else if (!$auth->acl_get('f_post', $to_forum_id) || (!$auth->acl_get('m_approve', $to_forum_id) && !$auth->acl_get('f_noapprove', $to_forum_id)))
572            {
573                $additional_msg = $user->lang['USER_CANNOT_POST'];
574            }
575            else if ($forum_id == $to_forum_id)
576            {
577                $additional_msg = $user->lang['CANNOT_MOVE_SAME_FORUM'];
578            }
579        }
580    }
581    else if (isset($_POST['confirm']))
582    {
583        $additional_msg = $user->lang['FORUM_NOT_EXIST'];
584    }
585
586    if (!$to_forum_id || $additional_msg)
587    {
588        $request->overwrite('confirm', null, \phpbb\request\request_interface::POST);
589        $request->overwrite('confirm_key', null);
590    }
591
592    if (confirm_box(true))
593    {
594        $topic_data = phpbb_get_topic_data($topic_ids);
595        $leave_shadow = (isset($_POST['move_leave_shadow'])) ? true : false;
596
597        $topics_moved = $topics_moved_unapproved = $topics_moved_softdeleted = 0;
598        $posts_moved = $posts_moved_unapproved = $posts_moved_softdeleted = 0;
599
600        foreach ($topic_data as $topic_id => $topic_info)
601        {
602            if ($topic_info['topic_visibility'] == ITEM_APPROVED)
603            {
604                $topics_moved++;
605            }
606            else if ($topic_info['topic_visibility'] == ITEM_UNAPPROVED || $topic_info['topic_visibility'] == ITEM_REAPPROVE)
607            {
608                $topics_moved_unapproved++;
609            }
610            else if ($topic_info['topic_visibility'] == ITEM_DELETED)
611            {
612                $topics_moved_softdeleted++;
613            }
614
615            $posts_moved += $topic_info['topic_posts_approved'];
616            $posts_moved_unapproved += $topic_info['topic_posts_unapproved'];
617            $posts_moved_softdeleted += $topic_info['topic_posts_softdeleted'];
618        }
619
620        $db->sql_transaction('begin');
621
622        // Move topics, but do not resync yet
623        move_topics($topic_ids, $to_forum_id, false);
624
625        if ($request->is_set_post('move_lock_topics') && $auth->acl_get('m_lock', $to_forum_id))
626        {
627            $sql = 'UPDATE ' . TOPICS_TABLE . '
628                SET topic_status = ' . ITEM_LOCKED . '
629                WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
630            $db->sql_query($sql);
631        }
632
633        $shadow_topics = 0;
634        foreach ($topic_data as $topic_id => $row)
635        {
636            // We add the $to_forum_id twice, because 'forum_id' is updated
637            // when the topic is moved again later.
638            $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_MOVE', false, array(
639                'forum_id'        => (int) $to_forum_id,
640                'topic_id'        => (int) $topic_id,
641                $row['forum_name'],
642                $forum_data['forum_name'],
643                (int) $row['forum_id'],
644                (int) $forum_data['forum_id'],
645            ));
646
647            // Leave a redirection if required and only if the topic is visible to users
648            if ($leave_shadow && $row['topic_visibility'] == ITEM_APPROVED && $row['topic_type'] != POST_GLOBAL)
649            {
650                $shadow = array(
651                    'forum_id'                =>    (int) $row['forum_id'],
652                    'icon_id'                =>    (int) $row['icon_id'],
653                    'topic_attachment'        =>    (int) $row['topic_attachment'],
654                    'topic_visibility'        =>    ITEM_APPROVED, // a shadow topic is always approved
655                    'topic_reported'        =>    0, // a shadow topic is never reported
656                    'topic_title'            =>    (string) $row['topic_title'],
657                    'topic_poster'            =>    (int) $row['topic_poster'],
658                    'topic_time'            =>    (int) $row['topic_time'],
659                    'topic_time_limit'        =>    (int) $row['topic_time_limit'],
660                    'topic_views'            =>    (int) $row['topic_views'],
661                    'topic_posts_approved'    =>    (int) $row['topic_posts_approved'],
662                    'topic_posts_unapproved'=>    (int) $row['topic_posts_unapproved'],
663                    'topic_posts_softdeleted'=>    (int) $row['topic_posts_softdeleted'],
664                    'topic_status'            =>    ITEM_MOVED,
665                    'topic_type'            =>    POST_NORMAL,
666                    'topic_first_post_id'    =>    (int) $row['topic_first_post_id'],
667                    'topic_first_poster_colour'=>(string) $row['topic_first_poster_colour'],
668                    'topic_first_poster_name'=>    (string) $row['topic_first_poster_name'],
669                    'topic_last_post_id'    =>    (int) $row['topic_last_post_id'],
670                    'topic_last_poster_id'    =>    (int) $row['topic_last_poster_id'],
671                    'topic_last_poster_colour'=>(string) $row['topic_last_poster_colour'],
672                    'topic_last_poster_name'=>    (string) $row['topic_last_poster_name'],
673                    'topic_last_post_subject'=>    (string) $row['topic_last_post_subject'],
674                    'topic_last_post_time'    =>    (int) $row['topic_last_post_time'],
675                    'topic_last_view_time'    =>    (int) $row['topic_last_view_time'],
676                    'topic_moved_id'        =>    (int) $row['topic_id'],
677                    'topic_bumped'            =>    (int) $row['topic_bumped'],
678                    'topic_bumper'            =>    (int) $row['topic_bumper'],
679                    'poll_title'            =>    (string) $row['poll_title'],
680                    'poll_start'            =>    (int) $row['poll_start'],
681                    'poll_length'            =>    (int) $row['poll_length'],
682                    'poll_max_options'        =>    (int) $row['poll_max_options'],
683                    'poll_last_vote'        =>    (int) $row['poll_last_vote']
684                );
685
686                /**
687                * Perform actions before shadow topic is created.
688                *
689                * @event core.mcp_main_modify_shadow_sql
690                * @var    array    shadow    SQL array to be used by $db->sql_build_array
691                * @var    array    row        Topic data
692                * @since 3.1.11-RC1
693                * @changed 3.1.11-RC1 Added variable: row
694                */
695                $vars = array(
696                    'shadow',
697                    'row',
698                );
699                extract($phpbb_dispatcher->trigger_event('core.mcp_main_modify_shadow_sql', compact($vars)));
700
701                $db->sql_query('INSERT INTO ' . TOPICS_TABLE . $db->sql_build_array('INSERT', $shadow));
702
703                // Shadow topics only count on new "topics" and not posts... a shadow topic alone has 0 posts
704                $shadow_topics++;
705            }
706        }
707        unset($topic_data);
708
709        $sync_sql = array();
710        if ($posts_moved)
711        {
712            $sync_sql[$to_forum_id][] = 'forum_posts_approved = forum_posts_approved + ' . (int) $posts_moved;
713            $sync_sql[$forum_id][] = 'forum_posts_approved = forum_posts_approved - ' . (int) $posts_moved;
714        }
715        if ($posts_moved_unapproved)
716        {
717            $sync_sql[$to_forum_id][] = 'forum_posts_unapproved = forum_posts_unapproved + ' . (int) $posts_moved_unapproved;
718            $sync_sql[$forum_id][] = 'forum_posts_unapproved = forum_posts_unapproved - ' . (int) $posts_moved_unapproved;
719        }
720        if ($posts_moved_softdeleted)
721        {
722            $sync_sql[$to_forum_id][] = 'forum_posts_softdeleted = forum_posts_softdeleted + ' . (int) $posts_moved_softdeleted;
723            $sync_sql[$forum_id][] = 'forum_posts_softdeleted = forum_posts_softdeleted - ' . (int) $posts_moved_softdeleted;
724        }
725
726        if ($topics_moved)
727        {
728            $sync_sql[$to_forum_id][] = 'forum_topics_approved = forum_topics_approved + ' . (int) $topics_moved;
729            if ($topics_moved - $shadow_topics > 0)
730            {
731                $sync_sql[$forum_id][] = 'forum_topics_approved = forum_topics_approved - ' . (int) ($topics_moved - $shadow_topics);
732            }
733        }
734        if ($topics_moved_unapproved)
735        {
736            $sync_sql[$to_forum_id][] = 'forum_topics_unapproved = forum_topics_unapproved + ' . (int) $topics_moved_unapproved;
737            $sync_sql[$forum_id][] = 'forum_topics_unapproved = forum_topics_unapproved - ' . (int) $topics_moved_unapproved;
738        }
739        if ($topics_moved_softdeleted)
740        {
741            $sync_sql[$to_forum_id][] = 'forum_topics_softdeleted = forum_topics_softdeleted + ' . (int) $topics_moved_softdeleted;
742            $sync_sql[$forum_id][] = 'forum_topics_softdeleted = forum_topics_softdeleted - ' . (int) $topics_moved_softdeleted;
743        }
744
745        $success_msg = (count($topic_ids) == 1) ? 'TOPIC_MOVED_SUCCESS' : 'TOPICS_MOVED_SUCCESS';
746
747        foreach ($sync_sql as $forum_id_key => $array)
748        {
749            $sql = 'UPDATE ' . FORUMS_TABLE . '
750                SET ' . implode(', ', $array) . '
751                WHERE forum_id = ' . $forum_id_key;
752            $db->sql_query($sql);
753        }
754
755        $db->sql_transaction('commit');
756
757        sync('forum', 'forum_id', array($forum_id, $to_forum_id));
758    }
759    else
760    {
761        $template->assign_vars(array(
762            'S_FORUM_SELECT'        => make_forum_select($to_forum_id, $forum_id, false, true, true, true),
763            'S_CAN_LEAVE_SHADOW'    => true,
764            'S_CAN_LOCK_TOPIC'        => ($auth->acl_get('m_lock', $to_forum_id)) ? true : false,
765            'ADDITIONAL_MSG'        => $additional_msg)
766        );
767
768        confirm_box(false, 'MOVE_TOPIC' . ((count($topic_ids) == 1) ? '' : 'S'), $s_hidden_fields, 'mcp_move.html');
769    }
770
771    $redirect = $request->variable('redirect', "index.$phpEx");
772    $redirect = reapply_sid($redirect);
773
774    if (!$success_msg)
775    {
776        redirect($redirect);
777    }
778    else
779    {
780        meta_refresh(3, $redirect);
781
782        $message = $user->lang[$success_msg];
783        $message .= '<br /><br />' . sprintf($user->lang['RETURN_PAGE'], '<a href="' . $redirect . '">', '</a>');
784        $message .= '<br /><br />' . sprintf($user->lang['RETURN_FORUM'], '<a href="' . append_sid("{$phpbb_root_path}viewforum.$phpEx", "f=$forum_id") . '">', '</a>');
785        $message .= '<br /><br />' . sprintf($user->lang['RETURN_NEW_FORUM'], '<a href="' . append_sid("{$phpbb_root_path}viewforum.$phpEx", "f=$to_forum_id") . '">', '</a>');
786
787        trigger_error($message);
788    }
789}
790
791/**
792* Restore Topics
793*/
794function mcp_restore_topic($topic_ids)
795{
796    global $user, $phpEx, $phpbb_root_path, $request, $phpbb_container, $phpbb_log;
797
798    if (!phpbb_check_ids($topic_ids, TOPICS_TABLE, 'topic_id', array('m_approve')))
799    {
800        return;
801    }
802
803    $redirect = $request->variable('redirect', build_url(array('action', 'quickmod')));
804    $forum_id = $request->variable('f', 0);
805
806    $s_hidden_fields = build_hidden_fields(array(
807        'topic_id_list'    => $topic_ids,
808        'f'                => $forum_id,
809        'action'        => 'restore_topic',
810        'redirect'        => $redirect,
811    ));
812    $success_msg = '';
813
814    if (confirm_box(true))
815    {
816        $success_msg = (count($topic_ids) == 1) ? 'TOPIC_RESTORED_SUCCESS' : 'TOPICS_RESTORED_SUCCESS';
817
818        $data = phpbb_get_topic_data($topic_ids);
819
820        /* @var $phpbb_content_visibility \phpbb\content_visibility */
821        $phpbb_content_visibility = $phpbb_container->get('content.visibility');
822        foreach ($data as $topic_id => $row)
823        {
824            $return = $phpbb_content_visibility->set_topic_visibility(ITEM_APPROVED, $topic_id, $row['forum_id'], $user->data['user_id'], time(), '');
825            if (!empty($return))
826            {
827                $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_RESTORE_TOPIC', false, array(
828                    'forum_id' => $row['forum_id'],
829                    'topic_id' => $topic_id,
830                    $row['topic_title'],
831                    $row['topic_first_poster_name']
832                ));
833            }
834        }
835    }
836    else
837    {
838        confirm_box(false, (count($topic_ids) == 1) ? 'RESTORE_TOPIC' : 'RESTORE_TOPICS', $s_hidden_fields);
839    }
840
841    $topic_id = $request->variable('t', 0);
842    if (!$request->is_set('quickmod', \phpbb\request\request_interface::REQUEST))
843    {
844        $redirect = $request->variable('redirect', "index.$phpEx");
845        $redirect = reapply_sid($redirect);
846        $redirect_message = 'PAGE';
847    }
848    else if ($topic_id)
849    {
850        $redirect = append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=' . $topic_id);
851        $redirect_message = 'TOPIC';
852    }
853    else
854    {
855        $redirect = append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $forum_id);
856        $redirect_message = 'FORUM';
857    }
858
859    if (!$success_msg)
860    {
861        redirect($redirect);
862    }
863    else
864    {
865        meta_refresh(3, $redirect);
866        trigger_error($user->lang[$success_msg] . '<br /><br />' . sprintf($user->lang['RETURN_' . $redirect_message], '<a href="' . $redirect . '">', '</a>'));
867    }
868}
869
870/**
871* Delete Topics
872*/
873function mcp_delete_topic($topic_ids, $is_soft = false, $soft_delete_reason = '', $action = 'delete_topic')
874{
875    global $auth, $user, $db, $phpEx, $phpbb_root_path, $request, $phpbb_container, $phpbb_log, $phpbb_dispatcher;
876
877    $forum_id = $request->variable('f', 0);
878    $check_permission = ($is_soft) ? ['m_softdelete'] : ['m_delete'];
879    /**
880    * This event allows you to modify the current user's checked permissions when deleting a topic
881    *
882    * @event core.mcp_delete_topic_modify_permissions
883    * @var    array    topic_ids                The array of topic IDs to be deleted
884    * @var    int        forum_id                The current forum ID
885    * @var    bool    is_soft                    Boolean designating whether we're soft deleting or not
886    * @var    string    soft_delete_reason        The reason we're soft deleting
887    * @var    string    action                    The current delete action
888    * @var    array    check_permission        The array with a permission to check for, can be set to false to not check them
889    * @since 3.3.3-RC1
890    */
891    $vars = array(
892        'topic_ids',
893        'forum_id',
894        'is_soft',
895        'soft_delete_reason',
896        'action',
897        'check_permission',
898    );
899    extract($phpbb_dispatcher->trigger_event('core.mcp_delete_topic_modify_permissions', compact($vars)));
900
901    if (!phpbb_check_ids($topic_ids, TOPICS_TABLE, 'topic_id', $check_permission))
902    {
903        return;
904    }
905
906    $redirect = $request->variable('redirect', build_url(array('action', 'quickmod')));
907
908    $s_hidden_fields = array(
909        'topic_id_list'    => $topic_ids,
910        'f'                => $forum_id,
911        'action'        => $action,
912        'redirect'        => $redirect,
913    );
914    $success_msg = '';
915
916    if (confirm_box(true))
917    {
918        $success_msg = (count($topic_ids) == 1) ? 'TOPIC_DELETED_SUCCESS' : 'TOPICS_DELETED_SUCCESS';
919
920        $data = phpbb_get_topic_data($topic_ids);
921
922        foreach ($data as $topic_id => $row)
923        {
924            if ($row['topic_moved_id'])
925            {
926                $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_DELETE_SHADOW_TOPIC', false, array(
927                    'forum_id' => $row['forum_id'],
928                    'topic_id' => $topic_id,
929                    $row['topic_title']
930                ));
931            }
932            else
933            {
934                // Only soft delete non-shadow topics
935                if ($is_soft)
936                {
937                    /* @var $phpbb_content_visibility \phpbb\content_visibility */
938                    $phpbb_content_visibility = $phpbb_container->get('content.visibility');
939                    $return = $phpbb_content_visibility->set_topic_visibility(ITEM_DELETED, $topic_id, $row['forum_id'], $user->data['user_id'], time(), $soft_delete_reason);
940                    if (!empty($return))
941                    {
942                        $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_SOFTDELETE_TOPIC', false, array(
943                            'forum_id' => $row['forum_id'],
944                            'topic_id' => $topic_id,
945                            $row['topic_title'],
946                            $row['topic_first_poster_name'],
947                            $soft_delete_reason
948                        ));
949                    }
950                }
951                else
952                {
953                    $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_DELETE_TOPIC', false, array(
954                        'forum_id' => $row['forum_id'],
955                        'topic_id' => $topic_id,
956                        $row['topic_title'],
957                        $row['topic_first_poster_name'],
958                        $soft_delete_reason
959                    ));
960                }
961            }
962        }
963
964        if (!$is_soft)
965        {
966            delete_topics('topic_id', $topic_ids);
967        }
968    }
969    else
970    {
971        global $template;
972
973        $user->add_lang('posting');
974
975        // If there are only shadow topics, we neither need a reason nor softdelete
976        $sql = 'SELECT topic_id
977            FROM ' . TOPICS_TABLE . '
978            WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
979                AND topic_moved_id = 0';
980        $result = $db->sql_query_limit($sql, 1);
981        $only_shadow = !$db->sql_fetchfield('topic_id');
982        $db->sql_freeresult($result);
983
984        $only_softdeleted = false;
985        if (!$only_shadow && $auth->acl_get('m_delete', $forum_id) && $auth->acl_get('m_softdelete', $forum_id))
986        {
987            // If there are only soft deleted topics, we display a message why the option is not available
988            $sql = 'SELECT topic_id
989                FROM ' . TOPICS_TABLE . '
990                WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
991                    AND topic_visibility <> ' . ITEM_DELETED;
992            $result = $db->sql_query_limit($sql, 1);
993            $only_softdeleted = !$db->sql_fetchfield('topic_id');
994            $db->sql_freeresult($result);
995        }
996
997        $template->assign_vars(array(
998            'S_SHADOW_TOPICS'                    => $only_shadow,
999            'S_SOFTDELETED'                        => $only_softdeleted,
1000            'S_TOPIC_MODE'                        => true,
1001            'S_ALLOWED_DELETE'                    => $auth->acl_get('m_delete', $forum_id),
1002            'S_ALLOWED_SOFTDELETE'                => $auth->acl_get('m_softdelete', $forum_id),
1003            'DELETE_TOPIC_PERMANENTLY_EXPLAIN'    => $user->lang('DELETE_TOPIC_PERMANENTLY', count($topic_ids)),
1004        ));
1005
1006        $count = count($topic_ids);
1007        $l_confirm = $count === 1 ? 'DELETE_TOPIC' : 'DELETE_TOPICS';
1008        if ($only_softdeleted)
1009        {
1010            $l_confirm = array($l_confirm . '_PERMANENTLY', $count);
1011            $s_hidden_fields['delete_permanent'] = '1';
1012        }
1013        else if ($only_shadow || !$auth->acl_get('m_softdelete', $forum_id))
1014        {
1015            $s_hidden_fields['delete_permanent'] = '1';
1016        }
1017
1018        /**
1019        * This event allows you to modify the hidden form fields when deleting topics
1020        *
1021        * @event core.mcp_delete_topic_modify_hidden_fields
1022        * @var    string    l_confirm                The confirmation text language variable (DELETE_TOPIC(S), DELETE_TOPIC(S)_PERMANENTLY)
1023        * @var    array    s_hidden_fields            The array holding the hidden form fields
1024        * @var    array    topic_ids                The array of topic IDs to be deleted
1025        * @var    int        forum_id                The current forum ID
1026        * @var    bool    only_softdeleted        If the topic_ids are all soft deleted, this is true
1027        * @var    bool    only_shadow                If the topic_ids are all shadow topics, this is true
1028        * @since 3.3.3-RC1
1029        */
1030        $vars = array(
1031            'l_confirm',
1032            's_hidden_fields',
1033            'topic_ids',
1034            'forum_id',
1035            'only_softdeleted',
1036            'only_shadow',
1037        );
1038        extract($phpbb_dispatcher->trigger_event('core.mcp_delete_topic_modify_hidden_fields', compact($vars)));
1039
1040        confirm_box(false, $l_confirm, build_hidden_fields($s_hidden_fields), 'confirm_delete_body.html');
1041    }
1042
1043    $topic_id = $request->variable('t', 0);
1044    if (!$request->is_set('quickmod', \phpbb\request\request_interface::REQUEST))
1045    {
1046        $redirect = $request->variable('redirect', "index.$phpEx");
1047        $redirect = reapply_sid($redirect);
1048        $redirect_message = 'PAGE';
1049    }
1050    else if ($is_soft && $topic_id)
1051    {
1052        $redirect = append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=' . $topic_id);
1053        $redirect_message = 'TOPIC';
1054    }
1055    else
1056    {
1057        $redirect = append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $forum_id);
1058        $redirect_message = 'FORUM';
1059    }
1060
1061    if (!$success_msg)
1062    {
1063        redirect($redirect);
1064    }
1065    else
1066    {
1067        meta_refresh(3, $redirect);
1068        trigger_error($user->lang[$success_msg] . '<br /><br />' . sprintf($user->lang['RETURN_' . $redirect_message], '<a href="' . $redirect . '">', '</a>'));
1069    }
1070}
1071
1072/**
1073* Delete Posts
1074*/
1075function mcp_delete_post($post_ids, $is_soft = false, $soft_delete_reason = '', $action = 'delete_post')
1076{
1077    global $auth, $user, $db, $phpEx, $phpbb_root_path, $request, $phpbb_container, $phpbb_log;
1078
1079    $check_permission = ($is_soft) ? 'm_softdelete' : 'm_delete';
1080    if (!phpbb_check_ids($post_ids, POSTS_TABLE, 'post_id', array($check_permission)))
1081    {
1082        return;
1083    }
1084
1085    $redirect = $request->variable('redirect', build_url(array('action', 'quickmod')));
1086    $forum_id = $request->variable('f', 0);
1087    $topic_id = 0;
1088
1089    $s_hidden_fields = array(
1090        'post_id_list'    => $post_ids,
1091        'f'                => $forum_id,
1092        'action'        => $action,
1093        'redirect'        => $redirect,
1094    );
1095    $success_msg = '';
1096
1097    if (confirm_box(true) && $is_soft)
1098    {
1099        $post_info = phpbb_get_post_data($post_ids);
1100
1101        $topic_info = $approve_log = array();
1102
1103        // Group the posts by topic_id
1104        foreach ($post_info as $post_id => $post_data)
1105        {
1106            if ($post_data['post_visibility'] != ITEM_APPROVED)
1107            {
1108                continue;
1109            }
1110            $topic_id = (int) $post_data['topic_id'];
1111
1112            $topic_info[$topic_id]['posts'][] = (int) $post_id;
1113            $topic_info[$topic_id]['forum_id'] = (int) $post_data['forum_id'];
1114
1115            if ($post_id == $post_data['topic_first_post_id'])
1116            {
1117                $topic_info[$topic_id]['first_post'] = true;
1118            }
1119
1120            if ($post_id == $post_data['topic_last_post_id'])
1121            {
1122                $topic_info[$topic_id]['last_post'] = true;
1123            }
1124
1125            $approve_log[] = array(
1126                'forum_id'        => $post_data['forum_id'],
1127                'topic_id'        => $post_data['topic_id'],
1128                'post_id'        => $post_id,
1129                'post_subject'    => $post_data['post_subject'],
1130                'poster_id'        => $post_data['poster_id'],
1131                'post_username'    => $post_data['post_username'],
1132                'username'        => $post_data['username'],
1133            );
1134        }
1135
1136        /* @var $phpbb_content_visibility \phpbb\content_visibility */
1137        $phpbb_content_visibility = $phpbb_container->get('content.visibility');
1138        foreach ($topic_info as $topic_id => $topic_data)
1139        {
1140            $phpbb_content_visibility->set_post_visibility(ITEM_DELETED, $topic_data['posts'], $topic_id, $topic_data['forum_id'], $user->data['user_id'], time(), $soft_delete_reason, isset($topic_data['first_post']), isset($topic_data['last_post']));
1141        }
1142        $affected_topics = count($topic_info);
1143        // None of the topics is really deleted, so a redirect won't hurt much.
1144        $deleted_topics = 0;
1145
1146        $success_msg = (count($post_info) == 1) ? $user->lang['POST_DELETED_SUCCESS'] : $user->lang['POSTS_DELETED_SUCCESS'];
1147
1148        foreach ($approve_log as $row)
1149        {
1150            $post_username = ($row['poster_id'] == ANONYMOUS && !empty($row['post_username'])) ? $row['post_username'] : $row['username'];
1151            $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_SOFTDELETE_POST', false, array(
1152                'forum_id' => $row['forum_id'],
1153                'topic_id' => $row['topic_id'],
1154                'post_id'  => $row['post_id'],
1155                $row['post_subject'],
1156                $post_username,
1157                $soft_delete_reason
1158            ));
1159        }
1160
1161        // Return links
1162        $return_link = array();
1163        if ($affected_topics == 1 && $topic_id)
1164        {
1165            $return_link[] = sprintf($user->lang['RETURN_TOPIC'], '<a href="' . append_sid("{$phpbb_root_path}viewtopic.$phpEx", "t=$topic_id") . '">', '</a>');
1166        }
1167        $return_link[] = sprintf($user->lang['RETURN_FORUM'], '<a href="' . append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $forum_id) . '">', '</a>');
1168
1169    }
1170    else if (confirm_box(true))
1171    {
1172        if (!function_exists('delete_posts'))
1173        {
1174            include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
1175        }
1176
1177        // Count the number of topics that are affected
1178        // I did not use COUNT(DISTINCT ...) because I remember having problems
1179        // with it on older versions of MySQL -- Ashe
1180
1181        $sql = 'SELECT DISTINCT topic_id
1182            FROM ' . POSTS_TABLE . '
1183            WHERE ' . $db->sql_in_set('post_id', $post_ids);
1184        $result = $db->sql_query($sql);
1185
1186        $topic_id_list = array();
1187        while ($row = $db->sql_fetchrow($result))
1188        {
1189            $topic_id_list[] = $topic_id = $row['topic_id'];
1190        }
1191        $affected_topics = count($topic_id_list);
1192        $db->sql_freeresult($result);
1193
1194        $post_data = phpbb_get_post_data($post_ids);
1195
1196        foreach ($post_data as $row)
1197        {
1198            $post_username = ($row['poster_id'] == ANONYMOUS && !empty($row['post_username'])) ? $row['post_username'] : $row['username'];
1199            $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_DELETE_POST', false, array(
1200                'forum_id' => $row['forum_id'],
1201                'topic_id' => $row['topic_id'],
1202                'post_id'  => $row['post_id'],
1203                $row['post_subject'],
1204                $post_username,
1205                $soft_delete_reason
1206            ));
1207        }
1208
1209        // Now delete the posts, topics and forums are automatically resync'ed
1210        delete_posts('post_id', $post_ids);
1211
1212        $sql = 'SELECT COUNT(topic_id) AS topics_left
1213            FROM ' . TOPICS_TABLE . '
1214            WHERE ' . $db->sql_in_set('topic_id', $topic_id_list);
1215        $result = $db->sql_query_limit($sql, 1);
1216
1217        $deleted_topics = ($row = $db->sql_fetchrow($result)) ? ($affected_topics - $row['topics_left']) : $affected_topics;
1218        $db->sql_freeresult($result);
1219
1220        // Return links
1221        $return_link = array();
1222        if ($affected_topics == 1 && !$deleted_topics && $topic_id)
1223        {
1224            $return_link[] = sprintf($user->lang['RETURN_TOPIC'], '<a href="' . append_sid("{$phpbb_root_path}viewtopic.$phpEx", "t=$topic_id") . '">', '</a>');
1225        }
1226        $return_link[] = sprintf($user->lang['RETURN_FORUM'], '<a href="' . append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $forum_id) . '">', '</a>');
1227
1228        if (count($post_ids) == 1)
1229        {
1230            if ($deleted_topics)
1231            {
1232                // We deleted the only post of a topic, which in turn has
1233                // been removed from the database
1234                $success_msg = $user->lang['TOPIC_DELETED_SUCCESS'];
1235            }
1236            else
1237            {
1238                // Remove any post id anchor
1239                if (($anchor_pos = strrpos($redirect, '#p')) !== false)
1240                {
1241                    $redirect = substr($redirect, 0, $anchor_pos);
1242                }
1243
1244                $success_msg = $user->lang['POST_DELETED_SUCCESS'];
1245            }
1246        }
1247        else
1248        {
1249            if ($deleted_topics)
1250            {
1251                // Some of topics disappeared
1252                $success_msg = $user->lang['POSTS_DELETED_SUCCESS'] . '<br /><br />' . $user->lang['EMPTY_TOPICS_REMOVED_WARNING'];
1253            }
1254            else
1255            {
1256                $success_msg = $user->lang['POSTS_DELETED_SUCCESS'];
1257            }
1258        }
1259    }
1260    else
1261    {
1262        global $template;
1263
1264        $user->add_lang('posting');
1265
1266        $only_softdeleted = false;
1267        if ($auth->acl_get('m_delete', $forum_id) && $auth->acl_get('m_softdelete', $forum_id))
1268        {
1269            // If there are only soft deleted posts, we display a message why the option is not available
1270            $sql = 'SELECT post_id
1271                FROM ' . POSTS_TABLE . '
1272                WHERE ' . $db->sql_in_set('post_id', $post_ids) . '
1273                    AND post_visibility <> ' . ITEM_DELETED;
1274            $result = $db->sql_query_limit($sql, 1);
1275            $only_softdeleted = !$db->sql_fetchfield('post_id');
1276            $db->sql_freeresult($result);
1277        }
1278
1279        $template->assign_vars(array(
1280            'S_SOFTDELETED'                        => $only_softdeleted,
1281            'S_ALLOWED_DELETE'                    => $auth->acl_get('m_delete', $forum_id),
1282            'S_ALLOWED_SOFTDELETE'                => $auth->acl_get('m_softdelete', $forum_id),
1283            'DELETE_POST_PERMANENTLY_EXPLAIN'    => $user->lang('DELETE_POST_PERMANENTLY', count($post_ids)),
1284        ));
1285
1286        $count = count($post_ids);
1287        $l_confirm = $count === 1 ? 'DELETE_POST' : 'DELETE_POSTS';
1288        if ($only_softdeleted)
1289        {
1290            $l_confirm = array($l_confirm . '_PERMANENTLY', $count);
1291            $s_hidden_fields['delete_permanent'] = '1';
1292        }
1293        else if (!$auth->acl_get('m_softdelete', $forum_id))
1294        {
1295            $s_hidden_fields['delete_permanent'] = '1';
1296        }
1297
1298        confirm_box(false, $l_confirm, build_hidden_fields($s_hidden_fields), 'confirm_delete_body.html');
1299    }
1300
1301    $redirect = reapply_sid($redirect);
1302
1303    if (!$success_msg)
1304    {
1305        redirect($redirect);
1306    }
1307    else
1308    {
1309        if ($affected_topics != 1 || $deleted_topics || !$topic_id)
1310        {
1311            $redirect = append_sid("{$phpbb_root_path}mcp.$phpEx", "f=$forum_id&i=main&mode=forum_view", false);
1312        }
1313
1314        meta_refresh(3, $redirect);
1315        trigger_error($success_msg . '<br /><br />' . sprintf($user->lang['RETURN_PAGE'], '<a href="' . $redirect . '">', '</a>') . '<br /><br />' . implode('<br /><br />', $return_link));
1316    }
1317}
1318
1319/**
1320* Fork Topic
1321*/
1322function mcp_fork_topic($topic_ids)
1323{
1324    global $auth, $user, $db, $template, $config, $phpbb_container;
1325    global $phpEx, $phpbb_root_path, $phpbb_log, $request, $phpbb_dispatcher;
1326
1327    if (!phpbb_check_ids($topic_ids, TOPICS_TABLE, 'topic_id', array('m_')))
1328    {
1329        return;
1330    }
1331
1332    $to_forum_id = $request->variable('to_forum_id', 0);
1333    $forum_id = $request->variable('f', 0);
1334    $redirect = $request->variable('redirect', build_url(array('action', 'quickmod')));
1335    $additional_msg = $success_msg = '';
1336    $counter = array();
1337
1338    $s_hidden_fields = build_hidden_fields(array(
1339        'topic_id_list'    => $topic_ids,
1340        'f'                => $forum_id,
1341        'action'        => 'fork',
1342        'redirect'        => $redirect)
1343    );
1344
1345    if ($to_forum_id)
1346    {
1347        $forum_data = phpbb_get_forum_data($to_forum_id, 'f_post');
1348
1349        if (!count($topic_ids))
1350        {
1351            $additional_msg = $user->lang['NO_TOPIC_SELECTED'];
1352        }
1353        else if (!count($forum_data))
1354        {
1355            $additional_msg = $user->lang['FORUM_NOT_EXIST'];
1356        }
1357        else
1358        {
1359            $forum_data = $forum_data[$to_forum_id];
1360
1361            if ($forum_data['forum_type'] != FORUM_POST)
1362            {
1363                $additional_msg = $user->lang['FORUM_NOT_POSTABLE'];
1364            }
1365            else if (!$auth->acl_get('f_post', $to_forum_id))
1366            {
1367                $additional_msg = $user->lang['USER_CANNOT_POST'];
1368            }
1369        }
1370    }
1371    else if (isset($_POST['confirm']))
1372    {
1373        $additional_msg = $user->lang['FORUM_NOT_EXIST'];
1374    }
1375
1376    if ($additional_msg)
1377    {
1378        $request->overwrite('confirm', null, \phpbb\request\request_interface::POST);
1379        $request->overwrite('confirm_key', null);
1380    }
1381
1382    if (confirm_box(true))
1383    {
1384        $topic_data = phpbb_get_topic_data($topic_ids, 'f_post');
1385
1386        $total_topics = $total_topics_unapproved = $total_topics_softdeleted = 0;
1387        $total_posts = $total_posts_unapproved = $total_posts_softdeleted = 0;
1388        $new_topic_id_list = array();
1389
1390        foreach ($topic_data as $topic_id => $topic_row)
1391        {
1392            if (!isset($search) && $topic_row['enable_indexing'])
1393            {
1394                // Select the search method and do some additional checks to ensure it can actually be utilised
1395                try
1396                {
1397                    $search_backend_factory = $phpbb_container->get('search.backend_factory');
1398                    $search = $search_backend_factory->get_active();
1399                }
1400                catch (\phpbb\search\exception\no_search_backend_found_exception $e)
1401                {
1402                    trigger_error('NO_SUCH_SEARCH_MODULE');
1403                }
1404                $search_mode = 'post';
1405            }
1406            else if (!isset($search) && !$topic_row['enable_indexing'])
1407            {
1408                $search = false;
1409            }
1410
1411            $sql_ary = array(
1412                'forum_id'                    => (int) $to_forum_id,
1413                'icon_id'                    => (int) $topic_row['icon_id'],
1414                'topic_attachment'            => (int) $topic_row['topic_attachment'],
1415                'topic_visibility'            => (int) $topic_row['topic_visibility'],
1416                'topic_reported'            => 0,
1417                'topic_title'                => (string) $topic_row['topic_title'],
1418                'topic_poster'                => (int) $topic_row['topic_poster'],
1419                'topic_time'                => (int) $topic_row['topic_time'],
1420                'topic_posts_approved'        => (int) $topic_row['topic_posts_approved'],
1421                'topic_posts_unapproved'    => (int) $topic_row['topic_posts_unapproved'],
1422                'topic_posts_softdeleted'    => (int) $topic_row['topic_posts_softdeleted'],
1423                'topic_status'                => (int) $topic_row['topic_status'],
1424                'topic_type'                => (int) $topic_row['topic_type'],
1425                'topic_first_poster_name'    => (string) $topic_row['topic_first_poster_name'],
1426                'topic_last_poster_id'        => (int) $topic_row['topic_last_poster_id'],
1427                'topic_last_poster_name'    => (string) $topic_row['topic_last_poster_name'],
1428                'topic_last_post_time'        => (int) $topic_row['topic_last_post_time'],
1429                'topic_last_view_time'        => (int) $topic_row['topic_last_view_time'],
1430                'topic_bumped'                => (int) $topic_row['topic_bumped'],
1431                'topic_bumper'                => (int) $topic_row['topic_bumper'],
1432                'poll_title'                => (string) $topic_row['poll_title'],
1433                'poll_start'                => (int) $topic_row['poll_start'],
1434                'poll_length'                => (int) $topic_row['poll_length'],
1435                'poll_max_options'            => (int) $topic_row['poll_max_options'],
1436                'poll_vote_change'            => (int) $topic_row['poll_vote_change'],
1437            );
1438
1439            /**
1440            * Perform actions before forked topic is created.
1441            *
1442            * @event core.mcp_main_modify_fork_sql
1443            * @var    array    sql_ary        SQL array to be used by $db->sql_build_array
1444            * @var    array    topic_row    Topic data
1445            * @since 3.1.11-RC1
1446            * @changed 3.1.11-RC1 Added variable: topic_row
1447            */
1448            $vars = array(
1449                'sql_ary',
1450                'topic_row',
1451            );
1452            extract($phpbb_dispatcher->trigger_event('core.mcp_main_modify_fork_sql', compact($vars)));
1453
1454            $db->sql_query('INSERT INTO ' . TOPICS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1455            $new_topic_id = $db->sql_nextid();
1456            $new_topic_id_list[$topic_id] = $new_topic_id;
1457
1458            switch ($topic_row['topic_visibility'])
1459            {
1460                case ITEM_APPROVED:
1461                    $total_topics++;
1462                break;
1463                case ITEM_UNAPPROVED:
1464                case ITEM_REAPPROVE:
1465                    $total_topics_unapproved++;
1466                break;
1467                case ITEM_DELETED:
1468                    $total_topics_softdeleted++;
1469                break;
1470            }
1471
1472            if ($topic_row['poll_start'])
1473            {
1474                $sql = 'SELECT *
1475                    FROM ' . POLL_OPTIONS_TABLE . "
1476                    WHERE topic_id = $topic_id";
1477                $result = $db->sql_query($sql);
1478
1479                while ($row = $db->sql_fetchrow($result))
1480                {
1481                    $sql_ary = array(
1482                        'poll_option_id'    => (int) $row['poll_option_id'],
1483                        'topic_id'            => (int) $new_topic_id,
1484                        'poll_option_text'    => (string) $row['poll_option_text'],
1485                        'poll_option_total'    => 0
1486                    );
1487
1488                    $db->sql_query('INSERT INTO ' . POLL_OPTIONS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1489                }
1490                $db->sql_freeresult($result);
1491            }
1492
1493            $sql = 'SELECT *
1494                FROM ' . POSTS_TABLE . "
1495                WHERE topic_id = $topic_id
1496                ORDER BY post_time ASC, post_id ASC";
1497            $result = $db->sql_query($sql);
1498
1499            $post_rows = array();
1500            while ($row = $db->sql_fetchrow($result))
1501            {
1502                $post_rows[] = $row;
1503            }
1504            $db->sql_freeresult($result);
1505
1506            if (!count($post_rows))
1507            {
1508                continue;
1509            }
1510
1511            foreach ($post_rows as $row)
1512            {
1513                $sql_ary = array(
1514                    'topic_id'            => (int) $new_topic_id,
1515                    'forum_id'            => (int) $to_forum_id,
1516                    'poster_id'            => (int) $row['poster_id'],
1517                    'icon_id'            => (int) $row['icon_id'],
1518                    'poster_ip'            => (string) $row['poster_ip'],
1519                    'post_time'            => (int) $row['post_time'],
1520                    'post_visibility'    => (int) $row['post_visibility'],
1521                    'post_reported'        => 0,
1522                    'enable_bbcode'        => (int) $row['enable_bbcode'],
1523                    'enable_smilies'    => (int) $row['enable_smilies'],
1524                    'enable_magic_url'    => (int) $row['enable_magic_url'],
1525                    'enable_sig'        => (int) $row['enable_sig'],
1526                    'post_username'        => (string) $row['post_username'],
1527                    'post_subject'        => (string) $row['post_subject'],
1528                    'post_text'            => (string) $row['post_text'],
1529                    'post_edit_reason'    => (string) $row['post_edit_reason'],
1530                    'post_edit_user'    => (int) $row['post_edit_user'],
1531                    'post_checksum'        => (string) $row['post_checksum'],
1532                    'post_attachment'    => (int) $row['post_attachment'],
1533                    'bbcode_bitfield'    => $row['bbcode_bitfield'],
1534                    'bbcode_uid'        => (string) $row['bbcode_uid'],
1535                    'post_edit_time'    => (int) $row['post_edit_time'],
1536                    'post_edit_count'    => (int) $row['post_edit_count'],
1537                    'post_edit_locked'    => (int) $row['post_edit_locked'],
1538                    'post_postcount'    => $row['post_postcount'],
1539                );
1540                // Adjust post count only if the post can be incremented to the user counter
1541                if ($row['post_postcount'])
1542                {
1543                    if (isset($counter[$row['poster_id']]))
1544                    {
1545                        ++$counter[$row['poster_id']];
1546                    }
1547                    else
1548                    {
1549                        $counter[$row['poster_id']] = 1;
1550                    }
1551                }
1552
1553                /**
1554                * Modify the forked post's sql array before it's inserted into the database.
1555                *
1556                * @event core.mcp_main_modify_fork_post_sql
1557                * @var    int        new_topic_id    The newly created topic ID
1558                * @var    int        to_forum_id        The forum ID where the forked topic has been moved to
1559                * @var    array    sql_ary            SQL Array with the post's data
1560                * @var    array    row                Post data
1561                * @var    array    counter            Array with post counts
1562                * @since 3.3.5-RC1
1563                */
1564                $vars = [
1565                    'new_topic_id',
1566                    'to_forum_id',
1567                    'sql_ary',
1568                    'row',
1569                    'counter',
1570                ];
1571                extract($phpbb_dispatcher->trigger_event('core.mcp_main_modify_fork_post_sql', compact($vars)));
1572                $db->sql_query('INSERT INTO ' . POSTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1573                $new_post_id = (int) $db->sql_nextid();
1574
1575                /**
1576                * Perform actions after forked topic is created.
1577                *
1578                * @event core.mcp_main_fork_sql_after
1579                * @var    int        new_topic_id    The newly created topic ID
1580                * @var    int        to_forum_id        The forum ID where the forked topic has been moved to
1581                * @var    int        new_post_id        The newly created post ID
1582                * @var    array    row                Post data
1583                * @since 3.2.4-RC1
1584                */
1585                $vars = array(
1586                    'new_topic_id',
1587                    'to_forum_id',
1588                    'new_post_id',
1589                    'row',
1590                );
1591                extract($phpbb_dispatcher->trigger_event('core.mcp_main_fork_sql_after', compact($vars)));
1592
1593                switch ($row['post_visibility'])
1594                {
1595                    case ITEM_APPROVED:
1596                        $total_posts++;
1597                    break;
1598                    case ITEM_UNAPPROVED:
1599                    case ITEM_REAPPROVE:
1600                        $total_posts_unapproved++;
1601                    break;
1602                    case ITEM_DELETED:
1603                        $total_posts_softdeleted++;
1604                    break;
1605                }
1606
1607                // Copy whether the topic is dotted
1608                markread('post', $to_forum_id, $new_topic_id, 0, $row['poster_id']);
1609
1610                if (!empty($search))
1611                {
1612                    $search->index($search_mode, $new_post_id, $sql_ary['post_text'], $sql_ary['post_subject'], (int) $sql_ary['poster_id'], ($topic_row['topic_type'] == POST_GLOBAL) ? 0 : $to_forum_id);
1613                    $search_mode = 'reply'; // After one we index replies
1614                }
1615
1616                // Copy Attachments
1617                if ($row['post_attachment'])
1618                {
1619                    $sql = 'SELECT * FROM ' . ATTACHMENTS_TABLE . '
1620                        WHERE post_msg_id = ' . (int) $row['post_id'] . '
1621                            AND topic_id = ' . (int) $topic_id . '
1622                            AND in_message = 0
1623                        ORDER BY attach_id ASC';
1624                    $result = $db->sql_query($sql);
1625
1626                    $sql_ary = array();
1627                    while ($attach_row = $db->sql_fetchrow($result))
1628                    {
1629                        $sql_ary[] = array(
1630                            'post_msg_id'        => (int) $new_post_id,
1631                            'topic_id'            => (int) $new_topic_id,
1632                            'in_message'        => 0,
1633                            'is_orphan'            => (int) $attach_row['is_orphan'],
1634                            'poster_id'            => (int) $attach_row['poster_id'],
1635                            'physical_filename'    => (string) utf8_basename($attach_row['physical_filename']),
1636                            'real_filename'        => (string) utf8_basename($attach_row['real_filename']),
1637                            'download_count'    => (int) $attach_row['download_count'],
1638                            'attach_comment'    => (string) $attach_row['attach_comment'],
1639                            'extension'            => (string) $attach_row['extension'],
1640                            'mimetype'            => (string) $attach_row['mimetype'],
1641                            'filesize'            => (int) $attach_row['filesize'],
1642                            'filetime'            => (int) $attach_row['filetime'],
1643                            'thumbnail'            => (int) $attach_row['thumbnail']
1644                        );
1645                    }
1646                    $db->sql_freeresult($result);
1647
1648                    if (count($sql_ary))
1649                    {
1650                        $db->sql_multi_insert(ATTACHMENTS_TABLE, $sql_ary);
1651                    }
1652                }
1653            }
1654
1655            // Copy topic subscriptions to new topic
1656            $sql = 'SELECT user_id, notify_status
1657                FROM ' . TOPICS_WATCH_TABLE . '
1658                WHERE topic_id = ' . $topic_id;
1659            $result = $db->sql_query($sql);
1660
1661            $sql_ary = array();
1662            while ($row = $db->sql_fetchrow($result))
1663            {
1664                $sql_ary[] = array(
1665                    'topic_id'        => (int) $new_topic_id,
1666                    'user_id'        => (int) $row['user_id'],
1667                    'notify_status'    => (int) $row['notify_status'],
1668                );
1669            }
1670            $db->sql_freeresult($result);
1671
1672            if (count($sql_ary))
1673            {
1674                $db->sql_multi_insert(TOPICS_WATCH_TABLE, $sql_ary);
1675            }
1676
1677            // Copy bookmarks to new topic
1678            $sql = 'SELECT user_id
1679                FROM ' . BOOKMARKS_TABLE . '
1680                WHERE topic_id = ' . $topic_id;
1681            $result = $db->sql_query($sql);
1682
1683            $sql_ary = array();
1684            while ($row = $db->sql_fetchrow($result))
1685            {
1686                $sql_ary[] = array(
1687                    'topic_id'        => (int) $new_topic_id,
1688                    'user_id'        => (int) $row['user_id'],
1689                );
1690            }
1691            $db->sql_freeresult($result);
1692
1693            if (count($sql_ary))
1694            {
1695                $db->sql_multi_insert(BOOKMARKS_TABLE, $sql_ary);
1696            }
1697        }
1698
1699        // Sync new topics, parent forums and board stats
1700        $sql = 'UPDATE ' . FORUMS_TABLE . '
1701            SET forum_posts_approved = forum_posts_approved + ' . $total_posts . ',
1702                forum_posts_unapproved = forum_posts_unapproved + ' . $total_posts_unapproved . ',
1703                forum_posts_softdeleted = forum_posts_softdeleted + ' . $total_posts_softdeleted . ',
1704                forum_topics_approved = forum_topics_approved + ' . $total_topics . ',
1705                forum_topics_unapproved = forum_topics_unapproved + ' . $total_topics_unapproved . ',
1706                forum_topics_softdeleted = forum_topics_softdeleted + ' . $total_topics_softdeleted . '
1707            WHERE forum_id = ' . $to_forum_id;
1708        $db->sql_query($sql);
1709
1710        if (!empty($counter))
1711        {
1712            // Do only one query per user and not a query per post.
1713            foreach ($counter as $user_id => $count)
1714            {
1715                $sql = 'UPDATE ' . USERS_TABLE . '
1716                    SET user_posts = user_posts + ' . (int) $count . '
1717                    WHERE user_id = ' . (int) $user_id;
1718                $db->sql_query($sql);
1719            }
1720        }
1721
1722        sync('topic', 'topic_id', $new_topic_id_list);
1723        sync('forum', 'forum_id', $to_forum_id);
1724
1725        $config->increment('num_topics', count($new_topic_id_list), false);
1726        $config->increment('num_posts', $total_posts, false);
1727
1728        foreach ($new_topic_id_list as $new_topic_id)
1729        {
1730            $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_FORK', false, array(
1731                'forum_id' => $to_forum_id,
1732                'topic_id' => $new_topic_id,
1733                $topic_row['forum_name']
1734            ));
1735        }
1736
1737        $success_msg = (count($topic_ids) == 1) ? 'TOPIC_FORKED_SUCCESS' : 'TOPICS_FORKED_SUCCESS';
1738    }
1739    else
1740    {
1741        $template->assign_vars(array(
1742            'S_FORUM_SELECT'        => make_forum_select($to_forum_id, false, false, true, true, true),
1743            'S_CAN_LEAVE_SHADOW'    => false,
1744            'ADDITIONAL_MSG'        => $additional_msg)
1745        );
1746
1747        confirm_box(false, 'FORK_TOPIC' . ((count($topic_ids) == 1) ? '' : 'S'), $s_hidden_fields, 'mcp_move.html');
1748    }
1749
1750    $redirect = $request->variable('redirect', "index.$phpEx");
1751    $redirect = reapply_sid($redirect);
1752
1753    if (!$success_msg)
1754    {
1755        redirect($redirect);
1756    }
1757    else
1758    {
1759        $redirect_url = append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $forum_id);
1760        meta_refresh(3, $redirect_url);
1761        $return_link = sprintf($user->lang['RETURN_FORUM'], '<a href="' . $redirect_url . '">', '</a>');
1762
1763        if ($forum_id != $to_forum_id)
1764        {
1765            $return_link .= '<br /><br />' . sprintf($user->lang['RETURN_NEW_FORUM'], '<a href="' . append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $to_forum_id) . '">', '</a>');
1766        }
1767
1768        trigger_error($user->lang[$success_msg] . '<br /><br />' . $return_link);
1769    }
1770}