comparison forum/Sources/PersonalMessage.php @ 76:e3e11437ecea website

Add forum code
author Chris Cannam
date Sun, 07 Jul 2013 11:25:48 +0200
parents
children
comparison
equal deleted inserted replaced
75:72f59aa7e503 76:e3e11437ecea
1 <?php
2
3 /**
4 * Simple Machines Forum (SMF)
5 *
6 * @package SMF
7 * @author Simple Machines http://www.simplemachines.org
8 * @copyright 2011 Simple Machines
9 * @license http://www.simplemachines.org/about/smf/license.php BSD
10 *
11 * @version 2.0
12 */
13
14 if (!defined('SMF'))
15 die('Hacking attempt...');
16
17 /* This file is mainly meant for viewing personal messages. It also sends,
18 deletes, and marks personal messages. For compatibility reasons, they are
19 often called "instant messages". The following functions are used:
20
21 void MessageMain()
22 // !!! ?action=pm
23
24 void messageIndexBar(string area)
25 // !!!
26
27 void MessageFolder()
28 // !!! ?action=pm;sa=folder
29
30 void prepareMessageContext(type reset = 'subject', bool reset = false)
31 // !!!
32
33 void MessageSearch()
34 // !!!
35
36 void MessageSearch2()
37 // !!!
38
39 void MessagePost()
40 // !!! ?action=pm;sa=post
41
42 void messagePostError(array error_types, array named_recipients, array recipient_ids)
43 // !!!
44
45 void MessagePost2()
46 // !!! ?action=pm;sa=post2
47
48 void WirelessAddBuddy()
49 // !!!
50
51 void MessageActionsApply()
52 // !!! ?action=pm;sa=pmactions
53
54 void MessageKillAllQuery()
55 // !!! ?action=pm;sa=killall
56
57 void MessageKillAll()
58 // !!! ?action=pm;sa=killall2
59
60 void MessagePrune()
61 // !!! ?action=pm;sa=prune
62
63 void deleteMessages(array personal_messages, string folder,
64 int owner = user)
65 // !!!
66
67 void markMessages(array personal_messages = all, int label = all,
68 int owner = user)
69 - marks the specified personal_messages read.
70 - if label is set, only marks messages with that label.
71 - if owner is set, marks messages owned by that member id.
72
73 void ManageLabels()
74 // !!!
75
76 void MessageSettings()
77 // !!!
78
79 void ReportMessage()
80 - allows the user to report a personal message to an administrator.
81 - in the first instance requires that the ID of the message to report
82 is passed through $_GET.
83 - allows the user to report to either a particular administrator - or
84 the whole admin team.
85 - will forward on a copy of the original message without allowing the
86 reporter to make changes.
87 - uses the report_message sub-template.
88
89 void ManageRules()
90 // !!!
91
92 void LoadRules()
93 // !!!
94
95 void ApplyRules()
96 // !!!
97 */
98
99 // This helps organize things...
100 function MessageMain()
101 {
102 global $txt, $scripturl, $sourcedir, $context, $user_info, $user_settings, $smcFunc, $modSettings;
103
104 // No guests!
105 is_not_guest();
106
107 // You're not supposed to be here at all, if you can't even read PMs.
108 isAllowedTo('pm_read');
109
110 // This file contains the basic functions for sending a PM.
111 require_once($sourcedir . '/Subs-Post.php');
112
113 loadLanguage('PersonalMessage');
114
115 if (WIRELESS && WIRELESS_PROTOCOL == 'wap')
116 fatal_lang_error('wireless_error_notyet', false);
117 elseif (WIRELESS)
118 $context['sub_template'] = WIRELESS_PROTOCOL . '_pm';
119 else
120 loadTemplate('PersonalMessage');
121
122 // Load up the members maximum message capacity.
123 if ($user_info['is_admin'])
124 $context['message_limit'] = 0;
125 elseif (($context['message_limit'] = cache_get_data('msgLimit:' . $user_info['id'], 360)) === null)
126 {
127 // !!! Why do we do this? It seems like if they have any limit we should use it.
128 $request = $smcFunc['db_query']('', '
129 SELECT MAX(max_messages) AS top_limit, MIN(max_messages) AS bottom_limit
130 FROM {db_prefix}membergroups
131 WHERE id_group IN ({array_int:users_groups})',
132 array(
133 'users_groups' => $user_info['groups'],
134 )
135 );
136 list ($maxMessage, $minMessage) = $smcFunc['db_fetch_row']($request);
137 $smcFunc['db_free_result']($request);
138
139 $context['message_limit'] = $minMessage == 0 ? 0 : $maxMessage;
140
141 // Save us doing it again!
142 cache_put_data('msgLimit:' . $user_info['id'], $context['message_limit'], 360);
143 }
144
145 // Prepare the context for the capacity bar.
146 if (!empty($context['message_limit']))
147 {
148 $bar = ($user_info['messages'] * 100) / $context['message_limit'];
149
150 $context['limit_bar'] = array(
151 'messages' => $user_info['messages'],
152 'allowed' => $context['message_limit'],
153 'percent' => $bar,
154 'bar' => min(100, (int) $bar),
155 'text' => sprintf($txt['pm_currently_using'], $user_info['messages'], round($bar, 1)),
156 );
157 }
158
159 // a previous message was sent successfully? show a small indication.
160 if (isset($_GET['done']) && ($_GET['done'] == 'sent'))
161 $context['pm_sent'] = true;
162
163 // Now we have the labels, and assuming we have unsorted mail, apply our rules!
164 if ($user_settings['new_pm'])
165 {
166 $context['labels'] = $user_settings['message_labels'] == '' ? array() : explode(',', $user_settings['message_labels']);
167 foreach ($context['labels'] as $id_label => $label_name)
168 $context['labels'][(int) $id_label] = array(
169 'id' => $id_label,
170 'name' => trim($label_name),
171 'messages' => 0,
172 'unread_messages' => 0,
173 );
174 $context['labels'][-1] = array(
175 'id' => -1,
176 'name' => $txt['pm_msg_label_inbox'],
177 'messages' => 0,
178 'unread_messages' => 0,
179 );
180
181 ApplyRules();
182 updateMemberData($user_info['id'], array('new_pm' => 0));
183 $smcFunc['db_query']('', '
184 UPDATE {db_prefix}pm_recipients
185 SET is_new = {int:not_new}
186 WHERE id_member = {int:current_member}',
187 array(
188 'current_member' => $user_info['id'],
189 'not_new' => 0,
190 )
191 );
192 }
193
194 // Load the label data.
195 if ($user_settings['new_pm'] || ($context['labels'] = cache_get_data('labelCounts:' . $user_info['id'], 720)) === null)
196 {
197 $context['labels'] = $user_settings['message_labels'] == '' ? array() : explode(',', $user_settings['message_labels']);
198 foreach ($context['labels'] as $id_label => $label_name)
199 $context['labels'][(int) $id_label] = array(
200 'id' => $id_label,
201 'name' => trim($label_name),
202 'messages' => 0,
203 'unread_messages' => 0,
204 );
205 $context['labels'][-1] = array(
206 'id' => -1,
207 'name' => $txt['pm_msg_label_inbox'],
208 'messages' => 0,
209 'unread_messages' => 0,
210 );
211
212 // Looks like we need to reseek!
213 $result = $smcFunc['db_query']('', '
214 SELECT labels, is_read, COUNT(*) AS num
215 FROM {db_prefix}pm_recipients
216 WHERE id_member = {int:current_member}
217 AND deleted = {int:not_deleted}
218 GROUP BY labels, is_read',
219 array(
220 'current_member' => $user_info['id'],
221 'not_deleted' => 0,
222 )
223 );
224 while ($row = $smcFunc['db_fetch_assoc']($result))
225 {
226 $this_labels = explode(',', $row['labels']);
227 foreach ($this_labels as $this_label)
228 {
229 $context['labels'][(int) $this_label]['messages'] += $row['num'];
230 if (!($row['is_read'] & 1))
231 $context['labels'][(int) $this_label]['unread_messages'] += $row['num'];
232 }
233 }
234 $smcFunc['db_free_result']($result);
235
236 // Store it please!
237 cache_put_data('labelCounts:' . $user_info['id'], $context['labels'], 720);
238 }
239
240 // This determines if we have more labels than just the standard inbox.
241 $context['currently_using_labels'] = count($context['labels']) > 1 ? 1 : 0;
242
243 // Some stuff for the labels...
244 $context['current_label_id'] = isset($_REQUEST['l']) && isset($context['labels'][(int) $_REQUEST['l']]) ? (int) $_REQUEST['l'] : -1;
245 $context['current_label'] = &$context['labels'][(int) $context['current_label_id']]['name'];
246 $context['folder'] = !isset($_REQUEST['f']) || $_REQUEST['f'] != 'sent' ? 'inbox' : 'sent';
247
248 // This is convenient. Do you know how annoying it is to do this every time?!
249 $context['current_label_redirect'] = 'action=pm;f=' . $context['folder'] . (isset($_GET['start']) ? ';start=' . $_GET['start'] : '') . (isset($_REQUEST['l']) ? ';l=' . $_REQUEST['l'] : '');
250 $context['can_issue_warning'] = in_array('w', $context['admin_features']) && allowedTo('issue_warning') && $modSettings['warning_settings'][0] == 1;
251
252 // Build the linktree for all the actions...
253 $context['linktree'][] = array(
254 'url' => $scripturl . '?action=pm',
255 'name' => $txt['personal_messages']
256 );
257
258 // Preferences...
259 $context['display_mode'] = WIRELESS ? 0 : $user_settings['pm_prefs'] & 3;
260
261 $subActions = array(
262 'addbuddy' => 'WirelessAddBuddy',
263 'manlabels' => 'ManageLabels',
264 'manrules' => 'ManageRules',
265 'pmactions' => 'MessageActionsApply',
266 'prune' => 'MessagePrune',
267 'removeall' => 'MessageKillAllQuery',
268 'removeall2' => 'MessageKillAll',
269 'report' => 'ReportMessage',
270 'search' => 'MessageSearch',
271 'search2' => 'MessageSearch2',
272 'send' => 'MessagePost',
273 'send2' => 'MessagePost2',
274 'settings' => 'MessageSettings',
275 );
276
277 if (!isset($_REQUEST['sa']) || !isset($subActions[$_REQUEST['sa']]))
278 MessageFolder();
279 else
280 {
281 messageIndexBar($_REQUEST['sa']);
282 $subActions[$_REQUEST['sa']]();
283 }
284 }
285
286 // A sidebar to easily access different areas of the section
287 function messageIndexBar($area)
288 {
289 global $txt, $context, $scripturl, $sourcedir, $sc, $modSettings, $settings, $user_info, $options;
290
291 $pm_areas = array(
292 'folders' => array(
293 'title' => $txt['pm_messages'],
294 'areas' => array(
295 'send' => array(
296 'label' => $txt['new_message'],
297 'custom_url' => $scripturl . '?action=pm;sa=send',
298 'permission' => allowedTo('pm_send'),
299 ),
300 'inbox' => array(
301 'label' => $txt['inbox'],
302 'custom_url' => $scripturl . '?action=pm',
303 ),
304 'sent' => array(
305 'label' => $txt['sent_items'],
306 'custom_url' => $scripturl . '?action=pm;f=sent',
307 ),
308 ),
309 ),
310 'labels' => array(
311 'title' => $txt['pm_labels'],
312 'areas' => array(),
313 ),
314 'actions' => array(
315 'title' => $txt['pm_actions'],
316 'areas' => array(
317 'search' => array(
318 'label' => $txt['pm_search_bar_title'],
319 'custom_url' => $scripturl . '?action=pm;sa=search',
320 ),
321 'prune' => array(
322 'label' => $txt['pm_prune'],
323 'custom_url' => $scripturl . '?action=pm;sa=prune'
324 ),
325 ),
326 ),
327 'pref' => array(
328 'title' => $txt['pm_preferences'],
329 'areas' => array(
330 'manlabels' => array(
331 'label' => $txt['pm_manage_labels'],
332 'custom_url' => $scripturl . '?action=pm;sa=manlabels',
333 ),
334 'manrules' => array(
335 'label' => $txt['pm_manage_rules'],
336 'custom_url' => $scripturl . '?action=pm;sa=manrules',
337 ),
338 'settings' => array(
339 'label' => $txt['pm_settings'],
340 'custom_url' => $scripturl . '?action=pm;sa=settings',
341 ),
342 ),
343 ),
344 );
345
346 // Handle labels.
347 if (empty($context['currently_using_labels']))
348 unset($pm_areas['labels']);
349 else
350 {
351 // Note we send labels by id as it will have less problems in the querystring.
352 $unread_in_labels = 0;
353 foreach ($context['labels'] as $label)
354 {
355 if ($label['id'] == -1)
356 continue;
357
358 // Count the amount of unread items in labels.
359 $unread_in_labels += $label['unread_messages'];
360
361 // Add the label to the menu.
362 $pm_areas['labels']['areas']['label' . $label['id']] = array(
363 'label' => $label['name'] . (!empty($label['unread_messages']) ? ' (<strong>' . $label['unread_messages'] . '</strong>)' : ''),
364 'custom_url' => $scripturl . '?action=pm;l=' . $label['id'],
365 'unread_messages' => $label['unread_messages'],
366 'messages' => $label['messages'],
367 );
368 }
369
370 if (!empty($unread_in_labels))
371 $pm_areas['labels']['title'] .= ' (' . $unread_in_labels . ')';
372 }
373
374 $pm_areas['folders']['areas']['inbox']['unread_messages'] = &$context['labels'][-1]['unread_messages'];
375 $pm_areas['folders']['areas']['inbox']['messages'] = &$context['labels'][-1]['messages'];
376 if (!empty($context['labels'][-1]['unread_messages']))
377 {
378 $pm_areas['folders']['areas']['inbox']['label'] .= ' (<strong>' . $context['labels'][-1]['unread_messages'] . '</strong>)';
379 $pm_areas['folders']['title'] .= ' (' . $context['labels'][-1]['unread_messages'] . ')';
380 }
381
382 // Do we have a limit on the amount of messages we can keep?
383 if (!empty($context['message_limit']))
384 {
385 $bar = round(($user_info['messages'] * 100) / $context['message_limit'], 1);
386
387 $context['limit_bar'] = array(
388 'messages' => $user_info['messages'],
389 'allowed' => $context['message_limit'],
390 'percent' => $bar,
391 'bar' => $bar > 100 ? 100 : (int) $bar,
392 'text' => sprintf($txt['pm_currently_using'], $user_info['messages'], $bar)
393 );
394 }
395
396 require_once($sourcedir . '/Subs-Menu.php');
397
398 // What page is this, again?
399 $current_page = $scripturl . '?action=pm' . (!empty($_REQUEST['sa']) ? ';sa=' . $_REQUEST['sa'] : '') . (!empty($context['folder']) ? ';f=' . $context['folder'] : '') . (!empty($context['current_label_id']) ? ';l=' . $context['current_label_id'] : '');
400
401 // Set a few options for the menu.
402 $menuOptions = array(
403 'current_area' => $area,
404 'disable_url_session_check' => true,
405 'toggle_url' => $current_page . ';togglebar',
406 'toggle_redirect_url' => $current_page,
407 );
408
409 // Actually create the menu!
410 $pm_include_data = createMenu($pm_areas, $menuOptions);
411 unset($pm_areas);
412
413 // Make a note of the Unique ID for this menu.
414 $context['pm_menu_id'] = $context['max_menu_id'];
415 $context['pm_menu_name'] = 'menu_data_' . $context['pm_menu_id'];
416
417 // Set the selected item.
418 $context['menu_item_selected'] = $pm_include_data['current_area'];
419
420 // obExit will know what to do!
421 if (!WIRELESS)
422 $context['template_layers'][] = 'pm';
423 }
424
425 // A folder, ie. inbox/sent etc.
426 function MessageFolder()
427 {
428 global $txt, $scripturl, $modSettings, $context, $subjects_request;
429 global $messages_request, $user_info, $recipients, $options, $smcFunc, $memberContext, $user_settings;
430
431 // Changing view?
432 if (isset($_GET['view']))
433 {
434 $context['display_mode'] = $context['display_mode'] > 1 ? 0 : $context['display_mode'] + 1;
435 updateMemberData($user_info['id'], array('pm_prefs' => ($user_settings['pm_prefs'] & 252) | $context['display_mode']));
436 }
437
438 // Make sure the starting location is valid.
439 if (isset($_GET['start']) && $_GET['start'] != 'new')
440 $_GET['start'] = (int) $_GET['start'];
441 elseif (!isset($_GET['start']) && !empty($options['view_newest_pm_first']))
442 $_GET['start'] = 0;
443 else
444 $_GET['start'] = 'new';
445
446 // Set up some basic theme stuff.
447 $context['from_or_to'] = $context['folder'] != 'sent' ? 'from' : 'to';
448 $context['get_pmessage'] = 'prepareMessageContext';
449 $context['signature_enabled'] = substr($modSettings['signature_settings'], 0, 1) == 1;
450
451 $labelQuery = $context['folder'] != 'sent' ? '
452 AND FIND_IN_SET(' . $context['current_label_id'] . ', pmr.labels) != 0' : '';
453
454 // Set the index bar correct!
455 messageIndexBar($context['current_label_id'] == -1 ? $context['folder'] : 'label' . $context['current_label_id']);
456
457 // Sorting the folder.
458 $sort_methods = array(
459 'date' => 'pm.id_pm',
460 'name' => 'IFNULL(mem.real_name, \'\')',
461 'subject' => 'pm.subject',
462 );
463
464 // They didn't pick one, use the forum default.
465 if (!isset($_GET['sort']) || !isset($sort_methods[$_GET['sort']]))
466 {
467 $context['sort_by'] = 'date';
468 $_GET['sort'] = 'pm.id_pm';
469 // An overriding setting?
470 $descending = !empty($options['view_newest_pm_first']);
471 }
472 // Otherwise use the defaults: ascending, by date.
473 else
474 {
475 $context['sort_by'] = $_GET['sort'];
476 $_GET['sort'] = $sort_methods[$_GET['sort']];
477 $descending = isset($_GET['desc']);
478 }
479
480 $context['sort_direction'] = $descending ? 'down' : 'up';
481
482 // Why would you want access to your sent items if you're not allowed to send anything?
483 if ($context['folder'] == 'sent')
484 isAllowedTo('pm_send');
485
486 // Set the text to resemble the current folder.
487 $pmbox = $context['folder'] != 'sent' ? $txt['inbox'] : $txt['sent_items'];
488 $txt['delete_all'] = str_replace('PMBOX', $pmbox, $txt['delete_all']);
489
490 // Now, build the link tree!
491 if ($context['current_label_id'] == -1)
492 $context['linktree'][] = array(
493 'url' => $scripturl . '?action=pm;f=' . $context['folder'],
494 'name' => $pmbox
495 );
496
497 // Build it further for a label.
498 if ($context['current_label_id'] != -1)
499 $context['linktree'][] = array(
500 'url' => $scripturl . '?action=pm;f=' . $context['folder'] . ';l=' . $context['current_label_id'],
501 'name' => $txt['pm_current_label'] . ': ' . $context['current_label']
502 );
503
504 // Figure out how many messages there are.
505 if ($context['folder'] == 'sent')
506 $request = $smcFunc['db_query']('', '
507 SELECT COUNT(' . ($context['display_mode'] == 2 ? 'DISTINCT pm.id_pm_head' : '*') . ')
508 FROM {db_prefix}personal_messages AS pm
509 WHERE pm.id_member_from = {int:current_member}
510 AND pm.deleted_by_sender = {int:not_deleted}',
511 array(
512 'current_member' => $user_info['id'],
513 'not_deleted' => 0,
514 )
515 );
516 else
517 $request = $smcFunc['db_query']('', '
518 SELECT COUNT(' . ($context['display_mode'] == 2 ? 'DISTINCT pm.id_pm_head' : '*') . ')
519 FROM {db_prefix}pm_recipients AS pmr' . ($context['display_mode'] == 2 ? '
520 INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)' : '') . '
521 WHERE pmr.id_member = {int:current_member}
522 AND pmr.deleted = {int:not_deleted}' . $labelQuery,
523 array(
524 'current_member' => $user_info['id'],
525 'not_deleted' => 0,
526 )
527 );
528 list ($max_messages) = $smcFunc['db_fetch_row']($request);
529 $smcFunc['db_free_result']($request);
530
531 // Only show the button if there are messages to delete.
532 $context['show_delete'] = $max_messages > 0;
533
534 // Start on the last page.
535 if (!is_numeric($_GET['start']) || $_GET['start'] >= $max_messages)
536 $_GET['start'] = ($max_messages - 1) - (($max_messages - 1) % $modSettings['defaultMaxMessages']);
537 elseif ($_GET['start'] < 0)
538 $_GET['start'] = 0;
539
540 // ... but wait - what if we want to start from a specific message?
541 if (isset($_GET['pmid']))
542 {
543 $pmID = (int) $_GET['pmid'];
544
545 // Make sure you have access to this PM.
546 if (!isAccessiblePM($pmID, $context['folder'] == 'sent' ? 'outbox' : 'inbox'))
547 fatal_lang_error('no_access', false);
548
549 $context['current_pm'] = $pmID;
550
551 // With only one page of PM's we're gonna want page 1.
552 if ($max_messages <= $modSettings['defaultMaxMessages'])
553 $_GET['start'] = 0;
554 // If we pass kstart we assume we're in the right place.
555 elseif (!isset($_GET['kstart']))
556 {
557 if ($context['folder'] == 'sent')
558 $request = $smcFunc['db_query']('', '
559 SELECT COUNT(' . ($context['display_mode'] == 2 ? 'DISTINCT pm.id_pm_head' : '*') . ')
560 FROM {db_prefix}personal_messages
561 WHERE id_member_from = {int:current_member}
562 AND deleted_by_sender = {int:not_deleted}
563 AND id_pm ' . ($descending ? '>' : '<') . ' {int:id_pm}',
564 array(
565 'current_member' => $user_info['id'],
566 'not_deleted' => 0,
567 'id_pm' => $pmID,
568 )
569 );
570 else
571 $request = $smcFunc['db_query']('', '
572 SELECT COUNT(' . ($context['display_mode'] == 2 ? 'DISTINCT pm.id_pm_head' : '*') . ')
573 FROM {db_prefix}pm_recipients AS pmr' . ($context['display_mode'] == 2 ? '
574 INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)' : '') . '
575 WHERE pmr.id_member = {int:current_member}
576 AND pmr.deleted = {int:not_deleted}' . $labelQuery . '
577 AND pmr.id_pm ' . ($descending ? '>' : '<') . ' {int:id_pm}',
578 array(
579 'current_member' => $user_info['id'],
580 'not_deleted' => 0,
581 'id_pm' => $pmID,
582 )
583 );
584
585 list ($_GET['start']) = $smcFunc['db_fetch_row']($request);
586 $smcFunc['db_free_result']($request);
587
588 // To stop the page index's being abnormal, start the page on the page the message would normally be located on...
589 $_GET['start'] = $modSettings['defaultMaxMessages'] * (int) ($_GET['start'] / $modSettings['defaultMaxMessages']);
590 }
591 }
592
593 // Sanitize and validate pmsg variable if set.
594 if (isset($_GET['pmsg']))
595 {
596 $pmsg = (int) $_GET['pmsg'];
597
598 if (!isAccessiblePM($pmsg, $context['folder'] == 'sent' ? 'outbox' : 'inbox'))
599 fatal_lang_error('no_access', false);
600 }
601
602 // Set up the page index.
603 $context['page_index'] = constructPageIndex($scripturl . '?action=pm;f=' . $context['folder'] . (isset($_REQUEST['l']) ? ';l=' . (int) $_REQUEST['l'] : '') . ';sort=' . $context['sort_by'] . ($descending ? ';desc' : ''), $_GET['start'], $max_messages, $modSettings['defaultMaxMessages']);
604 $context['start'] = $_GET['start'];
605
606 // Determine the navigation context (especially useful for the wireless template).
607 $context['links'] = array(
608 'first' => $_GET['start'] >= $modSettings['defaultMaxMessages'] ? $scripturl . '?action=pm;start=0' : '',
609 'prev' => $_GET['start'] >= $modSettings['defaultMaxMessages'] ? $scripturl . '?action=pm;start=' . ($_GET['start'] - $modSettings['defaultMaxMessages']) : '',
610 'next' => $_GET['start'] + $modSettings['defaultMaxMessages'] < $max_messages ? $scripturl . '?action=pm;start=' . ($_GET['start'] + $modSettings['defaultMaxMessages']) : '',
611 'last' => $_GET['start'] + $modSettings['defaultMaxMessages'] < $max_messages ? $scripturl . '?action=pm;start=' . (floor(($max_messages - 1) / $modSettings['defaultMaxMessages']) * $modSettings['defaultMaxMessages']) : '',
612 'up' => $scripturl,
613 );
614 $context['page_info'] = array(
615 'current_page' => $_GET['start'] / $modSettings['defaultMaxMessages'] + 1,
616 'num_pages' => floor(($max_messages - 1) / $modSettings['defaultMaxMessages']) + 1
617 );
618
619 // First work out what messages we need to see - if grouped is a little trickier...
620 if ($context['display_mode'] == 2)
621 {
622 // On a non-default sort due to PostgreSQL we have to do a harder sort.
623 if ($smcFunc['db_title'] == 'PostgreSQL' && $_GET['sort'] != 'pm.id_pm')
624 {
625 $sub_request = $smcFunc['db_query']('', '
626 SELECT MAX({raw:sort}) AS sort_param, pm.id_pm_head
627 FROM {db_prefix}personal_messages AS pm' . ($context['folder'] == 'sent' ? ($context['sort_by'] == 'name' ? '
628 LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)' : '') : '
629 INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm
630 AND pmr.id_member = {int:current_member}
631 AND pmr.deleted = {int:not_deleted}
632 ' . $labelQuery . ')') . ($context['sort_by'] == 'name' ? ( '
633 LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = {raw:id_member})') : '') . '
634 WHERE ' . ($context['folder'] == 'sent' ? 'pm.id_member_from = {int:current_member}
635 AND pm.deleted_by_sender = {int:not_deleted}' : '1=1') . (empty($pmsg) ? '' : '
636 AND pm.id_pm = {int:id_pm}') . '
637 GROUP BY pm.id_pm_head
638 ORDER BY sort_param' . ($descending ? ' DESC' : ' ASC') . (empty($pmsg) ? '
639 LIMIT ' . $_GET['start'] . ', ' . $modSettings['defaultMaxMessages'] : ''),
640 array(
641 'current_member' => $user_info['id'],
642 'not_deleted' => 0,
643 'id_member' => $context['folder'] == 'sent' ? 'pmr.id_member' : 'pm.id_member_from',
644 'id_pm' => isset($pmsg) ? $pmsg : '0',
645 'sort' => $_GET['sort'],
646 )
647 );
648 $sub_pms = array();
649 while ($row = $smcFunc['db_fetch_assoc']($sub_request))
650 $sub_pms[$row['id_pm_head']] = $row['sort_param'];
651
652 $smcFunc['db_free_result']($sub_request);
653
654 $request = $smcFunc['db_query']('', '
655 SELECT pm.id_pm AS id_pm, pm.id_pm_head
656 FROM {db_prefix}personal_messages AS pm' . ($context['folder'] == 'sent' ? ($context['sort_by'] == 'name' ? '
657 LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)' : '') : '
658 INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm
659 AND pmr.id_member = {int:current_member}
660 AND pmr.deleted = {int:not_deleted}
661 ' . $labelQuery . ')') . ($context['sort_by'] == 'name' ? ( '
662 LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = {raw:id_member})') : '') . '
663 WHERE ' . (empty($sub_pms) ? '0=1' : 'pm.id_pm IN ({array_int:pm_list})') . '
664 ORDER BY ' . ($_GET['sort'] == 'pm.id_pm' && $context['folder'] != 'sent' ? 'id_pm' : '{raw:sort}') . ($descending ? ' DESC' : ' ASC') . (empty($pmsg) ? '
665 LIMIT ' . $_GET['start'] . ', ' . $modSettings['defaultMaxMessages'] : ''),
666 array(
667 'current_member' => $user_info['id'],
668 'pm_list' => array_keys($sub_pms),
669 'not_deleted' => 0,
670 'sort' => $_GET['sort'],
671 'id_member' => $context['folder'] == 'sent' ? 'pmr.id_member' : 'pm.id_member_from',
672 )
673 );
674 }
675 else
676 {
677 $request = $smcFunc['db_query']('pm_conversation_list', '
678 SELECT MAX(pm.id_pm) AS id_pm, pm.id_pm_head
679 FROM {db_prefix}personal_messages AS pm' . ($context['folder'] == 'sent' ? ($context['sort_by'] == 'name' ? '
680 LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)' : '') : '
681 INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm
682 AND pmr.id_member = {int:current_member}
683 AND pmr.deleted = {int:deleted_by}
684 ' . $labelQuery . ')') . ($context['sort_by'] == 'name' ? ( '
685 LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = {raw:pm_member})') : '') . '
686 WHERE ' . ($context['folder'] == 'sent' ? 'pm.id_member_from = {int:current_member}
687 AND pm.deleted_by_sender = {int:deleted_by}' : '1=1') . (empty($pmsg) ? '' : '
688 AND pm.id_pm = {int:pmsg}') . '
689 GROUP BY pm.id_pm_head
690 ORDER BY ' . ($_GET['sort'] == 'pm.id_pm' && $context['folder'] != 'sent' ? 'id_pm' : '{raw:sort}') . ($descending ? ' DESC' : ' ASC') . (empty($_GET['pmsg']) ? '
691 LIMIT ' . $_GET['start'] . ', ' . $modSettings['defaultMaxMessages'] : ''),
692 array(
693 'current_member' => $user_info['id'],
694 'deleted_by' => 0,
695 'sort' => $_GET['sort'],
696 'pm_member' => $context['folder'] == 'sent' ? 'pmr.id_member' : 'pm.id_member_from',
697 'pmsg' => isset($pmsg) ? (int) $pmsg : 0,
698 )
699 );
700 }
701 }
702 // This is kinda simple!
703 else
704 {
705 // !!!SLOW This query uses a filesort. (inbox only.)
706 $request = $smcFunc['db_query']('', '
707 SELECT pm.id_pm, pm.id_pm_head, pm.id_member_from
708 FROM {db_prefix}personal_messages AS pm' . ($context['folder'] == 'sent' ? '' . ($context['sort_by'] == 'name' ? '
709 LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)' : '') : '
710 INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm
711 AND pmr.id_member = {int:current_member}
712 AND pmr.deleted = {int:is_deleted}
713 ' . $labelQuery . ')') . ($context['sort_by'] == 'name' ? ( '
714 LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = {raw:pm_member})') : '') . '
715 WHERE ' . ($context['folder'] == 'sent' ? 'pm.id_member_from = {raw:current_member}
716 AND pm.deleted_by_sender = {int:is_deleted}' : '1=1') . (empty($pmsg) ? '' : '
717 AND pm.id_pm = {int:pmsg}') . '
718 ORDER BY ' . ($_GET['sort'] == 'pm.id_pm' && $context['folder'] != 'sent' ? 'pmr.id_pm' : '{raw:sort}') . ($descending ? ' DESC' : ' ASC') . (empty($pmsg) ? '
719 LIMIT ' . $_GET['start'] . ', ' . $modSettings['defaultMaxMessages'] : ''),
720 array(
721 'current_member' => $user_info['id'],
722 'is_deleted' => 0,
723 'sort' => $_GET['sort'],
724 'pm_member' => $context['folder'] == 'sent' ? 'pmr.id_member' : 'pm.id_member_from',
725 'pmsg' => isset($pmsg) ? (int) $pmsg : 0,
726 )
727 );
728 }
729 // Load the id_pms and initialize recipients.
730 $pms = array();
731 $lastData = array();
732 $posters = $context['folder'] == 'sent' ? array($user_info['id']) : array();
733 $recipients = array();
734
735 while ($row = $smcFunc['db_fetch_assoc']($request))
736 {
737 if (!isset($recipients[$row['id_pm']]))
738 {
739 if (isset($row['id_member_from']))
740 $posters[$row['id_pm']] = $row['id_member_from'];
741 $pms[$row['id_pm']] = $row['id_pm'];
742 $recipients[$row['id_pm']] = array(
743 'to' => array(),
744 'bcc' => array()
745 );
746 }
747
748 // Keep track of the last message so we know what the head is without another query!
749 if ((empty($pmID) && (empty($options['view_newest_pm_first']) || !isset($lastData))) || empty($lastData) || (!empty($pmID) && $pmID == $row['id_pm']))
750 $lastData = array(
751 'id' => $row['id_pm'],
752 'head' => $row['id_pm_head'],
753 );
754 }
755 $smcFunc['db_free_result']($request);
756
757 // Make sure that we have been given a correct head pm id!
758 if ($context['display_mode'] == 2 && !empty($pmID) && $pmID != $lastData['id'])
759 fatal_lang_error('no_access', false);
760
761 if (!empty($pms))
762 {
763 // Select the correct current message.
764 if (empty($pmID))
765 $context['current_pm'] = $lastData['id'];
766
767 // This is a list of the pm's that are used for "full" display.
768 if ($context['display_mode'] == 0)
769 $display_pms = $pms;
770 else
771 $display_pms = array($context['current_pm']);
772
773 // At this point we know the main id_pm's. But - if we are looking at conversations we need the others!
774 if ($context['display_mode'] == 2)
775 {
776 $request = $smcFunc['db_query']('', '
777 SELECT pm.id_pm, pm.id_member_from, pm.deleted_by_sender, pmr.id_member, pmr.deleted
778 FROM {db_prefix}personal_messages AS pm
779 INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)
780 WHERE pm.id_pm_head = {int:id_pm_head}
781 AND ((pm.id_member_from = {int:current_member} AND pm.deleted_by_sender = {int:not_deleted})
782 OR (pmr.id_member = {int:current_member} AND pmr.deleted = {int:not_deleted}))
783 ORDER BY pm.id_pm',
784 array(
785 'current_member' => $user_info['id'],
786 'id_pm_head' => $lastData['head'],
787 'not_deleted' => 0,
788 )
789 );
790 while ($row = $smcFunc['db_fetch_assoc']($request))
791 {
792 // This is, frankly, a joke. We will put in a workaround for people sending to themselves - yawn!
793 if ($context['folder'] == 'sent' && $row['id_member_from'] == $user_info['id'] && $row['deleted_by_sender'] == 1)
794 continue;
795 elseif ($row['id_member'] == $user_info['id'] & $row['deleted'] == 1)
796 continue;
797
798 if (!isset($recipients[$row['id_pm']]))
799 $recipients[$row['id_pm']] = array(
800 'to' => array(),
801 'bcc' => array()
802 );
803 $display_pms[] = $row['id_pm'];
804 $posters[$row['id_pm']] = $row['id_member_from'];
805 }
806 $smcFunc['db_free_result']($request);
807 }
808
809 // This is pretty much EVERY pm!
810 $all_pms = array_merge($pms, $display_pms);
811 $all_pms = array_unique($all_pms);
812
813 // Get recipients (don't include bcc-recipients for your inbox, you're not supposed to know :P).
814 $request = $smcFunc['db_query']('', '
815 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
816 FROM {db_prefix}pm_recipients AS pmr
817 LEFT JOIN {db_prefix}members AS mem_to ON (mem_to.id_member = pmr.id_member)
818 WHERE pmr.id_pm IN ({array_int:pm_list})',
819 array(
820 'pm_list' => $all_pms,
821 )
822 );
823 $context['message_labels'] = array();
824 $context['message_replied'] = array();
825 $context['message_unread'] = array();
826 while ($row = $smcFunc['db_fetch_assoc']($request))
827 {
828 if ($context['folder'] == 'sent' || empty($row['bcc']))
829 $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>';
830
831 if ($row['id_member_to'] == $user_info['id'] && $context['folder'] != 'sent')
832 {
833 $context['message_replied'][$row['id_pm']] = $row['is_read'] & 2;
834 $context['message_unread'][$row['id_pm']] = $row['is_read'] == 0;
835
836 $row['labels'] = $row['labels'] == '' ? array() : explode(',', $row['labels']);
837 foreach ($row['labels'] as $v)
838 {
839 if (isset($context['labels'][(int) $v]))
840 $context['message_labels'][$row['id_pm']][(int) $v] = array('id' => $v, 'name' => $context['labels'][(int) $v]['name']);
841 }
842 }
843 }
844 $smcFunc['db_free_result']($request);
845
846 // Make sure we don't load unnecessary data.
847 if ($context['display_mode'] == 1)
848 {
849 foreach ($posters as $k => $v)
850 if (!in_array($k, $display_pms))
851 unset($posters[$k]);
852 }
853
854 // Load any users....
855 $posters = array_unique($posters);
856 if (!empty($posters))
857 loadMemberData($posters);
858
859 // If we're on grouped/restricted view get a restricted list of messages.
860 if ($context['display_mode'] != 0)
861 {
862 // Get the order right.
863 $orderBy = array();
864 foreach (array_reverse($pms) as $pm)
865 $orderBy[] = 'pm.id_pm = ' . $pm;
866
867 // Seperate query for these bits!
868 $subjects_request = $smcFunc['db_query']('', '
869 SELECT pm.id_pm, pm.subject, pm.id_member_from, pm.msgtime, IFNULL(mem.real_name, pm.from_name) AS from_name,
870 IFNULL(mem.id_member, 0) AS not_guest
871 FROM {db_prefix}personal_messages AS pm
872 LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from)
873 WHERE pm.id_pm IN ({array_int:pm_list})
874 ORDER BY ' . implode(', ', $orderBy) . '
875 LIMIT ' . count($pms),
876 array(
877 'pm_list' => $pms,
878 )
879 );
880 }
881
882 // Execute the query!
883 $messages_request = $smcFunc['db_query']('', '
884 SELECT pm.id_pm, pm.subject, pm.id_member_from, pm.body, pm.msgtime, pm.from_name
885 FROM {db_prefix}personal_messages AS pm' . ($context['folder'] == 'sent' ? '
886 LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)' : '') . ($context['sort_by'] == 'name' ? '
887 LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = {raw:id_member})' : '') . '
888 WHERE pm.id_pm IN ({array_int:display_pms})' . ($context['folder'] == 'sent' ? '
889 GROUP BY pm.id_pm, pm.subject, pm.id_member_from, pm.body, pm.msgtime, pm.from_name' : '') . '
890 ORDER BY ' . ($context['display_mode'] == 2 ? 'pm.id_pm' : $_GET['sort']) . ($descending ? ' DESC' : ' ASC') . '
891 LIMIT ' . count($display_pms),
892 array(
893 'display_pms' => $display_pms,
894 'id_member' => $context['folder'] == 'sent' ? 'pmr.id_member' : 'pm.id_member_from',
895 )
896 );
897 }
898 else
899 $messages_request = false;
900
901 $context['can_send_pm'] = allowedTo('pm_send');
902 if (!WIRELESS)
903 $context['sub_template'] = 'folder';
904 $context['page_title'] = $txt['pm_inbox'];
905
906 // Finally mark the relevant messages as read.
907 if ($context['folder'] != 'sent' && !empty($context['labels'][(int) $context['current_label_id']]['unread_messages']))
908 {
909 // If the display mode is "old sk00l" do them all...
910 if ($context['display_mode'] == 0)
911 markMessages(null, $context['current_label_id']);
912 // Otherwise do just the current one!
913 elseif (!empty($context['current_pm']))
914 markMessages($display_pms, $context['current_label_id']);
915 }
916 }
917
918 // Get a personal message for the theme. (used to save memory.)
919 function prepareMessageContext($type = 'subject', $reset = false)
920 {
921 global $txt, $scripturl, $modSettings, $context, $messages_request, $memberContext, $recipients, $smcFunc;
922 global $user_info, $subjects_request;
923
924 // Count the current message number....
925 static $counter = null;
926 if ($counter === null || $reset)
927 $counter = $context['start'];
928
929 static $temp_pm_selected = null;
930 if ($temp_pm_selected === null)
931 {
932 $temp_pm_selected = isset($_SESSION['pm_selected']) ? $_SESSION['pm_selected'] : array();
933 $_SESSION['pm_selected'] = array();
934 }
935
936 // If we're in non-boring view do something exciting!
937 if ($context['display_mode'] != 0 && $subjects_request && $type == 'subject')
938 {
939 $subject = $smcFunc['db_fetch_assoc']($subjects_request);
940 if (!$subject)
941 {
942 $smcFunc['db_free_result']($subjects_request);
943 return false;
944 }
945
946 $subject['subject'] = $subject['subject'] == '' ? $txt['no_subject'] : $subject['subject'];
947 censorText($subject['subject']);
948
949 $output = array(
950 'id' => $subject['id_pm'],
951 'member' => array(
952 'id' => $subject['id_member_from'],
953 'name' => $subject['from_name'],
954 'link' => $subject['not_guest'] ? '<a href="' . $scripturl . '?action=profile;u=' . $subject['id_member_from'] . '">' . $subject['from_name'] . '</a>' : $subject['from_name'],
955 ),
956 'recipients' => &$recipients[$subject['id_pm']],
957 'subject' => $subject['subject'],
958 'time' => timeformat($subject['msgtime']),
959 'timestamp' => forum_time(true, $subject['msgtime']),
960 'number_recipients' => count($recipients[$subject['id_pm']]['to']),
961 'labels' => &$context['message_labels'][$subject['id_pm']],
962 'fully_labeled' => count($context['message_labels'][$subject['id_pm']]) == count($context['labels']),
963 'is_replied_to' => &$context['message_replied'][$subject['id_pm']],
964 'is_unread' => &$context['message_unread'][$subject['id_pm']],
965 'is_selected' => !empty($temp_pm_selected) && in_array($subject['id_pm'], $temp_pm_selected),
966 );
967
968 return $output;
969 }
970
971 // Bail if it's false, ie. no messages.
972 if ($messages_request == false)
973 return false;
974
975 // Reset the data?
976 if ($reset == true)
977 return @$smcFunc['db_data_seek']($messages_request, 0);
978
979 // Get the next one... bail if anything goes wrong.
980 $message = $smcFunc['db_fetch_assoc']($messages_request);
981 if (!$message)
982 {
983 if ($type != 'subject')
984 $smcFunc['db_free_result']($messages_request);
985
986 return false;
987 }
988
989 // Use '(no subject)' if none was specified.
990 $message['subject'] = $message['subject'] == '' ? $txt['no_subject'] : $message['subject'];
991
992 // Load the message's information - if it's not there, load the guest information.
993 if (!loadMemberContext($message['id_member_from'], true))
994 {
995 $memberContext[$message['id_member_from']]['name'] = $message['from_name'];
996 $memberContext[$message['id_member_from']]['id'] = 0;
997 // Sometimes the forum sends messages itself (Warnings are an example) - in this case don't label it from a guest.
998 $memberContext[$message['id_member_from']]['group'] = $message['from_name'] == $context['forum_name'] ? '' : $txt['guest_title'];
999 $memberContext[$message['id_member_from']]['link'] = $message['from_name'];
1000 $memberContext[$message['id_member_from']]['email'] = '';
1001 $memberContext[$message['id_member_from']]['show_email'] = showEmailAddress(true, 0);
1002 $memberContext[$message['id_member_from']]['is_guest'] = true;
1003 }
1004 else
1005 {
1006 $memberContext[$message['id_member_from']]['can_view_profile'] = allowedTo('profile_view_any') || ($message['id_member_from'] == $user_info['id'] && allowedTo('profile_view_own'));
1007 $memberContext[$message['id_member_from']]['can_see_warning'] = !isset($context['disabled_fields']['warning_status']) && $memberContext[$message['id_member_from']]['warning_status'] && ($context['user']['can_mod'] || (!empty($modSettings['warning_show']) && ($modSettings['warning_show'] > 1 || $message['id_member_from'] == $user_info['id'])));
1008 }
1009
1010 // Censor all the important text...
1011 censorText($message['body']);
1012 censorText($message['subject']);
1013
1014 // Run UBBC interpreter on the message.
1015 $message['body'] = parse_bbc($message['body'], true, 'pm' . $message['id_pm']);
1016
1017 // Send the array.
1018 $output = array(
1019 'alternate' => $counter % 2,
1020 'id' => $message['id_pm'],
1021 'member' => &$memberContext[$message['id_member_from']],
1022 'subject' => $message['subject'],
1023 'time' => timeformat($message['msgtime']),
1024 'timestamp' => forum_time(true, $message['msgtime']),
1025 'counter' => $counter,
1026 'body' => $message['body'],
1027 'recipients' => &$recipients[$message['id_pm']],
1028 'number_recipients' => count($recipients[$message['id_pm']]['to']),
1029 'labels' => &$context['message_labels'][$message['id_pm']],
1030 'fully_labeled' => count($context['message_labels'][$message['id_pm']]) == count($context['labels']),
1031 'is_replied_to' => &$context['message_replied'][$message['id_pm']],
1032 'is_unread' => &$context['message_unread'][$message['id_pm']],
1033 'is_selected' => !empty($temp_pm_selected) && in_array($message['id_pm'], $temp_pm_selected),
1034 );
1035
1036 $counter++;
1037
1038 return $output;
1039 }
1040
1041 function MessageSearch()
1042 {
1043 global $context, $txt, $scripturl, $modSettings, $smcFunc;
1044
1045 if (isset($_REQUEST['params']))
1046 {
1047 $temp_params = explode('|"|', base64_decode(strtr($_REQUEST['params'], array(' ' => '+'))));
1048 $context['search_params'] = array();
1049 foreach ($temp_params as $i => $data)
1050 {
1051 @list ($k, $v) = explode('|\'|', $data);
1052 $context['search_params'][$k] = $v;
1053 }
1054 }
1055 if (isset($_REQUEST['search']))
1056 $context['search_params']['search'] = un_htmlspecialchars($_REQUEST['search']);
1057
1058 if (isset($context['search_params']['search']))
1059 $context['search_params']['search'] = htmlspecialchars($context['search_params']['search']);
1060 if (isset($context['search_params']['userspec']))
1061 $context['search_params']['userspec'] = htmlspecialchars($context['search_params']['userspec']);
1062
1063 if (!empty($context['search_params']['searchtype']))
1064 $context['search_params']['searchtype'] = 2;
1065
1066 if (!empty($context['search_params']['minage']))
1067 $context['search_params']['minage'] = (int) $context['search_params']['minage'];
1068
1069 if (!empty($context['search_params']['maxage']))
1070 $context['search_params']['maxage'] = (int) $context['search_params']['maxage'];
1071
1072 $context['search_params']['subject_only'] = !empty($context['search_params']['subject_only']);
1073 $context['search_params']['show_complete'] = !empty($context['search_params']['show_complete']);
1074
1075 // Create the array of labels to be searched.
1076 $context['search_labels'] = array();
1077 $searchedLabels = isset($context['search_params']['labels']) && $context['search_params']['labels'] != '' ? explode(',', $context['search_params']['labels']) : array();
1078 foreach ($context['labels'] as $label)
1079 {
1080 $context['search_labels'][] = array(
1081 'id' => $label['id'],
1082 'name' => $label['name'],
1083 'checked' => !empty($searchedLabels) ? in_array($label['id'], $searchedLabels) : true,
1084 );
1085 }
1086
1087 // Are all the labels checked?
1088 $context['check_all'] = empty($searchedLabels) || count($context['search_labels']) == count($searchedLabels);
1089
1090 // Load the error text strings if there were errors in the search.
1091 if (!empty($context['search_errors']))
1092 {
1093 loadLanguage('Errors');
1094 $context['search_errors']['messages'] = array();
1095 foreach ($context['search_errors'] as $search_error => $dummy)
1096 {
1097 if ($search_error == 'messages')
1098 continue;
1099
1100 $context['search_errors']['messages'][] = $txt['error_' . $search_error];
1101 }
1102 }
1103
1104 $context['simple_search'] = isset($context['search_params']['advanced']) ? empty($context['search_params']['advanced']) : !empty($modSettings['simpleSearch']) && !isset($_REQUEST['advanced']);
1105 $context['page_title'] = $txt['pm_search_title'];
1106 $context['sub_template'] = 'search';
1107 $context['linktree'][] = array(
1108 'url' => $scripturl . '?action=pm;sa=search',
1109 'name' => $txt['pm_search_bar_title'],
1110 );
1111 }
1112
1113 function MessageSearch2()
1114 {
1115 global $scripturl, $modSettings, $user_info, $context, $txt;
1116 global $memberContext, $smcFunc;
1117
1118 if (!empty($context['load_average']) && !empty($modSettings['loadavg_search']) && $context['load_average'] >= $modSettings['loadavg_search'])
1119 fatal_lang_error('loadavg_search_disabled', false);
1120
1121 // !!! For the moment force the folder to the inbox.
1122 $context['folder'] = 'inbox';
1123
1124 // Some useful general permissions.
1125 $context['can_send_pm'] = allowedTo('pm_send');
1126
1127 // Some hardcoded veriables that can be tweaked if required.
1128 $maxMembersToSearch = 500;
1129
1130 // Extract all the search parameters.
1131 $search_params = array();
1132 if (isset($_REQUEST['params']))
1133 {
1134 $temp_params = explode('|"|', base64_decode(strtr($_REQUEST['params'], array(' ' => '+'))));
1135 foreach ($temp_params as $i => $data)
1136 {
1137 @list ($k, $v) = explode('|\'|', $data);
1138 $search_params[$k] = $v;
1139 }
1140 }
1141
1142 $context['start'] = isset($_GET['start']) ? (int) $_GET['start'] : 0;
1143
1144 // Store whether simple search was used (needed if the user wants to do another query).
1145 if (!isset($search_params['advanced']))
1146 $search_params['advanced'] = empty($_REQUEST['advanced']) ? 0 : 1;
1147
1148 // 1 => 'allwords' (default, don't set as param) / 2 => 'anywords'.
1149 if (!empty($search_params['searchtype']) || (!empty($_REQUEST['searchtype']) && $_REQUEST['searchtype'] == 2))
1150 $search_params['searchtype'] = 2;
1151
1152 // Minimum age of messages. Default to zero (don't set param in that case).
1153 if (!empty($search_params['minage']) || (!empty($_REQUEST['minage']) && $_REQUEST['minage'] > 0))
1154 $search_params['minage'] = !empty($search_params['minage']) ? (int) $search_params['minage'] : (int) $_REQUEST['minage'];
1155
1156 // Maximum age of messages. Default to infinite (9999 days: param not set).
1157 if (!empty($search_params['maxage']) || (!empty($_REQUEST['maxage']) && $_REQUEST['maxage'] != 9999))
1158 $search_params['maxage'] = !empty($search_params['maxage']) ? (int) $search_params['maxage'] : (int) $_REQUEST['maxage'];
1159
1160 $search_params['subject_only'] = !empty($search_params['subject_only']) || !empty($_REQUEST['subject_only']);
1161 $search_params['show_complete'] = !empty($search_params['show_complete']) || !empty($_REQUEST['show_complete']);
1162
1163 // Default the user name to a wildcard matching every user (*).
1164 if (!empty($search_params['user_spec']) || (!empty($_REQUEST['userspec']) && $_REQUEST['userspec'] != '*'))
1165 $search_params['userspec'] = isset($search_params['userspec']) ? $search_params['userspec'] : $_REQUEST['userspec'];
1166
1167 // This will be full of all kinds of parameters!
1168 $searchq_parameters = array();
1169
1170 // If there's no specific user, then don't mention it in the main query.
1171 if (empty($search_params['userspec']))
1172 $userQuery = '';
1173 else
1174 {
1175 $userString = strtr($smcFunc['htmlspecialchars']($search_params['userspec'], ENT_QUOTES), array('&quot;' => '"'));
1176 $userString = strtr($userString, array('%' => '\%', '_' => '\_', '*' => '%', '?' => '_'));
1177
1178 preg_match_all('~"([^"]+)"~', $userString, $matches);
1179 $possible_users = array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $userString)));
1180
1181 for ($k = 0, $n = count($possible_users); $k < $n; $k++)
1182 {
1183 $possible_users[$k] = trim($possible_users[$k]);
1184
1185 if (strlen($possible_users[$k]) == 0)
1186 unset($possible_users[$k]);
1187 }
1188
1189 // Who matches those criteria?
1190 // !!! This doesn't support sent item searching.
1191 $request = $smcFunc['db_query']('', '
1192 SELECT id_member
1193 FROM {db_prefix}members
1194 WHERE real_name LIKE {raw:real_name_implode}',
1195 array(
1196 'real_name_implode' => '\'' . implode('\' OR real_name LIKE \'', $possible_users) . '\'',
1197 )
1198 );
1199 // Simply do nothing if there're too many members matching the criteria.
1200 if ($smcFunc['db_num_rows']($request) > $maxMembersToSearch)
1201 $userQuery = '';
1202 elseif ($smcFunc['db_num_rows']($request) == 0)
1203 {
1204 $userQuery = 'AND pm.id_member_from = 0 AND (pm.from_name LIKE {raw:guest_user_name_implode})';
1205 $searchq_parameters['guest_user_name_implode'] = '\'' . implode('\' OR pm.from_name LIKE \'', $possible_users) . '\'';
1206 }
1207 else
1208 {
1209 $memberlist = array();
1210 while ($row = $smcFunc['db_fetch_assoc']($request))
1211 $memberlist[] = $row['id_member'];
1212 $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})))';
1213 $searchq_parameters['guest_user_name_implode'] = '\'' . implode('\' OR pm.from_name LIKE \'', $possible_users) . '\'';
1214 $searchq_parameters['member_list'] = $memberlist;
1215 }
1216 $smcFunc['db_free_result']($request);
1217 }
1218
1219 // Setup the sorting variables...
1220 // !!! Add more in here!
1221 $sort_columns = array(
1222 'pm.id_pm',
1223 );
1224 if (empty($search_params['sort']) && !empty($_REQUEST['sort']))
1225 list ($search_params['sort'], $search_params['sort_dir']) = array_pad(explode('|', $_REQUEST['sort']), 2, '');
1226 $search_params['sort'] = !empty($search_params['sort']) && in_array($search_params['sort'], $sort_columns) ? $search_params['sort'] : 'pm.id_pm';
1227 $search_params['sort_dir'] = !empty($search_params['sort_dir']) && $search_params['sort_dir'] == 'asc' ? 'asc' : 'desc';
1228
1229 // Sort out any labels we may be searching by.
1230 $labelQuery = '';
1231 if ($context['folder'] == 'inbox' && !empty($search_params['advanced']) && $context['currently_using_labels'])
1232 {
1233 // Came here from pagination? Put them back into $_REQUEST for sanitization.
1234 if (isset($search_params['labels']))
1235 $_REQUEST['searchlabel'] = explode(',', $search_params['labels']);
1236
1237 // Assuming we have some labels - make them all integers.
1238 if (!empty($_REQUEST['searchlabel']) && is_array($_REQUEST['searchlabel']))
1239 {
1240 foreach ($_REQUEST['searchlabel'] as $key => $id)
1241 $_REQUEST['searchlabel'][$key] = (int) $id;
1242 }
1243 else
1244 $_REQUEST['searchlabel'] = array();
1245
1246 // Now that everything is cleaned up a bit, make the labels a param.
1247 $search_params['labels'] = implode(',', $_REQUEST['searchlabel']);
1248
1249 // No labels selected? That must be an error!
1250 if (empty($_REQUEST['searchlabel']))
1251 $context['search_errors']['no_labels_selected'] = true;
1252 // Otherwise prepare the query!
1253 elseif (count($_REQUEST['searchlabel']) != count($context['labels']))
1254 {
1255 $labelQuery = '
1256 AND {raw:label_implode}';
1257
1258 $labelStatements = array();
1259 foreach ($_REQUEST['searchlabel'] as $label)
1260 $labelStatements[] = $smcFunc['db_quote']('FIND_IN_SET({string:label}, pmr.labels) != 0', array(
1261 'label' => $label,
1262 ));
1263
1264 $searchq_parameters['label_implode'] = '(' . implode(' OR ', $labelStatements) . ')';
1265 }
1266 }
1267
1268 // What are we actually searching for?
1269 $search_params['search'] = !empty($search_params['search']) ? $search_params['search'] : (isset($_REQUEST['search']) ? $_REQUEST['search'] : '');
1270 // If we ain't got nothing - we should error!
1271 if (!isset($search_params['search']) || $search_params['search'] == '')
1272 $context['search_errors']['invalid_search_string'] = true;
1273
1274 // Extract phrase parts first (e.g. some words "this is a phrase" some more words.)
1275 preg_match_all('~(?:^|\s)([-]?)"([^"]+)"(?:$|\s)~' . ($context['utf8'] ? 'u' : ''), $search_params['search'], $matches, PREG_PATTERN_ORDER);
1276 $searchArray = $matches[2];
1277
1278 // Remove the phrase parts and extract the words.
1279 $tempSearch = explode(' ', preg_replace('~(?:^|\s)(?:[-]?)"(?:[^"]+)"(?:$|\s)~' . ($context['utf8'] ? 'u' : ''), ' ', $search_params['search']));
1280
1281 // A minus sign in front of a word excludes the word.... so...
1282 $excludedWords = array();
1283
1284 // .. first, we check for things like -"some words", but not "-some words".
1285 foreach ($matches[1] as $index => $word)
1286 if ($word == '-')
1287 {
1288 $word = $smcFunc['strtolower'](trim($searchArray[$index]));
1289 if (strlen($word) > 0)
1290 $excludedWords[] = $word;
1291 unset($searchArray[$index]);
1292 }
1293
1294 // Now we look for -test, etc.... normaller.
1295 foreach ($tempSearch as $index => $word)
1296 if (strpos(trim($word), '-') === 0)
1297 {
1298 $word = substr($smcFunc['strtolower'](trim($word)), 1);
1299 if (strlen($word) > 0)
1300 $excludedWords[] = $word;
1301 unset($tempSearch[$index]);
1302 }
1303
1304 $searchArray = array_merge($searchArray, $tempSearch);
1305
1306 // Trim everything and make sure there are no words that are the same.
1307 foreach ($searchArray as $index => $value)
1308 {
1309 $searchArray[$index] = $smcFunc['strtolower'](trim($value));
1310 if ($searchArray[$index] == '')
1311 unset($searchArray[$index]);
1312 else
1313 {
1314 // Sort out entities first.
1315 $searchArray[$index] = $smcFunc['htmlspecialchars']($searchArray[$index]);
1316 }
1317 }
1318 $searchArray = array_unique($searchArray);
1319
1320 // Create an array of replacements for highlighting.
1321 $context['mark'] = array();
1322 foreach ($searchArray as $word)
1323 $context['mark'][$word] = '<strong class="highlight">' . $word . '</strong>';
1324
1325 // This contains *everything*
1326 $searchWords = array_merge($searchArray, $excludedWords);
1327
1328 // Make sure at least one word is being searched for.
1329 if (empty($searchArray))
1330 $context['search_errors']['invalid_search_string'] = true;
1331
1332 // Sort out the search query so the user can edit it - if they want.
1333 $context['search_params'] = $search_params;
1334 if (isset($context['search_params']['search']))
1335 $context['search_params']['search'] = htmlspecialchars($context['search_params']['search']);
1336 if (isset($context['search_params']['userspec']))
1337 $context['search_params']['userspec'] = htmlspecialchars($context['search_params']['userspec']);
1338
1339 // Now we have all the parameters, combine them together for pagination and the like...
1340 $context['params'] = array();
1341 foreach ($search_params as $k => $v)
1342 $context['params'][] = $k . '|\'|' . $v;
1343 $context['params'] = base64_encode(implode('|"|', $context['params']));
1344
1345 // Compile the subject query part.
1346 $andQueryParts = array();
1347
1348 foreach ($searchWords as $index => $word)
1349 {
1350 if ($word == '')
1351 continue;
1352
1353 if ($search_params['subject_only'])
1354 $andQueryParts[] = 'pm.subject' . (in_array($word, $excludedWords) ? ' NOT' : '') . ' LIKE {string:search_' . $index . '}';
1355 else
1356 $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 . '})';
1357 $searchq_parameters['search_' . $index] = '%' . strtr($word, array('_' => '\\_', '%' => '\\%')) . '%';
1358 }
1359
1360 $searchQuery = ' 1=1';
1361 if (!empty($andQueryParts))
1362 $searchQuery = implode(!empty($search_params['searchtype']) && $search_params['searchtype'] == 2 ? ' OR ' : ' AND ', $andQueryParts);
1363
1364 // Age limits?
1365 $timeQuery = '';
1366 if (!empty($search_params['minage']))
1367 $timeQuery .= ' AND pm.msgtime < ' . (time() - $search_params['minage'] * 86400);
1368 if (!empty($search_params['maxage']))
1369 $timeQuery .= ' AND pm.msgtime > ' . (time() - $search_params['maxage'] * 86400);
1370
1371 // If we have errors - return back to the first screen...
1372 if (!empty($context['search_errors']))
1373 {
1374 $_REQUEST['params'] = $context['params'];
1375 return MessageSearch();
1376 }
1377
1378 // Get the amount of results.
1379 $request = $smcFunc['db_query']('', '
1380 SELECT COUNT(*)
1381 FROM {db_prefix}pm_recipients AS pmr
1382 INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)
1383 WHERE ' . ($context['folder'] == 'inbox' ? '
1384 pmr.id_member = {int:current_member}
1385 AND pmr.deleted = {int:not_deleted}' : '
1386 pm.id_member_from = {int:current_member}
1387 AND pm.deleted_by_sender = {int:not_deleted}') . '
1388 ' . $userQuery . $labelQuery . $timeQuery . '
1389 AND (' . $searchQuery . ')',
1390 array_merge($searchq_parameters, array(
1391 'current_member' => $user_info['id'],
1392 'not_deleted' => 0,
1393 ))
1394 );
1395 list ($numResults) = $smcFunc['db_fetch_row']($request);
1396 $smcFunc['db_free_result']($request);
1397
1398 // Get all the matching messages... using standard search only (No caching and the like!)
1399 // !!! This doesn't support sent item searching yet.
1400 $request = $smcFunc['db_query']('', '
1401 SELECT pm.id_pm, pm.id_pm_head, pm.id_member_from
1402 FROM {db_prefix}pm_recipients AS pmr
1403 INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)
1404 WHERE ' . ($context['folder'] == 'inbox' ? '
1405 pmr.id_member = {int:current_member}
1406 AND pmr.deleted = {int:not_deleted}' : '
1407 pm.id_member_from = {int:current_member}
1408 AND pm.deleted_by_sender = {int:not_deleted}') . '
1409 ' . $userQuery . $labelQuery . $timeQuery . '
1410 AND (' . $searchQuery . ')
1411 ORDER BY ' . $search_params['sort'] . ' ' . $search_params['sort_dir'] . '
1412 LIMIT ' . $context['start'] . ', ' . $modSettings['search_results_per_page'],
1413 array_merge($searchq_parameters, array(
1414 'current_member' => $user_info['id'],
1415 'not_deleted' => 0,
1416 ))
1417 );
1418 $foundMessages = array();
1419 $posters = array();
1420 $head_pms = array();
1421 while ($row = $smcFunc['db_fetch_assoc']($request))
1422 {
1423 $foundMessages[] = $row['id_pm'];
1424 $posters[] = $row['id_member_from'];
1425 $head_pms[$row['id_pm']] = $row['id_pm_head'];
1426 }
1427 $smcFunc['db_free_result']($request);
1428
1429 // Find the real head pms!
1430 if ($context['display_mode'] == 2 && !empty($head_pms))
1431 {
1432 $request = $smcFunc['db_query']('', '
1433 SELECT MAX(pm.id_pm) AS id_pm, pm.id_pm_head
1434 FROM {db_prefix}personal_messages AS pm
1435 INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)
1436 WHERE pm.id_pm_head IN ({array_int:head_pms})
1437 AND pmr.id_member = {int:current_member}
1438 AND pmr.deleted = {int:not_deleted}
1439 GROUP BY pm.id_pm_head
1440 LIMIT {int:limit}',
1441 array(
1442 'head_pms' => array_unique($head_pms),
1443 'current_member' => $user_info['id'],
1444 'not_deleted' => 0,
1445 'limit' => count($head_pms),
1446 )
1447 );
1448 $real_pm_ids = array();
1449 while ($row = $smcFunc['db_fetch_assoc']($request))
1450 $real_pm_ids[$row['id_pm_head']] = $row['id_pm'];
1451 $smcFunc['db_free_result']($request);
1452 }
1453
1454 // Load the users...
1455 $posters = array_unique($posters);
1456 if (!empty($posters))
1457 loadMemberData($posters);
1458
1459 // Sort out the page index.
1460 $context['page_index'] = constructPageIndex($scripturl . '?action=pm;sa=search2;params=' . $context['params'], $_GET['start'], $numResults, $modSettings['search_results_per_page'], false);
1461
1462 $context['message_labels'] = array();
1463 $context['message_replied'] = array();
1464 $context['personal_messages'] = array();
1465
1466 if (!empty($foundMessages))
1467 {
1468 // Now get recipients (but don't include bcc-recipients for your inbox, you're not supposed to know :P!)
1469 $request = $smcFunc['db_query']('', '
1470 SELECT
1471 pmr.id_pm, mem_to.id_member AS id_member_to, mem_to.real_name AS to_name,
1472 pmr.bcc, pmr.labels, pmr.is_read
1473 FROM {db_prefix}pm_recipients AS pmr
1474 LEFT JOIN {db_prefix}members AS mem_to ON (mem_to.id_member = pmr.id_member)
1475 WHERE pmr.id_pm IN ({array_int:message_list})',
1476 array(
1477 'message_list' => $foundMessages,
1478 )
1479 );
1480 while ($row = $smcFunc['db_fetch_assoc']($request))
1481 {
1482 if ($context['folder'] == 'sent' || empty($row['bcc']))
1483 $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>';
1484
1485 if ($row['id_member_to'] == $user_info['id'] && $context['folder'] != 'sent')
1486 {
1487 $context['message_replied'][$row['id_pm']] = $row['is_read'] & 2;
1488
1489 $row['labels'] = $row['labels'] == '' ? array() : explode(',', $row['labels']);
1490 // This is a special need for linking to messages.
1491 foreach ($row['labels'] as $v)
1492 {
1493 if (isset($context['labels'][(int) $v]))
1494 $context['message_labels'][$row['id_pm']][(int) $v] = array('id' => $v, 'name' => $context['labels'][(int) $v]['name']);
1495
1496 // Here we find the first label on a message - for linking to posts in results
1497 if (!isset($context['first_label'][$row['id_pm']]) && !in_array('-1', $row['labels']))
1498 $context['first_label'][$row['id_pm']] = (int) $v;
1499 }
1500 }
1501 }
1502
1503 // Prepare the query for the callback!
1504 $request = $smcFunc['db_query']('', '
1505 SELECT pm.id_pm, pm.subject, pm.id_member_from, pm.body, pm.msgtime, pm.from_name
1506 FROM {db_prefix}personal_messages AS pm
1507 WHERE pm.id_pm IN ({array_int:message_list})
1508 ORDER BY ' . $search_params['sort'] . ' ' . $search_params['sort_dir'] . '
1509 LIMIT ' . count($foundMessages),
1510 array(
1511 'message_list' => $foundMessages,
1512 )
1513 );
1514 $counter = 0;
1515 while ($row = $smcFunc['db_fetch_assoc']($request))
1516 {
1517 // If there's no message subject, use the default.
1518 $row['subject'] = $row['subject'] == '' ? $txt['no_subject'] : $row['subject'];
1519
1520 // Load this posters context info, if it ain't there then fill in the essentials...
1521 if (!loadMemberContext($row['id_member_from'], true))
1522 {
1523 $memberContext[$row['id_member_from']]['name'] = $row['from_name'];
1524 $memberContext[$row['id_member_from']]['id'] = 0;
1525 $memberContext[$row['id_member_from']]['group'] = $txt['guest_title'];
1526 $memberContext[$row['id_member_from']]['link'] = $row['from_name'];
1527 $memberContext[$row['id_member_from']]['email'] = '';
1528 $memberContext[$row['id_member_from']]['show_email'] = showEmailAddress(true, 0);
1529 $memberContext[$row['id_member_from']]['is_guest'] = true;
1530 }
1531
1532 // Censor anything we don't want to see...
1533 censorText($row['body']);
1534 censorText($row['subject']);
1535
1536 // Parse out any BBC...
1537 $row['body'] = parse_bbc($row['body'], true, 'pm' . $row['id_pm']);
1538
1539 $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'];
1540 $context['personal_messages'][] = array(
1541 'id' => $row['id_pm'],
1542 'member' => &$memberContext[$row['id_member_from']],
1543 'subject' => $row['subject'],
1544 'body' => $row['body'],
1545 'time' => timeformat($row['msgtime']),
1546 'recipients' => &$recipients[$row['id_pm']],
1547 'labels' => &$context['message_labels'][$row['id_pm']],
1548 'fully_labeled' => count($context['message_labels'][$row['id_pm']]) == count($context['labels']),
1549 'is_replied_to' => &$context['message_replied'][$row['id_pm']],
1550 'href' => $href,
1551 'link' => '<a href="' . $href . '">' . $row['subject'] . '</a>',
1552 'counter' => ++$counter,
1553 );
1554 }
1555 $smcFunc['db_free_result']($request);
1556 }
1557
1558 // Finish off the context.
1559 $context['page_title'] = $txt['pm_search_title'];
1560 $context['sub_template'] = 'search_results';
1561 $context['menu_data_' . $context['pm_menu_id']]['current_area'] = 'search';
1562 $context['linktree'][] = array(
1563 'url' => $scripturl . '?action=pm;sa=search',
1564 'name' => $txt['pm_search_bar_title'],
1565 );
1566 }
1567
1568 // Send a new message?
1569 function MessagePost()
1570 {
1571 global $txt, $sourcedir, $scripturl, $modSettings;
1572 global $context, $options, $smcFunc, $language, $user_info;
1573
1574 isAllowedTo('pm_send');
1575
1576 loadLanguage('PersonalMessage');
1577 // Just in case it was loaded from somewhere else.
1578 if (!WIRELESS)
1579 {
1580 loadTemplate('PersonalMessage');
1581 $context['sub_template'] = 'send';
1582 }
1583
1584 // Extract out the spam settings - cause it's neat.
1585 list ($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']);
1586
1587 // Set the title...
1588 $context['page_title'] = $txt['send_message'];
1589
1590 $context['reply'] = isset($_REQUEST['pmsg']) || isset($_REQUEST['quote']);
1591
1592 // Check whether we've gone over the limit of messages we can send per hour.
1593 if (!empty($modSettings['pm_posts_per_hour']) && !allowedTo(array('admin_forum', 'moderate_forum', 'send_mail')) && $user_info['mod_cache']['bq'] == '0=1' && $user_info['mod_cache']['gq'] == '0=1')
1594 {
1595 // How many messages have they sent this last hour?
1596 $request = $smcFunc['db_query']('', '
1597 SELECT COUNT(pr.id_pm) AS post_count
1598 FROM {db_prefix}personal_messages AS pm
1599 INNER JOIN {db_prefix}pm_recipients AS pr ON (pr.id_pm = pm.id_pm)
1600 WHERE pm.id_member_from = {int:current_member}
1601 AND pm.msgtime > {int:msgtime}',
1602 array(
1603 'current_member' => $user_info['id'],
1604 'msgtime' => time() - 3600,
1605 )
1606 );
1607 list ($postCount) = $smcFunc['db_fetch_row']($request);
1608 $smcFunc['db_free_result']($request);
1609
1610 if (!empty($postCount) && $postCount >= $modSettings['pm_posts_per_hour'])
1611 fatal_lang_error('pm_too_many_per_hour', true, array($modSettings['pm_posts_per_hour']));
1612 }
1613
1614 // Quoting/Replying to a message?
1615 if (!empty($_REQUEST['pmsg']))
1616 {
1617 $pmsg = (int) $_REQUEST['pmsg'];
1618
1619 // Make sure this is yours.
1620 if (!isAccessiblePM($pmsg))
1621 fatal_lang_error('no_access', false);
1622
1623 // Work out whether this is one you've received?
1624 $request = $smcFunc['db_query']('', '
1625 SELECT
1626 id_pm
1627 FROM {db_prefix}pm_recipients
1628 WHERE id_pm = {int:id_pm}
1629 AND id_member = {int:current_member}
1630 LIMIT 1',
1631 array(
1632 'current_member' => $user_info['id'],
1633 'id_pm' => $pmsg,
1634 )
1635 );
1636 $isReceived = $smcFunc['db_num_rows']($request) != 0;
1637 $smcFunc['db_free_result']($request);
1638
1639 // Get the quoted message (and make sure you're allowed to see this quote!).
1640 $request = $smcFunc['db_query']('', '
1641 SELECT
1642 pm.id_pm, CASE WHEN pm.id_pm_head = {int:id_pm_head_empty} THEN pm.id_pm ELSE pm.id_pm_head END AS pm_head,
1643 pm.body, pm.subject, pm.msgtime, mem.member_name, IFNULL(mem.id_member, 0) AS id_member,
1644 IFNULL(mem.real_name, pm.from_name) AS real_name
1645 FROM {db_prefix}personal_messages AS pm' . (!$isReceived ? '' : '
1646 INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = {int:id_pm})') . '
1647 LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from)
1648 WHERE pm.id_pm = {int:id_pm}' . (!$isReceived ? '
1649 AND pm.id_member_from = {int:current_member}' : '
1650 AND pmr.id_member = {int:current_member}') . '
1651 LIMIT 1',
1652 array(
1653 'current_member' => $user_info['id'],
1654 'id_pm_head_empty' => 0,
1655 'id_pm' => $pmsg,
1656 )
1657 );
1658 if ($smcFunc['db_num_rows']($request) == 0)
1659 fatal_lang_error('pm_not_yours', false);
1660 $row_quoted = $smcFunc['db_fetch_assoc']($request);
1661 $smcFunc['db_free_result']($request);
1662
1663 // Censor the message.
1664 censorText($row_quoted['subject']);
1665 censorText($row_quoted['body']);
1666
1667 // Add 'Re: ' to it....
1668 if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix')))
1669 {
1670 if ($language === $user_info['language'])
1671 $context['response_prefix'] = $txt['response_prefix'];
1672 else
1673 {
1674 loadLanguage('index', $language, false);
1675 $context['response_prefix'] = $txt['response_prefix'];
1676 loadLanguage('index');
1677 }
1678 cache_put_data('response_prefix', $context['response_prefix'], 600);
1679 }
1680 $form_subject = $row_quoted['subject'];
1681 if ($context['reply'] && trim($context['response_prefix']) != '' && $smcFunc['strpos']($form_subject, trim($context['response_prefix'])) !== 0)
1682 $form_subject = $context['response_prefix'] . $form_subject;
1683
1684 if (isset($_REQUEST['quote']))
1685 {
1686 // Remove any nested quotes and <br />...
1687 $form_message = preg_replace('~<br ?/?' . '>~i', "\n", $row_quoted['body']);
1688 if (!empty($modSettings['removeNestedQuotes']))
1689 $form_message = preg_replace(array('~\n?\[quote.*?\].+?\[/quote\]\n?~is', '~^\n~', '~\[/quote\]~'), '', $form_message);
1690 if (empty($row_quoted['id_member']))
1691 $form_message = '[quote author=&quot;' . $row_quoted['real_name'] . '&quot;]' . "\n" . $form_message . "\n" . '[/quote]';
1692 else
1693 $form_message = '[quote author=' . $row_quoted['real_name'] . ' link=action=profile;u=' . $row_quoted['id_member'] . ' date=' . $row_quoted['msgtime'] . ']' . "\n" . $form_message . "\n" . '[/quote]';
1694 }
1695 else
1696 $form_message = '';
1697
1698 // Do the BBC thang on the message.
1699 $row_quoted['body'] = parse_bbc($row_quoted['body'], true, 'pm' . $row_quoted['id_pm']);
1700
1701 // Set up the quoted message array.
1702 $context['quoted_message'] = array(
1703 'id' => $row_quoted['id_pm'],
1704 'pm_head' => $row_quoted['pm_head'],
1705 'member' => array(
1706 'name' => $row_quoted['real_name'],
1707 'username' => $row_quoted['member_name'],
1708 'id' => $row_quoted['id_member'],
1709 'href' => !empty($row_quoted['id_member']) ? $scripturl . '?action=profile;u=' . $row_quoted['id_member'] : '',
1710 'link' => !empty($row_quoted['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row_quoted['id_member'] . '">' . $row_quoted['real_name'] . '</a>' : $row_quoted['real_name'],
1711 ),
1712 'subject' => $row_quoted['subject'],
1713 'time' => timeformat($row_quoted['msgtime']),
1714 'timestamp' => forum_time(true, $row_quoted['msgtime']),
1715 'body' => $row_quoted['body']
1716 );
1717 }
1718 else
1719 {
1720 $context['quoted_message'] = false;
1721 $form_subject = '';
1722 $form_message = '';
1723 }
1724
1725 $context['recipients'] = array(
1726 'to' => array(),
1727 'bcc' => array(),
1728 );
1729
1730 // Sending by ID? Replying to all? Fetch the real_name(s).
1731 if (isset($_REQUEST['u']))
1732 {
1733 // If the user is replying to all, get all the other members this was sent to..
1734 if ($_REQUEST['u'] == 'all' && isset($row_quoted))
1735 {
1736 // Firstly, to reply to all we clearly already have $row_quoted - so have the original member from.
1737 if ($row_quoted['id_member'] != $user_info['id'])
1738 $context['recipients']['to'][] = array(
1739 'id' => $row_quoted['id_member'],
1740 'name' => htmlspecialchars($row_quoted['real_name']),
1741 );
1742
1743 // Now to get the others.
1744 $request = $smcFunc['db_query']('', '
1745 SELECT mem.id_member, mem.real_name
1746 FROM {db_prefix}pm_recipients AS pmr
1747 INNER JOIN {db_prefix}members AS mem ON (mem.id_member = pmr.id_member)
1748 WHERE pmr.id_pm = {int:id_pm}
1749 AND pmr.id_member != {int:current_member}
1750 AND pmr.bcc = {int:not_bcc}',
1751 array(
1752 'current_member' => $user_info['id'],
1753 'id_pm' => $pmsg,
1754 'not_bcc' => 0,
1755 )
1756 );
1757 while ($row = $smcFunc['db_fetch_assoc']($request))
1758 $context['recipients']['to'][] = array(
1759 'id' => $row['id_member'],
1760 'name' => $row['real_name'],
1761 );
1762 $smcFunc['db_free_result']($request);
1763 }
1764 else
1765 {
1766 $_REQUEST['u'] = explode(',', $_REQUEST['u']);
1767 foreach ($_REQUEST['u'] as $key => $uID)
1768 $_REQUEST['u'][$key] = (int) $uID;
1769
1770 $_REQUEST['u'] = array_unique($_REQUEST['u']);
1771
1772 $request = $smcFunc['db_query']('', '
1773 SELECT id_member, real_name
1774 FROM {db_prefix}members
1775 WHERE id_member IN ({array_int:member_list})
1776 LIMIT ' . count($_REQUEST['u']),
1777 array(
1778 'member_list' => $_REQUEST['u'],
1779 )
1780 );
1781 while ($row = $smcFunc['db_fetch_assoc']($request))
1782 $context['recipients']['to'][] = array(
1783 'id' => $row['id_member'],
1784 'name' => $row['real_name'],
1785 );
1786 $smcFunc['db_free_result']($request);
1787 }
1788
1789 // Get a literal name list in case the user has JavaScript disabled.
1790 $names = array();
1791 foreach ($context['recipients']['to'] as $to)
1792 $names[] = $to['name'];
1793 $context['to_value'] = empty($names) ? '' : '&quot;' . implode('&quot;, &quot;', $names) . '&quot;';
1794 }
1795 else
1796 $context['to_value'] = '';
1797
1798 // Set the defaults...
1799 $context['subject'] = $form_subject != '' ? $form_subject : $txt['no_subject'];
1800 $context['message'] = str_replace(array('"', '<', '>', '&nbsp;'), array('&quot;', '&lt;', '&gt;', ' '), $form_message);
1801 $context['post_error'] = array();
1802 $context['copy_to_outbox'] = !empty($options['copy_to_outbox']);
1803
1804 // And build the link tree.
1805 $context['linktree'][] = array(
1806 'url' => $scripturl . '?action=pm;sa=send',
1807 'name' => $txt['new_message']
1808 );
1809
1810 $modSettings['disable_wysiwyg'] = !empty($modSettings['disable_wysiwyg']) || empty($modSettings['enableBBC']);
1811
1812 // Needed for the WYSIWYG editor.
1813 require_once($sourcedir . '/Subs-Editor.php');
1814
1815 // Now create the editor.
1816 $editorOptions = array(
1817 'id' => 'message',
1818 'value' => $context['message'],
1819 'height' => '175px',
1820 'width' => '100%',
1821 'labels' => array(
1822 'post_button' => $txt['send_message'],
1823 ),
1824 );
1825 create_control_richedit($editorOptions);
1826
1827 // Store the ID for old compatibility.
1828 $context['post_box_name'] = $editorOptions['id'];
1829
1830 $context['bcc_value'] = '';
1831
1832 $context['require_verification'] = !$user_info['is_admin'] && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification'];
1833 if ($context['require_verification'])
1834 {
1835 $verificationOptions = array(
1836 'id' => 'pm',
1837 );
1838 $context['require_verification'] = create_control_verification($verificationOptions);
1839 $context['visual_verification_id'] = $verificationOptions['id'];
1840 }
1841
1842 // Register this form and get a sequence number in $context.
1843 checkSubmitOnce('register');
1844 }
1845
1846 // An error in the message...
1847 function messagePostError($error_types, $named_recipients, $recipient_ids = array())
1848 {
1849 global $txt, $context, $scripturl, $modSettings;
1850 global $smcFunc, $user_info, $sourcedir;
1851
1852 $context['menu_data_' . $context['pm_menu_id']]['current_area'] = 'send';
1853
1854 if (!WIRELESS)
1855 $context['sub_template'] = 'send';
1856
1857 $context['page_title'] = $txt['send_message'];
1858
1859 // Got some known members?
1860 $context['recipients'] = array(
1861 'to' => array(),
1862 'bcc' => array(),
1863 );
1864 if (!empty($recipient_ids['to']) || !empty($recipient_ids['bcc']))
1865 {
1866 $allRecipients = array_merge($recipient_ids['to'], $recipient_ids['bcc']);
1867
1868 $request = $smcFunc['db_query']('', '
1869 SELECT id_member, real_name
1870 FROM {db_prefix}members
1871 WHERE id_member IN ({array_int:member_list})',
1872 array(
1873 'member_list' => $allRecipients,
1874 )
1875 );
1876 while ($row = $smcFunc['db_fetch_assoc']($request))
1877 {
1878 $recipientType = in_array($row['id_member'], $recipient_ids['bcc']) ? 'bcc' : 'to';
1879 $context['recipients'][$recipientType][] = array(
1880 'id' => $row['id_member'],
1881 'name' => $row['real_name'],
1882 );
1883 }
1884 $smcFunc['db_free_result']($request);
1885 }
1886
1887 // Set everything up like before....
1888 $context['subject'] = isset($_REQUEST['subject']) ? $smcFunc['htmlspecialchars']($_REQUEST['subject']) : '';
1889 $context['message'] = isset($_REQUEST['message']) ? str_replace(array(' '), array('&nbsp; '), $smcFunc['htmlspecialchars']($_REQUEST['message'])) : '';
1890 $context['copy_to_outbox'] = !empty($_REQUEST['outbox']);
1891 $context['reply'] = !empty($_REQUEST['replied_to']);
1892
1893 if ($context['reply'])
1894 {
1895 $_REQUEST['replied_to'] = (int) $_REQUEST['replied_to'];
1896
1897 $request = $smcFunc['db_query']('', '
1898 SELECT
1899 pm.id_pm, CASE WHEN pm.id_pm_head = {int:no_id_pm_head} THEN pm.id_pm ELSE pm.id_pm_head END AS pm_head,
1900 pm.body, pm.subject, pm.msgtime, mem.member_name, IFNULL(mem.id_member, 0) AS id_member,
1901 IFNULL(mem.real_name, pm.from_name) AS real_name
1902 FROM {db_prefix}personal_messages AS pm' . ($context['folder'] == 'sent' ? '' : '
1903 INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = {int:replied_to})') . '
1904 LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from)
1905 WHERE pm.id_pm = {int:replied_to}' . ($context['folder'] == 'sent' ? '
1906 AND pm.id_member_from = {int:current_member}' : '
1907 AND pmr.id_member = {int:current_member}') . '
1908 LIMIT 1',
1909 array(
1910 'current_member' => $user_info['id'],
1911 'no_id_pm_head' => 0,
1912 'replied_to' => $_REQUEST['replied_to'],
1913 )
1914 );
1915 if ($smcFunc['db_num_rows']($request) == 0)
1916 fatal_lang_error('pm_not_yours', false);
1917 $row_quoted = $smcFunc['db_fetch_assoc']($request);
1918 $smcFunc['db_free_result']($request);
1919
1920 censorText($row_quoted['subject']);
1921 censorText($row_quoted['body']);
1922
1923 $context['quoted_message'] = array(
1924 'id' => $row_quoted['id_pm'],
1925 'pm_head' => $row_quoted['pm_head'],
1926 'member' => array(
1927 'name' => $row_quoted['real_name'],
1928 'username' => $row_quoted['member_name'],
1929 'id' => $row_quoted['id_member'],
1930 'href' => !empty($row_quoted['id_member']) ? $scripturl . '?action=profile;u=' . $row_quoted['id_member'] : '',
1931 'link' => !empty($row_quoted['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row_quoted['id_member'] . '">' . $row_quoted['real_name'] . '</a>' : $row_quoted['real_name'],
1932 ),
1933 'subject' => $row_quoted['subject'],
1934 'time' => timeformat($row_quoted['msgtime']),
1935 'timestamp' => forum_time(true, $row_quoted['msgtime']),
1936 'body' => parse_bbc($row_quoted['body'], true, 'pm' . $row_quoted['id_pm']),
1937 );
1938 }
1939
1940 // Build the link tree....
1941 $context['linktree'][] = array(
1942 'url' => $scripturl . '?action=pm;sa=send',
1943 'name' => $txt['new_message']
1944 );
1945
1946 // Set each of the errors for the template.
1947 loadLanguage('Errors');
1948 $context['post_error'] = array(
1949 'messages' => array(),
1950 );
1951 foreach ($error_types as $error_type)
1952 {
1953 $context['post_error'][$error_type] = true;
1954 if (isset($txt['error_' . $error_type]))
1955 {
1956 if ($error_type == 'long_message')
1957 $txt['error_' . $error_type] = sprintf($txt['error_' . $error_type], $modSettings['max_messageLength']);
1958 $context['post_error']['messages'][] = $txt['error_' . $error_type];
1959 }
1960 }
1961
1962 // We need to load the editor once more.
1963 require_once($sourcedir . '/Subs-Editor.php');
1964
1965 // Create it...
1966 $editorOptions = array(
1967 'id' => 'message',
1968 'value' => $context['message'],
1969 'width' => '90%',
1970 'labels' => array(
1971 'post_button' => $txt['send_message'],
1972 ),
1973 );
1974 create_control_richedit($editorOptions);
1975
1976 // ... and store the ID again...
1977 $context['post_box_name'] = $editorOptions['id'];
1978
1979 // Check whether we need to show the code again.
1980 $context['require_verification'] = !$user_info['is_admin'] && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification'];
1981 if ($context['require_verification'])
1982 {
1983 require_once($sourcedir . '/Subs-Editor.php');
1984 $verificationOptions = array(
1985 'id' => 'pm',
1986 );
1987 $context['require_verification'] = create_control_verification($verificationOptions);
1988 $context['visual_verification_id'] = $verificationOptions['id'];
1989 }
1990
1991 $context['to_value'] = empty($named_recipients['to']) ? '' : '&quot;' . implode('&quot;, &quot;', $named_recipients['to']) . '&quot;';
1992 $context['bcc_value'] = empty($named_recipients['bcc']) ? '' : '&quot;' . implode('&quot;, &quot;', $named_recipients['bcc']) . '&quot;';
1993
1994 // No check for the previous submission is needed.
1995 checkSubmitOnce('free');
1996
1997 // Acquire a new form sequence number.
1998 checkSubmitOnce('register');
1999 }
2000
2001 // Send it!
2002 function MessagePost2()
2003 {
2004 global $txt, $context, $sourcedir;
2005 global $user_info, $modSettings, $scripturl, $smcFunc;
2006
2007 isAllowedTo('pm_send');
2008 require_once($sourcedir . '/Subs-Auth.php');
2009
2010 loadLanguage('PersonalMessage', '', false);
2011
2012 // Extract out the spam settings - it saves database space!
2013 list ($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']);
2014
2015 // Check whether we've gone over the limit of messages we can send per hour - fatal error if fails!
2016 if (!empty($modSettings['pm_posts_per_hour']) && !allowedTo(array('admin_forum', 'moderate_forum', 'send_mail')) && $user_info['mod_cache']['bq'] == '0=1' && $user_info['mod_cache']['gq'] == '0=1')
2017 {
2018 // How many have they sent this last hour?
2019 $request = $smcFunc['db_query']('', '
2020 SELECT COUNT(pr.id_pm) AS post_count
2021 FROM {db_prefix}personal_messages AS pm
2022 INNER JOIN {db_prefix}pm_recipients AS pr ON (pr.id_pm = pm.id_pm)
2023 WHERE pm.id_member_from = {int:current_member}
2024 AND pm.msgtime > {int:msgtime}',
2025 array(
2026 'current_member' => $user_info['id'],
2027 'msgtime' => time() - 3600,
2028 )
2029 );
2030 list ($postCount) = $smcFunc['db_fetch_row']($request);
2031 $smcFunc['db_free_result']($request);
2032
2033 if (!empty($postCount) && $postCount >= $modSettings['pm_posts_per_hour'])
2034 fatal_lang_error('pm_too_many_per_hour', true, array($modSettings['pm_posts_per_hour']));
2035 }
2036
2037 // If we came from WYSIWYG then turn it back into BBC regardless.
2038 if (!empty($_POST['message_mode']) && isset($_POST['message']))
2039 {
2040 require_once($sourcedir . '/Subs-Editor.php');
2041 $_POST['message'] = html_to_bbc($_POST['message']);
2042
2043 // We need to unhtml it now as it gets done shortly.
2044 $_POST['message'] = un_htmlspecialchars($_POST['message']);
2045
2046 // We need this in case of errors etc.
2047 $_REQUEST['message'] = $_POST['message'];
2048 }
2049
2050 // Initialize the errors we're about to make.
2051 $post_errors = array();
2052
2053 // If your session timed out, show an error, but do allow to re-submit.
2054 if (checkSession('post', '', false) != '')
2055 $post_errors[] = 'session_timeout';
2056
2057 $_REQUEST['subject'] = isset($_REQUEST['subject']) ? trim($_REQUEST['subject']) : '';
2058 $_REQUEST['to'] = empty($_POST['to']) ? (empty($_GET['to']) ? '' : $_GET['to']) : $_POST['to'];
2059 $_REQUEST['bcc'] = empty($_POST['bcc']) ? (empty($_GET['bcc']) ? '' : $_GET['bcc']) : $_POST['bcc'];
2060
2061 // Route the input from the 'u' parameter to the 'to'-list.
2062 if (!empty($_POST['u']))
2063 $_POST['recipient_to'] = explode(',', $_POST['u']);
2064
2065 // Construct the list of recipients.
2066 $recipientList = array();
2067 $namedRecipientList = array();
2068 $namesNotFound = array();
2069 foreach (array('to', 'bcc') as $recipientType)
2070 {
2071 // First, let's see if there's user ID's given.
2072 $recipientList[$recipientType] = array();
2073 if (!empty($_POST['recipient_' . $recipientType]) && is_array($_POST['recipient_' . $recipientType]))
2074 {
2075 foreach ($_POST['recipient_' . $recipientType] as $recipient)
2076 $recipientList[$recipientType][] = (int) $recipient;
2077 }
2078
2079 // Are there also literal names set?
2080 if (!empty($_REQUEST[$recipientType]))
2081 {
2082 // We're going to take out the "s anyway ;).
2083 $recipientString = strtr($_REQUEST[$recipientType], array('\\"' => '"'));
2084
2085 preg_match_all('~"([^"]+)"~', $recipientString, $matches);
2086 $namedRecipientList[$recipientType] = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $recipientString))));
2087
2088 foreach ($namedRecipientList[$recipientType] as $index => $recipient)
2089 {
2090 if (strlen(trim($recipient)) > 0)
2091 $namedRecipientList[$recipientType][$index] = $smcFunc['htmlspecialchars']($smcFunc['strtolower'](trim($recipient)));
2092 else
2093 unset($namedRecipientList[$recipientType][$index]);
2094 }
2095
2096 if (!empty($namedRecipientList[$recipientType]))
2097 {
2098 $foundMembers = findMembers($namedRecipientList[$recipientType]);
2099
2100 // Assume all are not found, until proven otherwise.
2101 $namesNotFound[$recipientType] = $namedRecipientList[$recipientType];
2102
2103 foreach ($foundMembers as $member)
2104 {
2105 $testNames = array(
2106 $smcFunc['strtolower']($member['username']),
2107 $smcFunc['strtolower']($member['name']),
2108 $smcFunc['strtolower']($member['email']),
2109 );
2110
2111 if (count(array_intersect($testNames, $namedRecipientList[$recipientType])) !== 0)
2112 {
2113 $recipientList[$recipientType][] = $member['id'];
2114
2115 // Get rid of this username, since we found it.
2116 $namesNotFound[$recipientType] = array_diff($namesNotFound[$recipientType], $testNames);
2117 }
2118 }
2119 }
2120 }
2121
2122 // Selected a recipient to be deleted? Remove them now.
2123 if (!empty($_POST['delete_recipient']))
2124 $recipientList[$recipientType] = array_diff($recipientList[$recipientType], array((int) $_POST['delete_recipient']));
2125
2126 // Make sure we don't include the same name twice
2127 $recipientList[$recipientType] = array_unique($recipientList[$recipientType]);
2128 }
2129
2130 // Are we changing the recipients some how?
2131 $is_recipient_change = !empty($_POST['delete_recipient']) || !empty($_POST['to_submit']) || !empty($_POST['bcc_submit']);
2132
2133 // Check if there's at least one recipient.
2134 if (empty($recipientList['to']) && empty($recipientList['bcc']))
2135 $post_errors[] = 'no_to';
2136
2137 // Make sure that we remove the members who did get it from the screen.
2138 if (!$is_recipient_change)
2139 {
2140 foreach ($recipientList as $recipientType => $dummy)
2141 {
2142 if (!empty($namesNotFound[$recipientType]))
2143 {
2144 $post_errors[] = 'bad_' . $recipientType;
2145
2146 // Since we already have a post error, remove the previous one.
2147 $post_errors = array_diff($post_errors, array('no_to'));
2148
2149 foreach ($namesNotFound[$recipientType] as $name)
2150 $context['send_log']['failed'][] = sprintf($txt['pm_error_user_not_found'], $name);
2151 }
2152 }
2153 }
2154
2155 // Did they make any mistakes?
2156 if ($_REQUEST['subject'] == '')
2157 $post_errors[] = 'no_subject';
2158 if (!isset($_REQUEST['message']) || $_REQUEST['message'] == '')
2159 $post_errors[] = 'no_message';
2160 elseif (!empty($modSettings['max_messageLength']) && $smcFunc['strlen']($_REQUEST['message']) > $modSettings['max_messageLength'])
2161 $post_errors[] = 'long_message';
2162 else
2163 {
2164 // Preparse the message.
2165 $message = $_REQUEST['message'];
2166 preparsecode($message);
2167
2168 // Make sure there's still some content left without the tags.
2169 if ($smcFunc['htmltrim'](strip_tags(parse_bbc($smcFunc['htmlspecialchars']($message, ENT_QUOTES), false), '<img>')) === '' && (!allowedTo('admin_forum') || strpos($message, '[html]') === false))
2170 $post_errors[] = 'no_message';
2171 }
2172
2173 // Wrong verification code?
2174 if (!$user_info['is_admin'] && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification'])
2175 {
2176 require_once($sourcedir . '/Subs-Editor.php');
2177 $verificationOptions = array(
2178 'id' => 'pm',
2179 );
2180 $context['require_verification'] = create_control_verification($verificationOptions, true);
2181
2182 if (is_array($context['require_verification']))
2183 {
2184 $post_errors = array_merge($post_errors, $context['require_verification']);
2185 }
2186 }
2187
2188 // If they did, give a chance to make ammends.
2189 if (!empty($post_errors) && !$is_recipient_change && !isset($_REQUEST['preview']))
2190 return messagePostError($post_errors, $namedRecipientList, $recipientList);
2191
2192 // Want to take a second glance before you send?
2193 if (isset($_REQUEST['preview']))
2194 {
2195 // Set everything up to be displayed.
2196 $context['preview_subject'] = $smcFunc['htmlspecialchars']($_REQUEST['subject']);
2197 $context['preview_message'] = $smcFunc['htmlspecialchars']($_REQUEST['message'], ENT_QUOTES);
2198 preparsecode($context['preview_message'], true);
2199
2200 // Parse out the BBC if it is enabled.
2201 $context['preview_message'] = parse_bbc($context['preview_message']);
2202
2203 // Censor, as always.
2204 censorText($context['preview_subject']);
2205 censorText($context['preview_message']);
2206
2207 // Set a descriptive title.
2208 $context['page_title'] = $txt['preview'] . ' - ' . $context['preview_subject'];
2209
2210 // Pretend they messed up but don't ignore if they really did :P.
2211 return messagePostError($post_errors, $namedRecipientList, $recipientList);
2212 }
2213
2214 // Adding a recipient cause javascript ain't working?
2215 elseif ($is_recipient_change)
2216 {
2217 // Maybe we couldn't find one?
2218 foreach ($namesNotFound as $recipientType => $names)
2219 {
2220 $post_errors[] = 'bad_' . $recipientType;
2221 foreach ($names as $name)
2222 $context['send_log']['failed'][] = sprintf($txt['pm_error_user_not_found'], $name);
2223 }
2224
2225 return messagePostError(array(), $namedRecipientList, $recipientList);
2226 }
2227
2228 // Before we send the PM, let's make sure we don't have an abuse of numbers.
2229 elseif (!empty($modSettings['max_pm_recipients']) && count($recipientList['to']) + count($recipientList['bcc']) > $modSettings['max_pm_recipients'] && !allowedTo(array('moderate_forum', 'send_mail', 'admin_forum')))
2230 {
2231 $context['send_log'] = array(
2232 'sent' => array(),
2233 'failed' => array(sprintf($txt['pm_too_many_recipients'], $modSettings['max_pm_recipients'])),
2234 );
2235 return messagePostError($post_errors, $namedRecipientList, $recipientList);
2236 }
2237
2238 // Protect from message spamming.
2239 spamProtection('pm');
2240
2241 // Prevent double submission of this form.
2242 checkSubmitOnce('check');
2243
2244 // Do the actual sending of the PM.
2245 if (!empty($recipientList['to']) || !empty($recipientList['bcc']))
2246 $context['send_log'] = sendpm($recipientList, $_REQUEST['subject'], $_REQUEST['message'], !empty($_REQUEST['outbox']), null, !empty($_REQUEST['pm_head']) ? (int) $_REQUEST['pm_head'] : 0);
2247 else
2248 $context['send_log'] = array(
2249 'sent' => array(),
2250 'failed' => array()
2251 );
2252
2253 // Mark the message as "replied to".
2254 if (!empty($context['send_log']['sent']) && !empty($_REQUEST['replied_to']) && isset($_REQUEST['f']) && $_REQUEST['f'] == 'inbox')
2255 {
2256 $smcFunc['db_query']('', '
2257 UPDATE {db_prefix}pm_recipients
2258 SET is_read = is_read | 2
2259 WHERE id_pm = {int:replied_to}
2260 AND id_member = {int:current_member}',
2261 array(
2262 'current_member' => $user_info['id'],
2263 'replied_to' => (int) $_REQUEST['replied_to'],
2264 )
2265 );
2266 }
2267
2268 // If one or more of the recipient were invalid, go back to the post screen with the failed usernames.
2269 if (!empty($context['send_log']['failed']))
2270 return messagePostError($post_errors, $namesNotFound, array(
2271 'to' => array_intersect($recipientList['to'], $context['send_log']['failed']),
2272 'bcc' => array_intersect($recipientList['bcc'], $context['send_log']['failed'])
2273 ));
2274
2275 // Message sent successfully?
2276 if (!empty($context['send_log']) && empty($context['send_log']['failed']))
2277 $context['current_label_redirect'] = $context['current_label_redirect'] . ';done=sent';
2278
2279 // Go back to the where they sent from, if possible...
2280 redirectexit($context['current_label_redirect']);
2281 }
2282
2283 // This function lists all buddies for wireless protocols.
2284 function WirelessAddBuddy()
2285 {
2286 global $scripturl, $txt, $user_info, $context, $smcFunc;
2287
2288 isAllowedTo('pm_send');
2289 $context['page_title'] = $txt['wireless_pm_add_buddy'];
2290
2291 $current_buddies = empty($_REQUEST['u']) ? array() : explode(',', $_REQUEST['u']);
2292 foreach ($current_buddies as $key => $buddy)
2293 $current_buddies[$key] = (int) $buddy;
2294
2295 $base_url = $scripturl . '?action=pm;sa=send;u=' . (empty($current_buddies) ? '' : implode(',', $current_buddies) . ',');
2296 $context['pm_href'] = $scripturl . '?action=pm;sa=send' . (empty($current_buddies) ? '' : ';u=' . implode(',', $current_buddies));
2297
2298 $context['buddies'] = array();
2299 if (!empty($user_info['buddies']))
2300 {
2301 $request = $smcFunc['db_query']('', '
2302 SELECT id_member, real_name
2303 FROM {db_prefix}members
2304 WHERE id_member IN ({array_int:buddy_list})
2305 ORDER BY real_name
2306 LIMIT ' . count($user_info['buddies']),
2307 array(
2308 'buddy_list' => $user_info['buddies'],
2309 )
2310 );
2311 while ($row = $smcFunc['db_fetch_assoc']($request))
2312 $context['buddies'][] = array(
2313 'id' => $row['id_member'],
2314 'name' => $row['real_name'],
2315 'selected' => in_array($row['id_member'], $current_buddies),
2316 'add_href' => $base_url . $row['id_member'],
2317 );
2318 $smcFunc['db_free_result']($request);
2319 }
2320 }
2321
2322 // This function performs all additional stuff...
2323 function MessageActionsApply()
2324 {
2325 global $txt, $context, $user_info, $options, $smcFunc;
2326
2327 checkSession('request');
2328
2329 if (isset($_REQUEST['del_selected']))
2330 $_REQUEST['pm_action'] = 'delete';
2331
2332 if (isset($_REQUEST['pm_action']) && $_REQUEST['pm_action'] != '' && !empty($_REQUEST['pms']) && is_array($_REQUEST['pms']))
2333 {
2334 foreach ($_REQUEST['pms'] as $pm)
2335 $_REQUEST['pm_actions'][(int) $pm] = $_REQUEST['pm_action'];
2336 }
2337
2338 if (empty($_REQUEST['pm_actions']))
2339 redirectexit($context['current_label_redirect']);
2340
2341 // If we are in conversation, we may need to apply this to every message in the conversation.
2342 if ($context['display_mode'] == 2 && isset($_REQUEST['conversation']))
2343 {
2344 $id_pms = array();
2345 foreach ($_REQUEST['pm_actions'] as $pm => $dummy)
2346 $id_pms[] = (int) $pm;
2347
2348 $request = $smcFunc['db_query']('', '
2349 SELECT id_pm_head, id_pm
2350 FROM {db_prefix}personal_messages
2351 WHERE id_pm IN ({array_int:id_pms})',
2352 array(
2353 'id_pms' => $id_pms,
2354 )
2355 );
2356 $pm_heads = array();
2357 while ($row = $smcFunc['db_fetch_assoc']($request))
2358 $pm_heads[$row['id_pm_head']] = $row['id_pm'];
2359 $smcFunc['db_free_result']($request);
2360
2361 $request = $smcFunc['db_query']('', '
2362 SELECT id_pm, id_pm_head
2363 FROM {db_prefix}personal_messages
2364 WHERE id_pm_head IN ({array_int:pm_heads})',
2365 array(
2366 'pm_heads' => array_keys($pm_heads),
2367 )
2368 );
2369 // Copy the action from the single to PM to the others.
2370 while ($row = $smcFunc['db_fetch_assoc']($request))
2371 {
2372 if (isset($pm_heads[$row['id_pm_head']]) && isset($_REQUEST['pm_actions'][$pm_heads[$row['id_pm_head']]]))
2373 $_REQUEST['pm_actions'][$row['id_pm']] = $_REQUEST['pm_actions'][$pm_heads[$row['id_pm_head']]];
2374 }
2375 $smcFunc['db_free_result']($request);
2376 }
2377
2378 $to_delete = array();
2379 $to_label = array();
2380 $label_type = array();
2381 foreach ($_REQUEST['pm_actions'] as $pm => $action)
2382 {
2383 if ($action === 'delete')
2384 $to_delete[] = (int) $pm;
2385 else
2386 {
2387 if (substr($action, 0, 4) == 'add_')
2388 {
2389 $type = 'add';
2390 $action = substr($action, 4);
2391 }
2392 elseif (substr($action, 0, 4) == 'rem_')
2393 {
2394 $type = 'rem';
2395 $action = substr($action, 4);
2396 }
2397 else
2398 $type = 'unk';
2399
2400 if ($action == '-1' || $action == '0' || (int) $action > 0)
2401 {
2402 $to_label[(int) $pm] = (int) $action;
2403 $label_type[(int) $pm] = $type;
2404 }
2405 }
2406 }
2407
2408 // Deleting, it looks like?
2409 if (!empty($to_delete))
2410 deleteMessages($to_delete, $context['display_mode'] == 2 ? null : $context['folder']);
2411
2412 // Are we labeling anything?
2413 if (!empty($to_label) && $context['folder'] == 'inbox')
2414 {
2415 $updateErrors = 0;
2416
2417 // Get information about each message...
2418 $request = $smcFunc['db_query']('', '
2419 SELECT id_pm, labels
2420 FROM {db_prefix}pm_recipients
2421 WHERE id_member = {int:current_member}
2422 AND id_pm IN ({array_int:to_label})
2423 LIMIT ' . count($to_label),
2424 array(
2425 'current_member' => $user_info['id'],
2426 'to_label' => array_keys($to_label),
2427 )
2428 );
2429 while ($row = $smcFunc['db_fetch_assoc']($request))
2430 {
2431 $labels = $row['labels'] == '' ? array('-1') : explode(',', trim($row['labels']));
2432
2433 // Already exists? Then... unset it!
2434 $ID_LABEL = array_search($to_label[$row['id_pm']], $labels);
2435 if ($ID_LABEL !== false && $label_type[$row['id_pm']] !== 'add')
2436 unset($labels[$ID_LABEL]);
2437 elseif ($label_type[$row['id_pm']] !== 'rem')
2438 $labels[] = $to_label[$row['id_pm']];
2439
2440 if (!empty($options['pm_remove_inbox_label']) && $to_label[$row['id_pm']] != '-1' && ($key = array_search('-1', $labels)) !== false)
2441 unset($labels[$key]);
2442
2443 $set = implode(',', array_unique($labels));
2444 if ($set == '')
2445 $set = '-1';
2446
2447 // Check that this string isn't going to be too large for the database.
2448 if ($set > 60)
2449 $updateErrors++;
2450 else
2451 {
2452 $smcFunc['db_query']('', '
2453 UPDATE {db_prefix}pm_recipients
2454 SET labels = {string:labels}
2455 WHERE id_pm = {int:id_pm}
2456 AND id_member = {int:current_member}',
2457 array(
2458 'current_member' => $user_info['id'],
2459 'id_pm' => $row['id_pm'],
2460 'labels' => $set,
2461 )
2462 );
2463 }
2464 }
2465 $smcFunc['db_free_result']($request);
2466
2467 // Any errors?
2468 // !!! Separate the sprintf?
2469 if (!empty($updateErrors))
2470 fatal_lang_error('labels_too_many', true, array($updateErrors));
2471 }
2472
2473 // Back to the folder.
2474 $_SESSION['pm_selected'] = array_keys($to_label);
2475 redirectexit($context['current_label_redirect'] . (count($to_label) == 1 ? '#msg' . $_SESSION['pm_selected'][0] : ''), count($to_label) == 1 && $context['browser']['is_ie']);
2476 }
2477
2478 // Are you sure you want to PERMANENTLY (mostly) delete ALL your messages?
2479 function MessageKillAllQuery()
2480 {
2481 global $txt, $context;
2482
2483 // Only have to set up the template....
2484 $context['sub_template'] = 'ask_delete';
2485 $context['page_title'] = $txt['delete_all'];
2486 $context['delete_all'] = $_REQUEST['f'] == 'all';
2487
2488 // And set the folder name...
2489 $txt['delete_all'] = str_replace('PMBOX', $context['folder'] != 'sent' ? $txt['inbox'] : $txt['sent_items'], $txt['delete_all']);
2490 }
2491
2492 // Delete ALL the messages!
2493 function MessageKillAll()
2494 {
2495 global $context;
2496
2497 checkSession('get');
2498
2499 // If all then delete all messages the user has.
2500 if ($_REQUEST['f'] == 'all')
2501 deleteMessages(null, null);
2502 // Otherwise just the selected folder.
2503 else
2504 deleteMessages(null, $_REQUEST['f'] != 'sent' ? 'inbox' : 'sent');
2505
2506 // Done... all gone.
2507 redirectexit($context['current_label_redirect']);
2508 }
2509
2510 // This function allows the user to delete all messages older than so many days.
2511 function MessagePrune()
2512 {
2513 global $txt, $context, $user_info, $scripturl, $smcFunc;
2514
2515 // Actually delete the messages.
2516 if (isset($_REQUEST['age']))
2517 {
2518 checkSession();
2519
2520 // Calculate the time to delete before.
2521 $deleteTime = max(0, time() - (86400 * (int) $_REQUEST['age']));
2522
2523 // Array to store the IDs in.
2524 $toDelete = array();
2525
2526 // Select all the messages they have sent older than $deleteTime.
2527 $request = $smcFunc['db_query']('', '
2528 SELECT id_pm
2529 FROM {db_prefix}personal_messages
2530 WHERE deleted_by_sender = {int:not_deleted}
2531 AND id_member_from = {int:current_member}
2532 AND msgtime < {int:msgtime}',
2533 array(
2534 'current_member' => $user_info['id'],
2535 'not_deleted' => 0,
2536 'msgtime' => $deleteTime,
2537 )
2538 );
2539 while ($row = $smcFunc['db_fetch_row']($request))
2540 $toDelete[] = $row[0];
2541 $smcFunc['db_free_result']($request);
2542
2543 // Select all messages in their inbox older than $deleteTime.
2544 $request = $smcFunc['db_query']('', '
2545 SELECT pmr.id_pm
2546 FROM {db_prefix}pm_recipients AS pmr
2547 INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)
2548 WHERE pmr.deleted = {int:not_deleted}
2549 AND pmr.id_member = {int:current_member}
2550 AND pm.msgtime < {int:msgtime}',
2551 array(
2552 'current_member' => $user_info['id'],
2553 'not_deleted' => 0,
2554 'msgtime' => $deleteTime,
2555 )
2556 );
2557 while ($row = $smcFunc['db_fetch_assoc']($request))
2558 $toDelete[] = $row['id_pm'];
2559 $smcFunc['db_free_result']($request);
2560
2561 // Delete the actual messages.
2562 deleteMessages($toDelete);
2563
2564 // Go back to their inbox.
2565 redirectexit($context['current_label_redirect']);
2566 }
2567
2568 // Build the link tree elements.
2569 $context['linktree'][] = array(
2570 'url' => $scripturl . '?action=pm;sa=prune',
2571 'name' => $txt['pm_prune']
2572 );
2573
2574 $context['sub_template'] = 'prune';
2575 $context['page_title'] = $txt['pm_prune'];
2576 }
2577
2578 // Delete the specified personal messages.
2579 function deleteMessages($personal_messages, $folder = null, $owner = null)
2580 {
2581 global $user_info, $smcFunc;
2582
2583 if ($owner === null)
2584 $owner = array($user_info['id']);
2585 elseif (empty($owner))
2586 return;
2587 elseif (!is_array($owner))
2588 $owner = array($owner);
2589
2590 if ($personal_messages !== null)
2591 {
2592 if (empty($personal_messages) || !is_array($personal_messages))
2593 return;
2594
2595 foreach ($personal_messages as $index => $delete_id)
2596 $personal_messages[$index] = (int) $delete_id;
2597
2598 $where = '
2599 AND id_pm IN ({array_int:pm_list})';
2600 }
2601 else
2602 $where = '';
2603
2604 if ($folder == 'sent' || $folder === null)
2605 {
2606 $smcFunc['db_query']('', '
2607 UPDATE {db_prefix}personal_messages
2608 SET deleted_by_sender = {int:is_deleted}
2609 WHERE id_member_from IN ({array_int:member_list})
2610 AND deleted_by_sender = {int:not_deleted}' . $where,
2611 array(
2612 'member_list' => $owner,
2613 'is_deleted' => 1,
2614 'not_deleted' => 0,
2615 'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(),
2616 )
2617 );
2618 }
2619 if ($folder != 'sent' || $folder === null)
2620 {
2621 // Calculate the number of messages each member's gonna lose...
2622 $request = $smcFunc['db_query']('', '
2623 SELECT id_member, COUNT(*) AS num_deleted_messages, CASE WHEN is_read & 1 >= 1 THEN 1 ELSE 0 END AS is_read
2624 FROM {db_prefix}pm_recipients
2625 WHERE id_member IN ({array_int:member_list})
2626 AND deleted = {int:not_deleted}' . $where . '
2627 GROUP BY id_member, is_read',
2628 array(
2629 'member_list' => $owner,
2630 'not_deleted' => 0,
2631 'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(),
2632 )
2633 );
2634 // ...And update the statistics accordingly - now including unread messages!.
2635 while ($row = $smcFunc['db_fetch_assoc']($request))
2636 {
2637 if ($row['is_read'])
2638 updateMemberData($row['id_member'], array('instant_messages' => $where == '' ? 0 : 'instant_messages - ' . $row['num_deleted_messages']));
2639 else
2640 updateMemberData($row['id_member'], array('instant_messages' => $where == '' ? 0 : 'instant_messages - ' . $row['num_deleted_messages'], 'unread_messages' => $where == '' ? 0 : 'unread_messages - ' . $row['num_deleted_messages']));
2641
2642 // If this is the current member we need to make their message count correct.
2643 if ($user_info['id'] == $row['id_member'])
2644 {
2645 $user_info['messages'] -= $row['num_deleted_messages'];
2646 if (!($row['is_read']))
2647 $user_info['unread_messages'] -= $row['num_deleted_messages'];
2648 }
2649 }
2650 $smcFunc['db_free_result']($request);
2651
2652 // Do the actual deletion.
2653 $smcFunc['db_query']('', '
2654 UPDATE {db_prefix}pm_recipients
2655 SET deleted = {int:is_deleted}
2656 WHERE id_member IN ({array_int:member_list})
2657 AND deleted = {int:not_deleted}' . $where,
2658 array(
2659 'member_list' => $owner,
2660 'is_deleted' => 1,
2661 'not_deleted' => 0,
2662 'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(),
2663 )
2664 );
2665 }
2666
2667 // If sender and recipients all have deleted their message, it can be removed.
2668 $request = $smcFunc['db_query']('', '
2669 SELECT pm.id_pm AS sender, pmr.id_pm
2670 FROM {db_prefix}personal_messages AS pm
2671 LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm AND pmr.deleted = {int:not_deleted})
2672 WHERE pm.deleted_by_sender = {int:is_deleted}
2673 ' . str_replace('id_pm', 'pm.id_pm', $where) . '
2674 GROUP BY sender, pmr.id_pm
2675 HAVING pmr.id_pm IS null',
2676 array(
2677 'not_deleted' => 0,
2678 'is_deleted' => 1,
2679 'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(),
2680 )
2681 );
2682 $remove_pms = array();
2683 while ($row = $smcFunc['db_fetch_assoc']($request))
2684 $remove_pms[] = $row['sender'];
2685 $smcFunc['db_free_result']($request);
2686
2687 if (!empty($remove_pms))
2688 {
2689 $smcFunc['db_query']('', '
2690 DELETE FROM {db_prefix}personal_messages
2691 WHERE id_pm IN ({array_int:pm_list})',
2692 array(
2693 'pm_list' => $remove_pms,
2694 )
2695 );
2696
2697 $smcFunc['db_query']('', '
2698 DELETE FROM {db_prefix}pm_recipients
2699 WHERE id_pm IN ({array_int:pm_list})',
2700 array(
2701 'pm_list' => $remove_pms,
2702 )
2703 );
2704 }
2705
2706 // Any cached numbers may be wrong now.
2707 cache_put_data('labelCounts:' . $user_info['id'], null, 720);
2708 }
2709
2710 // Mark personal messages read.
2711 function markMessages($personal_messages = null, $label = null, $owner = null)
2712 {
2713 global $user_info, $context, $smcFunc;
2714
2715 if ($owner === null)
2716 $owner = $user_info['id'];
2717
2718 $smcFunc['db_query']('', '
2719 UPDATE {db_prefix}pm_recipients
2720 SET is_read = is_read | 1
2721 WHERE id_member = {int:id_member}
2722 AND NOT (is_read & 1 >= 1)' . ($label === null ? '' : '
2723 AND FIND_IN_SET({string:label}, labels) != 0') . ($personal_messages !== null ? '
2724 AND id_pm IN ({array_int:personal_messages})' : ''),
2725 array(
2726 'personal_messages' => $personal_messages,
2727 'id_member' => $owner,
2728 'label' => $label,
2729 )
2730 );
2731
2732 // If something wasn't marked as read, get the number of unread messages remaining.
2733 if ($smcFunc['db_affected_rows']() > 0)
2734 {
2735 if ($owner == $user_info['id'])
2736 {
2737 foreach ($context['labels'] as $label)
2738 $context['labels'][(int) $label['id']]['unread_messages'] = 0;
2739 }
2740
2741 $result = $smcFunc['db_query']('', '
2742 SELECT labels, COUNT(*) AS num
2743 FROM {db_prefix}pm_recipients
2744 WHERE id_member = {int:id_member}
2745 AND NOT (is_read & 1 >= 1)
2746 AND deleted = {int:is_not_deleted}
2747 GROUP BY labels',
2748 array(
2749 'id_member' => $owner,
2750 'is_not_deleted' => 0,
2751 )
2752 );
2753 $total_unread = 0;
2754 while ($row = $smcFunc['db_fetch_assoc']($result))
2755 {
2756 $total_unread += $row['num'];
2757
2758 if ($owner != $user_info['id'])
2759 continue;
2760
2761 $this_labels = explode(',', $row['labels']);
2762 foreach ($this_labels as $this_label)
2763 $context['labels'][(int) $this_label]['unread_messages'] += $row['num'];
2764 }
2765 $smcFunc['db_free_result']($result);
2766
2767 // Need to store all this.
2768 cache_put_data('labelCounts:' . $owner, $context['labels'], 720);
2769 updateMemberData($owner, array('unread_messages' => $total_unread));
2770
2771 // If it was for the current member, reflect this in the $user_info array too.
2772 if ($owner == $user_info['id'])
2773 $user_info['unread_messages'] = $total_unread;
2774 }
2775 }
2776
2777 // This function handles adding, deleting and editing labels on messages.
2778 function ManageLabels()
2779 {
2780 global $txt, $context, $user_info, $scripturl, $smcFunc;
2781
2782 // Build the link tree elements...
2783 $context['linktree'][] = array(
2784 'url' => $scripturl . '?action=pm;sa=manlabels',
2785 'name' => $txt['pm_manage_labels']
2786 );
2787
2788 $context['page_title'] = $txt['pm_manage_labels'];
2789 $context['sub_template'] = 'labels';
2790
2791 $the_labels = array();
2792 // Add all existing labels to the array to save, slashing them as necessary...
2793 foreach ($context['labels'] as $label)
2794 {
2795 if ($label['id'] != -1)
2796 $the_labels[$label['id']] = $label['name'];
2797 }
2798
2799 if (isset($_POST[$context['session_var']]))
2800 {
2801 checkSession('post');
2802
2803 // This will be for updating messages.
2804 $message_changes = array();
2805 $new_labels = array();
2806 $rule_changes = array();
2807
2808 // Will most likely need this.
2809 LoadRules();
2810
2811 // Adding a new label?
2812 if (isset($_POST['add']))
2813 {
2814 $_POST['label'] = strtr($smcFunc['htmlspecialchars'](trim($_POST['label'])), array(',' => '&#044;'));
2815
2816 if ($smcFunc['strlen']($_POST['label']) > 30)
2817 $_POST['label'] = $smcFunc['substr']($_POST['label'], 0, 30);
2818 if ($_POST['label'] != '')
2819 $the_labels[] = $_POST['label'];
2820 }
2821 // Deleting an existing label?
2822 elseif (isset($_POST['delete'], $_POST['delete_label']))
2823 {
2824 $i = 0;
2825 foreach ($the_labels as $id => $name)
2826 {
2827 if (isset($_POST['delete_label'][$id]))
2828 {
2829 unset($the_labels[$id]);
2830 $message_changes[$id] = true;
2831 }
2832 else
2833 $new_labels[$id] = $i++;
2834 }
2835 }
2836 // The hardest one to deal with... changes.
2837 elseif (isset($_POST['save']) && !empty($_POST['label_name']))
2838 {
2839 $i = 0;
2840 foreach ($the_labels as $id => $name)
2841 {
2842 if ($id == -1)
2843 continue;
2844 elseif (isset($_POST['label_name'][$id]))
2845 {
2846 $_POST['label_name'][$id] = trim(strtr($smcFunc['htmlspecialchars']($_POST['label_name'][$id]), array(',' => '&#044;')));
2847
2848 if ($smcFunc['strlen']($_POST['label_name'][$id]) > 30)
2849 $_POST['label_name'][$id] = $smcFunc['substr']($_POST['label_name'][$id], 0, 30);
2850 if ($_POST['label_name'][$id] != '')
2851 {
2852 $the_labels[(int) $id] = $_POST['label_name'][$id];
2853 $new_labels[$id] = $i++;
2854 }
2855 else
2856 {
2857 unset($the_labels[(int) $id]);
2858 $message_changes[(int) $id] = true;
2859 }
2860 }
2861 else
2862 $new_labels[$id] = $i++;
2863 }
2864 }
2865
2866 // Save the label status.
2867 updateMemberData($user_info['id'], array('message_labels' => implode(',', $the_labels)));
2868
2869 // Update all the messages currently with any label changes in them!
2870 if (!empty($message_changes))
2871 {
2872 $searchArray = array_keys($message_changes);
2873
2874 if (!empty($new_labels))
2875 {
2876 for ($i = max($searchArray) + 1, $n = max(array_keys($new_labels)); $i <= $n; $i++)
2877 $searchArray[] = $i;
2878 }
2879
2880 // Now find the messages to change.
2881 $request = $smcFunc['db_query']('', '
2882 SELECT id_pm, labels
2883 FROM {db_prefix}pm_recipients
2884 WHERE FIND_IN_SET({raw:find_label_implode}, labels) != 0
2885 AND id_member = {int:current_member}',
2886 array(
2887 'current_member' => $user_info['id'],
2888 'find_label_implode' => '\'' . implode('\', labels) != 0 OR FIND_IN_SET(\'', $searchArray) . '\'',
2889 )
2890 );
2891 while ($row = $smcFunc['db_fetch_assoc']($request))
2892 {
2893 // Do the long task of updating them...
2894 $toChange = explode(',', $row['labels']);
2895
2896 foreach ($toChange as $key => $value)
2897 if (in_array($value, $searchArray))
2898 {
2899 if (isset($new_labels[$value]))
2900 $toChange[$key] = $new_labels[$value];
2901 else
2902 unset($toChange[$key]);
2903 }
2904
2905 if (empty($toChange))
2906 $toChange[] = '-1';
2907
2908 // Update the message.
2909 $smcFunc['db_query']('', '
2910 UPDATE {db_prefix}pm_recipients
2911 SET labels = {string:new_labels}
2912 WHERE id_pm = {int:id_pm}
2913 AND id_member = {int:current_member}',
2914 array(
2915 'current_member' => $user_info['id'],
2916 'id_pm' => $row['id_pm'],
2917 'new_labels' => implode(',', array_unique($toChange)),
2918 )
2919 );
2920 }
2921 $smcFunc['db_free_result']($request);
2922
2923 // Now do the same the rules - check through each rule.
2924 foreach ($context['rules'] as $k => $rule)
2925 {
2926 // Each action...
2927 foreach ($rule['actions'] as $k2 => $action)
2928 {
2929 if ($action['t'] != 'lab' || !in_array($action['v'], $searchArray))
2930 continue;
2931
2932 $rule_changes[] = $rule['id'];
2933 // If we're here we have a label which is either changed or gone...
2934 if (isset($new_labels[$action['v']]))
2935 $context['rules'][$k]['actions'][$k2]['v'] = $new_labels[$action['v']];
2936 else
2937 unset($context['rules'][$k]['actions'][$k2]);
2938 }
2939 }
2940 }
2941
2942 // If we have rules to change do so now.
2943 if (!empty($rule_changes))
2944 {
2945 $rule_changes = array_unique($rule_changes);
2946 // Update/delete as appropriate.
2947 foreach ($rule_changes as $k => $id)
2948 if (!empty($context['rules'][$id]['actions']))
2949 {
2950 $smcFunc['db_query']('', '
2951 UPDATE {db_prefix}pm_rules
2952 SET actions = {string:actions}
2953 WHERE id_rule = {int:id_rule}
2954 AND id_member = {int:current_member}',
2955 array(
2956 'current_member' => $user_info['id'],
2957 'id_rule' => $id,
2958 'actions' => serialize($context['rules'][$id]['actions']),
2959 )
2960 );
2961 unset($rule_changes[$k]);
2962 }
2963
2964 // Anything left here means it's lost all actions...
2965 if (!empty($rule_changes))
2966 $smcFunc['db_query']('', '
2967 DELETE FROM {db_prefix}pm_rules
2968 WHERE id_rule IN ({array_int:rule_list})
2969 AND id_member = {int:current_member}',
2970 array(
2971 'current_member' => $user_info['id'],
2972 'rule_list' => $rule_changes,
2973 )
2974 );
2975 }
2976
2977 // Make sure we're not caching this!
2978 cache_put_data('labelCounts:' . $user_info['id'], null, 720);
2979
2980 // To make the changes appear right away, redirect.
2981 redirectexit('action=pm;sa=manlabels');
2982 }
2983 }
2984
2985 // Edit Personal Message Settings
2986 function MessageSettings()
2987 {
2988 global $txt, $user_settings, $user_info, $context, $sourcedir, $smcFunc;
2989 global $scripturl, $profile_vars, $cur_profile, $user_profile;
2990
2991 // Need this for the display.
2992 require_once($sourcedir . '/Profile.php');
2993 require_once($sourcedir . '/Profile-Modify.php');
2994
2995 // We want them to submit back to here.
2996 $context['profile_custom_submit_url'] = $scripturl . '?action=pm;sa=settings;save';
2997
2998 loadMemberData($user_info['id'], false, 'profile');
2999 $cur_profile = $user_profile[$user_info['id']];
3000
3001 loadLanguage('Profile');
3002 loadTemplate('Profile');
3003
3004 $context['page_title'] = $txt['pm_settings'];
3005 $context['user']['is_owner'] = true;
3006 $context['id_member'] = $user_info['id'];
3007 $context['require_password'] = false;
3008 $context['menu_item_selected'] = 'settings';
3009 $context['submit_button_text'] = $txt['pm_settings'];
3010 $context['profile_header_text'] = $txt['personal_messages'];
3011
3012 // Add our position to the linktree.
3013 $context['linktree'][] = array(
3014 'url' => $scripturl . '?action=pm;sa=settings',
3015 'name' => $txt['pm_settings']
3016 );
3017
3018 // Are they saving?
3019 if (isset($_REQUEST['save']))
3020 {
3021 checkSession('post');
3022
3023 // Mimic what profile would do.
3024 $_POST = htmltrim__recursive($_POST);
3025 $_POST = htmlspecialchars__recursive($_POST);
3026
3027 // Save the fields.
3028 saveProfileFields();
3029
3030 if (!empty($profile_vars))
3031 updateMemberData($user_info['id'], $profile_vars);
3032 }
3033
3034 // Load up the fields.
3035 pmprefs($user_info['id']);
3036 }
3037
3038 // Allows a user to report a personal message they receive to the administrator.
3039 function ReportMessage()
3040 {
3041 global $txt, $context, $scripturl, $sourcedir;
3042 global $user_info, $language, $modSettings, $smcFunc;
3043
3044 // Check that this feature is even enabled!
3045 if (empty($modSettings['enableReportPM']) || empty($_REQUEST['pmsg']))
3046 fatal_lang_error('no_access', false);
3047
3048 $pmsg = (int) $_REQUEST['pmsg'];
3049
3050 if (!isAccessiblePM($pmsg, 'inbox'))
3051 fatal_lang_error('no_access', false);
3052
3053 $context['pm_id'] = $pmsg;
3054 $context['page_title'] = $txt['pm_report_title'];
3055
3056 // If we're here, just send the user to the template, with a few useful context bits.
3057 if (!isset($_POST['report']))
3058 {
3059 $context['sub_template'] = 'report_message';
3060
3061 // !!! I don't like being able to pick who to send it to. Favoritism, etc. sucks.
3062 // Now, get all the administrators.
3063 $request = $smcFunc['db_query']('', '
3064 SELECT id_member, real_name
3065 FROM {db_prefix}members
3066 WHERE id_group = {int:admin_group} OR FIND_IN_SET({int:admin_group}, additional_groups) != 0
3067 ORDER BY real_name',
3068 array(
3069 'admin_group' => 1,
3070 )
3071 );
3072 $context['admins'] = array();
3073 while ($row = $smcFunc['db_fetch_assoc']($request))
3074 $context['admins'][$row['id_member']] = $row['real_name'];
3075 $smcFunc['db_free_result']($request);
3076
3077 // How many admins in total?
3078 $context['admin_count'] = count($context['admins']);
3079 }
3080 // Otherwise, let's get down to the sending stuff.
3081 else
3082 {
3083 // Check the session before proceeding any further!
3084 checkSession('post');
3085
3086 // First, pull out the message contents, and verify it actually went to them!
3087 $request = $smcFunc['db_query']('', '
3088 SELECT pm.subject, pm.body, pm.msgtime, pm.id_member_from, IFNULL(m.real_name, pm.from_name) AS sender_name
3089 FROM {db_prefix}personal_messages AS pm
3090 INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm)
3091 LEFT JOIN {db_prefix}members AS m ON (m.id_member = pm.id_member_from)
3092 WHERE pm.id_pm = {int:id_pm}
3093 AND pmr.id_member = {int:current_member}
3094 AND pmr.deleted = {int:not_deleted}
3095 LIMIT 1',
3096 array(
3097 'current_member' => $user_info['id'],
3098 'id_pm' => $context['pm_id'],
3099 'not_deleted' => 0,
3100 )
3101 );
3102 // Can only be a hacker here!
3103 if ($smcFunc['db_num_rows']($request) == 0)
3104 fatal_lang_error('no_access', false);
3105 list ($subject, $body, $time, $memberFromID, $memberFromName) = $smcFunc['db_fetch_row']($request);
3106 $smcFunc['db_free_result']($request);
3107
3108 // Remove the line breaks...
3109 $body = preg_replace('~<br ?/?' . '>~i', "\n", $body);
3110
3111 // Get any other recipients of the email.
3112 $request = $smcFunc['db_query']('', '
3113 SELECT mem_to.id_member AS id_member_to, mem_to.real_name AS to_name, pmr.bcc
3114 FROM {db_prefix}pm_recipients AS pmr
3115 LEFT JOIN {db_prefix}members AS mem_to ON (mem_to.id_member = pmr.id_member)
3116 WHERE pmr.id_pm = {int:id_pm}
3117 AND pmr.id_member != {int:current_member}',
3118 array(
3119 'current_member' => $user_info['id'],
3120 'id_pm' => $context['pm_id'],
3121 )
3122 );
3123 $recipients = array();
3124 $hidden_recipients = 0;
3125 while ($row = $smcFunc['db_fetch_assoc']($request))
3126 {
3127 // If it's hidden still don't reveal their names - privacy after all ;)
3128 if ($row['bcc'])
3129 $hidden_recipients++;
3130 else
3131 $recipients[] = '[url=' . $scripturl . '?action=profile;u=' . $row['id_member_to'] . ']' . $row['to_name'] . '[/url]';
3132 }
3133 $smcFunc['db_free_result']($request);
3134
3135 if ($hidden_recipients)
3136 $recipients[] = sprintf($txt['pm_report_pm_hidden'], $hidden_recipients);
3137
3138 // Now let's get out and loop through the admins.
3139 $request = $smcFunc['db_query']('', '
3140 SELECT id_member, real_name, lngfile
3141 FROM {db_prefix}members
3142 WHERE (id_group = {int:admin_id} OR FIND_IN_SET({int:admin_id}, additional_groups) != 0)
3143 ' . (empty($_POST['ID_ADMIN']) ? '' : 'AND id_member = {int:specific_admin}') . '
3144 ORDER BY lngfile',
3145 array(
3146 'admin_id' => 1,
3147 'specific_admin' => isset($_POST['ID_ADMIN']) ? (int) $_POST['ID_ADMIN'] : 0,
3148 )
3149 );
3150
3151 // Maybe we shouldn't advertise this?
3152 if ($smcFunc['db_num_rows']($request) == 0)
3153 fatal_lang_error('no_access', false);
3154
3155 $memberFromName = un_htmlspecialchars($memberFromName);
3156
3157 // Prepare the message storage array.
3158 $messagesToSend = array();
3159 // Loop through each admin, and add them to the right language pile...
3160 while ($row = $smcFunc['db_fetch_assoc']($request))
3161 {
3162 // Need to send in the correct language!
3163 $cur_language = empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile'];
3164
3165 if (!isset($messagesToSend[$cur_language]))
3166 {
3167 loadLanguage('PersonalMessage', $cur_language, false);
3168
3169 // Make the body.
3170 $report_body = str_replace(array('{REPORTER}', '{SENDER}'), array(un_htmlspecialchars($user_info['name']), $memberFromName), $txt['pm_report_pm_user_sent']);
3171 $report_body .= "\n" . '[b]' . $_POST['reason'] . '[/b]' . "\n\n";
3172 if (!empty($recipients))
3173 $report_body .= $txt['pm_report_pm_other_recipients'] . ' ' . implode(', ', $recipients) . "\n\n";
3174 $report_body .= $txt['pm_report_pm_unedited_below'] . "\n" . '[quote author=' . (empty($memberFromID) ? '&quot;' . $memberFromName . '&quot;' : $memberFromName . ' link=action=profile;u=' . $memberFromID . ' date=' . $time) . ']' . "\n" . un_htmlspecialchars($body) . '[/quote]';
3175
3176 // Plonk it in the array ;)
3177 $messagesToSend[$cur_language] = array(
3178 'subject' => ($smcFunc['strpos']($subject, $txt['pm_report_pm_subject']) === false ? $txt['pm_report_pm_subject'] : '') . un_htmlspecialchars($subject),
3179 'body' => $report_body,
3180 'recipients' => array(
3181 'to' => array(),
3182 'bcc' => array()
3183 ),
3184 );
3185 }
3186
3187 // Add them to the list.
3188 $messagesToSend[$cur_language]['recipients']['to'][$row['id_member']] = $row['id_member'];
3189 }
3190 $smcFunc['db_free_result']($request);
3191
3192 // Send a different email for each language.
3193 foreach ($messagesToSend as $lang => $message)
3194 sendpm($message['recipients'], $message['subject'], $message['body']);
3195
3196 // Give the user their own language back!
3197 if (!empty($modSettings['userLanguage']))
3198 loadLanguage('PersonalMessage', '', false);
3199
3200 // Leave them with a template.
3201 $context['sub_template'] = 'report_message_complete';
3202 }
3203 }
3204
3205 // List all rules, and allow adding/entering etc....
3206 function ManageRules()
3207 {
3208 global $txt, $context, $user_info, $scripturl, $smcFunc;
3209
3210 // The link tree - gotta have this :o
3211 $context['linktree'][] = array(
3212 'url' => $scripturl . '?action=pm;sa=manrules',
3213 'name' => $txt['pm_manage_rules']
3214 );
3215
3216 $context['page_title'] = $txt['pm_manage_rules'];
3217 $context['sub_template'] = 'rules';
3218
3219 // Load them... load them!!
3220 LoadRules();
3221
3222 // Likely to need all the groups!
3223 $request = $smcFunc['db_query']('', '
3224 SELECT mg.id_group, mg.group_name, IFNULL(gm.id_member, 0) AS can_moderate, mg.hidden
3225 FROM {db_prefix}membergroups AS mg
3226 LEFT JOIN {db_prefix}group_moderators AS gm ON (gm.id_group = mg.id_group AND gm.id_member = {int:current_member})
3227 WHERE mg.min_posts = {int:min_posts}
3228 AND mg.id_group != {int:moderator_group}
3229 AND mg.hidden = {int:not_hidden}
3230 ORDER BY mg.group_name',
3231 array(
3232 'current_member' => $user_info['id'],
3233 'min_posts' => -1,
3234 'moderator_group' => 3,
3235 'not_hidden' => 0,
3236 )
3237 );
3238 $context['groups'] = array();
3239 while ($row = $smcFunc['db_fetch_assoc']($request))
3240 {
3241 // Hide hidden groups!
3242 if ($row['hidden'] && !$row['can_moderate'] && !allowedTo('manage_membergroups'))
3243 continue;
3244
3245 $context['groups'][$row['id_group']] = $row['group_name'];
3246 }
3247 $smcFunc['db_free_result']($request);
3248
3249 // Applying all rules?
3250 if (isset($_GET['apply']))
3251 {
3252 checkSession('get');
3253
3254 ApplyRules(true);
3255 redirectexit('action=pm;sa=manrules');
3256 }
3257 // Editing a specific one?
3258 if (isset($_GET['add']))
3259 {
3260 $context['rid'] = isset($_GET['rid']) && isset($context['rules'][$_GET['rid']])? (int) $_GET['rid'] : 0;
3261 $context['sub_template'] = 'add_rule';
3262
3263 // Current rule information...
3264 if ($context['rid'])
3265 {
3266 $context['rule'] = $context['rules'][$context['rid']];
3267 $members = array();
3268 // Need to get member names!
3269 foreach ($context['rule']['criteria'] as $k => $criteria)
3270 if ($criteria['t'] == 'mid' && !empty($criteria['v']))
3271 $members[(int) $criteria['v']] = $k;
3272
3273 if (!empty($members))
3274 {
3275 $request = $smcFunc['db_query']('', '
3276 SELECT id_member, member_name
3277 FROM {db_prefix}members
3278 WHERE id_member IN ({array_int:member_list})',
3279 array(
3280 'member_list' => array_keys($members),
3281 )
3282 );
3283 while ($row = $smcFunc['db_fetch_assoc']($request))
3284 $context['rule']['criteria'][$members[$row['id_member']]]['v'] = $row['member_name'];
3285 $smcFunc['db_free_result']($request);
3286 }
3287 }
3288 else
3289 $context['rule'] = array(
3290 'id' => '',
3291 'name' => '',
3292 'criteria' => array(),
3293 'actions' => array(),
3294 'logic' => 'and',
3295 );
3296 }
3297 // Saving?
3298 elseif (isset($_GET['save']))
3299 {
3300 checkSession('post');
3301 $context['rid'] = isset($_GET['rid']) && isset($context['rules'][$_GET['rid']])? (int) $_GET['rid'] : 0;
3302
3303 // Name is easy!
3304 $ruleName = $smcFunc['htmlspecialchars'](trim($_POST['rule_name']));
3305 if (empty($ruleName))
3306 fatal_lang_error('pm_rule_no_name', false);
3307
3308 // Sanity check...
3309 if (empty($_POST['ruletype']) || empty($_POST['acttype']))
3310 fatal_lang_error('pm_rule_no_criteria', false);
3311
3312 // Let's do the criteria first - it's also hardest!
3313 $criteria = array();
3314 foreach ($_POST['ruletype'] as $ind => $type)
3315 {
3316 // Check everything is here...
3317 if ($type == 'gid' && (!isset($_POST['ruledefgroup'][$ind]) || !isset($context['groups'][$_POST['ruledefgroup'][$ind]])))
3318 continue;
3319 elseif ($type != 'bud' && !isset($_POST['ruledef'][$ind]))
3320 continue;
3321
3322 // Members need to be found.
3323 if ($type == 'mid')
3324 {
3325 $name = trim($_POST['ruledef'][$ind]);
3326 $request = $smcFunc['db_query']('', '
3327 SELECT id_member
3328 FROM {db_prefix}members
3329 WHERE real_name = {string:member_name}
3330 OR member_name = {string:member_name}',
3331 array(
3332 'member_name' => $name,
3333 )
3334 );
3335 if ($smcFunc['db_num_rows']($request) == 0)
3336 continue;
3337 list ($memID) = $smcFunc['db_fetch_row']($request);
3338 $smcFunc['db_free_result']($request);
3339
3340 $criteria[] = array('t' => 'mid', 'v' => $memID);
3341 }
3342 elseif ($type == 'bud')
3343 $criteria[] = array('t' => 'bud', 'v' => 1);
3344 elseif ($type == 'gid')
3345 $criteria[] = array('t' => 'gid', 'v' => (int) $_POST['ruledefgroup'][$ind]);
3346 elseif (in_array($type, array('sub', 'msg')) && trim($_POST['ruledef'][$ind]) != '')
3347 $criteria[] = array('t' => $type, 'v' => $smcFunc['htmlspecialchars'](trim($_POST['ruledef'][$ind])));
3348 }
3349
3350 // Also do the actions!
3351 $actions = array();
3352 $doDelete = 0;
3353 $isOr = $_POST['rule_logic'] == 'or' ? 1 : 0;
3354 foreach ($_POST['acttype'] as $ind => $type)
3355 {
3356 // Picking a valid label?
3357 if ($type == 'lab' && (!isset($_POST['labdef'][$ind]) || !isset($context['labels'][$_POST['labdef'][$ind] - 1])))
3358 continue;
3359
3360 // Record what we're doing.
3361 if ($type == 'del')
3362 $doDelete = 1;
3363 elseif ($type == 'lab')
3364 $actions[] = array('t' => 'lab', 'v' => (int) $_POST['labdef'][$ind] - 1);
3365 }
3366
3367 if (empty($criteria) || (empty($actions) && !$doDelete))
3368 fatal_lang_error('pm_rule_no_criteria', false);
3369
3370 // What are we storing?
3371 $criteria = serialize($criteria);
3372 $actions = serialize($actions);
3373
3374 // Create the rule?
3375 if (empty($context['rid']))
3376 $smcFunc['db_insert']('',
3377 '{db_prefix}pm_rules',
3378 array(
3379 'id_member' => 'int', 'rule_name' => 'string', 'criteria' => 'string', 'actions' => 'string',
3380 'delete_pm' => 'int', 'is_or' => 'int',
3381 ),
3382 array(
3383 $user_info['id'], $ruleName, $criteria, $actions, $doDelete, $isOr,
3384 ),
3385 array('id_rule')
3386 );
3387 else
3388 $smcFunc['db_query']('', '
3389 UPDATE {db_prefix}pm_rules
3390 SET rule_name = {string:rule_name}, criteria = {string:criteria}, actions = {string:actions},
3391 delete_pm = {int:delete_pm}, is_or = {int:is_or}
3392 WHERE id_rule = {int:id_rule}
3393 AND id_member = {int:current_member}',
3394 array(
3395 'current_member' => $user_info['id'],
3396 'delete_pm' => $doDelete,
3397 'is_or' => $isOr,
3398 'id_rule' => $context['rid'],
3399 'rule_name' => $ruleName,
3400 'criteria' => $criteria,
3401 'actions' => $actions,
3402 )
3403 );
3404
3405 redirectexit('action=pm;sa=manrules');
3406 }
3407 // Deleting?
3408 elseif (isset($_POST['delselected']) && !empty($_POST['delrule']))
3409 {
3410 checkSession('post');
3411 $toDelete = array();
3412 foreach ($_POST['delrule'] as $k => $v)
3413 $toDelete[] = (int) $k;
3414
3415 if (!empty($toDelete))
3416 $smcFunc['db_query']('', '
3417 DELETE FROM {db_prefix}pm_rules
3418 WHERE id_rule IN ({array_int:delete_list})
3419 AND id_member = {int:current_member}',
3420 array(
3421 'current_member' => $user_info['id'],
3422 'delete_list' => $toDelete,
3423 )
3424 );
3425
3426 redirectexit('action=pm;sa=manrules');
3427 }
3428 }
3429
3430 // This will apply rules to all unread messages. If all_messages is set will, clearly, do it to all!
3431 function ApplyRules($all_messages = false)
3432 {
3433 global $user_info, $smcFunc, $context, $options;
3434
3435 // Want this - duh!
3436 loadRules();
3437
3438 // No rules?
3439 if (empty($context['rules']))
3440 return;
3441
3442 // Just unread ones?
3443 $ruleQuery = $all_messages ? '' : ' AND pmr.is_new = 1';
3444
3445 //!!! Apply all should have timeout protection!
3446 // Get all the messages that match this.
3447 $request = $smcFunc['db_query']('', '
3448 SELECT
3449 pmr.id_pm, pm.id_member_from, pm.subject, pm.body, mem.id_group, pmr.labels
3450 FROM {db_prefix}pm_recipients AS pmr
3451 INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)
3452 LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from)
3453 WHERE pmr.id_member = {int:current_member}
3454 AND pmr.deleted = {int:not_deleted}
3455 ' . $ruleQuery,
3456 array(
3457 'current_member' => $user_info['id'],
3458 'not_deleted' => 0,
3459 )
3460 );
3461 $actions = array();
3462 while ($row = $smcFunc['db_fetch_assoc']($request))
3463 {
3464 foreach ($context['rules'] as $rule)
3465 {
3466 $match = false;
3467 // Loop through all the criteria hoping to make a match.
3468 foreach ($rule['criteria'] as $criterium)
3469 {
3470 if (($criterium['t'] == 'mid' && $criterium['v'] == $row['id_member_from']) || ($criterium['t'] == 'gid' && $criterium['v'] == $row['id_group']) || ($criterium['t'] == 'sub' && strpos($row['subject'], $criterium['v']) !== false) || ($criterium['t'] == 'msg' && strpos($row['body'], $criterium['v']) !== false))
3471 $match = true;
3472 // If we're adding and one criteria don't match then we stop!
3473 elseif ($rule['logic'] == 'and')
3474 {
3475 $match = false;
3476 break;
3477 }
3478 }
3479
3480 // If we have a match the rule must be true - act!
3481 if ($match)
3482 {
3483 if ($rule['delete'])
3484 $actions['deletes'][] = $row['id_pm'];
3485 else
3486 {
3487 foreach ($rule['actions'] as $ruleAction)
3488 {
3489 if ($ruleAction['t'] == 'lab')
3490 {
3491 // Get a basic pot started!
3492 if (!isset($actions['labels'][$row['id_pm']]))
3493 $actions['labels'][$row['id_pm']] = empty($row['labels']) ? array() : explode(',', $row['labels']);
3494 $actions['labels'][$row['id_pm']][] = $ruleAction['v'];
3495 }
3496 }
3497 }
3498 }
3499 }
3500 }
3501 $smcFunc['db_free_result']($request);
3502
3503 // Deletes are easy!
3504 if (!empty($actions['deletes']))
3505 deleteMessages($actions['deletes']);
3506
3507 // Relabel?
3508 if (!empty($actions['labels']))
3509 {
3510 foreach ($actions['labels'] as $pm => $labels)
3511 {
3512 // Quickly check each label is valid!
3513 $realLabels = array();
3514 foreach ($context['labels'] as $label)
3515 if (in_array($label['id'], $labels) && ($label['id'] != -1 || empty($options['pm_remove_inbox_label'])))
3516 $realLabels[] = $label['id'];
3517
3518 $smcFunc['db_query']('', '
3519 UPDATE {db_prefix}pm_recipients
3520 SET labels = {string:new_labels}
3521 WHERE id_pm = {int:id_pm}
3522 AND id_member = {int:current_member}',
3523 array(
3524 'current_member' => $user_info['id'],
3525 'id_pm' => $pm,
3526 'new_labels' => empty($realLabels) ? '' : implode(',', $realLabels),
3527 )
3528 );
3529 }
3530 }
3531 }
3532
3533 // Load up all the rules for the current user.
3534 function LoadRules($reload = false)
3535 {
3536 global $user_info, $context, $smcFunc;
3537
3538 if (isset($context['rules']) && !$reload)
3539 return;
3540
3541 $request = $smcFunc['db_query']('', '
3542 SELECT
3543 id_rule, rule_name, criteria, actions, delete_pm, is_or
3544 FROM {db_prefix}pm_rules
3545 WHERE id_member = {int:current_member}',
3546 array(
3547 'current_member' => $user_info['id'],
3548 )
3549 );
3550 $context['rules'] = array();
3551 // Simply fill in the data!
3552 while ($row = $smcFunc['db_fetch_assoc']($request))
3553 {
3554 $context['rules'][$row['id_rule']] = array(
3555 'id' => $row['id_rule'],
3556 'name' => $row['rule_name'],
3557 'criteria' => unserialize($row['criteria']),
3558 'actions' => unserialize($row['actions']),
3559 'delete' => $row['delete_pm'],
3560 'logic' => $row['is_or'] ? 'or' : 'and',
3561 );
3562
3563 if ($row['delete_pm'])
3564 $context['rules'][$row['id_rule']]['actions'][] = array('t' => 'del', 'v' => 1);
3565 }
3566 $smcFunc['db_free_result']($request);
3567 }
3568
3569 // Check if the PM is available to the current user.
3570 function isAccessiblePM($pmID, $validFor = 'in_or_outbox')
3571 {
3572 global $user_info, $smcFunc;
3573
3574 $request = $smcFunc['db_query']('', '
3575 SELECT
3576 pm.id_member_from = {int:id_current_member} AND pm.deleted_by_sender = {int:not_deleted} AS valid_for_outbox,
3577 pmr.id_pm IS NOT NULL AS valid_for_inbox
3578 FROM {db_prefix}personal_messages AS pm
3579 LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm AND pmr.id_member = {int:id_current_member} AND pmr.deleted = {int:not_deleted})
3580 WHERE pm.id_pm = {int:id_pm}
3581 AND ((pm.id_member_from = {int:id_current_member} AND pm.deleted_by_sender = {int:not_deleted}) OR pmr.id_pm IS NOT NULL)',
3582 array(
3583 'id_pm' => $pmID,
3584 'id_current_member' => $user_info['id'],
3585 'not_deleted' => 0,
3586 )
3587 );
3588
3589 if ($smcFunc['db_num_rows']($request) === 0)
3590 {
3591 $smcFunc['db_free_result']($request);
3592 return false;
3593 }
3594
3595 $validationResult = $smcFunc['db_fetch_assoc']($request);
3596 $smcFunc['db_free_result']($request);
3597
3598 switch ($validFor)
3599 {
3600 case 'inbox':
3601 return !empty($validationResult['valid_for_inbox']);
3602 break;
3603
3604 case 'outbox':
3605 return !empty($validationResult['valid_for_outbox']);
3606 break;
3607
3608 case 'in_or_outbox':
3609 return !empty($validationResult['valid_for_inbox']) || !empty($validationResult['valid_for_outbox']);
3610 break;
3611
3612 default:
3613 trigger_error('Undefined validation type given', E_USER_ERROR);
3614 break;
3615 }
3616 }
3617
3618 ?>