add some properties
[phpbb.git] / phpBB / includes / functions_privmsgs.php
blobb13d2eb6249a932739c28ff11047f5ab4f5088c9
1 <?php
2 /**
4 * @package phpBB3
5 * @version $Id$
6 * @copyright (c) 2005 phpBB Group
7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
9 */
11 /**
13 if (!defined('IN_PHPBB'))
15 exit;
19 Ability to simply add own rules by doing three things:
20 1) Add an appropriate constant
21 2) Add a new check array to the global_privmsgs_rules variable and the condition array (if one is required)
22 3) Add a new language variable to ucp.php
24 The user is then able to select the new rule. It will be checked against and handled as specified.
25 To add new actions (yes, checks can be added here too) to the rule management, the core code has to be modified.
28 define('RULE_IS_LIKE', 1); // Is Like
29 define('RULE_IS_NOT_LIKE', 2); // Is Not Like
30 define('RULE_IS', 3); // Is
31 define('RULE_IS_NOT', 4); // Is Not
32 define('RULE_BEGINS_WITH', 5); // Begins with
33 define('RULE_ENDS_WITH', 6); // Ends with
34 define('RULE_IS_FRIEND', 7); // Is Friend
35 define('RULE_IS_FOE', 8); // Is Foe
36 define('RULE_IS_USER', 9); // Is User
37 define('RULE_IS_GROUP', 10); // Is In Usergroup
38 define('RULE_ANSWERED', 11); // Answered
39 define('RULE_FORWARDED', 12); // Forwarded
40 define('RULE_TO_GROUP', 14); // Usergroup
41 define('RULE_TO_ME', 15); // Me
43 define('ACTION_PLACE_INTO_FOLDER', 1);
44 define('ACTION_MARK_AS_READ', 2);
45 define('ACTION_MARK_AS_IMPORTANT', 3);
46 define('ACTION_DELETE_MESSAGE', 4);
48 define('CHECK_SUBJECT', 1);
49 define('CHECK_SENDER', 2);
50 define('CHECK_MESSAGE', 3);
51 define('CHECK_STATUS', 4);
52 define('CHECK_TO', 5);
54 /**
55 * Global private message rules
56 * These rules define what to do if a rule is hit
58 $global_privmsgs_rules = array(
59 CHECK_SUBJECT => array(
60 RULE_IS_LIKE => array('check0' => 'message_subject', 'function' => 'preg_match("/" . preg_quote({STRING}, "/") . "/i", {CHECK0})'),
61 RULE_IS_NOT_LIKE => array('check0' => 'message_subject', 'function' => '!(preg_match("/" . preg_quote({STRING}, "/") . "/i", {CHECK0}))'),
62 RULE_IS => array('check0' => 'message_subject', 'function' => '{CHECK0} == {STRING}'),
63 RULE_IS_NOT => array('check0' => 'message_subject', 'function' => '{CHECK0} != {STRING}'),
64 RULE_BEGINS_WITH => array('check0' => 'message_subject', 'function' => 'preg_match("/^" . preg_quote({STRING}, "/") . "/i", {CHECK0})'),
65 RULE_ENDS_WITH => array('check0' => 'message_subject', 'function' => 'preg_match("/" . preg_quote({STRING}, "/") . "$/i", {CHECK0})'),
68 CHECK_SENDER => array(
69 RULE_IS_LIKE => array('check0' => 'username', 'function' => 'preg_match("/" . preg_quote({STRING}, "/") . "/i", {CHECK0})'),
70 RULE_IS_NOT_LIKE => array('check0' => 'username', 'function' => '!(preg_match("/" . preg_quote({STRING}, "/") . "/i", {CHECK0}))'),
71 RULE_IS => array('check0' => 'username', 'function' => '{CHECK0} == {STRING}'),
72 RULE_IS_NOT => array('check0' => 'username', 'function' => '{CHECK0} != {STRING}'),
73 RULE_BEGINS_WITH => array('check0' => 'username', 'function' => 'preg_match("/^" . preg_quote({STRING}, "/") . "/i", {CHECK0})'),
74 RULE_ENDS_WITH => array('check0' => 'username', 'function' => 'preg_match("/" . preg_quote({STRING}, "/") . "$/i", {CHECK0})'),
75 RULE_IS_FRIEND => array('check0' => 'friend', 'function' => '{CHECK0} == 1'),
76 RULE_IS_FOE => array('check0' => 'foe', 'function' => '{CHECK0} == 1'),
77 RULE_IS_USER => array('check0' => 'author_id', 'function' => '{CHECK0} == {USER_ID}'),
78 RULE_IS_GROUP => array('check0' => 'author_in_group', 'function' => 'in_array({GROUP_ID}, {CHECK0})'),
81 CHECK_MESSAGE => array(
82 RULE_IS_LIKE => array('check0' => 'message_text', 'function' => 'preg_match("/" . preg_quote({STRING}, "/") . "/i", {CHECK0})'),
83 RULE_IS_NOT_LIKE => array('check0' => 'message_text', 'function' => '!(preg_match("/" . preg_quote({STRING}, "/") . "/i", {CHECK0}))'),
84 RULE_IS => array('check0' => 'message_text', 'function' => '{CHECK0} == {STRING}'),
85 RULE_IS_NOT => array('check0' => 'message_text', 'function' => '{CHECK0} != {STRING}'),
88 CHECK_STATUS => array(
89 RULE_ANSWERED => array('check0' => 'pm_replied', 'function' => '{CHECK0} == 1'),
90 RULE_FORWARDED => array('check0' => 'pm_forwarded', 'function' => '{CHECK0} == 1'),
93 CHECK_TO => array(
94 RULE_TO_GROUP => array('check0' => 'to', 'check1' => 'bcc', 'check2' => 'user_in_group', 'function' => 'in_array("g_" . {CHECK2}, {CHECK0}) || in_array("g_" . {CHECK2}, {CHECK1})'),
95 RULE_TO_ME => array('check0' => 'to', 'check1' => 'bcc', 'function' => 'in_array("u_" . $user_id, {CHECK0}) || in_array("u_" . $user_id, {CHECK1})'),
99 /**
100 * This is for defining which condition fields to show for which Rule
102 $global_rule_conditions = array(
103 RULE_IS_LIKE => 'text',
104 RULE_IS_NOT_LIKE => 'text',
105 RULE_IS => 'text',
106 RULE_IS_NOT => 'text',
107 RULE_BEGINS_WITH => 'text',
108 RULE_ENDS_WITH => 'text',
109 RULE_IS_USER => 'user',
110 RULE_IS_GROUP => 'group'
114 * Get all folder
116 function get_folder($user_id, $folder_id = false)
118 global $db, $user, $template;
120 $folder = array();
122 // Get folder information
123 $sql = 'SELECT folder_id, COUNT(msg_id) as num_messages, SUM(pm_unread) as num_unread
124 FROM ' . PRIVMSGS_TO_TABLE . "
125 WHERE user_id = $user_id
126 AND folder_id <> " . PRIVMSGS_NO_BOX . '
127 GROUP BY folder_id';
128 $result = $db->sql_query($sql);
130 $num_messages = $num_unread = array();
131 while ($row = $db->sql_fetchrow($result))
133 $num_messages[(int) $row['folder_id']] = $row['num_messages'];
134 $num_unread[(int) $row['folder_id']] = $row['num_unread'];
136 $db->sql_freeresult($result);
138 // Make sure the default boxes are defined
139 $available_folder = array(PRIVMSGS_INBOX, PRIVMSGS_OUTBOX, PRIVMSGS_SENTBOX);
141 foreach ($available_folder as $default_folder)
143 if (!isset($num_messages[$default_folder]))
145 $num_messages[$default_folder] = 0;
148 if (!isset($num_unread[$default_folder]))
150 $num_unread[$default_folder] = 0;
154 // Adjust unread status for outbox
155 $num_unread[PRIVMSGS_OUTBOX] = $num_messages[PRIVMSGS_OUTBOX];
157 $folder[PRIVMSGS_INBOX] = array(
158 'folder_name' => $user->lang['PM_INBOX'],
159 'num_messages' => $num_messages[PRIVMSGS_INBOX],
160 'unread_messages' => $num_unread[PRIVMSGS_INBOX]
163 // Custom Folder
164 $sql = 'SELECT folder_id, folder_name, pm_count
165 FROM ' . PRIVMSGS_FOLDER_TABLE . "
166 WHERE user_id = $user_id";
167 $result = $db->sql_query($sql);
169 while ($row = $db->sql_fetchrow($result))
171 $folder[$row['folder_id']] = array(
172 'folder_name' => $row['folder_name'],
173 'num_messages' => $row['pm_count'],
174 'unread_messages' => ((isset($num_unread[$row['folder_id']])) ? $num_unread[$row['folder_id']] : 0)
177 $db->sql_freeresult($result);
179 $folder[PRIVMSGS_OUTBOX] = array(
180 'folder_name' => $user->lang['PM_OUTBOX'],
181 'num_messages' => $num_messages[PRIVMSGS_OUTBOX],
182 'unread_messages' => $num_unread[PRIVMSGS_OUTBOX]
185 $folder[PRIVMSGS_SENTBOX] = array(
186 'folder_name' => $user->lang['PM_SENTBOX'],
187 'num_messages' => $num_messages[PRIVMSGS_SENTBOX],
188 'unread_messages' => $num_unread[PRIVMSGS_SENTBOX]
191 // Define Folder Array for template designers (and for making custom folders usable by the template too)
192 foreach ($folder as $f_id => $folder_ary)
194 $folder_id_name = ($f_id == PRIVMSGS_INBOX) ? 'inbox' : (($f_id == PRIVMSGS_OUTBOX) ? 'outbox' : 'sentbox');
196 $template->assign_block_vars('folder', array(
197 'FOLDER_ID' => $f_id,
198 'FOLDER_NAME' => $folder_ary['folder_name'],
199 'NUM_MESSAGES' => $folder_ary['num_messages'],
200 'UNREAD_MESSAGES' => $folder_ary['unread_messages'],
202 'U_FOLDER' => ($f_id > 0) ? append_sid('ucp', 'i=pm&amp;folder=' . $f_id) : append_sid('ucp', 'i=pm&amp;folder=' . $folder_id_name),
204 'S_CUR_FOLDER' => ($f_id === $folder_id) ? true : false,
205 'S_UNREAD_MESSAGES' => ($folder_ary['unread_messages']) ? true : false,
206 'S_CUSTOM_FOLDER' => ($f_id > 0) ? true : false)
210 if ($folder_id !== false && !isset($folder[$folder_id]))
212 trigger_error('UNKNOWN_FOLDER');
215 return $folder;
219 * Delete Messages From Sentbox
220 * we are doing this here because this saves us a bunch of checks and queries
222 function clean_sentbox($num_sentbox_messages)
224 global $db, $user;
226 // Check Message Limit
227 if ($user->data['message_limit'] && $num_sentbox_messages > $user->data['message_limit'])
229 // Delete old messages
230 $sql = 'SELECT t.msg_id
231 FROM ' . PRIVMSGS_TO_TABLE . ' t, ' . PRIVMSGS_TABLE . ' p
232 WHERE t.msg_id = p.msg_id
233 AND t.user_id = ' . $user->data['user_id'] . '
234 AND t.folder_id = ' . PRIVMSGS_SENTBOX . '
235 ORDER BY p.message_time ASC';
236 $result = $db->sql_query_limit($sql, ($num_sentbox_messages - $user->data['message_limit']));
238 $delete_ids = array();
239 while ($row = $db->sql_fetchrow($result))
241 $delete_ids[] = $row['msg_id'];
243 $db->sql_freeresult($result);
244 delete_pm($user->data['user_id'], $delete_ids, PRIVMSGS_SENTBOX);
249 * Check Rule against Message Information
251 function check_rule(&$rules, &$rule_row, &$message_row, $user_id)
253 global $user;
255 if (!isset($rules[$rule_row['rule_check']][$rule_row['rule_connection']]))
257 return false;
260 $check_ary = $rules[$rule_row['rule_check']][$rule_row['rule_connection']];
262 // Replace Check Literals
263 $evaluate = $check_ary['function'];
264 $evaluate = preg_replace('/{(CHECK[0-9])}/', '$message_row[$check_ary[strtolower("\1")]]', $evaluate);
266 // Replace Rule Literals
267 $evaluate = preg_replace('/{(STRING|USER_ID|GROUP_ID)}/', '$rule_row["rule_" . strtolower("\1")]', $evaluate);
269 // Evil Statement
270 $result = false;
271 eval('$result = (' . $evaluate . ') ? true : false;');
273 if (!$result)
275 return false;
278 switch ($rule_row['rule_action'])
280 case ACTION_PLACE_INTO_FOLDER:
281 return array('action' => $rule_row['rule_action'], 'folder_id' => $rule_row['rule_folder_id']);
282 break;
284 case ACTION_MARK_AS_READ:
285 case ACTION_MARK_AS_IMPORTANT:
286 return array('action' => $rule_row['rule_action'], 'pm_unread' => $message_row['pm_unread'], 'pm_marked' => $message_row['pm_marked']);
287 break;
289 case ACTION_DELETE_MESSAGE:
290 global $db, $auth;
292 // Check for admins/mods - users are not allowed to remove those messages...
293 // We do the check here to make sure the data we use is consistent
294 $sql = 'SELECT user_id, user_type, user_permissions
295 FROM ' . USERS_TABLE . '
296 WHERE user_id = ' . (int) $message_row['author_id'];
297 $result = $db->sql_query($sql);
298 $userdata = $db->sql_fetchrow($result);
299 $db->sql_freeresult($result);
301 $auth2 = new auth();
302 $auth2->acl($userdata);
304 if (!$auth2->acl_get('a_') && !$auth2->acl_get('m_') && !$auth2->acl_getf_global('m_'))
306 return array('action' => $rule_row['rule_action'], 'pm_unread' => $message_row['pm_unread'], 'pm_marked' => $message_row['pm_marked']);
309 return false;
310 break;
312 default:
313 return false;
316 return false;
320 * Update user PM count
322 function update_pm_counts()
324 global $user, $db;
326 // Update unread count
327 $sql = 'SELECT COUNT(msg_id) as num_messages
328 FROM ' . PRIVMSGS_TO_TABLE . '
329 WHERE pm_unread = 1
330 AND folder_id <> ' . PRIVMSGS_OUTBOX . '
331 AND user_id = ' . $user->data['user_id'];
332 $result = $db->sql_query($sql);
333 $user->data['user_unread_privmsg'] = (int) $db->sql_fetchfield('num_messages');
334 $db->sql_freeresult($result);
336 // Update new pm count
337 $sql = 'SELECT COUNT(msg_id) as num_messages
338 FROM ' . PRIVMSGS_TO_TABLE . '
339 WHERE pm_new = 1
340 AND folder_id IN (' . PRIVMSGS_NO_BOX . ', ' . PRIVMSGS_HOLD_BOX . ')
341 AND user_id = ' . $user->data['user_id'];
342 $result = $db->sql_query($sql);
343 $user->data['user_new_privmsg'] = (int) $db->sql_fetchfield('num_messages');
344 $db->sql_freeresult($result);
346 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', array(
347 'user_unread_privmsg' => (int) $user->data['user_unread_privmsg'],
348 'user_new_privmsg' => (int) $user->data['user_new_privmsg'],
349 )) . ' WHERE user_id = ' . $user->data['user_id']);
351 // Ok, here we need to repair something, other boxes than privmsgs_no_box and privmsgs_hold_box should not carry the pm_new flag.
352 if (!$user->data['user_new_privmsg'])
354 $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
355 SET pm_new = 0
356 WHERE pm_new = 1
357 AND folder_id NOT IN (' . PRIVMSGS_NO_BOX . ', ' . PRIVMSGS_HOLD_BOX . ')
358 AND user_id = ' . $user->data['user_id'];
359 $db->sql_query($sql);
364 * Place new messages into appropriate folder
366 function place_pm_into_folder(&$global_privmsgs_rules, $release = false)
368 global $db, $user;
370 if (!$user->data['user_new_privmsg'])
372 return array('not_moved' => 0, 'removed' => 0);
375 $user_message_rules = (int) $user->data['user_message_rules'];
376 $user_id = (int) $user->data['user_id'];
378 $action_ary = $move_into_folder = array();
379 $num_removed = 0;
381 // Newly processing on-hold messages
382 if ($release)
384 $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
385 SET folder_id = ' . PRIVMSGS_NO_BOX . '
386 WHERE folder_id = ' . PRIVMSGS_HOLD_BOX . "
387 AND user_id = $user_id";
388 $db->sql_query($sql);
391 // Get those messages not yet placed into any box
392 $retrieve_sql = 'SELECT t.*, p.*, u.username, u.user_id, u.group_id
393 FROM ' . PRIVMSGS_TO_TABLE . ' t, ' . PRIVMSGS_TABLE . ' p, ' . USERS_TABLE . " u
394 WHERE t.user_id = $user_id
395 AND p.author_id = u.user_id
396 AND t.folder_id = " . PRIVMSGS_NO_BOX . '
397 AND t.msg_id = p.msg_id';
399 // Just place into the appropriate arrays if no rules need to be checked
400 if (!$user_message_rules)
402 $result = $db->sql_query($retrieve_sql);
404 while ($row = $db->sql_fetchrow($result))
406 $action_ary[$row['msg_id']][] = array('action' => false);
408 $db->sql_freeresult($result);
410 else
412 $user_rules = $zebra = $check_rows = array();
413 $user_ids = $memberships = array();
415 // First of all, grab all rules and retrieve friends/foes
416 $sql = 'SELECT *
417 FROM ' . PRIVMSGS_RULES_TABLE . "
418 WHERE user_id = $user_id";
419 $result = $db->sql_query($sql);
420 $user_rules = $db->sql_fetchrowset($result);
421 $db->sql_freeresult($result);
423 if (sizeof($user_rules))
425 $sql = 'SELECT zebra_id, friend, foe
426 FROM ' . ZEBRA_TABLE . "
427 WHERE user_id = $user_id";
428 $result = $db->sql_query($sql);
430 while ($row = $db->sql_fetchrow($result))
432 $zebra[$row['zebra_id']] = $row;
434 $db->sql_freeresult($result);
437 // Now build a bare-bone check_row array
438 $result = $db->sql_query($retrieve_sql);
440 while ($row = $db->sql_fetchrow($result))
442 $check_rows[] = array_merge($row, array(
443 'to' => explode(':', $row['to_address']),
444 'bcc' => explode(':', $row['bcc_address']),
445 'friend' => (isset($zebra[$row['author_id']])) ? $zebra[$row['author_id']]['friend'] : 0,
446 'foe' => (isset($zebra[$row['author_id']])) ? $zebra[$row['author_id']]['foe'] : 0,
447 'user_in_group' => array($user->data['group_id']),
448 'author_in_group' => array())
451 $user_ids[] = $row['user_id'];
453 $db->sql_freeresult($result);
455 // Retrieve user memberships
456 if (sizeof($user_ids))
458 $sql = 'SELECT *
459 FROM ' . USER_GROUP_TABLE . '
460 WHERE ' . $db->sql_in_set('user_id', $user_ids) . '
461 AND user_pending = 0';
462 $result = $db->sql_query($sql);
464 while ($row = $db->sql_fetchrow($result))
466 $memberships[$row['user_id']][] = $row['group_id'];
468 $db->sql_freeresult($result);
471 // Now place into the appropriate folder
472 foreach ($check_rows as $row)
474 // Add membership if set
475 if (isset($memberships[$row['author_id']]))
477 $row['author_in_group'] = $memberships[$row['user_id']];
480 // Check Rule - this should be very quick since we have all information we need
481 $is_match = false;
482 foreach ($user_rules as $rule_row)
484 if (($action = check_rule($global_privmsgs_rules, $rule_row, $row, $user_id)) !== false)
486 $is_match = true;
487 $action_ary[$row['msg_id']][] = $action;
491 if (!$is_match)
493 $action_ary[$row['msg_id']][] = array('action' => false);
497 unset($user_rules, $zebra, $check_rows, $user_ids, $memberships);
500 // We place actions into arrays, to save queries.
501 $sql = $unread_ids = $delete_ids = $important_ids = array();
503 foreach ($action_ary as $msg_id => $msg_ary)
505 // It is allowed to execute actions more than once, except placing messages into folder
506 $folder_action = $message_removed = false;
508 foreach ($msg_ary as $pos => $rule_ary)
510 if ($folder_action && $rule_ary['action'] == ACTION_PLACE_INTO_FOLDER)
512 continue;
515 switch ($rule_ary['action'])
517 case ACTION_PLACE_INTO_FOLDER:
518 // Folder actions have precedence, so we will remove any other ones
519 $folder_action = true;
520 $move_into_folder[(int) $rule_ary['folder_id']][] = $msg_id;
521 break;
523 case ACTION_MARK_AS_READ:
524 if ($rule_ary['pm_unread'])
526 $unread_ids[] = $msg_id;
528 break;
530 case ACTION_DELETE_MESSAGE:
531 $delete_ids[] = $msg_id;
532 $message_removed = true;
533 break;
535 case ACTION_MARK_AS_IMPORTANT:
536 if (!$rule_ary['pm_marked'])
538 $important_ids[] = $msg_id;
540 break;
544 // We place this here because it could happen that the messages are doubled if a rule marks a message and then moves it into a specific
545 // folder. Here we simply move the message into the INBOX if it gets not removed and also not put into a custom folder.
546 if (!$folder_action && !$message_removed)
548 $move_into_folder[PRIVMSGS_INBOX][] = $msg_id;
552 // Do not change the order of processing
553 // The number of queries needed to be executed here highly depends on the defined rules and are
554 // only gone through if new messages arrive.
556 // Delete messages
557 if (sizeof($delete_ids))
559 $num_removed += sizeof($delete_ids);
560 delete_pm($user_id, $delete_ids, PRIVMSGS_NO_BOX);
563 // Set messages to Unread
564 if (sizeof($unread_ids))
566 $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
567 SET pm_unread = 0
568 WHERE ' . $db->sql_in_set('msg_id', $unread_ids) . "
569 AND user_id = $user_id
570 AND folder_id = " . PRIVMSGS_NO_BOX;
571 $db->sql_query($sql);
574 // mark messages as important
575 if (sizeof($important_ids))
577 $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
578 SET pm_marked = 1 - pm_marked
579 WHERE folder_id = ' . PRIVMSGS_NO_BOX . "
580 AND user_id = $user_id
581 AND " . $db->sql_in_set('msg_id', $important_ids);
582 $db->sql_query($sql);
585 // Move into folder
586 $folder = array();
588 if (sizeof($move_into_folder))
590 // Determine Full Folder Action - we need the move to folder id later eventually
591 $full_folder_action = ($user->data['user_full_folder'] == FULL_FOLDER_NONE) ? (phpbb::$config['full_folder_action'] - (FULL_FOLDER_NONE*(-1))) : $user->data['user_full_folder'];
593 $sql_folder = array_keys($move_into_folder);
594 if ($full_folder_action >= 0)
596 $sql_folder[] = $full_folder_action;
599 $sql = 'SELECT folder_id, pm_count
600 FROM ' . PRIVMSGS_FOLDER_TABLE . '
601 WHERE ' . $db->sql_in_set('folder_id', $sql_folder) . "
602 AND user_id = $user_id";
603 $result = $db->sql_query($sql);
605 while ($row = $db->sql_fetchrow($result))
607 $folder[(int) $row['folder_id']] = (int) $row['pm_count'];
609 $db->sql_freeresult($result);
611 unset($sql_folder);
613 if (isset($move_into_folder[PRIVMSGS_INBOX]))
615 $sql = 'SELECT COUNT(msg_id) as num_messages
616 FROM ' . PRIVMSGS_TO_TABLE . "
617 WHERE user_id = $user_id
618 AND folder_id = " . PRIVMSGS_INBOX;
619 $result = $db->sql_query($sql);
620 $folder[PRIVMSGS_INBOX] = (int) $db->sql_fetchfield('num_messages');
621 $db->sql_freeresult($result);
625 // Here we have ideally only one folder to move into
626 foreach ($move_into_folder as $folder_id => $msg_ary)
628 $dest_folder = $folder_id;
629 $full_folder_action = FULL_FOLDER_NONE;
631 // Check Message Limit - we calculate with the complete array, most of the time it is one message
632 // But we are making sure that the other way around works too (more messages in queue than allowed to be stored)
633 if ($user->data['message_limit'] && $folder[$folder_id] && ($folder[$folder_id] + sizeof($msg_ary)) > $user->data['message_limit'])
635 $full_folder_action = ($user->data['user_full_folder'] == FULL_FOLDER_NONE) ? (phpbb::$config['full_folder_action'] - (FULL_FOLDER_NONE*(-1))) : $user->data['user_full_folder'];
637 // If destination folder itself is full...
638 if ($full_folder_action >= 0 && ($folder[$full_folder_action] + sizeof($msg_ary)) > $user->data['message_limit'])
640 $full_folder_action = phpbb::$config['full_folder_action'] - (FULL_FOLDER_NONE*(-1));
643 // If Full Folder Action is to move to another folder, we simply adjust the destination folder
644 if ($full_folder_action >= 0)
646 $dest_folder = $full_folder_action;
648 else if ($full_folder_action == FULL_FOLDER_DELETE)
650 // Delete some messages. NOTE: Ordered by msg_id here instead of message_time!
651 $sql = 'SELECT msg_id
652 FROM ' . PRIVMSGS_TO_TABLE . "
653 WHERE user_id = $user_id
654 AND folder_id = $dest_folder
655 ORDER BY msg_id ASC";
656 $result = $db->sql_query_limit($sql, (($folder[$dest_folder] + sizeof($msg_ary)) - $user->data['message_limit']));
658 $delete_ids = array();
659 while ($row = $db->sql_fetchrow($result))
661 $delete_ids[] = $row['msg_id'];
663 $db->sql_freeresult($result);
665 $num_removed += sizeof($delete_ids);
666 delete_pm($user_id, $delete_ids, $dest_folder);
671 if ($full_folder_action == FULL_FOLDER_HOLD)
673 $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
674 SET folder_id = ' . PRIVMSGS_HOLD_BOX . '
675 WHERE folder_id = ' . PRIVMSGS_NO_BOX . "
676 AND user_id = $user_id
677 AND " . $db->sql_in_set('msg_id', $msg_ary);
678 $db->sql_query($sql);
680 else
682 $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . "
683 SET folder_id = $dest_folder, pm_new = 0
684 WHERE folder_id = " . PRIVMSGS_NO_BOX . "
685 AND user_id = $user_id
686 AND pm_new = 1
687 AND " . $db->sql_in_set('msg_id', $msg_ary);
688 $db->sql_query($sql);
690 if ($dest_folder != PRIVMSGS_INBOX)
692 $sql = 'UPDATE ' . PRIVMSGS_FOLDER_TABLE . '
693 SET pm_count = pm_count + ' . (int) $db->sql_affectedrows() . "
694 WHERE folder_id = $dest_folder
695 AND user_id = $user_id";
696 $db->sql_query($sql);
701 if (sizeof($action_ary))
703 // Move from OUTBOX to SENTBOX
704 // We are not checking any full folder status here... SENTBOX is a special treatment (old messages get deleted)
705 $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
706 SET folder_id = ' . PRIVMSGS_SENTBOX . '
707 WHERE folder_id = ' . PRIVMSGS_OUTBOX . '
708 AND ' . $db->sql_in_set('msg_id', array_keys($action_ary));
709 $db->sql_query($sql);
712 // Update new/unread count
713 update_pm_counts();
715 // Now check how many messages got not moved...
716 $sql = 'SELECT COUNT(msg_id) as num_messages
717 FROM ' . PRIVMSGS_TO_TABLE . "
718 WHERE user_id = $user_id
719 AND folder_id = " . PRIVMSGS_HOLD_BOX;
720 $result = $db->sql_query($sql);
721 $num_not_moved = (int) $db->sql_fetchfield('num_messages');
722 $db->sql_freeresult($result);
724 return array('not_moved' => $num_not_moved, 'removed' => $num_removed);
728 * Move PM from one to another folder
730 function move_pm($user_id, $message_limit, $move_msg_ids, $dest_folder, $cur_folder_id)
732 global $db, $user;
734 $num_moved = 0;
736 if (!is_array($move_msg_ids))
738 $move_msg_ids = array($move_msg_ids);
741 if (sizeof($move_msg_ids) && !in_array($dest_folder, array(PRIVMSGS_NO_BOX, PRIVMSGS_OUTBOX, PRIVMSGS_SENTBOX)) &&
742 !in_array($cur_folder_id, array(PRIVMSGS_NO_BOX, PRIVMSGS_OUTBOX)) && $cur_folder_id != $dest_folder)
744 // We have to check the destination folder ;)
745 if ($dest_folder != PRIVMSGS_INBOX)
747 $sql = 'SELECT folder_id, folder_name, pm_count
748 FROM ' . PRIVMSGS_FOLDER_TABLE . "
749 WHERE folder_id = $dest_folder
750 AND user_id = $user_id";
751 $result = $db->sql_query($sql);
752 $row = $db->sql_fetchrow($result);
753 $db->sql_freeresult($result);
755 if (!$row)
757 trigger_error('NOT_AUTHORISED');
760 if ($message_limit && $row['pm_count'] + sizeof($move_msg_ids) > $message_limit)
762 $message = sprintf($user->lang['NOT_ENOUGH_SPACE_FOLDER'], $row['folder_name']) . '<br /><br />';
763 $message .= sprintf($user->lang['CLICK_RETURN_FOLDER'], '<a href="' . append_sid('ucp', 'i=pm&amp;folder=' . $row['folder_id']) . '">', '</a>', $row['folder_name']);
764 trigger_error($message);
767 else
769 $sql = 'SELECT COUNT(msg_id) as num_messages
770 FROM ' . PRIVMSGS_TO_TABLE . '
771 WHERE folder_id = ' . PRIVMSGS_INBOX . "
772 AND user_id = $user_id";
773 $result = $db->sql_query($sql);
774 $num_messages = (int) $db->sql_fetchfield('num_messages');
775 $db->sql_freeresult($result);
777 if ($message_limit && $num_messages + sizeof($move_msg_ids) > $message_limit)
779 $message = sprintf($user->lang['NOT_ENOUGH_SPACE_FOLDER'], $user->lang['PM_INBOX']) . '<br /><br />';
780 $message .= sprintf($user->lang['CLICK_RETURN_FOLDER'], '<a href="' . append_sid('ucp', 'i=pm&amp;folder=inbox') . '">', '</a>', $user->lang['PM_INBOX']);
781 trigger_error($message);
785 $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . "
786 SET folder_id = $dest_folder
787 WHERE folder_id = $cur_folder_id
788 AND user_id = $user_id
789 AND " . $db->sql_in_set('msg_id', $move_msg_ids);
790 $db->sql_query($sql);
791 $num_moved = $db->sql_affectedrows();
793 // Update pm counts
794 if ($num_moved)
796 if (!in_array($cur_folder_id, array(PRIVMSGS_INBOX, PRIVMSGS_OUTBOX, PRIVMSGS_SENTBOX)))
798 $sql = 'UPDATE ' . PRIVMSGS_FOLDER_TABLE . "
799 SET pm_count = pm_count - $num_moved
800 WHERE folder_id = $cur_folder_id
801 AND user_id = $user_id";
802 $db->sql_query($sql);
805 if ($dest_folder != PRIVMSGS_INBOX)
807 $sql = 'UPDATE ' . PRIVMSGS_FOLDER_TABLE . "
808 SET pm_count = pm_count + $num_moved
809 WHERE folder_id = $dest_folder
810 AND user_id = $user_id";
811 $db->sql_query($sql);
815 else if (in_array($cur_folder_id, array(PRIVMSGS_NO_BOX, PRIVMSGS_OUTBOX)))
817 trigger_error('CANNOT_MOVE_SPECIAL');
820 return $num_moved;
824 * Update unread message status
826 function update_unread_status($unread, $msg_id, $user_id, $folder_id)
828 if (!$unread)
830 return;
833 global $db, $user;
835 $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . "
836 SET pm_unread = 0
837 WHERE msg_id = $msg_id
838 AND user_id = $user_id
839 AND folder_id = $folder_id";
840 $db->sql_query($sql);
842 $sql = 'UPDATE ' . USERS_TABLE . "
843 SET user_unread_privmsg = user_unread_privmsg - 1
844 WHERE user_id = $user_id";
845 $db->sql_query($sql);
847 if ($user->data['user_id'] == $user_id)
849 $user->data['user_unread_privmsg']--;
851 // Try to cope with previous wrong conversions...
852 if ($user->data['user_unread_privmsg'] < 0)
854 $sql = 'UPDATE ' . USERS_TABLE . "
855 SET user_unread_privmsg = 0
856 WHERE user_id = $user_id";
857 $db->sql_query($sql);
859 $user->data['user_unread_privmsg'] = 0;
865 * Handle all actions possible with marked messages
867 function handle_mark_actions($user_id, $mark_action)
869 global $db, $user;
871 $msg_ids = request_var('marked_msg_id', array(0));
872 $cur_folder_id = request_var('cur_folder_id', PRIVMSGS_NO_BOX);
873 $confirm = phpbb_request::is_set_post('confirm');
875 if (!sizeof($msg_ids))
877 return false;
880 switch ($mark_action)
882 case 'mark_important':
884 $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . "
885 SET pm_marked = 1 - pm_marked
886 WHERE folder_id = $cur_folder_id
887 AND user_id = $user_id
888 AND " . $db->sql_in_set('msg_id', $msg_ids);
889 $db->sql_query($sql);
891 break;
893 case 'delete_marked':
895 if (confirm_box(true))
897 delete_pm($user_id, $msg_ids, $cur_folder_id);
899 $success_msg = (sizeof($msg_ids) == 1) ? 'MESSAGE_DELETED' : 'MESSAGES_DELETED';
900 $redirect = append_sid('ucp', 'i=pm&amp;folder=' . $cur_folder_id);
902 meta_refresh(3, $redirect);
903 trigger_error($user->lang[$success_msg] . '<br /><br />' . sprintf($user->lang['RETURN_FOLDER'], '<a href="' . $redirect . '">', '</a>'));
905 else
907 $s_hidden_fields = array(
908 'cur_folder_id' => $cur_folder_id,
909 'mark_option' => 'delete_marked',
910 'submit_mark' => true,
911 'marked_msg_id' => $msg_ids
914 confirm_box(false, 'DELETE_MARKED_PM', build_hidden_fields($s_hidden_fields));
917 break;
919 default:
920 return false;
923 return true;
927 * Delete PM(s)
929 function delete_pm($user_id, $msg_ids, $folder_id)
931 global $db, $user;
933 $user_id = (int) $user_id;
934 $folder_id = (int) $folder_id;
936 if (!$user_id)
938 return false;
941 if (!is_array($msg_ids))
943 if (!$msg_ids)
945 return false;
947 $msg_ids = array($msg_ids);
950 if (!sizeof($msg_ids))
952 return false;
955 // Get PM Information for later deleting
956 $sql = 'SELECT msg_id, pm_unread, pm_new
957 FROM ' . PRIVMSGS_TO_TABLE . '
958 WHERE ' . $db->sql_in_set('msg_id', array_map('intval', $msg_ids)) . "
959 AND folder_id = $folder_id
960 AND user_id = $user_id";
961 $result = $db->sql_query($sql);
963 $delete_rows = array();
964 $num_unread = $num_new = $num_deleted = 0;
965 while ($row = $db->sql_fetchrow($result))
967 $num_unread += (int) $row['pm_unread'];
968 $num_new += (int) $row['pm_new'];
970 $delete_rows[$row['msg_id']] = 1;
972 $db->sql_freeresult($result);
973 unset($msg_ids);
975 if (!sizeof($delete_rows))
977 return false;
980 $db->sql_transaction('begin');
982 // if no one has read the message yet (meaning it is in users outbox)
983 // then mark the message as deleted...
984 if ($folder_id == PRIVMSGS_OUTBOX)
986 // Remove PM from Outbox
987 $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . "
988 WHERE user_id = $user_id AND folder_id = " . PRIVMSGS_OUTBOX . '
989 AND ' . $db->sql_in_set('msg_id', array_keys($delete_rows));
990 $db->sql_query($sql);
992 // Update PM Information for safety
993 $sql = 'UPDATE ' . PRIVMSGS_TABLE . " SET message_text = ''
994 WHERE " . $db->sql_in_set('msg_id', array_keys($delete_rows));
995 $db->sql_query($sql);
997 // Set delete flag for those intended to receive the PM
998 // We do not remove the message actually, to retain some basic information (sent time for example)
999 $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
1000 SET pm_deleted = 1
1001 WHERE ' . $db->sql_in_set('msg_id', array_keys($delete_rows));
1002 $db->sql_query($sql);
1004 $num_deleted = $db->sql_affectedrows();
1006 else
1008 // Delete private message data
1009 $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . "
1010 WHERE user_id = $user_id
1011 AND folder_id = $folder_id
1012 AND " . $db->sql_in_set('msg_id', array_keys($delete_rows));
1013 $db->sql_query($sql);
1014 $num_deleted = $db->sql_affectedrows();
1017 // if folder id is user defined folder then decrease pm_count
1018 if (!in_array($folder_id, array(PRIVMSGS_INBOX, PRIVMSGS_OUTBOX, PRIVMSGS_SENTBOX, PRIVMSGS_NO_BOX)))
1020 $sql = 'UPDATE ' . PRIVMSGS_FOLDER_TABLE . "
1021 SET pm_count = pm_count - $num_deleted
1022 WHERE folder_id = $folder_id";
1023 $db->sql_query($sql);
1026 // Update unread and new status field
1027 if ($num_unread || $num_new)
1029 $set_sql = ($num_unread) ? 'user_unread_privmsg = user_unread_privmsg - ' . $num_unread : '';
1031 if ($num_new)
1033 $set_sql .= ($set_sql != '') ? ', ' : '';
1034 $set_sql .= 'user_new_privmsg = user_new_privmsg - ' . $num_new;
1037 $db->sql_query('UPDATE ' . USERS_TABLE . " SET $set_sql WHERE user_id = $user_id");
1039 $user->data['user_new_privmsg'] -= $num_new;
1040 $user->data['user_unread_privmsg'] -= $num_unread;
1043 // Now we have to check which messages we can delete completely
1044 $sql = 'SELECT msg_id
1045 FROM ' . PRIVMSGS_TO_TABLE . '
1046 WHERE ' . $db->sql_in_set('msg_id', array_keys($delete_rows));
1047 $result = $db->sql_query($sql);
1049 while ($row = $db->sql_fetchrow($result))
1051 unset($delete_rows[$row['msg_id']]);
1053 $db->sql_freeresult($result);
1055 $delete_ids = array_keys($delete_rows);
1057 if (sizeof($delete_ids))
1059 // Check if there are any attachments we need to remove
1060 if (!function_exists('delete_attachments'))
1062 include(PHPBB_ROOT_PATH . 'includes/functions_admin.' . PHP_EXT);
1065 delete_attachments('message', $delete_ids, false);
1067 $sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '
1068 WHERE ' . $db->sql_in_set('msg_id', $delete_ids);
1069 $db->sql_query($sql);
1072 $db->sql_transaction('commit');
1074 return true;
1078 * Rebuild message header
1080 function rebuild_header($check_ary)
1082 global $db;
1084 $address = array();
1086 foreach ($check_ary as $check_type => $address_field)
1088 // Split Addresses into users and groups
1089 preg_match_all('/:?(u|g)_([0-9]+):?/', $address_field, $match);
1091 $u = $g = array();
1092 foreach ($match[1] as $id => $type)
1094 ${$type}[] = (int) $match[2][$id];
1097 $_types = array('u', 'g');
1098 foreach ($_types as $type)
1100 if (sizeof($$type))
1102 foreach ($$type as $id)
1104 $address[$type][$id] = $check_type;
1110 return $address;
1114 * Print out/assign recipient information
1116 function write_pm_addresses($check_ary, $author_id, $plaintext = false)
1118 global $db, $user, $template;
1120 $addresses = array();
1122 foreach ($check_ary as $check_type => $address_field)
1124 if (!is_array($address_field))
1126 // Split Addresses into users and groups
1127 preg_match_all('/:?(u|g)_([0-9]+):?/', $address_field, $match);
1129 $u = $g = array();
1130 foreach ($match[1] as $id => $type)
1132 ${$type}[] = (int) $match[2][$id];
1135 else
1137 $u = $address_field['u'];
1138 $g = $address_field['g'];
1141 $address = array();
1142 if (sizeof($u))
1144 $sql = 'SELECT user_id, username, user_colour
1145 FROM ' . USERS_TABLE . '
1146 WHERE ' . $db->sql_in_set('user_id', $u) . '
1147 AND user_type IN (' . phpbb::USER_NORMAL . ', ' . phpbb::USER_FOUNDER . ')';
1148 $result = $db->sql_query($sql);
1150 while ($row = $db->sql_fetchrow($result))
1152 if ($check_type == 'to' || $author_id == $user->data['user_id'] || $row['user_id'] == $user->data['user_id'])
1154 if ($plaintext)
1156 $address[] = $row['username'];
1158 else
1160 $address['user'][$row['user_id']] = array('name' => $row['username'], 'colour' => $row['user_colour']);
1164 $db->sql_freeresult($result);
1167 if (sizeof($g))
1169 if ($plaintext)
1171 $sql = 'SELECT group_name, group_type
1172 FROM ' . GROUPS_TABLE . '
1173 WHERE ' . $db->sql_in_set('group_id', $g);
1174 $result = $db->sql_query($sql);
1176 while ($row = $db->sql_fetchrow($result))
1178 if ($check_type == 'to' || $author_id == $user->data['user_id'] || $row['user_id'] == $user->data['user_id'])
1180 $address[] = ($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['group_name']] : $row['group_name'];
1183 $db->sql_freeresult($result);
1185 else
1187 $sql = 'SELECT g.group_id, g.group_name, g.group_colour, g.group_type, ug.user_id
1188 FROM ' . GROUPS_TABLE . ' g, ' . USER_GROUP_TABLE . ' ug
1189 WHERE ' . $db->sql_in_set('g.group_id', $g) . '
1190 AND g.group_id = ug.group_id
1191 AND ug.user_pending = 0';
1192 $result = $db->sql_query($sql);
1194 while ($row = $db->sql_fetchrow($result))
1196 if (!isset($address['group'][$row['group_id']]))
1198 if ($check_type == 'to' || $author_id == $user->data['user_id'] || $row['user_id'] == $user->data['user_id'])
1200 $row['group_name'] = ($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['group_name']] : $row['group_name'];
1201 $address['group'][$row['group_id']] = array('name' => $row['group_name'], 'colour' => $row['group_colour']);
1205 if (isset($address['user'][$row['user_id']]))
1207 $address['user'][$row['user_id']]['in_group'] = $row['group_id'];
1210 $db->sql_freeresult($result);
1214 if (sizeof($address) && !$plaintext)
1216 $template->assign_var('S_' . strtoupper($check_type) . '_RECIPIENT', true);
1218 foreach ($address as $type => $adr_ary)
1220 foreach ($adr_ary as $id => $row)
1222 $tpl_ary = array(
1223 'IS_GROUP' => ($type == 'group') ? true : false,
1224 'IS_USER' => ($type == 'user') ? true : false,
1225 'UG_ID' => $id,
1226 'NAME' => $row['name'],
1227 'COLOUR' => ($row['colour']) ? '#' . $row['colour'] : '',
1228 'TYPE' => $type,
1231 if ($type == 'user')
1233 $tpl_ary = array_merge($tpl_ary, array(
1234 'U_VIEW' => get_username_string('profile', $id, $row['name'], $row['colour']),
1235 'NAME_FULL' => get_username_string('full', $id, $row['name'], $row['colour']),
1238 else
1240 $tpl_ary = array_merge($tpl_ary, array(
1241 'U_VIEW' => append_sid('memberlist', 'mode=group&amp;g=' . $id),
1245 $template->assign_block_vars($check_type . '_recipient', $tpl_ary);
1250 $addresses[$check_type] = $address;
1253 return $addresses;
1257 * Get folder status
1259 function get_folder_status($folder_id, $folder)
1261 global $db, $user;
1263 if (isset($folder[$folder_id]))
1265 $folder = $folder[$folder_id];
1267 else
1269 return false;
1272 $return = array(
1273 'folder_name' => $folder['folder_name'],
1274 'cur' => $folder['num_messages'],
1275 'remaining' => ($user->data['message_limit']) ? $user->data['message_limit'] - $folder['num_messages'] : 0,
1276 'max' => $user->data['message_limit'],
1277 'percent' => ($user->data['message_limit']) ? (($user->data['message_limit'] > 0) ? round(($folder['num_messages'] / $user->data['message_limit']) * 100) : 100) : 0,
1280 $return['message'] = sprintf($user->lang['FOLDER_STATUS_MSG'], $return['percent'], $return['cur'], $return['max']);
1282 return $return;
1286 // COMPOSE MESSAGES
1290 * Submit PM
1292 function submit_pm($mode, $subject, &$data, $put_in_outbox = true)
1294 global $db, $auth, $template, $user;
1296 // We do not handle erasing pms here
1297 if ($mode == 'delete')
1299 return false;
1302 $current_time = time();
1304 // Collect some basic information about which tables and which rows to update/insert
1305 $sql_data = array();
1306 $root_level = 0;
1308 // Recipient Information
1309 $recipients = $to = $bcc = array();
1311 if ($mode != 'edit')
1313 // Build Recipient List
1314 // u|g => array($user_id => 'to'|'bcc')
1315 $_types = array('u', 'g');
1316 foreach ($_types as $ug_type)
1318 if (isset($data['address_list'][$ug_type]) && sizeof($data['address_list'][$ug_type]))
1320 foreach ($data['address_list'][$ug_type] as $id => $field)
1322 $id = (int) $id;
1324 // Do not rely on the address list being "valid"
1325 if (!$id || ($ug_type == 'u' && $id == ANONYMOUS))
1327 continue;
1330 $field = ($field == 'to') ? 'to' : 'bcc';
1331 if ($ug_type == 'u')
1333 $recipients[$id] = $field;
1335 ${$field}[] = $ug_type . '_' . $id;
1340 if (isset($data['address_list']['g']) && sizeof($data['address_list']['g']))
1342 // We need to check the PM status of group members (do they want to receive PM's?)
1343 // Only check if not a moderator or admin, since they are allowed to override this user setting
1344 $sql_allow_pm = (!$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_')) ? ' AND u.user_allow_pm = 1' : '';
1346 $sql = 'SELECT u.user_type, ug.group_id, ug.user_id
1347 FROM ' . USERS_TABLE . ' u, ' . USER_GROUP_TABLE . ' ug
1348 WHERE ' . $db->sql_in_set('ug.group_id', array_keys($data['address_list']['g'])) . '
1349 AND ug.user_pending = 0
1350 AND u.user_id = ug.user_id
1351 AND u.user_type IN (' . phpbb::USER_NORMAL . ', ' . phpbb::USER_FOUNDER . ')' .
1352 $sql_allow_pm;
1353 $result = $db->sql_query($sql);
1355 while ($row = $db->sql_fetchrow($result))
1357 $field = ($data['address_list']['g'][$row['group_id']] == 'to') ? 'to' : 'bcc';
1358 $recipients[$row['user_id']] = $field;
1360 $db->sql_freeresult($result);
1363 if (!sizeof($recipients))
1365 trigger_error('NO_RECIPIENT');
1369 $db->sql_transaction('begin');
1371 $sql = '';
1373 switch ($mode)
1375 case 'reply':
1376 case 'quote':
1377 $root_level = ($data['reply_from_root_level']) ? $data['reply_from_root_level'] : $data['reply_from_msg_id'];
1379 // Set message_replied switch for this user
1380 $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . '
1381 SET pm_replied = 1
1382 WHERE user_id = ' . $data['from_user_id'] . '
1383 AND msg_id = ' . $data['reply_from_msg_id'];
1385 // no break
1387 case 'forward':
1388 case 'post':
1389 case 'quotepost':
1390 $sql_data = array(
1391 'root_level' => $root_level,
1392 'author_id' => $data['from_user_id'],
1393 'icon_id' => $data['icon_id'],
1394 'author_ip' => $data['from_user_ip'],
1395 'message_time' => $current_time,
1396 'enable_bbcode' => $data['enable_bbcode'],
1397 'enable_smilies' => $data['enable_smilies'],
1398 'enable_magic_url' => $data['enable_urls'],
1399 'enable_sig' => $data['enable_sig'],
1400 'message_subject' => $subject,
1401 'message_text' => $data['message'],
1402 'message_attachment'=> (!empty($data['attachment_data'])) ? 1 : 0,
1403 'bbcode_bitfield' => $data['bbcode_bitfield'],
1404 'bbcode_uid' => $data['bbcode_uid'],
1405 'to_address' => implode(':', $to),
1406 'bcc_address' => implode(':', $bcc)
1408 break;
1410 case 'edit':
1411 $sql_data = array(
1412 'icon_id' => $data['icon_id'],
1413 'message_edit_time' => $current_time,
1414 'enable_bbcode' => $data['enable_bbcode'],
1415 'enable_smilies' => $data['enable_smilies'],
1416 'enable_magic_url' => $data['enable_urls'],
1417 'enable_sig' => $data['enable_sig'],
1418 'message_subject' => $subject,
1419 'message_text' => $data['message'],
1420 'message_attachment'=> (!empty($data['attachment_data'])) ? 1 : 0,
1421 'bbcode_bitfield' => $data['bbcode_bitfield'],
1422 'bbcode_uid' => $data['bbcode_uid']
1424 break;
1427 if (sizeof($sql_data))
1429 $query = '';
1431 if ($mode == 'post' || $mode == 'reply' || $mode == 'quote' || $mode == 'quotepost' || $mode == 'forward')
1433 $db->sql_query('INSERT INTO ' . PRIVMSGS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_data));
1434 $data['msg_id'] = $db->sql_nextid();
1436 else if ($mode == 'edit')
1438 $sql = 'UPDATE ' . PRIVMSGS_TABLE . '
1439 SET message_edit_count = message_edit_count + 1, ' . $db->sql_build_array('UPDATE', $sql_data) . '
1440 WHERE msg_id = ' . $data['msg_id'];
1441 $db->sql_query($sql);
1445 if ($mode != 'edit')
1447 if ($sql)
1449 $db->sql_query($sql);
1451 unset($sql);
1453 $sql_ary = array();
1454 foreach ($recipients as $user_id => $type)
1456 $sql_ary[] = array(
1457 'msg_id' => (int) $data['msg_id'],
1458 'user_id' => (int) $user_id,
1459 'author_id' => (int) $data['from_user_id'],
1460 'folder_id' => PRIVMSGS_NO_BOX,
1461 'pm_new' => 1,
1462 'pm_unread' => 1,
1463 'pm_forwarded' => ($mode == 'forward') ? 1 : 0
1467 $db->sql_multi_insert(PRIVMSGS_TO_TABLE, $sql_ary);
1469 $sql = 'UPDATE ' . USERS_TABLE . '
1470 SET user_new_privmsg = user_new_privmsg + 1, user_unread_privmsg = user_unread_privmsg + 1, user_last_privmsg = ' . time() . '
1471 WHERE ' . $db->sql_in_set('user_id', array_keys($recipients));
1472 $db->sql_query($sql);
1474 // Put PM into outbox
1475 if ($put_in_outbox)
1477 $db->sql_query('INSERT INTO ' . PRIVMSGS_TO_TABLE . ' ' . $db->sql_build_array('INSERT', array(
1478 'msg_id' => (int) $data['msg_id'],
1479 'user_id' => (int) $data['from_user_id'],
1480 'author_id' => (int) $data['from_user_id'],
1481 'folder_id' => PRIVMSGS_OUTBOX,
1482 'pm_new' => 0,
1483 'pm_unread' => 0,
1484 'pm_forwarded' => ($mode == 'forward') ? 1 : 0))
1489 // Set user last post time
1490 if ($mode == 'reply' || $mode == 'quote' || $mode == 'quotepost' || $mode == 'forward' || $mode == 'post')
1492 $sql = 'UPDATE ' . USERS_TABLE . "
1493 SET user_lastpost_time = $current_time
1494 WHERE user_id = " . $data['from_user_id'];
1495 $db->sql_query($sql);
1498 // Submit Attachments
1499 if (!empty($data['attachment_data']) && $data['msg_id'] && in_array($mode, array('post', 'reply', 'quote', 'quotepost', 'edit', 'forward')))
1501 $space_taken = $files_added = 0;
1502 $orphan_rows = array();
1504 foreach ($data['attachment_data'] as $pos => $attach_row)
1506 $orphan_rows[(int) $attach_row['attach_id']] = array();
1509 if (sizeof($orphan_rows))
1511 $sql = 'SELECT attach_id, filesize, physical_filename
1512 FROM ' . ATTACHMENTS_TABLE . '
1513 WHERE ' . $db->sql_in_set('attach_id', array_keys($orphan_rows)) . '
1514 AND in_message = 1
1515 AND is_orphan = 1
1516 AND poster_id = ' . $user->data['user_id'];
1517 $result = $db->sql_query($sql);
1519 $orphan_rows = array();
1520 while ($row = $db->sql_fetchrow($result))
1522 $orphan_rows[$row['attach_id']] = $row;
1524 $db->sql_freeresult($result);
1527 foreach ($data['attachment_data'] as $pos => $attach_row)
1529 if ($attach_row['is_orphan'] && !isset($orphan_rows[$attach_row['attach_id']]))
1531 continue;
1534 if (!$attach_row['is_orphan'])
1536 // update entry in db if attachment already stored in db and filespace
1537 $sql = 'UPDATE ' . ATTACHMENTS_TABLE . "
1538 SET attach_comment = '" . $db->sql_escape($attach_row['attach_comment']) . "'
1539 WHERE attach_id = " . (int) $attach_row['attach_id'] . '
1540 AND is_orphan = 0';
1541 $db->sql_query($sql);
1543 else
1545 // insert attachment into db
1546 if (!@file_exists(PHPBB_ROOT_PATH . phpbb::$config['upload_path'] . '/' . basename($orphan_rows[$attach_row['attach_id']]['physical_filename'])))
1548 continue;
1551 $space_taken += $orphan_rows[$attach_row['attach_id']]['filesize'];
1552 $files_added++;
1554 $attach_sql = array(
1555 'post_msg_id' => $data['msg_id'],
1556 'topic_id' => 0,
1557 'is_orphan' => 0,
1558 'poster_id' => $data['from_user_id'],
1559 'attach_comment' => $attach_row['attach_comment'],
1562 $sql = 'UPDATE ' . ATTACHMENTS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $attach_sql) . '
1563 WHERE attach_id = ' . $attach_row['attach_id'] . '
1564 AND is_orphan = 1
1565 AND poster_id = ' . $user->data['user_id'];
1566 $db->sql_query($sql);
1570 if ($space_taken && $files_added)
1572 set_config('upload_dir_size', phpbb::$config['upload_dir_size'] + $space_taken, true);
1573 set_config('num_files', phpbb::$config['num_files'] + $files_added, true);
1577 // Delete draft if post was loaded...
1578 $draft_id = request_var('draft_loaded', 0);
1579 if ($draft_id)
1581 $sql = 'DELETE FROM ' . DRAFTS_TABLE . "
1582 WHERE draft_id = $draft_id
1583 AND user_id = " . $data['from_user_id'];
1584 $db->sql_query($sql);
1587 $db->sql_transaction('commit');
1589 // Send Notifications
1590 if ($mode != 'edit')
1592 pm_notification($mode, $data['from_username'], $recipients, $subject, $data['message']);
1595 return $data['msg_id'];
1599 * PM Notification
1601 function pm_notification($mode, $author, $recipients, $subject, $message)
1603 global $db, $user, $auth;
1605 $subject = censor_text($subject);
1607 unset($recipients[ANONYMOUS], $recipients[$user->data['user_id']]);
1609 if (!sizeof($recipients))
1611 return;
1614 // Get banned User ID's
1615 $sql = 'SELECT ban_userid
1616 FROM ' . BANLIST_TABLE . '
1617 WHERE ' . $db->sql_in_set('ban_userid', array_map('intval', array_keys($recipients))) . '
1618 AND ban_exclude = 0';
1619 $result = $db->sql_query($sql);
1621 while ($row = $db->sql_fetchrow($result))
1623 unset($recipients[$row['ban_userid']]);
1625 $db->sql_freeresult($result);
1627 if (!sizeof($recipients))
1629 return;
1632 $sql = 'SELECT user_id, username, user_email, user_lang, user_notify_pm, user_notify_type, user_jabber
1633 FROM ' . USERS_TABLE . '
1634 WHERE ' . $db->sql_in_set('user_id', array_map('intval', array_keys($recipients)));
1635 $result = $db->sql_query($sql);
1637 $msg_list_ary = array();
1638 while ($row = $db->sql_fetchrow($result))
1640 if ($row['user_notify_pm'] == 1 && trim($row['user_email']))
1642 $msg_list_ary[] = array(
1643 'method' => $row['user_notify_type'],
1644 'email' => $row['user_email'],
1645 'jabber' => $row['user_jabber'],
1646 'name' => $row['username'],
1647 'lang' => $row['user_lang']
1651 $db->sql_freeresult($result);
1653 if (!sizeof($msg_list_ary))
1655 return;
1658 include_once(PHPBB_ROOT_PATH . 'includes/functions_messenger.' . PHP_EXT);
1659 $messenger = new messenger();
1661 foreach ($msg_list_ary as $pos => $addr)
1663 $messenger->template('privmsg_notify', $addr['lang']);
1665 $messenger->to($addr['email'], $addr['name']);
1666 $messenger->im($addr['jabber'], $addr['name']);
1668 $messenger->assign_vars(array(
1669 'SUBJECT' => htmlspecialchars_decode($subject),
1670 'AUTHOR_NAME' => htmlspecialchars_decode($author),
1671 'USERNAME' => htmlspecialchars_decode($addr['name']),
1673 'U_INBOX' => generate_board_url() . '/ucp.' . PHP_EXT . '?i=pm&folder=inbox')
1676 $messenger->send($addr['method']);
1678 unset($msg_list_ary);
1680 $messenger->save_queue();
1682 unset($messenger);
1686 * Display Message History
1688 function message_history($msg_id, $user_id, $message_row, $folder, $in_post_mode = false)
1690 global $db, $user, $template, $auth, $bbcode;
1692 // Get History Messages (could be newer)
1693 $sql = 'SELECT t.*, p.*, u.*
1694 FROM ' . PRIVMSGS_TABLE . ' p, ' . PRIVMSGS_TO_TABLE . ' t, ' . USERS_TABLE . ' u
1695 WHERE t.msg_id = p.msg_id
1696 AND p.author_id = u.user_id
1697 AND t.folder_id NOT IN (' . PRIVMSGS_NO_BOX . ', ' . PRIVMSGS_HOLD_BOX . ")
1698 AND t.user_id = $user_id";
1700 if (!$message_row['root_level'])
1702 $sql .= " AND (p.root_level = $msg_id OR (p.root_level = 0 AND p.msg_id = $msg_id))";
1704 else
1706 $sql .= " AND (p.root_level = " . $message_row['root_level'] . ' OR p.msg_id = ' . $message_row['root_level'] . ')';
1708 $sql .= ' ORDER BY p.message_time DESC';
1710 $result = $db->sql_query($sql);
1711 $row = $db->sql_fetchrow($result);
1713 if (!$row)
1715 $db->sql_freeresult($result);
1716 return false;
1719 $rowset = array();
1720 $bbcode_bitfield = '';
1721 $folder_url = append_sid('ucp', 'i=pm') . '&amp;folder=';
1725 $folder_id = (int) $row['folder_id'];
1727 $row['folder'][] = (isset($folder[$folder_id])) ? '<a href="' . $folder_url . $folder_id . '">' . $folder[$folder_id]['folder_name'] . '</a>' : $user->lang['UNKNOWN_FOLDER'];
1729 if (isset($rowset[$row['msg_id']]))
1731 $rowset[$row['msg_id']]['folder'][] = (isset($folder[$folder_id])) ? '<a href="' . $folder_url . $folder_id . '">' . $folder[$folder_id]['folder_name'] . '</a>' : $user->lang['UNKNOWN_FOLDER'];
1733 else
1735 $rowset[$row['msg_id']] = $row;
1736 $bbcode_bitfield = $bbcode_bitfield | base64_decode($row['bbcode_bitfield']);
1739 while ($row = $db->sql_fetchrow($result));
1740 $db->sql_freeresult($result);
1742 $title = $row['message_subject'];
1744 if (sizeof($rowset) == 1 && !$in_post_mode)
1746 return false;
1749 // Instantiate BBCode class
1750 if ((empty($bbcode) || $bbcode === false) && $bbcode_bitfield !== '')
1752 if (!class_exists('bbcode'))
1754 include(PHPBB_ROOT_PATH . 'includes/bbcode.' . PHP_EXT);
1756 $bbcode = new bbcode(base64_encode($bbcode_bitfield));
1759 $title = censor_text($title);
1761 $url = append_sid('ucp', 'i=pm');
1762 $next_history_pm = $previous_history_pm = $prev_id = 0;
1764 foreach ($rowset as $id => $row)
1766 $author_id = $row['author_id'];
1767 $folder_id = (int) $row['folder_id'];
1769 $subject = $row['message_subject'];
1770 $message = $row['message_text'];
1772 $message = censor_text($message);
1774 $decoded_message = false;
1776 if ($in_post_mode && $auth->acl_get('u_sendpm') && $author_id != ANONYMOUS && $author_id != $user->data['user_id'])
1778 $decoded_message = $message;
1779 decode_message($decoded_message, $row['bbcode_uid']);
1781 $decoded_message = bbcode_nl2br($decoded_message);
1784 if ($row['bbcode_bitfield'])
1786 $bbcode->bbcode_second_pass($message, $row['bbcode_uid'], $row['bbcode_bitfield']);
1789 $message = bbcode_nl2br($message);
1790 $message = smiley_text($message, !$row['enable_smilies']);
1792 $subject = censor_text($subject);
1794 if ($id == $msg_id)
1796 $next_history_pm = next($rowset);
1797 $next_history_pm = (sizeof($next_history_pm)) ? (int) $next_history_pm['msg_id'] : 0;
1798 $previous_history_pm = $prev_id;
1801 $template->assign_block_vars('history_row', array(
1802 'MESSAGE_AUTHOR_QUOTE' => (($decoded_message) ? addslashes(get_username_string('username', $author_id, $row['username'], $row['user_colour'], $row['username'])) : ''),
1803 'MESSAGE_AUTHOR_FULL' => get_username_string('full', $author_id, $row['username'], $row['user_colour'], $row['username']),
1804 'MESSAGE_AUTHOR_COLOUR' => get_username_string('colour', $author_id, $row['username'], $row['user_colour'], $row['username']),
1805 'MESSAGE_AUTHOR' => get_username_string('username', $author_id, $row['username'], $row['user_colour'], $row['username']),
1806 'U_MESSAGE_AUTHOR' => get_username_string('profile', $author_id, $row['username'], $row['user_colour'], $row['username']),
1808 'SUBJECT' => $subject,
1809 'SENT_DATE' => $user->format_date($row['message_time']),
1810 'MESSAGE' => $message,
1811 'FOLDER' => implode(', ', $row['folder']),
1812 'DECODED_MESSAGE' => $decoded_message,
1814 'S_CURRENT_MSG' => ($row['msg_id'] == $msg_id),
1815 'S_AUTHOR_DELETED' => ($author_id == ANONYMOUS) ? true : false,
1816 'S_IN_POST_MODE' => $in_post_mode,
1818 'MSG_ID' => $row['msg_id'],
1819 'U_VIEW_MESSAGE' => "$url&amp;f=$folder_id&amp;p=" . $row['msg_id'],
1820 'U_QUOTE' => (!$in_post_mode && $auth->acl_get('u_sendpm') && $author_id != ANONYMOUS && $author_id != $user->data['user_id']) ? "$url&amp;mode=compose&amp;action=quote&amp;f=" . $folder_id . "&amp;p=" . $row['msg_id'] : '',
1821 'U_POST_REPLY_PM' => ($author_id != $user->data['user_id'] && $author_id != ANONYMOUS && $auth->acl_get('u_sendpm')) ? "$url&amp;mode=compose&amp;action=reply&amp;f=$folder_id&amp;p=" . $row['msg_id'] : '')
1823 unset($rowset[$id]);
1824 $prev_id = $id;
1827 $template->assign_vars(array(
1828 'QUOTE_IMG' => $user->img('icon_post_quote', $user->lang['REPLY_WITH_QUOTE']),
1829 'HISTORY_TITLE' => $title,
1831 'U_VIEW_NEXT_HISTORY' => ($next_history_pm) ? "$url&amp;p=" . $next_history_pm : '',
1832 'U_VIEW_PREVIOUS_HISTORY' => ($previous_history_pm) ? "$url&amp;p=" . $previous_history_pm : '',
1835 return true;
1839 * Set correct users max messages in PM folder.
1840 * If several group memberships define different amount of messages, the highest will be chosen.
1842 function set_user_message_limit()
1844 global $user, $db;
1846 // Get maximum about from user memberships - if it is 0, there is no limit set and we use the maximum value within the config.
1847 $sql = 'SELECT MAX(g.group_message_limit) as max_message_limit
1848 FROM ' . GROUPS_TABLE . ' g, ' . USER_GROUP_TABLE . ' ug
1849 WHERE ug.user_id = ' . $user->data['user_id'] . '
1850 AND ug.user_pending = 0
1851 AND ug.group_id = g.group_id';
1852 $result = $db->sql_query($sql);
1853 $message_limit = (int) $db->sql_fetchfield('max_message_limit');
1854 $db->sql_freeresult($result);
1856 $user->data['message_limit'] = (!$message_limit) ? phpbb::$config['pm_max_msgs'] : $message_limit;