/** * Actually do the search of personal messages. */ function MessageSearch2() { global $scripturl, $modSettings, $user_info, $context, $txt; global $memberContext, $smcFunc; if (!empty($context['load_average']) && !empty($modSettings['loadavg_search']) && $context['load_average'] >= $modSettings['loadavg_search']) { fatal_lang_error('loadavg_search_disabled', false); } /** * @todo For the moment force the folder to the inbox. * @todo Maybe set the inbox based on a cookie or theme setting? */ $context['folder'] = 'inbox'; // Some useful general permissions. $context['can_send_pm'] = allowedTo('pm_send'); // Some hardcoded veriables 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_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']); // Default the user name to a wildcard matching every user (*). if (!empty($search_params['user_spec']) || !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 { $userString = strtr($smcFunc['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))); for ($k = 0, $n = count($possible_users); $k < $n; $k++) { $possible_users[$k] = trim($possible_users[$k]); if (strlen($possible_users[$k]) == 0) { unset($possible_users[$k]); } } // Who matches those criteria? // @todo This doesn't support sent item searching. $request = $smcFunc['db_query']('', ' SELECT id_member FROM {db_prefix}members WHERE real_name LIKE {raw:real_name_implode}', array('real_name_implode' => '\'' . implode('\' OR real_name LIKE \'', $possible_users) . '\'')); // Simply do nothing if there're too many members matching the criteria. if ($smcFunc['db_num_rows']($request) > $maxMembersToSearch) { $userQuery = ''; } elseif ($smcFunc['db_num_rows']($request) == 0) { $userQuery = 'AND pm.id_member_from = 0 AND (pm.from_name LIKE {raw:guest_user_name_implode})'; $searchq_parameters['guest_user_name_implode'] = '\'' . implode('\' OR pm.from_name LIKE \'', $possible_users) . '\''; } else { $memberlist = array(); while ($row = $smcFunc['db_fetch_assoc']($request)) { $memberlist[] = $row['id_member']; } $userQuery = 'AND (pm.id_member_from IN ({array_int:member_list}) OR (pm.id_member_from = 0 AND (pm.from_name LIKE {raw:guest_user_name_implode})))'; $searchq_parameters['guest_user_name_implode'] = '\'' . implode('\' OR pm.from_name LIKE \'', $possible_users) . '\''; $searchq_parameters['member_list'] = $memberlist; } $smcFunc['db_free_result']($request); } // Setup the sorting variables... // @todo Add more in here! $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'])) { foreach ($_REQUEST['searchlabel'] as $key => $id) { $_REQUEST['searchlabel'][$key] = (int) $id; } } 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[] = $smcFunc['db_quote']('FIND_IN_SET({string:label}, pmr.labels) != 0', array('label' => $label)); } $searchq_parameters['label_implode'] = '(' . implode(' OR ', $labelStatements) . ')'; } } // What are we actually searching for? $search_params['search'] = !empty($search_params['search']) ? $search_params['search'] : (isset($_REQUEST['search']) ? $_REQUEST['search'] : ''); // If we ain't got nothing - we should error! if (!isset($search_params['search']) || $search_params['search'] == '') { $context['search_errors']['invalid_search_string'] = true; } // Extract phrase parts first (e.g. some words "this is a phrase" some more words.) preg_match_all('~(?:^|\\s)([-]?)"([^"]+)"(?:$|\\s)~' . ($context['utf8'] ? 'u' : ''), $search_params['search'], $matches, PREG_PATTERN_ORDER); $searchArray = $matches[2]; // Remove the phrase parts and extract the words. $tempSearch = explode(' ', preg_replace('~(?:^|\\s)(?:[-]?)"(?:[^"]+)"(?:$|\\s)~' . ($context['utf8'] ? 'u' : ''), ' ', $search_params['search'])); // A minus sign in front of a word excludes the word.... so... $excludedWords = array(); // .. first, we check for things like -"some words", but not "-some words". foreach ($matches[1] as $index => $word) { if ($word == '-') { $word = $smcFunc['strtolower'](trim($searchArray[$index])); if (strlen($word) > 0) { $excludedWords[] = $word; } unset($searchArray[$index]); } } // Now we look for -test, etc.... normaller. foreach ($tempSearch as $index => $word) { if (strpos(trim($word), '-') === 0) { $word = substr($smcFunc['strtolower']($word), 1); if (strlen($word) > 0) { $excludedWords[] = $word; } unset($tempSearch[$index]); } } $searchArray = array_merge($searchArray, $tempSearch); // Trim everything and make sure there are no words that are the same. foreach ($searchArray as $index => $value) { $searchArray[$index] = $smcFunc['strtolower'](trim($value)); if ($searchArray[$index] == '') { unset($searchArray[$index]); } else { // Sort out entities first. $searchArray[$index] = $smcFunc['htmlspecialchars']($searchArray[$index]); } } $searchArray = array_unique($searchArray); // 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'] = 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'] = htmlspecialchars($context['search_params']['search']); } if (isset($context['search_params']['userspec'])) { $context['search_params']['userspec'] = 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 MessageSearch(); } // Get the amount of results. $request = $smcFunc['db_query']('', ' SELECT COUNT(*) FROM {db_prefix}pm_recipients AS pmr INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm) WHERE ' . ($context['folder'] == 'inbox' ? ' pmr.id_member = {int:current_member} AND pmr.deleted = {int:not_deleted}' : ' pm.id_member_from = {int:current_member} AND pm.deleted_by_sender = {int:not_deleted}') . ' ' . $userQuery . $labelQuery . $timeQuery . ' AND (' . $searchQuery . ')', array_merge($searchq_parameters, array('current_member' => $user_info['id'], 'not_deleted' => 0))); list($numResults) = $smcFunc['db_fetch_row']($request); $smcFunc['db_free_result']($request); // Get all the matching messages... using standard search only (No caching and the like!) // @todo This doesn't support sent item searching yet. $request = $smcFunc['db_query']('', ' SELECT pm.id_pm, pm.id_pm_head, pm.id_member_from FROM {db_prefix}pm_recipients AS pmr INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm) WHERE ' . ($context['folder'] == 'inbox' ? ' pmr.id_member = {int:current_member} AND pmr.deleted = {int:not_deleted}' : ' pm.id_member_from = {int:current_member} AND pm.deleted_by_sender = {int:not_deleted}') . ' ' . $userQuery . $labelQuery . $timeQuery . ' AND (' . $searchQuery . ') ORDER BY ' . $search_params['sort'] . ' ' . $search_params['sort_dir'] . ' LIMIT ' . $context['start'] . ', ' . $modSettings['search_results_per_page'], array_merge($searchq_parameters, array('current_member' => $user_info['id'], 'not_deleted' => 0))); $foundMessages = array(); $posters = array(); $head_pms = array(); while ($row = $smcFunc['db_fetch_assoc']($request)) { $foundMessages[] = $row['id_pm']; $posters[] = $row['id_member_from']; $head_pms[$row['id_pm']] = $row['id_pm_head']; } $smcFunc['db_free_result']($request); // Find the real head pms! if ($context['display_mode'] == 2 && !empty($head_pms)) { $request = $smcFunc['db_query']('', ' SELECT MAX(pm.id_pm) AS id_pm, pm.id_pm_head FROM {db_prefix}personal_messages AS pm INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm) WHERE pm.id_pm_head IN ({array_int:head_pms}) AND pmr.id_member = {int:current_member} AND pmr.deleted = {int:not_deleted} GROUP BY pm.id_pm_head LIMIT {int:limit}', array('head_pms' => array_unique($head_pms), 'current_member' => $user_info['id'], 'not_deleted' => 0, 'limit' => count($head_pms))); $real_pm_ids = array(); while ($row = $smcFunc['db_fetch_assoc']($request)) { $real_pm_ids[$row['id_pm_head']] = $row['id_pm']; } $smcFunc['db_free_result']($request); } // Load the users... $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(); if (!empty($foundMessages)) { // Now get recipients (but don't include bcc-recipients for your inbox, you're not supposed to know :P!) $request = $smcFunc['db_query']('', ' SELECT pmr.id_pm, mem_to.id_member AS id_member_to, mem_to.real_name AS to_name, pmr.bcc, pmr.labels, pmr.is_read FROM {db_prefix}pm_recipients AS pmr LEFT JOIN {db_prefix}members AS mem_to ON (mem_to.id_member = pmr.id_member) WHERE pmr.id_pm IN ({array_int:message_list})', array('message_list' => $foundMessages)); while ($row = $smcFunc['db_fetch_assoc']($request)) { if ($context['folder'] == 'sent' || empty($row['bcc'])) { $recipients[$row['id_pm']][empty($row['bcc']) ? 'to' : 'bcc'][] = empty($row['id_member_to']) ? $txt['guest_title'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_to'] . '">' . $row['to_name'] . '</a>'; } if ($row['id_member_to'] == $user_info['id'] && $context['folder'] != 'sent') { $context['message_replied'][$row['id_pm']] = $row['is_read'] & 2; $row['labels'] = $row['labels'] == '' ? array() : explode(',', $row['labels']); // This is a special need for linking to messages. foreach ($row['labels'] as $v) { if (isset($context['labels'][(int) $v])) { $context['message_labels'][$row['id_pm']][(int) $v] = array('id' => $v, 'name' => $context['labels'][(int) $v]['name']); } // Here we find the first label on a message - for linking to posts in results if (!isset($context['first_label'][$row['id_pm']]) && !in_array('-1', $row['labels'])) { $context['first_label'][$row['id_pm']] = (int) $v; } } } } // Prepare the query for the callback! $request = $smcFunc['db_query']('', ' SELECT pm.id_pm, pm.subject, pm.id_member_from, pm.body, pm.msgtime, pm.from_name FROM {db_prefix}personal_messages AS pm WHERE pm.id_pm IN ({array_int:message_list}) ORDER BY ' . $search_params['sort'] . ' ' . $search_params['sort_dir'] . ' LIMIT ' . count($foundMessages), array('message_list' => $foundMessages)); $counter = 0; while ($row = $smcFunc['db_fetch_assoc']($request)) { // If there's no message subject, use the default. $row['subject'] = $row['subject'] == '' ? $txt['no_subject'] : $row['subject']; // Load this posters context info, if it ain't 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']); $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']]]) ? $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' => $row['subject'], 'body' => $row['body'], 'time' => timeformat($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 . '">' . $row['subject'] . '</a>', 'counter' => ++$counter); } $smcFunc['db_free_result']($request); } // 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']); }
function MessageSearch2() { global $scripturl, $modSettings, $user_info, $context, $txt, $db_prefix; global $ID_MEMBER, $memberContext, $func; if (!empty($context['load_average']) && !empty($modSettings['loadavg_search']) && $context['load_average'] >= $modSettings['loadavg_search']) { fatal_lang_error('loadavg_search_disabled', false); } // !!! For the moment force the folder to the inbox. $context['folder'] = 'inbox'; // Some useful general permissions. $context['can_send_pm'] = allowedTo('pm_send'); // Some hardcoded veriables 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] = stripslashes($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_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']); // Default the user name to a wildcard matching every user (*). if (!empty($search_params['user_spec']) || !empty($_REQUEST['userspec']) && $_REQUEST['userspec'] != '*') { $search_params['userspec'] = isset($search_params['userspec']) ? $search_params['userspec'] : $_REQUEST['userspec']; } // If there's no specific user, then don't mention it in the main query. if (empty($search_params['userspec'])) { $userQuery = ''; } else { $userString = strtr(addslashes($func['htmlspecialchars'](stripslashes($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))); for ($k = 0, $n = count($possible_users); $k < $n; $k++) { $possible_users[$k] = trim($possible_users[$k]); if (strlen($possible_users[$k]) == 0) { unset($possible_users[$k]); } } // Who matches those criteria? // !!! This doesn't support outbox searching. $request = db_query("\n\t\t\tSELECT ID_MEMBER\n\t\t\tFROM {$db_prefix}members\n\t\t\tWHERE realName LIKE '" . implode("' OR realName LIKE '", $possible_users) . "'", __FILE__, __LINE__); // Simply do nothing if there're too many members matching the criteria. if (mysql_num_rows($request) > $maxMembersToSearch) { $userQuery = ''; } elseif (mysql_num_rows($request) == 0) { $userQuery = "AND pm.ID_MEMBER_FROM = 0 AND (pm.fromName LIKE '" . implode("' OR pm.fromName LIKE '", $possible_users) . "')"; } else { $memberlist = array(); while ($row = mysql_fetch_assoc($request)) { $memberlist[] = $row['ID_MEMBER']; } $userQuery = "AND (pm.ID_MEMBER_FROM IN (" . implode(', ', $memberlist) . ") OR (pm.ID_MEMBER_FROM = 0 AND (pm.fromName LIKE '" . implode("' OR pm.fromName LIKE '", $possible_users) . "')))"; } mysql_free_result($request); } // Setup the sorting variables... // !!! Add more in here! $sort_columns = array('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'] : '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'])) { foreach ($_REQUEST['searchlabel'] as $key => $id) { $_REQUEST['searchlabel'][$key] = (int) $id; } } 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 = "\n\t\t\tAND (FIND_IN_SET('" . implode("', pmr.labels) OR FIND_IN_SET('", $_REQUEST['searchlabel']) . "', pmr.labels))"; } } // What are we actually searching for? $search_params['search'] = !empty($search_params['search']) ? $search_params['search'] : (isset($_REQUEST['search']) ? stripslashes($_REQUEST['search']) : ''); // If we ain't got nothing - we should error! if (!isset($search_params['search']) || $search_params['search'] == '') { $context['search_errors']['invalid_search_string'] = true; } // Extract phrase parts first (e.g. some words "this is a phrase" some more words.) preg_match_all('~(?:^|\\s)([-]?)"([^"]+)"(?:$|\\s)~' . ($context['utf8'] ? 'u' : ''), $search_params['search'], $matches, PREG_PATTERN_ORDER); $searchArray = $matches[2]; // Remove the phrase parts and extract the words. $tempSearch = explode(' ', preg_replace('~(?:^|\\s)([-]?)"([^"]+)"(?:$|\\s)~' . ($context['utf8'] ? 'u' : ''), ' ', $search_params['search'])); // A minus sign in front of a word excludes the word.... so... $excludedWords = array(); // .. first, we check for things like -"some words", but not "-some words". foreach ($matches[1] as $index => $word) { if ($word == '-') { $word = $func['strtolower'](trim($searchArray[$index])); if (strlen($word) > 0) { $excludedWords[] = addslashes($word); } unset($searchArray[$index]); } } // Now we look for -test, etc.... normaller. foreach ($tempSearch as $index => $word) { if (strpos(trim($word), '-') === 0) { $word = substr($func['strtolower'](trim($word)), 1); if (strlen($word) > 0) { $excludedWords[] = addslashes($word); } unset($tempSearch[$index]); } } $searchArray = array_merge($searchArray, $tempSearch); // Trim everything and make sure there are no words that are the same. foreach ($searchArray as $index => $value) { $searchArray[$index] = $func['strtolower'](trim($value)); if ($searchArray[$index] == '') { unset($searchArray[$index]); } else { // Sort out entities first. $searchArray[$index] = $func['htmlspecialchars']($searchArray[$index]); $searchArray[$index] = addslashes($searchArray[$index]); } } $searchArray = array_unique($searchArray); // Create an array of replacements for highlighting. $context['mark'] = array(); foreach ($searchArray as $word) { $context['mark'][$word] = '<b class="highlight">' . $word . '</b>'; } // 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'] = 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'] = htmlspecialchars($context['search_params']['search']); } if (isset($context['search_params']['userspec'])) { $context['search_params']['userspec'] = 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 . '|\'|' . addslashes($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 '%" . strtr($word, array('_' => '\\_', '%' => '\\%')) . "%'"; } else { $andQueryParts[] = '(pm.subject' . (in_array($word, $excludedWords) ? ' NOT' : '') . " LIKE '%" . strtr($word, array('_' => '\\_', '%' => '\\%')) . "%' " . (in_array($word, $excludedWords) ? 'AND pm.body NOT' : 'OR pm.body') . " LIKE '%" . strtr($word, array('_' => '\\_', '%' => '\\%')) . "%')"; } } $searchQuery = ' 1'; if (!empty($andQueryParts)) { $searchQuery = implode(!empty($search_params['searchtype']) && $search_params['searchtype'] == 2 ? ' OR ' : ' AND ', $andQueryParts); } // If we have errors - return back to the first screen... if (!empty($context['search_errors'])) { $_REQUEST['params'] = $context['params']; return MessageSearch(); } // Get the amount of results. $request = db_query("\n\t\tSELECT COUNT(*)\n\t\tFROM ({$db_prefix}pm_recipients AS pmr, {$db_prefix}personal_messages AS pm)\n\t\tWHERE pm.ID_PM = pmr.ID_PM" . ($context['folder'] == 'inbox' ? "\n\t\t\tAND pmr.ID_MEMBER = {$ID_MEMBER}\n\t\t\tAND pmr.deleted = 0" : "\n\t\t\tAND pm.ID_MEMBER_FROM = {$ID_MEMBER}\n\t\t\tAND pm.deletedBySender = 0") . "\n\t\t\t{$userQuery}{$labelQuery}\n\t\t\tAND ({$searchQuery})", __FILE__, __LINE__); list($numResults) = mysql_fetch_row($request); mysql_free_result($request); // Get all the matching messages... using standard search only (No caching and the like!) // !!! This doesn't support outbox searching yet. $request = db_query("\n\t\tSELECT pm.ID_PM, pm.ID_MEMBER_FROM\n\t\tFROM ({$db_prefix}pm_recipients AS pmr, {$db_prefix}personal_messages AS pm)\n\t\tWHERE pm.ID_PM = pmr.ID_PM" . ($context['folder'] == 'inbox' ? "\n\t\t\tAND pmr.ID_MEMBER = {$ID_MEMBER}\n\t\t\tAND pmr.deleted = 0" : "\n\t\t\tAND pm.ID_MEMBER_FROM = {$ID_MEMBER}\n\t\t\tAND pm.deletedBySender = 0") . "\n\t\t\t{$userQuery}{$labelQuery}\n\t\t\tAND ({$searchQuery})\n\t\tORDER BY {$search_params['sort']} {$search_params['sort_dir']}\n\t\tLIMIT {$context['start']}, {$modSettings['search_results_per_page']}", __FILE__, __LINE__); $foundMessages = array(); $posters = array(); while ($row = mysql_fetch_assoc($request)) { $foundMessages[] = $row['ID_PM']; $posters[] = $row['ID_MEMBER_FROM']; } mysql_free_result($request); // Load the users... $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(); if (!empty($foundMessages)) { // Now get recipients (but don't include bcc-recipients for your inbox, you're not supposed to know :P!) $request = db_query("\n\t\t\tSELECT\n\t\t\t\tpmr.ID_PM, mem_to.ID_MEMBER AS ID_MEMBER_TO, mem_to.realName AS toName,\n\t\t\t\tpmr.bcc, pmr.labels, pmr.is_read\n\t\t\tFROM {$db_prefix}pm_recipients AS pmr\n\t\t\t\tLEFT JOIN {$db_prefix}members AS mem_to ON (mem_to.ID_MEMBER = pmr.ID_MEMBER)\n\t\t\tWHERE pmr.ID_PM IN (" . implode(', ', $foundMessages) . ")", __FILE__, __LINE__); while ($row = mysql_fetch_assoc($request)) { if ($context['folder'] == 'outbox' || empty($row['bcc'])) { $recipients[$row['ID_PM']][empty($row['bcc']) ? 'to' : 'bcc'][] = empty($row['ID_MEMBER_TO']) ? $txt[28] : '<a href="' . $scripturl . '?action=profile;u=' . $row['ID_MEMBER_TO'] . '">' . $row['toName'] . '</a>'; } if ($row['ID_MEMBER_TO'] == $ID_MEMBER && $context['folder'] != 'outbox') { $context['message_replied'][$row['ID_PM']] = $row['is_read'] & 2; $row['labels'] = $row['labels'] == '' ? array() : explode(',', $row['labels']); // This is a special need for linking to messages. foreach ($row['labels'] as $v) { if (isset($context['labels'][(int) $v])) { $context['message_labels'][$row['ID_PM']][(int) $v] = array('id' => $v, 'name' => $context['labels'][(int) $v]['name']); } // Here we find the first label on a message - for linking to posts in results if (!isset($context['first_label'][$row['ID_PM']]) && !in_array('-1', $row['labels'])) { $context['first_label'][$row['ID_PM']] = (int) $v; } } } } // Prepare the query for the callback! $request = db_query("\n\t\t\tSELECT pm.ID_PM, pm.subject, pm.ID_MEMBER_FROM, pm.body, pm.msgtime, pm.fromName\n\t\t\tFROM {$db_prefix}personal_messages AS pm\n\t\t\tWHERE pm.ID_PM IN (" . implode(',', $foundMessages) . ")\n\t\t\tORDER BY {$search_params['sort']} {$search_params['sort_dir']}\n\t\t\tLIMIT " . count($foundMessages), __FILE__, __LINE__); $counter = 0; while ($row = mysql_fetch_assoc($request)) { // If there's no message subject, use the default. $row['subject'] = $row['subject'] == '' ? $txt[24] : $row['subject']; // Load this posters context info, if it ain't there then fill in the essentials... if (!loadMemberContext($row['ID_MEMBER_FROM'])) { $memberContext[$row['ID_MEMBER_FROM']]['name'] = $row['fromName']; $memberContext[$row['ID_MEMBER_FROM']]['id'] = 0; $memberContext[$row['ID_MEMBER_FROM']]['group'] = $txt[28]; $memberContext[$row['ID_MEMBER_FROM']]['link'] = $row['fromName']; $memberContext[$row['ID_MEMBER_FROM']]['email'] = ''; $memberContext[$row['ID_MEMBER_FROM']]['hide_email'] = true; $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']); $href = $scripturl . '?action=pm;f=' . $context['folder'] . (isset($context['first_label'][$row['ID_PM']]) ? ';l=' . $context['first_label'][$row['ID_PM']] : '') . ';pmid=' . $row['ID_PM'] . '#msg' . $row['ID_PM']; $context['personal_messages'][] = array('id' => $row['ID_PM'], 'member' => &$memberContext[$row['ID_MEMBER_FROM']], 'subject' => $row['subject'], 'body' => $row['body'], 'time' => timeformat($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 . '">' . $row['subject'] . '</a>', 'counter' => ++$counter); } mysql_free_result($request); } // Finish off the context. $context['page_title'] = $txt['pm_search_title']; $context['sub_template'] = 'search_results'; $context['pm_area'] = 'search'; $context['linktree'][] = array('url' => $scripturl . '?action=pm;sa=search', 'name' => $txt['pm_search_bar_title']); }