/**
 * Validate a post.
 *
 * @param  AUTO_LINK		The ID of the post.
 * @param  ?AUTO_LINK	The ID of the topic that contains the post (NULL: find out from the DB).
 * @param  ?AUTO_LINK	The forum that the topic containing the post is in (NULL: find out from the DB).
 * @param  ?MEMBER		The member that made the post being validated (NULL: find out from the DB).
 * @param  ?LONG_TEXT	The post, in Comcode format (NULL: It'll have to be looked-up).
 * @return AUTO_LINK		The ID of the topic (whilst this could be known without calling this function, as we've gone to effort and grabbed it from the DB, it might turn out useful for something).
 */
function ocf_validate_post($post_id, $topic_id = NULL, $forum_id = NULL, $poster = NULL, $post = NULL)
{
    require_code('submit');
    send_content_validated_notification('post', strval($post_id));
    if (is_null($topic_id)) {
        $post_info = $GLOBALS['FORUM_DB']->query_select('f_posts', array('*'), array('id' => $post_id), '', 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']);
    }
    if (!ocf_may_moderate_forum($forum_id)) {
        access_denied('I_ERROR');
    }
    $topic_info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('t_cache_first_post_id', 't_pt_from', 't_cache_first_title'), array('id' => $topic_id), '', 1);
    $GLOBALS['FORUM_DB']->query_update('f_posts', array('p_validated' => 1), array('id' => $post_id), '', 1);
    if (!array_key_exists(0, $topic_info)) {
        return $topic_id;
    }
    // Dodgy, topics gone missing
    $is_starter = $topic_info[0]['t_cache_first_post_id'] == $post_id;
    $GLOBALS['FORUM_DB']->query_update('f_topics', array('t_validated' => 1), array('id' => $topic_id), '', 1);
    $_url = build_url(array('page' => 'topicview', 'id' => $topic_id), 'forum', NULL, false, false, true, 'post_' . strval($post_id));
    $url = $_url->evaluate();
    if (!is_null($forum_id)) {
        $post_counts = $GLOBALS['FORUM_DB']->query_value_null_ok('f_forums', 'f_post_count_increment', array('id' => $forum_id));
        if ($post_counts === 1) {
            ocf_force_update_member_post_count($poster, 1);
            require_code('ocf_posts_action2');
            ocf_member_handle_promotion($poster);
        }
    }
    ocf_send_topic_notification($url, $topic_id, $forum_id, $poster, $is_starter, $post, $topic_info[0]['t_cache_first_title'], NULL, !is_null($topic_info[0]['t_pt_from']));
    if (!is_null($forum_id)) {
        ocf_force_update_forum_cacheing($forum_id, 0, 1);
    }
    ocf_force_update_topic_cacheing($topic_id, 1, true, true);
    return $topic_id;
    // Because we might want this
}
/**
 * Makes a post in the specified forum, in the specified topic according to the given specifications. If the topic doesn't exist, it is created along with a spacer-post.
 * Spacer posts exist in order to allow staff to delete the first true post in a topic. Without spacers, this would not be possible with most forum systems. They also serve to provide meta information on the topic that cannot be encoded in the title (such as a link to the content being commented upon).
 *
 * @param  object			Link to the real forum driver
 * @param  SHORT_TEXT	The forum name
 * @param  SHORT_TEXT	The topic identifier (usually <content-type>_<content-id>)
 * @param  MEMBER			The member ID
 * @param  LONG_TEXT		The post title
 * @param  LONG_TEXT		The post content in Comcode format
 * @param  string			The topic title; must be same as content title if this is for a comment topic
 * @param  string			This is put together with the topic identifier to make a more-human-readable topic title or topic description (hopefully the latter and a $content_title title, but only if the forum supports descriptions)
 * @param  ?URLPATH		URL to the content (NULL: do not make spacer post)
 * @param  ?TIME			The post time (NULL: use current time)
 * @param  ?IP				The post IP address (NULL: use current members IP address)
 * @param  ?BINARY		Whether the post is validated (NULL: unknown, find whether it needs to be marked unvalidated initially). This only works with the OCF driver.
 * @param  ?BINARY		Whether the topic is validated (NULL: unknown, find whether it needs to be marked unvalidated initially). This only works with the OCF driver.
 * @param  boolean		Whether to skip post checks
 * @param  SHORT_TEXT	The name of the poster
 * @param  ?AUTO_LINK	ID of post being replied to (NULL: N/A)
 * @param  boolean		Whether the reply is only visible to staff
 * @param  ?ID_TEXT		DO NOT send notifications to: The notification code (NULL: no restriction)
 * @param  ?SHORT_TEXT	DO NOT send notifications to: The category within the notification code (NULL: none / no restriction)
 * @return array			Topic ID (may be NULL), and whether a hidden post has been made
 */
function _helper_make_post_forum_topic($this_ref, $forum_name, $topic_identifier, $member_id, $post_title, $post, $content_title, $topic_identifier_encapsulation_prefix, $content_url, $time, $ip, $validated, $topic_validated, $skip_post_checks, $poster_name_if_guest, $parent_id, $staff_only, $no_notify_for__notification_code, $no_notify_for__code_category)
{
    if (is_null($time)) {
        $time = time();
    }
    if (is_null($ip)) {
        $ip = get_ip_address();
    }
    require_code('comcode_check');
    check_comcode($post, NULL, false, NULL, true);
    require_code('ocf_topics');
    require_code('ocf_posts');
    //require_code('ocf_forums');
    require_lang('ocf');
    require_code('ocf_posts_action');
    require_code('ocf_posts_action2');
    if (!is_integer($forum_name)) {
        $forum_id = $this_ref->forum_id_from_name($forum_name);
        if (is_null($forum_id)) {
            warn_exit(do_lang_tempcode('MISSING_FORUM', escape_html($forum_name)));
        }
    } else {
        $forum_id = (int) $forum_name;
    }
    $topic_id = $this_ref->find_topic_id_for_topic_identifier($forum_name, $topic_identifier);
    $update_caching = false;
    $support_attachments = false;
    if (!running_script('stress_test_loader') && get_page_name() != 'admin_import') {
        $update_caching = true;
        $support_attachments = true;
    }
    if (is_null($topic_id)) {
        $is_starter = true;
        require_code('ocf_topics_action');
        $topic_id = ocf_make_topic($forum_id, $topic_identifier_encapsulation_prefix . ': #' . $topic_identifier, '', $topic_validated, 1, 0, 0, 0, NULL, NULL, false, 0, NULL, $content_url);
        if (strpos($topic_identifier, ':') !== false) {
            // Sync comment_posted ones to also monitor the forum ones; no need for opposite way as comment ones already trigger forum ones
            $start = 0;
            $max = 300;
            require_code('notifications');
            $ob = _get_notification_ob_for_code('comment_posted');
            do {
                list($members, $possibly_has_more) = $ob->list_members_who_have_enabled('comment_posted', $topic_identifier, NULL, $start, $max);
                foreach ($members as $to_member_id => $setting) {
                    enable_notifications('ocf_topic', strval($topic_id), $to_member_id);
                }
                $start += $max;
            } while ($possibly_has_more);
        }
        // Make spacer post
        if (!is_null($content_url)) {
            $spacer_title = $content_title;
            $home_link = hyperlink($content_url, escape_html($content_title));
            $spacer_post = '[semihtml]' . do_lang('SPACER_POST', $home_link->evaluate(), '', '', get_site_default_lang()) . '[/semihtml]';
            ocf_make_post($topic_id, $spacer_title, $spacer_post, 0, true, 1, 0, do_lang('SYSTEM'), $ip, $time, db_get_first_id(), NULL, NULL, NULL, false, $update_caching, $forum_id, $support_attachments, $content_title, 0, NULL, false, false, false, false);
            $is_starter = false;
        }
        $is_new = true;
    } else {
        $is_starter = false;
        $is_new = false;
    }
    $GLOBALS['LAST_TOPIC_ID'] = $topic_id;
    $GLOBALS['LAST_TOPIC_IS_NEW'] = $is_new;
    if ($post == '') {
        return array(NULL, false);
    }
    ocf_check_post($post, $topic_id, $member_id);
    $poster_name = $poster_name_if_guest;
    if ($poster_name == '') {
        $poster_name = $this_ref->get_username($member_id);
    }
    $post_id = ocf_make_post($topic_id, $post_title, $post, 0, $is_starter, $validated, 0, $poster_name, $ip, $time, $member_id, $staff_only ? $GLOBALS['FORUM_DRIVER']->get_guest_id() : NULL, NULL, NULL, false, $update_caching, $forum_id, $support_attachments, $content_title, 0, NULL, false, $skip_post_checks, false, false, $parent_id);
    $GLOBALS['LAST_POST_ID'] = $post_id;
    if ($is_new) {
        // Broken cache now for the rest of this page view - fix by flushing
        global $TOPIC_IDENTIFIERS_TO_IDS;
        $TOPIC_IDENTIFIERS_TO_IDS = array();
    }
    // Send out notifications
    $_url = build_url(array('page' => 'topicview', 'type' => 'findpost', 'id' => $post_id), 'forum', NULL, false, false, true, 'post_' . strval($post_id));
    $url = $_url->evaluate();
    ocf_send_topic_notification($url, $topic_id, $forum_id, $member_id, $is_new, $post, $content_title, NULL, false, $no_notify_for__notification_code, $no_notify_for__code_category);
    $is_hidden = false;
    if (!running_script('stress_test_loader') && get_page_name() != 'admin_import') {
        $validated_actual = $this_ref->connection->query_value('f_posts', 'p_validated', array('id' => $post_id));
        if ($validated_actual == 0) {
            require_code('site');
            attach_message(do_lang_tempcode('SUBMIT_UNVALIDATED'), 'inform');
            $is_hidden = true;
        }
    }
    return array($topic_id, $is_hidden);
}
/**
 * Add a post.
 *
 * @param  AUTO_LINK		The ID of the topic to add the post to.
 * @param  SHORT_TEXT	The title of the post (may be blank).
 * @param  LONG_TEXT		The post.
 * @param  BINARY			Whether to skip showing the posters signature in the post.
 * @param  boolean		Whether the post is the first in the topic.
 * @param  ?BINARY		Whether the post is validated (NULL: unknown, find whether it needs to be marked unvalidated initially).
 * @param  BINARY			Whether the post is marked emphasised.
 * @param  ?string		The name of the person making the post (NULL: username of current member).
 * @param  ?IP				The IP address the post is to be made under (NULL: IP of current user).
 * @param  ?TIME			The time of the post (NULL: now).
 * @param  ?MEMBER		The poster (NULL: current member).
 * @param  ?MEMBER		The member that this post is intended solely for (NULL: public).
 * @param  ?TIME			The last edit time of the post (NULL: never edited).
 * @param  ?MEMBER		The member that was last to edit the post (NULL: never edited).
 * @param  boolean		Whether to check permissions for whether the post may be made as it is given.
 * @param  boolean		Whether to update the caches after making the post.
 * @param  ?AUTO_LINK	The forum the post will be in (NULL: find out from the DB).
 * @param  boolean		Whether to allow attachments in this post.
 * @param  ?string		The title of the topic (NULL: find from the DB).
 * @param  BINARY			Whether the topic is a sunk topic.
 * @param  ?AUTO_LINK 	Force an ID (NULL: don't force an ID)
 * @param  boolean		Whether to make the post anonymous
 * @param  boolean		Whether to skip post checks
 * @param  boolean		Whether this is for a new Private Topic
 * @param  boolean		Whether to explicitly insert the Comcode with admin privileges
 * @param  ?AUTO_LINK	Parent post ID (NULL: none-threaded/root-of-thread)
 * @return AUTO_LINK		The ID of the new post.
 */
function ocf_make_post($topic_id, $title, $post, $skip_sig = 0, $is_starter = false, $validated = NULL, $is_emphasised = 0, $poster_name_if_guest = NULL, $ip_address = NULL, $time = NULL, $poster = NULL, $intended_solely_for = NULL, $last_edit_time = NULL, $last_edit_by = NULL, $check_permissions = true, $update_cacheing = true, $forum_id = NULL, $support_attachments = true, $topic_title = '', $sunk = 0, $id = NULL, $anonymous = false, $skip_post_checks = false, $is_pt = false, $insert_comcode_as_admin = false, $parent_id = NULL)
{
    if (is_null($poster)) {
        $poster = get_member();
    }
    if ($check_permissions) {
        if (strlen($title) > 120) {
            warn_exit(do_lang_tempcode('TITLE_TOO_LONG'));
        }
        if (get_option('prevent_shouting') == '1') {
            if (strtoupper($title) == $title) {
                $title = ucwords($title);
            }
        }
        if (is_null($intended_solely_for) && !$skip_post_checks) {
            ocf_check_post($post, $topic_id, $poster);
        }
    }
    if (is_null($ip_address)) {
        $ip_address = get_ip_address();
    }
    if (is_null($time)) {
        $time = time();
        $send_notification = true;
    } else {
        $send_notification = false;
    }
    if (is_null($poster_name_if_guest)) {
        if ($poster == $GLOBALS['OCF_DRIVER']->get_guest_id() || $anonymous) {
            $poster_name_if_guest = do_lang('GUEST');
        } else {
            $poster_name_if_guest = $GLOBALS['OCF_DRIVER']->get_username($poster);
            if (is_null($poster_name_if_guest)) {
                $poster_name_if_guest = do_lang('UNKNOWN');
            }
        }
    }
    if (is_null($forum_id) || $topic_title == '' && !$is_starter) {
        $info = $GLOBALS['FORUM_DB']->query_select('f_topics', array('t_is_open', 't_pt_from', 't_pt_to', 't_forum_id', 't_cache_last_member_id', 't_cache_first_title'), array('id' => $topic_id), '', 1);
        if (!array_key_exists(0, $info)) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE'));
        }
        $forum_id = $info[0]['t_forum_id'];
        $topic_title = $info[0]['t_cache_first_title'];
        if ($topic_title == '') {
            $topic_title = $title;
        }
        if ($check_permissions) {
            if ($info[0]['t_pt_from'] != get_member() && $info[0]['t_pt_to'] != get_member() && !ocf_has_special_pt_access($topic_id) && !has_specific_permission(get_member(), 'view_other_pt') && is_null($forum_id)) {
                access_denied('I_ERROR');
            }
        }
    }
    if (is_null($forum_id)) {
        if ($check_permissions && $poster == $GLOBALS['OCF_DRIVER']->get_guest_id()) {
            access_denied('I_ERROR');
        }
        $validated = 1;
        // Personal posts always validated
    } else {
        if ($check_permissions) {
            if ($info[0]['t_is_open'] == 0 && !ocf_may_moderate_forum($forum_id)) {
                access_denied('I_ERROR');
            }
            $last_member_id = $info[0]['t_cache_last_member_id'];
            if (!ocf_may_post_in_topic($forum_id, $topic_id, $last_member_id) && !$is_starter) {
                access_denied('I_ERROR');
            }
        }
    }
    // Ensure parent post is from the same topic
    if (!is_null($parent_id)) {
        $test_topic_id = $GLOBALS['FORUM_DB']->query_value_null_ok('f_posts', 'p_topic_id', array('id' => $parent_id), ' AND ' . ocf_get_topic_where($topic_id, $poster));
        if (is_null($test_topic_id)) {
            $parent_id = NULL;
        }
    }
    if (is_null($validated) || $validated == 1 && $check_permissions) {
        if (!is_null($forum_id) && !has_specific_permission(get_member(), 'bypass_validation_lowrange_content', 'topics', array('forums', $forum_id))) {
            $validated = 0;
        } else {
            $validated = 1;
        }
    }
    if (!$support_attachments) {
        $lang_id = insert_lang_comcode($post, 4, $GLOBALS['FORUM_DB'], $insert_comcode_as_admin);
    } else {
        $lang_id = 0;
    }
    if (!addon_installed('unvalidated')) {
        $validated = 1;
    }
    $map = array('p_title' => substr($title, 0, 255), 'p_post' => $lang_id, 'p_ip_address' => $ip_address, 'p_time' => $time, 'p_poster' => $anonymous ? db_get_first_id() : $poster, 'p_poster_name_if_guest' => substr($poster_name_if_guest, 0, 80), 'p_validated' => $validated, 'p_topic_id' => $topic_id, 'p_is_emphasised' => $is_emphasised, 'p_cache_forum_id' => $forum_id, 'p_last_edit_time' => $last_edit_time, 'p_last_edit_by' => $last_edit_by, 'p_intended_solely_for' => $intended_solely_for, 'p_skip_sig' => $skip_sig, 'p_parent_id' => $parent_id);
    if (!is_null($id)) {
        $map['id'] = $id;
    }
    $post_id = $GLOBALS['FORUM_DB']->query_insert('f_posts', $map, true);
    if ($support_attachments) {
        require_code('attachments2');
        $lang_id = insert_lang_comcode_attachments(4, $post, 'ocf_post', strval($post_id), $GLOBALS['FORUM_DB']);
        $GLOBALS['FORUM_DB']->query_update('f_posts', array('p_post' => $lang_id), array('id' => $post_id), '', 1);
    }
    $_url = build_url(array('page' => 'topicview', 'type' => 'findpost', 'id' => $post_id), 'forum', NULL, false, false, true, 'post_' . strval($post_id));
    $url = $_url->evaluate();
    if ($validated == 0) {
        if ($check_permissions) {
            // send_validation_mail is used for other content - but forum is special
            $subject = do_lang('POST_REQUIRING_VALIDATION_MAIL_SUBJECT', $topic_title, NULL, NULL, get_site_default_lang());
            $post_text = get_translated_text($lang_id, $GLOBALS['FORUM_DB'], get_site_default_lang());
            $mail = do_lang('POST_REQUIRING_VALIDATION_MAIL', comcode_escape($url), comcode_escape($poster_name_if_guest), $post_text);
            require_code('notifications');
            dispatch_notification('needs_validation', NULL, $subject, $mail);
        }
    } else {
        if ($check_permissions) {
            if ($send_notification) {
                $post_comcode = get_translated_text($lang_id, $GLOBALS['FORUM_DB']);
                require_code('ocf_posts_action2');
                ocf_send_topic_notification($url, $topic_id, $forum_id, $anonymous ? db_get_first_id() : $poster, $is_starter, $post_comcode, $topic_title, $intended_solely_for, $is_pt);
                // Send a notification for the inline PP
                if (!is_null($intended_solely_for)) {
                    require_code('notifications');
                    $msubject = do_lang('NEW_PERSONAL_POST_SUBJECT', $topic_title, NULL, NULL, get_lang($intended_solely_for));
                    $mmessage = do_lang('NEW_PERSONAL_POST_MESSAGE', comcode_escape($GLOBALS['FORUM_DRIVER']->get_username($anonymous ? db_get_first_id() : $poster)), comcode_escape($topic_title), array(comcode_escape($url), $post_comcode), get_lang($intended_solely_for));
                    dispatch_notification('ocf_new_pt', NULL, $msubject, $mmessage, array($intended_solely_for), $anonymous ? db_get_first_id() : $poster);
                }
            }
        }
    }
    if ($check_permissions) {
        // Is the user gonna automatically enable notifications for this?
        $auto_monitor_contrib_content = $GLOBALS['OCF_DRIVER']->get_member_row_field($poster, 'm_auto_monitor_contrib_content');
        if ($auto_monitor_contrib_content == 1) {
            require_code('notifications');
            enable_notifications('ocf_topic', strval($topic_id), $poster);
        }
    }
    if ($update_cacheing) {
        if (function_exists('get_member')) {
            if (function_exists('ocf_ping_topic_read')) {
                ocf_ping_topic_read($topic_id);
            }
            if (is_null($forum_id)) {
                $with = $info[0]['t_pt_from'];
                if ($with == get_member()) {
                    $with = $info[0]['t_pt_to'];
                }
                decache('side_ocf_personal_topics', array($with));
                decache('_new_pp', array($with));
            }
            if (get_option('show_post_validation') == '1') {
                decache('main_staff_checklist');
            }
        }
        if (is_null($intended_solely_for)) {
            if ($validated == 1) {
                require_code('ocf_posts_action2');
                ocf_force_update_topic_cacheing($topic_id, 1, true, $is_starter, $post_id, $time, $title, $lang_id, $poster_name_if_guest, $poster);
            }
            if ($validated == 1) {
                if (!is_null($forum_id)) {
                    require_code('ocf_posts_action2');
                    // Find if the topic is validated. This can be approximate, if we don't get 1 then ocf_force_update_forum_cacheing will do a search, making the code very slightly slower
                    if (!$check_permissions || is_null($forum_id)) {
                        $topic_validated = 1;
                    } else {
                        if ($is_starter) {
                            $topic_validated = has_specific_permission($poster, 'bypass_validation_midrange_content', 'topics', array('forums', $forum_id)) ? 1 : 0;
                        } else {
                            $topic_validated = $GLOBALS['FORUM_DB']->query_value('f_topics', 't_validated', array('id' => $topic_id));
                        }
                    }
                    ocf_force_update_forum_cacheing($forum_id, $is_starter ? 1 : 0, 1, $topic_validated == 0 ? NULL : $topic_id, $topic_validated == 0 ? NULL : $topic_title, $topic_validated == 0 ? NULL : $time, $topic_validated == 0 ? NULL : $poster_name_if_guest, $topic_validated == 0 ? NULL : $poster, $topic_validated == 0 ? NULL : $forum_id);
                }
            }
        }
        // Update post count
        if (!is_null($forum_id)) {
            $post_counts = is_null($forum_id) ? 1 : $GLOBALS['FORUM_DB']->query_value_null_ok('f_forums', 'f_post_count_increment', array('id' => $forum_id));
            if ($post_counts === 1 && !$anonymous && $validated == 1) {
                ocf_force_update_member_post_count($poster, 1);
            }
            if ($check_permissions) {
                ocf_decache_ocp_blocks($forum_id, NULL, $intended_solely_for);
            }
            // i.e. we don't run this if in installer
        }
        if ($poster != $GLOBALS['OCF_DRIVER']->get_guest_id()) {
            require_code('ocf_posts_action2');
            ocf_member_handle_promotion($poster);
        }
    }
    return $post_id;
}