<?php /*

 Composr
 Copyright (c) Christopher Graham, 2004-2024

 See docs/LICENSE.md for full licensing information.


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

*/

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

/**
 * Standard code module initialisation function.
 *
 * @ignore
 */
function init__cns_posts()
{
    global $SIGNATURES_CACHE;
    $SIGNATURES_CACHE = [];
}

/**
 * Find whether a member can access a particular post.
 *
 * @param  AUTO_LINK $post_id Post ID
 * @param  ?MEMBER $member_id Member involved (null: current member)
 * @param  ?array $post_details Post row (null: lookup)
 * @return boolean Whether they can
 */
function has_post_access(int $post_id, ?int $member_id = null, ?array $post_details = null) : bool
{
    if ($member_id === null) {
        $member_id = get_member();
    }

    if ($post_details === null) {
        $table_prefix = $GLOBALS['FORUM_DB']->get_table_prefix();
        $_post_details = $GLOBALS['FORUM_DB']->query_select('f_posts p JOIN ' . $table_prefix . 'f_topics t ON t.id=p.p_topic_id', ['*', 't.id AS topic_id', 'p.id AS post_id'], ['p.id' => $post_id], '', 1);
        if (!isset($_post_details[0])) {
            warn_exit(do_lang_tempcode('MISSING_RESOURCE', 'post'));
        }
        $post_details = $_post_details[0];
    }

    if (!has_privilege($member_id, 'view_other_pt')) {
        if (($post_details['p_whisper_to_member'] !== null) && ($post_details['p_whisper_to_member'] != $member_id) && ($post_details['p_posting_member'] != $member_id)) {
            return false;
        }
    }

    if (addon_installed('validation')) {
        if (($post_details['p_validated'] == 0) && (!has_privilege($member_id, 'jump_to_not_validated'))) {
            return false;
        }
    }

    if (!cns_may_access_topic($post_details['topic_id'], $member_id, $post_details/*Contains topic details too*/)) {
        return false;
    }

    return true;
}

/**
 * Find whether a member may post in a certain topic.
 *
 * @param  ?AUTO_LINK $forum_id The forum ID of the forum the topic is in (null: private topics)
 * @param  AUTO_LINK $topic_id The topic ID is in
 * @param  ?MEMBER $last_member_id The last poster in the topic (null: do not check for double posting)
 * @param  boolean $closed Whether this topic is closed
 * @param  ?MEMBER $member_id The member (null: current member)
 * @param  boolean $will_be_private_post Whether this post will be private
 * @return boolean The answer
 */
function cns_may_post_in_topic(?int $forum_id, int $topic_id, ?int $last_member_id = null, bool $closed = false, ?int $member_id = null, bool $will_be_private_post = false) : bool
{
    if ($member_id === null) {
        $member_id = get_member();
    }

    $show_messages = false;
    if ($member_id == get_member()) {
        $show_messages = true;
    }

    if (!has_actual_page_access($member_id, 'topics')) {
        return false;
    }

    require_code('cns_forums');
    if (($closed) && (!cns_may_moderate_forum($forum_id, $member_id))) {
        return false;
    }

    $sql = 'SELECT p_warning_id FROM ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_warnings_punitive p RIGHT JOIN ' . $GLOBALS['FORUM_DB']->get_table_prefix() . 'f_warnings w ON w.id=p.p_warning_id WHERE ((' . db_string_equal_to('p.p_action', '_PUNITIVE_SILENCE_FROM_TOPIC') . ' AND ' . db_string_equal_to('p.p_param_a', strval($topic_id)) . ')';
    if ($forum_id !== null) {
        $sql .= ' OR (' . db_string_equal_to('p.p_action', '_PUNITIVE_SILENCE_FROM_FORUM') . ' AND ' . db_string_equal_to('p.p_param_a', strval($forum_id)) . ')';
    }
    $sql .= ') AND p.p_reversed=0 AND w.w_member_id=' . strval($member_id) . ' AND p.p_param_b>' . strval(time());
    $test = $GLOBALS['FORUM_DB']->query_value_if_there($sql, false, true);
    if ($test !== null) {
        if ($show_messages) {
            attach_message(do_lang_tempcode('SILENCED_FROM_TOPIC'), 'warn');
        }
        return false;
    }

    if (!cns_may_access_topic($topic_id, $member_id)) {
        return false;
    }

    if ($forum_id === null) {
        // No extra checks for private topics...

        return true;
    }

    // Extra checks for regular topics...

    if (!has_privilege($member_id, 'submit_lowrange_content', 'topics', ['forums', $forum_id, 'topics', $topic_id])) {
        return false;
    }

    if (($last_member_id !== null) && (!has_privilege($member_id, 'double_post'))) {
        if (($last_member_id == $member_id) && (!$will_be_private_post)) {
            return false;
        }
    }

    return true;
}

/**
 * Find whether a member may edit the detailed post.
 *
 * @param  AUTO_LINK $post_id The post ID
 * @param  ?TIME $post_time The time of the post (null: lookup)
 * @param  ?MEMBER $resource_owner The owner of the post (null: lookup)
 * @param  ?AUTO_LINK $forum_id The forum the post is in (null: is a Private Topic, unless $post_time is null in which case we look this up too)
 * @param  ?MEMBER $member_id The member (null: current member)
 * @param  ?boolean $topic_is_closed Whether the topic the post is in is closed (null: don't consider this, maybe we're not considering any one specific case, unless $post_time is null in which case we look this up to)
 * @param  ?string $reason The reason for the topic being closed is put here (null: no interesting reason)
 * @return boolean The answer
 */
function cns_may_edit_post_by(int $post_id, ?int $post_time, ?int $resource_owner, ?int $forum_id, ?int $member_id = null, ?bool $topic_is_closed = null, ?string &$reason = null) : bool
{
    if (!addon_installed('cns_forum')) {
        return false;
    }

    if ($member_id === null) {
        $member_id = get_member();
    }

    $reason = null;

    if ($post_time === null) {
        $posts = $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', ['p_time', 'p_posting_member', 'p_cache_forum_id', 't_is_open'], ['id' => $post_id], '', 1);
        if (!array_key_exists(0, $posts)) {
            $reason = do_lang('INTERNAL_ERROR');
            return false;
        }
        $post_time = $posts[0]['p_time'];
        $resource_owner = $posts[0]['p_posting_member'];
        $forum_id = $posts[0]['p_cache_forum_id'];
        $topic_is_closed = ($posts[0]['t_is_open'] == 0);
    }

    if ($topic_is_closed === true) {
        require_code('cns_forums');
        if (!cns_may_moderate_forum($forum_id, $member_id)) {
            return false;
        }
    }

    if ($forum_id !== null || !has_privilege($member_id, 'moderate_private_topic')) {
        if (!has_edit_permission('low', $member_id, $resource_owner, 'topics', ['forums', $forum_id])) {
            return false;
        }
    }

    if (((time() - $post_time) > intval(get_option('edit_time_limit')) * 60) && (!has_privilege($member_id, 'exceed_post_edit_time_limit'))) {
        require_lang('cns');
        $reason = do_lang('EXCEEDED_TIME_LIMIT', escape_html(display_time_period(intval(get_option('edit_time_limit')) * 60)));
        return false;
    }

    if ($forum_id === null) {
        if (!has_privilege($member_id, 'moderate_private_topic')) {
            if (($resource_owner != $member_id) || (!has_privilege($member_id, 'delete_private_topic_posts'))) {
                return false;
            }
        }
    } else {
        $comments_forum = get_option('comments_forum_name', true);
        $ticket_forum = get_option('ticket_forum_name', true);
        if (!has_category_access($member_id, 'forums', strval($forum_id))) {
            if (
                ($comments_forum === null) || ($forum_id != $GLOBALS['FORUM_DRIVER']->forum_id_from_name($comments_forum)) &&
                ($ticket_forum === null) || ($forum_id != $GLOBALS['FORUM_DRIVER']->forum_id_from_name($ticket_forum))
            ) {
                return false;
            }
        }
    }

    return true;
}

/**
 * Find whether a member may delete the detailed post.
 *
 * @param  AUTO_LINK $post_id The post ID
 * @param  ?TIME $post_time The time of the post (null: lookup)
 * @param  ?MEMBER $resource_owner The owner of the post (null: lookup)
 * @param  ?AUTO_LINK $forum_id The forum the post is in (null: is a Private Topic, unless $post_time is null in which case we look this up too)
 * @param  ?MEMBER $member_id The member (null: current member)
 * @param  ?boolean $topic_is_closed Whether the topic the post is in is closed (null: don't consider this, maybe we're not considering any one specific case, unless $post_time is null in which case we look this up to)
 * @param  ?string $reason The reason for the topic being closed is put here (null: no interesting reason)
 * @return boolean The answer
 */
function cns_may_delete_post_by(int $post_id, ?int $post_time, ?int $resource_owner, ?int $forum_id, ?int $member_id = null, ?bool $topic_is_closed = null, ?string &$reason = null) : bool
{
    if (!addon_installed('cns_forum')) {
        return false;
    }

    if ($member_id === null) {
        $member_id = get_member();
    }

    $reason = null;

    if ($post_time === null) {
        $posts = $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', ['p_time', 'p_posting_member', 'p_cache_forum_id', 't_is_open'], ['p.id' => $post_id], '', 1);
        if (!array_key_exists(0, $posts)) {
            $reason = do_lang('INTERNAL_ERROR');
            return false;
        }
        $post_time = $posts[0]['p_time'];
        $resource_owner = $posts[0]['p_posting_member'];
        $forum_id = $posts[0]['p_cache_forum_id'];
        $topic_is_closed = ($posts[0]['t_is_open'] == 0);
    }

    if ($forum_id !== null || !has_privilege($member_id, 'moderate_private_topic')) {
        if (!has_delete_permission('low', $member_id, $resource_owner, 'topics', ['forums', $forum_id])) {
            return false;
        }
    }

    if ($forum_id === null) {
        if (!has_privilege($member_id, 'moderate_private_topic')) {
            if (($resource_owner != $member_id) || (!has_privilege($member_id, 'delete_private_topic_posts'))) {
                return false;
            }
        }
    } else {
        $comments_forum = get_option('comments_forum_name', true);
        $ticket_forum = get_option('ticket_forum_name', true);
        if (!has_category_access($member_id, 'forums', strval($forum_id))) {
            if (
                ($comments_forum === null) || ($forum_id != $GLOBALS['FORUM_DRIVER']->forum_id_from_name($comments_forum)) &&
                ($ticket_forum === null) || ($forum_id != $GLOBALS['FORUM_DRIVER']->forum_id_from_name($ticket_forum))
            ) {
                return false;
            }
        }
    }

    if (((time() - $post_time) > intval(get_option('delete_time_limit')) * 60) && (!has_privilege($member_id, 'exceed_post_delete_time_limit'))) {
        require_lang('cns');
        $reason = do_lang('EXCEEDED_TIME_LIMIT', escape_html(display_time_period(intval(get_option('delete_time_limit')) * 60)));
        return false;
    }

    return true;
}

/**
 * Try and make a spacer post look nicer on Conversr than it automatically would.
 *
 * @param  ID_TEXT $linked_type Content type
 * @param  ID_TEXT $linked_id Content ID
 * @return array A pair: better description (may be null), better post (may be null)
 */
function cns_display_spacer_post(string $linked_type, string $linked_id) : array
{
    $new_description = null;
    $new_post = null;

    require_code('content');
    $linked_type = convert_cms_type_codes('feedback_type_code', $linked_type, 'content_type');
    if ($linked_type != '') {
        require_code('content');
        $cma_ob = get_content_object($linked_type);
        if ($cma_ob !== null) {
            $cma_info = $cma_ob->info();
            if ($cma_info !== null) {
                $linked_rows = $GLOBALS['SITE_DB']->query_select($cma_info['table'], ['*'], get_content_where_for_str_id($linked_id, $cma_info), '', 1);
                if (array_key_exists(0, $linked_rows)) {
                    $new_post = $cma_ob->render_box($linked_rows[0], '_SEARCH', true, true);
                }
                $new_description = do_lang('THIS_IS_COMMENT_TOPIC', get_site_name());
            }
        }
    }

    return [$new_description, $new_post];
}
