PageRenderTime 51ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/sources/subs/PersonalMessage.subs.php

https://github.com/Arantor/Elkarte
PHP | 789 lines | 597 code | 91 blank | 101 comment | 112 complexity | b2a64f88ee1b8cdbf24e0b8c2310d25a MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-3.0
  1. <?php
  2. /**
  3. * @name ElkArte Forum
  4. * @copyright ElkArte Forum contributors
  5. * @license BSD http://opensource.org/licenses/BSD-3-Clause
  6. *
  7. * This software is a derived product, based on:
  8. *
  9. * Simple Machines Forum (SMF)
  10. * copyright: 2011 Simple Machines (http://www.simplemachines.org)
  11. * license: BSD, See included LICENSE.TXT for terms and conditions.
  12. *
  13. * @version 1.0 Alpha
  14. *
  15. * This file handles tasks related to personal messages. It performs all
  16. * the necessary (database updates, statistics updates) to add, delete, mark
  17. * etc personal messages.
  18. *
  19. * The functions in this file do NOT check permissions.
  20. *
  21. */
  22. if (!defined('ELKARTE'))
  23. die('No access...');
  24. function loadMessageLimit()
  25. {
  26. global $user_info, $context, $smcFunc;
  27. if ($user_info['is_admin'])
  28. $context['message_limit'] = 0;
  29. elseif (($context['message_limit'] = cache_get_data('msgLimit:' . $user_info['id'], 360)) === null)
  30. {
  31. $request = $smcFunc['db_query']('', '
  32. SELECT MAX(max_messages) AS top_limit, MIN(max_messages) AS bottom_limit
  33. FROM {db_prefix}membergroups
  34. WHERE id_group IN ({array_int:users_groups})',
  35. array(
  36. 'users_groups' => $user_info['groups'],
  37. )
  38. );
  39. list ($maxMessage, $minMessage) = $smcFunc['db_fetch_row']($request);
  40. $smcFunc['db_free_result']($request);
  41. $context['message_limit'] = $minMessage == 0 ? 0 : $maxMessage;
  42. // Save us doing it again!
  43. cache_put_data('msgLimit:' . $user_info['id'], $context['message_limit'], 360);
  44. }
  45. }
  46. function loadPMLabels()
  47. {
  48. global $user_info, $context, $smcFunc;
  49. // Looks like we need to reseek!
  50. $result = $smcFunc['db_query']('', '
  51. SELECT labels, is_read, COUNT(*) AS num
  52. FROM {db_prefix}pm_recipients
  53. WHERE id_member = {int:current_member}
  54. AND deleted = {int:not_deleted}
  55. GROUP BY labels, is_read',
  56. array(
  57. 'current_member' => $user_info['id'],
  58. 'not_deleted' => 0,
  59. )
  60. );
  61. while ($row = $smcFunc['db_fetch_assoc']($result))
  62. {
  63. $this_labels = explode(',', $row['labels']);
  64. foreach ($this_labels as $this_label)
  65. {
  66. $context['labels'][(int) $this_label]['messages'] += $row['num'];
  67. if (!($row['is_read'] & 1))
  68. $context['labels'][(int) $this_label]['unread_messages'] += $row['num'];
  69. }
  70. }
  71. $smcFunc['db_free_result']($result);
  72. // Store it please!
  73. cache_put_data('labelCounts:' . $user_info['id'], $context['labels'], 720);
  74. }
  75. function getPMCount($descending = false, $pmID = null, $labelQuery)
  76. {
  77. global $user_info, $context, $smcFunc;
  78. // Figure out how many messages there are.
  79. if ($context['folder'] == 'sent')
  80. {
  81. $request = $smcFunc['db_query']('', '
  82. SELECT COUNT(' . ($context['display_mode'] == 2 ? 'DISTINCT id_pm_head' : '*') . ')
  83. FROM {db_prefix}personal_messages
  84. WHERE id_member_from = {int:current_member}
  85. AND deleted_by_sender = {int:not_deleted}' . ($pmID !== null ? '
  86. AND id_pm ' . ($descending ? '>' : '<') . ' {int:id_pm}' : ''),
  87. array(
  88. 'current_member' => $user_info['id'],
  89. 'not_deleted' => 0,
  90. 'id_pm' => $pmID,
  91. )
  92. );
  93. }
  94. else
  95. {
  96. $request = $smcFunc['db_query']('', '
  97. SELECT COUNT(' . ($context['display_mode'] == 2 ? 'DISTINCT pm.id_pm_head' : '*') . ')
  98. FROM {db_prefix}pm_recipients AS pmr' . ($context['display_mode'] == 2 ? '
  99. INNER JOIN {db_prefix}personal_messages AS pm ON (pm.id_pm = pmr.id_pm)' : '') . '
  100. WHERE pmr.id_member = {int:current_member}
  101. AND pmr.deleted = {int:not_deleted}' . $labelQuery . ($pmID !== null ? '
  102. AND pmr.id_pm ' . ($descending ? '>' : '<') . ' {int:id_pm}' : ''),
  103. array(
  104. 'current_member' => $user_info['id'],
  105. 'not_deleted' => 0,
  106. 'id_pm' => $pmID,
  107. )
  108. );
  109. }
  110. list ($count) = $smcFunc['db_fetch_row']($request);
  111. $smcFunc['db_free_result']($request);
  112. return $count;
  113. }
  114. /**
  115. * Delete the specified personal messages.
  116. *
  117. * @param array $personal_messages array of pm ids
  118. * @param string $folder = null
  119. * @param int $owner = null
  120. */
  121. function deleteMessages($personal_messages, $folder = null, $owner = null)
  122. {
  123. global $user_info, $smcFunc;
  124. if ($owner === null)
  125. $owner = array($user_info['id']);
  126. elseif (empty($owner))
  127. return;
  128. elseif (!is_array($owner))
  129. $owner = array($owner);
  130. if ($personal_messages !== null)
  131. {
  132. if (empty($personal_messages) || !is_array($personal_messages))
  133. return;
  134. foreach ($personal_messages as $index => $delete_id)
  135. $personal_messages[$index] = (int) $delete_id;
  136. $where = '
  137. AND id_pm IN ({array_int:pm_list})';
  138. }
  139. else
  140. $where = '';
  141. if ($folder == 'sent' || $folder === null)
  142. {
  143. $smcFunc['db_query']('', '
  144. UPDATE {db_prefix}personal_messages
  145. SET deleted_by_sender = {int:is_deleted}
  146. WHERE id_member_from IN ({array_int:member_list})
  147. AND deleted_by_sender = {int:not_deleted}' . $where,
  148. array(
  149. 'member_list' => $owner,
  150. 'is_deleted' => 1,
  151. 'not_deleted' => 0,
  152. 'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(),
  153. )
  154. );
  155. }
  156. if ($folder != 'sent' || $folder === null)
  157. {
  158. // Calculate the number of messages each member's gonna lose...
  159. $request = $smcFunc['db_query']('', '
  160. SELECT id_member, COUNT(*) AS num_deleted_messages, CASE WHEN is_read & 1 >= 1 THEN 1 ELSE 0 END AS is_read
  161. FROM {db_prefix}pm_recipients
  162. WHERE id_member IN ({array_int:member_list})
  163. AND deleted = {int:not_deleted}' . $where . '
  164. GROUP BY id_member, is_read',
  165. array(
  166. 'member_list' => $owner,
  167. 'not_deleted' => 0,
  168. 'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(),
  169. )
  170. );
  171. // ...And update the statistics accordingly - now including unread messages!.
  172. while ($row = $smcFunc['db_fetch_assoc']($request))
  173. {
  174. if ($row['is_read'])
  175. updateMemberData($row['id_member'], array('instant_messages' => $where == '' ? 0 : 'instant_messages - ' . $row['num_deleted_messages']));
  176. else
  177. 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']));
  178. // If this is the current member we need to make their message count correct.
  179. if ($user_info['id'] == $row['id_member'])
  180. {
  181. $user_info['messages'] -= $row['num_deleted_messages'];
  182. if (!($row['is_read']))
  183. $user_info['unread_messages'] -= $row['num_deleted_messages'];
  184. }
  185. }
  186. $smcFunc['db_free_result']($request);
  187. // Do the actual deletion.
  188. $smcFunc['db_query']('', '
  189. UPDATE {db_prefix}pm_recipients
  190. SET deleted = {int:is_deleted}
  191. WHERE id_member IN ({array_int:member_list})
  192. AND deleted = {int:not_deleted}' . $where,
  193. array(
  194. 'member_list' => $owner,
  195. 'is_deleted' => 1,
  196. 'not_deleted' => 0,
  197. 'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(),
  198. )
  199. );
  200. }
  201. // If sender and recipients all have deleted their message, it can be removed.
  202. $request = $smcFunc['db_query']('', '
  203. SELECT pm.id_pm AS sender, pmr.id_pm
  204. FROM {db_prefix}personal_messages AS pm
  205. LEFT JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = pm.id_pm AND pmr.deleted = {int:not_deleted})
  206. WHERE pm.deleted_by_sender = {int:is_deleted}
  207. ' . str_replace('id_pm', 'pm.id_pm', $where) . '
  208. GROUP BY sender, pmr.id_pm
  209. HAVING pmr.id_pm IS null',
  210. array(
  211. 'not_deleted' => 0,
  212. 'is_deleted' => 1,
  213. 'pm_list' => $personal_messages !== null ? array_unique($personal_messages) : array(),
  214. )
  215. );
  216. $remove_pms = array();
  217. while ($row = $smcFunc['db_fetch_assoc']($request))
  218. $remove_pms[] = $row['sender'];
  219. $smcFunc['db_free_result']($request);
  220. if (!empty($remove_pms))
  221. {
  222. $smcFunc['db_query']('', '
  223. DELETE FROM {db_prefix}personal_messages
  224. WHERE id_pm IN ({array_int:pm_list})',
  225. array(
  226. 'pm_list' => $remove_pms,
  227. )
  228. );
  229. $smcFunc['db_query']('', '
  230. DELETE FROM {db_prefix}pm_recipients
  231. WHERE id_pm IN ({array_int:pm_list})',
  232. array(
  233. 'pm_list' => $remove_pms,
  234. )
  235. );
  236. }
  237. // Any cached numbers may be wrong now.
  238. cache_put_data('labelCounts:' . $user_info['id'], null, 720);
  239. }
  240. /**
  241. * Mark the specified personal messages read.
  242. *
  243. * @param array $personal_messages = null, array of pm ids
  244. * @param string $label = null, if label is set, only marks messages with that label
  245. * @param int $owner = null, if owner is set, marks messages owned by that member id
  246. */
  247. function markMessages($personal_messages = null, $label = null, $owner = null)
  248. {
  249. global $user_info, $context, $smcFunc;
  250. if ($owner === null)
  251. $owner = $user_info['id'];
  252. $smcFunc['db_query']('', '
  253. UPDATE {db_prefix}pm_recipients
  254. SET is_read = is_read | 1
  255. WHERE id_member = {int:id_member}
  256. AND NOT (is_read & 1 >= 1)' . ($label === null ? '' : '
  257. AND FIND_IN_SET({string:label}, labels) != 0') . ($personal_messages !== null ? '
  258. AND id_pm IN ({array_int:personal_messages})' : ''),
  259. array(
  260. 'personal_messages' => $personal_messages,
  261. 'id_member' => $owner,
  262. 'label' => $label,
  263. )
  264. );
  265. // If something wasn't marked as read, get the number of unread messages remaining.
  266. if ($smcFunc['db_affected_rows']() > 0)
  267. {
  268. if ($owner == $user_info['id'])
  269. {
  270. foreach ($context['labels'] as $label)
  271. $context['labels'][(int) $label['id']]['unread_messages'] = 0;
  272. }
  273. $result = $smcFunc['db_query']('', '
  274. SELECT labels, COUNT(*) AS num
  275. FROM {db_prefix}pm_recipients
  276. WHERE id_member = {int:id_member}
  277. AND NOT (is_read & 1 >= 1)
  278. AND deleted = {int:is_not_deleted}
  279. GROUP BY labels',
  280. array(
  281. 'id_member' => $owner,
  282. 'is_not_deleted' => 0,
  283. )
  284. );
  285. $total_unread = 0;
  286. while ($row = $smcFunc['db_fetch_assoc']($result))
  287. {
  288. $total_unread += $row['num'];
  289. if ($owner != $user_info['id'])
  290. continue;
  291. $this_labels = explode(',', $row['labels']);
  292. foreach ($this_labels as $this_label)
  293. $context['labels'][(int) $this_label]['unread_messages'] += $row['num'];
  294. }
  295. $smcFunc['db_free_result']($result);
  296. // Need to store all this.
  297. cache_put_data('labelCounts:' . $owner, $context['labels'], 720);
  298. updateMemberData($owner, array('unread_messages' => $total_unread));
  299. // If it was for the current member, reflect this in the $user_info array too.
  300. if ($owner == $user_info['id'])
  301. $user_info['unread_messages'] = $total_unread;
  302. }
  303. }
  304. /**
  305. * Check if the PM is available to the current user.
  306. *
  307. * @param int $pmID
  308. * @param $validFor
  309. * @return boolean
  310. */
  311. function isAccessiblePM($pmID, $validFor = 'in_or_outbox')
  312. {
  313. global $user_info, $smcFunc;
  314. $request = $smcFunc['db_query']('', '
  315. SELECT
  316. pm.id_member_from = {int:id_current_member} AND pm.deleted_by_sender = {int:not_deleted} AS valid_for_outbox,
  317. pmr.id_pm IS NOT NULL AS valid_for_inbox
  318. FROM {db_prefix}personal_messages AS pm
  319. 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})
  320. WHERE pm.id_pm = {int:id_pm}
  321. AND ((pm.id_member_from = {int:id_current_member} AND pm.deleted_by_sender = {int:not_deleted}) OR pmr.id_pm IS NOT NULL)',
  322. array(
  323. 'id_pm' => $pmID,
  324. 'id_current_member' => $user_info['id'],
  325. 'not_deleted' => 0,
  326. )
  327. );
  328. if ($smcFunc['db_num_rows']($request) === 0)
  329. {
  330. $smcFunc['db_free_result']($request);
  331. return false;
  332. }
  333. $validationResult = $smcFunc['db_fetch_assoc']($request);
  334. $smcFunc['db_free_result']($request);
  335. switch ($validFor)
  336. {
  337. case 'inbox':
  338. return !empty($validationResult['valid_for_inbox']);
  339. break;
  340. case 'outbox':
  341. return !empty($validationResult['valid_for_outbox']);
  342. break;
  343. case 'in_or_outbox':
  344. return !empty($validationResult['valid_for_inbox']) || !empty($validationResult['valid_for_outbox']);
  345. break;
  346. default:
  347. trigger_error('Undefined validation type given', E_USER_ERROR);
  348. break;
  349. }
  350. }
  351. /**
  352. * Sends a personal message from the specified person to the specified people
  353. * ($from defaults to the user)
  354. *
  355. * @param array $recipients - an array containing the arrays 'to' and 'bcc', both containing id_member's.
  356. * @param string $subject - should have no slashes and no html entities
  357. * @param string $message - should have no slashes and no html entities
  358. * @param bool $store_outbox
  359. * @param array $from - an array with the id, name, and username of the member.
  360. * @param int $pm_head - the ID of the chain being replied to - if any.
  361. * @return array, an array with log entries telling how many recipients were successful and which recipients it failed to send to.
  362. */
  363. function sendpm($recipients, $subject, $message, $store_outbox = false, $from = null, $pm_head = 0)
  364. {
  365. global $scripturl, $txt, $user_info, $language;
  366. global $modSettings, $smcFunc;
  367. // Make sure the PM language file is loaded, we might need something out of it.
  368. loadLanguage('PersonalMessage');
  369. // Needed for our email and post functions
  370. require_once(SUBSDIR . '/Mail.subs.php');
  371. require_once(SUBSDIR . '/Post.subs.php');
  372. $onBehalf = $from !== null;
  373. // Initialize log array.
  374. $log = array(
  375. 'failed' => array(),
  376. 'sent' => array()
  377. );
  378. if ($from === null)
  379. $from = array(
  380. 'id' => $user_info['id'],
  381. 'name' => $user_info['name'],
  382. 'username' => $user_info['username']
  383. );
  384. // Probably not needed. /me something should be of the typer.
  385. else
  386. $user_info['name'] = $from['name'];
  387. // This is the one that will go in their inbox.
  388. $htmlmessage = $smcFunc['htmlspecialchars']($message, ENT_QUOTES);
  389. preparsecode($htmlmessage);
  390. $htmlsubject = strtr($smcFunc['htmlspecialchars']($subject), array("\r" => '', "\n" => '', "\t" => ''));
  391. if ($smcFunc['strlen']($htmlsubject) > 100)
  392. $htmlsubject = $smcFunc['substr']($htmlsubject, 0, 100);
  393. // Make sure is an array
  394. if (!is_array($recipients))
  395. $recipients = array($recipients);
  396. // Integrated PMs
  397. call_integration_hook('integrate_personal_message', array(&$recipients, &$from, &$subject, &$message));
  398. // Get a list of usernames and convert them to IDs.
  399. $usernames = array();
  400. foreach ($recipients as $rec_type => $rec)
  401. {
  402. foreach ($rec as $id => $member)
  403. {
  404. if (!is_numeric($recipients[$rec_type][$id]))
  405. {
  406. $recipients[$rec_type][$id] = $smcFunc['strtolower'](trim(preg_replace('/[<>&"\'=\\\]/', '', $recipients[$rec_type][$id])));
  407. $usernames[$recipients[$rec_type][$id]] = 0;
  408. }
  409. }
  410. }
  411. if (!empty($usernames))
  412. {
  413. $request = $smcFunc['db_query']('pm_find_username', '
  414. SELECT id_member, member_name
  415. FROM {db_prefix}members
  416. WHERE ' . ($smcFunc['db_case_sensitive'] ? 'LOWER(member_name)' : 'member_name') . ' IN ({array_string:usernames})',
  417. array(
  418. 'usernames' => array_keys($usernames),
  419. )
  420. );
  421. while ($row = $smcFunc['db_fetch_assoc']($request))
  422. if (isset($usernames[$smcFunc['strtolower']($row['member_name'])]))
  423. $usernames[$smcFunc['strtolower']($row['member_name'])] = $row['id_member'];
  424. $smcFunc['db_free_result']($request);
  425. // Replace the usernames with IDs. Drop usernames that couldn't be found.
  426. foreach ($recipients as $rec_type => $rec)
  427. foreach ($rec as $id => $member)
  428. {
  429. if (is_numeric($recipients[$rec_type][$id]))
  430. continue;
  431. if (!empty($usernames[$member]))
  432. $recipients[$rec_type][$id] = $usernames[$member];
  433. else
  434. {
  435. $log['failed'][$id] = sprintf($txt['pm_error_user_not_found'], $recipients[$rec_type][$id]);
  436. unset($recipients[$rec_type][$id]);
  437. }
  438. }
  439. }
  440. // Make sure there are no duplicate 'to' members.
  441. $recipients['to'] = array_unique($recipients['to']);
  442. // Only 'bcc' members that aren't already in 'to'.
  443. $recipients['bcc'] = array_diff(array_unique($recipients['bcc']), $recipients['to']);
  444. // Combine 'to' and 'bcc' recipients.
  445. $all_to = array_merge($recipients['to'], $recipients['bcc']);
  446. // Check no-one will want it deleted right away!
  447. $request = $smcFunc['db_query']('', '
  448. SELECT
  449. id_member, criteria, is_or
  450. FROM {db_prefix}pm_rules
  451. WHERE id_member IN ({array_int:to_members})
  452. AND delete_pm = {int:delete_pm}',
  453. array(
  454. 'to_members' => $all_to,
  455. 'delete_pm' => 1,
  456. )
  457. );
  458. $deletes = array();
  459. // Check whether we have to apply anything...
  460. while ($row = $smcFunc['db_fetch_assoc']($request))
  461. {
  462. $criteria = unserialize($row['criteria']);
  463. // Note we don't check the buddy status, cause deletion from buddy = madness!
  464. $delete = false;
  465. foreach ($criteria as $criterium)
  466. {
  467. $match = false;
  468. if (($criterium['t'] == 'mid' && $criterium['v'] == $from['id']) || ($criterium['t'] == 'gid' && in_array($criterium['v'], $user_info['groups'])) || ($criterium['t'] == 'sub' && strpos($subject, $criterium['v']) !== false) || ($criterium['t'] == 'msg' && strpos($message, $criterium['v']) !== false))
  469. $delete = true;
  470. // If we're adding and one criteria don't match then we stop!
  471. elseif (!$row['is_or'])
  472. {
  473. $delete = false;
  474. break;
  475. }
  476. }
  477. if ($delete)
  478. $deletes[$row['id_member']] = 1;
  479. }
  480. $smcFunc['db_free_result']($request);
  481. // Load the membergrounp message limits.
  482. static $message_limit_cache = array();
  483. if (!allowedTo('moderate_forum') && empty($message_limit_cache))
  484. {
  485. $request = $smcFunc['db_query']('', '
  486. SELECT id_group, max_messages
  487. FROM {db_prefix}membergroups',
  488. array(
  489. )
  490. );
  491. while ($row = $smcFunc['db_fetch_assoc']($request))
  492. $message_limit_cache[$row['id_group']] = $row['max_messages'];
  493. $smcFunc['db_free_result']($request);
  494. }
  495. // Load the groups that are allowed to read PMs.
  496. // @todo move into a separate function on $permission.
  497. $allowed_groups = array();
  498. $disallowed_groups = array();
  499. $request = $smcFunc['db_query']('', '
  500. SELECT id_group, add_deny
  501. FROM {db_prefix}permissions
  502. WHERE permission = {string:read_permission}',
  503. array(
  504. 'read_permission' => 'pm_read',
  505. )
  506. );
  507. while ($row = $smcFunc['db_fetch_assoc']($request))
  508. {
  509. if (empty($row['add_deny']))
  510. $disallowed_groups[] = $row['id_group'];
  511. else
  512. $allowed_groups[] = $row['id_group'];
  513. }
  514. $smcFunc['db_free_result']($request);
  515. if (empty($modSettings['permission_enable_deny']))
  516. $disallowed_groups = array();
  517. $request = $smcFunc['db_query']('', '
  518. SELECT
  519. member_name, real_name, id_member, email_address, lngfile,
  520. pm_email_notify, instant_messages,' . (allowedTo('moderate_forum') ? ' 0' : '
  521. (pm_receive_from = {int:admins_only}' . (empty($modSettings['enable_buddylist']) ? '' : ' OR
  522. (pm_receive_from = {int:buddies_only} AND FIND_IN_SET({string:from_id}, buddy_list) = 0) OR
  523. (pm_receive_from = {int:not_on_ignore_list} AND FIND_IN_SET({string:from_id}, pm_ignore_list) != 0)') . ')') . ' AS ignored,
  524. FIND_IN_SET({string:from_id}, buddy_list) != 0 AS is_buddy, is_activated,
  525. additional_groups, id_group, id_post_group
  526. FROM {db_prefix}members
  527. WHERE id_member IN ({array_int:recipients})
  528. ORDER BY lngfile
  529. LIMIT {int:count_recipients}',
  530. array(
  531. 'not_on_ignore_list' => 1,
  532. 'buddies_only' => 2,
  533. 'admins_only' => 3,
  534. 'recipients' => $all_to,
  535. 'count_recipients' => count($all_to),
  536. 'from_id' => $from['id'],
  537. )
  538. );
  539. $notifications = array();
  540. while ($row = $smcFunc['db_fetch_assoc']($request))
  541. {
  542. // Don't do anything for members to be deleted!
  543. if (isset($deletes[$row['id_member']]))
  544. continue;
  545. // We need to know this members groups.
  546. $groups = explode(',', $row['additional_groups']);
  547. $groups[] = $row['id_group'];
  548. $groups[] = $row['id_post_group'];
  549. $message_limit = -1;
  550. // For each group see whether they've gone over their limit - assuming they're not an admin.
  551. if (!in_array(1, $groups))
  552. {
  553. foreach ($groups as $id)
  554. {
  555. if (isset($message_limit_cache[$id]) && $message_limit != 0 && $message_limit < $message_limit_cache[$id])
  556. $message_limit = $message_limit_cache[$id];
  557. }
  558. if ($message_limit > 0 && $message_limit <= $row['instant_messages'])
  559. {
  560. $log['failed'][$row['id_member']] = sprintf($txt['pm_error_data_limit_reached'], $row['real_name']);
  561. unset($all_to[array_search($row['id_member'], $all_to)]);
  562. continue;
  563. }
  564. // Do they have any of the allowed groups?
  565. if (count(array_intersect($allowed_groups, $groups)) == 0 || count(array_intersect($disallowed_groups, $groups)) != 0)
  566. {
  567. $log['failed'][$row['id_member']] = sprintf($txt['pm_error_user_cannot_read'], $row['real_name']);
  568. unset($all_to[array_search($row['id_member'], $all_to)]);
  569. continue;
  570. }
  571. }
  572. // Note that PostgreSQL can return a lowercase t/f for FIND_IN_SET
  573. if (!empty($row['ignored']) && $row['ignored'] != 'f' && $row['id_member'] != $from['id'])
  574. {
  575. $log['failed'][$row['id_member']] = sprintf($txt['pm_error_ignored_by_user'], $row['real_name']);
  576. unset($all_to[array_search($row['id_member'], $all_to)]);
  577. continue;
  578. }
  579. // If the receiving account is banned (>=10) or pending deletion (4), refuse to send the PM.
  580. if ($row['is_activated'] >= 10 || ($row['is_activated'] == 4 && !$user_info['is_admin']))
  581. {
  582. $log['failed'][$row['id_member']] = sprintf($txt['pm_error_user_cannot_read'], $row['real_name']);
  583. unset($all_to[array_search($row['id_member'], $all_to)]);
  584. continue;
  585. }
  586. // Send a notification, if enabled - taking the buddy list into account.
  587. if (!empty($row['email_address']) && ($row['pm_email_notify'] == 1 || ($row['pm_email_notify'] > 1 && (!empty($modSettings['enable_buddylist']) && $row['is_buddy']))) && $row['is_activated'] == 1)
  588. $notifications[empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']][] = $row['email_address'];
  589. $log['sent'][$row['id_member']] = sprintf(isset($txt['pm_successfully_sent']) ? $txt['pm_successfully_sent'] : '', $row['real_name']);
  590. }
  591. $smcFunc['db_free_result']($request);
  592. // Only 'send' the message if there are any recipients left.
  593. if (empty($all_to))
  594. return $log;
  595. // Insert the message itself and then grab the last insert id.
  596. $smcFunc['db_insert']('',
  597. '{db_prefix}personal_messages',
  598. array(
  599. 'id_pm_head' => 'int', 'id_member_from' => 'int', 'deleted_by_sender' => 'int',
  600. 'from_name' => 'string-255', 'msgtime' => 'int', 'subject' => 'string-255', 'body' => 'string-65534',
  601. ),
  602. array(
  603. $pm_head, $from['id'], ($store_outbox ? 0 : 1),
  604. $from['username'], time(), $htmlsubject, $htmlmessage,
  605. ),
  606. array('id_pm')
  607. );
  608. $id_pm = $smcFunc['db_insert_id']('{db_prefix}personal_messages', 'id_pm');
  609. // Add the recipients.
  610. if (!empty($id_pm))
  611. {
  612. // If this is new we need to set it part of it's own conversation.
  613. if (empty($pm_head))
  614. $smcFunc['db_query']('', '
  615. UPDATE {db_prefix}personal_messages
  616. SET id_pm_head = {int:id_pm_head}
  617. WHERE id_pm = {int:id_pm_head}',
  618. array(
  619. 'id_pm_head' => $id_pm,
  620. )
  621. );
  622. // Some people think manually deleting personal_messages is fun... it's not. We protect against it though :)
  623. $smcFunc['db_query']('', '
  624. DELETE FROM {db_prefix}pm_recipients
  625. WHERE id_pm = {int:id_pm}',
  626. array(
  627. 'id_pm' => $id_pm,
  628. )
  629. );
  630. $insertRows = array();
  631. $to_list = array();
  632. foreach ($all_to as $to)
  633. {
  634. $insertRows[] = array($id_pm, $to, in_array($to, $recipients['bcc']) ? 1 : 0, isset($deletes[$to]) ? 1 : 0, 1);
  635. if (!in_array($to, $recipients['bcc']))
  636. $to_list[] = $to;
  637. }
  638. $smcFunc['db_insert']('insert',
  639. '{db_prefix}pm_recipients',
  640. array(
  641. 'id_pm' => 'int', 'id_member' => 'int', 'bcc' => 'int', 'deleted' => 'int', 'is_new' => 'int'
  642. ),
  643. $insertRows,
  644. array('id_pm', 'id_member')
  645. );
  646. }
  647. censorText($subject);
  648. if (empty($modSettings['disallow_sendBody']))
  649. {
  650. censorText($message);
  651. $message = trim(un_htmlspecialchars(strip_tags(strtr(parse_bbc(htmlspecialchars($message), false), array('<br />' => "\n", '</div>' => "\n", '</li>' => "\n", '&#91;' => '[', '&#93;' => ']')))));
  652. }
  653. else
  654. $message = '';
  655. $to_names = array();
  656. if (count($to_list) > 1)
  657. {
  658. $request = $smcFunc['db_query']('', '
  659. SELECT real_name
  660. FROM {db_prefix}members
  661. WHERE id_member IN ({array_int:to_members})',
  662. array(
  663. 'to_members' => $to_list,
  664. )
  665. );
  666. while ($row = $smcFunc['db_fetch_assoc']($request))
  667. $to_names[] = un_htmlspecialchars($row['real_name']);
  668. $smcFunc['db_free_result']($request);
  669. }
  670. $replacements = array(
  671. 'SUBJECT' => $subject,
  672. 'MESSAGE' => $message,
  673. 'SENDER' => un_htmlspecialchars($from['name']),
  674. 'READLINK' => $scripturl . '?action=pm;pmsg=' . $id_pm . '#msg' . $id_pm,
  675. 'REPLYLINK' => $scripturl . '?action=pm;sa=send;f=inbox;pmsg=' . $id_pm . ';quote;u=' . $from['id'],
  676. 'TOLIST' => implode(', ', $to_names),
  677. );
  678. $email_template = 'new_pm' . (empty($modSettings['disallow_sendBody']) ? '_body' : '') . (!empty($to_names) ? '_tolist' : '');
  679. foreach ($notifications as $lang => $notification_list)
  680. {
  681. $mail = loadEmailTemplate($email_template, $replacements, $lang);
  682. // Off the notification email goes!
  683. sendmail($notification_list, $mail['subject'], $mail['body'], null, 'p' . $id_pm, false, 2, null, true);
  684. }
  685. // Integrated After PMs
  686. call_integration_hook('integrate_personal_message_after', array(&$id_pm, &$log, &$recipients, &$from, &$subject, &$message));
  687. // Back to what we were on before!
  688. loadLanguage('index+PersonalMessage');
  689. // Add one to their unread and read message counts.
  690. foreach ($all_to as $k => $id)
  691. {
  692. if (isset($deletes[$id]))
  693. unset($all_to[$k]);
  694. }
  695. if (!empty($all_to))
  696. updateMemberData($all_to, array('instant_messages' => '+', 'unread_messages' => '+', 'new_pm' => 1));
  697. return $log;
  698. }