<?php /*

 Composr
 Copyright (c) Christopher Graham, 2004-2024

 See docs/LICENSE.md for full licensing information.


 NOTE TO PROGRAMMERS:
   Do not edit this file. If you need to make changes, save your changed file to the appropriate *_custom folder
   **** If you ignore this advice, then your website upgrades (e.g. for bug fixes) will likely kill your changes ****

*/

/**
 * @license    http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
 * @copyright  Christopher Graham
 * @package    import
 */

/**
 * Standard code module initialisation function.
 *
 * @ignore
 */
function init__hooks__modules__admin_import__mybb()
{
    global $TOPIC_FORUM_CACHE;
    $TOPIC_FORUM_CACHE = [];

    global $STRICT_FILE;
    $STRICT_FILE = false; // Disable this for a quicker import that is quite liable to go wrong if you don't have the files in the right place

    global $OLD_BASE_URL;
    $OLD_BASE_URL = null;
}

/**
 * Forum Driver.
 */
class Hook_import_mybb
{
    /**
     * Standard importer hook info function.
     *
     * @return ?array Importer handling details, including lists of all the import types covered (import types are not necessarily the same as actual tables) (null: importer is disabled)
     */
    public function info() : ?array
    {
        require_lang('cns');

        $info = [];
        $info['supports_advanced_import'] = false;
        $info['product'] = 'MyBB 1.8.x';
        $info['prefix'] = 'mybb_';
        $info['import'] = [
            'config',
            'cns_groups',
            'cns_custom_profile_fields',
            'cns_members',
            'cns_member_files',
            'ip_bans',
            'custom_comcode',
            'cns_forum_groupings',
            'cns_forums',
            'cns_topics',
            'cns_private_topics',
            'cns_posts',
            'cns_post_files',
            'cns_polls_and_votes',
            'notifications',
            'wordfilter',
            'calendar',
            'cns_multi_moderations',
        ];

        $info['dependencies'] = [
            // This dependency tree is overdefined, but I wanted to make it clear what depends on what, rather than having a simplified version
           'cns_members' => ['cns_groups'],
           'cns_member_files' => ['cns_members'],
           'cns_forums' => ['cns_forum_groupings', 'cns_members', 'cns_groups'],
           'cns_topics' => ['cns_forums', 'cns_members'],
           'cns_polls_and_votes' => ['cns_topics', 'cns_posts', 'cns_members'],
           'cns_posts' => ['cns_topics', 'cns_members'],
           'cns_post_files' => ['cns_posts', 'cns_private_topics'],
           'notifications' => ['cns_topics', 'cns_members', 'cns_polls_and_votes'],
           'cns_private_topics' => ['cns_members'],
           'cns_multi_moderations' => ['cns_forums', 'cns_members', 'cns_topics', 'cns_posts', 'cns_private_topics', 'cns_forum_groupings'],
        ];

        $_cleanup_url = build_url(['page' => 'admin_cleanup'], get_module_zone('admin_cleanup'));
        $cleanup_url = $_cleanup_url->evaluate();
        $info['final_message'] = do_lang_tempcode('FORUM_CACHE_CLEAR', escape_html($cleanup_url));

        $info['final_tasks'] = [
            ['cns_topics_recache', do_lang('CACHE_TOPICS'), 'f_topics', 100],
            ['cns_recache', do_lang('CACHE_FORUMS'), 'f_topics', 100],
            ['cns_members_recache', do_lang('CACHE_MEMBERS'), 'f_members', 100],
        ];

        return $info;
    }

    /**
     * Probe a file path for DB access details.
     *
     * @param  string $file_base The probe path
     * @return array A quartet of the details (db_name, db_user, db_pass, table_prefix)
     */
    public function probe_db_access(string $file_base) : array
    {
        $config = [];
        if (!file_exists($file_base . '/inc/config.php')) {
            warn_exit(do_lang_tempcode('BAD_IMPORT_PATH', escape_html('inc/config.php')));
        }
        require $file_base . '/inc/config.php';

        return [$config['database']['database'], $config['database']['username'], $config['database']['password'], $config['database']['table_prefix'], $config['database']['hostname']];
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $file_base The base directory we are importing from
     */
    public function import_config(object $db, string $table_prefix, string $file_base)
    {
        require $file_base . '/inc/config.php';

        $rows = $db->query_select('settings');

        $board_url = '';
        $config_remapping = [];
        $additional_data = [];
        $groups = $GLOBALS['CNS_DRIVER']->get_usergroup_list();
        $page_remap_data = [];
        $page_remap = [
            'enableregs' => 'join', //!'disableregs'
            'portal_showsearch' => 'search',
            'enablememberlist' => 'members',
        ];

        foreach ($rows as $row) {
            if ($row['name'] == 'boardclosed') {
                $config_remapping['site_closed'] = $row['value'];
                continue;
            }

            if ($row['name'] == 'boardclosed_reason') {
                $config_remapping['closed'] = $row['value'];
                continue;
            }

            if ($row['name'] == 'adminemail') {
                $config_remapping['staff_address'] = $row['value'];
                continue;
            }

            if ($row['name'] == 'homename') {
                $config_remapping['site_name'] = $row['value'];
                continue;
            }

            if ($row['name'] == 'postsperpage') {
                $config_remapping['forum_posts_per_page'] = $row['value'];
                continue;
            }

            if ($row['name'] == 'gzipoutput') {
                $config_remapping['output_compression'] = $row['value'];
                continue;
            }

            if ($row['name'] == 'captchaimage') {
                $config_remapping['use_captchas'] = $row['value'];
                continue;
            }

            if ($row['name'] == 'timezoneoffset') {
                $config_remapping['timezone'] = $row['value'];
                continue;
            }

            if ($row['name'] == 'quickreply') {
                $additional_data['quickreply'] = intval($row['value']);
                continue;
            }

            if ($row['name'] == 'maxattachments') {
                $additional_data['maxattachments'] = intval($row['value']);
                continue;
            }

            if ($row['name'] == 'postmaxavatarsize') {
                $avatar_dimensions = explode('x', $row['value']);
                if ((!empty($avatar_dimensions[0])) && (!empty($avatar_dimensions[1]))) {
                    $additional_data['avatar_max_width'] = intval($avatar_dimensions[0]);
                    $additional_data['avatar_max_height'] = intval($avatar_dimensions[1]);
                }
                continue;
            }

            if (($row['name']) && $row['name'] == 'disableregs') {
                $page_remap_data['enableregs'] = 1 - intval($row['value']);
                continue;
            }

            if ($row['name'] == 'portal_showsearch') {
                $page_remap_data['portal_showsearch'] = intval($row['value']);
                continue;
            }

            if ($row['name'] == 'enablememberlist') {
                $page_remap_data['enablememberlist'] = intval($row['value']);
                continue;
            }

            if ($row['name'] == 'bburl') {
                $board_url = $row['value'];
                continue;
            }
        }

        $PROBED_FORUM_CONFIG = [];

        foreach ($config_remapping as $key => $value) {
            set_option($key, $value);

            $PROBED_FORUM_CONFIG[$key] = $value;
        }

        foreach ($page_remap as $to) {
            $GLOBALS['SITE_DB']->query_delete('group_page_access', ['page_name' => $to, 'zone_name' => get_module_zone($to)]);
        }
        foreach ($groups as $id => $groupname) {
            if (preg_match('/Administrator/i', $groupname) != 0) {
                continue;
            }

            foreach ($page_remap as $from => $to) {
                if ($page_remap_data[$from] == 1) {
                    $GLOBALS['SITE_DB']->query_insert('group_page_access', ['page_name' => $to, 'zone_name' => get_module_zone($to), 'group_id' => $id]);
                }
            }

            $GLOBALS['FORUM_DB']->query_update('f_groups', ['g_max_attachments_per_post' => $additional_data['maxattachments'], 'g_max_avatar_width' => $additional_data['avatar_max_width'], 'g_max_avatar_height' => $additional_data['avatar_max_height']], ['id' => $id], '', 1);

            set_privilege($id, 'use_quick_reply', $additional_data['quickreply']);
        }

        $PROBED_FORUM_CONFIG['forum_base_url'] = $board_url;
        $PROBED_FORUM_CONFIG['user_cookie'] = 'mybbuser';
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $file_base The base directory we are importing from
     */
    public function import_cns_groups(object $db, string $table_prefix, string $file_base)
    {
        require $file_base . '/inc/config.php';

        $rows = $db->query_select('settings');
        $PROBED_FORUM_CONFIG = [];
        foreach ($rows as $row) {
            $key = $row['name'];
            $val = $row['value'];
            $PROBED_FORUM_CONFIG[$key] = $val;
        }

        $avatar_dimensions = explode('x', $PROBED_FORUM_CONFIG['postmaxavatarsize']);
        if ((!empty($avatar_dimensions[0])) && (!empty($avatar_dimensions[1]))) {
            $avatar_max_width = intval($avatar_dimensions[0]);
            $avatar_max_height = intval($avatar_dimensions[1]);
        } else {
            $avatar_max_width = null;
            $avatar_max_height = null;
        }

        $rows = $db->query_select('usergroups', ['*'], [], 'ORDER BY gid');
        foreach ($rows as $row) {
            if (import_check_if_imported('group', strval($row['gid']))) {
                continue;
            }

            $is_super_admin = ($row['title'] == 'Administrator') ? 1 : 0;
            $is_super_moderator = ($row['title'] == 'Universal Moderator') ? 1 : 0;

            $id_new = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_groups', 'id', [$GLOBALS['FORUM_DB']->translate_field_ref('g_name') => $row['title']]);
            if ($id_new === null) {
                $id_new = cns_make_group($row['title'], 0, $is_super_admin, $is_super_moderator, '', '', null, null, 0, null, null, null, null, null, $avatar_max_width, $avatar_max_height, null);
            }

            set_privilege($id_new, 'allow_html', true);

            if (!import_check_if_imported('group', strval($row['gid']))) {
                import_id_remap_put('group', strval($row['gid']), $id_new);
            }
        }
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $file_base The base directory we are importing from
     */
    public function import_cns_members(object $db, string $table_prefix, string $file_base)
    {
        $row_start = 0;
        $rows = [];

        do {
            $rows = $db->query('SELECT u.uid AS \'muid\',u.*,b.* FROM ' . $table_prefix . 'users u LEFT JOIN ' . $table_prefix . 'banned b ON u.uid=b.uid WHERE u.uid<>-1 ORDER BY u.uid', 200, $row_start);

            foreach ($rows as $row) {
                if (import_check_if_imported('member', strval($row['muid']))) {
                    continue;
                }

                $test = $GLOBALS['CNS_DRIVER']->get_member_from_username($row['username']);
                if ($test !== null) {
                    import_id_remap_put('member', strval($row['muid']), $test);
                    continue;
                }

                $language = '';
                if ($row['language'] != '') {
                    switch ($language) { // Can be extended as needed
                        case 'english':
                        default:
                            $language = 'EN';
                            break;
                    }
                }

                $primary_group = $row['usergroup'];
                $_secondary_groups = explode(',', $row['additionalgroups']);
                $secondary_groups = [];
                foreach ($_secondary_groups as $_sec_group) {
                    $sec_group = import_id_remap_get('group', $_sec_group, true);
                    if ($sec_group !== null) {
                        $secondary_groups[] = $sec_group;
                    }
                }

                $primary_group = import_id_remap_get('group', strval($row['usergroup']));

                if ($row['usergroup'] == 4) {
                    $secondary_groups[] = db_get_first_id() + 1;
                }

                $custom_fields = [];
                if ($row['website'] != '') {
                    $custom_fields[cns_make_predefined_content_field('website')] = (strlen($row['website']) > 0) ? ('[url]' . $row['website'] . '[/url]') : '';
                }

                $signature = $this->fix_links($row['signature'], $db, $table_prefix);
                $validated = 1;
                $reveal_age = 0;

                if ($row['birthday'] != '') {
                    $birthdate = date('Y-m-d', strtotime($row['birthday']));
                    $birthdata = array_map('intval', explode('-', $birthdate));
                    $bday_day = (!empty($birthdata[0])) ? $birthdata[0] : null;
                    $bday_month = (!empty($birthdata[1])) ? $birthdata[1] : null;
                    $bday_year = (!empty($birthdata[2])) ? $birthdata[2] : null;
                } else {
                    list($bday_day, $bday_month, $bday_year) = [null, null, null];
                }

                $views_signatures = 1;
                $preview_posts = 1;
                $track_posts = $row['allownotices'];
                $title = $row['usertitle'];
                $title = @html_entity_decode($title, ENT_QUOTES);

                // These are done in the members-files stage
                $avatar_url = '';
                $photo_url = '';

                $password = $row['password'];
                $type = 'md5';
                $salt = $row['salt'];

                $id_new = cns_make_member(
                    $row['username'], // username
                    $password, // password
                    $row['email'], // email_address
                    $primary_group, // primary_group
                    null, // secondary_groups
                    $bday_day, // dob_day
                    $bday_month, // dob_month
                    $bday_year, // dob_year
                    $custom_fields, // custom_fields
                    strval($row['timezone']), // timezone
                    '', // TODO: region
                    $language, // language
                    '', // theme
                    $title, // title
                    $photo_url, // photo_url
                    $avatar_url, // avatar_url
                    $signature, // signature
                    $preview_posts, // preview_posts
                    $reveal_age, // reveal_age
                    $views_signatures, // views_signatures
                    $track_posts, // auto_monitor_contrib_content
                    null, // smart_topic_notification
                    null, // mailing_list_style
                    1, // auto_mark_read
                    null, // sound_enabled
                    $row['receivepms'], // allow_emails
                    $row['receivepms'], // allow_emails_from_staff
                    0, // highlighted_name
                    '*', // pt_allow
                    '', // pt_rules_text
                    $validated, // validated
                    '', // validated_email_confirm_code
                    $row['lifted'], // probation_expiration_time
                    ($row['lifted'] > time()) ? '1' : '0', // is_perm_banned
                    false, // check_correctness
                    '', // ip_address
                    $type, // password_compatibility_scheme
                    $salt, // salt
                    $row['regdate'], // join_time
                    $row['lastvisit'] // last_visit_time
                );

                // Fix usergroup leadership
                $GLOBALS['FORUM_DB']->query_update('f_groups', ['g_group_lead_member' => $id_new], ['g_group_lead_member' => -$row['muid']]);

                import_id_remap_put('member', strval($row['muid']), $id_new);

                // Set up usergroup membership
                foreach ($secondary_groups as $s) {
                    cns_add_member_to_secondary_group($id_new, $s, 1);
                }
                cns_update_group_approvals($id_new, get_member());
            }

            $row_start += 200;
        } while (!empty($rows));
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $file_base The base directory we are importing from
     */
    public function import_cns_member_files(object $db, string $table_prefix, string $file_base)
    {
        global $STRICT_FILE;

        $options = $db->query('SELECT * FROM ' . $table_prefix . 'settings WHERE name LIKE \'' . db_encode_like('%avatar%') . '\' OR ' . db_string_equal_to('name', 'homeurl'));
        $options_array = [];

        $avatar_path = '';
        $avatar_gallery_path = '';

        foreach ($options as $option) {
            $options_array[$option['name']] = $option['value'];
            if ($option['name'] == 'avataruploadpath') {
                $avatar_path = preg_replace('/^\./', '', $option['value']);
            }
            if ($option['name'] == 'avatardir') {
                $avatar_gallery_path = $option['value'];
            }
            if ($option['name'] == 'homeurl') {
                $homeurl = $option['value'];
            }
        }

        $home_dir_parts = cms_parse_url_safe($homeurl);
        $forum_dir = $_SERVER['DOCUMENT_ROOT'] . urldecode($home_dir_parts['path']);

        $avatar_gallery_path = $forum_dir . '/' . $avatar_gallery_path;
        $avatar_path = preg_replace('#\.\/#', '/', $avatar_path);
        $avatar_path = $forum_dir . $avatar_path;

        $row_start = 0;
        $rows = [];
        do {
            $query = 'SELECT uid,avatar,avatardimensions,avatartype,showavatars FROM ' . $table_prefix . 'users WHERE uid<>-1 ORDER BY uid';
            $rows = $db->query($query, 200, $row_start);
            foreach ($rows as $row) {
                if (import_check_if_imported('member_files', strval($row['uid']))) {
                    continue;
                }

                $member_id = import_id_remap_get('member', strval($row['uid']));

                $avatar_url = '';
                switch ($row['avatartype']) {
                    case 'gallery': // Gallery
                        $filename = $row['avatar'];
                        $filename = preg_replace('#images\/avatars\/#', '', $filename); // We need just a filename

                        if ((file_exists(get_custom_file_base() . '/uploads/cns_avatars/' . $filename)) || (@copy($avatar_gallery_path . '/' . $filename, get_custom_file_base() . '/uploads/cns_avatars/' . $filename))) {
                            $avatar_url = 'uploads/cns_avatars/' . substr($filename, strrpos($filename, '/'));
                            sync_file(get_custom_file_base() . '/' . $avatar_url);
                        } else {
                            // Try as a pack avatar then
                            $striped_filename = str_replace('/', '_', $filename);
                            if (file_exists(get_custom_file_base() . '/uploads/cns_avatars/' . $striped_filename)) {
                                $avatar_url = 'uploads/cns_avatars/' . substr($filename, strrpos($filename, '/'));
                            } else {
                                if ($STRICT_FILE) {
                                    warn_exit(do_lang_tempcode('MISSING_AVATAR', escape_html($filename)));
                                }
                                $avatar_url = '';
                            }
                        }
                        break;

                    case 'remote': // Remote
                        $avatar_url = $row['avatar'];
                        break;

                    case 'upload': // Upload
                        $filename = $row['avatar'];
                        $filename = preg_replace('#\.\/uploads\/avatars\/#', '', $filename);
                        if ((file_exists(get_custom_file_base() . '/uploads/cns_avatars/' . $filename)) || (@copy($avatar_path . '/' . $filename, get_custom_file_base() . '/uploads/cns_avatars/' . $filename))) {
                            $avatar_url = 'uploads/cns_avatars/' . $filename;
                            sync_file(get_custom_file_base() . '/' . $avatar_url);
                        } else {
                            if ($STRICT_FILE) {
                                warn_exit(do_lang_tempcode('MISSING_AVATAR', escape_html($filename)));
                            }
                            $avatar_url = '';
                        }
                        break;
                }

                $GLOBALS['FORUM_DB']->query_update('f_members', ['m_avatar_url' => $avatar_url], ['id' => $member_id], '', 1);

                import_id_remap_put('member_files', strval($row['uid']), 1);
            }

            $row_start += 200;
        } while (!empty($rows));
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $file_base The base directory we are importing from
     */
    public function import_ip_bans(object $db, string $table_prefix, string $file_base)
    {
        $rows = $db->query_select('users u LEFT JOIN ' . $table_prefix . 'banned b ON u.uid=b.uid', ['*'], ['b.gid' => 7]);

        require_code('failure');

        foreach ($rows as $row) {
            $ban_time = $row['dateline']; // When is banned user
            $ban_period = $row['bantime']; // How many days/months/years is banned
            $perm_banned = false;

            if ($ban_period == '---') {
                // Permanently banned
                $perm_banned = true;
            } else {
                // Calculate the ban period
                $period_array = array_map('intval', explode('-', $ban_period));
                if (!empty($period_array[0])) {
                    $ban_till = $ban_time + strtotime("+ " . $period_array[0] . " day", strtotime($ban_time)); // The user is banned till this date/time
                } elseif (!empty($period_array[1])) {
                    $ban_till = $ban_time + strtotime("+ " . $period_array[1] . " month", strtotime($ban_time)); // The user is banned till this date/time
                } elseif (!empty($period_array[2])) {
                    $ban_till = $ban_time + strtotime("+ " . $period_array[2] . " year", strtotime($ban_time)); // The user is banned till this date/time
                }
            }

            $ban_till = $ban_time + $ban_period; // The user is banned till this date/time

            if (!$perm_banned) {
                continue; // Add just IPs of permanently banned users
            }

            if (import_check_if_imported('ip_ban', strval($row['uid']))) {
                continue;
            }

            add_ip_ban($row['lastip'], '', null, true, true);

            import_id_remap_put('ip_ban', strval($row['lastip']), 0);
        }
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $old_base_dir The base directory we are importing from
     */
    public function import_cns_forum_groupings(object $db, string $table_prefix, string $old_base_dir)
    {
        $rows = $db->query_select('forums', ['*'], ['type' => 'c']);
        foreach ($rows as $row) {
            if (import_check_if_imported('category', strval($row['fid']))) {
                continue;
            }

            $title = $row['name'];
            $title = @html_entity_decode($title, ENT_QUOTES);

            $test = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_forum_groupings', 'id', ['c_title' => $title]);
            if ($test !== null) {
                import_id_remap_put('category', strval($row['fid']), $test);
                continue;
            }

            $id_new = cns_make_forum_grouping($title, '', 1);

            import_id_remap_put('category', strval($row['fid']), $id_new);
        }
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $old_base_dir The base directory we are importing from
     */
    public function import_cns_forums(object $db, string $table_prefix, string $old_base_dir)
    {
        require_code('cns_forums_action2');

        $rows = $db->query_select('forums', ['*'], ['type' => 'f']);
        foreach ($rows as $row) {
            $remapped = import_id_remap_get('forum', strval($row['fid']), true);
            if ($remapped !== null) {
                continue;
            }

            $name = $row['name'];
            cns_over_msn();
            $description = html_to_comcode($row['description']);
            cns_over_local();
            $position = $row['disporder'];
            $post_count_increment = 1;

            $parentlist = explode(',', $row['parentlist']);
            $cat_id = $parentlist[0];

            $category_id = import_id_remap_get('category', $cat_id, true);
            end($parentlist);
            $prev = prev($parentlist);
            $parent_forum = ($prev != $parentlist[0]) ? intval($prev) : db_get_first_id();

            $access_mapping = [];
            if ($row['status'] == 1) {
                $permissions = $db->query_select('forumpermissions', ['*'], ['fid' => $row['fid']]);

                foreach ($permissions as $p) {
                    $v = 0;
                    if ($p['canpostthreads'] == 1) {
                        $v = 2;
                    }
                    if ($p['canpostreplys'] == 1) {
                        $v = 3;
                    }
                    if ($p['canpostpolls'] == 1) {
                        $v = 4;
                    }

                    $group_id = import_id_remap_get('group', strval($p['gid']));
                    $access_mapping[$group_id] = $v;
                }
            }

            $id_new = cns_make_forum($name, $description, $category_id, $access_mapping, $parent_forum, $position, $post_count_increment, 0, '');

            import_id_remap_put('forum', strval($row['fid']), $id_new);
        }
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $file_base The base directory we are importing from
     */
    public function import_cns_topics(object $db, string $table_prefix, string $file_base)
    {
        $row_start = 0;
        $rows = [];
        do {
            $rows = $db->query_select('threads', ['*'], ['visible' => 1], 'ORDER BY tid', 200, $row_start);
            foreach ($rows as $row) {
                if (import_check_if_imported('topic', strval($row['tid']))) {
                    continue;
                }

                $forum_id = import_id_remap_get('forum', strval($row['fid']));

                $id_new = cns_make_topic($forum_id, $row['subject'], '', 1, ($row['visible'] == 1) ? 0 : 1, 0, 0, null, null, false, $row['views']);

                import_id_remap_put('topic', strval($row['tid']), $id_new);
            }

            $row_start += 200;
        } while (!empty($rows));
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $file_base The base directory we are importing from
     */
    public function import_cns_posts(object $db, string $table_prefix, string $file_base)
    {
        global $STRICT_FILE;

        $row_start = 0;

        // Optimisation to speed through quickly, as can be slow scrolling through so many posts we may have already imported!
        do {
            $rows = $db->query('SELECT p.pid FROM ' . $table_prefix . 'posts p ORDER BY p.pid', 1, $row_start + 200 - 1);
            if ((!array_key_exists(0, $rows)) || (!import_check_if_imported('post', strval($rows[0]['pid'])))) {
                break;
            }

            $row_start += 200;
        } while (true);

        $rows = [];
        do {
            $rows = $db->query_select('posts p', ['*'], [], 'ORDER BY p.pid', 200, $row_start);
            foreach ($rows as $row) {
                if (import_check_if_imported('post', strval($row['pid']))) {
                    continue;
                }

                $topic_id = import_id_remap_get('topic', strval($row['tid']), true);
                if ($topic_id === null) {
                    import_id_remap_put('post', strval($row['pid']), -1);
                    continue;
                }
                $member_id = import_id_remap_get('member', strval($row['uid']), true);
                if ($member_id === null) {
                    $member_id = db_get_first_id();
                }

                $forum_id = import_id_remap_get('forum', strval($row['fid']), true);

                $title = '';
                $topics = $db->query_select('threads', ['subject'], ['tid' => $row['tid']]);
                $first_post = $row['dateline'];
                if ($first_post) {
                    $title = $topics[0]['subject'];
                } elseif ($row['subject'] !== null) {
                    $title = $row['subject'];
                }

                $title = @html_entity_decode($title, ENT_QUOTES);

                $post = $this->fix_links($row['message'], $db, $table_prefix);

                $last_edit_member = null;
                $last_edit_time = $row['edittime'];

                $post_username = $GLOBALS['CNS_DRIVER']->get_username($member_id);

                $id_new = cns_make_post($topic_id, $title, $post, 0, $first_post, 1, 0, $post_username, $row['ipaddress'], $row['dateline'], $member_id, null, $last_edit_time, $last_edit_member, false, false, $forum_id, false);

                import_id_remap_put('post', strval($row['pid']), $id_new);
            }

            $row_start += 200;
        } while (!empty($rows));
    }

    /**
     * Substitution callback for 'fix_links'.
     *
     * @param  array $m The match
     * @return string The substitution string
     */
    protected function _fix_links_callback_topic(array $m) : string
    {
        return 'index.php?page=topicview&id=' . strval(import_id_remap_get('topic', strval($m[2]), true));
    }

    /**
     * Substitution callback for 'fix_links'.
     *
     * @param  array $m The match
     * @return string The substitution string
     */
    protected function _fix_links_callback_post(array $m) : string
    {
        return 'index.php?page=topicview&type=findpost&id=' . strval(import_id_remap_get('post', strval($m[2]), true));
    }

    /**
     * Substitution callback for 'fix_links'.
     *
     * @param  array $m The match
     * @return string The substitution string
     */
    protected function _fix_links_callback_forum(array $m) : string
    {
        return 'index.php?page=forumview&id=' . strval(import_id_remap_get('forum', strval($m[2]), true));
    }

    /**
     * Substitution callback for 'fix_links'.
     *
     * @param  array $m The match
     * @return string The substitution string
     */
    protected function _fix_links_callback_member(array $m) : string
    {
        return 'index.php?page=members&type=view&id=' . strval(import_id_remap_get('member', strval($m[2]), true));
    }

    /**
     * Convert MyBB URLs pasted in text fields into software ones.
     *
     * @param  string $post The text field text (e.g. a post)
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @return string The new text field text
     */
    public function fix_links(string $post, object $db, string $table_prefix) : string
    {
        $options = $db->query_select('settings', ['*'], ['name' => 'bburl']);

        $OLD_BASE_URL = empty($options[0]['value']) ? '' : $options[0]['value'];

        $post = preg_replace_callback('#' . preg_quote($OLD_BASE_URL) . '/(showthread\.php\?tid=)(\d*)#', [$this, '_fix_links_callback_topic'], $post);
        $post = preg_replace_callback('#' . preg_quote($OLD_BASE_URL) . '/(forumdisplay\.php\?fid=)(\d*)#', [$this, '_fix_links_callback_forum'], $post);
        $post = preg_replace_callback('#' . preg_quote($OLD_BASE_URL) . '/(member\.php\?action=profile\&uid=)(\d*)#', [$this, '_fix_links_callback_member'], $post);
        $post = preg_replace('#:[0-9a-f]{10}#', '', $post);
        return $post;
    }

    /**
     * Convert a MyBB database file to a software uploaded file (stored on disk).
     *
     * @param  string $data The file data
     * @param  string $filename The optimal filename
     * @param  ID_TEXT $sections The upload type (e.g. cns_photos)
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  string $output_filename The filename to output to
     * @return URLPATH The URL
     */
    public function data_to_disk(string $data, string $filename, string $sections, object $db, string $table_prefix = '', string $output_filename = '') : string
    {
        $options = $db->query_select('settings', ['*'], ['name' => 'bburl']);

        $homeurl = empty($options[0]['value']) ? '' : $options[0]['value'];

        $home_dir_parts = cms_parse_url_safe($homeurl);
        $forum_dir = $_SERVER['DOCUMENT_ROOT'] . urldecode($home_dir_parts['path']);

        $attachments_dir = $forum_dir . '/uploads/'; // Forum attachments directory
        $file_path = $attachments_dir . $filename;
        if ($data == '') {
            if (file_exists($file_path)) {
                $data = cms_file_get_contents_safe($file_path, FILE_READ_LOCK);
            }
        }

        $filename = ($output_filename == '') ? preg_replace('#.*\/#', '', $filename) : $output_filename;

        require_code('files');
        list($path, $url) = find_unique_path('uploads/' . $sections, $filename);
        cms_file_put_contents_safe($path, $data, FILE_WRITE_FIX_PERMISSIONS | FILE_WRITE_SYNC_FILE);

        return $url;
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $file_base The base directory we are importing from
     */
    public function import_cns_post_files(object $db, string $table_prefix, string $file_base)
    {
        global $STRICT_FILE;
        require_code('attachments2');
        require_code('attachments3');

        $row_start = 0;
        $rows = [];
        do {
            $rows = $db->query_select('attachments', ['*'], [], 'ORDER BY aid', 200, $row_start);
            foreach ($rows as $row) {
                if (import_check_if_imported('post_files', strval($row['aid']))) {
                    continue;
                }

                $post_id = import_id_remap_get('post', strval($row['pid']));

                $post_row = $GLOBALS['FORUM_DB']->query_select('f_posts', ['p_time', 'p_posting_member', 'p_post'], ['id' => $post_id], '', 1);
                if (!array_key_exists(0, $post_row)) {
                    import_id_remap_put('post_files', strval($row['aid']), 1);
                    continue; // Orphaned post
                }
                $post = get_translated_text($post_row[0]['p_post']);
                $member_id = $post_row[0]['p_posting_member'];

                $url = $this->data_to_disk('', $row['attachname'], 'attachments', $db, $table_prefix, $row['filename']);
                $thumb_url = $this->data_to_disk('', $row['thumbnail'], 'attachments_thumbs', $db, $table_prefix, $row['filename']);
                $attachment_map = ['a_member_id' => $member_id, 'a_file_size' => $row['filesize'], 'a_url' => $url, 'a_thumb_url' => $thumb_url, 'a_original_filename' => $row['filename'], 'a_num_downloads' => $row['downloads'], 'a_last_downloaded_time' => null, 'a_add_time' => $row['dateuploaded'], 'a_description' => ''];
                $a_id = $GLOBALS['FORUM_DB']->query_insert('attachments', $attachment_map, true);
                $GLOBALS['FORUM_DB']->query_insert('attachment_refs', ['r_referer_type' => 'cns_post', 'r_referer_id' => strval($post_id), 'a_id' => $a_id]);
                $post .= "\n\n" . '[attachment]' . strval($a_id) . '[/attachment]';

                cns_over_msn();
                $GLOBALS['FORUM_DB']->query_update('f_posts', update_lang_comcode_attachments('p_post', $post_row[0]['p_post'], $post, 'cns_post', strval($post_id)), ['id' => $post_id], '', 1);
                cns_over_local();

                import_id_remap_put('post_files', strval($row['aid']), 1);
            }

            $row_start += 200;
        } while (!empty($rows));
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $file_base The base directory we are importing from
     */
    public function import_cns_polls_and_votes(object $db, string $table_prefix, string $file_base)
    {
        $rows = $db->query_select('polls');
        foreach ($rows as $row) {
            if (import_check_if_imported('poll', strval($row['pid']))) {
                continue;
            }

            $topic_id = import_id_remap_get('topic', strval($row['tid']), true);
            if ($topic_id === null) {
                import_id_remap_put('poll', strval($row['pid']), -1);
                continue;
            }

            $is_open = 1 - $row['closed'];

            $answers = [];
            $answers_array = explode('||~|~||', $row['options']);

            $answer_map = [];
            foreach ($answers_array as $key => $answer) {
                $answer_map[$key + 1] = count($answers);
                $answers[] = $answer;
            }
            $maximum = count($answers);

            $rows2 = $db->query_select('pollvotes', ['*'], ['pid' => $row['pid']]);
            foreach ($rows2 as $row2) {
                $row2['uid'] = import_id_remap_get('member', strval($row2['uid']), true);
            }

            $id_new = cns_make_poll($topic_id, $row['question'], 0, $is_open, 1, $maximum, 0, $answers, 0, 0, 1, 0, false);

            $answers = collapse_1d_complexity('id', $GLOBALS['FORUM_DB']->query_select('f_poll_answers', ['id'], ['pa_poll_id' => $id_new]));

            foreach ($rows2 as $row2) {
                $member_id = $row2['uid'];
                if (!empty($member_id)) {
                    if ($row2['voteoption'] == 0) {
                        $answer = -1;
                    } else {
                        $answer = $answers[$answer_map[$row2['voteoption']]];
                    }
                    $GLOBALS['FORUM_DB']->query_insert('f_poll_votes', ['pv_poll_id' => $id_new, 'pv_member_id' => $member_id, 'pv_answer_id' => $answer, 'pv_ip_address' => '', 'pv_revoked' => 0, 'pv_date_time' => $row2['dateline'], 'pv_points_when_voted' => 0]);
                }
            }

            import_id_remap_put('poll', strval($row['pid']), $id_new);
        }
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $old_base_dir The base directory we are importing from
     */
    public function import_cns_private_topics(object $db, string $table_prefix, string $old_base_dir)
    {
        $rows = $db->query_select('privatemessages p', ['*'], [], 'ORDER BY dateline');

        // Group them up into what will become topics
        $groups = [];
        foreach ($rows as $row) {
            // Do some fiddling around for duplication
            if ($row['fromid'] > $row['toid']) {
                $a = $row['toid'];
                $b = $row['fromid'];
            } else {
                $a = $row['fromid'];
                $b = $row['toid'];
            }
            $row['subject'] = str_replace('Re: ', '', $row['subject']);
            $groups[strval($a) . ':' . strval($b) . ':' . $row['subject']][] = $row;
        }

        // Import topics
        foreach ($groups as $group) {
            $row = $group[0];

            if (import_check_if_imported('pt', strval($row['pmid']))) {
                continue;
            }

            // Create topic
            $from_id = import_id_remap_get('member', strval($row['fromid']), true);
            if ($from_id === null) {
                $from_id = $GLOBALS['CNS_DRIVER']->get_guest_id();
            }
            $to_id = import_id_remap_get('member', strval($row['toid']), true);
            if ($to_id === null) {
                $to_id = $GLOBALS['CNS_DRIVER']->get_guest_id();
            }
            $topic_id = cns_make_topic(null, '', '', 1, 1, 0, 0, $from_id, $to_id, false);

            $first_post = true;
            foreach ($group as $_post) {
                if ($first_post) {
                    $title = $row['subject'];
                } else {
                    $title = '';
                }

                $title = @html_entity_decode($title, ENT_QUOTES);

                $post = $this->fix_links($_post['message'], $db, $table_prefix);
                $validated = 1;
                $from_id = import_id_remap_get('member', strval($_post['fromid']), true);
                if ($from_id === null) {
                    $from_id = $GLOBALS['CNS_DRIVER']->get_guest_id();
                }
                $poster_name_if_guest = $GLOBALS['CNS_DRIVER']->get_username($from_id);
                $ip_address = '';
                $time = $_post['dateline'];
                $poster = $from_id;
                $last_edit_time = null;
                $last_edit_member = null;

                cns_make_post($topic_id, $title, $post, 0, $first_post, $validated, 0, $poster_name_if_guest, $ip_address, $time, $poster, null, $last_edit_time, $last_edit_member, false, false, null, false);
                $first_post = false;
            }

            import_id_remap_put('pt', strval($row['pmid']), $topic_id);
        }
    }

    /**
     * Convert a MyBB topic icon code into a standard software theme image code.
     *
     * @param  integer $iconid VB code
     * @return ID_TEXT Software code
     */
    public function convert_topic_emoticon(int $iconid) : string
    {
        switch ($iconid) {
            case 1:
                return 'cns_emoticons/smile';
            case 2:
                return 'cns_emoticons/wink';
            case 3:
                return 'images/smilies/cool.gif';
            case 4:
                return 'cns_emoticons/grin';
            case 5:
                return 'cns_emoticons/cheeky';
            case 6:
                return 'cns_emoticons/rolleyes';
            case 7:
                return 'cns_emoticons/nerd';
            case 8:
                return 'cns_emoticons/sad';
            case 11:
                return 'cns_emoticons/angry';
            case 12:
                return 'cns_emoticons/blush';
            case 13:
                return 'cns_emoticons/confused';
            case 19:
                return 'cns_emoticons/zzz';
        }
        return '';
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $file_base The base directory we are importing from
     */
    public function import_notifications(object $db, string $table_prefix, string $file_base)
    {
        require_code('notifications');

        $row_start = 0;
        $rows = [];
        do {
            $rows = $db->query_select('threadsubscriptions', ['*'], [], '', 200, $row_start);
            foreach ($rows as $row) {
                if (import_check_if_imported('topic_notification', strval($row['tid']) . '-' . strval($row['uid']))) {
                    continue;
                }

                $member_id = import_id_remap_get('member', strval($row['uid']), true);
                if ($member_id === null) {
                    continue;
                }
                $topic_id = import_id_remap_get('topic', strval($row['tid']), true);
                if ($topic_id === null) {
                    continue;
                }
                set_notifications('cns_topic', strval($topic_id), $member_id);

                import_id_remap_put('topic_notification', strval($row['tid']) . '-' . strval($row['uid']), 1);
            }

            $row_start += 200;
        } while (!empty($rows));
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $file_base The base directory we are importing from
     */
    public function import_wordfilter(object $db, string $table_prefix, string $file_base)
    {
        $rows = $db->query_select('badwords');
        foreach ($rows as $row) {
            add_wordfilter_word($row['badword'], $row['replacement']);
        }
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $file_base The base directory we are importing from
     */
    public function import_custom_comcode(object $db, string $table_prefix, string $file_base)
    {
        require_code('custom_comcode');
        require_code('comcode_compiler');

        init_valid_comcode_tags();

        $rows = $db->query_select('mycode');
        foreach ($rows as $row) {
            if (import_check_if_imported('custom_comcode', strval($row['cid']))) {
                continue;
            }

            $matches = [];
            preg_match('#\[(.*?)\\\]#', $row['regex'], $matches);
            $custom_tag = empty($matches[1]) ? '' : $matches[1];
            $title = $row['title'];
            $description = $row['description'];

            $parameter = '';
            $tag_replace = preg_replace('#\$1#', '{content}', $row['replacement']);
            $tag_enabled = $row['active'];

            global $VALID_COMCODE_TAGS;
            $test = $GLOBALS['SITE_DB']->query_select_value_if_there('custom_comcode', 'tag_tag', ['tag_tag' => $custom_tag]);
            if ((array_key_exists($custom_tag, $VALID_COMCODE_TAGS)) || ($test !== null)) {
                import_id_remap_put('custom_comcode', strval($row['cid']), 1);
                continue;
            }

            $tag = $custom_tag;
            $replace = $tag_replace;
            $example = '';
            $parameters = '';
            $enabled = $tag_enabled;
            $dangerous_tag = 0;
            $block_tag = 0;
            $textual_tag = 1;

            add_custom_comcode_tag($tag, $title, $description, $replace, $example, $parameters, $enabled, $dangerous_tag, $block_tag, $textual_tag);

            import_id_remap_put('custom_comcode', strval($row['cid']), 1);
        }
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $file_base The base directory we are importing from
     */
    public function import_cns_custom_profile_fields(object $db, string $table_prefix, string $file_base)
    {
        $rows = $db->query_select('profilefields', ['*']);
        $members = $db->query_select('userfields', ['*']);
        foreach ($rows as $row) {
            if (import_check_if_imported('cpf', strval($row['fid']))) {
                continue;
            }

            $type = 'short_text';
            if ($row['type'] == 'text') {
                $type = 'short_text';
            } elseif ($row['type'] == 'textarea') {
                $type = 'long_text';
            }

            $id_new = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_custom_fields', 'id', [$GLOBALS['FORUM_DB']->translate_field_ref('cf_name') => $row['name']]);
            if ($id_new === null) {
                $id_new = cns_make_custom_field($row['name'], 0, $row['description'], '', 1 - $row['hidden'], 1 - $row['hidden'], $row['editable'], 0, $type, $row['required'], 0, 0, $row['disporder'], '', 0, '', 0, 0, '', '', '', true);
            }

            foreach ($members as $member) {
                $v = $member['fid' . strval($row['fid'])];
                $member_id = import_id_remap_get('member', strval($member['ufid']), true);
                if (($v != '') && ($member_id !== null)) {
                    cns_set_custom_field($member_id, $id_new, $v);
                }
            }

            import_id_remap_put('cpf', strval($row['fid']), $id_new);
        }
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $file_base The base directory we are importing from
     */
    public function import_calendar(object $db, string $table_prefix, string $file_base)
    {
        require_code('calendar2');

        $rows = $db->query_select('events');
        foreach ($rows as $row) {
            if (import_check_if_imported('event', strval($row['eid']))) {
                continue;
            }

            $submitter = import_id_remap_get('member', strval($row['uid']), true);
            if ($submitter === null) {
                $submitter = $GLOBALS['CNS_DRIVER']->get_guest_id();
            }

            $recurrence = 'none';
            $recurrences = null;

            $event_repeat_data = unserialize($row['repeats']);
            $event_repeats = (!empty($event_repeat_data['repeats'])) ? $event_repeat_data['repeats'] : 0;

            switch ($event_repeats) {
                case 1:
                    $week_array = [0, 0, 0, 0, 0, 0, 0];
                    $start_day_num = intval(date('w', $row['starttime']));
                    $every_days = $event_repeat_data['days'];

                    $week_array[$start_day_num] = 1;
                    $next_day = $start_day_num;
                    for ($i = 1; $i < 7; $i++) {
                        $next_day += $every_days;
                        if ($next_day > 6) {
                            $next_day -= 6;
                        }
                        $week_array[$next_day] = 1;
                    }

                    $recurrence = 'daily ' . implode('', $week_array);
                    break;
                case 2:
                    $recurrence = 'daily 1111100';
                    break;
                case 3:
                    $week_array = [0, 0, 0, 0, 0, 0, 0];
                    $week_array[0] = empty($event_repeat_data['days'][0]) ? 1 : 0;
                    $week_array[1] = empty($event_repeat_data['days'][1]) ? 1 : 0;
                    $week_array[2] = empty($event_repeat_data['days'][2]) ? 1 : 0;
                    $week_array[3] = empty($event_repeat_data['days'][3]) ? 1 : 0;
                    $week_array[4] = empty($event_repeat_data['days'][4]) ? 1 : 0;
                    $week_array[5] = empty($event_repeat_data['days'][5]) ? 1 : 0;
                    $week_array[6] = empty($event_repeat_data['days'][6]) ? 1 : 0;

                    $recurrence = 'daily ' . implode('', $week_array);
                    break;
                case 4:
                    $pattern = '1';
                    $repeat_every_n_years = $event_repeat_data['months'];
                    for ($i = 1; $i < $repeat_every_n_years; $i++) {
                        $pattern .= '0';
                    }

                    $recurrence = 'monthly ' . $pattern;
                    break;
                case 5:
                    $pattern = '1';
                    $repeat_every_n_years = $event_repeat_data['years'];
                    for ($i = 1; $i < $repeat_every_n_years; $i++) {
                        $pattern .= '0';
                    }

                    $recurrence = 'yearly ' . $pattern;
                    break;
            }

            list($start_year, $start_month, $start_day, $start_hour, $start_minute) = explode('-', date('Y-m-d-h-i', strtotime($row['starttime'])));
            list($end_year, $end_month, $end_day, $end_hour, $end_minute) = explode('-', date('Y-m-d-h-i', strtotime($row['endtime'])));
            cns_over_msn();
            $id_new = add_calendar_event(db_get_first_id() + 1, $recurrence, $recurrences, 0, $row['name'], $row['description'], 3, $start_year, $start_month, $start_day, 'day_of_month', $start_hour, $start_minute, $end_year, $end_month, $end_day, 'day_of_month', $end_hour, $end_minute, null, 1, null, 1, 1, 1, 1, '', $submitter, 0, $row['dateline']);
            cns_over_local();
            if ($row['visible'] == 0) {
                if (addon_installed('content_privacy')) {
                    $GLOBALS['SITE_DB']->query_insert('content_privacy', [
                        'content_type' => 'event',
                        'content_id' => strval($id_new),
                        'guest_view' => 0,
                        'member_view' => 0,
                        'friend_view' => 0,
                    ]);
                }
            }

            import_id_remap_put('event', strval($row['eid']), $id_new);
        }
    }

    /**
     * Standard import function.
     *
     * @param  object $db The database connector to import from
     * @param  string $table_prefix The table prefix the target prefix is using
     * @param  PATH $file_base The base directory we are importing from
     */
    public function import_cns_multi_moderations(object $db, string $table_prefix, string $file_base)
    {
        $rows = $db->query_select('modtools', ['*'], ['type' => 't']);
        foreach ($rows as $row) {
            $mm_forum_multi_code = (!cms_empty_safe($row['forums'])) ? ('+' . $row['forums']) : '*'; //'mm_forum_multi_code'
            $mm_name = $row['name'];

            $topic_options = unserialize($row['threadoptions']);

            $mm_open_state = ($topic_options['openthread'] == 'open') ? 1 : (($topic_options['openthread'] == 'close') ? 0 : null);
            $mm_move_to_forum_id = (!empty($topic_options['movethread'])) ? $topic_options['movethread'] : null;

            $mm_title_suffix = ($topic_options['newsubject'] != '{subject}') ? preg_replace('#\{username\}#', '', $topic_options['newsubject']) : '';

            $mm_post_text = ($topic_options['replysubject'] != '{subject}') ? preg_replace('#\{username\}#', '', $topic_options['replysubject']) : '';
            $mm_post_text .= ($topic_options['addreply'] != '') ? preg_replace('#\{username\}#', '', $topic_options['addreply']) : '';
            $map = [
                'mm_forum_multi_code' => $mm_forum_multi_code,
                'mm_open_state' => $mm_open_state,
                'mm_move_to_forum_id' => $mm_move_to_forum_id,
                'mm_title_suffix' => $mm_title_suffix,
                'mm_post_text' => $mm_post_text,
            ];
            $map += insert_lang('mm_name', $mm_name, 3, $GLOBALS['FORUM_DB']);
            $GLOBALS['FORUM_DB']->query_insert('f_multi_moderations', $map);
        }
    }
}
