/** * Search for a member - by real_name or member_name by default. * * @return string */ public function member() { global $user_info, $context; $search = trim(Util::strtolower($_REQUEST['search'])) . '*'; $search = strtr($search, array('%' => '\\%', '_' => '\\_', '*' => '%', '?' => '_', '&' => '&')); require_once SUBSDIR . '/Members.subs.php'; // Find the member. $xml_data = getMember($search, !empty($context['search_param']['buddies']) ? $user_info['buddies'] : array()); return $xml_data; }
/** * Shows all of the addon packs available on the package server */ function template_package_list() { global $context, $settings, $txt; echo ' <div id="admincenter"> <h2 class="category_header">' . $context['page_title'] . '</h2> <div class="content">'; // No packages, as yet. if (empty($context['package_list'])) { echo ' <ul> <li>', $txt['no_packages'], '</li> </ul>'; } else { echo ' <ul id="package_list">'; foreach ($context['package_list'] as $i => $packageSection) { echo ' <li> <span class="package_toggle"> <span id="ps_img_', $i, '" class="collapse" style="display: none;" title="', $txt['hide'], '"></span> </span> <a href="#" id="upshrink_link_', $i, '" class="highlight">', $packageSection['title'], '</a>'; if (!empty($packageSection['text'])) { echo ' <div class="content">', $packageSection['text'], '</div>'; } // List of addons available in this section echo ' <ol id="package_section_', $i, '" class="packages">'; $alt = false; foreach ($packageSection['items'] as $id => $package) { echo ' <li>'; // 1. Some addon [ Download ]. echo ' <strong> <img id="ps_img_', $i, '_pkg_', $id, '" src="', $settings['images_url'], '/selected_open.png" alt="*" style="display: none;" /> ', $package['can_install'] ? '<strong>' . $package['name'] . '</strong> <a class="linkbutton" href="' . $package['download']['href'] . '">' . $txt['download'] . '</a>' : $package['name']; // Mark as installed and current? if ($package['is_installed'] && !$package['is_newer']) { echo '<img src="', $settings['images_url'], '/icons/package_', $package['is_current'] ? 'installed' : 'old', '.png" class="centericon package_img" alt="', $package['is_current'] ? $txt['package_installed_current'] : $txt['package_installed_old'], '" />'; } echo ' </strong> <ul id="package_section_', $i, '_pkg_', $id, '" class="package_section">'; // Show the addon type? if ($package['type'] != '') { echo ' <li class="package_section">', $txt['package_type'], ': ', Util::ucwords(Util::strtolower($package['type'])), '</li>'; } // Show the version number? if ($package['version'] != '') { echo ' <li class="package_section">', $txt['mod_version'], ': ', $package['version'], '</li>'; } // Show the last date? if ($package['date'] != '') { echo ' <li class="package_section">', $txt['mod_date'], ': ', $package['date'], '</li>'; } // How 'bout the author? if (!empty($package['author'])) { echo ' <li class="package_section">', $txt['mod_author'], ': ', $package['author'], '</li>'; } // Nothing but hooks ? if ($package['hooks'] != '' && in_array($package['hooks'], array('yes', 'true'))) { echo ' <li class="package_section">', $txt['mod_hooks'], ' <i class="fa fa-check-circle-o"></i></li>'; } // Location of file: http://someplace/. echo ' <ul style="margin-left: 5em"> <li class="package_section"><i class="fa fa-cloud-download"></i> ', $txt['file_location'], ': <a href="', $package['server']['download'], '">', $package['server']['download'], '</a></li>'; // Location of issues? if (!empty($package['server']['bugs'])) { echo ' <li class="package_section"><i class="fa fa-bug"></i> ', $txt['bug_location'], ': <a href="', $package['server']['bugs'], '">', $package['server']['bugs'], '</a></li>'; } // Location of support? if (!empty($package['server']['support'])) { echo ' <li class="package_section"><i class="fa fa-support"></i> ', $txt['support_location'], ': <a href="', $package['server']['support'], '">', $package['server']['support'], '</a></li>'; } // Description: bleh bleh! echo ' </ul> <li class="package_section"><div class="infobox">', $txt['package_description'], ': ', $package['description'], '</div></li> </ul>'; $alt = !$alt; echo ' </li>'; } echo ' </ol> </li>'; } echo ' </ul>'; } echo ' </div> <div> ', $txt['package_installed_key'], ' <img src="', $settings['images_url'], '/icons/package_installed.png" alt="" class="centericon" style="margin-left: 1ex;" /> ', $txt['package_installed_current'], ' <img src="', $settings['images_url'], '/icons/package_old.png" alt="" class="centericon" style="margin-left: 2ex;" /> ', $txt['package_installed_old'], ' </div> </div>'; // Now go through and turn off / collapse all the sections. if (!empty($context['package_list'])) { echo ' <script><!-- // --><![CDATA['; foreach ($context['package_list'] as $section => $ps) { echo ' var oPackageServerToggle_', $section, ' = new elk_Toggle({ bToggleEnabled: true, bCurrentlyCollapsed: true, aSwappableContainers: [ \'package_section_', $section, '\' ], aSwapClasses: [ { sId: \'ps_img_', $section, '\', classExpanded: \'collapse\', titleExpanded: ', JavaScriptEscape($txt['hide']), ', classCollapsed: \'expand\', titleCollapsed: ', JavaScriptEscape($txt['show']), ' } ], aSwapLinks: [ { sId: \'upshrink_link_', $section, '\', msgExpanded: ', JavaScriptEscape($ps['title']), ', msgCollapsed: ', JavaScriptEscape($ps['title']), ' } ] });'; foreach ($ps['items'] as $id => $package) { echo ' var oPackageToggle_', $section, '_pkg_', $id, ' = new elk_Toggle({ bToggleEnabled: true, bCurrentlyCollapsed: true, aSwappableContainers: [ \'package_section_', $section, '_pkg_', $id, '\' ], aSwapImages: [ { sId: \'ps_img_', $section, '_pkg_', $id, '\', srcExpanded: elk_images_url + \'/selected.png\', altExpanded: \'*\', srcCollapsed: elk_images_url + \'/selected_open.png\', altCollapsed: \'*\' } ] });'; } } echo ' // ]]></script>'; } }
/** * 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; }
/** * Loads all the shouts for a given shoutbox * * @param int $shoutbox id of the shoutbox to get data from * @param mixed[] $parameters * * @return type */ function sportal_get_shouts($shoutbox, $parameters) { global $scripturl, $context, $user_info, $modSettings, $txt; $db = database(); // Set defaults or used what was passed $shoutbox = !empty($shoutbox) ? (int) $shoutbox : 0; $start = !empty($parameters['start']) ? (int) $parameters['start'] : 0; $limit = !empty($parameters['limit']) ? (int) $parameters['limit'] : 20; $bbc = !empty($parameters['bbc']) ? $parameters['bbc'] : array(); $reverse = !empty($parameters['reverse']); $cache = !empty($parameters['cache']); $can_delete = !empty($parameters['can_moderate']); // Cached, use it first if (!empty($start) || !$cache || ($shouts = cache_get_data('shoutbox_shouts-' . $shoutbox, 240)) === null) { $request = $db->query('', ' SELECT sh.id_shout, sh.body, IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, sh.member_name) AS member_name, sh.log_time, mg.online_color AS member_group_color, pg.online_color AS post_group_color FROM {db_prefix}sp_shouts AS sh LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = sh.id_member) LEFT JOIN {db_prefix}membergroups AS pg ON (pg.id_group = mem.id_post_group) LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = mem.id_group) WHERE sh.id_shoutbox = {int:id_shoutbox} ORDER BY sh.id_shout DESC LIMIT {int:start}, {int:limit}', array('id_shoutbox' => $shoutbox, 'start' => $start, 'limit' => $limit)); $shouts = array(); while ($row = $db->fetch_assoc($request)) { // Disable the aeva mod for the shoutbox. $context['aeva_disable'] = true; $online_color = !empty($row['member_group_color']) ? $row['member_group_color'] : $row['post_group_color']; $shouts[$row['id_shout']] = array('id' => $row['id_shout'], 'author' => array('id' => $row['id_member'], 'name' => $row['member_name'], 'link' => $row['id_member'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '" title="' . $txt['on'] . ' ' . strip_tags(standardTime($row['log_time'])) . '"' . (!empty($online_color) ? ' style="color: ' . $online_color . ';"' : '') . '>' . $row['member_name'] . '</a>' : $row['member_name'], 'color' => $online_color), 'time' => $row['log_time'], 'text' => parse_bbc($row['body'], true, '', $bbc)); } $db->free_result($request); if (empty($start) && $cache) { cache_put_data('shoutbox_shouts-' . $shoutbox, $shouts, 240); } } foreach ($shouts as $shout) { // Private shouts @username: only get shown to the shouter and shoutee, and the admin if (preg_match('~^@(.+?): ~u', $shout['text'], $target) && Util::strtolower($target[1]) !== Util::strtolower($user_info['name']) && $shout['author']['id'] != $user_info['id'] && !$user_info['is_admin']) { unset($shouts[$shout['id']]); continue; } $shouts[$shout['id']] += array('is_me' => preg_match('~^<div\\sclass="meaction">\\* ' . preg_quote($shout['author']['name'], '~') . '.+</div>$~', $shout['text']) != 0, 'delete_link' => $can_delete ? '<a class="dot dotdelete" href="' . $scripturl . '?action=shoutbox;shoutbox_id=' . $shoutbox . ';delete=' . $shout['id'] . ';' . $context['session_var'] . '=' . $context['session_id'] . '"></a> ' : '', 'delete_link_js' => $can_delete ? '<a class="dot dotdelete" href="' . $scripturl . '?action=shoutbox;shoutbox_id=' . $shoutbox . ';delete=' . $shout['id'] . ';' . $context['session_var'] . '=' . $context['session_id'] . '" onclick="sp_delete_shout(' . $shoutbox . ', ' . $shout['id'] . ', \'' . $context['session_var'] . '\', \'' . $context['session_id'] . '\'); return false;"></a> ' : ''); // Prepare for display in the box $shouts[$shout['id']]['time'] = standardTime($shouts[$shout['id']]['time']); $shouts[$shout['id']]['text'] = preg_replace('~(</?)div([^<]*>)~', '$1span$2', $shouts[$shout['id']]['text']); $shouts[$shout['id']]['text'] = preg_replace('~<a([^>]+>)([^<]+)</a>~', '<a$1' . $txt['sp_link'] . '</a>', $shouts[$shout['id']]['text']); $shouts[$shout['id']]['text'] = censorText($shouts[$shout['id']]['text']); // Ignored user, hide the shout with option to show it if (!empty($modSettings['enable_buddylist']) && in_array($shout['author']['id'], $context['user']['ignoreusers'])) { $shouts[$shout['id']]['text'] = '<a href="#toggle" id="ignored_shout_link_' . $shout['id'] . '" onclick="sp_show_ignored_shout(' . $shout['id'] . '); return false;">[' . $txt['sp_shoutbox_show_ignored'] . ']</a><span id="ignored_shout_' . $shout['id'] . '" style="display: none;">' . $shouts[$shout['id']]['text'] . '</span>'; } } if ($reverse) { $shouts = array_reverse($shouts); } return $shouts; }
/** * Reload a users settings. */ function profileReloadUser() { global $modSettings, $context, $cur_profile; // Log them back in - using the verify password as they must have matched and this one doesn't get changed by anyone! if (isset($_POST['passwrd2']) && $_POST['passwrd2'] != '') { require_once SUBSDIR . '/Auth.subs.php'; setLoginCookie(60 * $modSettings['cookieTime'], $context['id_member'], hash('sha256', Util::strtolower($cur_profile['member_name']) . un_htmlspecialchars($_POST['passwrd2']) . $cur_profile['password_salt'])); } loadUserSettings(); writeLog(); }
/** * Shows all of the addon packs available on the package server */ function template_package_list() { global $context, $settings, $txt; echo ' <div id="admincenter"> <h2 class="category_header">' . $context['page_title'] . '</h2> <div class="windowbg"> <div class="content">'; // No packages, as yet. if (empty($context['package_list'])) { echo ' <ul> <li>', $txt['no_packages'], '</li> </ul>'; } else { echo ' <ul id="package_list">'; foreach ($context['package_list'] as $i => $packageSection) { echo ' <li> <img id="ps_img_', $i, '" src="', $settings['images_url'], '/collapse.png" class="floatright" alt="*" style="display: none;" /><strong>', $packageSection['title'], '</strong>'; if (!empty($packageSection['text'])) { echo ' <div class="information">', $packageSection['text'], '</div>'; } echo ' <', $context['list_type'], ' id="package_section_', $i, '" class="packages">'; $alt = false; foreach ($packageSection['items'] as $id => $package) { echo ' <li>'; // Textual message. Could be empty just for a blank line... if ($package['is_text']) { echo ' ', empty($package['name']) ? ' ' : $package['name']; } elseif ($package['is_line']) { echo ' <hr />'; } elseif ($package['is_remote']) { echo ' <strong>', $package['link'], '</strong>'; } elseif ($package['is_heading'] || $package['is_title']) { echo ' <strong>', $package['name'], '</strong>'; } else { // 1. Some addon [ Download ]. echo ' <strong><img id="ps_img_', $i, '_pkg_', $id, '" src="', $settings['images_url'], '/selected_open.png" alt="*" style="display: none;" /> ', $package['can_install'] ? '<strong>' . $package['name'] . '</strong> <a href="' . $package['download']['href'] . '">[ ' . $txt['download'] . ' ]</a>' : $package['name']; // Mark as installed and current? if ($package['is_installed'] && !$package['is_newer']) { echo '<img src="', $settings['images_url'], '/icons/package_', $package['is_current'] ? 'installed' : 'old', '.png" class="centericon" style="width: 12px; height: 11px; margin-left: 2ex;" alt="', $package['is_current'] ? $txt['package_installed_current'] : $txt['package_installed_old'], '" />'; } echo ' </strong> <ul id="package_section_', $i, '_pkg_', $id, '" class="package_section">'; // Show the addon type? if ($package['type'] != '') { echo ' <li class="package_section">', $txt['package_type'], ': ', Util::ucwords(Util::strtolower($package['type'])), '</li>'; } // Show the version number? if ($package['version'] != '') { echo ' <li class="package_section">', $txt['mod_version'], ': ', $package['version'], '</li>'; } // How 'bout the author? if (!empty($package['author']) && $package['author']['name'] != '' && isset($package['author']['link'])) { echo ' <li class="package_section">', $txt['mod_author'], ': ', $package['author']['link'], '</li>'; } // The homepage.... if ($package['author']['website']['link'] != '') { echo ' <li class="package_section">', $txt['author_website'], ': ', $package['author']['website']['link'], '</li>'; } // Description: bleh bleh! // Location of file: http://someplace/. echo ' <li class="package_section">', $txt['file_location'], ': <a href="', $package['href'], '">', $package['href'], '</a></li> <li class="package_section"><div class="information">', $txt['package_description'], ': ', $package['description'], '</div></li> </ul>'; } $alt = !$alt; echo ' </li>'; } echo ' </', $context['list_type'], '> </li>'; } echo ' </ul>'; } echo ' </div> </div> <div> ', $txt['package_installed_key'], ' <img src="', $settings['images_url'], '/icons/package_installed.png" alt="" class="centericon" style="margin-left: 1ex;" /> ', $txt['package_installed_current'], ' <img src="', $settings['images_url'], '/icons/package_old.png" alt="" class="centericon" style="margin-left: 2ex;" /> ', $txt['package_installed_old'], ' </div> </div>'; // Now go through and turn off / collapse all the sections. if (!empty($context['package_list'])) { $section_count = count($context['package_list']); echo ' <script><!-- // --><![CDATA['; foreach ($context['package_list'] as $section => $ps) { echo ' var oPackageServerToggle_', $section, ' = new elk_Toggle({ bToggleEnabled: true, bCurrentlyCollapsed: ', count($ps['items']) == 1 || $section_count == 1 ? 'false' : 'true', ', aSwappableContainers: [ \'package_section_', $section, '\' ], aSwapImages: [ { sId: \'ps_img_', $section, '\', srcExpanded: elk_images_url + \'/collapse.png\', altExpanded: \'*\', srcCollapsed: elk_images_url + \'/expand.png\', altCollapsed: \'*\' } ] });'; foreach ($ps['items'] as $id => $package) { if (!$package['is_text'] && !$package['is_line'] && !$package['is_remote']) { echo ' var oPackageToggle_', $section, '_pkg_', $id, ' = new elk_Toggle({ bToggleEnabled: true, bCurrentlyCollapsed: true, aSwappableContainers: [ \'package_section_', $section, '_pkg_', $id, '\' ], aSwapImages: [ { sId: \'ps_img_', $section, '_pkg_', $id, '\', srcExpanded: elk_images_url + \'/selected.png\', altExpanded: \'*\', srcCollapsed: elk_images_url + \'/selected_open.png\', altCollapsed: \'*\' } ] });'; } } } echo ' // ]]></script>'; } }
/** * 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']); }
/** * Builds a theme-info.xml file for use when a new theme is installed by copying * an existing theme * * @param string $name * @param string $version * @param string $theme_dir * @param mixed[] $theme_values */ function write_theme_info($name, $version, $theme_dir, $theme_values) { $xml_info = '<' . '?xml version="1.0"?' . '> <theme-info xmlns="http://www.elkarte.net/xml/theme-info" xmlns:elk="http://www.elkarte.net/"> <!-- For the id, always use something unique - put your name, a colon, and then the package name. --> <id>elk:' . Util::strtolower(str_replace(array(' '), '_', $name)) . '</id> <version>' . $version . '</version> <!-- Theme name, used purely for aesthetics. --> <name>' . $name . '</name> <!-- Author: your email address or contact information. The name attribute is optional. --> <author name="Your Name">info@youremailaddress.tld</author> <!-- Website... where to get updates and more information. --> <website>http://www.yourdomain.tld/</website> <!-- Template layers to use, defaults to "html,body". --> <layers>' . (empty($theme_values['theme_layers']) ? 'html,body' : $theme_values['theme_layers']) . '</layers> <!-- Templates to load on startup. Default is "index". --> <templates>' . (empty($theme_values['theme_templates']) ? 'index' : $theme_values['theme_templates']) . '</templates> <!-- Base this theme off another? Default is blank, or no. It could be "default". --> <based-on></based-on> </theme-info>'; // Now write it. file_put_contents($theme_dir . '/theme_info.xml', $xml_info); }
/** * Checks whether an entered password is correct for the user * * What it does: * - called when logging in or whenever a password needs to be validated for a user * - used to generate a new hash for the db, used during registration or any password changes * - if a non SHA256 password is sent, will generate one with SHA256(user + password) and return it in password * * @package Authorization * @param string $password user password if not already 64 characters long will be SHA256 with the user name * @param string $hash hash as generated from a SHA256 password * @param string $user user name only required if creating a SHA-256 password * @param boolean $returnhash flag to determine if we are returning a hash suitable for the database */ function validateLoginPassword(&$password, $hash, $user = '', $returnhash = false) { // Our hashing controller require_once EXTDIR . '/PasswordHash.php'; // Base-2 logarithm of the iteration count used for password stretching, the // higher the number the more secure and CPU time consuming $hash_cost_log2 = 10; // Do we require the hashes to be portable to older systems (less secure)? $hash_portable = false; // Get an instance of the hasher $hasher = new PasswordHash($hash_cost_log2, $hash_portable); // Guilty until we know otherwise $passhash = false; // If the password is not 64 characters, lets make it a (SHA-256) if (strlen($password) !== 64) { $password = hash('sha256', Util::strtolower($user) . un_htmlspecialchars($password)); } // They need a password hash, something to save in the db? if ($returnhash) { $passhash = $hasher->HashPassword($password); // Something is not right, we can not generate a valid hash thats <20 characters if (strlen($passhash) < 20) { $passhash = false; } } else { $passhash = (bool) $hasher->CheckPassword($password, $hash); } unset($hasher); return $passhash; }
/** * Finds a member from the database using supplied string as real_name * * - Optionaly will only search/find the member in a buddy list * * @package Members * @param string $search string to search real_name for like finds * @param int[]|null $buddies */ function getMember($search, $buddies = array()) { $db = database(); // Find the member. $request = $db->query('', ' SELECT id_member, real_name FROM {db_prefix}members WHERE {raw:real_name} LIKE {string:search}' . (!empty($buddies) ? ' AND id_member IN ({array_int:buddy_list})' : '') . ' AND is_activated IN ({array_int:activation_status}) LIMIT {int:limit}', array('real_name' => defined('DB_CASE_SENSITIVE') ? 'LOWER(real_name)' : 'real_name', 'buddy_list' => $buddies, 'search' => Util::strtolower($search), 'activation_status' => array(1, 12), 'limit' => Util::strlen($search) <= 2 ? 100 : 800)); $xml_data = array('items' => array('identifier' => 'item', 'children' => array())); while ($row = $db->fetch_assoc($request)) { $row['real_name'] = strtr($row['real_name'], array('&' => '&', '<' => '<', '>' => '>', '"' => '"')); $xml_data['items']['children'][] = array('attributes' => array('id' => $row['id_member']), 'value' => $row['real_name']); } $db->free_result($request); return $xml_data; }
/** * Spell checks the post for typos ;). * It uses the pspell library, which MUST be installed. * It has problems with internationalization. * It is accessed via ?action=spellcheck. */ public function action_spellcheck() { global $txt, $context; // A list of "words" we know about but pspell doesn't. $known_words = array('elkarte', 'php', 'mysql', 'www', 'gif', 'jpeg', 'png', 'http'); loadLanguage('Post'); loadTemplate('Post'); // Okay, this looks funny, but it actually fixes a weird bug. ob_start(); $old = error_reporting(0); // See, first, some windows machines don't load pspell properly on the first try. Dumb, but this is a workaround. pspell_new('en'); // Next, the dictionary in question may not exist. So, we try it... but... $pspell_link = pspell_new($txt['lang_dictionary'], $txt['lang_spelling'], '', 'utf-8', PSPELL_FAST | PSPELL_RUN_TOGETHER); // Most people don't have anything but English installed... So we use English as a last resort. if (!$pspell_link) { $pspell_link = pspell_new('en', '', '', '', PSPELL_FAST | PSPELL_RUN_TOGETHER); } error_reporting($old); @ob_end_clean(); if (!isset($_POST['spellstring']) || !$pspell_link) { die; } // Construct a bit of Javascript code. $context['spell_js'] = ' var txt = {"done": "' . $txt['spellcheck_done'] . '"}, mispstr = ' . ($_POST['fulleditor'] === 'true' ? 'window.opener.spellCheckGetText(spell_fieldname)' : 'window.opener.document.forms[spell_formname][spell_fieldname].value') . ', misps = Array('; // Get all the words (Javascript already separated them). $alphas = explode("\n", strtr($_POST['spellstring'], array("\r" => ''))); $found_words = false; for ($i = 0, $n = count($alphas); $i < $n; $i++) { // Words are sent like 'word|offset_begin|offset_end'. $check_word = explode('|', $alphas[$i]); // If the word is a known word, or spelled right... if (in_array(Util::strtolower($check_word[0]), $known_words) || pspell_check($pspell_link, $check_word[0]) || !isset($check_word[2])) { continue; } // Find the word, and move up the "last occurance" to here. $found_words = true; // Add on the javascript for this misspelling. $context['spell_js'] .= ' new misp("' . strtr($check_word[0], array('\\' => '\\\\', '"' => '\\"', '<' => '', '>' => '')) . '", ' . (int) $check_word[1] . ', ' . (int) $check_word[2] . ', ['; // If there are suggestions, add them in... $suggestions = pspell_suggest($pspell_link, $check_word[0]); if (!empty($suggestions)) { // But first check they aren't going to be censored - no naughty words! foreach ($suggestions as $k => $word) { if ($suggestions[$k] != censorText($word)) { unset($suggestions[$k]); } } if (!empty($suggestions)) { $context['spell_js'] .= '"' . implode('", "', $suggestions) . '"'; } } $context['spell_js'] .= ']),'; } // If words were found, take off the last comma. if ($found_words) { $context['spell_js'] = substr($context['spell_js'], 0, -1); } $context['spell_js'] .= ' );'; // And instruct the template system to just show the spellcheck sub template. Template_Layers::getInstance()->removeAll(); $context['sub_template'] = 'spellcheck'; }
/** * Actually do the search of personal messages and show the results * * What it does: * - accessed with ?action=pm;sa=search2 * - checks user input and searches the pm table for messages matching the query. * - uses the search_results sub template of the PersonalMessage template. * - show the results of the search query. */ public function action_search2() { global $scripturl, $modSettings, $context, $txt, $memberContext; $db = database(); // Make sure the server is able to do this right now if (!empty($modSettings['loadavg_search']) && $modSettings['current_load'] >= $modSettings['loadavg_search']) { fatal_lang_error('loadavg_search_disabled', false); } // Some useful general permissions. $context['can_send_pm'] = allowedTo('pm_send'); // Some hardcoded variables that can be tweaked if required. $maxMembersToSearch = 500; // Extract all the search parameters. $search_params = array(); if (isset($_REQUEST['params'])) { $temp_params = explode('|"|', base64_decode(strtr($_REQUEST['params'], array(' ' => '+')))); foreach ($temp_params as $i => $data) { @(list($k, $v) = explode('|\'|', $data)); $search_params[$k] = $v; } } $context['start'] = isset($_GET['start']) ? (int) $_GET['start'] : 0; // Store whether simple search was used (needed if the user wants to do another query). if (!isset($search_params['advanced'])) { $search_params['advanced'] = empty($_REQUEST['advanced']) ? 0 : 1; } // 1 => 'allwords' (default, don't set as param) / 2 => 'anywords'. if (!empty($search_params['searchtype']) || !empty($_REQUEST['searchtype']) && $_REQUEST['searchtype'] == 2) { $search_params['searchtype'] = 2; } // Minimum age of messages. Default to zero (don't set param in that case). if (!empty($search_params['minage']) || !empty($_REQUEST['minage']) && $_REQUEST['minage'] > 0) { $search_params['minage'] = !empty($search_params['minage']) ? (int) $search_params['minage'] : (int) $_REQUEST['minage']; } // Maximum age of messages. Default to infinite (9999 days: param not set). if (!empty($search_params['maxage']) || !empty($_REQUEST['maxage']) && $_REQUEST['maxage'] < 9999) { $search_params['maxage'] = !empty($search_params['maxage']) ? (int) $search_params['maxage'] : (int) $_REQUEST['maxage']; } // Search modifiers $search_params['subject_only'] = !empty($search_params['subject_only']) || !empty($_REQUEST['subject_only']); $search_params['show_complete'] = !empty($search_params['show_complete']) || !empty($_REQUEST['show_complete']); $search_params['sent_only'] = !empty($search_params['sent_only']) || !empty($_REQUEST['sent_only']); $context['folder'] = empty($search_params['sent_only']) ? 'inbox' : 'sent'; // Default the user name to a wildcard matching every user (*). if (!empty($search_params['userspec']) || !empty($_REQUEST['userspec']) && $_REQUEST['userspec'] != '*') { $search_params['userspec'] = isset($search_params['userspec']) ? $search_params['userspec'] : $_REQUEST['userspec']; } // This will be full of all kinds of parameters! $searchq_parameters = array(); // If there's no specific user, then don't mention it in the main query. if (empty($search_params['userspec'])) { $userQuery = ''; } else { // Set up so we can seach by user name, wildcards, like, etc $userString = strtr(Util::htmlspecialchars($search_params['userspec'], ENT_QUOTES), array('"' => '"')); $userString = strtr($userString, array('%' => '\\%', '_' => '\\_', '*' => '%', '?' => '_')); preg_match_all('~"([^"]+)"~', $userString, $matches); $possible_users = array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $userString))); require_once SUBSDIR . '/Members.subs.php'; // Who matches those criteria? $members = membersBy('member_names', array('member_names' => $possible_users)); foreach ($possible_users as $key => $possible_user) { $searchq_parameters['guest_user_name_implode_' . $key] = defined('DB_CASE_SENSITIVE') ? strtolower($possible_user) : $possible_user; } // Simply do nothing if there are too many members matching the criteria. if (count($members) > $maxMembersToSearch) { $userQuery = ''; } elseif (count($members) == 0) { if ($context['folder'] === 'inbox') { $uq = array(); $name = defined('DB_CASE_SENSITIVE') ? 'LOWER(pm.from_name)' : 'pm.from_name'; foreach (array_keys($possible_users) as $key) { $uq[] = 'AND pm.id_member_from = 0 AND (' . $name . ' LIKE {string:guest_user_name_implode_' . $key . '})'; } $userQuery = implode(' ', $uq); $searchq_parameters['pm_from_name'] = defined('DB_CASE_SENSITIVE') ? 'LOWER(pm.from_name)' : 'pm.from_name'; } else { $userQuery = ''; } } else { $memberlist = array(); foreach ($members as $id) { $memberlist[] = $id; } // Use the name as as sent from or sent to if ($context['folder'] === 'inbox') { $uq = array(); $name = defined('DB_CASE_SENSITIVE') ? 'LOWER(pm.from_name)' : 'pm.from_name'; foreach (array_keys($possible_users) as $key) { $uq[] = 'AND (pm.id_member_from IN ({array_int:member_list}) OR (pm.id_member_from = 0 AND (' . $name . ' LIKE {string:guest_user_name_implode_' . $key . '})))'; } $userQuery = implode(' ', $uq); } else { $userQuery = 'AND (pmr.id_member IN ({array_int:member_list}))'; } $searchq_parameters['pm_from_name'] = defined('DB_CASE_SENSITIVE') ? 'LOWER(pm.from_name)' : 'pm.from_name'; $searchq_parameters['member_list'] = $memberlist; } } // Setup the sorting variables... $sort_columns = array('pm.id_pm'); if (empty($search_params['sort']) && !empty($_REQUEST['sort'])) { list($search_params['sort'], $search_params['sort_dir']) = array_pad(explode('|', $_REQUEST['sort']), 2, ''); } $search_params['sort'] = !empty($search_params['sort']) && in_array($search_params['sort'], $sort_columns) ? $search_params['sort'] : 'pm.id_pm'; $search_params['sort_dir'] = !empty($search_params['sort_dir']) && $search_params['sort_dir'] == 'asc' ? 'asc' : 'desc'; // Sort out any labels we may be searching by. $labelQuery = ''; if ($context['folder'] == 'inbox' && !empty($search_params['advanced']) && $context['currently_using_labels']) { // Came here from pagination? Put them back into $_REQUEST for sanitization. if (isset($search_params['labels'])) { $_REQUEST['searchlabel'] = explode(',', $search_params['labels']); } // Assuming we have some labels - make them all integers. if (!empty($_REQUEST['searchlabel']) && is_array($_REQUEST['searchlabel'])) { $_REQUEST['searchlabel'] = array_map('intval', $_REQUEST['searchlabel']); } else { $_REQUEST['searchlabel'] = array(); } // Now that everything is cleaned up a bit, make the labels a param. $search_params['labels'] = implode(',', $_REQUEST['searchlabel']); // No labels selected? That must be an error! if (empty($_REQUEST['searchlabel'])) { $context['search_errors']['no_labels_selected'] = true; } elseif (count($_REQUEST['searchlabel']) != count($context['labels'])) { $labelQuery = ' AND {raw:label_implode}'; $labelStatements = array(); foreach ($_REQUEST['searchlabel'] as $label) { $labelStatements[] = $db->quote('FIND_IN_SET({string:label}, pmr.labels) != 0', array('label' => $label)); } $searchq_parameters['label_implode'] = '(' . implode(' OR ', $labelStatements) . ')'; } } // Unfortunately, searching for words like this is going to be slow, so we're blacklisting them. $blacklisted_words = array('quote', 'the', 'is', 'it', 'are', 'if'); // What are we actually searching for? $search_params['search'] = !empty($search_params['search']) ? $search_params['search'] : (isset($_REQUEST['search']) ? $_REQUEST['search'] : ''); // If nothing is left to search on - we set an error! if (!isset($search_params['search']) || $search_params['search'] == '') { $context['search_errors']['invalid_search_string'] = true; } // Change non-word characters into spaces. $stripped_query = preg_replace('~(?:[\\x0B\\0\\x{A0}\\t\\r\\s\\n(){}\\[\\]<>!@$%^*.,:+=`\\~\\?/\\\\]+|&(?:amp|lt|gt|quot);)+~u', ' ', $search_params['search']); // Make the query lower case since it will case insensitive anyway. $stripped_query = un_htmlspecialchars(Util::strtolower($stripped_query)); // Extract phrase parts first (e.g. some words "this is a phrase" some more words.) preg_match_all('/(?:^|\\s)([-]?)"([^"]+)"(?:$|\\s)/', $stripped_query, $matches, PREG_PATTERN_ORDER); $phraseArray = $matches[2]; // Remove the phrase parts and extract the words. $wordArray = preg_replace('~(?:^|\\s)(?:[-]?)"(?:[^"]+)"(?:$|\\s)~u', ' ', $search_params['search']); $wordArray = explode(' ', Util::htmlspecialchars(un_htmlspecialchars($wordArray), ENT_QUOTES)); // A minus sign in front of a word excludes the word.... so... $excludedWords = array(); // Check for things like -"some words", but not "-some words". foreach ($matches[1] as $index => $word) { if ($word === '-') { if (($word = trim($phraseArray[$index], '-_\' ')) !== '' && !in_array($word, $blacklisted_words)) { $excludedWords[] = $word; } unset($phraseArray[$index]); } } // Now we look for -test, etc foreach ($wordArray as $index => $word) { if (strpos(trim($word), '-') === 0) { if (($word = trim($word, '-_\' ')) !== '' && !in_array($word, $blacklisted_words)) { $excludedWords[] = $word; } unset($wordArray[$index]); } } // The remaining words and phrases are all included. $searchArray = array_merge($phraseArray, $wordArray); // Trim everything and make sure there are no words that are the same. foreach ($searchArray as $index => $value) { // Skip anything thats close to empty. if (($searchArray[$index] = trim($value, '-_\' ')) === '') { unset($searchArray[$index]); } elseif (in_array($searchArray[$index], $blacklisted_words)) { $foundBlackListedWords = true; unset($searchArray[$index]); } $searchArray[$index] = Util::strtolower(trim($value)); if ($searchArray[$index] == '') { unset($searchArray[$index]); } else { // Sort out entities first. $searchArray[$index] = Util::htmlspecialchars($searchArray[$index]); } } $searchArray = array_slice(array_unique($searchArray), 0, 10); // Create an array of replacements for highlighting. $context['mark'] = array(); foreach ($searchArray as $word) { $context['mark'][$word] = '<strong class="highlight">' . $word . '</strong>'; } // This contains *everything* $searchWords = array_merge($searchArray, $excludedWords); // Make sure at least one word is being searched for. if (empty($searchArray)) { $context['search_errors']['invalid_search_string' . (!empty($foundBlackListedWords) ? '_blacklist' : '')] = true; } // Sort out the search query so the user can edit it - if they want. $context['search_params'] = $search_params; if (isset($context['search_params']['search'])) { $context['search_params']['search'] = Util::htmlspecialchars($context['search_params']['search']); } if (isset($context['search_params']['userspec'])) { $context['search_params']['userspec'] = Util::htmlspecialchars($context['search_params']['userspec']); } // Now we have all the parameters, combine them together for pagination and the like... $context['params'] = array(); foreach ($search_params as $k => $v) { $context['params'][] = $k . '|\'|' . $v; } $context['params'] = base64_encode(implode('|"|', $context['params'])); // Compile the subject query part. $andQueryParts = array(); foreach ($searchWords as $index => $word) { if ($word == '') { continue; } if ($search_params['subject_only']) { $andQueryParts[] = 'pm.subject' . (in_array($word, $excludedWords) ? ' NOT' : '') . ' LIKE {string:search_' . $index . '}'; } else { $andQueryParts[] = '(pm.subject' . (in_array($word, $excludedWords) ? ' NOT' : '') . ' LIKE {string:search_' . $index . '} ' . (in_array($word, $excludedWords) ? 'AND pm.body NOT' : 'OR pm.body') . ' LIKE {string:search_' . $index . '})'; } $searchq_parameters['search_' . $index] = '%' . strtr($word, array('_' => '\\_', '%' => '\\%')) . '%'; } $searchQuery = ' 1=1'; if (!empty($andQueryParts)) { $searchQuery = implode(!empty($search_params['searchtype']) && $search_params['searchtype'] == 2 ? ' OR ' : ' AND ', $andQueryParts); } // Age limits? $timeQuery = ''; if (!empty($search_params['minage'])) { $timeQuery .= ' AND pm.msgtime < ' . (time() - $search_params['minage'] * 86400); } if (!empty($search_params['maxage'])) { $timeQuery .= ' AND pm.msgtime > ' . (time() - $search_params['maxage'] * 86400); } // If we have errors - return back to the first screen... if (!empty($context['search_errors'])) { $_REQUEST['params'] = $context['params']; return $this->action_search(); } // Get the number of results. $numResults = numPMSeachResults($userQuery, $labelQuery, $timeQuery, $searchQuery, $searchq_parameters); // Get all the matching message ids, senders and head pm nodes list($foundMessages, $posters, $head_pms) = loadPMSearchMessages($userQuery, $labelQuery, $timeQuery, $searchQuery, $searchq_parameters, $search_params); // Find the real head pm when in converstaion view if ($context['display_mode'] == 2 && !empty($head_pms)) { $real_pm_ids = loadPMSearchHeads($head_pms); } // Load the found user data $posters = array_unique($posters); if (!empty($posters)) { loadMemberData($posters); } // Sort out the page index. $context['page_index'] = constructPageIndex($scripturl . '?action=pm;sa=search2;params=' . $context['params'], $_GET['start'], $numResults, $modSettings['search_results_per_page'], false); $context['message_labels'] = array(); $context['message_replied'] = array(); $context['personal_messages'] = array(); $context['first_label'] = array(); // If we have results, we have work to do! if (!empty($foundMessages)) { $recipients = array(); list($context['message_labels'], $context['message_replied'], $context['message_unread'], $context['first_label']) = loadPMRecipientInfo($foundMessages, $recipients, $context['folder'], true); // Prepare for the callback! $search_results = loadPMSearchResults($foundMessages, $search_params); $counter = 0; foreach ($search_results as $row) { // If there's no subject, use the default. $row['subject'] = $row['subject'] == '' ? $txt['no_subject'] : $row['subject']; // Load this posters context info, if its not there then fill in the essentials... if (!loadMemberContext($row['id_member_from'], true)) { $memberContext[$row['id_member_from']]['name'] = $row['from_name']; $memberContext[$row['id_member_from']]['id'] = 0; $memberContext[$row['id_member_from']]['group'] = $txt['guest_title']; $memberContext[$row['id_member_from']]['link'] = $row['from_name']; $memberContext[$row['id_member_from']]['email'] = ''; $memberContext[$row['id_member_from']]['show_email'] = showEmailAddress(true, 0); $memberContext[$row['id_member_from']]['is_guest'] = true; } // Censor anything we don't want to see... censorText($row['body']); censorText($row['subject']); // Parse out any BBC... $row['body'] = parse_bbc($row['body'], true, 'pm' . $row['id_pm']); // Highlight the hits $body_highlighted = ''; $subject_highlighted = ''; foreach ($searchArray as $query) { // Fix the international characters in the keyword too. $query = un_htmlspecialchars($query); $query = trim($query, '\\*+'); $query = strtr(Util::htmlspecialchars($query), array('\\\'' => '\'')); $body_highlighted = preg_replace_callback('/((<[^>]*)|' . preg_quote(strtr($query, array('\'' => ''')), '/') . ')/iu', array($this, '_highlighted_callback'), $row['body']); $subject_highlighted = preg_replace('/(' . preg_quote($query, '/') . ')/iu', '<strong class="highlight">$1</strong>', $row['subject']); } // Set a link using the first label information $href = $scripturl . '?action=pm;f=' . $context['folder'] . (isset($context['first_label'][$row['id_pm']]) ? ';l=' . $context['first_label'][$row['id_pm']] : '') . ';pmid=' . ($context['display_mode'] == 2 && isset($real_pm_ids[$head_pms[$row['id_pm']]]) && $context['folder'] == 'inbox' ? $real_pm_ids[$head_pms[$row['id_pm']]] : $row['id_pm']) . '#msg_' . $row['id_pm']; $context['personal_messages'][] = array('id' => $row['id_pm'], 'member' => &$memberContext[$row['id_member_from']], 'subject' => $subject_highlighted, 'body' => $body_highlighted, 'time' => standardTime($row['msgtime']), 'html_time' => htmlTime($row['msgtime']), 'timestamp' => forum_time(true, $row['msgtime']), 'recipients' => &$recipients[$row['id_pm']], 'labels' => &$context['message_labels'][$row['id_pm']], 'fully_labeled' => count($context['message_labels'][$row['id_pm']]) == count($context['labels']), 'is_replied_to' => &$context['message_replied'][$row['id_pm']], 'href' => $href, 'link' => '<a href="' . $href . '">' . $subject_highlighted . '</a>', 'counter' => ++$counter); } } // Finish off the context. $context['page_title'] = $txt['pm_search_title']; $context['sub_template'] = 'search_results'; $context['menu_data_' . $context['pm_menu_id']]['current_area'] = 'search'; $context['linktree'][] = array('url' => $scripturl . '?action=pm;sa=search', 'name' => $txt['pm_search_bar_title']); }
public function action_register2() { global $txt, $modSettings, $context, $user_info; // Start collecting together any errors. $reg_errors = Error_Context::context('register', 0); // Check they are who they should be checkSession(); if (!validateToken('register', 'post', true, false)) { $reg_errors->addError('token_verification'); } // You can't register if it's disabled. if (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 3) { fatal_lang_error('registration_disabled', false); } // Well, if you don't agree, you can't register. if (!empty($modSettings['requireAgreement']) && !isset($_POST['checkbox_agreement'])) { $reg_errors->addError('agreement_unchecked'); } // Make sure they came from *somewhere*, have a session. if (!isset($_SESSION['old_url'])) { redirectexit('action=register'); } // Check their provider deatils match up correctly in case they're pulling something funny if ($_POST['provider'] != $_SESSION['extauth_info']['provider']) { redirectexit('action=register'); } // Clean up foreach ($_POST as $key => $value) { if (!is_array($_POST[$key])) { $_POST[$key] = htmltrim__recursive(str_replace(array("\n", "\r"), '', $_POST[$key])); } } // Needed for isReservedName() and registerMember() require_once SUBSDIR . '/Members.subs.php'; // Needed for generateValidationCode() require_once SUBSDIR . '/Auth.subs.php'; // Set the options needed for registration. $regOptions = array('interface' => 'guest', 'username' => !empty($_POST['user']) ? $_POST['user'] : '', 'email' => !empty($_POST['email']) ? $_POST['email'] : '', 'check_reserved_name' => true, 'check_password_strength' => true, 'check_email_ban' => true, 'send_welcome_email' => !empty($modSettings['send_welcomeEmail']), 'require' => empty($modSettings['registration_method']) ? 'nothing' : ($modSettings['registration_method'] == 1 ? 'activation' : 'approval')); // Lets check for other errors before trying to register the member. if ($reg_errors->hasErrors()) { return $this->action_register(); } mt_srand(time() + 1277); $regOptions['password'] = generateValidationCode(); $regOptions['password_check'] = $regOptions['password']; // 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'); } } // One last error check if ($reg_errors->hasErrors()) { return $this->action_register(); } // Do our spam protection now. spamProtection('register'); // Since all is well, we'll go ahead and associate the member's external account addAuth($memberID, $_SESSION['extauth_info']['provider'], $_SESSION['extauth_info']['uid'], $_SESSION['extauth_info']['name']); // Basic template variable setup. if (!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']); } }
/** * Chops a string into words and prepares them to be inserted into (or searched from) the database. * * @param string $text * @param int|null $max_chars = 20 * - if encrypt = true this is the maximum number of bytes to use in integer hashes (for searching) * - if encrypt = false this is the maximum number of letters in each word * @param bool $encrypt = false Used for custom search indexes to return an array of ints representing the words */ function text2words($text, $max_chars = 20, $encrypt = false) { // Step 1: Remove entities/things we don't consider words: $words = preg_replace('~(?:[\\x0B\\0\\x{A0}\\t\\r\\s\\n(){}\\[\\]<>!@$%^*.,:+=`\\~\\?/\\\\]+|&(?:amp|lt|gt|quot);)+~u', ' ', strtr($text, array('<br />' => ' '))); // Step 2: Entities we left to letters, where applicable, lowercase. $words = un_htmlspecialchars(Util::strtolower($words)); // Step 3: Ready to split apart and index! $words = explode(' ', $words); if ($encrypt) { // Range of characters that crypt will produce (0-9, a-z, A-Z .) $possible_chars = array_flip(array_merge(range(46, 57), range(65, 90), range(97, 122))); $returned_ints = array(); foreach ($words as $word) { if (($word = trim($word, '-_\'')) !== '') { // Get a crypt representation of this work $encrypted = substr(crypt($word, 'uk'), 2, $max_chars); $total = 0; // Create an integer reprsentation for ($i = 0; $i < $max_chars; $i++) { $total += $possible_chars[ord($encrypted[$i])] * pow(63, $i); } // Return the value $returned_ints[] = $max_chars == 4 ? min($total, 16777215) : $total; } } return array_unique($returned_ints); } else { // Trim characters before and after and add slashes for database insertion. $returned_words = array(); foreach ($words as $word) { if (($word = trim($word, '-_\'')) !== '') { $returned_words[] = $max_chars === null ? $word : substr($word, 0, $max_chars); } } // Filter out all words that occur more than once. return array_unique($returned_words); } }
/** * Display members of a group, and allow adding of members to a group. * * What it does: * - It can be called from ManageMembergroups if it needs templating within the admin environment. * - It shows a list of members that are part of a given membergroup. * - It is called by ?action=moderate;area=viewgroups;sa=members;group=x * - It requires the manage_membergroups permission. * - It allows to add and remove members from the selected membergroup. * - It allows sorting on several columns. * - It redirects to itself. * @uses ManageMembergroups template, group_members sub template. */ public function action_members() { global $txt, $scripturl, $context, $modSettings, $user_info, $settings; $current_group = isset($_REQUEST['group']) ? (int) $_REQUEST['group'] : 0; // These will be needed require_once SUBSDIR . '/Membergroups.subs.php'; require_once SUBSDIR . '/Members.subs.php'; // Load up the group details. $context['group'] = membergroupById($current_group, true, true); // No browsing of guests, membergroup 0 or moderators or non-existing groups. if ($context['group'] === false || in_array($current_group, array(-1, 0, 3))) { fatal_lang_error('membergroup_does_not_exist', false); } $context['group']['id'] = $context['group']['id_group']; $context['group']['name'] = $context['group']['group_name']; // Fix the membergroup icons. $context['group']['icons'] = explode('#', $context['group']['icons']); $context['group']['icons'] = !empty($context['group']['icons'][0]) && !empty($context['group']['icons'][1]) ? str_repeat('<img src="' . $settings['images_url'] . '/group_icons/' . $context['group']['icons'][1] . '" alt="*" />', $context['group']['icons'][0]) : ''; $context['group']['can_moderate'] = allowedTo('manage_membergroups') && (allowedTo('admin_forum') || $context['group']['group_type'] != 1); // The template is very needy $context['linktree'][] = array('url' => $scripturl . '?action=groups;sa=members;group=' . $context['group']['id'], 'name' => $context['group']['name']); $context['can_send_email'] = allowedTo('send_email_to_members'); $context['sort_direction'] = isset($_REQUEST['desc']) ? 'down' : 'up'; $context['start'] = $_REQUEST['start']; $context['can_moderate_forum'] = allowedTo('moderate_forum'); // @todo: use createList // Load all the group moderators, for fun. $context['group']['moderators'] = array(); $moderators = getGroupModerators($current_group); foreach ($moderators as $id_member => $name) { $context['group']['moderators'][] = array('id' => $id_member, 'name' => $name); if ($user_info['id'] == $id_member && $context['group']['group_type'] != 1) { $context['group']['can_moderate'] = true; } } // If this group is hidden then it can only "exist" if the user can moderate it! if ($context['group']['hidden'] && !$context['group']['can_moderate']) { fatal_lang_error('membergroup_does_not_exist', false); } // You can only assign membership if you are the moderator and/or can manage groups! if (!$context['group']['can_moderate']) { $context['group']['assignable'] = 0; } elseif ($context['group']['id'] == 1 && !allowedTo('admin_forum')) { $context['group']['assignable'] = 0; } // Removing member from group? if (isset($_POST['remove']) && !empty($_REQUEST['rem']) && is_array($_REQUEST['rem']) && $context['group']['assignable']) { // Security first checkSession(); validateToken('mod-mgm'); // Make sure we're dealing with integers only. foreach ($_REQUEST['rem'] as $key => $group) { $_REQUEST['rem'][$key] = (int) $group; } removeMembersFromGroups($_REQUEST['rem'], $current_group, true); } elseif (isset($_REQUEST['add']) && (!empty($_REQUEST['toAdd']) || !empty($_REQUEST['member_add'])) && $context['group']['assignable']) { // Make sure you can do this checkSession(); validateToken('mod-mgm'); $member_query = array(array('and' => 'not_in_group')); $member_parameters = array('not_in_group' => $current_group); // Get all the members to be added... taking into account names can be quoted ;) $_REQUEST['toAdd'] = strtr(Util::htmlspecialchars($_REQUEST['toAdd'], ENT_QUOTES), array('"' => '"')); preg_match_all('~"([^"]+)"~', $_REQUEST['toAdd'], $matches); $member_names = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $_REQUEST['toAdd'])))); foreach ($member_names as $index => $member_name) { $member_names[$index] = trim(Util::strtolower($member_names[$index])); if (strlen($member_names[$index]) == 0) { unset($member_names[$index]); } } // Any members passed by ID? $member_ids = array(); if (!empty($_REQUEST['member_add'])) { foreach ($_REQUEST['member_add'] as $id) { if ($id > 0) { $member_ids[] = (int) $id; } } } // Construct the query pelements, first for adds by name if (!empty($member_ids)) { $member_query[] = array('or' => 'member_ids'); $member_parameters['member_ids'] = $member_ids; } // And then adds by ID if (!empty($member_names)) { $member_query[] = array('or' => 'member_names'); $member_parameters['member_names'] = $member_names; } // Get back the ones that were not already in the group $members = membersBy($member_query, $member_parameters); // Do the updates... if (!empty($members)) { addMembersToGroup($members, $current_group, $context['group']['hidden'] ? 'only_additional' : 'auto', true); } } // Sort out the sorting! $sort_methods = array('name' => 'real_name', 'email' => allowedTo('moderate_forum') ? 'email_address' : 'hide_email ' . (isset($_REQUEST['desc']) ? 'DESC' : 'ASC') . ', email_address', 'active' => 'last_login', 'registered' => 'date_registered', 'posts' => 'posts'); // They didn't pick one, or tried a wrong one, so default to by name.. if (!isset($_REQUEST['sort']) || !isset($sort_methods[$_REQUEST['sort']])) { $context['sort_by'] = 'name'; $querySort = 'real_name' . (isset($_REQUEST['desc']) ? ' DESC' : ' ASC'); } else { $context['sort_by'] = $_REQUEST['sort']; $querySort = $sort_methods[$_REQUEST['sort']] . (isset($_REQUEST['desc']) ? ' DESC' : ' ASC'); } // The where on the query is interesting. Non-moderators should only see people who are in this group as primary. if ($context['group']['can_moderate']) { $where = $context['group']['is_post_group'] ? 'in_post_group' : 'in_group'; } else { $where = $context['group']['is_post_group'] ? 'in_post_group' : 'in_group_no_add'; } // Count members of the group. $context['total_members'] = countMembersBy($where, array($where => $current_group)); $context['total_members'] = comma_format($context['total_members']); // Create the page index. $context['page_index'] = constructPageIndex($scripturl . '?action=' . ($context['group']['can_moderate'] ? 'moderate;area=viewgroups' : 'groups') . ';sa=members;group=' . $current_group . ';sort=' . $context['sort_by'] . (isset($_REQUEST['desc']) ? ';desc' : ''), $_REQUEST['start'], $context['total_members'], $modSettings['defaultMaxMembers']); // Fetch the members that meet the where criteria $context['members'] = membersBy($where, array($where => $current_group, 'order' => $querySort), true); foreach ($context['members'] as $id => $row) { $last_online = empty($row['last_login']) ? $txt['never'] : standardTime($row['last_login']); // Italicize the online note if they aren't activated. if ($row['is_activated'] % 10 != 1) { $last_online = '<em title="' . $txt['not_activated'] . '">' . $last_online . '</em>'; } $context['members'][$id] = array('id' => $row['id_member'], 'name' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>', 'email' => $row['email_address'], 'show_email' => showEmailAddress(!empty($row['hide_email']), $row['id_member']), 'ip' => '<a href="' . $scripturl . '?action=trackip;searchip=' . $row['member_ip'] . '">' . $row['member_ip'] . '</a>', 'registered' => standardTime($row['date_registered']), 'last_online' => $last_online, 'posts' => comma_format($row['posts']), 'is_activated' => $row['is_activated'] % 10 == 1); } if (!empty($context['group']['assignable'])) { loadJavascriptFile('suggest.js', array('defer' => true)); } // Select the template. $context['sub_template'] = 'group_members'; $context['page_title'] = $txt['membergroups_members_title'] . ': ' . $context['group']['name']; createToken('mod-mgm'); }
function text2words($text) { // Step 1: Remove entities/things we don't consider words: $words = preg_replace('~(?:[\\x0B\\0\\x{A0}\\t\\r\\s\\n(){}\\[\\]<>!@$%^*.,:+=`\\~\\?/\\\\]+|&(?:amp|lt|gt|quot);)+~u', ' ', strtr($text, array('<br />' => ' '))); // Step 2: Entities we left to letters, where applicable, lowercase. $words = un_htmlspecialchars(Util::strtolower($words)); // Step 3: Ready to split apart and index! $words = explode(' ', $words); // Trim characters before and after and add slashes for database insertion. $returned_words = array(); foreach ($words as $word) { if (($word = trim($word, '-_\'')) !== '') { $returned_words[] = $max_chars === null ? $word : substr($word, 0, $max_chars); } } // Filter out all words that occur more than once. return array_unique($returned_words); }
/** * 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']); } }
/** * List all members, page by page, with sorting. * Called from MemberList(). * Can be passed a sort parameter, to order the display of members. * Calls printMemberListRows to retrieve the results of the query. */ public function action_mlall() { global $txt, $scripturl, $modSettings, $context; // The chunk size for the cached index. $cache_step_size = 500; require_once SUBSDIR . '/Memberlist.subs.php'; // Only use caching if: // 1. there are at least 2k members, // 2. the default sorting method (real_name) is being used, // 3. the page shown is high enough to make a DB filesort unprofitable. $use_cache = $modSettings['totalMembers'] > 2000 && (!isset($_REQUEST['sort']) || $_REQUEST['sort'] === 'real_name') && isset($_REQUEST['start']) && $_REQUEST['start'] > $cache_step_size; if ($use_cache) { // Maybe there's something cached already. if (!empty($modSettings['memberlist_cache'])) { $memberlist_cache = @unserialize($modSettings['memberlist_cache']); } // The chunk size for the cached index. $cache_step_size = 500; // Only update the cache if something changed or no cache existed yet. if (empty($memberlist_cache) || empty($modSettings['memberlist_updated']) || $memberlist_cache['last_update'] < $modSettings['memberlist_updated']) { $memberlist_cache = ml_memberCache($cache_step_size); } $context['num_members'] = $memberlist_cache['num_members']; } else { $context['num_members'] = ml_memberCount(); } // Set defaults for sort (real_name) and start. (0) if (!isset($_REQUEST['sort']) || !isset($context['columns'][$_REQUEST['sort']])) { $_REQUEST['sort'] = 'real_name'; } if (!is_numeric($_REQUEST['start'])) { if (preg_match('~^[^\'\\\\/]~u', Util::strtolower($_REQUEST['start']), $match) === 0) { fatal_error('Hacker?', false); } $_REQUEST['start'] = ml_alphaStart($match[0]); } // Build out the letter selection link bar $context['letter_links'] = ''; for ($i = 97; $i < 123; $i++) { $context['letter_links'] .= '<a href="' . $scripturl . '?action=memberlist;sa=all;start=' . chr($i) . '#letter' . chr($i) . '">' . chr($i - 32) . '</a> '; } // Sort out the column information. foreach ($context['columns'] as $col => $column_details) { $context['columns'][$col]['href'] = $scripturl . '?action=memberlist;sort=' . $col . ';start=0'; if (!isset($_REQUEST['desc']) && $col == $_REQUEST['sort'] || $col != $_REQUEST['sort'] && !empty($column_details['default_sort_rev'])) { $context['columns'][$col]['href'] .= ';desc'; } $context['columns'][$col]['link'] = '<a href="' . $context['columns'][$col]['href'] . '" rel="nofollow">' . $context['columns'][$col]['label'] . '</a>'; $context['columns'][$col]['selected'] = $_REQUEST['sort'] == $col; if ($context['columns'][$col]['selected']) { $context['columns'][$col]['class'] .= ' selected'; } } // Are we sorting the results $context['sort_by'] = $_REQUEST['sort']; $context['sort_direction'] = !isset($_REQUEST['desc']) ? 'up' : 'down'; // Construct the page index. $context['page_index'] = constructPageIndex($scripturl . '?action=memberlist;sort=' . $_REQUEST['sort'] . (isset($_REQUEST['desc']) ? ';desc' : ''), $_REQUEST['start'], $context['num_members'], $modSettings['defaultMaxMembers']); // Send the data to the template. $context['start'] = $_REQUEST['start'] + 1; $context['end'] = min($_REQUEST['start'] + $modSettings['defaultMaxMembers'], $context['num_members']); $context['can_moderate_forum'] = allowedTo('moderate_forum'); $context['page_title'] = sprintf($txt['viewing_members'], $context['start'], $context['end']); $context['linktree'][] = array('url' => $scripturl . '?action=memberlist;sort=' . $_REQUEST['sort'] . ';start=' . $_REQUEST['start'], 'name' => &$context['page_title'], 'extra_after' => ' (' . sprintf($txt['of_total_members'], $context['num_members']) . ')'); $limit = $_REQUEST['start']; $where = ''; $query_parameters = array('regular_id_group' => 0, 'is_activated' => 1, 'sort' => $context['columns'][$_REQUEST['sort']]['sort'][$context['sort_direction']]); // Using cache allows to narrow down the list to be retrieved. if ($use_cache && $_REQUEST['sort'] === 'real_name' && !isset($_REQUEST['desc'])) { $first_offset = $_REQUEST['start'] - $_REQUEST['start'] % $cache_step_size; $second_offset = ceil(($_REQUEST['start'] + $modSettings['defaultMaxMembers']) / $cache_step_size) * $cache_step_size; $where = 'mem.real_name BETWEEN {string:real_name_low} AND {string:real_name_high}'; $query_parameters['real_name_low'] = $memberlist_cache['index'][$first_offset]; $query_parameters['real_name_high'] = $memberlist_cache['index'][$second_offset]; $limit -= $first_offset; } elseif ($use_cache && $_REQUEST['sort'] === 'real_name') { $first_offset = floor(($memberlist_cache['num_members'] - $modSettings['defaultMaxMembers'] - $_REQUEST['start']) / $cache_step_size) * $cache_step_size; if ($first_offset < 0) { $first_offset = 0; } $second_offset = ceil(($memberlist_cache['num_members'] - $_REQUEST['start']) / $cache_step_size) * $cache_step_size; $where = 'mem.real_name BETWEEN {string:real_name_low} AND {string:real_name_high}'; $query_parameters['real_name_low'] = $memberlist_cache['index'][$first_offset]; $query_parameters['real_name_high'] = $memberlist_cache['index'][$second_offset]; $limit = $second_offset - ($memberlist_cache['num_members'] - $_REQUEST['start']) - ($second_offset > $memberlist_cache['num_members'] ? $cache_step_size - $memberlist_cache['num_members'] % $cache_step_size : 0); } // Add custom fields parameters too. if (!empty($context['custom_profile_fields']['parameters'])) { $query_parameters += $context['custom_profile_fields']['parameters']; } // Select the members from the database. ml_selectMembers($query_parameters, $where, $limit, $_REQUEST['sort']); // Add anchors at the start of each letter. if ($_REQUEST['sort'] === 'real_name') { $last_letter = ''; foreach ($context['members'] as $i => $dummy) { $this_letter = Util::strtolower(Util::substr($context['members'][$i]['name'], 0, 1)); if ($this_letter != $last_letter && preg_match('~[a-z]~', $this_letter) === 1) { $context['members'][$i]['sort_letter'] = htmlspecialchars($this_letter, ENT_COMPAT, 'UTF-8'); $last_letter = $this_letter; } } } }
/** * 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); }
/** * Setup spellchecking suggestions and load them into $context * * @param string[] $search_params the search parameters * @param string[] $searchArray an array of terms */ private function _load_suggestions($search_params, $searchArray = array()) { global $txt, $context; // Windows fix. ob_start(); $old = error_reporting(0); pspell_new('en'); $pspell_link = pspell_new($txt['lang_dictionary'], $txt['lang_spelling'], '', 'utf-8', PSPELL_FAST | PSPELL_RUN_TOGETHER); if (!$pspell_link) { $pspell_link = pspell_new('en', '', '', '', PSPELL_FAST | PSPELL_RUN_TOGETHER); } error_reporting($old); @ob_end_clean(); $did_you_mean = array('search' => array(), 'display' => array()); $found_misspelling = false; foreach ($searchArray as $word) { if (empty($pspell_link)) { continue; } // Don't check phrases. if (preg_match('~^\\w+$~', $word) === 0) { $did_you_mean['search'][] = '"' . $word . '"'; $did_you_mean['display'][] = '"' . Util::htmlspecialchars($word) . '"'; continue; } elseif (preg_match('~\\d~', $word) === 1) { $did_you_mean['search'][] = $word; $did_you_mean['display'][] = Util::htmlspecialchars($word); continue; } elseif (pspell_check($pspell_link, $word)) { $did_you_mean['search'][] = $word; $did_you_mean['display'][] = Util::htmlspecialchars($word); continue; } $suggestions = pspell_suggest($pspell_link, $word); foreach ($suggestions as $i => $s) { // Search is case insensitive. if (Util::strtolower($s) == Util::strtolower($word)) { unset($suggestions[$i]); } elseif ($suggestions[$i] != censorText($s)) { unset($suggestions[$i]); } } // Anything found? If so, correct it! if (!empty($suggestions)) { $suggestions = array_values($suggestions); $did_you_mean['search'][] = $suggestions[0]; $did_you_mean['display'][] = '<em><strong>' . Util::htmlspecialchars($suggestions[0]) . '</strong></em>'; $found_misspelling = true; } else { $did_you_mean['search'][] = $word; $did_you_mean['display'][] = Util::htmlspecialchars($word); } } if ($found_misspelling) { // Don't spell check excluded words, but add them still... $temp_excluded = array('search' => array(), 'display' => array()); if (!empty($excludedWords)) { foreach ($excludedWords as $word) { if (preg_match('~^\\w+$~', $word) == 0) { $temp_excluded['search'][] = '-"' . $word . '"'; $temp_excluded['display'][] = '-"' . Util::htmlspecialchars($word) . '"'; } else { $temp_excluded['search'][] = '-' . $word; $temp_excluded['display'][] = '-' . Util::htmlspecialchars($word); } } } $did_you_mean['search'] = array_merge($did_you_mean['search'], $temp_excluded['search']); $did_you_mean['display'] = array_merge($did_you_mean['display'], $temp_excluded['display']); $search_params['search'] = implode(' ', $did_you_mean['search']); if (isset($search_params['brd'])) { $search_params['brd'] = implode(',', $search_params['brd']); } $context['params'] = array(); foreach ($search_params as $k => $v) { $context['did_you_mean_params'][] = $k . '|\'|' . $v; } $context['did_you_mean_params'] = base64_encode(implode('|"|', $context['did_you_mean_params'])); $context['did_you_mean'] = implode(' ', $did_you_mean['display']); } }
/** * Cleans a string of everything but alphanumeric characters * * @param string $string A string to clean * @return string A cleaned up string */ private function _cleanString($string) { // Decode the entities first $string = html_entity_decode($string, ENT_QUOTES, 'UTF-8'); // Lowercase string $string = Util::strtolower($string); // Fix numbers so they search easier (phone numbers, SSN, dates, etc) $string = preg_replace('~([[:digit:]]+)\\pP+(?=[[:digit:]])~u', '', $string); // Last but not least, strip everything out that's not alphanumeric $string = preg_replace('~[^\\pL\\pN]+~u', ' ', $string); return $string; }
/** * Gets a list of available languages from the mother ship * * - Will return a subset if searching, otherwise all available * * @package Languages * @return string */ function list_getLanguagesList() { global $forum_version, $context, $txt, $scripturl; // We're going to use this URL. // @todo no we are not, this needs to be changed - again $url = 'http://download.elkarte.net/fetch_language.php?version=' . urlencode(strtr($forum_version, array('ElkArte ' => ''))); // Load the class file and stick it into an array. require_once SUBSDIR . '/XmlArray.class.php'; $language_list = new Xml_Array(fetch_web_data($url), true); // Check that the site responded and that the language exists. if (!$language_list->exists('languages')) { $context['langfile_error'] = 'no_response'; } elseif (!$language_list->exists('languages/language')) { $context['langfile_error'] = 'no_files'; } else { $language_list = $language_list->path('languages[0]'); $lang_files = $language_list->set('language'); $languages = array(); foreach ($lang_files as $file) { // Were we searching? if (!empty($context['elk_search_term']) && strpos($file->fetch('name'), Util::strtolower($context['elk_search_term'])) === false) { continue; } $languages[] = array('id' => $file->fetch('id'), 'name' => Util::ucwords($file->fetch('name')), 'version' => $file->fetch('version'), 'utf8' => $txt['yes'], 'description' => $file->fetch('description'), 'install_link' => '<a href="' . $scripturl . '?action=admin;area=languages;sa=downloadlang;did=' . $file->fetch('id') . ';' . $context['session_var'] . '=' . $context['session_id'] . '">' . $txt['add_language_elk_install'] . '</a>'); } if (empty($languages)) { $context['langfile_error'] = 'no_files'; } else { return $languages; } } }