protected function cleanMessage() { $this->message = Util::htmlspecialchars($this->message, ENT_QUOTES, 'UTF-8', true); $this->message = strtr($this->message, array("\r" => '', '[]' => '[]', '['' => '['')); // Clean up any cut/paste issues we may have $this->message = sanitizeMSCutPaste($this->message); }
/** * Common actions for all methods in the class */ public function pre_dispatch() { global $context; $context['page_title'] = $context['forum_name']; if (isset($context['page_title_html_safe'])) { $context['page_title_html_safe'] = Util::htmlspecialchars(un_htmlspecialchars($context['page_title'])); } if (!empty($context['standalone'])) { setupMenuContext(); } }
/** * Show boxes with more detailed help on items, when the user clicks on their help icon. * It handles both administrative or user help. * Data: $_GET['help'] parameter, it holds what string to display * and where to get the string from. ($helptxt or $txt) * It is accessed via ?action=quickhelp;help=?. * * @uses ManagePermissions language file, if the help starts with permissionhelp. * @uses Help template, 'popup' sub-template. */ public function action_quickhelp() { global $txt, $helptxt, $context, $scripturl; if (!isset($_GET['help']) || !is_string($_GET['help'])) { fatal_lang_error('no_access', false); } if (!isset($helptxt)) { $helptxt = array(); } $help_str = Util::htmlspecialchars($_GET['help']); // Load the admin help language file and template. loadLanguage('Help'); // Load permission specific help if (substr($help_str, 0, 14) == 'permissionhelp') { loadLanguage('ManagePermissions'); } // Load our template loadTemplate('Help'); // Allow addons to load their own language file here. call_integration_hook('integrate_quickhelp'); // Set the page title to something relevant. $context['page_title'] = $context['forum_name'] . ' - ' . $txt['help']; // Only show the 'popup' sub-template, no layers. Template_Layers::getInstance()->removeAll(); $context['sub_template'] = 'popup'; $helps = explode('+', $help_str); $context['help_text'] = ''; // Find what to display: the string will be in $helptxt['help'] or in $txt['help] foreach ($helps as $help) { if (isset($helptxt[$help])) { $context['help_text'] .= $helptxt[$help]; } elseif (isset($txt[$help])) { $context['help_text'] .= $txt[$help]; } else { // nothing :( $context['help_text'] .= $help; } } // Link to the forum URL, and include session id. if (preg_match('~%([0-9]+\\$)?s\\?~', $context['help_text'], $match)) { $context['help_text'] = sprintf($context['help_text'], $scripturl, $context['session_id'], $context['session_var']); } }
/** * Images cache * * @name Images cache * @copyright Images cache contributors * @license BSD http://opensource.org/licenses/BSD-3-Clause * * @version 0.1 * */ function imageNeedsCache($img) { global $boardurl, $txt; static $js_loaded = false; $parseboard = parse_url($boardurl); $parseimg = parse_url($img); if (!($parseboard['scheme'] === 'https') || $parseboard['scheme'] === $parseimg['scheme']) { return false; } if ($js_loaded === false) { $js_loaded = true; loadJavascriptFile('imgcache.js', array('defer' => true)); loadLanguage('imgcache'); } require_once SUBSDIR . '/Graphics.subs.php'; $destination = CACHEDIR . '/img_cache_' . md5($img); if (!file_exists($destination)) { resizeImageFile($img, $destination, 200, 200, 3); } return $boardurl . '/imgcache.php?id=' . md5($img) . '" rel="cached" data-warn="' . Util::htmlspecialchars($txt['httpimgcache_warn_ext']) . '" data-url="' . Util::htmlspecialchars($img); }
/** * View a specific category, showing all articles it contains */ public function action_sportal_category() { global $context, $scripturl, $modSettings; // Basic article support require_once SUBSDIR . '/PortalArticle.subs.php'; $category_id = !empty($_REQUEST['category']) ? $_REQUEST['category'] : 0; if (is_int($category_id)) { $category_id = (int) $category_id; } else { $category_id = Util::htmlspecialchars($category_id, ENT_QUOTES); } $context['category'] = sportal_get_categories($category_id, true, true); if (empty($context['category']['id'])) { fatal_lang_error('error_sp_category_not_found', false); } // Set up the pages $total_articles = sportal_get_articles_in_cat_count($context['category']['id']); $per_page = min($total_articles, !empty($modSettings['sp_articles_per_page']) ? $modSettings['sp_articles_per_page'] : 10); $start = !empty($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0; if ($total_articles > $per_page) { $context['page_index'] = constructPageIndex($context['category']['href'] . ';start=%1$d', $start, $total_articles, $per_page, true); } // Load the articles in this category $context['articles'] = sportal_get_articles(0, true, true, 'spa.id_article DESC', $context['category']['id'], $per_page, $start); foreach ($context['articles'] as $article) { // Cut me mick if (($cutoff = Util::strpos($article['body'], '[cutoff]')) !== false) { $article['body'] = Util::substr($article['body'], 0, $cutoff); if ($article['type'] === 'bbc') { require_once SUBSDIR . '/Post.subs.php'; preparsecode($article['body']); } } $context['articles'][$article['id']]['preview'] = sportal_parse_content($article['body'], $article['type'], 'return'); $context['articles'][$article['id']]['date'] = htmlTime($article['date']); } $context['linktree'][] = array('url' => $scripturl . '?category=' . $context['category']['category_id'], 'name' => $context['category']['name']); $context['page_title'] = $context['category']['name']; $context['sub_template'] = 'view_category'; }
/** * Actually register the member. * @todo split this function in two functions: * - a function that handles action=register2, which needs no parameter; * - a function that processes the case of OpenID verification. * * @param bool $verifiedOpenID = false */ public function action_register2($verifiedOpenID = false) { global $txt, $modSettings, $context, $user_info; // Start collecting together any errors. $reg_errors = Error_Context::context('register', 0); // We can't validate the token and the session with OpenID enabled. if (!$verifiedOpenID) { checkSession(); if (!validateToken('register', 'post', true, false)) { $reg_errors->addError('token_verification'); } } // Did we save some open ID fields? if ($verifiedOpenID && !empty($context['openid_save_fields'])) { foreach ($context['openid_save_fields'] as $id => $value) { $_POST[$id] = $value; } } // You can't register if it's disabled. if (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 3) { fatal_lang_error('registration_disabled', false); } // If we're using an agreement checkbox, did they check it? if (!empty($modSettings['checkboxAgreement']) && !empty($_POST['checkbox_agreement'])) { $_SESSION['registration_agreed'] = true; } // Things we don't do for people who have already confirmed their OpenID allegances via register. if (!$verifiedOpenID) { // Well, if you don't agree, you can't register. if (!empty($modSettings['requireAgreement']) && empty($_SESSION['registration_agreed'])) { redirectexit(); } // Make sure they came from *somewhere*, have a session. if (!isset($_SESSION['old_url'])) { redirectexit('action=register'); } // If we don't require an agreement, we need a extra check for coppa. if (empty($modSettings['requireAgreement']) && !empty($modSettings['coppaAge'])) { $_SESSION['skip_coppa'] = !empty($_POST['accept_agreement']); } // Are they under age, and under age users are banned? if (!empty($modSettings['coppaAge']) && empty($modSettings['coppaType']) && empty($_SESSION['skip_coppa'])) { loadLanguage('Login'); fatal_lang_error('under_age_registration_prohibited', false, array($modSettings['coppaAge'])); } // Check the time gate for miscreants. First make sure they came from somewhere that actually set it up. if (empty($_SESSION['register']['timenow']) || empty($_SESSION['register']['limit'])) { redirectexit('action=register'); } // Failing that, check the time limit for exessive speed. if (time() - $_SESSION['register']['timenow'] < $_SESSION['register']['limit']) { loadLanguage('Login'); $reg_errors->addError('too_quickly'); } // Check whether the visual verification code was entered correctly. if (!empty($modSettings['reg_verification'])) { require_once SUBSDIR . '/VerificationControls.class.php'; $verificationOptions = array('id' => 'register'); $context['visual_verification'] = create_control_verification($verificationOptions, true); if (is_array($context['visual_verification'])) { foreach ($context['visual_verification'] as $error) { $reg_errors->addError($error); } } } } foreach ($_POST as $key => $value) { if (!is_array($_POST[$key])) { $_POST[$key] = htmltrim__recursive(str_replace(array("\n", "\r"), '', $_POST[$key])); } } // Collect all extra registration fields someone might have filled in. $possible_strings = array('birthdate', 'time_format', 'buddy_list', 'pm_ignore_list', 'smiley_set', 'personal_text', 'avatar', 'lngfile', 'location', 'secret_question', 'secret_answer', 'website_url', 'website_title'); $possible_ints = array('pm_email_notify', 'notify_types', 'id_theme', 'gender'); $possible_floats = array('time_offset'); $possible_bools = array('notify_announcements', 'notify_regularity', 'notify_send_body', 'hide_email', 'show_online'); if (isset($_POST['secret_answer']) && $_POST['secret_answer'] != '') { $_POST['secret_answer'] = md5($_POST['secret_answer']); } // Needed for isReservedName() and registerMember(). require_once SUBSDIR . '/Members.subs.php'; // Validation... even if we're not a mall. if (isset($_POST['real_name']) && (!empty($modSettings['allow_editDisplayName']) || allowedTo('moderate_forum'))) { $_POST['real_name'] = trim(preg_replace('~[\\t\\n\\r \\x0B\\0\\x{A0}\\x{AD}\\x{2000}-\\x{200F}\\x{201F}\\x{202F}\\x{3000}\\x{FEFF}]+~u', ' ', $_POST['real_name'])); if (trim($_POST['real_name']) != '' && !isReservedName($_POST['real_name']) && Util::strlen($_POST['real_name']) < 60) { $possible_strings[] = 'real_name'; } } // Handle a string as a birthdate... if (isset($_POST['birthdate']) && $_POST['birthdate'] != '') { $_POST['birthdate'] = strftime('%Y-%m-%d', strtotime($_POST['birthdate'])); } elseif (!empty($_POST['bday1']) && !empty($_POST['bday2'])) { $_POST['birthdate'] = sprintf('%04d-%02d-%02d', empty($_POST['bday3']) ? 0 : (int) $_POST['bday3'], (int) $_POST['bday1'], (int) $_POST['bday2']); } // By default assume email is hidden, only show it if we tell it to. $_POST['hide_email'] = !empty($_POST['allow_email']) ? 0 : 1; // Validate the passed language file. if (isset($_POST['lngfile']) && !empty($modSettings['userLanguage'])) { // Do we have any languages? $context['languages'] = getLanguages(); // Did we find it? if (isset($context['languages'][$_POST['lngfile']])) { $_SESSION['language'] = $_POST['lngfile']; } else { unset($_POST['lngfile']); } } else { unset($_POST['lngfile']); } // Some of these fields we may not want. if (!empty($modSettings['registration_fields'])) { // But we might want some of them if the admin asks for them. $standard_fields = array('location', 'gender'); $reg_fields = explode(',', $modSettings['registration_fields']); $exclude_fields = array_diff($standard_fields, $reg_fields); // Website is a little different if (!in_array('website', $reg_fields)) { $exclude_fields = array_merge($exclude_fields, array('website_url', 'website_title')); } // We used to accept signature on registration but it's being abused by spammers these days, so no more. $exclude_fields[] = 'signature'; } else { $exclude_fields = array('signature', 'location', 'gender', 'website_url', 'website_title'); } $possible_strings = array_diff($possible_strings, $exclude_fields); $possible_ints = array_diff($possible_ints, $exclude_fields); $possible_floats = array_diff($possible_floats, $exclude_fields); $possible_bools = array_diff($possible_bools, $exclude_fields); // Set the options needed for registration. $regOptions = array('interface' => 'guest', 'username' => !empty($_POST['user']) ? $_POST['user'] : '', 'email' => !empty($_POST['email']) ? $_POST['email'] : '', 'password' => !empty($_POST['passwrd1']) ? $_POST['passwrd1'] : '', 'password_check' => !empty($_POST['passwrd2']) ? $_POST['passwrd2'] : '', 'openid' => !empty($_POST['openid_identifier']) ? $_POST['openid_identifier'] : '', 'auth_method' => !empty($_POST['authenticate']) ? $_POST['authenticate'] : '', 'check_reserved_name' => true, 'check_password_strength' => true, 'check_email_ban' => true, 'send_welcome_email' => !empty($modSettings['send_welcomeEmail']), 'require' => !empty($modSettings['coppaAge']) && !$verifiedOpenID && empty($_SESSION['skip_coppa']) ? 'coppa' : (empty($modSettings['registration_method']) ? 'nothing' : ($modSettings['registration_method'] == 1 ? 'activation' : 'approval')), 'extra_register_vars' => array(), 'theme_vars' => array()); // Include the additional options that might have been filled in. foreach ($possible_strings as $var) { if (isset($_POST[$var])) { $regOptions['extra_register_vars'][$var] = Util::htmlspecialchars($_POST[$var], ENT_QUOTES); } } foreach ($possible_ints as $var) { if (isset($_POST[$var])) { $regOptions['extra_register_vars'][$var] = (int) $_POST[$var]; } } foreach ($possible_floats as $var) { if (isset($_POST[$var])) { $regOptions['extra_register_vars'][$var] = (double) $_POST[$var]; } } foreach ($possible_bools as $var) { if (isset($_POST[$var])) { $regOptions['extra_register_vars'][$var] = empty($_POST[$var]) ? 0 : 1; } } // Registration options are always default options... if (isset($_POST['default_options'])) { $_POST['options'] = isset($_POST['options']) ? $_POST['options'] + $_POST['default_options'] : $_POST['default_options']; } $regOptions['theme_vars'] = isset($_POST['options']) && is_array($_POST['options']) ? $_POST['options'] : array(); // Make sure they are clean, dammit! $regOptions['theme_vars'] = htmlspecialchars__recursive($regOptions['theme_vars']); // Check whether we have fields that simply MUST be displayed? require_once SUBSDIR . '/Profile.subs.php'; loadCustomFields(0, 'register'); foreach ($context['custom_fields'] as $row) { // Don't allow overriding of the theme variables. if (isset($regOptions['theme_vars'][$row['colname']])) { unset($regOptions['theme_vars'][$row['colname']]); } // Prepare the value! $value = isset($_POST['customfield'][$row['colname']]) ? trim($_POST['customfield'][$row['colname']]) : ''; // We only care for text fields as the others are valid to be empty. if (!in_array($row['type'], array('check', 'select', 'radio'))) { // Is it too long? if ($row['field_length'] && $row['field_length'] < Util::strlen($value)) { $reg_errors->addError(array('custom_field_too_long', array($row['name'], $row['field_length']))); } // Any masks to apply? if ($row['type'] == 'text' && !empty($row['mask']) && $row['mask'] != 'none') { // @todo We never error on this - just ignore it at the moment... if ($row['mask'] == 'email' && !isValidEmail($value)) { $reg_errors->addError(array('custom_field_invalid_email', array($row['name']))); } elseif ($row['mask'] == 'number' && preg_match('~[^\\d]~', $value)) { $reg_errors->addError(array('custom_field_not_number', array($row['name']))); } elseif (substr($row['mask'], 0, 5) == 'regex' && trim($value) !== '' && preg_match(substr($row['mask'], 5), $value) === 0) { $reg_errors->addError(array('custom_field_inproper_format', array($row['name']))); } } } // Is this required but not there? if (trim($value) == '' && $row['show_reg'] > 1) { $reg_errors->addError(array('custom_field_empty', array($row['name']))); } } // Lets check for other errors before trying to register the member. if ($reg_errors->hasErrors()) { $_REQUEST['step'] = 2; // If they've filled in some details but made an error then they need less time to finish $_SESSION['register']['limit'] = 4; return $this->action_register(); } // If they're wanting to use OpenID we need to validate them first. if (empty($_SESSION['openid']['verified']) && !empty($_POST['authenticate']) && $_POST['authenticate'] == 'openid') { // What do we need to save? $save_variables = array(); foreach ($_POST as $k => $v) { if (!in_array($k, array('sc', 'sesc', $context['session_var'], 'passwrd1', 'passwrd2', 'regSubmit'))) { $save_variables[$k] = $v; } } require_once SUBSDIR . '/OpenID.subs.php'; $openID = new OpenID(); $openID->validate($_POST['openid_identifier'], false, $save_variables); } elseif ($verifiedOpenID || (!empty($_POST['openid_identifier']) || !empty($_SESSION['openid']['openid_uri'])) && $_POST['authenticate'] == 'openid') { $regOptions['username'] = !empty($_POST['user']) && trim($_POST['user']) != '' ? $_POST['user'] : $_SESSION['openid']['nickname']; $regOptions['email'] = !empty($_POST['email']) && trim($_POST['email']) != '' ? $_POST['email'] : $_SESSION['openid']['email']; $regOptions['auth_method'] = 'openid'; $regOptions['openid'] = !empty($_SESSION['openid']['openid_uri']) ? $_SESSION['openid']['openid_uri'] : (!empty($_POST['openid_identifier']) ? $_POST['openid_identifier'] : ''); } // Registration needs to know your IP $req = request(); $regOptions['ip'] = $user_info['ip']; $regOptions['ip2'] = $req->ban_ip(); $memberID = registerMember($regOptions, 'register'); // If there are "important" errors and you are not an admin: log the first error // Otherwise grab all of them and don't log anything if ($reg_errors->hasErrors(1) && !$user_info['is_admin']) { foreach ($reg_errors->prepareErrors(1) as $error) { fatal_error($error, 'general'); } } // Was there actually an error of some kind dear boy? if ($reg_errors->hasErrors()) { $_REQUEST['step'] = 2; return $this->action_register(); } // Do our spam protection now. spamProtection('register'); // We'll do custom fields after as then we get to use the helper function! if (!empty($_POST['customfield'])) { require_once SUBSDIR . '/Profile.subs.php'; makeCustomFieldChanges($memberID, 'register'); } // If COPPA has been selected then things get complicated, setup the template. if (!empty($modSettings['coppaAge']) && empty($_SESSION['skip_coppa'])) { redirectexit('action=coppa;member=' . $memberID); } elseif (!empty($modSettings['registration_method'])) { loadTemplate('Register'); $context += array('page_title' => $txt['register'], 'title' => $txt['registration_successful'], 'sub_template' => 'after', 'description' => $modSettings['registration_method'] == 2 ? $txt['approval_after_registration'] : $txt['activate_after_registration']); } else { call_integration_hook('integrate_activate', array($regOptions['username'])); setLoginCookie(60 * $modSettings['cookieTime'], $memberID, hash('sha256', Util::strtolower($regOptions['username']) . $regOptions['password'] . $regOptions['register_vars']['password_salt'])); redirectexit('action=auth;sa=check;member=' . $memberID, $context['server']['needs_login_fix']); } }
/** * Modifies an event. * * - allows to either set a time span (in days) or an end_date. * - does not check any permissions of any sort. * * @package Calendar * @param int $event_id * @param mixed[] $eventOptions */ function modifyEvent($event_id, &$eventOptions) { $db = database(); // Properly sanitize the title. $eventOptions['title'] = Util::htmlspecialchars($eventOptions['title'], ENT_QUOTES); // Scan the start date for validity and get its components. if (($num_results = sscanf($eventOptions['start_date'], '%d-%d-%d', $year, $month, $day)) !== 3) { trigger_error('modifyEvent(): invalid start date format given', E_USER_ERROR); } // Default span to 0 days. $eventOptions['span'] = isset($eventOptions['span']) ? (int) $eventOptions['span'] : 0; // Set the end date to the start date + span (if the end date wasn't already given). if (!isset($eventOptions['end_date'])) { $eventOptions['end_date'] = strftime('%Y-%m-%d', mktime(0, 0, 0, $month, $day, $year) + $eventOptions['span'] * 86400); } $event_columns = array('start_date' => 'start_date = {date:start_date}', 'end_date' => 'end_date = {date:end_date}', 'title' => 'title = SUBSTRING({string:title}, 1, 60)', 'id_board' => 'id_board = {int:id_board}', 'id_topic' => 'id_topic = {int:id_topic}'); call_integration_hook('integrate_modify_event', array($event_id, &$eventOptions, &$event_columns)); $eventOptions['id_event'] = $event_id; $to_update = array(); foreach ($event_columns as $key => $value) { if (isset($eventOptions[$key])) { $to_update[] = $value; } } if (empty($to_update)) { return; } $db->query('', ' UPDATE {db_prefix}calendar SET ' . implode(', ', $to_update) . ' WHERE id_event = {int:id_event}', $eventOptions); updateSettings(array('calendar_updated' => time())); }
/** * This function validates the ban triggers * * @package Bans * @param mixed[] $triggers */ function validateTriggers(&$triggers) { $db = database(); $ban_errors = Error_Context::context('ban', 1); if (empty($triggers)) { $ban_errors->addError('ban_empty_triggers'); } $ban_triggers = array(); $log_info = array(); // Go through each trigger and make sure its valid foreach ($triggers as $key => $value) { if (!empty($value)) { if ($key == 'member') { continue; } if ($key == 'main_ip') { $value = trim($value); $ip_parts = ip2range($value); if (!checkExistingTriggerIP($ip_parts, $value)) { $ban_errors->addError('invalid_ip'); } else { $ban_triggers['main_ip'] = array('ip_low1' => $ip_parts[0]['low'], 'ip_high1' => $ip_parts[0]['high'], 'ip_low2' => $ip_parts[1]['low'], 'ip_high2' => $ip_parts[1]['high'], 'ip_low3' => $ip_parts[2]['low'], 'ip_high3' => $ip_parts[2]['high'], 'ip_low4' => $ip_parts[3]['low'], 'ip_high4' => $ip_parts[3]['high'], 'ip_low5' => $ip_parts[4]['low'], 'ip_high5' => $ip_parts[4]['high'], 'ip_low6' => $ip_parts[5]['low'], 'ip_high6' => $ip_parts[5]['high'], 'ip_low7' => $ip_parts[6]['low'], 'ip_high7' => $ip_parts[6]['high'], 'ip_low8' => $ip_parts[7]['low'], 'ip_high8' => $ip_parts[7]['high']); } } elseif ($key == 'hostname') { if (preg_match('/[^\\w.\\-*]/', $value) == 1) { $ban_errors->addError('invalid_hostname'); } else { // Replace the * wildcard by a MySQL wildcard %. $value = substr(str_replace('*', '%', $value), 0, 255); $ban_triggers['hostname']['hostname'] = $value; } } elseif ($key == 'email') { if (preg_match('/[^\\w.\\-\\+*@]/', $value) == 1) { $ban_errors->addError('invalid_email'); } // Check the user is not banning an admin. $request = $db->query('', ' SELECT id_member FROM {db_prefix}members WHERE (id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0) AND email_address LIKE {string:email} LIMIT 1', array('admin_group' => 1, 'email' => $value)); if ($db->num_rows($request) != 0) { $ban_errors->addError('no_ban_admin'); } $db->free_result($request); $value = substr(strtolower(str_replace('*', '%', $value)), 0, 255); $ban_triggers['email']['email_address'] = $value; } elseif ($key == 'user') { $user = preg_replace('~&#(\\d{4,5}|[2-9]\\d{2,4}|1[2-9]\\d);~', '&#$1;', Util::htmlspecialchars($value, ENT_QUOTES)); $request = $db->query('', ' SELECT id_member, (id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0) AS isAdmin FROM {db_prefix}members WHERE member_name = {string:username} OR real_name = {string:username} LIMIT 1', array('admin_group' => 1, 'username' => $user)); if ($db->num_rows($request) == 0) { $ban_errors->addError('invalid_username'); } list($value, $isAdmin) = $db->fetch_row($request); $db->free_result($request); if ($isAdmin && strtolower($isAdmin) != 'f') { unset($value); $ban_errors->addError('no_ban_admin'); } else { $ban_triggers['user']['id_member'] = $value; } } elseif (in_array($key, array('ips_in_messages', 'ips_in_errors'))) { // Special case, those two are arrays themselves $values = array_unique($value); // Don't add the main IP again. if (isset($triggers['main_ip'])) { $values = array_diff($values, array($triggers['main_ip'])); } unset($value); foreach ($values as $val) { $val = trim($val); $ip_parts = ip2range($val); if (!checkExistingTriggerIP($ip_parts, $val)) { $ban_errors->addError('invalid_ip'); } else { $ban_triggers[$key][] = array('ip_low1' => $ip_parts[0]['low'], 'ip_high1' => $ip_parts[0]['high'], 'ip_low2' => $ip_parts[1]['low'], 'ip_high2' => $ip_parts[1]['high'], 'ip_low3' => $ip_parts[2]['low'], 'ip_high3' => $ip_parts[2]['high'], 'ip_low4' => $ip_parts[3]['low'], 'ip_high4' => $ip_parts[3]['high'], 'ip_low5' => $ip_parts[4]['low'], 'ip_high5' => $ip_parts[4]['high'], 'ip_low6' => $ip_parts[5]['low'], 'ip_high6' => $ip_parts[5]['high'], 'ip_low7' => $ip_parts[6]['low'], 'ip_high7' => $ip_parts[6]['high'], 'ip_low8' => $ip_parts[7]['low'], 'ip_high8' => $ip_parts[7]['high']); $log_info[] = array('value' => $val, 'bantype' => 'ip_range'); } } } else { $ban_errors->addError('no_bantype_selected'); } if (isset($value) && !is_array($value)) { $log_info[] = array('value' => $value, 'bantype' => $key); } } } return array('ban_triggers' => $ban_triggers, 'log_info' => $log_info); }
/** * Gets a members ID from their userid or display name, used to * prune a members shouts from a box * * @param string $member */ function sp_shoutbox_prune_member($member) { $db = database(); $request = $db->query('', ' SELECT id_member FROM {db_prefix}members WHERE member_name = {string:member} OR real_name = {string:member} LIMIT {int:limit}', array('member' => strtr(trim(Util::htmlspecialchars($member, ENT_QUOTES)), array('\'' => ''')), 'limit' => 1)); list($member_id) = $db->fetch_row($request); $db->free_result($request); return (int) $member_id; }
/** * Checks if an the answers to anti-spam questions are correct * * @return boolean */ private function _verifyAnswers() { // Get the answers and see if they are all right! $questions = $this->_loadAntispamQuestions(array('type' => 'id_question', 'value' => $_SESSION[$this->_options['id'] . '_vv']['q'])); $this->_incorrectQuestions = array(); foreach ($questions as $row) { // Everything lowercase $answers = array(); foreach ($row['answer'] as $answer) { $answers[] = Util::strtolower($answer); } if (!isset($_REQUEST[$this->_options['id'] . '_vv']['q'][$row['id_question']]) || trim($_REQUEST[$this->_options['id'] . '_vv']['q'][$row['id_question']]) == '' || !in_array(trim(Util::htmlspecialchars(Util::strtolower($_REQUEST[$this->_options['id'] . '_vv']['q'][$row['id_question']]))), $answers)) { $this->_incorrectQuestions[] = $row['id_question']; } } return empty($this->_incorrectQuestions); }
/** * Do some important security checks: * * What it does: * - checks the existence of critical files e.g. install.php * - checks for an active admin session. * - checks cache directory is writable. * - calls secureDirectory to protect attachments & cache. * - checks if the forum is in maintance mode. */ function doSecurityChecks() { global $modSettings, $context, $maintenance, $user_info, $txt, $scripturl, $user_settings, $options; $show_warnings = false; if (allowedTo('admin_forum') && !$user_info['is_guest']) { // If agreement is enabled, at least the english version shall exists if ($modSettings['requireAgreement'] && !file_exists(BOARDDIR . '/agreement.txt')) { $context['security_controls_files']['title'] = $txt['generic_warning']; $context['security_controls_files']['errors']['agreement'] = $txt['agreement_missing']; $show_warnings = true; } // Cache directory writeable? if (!empty($modSettings['cache_enable']) && !is_writable(CACHEDIR)) { $context['security_controls_files']['title'] = $txt['generic_warning']; $context['security_controls_files']['errors']['cache'] = $txt['cache_writable']; $show_warnings = true; } // @todo add a hook here $securityFiles = array('install.php', 'upgrade.php', 'convert.php', 'repair_paths.php', 'repair_settings.php', 'Settings.php~', 'Settings_bak.php~'); foreach ($securityFiles as $securityFile) { if (file_exists(BOARDDIR . '/' . $securityFile)) { $context['security_controls_files']['title'] = $txt['security_risk']; $context['security_controls_files']['errors'][$securityFile] = sprintf($txt['not_removed'], $securityFile); $show_warnings = true; if ($securityFile == 'Settings.php~' || $securityFile == 'Settings_bak.php~') { $context['security_controls_files']['errors'][$securityFile] .= '<span class="smalltext">' . sprintf($txt['not_removed_extra'], $securityFile, substr($securityFile, 0, -1)) . '</span>'; } } } // We are already checking so many files...just few more doesn't make any difference! :P require_once SUBSDIR . '/Attachments.subs.php'; $path = getAttachmentPath(); secureDirectory($path, true); secureDirectory(CACHEDIR); // Active admin session? if (empty($modSettings['securityDisable']) && (isset($_SESSION['admin_time']) && $_SESSION['admin_time'] + $modSettings['admin_session_lifetime'] * 60 > time())) { $context['warning_controls']['admin_session'] = sprintf($txt['admin_session_active'], $scripturl . '?action=admin;area=adminlogoff;redir;' . $context['session_var'] . '=' . $context['session_id']); } // Maintenance mode enabled? if (!empty($maintenance)) { $context['warning_controls']['maintenance'] = sprintf($txt['admin_maintenance_active'], $scripturl . '?action=admin;area=serversettings;' . $context['session_var'] . '=' . $context['session_id']); } // New updates if (defined('FORUM_VERSION')) { $index = 'new_in_' . str_replace(array('ElkArte ', '.'), array('', '_'), FORUM_VERSION); if (!empty($modSettings[$index]) && empty($options['dismissed_' . $index])) { $show_warnings = true; $context['new_version_updates'] = array('title' => $txt['new_version_updates'], 'errors' => array(replaceBasicActionUrl($txt['new_version_updates_text']))); } } } // Check for database errors. if (!empty($_SESSION['query_command_denied'])) { if ($user_info['is_admin']) { $context['security_controls_query']['title'] = $txt['query_command_denied']; $show_warnings = true; foreach ($_SESSION['query_command_denied'] as $command => $error) { $context['security_controls_query']['errors'][$command] = '<pre>' . Util::htmlspecialchars($error) . '</pre>'; } } else { $context['security_controls_query']['title'] = $txt['query_command_denied_guests']; foreach ($_SESSION['query_command_denied'] as $command => $error) { $context['security_controls_query']['errors'][$command] = '<pre>' . sprintf($txt['query_command_denied_guests_msg'], Util::htmlspecialchars($command)) . '</pre>'; } } } // Are there any members waiting for approval? if (allowedTo('moderate_forum') && (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 2 || !empty($modSettings['approveAccountDeletion'])) && !empty($modSettings['unapprovedMembers'])) { $context['warning_controls']['unapproved_members'] = sprintf($txt[$modSettings['unapprovedMembers'] == 1 ? 'approve_one_member_waiting' : 'approve_many_members_waiting'], $scripturl . '?action=admin;area=viewmembers;sa=browse;type=approve', $modSettings['unapprovedMembers']); } if (!empty($context['open_mod_reports']) && (empty($user_settings['mod_prefs']) || $user_settings['mod_prefs'][0] == 1)) { $context['warning_controls']['open_mod_reports'] = '<a href="' . $scripturl . '?action=moderate;area=reports">' . sprintf($txt['mod_reports_waiting'], $context['open_mod_reports']) . '</a>'; } if (isset($_SESSION['ban']['cannot_post'])) { // An admin cannot be banned (technically he could), and if it is better he knows. $context['security_controls_ban']['title'] = sprintf($txt['you_are_post_banned'], $user_info['is_guest'] ? $txt['guest_title'] : $user_info['name']); $show_warnings = true; $context['security_controls_ban']['errors']['reason'] = ''; if (!empty($_SESSION['ban']['cannot_post']['reason'])) { $context['security_controls_ban']['errors']['reason'] = $_SESSION['ban']['cannot_post']['reason']; } if (!empty($_SESSION['ban']['expire_time'])) { $context['security_controls_ban']['errors']['reason'] .= '<span class="smalltext">' . sprintf($txt['your_ban_expires'], standardTime($_SESSION['ban']['expire_time'], false)) . '</span>'; } else { $context['security_controls_ban']['errors']['reason'] .= '<span class="smalltext">' . $txt['your_ban_expires_never'] . '</span>'; } } // Finally, let's show the layer. if ($show_warnings || !empty($context['warning_controls'])) { Template_Layers::getInstance()->addAfter('admin_warning', 'body'); } }
/** * Prepares the information from the moderation log for viewing. * Show the moderation log, or admin log... * Disallows the deletion of events within twenty-four hours of now. * Requires the admin_forum permission for admin log. * Accessed via ?action=moderate;area=modlog. * * @uses Modlog template, main sub-template. */ public function action_log() { global $txt, $context, $scripturl; require_once SUBSDIR . '/Modlog.subs.php'; // Are we looking at the moderation log or the administration log. $context['log_type'] = isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'adminlog' ? 3 : 1; if ($context['log_type'] == 3) { isAllowedTo('admin_forum'); } // These change dependant on whether we are viewing the moderation or admin log. if ($context['log_type'] == 3 || $_REQUEST['action'] == 'admin') { $context['url_start'] = '?action=admin;area=logs;sa=' . ($context['log_type'] == 3 ? 'adminlog' : 'modlog') . ';type=' . $context['log_type']; } else { $context['url_start'] = '?action=moderate;area=modlog;type=' . $context['log_type']; } $context['can_delete'] = allowedTo('admin_forum'); loadLanguage('Modlog'); $context['page_title'] = $context['log_type'] == 3 ? $txt['modlog_admin_log'] : $txt['modlog_view']; // The number of entries to show per page of log file. $context['displaypage'] = 30; // Amount of hours that must pass before allowed to delete file. $context['hoursdisable'] = 24; // Handle deletion... if (isset($_POST['removeall']) && $context['can_delete']) { checkSession(); validateToken('mod-ml'); deleteLogAction($context['log_type'], $context['hoursdisable']); } elseif (!empty($_POST['remove']) && isset($_POST['delete']) && $context['can_delete']) { checkSession(); validateToken('mod-ml'); deleteLogAction($context['log_type'], $context['hoursdisable'], $_POST['delete']); } // If we're coming from a search, get the variables. if (!empty($_REQUEST['params']) && empty($_REQUEST['is_search'])) { $search_params = base64_decode(strtr($_REQUEST['params'], array(' ' => '+'))); $search_params = @unserialize($search_params); } // This array houses all the valid quick search types. $searchTypes = array('action' => array('sql' => 'lm.action', 'label' => $txt['modlog_action']), 'member' => array('sql' => 'mem.real_name', 'label' => $txt['modlog_member']), 'position' => array('sql' => 'mg.group_name', 'label' => $txt['modlog_position']), 'ip' => array('sql' => 'lm.ip', 'label' => $txt['modlog_ip'])); // Setup the allowed search $context['order'] = isset($_REQUEST['sort']) && isset($searchTypes[$_REQUEST['sort']]) ? $_REQUEST['sort'] : 'member'; if (!isset($search_params['string']) || !empty($_REQUEST['search']) && $search_params['string'] != $_REQUEST['search']) { $search_params_string = empty($_REQUEST['search']) ? '' : $_REQUEST['search']; } else { $search_params_string = $search_params['string']; } if (isset($_REQUEST['search_type']) || empty($search_params['type']) || !isset($searchTypes[$search_params['type']])) { $search_params_type = isset($_REQUEST['search_type']) && isset($searchTypes[$_REQUEST['search_type']]) ? $_REQUEST['search_type'] : $context['order']; } else { $search_params_type = $search_params['type']; } $search_params_column = $searchTypes[$search_params_type]['sql']; $search_params = array('string' => $search_params_string, 'type' => $search_params_type); // Setup the search context. $context['search_params'] = empty($search_params['string']) ? '' : base64_encode(serialize($search_params)); $context['search'] = array('string' => $search_params['string'], 'type' => $search_params['type'], 'label' => $searchTypes[$search_params_type]['label']); // If they are searching by action, then we must do some manual intervention to search in their language! if ($search_params['type'] == 'action' && !empty($search_params['string'])) { // Build a regex which looks for the words $regex = ''; $search = explode(' ', $search_params['string']); foreach ($search as $word) { $regex .= '(?=[\\w\\s]*' . $word . ')'; } // For the moment they can only search for ONE action! foreach ($txt as $key => $text) { if (strpos($key, 'modlog_ac_') === 0 && preg_match('~' . $regex . '~i', $text)) { $search_params['string'] = substr($key, 10); break; } } } require_once SUBSDIR . '/GenericList.class.php'; // This is all the information required for a moderation/admin log listing. $listOptions = array('id' => 'moderation_log_list', 'width' => '100%', 'items_per_page' => $context['displaypage'], 'no_items_label' => $txt['modlog_' . ($context['log_type'] == 3 ? 'admin_log_' : '') . 'no_entries_found'], 'base_href' => $scripturl . $context['url_start'] . (!empty($context['search_params']) ? ';params=' . $context['search_params'] : ''), 'default_sort_col' => 'time', 'get_items' => array('function' => array($this, 'getModLogEntries'), 'params' => array(!empty($search_params['string']) ? ' INSTR({raw:sql_type}, {string:search_string})' : '', array('sql_type' => $search_params_column, 'search_string' => $search_params['string']), $context['log_type'])), 'get_count' => array('function' => array($this, 'getModLogEntryCount'), 'params' => array(!empty($search_params['string']) ? ' INSTR({raw:sql_type}, {string:search_string})' : '', array('sql_type' => $search_params_column, 'search_string' => $search_params['string']), $context['log_type'])), 'columns' => array('action' => array('header' => array('value' => $txt['modlog_action'], 'class' => 'lefttext'), 'data' => array('db' => 'action_text', 'class' => 'smalltext'), 'sort' => array('default' => 'lm.action', 'reverse' => 'lm.action DESC')), 'time' => array('header' => array('value' => $txt['modlog_date'], 'class' => 'lefttext'), 'data' => array('db' => 'time', 'class' => 'smalltext'), 'sort' => array('default' => 'lm.log_time DESC', 'reverse' => 'lm.log_time')), 'moderator' => array('header' => array('value' => $txt['modlog_member'], 'class' => 'lefttext'), 'data' => array('db' => 'moderator_link', 'class' => 'smalltext'), 'sort' => array('default' => 'mem.real_name', 'reverse' => 'mem.real_name DESC')), 'position' => array('header' => array('value' => $txt['modlog_position'], 'class' => 'lefttext'), 'data' => array('db' => 'position', 'class' => 'smalltext'), 'sort' => array('default' => 'mg.group_name', 'reverse' => 'mg.group_name DESC')), 'ip' => array('header' => array('value' => $txt['modlog_ip'], 'class' => 'lefttext'), 'data' => array('db' => 'ip', 'class' => 'smalltext'), 'sort' => array('default' => 'lm.ip', 'reverse' => 'lm.ip DESC')), 'delete' => array('header' => array('value' => '<input type="checkbox" name="all" class="input_check" onclick="invertAll(this, this.form);" />', 'class' => 'centertext'), 'data' => array('function' => create_function('$entry', ' return \'<input type="checkbox" class="input_check" name="delete[]" value="\' . $entry[\'id\'] . \'"\' . ($entry[\'editable\'] ? \'\' : \' disabled="disabled"\') . \' />\'; '), 'class' => 'centertext'))), 'form' => array('href' => $scripturl . $context['url_start'], 'include_sort' => true, 'include_start' => true, 'hidden_fields' => array($context['session_var'] => $context['session_id'], 'params' => $context['search_params']), 'token' => 'mod-ml'), 'additional_rows' => array(array('class' => 'submitbutton', 'position' => 'below_table_data', 'value' => ' <div id="quick_log_search"> ' . $txt['modlog_search'] . ' (' . $txt['modlog_by'] . ': ' . $context['search']['label'] . ') <input type="text" name="search" size="18" value="' . Util::htmlspecialchars($context['search']['string']) . '" class="input_text" /> <input type="submit" name="is_search" value="' . $txt['modlog_go'] . '" class="button_submit" /> ' . ($context['can_delete'] ? '| <input type="submit" name="remove" value="' . $txt['modlog_remove'] . '" onclick="return confirm(\'' . $txt['modlog_remove_selected_confirm'] . '\');" class="right_submit" /> <input type="submit" name="removeall" value="' . $txt['modlog_removeall'] . '" onclick="return confirm(\'' . $txt['modlog_remove_all_confirm'] . '\');" class="right_submit" />' : '') . ' </div>'))); createToken('mod-ml'); // Create the log listing createList($listOptions); $context['sub_template'] = 'show_list'; $context['default_list'] = 'moderation_log_list'; }
/** * Main template for displaying the list of boards * * @param int $boards * @param string $id */ function template_list_boards($boards, $id) { global $context, $settings, $txt, $scripturl, $theme_bi_alternating_row; echo ' <ul class="category_boards" id="', $id, '">'; // Each board in each category's boards has: // new (is it new?), id, name, description, moderators (see below), link_moderators (just a list.), // children (see below.), link_children (easier to use.), children_new (are they new?), // topics (# of), posts (# of), link, href, and last_post. (see below.) foreach ($boards as $board) { echo ' <li class="board_row', !empty($board['children']) ? ' parent_board' : '', $board['is_redirect'] ? ' board_row_redirect' : '', $theme_bi_alternating_row ? ' alternating_row' : '', '" id="board_', $board['id'], '"> <div class="board_info"> <a class="icon_anchor" href="', $board['is_redirect'] || $context['user']['is_guest'] ? $board['href'] : $scripturl . '?action=unread;board=' . $board['id'] . '.0;children', '">'; // If the board or children is new, show an indicator. if ($board['new'] || $board['children_new']) { echo ' <span class="board_icon ', $board['new'] ? 'on_board' : 'on2_board', '" title="', $txt['new_posts'], '"></span>'; } elseif ($board['is_redirect']) { echo ' <span class="board_icon redirect_board" title="', sprintf($txt['redirect_board_to'], Util::htmlspecialchars($board['name'])), '"></span>'; } else { echo ' <span class="board_icon off_board" title="', $txt['old_posts'], '"></span>'; } echo ' </a> <h3 class="board_name"> <a href="', $board['href'], '" id="b', $board['id'], '">', $board['name'], '</a>'; // Has it outstanding posts for approval? @todo - Might change presentation here. if ($board['can_approve_posts'] && ($board['unapproved_posts'] || $board['unapproved_topics'])) { echo ' <a href="', $scripturl, '?action=moderate;area=postmod;sa=', $board['unapproved_topics'] > 0 ? 'topics' : 'posts', ';brd=', $board['id'], ';', $context['session_var'], '=', $context['session_id'], '" title="', sprintf($txt['unapproved_posts'], $board['unapproved_topics'], $board['unapproved_posts']), '" class="moderation_link"><img class="icon" src="', $settings['images_url'], '/icons/field_invalid.png" alt="(!)" /></a>'; } echo ' </h3> <p class="board_description">', $board['description'], '</p>'; // Show the "Moderators: ". Each has name, href, link, and id. (but we're gonna use link_moderators.) if (!empty($board['moderators'])) { echo ' <p class="moderators">', count($board['moderators']) === 1 ? $txt['moderator'] : $txt['moderators'], ': ', implode(', ', $board['link_moderators']), '</p>'; } // Show some basic information about the number of posts, etc. echo ' </div> <div class="board_latest"> <p class="board_stats"> ', comma_format($board['posts']), ' ', $board['is_redirect'] ? $txt['redirects'] : $txt['posts'], $board['is_redirect'] ? '' : '<br /> ' . comma_format($board['topics']) . ' ' . $txt['board_topics'], ' </p>'; // @todo - Last post message still needs some work. Probably split the language string into three chunks. // Example: // <chunk>Re: Nunc aliquam justo e...</chunk> <chunk>by Whoever</chunk> <chunk>Last post: Today at 08:00:37 am</chunk> // That should still allow sufficient scope for any language, if done sensibly. if (!empty($board['last_post']['id'])) { echo ' <p class="board_lastpost">'; if (!empty($settings['avatars_on_indexes'])) { echo ' <span class="board_avatar"><a href="', $board['last_post']['member']['href'], '"><img class="avatar" src="', $board['last_post']['member']['avatar']['href'], '" alt="" /></a></span>'; } echo ' ', $board['last_post']['last_post_message'], ' </p>'; } echo ' </div> </li>'; // Show the "Sub-boards: ". (there's a link_children but we're going to bold the new ones...) if (!empty($board['children'])) { // Sort the links into an array with new boards bold so it can be imploded. $children = array(); // Each child in each board's children has: // id, name, description, new (is it new?), topics (#), posts (#), href, link, and last_post. foreach ($board['children'] as $child) { if (!$child['is_redirect']) { $child['link'] = '<a href="' . $child['href'] . '" ' . ($child['new'] ? 'class="board_new_posts" ' : '') . 'title="' . ($child['new'] ? $txt['new_posts'] : $txt['old_posts']) . ' (' . $txt['board_topics'] . ': ' . comma_format($child['topics']) . ', ' . $txt['posts'] . ': ' . comma_format($child['posts']) . ')">' . $child['name'] . ($child['new'] ? '</a> <a ' . ($child['new'] ? 'class="new_posts" ' : '') . 'href="' . $scripturl . '?action=unread;board=' . $child['id'] . '" title="' . $txt['new_posts'] . ' (' . $txt['board_topics'] . ': ' . comma_format($child['topics']) . ', ' . $txt['posts'] . ': ' . comma_format($child['posts']) . ')"><span class="new_posts">' . $txt['new'] . '</span>' : '') . '</a>'; } else { $child['link'] = '<a href="' . $child['href'] . '" title="' . comma_format($child['posts']) . ' ' . $txt['redirects'] . '">' . $child['name'] . '</a>'; } // Has it posts awaiting approval? if ($child['can_approve_posts'] && ($child['unapproved_posts'] || $child['unapproved_topics'])) { $child['link'] .= ' <a href="' . $scripturl . '?action=moderate;area=postmod;sa=' . ($child['unapproved_topics'] > 0 ? 'topics' : 'posts') . ';brd=' . $child['id'] . ';' . $context['session_var'] . '=' . $context['session_id'] . '" title="' . sprintf($txt['unapproved_posts'], $child['unapproved_topics'], $child['unapproved_posts']) . '" class="moderation_link"><img class="icon" src="' . $settings['images_url'] . '/icons/field_invalid.png" alt="(!)" /></a>'; } $children[] = $child['link']; } // New <li> for sub-boards (if any). Can be styled to look like part of previous <li>. // Use h4 tag here for better a11y. Use <ul> for list of sub-boards. // Having sub-board links in <li>'s will allow "tidy sub-boards" via easy CSS tweaks. ;) echo ' <li class="childboard_row', $theme_bi_alternating_row ? ' alternating_row' : '', '" id="board_', $board['id'], '_children"> <ul class="childboards"> <li> <h4>', $txt['parent_boards'], ':</h4> </li> <li> ', implode('</li><li>', $children), ' </li> </ul> </li>'; } $theme_bi_alternating_row = $theme_bi_alternating_row ? 0 : 1; } echo ' </ul>'; }
/** * Adds html entities to the array/variable. Uses two underscores to guard against overloading. * * What it does: * - adds entities (", <, >) to the array or string var. * - importantly, does not effect keys, only values. * - calls itself recursively if necessary. * * @param string[]|string $var * @param int $level = 0 * @return mixed[]|string */ function htmlspecialchars__recursive($var, $level = 0) { if (!is_array($var)) { return Util::htmlspecialchars($var, ENT_QUOTES); } // Add the htmlspecialchars to every element. foreach ($var as $k => $v) { $var[$k] = $level > 25 ? null : htmlspecialchars__recursive($v, $level + 1); } return $var; }
/** * This function allocates out all the search stuff. */ public function action_search() { global $txt, $context; // What can we search for? $subActions = array('internal' => array($this, 'action_search_internal', 'permission' => 'admin_forum'), 'online' => array($this, 'action_search_doc', 'permission' => 'admin_forum'), 'member' => array($this, 'action_search_member', 'permission' => 'admin_forum')); // Set the subaction $action = new Action(); $subAction = $action->initialize($subActions, 'internal'); // Keep track of what the admin wants in terms of advanced or not if (empty($context['admin_preferences']['sb']) || $context['admin_preferences']['sb'] != $subAction) { $context['admin_preferences']['sb'] = $subAction; // Update the preferences. require_once SUBSDIR . '/Admin.subs.php'; updateAdminPreferences(); } // Setup for the template $context['search_type'] = $subAction; $context['search_term'] = isset($_REQUEST['search_term']) ? Util::htmlspecialchars($_REQUEST['search_term'], ENT_QUOTES) : ''; $context['sub_template'] = 'admin_search_results'; $context['page_title'] = $txt['admin_search_results']; // You did remember to enter something to search for, otherwise its easy if (trim($context['search_term']) == '') { $context['search_results'] = array(); } else { $action->dispatch($subAction); } }
/** * General function to split off a topic. * creates a new topic and moves the messages with the IDs in * array messagesToBeSplit to the new topic. * the subject of the newly created topic is set to 'newSubject'. * marks the newly created message as read for the user splitting it. * updates the statistics to reflect a newly created topic. * logs the action in the moderation log. * a notification is sent to all users monitoring this topic. * * @param int $split1_ID_TOPIC * @param int[] $splitMessages * @param string $new_subject * @return int the topic ID of the new split topic. */ function splitTopic($split1_ID_TOPIC, $splitMessages, $new_subject) { global $txt; $db = database(); // Nothing to split? if (empty($splitMessages)) { fatal_lang_error('no_posts_selected', false); } // Get some board info. $request = $db->query('', ' SELECT id_board, approved FROM {db_prefix}topics WHERE id_topic = {int:id_topic} LIMIT 1', array('id_topic' => $split1_ID_TOPIC)); list($id_board, $split1_approved) = $db->fetch_row($request); $db->free_result($request); // Find the new first and last not in the list. (old topic) $request = $db->query('', ' SELECT MIN(m.id_msg) AS myid_first_msg, MAX(m.id_msg) AS myid_last_msg, COUNT(*) AS message_count, m.approved FROM {db_prefix}messages AS m INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:id_topic}) WHERE m.id_msg NOT IN ({array_int:no_msg_list}) AND m.id_topic = {int:id_topic} GROUP BY m.approved ORDER BY m.approved DESC LIMIT 2', array('id_topic' => $split1_ID_TOPIC, 'no_msg_list' => $splitMessages)); // You can't select ALL the messages! if ($db->num_rows($request) == 0) { fatal_lang_error('selected_all_posts', false); } $split1_first_msg = null; $split1_last_msg = null; while ($row = $db->fetch_assoc($request)) { // Get the right first and last message dependant on approved state... if (empty($split1_first_msg) || $row['myid_first_msg'] < $split1_first_msg) { $split1_first_msg = $row['myid_first_msg']; } if (empty($split1_last_msg) || $row['approved']) { $split1_last_msg = $row['myid_last_msg']; } // Get the counts correct... if ($row['approved']) { $split1_replies = $row['message_count'] - 1; $split1_unapprovedposts = 0; } else { if (!isset($split1_replies)) { $split1_replies = 0; } elseif (!$split1_approved) { $split1_replies++; } $split1_unapprovedposts = $row['message_count']; } } $db->free_result($request); $split1_firstMem = getMsgMemberID($split1_first_msg); $split1_lastMem = getMsgMemberID($split1_last_msg); // Find the first and last in the list. (new topic) $request = $db->query('', ' SELECT MIN(id_msg) AS myid_first_msg, MAX(id_msg) AS myid_last_msg, COUNT(*) AS message_count, approved FROM {db_prefix}messages WHERE id_msg IN ({array_int:msg_list}) AND id_topic = {int:id_topic} GROUP BY id_topic, approved ORDER BY approved DESC LIMIT 2', array('msg_list' => $splitMessages, 'id_topic' => $split1_ID_TOPIC)); while ($row = $db->fetch_assoc($request)) { // As before get the right first and last message dependant on approved state... if (empty($split2_first_msg) || $row['myid_first_msg'] < $split2_first_msg) { $split2_first_msg = $row['myid_first_msg']; } if (empty($split2_last_msg) || $row['approved']) { $split2_last_msg = $row['myid_last_msg']; } // Then do the counts again... if ($row['approved']) { $split2_approved = true; $split2_replies = $row['message_count'] - 1; $split2_unapprovedposts = 0; } else { // Should this one be approved?? if ($split2_first_msg == $row['myid_first_msg']) { $split2_approved = false; } if (!isset($split2_replies)) { $split2_replies = 0; } elseif (!$split2_approved) { $split2_replies++; } $split2_unapprovedposts = $row['message_count']; } } $db->free_result($request); $split2_firstMem = getMsgMemberID($split2_first_msg); $split2_lastMem = getMsgMemberID($split2_last_msg); // No database changes yet, so let's double check to see if everything makes at least a little sense. if ($split1_first_msg <= 0 || $split1_last_msg <= 0 || $split2_first_msg <= 0 || $split2_last_msg <= 0 || $split1_replies < 0 || $split2_replies < 0 || $split1_unapprovedposts < 0 || $split2_unapprovedposts < 0 || !isset($split1_approved) || !isset($split2_approved)) { fatal_lang_error('cant_find_messages'); } // You cannot split off the first message of a topic. if ($split1_first_msg > $split2_first_msg) { fatal_lang_error('split_first_post', false); } // We're off to insert the new topic! Use 0 for now to avoid UNIQUE errors. $db->insert('', '{db_prefix}topics', array('id_board' => 'int', 'id_member_started' => 'int', 'id_member_updated' => 'int', 'id_first_msg' => 'int', 'id_last_msg' => 'int', 'num_replies' => 'int', 'unapproved_posts' => 'int', 'approved' => 'int', 'is_sticky' => 'int'), array((int) $id_board, $split2_firstMem, $split2_lastMem, 0, 0, $split2_replies, $split2_unapprovedposts, (int) $split2_approved, 0), array('id_topic')); $split2_ID_TOPIC = $db->insert_id('{db_prefix}topics', 'id_topic'); if ($split2_ID_TOPIC <= 0) { fatal_lang_error('cant_insert_topic'); } // Move the messages over to the other topic. $new_subject = strtr(Util::htmltrim(Util::htmlspecialchars($new_subject)), array("\r" => '', "\n" => '', "\t" => '')); // Check the subject length. if (Util::strlen($new_subject) > 100) { $new_subject = Util::substr($new_subject, 0, 100); } // Valid subject? if ($new_subject != '') { $db->query('', ' UPDATE {db_prefix}messages SET id_topic = {int:id_topic}, subject = CASE WHEN id_msg = {int:split_first_msg} THEN {string:new_subject} ELSE {string:new_subject_replies} END WHERE id_msg IN ({array_int:split_msgs})', array('split_msgs' => $splitMessages, 'id_topic' => $split2_ID_TOPIC, 'new_subject' => $new_subject, 'split_first_msg' => $split2_first_msg, 'new_subject_replies' => $txt['response_prefix'] . $new_subject)); // Cache the new topics subject... we can do it now as all the subjects are the same! updateStats('subject', $split2_ID_TOPIC, $new_subject); } // Any associated reported posts better follow... require_once SUBSDIR . '/Topic.subs.php'; updateSplitTopics(array('splitMessages' => $splitMessages, 'split2_ID_TOPIC' => $split2_ID_TOPIC, 'split1_replies' => $split1_replies, 'split1_first_msg' => $split1_first_msg, 'split1_last_msg' => $split1_last_msg, 'split1_firstMem' => $split1_firstMem, 'split1_lastMem' => $split1_lastMem, 'split1_unapprovedposts' => $split1_unapprovedposts, 'split1_ID_TOPIC' => $split1_ID_TOPIC, 'split2_first_msg' => $split2_first_msg, 'split2_last_msg' => $split2_last_msg, 'split2_ID_TOPIC' => $split2_ID_TOPIC, 'split2_approved' => $split2_approved), $id_board); require_once SUBSDIR . '/FollowUps.subs.php'; // Let's see if we can create a stronger bridge between the two topics // @todo not sure what message from the oldest topic I should link to the new one, so I'll go with the first linkMessages($split1_first_msg, $split2_ID_TOPIC); // Copy log topic entries. // @todo This should really be chunked. $request = $db->query('', ' SELECT id_member, id_msg, unwatched FROM {db_prefix}log_topics WHERE id_topic = {int:id_topic}', array('id_topic' => (int) $split1_ID_TOPIC)); if ($db->num_rows($request) > 0) { $replaceEntries = array(); while ($row = $db->fetch_assoc($request)) { $replaceEntries[] = array($row['id_member'], $split2_ID_TOPIC, $row['id_msg'], $row['unwatched']); } require_once SUBSDIR . '/Topic.subs.php'; markTopicsRead($replaceEntries, false); unset($replaceEntries); } $db->free_result($request); // Housekeeping. updateTopicStats(); updateLastMessages($id_board); logAction('split', array('topic' => $split1_ID_TOPIC, 'new_topic' => $split2_ID_TOPIC, 'board' => $id_board)); // Notify people that this topic has been split? require_once SUBSDIR . '/Notification.subs.php'; sendNotifications($split1_ID_TOPIC, 'split'); // If there's a search index that needs updating, update it... require_once SUBSDIR . '/Search.subs.php'; $searchAPI = findSearchAPI(); if (is_callable(array($searchAPI, 'topicSplit'))) { $searchAPI->topicSplit($split2_ID_TOPIC, $splitMessages); } // Return the ID of the newly created topic. return $split2_ID_TOPIC; }
/** * Sends a personal message from the specified person to the specified people * ($from defaults to the user) * * @package PersonalMessage * @param mixed[] $recipients - an array containing the arrays 'to' and 'bcc', both containing id_member's. * @param string $subject - should have no slashes and no html entities * @param string $message - should have no slashes and no html entities * @param bool $store_outbox * @param mixed[]|null $from - an array with the id, name, and username of the member. * @param int $pm_head - the ID of the chain being replied to - if any. * @return mixed[] an array with log entries telling how many recipients were successful and which recipients it failed to send to. */ function sendpm($recipients, $subject, $message, $store_outbox = true, $from = null, $pm_head = 0) { global $scripturl, $txt, $user_info, $language, $modSettings, $webmaster_email; $db = database(); // Make sure the PM language file is loaded, we might need something out of it. loadLanguage('PersonalMessage'); // Needed for our email and post functions require_once SUBSDIR . '/Mail.subs.php'; require_once SUBSDIR . '/Post.subs.php'; // Initialize log array. $log = array('failed' => array(), 'sent' => array()); if ($from === null) { $from = array('id' => $user_info['id'], 'name' => $user_info['name'], 'username' => $user_info['username']); } else { $user_info['name'] = $from['name']; } // This is the one that will go in their inbox. $htmlmessage = Util::htmlspecialchars($message, ENT_QUOTES, 'UTF-8', true); preparsecode($htmlmessage); $htmlsubject = strtr(Util::htmlspecialchars($subject), array("\r" => '', "\n" => '', "\t" => '')); if (Util::strlen($htmlsubject) > 100) { $htmlsubject = Util::substr($htmlsubject, 0, 100); } // Make sure is an array if (!is_array($recipients)) { $recipients = array($recipients); } // Integrated PMs call_integration_hook('integrate_personal_message', array(&$recipients, &$from, &$subject, &$message)); // Get a list of usernames and convert them to IDs. $usernames = array(); foreach ($recipients as $rec_type => $rec) { foreach ($rec as $id => $member) { if (!is_numeric($recipients[$rec_type][$id])) { $recipients[$rec_type][$id] = Util::strtolower(trim(preg_replace('/[<>&"\'=\\\\]/', '', $recipients[$rec_type][$id]))); $usernames[$recipients[$rec_type][$id]] = 0; } } } if (!empty($usernames)) { $request = $db->query('pm_find_username', ' SELECT id_member, member_name FROM {db_prefix}members WHERE ' . (defined('DB_CASE_SENSITIVE') ? 'LOWER(member_name)' : 'member_name') . ' IN ({array_string:usernames})', array('usernames' => array_keys($usernames))); while ($row = $db->fetch_assoc($request)) { if (isset($usernames[Util::strtolower($row['member_name'])])) { $usernames[Util::strtolower($row['member_name'])] = $row['id_member']; } } $db->free_result($request); // Replace the usernames with IDs. Drop usernames that couldn't be found. foreach ($recipients as $rec_type => $rec) { foreach ($rec as $id => $member) { if (is_numeric($recipients[$rec_type][$id])) { continue; } if (!empty($usernames[$member])) { $recipients[$rec_type][$id] = $usernames[$member]; } else { $log['failed'][$id] = sprintf($txt['pm_error_user_not_found'], $recipients[$rec_type][$id]); unset($recipients[$rec_type][$id]); } } } } // Make sure there are no duplicate 'to' members. $recipients['to'] = array_unique($recipients['to']); // Only 'bcc' members that aren't already in 'to'. $recipients['bcc'] = array_diff(array_unique($recipients['bcc']), $recipients['to']); // Combine 'to' and 'bcc' recipients. $all_to = array_merge($recipients['to'], $recipients['bcc']); // Check no-one will want it deleted right away! $request = $db->query('', ' SELECT id_member, criteria, is_or FROM {db_prefix}pm_rules WHERE id_member IN ({array_int:to_members}) AND delete_pm = {int:delete_pm}', array('to_members' => $all_to, 'delete_pm' => 1)); $deletes = array(); // Check whether we have to apply anything... while ($row = $db->fetch_assoc($request)) { $criteria = unserialize($row['criteria']); // Note we don't check the buddy status, cause deletion from buddy = madness! $delete = false; foreach ($criteria as $criterium) { if ($criterium['t'] == 'mid' && $criterium['v'] == $from['id'] || $criterium['t'] == 'gid' && in_array($criterium['v'], $user_info['groups']) || $criterium['t'] == 'sub' && strpos($subject, $criterium['v']) !== false || $criterium['t'] == 'msg' && strpos($message, $criterium['v']) !== false) { $delete = true; } elseif (!$row['is_or']) { $delete = false; break; } } if ($delete) { $deletes[$row['id_member']] = 1; } } $db->free_result($request); // Load the membergrounp message limits. static $message_limit_cache = array(); if (!allowedTo('moderate_forum') && empty($message_limit_cache)) { $request = $db->query('', ' SELECT id_group, max_messages FROM {db_prefix}membergroups', array()); while ($row = $db->fetch_assoc($request)) { $message_limit_cache[$row['id_group']] = $row['max_messages']; } $db->free_result($request); } // Load the groups that are allowed to read PMs. // @todo move into a separate function on $permission. $allowed_groups = array(); $disallowed_groups = array(); $request = $db->query('', ' SELECT id_group, add_deny FROM {db_prefix}permissions WHERE permission = {string:read_permission}', array('read_permission' => 'pm_read')); while ($row = $db->fetch_assoc($request)) { if (empty($row['add_deny'])) { $disallowed_groups[] = $row['id_group']; } else { $allowed_groups[] = $row['id_group']; } } $db->free_result($request); if (empty($modSettings['permission_enable_deny'])) { $disallowed_groups = array(); } $request = $db->query('', ' SELECT member_name, real_name, id_member, email_address, lngfile, pm_email_notify, personal_messages,' . (allowedTo('moderate_forum') ? ' 0' : ' (receive_from = {int:admins_only}' . (empty($modSettings['enable_buddylist']) ? '' : ' OR (receive_from = {int:buddies_only} AND FIND_IN_SET({string:from_id}, buddy_list) = 0) OR (receive_from = {int:not_on_ignore_list} AND FIND_IN_SET({string:from_id}, pm_ignore_list) != 0)') . ')') . ' AS ignored, FIND_IN_SET({string:from_id}, buddy_list) != 0 AS is_buddy, is_activated, additional_groups, id_group, id_post_group FROM {db_prefix}members WHERE id_member IN ({array_int:recipients}) ORDER BY lngfile LIMIT {int:count_recipients}', array('not_on_ignore_list' => 1, 'buddies_only' => 2, 'admins_only' => 3, 'recipients' => $all_to, 'count_recipients' => count($all_to), 'from_id' => $from['id'])); $notifications = array(); while ($row = $db->fetch_assoc($request)) { // Don't do anything for members to be deleted! if (isset($deletes[$row['id_member']])) { continue; } // We need to know this members groups. $groups = explode(',', $row['additional_groups']); $groups[] = $row['id_group']; $groups[] = $row['id_post_group']; $message_limit = -1; // For each group see whether they've gone over their limit - assuming they're not an admin. if (!in_array(1, $groups)) { foreach ($groups as $id) { if (isset($message_limit_cache[$id]) && $message_limit != 0 && $message_limit < $message_limit_cache[$id]) { $message_limit = $message_limit_cache[$id]; } } if ($message_limit > 0 && $message_limit <= $row['personal_messages']) { $log['failed'][$row['id_member']] = sprintf($txt['pm_error_data_limit_reached'], $row['real_name']); unset($all_to[array_search($row['id_member'], $all_to)]); continue; } // Do they have any of the allowed groups? if (count(array_intersect($allowed_groups, $groups)) == 0 || count(array_intersect($disallowed_groups, $groups)) != 0) { $log['failed'][$row['id_member']] = sprintf($txt['pm_error_user_cannot_read'], $row['real_name']); unset($all_to[array_search($row['id_member'], $all_to)]); continue; } } // Note that PostgreSQL can return a lowercase t/f for FIND_IN_SET if (!empty($row['ignored']) && $row['ignored'] != 'f' && $row['id_member'] != $from['id']) { $log['failed'][$row['id_member']] = sprintf($txt['pm_error_ignored_by_user'], $row['real_name']); unset($all_to[array_search($row['id_member'], $all_to)]); continue; } // If the receiving account is banned (>=10) or pending deletion (4), refuse to send the PM. if ($row['is_activated'] >= 10 || $row['is_activated'] == 4 && !$user_info['is_admin']) { $log['failed'][$row['id_member']] = sprintf($txt['pm_error_user_cannot_read'], $row['real_name']); unset($all_to[array_search($row['id_member'], $all_to)]); continue; } // Send a notification, if enabled - taking the buddy list into account. if (!empty($row['email_address']) && ($row['pm_email_notify'] == 1 || $row['pm_email_notify'] > 1 && (!empty($modSettings['enable_buddylist']) && $row['is_buddy'])) && $row['is_activated'] == 1) { $notifications[empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']][] = $row['email_address']; } $log['sent'][$row['id_member']] = sprintf(isset($txt['pm_successfully_sent']) ? $txt['pm_successfully_sent'] : '', $row['real_name']); } $db->free_result($request); // Only 'send' the message if there are any recipients left. if (empty($all_to)) { return $log; } // Track the pm count for our stats if (!empty($modSettings['trackStats'])) { trackStats(array('pm' => '+')); } // Insert the message itself and then grab the last insert id. $db->insert('', '{db_prefix}personal_messages', array('id_pm_head' => 'int', 'id_member_from' => 'int', 'deleted_by_sender' => 'int', 'from_name' => 'string-255', 'msgtime' => 'int', 'subject' => 'string-255', 'body' => 'string-65534'), array($pm_head, $from['id'], $store_outbox ? 0 : 1, $from['username'], time(), $htmlsubject, $htmlmessage), array('id_pm')); $id_pm = $db->insert_id('{db_prefix}personal_messages', 'id_pm'); // Add the recipients. if (!empty($id_pm)) { // If this is new we need to set it part of it's own conversation. if (empty($pm_head)) { $db->query('', ' UPDATE {db_prefix}personal_messages SET id_pm_head = {int:id_pm_head} WHERE id_pm = {int:id_pm_head}', array('id_pm_head' => $id_pm)); } // Some people think manually deleting personal_messages is fun... it's not. We protect against it though :) $db->query('', ' DELETE FROM {db_prefix}pm_recipients WHERE id_pm = {int:id_pm}', array('id_pm' => $id_pm)); $insertRows = array(); $to_list = array(); foreach ($all_to as $to) { $insertRows[] = array($id_pm, $to, in_array($to, $recipients['bcc']) ? 1 : 0, isset($deletes[$to]) ? 1 : 0, 1); if (!in_array($to, $recipients['bcc'])) { $to_list[] = $to; } } $db->insert('insert', '{db_prefix}pm_recipients', array('id_pm' => 'int', 'id_member' => 'int', 'bcc' => 'int', 'deleted' => 'int', 'is_new' => 'int'), $insertRows, array('id_pm', 'id_member')); } $maillist = !empty($modSettings['maillist_enabled']) && !empty($modSettings['pbe_pm_enabled']); // If they have post by email enabled, override disallow_sendBody if (!$maillist && !empty($modSettings['disallow_sendBody'])) { $message = ''; censorText($subject); } else { require_once SUBSDIR . '/Emailpost.subs.php'; pbe_prepare_text($message, $subject); } $to_names = array(); if (count($to_list) > 1) { require_once SUBSDIR . '/Members.subs.php'; $result = getBasicMemberData($to_list); foreach ($result as $row) { $to_names[] = un_htmlspecialchars($row['real_name']); } } $replacements = array('SUBJECT' => $subject, 'MESSAGE' => $message, 'SENDER' => un_htmlspecialchars($from['name']), 'READLINK' => $scripturl . '?action=pm;pmsg=' . $id_pm . '#msg' . $id_pm, 'REPLYLINK' => $scripturl . '?action=pm;sa=send;f=inbox;pmsg=' . $id_pm . ';quote;u=' . $from['id'], 'TOLIST' => implode(', ', $to_names)); // Select the right template $email_template = ($maillist && empty($modSettings['disallow_sendBody']) ? 'pbe_' : '') . 'new_pm' . (empty($modSettings['disallow_sendBody']) ? '_body' : '') . (!empty($to_names) ? '_tolist' : ''); foreach ($notifications as $lang => $notification_list) { // Using maillist functionality if ($maillist) { $sender_details = query_sender_wrapper($from['id']); $from_wrapper = !empty($modSettings['maillist_mail_from']) ? $modSettings['maillist_mail_from'] : (empty($modSettings['maillist_sitename_address']) ? $webmaster_email : $modSettings['maillist_sitename_address']); // Add in the signature $replacements['SIGNATURE'] = $sender_details['signature']; // And off it goes, looking a bit more personal $mail = loadEmailTemplate($email_template, $replacements, $lang); $reference = !empty($pm_head) ? $pm_head : null; sendmail($notification_list, $mail['subject'], $mail['body'], $from['name'], 'p' . $id_pm, false, 2, null, true, $from_wrapper, $reference); } else { // Off the notification email goes! $mail = loadEmailTemplate($email_template, $replacements, $lang); sendmail($notification_list, $mail['subject'], $mail['body'], null, 'p' . $id_pm, false, 2, null, true); } } // Integrated After PMs call_integration_hook('integrate_personal_message_after', array(&$id_pm, &$log, &$recipients, &$from, &$subject, &$message)); // Back to what we were on before! loadLanguage('index+PersonalMessage'); // Add one to their unread and read message counts. foreach ($all_to as $k => $id) { if (isset($deletes[$id])) { unset($all_to[$k]); } } if (!empty($all_to)) { updateMemberData($all_to, array('personal_messages' => '+', 'unread_messages' => '+', 'new_pm' => 1)); } return $log; }
/** * Used to preview custom email bounce templates before they are saved for use */ public function action_bounce_preview() { global $context, $txt, $scripturl, $mbname, $modSettings; require_once SUBSDIR . '/Post.subs.php'; loadLanguage('Errors'); loadLanguage('ModerationCenter'); $context['post_error']['errors'] = array(); // If you can't approve emails, what are you doing here? if (allowedTo('approve_emails')) { $body = !empty($_POST['body']) ? trim(censorText($_POST['body'])) : ''; $context['preview_subject'] = !empty($_POST['title']) ? trim(Util::htmlspecialchars($_POST['title'])) : ''; if (isset($_POST['issuing'])) { if (empty($_POST['title']) || empty($_POST['body'])) { $context['post_error']['errors'][] = $txt['warning_notify_blank']; } } else { if (empty($_POST['title'])) { $context['post_error']['errors'][] = $txt['mc_warning_template_error_no_title']; } if (empty($_POST['body'])) { $context['post_error']['errors'][] = $txt['mc_warning_template_error_no_body']; } // Add in few replacements. /** * These are the defaults: * - {FORUMNAME} - Forum Name, the full name with all the bells * - {FORUMNAMESHORT} - Short and simple name * - {SCRIPTURL} - Web address of forum. * - {ERROR} - The error that was generated by the post, its unique to the post so can't render it here * - {SUBJECT} - The subject of the email thats being discussed, unique to the post so can't render it here * - {REGARDS} - Standard email sign-off. * - {EMAILREGARDS} - Maybe a bit more friendly sign-off. */ $find = array('{FORUMNAME}', '{FORUMNAMESHORT}', '{SCRIPTURL}', '{REGARDS}', '{EMAILREGARDS}'); $replace = array($mbname, !empty($modSettings['maillist_sitename']) ? $modSettings['maillist_sitename'] : $mbname, $scripturl, replaceBasicActionUrl($txt['regards_team']), !empty($modSettings['maillist_sitename_regards']) ? $modSettings['maillist_sitename_regards'] : ''); $body = str_replace($find, $replace, $body); } // Deal with any BBC so it looks good for the preview if (!empty($_POST['body'])) { preparsecode($body); $body = parse_bbc($body, true); } $context['preview_message'] = $body; } $context['sub_template'] = 'generic_preview'; }
/** * Edit a 'it bounced' template. * * @uses bounce_template sub template */ public function action_modify_bounce_templates() { global $context, $txt, $user_info; require_once SUBSDIR . '/Moderation.subs.php'; $context['id_template'] = isset($_REQUEST['tid']) ? (int) $_REQUEST['tid'] : 0; $context['is_edit'] = (bool) $context['id_template']; // Standard template things, you know the drill $context['page_title'] = $context['is_edit'] ? $txt['ml_bounce_template_modify'] : $txt['ml_bounce_template_add']; $context['sub_template'] = 'bounce_template'; $context[$context['admin_menu_name']]['current_subsection'] = 'templates'; // Defaults to show $context['template_data'] = array('title' => '', 'body' => $txt['ml_bounce_template_body_default'], 'subject' => $txt['ml_bounce_template_subject_default'], 'personal' => false, 'can_edit_personal' => true); // If it's an edit load it. if ($context['is_edit']) { modLoadTemplate($context['id_template'], 'bnctpl'); } // Wait, we are saving? if (isset($_POST['save'])) { checkSession('post'); validateToken('mod-mlt'); // To check the BBC is good... require_once SUBSDIR . '/Post.subs.php'; // Bit of cleaning! $template_body = trim($_POST['template_body']); $template_title = trim($_POST['template_title']); // Need something in both boxes. if (!empty($template_body) && !empty($template_title)) { // Safety first. $template_title = Util::htmlspecialchars($template_title); // Clean up BBC. preparsecode($template_body); // But put line breaks back! $template_body = strtr($template_body, array('<br />' => "\n")); // Is this personal? $recipient_id = !empty($_POST['make_personal']) ? $user_info['id'] : 0; // Updating or adding ? if ($context['is_edit']) { // Simple update... modAddUpdateTemplate($recipient_id, $template_title, $template_body, $context['id_template'], true, 'bnctpl'); // If it wasn't visible and now is they've effectively added it. if ($context['template_data']['personal'] && !$recipient_id) { logAction('add_bounce_template', array('template' => $template_title)); } elseif (!$context['template_data']['personal'] && $recipient_id) { logAction('delete_bounce_template', array('template' => $template_title)); } else { logAction('modify_bounce_template', array('template' => $template_title)); } } else { modAddUpdateTemplate($recipient_id, $template_title, $template_body, $context['id_template'], false, 'bnctpl'); logAction('add_bounce_template', array('template' => $template_title)); } // Get out of town... redirectexit('action=admin;area=maillist;sa=emailtemplates'); } else { $context['warning_errors'] = array(); $context['template_data']['title'] = !empty($template_title) ? $template_title : ''; $context['template_data']['body'] = !empty($template_body) ? $template_body : $txt['ml_bounce_template_body_default']; $context['template_data']['personal'] = !empty($recipient_id); if (empty($template_title)) { $context['warning_errors'][] = $txt['ml_bounce_template_error_no_title']; } if (empty($template_body)) { $context['warning_errors'][] = $txt['ml_bounce_template_error_no_body']; } } } createToken('mod-mlt'); }
/** * Edit some profile fields? * * - Accessed with ?action=admin;area=featuresettings;sa=profileedit * * @uses sub template edit_profile_field */ public function action_profileedit() { global $txt, $scripturl, $context; require_once SUBSDIR . '/ManageFeatures.subs.php'; loadTemplate('ManageFeatures'); // Sort out the context! $context['fid'] = isset($_GET['fid']) ? (int) $_GET['fid'] : 0; $context[$context['admin_menu_name']]['current_subsection'] = 'profile'; $context['page_title'] = $context['fid'] ? $txt['custom_edit_title'] : $txt['custom_add_title']; $context['sub_template'] = 'edit_profile_field'; // any errors messages to show? if (isset($_GET['msg'])) { loadLanguage('Errors'); if (isset($txt['custom_option_' . $_GET['msg']])) { $context['custom_option__error'] = $txt['custom_option_' . $_GET['msg']]; } } // Load the profile language for section names. loadLanguage('Profile'); // Load up the profile field, if one was supplied if ($context['fid']) { $context['field'] = getProfileField($context['fid']); } // Setup the default values as needed. if (empty($context['field'])) { $context['field'] = array('name' => '', 'colname' => '???', 'desc' => '', 'profile_area' => 'forumprofile', 'reg' => false, 'display' => false, 'memberlist' => false, 'type' => 'text', 'max_length' => 255, 'rows' => 4, 'cols' => 30, 'bbc' => false, 'default_check' => false, 'default_select' => '', 'options' => array('', '', ''), 'active' => true, 'private' => false, 'can_search' => false, 'mask' => 'nohtml', 'regex' => '', 'enclose' => '', 'placement' => 0); } // All the javascript for this page... everything else is in admin.js addJavascriptVar(array('startOptID' => count($context['field']['options']))); addInlineJavascript('updateInputBoxes();', true); // Are we toggling which ones are active? if (isset($_POST['onoff'])) { checkSession(); validateToken('admin-scp'); // Enable and disable custom fields as required. $enabled = array(0); foreach ($_POST['cust'] as $id) { $enabled[] = (int) $id; } updateRenamedProfileStatus($enabled); } elseif (isset($_POST['save'])) { checkSession(); validateToken('admin-ecp'); // Everyone needs a name - even the (bracket) unknown... if (trim($_POST['field_name']) == '') { redirectexit($scripturl . '?action=admin;area=featuresettings;sa=profileedit;fid=' . $_GET['fid'] . ';msg=need_name'); } // Regex you say? Do a very basic test to see if the pattern is valid if (!empty($_POST['regex']) && @preg_match($_POST['regex'], 'dummy') === false) { redirectexit($scripturl . '?action=admin;area=featuresettings;sa=profileedit;fid=' . $_GET['fid'] . ';msg=regex_error'); } $_POST['field_name'] = Util::htmlspecialchars($_POST['field_name']); $_POST['field_desc'] = Util::htmlspecialchars($_POST['field_desc']); // Checkboxes... $show_reg = isset($_POST['reg']) ? (int) $_POST['reg'] : 0; $show_display = isset($_POST['display']) ? 1 : 0; $show_memberlist = isset($_POST['memberlist']) ? 1 : 0; $bbc = isset($_POST['bbc']) ? 1 : 0; $show_profile = $_POST['profile_area']; $active = isset($_POST['active']) ? 1 : 0; $private = isset($_POST['private']) ? (int) $_POST['private'] : 0; $can_search = isset($_POST['can_search']) ? 1 : 0; // Some masking stuff... $mask = isset($_POST['mask']) ? $_POST['mask'] : ''; if ($mask == 'regex' && isset($_POST['regex'])) { $mask .= $_POST['regex']; } $field_length = isset($_POST['max_length']) ? (int) $_POST['max_length'] : 255; $enclose = isset($_POST['enclose']) ? $_POST['enclose'] : ''; $placement = isset($_POST['placement']) ? (int) $_POST['placement'] : 0; // Select options? $field_options = ''; $newOptions = array(); $default = isset($_POST['default_check']) && $_POST['field_type'] == 'check' ? 1 : ''; if (!empty($_POST['select_option']) && ($_POST['field_type'] == 'select' || $_POST['field_type'] == 'radio')) { foreach ($_POST['select_option'] as $k => $v) { // Clean, clean, clean... $v = Util::htmlspecialchars($v); $v = strtr($v, array(',' => '')); // Nada, zip, etc... if (trim($v) == '') { continue; } // Otherwise, save it boy. $field_options .= $v . ','; // This is just for working out what happened with old options... $newOptions[$k] = $v; // Is it default? if (isset($_POST['default_select']) && $_POST['default_select'] == $k) { $default = $v; } } if (isset($_POST['default_select']) && $_POST['default_select'] == 'no_default') { $default = 'no_default'; } $field_options = substr($field_options, 0, -1); } // Text area by default has dimensions if ($_POST['field_type'] == 'textarea') { $default = (int) $_POST['rows'] . ',' . (int) $_POST['cols']; } // Come up with the unique name? if (empty($context['fid'])) { $colname = Util::substr(strtr($_POST['field_name'], array(' ' => '')), 0, 6); preg_match('~([\\w\\d_-]+)~', $colname, $matches); // If there is nothing to the name, then let's start our own - for foreign languages etc. if (isset($matches[1])) { $colname = $initial_colname = 'cust_' . strtolower($matches[1]); } else { $colname = $initial_colname = 'cust_' . mt_rand(1, 999999); } $unique = ensureUniqueProfileField($colname, $initial_colname); // Still not a unique colum name? Leave it up to the user, then. if (!$unique) { fatal_lang_error('custom_option_not_unique'); } } else { // Anything going to check or select is pointless keeping - as is anything coming from check! if ($_POST['field_type'] == 'check' && $context['field']['type'] != 'check' || ($_POST['field_type'] == 'select' || $_POST['field_type'] == 'radio') && $context['field']['type'] != 'select' && $context['field']['type'] != 'radio' || $context['field']['type'] == 'check' && $_POST['field_type'] != 'check') { deleteProfileFieldUserData($context['field']['colname']); } elseif ($_POST['field_type'] == 'select' || $_POST['field_type'] == 'radio') { $optionChanges = array(); $takenKeys = array(); // Work out what's changed! foreach ($context['field']['options'] as $k => $option) { if (trim($option) == '') { continue; } // Still exists? if (in_array($option, $newOptions)) { $takenKeys[] = $k; continue; } } // Finally - have we renamed it - or is it really gone? foreach ($optionChanges as $k => $option) { // Just been renamed? if (!in_array($k, $takenKeys) && !empty($newOptions[$k])) { updateRenamedProfileField($k, $newOptions, $context['field']['colname'], $option); } } } // @todo Maybe we should adjust based on new text length limits? } // Updating an existing field? if ($context['fid']) { $field_data = array('field_length' => $field_length, 'show_reg' => $show_reg, 'show_display' => $show_display, 'show_memberlist' => $show_memberlist, 'private' => $private, 'active' => $active, 'can_search' => $can_search, 'bbc' => $bbc, 'current_field' => $context['fid'], 'field_name' => $_POST['field_name'], 'field_desc' => $_POST['field_desc'], 'field_type' => $_POST['field_type'], 'field_options' => $field_options, 'show_profile' => $show_profile, 'default_value' => $default, 'mask' => $mask, 'enclose' => $enclose, 'placement' => $placement); updateProfileField($field_data); // Just clean up any old selects - these are a pain! if (($_POST['field_type'] == 'select' || $_POST['field_type'] == 'radio') && !empty($newOptions)) { deleteOldProfileFieldSelects($newOptions, $context['field']['colname']); } } else { $new_field = array('col_name' => $colname, 'field_name' => $_POST['field_name'], 'field_desc' => $_POST['field_desc'], 'field_type' => $_POST['field_type'], 'field_length' => $field_length, 'field_options' => $field_options, 'show_reg' => $show_reg, 'show_display' => $show_display, 'show_memberlist' => $show_memberlist, 'show_profile' => $show_profile, 'private' => $private, 'active' => $active, 'default' => $default, 'can_search' => $can_search, 'bbc' => $bbc, 'mask' => $mask, 'enclose' => $enclose, 'placement' => $placement, 'vieworder' => list_getProfileFieldSize() + 1); addProfileField($new_field); } } elseif (isset($_POST['delete']) && $context['field']['colname']) { checkSession(); validateToken('admin-ecp'); // Delete the old data first, then the field. deleteProfileFieldUserData($context['field']['colname']); deleteProfileField($context['fid']); } // Rebuild display cache etc. if (isset($_POST['delete']) || isset($_POST['save']) || isset($_POST['onoff'])) { checkSession(); // Update the display cache updateDisplayCache(); redirectexit('action=admin;area=featuresettings;sa=profile'); } createToken('admin-ecp'); }
/** * Add or edit a portal wide permissions profile */ public function action_sportal_admin_permission_profiles_edit() { global $context, $txt; // New or an edit? $context['is_new'] = empty($_REQUEST['profile_id']); // Saving the form if (!empty($_POST['submit'])) { // Security first checkSession(); // Always clean the name if (!isset($_POST['name']) || Util::htmltrim(Util::htmlspecialchars($_POST['name'], ENT_QUOTES)) === '') { fatal_lang_error('sp_error_profile_name_empty', false); } $groups_allowed = $groups_denied = ''; // If specific member groups were picked, build the allow/deny arrays if (!empty($_POST['membergroups']) && is_array($_POST['membergroups'])) { $groups_allowed = $groups_denied = array(); foreach ($_POST['membergroups'] as $id => $value) { if ($value == 1) { $groups_allowed[] = (int) $id; } elseif ($value == -1) { $groups_denied[] = (int) $id; } } $groups_allowed = implode(',', $groups_allowed); $groups_denied = implode(',', $groups_denied); } // Add the data to place in the fields $profile_info = array('id' => (int) $_POST['profile_id'], 'type' => 1, 'name' => Util::htmlspecialchars($_POST['name'], ENT_QUOTES), 'value' => implode('|', array($groups_allowed, $groups_denied))); // New we simply insert $profile_info['id'] = sp_add_permission_profile($profile_info, $context['is_new']); redirectexit('action=admin;area=portalprofiles'); } // Not saving, then its time to show the permission form if ($context['is_new']) { $context['profile'] = array('id' => 0, 'name' => $txt['sp_profiles_default_name'], 'label' => $txt['sp_profiles_default_name'], 'groups_allowed' => array(), 'groups_denied' => array()); } else { $_REQUEST['profile_id'] = (int) $_REQUEST['profile_id']; $context['profile'] = sportal_get_profiles($_REQUEST['profile_id']); } // Sub template time $context['profile']['groups'] = sp_load_membergroups(); $context['page_title'] = $context['is_new'] ? $txt['sp_admin_profiles_add'] : $txt['sp_admin_profiles_edit']; $context['sub_template'] = 'permission_profiles_edit'; }
/** * Issue/manage an user's warning status. * @uses ProfileAccount template issueWarning sub template * @uses Profile template */ public function action_issuewarning() { global $txt, $scripturl, $modSettings, $mbname, $context, $cur_profile; $memID = currentMemberID(); // make sure the sub-template is set... loadTemplate('ProfileAccount'); $context['sub_template'] = 'issueWarning'; // We need this because of template_load_warning_variables loadTemplate('Profile'); loadJavascriptFile('profile.js'); // jQuery-UI FTW! $modSettings['jquery_include_ui'] = true; loadCSSFile('jquery.ui.slider.css'); loadCSSFile('jquery.ui.theme.css'); // Get all the actual settings. list($modSettings['warning_enable'], $modSettings['user_limit']) = explode(',', $modSettings['warning_settings']); // This stores any legitimate errors. $issueErrors = array(); // Doesn't hurt to be overly cautious. if (empty($modSettings['warning_enable']) || $context['user']['is_owner'] && !$cur_profile['warning'] || !allowedTo('issue_warning')) { fatal_lang_error('no_access', false); } // Get the base (errors related) stuff done. loadLanguage('Errors'); $context['custom_error_title'] = $txt['profile_warning_errors_occurred']; // Make sure things which are disabled stay disabled. $modSettings['warning_watch'] = !empty($modSettings['warning_watch']) ? $modSettings['warning_watch'] : 110; $modSettings['warning_moderate'] = !empty($modSettings['warning_moderate']) && !empty($modSettings['postmod_active']) ? $modSettings['warning_moderate'] : 110; $modSettings['warning_mute'] = !empty($modSettings['warning_mute']) ? $modSettings['warning_mute'] : 110; $context['warning_limit'] = allowedTo('admin_forum') ? 0 : $modSettings['user_limit']; $context['member']['warning'] = $cur_profile['warning']; $context['member']['name'] = $cur_profile['real_name']; // What are the limits we can apply? $context['min_allowed'] = 0; $context['max_allowed'] = 100; if ($context['warning_limit'] > 0) { require_once SUBSDIR . '/Moderation.subs.php'; $current_applied = warningDailyLimit($memID); $context['min_allowed'] = max(0, $cur_profile['warning'] - $current_applied - $context['warning_limit']); $context['max_allowed'] = min(100, $cur_profile['warning'] - $current_applied + $context['warning_limit']); } // Defaults. $context['warning_data'] = array('reason' => '', 'notify' => '', 'notify_subject' => '', 'notify_body' => ''); // Are we saving? if (isset($_POST['save'])) { // Security is good here. checkSession('post'); // This cannot be empty! $_POST['warn_reason'] = isset($_POST['warn_reason']) ? trim($_POST['warn_reason']) : ''; if ($_POST['warn_reason'] == '' && !$context['user']['is_owner']) { $issueErrors[] = 'warning_no_reason'; } $_POST['warn_reason'] = Util::htmlspecialchars($_POST['warn_reason']); // If the value hasn't changed it's either no JS or a real no change (Which this will pass) if ($_POST['warning_level'] == 'SAME') { $_POST['warning_level'] = $_POST['warning_level_nojs']; } $_POST['warning_level'] = (int) $_POST['warning_level']; $_POST['warning_level'] = max(0, min(100, $_POST['warning_level'])); if ($_POST['warning_level'] < $context['min_allowed']) { $_POST['warning_level'] = $context['min_allowed']; } elseif ($_POST['warning_level'] > $context['max_allowed']) { $_POST['warning_level'] = $context['max_allowed']; } require_once SUBSDIR . '/Moderation.subs.php'; // Do we actually have to issue them with a PM? $id_notice = 0; if (!empty($_POST['warn_notify']) && empty($issueErrors)) { $_POST['warn_sub'] = trim($_POST['warn_sub']); $_POST['warn_body'] = trim($_POST['warn_body']); if (empty($_POST['warn_sub']) || empty($_POST['warn_body'])) { $issueErrors[] = 'warning_notify_blank'; } else { require_once SUBSDIR . '/PersonalMessage.subs.php'; $from = array('id' => 0, 'name' => $context['forum_name'], 'username' => $context['forum_name']); sendpm(array('to' => array($memID), 'bcc' => array()), $_POST['warn_sub'], $_POST['warn_body'], false, $from); // Log the notice. $id_notice = logWarningNotice($_POST['warn_sub'], $_POST['warn_body']); } } // Just in case - make sure notice is valid! $id_notice = (int) $id_notice; // What have we changed? $level_change = $_POST['warning_level'] - $cur_profile['warning']; // No errors? Proceed! Only log if you're not the owner. if (empty($issueErrors)) { // Log what we've done! if (!$context['user']['is_owner']) { logWarning($memID, $cur_profile['real_name'], $id_notice, $level_change, $_POST['warn_reason']); } // Make the change. updateMemberData($memID, array('warning' => $_POST['warning_level'])); // Leave a lovely message. $context['profile_updated'] = $context['user']['is_owner'] ? $txt['profile_updated_own'] : $txt['profile_warning_success']; } else { // Try to remember some bits. $context['warning_data'] = array('reason' => $_POST['warn_reason'], 'notify' => !empty($_POST['warn_notify']), 'notify_subject' => isset($_POST['warn_sub']) ? $_POST['warn_sub'] : '', 'notify_body' => isset($_POST['warn_body']) ? $_POST['warn_body'] : ''); } // Show the new improved warning level. $context['member']['warning'] = $_POST['warning_level']; } // Taking a look first, good idea that one. if (isset($_POST['preview'])) { $warning_body = !empty($_POST['warn_body']) ? trim(censorText($_POST['warn_body'])) : ''; $context['preview_subject'] = !empty($_POST['warn_sub']) ? trim(Util::htmlspecialchars($_POST['warn_sub'])) : ''; if (empty($_POST['warn_sub']) || empty($_POST['warn_body'])) { $issueErrors[] = 'warning_notify_blank'; } if (!empty($_POST['warn_body'])) { require_once SUBSDIR . '/Post.subs.php'; preparsecode($warning_body); $warning_body = parse_bbc($warning_body, true); } // Try to remember some bits. $context['warning_data'] = array('reason' => $_POST['warn_reason'], 'notify' => !empty($_POST['warn_notify']), 'notify_subject' => isset($_POST['warn_sub']) ? $_POST['warn_sub'] : '', 'notify_body' => isset($_POST['warn_body']) ? $_POST['warn_body'] : '', 'body_preview' => $warning_body); } if (!empty($issueErrors)) { // Fill in the suite of errors. $context['post_errors'] = array(); foreach ($issueErrors as $error) { $context['post_errors'][] = $txt[$error]; } } $context['page_title'] = $txt['profile_issue_warning']; // Let's use a generic list to get all the current warnings require_once SUBSDIR . '/GenericList.class.php'; require_once SUBSDIR . '/Profile.subs.php'; // Work our the various levels. $context['level_effects'] = array(0 => $txt['profile_warning_effect_none'], $modSettings['warning_watch'] => $txt['profile_warning_effect_watch'], $modSettings['warning_moderate'] => $txt['profile_warning_effect_moderation'], $modSettings['warning_mute'] => $txt['profile_warning_effect_mute']); $context['current_level'] = 0; foreach ($context['level_effects'] as $limit => $dummy) { if ($context['member']['warning'] >= $limit) { $context['current_level'] = $limit; } } // Build a list to view the warnings $listOptions = array('id' => 'issued_warnings', 'title' => $txt['profile_viewwarning_previous_warnings'], 'items_per_page' => $modSettings['defaultMaxMessages'], 'no_items_label' => $txt['profile_viewwarning_no_warnings'], 'base_href' => $scripturl . '?action=profile;area=issuewarning;sa=user;u=' . $memID, 'default_sort_col' => 'log_time', 'get_items' => array('function' => 'list_getUserWarnings', 'params' => array($memID)), 'get_count' => array('function' => 'list_getUserWarningCount', 'params' => array($memID)), 'columns' => array('issued_by' => array('header' => array('value' => $txt['profile_warning_previous_issued'], 'style' => 'width: 20%;'), 'data' => array('function' => create_function('$warning', ' return $warning[\'issuer\'][\'link\']; ')), 'sort' => array('default' => 'lc.member_name DESC', 'reverse' => 'lc.member_name')), 'log_time' => array('header' => array('value' => $txt['profile_warning_previous_time'], 'style' => 'width: 30%;'), 'data' => array('db' => 'time'), 'sort' => array('default' => 'lc.log_time DESC', 'reverse' => 'lc.log_time')), 'reason' => array('header' => array('value' => $txt['profile_warning_previous_reason']), 'data' => array('function' => create_function('$warning', ' global $scripturl, $txt, $settings; $ret = \' <div class="floatleft"> \' . $warning[\'reason\'] . \' </div>\'; // If a notice was sent, provide a way to view it if (!empty($warning[\'id_notice\'])) $ret .= \' <div class="floatright"> <a href="\' . $scripturl . \'?action=moderate;area=notice;nid=\' . $warning[\'id_notice\'] . \'" onclick="window.open(this.href, \\\'\\\', \\\'scrollbars=yes,resizable=yes,width=400,height=250\\\');return false;" target="_blank" class="new_win" title="\' . $txt[\'profile_warning_previous_notice\'] . \'"><img src="\' . $settings[\'images_url\'] . \'/filter.png" alt="" /></a> </div>\'; return $ret;'))), 'level' => array('header' => array('value' => $txt['profile_warning_previous_level'], 'style' => 'width: 6%;'), 'data' => array('db' => 'counter'), 'sort' => array('default' => 'lc.counter DESC', 'reverse' => 'lc.counter')))); // Create the list for viewing. createList($listOptions); $warning_for_message = isset($_REQUEST['msg']) ? (int) $_REQUEST['msg'] : false; $warned_message_subject = ''; // Are they warning because of a message? if (isset($_REQUEST['msg']) && 0 < (int) $_REQUEST['msg']) { require_once SUBSDIR . '/Messages.subs.php'; $message = basicMessageInfo((int) $_REQUEST['msg']); if (!empty($message)) { $warned_message_subject = $message['subject']; } } require_once SUBSDIR . '/Maillist.subs.php'; // Any custom templates? $context['notification_templates'] = array(); $notification_templates = maillist_templates('warntpl'); foreach ($notification_templates as $row) { // If we're not warning for a message skip any that are. if (!$warning_for_message && strpos($row['body'], '{MESSAGE}') !== false) { continue; } $context['notification_templates'][] = array('title' => $row['title'], 'body' => $row['body']); } // Setup the "default" templates. foreach (array('spamming', 'offence', 'insulting') as $type) { $context['notification_templates'][] = array('title' => $txt['profile_warning_notify_title_' . $type], 'body' => sprintf($txt['profile_warning_notify_template_outline' . (!empty($warning_for_message) ? '_post' : '')], $txt['profile_warning_notify_for_' . $type])); } // Replace all the common variables in the templates. foreach ($context['notification_templates'] as $k => $name) { $context['notification_templates'][$k]['body'] = strtr($name['body'], array('{MEMBER}' => un_htmlspecialchars($context['member']['name']), '{MESSAGE}' => '[url=' . $scripturl . '?msg=' . $warning_for_message . ']' . un_htmlspecialchars($warned_message_subject) . '[/url]', '{SCRIPTURL}' => $scripturl, '{FORUMNAME}' => $mbname, '{REGARDS}' => replaceBasicActionUrl($txt['regards_team']))); } }
/** * Used when a temp FTP access is needed to package functions */ public function action_options() { global $txt, $context, $modSettings; if (isset($_POST['save'])) { checkSession('post'); updateSettings(array('package_server' => trim(Util::htmlspecialchars($_POST['pack_server'])), 'package_port' => trim(Util::htmlspecialchars($_POST['pack_port'])), 'package_username' => trim(Util::htmlspecialchars($_POST['pack_user'])), 'package_make_backups' => !empty($_POST['package_make_backups']), 'package_make_full_backups' => !empty($_POST['package_make_full_backups']))); redirectexit('action=admin;area=packages;sa=options'); } if (preg_match('~^/home\\d*/([^/]+?)/public_html~', $_SERVER['DOCUMENT_ROOT'], $match)) { $default_username = $match[1]; } else { $default_username = ''; } $context['page_title'] = $txt['package_settings']; $context['sub_template'] = 'install_options'; $context['package_ftp_server'] = isset($modSettings['package_server']) ? $modSettings['package_server'] : 'localhost'; $context['package_ftp_port'] = isset($modSettings['package_port']) ? $modSettings['package_port'] : '21'; $context['package_ftp_username'] = isset($modSettings['package_username']) ? $modSettings['package_username'] : $default_username; $context['package_make_backups'] = !empty($modSettings['package_make_backups']); $context['package_make_full_backups'] = !empty($modSettings['package_make_full_backups']); }
/** * Load a page by ID * * @param int|null $page_id * @param boolean $active * @param boolean $allowed * @param string $sort */ function sportal_get_pages($page_id = null, $active = false, $allowed = false, $sort = 'title') { global $context, $scripturl; static $cache; $db = database(); // If we already have the information, just return it $cache_name = implode(':', array($page_id, $active, $allowed)); if (isset($cache[$cache_name])) { $return = $cache[$cache_name]; } else { $query = array(); $parameters = array('sort' => $sort); // Page id or Page Namespace if (!empty($page_id) && is_int($page_id)) { $query[] = 'id_page = {int:page_id}'; $parameters['page_id'] = $page_id; } elseif (!empty($page_id)) { $query[] = 'namespace = {string:namespace}'; $parameters['namespace'] = Util::htmlspecialchars((string) $page_id, ENT_QUOTES); } // Use permissions? if (!empty($allowed)) { $query[] = sprintf($context['SPortal']['permissions']['query'], 'permissions'); } // Only active pages? if (!empty($active)) { $query[] = 'status = {int:status}'; $parameters['status'] = 1; } // Make the page request $request = $db->query('', ' SELECT id_page, namespace, title, body, type, permissions, views, style, status FROM {db_prefix}sp_pages' . (!empty($query) ? ' WHERE ' . implode(' AND ', $query) : '') . ' ORDER BY {raw:sort}', $parameters); $return = array(); while ($row = $db->fetch_assoc($request)) { $return[$row['id_page']] = array('id' => $row['id_page'], 'page_id' => $row['namespace'], 'title' => $row['title'], 'href' => $scripturl . '?page=' . $row['namespace'], 'link' => '<a href="' . $scripturl . '?page=' . $row['namespace'] . '">' . $row['title'] . '</a>', 'body' => $row['body'], 'type' => $row['type'], 'permissions' => $row['permissions'], 'views' => $row['views'], 'style' => $row['style'], 'status' => $row['status']); } $db->free_result($request); // Save this so we don't have to do it again $cache[$cache_name] = $return; } return !empty($page_id) ? current($return) : $return; }
/** * Edit some general settings related to the search function. * * - Called by ?action=admin;area=managesearch;sa=settings. * - Requires the admin_forum permission. * * @uses ManageSearch template, 'modify_settings' sub-template. */ public function action_searchSettings_display() { global $txt, $context, $scripturl, $modSettings; // Initialize the form $this->_initSearchSettingsForm(); $config_vars = $this->_searchSettings->settings(); // Perhaps the search method wants to add some settings? require_once SUBSDIR . '/Search.subs.php'; $searchAPI = findSearchAPI(); if (is_callable(array($searchAPI, 'searchSettings'))) { call_user_func_array($searchAPI->searchSettings, array(&$config_vars)); } $context['page_title'] = $txt['search_settings_title']; $context['sub_template'] = 'show_settings'; $context['search_engines'] = array(); if (!empty($modSettings['additional_search_engines'])) { $context['search_engines'] = unserialize($modSettings['additional_search_engines']); } for ($count = 0; $count < 3; $count++) { $context['search_engines'][] = array('name' => '', 'url' => '', 'separator' => ''); } // A form was submitted. if (isset($_REQUEST['save'])) { checkSession(); call_integration_hook('integrate_save_search_settings'); if (empty($_POST['search_results_per_page'])) { $_POST['search_results_per_page'] = !empty($modSettings['search_results_per_page']) ? $modSettings['search_results_per_page'] : $modSettings['defaultMaxMessages']; } $new_engines = array(); foreach ($_POST['engine_name'] as $id => $searchengine) { // If no url, forget it if (!empty($_POST['engine_url'][$id])) { $new_engines[] = array('name' => trim(Util::htmlspecialchars($searchengine, ENT_COMPAT)), 'url' => trim(Util::htmlspecialchars($_POST['engine_url'][$id], ENT_COMPAT)), 'separator' => trim(Util::htmlspecialchars(!empty($_POST['engine_separator'][$id]) ? $_POST['engine_separator'][$id] : '+', ENT_COMPAT))); } } updateSettings(array('additional_search_engines' => !empty($new_engines) ? serialize($new_engines) : '')); Settings_Form::save_db($config_vars); redirectexit('action=admin;area=managesearch;sa=settings;' . $context['session_var'] . '=' . $context['session_id']); } // Prep the template! $context['post_url'] = $scripturl . '?action=admin;area=managesearch;save;sa=settings'; $context['settings_title'] = $txt['search_settings_title']; // We need this for the in-line permissions createToken('admin-mp'); Settings_Form::prepare_db($config_vars); }
public function action_save() { global $context, $txt; require_once SUBSDIR . '/Post.subs.php'; if (empty($_POST['expire_alt'])) { $expire = strtotime($_POST['expire']); } else { // This is the case date-picker doesn't kick in and the format is still an unix timestamp if (is_numeric($_POST['expire_alt'])) { $expire = $_POST['expire_alt']; } else { $expire = strtotime($_POST['expire_alt']); } } $expire = (int) $expire; $id = isset($_REQUEST['idnotice']) ? (int) $_REQUEST['idnotice'] : 0; $body = isset($_REQUEST['body']) ? Util::htmlspecialchars($_REQUEST['body']) : ''; $class = isset($_REQUEST['class']) ? Util::htmlspecialchars($_REQUEST['class']) : 'success'; preparsecode($body); $groups = json_encode(array_map('intval', array_keys($_POST['default_groups_list']))); $positioning = array('element' => $this->validPositioning(isset($_REQUEST['positioning']) ? $_REQUEST['positioning'] : null), 'element_name' => isset($_REQUEST['element_name']) ? Util::htmlspecialchars($_REQUEST['element_name']) : '', 'position' => isset($_REQUEST['position']) ? (int) $_REQUEST['position'] : 0); require_once SUBSDIR . '/DismissibleNotices.class.php'; $notice = new Dismissible_Notices(); $new = $notice->save($id, $expire, $body, $class, $groups, $positioning); loadTemplate('Json'); $context['sub_template'] = 'send_json'; $context['json_data'] = array('id' => $new['id_notice'], 'added' => standardTime($new['added']), 'expire' => Dismissible_Notices_Integrate::formatExpireCol($expire), 'body' => un_htmlspecialchars($body), 'class' => $new['class'], 'groups' => $new['show_to'], 'edit' => '<a data-idnotice="' . $new['id_notice'] . '" class="dismissnotice_editable" href="#">' . $txt['modify'] . '</a>'); }
/** * Shows a form to edit a forum mailing and its recipients. * * What it does: * - Called by ?action=admin;area=news;sa=mailingcompose. * - Requires the send_mail permission. * - Form is submitted to ?action=admin;area=news;sa=mailingsend. * * @uses ManageNews template, email_members_compose sub-template. */ public function action_mailingcompose() { global $txt, $context; // Setup the template! $context['page_title'] = $txt['admin_newsletters']; $context['sub_template'] = 'email_members_compose'; $context['subject'] = !empty($_POST['subject']) ? $_POST['subject'] : $context['forum_name'] . ': ' . htmlspecialchars($txt['subject'], ENT_COMPAT, 'UTF-8'); $context['message'] = !empty($_POST['message']) ? $_POST['message'] : htmlspecialchars($txt['message'] . "\n\n" . replaceBasicActionUrl($txt['regards_team']) . "\n\n" . '{$board_url}', ENT_COMPAT, 'UTF-8'); // Needed for the WYSIWYG editor. require_once SUBSDIR . '/Editor.subs.php'; // Now create the editor. $editorOptions = array('id' => 'message', 'value' => $context['message'], 'height' => '250px', 'width' => '100%', 'labels' => array('post_button' => $txt['sendtopic_send']), 'preview_type' => 2); create_control_richedit($editorOptions); if (isset($context['preview'])) { require_once SUBSDIR . '/Mail.subs.php'; $context['recipients']['members'] = !empty($_POST['members']) ? explode(',', $_POST['members']) : array(); $context['recipients']['exclude_members'] = !empty($_POST['exclude_members']) ? explode(',', $_POST['exclude_members']) : array(); $context['recipients']['groups'] = !empty($_POST['groups']) ? explode(',', $_POST['groups']) : array(); $context['recipients']['exclude_groups'] = !empty($_POST['exclude_groups']) ? explode(',', $_POST['exclude_groups']) : array(); $context['recipients']['emails'] = !empty($_POST['emails']) ? explode(';', $_POST['emails']) : array(); $context['email_force'] = !empty($_POST['email_force']) ? 1 : 0; $context['total_emails'] = !empty($_POST['total_emails']) ? (int) $_POST['total_emails'] : 0; $context['max_id_member'] = !empty($_POST['max_id_member']) ? (int) $_POST['max_id_member'] : 0; $context['send_pm'] = !empty($_POST['send_pm']) ? 1 : 0; $context['send_html'] = !empty($_POST['send_html']) ? '1' : '0'; return prepareMailingForPreview(); } // Start by finding any members! $toClean = array(); if (!empty($_POST['members'])) { $toClean[] = 'members'; } if (!empty($_POST['exclude_members'])) { $toClean[] = 'exclude_members'; } if (!empty($toClean)) { require_once SUBSDIR . '/Auth.subs.php'; foreach ($toClean as $type) { // Remove the quotes. $_POST[$type] = strtr((string) $_POST[$type], array('\\"' => '"')); preg_match_all('~"([^"]+)"~', $_POST[$type], $matches); $_POST[$type] = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $_POST[$type])))); foreach ($_POST[$type] as $index => $member) { if (strlen(trim($member)) > 0) { $_POST[$type][$index] = Util::htmlspecialchars(Util::strtolower(trim($member))); } else { unset($_POST[$type][$index]); } } // Find the members $_POST[$type] = implode(',', array_keys(findMembers($_POST[$type]))); } } if (isset($_POST['member_list']) && is_array($_POST['member_list'])) { $members = array(); foreach ($_POST['member_list'] as $member_id) { $members[] = (int) $member_id; } $_POST['members'] = implode(',', $members); } if (isset($_POST['exclude_member_list']) && is_array($_POST['exclude_member_list'])) { $members = array(); foreach ($_POST['exclude_member_list'] as $member_id) { $members[] = (int) $member_id; } $_POST['exclude_members'] = implode(',', $members); } // Clean the other vars. $this->action_mailingsend(true); // We need a couple strings from the email template file loadLanguage('EmailTemplates'); require_once SUBSDIR . '/News.subs.php'; // Get a list of all full banned users. Use their Username and email to find them. // Only get the ones that can't login to turn off notification. $context['recipients']['exclude_members'] = excludeBannedMembers(); // Did they select moderators - if so add them as specific members... if (!empty($context['recipients']['groups']) && in_array(3, $context['recipients']['groups']) || !empty($context['recipients']['exclude_groups']) && in_array(3, $context['recipients']['exclude_groups'])) { $mods = getModerators(); foreach ($mods as $row) { if (in_array(3, $context['recipients'])) { $context['recipients']['exclude_members'][] = $row; } else { $context['recipients']['members'][] = $row; } } } require_once SUBSDIR . '/Members.subs.php'; // For progress bar! $context['total_emails'] = count($context['recipients']['emails']); $context['max_id_member'] = maxMemberID(); // Clean up the arrays. $context['recipients']['members'] = array_unique($context['recipients']['members']); $context['recipients']['exclude_members'] = array_unique($context['recipients']['exclude_members']); }
/** * Log an error then exit * * @param string $text */ function generateSubscriptionError($text) { global $modSettings, $notify_users; // Send an email? if (!empty($modSettings['paid_email'])) { $replacements = array('ERROR' => $text); emailAdmins('paid_subscription_error', $replacements, $notify_users); } // Maybe we can try to give them the post data? if (!empty($_POST)) { foreach ($_POST as $key => $val) { $text .= '<br />' . Util::htmlspecialchars($key) . ': ' . Util::htmlspecialchars($val); } } // Then just log and die. log_error($text); exit; }
/** * This function reads from the database the addons credits, * and returns them in an array for display in credits section of the site. * The addons copyright, license, title informations are those saved from <license> * and <credits> tags in package.xml. * * @return array */ function addonsCredits() { global $txt; $db = database(); if (($credits = cache_get_data('addons_credits', 86400)) === null) { $credits = array(); $request = $db->query('substring', ' SELECT version, name, credits FROM {db_prefix}log_packages WHERE install_state = {int:installed_adds} AND credits != {string:empty} AND SUBSTRING(filename, 1, 9) != {string:old_patch_name} AND SUBSTRING(filename, 1, 9) != {string:patch_name}', array('installed_adds' => 1, 'old_patch_name' => 'smf_patch', 'patch_name' => 'elk_patch', 'empty' => '')); while ($row = $db->fetch_assoc($request)) { $credit_info = unserialize($row['credits']); $copyright = empty($credit_info['copyright']) ? '' : $txt['credits_copyright'] . ' © ' . Util::htmlspecialchars($credit_info['copyright']); $license = empty($credit_info['license']) ? '' : $txt['credits_license'] . ': ' . Util::htmlspecialchars($credit_info['license']); $version = $txt['credits_version'] . '' . $row['version']; $title = (empty($credit_info['title']) ? $row['name'] : Util::htmlspecialchars($credit_info['title'])) . ': ' . $version; // build this one out and stash it away $name = empty($credit_info['url']) ? $title : '<a href="' . $credit_info['url'] . '">' . $title . '</a>'; $credits[] = $name . (!empty($license) ? ' | ' . $license : '') . (!empty($copyright) ? ' | ' . $copyright : ''); } cache_put_data('addons_credits', $credits, 86400); } return $credits; }
/** * Set the values for this split session */ private function _set_session_values() { global $txt; // Clean up the subject. if (isset($_POST['subname']) && empty($this->_new_topic_subject)) { $this->_new_topic_subject = trim(Util::htmlspecialchars($_POST['subname'])); } if (empty($this->_new_topic_subject)) { $this->_new_topic_subject = $txt['new_topic']; } // Save in session so its available across all the form pages if (empty($_SESSION['move_to_board'])) { $_SESSION['move_to_board'] = !empty($_POST['move_new_topic']) && !empty($_POST['move_to_board']) ? (int) $_POST['move_to_board'] : 0; $_SESSION['reason'] = !empty($_POST['reason']) ? trim(Util::htmlspecialchars($_POST['reason'], ENT_QUOTES)) : ''; $_SESSION['messageRedirect'] = !empty($_POST['messageRedirect']); $_SESSION['new_topic_subject'] = $this->_new_topic_subject; } }