1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 
<?php

/**
 * This file contains all the administration settings for topics and posts.
 *
 * Simple Machines Forum (SMF)
 *
 * @package SMF
 * @author Simple Machines http://www.simplemachines.org
 * @copyright 2019 Simple Machines and individual contributors
 * @license http://www.simplemachines.org/about/smf/license.php BSD
 *
 * @version 2.1 RC1
 */

if (!defined('SMF'))
    die('No direct access...');

/**
 * The main entrance point for the 'Posts and topics' screen.
 * Like all others, it checks permissions, then forwards to the right function
 * based on the given sub-action.
 * Defaults to sub-action 'posts'.
 * Accessed from ?action=admin;area=postsettings.
 * Requires (and checks for) the admin_forum permission.
 */
function ManagePostSettings()
{
    global $context, $txt;

    // Make sure you can be here.
    isAllowedTo('admin_forum');
    loadLanguage('Drafts');

    $subActions = array(
        'posts' => 'ModifyPostSettings',
        'censor' => 'SetCensor',
        'topics' => 'ModifyTopicSettings',
        'drafts' => 'ModifyDraftSettings',
    );

    // Default the sub-action to 'posts'.
    $_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : 'posts';

    $context['page_title'] = $txt['manageposts_title'];

    // Tabs for browsing the different post functions.
    $context[$context['admin_menu_name']]['tab_data'] = array(
        'title' => $txt['manageposts_title'],
        'help' => 'posts_and_topics',
        'description' => $txt['manageposts_description'],
        'tabs' => array(
            'posts' => array(
                'description' => $txt['manageposts_settings_description'],
            ),
            'censor' => array(
                'description' => $txt['admin_censored_desc'],
            ),
            'topics' => array(
                'description' => $txt['manageposts_topic_settings_description'],
            ),
            'drafts' => array(
                'description' => $txt['managedrafts_settings_description'],
            ),
        ),
    );

    call_integration_hook('integrate_manage_posts', array(&$subActions));

    // Call the right function for this sub-action.
    call_helper($subActions[$_REQUEST['sa']]);
}

/**
 * Shows an interface to set and test censored words.
 * It uses the censor_vulgar, censor_proper, censorWholeWord, and censorIgnoreCase
 * settings.
 * Requires the admin_forum permission.
 * Accessed from ?action=admin;area=postsettings;sa=censor.
 *
 * uses the Admin template and the edit_censored sub template.
 */
function SetCensor()
{
    global $txt, $modSettings, $context, $smcFunc, $sourcedir;

    if (!empty($_POST['save_censor']))
    {
        // Make sure censoring is something they can do.
        checkSession();
        validateToken('admin-censor');

        $censored_vulgar = array();
        $censored_proper = array();

        // Rip it apart, then split it into two arrays.
        if (isset($_POST['censortext']))
        {
            $_POST['censortext'] = explode("\n", strtr($_POST['censortext'], array("\r" => '')));

            foreach ($_POST['censortext'] as $c)
                list ($censored_vulgar[], $censored_proper[]) = array_pad(explode('=', trim($c)), 2, '');
        }
        elseif (isset($_POST['censor_vulgar'], $_POST['censor_proper']))
        {
            if (is_array($_POST['censor_vulgar']))
            {
                foreach ($_POST['censor_vulgar'] as $i => $value)
                {
                    if (trim(strtr($value, '*', ' ')) == '')
                        unset($_POST['censor_vulgar'][$i], $_POST['censor_proper'][$i]);
                }

                $censored_vulgar = $_POST['censor_vulgar'];
                $censored_proper = $_POST['censor_proper'];
            }
            else
            {
                $censored_vulgar = explode("\n", strtr($_POST['censor_vulgar'], array("\r" => '')));
                $censored_proper = explode("\n", strtr($_POST['censor_proper'], array("\r" => '')));
            }
        }

        // Set the new arrays and settings in the database.
        $updates = array(
            'censor_vulgar' => implode("\n", $censored_vulgar),
            'censor_proper' => implode("\n", $censored_proper),
            'allow_no_censored' => empty($_POST['allow_no_censored']) ? '0' : '1',
            'censorWholeWord' => empty($_POST['censorWholeWord']) ? '0' : '1',
            'censorIgnoreCase' => empty($_POST['censorIgnoreCase']) ? '0' : '1',
        );

        call_integration_hook('integrate_save_censors', array(&$updates));

        $context['saved_successful'] = true;
        updateSettings($updates);
    }

    if (isset($_POST['censortest']))
    {
        require_once($sourcedir . '/Subs-Post.php');
        $censorText = $smcFunc['htmlspecialchars']($_POST['censortest'], ENT_QUOTES);
        preparsecode($censorText);
        $context['censor_test'] = strtr(censorText($censorText), array('"' => '&quot;'));
    }

    // Set everything up for the template to do its thang.
    $censor_vulgar = explode("\n", $modSettings['censor_vulgar']);
    $censor_proper = explode("\n", $modSettings['censor_proper']);

    $context['censored_words'] = array();
    for ($i = 0, $n = count($censor_vulgar); $i < $n; $i++)
    {
        if (empty($censor_vulgar[$i]))
            continue;

        // Skip it, it's either spaces or stars only.
        if (trim(strtr($censor_vulgar[$i], '*', ' ')) == '')
            continue;

        $context['censored_words'][$smcFunc['htmlspecialchars'](trim($censor_vulgar[$i]))] = isset($censor_proper[$i]) ? $smcFunc['htmlspecialchars']($censor_proper[$i]) : '';
    }

    call_integration_hook('integrate_censors');

    // Since the "Allow users to disable the word censor" stuff was moved from a theme setting to a global one, we need this...
    loadLanguage('Themes');

    $context['sub_template'] = 'edit_censored';
    $context['page_title'] = $txt['admin_censored_words'];

    createToken('admin-censor');
}

/**
 * Modify any setting related to posts and posting.
 * Requires the admin_forum permission.
 * Accessed from ?action=admin;area=postsettings;sa=posts.
 *
 * @param bool $return_config Whether or not to return the $config_vars array (used for admin search)
 * @return void|array Returns nothing or returns the config_vars array if $return_config is true
 * uses Admin template, edit_post_settings sub-template.
 */
function ModifyPostSettings($return_config = false)
{
    global $context, $txt, $modSettings, $scripturl, $sourcedir, $smcFunc, $db_type;

    // Make an inline conditional a little shorter...
    $can_spell_check = false;
    if (function_exists('pspell_new'))
        $can_spell_check = true;
    elseif (function_exists('enchant_broker_init') && ($txt['lang_character_set'] == 'UTF-8' || function_exists('iconv')))
        $can_spell_check = true;

    // All the settings...
    $config_vars = array(
        // Simple post options...
        array('check', 'removeNestedQuotes'),
        array('check', 'enableSpellChecking', 'disabled' => !$can_spell_check),
        array('check', 'disable_wysiwyg'),
        array('check', 'additional_options_collapsable'),
        array('check', 'guest_post_no_email'),
        '',

        // Posting limits...
        array('int', 'max_messageLength', 'subtext' => $txt['max_messageLength_zero'], 'postinput' => $txt['manageposts_characters']),
        array('int', 'topicSummaryPosts', 'postinput' => $txt['manageposts_posts']),
        '',

        // Posting time limits...
        array('int', 'spamWaitTime', 'postinput' => $txt['manageposts_seconds']),
        array('int', 'edit_wait_time', 'postinput' => $txt['manageposts_seconds']),
        array('int', 'edit_disable_time', 'subtext' => $txt['zero_to_disable'], 'postinput' => $txt['manageposts_minutes']),
        '',

        // Automagic image resizing.
        array('int', 'max_image_width', 'subtext' => $txt['zero_for_no_limit']),
        array('int', 'max_image_height', 'subtext' => $txt['zero_for_no_limit']),
        '',

        // First & Last message preview lengths
        array('int', 'preview_characters', 'subtext' => $txt['zero_to_disable'], 'postinput' => $txt['preview_characters_units']),
    );

    call_integration_hook('integrate_modify_post_settings', array(&$config_vars));

    if ($return_config)
        return $config_vars;

    // We'll want this for our easy save.
    require_once($sourcedir . '/ManageServer.php');

    // Setup the template.
    $context['page_title'] = $txt['manageposts_settings'];
    $context['sub_template'] = 'show_settings';

    // Are we saving them - are we??
    if (isset($_GET['save']))
    {
        checkSession();

        // If we're changing the message length (and we are using MySQL) let's check the column is big enough.
        if (isset($_POST['max_messageLength']) && $_POST['max_messageLength'] != $modSettings['max_messageLength'] && ($db_type == 'mysql'))
        {
            db_extend('packages');

            $colData = $smcFunc['db_list_columns']('{db_prefix}messages', true);
            foreach ($colData as $column)
                if ($column['name'] == 'body')
                    $body_type = $column['type'];

            if (isset($body_type) && ($_POST['max_messageLength'] > 65535 || $_POST['max_messageLength'] == 0) && $body_type == 'text')
                fatal_lang_error('convert_to_mediumtext', false, array($scripturl . '?action=admin;area=maintain;sa=database'));
        }

        // If we're changing the post preview length let's check its valid
        if (!empty($_POST['preview_characters']))
            $_POST['preview_characters'] = (int) min(max(0, $_POST['preview_characters']), 512);

        call_integration_hook('integrate_save_post_settings');

        saveDBSettings($config_vars);
        $_SESSION['adm-save'] = true;
        redirectexit('action=admin;area=postsettings;sa=posts');
    }

    // Final settings...
    $context['post_url'] = $scripturl . '?action=admin;area=postsettings;save;sa=posts';
    $context['settings_title'] = $txt['manageposts_settings'];

    // Prepare the settings...
    prepareDBSettingContext($config_vars);
}

/**
 * Modify any setting related to topics.
 * Requires the admin_forum permission.
 * Accessed from ?action=admin;area=postsettings;sa=topics.
 *
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
 * @return void|array Returns nothing or returns $config_vars if $return_config is true
 * uses Admin template, edit_topic_settings sub-template.
 */
function ModifyTopicSettings($return_config = false)
{
    global $context, $txt, $sourcedir, $scripturl;

    // Here are all the topic settings.
    $config_vars = array(
        // Some simple bools...
        array('check', 'enableParticipation'),
        '',

        // Pagination etc...
        array('int', 'oldTopicDays', 'postinput' => $txt['manageposts_days'], 'subtext' => $txt['zero_to_disable']),
        array('int', 'defaultMaxTopics', 'postinput' => $txt['manageposts_topics']),
        array('int', 'defaultMaxMessages', 'postinput' => $txt['manageposts_posts']),
        array('check', 'disable_print_topic'),
        '',

        // All, next/prev...
        array('int', 'enableAllMessages', 'postinput' => $txt['manageposts_posts'], 'subtext' => $txt['enableAllMessages_zero']),
        array('check', 'disableCustomPerPage'),
        array('check', 'enablePreviousNext'),
        '',

        // Topic related settings (show gender icon/avatars etc...)
        array('check', 'subject_toggle'),
        array('check', 'show_modify'),
        array('check', 'show_profile_buttons'),
        array('check', 'show_user_images'),
        array('check', 'show_blurb'),
        array('check', 'hide_post_group', 'subtext' => $txt['hide_post_group_desc']),
        '',

        // First & Last message preview lengths
        array('int', 'preview_characters', 'subtext' => $txt['zero_to_disable'], 'postinput' => $txt['preview_characters_units']),
        array('check', 'message_index_preview_first', 'subtext' => $txt['message_index_preview_first_desc']),
    );

    call_integration_hook('integrate_modify_topic_settings', array(&$config_vars));

    if ($return_config)
        return $config_vars;

    // Get the settings template ready.
    require_once($sourcedir . '/ManageServer.php');

    // Setup the template.
    $context['page_title'] = $txt['manageposts_topic_settings'];
    $context['sub_template'] = 'show_settings';

    // Are we saving them - are we??
    if (isset($_GET['save']))
    {
        checkSession();
        call_integration_hook('integrate_save_topic_settings');

        saveDBSettings($config_vars);
        $_SESSION['adm-save'] = true;
        redirectexit('action=admin;area=postsettings;sa=topics');
    }

    // Final settings...
    $context['post_url'] = $scripturl . '?action=admin;area=postsettings;save;sa=topics';
    $context['settings_title'] = $txt['manageposts_topic_settings'];

    // Prepare the settings...
    prepareDBSettingContext($config_vars);
}

/**
 * Modify any setting related to drafts.
 * Requires the admin_forum permission.
 * Accessed from ?action=admin;area=postsettings;sa=drafts
 *
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
 * uses Admin template, edit_topic_settings sub-template.
 */
function ModifyDraftSettings($return_config = false)
{
    global $context, $txt, $sourcedir, $scripturl, $smcFunc;

    // Here are all the draft settings, a bit lite for now, but we can add more :P
    $config_vars = array(
        // Draft settings ...
        array('check', 'drafts_post_enabled'),
        array('check', 'drafts_pm_enabled'),
        array('check', 'drafts_show_saved_enabled', 'subtext' => $txt['drafts_show_saved_enabled_subnote']),
        array('int', 'drafts_keep_days', 'postinput' => $txt['days_word'], 'subtext' => $txt['drafts_keep_days_subnote']),
        '',
        array('check', 'drafts_autosave_enabled', 'subtext' => $txt['drafts_autosave_enabled_subnote']),
        array('int', 'drafts_autosave_frequency', 'postinput' => $txt['manageposts_seconds'], 'subtext' => $txt['drafts_autosave_frequency_subnote']),
    );

    if ($return_config)
        return $config_vars;

    // Get the settings template ready.
    require_once($sourcedir . '/ManageServer.php');

    // Setup the template.
    $context['page_title'] = $txt['managedrafts_settings'];
    $context['sub_template'] = 'show_settings';

    // Saving them ?
    if (isset($_GET['save']))
    {
        checkSession();

        // Protect them from themselves.
        $_POST['drafts_autosave_frequency'] = !isset($_POST['drafts_autosave_frequency']) || $_POST['drafts_autosave_frequency'] < 30 ? 30 : $_POST['drafts_autosave_frequency'];

        // Also disable the scheduled task if we're not using it.
        $smcFunc['db_query']('', '
            UPDATE {db_prefix}scheduled_tasks
            SET disabled = {int:disabled}
            WHERE task = {string:task}',
            array(
                'disabled' => !empty($_POST['drafts_keep_days']) ? 0 : 1,
                'task' => 'remove_old_drafts',
            )
        );
        require_once($sourcedir . '/ScheduledTasks.php');
        CalculateNextTrigger();

        // Save everything else and leave.
        saveDBSettings($config_vars);
        $_SESSION['adm-save'] = true;
        redirectexit('action=admin;area=postsettings;sa=drafts');
    }

    // some javascript to enable / disable the frequency input box
    $context['settings_post_javascript'] = '
        function toggle()
        {
            $("#drafts_autosave_frequency").prop("disabled", !($("#drafts_autosave_enabled").prop("checked")));
        };
        toggle();

        $("#drafts_autosave_enabled").click(function() { toggle(); });
    ';

    // Final settings...
    $context['post_url'] = $scripturl . '?action=admin;area=postsettings;sa=drafts;save';
    $context['settings_title'] = $txt['managedrafts_settings'];

    // Prepare the settings...
    prepareDBSettingContext($config_vars);
}

?>