If you upload a copy of the 1.1.18 pm.php file, I might have some time to look at it.
Is the file named PersonalMessage.php in Sources what you would need? The code for it is below...
[code]<?php
/**********************************************************************************
* PersonalMessage.php *
***********************************************************************************
* SMF: Simple Machines Forum *
* Open-Source Project Inspired by Zef Hemel (zef@zefhemel.com) *
* =============================================================================== *
* Software Version: SMF 1.1.10 *
* Software by: Simple Machines (
http://www.simplemachines.org) *
* Copyright 2006-2009 by: Simple Machines LLC (
http://www.simplemachines.org) *
* 2001-2006 by: Lewis Media (
http://www.lewismedia.com) *
* Support, News, Updates at:
http://www.simplemachines.org *
***********************************************************************************
* This program is free software; you may redistribute it and/or modify it under *
* the terms of the provided license as published by Simple Machines LLC. *
* *
* This program is distributed in the hope that it is and will be useful, but *
* WITHOUT ANY WARRANTIES; without even any implied warranty of MERCHANTABILITY *
* or FITNESS FOR A PARTICULAR PURPOSE. *
* *
* See the "license.txt" file for details of the Simple Machines license. *
* The latest version can always be found at
http://www.simplemachines.org. *
**********************************************************************************/
if (!defined('SMF'))
die('Hacking attempt...');
/* This file is mainly meant for viewing personal messages. It also sends,
deletes, and marks personal messages. For compatibility reasons, they are
often called "instant messages". The following functions are used:
void MessageMain()
// !!! ?action=pm
void messageIndexBar(string area)
// !!!
void MessageFolder()
// !!! ?action=pm;sa=folder
void prepareMessageContext(bool reset = false)
// !!!
void MessageSearch()
// !!!
void MessageSearch2()
// !!!
void MessagePost()
// !!! ?action=pm;sa=post
void messagePostError(array error_types, string to, string bcc)
// !!!
void MessagePost2()
// !!! ?action=pm;sa=post2
void WirelessAddBuddy()
// !!!
void MessageActionsApply()
// !!! ?action=pm;sa=pmactions
void MessageKillAllQuery()
// !!! ?action=pm;sa=killall
void MessageKillAll()
// !!! ?action=pm;sa=killall2
void MessagePrune()
// !!! ?action=pm;sa=prune
void deleteMessages(array personal_messages, string folder,
int owner = user)
// !!!
void markMessages(array personal_messages = all, int label = all,
int owner = user)
- marks the specified personal_messages read.
- if label is set, only marks messages with that label.
- if owner is set, marks messages owned by that member id.
void ManageLabels()
// !!!
void ReportMessage()
- allows the user to report a personal message to an administrator.
- in the first instance requires that the ID of the message to report
is passed through $_GET.
- allows the user to report to either a particular administrator - or
the whole admin team.
- will forward on a copy of the original message without allowing the
reporter to make changes.
- uses the report_message sub-template.
*/
// This helps organize things...
function MessageMain()
{
global $txt, $scripturl, $sourcedir, $context, $user_info, $user_settings, $db_prefix, $ID_MEMBER;
// No guests!
is_not_guest();
// You're not supposed to be here at all, if you can't even read PMs.
isAllowedTo('pm_read');
// This file contains the basic functions for sending a PM.
require_once($sourcedir . '/Subs-Post.php');
if (loadLanguage('PersonalMessage', '', false) === false)
loadLanguage('InstantMessage');
if (WIRELESS)
$context['sub_template'] = WIRELESS_PROTOCOL . '_pm';
else
{
if (loadTemplate('PersonalMessage', false) === false)
loadTemplate('InstantMessage');
}
// Load up the members maximum message capacity.
if (!$user_info['is_admin'])
{
// !!! Why do we do this? It seems like if they have any limit we should use it.
$request = db_query("
SELECT MAX(maxMessages) AS topLimit, MIN(maxMessages) AS bottomLimit
FROM {$db_prefix}membergroups
WHERE ID_GROUP IN (" . implode(', ', $user_info['groups']) . ')', __FILE__, __LINE__);
list ($maxMessage, $minMessage) = mysql_fetch_row($request);
mysql_free_result($request);
$context['message_limit'] = $minMessage == 0 ? 0 : $maxMessage;
}
else
$context['message_limit'] = 0;
// Prepare the context for the capacity bar.
if (!empty($context['message_limit']))
{
$bar = ($user_info['messages'] * 100) / $context['message_limit'];
$context['limit_bar'] = array(
'messages' => $user_info['messages'],
'allowed' => $context['message_limit'],
'percent' => $bar,
'bar' => min(100, (int) $bar),
'text' => sprintf($txt['pm_currently_using'], $user_info['messages'], round($bar, 1)),
);
}
// We should probably cache this information for speed.
$context['labels'] = $user_settings['messageLabels'] == '' ? array() : explode(',', $user_settings['messageLabels']);
foreach ($context['labels'] as $k => $v)
$context['labels'][(int) $k] = array('id' => $k, 'name' => trim($v), 'messages' => 0, 'unread_messages' => 0);
$context['labels'][-1] = array('id' => -1, 'name' => $txt['pm_msg_label_inbox'], 'messages' => 0, 'unread_messages' => 0);
// !!! The idea would be to cache this information in the members table, and invlidate it when they are sent messages.
$result = db_query("
SELECT labels, is_read, COUNT(*) AS num
FROM {$db_prefix}pm_recipients
WHERE ID_MEMBER = $ID_MEMBER
GROUP BY labels, is_read", __FILE__, __LINE__);
while ($row = mysql_fetch_assoc($result))
{
$this_labels = explode(',', $row['labels']);
foreach ($this_labels as $this_label)
{
$context['labels'][(int) $this_label]['messages'] += $row['num'];
if (!($row['is_read'] & 1))
$context['labels'][(int) $this_label]['unread_messages'] += $row['num'];
}
}
mysql_free_result($result);
// This determines if we have more labels than just the standard inbox.
$context['currently_using_labels'] = count($context['labels']) > 1 ? 1 : 0;
// Some stuff for the labels...
$context['current_label_id'] = isset($_REQUEST['l']) && isset($context['labels'][(int) $_REQUEST['l']]) ? (int) $_REQUEST['l'] : -1;
$context['current_label'] = &$context['labels'][(int) $context['current_label_id']]['name'];
$context['folder'] = !isset($_REQUEST['f']) || $_REQUEST['f'] != 'outbox' ? 'inbox' : 'outbox';
// This is convenient. Do you know how annoying it is to do this every time?!
$context['current_label_redirect'] = 'action=pm;f=' . $context['folder'] . (isset($_GET['start']) ? ';start=' . $_GET['start'] : '') . (isset($_REQUEST['l']) ? ';l=' . $_REQUEST['l'] : '');
// Build the linktree for all the actions...
$context['linktree'][] = array(
'url' => $scripturl . '?action=pm',
'name' => $txt[144]
);
$subActions = array(
'addbuddy' => 'WirelessAddBuddy',
'manlabels' => 'ManageLabels',
'outbox' => 'MessageFolder',
'pmactions' => 'MessageActionsApply',
'prune' => 'MessagePrune',
'removeall' => 'MessageKillAllQuery',
'removeall2' => 'MessageKillAll',
'report' => 'ReportMessage',
'search' => 'MessageSearch',
'search2' => 'MessageSearch2',
'send' => 'MessagePost',
'send2' => 'MessagePost2',
);
if (!isset($_REQUEST['sa']) || !isset($subActions[$_REQUEST['sa']]))
MessageFolder();
else
{
messageIndexBar($_REQUEST['sa']);
$subActions[$_REQUEST['sa']]();
}
}
// A sidebar to easily access different areas of the section
function messageIndexBar($area)
{
global $txt, $context, $scripturl, $sc, $modSettings, $settings, $user_info;
$context['pm_areas'] = array(
'folders' => array(
'title' => $txt['pm_messages'],
'areas' => array(
'send' => array('link' => '<a href="' . $scripturl . '?action=pm;sa=send">' . $txt[321] . '</a>', 'href' => $scripturl . '?action=pm;sa=send'),
'' => array(),
'inbox' => array('link' => '<a href="' . $scripturl . '?action=pm">' . $txt[316] . '</a>', 'href' => $scripturl . '?action=pm'),
'outbox' => array('link' => '<a href="' . $scripturl . '?action=pm;f=outbox">' . $txt[320] . '</a>', 'href' => $scripturl . '?action=pm;f=outbox'),
),
),
'labels' => array(
'title' => $txt['pm_labels'],
'areas' => array(),
),
'pref' => array(
'title' => $txt['pm_preferences'],
'areas' => array(
'search' => array('link' => '<a href="' . $scripturl . '?action=pm;sa=search">' . $txt['pm_search_bar_title'] . '</a>', 'href' => $scripturl . '?action=pm;sa=search'),
'manlabels' => array('link' => '<a href="' . $scripturl . '?action=pm;sa=manlabels">' . $txt['pm_manage_labels'] . '</a>', 'href' => $scripturl . '?action=pm;sa=manlabels'),
'prune' => array('link' => '<a href="' . $scripturl . '?action=pm;sa=prune">' . $txt['pm_prune'] . '</a>', 'href' => $scripturl . '?action=pm;sa=prune'),
),
),
);
// Handle labels.
if (empty($context['currently_using_labels']))
unset($context['pm_areas']['labels']);
else
{
// Note we send labels by id as it will have less problems in the querystring.
foreach ($context['labels'] as $label)
{
if ($label['id'] == -1)
continue;
$context['pm_areas']['labels']['areas']['label' . $label['id']] = array(
'link' => '<a href="' . $scripturl . '?action=pm;l=' . $label['id'] . '">' . $label['name'] . '</a>',
'href' => $scripturl . '?action=pm;l=' . $label['id'],
'unread_messages' => &$context['labels'][(int) $label['id']]['unread_messages'],
'messages' => &$context['labels'][(int) $label['id']]['messages'],
);
}
}
$context['pm_areas']['folders']['areas']['inbox']['unread_messages'] = &$context['labels'][-1]['unread_messages'];
$context['pm_areas']['folders']['areas']['inbox']['messages'] = &$context['labels'][-1]['messages'];
// Do we have a limit on the amount of messages we can keep?
if (!empty($context['message_limit']))
{
$bar = round(($user_info['messages'] * 100) / $context['message_limit'], 1);
$context['limit_bar'] = array(
'messages' => $user_info['messages'],
'allowed' => $context['message_limit'],
'percent' => $bar,
'bar' => $bar > 100 ? 100 : (int) $bar,
'text' => sprintf($txt['pm_currently_using'], $user_info['messages'], $bar)
);
// Force it in to somewhere.
$context['pm_areas']['pref']['areas']['limit_bar'] = array('limit_bar' => true);
}
// Where we are now.
$context['pm_area'] = $area;
// obExit will know what to do!
if (!WIRELESS)
$context['template_layers'][] = 'pm';
}
// A folder, ie. outbox/inbox.
function MessageFolder()
{
global $txt, $scripturl, $db_prefix, $ID_MEMBER, $modSettings, $context;
global $messages_request, $user_info, $recipients, $options;
// Make sure the starting location is valid.
if (isset($_GET['start']) && $_GET['start'] != 'new')
$_GET['start'] = (int) $_GET['start'];
elseif (!isset($_GET['start']) && !empty($options['view_newest_pm_first']))
$_GET['start'] = 0;
else
$_GET['start'] = 'new';
// Set up some basic theme stuff.
$context['allow_hide_email'] = !empty($modSettings['allow_hideEmail']);
$context['from_or_to'] = $context['folder'] != 'outbox' ? 'from' : 'to';
$context['get_pmessage'] = 'prepareMessageContext';
$labelQuery = $context['folder'] != 'outbox' ? "
AND FIND_IN_SET('$context[current_label_id]', pmr.labels)" : '';
// Set the index bar correct!
messageIndexBar($context['current_label_id'] == -1 ? $context['folder'] : 'label' . $context['current_label_id']);
// Sorting the folder.
$sort_methods = array(
'date' => 'pm.ID_PM',
'name' => "IFNULL(mem.realName, '')",
'subject' => 'pm.subject',
);
// They didn't pick one, use the forum default.
if (!isset($_GET['sort']) || !isset($sort_methods[$_GET['sort']]))
{
$context['sort_by'] = 'date';
$_GET['sort'] = 'pm.ID_PM';
$descending = false;
}
// Otherwise use the defaults: ascending, by date.
else
{
$context['sort_by'] = $_GET['sort'];
$_GET['sort'] = $sort_methods[$_GET['sort']];
$descending = isset($_GET['desc']);
}
if (!empty($options['view_newest_pm_first']))
$descending = !$descending;
$context['sort_direction'] = $descending ? 'down' : 'up';
// Why would you want access to your outbox if you're not allowed to send anything?
if ($context['folder'] == 'outbox')
isAllowedTo('pm_send');
// Set the text to resemble the current folder.
$pmbox = $context['folder'] != 'outbox' ? $txt[316] : $txt[320];
$txt[412] = str_replace('PMBOX', $pmbox, $txt[412]);
// Now, build the link tree!
$context['linktree'][] = array(
'url' => $scripturl . '?action=pm;f=' . $context['folder'],
'name' => $pmbox
);
// Build it further for a label.
if ($context['current_label_id'] != -1)
$context['linktree'][] = array(
'url' => $scripturl . '?action=pm;f=' . $context['folder'] . ';l=' . $context['current_label_id'],
'name' => $txt['pm_current_label'] . ': ' . $context['current_label']
);
// Mark all messages as read if in the inbox.
if ($context['folder'] != 'outbox' && !empty($context['labels'][(int) $context['current_label_id']]['unread_messages']))
markMessages(null, $context['current_label_id']);
// Figure out how many messages there are.
if ($context['folder'] == 'outbox')
$request = db_query("
SELECT COUNT(*)
FROM {$db_prefix}personal_messages
WHERE ID_MEMBER_FROM = $ID_MEMBER
AND deletedBySender = 0", __FILE__, __LINE__);
else
$request = db_query("
SELECT COUNT(*)
FROM {$db_prefix}pm_recipients AS pmr
WHERE pmr.ID_MEMBER = $ID_MEMBER
AND pmr.deleted = 0$labelQuery", __FILE__, __LINE__);
list ($max_messages) = mysql_fetch_row($request);
mysql_free_result($request);
// Only show the button if there are messages to delete.
$context['show_delete'] = $max_messages > 0;
// Start on the last page.
if (!is_numeric($_GET['start']) || $_GET['start'] >= $max_messages)
$_GET['start'] = ($max_messages - 1) - (($max_messages - 1) % $modSettings['defaultMaxMessages']);
elseif ($_GET['start'] < 0)
$_GET['start'] = 0;
// ... but wait - what if we want to start from a specific message?
if (isset($_GET['pmid']))
{
$_GET['pmid'] = (int) $_GET['pmid'];
// With only one page of PM's we're gonna want page 1.
if ($max_messages <= $modSettings['defaultMaxMessages'])
$_GET['start'] = 0;
else
{
if ($context['folder'] == 'outbox')
$request = db_query("
SELECT COUNT(*)
FROM {$db_prefix}personal_messages
WHERE ID_MEMBER_FROM = $ID_MEMBER
AND deletedBySender = 0
AND ID_PM " . ($descending ? '>' : '<') . " $_GET[pmid]", __FILE__, __LINE__);
else
$request = db_query("
SELECT COUNT(*)
FROM {$db_prefix}pm_recipients AS pmr
WHERE pmr.ID_MEMBER = $ID_MEMBER
AND pmr.deleted = 0$labelQuery
AND ID_PM " . ($descending ? '>' : '<') . " $_GET[pmid]", __FILE__, __LINE__);
list ($_GET['start']) = mysql_fetch_row($request);
mysql_free_result($request);
// To stop the page index's being abnormal, start the page on the page the message would normally be located on...
$_GET['start'] = $modSettings['defaultMaxMessages'] * (int) ($_GET['start'] / $modSettings['defaultMaxMessages']);
}
}
// Set up the page index.
$context['page_index'] = constructPageIndex($scripturl . '?action=pm;f=' . $context['folder'] . (isset($_REQUEST['l']) ? ';l=' . (int) $_REQUEST['l'] : '') . ';sort=' . $context['sort_by'] . (isset($_GET['desc']) ? ';desc' : ''), $_GET['start'], $max_messages, $modSettings['defaultMaxMessages']);
$context['start'] = $_GET['start'];
// Determine the navigation context (especially useful for the wireless template).
$context['links'] = array(
'first' => $_GET['start'] >= $modSettings['defaultMaxMessages'] ? $scripturl . '?action=pm;start=0' : '',
'prev' => $_GET['start'] >= $modSettings['defaultMaxMessages'] ? $scripturl . '?action=pm;start=' . ($_GET['start'] - $modSettings['defaultMaxMessages']) : '',
'next' => $_GET['start'] + $modSettings['defaultMaxMessages'] < $max_messages ? $scripturl . '?action=pm;start=' . ($_GET['start'] + $modSettings['defaultMaxMessages']) : '',
'last' => $_GET['start'] + $modSettings['defaultMaxMessages'] < $max_messages ? $scripturl . '?action=pm;start=' . (floor(($max_messages - 1) / $modSettings['defaultMaxMessages']) * $modSettings['defaultMaxMessages']) : '',
'up' => $scripturl,
);
$context['page_info'] = array(
'current_page' => $_GET['start'] / $modSettings['defaultMaxMessages'] + 1,
'num_pages' => floor(($max_messages - 1) / $modSettings['defaultMaxMessages']) + 1
);
// Load the messages up...
// !!!SLOW This query uses a filesort. (inbox only.)
$request = db_query("
SELECT pm.ID_PM, pm.ID_MEMBER_FROM
FROM ({$db_prefix}personal_messages AS pm" . ($context['folder'] == 'outbox' ? ')' . ($context['sort_by'] == 'name' ? "
LEFT JOIN {$db_prefix}pm_recipients AS pmr ON (pmr.ID_PM = pm.ID_PM)" : '') : ", {$db_prefix}pm_recipients AS pmr)") . ($context['sort_by'] == 'name' ? ("
LEFT JOIN {$db_prefix}members AS mem ON (mem.ID_MEMBER = " . ($context['folder'] == 'outbox' ? 'pmr.ID_MEMBER' : 'pm.ID_MEMBER_FROM') . ")") : '') . "
WHERE " . ($context['folder'] == 'outbox' ? "pm.ID_MEMBER_FROM = $ID_MEMBER
AND pm.deletedBySender = 0" : "pmr.ID_PM = pm.ID_PM
AND pmr.ID_MEMBER = $ID_MEMBER
AND pmr.deleted = 0$labelQuery") . (empty($_GET['pmsg']) ? '' : "
AND pm.ID_PM = " . (int) $_GET['pmsg']) . "
ORDER BY " . ($_GET['sort'] == 'pm.ID_PM' && $context['folder'] != 'outbox' ? 'pmr.ID_PM' : $_GET['sort']) . ($descending ? ' DESC' : ' ASC') . (empty($_GET['pmsg']) ? "
LIMIT $_GET[start], $modSettings[defaultMaxMessages]" : ''), __FILE__, __LINE__);
// Load the ID_PMs and ID_MEMBERs and initialize recipients.
$pms = array();
$posters = $context['folder'] == 'outbox' ? array($ID_MEMBER) : array();
$recipients = array();
while ($row = mysql_fetch_assoc($request))
{
if (!isset($recipients[$row['ID_PM']]))
{
$pms[] = $row['ID_PM'];
if (!empty($row['ID_MEMBER_FROM']) && $context['folder'] != 'outbox')
$posters[] = $row['ID_MEMBER_FROM'];
$recipients[$row['ID_PM']] = array(
'to' => array(),
'bcc' => array()
);
}
}
mysql_free_result($request);
if (!empty($pms))
{
// Get recipients (don't include bcc-recipients for your inbox, you're not supposed to know

).
$request = db_query("
SELECT pmr.ID_PM, mem_to.ID_MEMBER AS ID_MEMBER_TO, mem_to.realName AS toName, 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 (" . implode(', ', $pms) . ")", __FILE__, __LINE__);
$context['message_labels'] = array();
$context['message_replied'] = array();
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']);
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']);
}
}
}
mysql_free_result($request);
// Load any users....
$posters = array_unique($posters);
if (!empty($posters))
loadMemberData($posters);
// Execute the query!
$messages_request = db_query("
SELECT pm.ID_PM, pm.subject, pm.ID_MEMBER_FROM, pm.body, pm.msgtime, pm.fromName
FROM {$db_prefix}personal_messages AS pm" . ($context['folder'] == 'outbox' ? "
LEFT JOIN {$db_prefix}pm_recipients AS pmr ON (pmr.ID_PM = pm.ID_PM)" : '') . ($context['sort_by'] == 'name' ? "
LEFT JOIN {$db_prefix}members AS mem ON (mem.ID_MEMBER = " . ($context['folder'] == 'outbox' ? 'pmr.ID_MEMBER' : 'pm.ID_MEMBER_FROM') . ")" : '') . "
WHERE pm.ID_PM IN (" . implode(',', $pms) . ")" . ($context['folder'] == 'outbox' ? "
GROUP BY pm.ID_PM" : '') . "
ORDER BY $_GET[sort] " . ($descending ? ' DESC' : ' ASC') . "
LIMIT " . count($pms), __FILE__, __LINE__);
}
else
$messages_request = false;
$context['can_send_pm'] = allowedTo('pm_send');
if (!WIRELESS)
$context['sub_template'] = 'folder';
$context['page_title'] = $txt[143];
}
// Get a personal message for the theme. (used to save memory.)
function prepareMessageContext($reset = false)
{
global $txt, $scripturl, $modSettings, $context, $messages_request, $memberContext, $recipients;
// Count the current message number....
static $counter = null;
if ($counter === null || $reset)
$counter = $context['start'];
static $temp_pm_selected = null;
if ($temp_pm_selected === null)
{
$temp_pm_selected = isset($_SESSION['pm_selected']) ? $_SESSION['pm_selected'] : array();
$_SESSION['pm_selected'] = array();
}
// Bail if it's false, ie. no messages.
if ($messages_request == false)
return false;
// Reset the data?
if ($reset == true)
return @mysql_data_seek($messages_request, 0);
// Get the next one... bail if anything goes wrong.
$message = mysql_fetch_assoc($messages_request);
if (!$message)
return(false);
// Use '(no subject)' if none was specified.
$message['subject'] = $message['subject'] == '' ? $txt[24] : $message['subject'];
// Load the message's information - if it's not there, load the guest information.
if (!loadMemberContext($message['ID_MEMBER_FROM']))
{
$memberContext[$message['ID_MEMBER_FROM']]['name'] = $message['fromName'];
$memberContext[$message['ID_MEMBER_FROM']]['id'] = 0;
$memberContext[$message['ID_MEMBER_FROM']]['group'] = $txt[28];
$memberContext[$message['ID_MEMBER_FROM']]['link'] = $message['fromName'];
$memberContext[$message['ID_MEMBER_FROM']]['email'] = '';
$memberContext[$message['ID_MEMBER_FROM']]['hide_email'] = true;
$memberContext[$message['ID_MEMBER_FROM']]['is_guest'] = true;
}
// Censor all the important text...
censorText($message['body']);
censorText($message['subject']);
// Run UBBC interpreter on the message.
$message['body'] = parse_bbc($message['body'], true, 'pm' . $message['ID_PM']);
// Send the array.
$output = array(
'alternate' => $counter % 2,
'id' => $message['ID_PM'],
'member' => &$memberContext[$message['ID_MEMBER_FROM']],
'subject' => $message['subject'],
'time' => timeformat($message['msgtime']),
'timestamp' => forum_time(true, $message['msgtime']),
'counter' => $counter,
'body' => $message['body'],
'recipients' => &$recipients[$message['ID_PM']],
'number_recipients' => count($recipients[$message['ID_PM']]['to']),
'labels' => &$context['message_labels'][$message['ID_PM']],
'fully_labeled' => count($context['message_labels'][$message['ID_PM']]) == count($context['labels']),
'is_replied_to' => &$context['message_replied'][$message['ID_PM']],
'is_selected' => !empty($temp_pm_selected) && in_array($message['ID_PM'], $temp_pm_selected),
);
$counter++;
return $output;
}
function MessageSearch()
{
global $context, $txt, $scripturl, $modSettings;
if (isset($_REQUEST['params']))
{
$temp_params = explode('|"|', base64_decode(strtr($_REQUEST['params'], array(' ' => '+'))));
$context['search_params'] = array();
foreach ($temp_params as $i => $data)
{
@list ($k, $v) = explode('|\'|', $data);
$context['search_params'][$k] = stripslashes($v);
}
}
if (isset($_REQUEST['search']))
$context['search_params']['search'] = stripslashes(un_htmlspecialchars($_REQUEST['search']));
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(stripslashes($context['search_params']['userspec']));
if (!empty($context['search_params']['searchtype']))
$context['search_params']['searchtype'] = 2;
if (!empty($context['search_params']['minage']))
$context['search_params']['minage'] = (int) $context['search_params']['minage'];
if (!empty($context['search_params']['maxage']))
$context['search_params']['maxage'] = (int) $context['search_params']['maxage'];
$context['search_params']['subject_only'] = !empty($context['search_params']['subject_only']);
$context['search_params']['show_complete'] = !empty($context['search_params']['show_complete']);
// Create the array of labels to be searched.
$context['search_labels'] = array();
$searchedLabels = isset($context['search_params']['labels']) && $context['search_params']['labels'] != '' ? explode(',', $context['search_params']['labels']) : array();
foreach ($context['labels'] as $label)
{
$context['search_labels'][] = array(
'id' => $label['id'],
'name' => $label['name'],
'checked' => !empty($searchedLabels) ? in_array($label['id'], $searchedLabels) : true,
);
}
// Are all the labels checked?
$context['check_all'] = empty($searchedLabels) || count($context['search_labels']) == count($searchedLabels);
// Load the error text strings if there were errors in the search.
if (!empty($context['search_errors']))
{
loadLanguage('Errors');
$context['search_errors']['messages'] = array();
foreach ($context['search_errors'] as $search_error => $dummy)
{
if ($search_error == 'messages')
continue;
$context['search_errors']['messages'][] = $txt['error_' . $search_error];
}
}
$context['simple_search'] = isset($context['search_params']['advanced']) ? empty($context['search_params']['advanced']) : !empty($modSettings['simpleSearch']) && !isset($_REQUEST['advanced']);
$context['page_title'] = $txt['pm_search_title'];
$context['sub_template'] = '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("
SELECT ID_MEMBER
FROM {$db_prefix}members
WHERE 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;
// Otherwise prepare the query!
elseif (count($_REQUEST['searchlabel']) != count($context['labels']))
$labelQuery = "
AND (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("
SELECT COUNT(*)
FROM ({$db_prefix}pm_recipients AS pmr, {$db_prefix}personal_messages AS pm)
WHERE pm.ID_PM = pmr.ID_PM" . ($context['folder'] == 'inbox' ? "
AND pmr.ID_MEMBER = $ID_MEMBER
AND pmr.deleted = 0" : "
AND pm.ID_MEMBER_FROM = $ID_MEMBER
AND pm.deletedBySender = 0") . "
$userQuery$labelQuery
AND ($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("
SELECT pm.ID_PM, pm.ID_MEMBER_FROM
FROM ({$db_prefix}pm_recipients AS pmr, {$db_prefix}personal_messages AS pm)
WHERE pm.ID_PM = pmr.ID_PM" . ($context['folder'] == 'inbox' ? "
AND pmr.ID_MEMBER = $ID_MEMBER
AND pmr.deleted = 0" : "
AND pm.ID_MEMBER_FROM = $ID_MEMBER
AND pm.deletedBySender = 0") . "
$userQuery$labelQuery
AND ($searchQuery)
ORDER BY $search_params[sort] $search_params[sort_dir]
LIMIT $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

!)
$request = db_query("
SELECT
pmr.ID_PM, mem_to.ID_MEMBER AS ID_MEMBER_TO, mem_to.realName AS toName,
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 (" . 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("
SELECT pm.ID_PM, pm.subject, pm.ID_MEMBER_FROM, pm.body, pm.msgtime, pm.fromName
FROM {$db_prefix}personal_messages AS pm
WHERE pm.ID_PM IN (" . implode(',', $foundMessages) . ")
ORDER BY $search_params[sort] $search_params[sort_dir]
LIMIT " . 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'],
);
}
// Send a new message?
function MessagePost()
{
global $txt, $sourcedir, $db_prefix, $ID_MEMBER, $scripturl, $modSettings;
global $context, $options, $func, $language, $user_info;
isAllowedTo('pm_send');
if (loadLanguage('PersonalMessage', '', false) === false)
loadLanguage('InstantMessage');
// Just in case it was loaded from somewhere else.
if (!WIRELESS)
{
if (loadTemplate('PersonalMessage', false) === false)
loadTemplate('InstantMessage');
$context['sub_template'] = 'send';
}
// Extract out the spam settings - cause it's neat.
list ($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']);
$context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new');
// Set the title...
$context['page_title'] = $txt[148];
$context['reply'] = isset($_REQUEST['pmsg']) || isset($_REQUEST['quote']);
// Check whether we've gone over the limit of messages we can send per hour.
if (!empty($modSettings['pm_posts_per_hour']) && !allowedTo(array('admin_forum', 'moderate_forum', 'send_mail')))
{
// How many have they sent this last hour?
$request = db_query("
SELECT COUNT(pr.ID_PM) AS postCount
FROM ({$db_prefix}personal_messages AS pm, {$db_prefix}pm_recipients AS pr)
WHERE pm.ID_MEMBER_FROM = $ID_MEMBER
AND pm.msgtime > " . (time() - 3600) . "
AND pr.ID_PM = pm.ID_PM", __FILE__, __LINE__);
list ($postCount) = mysql_fetch_row($request);
mysql_free_result($request);
if (!empty($postCount) && $postCount >= $modSettings['pm_posts_per_hour'])
{
// Excempt moderators.
$request = db_query("
SELECT ID_MEMBER
FROM {$db_prefix}moderators
WHERE ID_MEMBER = $ID_MEMBER", __FILE__, __LINE__);
if (mysql_num_rows($request) == 0)
fatal_error(sprintf($txt['pm_too_many_per_hour'], $modSettings['pm_posts_per_hour']));
mysql_free_result($request);
}
}
// Quoting/Replying to a message?
if (!empty($_REQUEST['pmsg']))
{
$_REQUEST['pmsg'] = (int) $_REQUEST['pmsg'];
// Get the quoted message (and make sure you're allowed to see this quote!).
$request = db_query("
SELECT
pm.ID_PM, pm.body, pm.subject, pm.msgtime, mem.memberName,
IFNULL(mem.ID_MEMBER, 0) AS ID_MEMBER, IFNULL(mem.realName, pm.fromName) AS realName
FROM ({$db_prefix}personal_messages AS pm" . ($context['folder'] == 'outbox' ? '' : ", {$db_prefix}pm_recipients AS pmr") . ")
LEFT JOIN {$db_prefix}members AS mem ON (mem.ID_MEMBER = pm.ID_MEMBER_FROM)
WHERE pm.ID_PM = $_REQUEST[pmsg]" . ($context['folder'] == 'outbox' ? "
AND pm.ID_MEMBER_FROM = $ID_MEMBER" : "
AND pmr.ID_PM = $_REQUEST[pmsg]
AND pmr.ID_MEMBER = $ID_MEMBER") . "
LIMIT 1", __FILE__, __LINE__);
if (mysql_num_rows($request) == 0)
fatal_lang_error('pm_not_yours', false);
$row_quoted = mysql_fetch_assoc($request);
mysql_free_result($request);
// Censor the message.
censorText($row_quoted['subject']);
censorText($row_quoted['body']);
// Add 'Re: ' to it...