<?php /*

 Composr
 Copyright (c) ocProducts, 2004-2016

 See text/EN/licence.txt for full licencing 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  ocProducts Ltd
 * @package    cns_forum
 */

/*
Overview of permissions we check...

New regular topic:
 - cns_may_post_topic

New private topic:
 - cns_may_make_private_topic / cns_check_make_private_topic
 - cns_may_whisper

Reply to regular or private topic:
 - cns_may_post_in_topic
 - cns_may_access_topic
... and an inline personal post:
 - cns_may_whisper
*/

/**
 * Module page class.
 */
class Module_topics
{
    /**
     * Find details of the module.
     *
     * @return ?array Map of module info (null: module is disabled).
     */
    public function info()
    {
        $info = array();
        $info['author'] = 'Chris Graham';
        $info['organisation'] = 'ocProducts';
        $info['hacked_by'] = null;
        $info['hack_version'] = null;
        $info['version'] = 2;
        $info['locked'] = false;
        return $info;
    }

    /**
     * Find entry-points available within this module.
     *
     * @param  boolean $check_perms Whether to check permissions.
     * @param  ?MEMBER $member_id The member to check permissions as (null: current user).
     * @param  boolean $support_crosslinks Whether to allow cross links to other modules (identifiable via a full-page-link rather than a screen-name).
     * @param  boolean $be_deferential Whether to avoid any entry-point (or even return null to disable the page in the Sitemap) if we know another module, or page_group, is going to link to that entry-point. Note that "!" and "browse" entry points are automatically merged with container page nodes (likely called by page-groupings) as appropriate.
     * @return ?array A map of entry points (screen-name=>language-code/string or screen-name=>[language-code/string, icon-theme-image]) (null: disabled).
     */
    public function get_entry_points($check_perms = true, $member_id = null, $support_crosslinks = true, $be_deferential = false)
    {
        if (get_forum_type() != 'cns') {
            return null;
        }
        require_code('cns_topics');
        if ($check_perms && !cns_may_make_private_topic($member_id)) {
            return array();
        }

        return array(
            'new_pt' => array('ADD_PRIVATE_TOPIC', 'buttons/new_topic'),
        );
    }

    /**
     * Find privileges defined as overridable by this module.
     *
     * @return array A map of privileges that are overridable; privilege to 0 or 1. 0 means "not category overridable". 1 means "category overridable".
     */
    public function get_privilege_overrides()
    {
        require_lang('cns');
        return array('submit_lowrange_content' => array(1, 'MAKE_POST'), 'bypass_validation_lowrange_content' => array(1, 'BYPASS_POST_VALIDATION'), 'edit_own_lowrange_content' => array(1, 'EDIT_OWN_POST'), 'edit_lowrange_content' => array(1, 'EDIT_POST'), 'delete_own_lowrange_content' => array(1, 'DELETE_OWN_POST'), 'delete_lowrange_content' => array(1, 'DELETE_POST'), 'submit_midrange_content' => array(1, 'ADD_TOPIC'), 'bypass_validation_midrange_content' => array(1, 'BYPASS_TOPIC_VALIDATION'), 'edit_own_midrange_content' => array(1, 'EDIT_OWN_TOPIC'), 'edit_midrange_content' => array(1, 'EDIT_TOPIC'), 'delete_own_midrange_content' => array(1, 'DELETE_OWN_TOPIC'), 'delete_midrange_content' => array(1, 'DELETE_TOPIC'));
    }

    public $title;

    /**
     * Module pre-run function. Allows us to know metadata for <head> before we start streaming output.
     *
     * @return ?Tempcode Tempcode indicating some kind of exceptional output (null: none).
     */
    public function pre_run()
    {
        $type = get_param_string('type', 'browse');

        require_lang('cns');

        inform_non_canonical_parameter('#^kfs.*$#');
        inform_non_canonical_parameter('#^mark_.*$#');

        if ($type == 'whisper') {
            inform_non_canonical_parameter('quote');
        }

        if ($type == 'new_topic') {
            inform_non_canonical_parameter('quote');
        }

        if ($type == 'new_post') {
            inform_non_canonical_parameter('quote');
            inform_non_canonical_parameter('intended_solely_for');
        }

        if ($type == 'browse' || $type == 'whisper' || $type == 'report_post' || $type == 'delete_post' || $type == 'new_post' || $type == 'edit_post' || $type == 'validate_post' || $type == 'new_topic') {
            // Bot (which runs as a dum guest) could conceivably try and index these things and we don't want that
            attach_to_screen_header('<meta name="robots" content="noindex" />'); // XHTMLXHTML
        }

        $GLOBALS['OUTPUT_STREAMING'] = false; // Too complex to do a pre_run for this properly

        return null;
    }

    /**
     * Execute the module.
     *
     * @return Tempcode The result of execution.
     */
    public function run()
    {
        @ignore_user_abort(true); // Must keep going till completion

        if (get_forum_type() != 'cns') {
            warn_exit(do_lang_tempcode('NO_CNS'));
        } else {
            cns_require_all_forum_stuff();
        }
        require_css('cns');

        require_code('form_templates');

        $type = get_param_string('type', 'browse');

        if (strpos($type, '__') !== false) {
            list($type, $_GET['id']) = explode('__', $type, 2);
        }

        $valid_types = array(
            'whisper',
            'mark_read',
            'mark_read_topic',
            'mark_unread_topic',
            'delete_topic',
            '_delete_topic',
            'delete_topics',
            '_delete_topics',
            'delete_topics_and_posts',
            '_delete_topics_and_posts',
            'categorise_pts',
            '_categorise_pts',
            'move_topic',
            '_move_topic',
            'validate_posts',
            'delete_posts',
            '_delete_posts',
            'delete_post',
            '_delete_post',
            'move_posts_a',
            'move_posts_b',
            '_move_posts',
            'move_topics',
            '_move_topics',
            'new_topic',
            'new_post',
            'new_pt',
            '_add_reply',
            'toggle_notifications_topic',
            'toggle_notifications_forum',
            'vote_poll',
            'add_poll',
            '_add_poll',
            'edit_poll',
            '_edit_poll',
            'delete_poll',
            '_delete_poll',
            'edit_post',
            '_edit_post',
            'edit_topic',
            '_edit_topic',
            'pin_topic',
            'unpin_topic',
            'sink_topic',
            'unsink_topic',
            'cascade_topic',
            'uncascade_topic',
            'open_topic',
            'close_topic',
            'validate_post',
            'birthday',
            'make_private',
            '_make_private',
            'validate_topics',
            'pin_topics',
            'unpin_topics',
            'sink_topics',
            'unsink_topics',
            'cascade_topics',
            'uncascade_topics',
            'open_topics',
            'close_topics',
            'mark_topics_read',
            'mark_topics_unread',
            '_mass_multimod',
            'invite_member',
            '_invite_member',
        );
        if (addon_installed('actionlog')) {
            $valid_types[] = 'topic_history';
        }
        if (addon_installed('cns_reported_posts')) {
            $valid_types[] = 'report_post';
        }
        if (addon_installed('cns_multi_moderations')) {
            $valid_types[] = 'multimod';
            $valid_types[] = '_multimod';
        }

        if (in_array($type, $valid_types)) {
            return call_user_func(array($this, $type));
        }

        if (substr($type, 0, 3) == 'mm_') {
            return $this->multimod();
        }
        if (substr($type, 0, 4) == 'mmt_') {
            return $this->mass_multimod();
        }

        if ($type == 'browse') {
            header('X-Robots-Tag: noindex');

            warn_exit(do_lang_tempcode('NOTHING_SELECTED'));
        }

        return new Tempcode();
    }

    /**
     * The UI for a trivial form page that just asks a reason before relaying the results to the same URL, but with an underscored type.
     *
     * @param  ID_TEXT $_title The language string ID for the title to use in the page
     * @return Tempcode The UI
     */
    public function relay_with_reason($_title)
    {
        $title = get_screen_title($_title);
        $text = paragraph(do_lang_tempcode('OPTIONAL_REASON'));
        $submit_icon = 'buttons__proceed';
        $submit_name = do_lang_tempcode('PROCEED');
        $type = '_' . get_param_string('type', 'browse');
        $post_url = build_url(array('page' => '_SELF', 'type' => $type, 'id' => get_param_integer('id', -1)), '_SELF', null, true);
        $fields = new Tempcode();
        $hidden = new Tempcode();
        $hidden->attach(build_keep_post_fields());
        $hidden->attach(build_keep_form_fields());
        $fields->attach(form_input_line(do_lang_tempcode('REASON'), '', 'reason', '', false));

        return do_template('FORM_SCREEN', array(
            '_GUID' => '85e30370bb9e45b2b9a7cd6463d69557',
            'SKIP_WEBSTANDARDS' => true,
            'STAFF_HELP_URL' => get_tutorial_url('tut_moderation'),
            'HIDDEN' => $hidden,
            'TITLE' => $title,
            'TEXT' => $text,
            'URL' => $post_url,
            'FIELDS' => $fields,
            'SUBMIT_ICON' => $submit_icon,
            'SUBMIT_NAME' => $submit_name,
        ));
    }

    /**
     * Do a redirection page to a certain topic/post (because we've just done an action and want to go back).
     *
     * @param  ID_TEXT $_title The language string ID for the title to use in the page
     * @param  AUTO_LINK $topic_id The ID of the topic to redirect to
     * @param  ?mixed $lang What to output (Tempcode or string) (null: default)
     * @param  ?AUTO_LINK $post_id The ID of the post to redirect to (null: redirect to topic instead)
     * @return Tempcode The UI
     */
    public function redirect_to($_title, $topic_id, $lang = null, $post_id = null)
    {
        require_code('cns_topicview');

        if (is_null($lang)) {
            $lang = do_lang_tempcode('SUCCESS');
        }
        $title = get_screen_title($_title);

        // Show it worked / Refresh
        if (is_null($post_id)) {
            $url = find_first_unread_url($topic_id);
        } else {
            $url = find_post_id_url($post_id);
        }

        return redirect_screen($title, $url, $lang);
    }

    /**
     * Do a redirection page to a certain forum (because we've just done an action and want to go back).
     *
     * @param  ID_TEXT $_title The language string ID for the title to use in the page
     * @param  AUTO_LINK $forum_id The ID of the forum to redirect to
     * @param  ?mixed $lang What to output (Tempcode or string) (null: default)
     * @return Tempcode The UI
     */
    public function redirect_to_forum($_title, $forum_id, $lang = null)
    {
        if (is_null($lang)) {
            $lang = do_lang_tempcode('SUCCESS');
        }
        $title = get_screen_title($_title);

        // Show it worked / Refresh
        if (is_null($forum_id)) {
            $url = build_url(array('page' => 'members', 'type' => 'view'), get_module_zone('members'), null, false, false, false, 'tab__pts');
        } else {
            $url = build_url(array('page' => 'forumview', 'id' => $forum_id), get_module_zone('forumview'));
        }

        return redirect_screen($title, $url, $lang);
    }

    /**
     * Extract marker IDs that were past through in the GET/POST.
     *
     * @return array A list of markers
     */
    public function get_markers()
    {
        $markers = array();
        foreach (array_keys($_REQUEST) as $key) {
            if (substr($key, 0, 5) == 'mark_') {
                $markers[] = intval(substr($key, 5));
            }
        }
        return $markers;
    }

    /**
     * Copy marker IDs that were past through in the GET/POST into hidden form fields, so they may be relayed.
     *
     * @return Tempcode Hidden fields facilitating the marker relaying
     */
    public function keep_markers()
    {
        $markers = new Tempcode();
        $_markers = $this->get_markers();
        foreach ($_markers as $marker) {
            $markers->attach(form_input_hidden('mark_' . strval($marker), '1'));
        }
        return $markers;
    }

    // ===========
    // FORUM LEVEL
    // ===========

    /**
     * The actualiser to toggle notifications for a forum.
     *
     * @return Tempcode The UI
     */
    public function toggle_notifications_forum() // Type
    {
        require_code('notifications2');
        return notifications_ui_advanced('cns_topic', do_lang_tempcode('NOW_ENABLED_NOTIFICATIONS_FORUM'), do_lang_tempcode('NOW_DISABLED_NOTIFICATIONS_FORUM'));
    }

    // =================
    // WITH MARKED POSTS
    // =================

    /**
     * The actualiser to validate some posts.
     *
     * @return Tempcode The UI
     */
    public function validate_posts() // Type
    {
        $posts = $this->get_markers();
        if (count($posts) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $post_info = $GLOBALS['FORUM_DB']->query_select('f_posts', array('*'), array('id' => $posts[0]), '', 1);
        $topic_id = $post_info[0]['p_topic_id'];
        $forum_id = $post_info[0]['p_cache_forum_id'];
        $poster = $post_info[0]['p_poster'];
        $post = get_translated_text($post_info[0]['p_post'], $GLOBALS['FORUM_DB']);

        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        $this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('VALIDATE_POSTS'));

        require_code('cns_posts_action');
        require_code('cns_posts_action2');
        require_code('cns_posts_action3');

        foreach ($posts as $post_id) {
            cns_validate_post($post_id, $topic_id, $forum_id, $poster, $post);
        }

        return $this->redirect_to('VALIDATE_POSTS', $topic_id);
    }

    /**
     * The UI to delete some posts.
     *
     * @return Tempcode The UI
     */
    public function delete_posts() // Type
    {
        $posts = $this->get_markers();
        if (count($posts) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $topic_id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_posts', 'p_topic_id', array('id' => $posts[0]));
        if (is_null($topic_id)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'post'));
        }
        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        $this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('DELETE_POSTS'));

        return $this->relay_with_reason('DELETE_POSTS');
    }

    /**
     * The actualiser to delete some posts.
     *
     * @return Tempcode The UI
     */
    public function _delete_posts() // Type
    {
        $posts = $this->get_markers();
        if (count($posts) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $topic_id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_posts', 'p_topic_id', array('id' => $posts[0]));
        if (is_null($topic_id)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'post'));
        }
        require_code('cns_posts_action');
        require_code('cns_posts_action2');
        require_code('cns_posts_action3');
        cns_delete_posts_topic($topic_id, $posts, post_param_string('reason'));

        $test = $GLOBALS['FORUM_DB']->query_select_value('f_posts', 'COUNT(*)', array('p_topic_id' => $topic_id));
        if ($test == 0) {
            return $this->redirect_to_forum('MOVE_POSTS', db_get_first_id());
        }

        return $this->redirect_to('DELETE_POSTS', $topic_id);
    }

    /**
     * The UI to move some posts to an existing topic.
     *
     * @return Tempcode The UI
     */
    public function move_posts_a() // Type
    {
        $posts = $this->get_markers();
        if (count($posts) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $post_url = build_url(array('page' => '_SELF', 'type' => '_move_posts'), '_SELF');

        $topic_id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_posts', 'p_topic_id', array('id' => $posts[0]));
        if (is_null($topic_id)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'post'));
        }
        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        $this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('MOVE_POSTS'));

        // Certain aspects relating to the posting system
        $fields = new Tempcode();
        $hidden = $this->keep_markers();

        $set_name = 'destination';
        $required = true;
        $set_title = do_lang_tempcode('DESTINATION_TOPIC');
        $field_set = alternate_fields_set__start($set_name);

        $field_set->attach(form_input_tree_list(do_lang_tempcode('CHOOSE'), '', 'select_topic_id', null, 'choose_topic', array(), false));

        $field_set->attach(form_input_line(do_lang_tempcode('DESTINATION_TOPIC_ID'), do_lang_tempcode('DESCRIPTION_DESTINATION_TOPIC'), 'manual_topic_id', null, false));

        $fields->attach(alternate_fields_set__end($set_name, $set_title, '', $field_set, $required));

        $fields->attach(form_input_tick(do_lang_tempcode('DELETE_IF_EMPTY'), do_lang_tempcode('DESCRIPTION_DELETE_IF_EMPTY'), 'delete_if_empty', true));
        $fields->attach(form_input_line(do_lang_tempcode('REASON'), do_lang_tempcode('DESCRIPTION_REASON'), 'reason', '', false));

        $title = get_screen_title('MOVE_POSTS');
        $submit_name = do_lang_tempcode('MOVE_POSTS');
        $text = do_lang_tempcode('MOVE_POSTS_A_TEXT');

        return do_template('FORM_SCREEN', array(
            '_GUID' => 'd62d2c81583398f26f900ee3df1894b1',
            'STAFF_HELP_URL' => get_tutorial_url('tut_moderation'),
            'HIDDEN' => $hidden,
            'TITLE' => $title,
            'FIELDS' => $fields,
            'TEXT' => $text,
            'SUBMIT_ICON' => 'buttons__move',
            'SUBMIT_NAME' => $submit_name,
            'URL' => $post_url,
        ));
    }

    /**
     * The UI to move some posts to a new topic.
     *
     * @return Tempcode The UI
     */
    public function move_posts_b() // Type
    {
        $posts = $this->get_markers();
        if (count($posts) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $post_url = build_url(array('page' => '_SELF', 'type' => '_move_posts'), '_SELF');

        $topic_id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_posts', 'p_topic_id', array('id' => $posts[0]));
        if (is_null($topic_id)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'post'));
        }
        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        $this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('MOVE_POSTS'));
        $default_title = do_lang('POSTS_SPLIT_FROM', $topic_info[0]['t_cache_first_title']);

        require_code('cns_forums2');

        // Certain aspects relating to the posting system
        $fields = new Tempcode();
        $hidden = $this->keep_markers();
        $fields->attach(form_input_tree_list(do_lang_tempcode('DESTINATION_FORUM'), do_lang_tempcode('DESCRIPTION_POSTS_DESTINATION_FORUM'), 'to_forum_id', null, 'choose_forum', array(), true, strval($topic_info[0]['t_forum_id'])));
        $fields->attach(form_input_line(do_lang_tempcode('TITLE'), do_lang_tempcode('TOPIC_TITLE_WILL_BE'), 'title', $default_title, false, null, 120));
        $fields->attach(form_input_tick(do_lang_tempcode('DELETE_IF_EMPTY'), do_lang_tempcode('DESCRIPTION_DELETE_IF_EMPTY'), 'delete_if_empty', true));
        $fields->attach(form_input_line(do_lang_tempcode('REASON'), do_lang_tempcode('DESCRIPTION_REASON'), 'reason', '', false));

        $title = get_screen_title('MOVE_POSTS');
        $submit_name = do_lang_tempcode('MOVE_POSTS');
        $text = do_lang_tempcode('MOVE_POSTS_B_TEXT');

        return do_template('FORM_SCREEN', array(
            '_GUID' => 'a476da1fecfbd932db4853cdbd7cfedd',
            'STAFF_HELP_URL' => get_tutorial_url('tut_moderation'),
            'HIDDEN' => $hidden,
            'TITLE' => $title,
            'FIELDS' => $fields,
            'TEXT' => $text,
            'SUBMIT_ICON' => 'buttons__move',
            'SUBMIT_NAME' => $submit_name,
            'URL' => $post_url,
        ));
    }

    /**
     * The actualiser to move some posts.
     *
     * @return Tempcode The UI
     */
    public function _move_posts() // Type
    {
        $posts = $this->get_markers();
        if (count($posts) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $to_forum_id = null;
        $to_topic_id = post_param_integer('select_topic_id', -1);
        if ($to_topic_id == -1) {
            $_to_topic_id = post_param_string('manual_topic_id', '');
            if ($_to_topic_id == '') {
                $to_topic_id = null;
                $to_forum_id = post_param_integer('to_forum_id');
                if ($to_forum_id == -1) {
                    warn_exit(do_lang_tempcode('MUST_MOVE_POSTS_SOMEWHERE'));
                }
            } else {
                if (!is_numeric($_to_topic_id)) {
                    $_to_topic_id = $GLOBALS['SITE_DB']->query_select_value_if_there('url_id_monikers', 'm_resource_id', array('m_resource_page' => 'topicview', 'm_resource_type' => 'browse', 'm_moniker' => urldecode($_to_topic_id)));
                    if (is_null($_to_topic_id)) {
                        warn_exit(do_lang_tempcode('MISSING_RESOURCE'));
                    }
                }
                $to_topic_id = intval($_to_topic_id);
            }
        }
        $from_topic_id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_posts', 'p_topic_id', array('id' => $posts[0]));
        if (is_null($from_topic_id)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'post'));
        }
        require_code('cns_posts_action');
        require_code('cns_posts_action2');
        require_code('cns_posts_action3');
        $delete_if_empty = post_param_integer('delete_if_empty', 0) == 1;
        $test = cns_move_posts($from_topic_id, $to_topic_id, $posts, post_param_string('reason'), $to_forum_id, $delete_if_empty, post_param_string('title', null));

        if ($test) {
            return $this->redirect_to_forum('MOVE_POSTS', db_get_first_id());
        }

        return $this->redirect_to('MOVE_POSTS', $from_topic_id);
    }

    // ==================
    // WITH MARKED TOPICS
    // ==================

    /**
     * The actualiser to mark topics as read.
     *
     * @return Tempcode The UI
     */
    public function mark_topics_read() // Type
    {
        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $forum_id = null;
        foreach ($topics as $i => $topic_id) {
            $id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('id' => $topic_id));
            if ($id === null) {
                continue;
            }

            if ($forum_id === null) {
                $forum_id = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_forum_id', array('id' => $topic_id));
            }
            cns_ping_topic_read($topic_id);
        }

        if (is_null($forum_id)) {
            decache('side_cns_private_topics', null, get_member());
            decache('_new_pp', null, get_member());
            decache('_get_pts', null, get_member());
        }

        $title = get_screen_title('MARK_READ');

        // Show it worked / Refresh
        $url = get_param_string('redirect', null);
        if ($url !== null) {
            return redirect_screen($title, $url, do_lang_tempcode('SUCCESS'));
        }

        // Show it worked / Refresh
        return $this->redirect_to_forum('MARK_READ', $forum_id);
    }

    /**
     * Mark a topic as unread by the current member.
     *
     * @param  AUTO_LINK $topic_id The ID of the topic to mark as unread.
     * @return boolean Success status (false = too old to mark read; true = marked read or topic entirely missing).
     */
    public function cns_ping_topic_unread($topic_id)
    {
        $last_time = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 't_cache_last_time', array('id' => $topic_id));
        if ($last_time === null) {
            return true;
        }
        $too_old = $last_time < time() - 60 * 60 * 24 * intval(get_option('post_read_history_days'));
        if (!$too_old) {
            if (!$GLOBALS['FORUM_DB']->table_is_locked('f_read_logs')) {
                $GLOBALS['FORUM_DB']->query_delete('f_read_logs', array('l_topic_id' => $topic_id, 'l_member_id' => get_member()), '', 1);
            }
            return true;
        }
        return false;
    }

    /**
     * The actualiser to mark topics as unread.
     *
     * @return Tempcode The UI
     */
    public function mark_topics_unread() // Type
    {
        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $forum_id = null;
        $success = 0;
        foreach ($topics as $i => $topic_id) {
            $id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('id' => $topic_id));
            if ($id === null) {
                continue;
            }

            if ($forum_id === null) {
                $forum_id = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_forum_id', array('id' => $topic_id));
            }
            if ($this->cns_ping_topic_unread($topic_id)) {
                $success++;
            }
        }

        if ($success != count($topics)) {
            attach_message(do_lang_tempcode('MARK_UNREAD_TOO_OLD', escape_html(integer_format(count($topics) - $success)), escape_html(integer_format(intval(get_option('post_read_history_days'))))), 'warn');
        }

        if (is_null($forum_id)) {
            decache('side_cns_private_topics', null, get_member());
            decache('_new_pp', null, get_member());
            decache('_get_pts', null, get_member());
        }

        return $this->redirect_to_forum('MARK_UNREAD', $forum_id);
    }

    /**
     * The actualiser to validate topics.
     *
     * @return Tempcode The UI
     */
    public function validate_topics() // Type
    {
        require_code('cns_topics_action');
        require_code('cns_topics_action2');

        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $forum_id = null;
        foreach ($topics as $i => $topic_id) {
            $id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('id' => $topic_id));
            if ($id === null) {
                continue;
            }

            if ($forum_id === null) {
                $forum_id = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_forum_id', array('id' => $topic_id));
            }
            cns_edit_topic($topic_id, null, null, 1, null, null, null, null, '');
        }

        return $this->redirect_to_forum('VALIDATE_TOPICS', $forum_id);
    }

    /**
     * The actualiser to pin topics.
     *
     * @return Tempcode The UI
     */
    public function pin_topics() // Type
    {
        require_code('cns_topics_action');
        require_code('cns_topics_action2');

        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $forum_id = null;
        foreach ($topics as $i => $topic_id) {
            $id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('id' => $topic_id));
            if ($id === null) {
                continue;
            }

            if ($forum_id === null) {
                $forum_id = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_forum_id', array('id' => $topic_id));
            }
            cns_edit_topic($topic_id, null, null, null, null, 1, null, null, '');
        }

        return $this->redirect_to_forum('PIN_TOPIC', $forum_id);
    }

    /**
     * The actualiser to unpin topics.
     *
     * @return Tempcode The UI
     */
    public function unpin_topics() // Type
    {
        require_code('cns_topics_action');
        require_code('cns_topics_action2');

        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $forum_id = null;
        foreach ($topics as $i => $topic_id) {
            $id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('id' => $topic_id));
            if ($id === null) {
                continue;
            }

            if ($forum_id === null) {
                $forum_id = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_forum_id', array('id' => $topic_id));
            }
            cns_edit_topic($topic_id, null, null, null, null, 0, null, null, '');
        }

        return $this->redirect_to_forum('UNPIN_TOPIC', $forum_id);
    }

    /**
     * The actualiser to pin topics.
     *
     * @return Tempcode The UI
     */
    public function sink_topics() // Type
    {
        require_code('cns_topics_action');
        require_code('cns_topics_action2');

        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $forum_id = null;
        foreach ($topics as $i => $topic_id) {
            $id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('id' => $topic_id));
            if ($id === null) {
                continue;
            }

            if ($forum_id === null) {
                $forum_id = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_forum_id', array('id' => $topic_id));
            }
            cns_edit_topic($topic_id, null, null, null, null, 1, null, null, '');
        }

        return $this->redirect_to_forum('SINK_TOPIC', $forum_id);
    }

    /**
     * The actualiser to unpin topics.
     *
     * @return Tempcode The UI
     */
    public function unsink_topics() // Type
    {
        require_code('cns_topics_action');
        require_code('cns_topics_action2');

        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $forum_id = null;
        foreach ($topics as $i => $topic_id) {
            $id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('id' => $topic_id));
            if ($id === null) {
                continue;
            }

            if ($forum_id === null) {
                $forum_id = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_forum_id', array('id' => $topic_id));
            }
            cns_edit_topic($topic_id, null, null, null, null, 0, null, null, '');
        }

        return $this->redirect_to_forum('UNSINK_TOPIC', $forum_id);
    }

    /**
     * The actualiser to cascade topics.
     *
     * @return Tempcode The UI
     */
    public function cascade_topics() // Type
    {
        require_code('cns_topics_action');
        require_code('cns_topics_action2');

        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $forum_id = null;
        foreach ($topics as $i => $topic_id) {
            $id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('id' => $topic_id));
            if ($id === null) {
                continue;
            }

            if ($forum_id === null) {
                $forum_id = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_forum_id', array('id' => $topic_id));
            }
            cns_edit_topic($topic_id, null, null, null, null, null, null, 1, '');
        }

        return $this->redirect_to_forum('CASCADE_TOPIC', $forum_id);
    }

    /**
     * The actualiser to uncascade topics.
     *
     * @return Tempcode The UI
     */
    public function uncascade_topics() // Type
    {
        require_code('cns_topics_action');
        require_code('cns_topics_action2');

        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $forum_id = null;
        foreach ($topics as $i => $topic_id) {
            $id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('id' => $topic_id));
            if ($id === null) {
                continue;
            }

            if ($forum_id === null) {
                $forum_id = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_forum_id', array('id' => $topic_id));
            }
            cns_edit_topic($topic_id, null, null, null, null, null, null, 0, '');
        }

        return $this->redirect_to_forum('UNCASCADE_TOPIC', $forum_id);
    }

    /**
     * The actualiser to open topics.
     *
     * @return Tempcode The UI
     */
    public function open_topics() // Type
    {
        require_code('cns_topics_action');
        require_code('cns_topics_action2');

        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $forum_id = null;
        foreach ($topics as $i => $topic_id) {
            $id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('id' => $topic_id));
            if ($id === null) {
                continue;
            }

            if ($forum_id === null) {
                $forum_id = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_forum_id', array('id' => $topic_id));
            }
            cns_edit_topic($topic_id, null, null, null, 1, null, null, null, '');
        }

        return $this->redirect_to_forum('OPEN_TOPIC', $forum_id);
    }

    /**
     * The actualiser to close topics.
     *
     * @return Tempcode The UI
     */
    public function close_topics() // Type
    {
        require_code('cns_topics_action');
        require_code('cns_topics_action2');

        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $forum_id = null;
        foreach ($topics as $i => $topic_id) {
            $id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('id' => $topic_id));
            if ($id === null) {
                continue;
            }

            if ($forum_id === null) {
                $forum_id = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_forum_id', array('id' => $topic_id));
            }
            cns_edit_topic($topic_id, null, null, null, 0, null, null, null, '');
        }

        return $this->redirect_to_forum('CLOSE_TOPIC', $forum_id);
    }

    /**
     * The UI to run multi-moderations.
     *
     * @return Tempcode The UI
     */
    public function mass_multimod() // Type
    {
        require_lang('cns_multi_moderations');

        $mm_id = intval(substr(get_param_string('type', 'browse'), 4));

        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $forum_id = null;
        foreach ($topics as $i => $topic_id) {
            $id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('id' => $topic_id));
            if ($id === null) {
                continue;
            }

            if ($forum_id === null) {
                $forum_id = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_forum_id', array('id' => $topic_id));
            }
            $this->check_has_mod_access($topic_id);
        }

        $breadcrumbs = cns_forum_breadcrumbs($forum_id, null, null, false);
        breadcrumb_set_parents($breadcrumbs);
        breadcrumb_set_self(do_lang_tempcode('PERFORM_MULTI_MODERATION'));

        $title = get_screen_title('PERFORM_MULTI_MODERATION');
        $mm = $GLOBALS['FORUM_DB']->query_select('f_multi_moderations', array('*'), array('id' => $mm_id), '', 1);
        if (!array_key_exists(0, $mm)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'multi_moderation'));
        }
        $_mm = $mm[0];
        $post_text = $_mm['mm_post_text'];
        $submit_name = do_lang_tempcode('PERFORM_MULTI_MODERATION');
        $post_url = build_url(array('page' => '_SELF', 'type' => '_mass_multimod', 'mm_id' => $mm_id), '_SELF', null, true);
        $fields = new Tempcode();
        $hidden = new Tempcode();
        $hidden->attach(build_keep_post_fields());
        $hidden->attach(build_keep_form_fields());
        $fields->attach(form_input_text(do_lang_tempcode('MM_POST_TEXT'), do_lang_tempcode('DESCRIPTION_MM_POST_TEXT'), 'post_text', $post_text, false));
        $options = array();
        if (addon_installed('cns_signatures')) {
            if (get_option('enable_skip_sig') == '1') {
                $options[] = array(do_lang_tempcode('SKIP_SIGNATURE'), 'skip_sig', false, do_lang_tempcode('DESCRIPTION_SKIP_SIGNATURE'));
            }
        }
        if (get_option('enable_post_emphasis') == '1') {
            $options[] = array(do_lang_tempcode('EMPHASISED'), 'is_emphasised', true, do_lang_tempcode('DESCRIPTION_EMPHASISED'));
        }
        $fields->attach(form_input_various_ticks($options, ''));
        $fields->attach(form_input_line(do_lang_tempcode('REASON'), do_lang_tempcode('OPTIONAL_REASON'), 'reason', '', false));

        $mm_title = get_translated_text($_mm['mm_name'], $GLOBALS['FORUM_DB']);
        $action_list = new Tempcode();
        if ($_mm['mm_open_state'] == 1) {
            $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_OPEN'));
        }
        if ($_mm['mm_open_state'] == 0) {
            $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_CLOSE'));
        }
        if ($_mm['mm_pin_state'] == 1) {
            $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_PIN'));
        }
        if ($_mm['mm_pin_state'] == 0) {
            $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_UNPIN'));
        }
        if ($_mm['mm_sink_state'] == 1) {
            $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_SINK'));
        }
        if ($_mm['mm_sink_state'] == 0) {
            $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_UNSINK'));
        }
        if ($_mm['mm_title_suffix'] != '') {
            $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_TITLE_SUFFIX', escape_html($_mm['mm_title_suffix'])));
        }
        if (!is_null($_mm['mm_move_to'])) {
            $target_forum = $GLOBALS['FORUM_DB']->query_select_value('f_forums', 'f_name', array('id' => $_mm['mm_move_to']));
            $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_MOVE', escape_html($target_forum)));
        }
        $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_POST'));
        $text = do_lang_tempcode('MULTI_MODERATION_WILL', make_string_tempcode($mm_title), $action_list);

        return do_template('FORM_SCREEN', array(
            '_GUID' => '69908b7e2711414c13395535d6547096',
            'STAFF_HELP_URL' => get_tutorial_url('tut_moderation'),
            'PREVIEW' => true,
            'HIDDEN' => $hidden,
            'TITLE' => $title,
            'TEXT' => $text,
            'URL' => $post_url,
            'FIELDS' => $fields,
            'SUBMIT_ICON' => 'menu__adminzone__structure__forum__multi_moderations',
            'SUBMIT_NAME' => $submit_name,
        ));
    }

    /**
     * The actualiser to run multi moderations.
     *
     * @return Tempcode The UI
     */
    public function _mass_multimod() // Type
    {
        require_lang('cns_multi_moderations');

        $mm_id = get_param_integer('mm_id');
        require_code('cns_moderation_action');
        require_code('cns_moderation_action2');

        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $forum_id = null;
        foreach ($topics as $i => $topic_id) {
            $id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('id' => $topic_id));
            if ($id === null) {
                continue;
            }

            if ($forum_id === null) {
                $forum_id = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_forum_id', array('id' => $topic_id));
            }
            cns_perform_multi_moderation($mm_id, $topic_id, post_param_string('reason'), post_param_string('post_text'), post_param_integer('is_emphasised', 0), post_param_integer('skip_sig', 0));
        }

        return $this->redirect_to_forum('PERFORM_MULTI_MODERATION', $forum_id);
    }

    /**
     * The UI to move some topics.
     *
     * @return Tempcode The UI
     */
    public function move_topics() // Type
    {
        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('id' => $topics[0]));
        if ($id === null) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'forum'));
        }

        $forum_id = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_forum_id', array('id' => $topics[0]));

        $post_url = build_url(array('page' => '_SELF', 'type' => '_move_topics'), '_SELF');

        require_code('cns_forums2');

        // Certain aspects relating to the posting system
        $fields = new Tempcode();
        $fields->attach(form_input_tree_list(do_lang_tempcode('DESTINATION_FORUM'), do_lang_tempcode('DESCRIPTION_DESTINATION_FORUM'), 'to', null, 'choose_forum', array(), true, strval($forum_id)));
        $fields->attach(form_input_line(do_lang_tempcode('REASON'), do_lang_tempcode('DESCRIPTION_REASON'), 'description', '', false));
        $hidden = $this->keep_markers();

        $breadcrumbs = cns_forum_breadcrumbs($forum_id, null, null, false);
        breadcrumb_set_self(do_lang_tempcode('MOVE_TOPICS'));
        breadcrumb_set_parents($breadcrumbs);

        $title = get_screen_title('MOVE_TOPICS');
        $submit_name = do_lang_tempcode('MOVE_TOPICS');
        return do_template('FORM_SCREEN', array(
            '_GUID' => '7532b5e7239e0f9ceb64d09c28fd7261',
            'SKIP_WEBSTANDARDS' => true,
            'STAFF_HELP_URL' => get_tutorial_url('tut_moderation'),
            'HIDDEN' => $hidden,
            'TITLE' => $title,
            'FIELDS' => $fields,
            'TEXT' => '',
            'SUBMIT_ICON' => 'buttons__move',
            'SUBMIT_NAME' => $submit_name,
            'URL' => $post_url,
        ));
    }

    /**
     * The actualiser to move some topics.
     *
     * @return Tempcode The UI
     */
    public function _move_topics() // Type
    {
        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $to = post_param_integer('to');
        $from = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 't_forum_id', array('id' => $topics[0]));
        //if (is_null($from)) warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));  May be from private topics, so can't do this check
        require_code('cns_topics_action');
        require_code('cns_topics_action2');
        cns_move_topics($from, $to, $topics);
        return $this->redirect_to_forum('MOVE_TOPIC', $from);
    }

    /**
     * The UI to delete some topics.
     *
     * @return Tempcode The UI
     */
    public function delete_topics() // Type
    {
        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $topic_id = $topics[0];
        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        $this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('DELETE_TOPICS'));

        return $this->relay_with_reason('DELETE_TOPICS');
    }

    /**
     * The actualiser to delete some topics.
     *
     * @return Tempcode The UI
     */
    public function _delete_topics() // Type
    {
        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }
        require_code('cns_topics_action');
        require_code('cns_topics_action2');
        foreach ($topics as $topic_id) {
            $forum_id = cns_delete_topic($topic_id, post_param_string('reason'), null);
        }
        return $this->redirect_to_forum('DELETE_TOPICS', $forum_id);
    }

    /**
     * The UI to delete some topics / posts.
     *
     * @return Tempcode The UI
     */
    public function delete_topics_and_posts() // Type
    {
        if (is_guest()) {
            access_denied('NOT_AS_GUEST');
        }

        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        $topic_id = $topics[0];
        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        //$this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('DELETE_TOPICS_AND_POSTS'));

        return $this->relay_with_reason('DELETE_TOPICS_AND_POSTS');
    }

    /**
     * The actualiser to delete some topics / posts.
     *
     * @return Tempcode The UI
     */
    public function _delete_topics_and_posts() // Type
    {
        if (is_guest()) {
            access_denied('NOT_AS_GUEST');
        }

        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }

        require_code('cns_topics_action');
        require_code('cns_topics_action2');
        require_code('cns_posts_action');
        require_code('cns_posts_action2');
        require_code('cns_posts_action3');

        $reason = post_param_string('reason');

        foreach ($topics as $topic_id) {
            $own_topic = ($GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 't_cache_first_member_id', array('id' => $topic_id)) == get_member());
            if ($own_topic === null) {
                continue;
            }

            if ($own_topic) {
                cns_delete_topic($topic_id, $reason, null);
            } else {
                $posts = collapse_1d_complexity('id', $GLOBALS['FORUM_DB']->query_select('f_posts', array('id'), array('p_poster' => get_member(), 'p_topic_id' => $topic_id)));
                cns_delete_posts_topic($topic_id, $posts, $reason);
            }
        }

        // Show it worked / Refresh
        $title = get_screen_title('DELETE_TOPICS_AND_POSTS');
        $url = build_url(array('page' => 'members', 'type' => 'view'), get_module_zone('members'));
        return redirect_screen($title, $url, do_lang_tempcode('SUCCESS'));
    }

    /**
     * The UI to categorise some PTs.
     *
     * @return Tempcode The UI
     */
    public function categorise_pts() // Type
    {
        $topics = $this->get_markers();
        if (count($topics) == 0) {
            $topics[] = get_param_string('id', null);
            if (is_null($topics[0])) {
                warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
            }
            $_REQUEST['mark_' . $topics[0]] = 1;
        }

        $post_url = build_url(array('page' => '_SELF', 'type' => '_categorise_pts'), '_SELF');

        $default_filter_cat = get_param_string('id', '', true);

        // Certain aspects relating to the posting system
        $fields = new Tempcode();
        $filter_cats = cns_get_filter_cats();
        $list = new Tempcode();
        foreach ($filter_cats as $filter_cat) {
            $filter_cat_text = ($filter_cat == '') ? do_lang_tempcode('NONE_EM') : make_string_tempcode($filter_cat);
            $selected = ($filter_cat == $default_filter_cat);
            $list->attach(form_input_list_entry($filter_cat, $selected, $filter_cat_text));
        }

        $set_name = 'category';
        $required = true;
        $set_title = do_lang_tempcode('CATEGORY');
        $field_set = alternate_fields_set__start($set_name);

        $field_set->attach(form_input_list(do_lang_tempcode('EXISTING'), '', 'category_a', $list, null, true));

        $field_set->attach(form_input_line(do_lang_tempcode('NEW'), '', 'category_b', '', false));

        $fields->attach(alternate_fields_set__end($set_name, $set_title, do_lang_tempcode('DESCRIPTION_CATEGORY'), $field_set, $required));

        $hidden = $this->keep_markers();

        breadcrumb_set_parents(array(array('_SEARCH:forumview:pt', do_lang_tempcode('PRIVATE_TOPICS'))));

        $title = get_screen_title('CATEGORISE_PTS');
        $submit_name = do_lang_tempcode('CATEGORISE_PTS');
        return do_template('FORM_SCREEN', array('_GUID' => 'c6d0e273b5ce0e84d50a1c6294ece157', 'SKIP_WEBSTANDARDS' => true, 'HIDDEN' => $hidden, 'TITLE' => $title, 'FIELDS' => $fields, 'TEXT' => '', 'SUBMIT_ICON' => 'buttons__save', 'SUBMIT_NAME' => $submit_name, 'URL' => $post_url));
    }

    /**
     * The actualiser to categorise some PTs.
     *
     * @return Tempcode The UI
     */
    public function _categorise_pts() // Type
    {
        $topics = $this->get_markers();
        if (count($topics) == 0) {
            warn_exit(do_lang_tempcode('NO_MARKERS_SELECTED'));
        }
        require_code('cns_topics_action');
        require_code('cns_topics_action2');
        $category = post_param_string('category_b', post_param_string('category_a', ''));
        foreach ($topics as $topic_id) {
            $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('t_pt_from', 't_pt_to', 't_pt_from_category', 't_pt_to_category'), array('id' => $topic_id), '', 1);
            if (array_key_exists(0, $topic_info)) {
                if ($topic_info[0]['t_pt_from'] == get_member()) {
                    $t = 't_pt_from_category';
                    if ((get_option('delete_trashed_pts') == '1') && ($category == do_lang('TRASH')) && ($topic_info[0]['t_pt_to_category'] == do_lang('TRASH'))) {
                        cns_delete_topic($topic_id, '');
                        return $this->redirect_to_forum('CATEGORISE_PTS', null);
                    }
                } elseif ($topic_info[0]['t_pt_to'] == get_member()) {
                    $t = 't_pt_to_category';
                    if ((get_option('delete_trashed_pts') == '1') && ($category == do_lang('TRASH')) && ($topic_info[0]['t_pt_from_category'] == do_lang('TRASH'))) {
                        cns_delete_topic($topic_id, '');
                        return $this->redirect_to_forum('CATEGORISE_PTS', null);
                    }
                } else {
                    warn_exit(do_lang_tempcode('CANNOT_CATEGORISE_INVITED'));
                }
                $GLOBALS['FORUM_DB']->query_update('f_topics', array($t => $category), array('id' => $topic_id), '', 1);
            }
        }
        return $this->redirect_to_forum('CATEGORISE_PTS', null);
    }

    // ======================
    // POST/REPLY BASED TYPES
    // ======================

    /**
     * The UI to choose between the two types of whisper.
     *
     * @return Tempcode The UI
     */
    public function whisper()
    {
        $title = get_screen_title('WHISPER');

        $url = build_url(array('page' => '_SELF'), '_SELF', array('type' => 1), true, true);

        $member_id = get_param_integer('intended_solely_for');
        $username = $GLOBALS['FORUM_DRIVER']->get_username($member_id);

        $topic_id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_posts', 'p_topic_id', array('id' => get_param_integer('quote')));
        if (is_null($topic_id)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'post'));
        }
        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        $this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('WHISPER'));

        return do_template('CNS_WHISPER_CHOICE_SCREEN', array('_GUID' => '1ecaa02e7e87a4d73798d3085cc27229', 'URL' => $url, 'TITLE' => $title, 'USERNAME' => $username));
    }

    /**
     * Choose an emoticon for a topic.
     *
     * @param  ID_TEXT $selected_path The currently selected emoticon
     * @return Tempcode The emoticon input field
     */
    public function choose_topic_emoticon($selected_path = '')
    {
        $tabindex = get_form_field_tabindex(null);

        $content = new Tempcode();
        $extra = has_privilege(get_member(), 'use_special_emoticons') ? array() : array('e_is_special' => 0);
        $rows = $GLOBALS['FORUM_DB']->query_select('f_emoticons', array('e_theme_img_code'), array('e_use_topics' => 1) + $extra);
        $content->attach(do_template('FORM_SCREEN_INPUT_THEME_IMAGE_ENTRY', array('_GUID' => 'd9f9399072af3f19f21695aef01168c7', 'PRETTY' => do_lang_tempcode('NONE'), 'CODE' => '', 'URL' => find_theme_image('cns_emoticons/none'), 'CHECKED' => $selected_path == '', 'NAME' => 'emoticon')));

        if (count($rows) == 0) {
            return new Tempcode();
        }
        foreach ($rows as $row) {
            $path = $row['e_theme_img_code'];

            $url = find_theme_image($path);
            $content->attach(do_template('FORM_SCREEN_INPUT_THEME_IMAGE_ENTRY', array('_GUID' => '22050272aebe90adf8cd4c89e4a7b06a', 'PRETTY' => '', 'CHECKED' => $path == $selected_path, 'NAME' => 'emoticon', 'CODE' => $path, 'URL' => $url)));
        }

        $input = do_template('FORM_SCREEN_INPUT_RADIO_LIST', array('_GUID' => '80fe581b26d04876180605cdbb111f6a', 'NAME' => 'emoticon', 'REQUIRED' => false, 'CODE' => $selected_path, 'TABINDEX' => strval($tabindex), 'CONTENT' => $content));

        return _form_input('', do_lang_tempcode('TOPIC_EMOTICON'), '', $input, false);
    }

    /**
     * Create a default post based on a template of quoting the given list of quotes.
     *
     * @param  array $quotes A list of posts to quote
     * @return Tempcode The default post
     */
    public function attach_quotes($quotes)
    {
        require_code('comcode_cleanup');

        $post = new Tempcode();
        foreach ($quotes as $quote) {
            $_postdetails = $GLOBALS['FORUM_DB']->query_select('f_posts', array('p_cache_forum_id', 'p_post', 'p_poster_name_if_guest', 'p_topic_id', 'p_intended_solely_for', 'p_poster', 'p_validated', 'p_ip_address'), array('id' => $quote), '', 1);
            if (!array_key_exists(0, $_postdetails)) {
                warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'post'));
            }
            if (!is_null($_postdetails[0]['p_cache_forum_id'])) {
                if ((is_guest()) && (!is_null($_postdetails[0]['p_intended_solely_for']))) {
                    access_denied('I_ERROR');
                } elseif ((!has_privilege(get_member(), 'view_other_pt')) && ($_postdetails[0]['p_intended_solely_for'] != get_member()) && ($_postdetails[0]['p_poster'] != get_member()) && (!is_null($_postdetails[0]['p_intended_solely_for']))) {
                    access_denied('I_ERROR');
                }
                if ((!has_privilege(get_member(), 'see_unvalidated')) && (addon_installed('unvalidated')) && ($_postdetails[0]['p_validated'] == 0) && (($_postdetails[0]['p_poster'] != get_member()) || ((is_guest($_postdetails[0]['p_poster'])) && ($_postdetails[0]['p_ip_address'] != get_ip_address())))) {
                    access_denied('I_ERROR');
                }
            }

            $_topic = $GLOBALS['FORUM_DB']->query_select('f_topics', array('t_pt_to', 't_pt_from', 't_cache_first_title'), array('id' => $_postdetails[0]['p_topic_id']), '', 1);
            if (!array_key_exists(0, $_topic)) {
                warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
            }

            if (!cns_may_access_topic($_postdetails[0]['p_topic_id'])) {
                access_denied('I_ERROR');
            }

            $post->attach(do_template('CNS_QUOTE_FCOMCODE', array(
                '_GUID' => '5542508cad43a0cd5798afbb06f9e616',
                'ID' => strval($quote),
                'TITLE' => $_topic[0]['t_cache_first_title'],
                'POST' => comcode_censored_raw_code_access(get_translated_text($_postdetails[0]['p_post'], $GLOBALS['FORUM_DB'])),
                'BY' => $_postdetails[0]['p_poster_name_if_guest'],
                'BY_ID' => strval($_postdetails[0]['p_poster']),
            ), null, false, null, '.txt', 'text'));
        }

        return $post;
    }

    /**
     * The form element for choosing a post template.
     *
     * @param  AUTO_LINK $forum_id The forum ID we are looking for post templates active in
     * @return array A pair: The form element (Tempcode) and the default post to make
     */
    public function post_templates($forum_id)
    {
        if (!addon_installed('cns_post_templates')) {
            return array(new Tempcode(), '');
        }

        require_lang('cns_post_templates');

        $specialisation = new Tempcode();

        require_code('cns_posts_action');
        $templates = cns_get_post_templates($forum_id);
        $post_templates = new Tempcode();
        $post = '';
        foreach ($templates as $template) {
            list($title, $text, $default) = $template;
            if ($default) {
                $post = $text;
            }
            $post_templates->attach(form_input_list_entry(str_replace("\n", '\n', $text), $default == 1, $title));
        }
        if ((!$post_templates->is_empty()) && (has_js())) {
            $post_templates2 = form_input_list_entry('', false, do_lang_tempcode('NA_EM'));
            $post_templates2->attach($post_templates);

            $input = do_template('CNS_POST_TEMPLATE_SELECT', array('_GUID' => '2e4270e8fb8050f0201f5aa2af56270a', 'TABINDEX' => '3', 'LIST' => $post_templates2));
            $specialisation->attach(_form_input('post_template', do_lang_tempcode('POST_TEMPLATE'), do_lang_tempcode('DESCRIPTION_POST_TEMPLATE'), $input, false, false));
        }

        return array($specialisation, $post);
    }

    /**
     * The UI to create a new topic.
     *
     * @param  boolean $private_topic Whether a new Private Topic is being created
     * @param  ?MEMBER $member_id The member ID being whispered too (null: N/A)
     * @param  string $img_path Theme image code
     * @param  ?Tempcode $text Text of screen (null: none)
     * @return Tempcode The UI
     */
    public function new_topic($private_topic = false, $member_id = null, $img_path = '', $text = null)
    {
        if (!$private_topic) {
            $forum_id = get_param_integer('id');

            require_code('cns_topics');
            if (!cns_may_post_topic($forum_id, get_member())) {
                access_denied('I_ERROR');
            }
        } else {
            $forum_id = null;
        }

        $javascript = '';

        $staff_help_url = null;

        // Breadcrumbs etc
        if ($private_topic) {
            breadcrumb_set_parents(array(array('_SEARCH:forumview:pt', do_lang_tempcode('PRIVATE_TOPICS'))));

            $username = mixed();
            $username = ($member_id == get_member()) ? false : $GLOBALS['FORUM_DRIVER']->get_username($member_id);
            if (is_null($username)) {
                warn_exit(do_lang_tempcode('MEMBER_NO_EXIST'));
            }
        } else {
            if (!is_null($forum_id)) {
                if (!has_category_access(get_member(), 'forums', strval($forum_id))) {
                    access_denied('CATEGORY_ACCESS'); // Can happen if trying to reply to a stated whisper made to you in a forum you don't have access to
                }
            }

            $breadcrumbs = cns_forum_breadcrumbs($forum_id, null, null, false);
            breadcrumb_set_self(do_lang_tempcode('ADD_TOPIC'));
            breadcrumb_set_parents($breadcrumbs);
        }

        url_default_parameters__enable();

        $hidden_fields = new Tempcode();
        $specialisation = new Tempcode();
        $specialisation2 = new Tempcode();

        // Where to post to
        $map = array('page' => '_SELF', 'type' => '_add_reply');
        $redirect = get_param_string('redirect', '');
        if ($redirect != '') {
            $map['redirect'] = $redirect;
        }
        $post_url = build_url($map, '_SELF');

        // Cloning support
        $clone_id = get_param_integer('clone_id', null);
        $existing_title = post_param_string('title', '');
        $existing_description = post_param_string('description', '');
        $post = post_param_string('post', '');
        if (!is_null($clone_id)) {
            $post_rows = $GLOBALS['FORUM_DB']->query_select('f_posts p JOIN ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_topics t ON t.id=p.p_topic_id', array('p.*', 't.t_description'), array('t.id' => $clone_id), '', 1);
            if ((array_key_exists(0, $post_rows)) && ($post_rows[0]['p_cache_forum_id'] !== null) && (has_category_access(get_member(), 'forums', strval($post_rows[0]['p_cache_forum_id'])))) {
                $existing_title = $post_rows[0]['p_title'];
                $existing_description = $post_rows[0]['t_description'];
                require_code('comcode_cleanup');
                $post = comcode_censored_raw_code_access(get_translated_text($post_rows[0]['p_post'], $GLOBALS['FORUM_DB']));
            }
        }

        // Title
        $specialisation->attach(form_input_line(do_lang_tempcode('TITLE'), '', 'title', $existing_title, true, 1, 120));

        // Where it goes to
        if ($private_topic) {
            if ($GLOBALS['FORUM_DRIVER']->is_banned($member_id)) {
                warn_exit(do_lang_tempcode('mail:NO_ACCEPT_EMAILS'));
            }

            if ($member_id == get_member()) {
                $specialisation->attach(form_input_username_multi(do_lang_tempcode('TO'), '', 'to_member_id_', array(), 1, true, 1));
            } else {
                $hidden_fields->attach(form_input_hidden('member_id', strval($member_id)));
            }
            $threaded = false;
        } else {
            $hidden_fields->attach(form_input_hidden('forum_id', strval($forum_id)));
            $_threaded = ($GLOBALS['FORUM_DB']->query_select_value_if_there('f_forums', 'f_is_threaded', array('id' => $forum_id)) == 1);
            if (is_null($_threaded)) {
                warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'forum'));
            }
            $threaded = ($_threaded === 1);
        }

        // Description
        if ((get_option('is_on_topic_descriptions') == '1') && (!$threaded)) {
            $field = form_input_line(do_lang_tempcode('DESCRIPTION'), '', 'description', $existing_description, false, 2);
            if (is_mobile()) {
                $specialisation2->attach($field);
            } else {
                $specialisation->attach($field);
            }
        }

        // Set up some post details
        $quote = get_param_integer('quote', -1);
        if ($quote != -1) {
            $_postdetails = $this->attach_quotes(array($quote));
            $post = $_postdetails->evaluate();
        }
        if (!$private_topic) {
            list($post_templates, $post2) = $this->post_templates($forum_id);
            if ($post == '') {
                $post = $post2;
            }
            $specialisation->attach($post_templates);
        }
        if (get_option('is_on_topic_emoticons') == '1') {
            $field = $this->choose_topic_emoticon($img_path);
            if (is_mobile()) {
                $specialisation2->attach($field);
            } else {
                $specialisation->attach($field);
            }
        }

        if (is_guest()) {
            $specialisation->attach(form_input_line(do_lang_tempcode('GUEST_NAME'), new Tempcode(), 'poster_name_if_guest', (get_option('force_guest_names') == '1') ? '' : do_lang('GUEST'), true));
        }

        // Various kinds of tick options
        if ((!$private_topic) && (cns_may_moderate_forum($forum_id, get_member()))) {
            $moderation_options = array(
                array(do_lang_tempcode('OPEN'), 'open', true, do_lang_tempcode('DESCRIPTION_OPEN')),
                array(do_lang_tempcode('EMPHASISED'), 'is_emphasised', false, do_lang_tempcode('DESCRIPTION_EMPHASISED')),
                array(do_lang_tempcode('PINNED'), 'pinned', false, do_lang_tempcode('DESCRIPTION_PINNED')),
            );
            if (addon_installed('unvalidated')) {
                $moderation_options[] = array(do_lang_tempcode('VALIDATED'), 'validated', true, do_lang_tempcode($GLOBALS['FORUM_DRIVER']->is_super_admin(get_member()) ? 'DESCRIPTION_VALIDATED_SIMPLE' : 'DESCRIPTION_VALIDATED', 'topic'));
            }
            if (get_option('enable_sunk') == '1') {
                $moderation_options[] = array(do_lang_tempcode('SUNK'), 'sunk', false, do_lang_tempcode('DESCRIPTION_SUNK'));
            }
            if (!$private_topic) {
                $moderation_options[] = array(do_lang_tempcode('CASCADING'), 'cascading', false, do_lang_tempcode('DESCRIPTION_CASCADING'));
            }
            if (addon_installed('calendar')) {
                $specialisation2->attach(form_input_date__scheduler(do_lang_tempcode('CNS_PUBLICATION_TIME'), do_lang_tempcode('CNS_DESCRIPTION_PUBLICATION_TIME'), 'schedule', false, true, true));
            }
        } else {
            $hidden_fields->attach(form_input_hidden('open', '1'));
            $hidden_fields->attach(form_input_hidden('validated', '1'));
            $moderation_options = array();
        }
        $hidden_fields->attach(form_input_hidden('from_url', get_self_url(true)));
        $options = array();
        if (!is_guest()) {
            if (addon_installed('cns_signatures')) {
                if (get_option('enable_skip_sig') == '1') {
                    $options[] = array(do_lang_tempcode('SKIP_SIGNATURE'), 'skip_sig', false, do_lang_tempcode('DESCRIPTION_SKIP_SIGNATURE'));
                }
            }
            if (cns_forum_allows_anonymous_posts($forum_id)) {
                $options[] = array(do_lang_tempcode('_MAKE_ANONYMOUS_POST'), 'anonymous', false, do_lang_tempcode('MAKE_ANONYMOUS_POST_DESCRIPTION'));
            }
        }
        $options[] = array(do_lang_tempcode('ADD_TOPIC_POLL'), 'add_poll', false, do_lang_tempcode('DESCRIPTION_ADD_TOPIC_POLL'));
        if (count($options) == 1) { // Oh, actually we know this was just the option to add a poll, so show simply
            $specialisation->attach(form_input_tick(do_lang_tempcode('ADD_TOPIC_POLL'), do_lang_tempcode('DESCRIPTION_ADD_TOPIC_POLL'), 'add_poll', false));
        } else {
            $specialisation2->attach(form_input_various_ticks($options, ''));
        }
        if (count($moderation_options) != 0) {
            $specialisation2->attach(form_input_various_ticks($moderation_options, '', null, do_lang_tempcode('MODERATION_OPTIONS')));
        }

        // Custom fields?
        require_code('fields');
        if (has_tied_catalogue('topic')) {
            append_form_custom_fields('topic', is_null($clone_id) ? null : strval($clone_id), $specialisation, $hidden_fields);
        }
        if (has_tied_catalogue('post')) {
            append_form_custom_fields('post', is_null($clone_id) ? null : strval($clone_id), $specialisation, $hidden_fields);
        }

        require_code('content2');
        $specialisation2->attach(metadata_get_fields('topic', null, false, array('submitter', 'add_time', 'edit_time')));

        if (is_null($text)) {
            $text = new Tempcode();
        }

        // CAPTCHA?
        if (addon_installed('captcha')) {
            require_code('captcha');
            if (use_captcha()) {
                $specialisation->attach(form_input_captcha());
                $text->attach(paragraph(do_lang_tempcode('FORM_TIME_SECURITY')));
            }
        }

        // Note about points?
        if (addon_installed('points')) {
            $login_url = build_url(array('page' => 'login', 'type' => 'browse', 'redirect' => get_self_url(true, true)), get_module_zone('login'));
            $_login_url = escape_html($login_url->evaluate());
            if ((is_guest()) && ((get_forum_type() != 'cns') || (has_actual_page_access(get_member(), 'join')))) {
                $text->attach(paragraph(do_lang_tempcode('NOT_LOGGED_IN_NO_CREDIT', $_login_url)));
            }
        }

        // Needs validating?
        if ((!is_null($forum_id)) && (!has_privilege(get_member(), 'bypass_validation_midrange_content', 'topics', array('forums', $forum_id)))) {
            $text->attach(paragraph(do_lang_tempcode('WILL_NEED_VALIDATING')));
        }

        // Awards?
        if (addon_installed('awards')) {
            require_code('awards');
            $specialisation2->attach(get_award_fields(array('topic', 'post')));
        }

        // Render form
        $posting_form = get_posting_form(do_lang($private_topic ? 'ADD_PRIVATE_TOPIC' : 'ADD_TOPIC'), 'buttons__new_topic', $post, $post_url, $hidden_fields, $specialisation, null, '', $specialisation2, null, $this->_post_javascript() . (function_exists('captcha_ajax_check') ? captcha_ajax_check() : ''), null, true, true, false, true, is_mobile());

        url_default_parameters__disable();

        // Work out title to show
        if (!$private_topic) {
            $forum_name = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_forums', 'f_name', array('id' => $forum_id));
            if (is_null($forum_name)) {
                warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'forum'));
            }
        }
        if ($private_topic) {
            if ($username === false) {
                $title = do_lang_tempcode('_ADD_PRIVATE_TOPIC_UNKNOWN');
            } else {
                $title = do_lang_tempcode('_ADD_PRIVATE_TOPIC', escape_html($username));
            }
        } else {
            $title = do_lang_tempcode('_ADD_TOPIC', escape_html($forum_name));
        }
        $_title = get_screen_title($title, false);

        return do_template('POSTING_SCREEN', array('_GUID' => 'ba5308fe0a8f9f9a24988209423a3a16', 'STAFF_HELP_URL' => $staff_help_url, 'TEXT' => $text, 'TITLE' => $_title, 'POSTING_FORM' => $posting_form));
    }

    /**
     * The UI to create a PT.
     *
     * @return Tempcode The UI
     */
    public function new_pt() // Type
    {
        if (is_guest()) {
            access_denied('NOT_AS_GUEST');
        }

        $member_id = get_param_integer('intended_solely_for', get_param_integer('id', get_member()));

        cns_check_make_private_topic();
        require_code('cns_members2');
        if (!cns_may_whisper($member_id)) {
            warn_exit(do_lang_tempcode('NO_PT_FROM_ALLOW'));
        }

        if (get_option('enable_pt_restrict') == '1') {
            $agreed = get_param_integer('agreed', 0);
            $member_row = $GLOBALS['FORUM_DRIVER']->get_member_row($member_id);
            $just_member_row = db_map_restrict($member_row, array('id', 'm_pt_rules_text'));
            $rules = get_translated_tempcode('f_members', $just_member_row, 'm_pt_rules_text', $GLOBALS['FORUM_DB']);
            if (($agreed == 0) && (!$rules->is_empty())) {
                $url = get_self_url(false, false, array('agreed' => '1'));
                $title = get_screen_title('NEW_PRIVATE_TOPIC');
                return do_template('CNS_MEMBER_PT_RULES_SCREEN', array('_GUID' => '0c39906d4aeb728cc386cd9a79a338c7', 'TITLE' => $title, 'USERNAME' => $GLOBALS['FORUM_DRIVER']->get_username($member_id), 'MEMBER_ID' => strval($member_id), 'URL' => $url, 'RULES' => $rules));
            }
        }

        $text = do_lang_tempcode('PRIVATE_TOPIC_DOC');

        return $this->new_topic(true, $member_id, '', $text);
    }

    /**
     * Sort out breadcrumbing for a forum/topic/additional combination.
     *
     * @param  AUTO_LINK $forum_id The forum for breadcrumbing
     * @param  AUTO_LINK $topic_id The topic for breadcrumbing
     * @param  string $topic_title The topic title
     * @param  Tempcode $doing The action currently being done
     */
    public function handle_topic_breadcrumbs($forum_id, $topic_id, $topic_title, $doing)
    {
        if (is_null($forum_id)) {
            breadcrumb_set_parents(array(array('_SEARCH:forumview:pt', do_lang_tempcode('PRIVATE_TOPICS')), array('_SEARCH:topicview:' . strval($topic_id), $topic_title)));
        } else {
            $breadcrumbs = cns_forum_breadcrumbs($forum_id, null, null, false);
            $breadcrumbs[] = array('_SEARCH:topicview:browse:' . strval($topic_id), $topic_title);
            $breadcrumbs[] = array('', $doing);
            breadcrumb_set_parents($breadcrumbs);
        }
    }

    /**
     * The UI to make a post.
     *
     * @return Tempcode The UI
     */
    public function new_post()
    {
        require_code('cns_posts2');

        url_default_parameters__enable();

        $topic_id = get_param_integer('id');
        $parent_id = either_param_integer('parent_id', null);
        $intended_solely_for = get_param_integer('intended_solely_for', -1);
        $post = mixed();
        $post = post_param_string('post', null); // Copy existing post into box (from quick reply 'more options' button)
        if (is_null($post)) {
            $quotes = array();
            $quote = get_param_integer('quote', -1);
            if ($quote == -1) {
                $quotes = $this->get_markers();
            } else {
                $quotes[] = $quote;
            }
            $post = $this->attach_quotes($quotes);

            if ((count($quotes) == 1) && (is_null($parent_id))) {
                $parent_id = $quotes[0];
            }
        }

        $whisperer = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_posts', 'p_poster', array('p_topic_id' => $topic_id, 'p_intended_solely_for' => get_member()), 'ORDER BY p_time DESC');
        if (!is_null($whisperer)) {
            $_whisperer = $GLOBALS['FORUM_DRIVER']->get_username($whisperer);
            if (!is_null($_whisperer)) {
                attach_message(do_lang_tempcode('TOPIC_HAS_WHISPER_TO_YOU', escape_html($_whisperer)), 'notice');
            }
        }

        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        $forum_id = $topic_info[0]['t_forum_id'];
        $topic_title = $topic_info[0]['t_cache_first_title'];
        if ($topic_title == '') {
            $topic_title = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_posts', 'p_title', array('p_topic_id' => $topic_id));
        }
        if (is_null($topic_title)) {
            $topic_title = '';
        }
        if (!cns_may_access_topic($topic_id)) { // cns_may_post_in_topic will be checked on actualiser, we can't check now as we don't know if it will be an inline personal post or not
            access_denied('I_ERROR');
        }
        $this->handle_topic_breadcrumbs($forum_id, $topic_id, $topic_title, do_lang_tempcode('ADD_POST'));

        if ($topic_info[0]['t_is_open'] == 0) {
            $may_moderate_forum = cns_may_moderate_forum($forum_id);
            if (!$may_moderate_forum) {
                warn_exit(do_lang_tempcode('TOPIC_IS_CLOSED'));
            }
        }

        $hidden_fields = new Tempcode();
        $hidden_fields->attach(form_input_hidden('topic_id', strval($topic_id)));
        $hidden_fields->attach(form_input_hidden('from_url', get_self_url(true, false, array('type' => get_param_string('type', 'browse')))));

        $map = array('page' => '_SELF', 'type' => '_add_reply', 'parent_id' => $parent_id, 'timestamp' => get_param_integer('timestamp', null));
        $test = get_param_string('kfs' . (is_null($forum_id) ? '' : strval($forum_id)), null, true);
        if (($test !== null) && ($test !== '0')) {
            $map['kfs' . (is_null($forum_id) ? '' : strval($forum_id))] = $test;
        }
        $test_threaded = get_param_integer('threaded', null);
        if ($test_threaded !== null) {
            $map['threaded'] = $test_threaded;
        }
        $post_url = build_url($map, '_SELF');

        // Certain aspects relating to the posting system
        $specialisation = new Tempcode();
        if (get_option('is_on_post_titles') == '1') {
            $specialisation->attach(form_input_line(do_lang_tempcode('TITLE'), '', 'title', post_param_string('title', ''), false, 1, 120));
        }
        if (cns_may_moderate_forum($forum_id, get_member())) {
            $moderation_options = array(
                array(do_lang_tempcode('EMPHASISED'), 'is_emphasised', false, do_lang_tempcode('DESCRIPTION_EMPHASISED'))
            );
            if (addon_installed('unvalidated')) {
                $moderation_options[] = array(do_lang_tempcode('VALIDATED'), 'validated', true, do_lang_tempcode($GLOBALS['FORUM_DRIVER']->is_super_admin(get_member()) ? 'DESCRIPTION_VALIDATED_SIMPLE' : 'DESCRIPTION_VALIDATED', 'post'));
            }
            //if ($intended_solely_for == -1) $moderation_options[] = array(do_lang_tempcode('CASCADING'), 'cascading', false, do_lang_tempcode('DESCRIPTION_CASCADING'));     Too much to offer this too
        } else {
            $moderation_options = array();
            $hidden_fields->attach(form_input_hidden('validated', '1'));
        }
        $options = array();
        if (!is_guest()) {
            if (addon_installed('cns_signatures')) {
                if (get_option('enable_skip_sig') == '1') {
                    $options[] = array(do_lang_tempcode('SKIP_SIGNATURE'), 'skip_sig', false, do_lang_tempcode('DESCRIPTION_SKIP_SIGNATURE'));
                }
            }
            if (cns_forum_allows_anonymous_posts($forum_id)) {
                $options[] = array(do_lang_tempcode('_MAKE_ANONYMOUS_POST'), 'anonymous', false, do_lang_tempcode('MAKE_ANONYMOUS_POST_DESCRIPTION'));
            }
        }
        $specialisation2 = new Tempcode();
        $specialisation2->attach(form_input_various_ticks($options, ''));
        if (count($moderation_options) != 0) {
            $specialisation2->attach(form_input_various_ticks($moderation_options, '', null, do_lang_tempcode('MODERATION_OPTIONS')));
        }

        if (!is_null($forum_id)) {
            $post_templates = $this->post_templates($forum_id);
            $specialisation->attach($post_templates[0]);
            if (((is_object($post)) && ($post->is_empty())) || ($post === '')) {
                $post = $post_templates[1];
            }
        }

        $_intended_solely_for = '';
        if ($intended_solely_for != -1) {
            $_intended_solely_for = $GLOBALS['FORUM_DRIVER']->get_username($intended_solely_for);
            if (is_null($_intended_solely_for)) {
                $_intended_solely_for = '';
            }
        }
        if (cns_may_make_private_topic()) {
            if ((($_intended_solely_for != '') || (get_option('inline_pp_advertise') == '1')) && (!is_null($forum_id))) {
                url_default_parameters__disable();

                $specialisation->attach(form_input_username(do_lang_tempcode('WHISPER'), do_lang_tempcode('DESCRIPTION_WHISPER'), 'intended_solely_for', $_intended_solely_for, false));

                url_default_parameters__enable();
            }
        }

        if (is_guest()) {
            $specialisation->attach(form_input_line(do_lang_tempcode('GUEST_NAME'), '', 'poster_name_if_guest', (get_option('force_guest_names') == '1') ? '' : do_lang('GUEST'), true));
        }

        require_code('fields');
        if (has_tied_catalogue('post')) {
            append_form_custom_fields('post', null, $specialisation, $hidden_fields);
        }

        $text = new Tempcode();

        if (addon_installed('captcha')) {
            require_code('captcha');
            if (use_captcha()) {
                $specialisation->attach(form_input_captcha());
                $text->attach(paragraph(do_lang_tempcode('FORM_TIME_SECURITY')));
            }
        }

        if (addon_installed('points')) {
            $login_url = build_url(array('page' => 'login', 'type' => 'browse', 'redirect' => get_self_url(true, true)), get_module_zone('login'));
            $_login_url = escape_html($login_url->evaluate());
            if ((is_guest()) && ((get_forum_type() != 'cns') || (has_actual_page_access(get_member(), 'join')))) {
                $text->attach(paragraph(do_lang_tempcode('NOT_LOGGED_IN_NO_CREDIT', $_login_url)));
            }
        }

        if ((!is_null($forum_id)) && (!has_privilege(get_member(), 'bypass_validation_lowrange_content', 'topics', array('forums', $forum_id)))) {
            $text->attach(paragraph(do_lang_tempcode('WILL_NEED_VALIDATING')));
        }

        // Awards?
        if (addon_installed('awards')) {
            require_code('awards');
            $specialisation2->attach(get_award_fields('post'));
        }

        // Moderator reply
        if (cns_may_moderate_forum($forum_id, get_member())) {
            require_code('cns_forums2');

            $specialisation2->attach(do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => '061fdbebcc17e08e8d6ff2c329f3d483', 'SECTION_HIDDEN' => true, 'TITLE' => do_lang_tempcode('TOPIC_MODERATION'))));
            $specialisation2->attach(form_input_line(do_lang_tempcode('TITLE'), '', 'new_title', $topic_title, false));
            $specialisation2->attach(form_input_tree_list(do_lang_tempcode('DESTINATION_FORUM'), do_lang_tempcode('DESCRIPTION_DESTINATION_FORUM'), 'to', null, 'choose_forum', array(), false, is_null($forum_id) ? '' : strval($forum_id)));
            $options = array(
                array(do_lang_tempcode('OPEN'), 'open', $topic_info[0]['t_is_open'] == 1, do_lang_tempcode('DESCRIPTION_OPEN')),
                array(do_lang_tempcode('PINNED'), 'pinned', $topic_info[0]['t_pinned'] == 1, do_lang_tempcode('DESCRIPTION_PINNED')),
            );
            if (addon_installed('unvalidated')) {
                if ($topic_info[0]['t_validated'] == 0) {
                    $topic_info[0]['t_validated'] = get_param_integer('validated', 0);
                    if (($topic_info[0]['t_validated'] == 1) && (addon_installed('unvalidated'))) {
                        attach_message(do_lang_tempcode('WILL_BE_VALIDATED_WHEN_SAVING'));
                    }
                }

                $options[] = array(do_lang_tempcode('VALIDATED'), 'topic_validated', $topic_info[0]['t_validated'] == 1, do_lang_tempcode($GLOBALS['FORUM_DRIVER']->is_super_admin(get_member()) ? 'DESCRIPTION_VALIDATED_SIMPLE' : 'DESCRIPTION_VALIDATED', 'topic'));
            }
            if (get_option('enable_sunk') == '1') {
                $moderation_options[] = array(do_lang_tempcode('SUNK'), 'sunk', $topic_info[0]['t_sunk'] == 1, do_lang_tempcode('DESCRIPTION_SUNK'));
            }
            if (!is_null($forum_id)) {
                $options[] = array(do_lang_tempcode('CASCADING'), 'cascading', $topic_info[0]['t_cascading'] == 1, do_lang_tempcode('DESCRIPTION_CASCADING'));
            }
            $specialisation2->attach(form_input_various_ticks($options, ''));
            if (addon_installed('calendar')) {
                $specialisation2->attach(form_input_date__scheduler(do_lang_tempcode('CNS_PUBLICATION_TIME'), do_lang_tempcode('CNS_DESCRIPTION_PUBLICATION_TIME'), 'schedule', false, true, true));
            }
        }

        require_code('content2');
        $specialisation2->attach(metadata_get_fields('post', null));

        $topic_posts = new Tempcode();
        $posts = $GLOBALS['FORUM_DB']->query('SELECT *,p.id AS id FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_posts p WHERE p_topic_id=' . strval($topic_id) . ' AND (p_intended_solely_for IS NULL OR p_intended_solely_for=' . strval(get_member()) . ' OR p_poster=' . strval(get_member()) . ') AND p_validated=1 ORDER BY p_time DESC,p.id DESC', 20);
        foreach ($posts as $row) {
            $topic_posts->attach(render_post_box($row, true, false, false));
        }
        if (!$topic_posts->is_empty()) {
            $topic_posts = do_template('CNS_POSTING_SCREEN_POSTS', array('_GUID' => '3d3b14cf3a48b2a16eed5b1bd92b1187', 'POSTS' => $topic_posts));
        }

        if (is_object($post)) {
            $post = $post->evaluate();
        }
        $posting_form = get_posting_form(do_lang('REPLY'), 'buttons__new_reply', $post, $post_url, $hidden_fields, $specialisation, null, $topic_posts->evaluate(), $specialisation2, null, $this->_post_javascript());

        if (is_null($parent_id)) {
            if (!is_null($forum_id) && get_param_integer('threaded', $GLOBALS['FORUM_DB']->query_select_value('f_forums', 'f_is_threaded', array('id' => $forum_id))) == 1) {
                $title = get_screen_title('_ADD_POST_TOP_LEVEL', true, array(escape_html($topic_title)));
            } else {
                $title = get_screen_title('_ADD_POST', true, array(escape_html($topic_title)));
            }
        } else {
            $_poster_name_if_guest = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_posts', 'p_poster_name_if_guest', array('id' => $parent_id));
            if ($_poster_name_if_guest === null) {
                warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'post'));
            }
            $title = get_screen_title('_ADD_POST_UNDER', true, array(escape_html($topic_title), escape_html($_poster_name_if_guest)));
        }

        if (post_param_integer('add_poll', 0) == 1) {
            // Show it worked / Refresh
            $url = build_url(array('page' => '_SELF', 'type' => 'add_poll'), '_SELF');
            return redirect_screen($title, $url, do_lang_tempcode('SUCCESS'));
        }

        url_default_parameters__disable();

        return do_template('POSTING_SCREEN', array('_GUID' => 'ca2eab9a9ffdab267a48eb7be48ccdc0', 'TEXT' => $text, 'TITLE' => $title, 'POSTING_FORM' => $posting_form));
    }

    /**
     * The UI to report a post.
     *
     * @return Tempcode The UI
     */
    public function report_post() // Type
    {
        if (!cns_may_report_post()) {
            access_denied('I_ERROR');
        }

        $post_id = get_param_integer('id');

        $post_info = $GLOBALS['FORUM_DB']->query_select('f_posts', array('*'), array('id' => $post_id), '', 1);
        if (!array_key_exists(0, $post_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'post'));
        }
        $topic_id = $post_info[0]['p_topic_id'];
        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }

        $this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('REPORT_POST'));

        $this->check_has_mod_access($topic_id);

        $poster = $post_info[0]['p_poster_name_if_guest'];

        $member_id = $post_info[0]['p_poster'];
        $name = $poster;
        if (!is_guest($post_info[0]['p_poster'])) {
            $name = $GLOBALS['FORUM_DRIVER']->get_username($post_info[0]['p_poster'], true);
            if ($name === null) {
                $name = $poster;
            }
        }

        $_postdetails = post_param_string('post', null);
        if (is_null($_postdetails)) {
            require_code('comcode_cleanup');
            $__post = comcode_censored_raw_code_access(get_translated_text($post_info[0]['p_post'], $GLOBALS['FORUM_DB']), $post_info[0]['p_poster']);
            $post = do_template('CNS_REPORTED_POST_FCOMCODE', array(
                '_GUID' => 'e0f65423f3cb7698d5f04431dbe52ddb',
                'POST_ID' => strval($post_id),
                'MEMBER_ID' => strval($member_id),
                'NAME' => $name,
                'TOPIC_TITLE' => $topic_info[0]['t_cache_first_title'],
                'POST' => $__post,
                'POSTER' => $poster,
            ), null, false, null, '.txt', 'text');
        } else {
            $post = make_string_tempcode($_postdetails);
        }

        url_default_parameters__enable();

        $forum_id = $GLOBALS['FORUM_DRIVER']->forum_id_from_name(get_option('reported_posts_forum'));
        if (is_null($forum_id)) {
            warn_exit(do_lang_tempcode('cns:NO_REPORTED_POST_FORUM'));
        }

        $hidden_fields = new Tempcode();
        if (!is_guest()) {
            $options = array();
            if (cns_forum_allows_anonymous_posts($forum_id)) {
                $options[] = array(do_lang_tempcode('_MAKE_ANONYMOUS_POST'), 'anonymous', false, do_lang_tempcode('MAKE_ANONYMOUS_POST_DESCRIPTION'));
            }
            $specialisation = form_input_various_ticks($options, '');
        } else {
            $specialisation = new Tempcode();
        }

        $hidden_fields->attach(form_input_hidden('forum_id', '-2'));
        $hidden_fields->attach(form_input_hidden('validated', '1'));
        $report_title = $post_info[0]['p_title'];
        if ($report_title == '') {
            $report_title = $topic_info[0]['t_cache_first_title'];
        }
        $hidden_fields->attach(form_input_hidden('title', do_lang('REPORTED_POST_TITLE', $report_title)));
        $hidden_fields->attach(form_input_hidden('o_post_id', strval($post_id)));
        $hidden_fields->attach(form_input_hidden('from_url', get_self_url(true)));

        $text = new Tempcode();

        if (addon_installed('captcha')) {
            require_code('captcha');
            if (use_captcha()) {
                $specialisation->attach(form_input_captcha());
                $text->attach(paragraph(do_lang_tempcode('FORM_TIME_SECURITY')));
            }
        }

        if (addon_installed('points')) {
            $login_url = build_url(array('page' => 'login', 'type' => 'browse', 'redirect' => get_self_url(true, true)), get_module_zone('login'));
            $_login_url = escape_html($login_url->evaluate());
            if ((is_guest()) && ((get_forum_type() != 'cns') || (has_actual_page_access(get_member(), 'join')))) {
                $text->attach(paragraph(do_lang_tempcode('NOT_LOGGED_IN_NO_CREDIT', $_login_url)));
            }
        }

        $post_url = build_url(array('page' => '_SELF', 'type' => '_add_reply'), '_SELF');

        $posting_form = get_posting_form(do_lang('REPORT_POST'), 'buttons__report', $post->evaluate(), $post_url, $hidden_fields, $specialisation, '', '', null, null, $this->_post_javascript() . (function_exists('captcha_ajax_check') ? captcha_ajax_check() : ''), null, true, false, true);

        url_default_parameters__disable();

        $title = get_screen_title('REPORT_POST');
        return do_template('POSTING_SCREEN', array('_GUID' => 'eee64757e66fed702f74fecf8d595260', 'TITLE' => $title, 'TEXT' => $text, 'POSTING_FORM' => $posting_form));
    }

    /**
     * The actualiser to add a reply.
     *
     * @return Tempcode The UI
     */
    public function _add_reply() // Type
    {
        if (addon_installed('captcha')) {
            require_code('captcha');
            enforce_captcha();
        }

        cms_profile_start_for('_add_reply');

        require_code('attachments2');
        require_code('cns_posts_action');
        require_code('cns_posts_action2');

        $invited_members = array();

        $topic_id = either_param_integer('topic_id', -1); // Posting into an existing topic?
        $forum_id = post_param_integer('forum_id', -1); // New topic in existing forum? (NB: -2 represents reported posts forum)
        $member_id = post_param_integer('member_id', -1); // Send TOPIC to specific member? Could be Private Topic (topic_id==-1, forum_id==-1), or personal post (topic_id!=-1, forum_id==-1)
        $parent_id = either_param_integer('parent_id', null);
        if ($member_id == -1) {
            $member_username = post_param_string('to_member_id_0', '');
            if ($member_username != '') {
                $member_id = $GLOBALS['FORUM_DRIVER']->get_member_from_username($member_username);
                if (($member_id === null) || (is_guest($member_id))) {
                    warn_exit(do_lang_tempcode('_MEMBER_NO_EXIST', escape_html($member_username)));
                }
                if ($member_id == get_member()) {
                    warn_exit(do_lang_tempcode('NO_PRIVATE_SELF'));
                }
            }
            foreach ($_POST as $key => $_invited_member) {
                if (substr($key, 0, 13) != 'to_member_id_') {
                    continue;
                }
                if ($key == 'to_member_id_0') {
                    continue;
                }
                if ($_invited_member == '') {
                    continue;
                }

                if (@get_magic_quotes_gpc()) {
                    $_invited_member = stripslashes($_invited_member);
                }

                $invited_member = $GLOBALS['FORUM_DRIVER']->get_member_from_username($_invited_member);
                if (($invited_member === null) || (is_guest($invited_member))) {
                    attach_message(do_lang_tempcode('_MEMBER_NO_EXIST', escape_html($_invited_member)), 'warn');
                } else {
                    $invited_members[] = intval($invited_member);
                }
            }
        }

        $validated = post_param_integer('validated', post_param_integer('_validated', 0));
        $is_emphasised = post_param_integer('is_emphasised', 0);
        $skip_sig = post_param_integer('skip_sig', 0);

        $post = post_param_string('post');
        $title = post_param_string('title', null);
        if (is_null($title)) {
            $title = '';
        }

        $check_permissions = true;
        $add_poll = post_param_integer('add_poll', 0);
        $topic_validated = $validated;
        if ($validated == 1) {
            $topic_validated = 1 - $add_poll; // If a topic is gonna have a poll added, it starts unvalidated. Adding the poll will validate it.
        }

        $is_reported_post = false;

        $anonymous = post_param_integer('anonymous', 0);
        $poster_name_if_guest = cns_get_safe_specified_poster_name();

        $new_topic = ($topic_id == -1);

        if (!$new_topic) {
            $_intended_solely_for = post_param_string('intended_solely_for', '');
            if ($_intended_solely_for == '') {
                $intended_solely_for = null;
            } else {
                $intended_solely_for = $GLOBALS['FORUM_DRIVER']->get_member_from_username($_intended_solely_for);
                if (($intended_solely_for === null) || (is_guest($intended_solely_for))) {
                    warn_exit(do_lang_tempcode('_MEMBER_NO_EXIST', escape_html($_intended_solely_for)));
                }
                if ($intended_solely_for == get_member()) {
                    warn_exit(do_lang_tempcode('NO_PRIVATE_SELF'));
                }
            }
        } else {
            $intended_solely_for = null;
        }

        require_code('cns_topics_action');
        require_code('cns_topics_action2');
        if ($new_topic) { // A new topic
            cns_check_post($post);

            if ($title == '') {
                warn_exit(do_lang_tempcode('NO_PARAMETER_SENT', 'title'));
            }

            $sunk = post_param_integer('sunk', 0);
            $topic_title = $title;

            require_code('content2');
            $metadata = actual_metadata_get_fields('topic', null, array('submitter', 'add_time', 'edit_time'));

            if ($forum_id == -1) { // New Private Topic
                if ($member_id == -1) {
                    warn_exit(do_lang_tempcode('NO_PARAMETER_SENT', 'to_member_id_0'));
                }

                cns_check_make_private_topic();
                require_code('cns_members2');
                if (!cns_may_whisper($member_id)) {
                    warn_exit(do_lang_tempcode('NO_PT_FROM_ALLOW'));
                }

                if ($anonymous == 1) {
                    if (cns_forum_allows_anonymous_posts(null)) {
                        $poster_name_if_guest = null;
                    } else {
                        $anonymous = 0;
                    }
                }

                $topic_id = cns_make_topic(null, post_param_string('description', ''), post_param_string('emoticon', ''), $topic_validated, post_param_integer('open', 0), post_param_integer('pinned', 0), $sunk, post_param_integer('cascading', 0), get_member(), $member_id, true, $metadata['views']);
                $first_post = true;
                $_title = get_screen_title('ADD_PRIVATE_TOPIC');
            } elseif ($forum_id == -2) { // New reported post topic
                if (!cns_may_report_post()) {
                    access_denied('I_ERROR');
                }

                $forum_id = $GLOBALS['FORUM_DRIVER']->forum_id_from_name(get_option('reported_posts_forum'));
                if (is_null($forum_id)) {
                    warn_exit(do_lang_tempcode('NO_REPORTED_POST_FORUM'));
                }

                if ($anonymous == 1) {
                    if (cns_forum_allows_anonymous_posts($forum_id)) {
                        $poster_name_if_guest = null;
                    } else {
                        $anonymous = 0;
                    }
                }

                // See if post already reported...
                $topic_id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics t LEFT JOIN ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_posts p ON p.id=t.t_cache_first_post_id', 't.id', array('p.p_title' => $title, 't.t_forum_id' => $forum_id));
                if (!is_null($topic_id)) {
                    // Already a topic
                    $first_post = false;
                } else { // New topic
                    $topic_id = cns_make_topic($forum_id, '', '', 1, 1, 0, 0, 0, null, null, false, $metadata['views']);
                    $first_post = true;
                }

                $_title = get_screen_title('REPORT_POST');
                $check_permissions = false;

                decache('main_staff_checklist');

                $is_reported_post = true;
            } else { // New topic
                if ($anonymous == 1) {
                    if (cns_forum_allows_anonymous_posts($forum_id)) {
                        $poster_name_if_guest = null;
                    } else {
                        $anonymous = 0;
                    }
                }

                $topic_id = cns_make_topic($forum_id, post_param_string('description', ''), post_param_string('emoticon', ''), $topic_validated, post_param_integer('open', 0), post_param_integer('pinned', 0), $sunk, post_param_integer('cascading', 0), null, null, true, $metadata['views']);
                $first_post = true;
                $_title = get_screen_title('ADD_TOPIC');

                $_topic_id = strval($topic_id);
                $schedule_code = <<<END
:\$GLOBALS['FORUM_DB']->query_update('f_topics',array('t_cache_first_time'=>time(),'t_validated'=>1),array('id'=>{$_topic_id}),'',1);
END;

                $schedule = post_param_date('schedule');

                if ((!is_null($schedule)) && (addon_installed('calendar'))) {
                    require_code('calendar');
                    $start_year = intval(date('Y', $schedule));
                    $start_month = intval(date('m', $schedule));
                    $start_day = intval(date('d', $schedule));
                    $start_hour = intval(date('H', $schedule));
                    $start_minute = intval(date('i', $schedule));
                    require_code('calendar2');
                    $event_id = add_calendar_event(db_get_first_id(), '', null, 0, do_lang('ADD_SCHEDULED_TOPIC', $topic_title), $schedule_code, 3, $start_year, $start_month, $start_day, 'day_of_month', $start_hour, $start_minute);
                    regenerate_event_reminder_jobs($event_id);

                    $GLOBALS['FORUM_DB']->query_update('f_topics', array('t_validated' => 0), array('id' => $topic_id), '', 1);
                }

                if (addon_installed('awards')) {
                    require_code('awards');
                    handle_award_setting('topic', strval($topic_id));
                }
            }

            set_url_moniker('topic', strval($topic_id));

            require_code('fields');
            if (has_tied_catalogue('topic')) {
                save_form_custom_fields('topic', strval($topic_id));
            }
        } else {
            $_title = get_screen_title('ADD_POST');
            $first_post = false;

            $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('t_cache_first_title', 't_sunk', 't_forum_id', 't_is_open', 't_description'), array('id' => $topic_id), '', 1);
            if (!array_key_exists(0, $topic_info)) {
                warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
            }
            $forum_id = $topic_info[0]['t_forum_id'];
            $topic_title = $topic_info[0]['t_cache_first_title'];
            $sunk = $topic_info[0]['t_sunk'];

            if ($anonymous == 1) {
                if (cns_forum_allows_anonymous_posts($forum_id)) {
                    $poster_name_if_guest = null;
                } else {
                    $anonymous = 0;
                }
            }

            if ($topic_info[0]['t_is_open'] == 0) {
                $may_moderate_forum = cns_may_moderate_forum($forum_id);
                if (!$may_moderate_forum) {
                    warn_exit(do_lang_tempcode('TOPIC_IS_CLOSED'));
                }
            }

            // Moderator reply
            $new_title = post_param_string('new_title', null);
            if ((!is_null($new_title)) && (!is_null($forum_id)) && (cns_may_moderate_forum($forum_id, get_member()))) {
                $cascading = post_param_integer('cascading', 0);
                $pinned = post_param_integer('pinned', 0);
                $sunk = post_param_integer('sunk', 0);
                $open = post_param_integer('open', 0);
                $topic_validated = post_param_integer('topic_validated', 0);
                $to = post_param_integer('to', null);
                $schedule = post_param_date('schedule');

                if ((!is_null($schedule)) && (addon_installed('calendar'))) {
                    $_intended_solely_for = is_null($intended_solely_for) ? 'null' : strval($intended_solely_for);
                    $_postdetailser_name_if_guest = is_null($poster_name_if_guest) ? 'null' : ('\'' . addslashes($poster_name_if_guest) . '\'');
                    $_first_post = $first_post ? 'true' : 'false';
                    $__title = is_null($title) ? 'null' : ('\'' . str_replace("\n", '\'."\n".\'', addslashes($title)) . '\'');
                    $_postdetails = is_null($post) ? 'null' : ('\'' . str_replace("\n", '\'."\n".\'', addslashes($post)) . '\'');
                    $_new_title = is_null($new_title) ? 'null' : ('\'' . str_replace("\n", '\'."\n".\'', addslashes($new_title)) . '\'');

                    $schedule_code = <<<END
:require_code('cns_topics_action2'); require_code('cns_topics_action'); cns_edit_topic($topic_id,null,null,$validated,$open,$pinned,$sunk,$cascading,'',$_new_title); if (($to!=$forum_id) && (!is_null($to))) cns_move_topics($forum_id,$to,array($topic_id)); \$post_id=cns_make_post($topic_id,$__title,$_postdetails,$skip_sig,$_first_post,$validated,$is_emphasised,$_postdetailser_name_if_guest,null,null,null,$_intended_solely_for,null,nullfalse,true,null,true,$topic_title,$sunk,null,$anonymous==1); if (addon_installed('awards')) { require_code('awards'); handle_award_setting('post',strval(\$post_id)); }
END;
                    require_code('calendar');
                    $start_year = intval(date('Y', $schedule));
                    $start_month = intval(date('m', $schedule));
                    $start_day = intval(date('d', $schedule));
                    $start_hour = intval(date('H', $schedule));
                    $start_minute = intval(date('i', $schedule));
                    require_code('calendar2');
                    $event_id = add_calendar_event(db_get_first_id(), '', null, 0, do_lang('ADD_POST'), $schedule_code, 3, $start_year, $start_month, $start_day, 'day_of_month', $start_hour, $start_minute);
                    regenerate_event_reminder_jobs($event_id);

                    $text = do_lang_tempcode('SUCCESS');
                    $map = array('page' => 'topicview', 'type' => 'first_unread', 'id' => $topic_id);
                    $test = get_param_string('kfs' . (is_null($forum_id) ? '' : strval($forum_id)), null, true);
                    if (($test !== null) && ($test !== '0')) {
                        $map['kfs' . (is_null($forum_id) ? '' : strval($forum_id))] = $test;
                    }
                    $test_threaded = get_param_integer('threaded', null);
                    if ($test_threaded !== null) {
                        $map['threaded'] = $test_threaded;
                    }
                    $_url = build_url($map, get_module_zone('topicview'));
                    $url = $_url->evaluate();
                    $url .= '#first_unread';
                    $url = get_param_string('redirect', $url);
                    return redirect_screen($_title, $url, $text);
                }

                cns_edit_topic($topic_id, null, null, $topic_validated, $open, $pinned, $sunk, $cascading, '', ($new_title == '') ? null : $new_title);
                if (($to != $forum_id) && (!is_null($to))) {
                    cns_move_topics($forum_id, $to, array($topic_id));
                }
            }
        }

        require_code('content2');
        $metadata = actual_metadata_get_fields('post', null);

        // Substitution of support operator for tickets, if required
        if (!is_null($forum_id)) {
            if (addon_installed('tickets')) {
                require_code('tickets');
                if (is_ticket_forum($forum_id)) {
                    if ($metadata['submitter'] === null || $metadata['submitter'] == get_member()) {
                        $metadata['submitter'] = get_active_support_user();
                        if ($poster_name_if_guest == $GLOBALS['FORUM_DRIVER']->get_username(get_member())) {
                            $poster_name_if_guest = $GLOBALS['FORUM_DRIVER']->get_username($metadata['submitter']);
                        }
                    }
                }
            }
        }

        $post_id = cns_make_post($topic_id, $title, $post, $skip_sig, $first_post, $validated, $is_emphasised, $poster_name_if_guest, null, $metadata['add_time'], $metadata['submitter'], $intended_solely_for, null, null, $check_permissions, true, null, true, $topic_title, $sunk, null, $anonymous == 1, $forum_id == -1 || is_null($forum_id), $forum_id == -1 || is_null($forum_id), false, $parent_id);

        set_url_moniker('post', strval($post_id));

        if (addon_installed('awards')) {
            require_code('awards');
            handle_award_setting('post', strval($post_id));
        }

        if ((!is_null($forum_id)) && ($anonymous == 0) && ($intended_solely_for === null)) {
            require_code('users2');
            if ((has_actual_page_access(get_modal_user(), 'forumview')) && (has_category_access(get_modal_user(), 'forums', strval($forum_id)))) {
                require_code('activities');
                syndicate_described_activity($first_post ? 'cns:ACTIVITY_ADD_TOPIC' : 'cns:ACTIVITY_ADD_POST_IN', $first_post ? $title : $topic_title, '', '', '_SEARCH:topicview:browse:' . strval($topic_id) . '#post_' . strval($post_id), '', '', 'cns_forum');
            }
        }

        require_code('fields');
        if (has_tied_catalogue('post')) {
            save_form_custom_fields('post', strval($post_id));
        }

        $validated = $GLOBALS['FORUM_DB']->query_select_value('f_posts', 'p_validated', array('id' => $post_id));

        $rep_post_id = post_param_integer('o_post_id', -1);
        if ($rep_post_id != -1) {
            require_code('cns_topicview');
            $url = find_post_id_url($rep_post_id);
        } else {
            if ($validated != 0) {
                require_code('cns_topicview');
                $url = find_post_id_url($post_id);
            } else {
                $map = array('page' => 'topicview', 'type' => 'findpost', 'id' => $post_id);
                $test = get_param_string('kfs' . (is_null($forum_id) ? '' : strval($forum_id)), null, true);
                if (($test !== null) && ($test !== '0')) {
                    $map['kfs' . (is_null($forum_id) ? '' : strval($forum_id))] = $test;
                }
                $test_threaded = get_param_integer('threaded', null);
                if ($test_threaded !== null) {
                    $map['threaded'] = $test_threaded;
                }
                $_url = build_url($map, get_module_zone('topicview'));
                $url = $_url->evaluate();
                $url .= '#post_' . strval($post_id);
            }
        }

        if ($is_reported_post) {
            $text = do_lang_tempcode('POST_REPORTED');
        } else {
            $text = ($validated == 1) ? do_lang_tempcode('SUCCESS') : do_lang_tempcode('SUBMIT_UNVALIDATED_FORUM_POSTS');
        }

        if ($forum_id >= 0) {
            $topic_validated = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_validated', array('id' => $topic_id));
            if (($topic_validated == 0) && (!has_privilege(get_member(), 'jump_to_unvalidated'))) {
                $map = array('page' => 'forumview', 'id' => $forum_id);
                $test = get_param_string('kfs' . (is_null($forum_id) ? '' : strval($forum_id)), null, true);
                if (($test !== null) && ($test !== '0')) {
                    $map['kfs' . (is_null($forum_id) ? '' : strval($forum_id))] = $test;
                }
                $test_threaded = get_param_integer('threaded', null);
                if ($test_threaded !== null) {
                    $map['threaded'] = $test_threaded;
                }
                $_url = build_url($map, get_module_zone('forumview'));
                $url = $_url->evaluate();

                $text = do_lang_tempcode('SUBMIT_UNVALIDATED_FORUM_TOPICS');
            }
        }

        if (($new_topic) && ($forum_id == -1)) {
            require_code('notifications');

            enable_notifications('cns_topic', strval($topic_id), get_member()); // from
            enable_notifications('cns_topic', strval($topic_id), $member_id); // to
            foreach ($invited_members as $invited_member) {
                enable_notifications('cns_topic', strval($topic_id), $invited_member);

                cns_invite_to_pt($invited_member, $topic_id);
            }
        }

        if ($anonymous == 1) {
            log_it('MAKE_ANONYMOUS_POST', strval($post_id), $title);
        }

        if (($forum_id == -1) && ($member_id != -1)) {
            send_pt_notification($post_id, $title, $topic_id, $member_id, null, $post);
        }

        cms_profile_end_for('_add_reply', '#' . strval($post_id));

        if ($add_poll == 1) {
            if (post_param_integer('add_poll', 0) == 1) {
                // Show it worked / Refresh
                $_url = build_url(array('page' => '_SELF', 'type' => 'add_poll', 'id' => $topic_id, 'try_validate' => is_null(post_param_date('schedule')) ? 1 : 0), '_SELF');
                return redirect_screen($_title, $_url, do_lang_tempcode('SUCCESS'));
            }
        }

        if ((!$new_topic) && ($forum_id != -1) && ($member_id == -1)) {
            handle_topic_ticket_reply($forum_id, $topic_id, $topic_title, $post);
        }

        // Show it worked / Refresh
        $url = get_param_string('redirect', $url);
        return redirect_screen($_title, $url, $text);
    }

    /**
     * The actualiser to toggle notifications for a topic.
     *
     * @return Tempcode The UI
     */
    public function toggle_notifications_topic() // Type
    {
        require_code('notifications2');
        return notifications_ui_advanced('cns_topic', do_lang_tempcode('NOW_ENABLED_NOTIFICATIONS_TOPIC'), do_lang_tempcode('NOW_DISABLED_NOTIFICATIONS_TOPIC'));
    }

    /**
     * The actualiser to mark a topic as read.
     *
     * @return Tempcode The UI
     */
    public function mark_read_topic() // Type
    {
        $topic_id = get_param_integer('id');

        $id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('id' => $topic_id));
        if ($id === null) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }

        $forum_id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 't_forum_id', array('id' => $topic_id));

        cns_ping_topic_read($topic_id, get_member(), get_param_integer('timestamp', null));
        if ((is_null($forum_id)) || (get_param_integer('ajax', 0) == 1)) {
            decache('side_cns_private_topics', null, get_member());
            decache('_new_pp', null, get_member());
            decache('_get_pts', null, get_member());
        }

        if (get_param_integer('ajax', 0) == 1) {
            exit();
        }

        return $this->redirect_to_forum('MARK_UNREAD', $forum_id, do_lang_tempcode('TOPIC_MARKED_READ'));
    }

    /**
     * The actualiser to mark a topic as unread.
     *
     * @return Tempcode The UI
     */
    public function mark_unread_topic() // Type
    {
        $topic_id = get_param_integer('id');

        $id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('id' => $topic_id));
        if ($id === null) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }

        $forum_id = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_forum_id', array('id' => $topic_id));

        if (is_null($forum_id)) {
            decache('side_cns_private_topics', null, get_member());
            decache('_new_pp', null, get_member());
            decache('_get_pts', null, get_member());
        }

        $this->cns_ping_topic_unread($topic_id);

        return $this->redirect_to_forum('MARK_UNREAD', $forum_id, do_lang_tempcode('TOPIC_MARKED_UNREAD'));
    }

    /**
     * The UI to grab a reason for deleting a post.
     *
     * @return Tempcode The UI
     */
    public function delete_post() // Type
    {
        $post_id = get_param_integer('id');
        $topic_id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_posts', 'p_topic_id', array('id' => $post_id));
        if (is_null($topic_id)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'post'));
        }
        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }

        $reason = null;
        $may_delete = cns_may_delete_post_by($post_id, null, null, null, get_member(), $reason);
        if (!$may_delete) {
            if (!is_null($reason)) {
                warn_exit($reason);
            }
            access_denied('I_ERROR');
        }

        $this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('DELETE_POST'));

        if ((addon_installed('securitylogging')) && (has_privilege(get_member(), 'mass_delete_from_ip'))) {
            $title = get_screen_title('DELETE_POST');
            $post_rows = $GLOBALS['FORUM_DB']->query_select('f_posts', array('p_ip_address', 'p_time'), array('id' => $post_id));
            if (!array_key_exists(0, $post_rows)) {
                warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'post'));
            }
            $ip = $post_rows[0]['p_ip_address'];
            $time = $post_rows[0]['p_time'];
            $count = $GLOBALS['FORUM_DB']->query_value_if_there('SELECT COUNT(*) FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_posts WHERE p_time>' . strval($time - 60 * 60 * 24) . ' AND p_time<' . strval($time + 60 * 60 * 24) . ' AND ' . db_string_equal_to('p_ip_address', $ip));
            $_ip_link = build_url(array('page' => 'admin_lookup', 'param' => get_ip_address()), get_module_zone('admin_lookup'));
            $lookup_ip_url = $_ip_link->evaluate();
            $text = paragraph(do_lang_tempcode('DELETE_POSTS_DESCRIPTION', escape_html(integer_format($count)), escape_html($ip), escape_html($lookup_ip_url)));
            $submit_name = do_lang_tempcode('DELETE_POST');
            $post_url = build_url(array('page' => '_SELF', 'type' => '_delete_post', 'id' => $post_id), '_SELF', null, true);
            $fields = new Tempcode();
            $hidden = new Tempcode();
            $hidden->attach(build_keep_post_fields());
            $hidden->attach(build_keep_form_fields());
            $fields->attach(form_input_line(do_lang_tempcode('REASON'), '', 'reason', '', false));

            $fields->attach(form_input_tick(do_lang_tempcode('DELETE_POSTS_FROM_IP'), do_lang_tempcode('DELETE_POSTS_FROM_IP_DESCRIPTION'), 'post_all', false));

            return do_template('FORM_SCREEN', array(
                '_GUID' => 'c10e882fa621b5230f455b41f40514c0',
                'SKIP_WEBSTANDARDS' => true,
                'STAFF_HELP_URL' => get_tutorial_url('tut_moderation'),
                'HIDDEN' => $hidden,
                'TITLE' => $title,
                'TEXT' => $text,
                'URL' => $post_url,
                'FIELDS' => $fields,
                'SUBMIT_ICON' => 'menu___generic_admin__delete',
                'SUBMIT_NAME' => $submit_name,
            ));
        }

        return $this->relay_with_reason('DELETE_POST');
    }

    /**
     * The actualiser for deleting a post.
     *
     * @return Tempcode The UI
     */
    public function _delete_post() // Type
    {
        $post_id = either_param_integer('id', null);
        if (is_null($post_id)) {
            $post_id = either_param_integer('post_id');
        }
        $reason = post_param_string('reason');

        require_code('cns_posts_action');
        require_code('cns_posts_action2');
        require_code('cns_posts_action3');

        if ((has_privilege(get_member(), 'mass_delete_from_ip')) && (post_param_integer('post_all', 0) == 1)) {
            $post_rows = $GLOBALS['FORUM_DB']->query_select('f_posts', array('p_ip_address', 'p_time', 'p_topic_id'), array('id' => $post_id));
            if (!array_key_exists(0, $post_rows)) {
                warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'post'));
            }
            $ip = $post_rows[0]['p_ip_address'];
            $time = $post_rows[0]['p_time'];
            $topic_id = $post_rows[0]['p_topic_id'];
            $posts = $GLOBALS['FORUM_DB']->query('SELECT id,p_topic_id,p_title FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_posts WHERE p_time>' . strval($time - 60 * 60 * 24) . ' AND p_time<' . strval($time + 60 * 60 * 24) . ' AND ' . db_string_equal_to('p_ip_address', $ip));

            $post_id = post_param_integer('id', null);
            if (is_null($post_id)) {
                $post_id = get_param_integer('id');

                $post_url = build_url(array('page' => '_SELF', 'type' => get_param_string('type')), '_SELF', null, true);
                $hidden = new Tempcode();
                $hidden->attach(form_input_hidden('id', strval($post_id)));
                $hidden->attach(form_input_hidden('reason', post_param_string('reason')));
                $hidden->attach(form_input_hidden('post_all', '1'));

                $stuff = new Tempcode();
                foreach ($posts as $post) {
                    if (!$stuff->is_empty()) {
                        $stuff->attach(do_lang_tempcode('LIST_SEP'));
                    }

                    $url = $GLOBALS['FORUM_DRIVER']->post_url($post['id'], '', true);

                    if ($post['p_title'] != '') {
                        $stuff->attach(hyperlink($url, $post['p_title'], true, true));
                    } else {
                        $stuff->attach(hyperlink($url, '#' . strval($post['id']), true, true));
                    }
                }

                return do_template('CONFIRM_SCREEN', array('_GUID' => 'ec5fd36fd869e42c59a0e7e3efa8a123', 'TITLE' => get_screen_title('DELETE_POSTS'), 'TEXT' => do_lang_tempcode('CONFIRM_DELETE', $stuff), 'URL' => $post_url, 'HIDDEN' => $hidden, 'FIELDS' => ''));
            }

            foreach ($posts as $post) {
                require_code('fields');
                if (has_tied_catalogue('post')) {
                    delete_form_custom_fields('post', $post['id']);
                }

                cns_delete_posts_topic($post['p_topic_id'], array($post['id']), $reason);
            }

            return $this->redirect_to('DELETE_POSTS_FROM_IP', $topic_id);
        }

        $topic_id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_posts', 'p_topic_id', array('id' => $post_id));
        if (is_null($topic_id)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'post'));
        }
        $_topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('t_cache_first_title', 't_cache_first_post_id', 't_forum_id'), array('id' => $topic_id), '', 1);
        $current_title = $_topic_info[0]['t_cache_first_title'];
        $deleted_all = cns_delete_posts_topic($topic_id, array($post_id), $reason);
        if ($_topic_info[0]['t_cache_first_post_id'] == $post_id) { // See if we need to copy title
            $_topic_info2 = $GLOBALS['FORUM_DB']->query_select('f_topics', array('t_cache_first_title', 't_cache_first_post_id'), array('id' => $topic_id), '', 1);
            if (array_key_exists(0, $_topic_info2)) {
                require_lang('cns');
                if ($_topic_info2[0]['t_cache_first_title'] == do_lang('NO_TOPIC_TITLE', strval($topic_id))) {
                    $GLOBALS['FORUM_DB']->query_update('f_posts', array('p_title' => $current_title), array('id' => $_topic_info2[0]['t_cache_first_post_id']), '', 1);
                }
            }
        }

        require_code('fields');
        if (has_tied_catalogue('post')) {
            delete_form_custom_fields('post', strval($post_id));
        }

        // Show it worked / Refresh
        $url = get_param_string('redirect', null);
        if ($url !== null) {
            return redirect_screen(get_screen_title('DELETE_POST'), $url, do_lang_tempcode('SUCCESS'));
        }

        if ($deleted_all) {
            return $this->redirect_to_forum('DELETE_POST', $_topic_info[0]['t_forum_id']);
        }
        return $this->redirect_to('DELETE_POST', $topic_id);
    }

    /**
     * The actualiser to vote in a poll.
     *
     * @return Tempcode The UI
     */
    public function vote_poll() // Type
    {
        $topic_id = get_param_integer('id'); // Yes, that's right -- we need to find the pollID from this, and will redirect back to given topic
        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        $poll_id = $topic_info[0]['t_poll_id'];

        $votes = array();
        $vote = post_param_integer('vote', -1);

        if ($vote != -1) {
            $votes[] = $vote;
        } else {
            foreach (array_keys($_POST) as $key) {
                if (substr($key, 0, 5) == 'vote_') {
                    $votes[] = intval(substr($key, 5));
                }
            }
        }

        require_code('cns_polls_action');
        require_code('cns_polls_action2');
        cns_vote_in_poll($poll_id, $votes, null, $topic_info);

        return $this->redirect_to('VOTE_IN_POLL', $topic_id, do_lang_tempcode('THANKYOU_VOTING'));
    }

    /**
     * Get Tempcode for a poll adding/editing form.
     *
     * @param  SHORT_TEXT $question The poll question
     * @param  ?array $answers A list of current answers for the poll (null: none yet)
     * @param  BINARY $is_private Whether it is a private poll (blind poll, where the results aren't visible until made public)
     * @param  BINARY $is_open Whether the poll is open for voting
     * @param  BINARY $requires_reply Whether a reply to the poll topic is required before voting
     * @param  integer $minimum_selections The minimum number of selections for voters
     * @param  integer $maximum_selections The maximum number of selections for voters
     * @return Tempcode The Tempcode for the fields
     */
    public function get_poll_form_fields($question = '', $answers = null, $is_private = 0, $is_open = 1, $requires_reply = 0, $minimum_selections = 1, $maximum_selections = 1)
    {
        require_lang('polls');

        if (is_null($answers)) {
            $answers = array();
        }

        $fields = new Tempcode();
        $fields->attach(form_input_line(do_lang_tempcode('QUESTION'), do_lang_tempcode('DESCRIPTION_QUESTION'), 'question', $question, true));
        $fields->attach(form_input_line_multi(do_lang_tempcode('ANSWER'), do_lang_tempcode('_DESCRIPTION_ANSWER'), 'answer_', $answers, 2));

        $options = array(
            array(do_lang_tempcode('POLL_IS_OPEN'), 'is_open', $is_open == 1, do_lang_tempcode('DESCRIPTION_POLL_IS_OPEN')),
            array(do_lang_tempcode('_POLL_REQUIRES_REPLY'), 'requires_reply', $requires_reply == 1, do_lang_tempcode('DESCRIPTION_POLL_REQUIRES_REPLY')),
        );
        if ((has_privilege(get_member(), 'may_unblind_own_poll')) && (!is_null($is_private))) {
            $options[] = array(do_lang_tempcode('POLL_IS_PRIVATE'), 'is_private', $is_private == 1, do_lang_tempcode('DESCRIPTION_POLL_IS_PRIVATE'));
        }
        $fields->attach(form_input_various_ticks($options, ''));

        $fields->attach(form_input_integer(do_lang_tempcode('MINIMUM_SELECTIONS'), do_lang_tempcode('DESCRIPTION_MINIMUM_SELECTIONS'), 'minimum_selections', $minimum_selections, true));
        $fields->attach(form_input_integer(do_lang_tempcode('MAXIMUM_SELECTIONS'), do_lang_tempcode('DESCRIPTION_MAXIMUM_SELECTIONS'), 'maximum_selections', $maximum_selections, true));
        return $fields;
    }

    /**
     * The UI to add a poll.
     *
     * @param  ?AUTO_LINK $topic_id The topic ID to add the poll to (null: it is instead gettable from a GET parameter named 'id')
     * @return Tempcode The UI
     */
    public function add_poll($topic_id = null) // Type
    {
        if (is_null($topic_id)) {
            $topic_id = get_param_integer('id');
        }

        $map = array('page' => '_SELF', 'type' => '_add_poll', 'id' => $topic_id);
        if (get_param_integer('try_validate', 0) == 1) {
            $map['try_validate'] = 1;
        }
        if ((get_param_string('type', 'browse') == '_add_reply') && (post_param_integer('validated', 0) == 1)) {
            $map['re_validate'] = 1;
        }
        $post_url = build_url($map, '_SELF');

        $fields = new Tempcode();

        url_default_parameters__enable();
        $fields->attach($this->get_poll_form_fields());
        url_default_parameters__disable();

        // Find polls we can grab
        require_code('cns_forums');
        $or_list = get_forum_access_sql('t.t_forum_id');
        $polls = $GLOBALS['FORUM_DB']->query('SELECT p.*,t_cache_first_username FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_topics t LEFT JOIN ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_polls p ON p.id=t.t_poll_id WHERE (' . $or_list . ') AND p.id IS NOT NULL ORDER BY id DESC', 30);
        if (count($polls) != 0) {
            $fields->attach(do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => '1fb2af282b014c3a6ae09d986e4f72eb', 'SECTION_HIDDEN' => true, 'TITLE' => do_lang_tempcode('ALT_COPY_EXISTING_POLL'))));

            $list = new Tempcode();
            $list->attach(form_input_list_entry('', true, ''));
            foreach ($polls as $poll) {
                $list->attach(form_input_list_entry(strval($poll['id']), false, do_lang_tempcode('POLL_IN_LIST', escape_html($poll['po_question']), escape_html($poll['t_cache_first_username']))));
            }
            $fields->attach(form_input_list(do_lang_tempcode('EXISTING'), do_lang_tempcode('COPY_EXISTING_POLL'), 'existing', $list, null, false, false));

            $javascript = 'var existing=document.getElementById(\'existing\'); var form=existing.form; var ch_func=function() { var disable_all=(existing.selectedIndex!=0); var i; for (i=0;i<form.elements.length;i++) if ((form.elements[i]!=existing) && (form.elements[i].id!=\'perform_keywordcheck\') && ((form.elements[i].getAttribute(\'type\')==\'checkbox\') || (form.elements[i].getAttribute(\'type\')==\'text\'))) { set_required(form.elements[i].name,(!disable_all) && ((form.elements[i].id==\'question\') || (form.elements[i].id==\'answer_0\'))); set_locked(form.elements[i],disable_all); } }; for (i=0;i<form.elements.length;i++) add_event_listener_abstract(form.elements[i],\'change\',ch_func);';
        } else {
            $javascript = '';
        }

        $title = get_screen_title('ADD_TOPIC_POLL');
        $submit_name = do_lang_tempcode('ADD_TOPIC_POLL');

        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        $this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('ADD_TOPIC_POLL'));

        return do_template('FORM_SCREEN', array('_GUID' => 'ce1752a0c5508a061bffbf242a13e5bd', 'HIDDEN' => '', 'TITLE' => $title, 'FIELDS' => $fields, 'TEXT' => '', 'SUBMIT_ICON' => 'menu___generic_admin__add_to_category', 'SUBMIT_NAME' => $submit_name, 'URL' => $post_url, 'JAVASCRIPT' => $javascript));
    }

    /**
     * The actualiser to add a poll.
     *
     * @return Tempcode The UI
     */
    public function _add_poll() // Type
    {
        $topic_id = get_param_integer('id');

        if (!cns_may_access_topic($topic_id)) {
            access_denied('I_ERROR');
        }

        require_code('cns_polls_action');
        require_code('cns_polls_action2');

        $_existing = post_param_string('existing', '');
        if ($_existing != '') {
            $existing = intval($_existing);

            $_poll_row = $GLOBALS['FORUM_DB']->query_select('f_topics t LEFT JOIN ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_polls p ON t.t_poll_id=p.id', array('t_forum_id', 'p.*'), array('p.id' => $existing), '', 1);
            if (!array_key_exists(0, $_poll_row)) {
                warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
            }
            $row = $_poll_row[0];

            $answer_rows = $GLOBALS['FORUM_DB']->query_select('f_poll_answers', array('pa_answer'), array('pa_poll_id' => $existing), 'ORDER BY id');
            $answers = array();
            foreach ($answer_rows as $trow) {
                $answers[] = $trow['pa_answer'];
            }

            cns_make_poll($topic_id, $row['po_question'], $row['po_is_private'], $row['po_is_open'], $row['po_minimum_selections'], $row['po_maximum_selections'], $row['po_requires_reply'], $answers);

            return $this->redirect_to('ADD_TOPIC_POLL', $topic_id);
        }

        $question = post_param_string('question');
        $is_private = post_param_integer('is_private', 0);
        $is_open = post_param_integer('is_open', 0);
        $minimum_selections = post_param_integer('minimum_selections', 0);
        $maximum_selections = post_param_integer('maximum_selections', 0);
        $requires_reply = post_param_integer('requires_reply', 0);

        $answers = array();
        foreach ($_POST as $key => $val) {
            if (!is_string($val)) {
                continue;
            }

            if (substr($key, 0, 7) == 'answer_') {
                if (@get_magic_quotes_gpc()) {
                    $val = stripslashes($val);
                }
                if ($val != '') {
                    $answers[] = array($val, 0);
                }
            }
        }

        cns_make_poll($topic_id, $question, $is_private, $is_open, $minimum_selections, $maximum_selections, $requires_reply, $answers);

        if (get_param_integer('try_validate', 0) == 1) {
            $forum_id = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_forum_id', array('id' => $topic_id));
            if ((!is_null($forum_id)) && (!has_privilege(get_member(), 'bypass_validation_midrange_content', 'topics', array('forums', $forum_id)))) {
                $validated = 0;
            } else {
                $validated = 1;
            }
            if ($validated == 1) {
                $GLOBALS['FORUM_DB']->query_update('f_topics', array('t_validated' => 1), array('id' => $topic_id), '', 1);
            }
        }

        return $this->redirect_to('ADD_TOPIC_POLL', $topic_id);
    }

    // ===============
    // POST MODERATION
    // ===============

    /**
     * The UI to edit a post.
     *
     * @return Tempcode The UI
     */
    public function edit_post() // Type
    {
        $post_id = get_param_integer('id');

        $post_details = $GLOBALS['FORUM_DB']->query_select('f_posts', array('*'), array('id' => $post_id), '', 1);
        if (!array_key_exists(0, $post_details)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'post'));
        }

        if (!has_privilege(get_member(), 'exceed_post_edit_time_limit')) {
            $post_time = $post_details[0]['p_time'];
            $time_left = ($post_time - time() + intval(get_option('edit_time_limit')) * 60);
            attach_message(do_lang_tempcode('TIME_LEFT_TO_EDIT_POST', escape_html(display_time_period($time_left))), 'notice');
        }

        $forum_id = $post_details[0]['p_cache_forum_id'];

        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $post_details[0]['p_topic_id']), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }

        $reason = null;
        $may_edit = cns_may_edit_post_by($post_id, $post_details[0]['p_time'], $post_details[0]['p_poster'], $forum_id, get_member(), $topic_info[0]['t_is_open'] == 0, $reason);
        if (!$may_edit) {
            if (!is_null($reason)) {
                warn_exit($reason);
            }
            access_denied('I_ERROR');
        }

        $this->handle_topic_breadcrumbs($forum_id, $post_details[0]['p_topic_id'], $topic_info[0]['t_cache_first_title'], do_lang_tempcode('EDIT_POST'));

        if (($topic_info[0]['t_cache_first_post_id'] == $post_id) && ((cns_may_moderate_forum($topic_info[0]['t_forum_id'])) || ($topic_info[0]['t_cache_first_member_id'] == get_member()))) {
            $edit_topic_url = build_url(array('page' => 'topics', 'type' => 'edit_topic', 'id' => $topic_info[0]['id']), get_module_zone('topics'));
            attach_message(do_lang_tempcode('EDITING_FIRST_TOPIC_POST', escape_html($edit_topic_url->evaluate())), 'inform');
        }

        $hidden_fields = new Tempcode();
        $intended_solely_for = $post_details[0]['p_intended_solely_for'];
        if (!is_null($intended_solely_for)) {
            $hidden_fields->attach(form_input_hidden('intended_solely_for', strval($intended_solely_for)));
        }
        $hidden_fields->attach(form_input_hidden('from_url', get_self_url(true)));
        $hidden_fields->attach(form_input_hidden('post_id', strval($post_id)));

        $map = array('page' => '_SELF', 'type' => '_edit_post');
        $redirect = get_param_string('redirect', '');
        if ($redirect != '') {
            $map['redirect'] = $redirect;
        }
        $test = get_param_string('kfs' . (is_null($forum_id) ? '' : strval($forum_id)), null, true);
        if (($test !== null) && ($test !== '0')) {
            $map['kfs' . (is_null($forum_id) ? '' : strval($forum_id))] = $test;
        }
        $test_threaded = get_param_integer('threaded', null);
        if ($test_threaded !== null) {
            $map['threaded'] = $test_threaded;
        }
        $post_url = build_url($map, '_SELF');

        $post = post_param_string('post', get_translated_text($post_details[0]['p_post'], $GLOBALS['FORUM_DB']));

        // Certain aspects relating to the posting system
        $specialisation = new Tempcode();
        if (((get_option('is_on_post_titles') == '1') || ($post_details[0]['p_title'] != '') || ($post_id == $topic_info[0]['t_cache_first_post_id']))) {
            $specialisation->attach(form_input_line(do_lang_tempcode('TITLE'), '', 'title', post_param_string('title', $post_details[0]['p_title']), false, 1, 120));
        }
        $specialisation->attach(form_input_line(do_lang_tempcode('REASON'), '', 'reason', '', false, 2));
        if (cns_may_moderate_forum($forum_id, get_member())) {
            $moderation_options = array(
                array(do_lang_tempcode('EMPHASISED'), 'is_emphasised', $post_details[0]['p_is_emphasised'] == 1, do_lang_tempcode('DESCRIPTION_EMPHASISED')),
            );
            if (addon_installed('unvalidated')) {
                if ($post_details[0]['p_validated'] == 0) {
                    $post_details[0]['p_validated'] = get_param_integer('validated', 0);
                    if (($post_details[0]['p_validated'] == 1) && (addon_installed('unvalidated'))) {
                        attach_message(do_lang_tempcode('WILL_BE_VALIDATED_WHEN_SAVING'));
                    }
                }
                $moderation_options[] = array(do_lang_tempcode('VALIDATED'), 'validated', $post_details[0]['p_validated'] == 1, do_lang_tempcode($GLOBALS['FORUM_DRIVER']->is_super_admin(get_member()) ? 'DESCRIPTION_VALIDATED_SIMPLE' : 'DESCRIPTION_VALIDATED', 'post'));
            }
        } else {
            $moderation_options = array();
            $hidden_fields->attach(form_input_hidden('validated', '1'));
        }

        $specialisation2 = new Tempcode();

        if (count($moderation_options) != 0) {
            $specialisation2->attach(form_input_various_ticks($moderation_options, '', null, do_lang_tempcode('MODERATION_OPTIONS')));
        }

        require_code('fields');
        if (has_tied_catalogue('post')) {
            append_form_custom_fields('post', strval($post_id), $specialisation2, $hidden_fields);
        }

        require_code('content2');
        $specialisation2->attach(metadata_get_fields('post', strval($post_id)));

        $specialisation2->attach(do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => 'bdab02bfa4ea2f50feedf8a15762c5f1', 'TITLE' => do_lang_tempcode('ACTIONS'))));
        $options = array();
        if (get_option('is_on_post_map') == '1') {
            $hidden_fields->attach(form_input_hidden('mark_as_unread', '1'));
        } else {
            $options[] = array(do_lang_tempcode('MARK_UNREAD'), 'mark_as_unread', false, do_lang_tempcode('DESCRIPTION_MARK_UNREAD'));
        }
        $options[] = array(do_lang_tempcode('SHOW_AS_EDITED'), 'show_as_edited', ((time() - $post_details[0]['p_time']) > 60 * 3), do_lang_tempcode('DESCRIPTION_POST_SHOW_AS_EDITED'));
        $specialisation2->attach(form_input_various_ticks($options, ''));
        if (cns_may_delete_post_by($post_id, $post_details[0]['p_time'], $post_details[0]['p_poster'], $forum_id)) {
            $specialisation2->attach(form_input_tick(do_lang_tempcode('DELETE'), do_lang_tempcode('DESCRIPTION_DELETE'), 'delete', false));
        }

        // Awards?
        if (addon_installed('awards')) {
            require_code('awards');
            $specialisation2->attach(get_award_fields('post', strval($post_id)));
        }

        if (is_null(get_param_string('post', null))) {
            $parsed = get_translated_tempcode('f_posts', $post_details[0], 'p_post', $GLOBALS['FORUM_DB']);
        } else {
            $parsed = null;
        }
        $posting_form = get_posting_form(do_lang('SAVE'), 'buttons__edit', $post, $post_url, $hidden_fields, $specialisation, null, '', $specialisation2, $parsed, $this->_post_javascript());

        list($warning_details, $ping_url) = handle_conflict_resolution();

        $title = get_screen_title('EDIT_POST');
        return do_template('POSTING_SCREEN', array(
            '_GUID' => '347e469de58882bf77722bba6ed4aba4',
            'STAFF_HELP_URL' => get_tutorial_url('tut_moderation'),
            'TITLE' => $title,
            'PING_URL' => $ping_url,
            'WARNING_DETAILS' => $warning_details,
            'POSTING_FORM' => $posting_form,
        ));
    }

    /**
     * Get JavaScript to restrict post lengths.
     *
     * @return string The post JavaScript
     */
    public function _post_javascript()
    {
        $size = cns_get_member_best_group_property(get_member(), 'max_post_length_comcode');

        $javascript = '';

        if (get_option('force_guest_names') == '1') {
            $javascript .= '
                var poster_name_if_guest=document.getElementById(\'poster_name_if_guest\');
                if (poster_name_if_guest)
                {
                    var crf=function() {
                        if (poster_name_if_guest.value==\'' . php_addslashes(do_lang('GUEST')) . '\') {
                            poster_name_if_guest.value=\'\';
                        }
                    };
                    crf();
                    poster_name_if_guest.onblur=crf;
                }
            ';
        }

        $javascript .= "
            var form=document.getElementById('post').form;
            form.old_submit=form.onsubmit;
            form.onsubmit=function() {
                var post=form.elements['post'];
                var text_value;
                if (is_wysiwyg_field(post))
                {
                    try
                    {
                        text_value=window.CKEDITOR.instances['post'].getData();
                    }
                    catch (e) {};
                } else
                {
                    if ((!post.value) && (post[1])) post=post[1];
                    text_value=post.value;
                }
                if (text_value.length>" . strval($size) . ")
                {
                    window.fauxmodal_alert('" . php_addslashes(do_lang('POST_TOO_LONG')) . "');
                    return false;
                }
        ";

        $stub = unixify_line_format(either_param_string('stub', ''));
        if ($stub != '') {
            $javascript .= "
                var df='" . str_replace("\n", '\n', addslashes($stub)) . "';

                var pv=post.value;
                if ((post) && (pv.substring(0,df.length)==df))
                {
                    pv=pv.substring(df.length,pv.length);
                }
                post.value=pv;
        ";
        }

        $javascript .= "
                if (typeof form.old_submit!='undefined' && form.old_submit) return form.old_submit();

                return true;
            };
        ";

        return $javascript;
    }

    /**
     * The actualiser to edit a post.
     *
     * @return Tempcode The UI
     */
    public function _edit_post() // Type
    {
        require_code('attachments2');

        if (post_param_integer('delete', 0) == 1) {
            return $this->_delete_post();
        }

        $post_id = post_param_integer('post_id');

        $post_details = $GLOBALS['FORUM_DB']->query_select('f_posts', array('*'), array('id' => $post_id), '', 1);
        if (!array_key_exists(0, $post_details)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'post'));
        }
        $forum_id = $post_details[0]['p_cache_forum_id'];

        $intended_solely_for = post_param_integer('intended_solely_for', null);
        $validated = post_param_integer('validated', 0);
        if ((!is_null($forum_id)) && (!has_privilege(get_member(), 'bypass_validation_lowrange_content', 'topics', array('forums', $forum_id)))) {
            $validated = 0;
        }
        $old_validated = $post_details[0]['p_validated'];
        require_code('cns_posts_action');
        require_code('cns_posts_action2');
        require_code('cns_posts_action3');

        require_code('content2');
        $metadata = actual_metadata_get_fields('post', strval($post_id));

        $poster_name_if_guest = null;
        if (isset($metadata['submitter'])) {
            if (($metadata['submitter'] != $post_details[0]['p_poster']) && ($post_details[0]['p_poster_name_if_guest'] == $GLOBALS['FORUM_DRIVER']->get_username($post_details[0]['p_poster'], true))) {
                $poster_name_if_guest = $GLOBALS['FORUM_DRIVER']->get_username($metadata['submitter'], true);
            }
        }

        $topic_id = cns_edit_post($post_id, $validated, post_param_string('title', ''), post_param_string('post'), post_param_integer('skip_sig', 0), post_param_integer('is_emphasised', 0), $intended_solely_for, (post_param_integer('show_as_edited', 0) == 1), (post_param_integer('mark_as_unread', 0) == 1), post_param_string('reason'), true, $metadata['edit_time'], $metadata['add_time'], $metadata['submitter'], true, true, $poster_name_if_guest);

        require_code('fields');
        if (has_tied_catalogue('post')) {
            save_form_custom_fields('post', strval($post_id));
        }

        if ($old_validated != $validated) {
            if ($validated == 1) {
                cns_validate_post($post_id);
            }
        }

        if (addon_installed('awards')) {
            require_code('awards');
            handle_award_setting('post', strval($post_id));
        }

        // Show it worked / Refresh
        $url = get_param_string('redirect', null);
        if (!is_null($url)) {
            return redirect_screen(get_screen_title('EDIT_POST'), $url, do_lang_tempcode('SUCCESS'));
        }

        return $this->redirect_to('EDIT_POST', $topic_id, null, $post_id);
    }

    /**
     * The actualiser to validate a post.
     *
     * @return Tempcode The UI
     */
    public function validate_post() // Type
    {
        $post_id = get_param_integer('id');

        require_code('cns_posts_action');
        require_code('cns_posts_action2');
        require_code('cns_posts_action3');
        $topic_id = cns_validate_post($post_id);

        $redirect = get_param_string('redirect', '');
        if ($redirect != '') {
            return redirect_screen(get_screen_title('VALIDATE_POST'), $redirect, do_lang_tempcode('SUCCESS'));
        }

        return $this->redirect_to('VALIDATE_POST', $topic_id);
    }

    // ================
    // TOPIC MODERATION
    // ================

    /**
     * Check there is at least some moderation access over the given topic.
     * This is here to prevent snooping into the details of things (the backend provides the true security).
     *
     * @param  AUTO_LINK $topic_id The topic ID
     */
    public function check_has_mod_access($topic_id)
    {
        if (!cns_may_access_topic($topic_id, get_member(), null, false)) {
            access_denied('I_ERROR');
        }
    }

    /**
     * The UI to edit a topic.
     *
     * @return Tempcode The UI
     */
    public function edit_topic() // Type
    {
        $topic_id = get_param_integer('id');

        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        $forum_id = $topic_info[0]['t_forum_id'];
        $private_topic = is_null($forum_id);

        $validated = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_posts', 'p_validated', array('id' => $topic_info[0]['t_cache_first_post_id']));
        if ($validated !== null) {
            if (($topic_info[0]['t_validated'] == 1) && ($validated == 0)) {
                attach_message(do_lang_tempcode('FIRST_POST_IS_UNVALIDATED'), 'notice');
            }
        }

        $this->handle_topic_breadcrumbs($forum_id, $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('EDIT_TOPIC'));

        $this->check_has_mod_access($topic_id);

        $post_url = build_url(array('page' => '_SELF', 'type' => '_edit_topic', 'id' => $topic_id), '_SELF');

        // Certain aspects relating to the posting system
        $fields = new Tempcode();
        $fields->attach(form_input_line(do_lang_tempcode('TITLE'), '', 'title', $topic_info[0]['t_cache_first_title'], false));
        if ((get_option('is_on_topic_descriptions') == '1') && (!$GLOBALS['FORUM_DRIVER']->topic_is_threaded($topic_id))) {
            $fields->attach(form_input_line(do_lang_tempcode('DESCRIPTION'), '', 'description', $topic_info[0]['t_description'], false));
        }
        $fields->attach(form_input_line(do_lang_tempcode('REASON'), do_lang_tempcode('DESCRIPTION_REASON'), 'reason', '', false));
        if (get_option('is_on_topic_emoticons') == '1') {
            $fields->attach($this->choose_topic_emoticon($topic_info[0]['t_emoticon']));
        }
        $options = array();
        $hidden_fields = new Tempcode();
        if (cns_may_moderate_forum($forum_id, get_member())) {
            $moderation_options = array(
                array(do_lang_tempcode('OPEN'), 'open', $topic_info[0]['t_is_open'] == 1, do_lang_tempcode('DESCRIPTION_OPEN')),
                array(do_lang_tempcode('PINNED'), 'pinned', $topic_info[0]['t_pinned'] == 1, do_lang_tempcode('DESCRIPTION_PINNED')),
            );
            if (addon_installed('unvalidated')) {
                if ($topic_info[0]['t_validated'] == 0) {
                    $topic_info[0]['t_validated'] = get_param_integer('validated', 0);
                    if (($topic_info[0]['t_validated'] == 1) && (addon_installed('unvalidated'))) {
                        attach_message(do_lang_tempcode('WILL_BE_VALIDATED_WHEN_SAVING'));
                    }
                }
                $moderation_options[] = array(do_lang_tempcode('VALIDATED'), 'validated', $topic_info[0]['t_validated'] == 1, do_lang_tempcode($GLOBALS['FORUM_DRIVER']->is_super_admin(get_member()) ? 'DESCRIPTION_VALIDATED_SIMPLE' : 'DESCRIPTION_VALIDATED', 'topic'));
            }
            if (get_option('enable_sunk') == '1') {
                $moderation_options[] = array(do_lang_tempcode('SUNK'), 'sunk', $topic_info[0]['t_sunk'] == 1, do_lang_tempcode('DESCRIPTION_SUNK'));
            }
            if (!$private_topic) {
                $options[] = array(do_lang_tempcode('CASCADING'), 'cascading', $topic_info[0]['t_cascading'] == 1, do_lang_tempcode('DESCRIPTION_CASCADING'));
            }
        } else {
            $moderation_options = array();
            $hidden_fields->attach(form_input_hidden('validated', '1'));
        }
        $fields->attach(form_input_various_ticks($options, ''));
        if (count($moderation_options) != 0) {
            $fields->attach(form_input_various_ticks($moderation_options, '', null, do_lang_tempcode('MODERATION_OPTIONS')));
        }

        require_code('fields');
        if (has_tied_catalogue('topic')) {
            append_form_custom_fields('topic', strval($topic_id), $fields, $hidden_fields);
        }

        // Awards?
        if (addon_installed('awards')) {
            require_code('awards');
            $fields->attach(get_award_fields('topic', strval($topic_id)));
        }

        require_code('content2');
        $fields->attach(metadata_get_fields('topic', strval($topic_id), false, array('submitter', 'add_time', 'edit_time')));

        $title = get_screen_title('EDIT_TOPIC');
        $submit_name = do_lang_tempcode('SAVE');
        return do_template('FORM_SCREEN', array(
            '_GUID' => '071b6747a1df1cf8e72f8f542422aa5b',
            'STAFF_HELP_URL' => get_tutorial_url('tut_moderation'),
            'HIDDEN' => $hidden_fields,
            'TITLE' => $title,
            'FIELDS' => $fields,
            'TEXT' => do_lang_tempcode('EDITING_TOPIC_NOT_POST'),
            'SUBMIT_ICON' => 'menu___generic_admin__edit_this',
            'SUBMIT_NAME' => $submit_name,
            'URL' => $post_url,
        ));
    }

    /**
     * The actualiser to edit a topic.
     *
     * @return Tempcode The UI
     */
    public function _edit_topic() // Type
    {
        $topic_id = get_param_integer('id');
        $cascading = post_param_integer('cascading', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $pinned = post_param_integer('pinned', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $sunk = post_param_integer('sunk', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $open = post_param_integer('open', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $validated = post_param_integer('validated', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $title = post_param_string('title');

        require_code('cns_topics_action');
        require_code('cns_topics_action2');

        require_code('content2');
        $metadata = actual_metadata_get_fields('topic', strval($topic_id), array('submitter', 'add_time', 'edit_time'));

        cns_edit_topic($topic_id, post_param_string('description', STRING_MAGIC_NULL), post_param_string('emoticon', STRING_MAGIC_NULL), $validated, $open, $pinned, $sunk, $cascading, post_param_string('reason', STRING_MAGIC_NULL), $title, null, true, $metadata['views']);

        require_code('fields');
        if (has_tied_catalogue('topic')) {
            save_form_custom_fields('topic', strval($topic_id));
        }

        if (addon_installed('awards')) {
            require_code('awards');
            handle_award_setting('topic', strval($topic_id));
        }

        return $this->redirect_to('EDIT_TOPIC', $topic_id);
    }

    /**
     * The UI to delete a topic.
     *
     * @return Tempcode The UI
     */
    public function delete_topic() // Type
    {
        $topic_id = get_param_integer('id');
        $post_url = build_url(array('page' => '_SELF', 'type' => '_delete_topic', 'id' => $topic_id), '_SELF');

        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        $this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('DELETE_TOPIC'));

        $this->check_has_mod_access($topic_id);

        // Certain aspects relating to the posting system
        $fields = new Tempcode();
        $hidden = new Tempcode();
        $hidden->attach(build_keep_post_fields());
        $hidden->attach(build_keep_form_fields());

        $set_name = 'destination';
        $required = false;
        $set_title = do_lang_tempcode('DESTINATION_TOPIC');
        $field_set = alternate_fields_set__start($set_name);

        $field_set->attach(form_input_tree_list(do_lang_tempcode('CHOOSE'), '', 'select_topic_id', null, 'choose_topic', array(), false));

        $field_set->attach(form_input_integer(do_lang_tempcode('DESTINATION_TOPIC_ID'), '', 'manual_topic_id', null, false));

        $fields->attach(alternate_fields_set__end($set_name, $set_title, do_lang_tempcode('DESCRIPTION_DESTINATION_TOPIC'), $field_set, $required));

        $fields->attach(form_input_line(do_lang_tempcode('REASON'), do_lang_tempcode('DESCRIPTION_REASON'), 'reason', '', false));

        $topic_title = $GLOBALS['FORUM_DB']->query_select_value('f_topics', 't_cache_first_title', array('id' => $topic_id));
        $title = get_screen_title('_DELETE_TOPIC', true, array(escape_html($topic_title)));
        $submit_name = do_lang_tempcode('DELETE');
        $text = paragraph(do_lang_tempcode('DELETE_TOPIC_TEXT'));
        return do_template('FORM_SCREEN', array(
            '_GUID' => '00b84f337de3683bfcdb60ea3086afa0',
            'SKIP_WEBSTANDARDS' => true,
            'STAFF_HELP_URL' => get_tutorial_url('tut_moderation'),
            'HIDDEN' => $hidden,
            'TITLE' => $title,
            'FIELDS' => $fields,
            'TEXT' => $text,
            'SUBMIT_ICON' => 'menu___generic_admin__delete',
            'SUBMIT_NAME' => $submit_name,
            'URL' => $post_url,
        ));
    }

    /**
     * The actualiser to delete a topic.
     *
     * @return Tempcode The UI
     */
    public function _delete_topic() // Type
    {
        $topic_id = get_param_integer('id');
        $post_target_topic_id = post_param_integer('select_topic_id', null);
        if ($post_target_topic_id === null) {
            $post_target_topic_id = post_param_integer('manual_topic_id', null);
        }

        require_code('cns_topics_action');
        require_code('cns_topics_action2');
        $forum_id = cns_delete_topic($topic_id, post_param_string('reason'), $post_target_topic_id);

        require_code('fields');
        if (has_tied_catalogue('topic')) {
            delete_form_custom_fields('topic', strval($topic_id));
        }

        return $this->redirect_to_forum('DELETE_TOPIC', $forum_id);
    }

    /**
     * The UI to invite a member to a PT.
     *
     * @return Tempcode The UI
     */
    public function invite_member()
    {
        $topic_id = get_param_integer('id');

        $fields = new Tempcode();
        $fields->attach(form_input_username(do_lang_tempcode('USERNAME'), do_lang_tempcode('MEMBER_TO_INVITE'), 'username', '', false));

        $title = get_screen_title('INVITE_MEMBER_TO_PT');

        $submit_name = do_lang_tempcode('INVITE_MEMBER_TO_PT');

        $text = paragraph(do_lang_tempcode('INVITE_MEMBER_TO_PT_TEXT'));

        $post_url = build_url(array('page' => '_SELF', 'type' => '_invite_member', 'topic_id' => $topic_id), '_SELF');

        return do_template('FORM_SCREEN', array(
            '_GUID' => '9f28869bd74262ae20ba79ace14b87ca',
            'SKIP_WEBSTANDARDS' => true,
            'STAFF_HELP_URL' => get_tutorial_url('tut_correspondence'),
            'HIDDEN' => '',
            'TITLE' => $title,
            'FIELDS' => $fields,
            'TEXT' => $text,
            'SUBMIT_ICON' => 'buttons__proceed',
            'SUBMIT_NAME' => $submit_name,
            'URL' => $post_url,
        ));
    }

    /**
     * The actualiser to invite a member to a PT.
     *
     * @return Tempcode The UI
     */
    public function _invite_member()
    {
        $username = trim(post_param_string('username'));
        $member_id = $GLOBALS['FORUM_DRIVER']->get_member_from_username($username);
        if (($member_id === null) || (is_guest($member_id))) {
            warn_exit(do_lang_tempcode('_MEMBER_NO_EXIST', escape_html($username)));
        }
        $topic_id = get_param_integer('topic_id');

        require_code('cns_topics_action');
        require_code('cns_topics_action2');
        require_code('notifications');
        enable_notifications('cns_topic', strval($topic_id), $member_id);
        cns_invite_to_pt($member_id, $topic_id);

        return $this->redirect_to('EDIT_TOPIC', $topic_id);
    }

    /**
     * The UI to edit a poll.
     *
     * @return Tempcode The UI
     */
    public function edit_poll() // Type
    {
        $topic_id = get_param_integer('id');
        $poll_id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 't_poll_id', array('id' => $topic_id));
        if (is_null($poll_id)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }

        $this->check_has_mod_access($topic_id);

        $post_url = build_url(array('page' => '_SELF', 'type' => '_edit_poll', 'id' => $poll_id), '_SELF');

        $poll_info = $GLOBALS['FORUM_DB']->query_select('f_polls', array('*'), array('id' => $poll_id), '', 1);
        if (!array_key_exists(0, $poll_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE'));
        }
        $poll = $poll_info[0];

        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        $this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('EDIT_TOPIC_POLL'));

        $answers = collapse_1d_complexity('pa_answer', $GLOBALS['FORUM_DB']->query_select('f_poll_answers', array('pa_answer'), array('pa_poll_id' => $poll_id)));
        $question = $poll['po_question'];
        $is_private = $poll['po_is_private'];
        $is_open = $poll['po_is_open'];
        $requires_reply = $poll['po_requires_reply'];
        $minimum_selections = $poll['po_minimum_selections'];
        $maximum_selections = $poll['po_maximum_selections'];
        $fields = $this->get_poll_form_fields($question, $answers, $is_private, $is_open, $requires_reply, $minimum_selections, $maximum_selections);
        $fields->attach(form_input_line(do_lang_tempcode('REASON'), do_lang_tempcode('DESCRIPTION_REASON'), 'reason', '', false));

        $title = get_screen_title('EDIT_TOPIC_POLL');
        $submit_name = do_lang_tempcode('SAVE');

        list($warning_details, $ping_url) = handle_conflict_resolution();

        return do_template('FORM_SCREEN', array(
            '_GUID' => '992a1bfd025e3fabea9d13307cfd2a91',
            'STAFF_HELP_URL' => get_tutorial_url('tut_moderation'),
            'PREVIEW' => true,
            'PING_URL' => $ping_url,
            'WARNING_DETAILS' => $warning_details,
            'HIDDEN' => '',
            'TITLE' => $title,
            'FIELDS' => $fields,
            'TEXT' => '',
            'SUBMIT_ICON' => 'menu___generic_admin__edit_this',
            'SUBMIT_NAME' => $submit_name,
            'URL' => $post_url,
        ));
    }

    /**
     * The actualiser to edit a poll.
     *
     * @return Tempcode The UI
     */
    public function _edit_poll() // Type
    {
        $poll_id = get_param_integer('id');
        $topic_id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 'id', array('t_poll_id' => $poll_id));
        if (is_null($topic_id)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }

        $question = post_param_string('question', STRING_MAGIC_NULL);
        $is_private = post_param_integer('is_private', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $is_open = post_param_integer('is_open', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $minimum_selections = post_param_integer('minimum_selections', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $maximum_selections = post_param_integer('maximum_selections', fractional_edit() ? INTEGER_MAGIC_NULL : 0);
        $reason = post_param_string('reason', STRING_MAGIC_NULL);
        $requires_reply = post_param_integer('requires_reply', fractional_edit() ? INTEGER_MAGIC_NULL : 0);

        if (fractional_edit()) {
            $answers = collapse_1d_complexity('pa_answer', $GLOBALS['FORUM_DB']->query_select('f_poll_answers', array('pa_answer'), array('pa_poll_id' => $poll_id)));
            foreach ($answers as $i => $answer) {
                $answers[$i] = post_param_string('answer_' . strval($i), $answer);
            }
        } else {
            $answers = array();
            foreach ($_POST as $key => $val) {
                if (!is_string($val)) {
                    continue;
                }

                if (substr($key, 0, 7) == 'answer_') {
                    if (@get_magic_quotes_gpc()) {
                        $val = stripslashes($val);
                    }
                    if ($val != '') {
                        $answers[] = $val;
                    }
                }
            }
        }

        require_code('cns_polls_action');
        require_code('cns_polls_action2');
        cns_edit_poll($poll_id, $question, $is_private, $is_open, $minimum_selections, $maximum_selections, $requires_reply, $answers, $reason);
        return $this->redirect_to('EDIT_TOPIC_POLL', $topic_id);
    }

    /**
     * The UI to grab a reason for deleting a poll.
     *
     * @return Tempcode The UI
     */
    public function delete_poll() // Type
    {
        $topic_id = get_param_integer('id');
        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        $this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('DELETE_TOPIC_POLL'));

        return $this->relay_with_reason('DELETE_TOPIC_POLL');
    }

    /**
     * The actualiser to delete a poll.
     *
     * @return Tempcode The UI
     */
    public function _delete_poll() // Type
    {
        $topic_id = get_param_integer('id');
        $poll_id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 't_poll_id', array('id' => $topic_id));
        if (is_null($poll_id)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        require_code('cns_polls_action');
        require_code('cns_polls_action2');
        cns_delete_poll($poll_id, post_param_string('reason'));
        return $this->redirect_to('DELETE_TOPIC_POLL', $topic_id);
    }

    /**
     * The UI to move a topic.
     *
     * @return Tempcode The UI
     */
    public function move_topic() // Type
    {
        $topic_id = get_param_integer('id');
        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('t_forum_id', 't_cache_first_title'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        $forum_id = $topic_info[0]['t_forum_id'];

        $this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('MOVE_TOPIC'));

        $this->check_has_mod_access($topic_id);

        $post_url = build_url(array('page' => '_SELF', 'type' => '_move_topic', 'id' => $topic_id), '_SELF');

        require_code('cns_forums2');

        // Certain aspects relating to the posting system
        $fields = new Tempcode();
        $fields->attach(form_input_line(do_lang_tempcode('TITLE'), '', 'title', $topic_info[0]['t_cache_first_title'], false));
        $fields->attach(form_input_tree_list(do_lang_tempcode('DESTINATION_FORUM'), do_lang_tempcode('DESCRIPTION_DESTINATION_FORUM'), 'to', null, 'choose_forum', array(), true, strval($forum_id)));
        $fields->attach(form_input_line(do_lang_tempcode('REASON'), do_lang_tempcode('DESCRIPTION_REASON'), 'description', '', false));

        $fields->attach(do_template('FORM_SCREEN_FIELD_SPACER', array('_GUID' => '20a9918ec1abc48c55dd7ba9ce5545a8', 'TITLE' => do_lang_tempcode('ACTIONS'))));
        $fields->attach(form_input_tick(do_lang_tempcode('REDIRECT_TO_TOPIC'), do_lang_tempcode('DESCRIPTION_REDIRECT_TO_TOPIC'), 'redir_topic', false));

        $topic_title = $topic_info[0]['t_cache_first_title'];
        $title = get_screen_title('_MOVE_TOPIC', true, array(escape_html($topic_title)));
        $submit_name = do_lang_tempcode('MOVE_TOPIC');

        return do_template('FORM_SCREEN', array(
            '_GUID' => '313fd175ccd376caa32794fedad21ac6',
            'SKIP_WEBSTANDARDS' => true,
            'STAFF_HELP_URL' => get_tutorial_url('tut_moderation'),
            'HIDDEN' => '',
            'TITLE' => $title,
            'FIELDS' => $fields,
            'TEXT' => '',
            'SUBMIT_ICON' => 'buttons__move',
            'SUBMIT_NAME' => $submit_name,
            'URL' => $post_url,
        ));
    }

    /**
     * The actualiser to move a topic.
     *
     * @return Tempcode The UI
     */
    public function _move_topic() // Type
    {
        $topic_id = get_param_integer('id');
        $to = post_param_integer('to');
        $from = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 't_forum_id', array('id' => $topic_id));
        require_code('cns_topics_action');
        require_code('cns_topics_action2');
        cns_move_topics($from, $to, array($topic_id));
        cns_edit_topic($topic_id, null, null, null, null, null, null, null, '', post_param_string('title'));
        return (post_param_integer('redir_topic', 0) == 0) ? $this->redirect_to_forum('MOVE_TOPIC', $from) : $this->redirect_to('MOVE_TOPIC', $topic_id);
    }

    /**
     * The actualiser to pin a topic.
     *
     * @return Tempcode The UI
     */
    public function pin_topic() // Type
    {
        $topic_id = get_param_integer('id');
        require_code('cns_topics_action');
        require_code('cns_topics_action2');
        cns_edit_topic($topic_id, null, null, null, null, 1, null, null, '');
        return $this->redirect_to('PIN_TOPIC', $topic_id);
    }

    /**
     * The actualiser to unpin a topic.
     *
     * @return Tempcode The UI
     */
    public function unpin_topic() // Type
    {
        $topic_id = get_param_integer('id');
        require_code('cns_topics_action');
        require_code('cns_topics_action2');
        cns_edit_topic($topic_id, null, null, null, null, 0, null, null, '');
        return $this->redirect_to('UNPIN_TOPIC', $topic_id);
    }

    /**
     * The actualiser to pin a topic.
     *
     * @return Tempcode The UI
     */
    public function sink_topic() // Type
    {
        $topic_id = get_param_integer('id');
        require_code('cns_topics_action');
        require_code('cns_topics_action2');
        cns_edit_topic($topic_id, null, null, null, null, null, 1, null, '');
        return $this->redirect_to('SINK_TOPIC', $topic_id);
    }

    /**
     * The actualiser to unpin a topic.
     *
     * @return Tempcode The UI
     */
    public function unsink_topic() // Type
    {
        $topic_id = get_param_integer('id');
        require_code('cns_topics_action');
        require_code('cns_topics_action2');
        cns_edit_topic($topic_id, null, null, null, null, null, 0, null, '');
        return $this->redirect_to('UNSINK_TOPIC', $topic_id);
    }

    /**
     * The actualiser to cascade a topic.
     *
     * @return Tempcode The UI
     */
    public function cascade_topic() // Type
    {
        $topic_id = get_param_integer('id');
        require_code('cns_topics_action');
        require_code('cns_topics_action2');
        cns_edit_topic($topic_id, null, null, null, null, null, null, 1, '');
        return $this->redirect_to('CASCADE_TOPIC', $topic_id);
    }

    /**
     * The actualiser to uncascade a topic.
     *
     * @return Tempcode The UI
     */
    public function uncascade_topic() // Type
    {
        $topic_id = get_param_integer('id');
        require_code('cns_topics_action');
        require_code('cns_topics_action2');
        cns_edit_topic($topic_id, null, null, null, null, null, null, 0, '');
        return $this->redirect_to('UNCASCADE_TOPIC', $topic_id);
    }

    /**
     * The actualiser to open a topic.
     *
     * @return Tempcode The UI
     */
    public function open_topic() // Type
    {
        $topic_id = get_param_integer('id');
        require_code('cns_topics_action');
        require_code('cns_topics_action2');
        cns_edit_topic($topic_id, null, null, null, 1, null, null, null, '');
        return $this->redirect_to('OPEN_TOPIC', $topic_id);
    }

    /**
     * The actualiser to close a topic.
     *
     * @return Tempcode The UI
     */
    public function close_topic() // Type
    {
        $topic_id = get_param_integer('id');
        require_code('cns_topics_action');
        require_code('cns_topics_action2');
        cns_edit_topic($topic_id, null, null, null, 0, null, null, null, '');
        return $this->redirect_to('CLOSE_TOPIC', $topic_id);
    }

    /**
     * The UI to run a multi-moderation.
     *
     * @return Tempcode The UI
     */
    public function multimod() // Type
    {
        require_lang('cns_multi_moderations');

        $mm_id = intval(substr(get_param_string('type', 'browse'), 3));
        $topic_id = get_param_integer('id');

        $this->check_has_mod_access($topic_id);

        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }
        $topic_title = $topic_info[0]['t_cache_first_title'];
        $this->handle_topic_breadcrumbs($topic_info[0]['t_forum_id'], $topic_id, $topic_info[0]['t_cache_first_title'], do_lang_tempcode('_PERFORM_MULTI_MODERATION', escape_html($topic_title)));

        $title = get_screen_title('_PERFORM_MULTI_MODERATION', true, array(escape_html($topic_title)));
        $mm = $GLOBALS['FORUM_DB']->query_select('f_multi_moderations', array('*'), array('id' => $mm_id), '', 1);
        if (!array_key_exists(0, $mm)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'multi_moderation'));
        }
        $_mm = $mm[0];
        $post_text = $_mm['mm_post_text'];
        $submit_name = do_lang_tempcode('PERFORM_MULTI_MODERATION');
        $post_url = build_url(array('page' => '_SELF', 'type' => '_multimod', 'id' => $topic_id, 'mm_id' => $mm_id), '_SELF', null, true);
        $fields = new Tempcode();
        $hidden = new Tempcode();
        $hidden->attach(build_keep_post_fields());
        $hidden->attach(build_keep_form_fields());
        $fields->attach(form_input_text(do_lang_tempcode('MM_POST_TEXT'), do_lang_tempcode('DESCRIPTION_MM_POST_TEXT'), 'post_text', $post_text, false));
        $options = array();
        if (addon_installed('cns_signatures')) {
            if (get_option('enable_skip_sig') == '1') {
                $options[] = array(do_lang_tempcode('SKIP_SIGNATURE'), 'skip_sig', false, do_lang_tempcode('DESCRIPTION_SKIP_SIGNATURE'));
            }
        }
        if (get_option('enable_post_emphasis') == '1') {
            $options[] = array(do_lang_tempcode('EMPHASISED'), 'is_emphasised', true, do_lang_tempcode('DESCRIPTION_EMPHASISED'));
        }
        $fields->attach(form_input_various_ticks($options, ''));
        $fields->attach(form_input_line(do_lang_tempcode('REASON'), do_lang_tempcode('OPTIONAL_REASON'), 'reason', '', false));

        $mm_title = get_translated_text($_mm['mm_name'], $GLOBALS['FORUM_DB']);
        $action_list = new Tempcode();
        if ($_mm['mm_open_state'] == 1) {
            $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_OPEN'));
        }
        if ($_mm['mm_open_state'] == 0) {
            $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_CLOSE'));
        }
        if ($_mm['mm_pin_state'] == 1) {
            $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_PIN'));
        }
        if ($_mm['mm_pin_state'] == 0) {
            $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_UNPIN'));
        }
        if ($_mm['mm_sink_state'] == 1) {
            $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_SINK'));
        }
        if ($_mm['mm_sink_state'] == 0) {
            $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_UNSINK'));
        }
        if ($_mm['mm_title_suffix'] != '') {
            $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_TITLE_SUFFIX', escape_html($_mm['mm_title_suffix'])));
        }
        if (!is_null($_mm['mm_move_to'])) {
            $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_MOVE', span(breadcrumb_segments_to_tempcode(cns_forum_breadcrumbs($_mm['mm_move_to'])), '', 'breadcrumbs')));
        }
        $action_list->attach(do_lang_tempcode('MULTI_MODERATION_WILL_POST'));
        $text = do_lang_tempcode('MULTI_MODERATION_WILL', make_string_tempcode($mm_title), $action_list);

        return do_template('FORM_SCREEN', array(
            '_GUID' => '2eef0c445d207bb10ff3fd28ea32ef8c',
            'STAFF_HELP_URL' => get_tutorial_url('tut_moderation'),
            'PREVIEW' => true,
            'HIDDEN' => $hidden,
            'TITLE' => $title,
            'TEXT' => $text,
            'URL' => $post_url,
            'FIELDS' => $fields,
            'SUBMIT_ICON' => 'menu__adminzone__structure__forum__multi_moderations',
            'SUBMIT_NAME' => $submit_name,
            'SUPPORT_AUTOSAVE' => true,
        ));
    }

    /**
     * The actualiser to run a multi moderation.
     *
     * @return Tempcode The UI
     */
    public function _multimod() // Type
    {
        require_lang('cns_multi_moderations');

        $topic_id = get_param_integer('id');
        $mm_id = get_param_integer('mm_id');
        require_code('cns_moderation_action');
        require_code('cns_moderation_action2');
        cns_perform_multi_moderation($mm_id, $topic_id, post_param_string('reason'), post_param_string('post_text'), post_param_integer('is_emphasised', 0), post_param_integer('skip_sig', 0));

        return $this->redirect_to('PERFORM_MULTI_MODERATION', $topic_id);
    }

    /**
     * A redirect for viewing post history: made like this so the history button can fit into the same URL architecture as the other post buttons.
     *
     * @return Tempcode The UI
     */
    public function topic_history() // Type
    {
        $title = get_screen_title('actionlog:REVISIONS');

        // We should be somewhere else entirely - it's just our moderator action list took us here
        $url = build_url(array('page' => 'admin_revisions', 'type' => 'browse', 'resource_types' => 'topic,post', 'category_id' => get_param_integer('id')), get_module_zone('admin_revisions'));
        return redirect_screen($title, $url);
    }

    /**
     * The actualiser to mark a forum as read.
     *
     * @return Tempcode The UI
     */
    public function mark_read() // Type
    {
        $_forum_id = get_param_string('id');
        if ($_forum_id == '') {
            $forum_id = null;
        } else {
            $forum_id = intval($_forum_id);
        }

        require_code('cns_forums_action2');
        cns_ping_forum_read_all($forum_id);

        if ($forum_id != db_get_first_id()) {
            // Take user to parent forum
            $forum_id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_forums', 'f_parent_forum', array('id' => $forum_id));
        }

        return $this->redirect_to_forum('MARK_READ', $forum_id);
    }

    /**
     * The UI to make a normal topic a Private Topic.
     *
     * @return Tempcode The UI
     */
    public function make_private()
    {
        $topic_id = get_param_integer('id');

        $this->check_has_mod_access($topic_id);

        $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('*'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $topic_info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }

        $a = $topic_info[0]['t_cache_first_username'];
        $b = $GLOBALS['FORUM_DRIVER']->get_username(get_member());

        $title = get_screen_title('MAKE_PERSONAL');
        $text = do_lang_tempcode('MAKE_PERSONAL_DESCRIPTION');
        $submit_name = do_lang_tempcode('MAKE_PERSONAL');
        $post_url = build_url(array('page' => '_SELF', 'type' => '_make_private'), '_SELF');
        $fields = new Tempcode();
        $hidden = form_input_hidden('id', strval($topic_id));
        $fields->attach(form_input_username(do_lang_tempcode('FROM'), '', 'a', $a, true));
        $fields->attach(form_input_username(do_lang_tempcode('TO'), '', 'b', $b, true));

        return do_template('FORM_SCREEN', array(
            '_GUID' => '9416df197ee157510e9d6be7458d510f',
            'STAFF_HELP_URL' => get_tutorial_url('tut_correspondence'),
            'HIDDEN' => $hidden,
            'TITLE' => $title,
            'TEXT' => $text,
            'URL' => $post_url,
            'FIELDS' => $fields,
            'SUBMIT_ICON' => 'buttons__proceed',
            'SUBMIT_NAME' => $submit_name,
        ));
    }

    /**
     * The actualiser to make a normal topic a Private Topic.
     *
     * @return Tempcode The UI
     */
    public function _make_private()
    {
        $topic_id = post_param_integer('id');
        $forum_id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_topics', 't_forum_id', array('id' => $topic_id));
        if (is_null($forum_id)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'topic'));
        }

        if (!cns_may_moderate_forum($forum_id, get_member())) {
            access_denied('I_ERROR');
        }

        $_a = post_param_string('a');
        $_b = post_param_string('b');
        $a = $GLOBALS['FORUM_DRIVER']->get_member_from_username($_a);
        if (($a === null) || (is_guest($a))) {
            warn_exit(do_lang_tempcode('_MEMBER_NO_EXIST', escape_html($_a)));
        }
        $b = $GLOBALS['FORUM_DRIVER']->get_member_from_username($_b);
        if (($b === null) || (is_guest($b))) {
            warn_exit(do_lang_tempcode('_MEMBER_NO_EXIST', escape_html($_b)));
        }

        $GLOBALS['FORUM_DB']->query_update('f_topics', array('t_pt_from' => $a, 't_pt_to' => $b, 't_forum_id' => null), array('id' => $topic_id), '', 1);
        require_code('sitemap_xml');
        notify_sitemap_node_delete('_SEARCH:topicview:id=' . strval($topic_id));

        require_code('notifications');
        enable_notifications('cns_topic', strval($topic_id), $a); // from
        enable_notifications('cns_topic', strval($topic_id), $b); // to

        // Update forum cache view
        require_code('cns_posts_action2');
        cns_force_update_forum_caching($forum_id, -1);

        return $this->redirect_to('MAKE_PERSONAL', $topic_id);
    }

    /**
     * Redirect to a screen suitable for celebrating a members birthday (existing topic, if possible).
     *
     * @return Tempcode The UI
     */
    public function birthday()
    {
        $id = get_param_string('id');
        $topic_id = $GLOBALS['FORUM_DB']->query_value_if_there('SELECT id FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_topics WHERE t_cache_first_time>' . strval(time() - 60 * 60 * 24 * 3) . ' AND ' . db_string_equal_to('t_cache_first_title', do_lang('HAPPY_BIRTHDAY_PERSON', $id)));
        if (!is_null($topic_id)) {
            $title = get_screen_title('VIEW_TOPIC');
            $url = $GLOBALS['FORUM_DRIVER']->topic_url($topic_id, '', true);
            return redirect_screen($title, $url, do_lang_tempcode('REDIRECTING_TO_BIRTHDAY_TOPIC'));
        }
        $_POST['title'] = do_lang('HAPPY_BIRTHDAY_PERSON', $id);
        if (@get_magic_quotes_gpc()) {
            $_POST['title'] = addslashes($_POST['title']);
        }
        $forum_id = $GLOBALS['FORUM_DB']->query_select_value_if_there('f_forums', 'id', array('f_name' => get_option('main_forum_name')));
        if (is_null($forum_id)) {
            $forum_id = db_get_first_id();
        }
        $_GET['id'] = strval($forum_id);
        return $this->new_topic(false, null, file_exists(get_file_base() . '/themes/default/images/emoticons/index.html') ? 'emoticons/birthday' : 'cns_emoticons/birthday');
    }
}
