/**
 * Edit a forum.
 *
 * @param  AUTO_LINK		The ID of the forum we are editing.
 * @param  SHORT_TEXT	The name of the forum.
 * @param  SHORT_TEXT	The description for the forum.
 * @param  AUTO_LINK		What forum category the forum will be filed with.
 * @param  ?AUTO_LINK	The ID of the parent forum (NULL: this is the root forum).
 * @param  integer		The position of this forum relative to other forums viewable on the same screen (if parent forum hasn't specified automatic ordering).
 * @param  BINARY			Whether post counts will be incremented if members post in the forum.
 * @param  BINARY			Whether the ordering of subforums is done automatically, alphabetically).
 * @param  LONG_TEXT		The question that is shown for newbies to the forum (blank: none).
 * @param  SHORT_TEXT	The answer to the question (blank: no specific answer.. if there's a 'question', it just requires a click-through).
 * @param  SHORT_TEXT	Either blank for no redirection, the ID of another forum we are mirroring, or a URL to redirect to.
 * @param  ID_TEXT		The order the topics are shown in, by default.
 * @param  BINARY			Whether the forum is threaded.
 * @param  boolean		Whether to force forum rules to be re-agreed to, if they've just been changed.
 */
function ocf_edit_forum($forum_id, $name, $description, $category_id, $new_parent, $position, $post_count_increment, $order_sub_alpha, $intro_question, $intro_answer, $redirection = '', $order = 'last_post', $is_threaded = 0, $reset_intro_acceptance = false)
{
    if ($category_id == -1) {
        $category_id = NULL;
    }
    if ($new_parent == -1) {
        $new_parent = NULL;
    }
    require_code('urls2');
    suggest_new_idmoniker_for('forumview', 'misc', strval($forum_id), $name);
    if (!is_null($category_id) && $category_id != INTEGER_MAGIC_NULL) {
        ocf_ensure_category_exists($category_id);
    }
    if (!is_null($new_parent) && $new_parent != INTEGER_MAGIC_NULL) {
        ocf_ensure_forum_exists($new_parent);
    }
    $forum_info = $GLOBALS['FORUM_DB']->query_select('f_forums', array('*'), array('id' => $forum_id), '', 1);
    if (!array_key_exists(0, $forum_info)) {
        warn_exit(do_lang_tempcode('MISSING_RESOURCE'));
    }
    $old_parent = $forum_info[0]['f_parent_forum'];
    $old_name = $forum_info[0]['f_name'];
    $under_category_id = $new_parent;
    while (!is_null($under_category_id) && $under_category_id != INTEGER_MAGIC_NULL) {
        if ($forum_id == $under_category_id) {
            warn_exit(do_lang_tempcode('FORUM_CANNOT_BE_OWN_PARENT'));
        }
        $under_category_id = $GLOBALS['FORUM_DB']->query_value_null_ok('f_forums', 'f_parent_forum', array('id' => $under_category_id));
    }
    if ($reset_intro_acceptance && trim(get_translated_text($forum_info[0]['f_intro_question'], $GLOBALS['FORUM_DB'])) != trim($intro_question) && $intro_question != STRING_MAGIC_NULL) {
        $GLOBALS['FORUM_DB']->query_delete('f_forum_intro_ip', array('i_forum_id' => $forum_id));
        $GLOBALS['FORUM_DB']->query_delete('f_forum_intro_member', array('i_forum_id' => $forum_id));
    }
    $GLOBALS['FORUM_DB']->query_update('f_forums', array('f_name' => $name, 'f_description' => lang_remap($forum_info[0]['f_description'], $description, $GLOBALS['FORUM_DB']), 'f_category_id' => $category_id, 'f_parent_forum' => $new_parent, 'f_position' => $position, 'f_order_sub_alpha' => $order_sub_alpha, 'f_intro_question' => lang_remap($forum_info[0]['f_intro_question'], $intro_question, $GLOBALS['FORUM_DB']), 'f_intro_answer' => $intro_answer, 'f_post_count_increment' => $post_count_increment, 'f_redirection' => $redirection, 'f_order' => $order, 'f_is_threaded' => $is_threaded), array('id' => $forum_id), '', 1);
    if ($old_name != $name) {
        $test = $GLOBALS['FORUM_DB']->query_value_null_ok('f_forums', 'f_name', array('f_name' => $old_name));
        if (is_null($test)) {
            require_code('config2');
            update_config_option_reference($old_name, $name, 'forum');
        }
    }
    if ($old_parent != $new_parent && $new_parent != INTEGER_MAGIC_NULL) {
        // Recalc stats
        require_code('ocf_posts_action2');
        $num_topics_forum = $forum_info[0]['f_cache_num_topics'];
        // This is valid, because we move all this forums subforums too
        $num_posts_forum = $forum_info[0]['f_cache_num_posts'];
        if (!is_null($old_parent)) {
            ocf_force_update_forum_cacheing($old_parent, -$num_topics_forum, -$num_posts_forum);
        }
        if (!is_null($new_parent)) {
            ocf_force_update_forum_cacheing($new_parent, $num_topics_forum, $num_posts_forum);
        }
    }
    log_it('EDIT_FORUM', strval($forum_id), $name);
}
/**
 * Make a forum.
 *
 * @param  SHORT_TEXT 	The name of the forum.
 * @param  SHORT_TEXT 	The description for the forum.
 * @param  ?AUTO_LINK	What forum category the forum will be filed with (NULL: this is the root forum).
 * @param  ?array			Permission map (NULL: do it the standard way, outside of this function). This parameter is for import/compatibility only and works upon an emulation of 'access levels' (ala ocPortal 2.5/2.6), and it is recommended to use the normal aed_module functionality for permissions setting.
 * @param  ?AUTO_LINK 	The ID of the parent forum (NULL: this is the root forum).
 * @param  integer		The position of this forum relative to other forums viewable on the same screen (if parent forum hasn't specified automatic ordering).
 * @param  BINARY			Whether post counts will be incremented if members post in the forum.
 * @param  BINARY			Whether the ordering of subforums is done automatically, alphabetically).
 * @param  LONG_TEXT		The question that is shown for newbies to the forum (blank: none).
 * @param  SHORT_TEXT	The answer to the question (blank: no specific answer.. if there's a 'question', it just requires a click-through).
 * @param  SHORT_TEXT	Either blank for no redirection, the ID of another forum we are mirroring, or a URL to redirect to.
 * @param  ID_TEXT		The order the topics are shown in, by default.
 * @param  BINARY			Whether the forum is threaded.
 * @return AUTO_LINK		The ID of the newly created forum.
 */
function ocf_make_forum($name, $description, $category_id, $access_mapping, $parent_forum, $position = 1, $post_count_increment = 1, $order_sub_alpha = 0, $intro_question = '', $intro_answer = '', $redirection = '', $order = 'last_post', $is_threaded = 0)
{
    if ($category_id == -1) {
        $category_id = NULL;
    }
    if ($parent_forum == -1) {
        $parent_forum = NULL;
    }
    if (get_page_name() != 'admin_import') {
        if (!is_null($category_id) && function_exists('ocf_ensure_category_exists')) {
            ocf_ensure_category_exists($category_id);
        }
        if (!is_null($parent_forum) && function_exists('ocf_ensure_forum_exists')) {
            ocf_ensure_forum_exists($parent_forum);
        }
    }
    $forum_id = $GLOBALS['FORUM_DB']->query_insert('f_forums', array('f_name' => $name, 'f_description' => insert_lang($description, 2, $GLOBALS['FORUM_DB']), 'f_category_id' => $category_id, 'f_parent_forum' => $parent_forum, 'f_position' => $position, 'f_order_sub_alpha' => $order_sub_alpha, 'f_post_count_increment' => $post_count_increment, 'f_intro_question' => insert_lang($intro_question, 3, $GLOBALS['FORUM_DB']), 'f_intro_answer' => $intro_answer, 'f_cache_num_topics' => 0, 'f_cache_num_posts' => 0, 'f_cache_last_topic_id' => NULL, 'f_cache_last_forum_id' => NULL, 'f_cache_last_title' => '', 'f_cache_last_time' => NULL, 'f_cache_last_username' => '', 'f_cache_last_member_id' => NULL, 'f_redirection' => $redirection, 'f_order' => $order, 'f_is_threaded' => $is_threaded), true);
    // Set permissions
    if (!is_null($access_mapping)) {
        $groups = $GLOBALS['OCF_DRIVER']->get_usergroup_list(false, true);
        foreach (array_keys($groups) as $group_id) {
            $level = 0;
            // No-access
            if (array_key_exists($group_id, $access_mapping)) {
                $level = $access_mapping[$group_id];
            }
            if ($level >= 1) {
                $GLOBALS['FORUM_DB']->query_insert('group_category_access', array('module_the_name' => 'forums', 'category_name' => strval($forum_id), 'group_id' => $group_id));
                if ($level == 1) {
                    $GLOBALS['FORUM_DB']->query_insert('gsp', array('specific_permission' => 'submit_lowrange_content', 'group_id' => $group_id, 'the_page' => '', 'module_the_name' => 'forums', 'category_name' => strval($forum_id), 'the_value' => 0));
                    $GLOBALS['FORUM_DB']->query_insert('gsp', array('specific_permission' => 'submit_midrange_content', 'group_id' => $group_id, 'the_page' => '', 'module_the_name' => 'forums', 'category_name' => strval($forum_id), 'the_value' => 0));
                }
                if ($level >= 3) {
                    $GLOBALS['FORUM_DB']->query_insert('gsp', array('specific_permission' => 'bypass_validation_lowrange_content', 'group_id' => $group_id, 'the_page' => '', 'module_the_name' => 'forums', 'category_name' => strval($forum_id), 'the_value' => 1));
                }
                if ($level >= 4) {
                    $GLOBALS['FORUM_DB']->query_insert('gsp', array('specific_permission' => 'bypass_validation_midrange_content', 'group_id' => $group_id, 'the_page' => '', 'module_the_name' => 'forums', 'category_name' => strval($forum_id), 'the_value' => 1));
                }
                // 2=May post, [3=May post instantly , 4=May start topics instantly , 5=Moderator  --  these ones will not be treated specially, so as to avoid overriding permissions unnecessary - let the admins configure it optimally manually]
            }
        }
    }
    log_it('ADD_FORUM', strval($forum_id), $name);
    return $forum_id;
}
/**
 * Move some topics.
 *
 * @param  AUTO_LINK		The forum the topics are currently in.
 * @param  AUTO_LINK		The forum the topics are being moved to.
 * @param  ?array 		A list of the topic IDs to move (NULL: move all topics from source forum).
 */
function ocf_move_topics($from, $to, $topics = NULL)
{
    if ($from == $to) {
        return;
    }
    // That would be nuts, and interfere with our logic
    require_code('notifications');
    require_code('ocf_topics');
    require_code('ocf_forums_action2');
    $forum_name = ocf_ensure_forum_exists($to);
    if (!ocf_may_moderate_forum($from)) {
        access_denied('I_ERROR');
    }
    $topic_count = 0;
    if (is_null($topics)) {
        if (is_null($from)) {
            access_denied('I_ERROR');
        }
        $all_topics = $GLOBALS['FORUM_DB']->query_select('f_topics', array('id', 't_cache_num_posts', 't_validated'), array('t_forum_id' => $from));
        $or_list = '';
        $post_count = 0;
        $topics = array();
        foreach ($all_topics as $topic_info) {
            $topics[] = $topic_info['id'];
            if ($or_list != '') {
                $or_list .= ' OR ';
            }
            $or_list .= 'id=' . strval((int) $topic_info['id']);
            $post_count += $topic_info['t_cache_num_posts'];
            if ($topic_info['t_validated'] == 1) {
                $topic_count++;
            }
        }
        $GLOBALS['FORUM_DB']->query_update('f_topics', array('t_forum_id' => $to), array('t_forum_id' => $from));
        // Update forum IDs' for posts
        $GLOBALS['FORUM_DB']->query_update('f_posts', array('p_cache_forum_id' => $to), array('p_cache_forum_id' => $from));
        $or_list_2 = str_replace('id', 'p_topic_id', $or_list);
        if ($or_list_2 == '') {
            return;
        }
    } elseif (count($topics) == 1) {
        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('t_forum_id', 't_pt_from', 't_pt_to', 't_cache_first_title', 't_cache_num_posts', 't_validated'), array('id' => $topics[0]));
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE'));
        }
        if ($topic_info[0]['t_forum_id'] != $from || $topic_info[0]['t_pt_from'] != get_member() && $topic_info[0]['t_pt_to'] != get_member() && !ocf_has_special_pt_access($topics[0]) && !has_specific_permission(get_member(), 'view_other_pt') && is_null($topic_info[0]['t_forum_id'])) {
            access_denied('I_ERROR');
        }
        if ($topic_info[0]['t_validated'] == 1) {
            $topic_count++;
        }
        $topic_title = $topic_info[0]['t_cache_first_title'];
        $post_count = $topic_info[0]['t_cache_num_posts'];
        $GLOBALS['FORUM_DB']->query_update('f_topics', array('t_pt_from' => NULL, 't_pt_to' => NULL, 't_forum_id' => $to), array('t_forum_id' => $from, 'id' => $topics[0]), '', 1);
        // Extra where constraint for added security
        log_it('MOVE_TOPICS', $topic_title, strval($topics[0]));
        $or_list = 'id=' . strval($topics[0]);
        $or_list_2 = 'p_topic_id=' . strval($topics[0]);
        // Update forum IDs' for posts
        $GLOBALS['FORUM_DB']->query_update('f_posts', array('p_cache_forum_id' => $to), array('p_topic_id' => $topics[0]));
    } else {
        if (count($topics) == 0) {
            return;
        }
        // Nuts, lol
        $or_list = '';
        foreach ($topics as $topic_id) {
            if ($or_list != '') {
                $or_list .= ' OR ';
            }
            $or_list .= 'id=' . strval((int) $topic_id);
            if (is_null($from)) {
                $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('t_forum_id', 't_pt_from', 't_pt_to'), array('id' => $topic_id));
                if (array_key_exists(0, $topic_info)) {
                    if ($topic_info[0]['t_validated'] == 1) {
                        $topic_count++;
                    }
                    if ($topic_info[0]['t_forum_id'] != $from || $topic_info[0]['t_pt_from'] != get_member() && $topic_info[0]['t_pt_to'] != get_member() && !ocf_has_special_pt_access($topic_id) && !has_specific_permission(get_member(), 'view_other_pt')) {
                        access_denied('I_ERROR');
                    }
                }
            } else {
                $topic_count++;
                // Might not be validated, which means technically we shouldn't do this, but it's low chance, low impact, and the indicator is only a cache thing anyway
            }
        }
        $GLOBALS['FORUM_DB']->query('UPDATE ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_topics SET t_forum_id=' . strval((int) $to) . ',t_pt_from=NULL,t_pt_to=NULL WHERE t_forum_id' . (is_null($from) ? ' IS NULL' : '=' . strval((int) $from)) . ' AND (' . $or_list . ')');
        log_it('MOVE_TOPICS', do_lang('MULTIPLE'));
        $post_count = $GLOBALS['FORUM_DB']->query_value_null_ok_full('SELECT SUM(t_cache_num_posts) FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_topics WHERE ' . $or_list);
        // Update forum IDs' for posts
        $or_list_2 = str_replace('id', 'p_topic_id', $or_list);
        $GLOBALS['FORUM_DB']->query('UPDATE ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_posts SET p_cache_forum_id=' . strval((int) $to) . ' WHERE ' . $or_list_2);
    }
    require_code('ocf_posts_action2');
    // Update source forum cache view
    if (!is_null($from)) {
        ocf_force_update_forum_cacheing($from, -$topic_count, -$post_count);
    }
    // Update dest forum cache view
    ocf_force_update_forum_cacheing($to, $topic_count, $post_count);
    if (!is_null($from)) {
        // Update member post counts if we've switched between post-count countable forums
        $post_count_info = $GLOBALS['FORUM_DB']->query('SELECT id,f_post_count_increment FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_forums WHERE id=' . strval((int) $from) . ' OR id=' . strval((int) $to), 2);
        if ($post_count_info[0]['id'] == $from) {
            $from_cnt = $post_count_info[0]['f_post_count_increment'];
            $to_cnt = $post_count_info[1]['f_post_count_increment'];
        } else {
            $from_cnt = $post_count_info[1]['f_post_count_increment'];
            $to_cnt = $post_count_info[0]['f_post_count_increment'];
        }
        require_code('ocf_posts_action');
        if ($from_cnt != $to_cnt) {
            $sql = 'SELECT p_poster FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_posts WHERE (' . $or_list_2 . ')';
            if (addon_installed('unvalidated')) {
                $sql .= ' AND p_validated=1';
            }
            $_member_post_counts = collapse_1d_complexity('p_poster', $GLOBALS['FORUM_DB']->query($sql));
            $member_post_counts = array_count_values($_member_post_counts);
            foreach ($member_post_counts as $member_id => $member_post_count) {
                if ($to == 0) {
                    $member_post_count = -$member_post_count;
                }
                ocf_force_update_member_post_count($member_id, $member_post_count);
            }
        }
    }
    require_code('ocf_posts_action');
    if (!is_null($from)) {
        ocf_decache_ocp_blocks($from);
    } else {
        decache('side_ocf_personal_topics');
        decache('_new_pp');
    }
    ocf_decache_ocp_blocks($to, $forum_name);
    if (function_exists('set_time_limit')) {
        @set_time_limit(0);
    }
    $start = 0;
    do {
        $topics2 = $GLOBALS['FORUM_DB']->query('SELECT id,t_cache_first_title,t_cache_last_time FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_topics WHERE ' . $or_list, 100, $start);
        require_code('urls2');
        foreach ($topics2 as $_topic) {
            if ($_topic['t_cache_last_time'] < time() - 60 * 60 * 24 * 14) {
                continue;
            }
            $topic_id = $_topic['id'];
            $topic_title = $_topic['t_cache_first_title'];
            suggest_new_idmoniker_for('topicview', 'misc', strval($topic_id), $topic_title);
            // Now lets inform people tracking the topic that it has moved
            $subject = do_lang('TOPIC_MOVE_MAIL_SUBJECT', get_site_name(), $topic_title);
            $mail = do_lang('TOPIC_MOVE_MAIL', comcode_escape(get_site_name()), comcode_escape($topic_title), array(comcode_escape($forum_name)));
            dispatch_notification('ocf_topic', strval($topic_id), $subject, $mail);
        }
    } while (count($topics2) == 100);
}