Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1338
lock_unlock
0.00% covered (danger)
0.00%
0 / 1
342.00
0.00% covered (danger)
0.00%
0 / 74
change_topic_type
0.00% covered (danger)
0.00%
0 / 1
272.00
0.00% covered (danger)
0.00%
0 / 91
mcp_move_topic
0.00% covered (danger)
0.00%
0 / 1
1406.00
0.00% covered (danger)
0.00%
0 / 217
mcp_restore_topic
0.00% covered (danger)
0.00%
0 / 1
110.00
0.00% covered (danger)
0.00%
0 / 65
mcp_delete_topic
0.00% covered (danger)
0.00%
0 / 1
462.00
0.00% covered (danger)
0.00%
0 / 152
mcp_delete_post
0.00% covered (danger)
0.00%
0 / 1
1560.00
0.00% covered (danger)
0.00%
0 / 194
mcp_fork_topic
0.00% covered (danger)
0.00%
0 / 1
2352.00
0.00% covered (danger)
0.00%
0 / 366
mcp_main
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 2
3906.00
0.00% covered (danger)
0.00%
0 / 175
 __construct
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 3
 main
0.00% covered (danger)
0.00%
0 / 1
3782.00
0.00% covered (danger)
0.00%
0 / 172
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
    exit;
}
/**
* mcp_main
* Handling mcp actions
*/
class mcp_main
{
    var $p_master;
    var $u_action;
    function __construct($p_master)
    {
        $this->p_master = $p_master;
    }
    function main($id, $mode)
    {
        global $auth, $user, $action;
        global $phpbb_root_path, $phpEx, $request;
        global $phpbb_dispatcher;
        $quickmod = ($mode == 'quickmod') ? true : false;
        /**
        * Event to perform additional actions before an MCP action is executed.
        *
        * @event core.mcp_main_before
        * @var    string    action                The action that is about to be performed
        * @var    string    mode                The mode in which the MCP is accessed, e.g. front, forum_view, topic_view, post_details, quickmod
        * @var    boolean    quickmod            Whether or not the action is performed via QuickMod
        * @since 3.2.8-RC1
        */
        $vars = [
            'action',
            'mode',
            'quickmod',
        ];
        extract($phpbb_dispatcher->trigger_event('core.mcp_main_before', compact($vars)));
        switch ($action)
        {
            case 'lock':
            case 'unlock':
                $topic_ids = (!$quickmod) ? $request->variable('topic_id_list', array(0)) : array($request->variable('t', 0));
                if (!count($topic_ids))
                {
                    trigger_error('NO_TOPIC_SELECTED');
                }
                lock_unlock($action, $topic_ids);
            break;
            case 'lock_post':
            case 'unlock_post':
                $post_ids = (!$quickmod) ? $request->variable('post_id_list', array(0)) : array($request->variable('p', 0));
                if (!count($post_ids))
                {
                    trigger_error('NO_POST_SELECTED');
                }
                lock_unlock($action, $post_ids);
            break;
            case 'make_announce':
            case 'make_sticky':
            case 'make_global':
            case 'make_normal':
                $topic_ids = (!$quickmod) ? $request->variable('topic_id_list', array(0)) : array($request->variable('t', 0));
                if (!count($topic_ids))
                {
                    trigger_error('NO_TOPIC_SELECTED');
                }
                change_topic_type($action, $topic_ids);
            break;
            case 'move':
                $user->add_lang('viewtopic');
                $topic_ids = (!$quickmod) ? $request->variable('topic_id_list', array(0)) : array($request->variable('t', 0));
                if (!count($topic_ids))
                {
                    trigger_error('NO_TOPIC_SELECTED');
                }
                mcp_move_topic($topic_ids);
            break;
            case 'fork':
                $user->add_lang('viewtopic');
                $topic_ids = (!$quickmod) ? $request->variable('topic_id_list', array(0)) : array($request->variable('t', 0));
                if (!count($topic_ids))
                {
                    trigger_error('NO_TOPIC_SELECTED');
                }
                mcp_fork_topic($topic_ids);
            break;
            case 'delete_topic':
                $user->add_lang('viewtopic');
                // f parameter is not reliable for permission usage, however we just use it to decide
                // which permission we will check later on. So if it is manipulated, we will still catch it later on.
                $forum_id = $request->variable('f', 0);
                $topic_ids = (!$quickmod) ? $request->variable('topic_id_list', array(0)) : array($request->variable('t', 0));
                $soft_delete = (($request->is_set_post('confirm') && !$request->is_set_post('delete_permanent')) || !$auth->acl_get('m_delete', $forum_id)) ? true : false;
                if (!count($topic_ids))
                {
                    trigger_error('NO_TOPIC_SELECTED');
                }
                mcp_delete_topic($topic_ids, $soft_delete, $request->variable('delete_reason', '', true));
            break;
            case 'delete_post':
                $user->add_lang('posting');
                // f parameter is not reliable for permission usage, however we just use it to decide
                // which permission we will check later on. So if it is manipulated, we will still catch it later on.
                $forum_id = $request->variable('f', 0);
                $post_ids = (!$quickmod) ? $request->variable('post_id_list', array(0)) : array($request->variable('p', 0));
                $soft_delete = (($request->is_set_post('confirm') && !$request->is_set_post('delete_permanent')) || !$auth->acl_get('m_delete', $forum_id)) ? true : false;
                if (!count($post_ids))
                {
                    trigger_error('NO_POST_SELECTED');
                }
                mcp_delete_post($post_ids, $soft_delete, $request->variable('delete_reason', '', true));
            break;
            case 'restore_topic':
                $user->add_lang('posting');
                $topic_ids = (!$quickmod) ? $request->variable('topic_id_list', array(0)) : array($request->variable('t', 0));
                if (!count($topic_ids))
                {
                    trigger_error('NO_TOPIC_SELECTED');
                }
                mcp_restore_topic($topic_ids);
            break;
            default:
                /**
                * This event allows you to handle custom quickmod options
                *
                * @event core.modify_quickmod_actions
                * @var    string    action        Topic quick moderation action name
                * @var    bool    quickmod    Flag indicating whether MCP is in quick moderation mode
                * @since 3.1.0-a4
                * @changed 3.1.0-RC4 Added variables: action, quickmod
                */
                $vars = array('action', 'quickmod');
                extract($phpbb_dispatcher->trigger_event('core.modify_quickmod_actions', compact($vars)));
            break;
        }
        switch ($mode)
        {
            case 'front':
                if (!function_exists('mcp_front_view'))
                {
                    include($phpbb_root_path . 'includes/mcp/mcp_front.' . $phpEx);
                }
                $user->add_lang('acp/common');
                mcp_front_view($id, $mode, $action);
                $this->tpl_name = 'mcp_front';
                $this->page_title = 'MCP_MAIN';
            break;
            case 'forum_view':
                if (!function_exists('mcp_forum_view'))
                {
                    include($phpbb_root_path . 'includes/mcp/mcp_forum.' . $phpEx);
                }
                $user->add_lang('viewforum');
                $forum_id = $request->variable('f', 0);
                $forum_info = phpbb_get_forum_data($forum_id, 'm_', true);
                if (!count($forum_info))
                {
                    $this->main('main', 'front');
                    return;
                }
                $forum_info = $forum_info[$forum_id];
                mcp_forum_view($id, $mode, $action, $forum_info);
                $this->tpl_name = 'mcp_forum';
                $this->page_title = 'MCP_MAIN_FORUM_VIEW';
            break;
            case 'topic_view':
                if (!function_exists('mcp_topic_view'))
                {
                    include($phpbb_root_path . 'includes/mcp/mcp_topic.' . $phpEx);
                }
                mcp_topic_view($id, $mode, $action);
                $this->tpl_name = 'mcp_topic';
                $this->page_title = 'MCP_MAIN_TOPIC_VIEW';
            break;
            case 'post_details':
                if (!function_exists('mcp_post_details'))
                {
                    include($phpbb_root_path . 'includes/mcp/mcp_post.' . $phpEx);
                }
                mcp_post_details($id, $mode, $action);
                $this->tpl_name = ($action == 'whois') ? 'mcp_whois' : 'mcp_post';
                $this->page_title = 'MCP_MAIN_POST_DETAILS';
            break;
            default:
                if ($quickmod)
                {
                    switch ($action)
                    {
                        case 'lock':
                        case 'unlock':
                        case 'make_announce':
                        case 'make_sticky':
                        case 'make_global':
                        case 'make_normal':
                        case 'make_onindex':
                        case 'move':
                        case 'fork':
                        case 'delete_topic':
                            trigger_error('TOPIC_NOT_EXIST');
                        break;
                        case 'lock_post':
                        case 'unlock_post':
                        case 'delete_post':
                            trigger_error('POST_NOT_EXIST');
                        break;
                    }
                }
                trigger_error('NO_MODE', E_USER_ERROR);
            break;
        }
    }
}
/**
* Lock/Unlock Topic/Post
*/
function lock_unlock($action, $ids)
{
    global $user, $db, $request, $phpbb_log, $phpbb_dispatcher;
    if ($action == 'lock' || $action == 'unlock')
    {
        $table = TOPICS_TABLE;
        $sql_id = 'topic_id';
        $set_id = 'topic_status';
        $l_prefix = 'TOPIC';
    }
    else
    {
        $table = POSTS_TABLE;
        $sql_id = 'post_id';
        $set_id = 'post_edit_locked';
        $l_prefix = 'POST';
    }
    $orig_ids = $ids;
    if (!phpbb_check_ids($ids, $table, $sql_id, array('m_lock')))
    {
        // Make sure that for f_user_lock only the lock action is triggered.
        if ($action != 'lock')
        {
            return;
        }
        $ids = $orig_ids;
        if (!phpbb_check_ids($ids, $table, $sql_id, array('f_user_lock')))
        {
            return;
        }
    }
    unset($orig_ids);
    $redirect = $request->variable('redirect', build_url(array('action', 'quickmod')));
    $redirect = reapply_sid($redirect);
    $s_hidden_fields = build_hidden_fields(array(
        $sql_id . '_list'    => $ids,
        'action'            => $action,
        'redirect'            => $redirect)
    );
    if (confirm_box(true))
    {
        $sql = "UPDATE $table
            SET $set_id = " . (($action == 'lock' || $action == 'lock_post') ? ITEM_LOCKED : ITEM_UNLOCKED) . '
            WHERE ' . $db->sql_in_set($sql_id, $ids);
        $db->sql_query($sql);
        $data = ($action == 'lock' || $action == 'unlock') ? phpbb_get_topic_data($ids) : phpbb_get_post_data($ids);
        foreach ($data as $id => $row)
        {
            $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_' . strtoupper($action), false, array(
                'forum_id' => $row['forum_id'],
                'topic_id' => $row['topic_id'],
                'post_id'  => isset($row['post_id']) ? $row['post_id'] : 0,
                $row['topic_title']
            ));
        }
        /**
         * Perform additional actions after locking/unlocking posts/topics
         *
         * @event core.mcp_lock_unlock_after
         * @var    string    action                Variable containing the action we perform on the posts/topics ('lock', 'unlock', 'lock_post' or 'unlock_post')
         * @var    array    ids                    Array containing the post/topic IDs that have been locked/unlocked
         * @var    array    data                Array containing posts/topics data
         * @since 3.1.7-RC1
         */
        $vars = array(
            'action',
            'ids',
            'data',
        );
        extract($phpbb_dispatcher->trigger_event('core.mcp_lock_unlock_after', compact($vars)));
        $success_msg = $l_prefix . ((count($ids) == 1) ? '' : 'S') . '_' . (($action == 'lock' || $action == 'lock_post') ? 'LOCKED' : 'UNLOCKED') . '_SUCCESS';
        meta_refresh(2, $redirect);
        $message = $user->lang[$success_msg];
        if (!$request->is_ajax())
        {
            $message .= '<br /><br />' . $user->lang('RETURN_PAGE', '<a href="' . $redirect . '">', '</a>');
        }
        trigger_error($message);
    }
    else
    {
        confirm_box(false, strtoupper($action) . '_' . $l_prefix . ((count($ids) == 1) ? '' : 'S'), $s_hidden_fields);
    }
    redirect($redirect);
}
/**
* Change Topic Type
*/
function change_topic_type($action, $topic_ids)
{
    global $user, $db, $request, $phpbb_log, $phpbb_dispatcher;
    switch ($action)
    {
        case 'make_announce':
            $new_topic_type = POST_ANNOUNCE;
            $check_acl = 'f_announce';
            $l_new_type = (count($topic_ids) == 1) ? 'MCP_MAKE_ANNOUNCEMENT' : 'MCP_MAKE_ANNOUNCEMENTS';
        break;
        case 'make_global':
            $new_topic_type = POST_GLOBAL;
            $check_acl = 'f_announce_global';
            $l_new_type = (count($topic_ids) == 1) ? 'MCP_MAKE_GLOBAL' : 'MCP_MAKE_GLOBALS';
        break;
        case 'make_sticky':
            $new_topic_type = POST_STICKY;
            $check_acl = 'f_sticky';
            $l_new_type = (count($topic_ids) == 1) ? 'MCP_MAKE_STICKY' : 'MCP_MAKE_STICKIES';
        break;
        default:
            $new_topic_type = POST_NORMAL;
            $check_acl = false;
            $l_new_type = (count($topic_ids) == 1) ? 'MCP_MAKE_NORMAL' : 'MCP_MAKE_NORMALS';
        break;
    }
    $forum_id = phpbb_check_ids($topic_ids, TOPICS_TABLE, 'topic_id', $check_acl, true);
    if ($forum_id === false)
    {
        return;
    }
    $redirect = $request->variable('redirect', build_url(array('action', 'quickmod')));
    $redirect = reapply_sid($redirect);
    $s_hidden_fields = array(
        'topic_id_list'    => $topic_ids,
        'f'                => $forum_id,
        'action'        => $action,
        'redirect'        => $redirect,
    );
    if (confirm_box(true))
    {
        /**
         * Perform additional actions before changing topic(s) type
         *
         * @event core.mcp_change_topic_type_before
         * @var    int        new_topic_type        The candidated topic type.
         * @var    int        forum_id            The forum ID for the topic ID(s).
         * @var    array    topic_ids            Array containing the topic ID(s) that will be changed
         * @since 3.2.6-RC1
         */
        $vars = array(
            'new_topic_type',
            'forum_id',
            'topic_ids',
        );
        extract($phpbb_dispatcher->trigger_event('core.mcp_change_topic_type_before', compact($vars)));
        $db->sql_transaction('begin');
        $sql = 'UPDATE ' . TOPICS_TABLE . "
            SET topic_type = $new_topic_type
            WHERE " . $db->sql_in_set('topic_id', $topic_ids);
        $db->sql_query($sql);
        if (($new_topic_type == POST_GLOBAL) && count($topic_ids))
        {
            // Delete topic shadows for global announcements
            $sql = 'DELETE FROM ' . TOPICS_TABLE . '
                WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids);
            $db->sql_query($sql);
        }
        $db->sql_transaction('commit');
        $success_msg = (count($topic_ids) == 1) ? 'TOPIC_TYPE_CHANGED' : 'TOPICS_TYPE_CHANGED';
        if (count($topic_ids))
        {
            $data = phpbb_get_topic_data($topic_ids);
            foreach ($data as $topic_id => $row)
            {
                $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_TOPIC_TYPE_CHANGED', false, array(
                    'forum_id' => $forum_id,
                    'topic_id' => $topic_id,
                    $row['topic_title']
                ));
            }
        }
        /**
         * Perform additional actions after changing topic types
         *
         * @event core.mcp_change_topic_type_after
         * @var    int        new_topic_type        The newly changed topic type.
         * @var    int        forum_id            The forum ID where the newly changed topic type belongs to.
         * @var    array    topic_ids            Array containing the topic IDs that have been changed
         * @since 3.2.6-RC1
         */
        $vars = array(
            'new_topic_type',
            'forum_id',
            'topic_ids',
        );
        extract($phpbb_dispatcher->trigger_event('core.mcp_change_topic_type_after', compact($vars)));
        meta_refresh(2, $redirect);
        $message = $user->lang[$success_msg];
        if (!$request->is_ajax())
        {
            $message .= '<br /><br />' . $user->lang('RETURN_PAGE', '<a href="' . $redirect . '">', '</a>');
        }
        trigger_error($message);
    }
    else
    {
        confirm_box(false, $l_new_type, build_hidden_fields($s_hidden_fields));
    }
    redirect($redirect);
}
/**
* Move Topic
*/
function mcp_move_topic($topic_ids)
{
    global $auth, $user, $db, $template, $phpbb_log, $request, $phpbb_dispatcher;
    global $phpEx, $phpbb_root_path;
    // Here we limit the operation to one forum only
    $forum_id = phpbb_check_ids($topic_ids, TOPICS_TABLE, 'topic_id', array('m_move'), true);
    if ($forum_id === false)
    {
        return;
    }
    $to_forum_id = $request->variable('to_forum_id', 0);
    $redirect = $request->variable('redirect', build_url(array('action', 'quickmod')));
    $additional_msg = $success_msg = '';
    $s_hidden_fields = build_hidden_fields(array(
        'topic_id_list'    => $topic_ids,
        'f'                => $forum_id,
        'action'        => 'move',
        'redirect'        => $redirect)
    );
    if ($to_forum_id)
    {
        $forum_data = phpbb_get_forum_data($to_forum_id, 'f_post');
        if (!count($forum_data))
        {
            $additional_msg = $user->lang['FORUM_NOT_EXIST'];
        }
        else
        {
            $forum_data = $forum_data[$to_forum_id];
            if ($forum_data['forum_type'] != FORUM_POST)
            {
                $additional_msg = $user->lang['FORUM_NOT_POSTABLE'];
            }
            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)))
            {
                $additional_msg = $user->lang['USER_CANNOT_POST'];
            }
            else if ($forum_id == $to_forum_id)
            {
                $additional_msg = $user->lang['CANNOT_MOVE_SAME_FORUM'];
            }
        }
    }
    else if (isset($_POST['confirm']))
    {
        $additional_msg = $user->lang['FORUM_NOT_EXIST'];
    }
    if (!$to_forum_id || $additional_msg)
    {
        $request->overwrite('confirm', null, \phpbb\request\request_interface::POST);
        $request->overwrite('confirm_key', null);
    }
    if (confirm_box(true))
    {
        $topic_data = phpbb_get_topic_data($topic_ids);
        $leave_shadow = (isset($_POST['move_leave_shadow'])) ? true : false;
        $forum_sync_data = array();
        $forum_sync_data[$forum_id] = current($topic_data);
        $forum_sync_data[$to_forum_id] = $forum_data;
        $topics_moved = $topics_moved_unapproved = $topics_moved_softdeleted = 0;
        $posts_moved = $posts_moved_unapproved = $posts_moved_softdeleted = 0;
        foreach ($topic_data as $topic_id => $topic_info)
        {
            if ($topic_info['topic_visibility'] == ITEM_APPROVED)
            {
                $topics_moved++;
            }
            else if ($topic_info['topic_visibility'] == ITEM_UNAPPROVED || $topic_info['topic_visibility'] == ITEM_REAPPROVE)
            {
                $topics_moved_unapproved++;
            }
            else if ($topic_info['topic_visibility'] == ITEM_DELETED)
            {
                $topics_moved_softdeleted++;
            }
            $posts_moved += $topic_info['topic_posts_approved'];
            $posts_moved_unapproved += $topic_info['topic_posts_unapproved'];
            $posts_moved_softdeleted += $topic_info['topic_posts_softdeleted'];
        }
        $db->sql_transaction('begin');
        // Move topics, but do not resync yet
        move_topics($topic_ids, $to_forum_id, false);
        if ($request->is_set_post('move_lock_topics') && $auth->acl_get('m_lock', $to_forum_id))
        {
            $sql = 'UPDATE ' . TOPICS_TABLE . '
                SET topic_status = ' . ITEM_LOCKED . '
                WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
            $db->sql_query($sql);
        }
        $shadow_topics = 0;
        $forum_ids = array($to_forum_id);
        foreach ($topic_data as $topic_id => $row)
        {
            // Get the list of forums to resync
            $forum_ids[] = $row['forum_id'];
            // We add the $to_forum_id twice, because 'forum_id' is updated
            // when the topic is moved again later.
            $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_MOVE', false, array(
                'forum_id'        => (int) $to_forum_id,
                'topic_id'        => (int) $topic_id,
                $row['forum_name'],
                $forum_data['forum_name'],
                (int) $row['forum_id'],
                (int) $forum_data['forum_id'],
            ));
            // Leave a redirection if required and only if the topic is visible to users
            if ($leave_shadow && $row['topic_visibility'] == ITEM_APPROVED && $row['topic_type'] != POST_GLOBAL)
            {
                $shadow = array(
                    'forum_id'                =>    (int) $row['forum_id'],
                    'icon_id'                =>    (int) $row['icon_id'],
                    'topic_attachment'        =>    (int) $row['topic_attachment'],
                    'topic_visibility'        =>    ITEM_APPROVED, // a shadow topic is always approved
                    'topic_reported'        =>    0, // a shadow topic is never reported
                    'topic_title'            =>    (string) $row['topic_title'],
                    'topic_poster'            =>    (int) $row['topic_poster'],
                    'topic_time'            =>    (int) $row['topic_time'],
                    'topic_time_limit'        =>    (int) $row['topic_time_limit'],
                    'topic_views'            =>    (int) $row['topic_views'],
                    'topic_posts_approved'    =>    (int) $row['topic_posts_approved'],
                    'topic_posts_unapproved'=>    (int) $row['topic_posts_unapproved'],
                    'topic_posts_softdeleted'=>    (int) $row['topic_posts_softdeleted'],
                    'topic_status'            =>    ITEM_MOVED,
                    'topic_type'            =>    POST_NORMAL,
                    'topic_first_post_id'    =>    (int) $row['topic_first_post_id'],
                    'topic_first_poster_colour'=>(string) $row['topic_first_poster_colour'],
                    'topic_first_poster_name'=>    (string) $row['topic_first_poster_name'],
                    'topic_last_post_id'    =>    (int) $row['topic_last_post_id'],
                    'topic_last_poster_id'    =>    (int) $row['topic_last_poster_id'],
                    'topic_last_poster_colour'=>(string) $row['topic_last_poster_colour'],
                    'topic_last_poster_name'=>    (string) $row['topic_last_poster_name'],
                    'topic_last_post_subject'=>    (string) $row['topic_last_post_subject'],
                    'topic_last_post_time'    =>    (int) $row['topic_last_post_time'],
                    'topic_last_view_time'    =>    (int) $row['topic_last_view_time'],
                    'topic_moved_id'        =>    (int) $row['topic_id'],
                    'topic_bumped'            =>    (int) $row['topic_bumped'],
                    'topic_bumper'            =>    (int) $row['topic_bumper'],
                    'poll_title'            =>    (string) $row['poll_title'],
                    'poll_start'            =>    (int) $row['poll_start'],
                    'poll_length'            =>    (int) $row['poll_length'],
                    'poll_max_options'        =>    (int) $row['poll_max_options'],
                    'poll_last_vote'        =>    (int) $row['poll_last_vote']
                );
                /**
                * Perform actions before shadow topic is created.
                *
                * @event core.mcp_main_modify_shadow_sql
                * @var    array    shadow    SQL array to be used by $db->sql_build_array
                * @var    array    row        Topic data
                * @since 3.1.11-RC1
                * @changed 3.1.11-RC1 Added variable: row
                */
                $vars = array(
                    'shadow',
                    'row',
                );
                extract($phpbb_dispatcher->trigger_event('core.mcp_main_modify_shadow_sql', compact($vars)));
                $db->sql_query('INSERT INTO ' . TOPICS_TABLE . $db->sql_build_array('INSERT', $shadow));
                // Shadow topics only count on new "topics" and not posts... a shadow topic alone has 0 posts
                $shadow_topics++;
            }
        }
        unset($topic_data);
        $sync_sql = array();
        if ($posts_moved)
        {
            $sync_sql[$to_forum_id][] = 'forum_posts_approved = forum_posts_approved + ' . (int) $posts_moved;
            $sync_sql[$forum_id][] = 'forum_posts_approved = forum_posts_approved - ' . (int) $posts_moved;
        }
        if ($posts_moved_unapproved)
        {
            $sync_sql[$to_forum_id][] = 'forum_posts_unapproved = forum_posts_unapproved + ' . (int) $posts_moved_unapproved;
            $sync_sql[$forum_id][] = 'forum_posts_unapproved = forum_posts_unapproved - ' . (int) $posts_moved_unapproved;
        }
        if ($posts_moved_softdeleted)
        {
            $sync_sql[$to_forum_id][] = 'forum_posts_softdeleted = forum_posts_softdeleted + ' . (int) $posts_moved_softdeleted;
            $sync_sql[$forum_id][] = 'forum_posts_softdeleted = forum_posts_softdeleted - ' . (int) $posts_moved_softdeleted;
        }
        if ($topics_moved)
        {
            $sync_sql[$to_forum_id][] = 'forum_topics_approved = forum_topics_approved + ' . (int) $topics_moved;
            if ($topics_moved - $shadow_topics > 0)
            {
                $sync_sql[$forum_id][] = 'forum_topics_approved = forum_topics_approved - ' . (int) ($topics_moved - $shadow_topics);
            }
        }
        if ($topics_moved_unapproved)
        {
            $sync_sql[$to_forum_id][] = 'forum_topics_unapproved = forum_topics_unapproved + ' . (int) $topics_moved_unapproved;
            $sync_sql[$forum_id][] = 'forum_topics_unapproved = forum_topics_unapproved - ' . (int) $topics_moved_unapproved;
        }
        if ($topics_moved_softdeleted)
        {
            $sync_sql[$to_forum_id][] = 'forum_topics_softdeleted = forum_topics_softdeleted + ' . (int) $topics_moved_softdeleted;
            $sync_sql[$forum_id][] = 'forum_topics_softdeleted = forum_topics_softdeleted - ' . (int) $topics_moved_softdeleted;
        }
        $success_msg = (count($topic_ids) == 1) ? 'TOPIC_MOVED_SUCCESS' : 'TOPICS_MOVED_SUCCESS';
        foreach ($sync_sql as $forum_id_key => $array)
        {
            $sql = 'UPDATE ' . FORUMS_TABLE . '
                SET ' . implode(', ', $array) . '
                WHERE forum_id = ' . $forum_id_key;
            $db->sql_query($sql);
        }
        $db->sql_transaction('commit');
        sync('forum', 'forum_id', array($forum_id, $to_forum_id));
    }
    else
    {
        $template->assign_vars(array(
            'S_FORUM_SELECT'        => make_forum_select($to_forum_id, $forum_id, false, true, true, true),
            'S_CAN_LEAVE_SHADOW'    => true,
            'S_CAN_LOCK_TOPIC'        => ($auth->acl_get('m_lock', $to_forum_id)) ? true : false,
            'ADDITIONAL_MSG'        => $additional_msg)
        );
        confirm_box(false, 'MOVE_TOPIC' . ((count($topic_ids) == 1) ? '' : 'S'), $s_hidden_fields, 'mcp_move.html');
    }
    $redirect = $request->variable('redirect', "index.$phpEx");
    $redirect = reapply_sid($redirect);
    if (!$success_msg)
    {
        redirect($redirect);
    }
    else
    {
        meta_refresh(3, $redirect);
        $message = $user->lang[$success_msg];
        $message .= '<br /><br />' . sprintf($user->lang['RETURN_PAGE'], '<a href="' . $redirect . '">', '</a>');
        $message .= '<br /><br />' . sprintf($user->lang['RETURN_FORUM'], '<a href="' . append_sid("{$phpbb_root_path}viewforum.$phpEx", "f=$forum_id") . '">', '</a>');
        $message .= '<br /><br />' . sprintf($user->lang['RETURN_NEW_FORUM'], '<a href="' . append_sid("{$phpbb_root_path}viewforum.$phpEx", "f=$to_forum_id") . '">', '</a>');
        trigger_error($message);
    }
}
/**
* Restore Topics
*/
function mcp_restore_topic($topic_ids)
{
    global $user, $phpEx, $phpbb_root_path, $request, $phpbb_container, $phpbb_log;
    if (!phpbb_check_ids($topic_ids, TOPICS_TABLE, 'topic_id', array('m_approve')))
    {
        return;
    }
    $redirect = $request->variable('redirect', build_url(array('action', 'quickmod')));
    $forum_id = $request->variable('f', 0);
    $s_hidden_fields = build_hidden_fields(array(
        'topic_id_list'    => $topic_ids,
        'f'                => $forum_id,
        'action'        => 'restore_topic',
        'redirect'        => $redirect,
    ));
    $success_msg = '';
    if (confirm_box(true))
    {
        $success_msg = (count($topic_ids) == 1) ? 'TOPIC_RESTORED_SUCCESS' : 'TOPICS_RESTORED_SUCCESS';
        $data = phpbb_get_topic_data($topic_ids);
        /* @var $phpbb_content_visibility \phpbb\content_visibility */
        $phpbb_content_visibility = $phpbb_container->get('content.visibility');
        foreach ($data as $topic_id => $row)
        {
            $return = $phpbb_content_visibility->set_topic_visibility(ITEM_APPROVED, $topic_id, $row['forum_id'], $user->data['user_id'], time(), '');
            if (!empty($return))
            {
                $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_RESTORE_TOPIC', false, array(
                    'forum_id' => $row['forum_id'],
                    'topic_id' => $topic_id,
                    $row['topic_title'],
                    $row['topic_first_poster_name']
                ));
            }
        }
    }
    else
    {
        confirm_box(false, (count($topic_ids) == 1) ? 'RESTORE_TOPIC' : 'RESTORE_TOPICS', $s_hidden_fields);
    }
    $topic_id = $request->variable('t', 0);
    if (!$request->is_set('quickmod', \phpbb\request\request_interface::REQUEST))
    {
        $redirect = $request->variable('redirect', "index.$phpEx");
        $redirect = reapply_sid($redirect);
        $redirect_message = 'PAGE';
    }
    else if ($topic_id)
    {
        $redirect = append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=' . $topic_id);
        $redirect_message = 'TOPIC';
    }
    else
    {
        $redirect = append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $forum_id);
        $redirect_message = 'FORUM';
    }
    if (!$success_msg)
    {
        redirect($redirect);
    }
    else
    {
        meta_refresh(3, $redirect);
        trigger_error($user->lang[$success_msg] . '<br /><br />' . sprintf($user->lang['RETURN_' . $redirect_message], '<a href="' . $redirect . '">', '</a>'));
    }
}
/**
* Delete Topics
*/
function mcp_delete_topic($topic_ids, $is_soft = false, $soft_delete_reason = '', $action = 'delete_topic')
{
    global $auth, $user, $db, $phpEx, $phpbb_root_path, $request, $phpbb_container, $phpbb_log, $phpbb_dispatcher;
    $forum_id = $request->variable('f', 0);
    $check_permission = ($is_soft) ? ['m_softdelete'] : ['m_delete'];
    /**
    * This event allows you to modify the current user's checked permissions when deleting a topic
    *
    * @event core.mcp_delete_topic_modify_permissions
    * @var    array    topic_ids                The array of topic IDs to be deleted
    * @var    int        forum_id                The current forum ID
    * @var    bool    is_soft                    Boolean designating whether we're soft deleting or not
    * @var    string    soft_delete_reason        The reason we're soft deleting
    * @var    string    action                    The current delete action
    * @var    array    check_permission        The array with a permission to check for, can be set to false to not check them
    * @since 3.3.3-RC1
    */
    $vars = array(
        'topic_ids',
        'forum_id',
        'is_soft',
        'soft_delete_reason',
        'action',
        'check_permission',
    );
    extract($phpbb_dispatcher->trigger_event('core.mcp_delete_topic_modify_permissions', compact($vars)));
    if (!phpbb_check_ids($topic_ids, TOPICS_TABLE, 'topic_id', $check_permission))
    {
        return;
    }
    $redirect = $request->variable('redirect', build_url(array('action', 'quickmod')));
    $s_hidden_fields = array(
        'topic_id_list'    => $topic_ids,
        'f'                => $forum_id,
        'action'        => $action,
        'redirect'        => $redirect,
    );
    $success_msg = '';
    if (confirm_box(true))
    {
        $success_msg = (count($topic_ids) == 1) ? 'TOPIC_DELETED_SUCCESS' : 'TOPICS_DELETED_SUCCESS';
        $data = phpbb_get_topic_data($topic_ids);
        foreach ($data as $topic_id => $row)
        {
            if ($row['topic_moved_id'])
            {
                $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_DELETE_SHADOW_TOPIC', false, array(
                    'forum_id' => $row['forum_id'],
                    'topic_id' => $topic_id,
                    $row['topic_title']
                ));
            }
            else
            {
                // Only soft delete non-shadow topics
                if ($is_soft)
                {
                    /* @var $phpbb_content_visibility \phpbb\content_visibility */
                    $phpbb_content_visibility = $phpbb_container->get('content.visibility');
                    $return = $phpbb_content_visibility->set_topic_visibility(ITEM_DELETED, $topic_id, $row['forum_id'], $user->data['user_id'], time(), $soft_delete_reason);
                    if (!empty($return))
                    {
                        $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_SOFTDELETE_TOPIC', false, array(
                            'forum_id' => $row['forum_id'],
                            'topic_id' => $topic_id,
                            $row['topic_title'],
                            $row['topic_first_poster_name'],
                            $soft_delete_reason
                        ));
                    }
                }
                else
                {
                    $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_DELETE_TOPIC', false, array(
                        'forum_id' => $row['forum_id'],
                        'topic_id' => $topic_id,
                        $row['topic_title'],
                        $row['topic_first_poster_name'],
                        $soft_delete_reason
                    ));
                }
            }
        }
        if (!$is_soft)
        {
            delete_topics('topic_id', $topic_ids);
        }
    }
    else
    {
        global $template;
        $user->add_lang('posting');
        // If there are only shadow topics, we neither need a reason nor softdelete
        $sql = 'SELECT topic_id
            FROM ' . TOPICS_TABLE . '
            WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
                AND topic_moved_id = 0';
        $result = $db->sql_query_limit($sql, 1);
        $only_shadow = !$db->sql_fetchfield('topic_id');
        $db->sql_freeresult($result);
        $only_softdeleted = false;
        if (!$only_shadow && $auth->acl_get('m_delete', $forum_id) && $auth->acl_get('m_softdelete', $forum_id))
        {
            // If there are only soft deleted topics, we display a message why the option is not available
            $sql = 'SELECT topic_id
                FROM ' . TOPICS_TABLE . '
                WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
                    AND topic_visibility <> ' . ITEM_DELETED;
            $result = $db->sql_query_limit($sql, 1);
            $only_softdeleted = !$db->sql_fetchfield('topic_id');
            $db->sql_freeresult($result);
        }
        $template->assign_vars(array(
            'S_SHADOW_TOPICS'                    => $only_shadow,
            'S_SOFTDELETED'                        => $only_softdeleted,
            'S_TOPIC_MODE'                        => true,
            'S_ALLOWED_DELETE'                    => $auth->acl_get('m_delete', $forum_id),
            'S_ALLOWED_SOFTDELETE'                => $auth->acl_get('m_softdelete', $forum_id),
            'DELETE_TOPIC_PERMANENTLY_EXPLAIN'    => $user->lang('DELETE_TOPIC_PERMANENTLY', count($topic_ids)),
        ));
        $count = count($topic_ids);
        $l_confirm = $count === 1 ? 'DELETE_TOPIC' : 'DELETE_TOPICS';
        if ($only_softdeleted)
        {
            $l_confirm = array($l_confirm . '_PERMANENTLY', $count);
            $s_hidden_fields['delete_permanent'] = '1';
        }
        else if ($only_shadow || !$auth->acl_get('m_softdelete', $forum_id))
        {
            $s_hidden_fields['delete_permanent'] = '1';
        }
        /**
        * This event allows you to modify the hidden form fields when deleting topics
        *
        * @event core.mcp_delete_topic_modify_hidden_fields
        * @var    string    l_confirm                The confirmation text language variable (DELETE_TOPIC(S), DELETE_TOPIC(S)_PERMANENTLY)
        * @var    array    s_hidden_fields            The array holding the hidden form fields
        * @var    array    topic_ids                The array of topic IDs to be deleted
        * @var    int        forum_id                The current forum ID
        * @var    bool    only_softdeleted        If the topic_ids are all soft deleted, this is true
        * @var    bool    only_shadow                If the topic_ids are all shadow topics, this is true
        * @since 3.3.3-RC1
        */
        $vars = array(
            'l_confirm',
            's_hidden_fields',
            'topic_ids',
            'forum_id',
            'only_softdeleted',
            'only_shadow',
        );
        extract($phpbb_dispatcher->trigger_event('core.mcp_delete_topic_modify_hidden_fields', compact($vars)));
        confirm_box(false, $l_confirm, build_hidden_fields($s_hidden_fields), 'confirm_delete_body.html');
    }
    $topic_id = $request->variable('t', 0);
    if (!$request->is_set('quickmod', \phpbb\request\request_interface::REQUEST))
    {
        $redirect = $request->variable('redirect', "index.$phpEx");
        $redirect = reapply_sid($redirect);
        $redirect_message = 'PAGE';
    }
    else if ($is_soft && $topic_id)
    {
        $redirect = append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=' . $topic_id);
        $redirect_message = 'TOPIC';
    }
    else
    {
        $redirect = append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $forum_id);
        $redirect_message = 'FORUM';
    }
    if (!$success_msg)
    {
        redirect($redirect);
    }
    else
    {
        meta_refresh(3, $redirect);
        trigger_error($user->lang[$success_msg] . '<br /><br />' . sprintf($user->lang['RETURN_' . $redirect_message], '<a href="' . $redirect . '">', '</a>'));
    }
}
/**
* Delete Posts
*/
function mcp_delete_post($post_ids, $is_soft = false, $soft_delete_reason = '', $action = 'delete_post')
{
    global $auth, $user, $db, $phpEx, $phpbb_root_path, $request, $phpbb_container, $phpbb_log;
    $check_permission = ($is_soft) ? 'm_softdelete' : 'm_delete';
    if (!phpbb_check_ids($post_ids, POSTS_TABLE, 'post_id', array($check_permission)))
    {
        return;
    }
    $redirect = $request->variable('redirect', build_url(array('action', 'quickmod')));
    $forum_id = $request->variable('f', 0);
    $topic_id = 0;
    $s_hidden_fields = array(
        'post_id_list'    => $post_ids,
        'f'                => $forum_id,
        'action'        => $action,
        'redirect'        => $redirect,
    );
    $success_msg = '';
    if (confirm_box(true) && $is_soft)
    {
        $post_info = phpbb_get_post_data($post_ids);
        $topic_info = $approve_log = array();
        // Group the posts by topic_id
        foreach ($post_info as $post_id => $post_data)
        {
            if ($post_data['post_visibility'] != ITEM_APPROVED)
            {
                continue;
            }
            $topic_id = (int) $post_data['topic_id'];
            $topic_info[$topic_id]['posts'][] = (int) $post_id;
            $topic_info[$topic_id]['forum_id'] = (int) $post_data['forum_id'];
            if ($post_id == $post_data['topic_first_post_id'])
            {
                $topic_info[$topic_id]['first_post'] = true;
            }
            if ($post_id == $post_data['topic_last_post_id'])
            {
                $topic_info[$topic_id]['last_post'] = true;
            }
            $approve_log[] = array(
                'forum_id'        => $post_data['forum_id'],
                'topic_id'        => $post_data['topic_id'],
                'post_id'        => $post_id,
                'post_subject'    => $post_data['post_subject'],
                'poster_id'        => $post_data['poster_id'],
                'post_username'    => $post_data['post_username'],
                'username'        => $post_data['username'],
            );
        }
        /* @var $phpbb_content_visibility \phpbb\content_visibility */
        $phpbb_content_visibility = $phpbb_container->get('content.visibility');
        foreach ($topic_info as $topic_id => $topic_data)
        {
            $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']));
        }
        $affected_topics = count($topic_info);
        // None of the topics is really deleted, so a redirect won't hurt much.
        $deleted_topics = 0;
        $success_msg = (count($post_info) == 1) ? $user->lang['POST_DELETED_SUCCESS'] : $user->lang['POSTS_DELETED_SUCCESS'];
        foreach ($approve_log as $row)
        {
            $post_username = ($row['poster_id'] == ANONYMOUS && !empty($row['post_username'])) ? $row['post_username'] : $row['username'];
            $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_SOFTDELETE_POST', false, array(
                'forum_id' => $row['forum_id'],
                'topic_id' => $row['topic_id'],
                'post_id'  => $row['post_id'],
                $row['post_subject'],
                $post_username,
                $soft_delete_reason
            ));
        }
        // Return links
        $return_link = array();
        if ($affected_topics == 1 && $topic_id)
        {
            $return_link[] = sprintf($user->lang['RETURN_TOPIC'], '<a href="' . append_sid("{$phpbb_root_path}viewtopic.$phpEx", "t=$topic_id") . '">', '</a>');
        }
        $return_link[] = sprintf($user->lang['RETURN_FORUM'], '<a href="' . append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $forum_id) . '">', '</a>');
    }
    else if (confirm_box(true))
    {
        if (!function_exists('delete_posts'))
        {
            include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
        }
        // Count the number of topics that are affected
        // I did not use COUNT(DISTINCT ...) because I remember having problems
        // with it on older versions of MySQL -- Ashe
        $sql = 'SELECT DISTINCT topic_id
            FROM ' . POSTS_TABLE . '
            WHERE ' . $db->sql_in_set('post_id', $post_ids);
        $result = $db->sql_query($sql);
        $topic_id_list = array();
        while ($row = $db->sql_fetchrow($result))
        {
            $topic_id_list[] = $topic_id = $row['topic_id'];
        }
        $affected_topics = count($topic_id_list);
        $db->sql_freeresult($result);
        $post_data = phpbb_get_post_data($post_ids);
        foreach ($post_data as $id => $row)
        {
            $post_username = ($row['poster_id'] == ANONYMOUS && !empty($row['post_username'])) ? $row['post_username'] : $row['username'];
            $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_DELETE_POST', false, array(
                'forum_id' => $row['forum_id'],
                'topic_id' => $row['topic_id'],
                'post_id'  => $row['post_id'],
                $row['post_subject'],
                $post_username,
                $soft_delete_reason
            ));
        }
        // Now delete the posts, topics and forums are automatically resync'ed
        delete_posts('post_id', $post_ids);
        $sql = 'SELECT COUNT(topic_id) AS topics_left
            FROM ' . TOPICS_TABLE . '
            WHERE ' . $db->sql_in_set('topic_id', $topic_id_list);
        $result = $db->sql_query_limit($sql, 1);
        $deleted_topics = ($row = $db->sql_fetchrow($result)) ? ($affected_topics - $row['topics_left']) : $affected_topics;
        $db->sql_freeresult($result);
        // Return links
        $return_link = array();
        if ($affected_topics == 1 && !$deleted_topics && $topic_id)
        {
            $return_link[] = sprintf($user->lang['RETURN_TOPIC'], '<a href="' . append_sid("{$phpbb_root_path}viewtopic.$phpEx", "t=$topic_id") . '">', '</a>');
        }
        $return_link[] = sprintf($user->lang['RETURN_FORUM'], '<a href="' . append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $forum_id) . '">', '</a>');
        if (count($post_ids) == 1)
        {
            if ($deleted_topics)
            {
                // We deleted the only post of a topic, which in turn has
                // been removed from the database
                $success_msg = $user->lang['TOPIC_DELETED_SUCCESS'];
            }
            else
            {
                // Remove any post id anchor
                if (($anchor_pos = strrpos($redirect, '#p')) !== false)
                {
                    $redirect = substr($redirect, 0, $anchor_pos);
                }
                $success_msg = $user->lang['POST_DELETED_SUCCESS'];
            }
        }
        else
        {
            if ($deleted_topics)
            {
                // Some of topics disappeared
                $success_msg = $user->lang['POSTS_DELETED_SUCCESS'] . '<br /><br />' . $user->lang['EMPTY_TOPICS_REMOVED_WARNING'];
            }
            else
            {
                $success_msg = $user->lang['POSTS_DELETED_SUCCESS'];
            }
        }
    }
    else
    {
        global $template;
        $user->add_lang('posting');
        $only_softdeleted = false;
        if ($auth->acl_get('m_delete', $forum_id) && $auth->acl_get('m_softdelete', $forum_id))
        {
            // If there are only soft deleted posts, we display a message why the option is not available
            $sql = 'SELECT post_id
                FROM ' . POSTS_TABLE . '
                WHERE ' . $db->sql_in_set('post_id', $post_ids) . '
                    AND post_visibility <> ' . ITEM_DELETED;
            $result = $db->sql_query_limit($sql, 1);
            $only_softdeleted = !$db->sql_fetchfield('post_id');
            $db->sql_freeresult($result);
        }
        $template->assign_vars(array(
            'S_SOFTDELETED'                        => $only_softdeleted,
            'S_ALLOWED_DELETE'                    => $auth->acl_get('m_delete', $forum_id),
            'S_ALLOWED_SOFTDELETE'                => $auth->acl_get('m_softdelete', $forum_id),
            'DELETE_POST_PERMANENTLY_EXPLAIN'    => $user->lang('DELETE_POST_PERMANENTLY', count($post_ids)),
        ));
        $count = count($post_ids);
        $l_confirm = $count === 1 ? 'DELETE_POST' : 'DELETE_POSTS';
        if ($only_softdeleted)
        {
            $l_confirm = array($l_confirm . '_PERMANENTLY', $count);
            $s_hidden_fields['delete_permanent'] = '1';
        }
        else if (!$auth->acl_get('m_softdelete', $forum_id))
        {
            $s_hidden_fields['delete_permanent'] = '1';
        }
        confirm_box(false, $l_confirm, build_hidden_fields($s_hidden_fields), 'confirm_delete_body.html');
    }
    $redirect = reapply_sid($redirect);
    if (!$success_msg)
    {
        redirect($redirect);
    }
    else
    {
        if ($affected_topics != 1 || $deleted_topics || !$topic_id)
        {
            $redirect = append_sid("{$phpbb_root_path}mcp.$phpEx", "f=$forum_id&i=main&mode=forum_view", false);
        }
        meta_refresh(3, $redirect);
        trigger_error($success_msg . '<br /><br />' . sprintf($user->lang['RETURN_PAGE'], '<a href="' . $redirect . '">', '</a>') . '<br /><br />' . implode('<br /><br />', $return_link));
    }
}
/**
* Fork Topic
*/
function mcp_fork_topic($topic_ids)
{
    global $auth, $user, $db, $template, $config;
    global $phpEx, $phpbb_root_path, $phpbb_log, $request, $phpbb_dispatcher;
    if (!phpbb_check_ids($topic_ids, TOPICS_TABLE, 'topic_id', array('m_')))
    {
        return;
    }
    $to_forum_id = $request->variable('to_forum_id', 0);
    $forum_id = $request->variable('f', 0);
    $redirect = $request->variable('redirect', build_url(array('action', 'quickmod')));
    $additional_msg = $success_msg = '';
    $counter = array();
    $s_hidden_fields = build_hidden_fields(array(
        'topic_id_list'    => $topic_ids,
        'f'                => $forum_id,
        'action'        => 'fork',
        'redirect'        => $redirect)
    );
    if ($to_forum_id)
    {
        $forum_data = phpbb_get_forum_data($to_forum_id, 'f_post');
        if (!count($topic_ids))
        {
            $additional_msg = $user->lang['NO_TOPIC_SELECTED'];
        }
        else if (!count($forum_data))
        {
            $additional_msg = $user->lang['FORUM_NOT_EXIST'];
        }
        else
        {
            $forum_data = $forum_data[$to_forum_id];
            if ($forum_data['forum_type'] != FORUM_POST)
            {
                $additional_msg = $user->lang['FORUM_NOT_POSTABLE'];
            }
            else if (!$auth->acl_get('f_post', $to_forum_id))
            {
                $additional_msg = $user->lang['USER_CANNOT_POST'];
            }
        }
    }
    else if (isset($_POST['confirm']))
    {
        $additional_msg = $user->lang['FORUM_NOT_EXIST'];
    }
    if ($additional_msg)
    {
        $request->overwrite('confirm', null, \phpbb\request\request_interface::POST);
        $request->overwrite('confirm_key', null);
    }
    if (confirm_box(true))
    {
        $topic_data = phpbb_get_topic_data($topic_ids, 'f_post');
        $total_topics = $total_topics_unapproved = $total_topics_softdeleted = 0;
        $total_posts = $total_posts_unapproved = $total_posts_softdeleted = 0;
        $new_topic_id_list = array();
        foreach ($topic_data as $topic_id => $topic_row)
        {
            if (!isset($search_type) && $topic_row['enable_indexing'])
            {
                // Select the search method and do some additional checks to ensure it can actually be utilised
                $search_type = $config['search_type'];
                if (!class_exists($search_type))
                {
                    trigger_error('NO_SUCH_SEARCH_MODULE');
                }
                $error = false;
                $search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher);
                $search_mode = 'post';
                if ($error)
                {
                    trigger_error($error);
                }
            }
            else if (!isset($search_type) && !$topic_row['enable_indexing'])
            {
                $search_type = false;
            }
            $sql_ary = array(
                'forum_id'                    => (int) $to_forum_id,
                'icon_id'                    => (int) $topic_row['icon_id'],
                'topic_attachment'            => (int) $topic_row['topic_attachment'],
                'topic_visibility'            => (int) $topic_row['topic_visibility'],
                'topic_reported'            => 0,
                'topic_title'                => (string) $topic_row['topic_title'],
                'topic_poster'                => (int) $topic_row['topic_poster'],
                'topic_time'                => (int) $topic_row['topic_time'],
                'topic_posts_approved'        => (int) $topic_row['topic_posts_approved'],
                'topic_posts_unapproved'    => (int) $topic_row['topic_posts_unapproved'],
                'topic_posts_softdeleted'    => (int) $topic_row['topic_posts_softdeleted'],
                'topic_status'                => (int) $topic_row['topic_status'],
                'topic_type'                => (int) $topic_row['topic_type'],
                'topic_first_poster_name'    => (string) $topic_row['topic_first_poster_name'],
                'topic_last_poster_id'        => (int) $topic_row['topic_last_poster_id'],
                'topic_last_poster_name'    => (string) $topic_row['topic_last_poster_name'],
                'topic_last_post_time'        => (int) $topic_row['topic_last_post_time'],
                'topic_last_view_time'        => (int) $topic_row['topic_last_view_time'],
                'topic_bumped'                => (int) $topic_row['topic_bumped'],
                'topic_bumper'                => (int) $topic_row['topic_bumper'],
                'poll_title'                => (string) $topic_row['poll_title'],
                'poll_start'                => (int) $topic_row['poll_start'],
                'poll_length'                => (int) $topic_row['poll_length'],
                'poll_max_options'            => (int) $topic_row['poll_max_options'],
                'poll_vote_change'            => (int) $topic_row['poll_vote_change'],
            );
            /**
            * Perform actions before forked topic is created.
            *
            * @event core.mcp_main_modify_fork_sql
            * @var    array    sql_ary        SQL array to be used by $db->sql_build_array
            * @var    array    topic_row    Topic data
            * @since 3.1.11-RC1
            * @changed 3.1.11-RC1 Added variable: topic_row
            */
            $vars = array(
                'sql_ary',
                'topic_row',
            );
            extract($phpbb_dispatcher->trigger_event('core.mcp_main_modify_fork_sql', compact($vars)));
            $db->sql_query('INSERT INTO ' . TOPICS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
            $new_topic_id = $db->sql_nextid();
            $new_topic_id_list[$topic_id] = $new_topic_id;
            switch ($topic_row['topic_visibility'])
            {
                case ITEM_APPROVED:
                    $total_topics++;
                break;
                case ITEM_UNAPPROVED:
                case ITEM_REAPPROVE:
                    $total_topics_unapproved++;
                break;
                case ITEM_DELETED:
                    $total_topics_softdeleted++;
                break;
            }
            if ($topic_row['poll_start'])
            {
                $sql = 'SELECT *
                    FROM ' . POLL_OPTIONS_TABLE . "
                    WHERE topic_id = $topic_id";
                $result = $db->sql_query($sql);
                while ($row = $db->sql_fetchrow($result))
                {
                    $sql_ary = array(
                        'poll_option_id'    => (int) $row['poll_option_id'],
                        'topic_id'            => (int) $new_topic_id,
                        'poll_option_text'    => (string) $row['poll_option_text'],
                        'poll_option_total'    => 0
                    );
                    $db->sql_query('INSERT INTO ' . POLL_OPTIONS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
                }
                $db->sql_freeresult($result);
            }
            $sql = 'SELECT *
                FROM ' . POSTS_TABLE . "
                WHERE topic_id = $topic_id
                ORDER BY post_time ASC, post_id ASC";
            $result = $db->sql_query($sql);
            $post_rows = array();
            while ($row = $db->sql_fetchrow($result))
            {
                $post_rows[] = $row;
            }
            $db->sql_freeresult($result);
            if (!count($post_rows))
            {
                continue;
            }
            foreach ($post_rows as $row)
            {
                $sql_ary = array(
                    'topic_id'            => (int) $new_topic_id,
                    'forum_id'            => (int) $to_forum_id,
                    'poster_id'            => (int) $row['poster_id'],
                    'icon_id'            => (int) $row['icon_id'],
                    'poster_ip'            => (string) $row['poster_ip'],
                    'post_time'            => (int) $row['post_time'],
                    'post_visibility'    => (int) $row['post_visibility'],
                    'post_reported'        => 0,
                    'enable_bbcode'        => (int) $row['enable_bbcode'],
                    'enable_smilies'    => (int) $row['enable_smilies'],
                    'enable_magic_url'    => (int) $row['enable_magic_url'],
                    'enable_sig'        => (int) $row['enable_sig'],
                    'post_username'        => (string) $row['post_username'],
                    'post_subject'        => (string) $row['post_subject'],
                    'post_text'            => (string) $row['post_text'],
                    'post_edit_reason'    => (string) $row['post_edit_reason'],
                    'post_edit_user'    => (int) $row['post_edit_user'],
                    'post_checksum'        => (string) $row['post_checksum'],
                    'post_attachment'    => (int) $row['post_attachment'],
                    'bbcode_bitfield'    => $row['bbcode_bitfield'],
                    'bbcode_uid'        => (string) $row['bbcode_uid'],
                    'post_edit_time'    => (int) $row['post_edit_time'],
                    'post_edit_count'    => (int) $row['post_edit_count'],
                    'post_edit_locked'    => (int) $row['post_edit_locked'],
                    'post_postcount'    => $row['post_postcount'],
                );
                // Adjust post count only if the post can be incremented to the user counter
                if ($row['post_postcount'])
                {
                    if (isset($counter[$row['poster_id']]))
                    {
                        ++$counter[$row['poster_id']];
                    }
                    else
                    {
                        $counter[$row['poster_id']] = 1;
                    }
                }
                /**
                * Modify the forked post's sql array before it's inserted into the database.
                *
                * @event core.mcp_main_modify_fork_post_sql
                * @var    int        new_topic_id    The newly created topic ID
                * @var    int        to_forum_id        The forum ID where the forked topic has been moved to
                * @var    array    sql_ary            SQL Array with the post's data
                * @var    array    row                Post data
                * @var    array    counter            Array with post counts
                * @since 3.3.5-RC1
                */
                $vars = [
                    'new_topic_id',
                    'to_forum_id',
                    'sql_ary',
                    'row',
                    'counter',
                ];
                extract($phpbb_dispatcher->trigger_event('core.mcp_main_modify_fork_post_sql', compact($vars)));
                $db->sql_query('INSERT INTO ' . POSTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
                $new_post_id = $db->sql_nextid();
                /**
                * Perform actions after forked topic is created.
                *
                * @event core.mcp_main_fork_sql_after
                * @var    int        new_topic_id    The newly created topic ID
                * @var    int        to_forum_id        The forum ID where the forked topic has been moved to
                * @var    int        new_post_id        The newly created post ID
                * @var    array    row                Post data
                * @since 3.2.4-RC1
                */
                $vars = array(
                    'new_topic_id',
                    'to_forum_id',
                    'new_post_id',
                    'row',
                );
                extract($phpbb_dispatcher->trigger_event('core.mcp_main_fork_sql_after', compact($vars)));
                switch ($row['post_visibility'])
                {
                    case ITEM_APPROVED:
                        $total_posts++;
                    break;
                    case ITEM_UNAPPROVED:
                    case ITEM_REAPPROVE:
                        $total_posts_unapproved++;
                    break;
                    case ITEM_DELETED:
                        $total_posts_softdeleted++;
                    break;
                }
                // Copy whether the topic is dotted
                markread('post', $to_forum_id, $new_topic_id, 0, $row['poster_id']);
                if (!empty($search_type))
                {
                    $search->index($search_mode, $new_post_id, $sql_ary['post_text'], $sql_ary['post_subject'], $sql_ary['poster_id'], ($topic_row['topic_type'] == POST_GLOBAL) ? 0 : $to_forum_id);
                    $search_mode = 'reply'; // After one we index replies
                }
                // Copy Attachments
                if ($row['post_attachment'])
                {
                    $sql = 'SELECT * FROM ' . ATTACHMENTS_TABLE . '
                        WHERE post_msg_id = ' . (int) $row['post_id'] . '
                            AND topic_id = ' . (int) $topic_id . '
                            AND in_message = 0
                        ORDER BY attach_id ASC';
                    $result = $db->sql_query($sql);
                    $sql_ary = array();
                    while ($attach_row = $db->sql_fetchrow($result))
                    {
                        $sql_ary[] = array(
                            'post_msg_id'        => (int) $new_post_id,
                            'topic_id'            => (int) $new_topic_id,
                            'in_message'        => 0,
                            'is_orphan'            => (int) $attach_row['is_orphan'],
                            'poster_id'            => (int) $attach_row['poster_id'],
                            'physical_filename'    => (string) utf8_basename($attach_row['physical_filename']),
                            'real_filename'        => (string) utf8_basename($attach_row['real_filename']),
                            'download_count'    => (int) $attach_row['download_count'],
                            'attach_comment'    => (string) $attach_row['attach_comment'],
                            'extension'            => (string) $attach_row['extension'],
                            'mimetype'            => (string) $attach_row['mimetype'],
                            'filesize'            => (int) $attach_row['filesize'],
                            'filetime'            => (int) $attach_row['filetime'],
                            'thumbnail'            => (int) $attach_row['thumbnail']
                        );
                    }
                    $db->sql_freeresult($result);
                    if (count($sql_ary))
                    {
                        $db->sql_multi_insert(ATTACHMENTS_TABLE, $sql_ary);
                    }
                }
            }
            // Copy topic subscriptions to new topic
            $sql = 'SELECT user_id, notify_status
                FROM ' . TOPICS_WATCH_TABLE . '
                WHERE topic_id = ' . $topic_id;
            $result = $db->sql_query($sql);
            $sql_ary = array();
            while ($row = $db->sql_fetchrow($result))
            {
                $sql_ary[] = array(
                    'topic_id'        => (int) $new_topic_id,
                    'user_id'        => (int) $row['user_id'],
                    'notify_status'    => (int) $row['notify_status'],
                );
            }
            $db->sql_freeresult($result);
            if (count($sql_ary))
            {
                $db->sql_multi_insert(TOPICS_WATCH_TABLE, $sql_ary);
            }
            // Copy bookmarks to new topic
            $sql = 'SELECT user_id
                FROM ' . BOOKMARKS_TABLE . '
                WHERE topic_id = ' . $topic_id;
            $result = $db->sql_query($sql);
            $sql_ary = array();
            while ($row = $db->sql_fetchrow($result))
            {
                $sql_ary[] = array(
                    'topic_id'        => (int) $new_topic_id,
                    'user_id'        => (int) $row['user_id'],
                );
            }
            $db->sql_freeresult($result);
            if (count($sql_ary))
            {
                $db->sql_multi_insert(BOOKMARKS_TABLE, $sql_ary);
            }
        }
        // Sync new topics, parent forums and board stats
        $sql = 'UPDATE ' . FORUMS_TABLE . '
            SET forum_posts_approved = forum_posts_approved + ' . $total_posts . ',
                forum_posts_unapproved = forum_posts_unapproved + ' . $total_posts_unapproved . ',
                forum_posts_softdeleted = forum_posts_softdeleted + ' . $total_posts_softdeleted . ',
                forum_topics_approved = forum_topics_approved + ' . $total_topics . ',
                forum_topics_unapproved = forum_topics_unapproved + ' . $total_topics_unapproved . ',
                forum_topics_softdeleted = forum_topics_softdeleted + ' . $total_topics_softdeleted . '
            WHERE forum_id = ' . $to_forum_id;
        $db->sql_query($sql);
        if (!empty($counter))
        {
            // Do only one query per user and not a query per post.
            foreach ($counter as $user_id => $count)
            {
                $sql = 'UPDATE ' . USERS_TABLE . '
                    SET user_posts = user_posts + ' . (int) $count . '
                    WHERE user_id = ' . (int) $user_id;
                $db->sql_query($sql);
            }
        }
        sync('topic', 'topic_id', $new_topic_id_list);
        sync('forum', 'forum_id', $to_forum_id);
        $config->increment('num_topics', count($new_topic_id_list), false);
        $config->increment('num_posts', $total_posts, false);
        foreach ($new_topic_id_list as $topic_id => $new_topic_id)
        {
            $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_FORK', false, array(
                'forum_id' => $to_forum_id,
                'topic_id' => $new_topic_id,
                $topic_row['forum_name']
            ));
        }
        $success_msg = (count($topic_ids) == 1) ? 'TOPIC_FORKED_SUCCESS' : 'TOPICS_FORKED_SUCCESS';
    }
    else
    {
        $template->assign_vars(array(
            'S_FORUM_SELECT'        => make_forum_select($to_forum_id, false, false, true, true, true),
            'S_CAN_LEAVE_SHADOW'    => false,
            'ADDITIONAL_MSG'        => $additional_msg)
        );
        confirm_box(false, 'FORK_TOPIC' . ((count($topic_ids) == 1) ? '' : 'S'), $s_hidden_fields, 'mcp_move.html');
    }
    $redirect = $request->variable('redirect', "index.$phpEx");
    $redirect = reapply_sid($redirect);
    if (!$success_msg)
    {
        redirect($redirect);
    }
    else
    {
        $redirect_url = append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $forum_id);
        meta_refresh(3, $redirect_url);
        $return_link = sprintf($user->lang['RETURN_FORUM'], '<a href="' . $redirect_url . '">', '</a>');
        if ($forum_id != $to_forum_id)
        {
            $return_link .= '<br /><br />' . sprintf($user->lang['RETURN_NEW_FORUM'], '<a href="' . append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $to_forum_id) . '">', '</a>');
        }
        trigger_error($user->lang[$success_msg] . '<br /><br />' . $return_link);
    }
}