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

/phpBB/includes/functions_posting.php

http://github.com/phpbb/phpbb
PHP | 2850 lines | 2049 code | 377 blank | 424 comment | 473 complexity | abfc12b8724791b8aad50d3946048472 MD5 | raw file
Possible License(s): GPL-3.0, AGPL-1.0
  1. <?php
  2. /**
  3. *
  4. * This file is part of the phpBB Forum Software package.
  5. *
  6. * @copyright (c) phpBB Limited <https://www.phpbb.com>
  7. * @license GNU General Public License, version 2 (GPL-2.0)
  8. *
  9. * For full copyright and license information, please see
  10. * the docs/CREDITS.txt file.
  11. *
  12. */
  13. /**
  14. * @ignore
  15. */
  16. if (!defined('IN_PHPBB'))
  17. {
  18. exit;
  19. }
  20. /**
  21. * Fill smiley templates (or just the variables) with smilies, either in a window or inline
  22. */
  23. function generate_smilies($mode, $forum_id)
  24. {
  25. global $db, $user, $config, $template, $phpbb_dispatcher, $request;
  26. global $phpEx, $phpbb_root_path, $phpbb_container, $phpbb_path_helper;
  27. /* @var $pagination \phpbb\pagination */
  28. $pagination = $phpbb_container->get('pagination');
  29. $base_url = append_sid("{$phpbb_root_path}posting.$phpEx", 'mode=smilies&amp;f=' . $forum_id);
  30. $start = $request->variable('start', 0);
  31. if ($mode == 'window')
  32. {
  33. if ($forum_id)
  34. {
  35. $sql = 'SELECT forum_style
  36. FROM ' . FORUMS_TABLE . "
  37. WHERE forum_id = $forum_id";
  38. $result = $db->sql_query_limit($sql, 1);
  39. $row = $db->sql_fetchrow($result);
  40. $db->sql_freeresult($result);
  41. $user->setup('posting', (int) $row['forum_style']);
  42. }
  43. else
  44. {
  45. $user->setup('posting');
  46. }
  47. page_header($user->lang['SMILIES']);
  48. $sql_ary = [
  49. 'SELECT' => 'COUNT(s.smiley_id) AS item_count',
  50. 'FROM' => [
  51. SMILIES_TABLE => 's',
  52. ],
  53. 'GROUP_BY' => 's.smiley_url',
  54. ];
  55. /**
  56. * Modify SQL query that fetches the total number of smilies in window mode
  57. *
  58. * @event core.generate_smilies_count_sql_before
  59. * @var int forum_id Forum where smilies are generated
  60. * @var array sql_ary Array with the SQL query
  61. * @var string base_url URL for the "More smilies" link and its pagination
  62. * @since 3.2.9-RC1
  63. * @changed 3.2.10-RC1 Added base_url
  64. */
  65. $vars = [
  66. 'forum_id',
  67. 'sql_ary',
  68. 'base_url',
  69. ];
  70. extract($phpbb_dispatcher->trigger_event('core.generate_smilies_count_sql_before', compact($vars)));
  71. $sql = $db->sql_build_query('SELECT', $sql_ary);
  72. $result = $db->sql_query($sql, 3600);
  73. $smiley_count = 0;
  74. while ($row = $db->sql_fetchrow($result))
  75. {
  76. ++$smiley_count;
  77. }
  78. $db->sql_freeresult($result);
  79. $template->set_filenames(array(
  80. 'body' => 'posting_smilies.html')
  81. );
  82. $start = $pagination->validate_start($start, $config['smilies_per_page'], $smiley_count);
  83. $pagination->generate_template_pagination($base_url, 'pagination', 'start', $smiley_count, $config['smilies_per_page'], $start);
  84. }
  85. $display_link = false;
  86. if ($mode == 'inline')
  87. {
  88. $sql = 'SELECT smiley_id
  89. FROM ' . SMILIES_TABLE . '
  90. WHERE display_on_posting = 0';
  91. $result = $db->sql_query_limit($sql, 1, 0, 3600);
  92. if ($row = $db->sql_fetchrow($result))
  93. {
  94. $display_link = true;
  95. }
  96. $db->sql_freeresult($result);
  97. }
  98. if ($mode == 'window')
  99. {
  100. $sql_ary = [
  101. 'SELECT' => 's.smiley_url, MIN(s.emotion) AS emotion, MIN(s.code) AS code, s.smiley_width, s.smiley_height, MIN(s.smiley_order) AS min_smiley_order',
  102. 'FROM' => [
  103. SMILIES_TABLE => 's',
  104. ],
  105. 'GROUP_BY' => 's.smiley_url, s.smiley_width, s.smiley_height',
  106. 'ORDER_BY' => 's.min_smiley_order',
  107. ];
  108. }
  109. else
  110. {
  111. $sql_ary = [
  112. 'SELECT' => 's.*',
  113. 'FROM' => [
  114. SMILIES_TABLE => 's',
  115. ],
  116. 'WHERE' => 's.display_on_posting = 1',
  117. 'ORDER_BY' => 's.smiley_order',
  118. ];
  119. }
  120. /**
  121. * Modify the SQL query that fetches the smilies
  122. *
  123. * @event core.generate_smilies_modify_sql
  124. * @var string mode Smiley mode, either window or inline
  125. * @var int forum_id Forum where smilies are generated, or 0 if composing a private message
  126. * @var array sql_ary Array with SQL query data
  127. * @since 3.2.10-RC1
  128. * @since 3.3.1-RC1
  129. */
  130. $vars = [
  131. 'mode',
  132. 'forum_id',
  133. 'sql_ary',
  134. ];
  135. extract($phpbb_dispatcher->trigger_event('core.generate_smilies_modify_sql', compact($vars)));
  136. $sql = $db->sql_build_query('SELECT', $sql_ary);
  137. if ($mode == 'window')
  138. {
  139. $result = $db->sql_query_limit($sql, $config['smilies_per_page'], $start, 3600);
  140. }
  141. else
  142. {
  143. $result = $db->sql_query($sql, 3600);
  144. }
  145. $smilies = array();
  146. while ($row = $db->sql_fetchrow($result))
  147. {
  148. if (empty($smilies[$row['smiley_url']]))
  149. {
  150. $smilies[$row['smiley_url']] = $row;
  151. }
  152. }
  153. $db->sql_freeresult($result);
  154. /**
  155. * Modify smilies before they are assigned to the template
  156. *
  157. * @event core.generate_smilies_modify_rowset
  158. * @var string mode Smiley mode, either window or inline
  159. * @var int forum_id Forum where smilies are generated, or 0 if composing a private message
  160. * @var array smilies Smiley rows fetched from the database
  161. * @since 3.2.9-RC1
  162. */
  163. $vars = [
  164. 'mode',
  165. 'forum_id',
  166. 'smilies',
  167. ];
  168. extract($phpbb_dispatcher->trigger_event('core.generate_smilies_modify_rowset', compact($vars)));
  169. if (count($smilies))
  170. {
  171. $root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $phpbb_path_helper->get_web_root_path();
  172. foreach ($smilies as $row)
  173. {
  174. /**
  175. * Modify smiley root path before populating smiley list
  176. *
  177. * @event core.generate_smilies_before
  178. * @var string root_path root_path for smilies
  179. * @since 3.1.11-RC1
  180. */
  181. $vars = array('root_path');
  182. extract($phpbb_dispatcher->trigger_event('core.generate_smilies_before', compact($vars)));
  183. $template->assign_block_vars('smiley', array(
  184. 'SMILEY_CODE' => $row['code'],
  185. 'A_SMILEY_CODE' => addslashes($row['code']),
  186. 'SMILEY_IMG' => $root_path . $config['smilies_path'] . '/' . $row['smiley_url'],
  187. 'SMILEY_WIDTH' => $row['smiley_width'],
  188. 'SMILEY_HEIGHT' => $row['smiley_height'],
  189. 'SMILEY_DESC' => $row['emotion'])
  190. );
  191. }
  192. }
  193. /**
  194. * This event is called after the smilies are populated
  195. *
  196. * @event core.generate_smilies_after
  197. * @var string mode Mode of the smilies: window|inline
  198. * @var int forum_id The forum ID we are currently in
  199. * @var bool display_link Shall we display the "more smilies" link?
  200. * @var string base_url URL for the "More smilies" link and its pagination
  201. * @since 3.1.0-a1
  202. * @changed 3.2.10-RC1 Added base_url
  203. */
  204. $vars = [
  205. 'mode',
  206. 'forum_id',
  207. 'display_link',
  208. 'base_url',
  209. ];
  210. extract($phpbb_dispatcher->trigger_event('core.generate_smilies_after', compact($vars)));
  211. if ($mode == 'inline' && $display_link)
  212. {
  213. $template->assign_vars(array(
  214. 'S_SHOW_SMILEY_LINK' => true,
  215. 'U_MORE_SMILIES' => $base_url,
  216. ));
  217. }
  218. if ($mode == 'window')
  219. {
  220. page_footer();
  221. }
  222. }
  223. /**
  224. * Update last post information
  225. * Should be used instead of sync() if only the last post information are out of sync... faster
  226. *
  227. * @param string $type Can be forum|topic
  228. * @param mixed $ids topic/forum ids
  229. * @param bool $return_update_sql true: SQL query shall be returned, false: execute SQL
  230. */
  231. function update_post_information($type, $ids, $return_update_sql = false)
  232. {
  233. global $db;
  234. if (empty($ids))
  235. {
  236. return;
  237. }
  238. if (!is_array($ids))
  239. {
  240. $ids = array($ids);
  241. }
  242. $update_sql = $empty_forums = $not_empty_forums = array();
  243. if ($type != 'topic')
  244. {
  245. $topic_join = ', ' . TOPICS_TABLE . ' t';
  246. $topic_condition = 'AND t.topic_id = p.topic_id AND t.topic_visibility = ' . ITEM_APPROVED;
  247. }
  248. else
  249. {
  250. $topic_join = '';
  251. $topic_condition = '';
  252. }
  253. if (count($ids) == 1)
  254. {
  255. $sql = 'SELECT p.post_id as last_post_id
  256. FROM ' . POSTS_TABLE . " p $topic_join
  257. WHERE " . $db->sql_in_set('p.' . $type . '_id', $ids) . "
  258. $topic_condition
  259. AND p.post_visibility = " . ITEM_APPROVED . "
  260. ORDER BY p.post_id DESC";
  261. $result = $db->sql_query_limit($sql, 1);
  262. }
  263. else
  264. {
  265. $sql = 'SELECT p.' . $type . '_id, MAX(p.post_id) as last_post_id
  266. FROM ' . POSTS_TABLE . " p $topic_join
  267. WHERE " . $db->sql_in_set('p.' . $type . '_id', $ids) . "
  268. $topic_condition
  269. AND p.post_visibility = " . ITEM_APPROVED . "
  270. GROUP BY p.{$type}_id";
  271. $result = $db->sql_query($sql);
  272. }
  273. $last_post_ids = array();
  274. while ($row = $db->sql_fetchrow($result))
  275. {
  276. if (count($ids) == 1)
  277. {
  278. $row[$type . '_id'] = $ids[0];
  279. }
  280. if ($type == 'forum')
  281. {
  282. $not_empty_forums[] = $row['forum_id'];
  283. if (empty($row['last_post_id']))
  284. {
  285. $empty_forums[] = $row['forum_id'];
  286. }
  287. }
  288. $last_post_ids[] = $row['last_post_id'];
  289. }
  290. $db->sql_freeresult($result);
  291. if ($type == 'forum')
  292. {
  293. $empty_forums = array_merge($empty_forums, array_diff($ids, $not_empty_forums));
  294. foreach ($empty_forums as $void => $forum_id)
  295. {
  296. $update_sql[$forum_id][] = 'forum_last_post_id = 0';
  297. $update_sql[$forum_id][] = "forum_last_post_subject = ''";
  298. $update_sql[$forum_id][] = 'forum_last_post_time = 0';
  299. $update_sql[$forum_id][] = 'forum_last_poster_id = 0';
  300. $update_sql[$forum_id][] = "forum_last_poster_name = ''";
  301. $update_sql[$forum_id][] = "forum_last_poster_colour = ''";
  302. }
  303. }
  304. if (count($last_post_ids))
  305. {
  306. $sql = 'SELECT p.' . $type . '_id, p.post_id, p.post_subject, p.post_time, p.poster_id, p.post_username, u.user_id, u.username, u.user_colour
  307. FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u
  308. WHERE p.poster_id = u.user_id
  309. AND ' . $db->sql_in_set('p.post_id', $last_post_ids);
  310. $result = $db->sql_query($sql);
  311. while ($row = $db->sql_fetchrow($result))
  312. {
  313. $update_sql[$row["{$type}_id"]][] = $type . '_last_post_id = ' . (int) $row['post_id'];
  314. $update_sql[$row["{$type}_id"]][] = "{$type}_last_post_subject = '" . $db->sql_escape($row['post_subject']) . "'";
  315. $update_sql[$row["{$type}_id"]][] = $type . '_last_post_time = ' . (int) $row['post_time'];
  316. $update_sql[$row["{$type}_id"]][] = $type . '_last_poster_id = ' . (int) $row['poster_id'];
  317. $update_sql[$row["{$type}_id"]][] = "{$type}_last_poster_colour = '" . $db->sql_escape($row['user_colour']) . "'";
  318. $update_sql[$row["{$type}_id"]][] = "{$type}_last_poster_name = '" . (($row['poster_id'] == ANONYMOUS) ? $db->sql_escape($row['post_username']) : $db->sql_escape($row['username'])) . "'";
  319. }
  320. $db->sql_freeresult($result);
  321. }
  322. unset($empty_forums, $ids, $last_post_ids);
  323. if ($return_update_sql || !count($update_sql))
  324. {
  325. return $update_sql;
  326. }
  327. $table = ($type == 'forum') ? FORUMS_TABLE : TOPICS_TABLE;
  328. foreach ($update_sql as $update_id => $update_sql_ary)
  329. {
  330. $sql = "UPDATE $table
  331. SET " . implode(', ', $update_sql_ary) . "
  332. WHERE {$type}_id = $update_id";
  333. $db->sql_query($sql);
  334. }
  335. return;
  336. }
  337. /**
  338. * Generate Topic Icons for display
  339. */
  340. function posting_gen_topic_icons($mode, $icon_id)
  341. {
  342. global $phpbb_root_path, $config, $template, $cache;
  343. // Grab icons
  344. $icons = $cache->obtain_icons();
  345. if (!$icon_id)
  346. {
  347. $template->assign_var('S_NO_ICON_CHECKED', ' checked="checked"');
  348. }
  349. if (count($icons))
  350. {
  351. $root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $phpbb_root_path;
  352. foreach ($icons as $id => $data)
  353. {
  354. if ($data['display'])
  355. {
  356. $template->assign_block_vars('topic_icon', array(
  357. 'ICON_ID' => $id,
  358. 'ICON_IMG' => $root_path . $config['icons_path'] . '/' . $data['img'],
  359. 'ICON_WIDTH' => $data['width'],
  360. 'ICON_HEIGHT' => $data['height'],
  361. 'ICON_ALT' => $data['alt'],
  362. 'S_CHECKED' => ($id == $icon_id) ? true : false,
  363. 'S_ICON_CHECKED' => ($id == $icon_id) ? ' checked="checked"' : '')
  364. );
  365. }
  366. }
  367. return true;
  368. }
  369. return false;
  370. }
  371. /**
  372. * Build topic types able to be selected
  373. */
  374. function posting_gen_topic_types($forum_id, $cur_topic_type = POST_NORMAL)
  375. {
  376. global $auth, $user, $template;
  377. $toggle = false;
  378. $topic_types = array(
  379. 'sticky' => array('const' => POST_STICKY, 'lang' => 'POST_STICKY'),
  380. 'announce' => array('const' => POST_ANNOUNCE, 'lang' => 'POST_ANNOUNCEMENT'),
  381. 'announce_global' => array('const' => POST_GLOBAL, 'lang' => 'POST_GLOBAL')
  382. );
  383. $topic_type_array = array();
  384. foreach ($topic_types as $auth_key => $topic_value)
  385. {
  386. if ($auth->acl_get('f_' . $auth_key, $forum_id))
  387. {
  388. $toggle = true;
  389. $topic_type_array[] = array(
  390. 'VALUE' => $topic_value['const'],
  391. 'S_CHECKED' => ($cur_topic_type == $topic_value['const']) ? ' checked="checked"' : '',
  392. 'L_TOPIC_TYPE' => $user->lang[$topic_value['lang']]
  393. );
  394. }
  395. }
  396. if ($toggle)
  397. {
  398. $topic_type_array = array_merge(array(0 => array(
  399. 'VALUE' => POST_NORMAL,
  400. 'S_CHECKED' => ($cur_topic_type == POST_NORMAL) ? ' checked="checked"' : '',
  401. 'L_TOPIC_TYPE' => $user->lang['POST_NORMAL'])),
  402. $topic_type_array
  403. );
  404. foreach ($topic_type_array as $array)
  405. {
  406. $template->assign_block_vars('topic_type', $array);
  407. }
  408. $template->assign_vars(array(
  409. 'S_TOPIC_TYPE_STICKY' => ($auth->acl_get('f_sticky', $forum_id)),
  410. 'S_TOPIC_TYPE_ANNOUNCE' => ($auth->acl_gets('f_announce', 'f_announce_global', $forum_id)),
  411. ));
  412. }
  413. return $toggle;
  414. }
  415. //
  416. // Attachment related functions
  417. //
  418. /**
  419. * Calculate the needed size for Thumbnail
  420. */
  421. function get_img_size_format($width, $height)
  422. {
  423. global $config;
  424. // Maximum Width the Image can take
  425. $max_width = ($config['img_max_thumb_width']) ? $config['img_max_thumb_width'] : 400;
  426. if ($width > $height)
  427. {
  428. return array(
  429. round($width * ($max_width / $width)),
  430. round($height * ($max_width / $width))
  431. );
  432. }
  433. else
  434. {
  435. return array(
  436. round($width * ($max_width / $height)),
  437. round($height * ($max_width / $height))
  438. );
  439. }
  440. }
  441. /**
  442. * Return supported image types
  443. */
  444. function get_supported_image_types($type = false)
  445. {
  446. if (@extension_loaded('gd'))
  447. {
  448. $format = imagetypes();
  449. $new_type = 0;
  450. if ($type !== false)
  451. {
  452. // Type is one of the IMAGETYPE constants - it is fetched from getimagesize()
  453. switch ($type)
  454. {
  455. // GIF
  456. case IMAGETYPE_GIF:
  457. $new_type = ($format & IMG_GIF) ? IMG_GIF : false;
  458. break;
  459. // JPG, JPC, JP2
  460. case IMAGETYPE_JPEG:
  461. case IMAGETYPE_JPC:
  462. case IMAGETYPE_JPEG2000:
  463. case IMAGETYPE_JP2:
  464. case IMAGETYPE_JPX:
  465. case IMAGETYPE_JB2:
  466. $new_type = ($format & IMG_JPG) ? IMG_JPG : false;
  467. break;
  468. // PNG
  469. case IMAGETYPE_PNG:
  470. $new_type = ($format & IMG_PNG) ? IMG_PNG : false;
  471. break;
  472. // WBMP
  473. case IMAGETYPE_WBMP:
  474. $new_type = ($format & IMG_WBMP) ? IMG_WBMP : false;
  475. break;
  476. }
  477. }
  478. else
  479. {
  480. $new_type = array();
  481. $go_through_types = array(IMG_GIF, IMG_JPG, IMG_PNG, IMG_WBMP);
  482. foreach ($go_through_types as $check_type)
  483. {
  484. if ($format & $check_type)
  485. {
  486. $new_type[] = $check_type;
  487. }
  488. }
  489. }
  490. return array(
  491. 'gd' => ($new_type) ? true : false,
  492. 'format' => $new_type,
  493. 'version' => (function_exists('imagecreatetruecolor')) ? 2 : 1
  494. );
  495. }
  496. return array('gd' => false);
  497. }
  498. /**
  499. * Create Thumbnail
  500. */
  501. function create_thumbnail($source, $destination, $mimetype)
  502. {
  503. global $config, $phpbb_filesystem, $phpbb_dispatcher;
  504. $min_filesize = (int) $config['img_min_thumb_filesize'];
  505. $img_filesize = (file_exists($source)) ? @filesize($source) : false;
  506. if (!$img_filesize || $img_filesize <= $min_filesize)
  507. {
  508. return false;
  509. }
  510. $dimension = @getimagesize($source);
  511. if ($dimension === false)
  512. {
  513. return false;
  514. }
  515. list($width, $height, $type, ) = $dimension;
  516. if (empty($width) || empty($height))
  517. {
  518. return false;
  519. }
  520. list($new_width, $new_height) = get_img_size_format($width, $height);
  521. // Do not create a thumbnail if the resulting width/height is bigger than the original one
  522. if ($new_width >= $width && $new_height >= $height)
  523. {
  524. return false;
  525. }
  526. $thumbnail_created = false;
  527. /**
  528. * Create thumbnail event to replace GD thumbnail creation with for example ImageMagick
  529. *
  530. * @event core.thumbnail_create_before
  531. * @var string source Image source path
  532. * @var string destination Thumbnail destination path
  533. * @var string mimetype Image mime type
  534. * @var float new_width Calculated thumbnail width
  535. * @var float new_height Calculated thumbnail height
  536. * @var bool thumbnail_created Set to true to skip default GD thumbnail creation
  537. * @since 3.2.4
  538. */
  539. $vars = array(
  540. 'source',
  541. 'destination',
  542. 'mimetype',
  543. 'new_width',
  544. 'new_height',
  545. 'thumbnail_created',
  546. );
  547. extract($phpbb_dispatcher->trigger_event('core.thumbnail_create_before', compact($vars)));
  548. if (!$thumbnail_created)
  549. {
  550. $type = get_supported_image_types($type);
  551. if ($type['gd'])
  552. {
  553. // If the type is not supported, we are not able to create a thumbnail
  554. if ($type['format'] === false)
  555. {
  556. return false;
  557. }
  558. switch ($type['format'])
  559. {
  560. case IMG_GIF:
  561. $image = @imagecreatefromgif($source);
  562. break;
  563. case IMG_JPG:
  564. @ini_set('gd.jpeg_ignore_warning', 1);
  565. $image = @imagecreatefromjpeg($source);
  566. break;
  567. case IMG_PNG:
  568. $image = @imagecreatefrompng($source);
  569. break;
  570. case IMG_WBMP:
  571. $image = @imagecreatefromwbmp($source);
  572. break;
  573. }
  574. if (empty($image))
  575. {
  576. return false;
  577. }
  578. if ($type['version'] == 1)
  579. {
  580. $new_image = imagecreate($new_width, $new_height);
  581. if ($new_image === false)
  582. {
  583. return false;
  584. }
  585. imagecopyresized($new_image, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
  586. }
  587. else
  588. {
  589. $new_image = imagecreatetruecolor($new_width, $new_height);
  590. if ($new_image === false)
  591. {
  592. return false;
  593. }
  594. // Preserve alpha transparency (png for example)
  595. @imagealphablending($new_image, false);
  596. @imagesavealpha($new_image, true);
  597. imagecopyresampled($new_image, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
  598. }
  599. switch ($type['format'])
  600. {
  601. case IMG_GIF:
  602. imagegif($new_image, $destination);
  603. break;
  604. case IMG_JPG:
  605. imagejpeg($new_image, $destination, 90);
  606. break;
  607. case IMG_PNG:
  608. imagepng($new_image, $destination);
  609. break;
  610. case IMG_WBMP:
  611. imagewbmp($new_image, $destination);
  612. break;
  613. }
  614. imagedestroy($new_image);
  615. }
  616. else
  617. {
  618. return false;
  619. }
  620. }
  621. if (!file_exists($destination))
  622. {
  623. return false;
  624. }
  625. try
  626. {
  627. $phpbb_filesystem->phpbb_chmod($destination, \phpbb\filesystem\filesystem_interface::CHMOD_READ | \phpbb\filesystem\filesystem_interface::CHMOD_WRITE);
  628. }
  629. catch (\phpbb\filesystem\exception\filesystem_exception $e)
  630. {
  631. // Do nothing
  632. }
  633. return true;
  634. }
  635. /**
  636. * Assign Inline attachments (build option fields)
  637. */
  638. function posting_gen_inline_attachments(&$attachment_data)
  639. {
  640. global $template;
  641. if (count($attachment_data))
  642. {
  643. $s_inline_attachment_options = '';
  644. foreach ($attachment_data as $i => $attachment)
  645. {
  646. $s_inline_attachment_options .= '<option value="' . $i . '">' . utf8_basename($attachment['real_filename']) . '</option>';
  647. }
  648. $template->assign_var('S_INLINE_ATTACHMENT_OPTIONS', $s_inline_attachment_options);
  649. return true;
  650. }
  651. return false;
  652. }
  653. /**
  654. * Generate inline attachment entry
  655. */
  656. function posting_gen_attachment_entry($attachment_data, &$filename_data, $show_attach_box = true)
  657. {
  658. global $template, $config, $phpbb_root_path, $phpEx, $user, $phpbb_dispatcher;
  659. // Some default template variables
  660. $template->assign_vars(array(
  661. 'S_SHOW_ATTACH_BOX' => $show_attach_box,
  662. 'S_HAS_ATTACHMENTS' => count($attachment_data),
  663. 'FILESIZE' => $config['max_filesize'],
  664. 'FILE_COMMENT' => (isset($filename_data['filecomment'])) ? $filename_data['filecomment'] : '',
  665. ));
  666. if (count($attachment_data))
  667. {
  668. // We display the posted attachments within the desired order.
  669. ($config['display_order']) ? krsort($attachment_data) : ksort($attachment_data);
  670. $attachrow_template_vars = [];
  671. foreach ($attachment_data as $count => $attach_row)
  672. {
  673. $hidden = '';
  674. $attach_row['real_filename'] = utf8_basename($attach_row['real_filename']);
  675. foreach ($attach_row as $key => $value)
  676. {
  677. $hidden .= '<input type="hidden" name="attachment_data[' . $count . '][' . $key . ']" value="' . $value . '" />';
  678. }
  679. $download_link = append_sid("{$phpbb_root_path}download/file.$phpEx", 'mode=view&amp;id=' . (int) $attach_row['attach_id'], true, ($attach_row['is_orphan']) ? $user->session_id : false);
  680. $attachrow_template_vars[(int) $attach_row['attach_id']] = array(
  681. 'FILENAME' => utf8_basename($attach_row['real_filename']),
  682. 'A_FILENAME' => addslashes(utf8_basename($attach_row['real_filename'])),
  683. 'FILE_COMMENT' => $attach_row['attach_comment'],
  684. 'ATTACH_ID' => $attach_row['attach_id'],
  685. 'S_IS_ORPHAN' => $attach_row['is_orphan'],
  686. 'ASSOC_INDEX' => $count,
  687. 'FILESIZE' => get_formatted_filesize($attach_row['filesize']),
  688. 'U_VIEW_ATTACHMENT' => $download_link,
  689. 'S_HIDDEN' => $hidden,
  690. );
  691. }
  692. /**
  693. * Modify inline attachments template vars
  694. *
  695. * @event core.modify_inline_attachments_template_vars
  696. * @var array attachment_data Array containing attachments data
  697. * @var array attachrow_template_vars Array containing attachments template vars
  698. * @since 3.2.2-RC1
  699. */
  700. $vars = array('attachment_data', 'attachrow_template_vars');
  701. extract($phpbb_dispatcher->trigger_event('core.modify_inline_attachments_template_vars', compact($vars)));
  702. $template->assign_block_vars_array('attach_row', $attachrow_template_vars);
  703. }
  704. return count($attachment_data);
  705. }
  706. //
  707. // General Post functions
  708. //
  709. /**
  710. * Load Drafts
  711. */
  712. function load_drafts($topic_id = 0, $forum_id = 0, $id = 0, $pm_action = '', $msg_id = 0)
  713. {
  714. global $user, $db, $template, $auth;
  715. global $phpbb_root_path, $phpbb_dispatcher, $phpEx;
  716. $topic_ids = $forum_ids = $draft_rows = array();
  717. // Load those drafts not connected to forums/topics
  718. // If forum_id == 0 AND topic_id == 0 then this is a PM draft
  719. if (!$topic_id && !$forum_id)
  720. {
  721. $sql_and = ' AND d.forum_id = 0 AND d.topic_id = 0';
  722. }
  723. else
  724. {
  725. $sql_and = '';
  726. $sql_and .= ($forum_id) ? ' AND d.forum_id = ' . (int) $forum_id : '';
  727. $sql_and .= ($topic_id) ? ' AND d.topic_id = ' . (int) $topic_id : '';
  728. }
  729. $sql = 'SELECT d.*, f.forum_id, f.forum_name
  730. FROM ' . DRAFTS_TABLE . ' d
  731. LEFT JOIN ' . FORUMS_TABLE . ' f ON (f.forum_id = d.forum_id)
  732. WHERE d.user_id = ' . $user->data['user_id'] . "
  733. $sql_and
  734. ORDER BY d.save_time DESC";
  735. $result = $db->sql_query($sql);
  736. while ($row = $db->sql_fetchrow($result))
  737. {
  738. if ($row['topic_id'])
  739. {
  740. $topic_ids[] = (int) $row['topic_id'];
  741. }
  742. $draft_rows[] = $row;
  743. }
  744. $db->sql_freeresult($result);
  745. if (!count($draft_rows))
  746. {
  747. return;
  748. }
  749. $topic_rows = array();
  750. if (count($topic_ids))
  751. {
  752. $sql = 'SELECT topic_id, forum_id, topic_title, topic_poster
  753. FROM ' . TOPICS_TABLE . '
  754. WHERE ' . $db->sql_in_set('topic_id', array_unique($topic_ids));
  755. $result = $db->sql_query($sql);
  756. while ($row = $db->sql_fetchrow($result))
  757. {
  758. $topic_rows[$row['topic_id']] = $row;
  759. }
  760. $db->sql_freeresult($result);
  761. }
  762. /**
  763. * Drafts found and their topics
  764. * Edit $draft_rows in order to add or remove drafts loaded
  765. *
  766. * @event core.load_drafts_draft_list_result
  767. * @var array draft_rows The drafts query result. Includes its forum id and everything about the draft
  768. * @var array topic_ids The list of topics got from the topics table
  769. * @var array topic_rows The topics that draft_rows references
  770. * @since 3.1.0-RC3
  771. */
  772. $vars = array('draft_rows', 'topic_ids', 'topic_rows');
  773. extract($phpbb_dispatcher->trigger_event('core.load_drafts_draft_list_result', compact($vars)));
  774. unset($topic_ids);
  775. $template->assign_var('S_SHOW_DRAFTS', true);
  776. foreach ($draft_rows as $draft)
  777. {
  778. $link_topic = $link_forum = $link_pm = false;
  779. $view_url = $title = '';
  780. if (isset($topic_rows[$draft['topic_id']])
  781. && (
  782. ($topic_rows[$draft['topic_id']]['forum_id'] && $auth->acl_get('f_read', $topic_rows[$draft['topic_id']]['forum_id']))
  783. ||
  784. (!$topic_rows[$draft['topic_id']]['forum_id'] && $auth->acl_getf_global('f_read'))
  785. ))
  786. {
  787. $topic_forum_id = ($topic_rows[$draft['topic_id']]['forum_id']) ? $topic_rows[$draft['topic_id']]['forum_id'] : $forum_id;
  788. $link_topic = true;
  789. $view_url = append_sid("{$phpbb_root_path}viewtopic.$phpEx", 'f=' . $topic_forum_id . '&amp;t=' . $draft['topic_id']);
  790. $title = $topic_rows[$draft['topic_id']]['topic_title'];
  791. $insert_url = append_sid("{$phpbb_root_path}posting.$phpEx", 'f=' . $topic_forum_id . '&amp;t=' . $draft['topic_id'] . '&amp;mode=reply&amp;d=' . $draft['draft_id']);
  792. }
  793. else if ($draft['forum_id'] && $auth->acl_get('f_read', $draft['forum_id']))
  794. {
  795. $link_forum = true;
  796. $view_url = append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $draft['forum_id']);
  797. $title = $draft['forum_name'];
  798. $insert_url = append_sid("{$phpbb_root_path}posting.$phpEx", 'f=' . $draft['forum_id'] . '&amp;mode=post&amp;d=' . $draft['draft_id']);
  799. }
  800. else
  801. {
  802. // Either display as PM draft if forum_id and topic_id are empty or if access to the forums has been denied afterwards...
  803. $link_pm = true;
  804. $insert_url = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=$id&amp;mode=compose&amp;d={$draft['draft_id']}" . (($pm_action) ? "&amp;action=$pm_action" : '') . (($msg_id) ? "&amp;p=$msg_id" : ''));
  805. }
  806. $template->assign_block_vars('draftrow', array(
  807. 'DRAFT_ID' => $draft['draft_id'],
  808. 'DATE' => $user->format_date($draft['save_time']),
  809. 'DRAFT_SUBJECT' => $draft['draft_subject'],
  810. 'TITLE' => $title,
  811. 'U_VIEW' => $view_url,
  812. 'U_INSERT' => $insert_url,
  813. 'S_LINK_PM' => $link_pm,
  814. 'S_LINK_TOPIC' => $link_topic,
  815. 'S_LINK_FORUM' => $link_forum)
  816. );
  817. }
  818. }
  819. /**
  820. * Topic Review
  821. */
  822. function topic_review($topic_id, $forum_id, $mode = 'topic_review', $cur_post_id = 0, $show_quote_button = true)
  823. {
  824. global $user, $auth, $db, $template;
  825. global $config, $phpbb_root_path, $phpEx, $phpbb_container, $phpbb_dispatcher;
  826. /* @var $phpbb_content_visibility \phpbb\content_visibility */
  827. $phpbb_content_visibility = $phpbb_container->get('content.visibility');
  828. $sql_sort = ($mode == 'post_review') ? 'ASC' : 'DESC';
  829. // Go ahead and pull all data for this topic
  830. $sql = 'SELECT p.post_id
  831. FROM ' . POSTS_TABLE . ' p' . "
  832. WHERE p.topic_id = $topic_id
  833. AND " . $phpbb_content_visibility->get_visibility_sql('post', $forum_id, 'p.') . '
  834. ' . (($mode == 'post_review') ? " AND p.post_id > $cur_post_id" : '') . '
  835. ' . (($mode == 'post_review_edit') ? " AND p.post_id = $cur_post_id" : '') . '
  836. ORDER BY p.post_time ' . $sql_sort . ', p.post_id ' . $sql_sort;
  837. $result = $db->sql_query_limit($sql, $config['posts_per_page']);
  838. $post_list = array();
  839. while ($row = $db->sql_fetchrow($result))
  840. {
  841. $post_list[] = $row['post_id'];
  842. }
  843. $db->sql_freeresult($result);
  844. if (!count($post_list))
  845. {
  846. return false;
  847. }
  848. // Handle 'post_review_edit' like 'post_review' from now on
  849. if ($mode == 'post_review_edit')
  850. {
  851. $mode = 'post_review';
  852. }
  853. $sql_ary = array(
  854. 'SELECT' => 'u.username, u.user_id, u.user_colour, p.*, z.friend, z.foe, uu.username as post_delete_username, uu.user_colour as post_delete_user_colour',
  855. 'FROM' => array(
  856. USERS_TABLE => 'u',
  857. POSTS_TABLE => 'p',
  858. ),
  859. 'LEFT_JOIN' => array(
  860. array(
  861. 'FROM' => array(ZEBRA_TABLE => 'z'),
  862. 'ON' => 'z.user_id = ' . $user->data['user_id'] . ' AND z.zebra_id = p.poster_id',
  863. ),
  864. array(
  865. 'FROM' => array(USERS_TABLE => 'uu'),
  866. 'ON' => 'uu.user_id = p.post_delete_user',
  867. ),
  868. ),
  869. 'WHERE' => $db->sql_in_set('p.post_id', $post_list) . '
  870. AND u.user_id = p.poster_id',
  871. );
  872. /**
  873. * Event to modify the SQL query for topic reviews
  874. *
  875. * @event core.topic_review_modify_sql_ary
  876. * @var int topic_id The topic ID that is being reviewed
  877. * @var int forum_id The topic's forum ID
  878. * @var string mode The topic review mode
  879. * @var int cur_post_id Post offset ID
  880. * @var bool show_quote_button Flag indicating if the quote button should be displayed
  881. * @var array post_list Array with the post IDs
  882. * @var array sql_ary Array with the SQL query
  883. * @since 3.2.8-RC1
  884. */
  885. $vars = array(
  886. 'topic_id',
  887. 'forum_id',
  888. 'mode',
  889. 'cur_post_id',
  890. 'show_quote_button',
  891. 'post_list',
  892. 'sql_ary',
  893. );
  894. extract($phpbb_dispatcher->trigger_event('core.topic_review_modify_sql_ary', compact($vars)));
  895. $sql = $db->sql_build_query('SELECT', $sql_ary);
  896. $result = $db->sql_query($sql);
  897. $rowset = array();
  898. $has_attachments = false;
  899. while ($row = $db->sql_fetchrow($result))
  900. {
  901. $rowset[$row['post_id']] = $row;
  902. if ($row['post_attachment'])
  903. {
  904. $has_attachments = true;
  905. }
  906. }
  907. $db->sql_freeresult($result);
  908. // Grab extensions
  909. $attachments = array();
  910. if ($has_attachments && $auth->acl_get('u_download') && $auth->acl_get('f_download', $forum_id))
  911. {
  912. // Get attachments...
  913. $sql = 'SELECT *
  914. FROM ' . ATTACHMENTS_TABLE . '
  915. WHERE ' . $db->sql_in_set('post_msg_id', $post_list) . '
  916. AND in_message = 0
  917. ORDER BY filetime DESC, post_msg_id ASC';
  918. $result = $db->sql_query($sql);
  919. while ($row = $db->sql_fetchrow($result))
  920. {
  921. $attachments[$row['post_msg_id']][] = $row;
  922. }
  923. $db->sql_freeresult($result);
  924. }
  925. /**
  926. * Event to modify the posts list for topic reviews
  927. *
  928. * @event core.topic_review_modify_post_list
  929. * @var array attachments Array with the post attachments data
  930. * @var int cur_post_id Post offset ID
  931. * @var int forum_id The topic's forum ID
  932. * @var string mode The topic review mode
  933. * @var array post_list Array with the post IDs
  934. * @var array rowset Array with the posts data
  935. * @var bool show_quote_button Flag indicating if the quote button should be displayed
  936. * @var int topic_id The topic ID that is being reviewed
  937. * @since 3.1.9-RC1
  938. */
  939. $vars = array(
  940. 'attachments',
  941. 'cur_post_id',
  942. 'forum_id',
  943. 'mode',
  944. 'post_list',
  945. 'rowset',
  946. 'show_quote_button',
  947. 'topic_id',
  948. );
  949. extract($phpbb_dispatcher->trigger_event('core.topic_review_modify_post_list', compact($vars)));
  950. for ($i = 0, $end = count($post_list); $i < $end; ++$i)
  951. {
  952. // A non-existing rowset only happens if there was no user present for the entered poster_id
  953. // This could be a broken posts table.
  954. if (!isset($rowset[$post_list[$i]]))
  955. {
  956. continue;
  957. }
  958. $row = $rowset[$post_list[$i]];
  959. $poster_id = $row['user_id'];
  960. $post_subject = $row['post_subject'];
  961. $decoded_message = false;
  962. if ($show_quote_button && $auth->acl_get('f_reply', $forum_id))
  963. {
  964. $decoded_message = censor_text($row['post_text']);
  965. decode_message($decoded_message, $row['bbcode_uid']);
  966. $decoded_message = bbcode_nl2br($decoded_message);
  967. }
  968. $parse_flags = ($row['bbcode_bitfield'] ? OPTION_FLAG_BBCODE : 0);
  969. $parse_flags |= ($row['enable_smilies'] ? OPTION_FLAG_SMILIES : 0);
  970. $message = generate_text_for_display($row['post_text'], $row['bbcode_uid'], $row['bbcode_bitfield'], $parse_flags, true);
  971. if (!empty($attachments[$row['post_id']]))
  972. {
  973. $update_count = array();
  974. parse_attachments($forum_id, $message, $attachments[$row['post_id']], $update_count);
  975. }
  976. $post_subject = censor_text($post_subject);
  977. $post_anchor = ($mode == 'post_review') ? 'ppr' . $row['post_id'] : 'pr' . $row['post_id'];
  978. $u_show_post = append_sid($phpbb_root_path . 'viewtopic.' . $phpEx, "f=$forum_id&amp;t=$topic_id&amp;p={$row['post_id']}&amp;view=show#p{$row['post_id']}");
  979. $l_deleted_message = '';
  980. if ($row['post_visibility'] == ITEM_DELETED)
  981. {
  982. $display_postername = get_username_string('full', $poster_id, $row['username'], $row['user_colour'], $row['post_username']);
  983. // User having deleted the post also being the post author?
  984. if (!$row['post_delete_user'] || $row['post_delete_user'] == $poster_id)
  985. {
  986. $display_username = $display_postername;
  987. }
  988. else
  989. {
  990. $display_username = get_username_string('full', $row['post_delete_user'], $row['post_delete_username'], $row['post_delete_user_colour']);
  991. }
  992. if ($row['post_delete_reason'])
  993. {
  994. $l_deleted_message = $user->lang('POST_DELETED_BY_REASON', $display_postername, $display_username, $user->format_date($row['post_delete_time'], false, true), $row['post_delete_reason']);
  995. }
  996. else
  997. {
  998. $l_deleted_message = $user->lang('POST_DELETED_BY', $display_postername, $display_username, $user->format_date($row['post_delete_time'], false, true));
  999. }
  1000. }
  1001. $post_row = array(
  1002. 'POST_AUTHOR_FULL' => get_username_string('full', $poster_id, $row['username'], $row['user_colour'], $row['post_username']),
  1003. 'POST_AUTHOR_COLOUR' => get_username_string('colour', $poster_id, $row['username'], $row['user_colour'], $row['post_username']),
  1004. 'POST_AUTHOR' => get_username_string('username', $poster_id, $row['username'], $row['user_colour'], $row['post_username']),
  1005. 'U_POST_AUTHOR' => get_username_string('profile', $poster_id, $row['username'], $row['user_colour'], $row['post_username']),
  1006. 'S_HAS_ATTACHMENTS' => (!empty($attachments[$row['post_id']])) ? true : false,
  1007. 'S_FRIEND' => ($row['friend']) ? true : false,
  1008. 'S_IGNORE_POST' => ($row['foe']) ? true : false,
  1009. 'L_IGNORE_POST' => ($row['foe']) ? sprintf($user->lang['POST_BY_FOE'], get_username_string('full', $poster_id, $row['username'], $row['user_colour'], $row['post_username']), "<a href=\"{$u_show_post}\" onclick=\"phpbb.toggleDisplay('{$post_anchor}', 1); return false;\">", '</a>') : '',
  1010. 'S_POST_DELETED' => ($row['post_visibility'] == ITEM_DELETED) ? true : false,
  1011. 'L_DELETE_POST' => $l_deleted_message,
  1012. 'POST_SUBJECT' => $post_subject,
  1013. 'MINI_POST_IMG' => $user->img('icon_post_target', $user->lang['POST']),
  1014. 'POST_DATE' => $user->format_date($row['post_time']),
  1015. 'MESSAGE' => $message,
  1016. 'DECODED_MESSAGE' => $decoded_message,
  1017. 'POST_ID' => $row['post_id'],
  1018. 'POST_TIME' => $row['post_time'],
  1019. 'USER_ID' => $row['user_id'],
  1020. 'U_MINI_POST' => append_sid("{$phpbb_root_path}viewtopic.$phpEx", 'p=' . $row['post_id']) . '#p' . $row['post_id'],
  1021. 'U_MCP_DETAILS' => ($auth->acl_get('m_info', $forum_id)) ? append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=main&amp;mode=post_details&amp;f=' . $forum_id . '&amp;p=' . $row['post_id'], true, $user->session_id) : '',
  1022. 'POSTER_QUOTE' => ($show_quote_button && $auth->acl_get('f_reply', $forum_id)) ? addslashes(get_username_string('username', $poster_id, $row['username'], $row['user_colour'], $row['post_username'])) : '',
  1023. );
  1024. $current_row_number = $i;
  1025. /**
  1026. * Event to modify the template data block for topic reviews
  1027. *
  1028. * @event core.topic_review_modify_row
  1029. * @var string mode The review mode
  1030. * @var int topic_id The topic that is being reviewed
  1031. * @var int forum_id The topic's forum
  1032. * @var int cur_post_id Post offset id
  1033. * @var int current_row_number Number of the current row being iterated
  1034. * @var array post_row Template block array of the current post
  1035. * @var array row Array with original post and user data
  1036. * @since 3.1.4-RC1
  1037. */
  1038. $vars = array(
  1039. 'mode',
  1040. 'topic_id',
  1041. 'forum_id',
  1042. 'cur_post_id',
  1043. 'current_row_number',
  1044. 'post_row',
  1045. 'row',
  1046. );
  1047. extract($phpbb_dispatcher->trigger_event('core.topic_review_modify_row', compact($vars)));
  1048. $template->assign_block_vars($mode . '_row', $post_row);
  1049. // Display not already displayed Attachments for this post, we already parsed them. ;)
  1050. if (!empty($attachments[$row['post_id']]))
  1051. {
  1052. foreach ($attachments[$row['post_id']] as $attachment)
  1053. {
  1054. $template->assign_block_vars($mode . '_row.attachment', array(
  1055. 'DISPLAY_ATTACHMENT' => $attachment)
  1056. );
  1057. }
  1058. }
  1059. unset($rowset[$post_list[$i]]);
  1060. }
  1061. if ($mode == 'topic_review')
  1062. {
  1063. $template->assign_var('QUOTE_IMG', $user->img('icon_post_quote', $user->lang['REPLY_WITH_QUOTE']));
  1064. }
  1065. return true;
  1066. }
  1067. //
  1068. // Post handling functions
  1069. //
  1070. /**
  1071. * Delete Post
  1072. */
  1073. function delete_post($forum_id, $topic_id, $post_id, &$data, $is_soft = false, $softdelete_reason = '')
  1074. {
  1075. global $db, $user, $phpbb_container, $phpbb_dispatcher;
  1076. global $config, $phpEx, $phpbb_root_path;
  1077. // Specify our post mode
  1078. $post_mode = 'delete';
  1079. if (($data['topic_first_post_id'] === $data['topic_last_post_id']) && ($data['topic_posts_approved'] + $data['topic_posts_unapproved'] + $data['topic_posts_softdeleted'] == 1))
  1080. {
  1081. $post_mode = 'delete_topic';
  1082. }
  1083. else if ($data['topic_first_post_id'] == $post_id)
  1084. {
  1085. $post_mode = 'delete_first_post';
  1086. }
  1087. else if ($data['topic_last_post_id'] == $post_id)
  1088. {
  1089. $post_mode = 'delete_last_post';
  1090. }
  1091. $sql_data = array();
  1092. $next_post_id = false;
  1093. include_once($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
  1094. $db->sql_transaction('begin');
  1095. // we must make sure to update forums that contain the shadow'd topic
  1096. if ($post_mode == 'delete_topic')
  1097. {
  1098. $shadow_forum_ids = array();
  1099. $sql = 'SELECT forum_id
  1100. FROM ' . TOPICS_TABLE . '
  1101. WHERE ' . $db->sql_in_set('topic_moved_id', $topic_id);
  1102. $result = $db->sql_query($sql);
  1103. while ($row = $db->sql_fetchrow($result))
  1104. {
  1105. if (!isset($shadow_forum_ids[(int) $row['forum_id']]))
  1106. {
  1107. $shadow_forum_ids[(int) $row['forum_id']] = 1;
  1108. }
  1109. else
  1110. {
  1111. $shadow_forum_ids[(int) $row['forum_id']]++;
  1112. }
  1113. }
  1114. $db->sql_freeresult($result);
  1115. }
  1116. /* @var $phpbb_content_visibility \phpbb\content_visibility */
  1117. $phpbb_content_visibility = $phpbb_container->get('content.visibility');
  1118. // (Soft) delete the post
  1119. if ($is_soft && ($post_mode != 'delete_topic'))
  1120. {
  1121. $phpbb_content_visibility->set_post_visibility(ITEM_DELETED, $post_id, $topic_id, $forum_id, $user->data['user_id'], time(), $softdelete_reason, ($data['topic_first_post_id'] == $post_id), ($data['topic_last_post_id'] == $post_id));
  1122. }
  1123. else if (!$is_soft)
  1124. {
  1125. if (!delete_posts('post_id', array($post_id), false, false, false))
  1126. {
  1127. // Try to delete topic, we may had an previous error causing inconsistency
  1128. if ($post_mode == 'delete_topic')
  1129. {
  1130. delete_topics('topic_id', array($topic_id), false);
  1131. }
  1132. trigger_error('ALREADY_DELETED');
  1133. }
  1134. }
  1135. $db->sql_transaction('commit');
  1136. // Collect the necessary information for updating the tables
  1137. $sql_data[FORUMS_TABLE] = $sql_data[TOPICS_TABLE] = '';
  1138. switch ($post_mode)
  1139. {
  1140. case 'delete_topic':
  1141. foreach ($shadow_forum_ids as $updated_forum => $topic_count)
  1142. {
  1143. // counting is fun! we only have to do count($forum_ids) number of queries,
  1144. // even if the topic is moved back to where its shadow lives (we count how many times it is in a forum)
  1145. $sql = 'UPDATE ' . FORUMS_TABLE . '
  1146. SET forum_topics_approved = forum_topics_approved - ' . $topic_count . '
  1147. WHERE forum_id = ' . $updated_forum;
  1148. $db->sql_query($sql);
  1149. update_post_information('forum', $updated_forum);
  1150. }
  1151. if ($is_soft)
  1152. {
  1153. $phpbb_content_visibility->set_topic_visibility(ITEM_DELETED, $topic_id, $forum_id, $user->data['user_id'], time(), $softdelete_reason);
  1154. }
  1155. else
  1156. {
  1157. delete_topics('topic_id', array($topic_id), false);
  1158. $phpbb_content_visibility->remove_topic_from_statistic($data, $sql_data);
  1159. $config->increment('num_posts', -1, false);
  1160. $update_sql = update_post_information('forum', $forum_id, true);
  1161. if (count($update_sql))
  1162. {
  1163. $sql_data[FORUMS_TABLE] .= ($sql_data[FORUMS_TABLE]) ? ', ' : '';
  1164. $sql_data[FORUMS_TABLE] .= implode(', ', $update_sql[$forum_id]);
  1165. }
  1166. }
  1167. break;
  1168. case 'delete_first_post':
  1169. $sql = 'SELECT p.post_id, p.poster_id, p.post_time, p.post_username, u.username, u.user_colour
  1170. FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . " u
  1171. WHERE p.topic_id = $topic_id
  1172. AND p.poster_id = u.user_id
  1173. AND p.post_visibility = " . ITEM_APPROVED . '
  1174. ORDER BY p.post_time ASC, p.post_id ASC';
  1175. $result = $db->sql_query_limit($sql, 1);
  1176. $row = $db->sql_fetchrow($result);
  1177. $db->sql_freeresult($result);
  1178. if (!$row)
  1179. {
  1180. // No approved post, so the first is a not-approved post (unapproved or soft deleted)
  1181. $sql = 'SELECT p.post_id, p.poster_id, p.post_time, p.post_username, u.username, u.user_colour
  1182. FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . " u
  1183. WHERE p.topic_id = $topic_id
  1184. AND p.poster_id = u.user_id
  1185. ORDER BY p.post_time ASC, p.post_id ASC";
  1186. $result = $db->sql_query_limit($sql, 1);
  1187. $row = $db->sql_fetchrow($result);
  1188. $db->sql_freeresult($result);
  1189. }
  1190. $next_post_id = (int) $row['post_id'];
  1191. $sql_data[TOPICS_TABLE] = $db->sql_build_array('UPDATE', array(
  1192. 'topic_poster' => (int) $row['poster_id'],
  1193. 'topic_first_post_id' => (int) $row['post_id'],
  1194. 'topic_first_poster_colour' => $row['user_colour'],
  1195. 'topic_first_poster_name' => ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username'],
  1196. 'topic_time' => (int) $row['post_time'],
  1197. ));
  1198. break;
  1199. case 'delete_last_post':
  1200. if (!$is_soft)
  1201. {
  1202. // Update last post information when hard deleting. Soft delete already did that by itself.
  1203. $update_sql = update_post_information('forum', $forum_id, true);
  1204. if (count($update_sql))
  1205. {
  1206. $sql_data[FORUMS_TABLE] = (($sql_data[FORUMS_TABLE]) ? $sql_data[FORUMS_TABLE] . ', ' : '') . implode(', ', $update_sql[$forum_id]);
  1207. }
  1208. $sql_data[TOPICS_TABLE] = (($sql_data[TOPICS_TABLE]) ? $sql_data[TOPICS_TABLE] . ', ' : '') . 'topic_bumped = 0, topic_bumper = 0';
  1209. $update_sql = update_post_information('topic', $topic_id, true);
  1210. if (!empty($update_sql))
  1211. {
  1212. $sql_data[TOPICS_TABLE] .= ', ' . implode(', ', $update_sql[$topic_id]);
  1213. $next_post_id = (int) str_replace('topic_last_post_id = ', '', $update_sql[$topic_id][0]);
  1214. }
  1215. }
  1216. if (!$next_post_id)
  1217. {
  1218. $sql = 'SELECT MAX(post_id) as last_post_id
  1219. FROM ' . POSTS_TABLE . "
  1220. WHERE topic_id = $topic_id
  1221. AND " . $phpbb_content_visibility->get_visibility_sql('post', $forum_id);
  1222. $result = $db->sql_query($sql);
  1223. $next_post_id = (int) $db->sql_fetchfield('last_post_id');
  1224. $db->sql_freeresult($result);
  1225. }
  1226. break;
  1227. case 'delete':
  1228. $sql = 'SELECT post_id
  1229. FROM ' . POSTS_TABLE . "
  1230. WHERE topic_id = $topic_id
  1231. AND " . $phpbb_content_visibility->get_visibility_sql('post', $forum_id) . '
  1232. AND post_time > ' . $data['post_time'] . '
  1233. ORDER BY post_time ASC, post_id ASC';
  1234. $result = $db->sql_query_limit($sql, 1);
  1235. $next_post_id = (int) $db->sql_fetchfield('post_id');
  1236. $db->sql_freeresult($result);
  1237. break;
  1238. }
  1239. if (($post_mode == 'delete') || ($post_mode == 'delete_last_post') || ($post_mode == 'delete_first_post'))
  1240. {
  1241. if (!$is_soft)
  1242. {
  1243. $phpbb_content_visibility->remove_post_from_statistic($data, $sql_data);
  1244. }
  1245. $sql = 'SELECT 1 AS has_attachments
  1246. FROM ' . ATTACHMENTS_TABLE . '
  1247. WHERE topic_id = ' . $topic_id;
  1248. $result = $db->sql_query_limit($sql, 1);
  1249. $has_attachments = (int) $db->sql_fetchfield('has_attachments');
  1250. $db->sql_freeresult($result);
  1251. if (!$has_attachments)
  1252. {
  1253. $sql_data[TOPICS_TABLE] = (($sql_data[TOPICS_TABLE]) ? $sql_data[TOPICS_TABLE] . ', ' : '') . 'topic_attachment = 0';
  1254. }
  1255. }
  1256. $db->sql_transaction('begin');
  1257. $where_sql = array(
  1258. FORUMS_TABLE => "forum_id = $forum_id",
  1259. TOPICS_TABLE => "topic_id = $topic_id",
  1260. USERS_TABLE => 'user_id = ' . $data['poster_id'],
  1261. );
  1262. foreach ($sql_data as $table => $update_sql)
  1263. {
  1264. if ($update_sql)
  1265. {
  1266. $db->sql_query("UPDATE $table SET $update_sql WHERE " . $where_sql[$table]);
  1267. }
  1268. }
  1269. // Adjust posted info for this user by looking for a post by him/her within this topic...
  1270. if ($post_mode != 'delete_topic' && $config['load_db_track'] && $data['poster_id'] != ANONYMOUS)
  1271. {
  1272. $sql = 'SELECT poster_id
  1273. FROM ' . POSTS_TABLE . '
  1274. WHERE topic_id = ' . $topic_id . '
  1275. AND poster_id = ' . $data['poster_id'];
  1276. $result = $db->sql_query_limit($sql, 1);
  1277. $poster_id = (int) $db->sql_fetchfield('poster_id');
  1278. $db->sql_freeresult($result);
  1279. // The user is not having any more posts within this topic
  1280. if (!$poster_id)
  1281. {
  1282. $sql = 'DELETE FROM ' . TOPICS_POSTED_TABLE . '
  1283. WHERE topic_id = ' . $topic_id . '
  1284. AND user_id = ' . $data['poster_id'];
  1285. $db->sql_query($sql);
  1286. }
  1287. }
  1288. $db->sql_transaction('commit');
  1289. if ($data['post_reported'] && ($post_mode != 'delete_topic'))
  1290. {
  1291. sync('topic_reported', 'topic_id', array($topic_id));
  1292. }
  1293. /**
  1294. * This event is used for performing actions directly after a post or topic
  1295. * has been deleted.
  1296. *
  1297. * @event core.delete_post_after
  1298. * @var int forum_id Post forum ID
  1299. * @var int topic_id Post topic ID
  1300. * @var int post_id Post ID
  1301. * @var array data Post data
  1302. * @var bool is_soft Soft delete flag
  1303. * @var string softdelete_reason Soft delete reason
  1304. * @var string post_mode delete_topic, delete_first_post, delete_last_post or delete
  1305. * @var mixed next_post_id Next post ID in the topic (post ID or false)
  1306. *
  1307. * @since 3.1.11-RC1
  1308. */
  1309. $vars = array(
  1310. 'forum_id',
  1311. 'topic_id',
  1312. 'post_id',
  1313. 'data',
  1314. 'is_soft',
  1315. 'softdelete_reason',
  1316. 'post_mode',
  1317. 'next_post_id',
  1318. );
  1319. extract($phpbb_dispatcher->trigger_event('core.delete_post_after', compact($vars)));
  1320. return $next_post_id;
  1321. }
  1322. /**
  1323. * Submit Post
  1324. * @todo Split up and create lightweight, simple API for this.
  1325. */
  1326. function submit_post($mode, $subject, $username, $topic_type, &$poll_ary, &$data_ary, $update_message = true, $update_search_index = true)
  1327. {
  1328. global $db, $auth, $user, $config, $phpEx, $phpbb_root_path, $phpbb_container, $phpbb_dispatcher, $phpbb_log, $request;
  1329. $attachment_storage = $phpbb_container->get('storage.attachment');
  1330. $poll = $poll_ary;
  1331. $data = $data_ary;
  1332. /**
  1333. * Modify the data for post submitting
  1334. *
  1335. * @event core.modify_submit_post_data
  1336. * @var string mode Variable containing posting mode value
  1337. * @var string subject Variable containing post subject value
  1338. * @var string username Variable containing post author name
  1339. * @var int topic_type Variable containing topic type value
  1340. * @var array poll Array with the poll data for the post
  1341. * @var array data Array with the data for the post
  1342. * @var bool update_message Flag indicating if the post will be updated
  1343. * @var bool update_search_index Flag indicating if the search index will be updated
  1344. * @since 3.1.0-a4
  1345. */
  1346. $vars = array(
  1347. 'mode',
  1348. 'subject',
  1349. 'username',
  1350. 'topic_type',
  1351. 'poll',
  1352. 'data',
  1353. 'update_message',
  1354. 'update_search_index',
  1355. );
  1356. extract($phpbb_dispatcher->trigger_event('core.modify_submit_post_data', compact($vars)));
  1357. $poll_ary = $poll;
  1358. $data_ary = $data;
  1359. unset($poll);
  1360. unset($data);
  1361. // We do not handle erasing posts here
  1362. if ($mode == 'delete')
  1363. {
  1364. return false;
  1365. }
  1366. if (!empty($data_ary['post_time']))
  1367. {
  1368. $current_time = $data_ary['post_time'];
  1369. }
  1370. else
  1371. {
  1372. $current_time = time();
  1373. }
  1374. if ($mode == 'post')
  1375. {
  1376. $post_mode = 'post';
  1377. $update_message = true;
  1378. }
  1379. else if ($mode != 'edit')
  1380. {
  1381. $post_mode = 'reply';
  1382. $update_message = true;
  1383. }
  1384. else if ($mode == 'edit')
  1385. {
  1386. $post_mode = ($data_ary['topic_posts_approved'] + $data_ary['topic_posts_unapproved'] + $data_ary['topic_posts_softdeleted'] == 1) ? 'edit_topic' : (($data_ary['topic_first_post_id'] == $data_ary['post_id']) ? 'edit_first_post' : (($data_ary['topic_last_post_id'] == $data_ary['post_id']) ? 'edit_last_post' : 'edit'));
  1387. }
  1388. // First of all make sure the subject and topic title are having the correct length.
  1389. // To achieve this without cutting off between special chars we convert to an array and then count the elements.
  1390. $subject = truncate_string($subject, 120);
  1391. $data_ary['topic_title'] = truncate_string($data_ary['topic_title'], 120);
  1392. // Collect some basic information about which tables and which rows to update/insert
  1393. $sql_data = $topic_row = array();
  1394. $poster_id = ($mode == 'edit') ? $data_ary['poster_id'] : (int) $user->data['user_id'];
  1395. // Retrieve some additional information if not present
  1396. if ($mode == 'edit' && (!isset($data_ary['post_visibility']) || !isset($data_ary['topic_visibility']) || $data_ary['post_visibility'] === false || $data_ary['topic_visibility'] === false))
  1397. {
  1398. $sql = 'SELECT p.post_visibility, t.topic_type, t.topic_posts_approved, t.topic_posts_unapproved, t.topic_posts_softdeleted, t.topic_visibility
  1399. FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . ' p
  1400. WHERE t.topic_id = p.topic_id
  1401. AND p.post_id = ' . $data_ary['post_id'];
  1402. $result = $db->sql_query($sql);
  1403. $topic_row = $db->sql_fetchrow($result);
  1404. $db->sql_freeresult($result);
  1405. $data_ary['topic_visibility'] = $topic_row['topic_visibility'];
  1406. $data_ary['post_visibility'] = $topic_row['post_visibility'];
  1407. }
  1408. // This variable indicates if the user is able to post or put into the queue
  1409. $post_visibility = ITEM_APPROVED;
  1410. // Check the permissions for post approval.
  1411. // Moderators must go through post approval like ordinary users.
  1412. if (!$auth->acl_get('f_noapprove', $data_ary['forum_id']))
  1413. {
  1414. // Post not approved, but in queue
  1415. $post_visibility = ITEM_UNAPPROVED;
  1416. switch ($post_mode)
  1417. {
  1418. case 'edit_first_post':
  1419. case 'edit':
  1420. case 'edit_last_post':
  1421. case 'edit_topic':
  1422. $post_visibility = ITEM_REAPPROVE;
  1423. break;
  1424. }
  1425. }
  1426. else if (isset($data_ary['post_visibility']) && $data_ary['post_visibility'] !== false)
  1427. {
  1428. $post_visibility = $data_ary['post_visibility'];
  1429. }
  1430. // MODs/Extensions are able to force any visibility on posts
  1431. if (isset($data_ary['force_approved_state']))
  1432. {
  1433. $post_visibility = (in_array((int) $data_ary['force_approved_state'], array(ITEM_APPROVED, ITEM_UNAPPROVED, ITEM_DELETED, ITEM_REAPPROVE))) ? (int) $data_ary['force_approved_state'] : $post_visibility;
  1434. }
  1435. if (isset($data_ary['force_visibility']))
  1436. {
  1437. $post_visibility = (in_array((int) $data_ary['force_visibility'], array(ITEM_APPROVED, ITEM_UNAPPROVED, ITEM_DELETED, ITEM_REAPPROVE))) ? (int) $data_ary['force_visibility'] : $post_visibility;
  1438. }
  1439. // Start the transaction here
  1440. $db->sql_transaction('begin');
  1441. // Collect Information
  1442. switch ($post_mode)
  1443. {
  1444. case 'post':
  1445. case 'reply':
  1446. $sql_data[POSTS_TABLE]['sql'] = array(
  1447. 'forum_id' => $data_ary['forum_id'],
  1448. 'poster_id' => (int) $user->data['user_id'],
  1449. 'icon_id' => $data_ary['icon_id'],
  1450. 'poster_ip' => $user->ip,
  1451. 'post_time' => $current_time,
  1452. 'post_visibility' => $post_visibility,
  1453. 'enable_bbcode' => $data_ary['enable_bbcode'],
  1454. 'enable_smilies' => $data_ary['enable_smilies'],
  1455. 'enable_magic_url' => $data_ary['enable_urls'],
  1456. 'enable_sig' => $data_ary['enable_sig'],
  1457. 'post_username' => (!$user->data['is_registered']) ? $username : '',
  1458. 'post_subject' => $subject,
  1459. 'post_text' => $data_ary['message'],
  1460. 'post_checksum' => $data_ary['message_md5'],
  1461. 'post_attachment' => (!empty($data_ary['attachment_data'])) ? 1 : 0,
  1462. 'bbcode_bitfield' => $data_ary['bbcode_bitfield'],
  1463. 'bbcode_uid' => $data_ary['bbcode_uid'],
  1464. 'post_postcount' => ($auth->acl_get('f_postcount', $data_ary['forum_id'])) ? 1 : 0,
  1465. 'post_edit_locked' => $data_ary['post_edit_locked']
  1466. );
  1467. break;
  1468. case 'edit_first_post':
  1469. case 'edit':
  1470. case 'edit_last_post':
  1471. case 'edit_topic':
  1472. // If edit reason is given always display edit info
  1473. // If editing last post then display no edit info
  1474. // If m_edit permission then display no edit info
  1475. // If normal edit display edit info
  1476. // Display edit info if edit reason given or user is editing his post, which is not the last within the topic.
  1477. if ($data_ary['post_edit_reason'] || (!$auth->acl_get('m_edit', $data_ary['forum_id']) && ($post_mode == 'edit' || $post_mode == 'edit_first_post')))
  1478. {
  1479. $data_ary['post_edit_reason'] = truncate_string($data_ary['post_edit_reason'], 255, 255, false);
  1480. $sql_data[POSTS_TABLE]['sql'] = array(
  1481. 'post_edit_time' => $current_time,
  1482. 'post_edit_reason' => $data_ary['post_edit_reason'],
  1483. 'post_edit_user' => (int) $data_ary['post_edit_user'],
  1484. );
  1485. $sql_data[POSTS_TABLE]['stat'][] = 'post_edit_count = post_edit_count + 1';
  1486. }
  1487. else if (!$data_ary['post_edit_reason'] && $mode == 'edit' && $auth->acl_get('m_edit', $data_ary['forum_id']))
  1488. {
  1489. $sql_data[POSTS_TABLE]['sql'] = array(
  1490. 'post_edit_reason' => '',
  1491. );
  1492. }
  1493. // If the person editing this post is different to the one having posted then we will add a log entry stating the edit
  1494. // Could be simplified by only adding to the log if the edit is not tracked - but this may confuse admins/mods
  1495. if ($user->data['user_id'] != $poster_id)
  1496. {
  1497. $log_subject = ($subject) ? $subject : $data_ary['topic_title'];
  1498. $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_POST_EDITED', false, array(
  1499. 'forum_id' => $data_ary['forum_id'],
  1500. 'topic_id' => $data_ary['topic_id'],
  1501. 'post_id' => $data_ary['post_id'],
  1502. $log_subject,
  1503. (!empty($username)) ? $username : $user->lang['GUEST'],
  1504. $data_ary['post_edit_reason']
  1505. ));
  1506. }
  1507. if (!isset($sql_data[POSTS_TABLE]['sql']))
  1508. {
  1509. $sql_data[POSTS_TABLE]['sql'] = array();
  1510. }
  1511. $sql_data[POSTS_TABLE]['sql'] = array_merge($sql_data[POSTS_TABLE]['sql'], array(
  1512. 'forum_id' => $data_ary['forum_id'],
  1513. 'poster_id' => $data_ary['poster_id'],
  1514. 'icon_id' => $data_ary['icon_id'],
  1515. // We will change the visibility later
  1516. //'post_visibility' => $post_visibility,
  1517. 'enable_bbcode' => $data_ary['enable_bbcode'],
  1518. 'enable_smilies' => $data_ary['enable_smilies'],
  1519. 'enable_magic_url' => $data_ary['enable_urls'],
  1520. 'enable_sig' => $data_ary['enable_sig'],
  1521. 'post_username' => ($username && $data_ary['poster_id'] == ANONYMOUS) ? $username : '',
  1522. 'post_subject' => $subject,
  1523. 'post_checksum' => $data_ary['message_md5'],
  1524. 'post_attachment' => (!empty($data_ary['attachment_data'])) ? 1 : 0,
  1525. 'bbcode_bitfield' => $data_ary['bbcode_bitfield'],
  1526. 'bbcode_uid' => $data_ary['bbcode_uid'],
  1527. 'post_edit_locked' => $data_ary['post_edit_locked'])
  1528. );
  1529. if ($update_message)
  1530. {
  1531. $sql_data[POSTS_TABLE]['sql']['post_text'] = $data_ary['message'];
  1532. }
  1533. break;
  1534. }
  1535. // And the topic ladies and gentlemen
  1536. switch ($post_mode)
  1537. {
  1538. case 'post':
  1539. $sql_data[TOPICS_TABLE]['sql'] = array(
  1540. 'topic_poster' => (int) $user->data['user_id'],
  1541. 'topic_time' => $current_time,
  1542. 'topic_last_view_time' => $current_time,
  1543. 'forum_id' => $data_ary['forum_id'],
  1544. 'icon_id' => $data_ary['icon_id'],
  1545. 'topic_posts_approved' => ($post_visibility == ITEM_APPROVED) ? 1 : 0,
  1546. 'topic_posts_softdeleted' => ($post_visibility == ITEM_DELETED) ? 1 : 0,
  1547. 'topic_posts_unapproved' => ($post_visibility == ITEM_UNAPPROVED) ? 1 : 0,
  1548. 'topic_visibility' => $post_visibility,
  1549. 'topic_delete_user' => ($post_visibility != ITEM_APPROVED) ? (int) $user->data['user_id'] : 0,
  1550. 'topic_title' => $subject,
  1551. 'topic_first_poster_name' => (!$user->data['is_registered'] && $username) ? $username : (($user->data['user_id'] != ANONYMOUS) ? $user->data['username'] : ''),
  1552. 'topic_first_poster_colour' => $user->data['user_colour'],
  1553. 'topic_type' => $topic_type,
  1554. 'topic_time_limit' => $topic_type != POST_NORMAL ? ($data_ary['topic_time_limit'] * 86400) : 0,
  1555. 'topic_attachment' => (!empty($data_ary['attachment_data'])) ? 1 : 0,
  1556. 'topic_status' => (isset($data_ary['topic_status'])) ? $data_ary['topic_status'] : ITEM_UNLOCKED,
  1557. );
  1558. if (isset($poll_ary['poll_options']) && !empty($poll_ary['poll_options']))
  1559. {
  1560. $poll_start = ($poll_ary['poll_start']) ? $poll_ary['poll_start'] : $current_time;
  1561. $poll_length = $poll_ary['poll_length'] * 86400;
  1562. if ($poll_length < 0)
  1563. {
  1564. $poll_start = $poll_start + $poll_length;
  1565. if ($poll_start < 0)
  1566. {
  1567. $poll_start = 0;
  1568. }
  1569. $poll_length = 1;
  1570. }
  1571. $sql_data[TOPICS_TABLE]['sql'] = array_merge($sql_data[TOPICS_TABLE]['sql'], array(
  1572. 'poll_title' => $poll_ary['poll_title'],
  1573. 'poll_start' => $poll_start,
  1574. 'poll_max_options' => $poll_ary['poll_max_options'],
  1575. 'poll_length' => $poll_length,
  1576. 'poll_vote_change' => $poll_ary['poll_vote_change'])
  1577. );
  1578. }
  1579. $sql_data[USERS_TABLE]['stat'][] = "user_lastpost_time = $current_time" . (($auth->acl_get('f_postcount', $data_ary['forum_id']) && $post_visibility == ITEM_APPROVED) ? ', user_posts = user_posts + 1' : '');
  1580. if ($post_visibility == ITEM_APPROVED)
  1581. {
  1582. $sql_data[FORUMS_TABLE]['stat'][] = 'forum_topics_approved = forum_topics_approved + 1';
  1583. $sql_data[FORUMS_TABLE]['stat'][] = 'forum_posts_approved = forum_posts_approved + 1';
  1584. }
  1585. else if ($post_visibility == ITEM_UNAPPROVED)
  1586. {
  1587. $sql_data[FORUMS_TABLE]['stat'][] = 'forum_topics_unapproved = forum_topics_unapproved + 1';
  1588. $sql_data[FORUMS_TABLE]['stat'][] = 'forum_posts_unapproved = forum_posts_unapproved + 1';
  1589. }
  1590. else if ($post_visibility == ITEM_DELETED)
  1591. {
  1592. $sql_data[FORUMS_TABLE]['stat'][] = 'forum_topics_softdeleted = forum_topics_softdeleted + 1';
  1593. $sql_data[FORUMS_TABLE]['stat'][] = 'forum_posts_softdeleted = forum_posts_softdeleted + 1';
  1594. }
  1595. break;
  1596. case 'reply':
  1597. $sql_data[TOPICS_TABLE]['stat'][] = 'topic_last_view_time = ' . $current_time . ',
  1598. topic_bumped = 0,
  1599. topic_bumper = 0' .
  1600. (($post_visibility == ITEM_APPROVED) ? ', topic_posts_approved = topic_posts_approved + 1' : '') .
  1601. (($post_visibility == ITEM_UNAPPROVED) ? ', topic_posts_unapproved = topic_posts_unapproved + 1' : '') .
  1602. (($post_visibility == ITEM_DELETED) ? ', topic_posts_softdeleted = topic_posts_softdeleted + 1' : '') .
  1603. ((!empty($data_ary['attachment_data']) || (isset($data_ary['topic_attachment']) && $data_ary['topic_attachment'])) ? ', topic_attachment = 1' : '');
  1604. $sql_data[USERS_TABLE]['stat'][] = "user_lastpost_time = $current_time" . (($auth->acl_get('f_postcount', $data_ary['forum_id']) && $post_visibility == ITEM_APPROVED) ? ', user_posts = user_posts + 1' : '');
  1605. if ($post_visibility == ITEM_APPROVED)
  1606. {
  1607. $sql_data[FORUMS_TABLE]['stat'][] = 'forum_posts_approved = forum_posts_approved + 1';
  1608. }
  1609. else if ($post_visibility == ITEM_UNAPPROVED)
  1610. {
  1611. $sql_data[FORUMS_TABLE]['stat'][] = 'forum_posts_unapproved = forum_posts_unapproved + 1';
  1612. }
  1613. else if ($post_visibility == ITEM_DELETED)
  1614. {
  1615. $sql_data[FORUMS_TABLE]['stat'][] = 'forum_posts_softdeleted = forum_posts_softdeleted + 1';
  1616. }
  1617. break;
  1618. case 'edit_topic':
  1619. case 'edit_first_post':
  1620. if (isset($poll_ary['poll_options']))
  1621. {
  1622. $poll_start = ($poll_ary['poll_start'] || empty($poll_ary['poll_options'])) ? $poll_ary['poll_start'] : $current_time;
  1623. $poll_length = $poll_ary['poll_length'] * 86400;
  1624. if ($poll_length < 0)
  1625. {
  1626. $poll_start = $poll_start + $poll_length;
  1627. if ($poll_start < 0)
  1628. {
  1629. $poll_start = 0;
  1630. }
  1631. $poll_length = 1;
  1632. }
  1633. }
  1634. $sql_data[TOPICS_TABLE]['sql'] = array(
  1635. 'forum_id' => $data_ary['forum_id'],
  1636. 'icon_id' => $data_ary['icon_id'],
  1637. 'topic_title' => $subject,
  1638. 'topic_first_poster_name' => $username,
  1639. 'topic_type' => $topic_type,
  1640. 'topic_time_limit' => $topic_type != POST_NORMAL ? ($data_ary['topic_time_limit'] * 86400) : 0,
  1641. 'poll_title' => (isset($poll_ary['poll_options'])) ? $poll_ary['poll_title'] : '',
  1642. 'poll_start' => (isset($poll_ary['poll_options'])) ? $poll_start : 0,
  1643. 'poll_max_options' => (isset($poll_ary['poll_options'])) ? $poll_ary['poll_max_options'] : 1,
  1644. 'poll_length' => (isset($poll_ary['poll_options'])) ? $poll_length : 0,
  1645. 'poll_vote_change' => (isset($poll_ary['poll_vote_change'])) ? $poll_ary['poll_vote_change'] : 0,
  1646. 'topic_last_view_time' => $current_time,
  1647. 'topic_attachment' => (!empty($data_ary['attachment_data'])) ? 1 : (isset($data_ary['topic_attachment']) ? $data_ary['topic_attachment'] : 0),
  1648. );
  1649. break;
  1650. }
  1651. $poll = $poll_ary;
  1652. $data = $data_ary;
  1653. /**
  1654. * Modify sql query data for post submitting
  1655. *
  1656. * @event core.submit_post_modify_sql_data
  1657. * @var array data Array with the data for the post
  1658. * @var array poll Array with the poll data for the post
  1659. * @var string post_mode Variable containing posting mode value
  1660. * @var bool sql_data Array with the data for the posting SQL query
  1661. * @var string subject Variable containing post subject value
  1662. * @var int topic_type Variable containing topic type value
  1663. * @var string username Variable containing post author name
  1664. * @since 3.1.3-RC1
  1665. */
  1666. $vars = array(
  1667. 'data',
  1668. 'poll',
  1669. 'post_mode',
  1670. 'sql_data',
  1671. 'subject',
  1672. 'topic_type',
  1673. 'username',
  1674. );
  1675. extract($phpbb_dispatcher->trigger_event('core.submit_post_modify_sql_data', compact($vars)));
  1676. $poll_ary = $poll;
  1677. $data_ary = $data;
  1678. unset($poll);
  1679. unset($data);
  1680. // Submit new topic
  1681. if ($post_mode == 'post')
  1682. {
  1683. $sql = 'INSERT INTO ' . TOPICS_TABLE . ' ' .
  1684. $db->sql_build_array('INSERT', $sql_data[TOPICS_TABLE]['sql']);
  1685. $db->sql_query($sql);
  1686. $data_ary['topic_id'] = $db->sql_nextid();
  1687. $sql_data[POSTS_TABLE]['sql'] = array_merge($sql_data[POSTS_TABLE]['sql'], array(
  1688. 'topic_id' => $data_ary['topic_id'])
  1689. );
  1690. unset($sql_data[TOPICS_TABLE]['sql']);
  1691. }
  1692. // Submit new post
  1693. if ($post_mode == 'post' || $post_mode == 'reply')
  1694. {
  1695. if ($post_mode == 'reply')
  1696. {
  1697. $sql_data[POSTS_TABLE]['sql'] = array_merge($sql_data[POSTS_TABLE]['sql'], array(
  1698. 'topic_id' => $data_ary['topic_id'],
  1699. ));
  1700. }
  1701. $sql = 'INSERT INTO ' . POSTS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_data[POSTS_TABLE]['sql']);
  1702. $db->sql_query($sql);
  1703. $data_ary['post_id'] = $db->sql_nextid();
  1704. if ($post_mode == 'post' || $post_visibility == ITEM_APPROVED)
  1705. {
  1706. $sql_data[TOPICS_TABLE]['sql'] = array(
  1707. 'topic_last_post_id' => $data_ary['post_id'],
  1708. 'topic_last_post_time' => $current_time,
  1709. 'topic_last_poster_id' => $sql_data[POSTS_TABLE]['sql']['poster_id'],
  1710. 'topic_last_poster_name' => ($user->data['user_id'] == ANONYMOUS) ? $sql_data[POSTS_TABLE]['sql']['post_username'] : $user->data['username'],
  1711. 'topic_last_poster_colour' => $user->data['user_colour'],
  1712. 'topic_last_post_subject' => (string) $subject,
  1713. );
  1714. }
  1715. if ($post_mode == 'post')
  1716. {
  1717. $sql_data[TOPICS_TABLE]['sql']['topic_first_post_id'] = $data_ary['post_id'];
  1718. }
  1719. // Update total post count and forum information
  1720. if ($post_visibility == ITEM_APPROVED)
  1721. {
  1722. if ($post_mode == 'post')
  1723. {
  1724. $config->increment('num_topics', 1, false);
  1725. }
  1726. $config->increment('num_posts', 1, false);
  1727. $sql_data[FORUMS_TABLE]['stat'][] = 'forum_last_post_id = ' . $data_ary['post_id'];
  1728. $sql_data[FORUMS_TABLE]['stat'][] = "forum_last_post_subject = '" . $db->sql_escape($subject) . "'";
  1729. $sql_data[FORUMS_TABLE]['stat'][] = 'forum_last_post_time = ' . $current_time;
  1730. $sql_data[FORUMS_TABLE]['stat'][] = 'forum_last_poster_id = ' . (int) $user->data['user_id'];
  1731. $sql_data[FORUMS_TABLE]['stat'][] = "forum_last_poster_name = '" . $db->sql_escape((!$user->data['is_registered'] && $username) ? $username : (($user->data['user_id'] != ANONYMOUS) ? $user->data['username'] : '')) . "'";
  1732. $sql_data[FORUMS_TABLE]['stat'][] = "forum_last_poster_colour = '" . $db->sql_escape($user->data['user_colour']) . "'";
  1733. }
  1734. unset($sql_data[POSTS_TABLE]['sql']);
  1735. }
  1736. // Update the topics table
  1737. if (isset($sql_data[TOPICS_TABLE]['sql']))
  1738. {
  1739. $sql = 'UPDATE ' . TOPICS_TABLE . '
  1740. SET ' . $db->sql_build_array('UPDATE', $sql_data[TOPICS_TABLE]['sql']) . '
  1741. WHERE topic_id = ' . $data_ary['topic_id'];
  1742. $db->sql_query($sql);
  1743. unset($sql_data[TOPICS_TABLE]['sql']);
  1744. }
  1745. // Update the posts table
  1746. if (isset($sql_data[POSTS_TABLE]['sql']))
  1747. {
  1748. $sql = 'UPDATE ' . POSTS_TABLE . '
  1749. SET ' . $db->sql_build_array('UPDATE', $sql_data[POSTS_TABLE]['sql']) . '
  1750. WHERE post_id = ' . $data_ary['post_id'];
  1751. $db->sql_query($sql);
  1752. unset($sql_data[POSTS_TABLE]['sql']);
  1753. }
  1754. // Update Poll Tables
  1755. if (isset($poll_ary['poll_options']))
  1756. {
  1757. $cur_poll_options = array();
  1758. if ($mode == 'edit')
  1759. {
  1760. $sql = 'SELECT *
  1761. FROM ' . POLL_OPTIONS_TABLE . '
  1762. WHERE topic_id = ' . $data_ary['topic_id'] . '
  1763. ORDER BY poll_option_id';
  1764. $result = $db->sql_query($sql);
  1765. $cur_poll_options = array();
  1766. while ($row = $db->sql_fetchrow($result))
  1767. {
  1768. $cur_poll_options[] = $row;
  1769. }
  1770. $db->sql_freeresult($result);
  1771. }
  1772. $sql_insert_ary = array();
  1773. for ($i = 0, $size = count($poll_ary['poll_options']); $i < $size; $i++)
  1774. {
  1775. if (strlen(trim($poll_ary['poll_options'][$i])))
  1776. {
  1777. if (empty($cur_poll_options[$i]))
  1778. {
  1779. // If we add options we need to put them to the end to be able to preserve votes...
  1780. $sql_insert_ary[] = array(
  1781. 'poll_option_id' => (int) count($cur_poll_options) + 1 + count($sql_insert_ary),
  1782. 'topic_id' => (int) $data_ary['topic_id'],
  1783. 'poll_option_text' => (string) $poll_ary['poll_options'][$i]
  1784. );
  1785. }
  1786. else if ($poll_ary['poll_options'][$i] != $cur_poll_options[$i])
  1787. {
  1788. $sql = 'UPDATE ' . POLL_OPTIONS_TABLE . "
  1789. SET poll_option_text = '" . $db->sql_escape($poll_ary['poll_options'][$i]) . "'
  1790. WHERE poll_option_id = " . $cur_poll_options[$i]['poll_option_id'] . '
  1791. AND topic_id = ' . $data_ary['topic_id'];
  1792. $db->sql_query($sql);
  1793. }
  1794. }
  1795. }
  1796. $db->sql_multi_insert(POLL_OPTIONS_TABLE, $sql_insert_ary);
  1797. if (count($poll_ary['poll_options']) < count($cur_poll_options))
  1798. {
  1799. $sql = 'DELETE FROM ' . POLL_OPTIONS_TABLE . '
  1800. WHERE poll_option_id > ' . count($poll_ary['poll_options']) . '
  1801. AND topic_id = ' . $data_ary['topic_id'];
  1802. $db->sql_query($sql);
  1803. }
  1804. // If edited, we would need to reset votes (since options can be re-ordered above, you can't be sure if the change is for changing the text or adding an option
  1805. if ($mode == 'edit' && count($poll_ary['poll_options']) != count($cur_poll_options))
  1806. {
  1807. $db->sql_query('DELETE FROM ' . POLL_VOTES_TABLE . ' WHERE topic_id = ' . $data_ary['topic_id']);
  1808. $db->sql_query('UPDATE ' . POLL_OPTIONS_TABLE . ' SET poll_option_total = 0 WHERE topic_id = ' . $data_ary['topic_id']);
  1809. }
  1810. }
  1811. // Submit Attachments
  1812. if (!empty($data_ary['attachment_data']) && $data_ary['post_id'] && in_array($mode, array('post', 'reply', 'quote', 'edit')))
  1813. {
  1814. $space_taken = $files_added = 0;
  1815. $orphan_rows = array();
  1816. foreach ($data_ary['attachment_data'] as $pos => $attach_row)
  1817. {
  1818. $orphan_rows[(int) $attach_row['attach_id']] = array();
  1819. }
  1820. if (count($orphan_rows))
  1821. {
  1822. $sql = 'SELECT attach_id, filesize, physical_filename
  1823. FROM ' . ATTACHMENTS_TABLE . '
  1824. WHERE ' . $db->sql_in_set('attach_id', array_keys($orphan_rows)) . '
  1825. AND is_orphan = 1
  1826. AND poster_id = ' . $user->data['user_id'];
  1827. $result = $db->sql_query($sql);
  1828. $orphan_rows = array();
  1829. while ($row = $db->sql_fetchrow($result))
  1830. {
  1831. $orphan_rows[$row['attach_id']] = $row;
  1832. }
  1833. $db->sql_freeresult($result);
  1834. }
  1835. foreach ($data_ary['attachment_data'] as $pos => $attach_row)
  1836. {
  1837. if ($attach_row['is_orphan'] && !isset($orphan_rows[$attach_row['attach_id']]))
  1838. {
  1839. continue;
  1840. }
  1841. if (preg_match('/[\x{10000}-\x{10FFFF}]/u', $attach_row['attach_comment']))
  1842. {
  1843. trigger_error('ATTACH_COMMENT_NO_EMOJIS');
  1844. }
  1845. if (!$attach_row['is_orphan'])
  1846. {
  1847. // update entry in db if attachment already stored in db and filespace
  1848. $sql = 'UPDATE ' . ATTACHMENTS_TABLE . "
  1849. SET attach_comment = '" . $db->sql_escape($attach_row['attach_comment']) . "'
  1850. WHERE attach_id = " . (int) $attach_row['attach_id'] . '
  1851. AND is_orphan = 0';
  1852. $db->sql_query($sql);
  1853. }
  1854. else
  1855. {
  1856. // insert attachment into db
  1857. if (!$attachment_storage->exists(utf8_basename($orphan_rows[$attach_row['attach_id']]['physical_filename'])))
  1858. {
  1859. continue;
  1860. }
  1861. $space_taken += $orphan_rows[$attach_row['attach_id']]['filesize'];
  1862. $files_added++;
  1863. $attach_sql = array(
  1864. 'post_msg_id' => $data_ary['post_id'],
  1865. 'topic_id' => $data_ary['topic_id'],
  1866. 'is_orphan' => 0,
  1867. 'poster_id' => $poster_id,
  1868. 'attach_comment' => $attach_row['attach_comment'],
  1869. );
  1870. $sql = 'UPDATE ' . ATTACHMENTS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $attach_sql) . '
  1871. WHERE attach_id = ' . $attach_row['attach_id'] . '
  1872. AND is_orphan = 1
  1873. AND poster_id = ' . $user->data['user_id'];
  1874. $db->sql_query($sql);
  1875. }
  1876. }
  1877. if ($space_taken && $files_added)
  1878. {
  1879. $config->increment('upload_dir_size', $space_taken, false);
  1880. $config->increment('num_files', $files_added, false);
  1881. }
  1882. }
  1883. $first_post_has_topic_info = ($post_mode == 'edit_first_post' &&
  1884. (($post_visibility == ITEM_DELETED && $data_ary['topic_posts_softdeleted'] == 1) ||
  1885. ($post_visibility == ITEM_UNAPPROVED && $data_ary['topic_posts_unapproved'] == 1) ||
  1886. ($post_visibility == ITEM_REAPPROVE && $data_ary['topic_posts_unapproved'] == 1) ||
  1887. ($post_visibility == ITEM_APPROVED && $data_ary['topic_posts_approved'] == 1)));
  1888. // Fix the post's and topic's visibility and first/last post information, when the post is edited
  1889. if (($post_mode != 'post' && $post_mode != 'reply') && $data_ary['post_visibility'] != $post_visibility)
  1890. {
  1891. // If the post was not approved, it could also be the starter,
  1892. // so we sync the starter after approving/restoring, to ensure that the stats are correct
  1893. // Same applies for the last post
  1894. $is_starter = ($post_mode == 'edit_first_post' || $post_mode == 'edit_topic' || $data_ary['post_visibility'] != ITEM_APPROVED);
  1895. $is_latest = ($post_mode == 'edit_last_post' || $post_mode == 'edit_topic' || $data_ary['post_visibility'] != ITEM_APPROVED);
  1896. /* @var $phpbb_content_visibility \phpbb\content_visibility */
  1897. $phpbb_content_visibility = $phpbb_container->get('content.visibility');
  1898. $phpbb_content_visibility->set_post_visibility($post_visibility, $data_ary['post_id'], $data_ary['topic_id'], $data_ary['forum_id'], $user->data['user_id'], time(), '', $is_starter, $is_latest);
  1899. }
  1900. else if ($post_mode == 'edit_last_post' || $post_mode == 'edit_topic' || $first_post_has_topic_info)
  1901. {
  1902. if ($post_visibility == ITEM_APPROVED || $data_ary['topic_visibility'] == $post_visibility)
  1903. {
  1904. // only the subject can be changed from edit
  1905. $sql_data[TOPICS_TABLE]['stat'][] = "topic_last_post_subject = '" . $db->sql_escape($subject) . "'";
  1906. // Maybe not only the subject, but also changing anonymous usernames. ;)
  1907. if ($data_ary['poster_id'] == ANONYMOUS)
  1908. {
  1909. $sql_data[TOPICS_TABLE]['stat'][] = "topic_last_poster_name = '" . $db->sql_escape($username) . "'";
  1910. }
  1911. if ($post_visibility == ITEM_APPROVED)
  1912. {
  1913. // this does not _necessarily_ mean that we must update the info again,
  1914. // it just means that we might have to
  1915. $sql = 'SELECT forum_last_post_id, forum_last_post_subject
  1916. FROM ' . FORUMS_TABLE . '
  1917. WHERE forum_id = ' . (int) $data_ary['forum_id'];
  1918. $result = $db->sql_query($sql);
  1919. $row = $db->sql_fetchrow($result);
  1920. $db->sql_freeresult($result);
  1921. // this post is the latest post in the forum, better update
  1922. if ($row['forum_last_post_id'] == $data_ary['post_id'] && ($row['forum_last_post_subject'] !== $subject || $data_ary['poster_id'] == ANONYMOUS))
  1923. {
  1924. // the post's subject changed
  1925. if ($row['forum_last_post_subject'] !== $subject)
  1926. {
  1927. $sql_data[FORUMS_TABLE]['stat'][] = "forum_last_post_subject = '" . $db->sql_escape($subject) . "'";
  1928. }
  1929. // Update the user name if poster is anonymous... just in case a moderator changed it
  1930. if ($data_ary['poster_id'] == ANONYMOUS)
  1931. {
  1932. $sql_data[FORUMS_TABLE]['stat'][] = "forum_last_poster_name = '" . $db->sql_escape($username) . "'";
  1933. }
  1934. }
  1935. }
  1936. }
  1937. }
  1938. // Update forum stats
  1939. $where_sql = array(
  1940. POSTS_TABLE => 'post_id = ' . $data_ary['post_id'],
  1941. TOPICS_TABLE => 'topic_id = ' . $data_ary['topic_id'],
  1942. FORUMS_TABLE => 'forum_id = ' . $data_ary['forum_id'],
  1943. USERS_TABLE => 'user_id = ' . $poster_id
  1944. );
  1945. foreach ($sql_data as $table => $update_ary)
  1946. {
  1947. if (isset($update_ary['stat']) && implode('', $update_ary['stat']))
  1948. {
  1949. $sql = "UPDATE $table SET " . implode(', ', $update_ary['stat']) . ' WHERE ' . $where_sql[$table];
  1950. $db->sql_query($sql);
  1951. }
  1952. }
  1953. // Delete topic shadows (if any exist). We do not need a shadow topic for an global announcement
  1954. if ($topic_type == POST_GLOBAL)
  1955. {
  1956. $sql = 'DELETE FROM ' . TOPICS_TABLE . '
  1957. WHERE topic_moved_id = ' . $data_ary['topic_id'];
  1958. $db->sql_query($sql);
  1959. }
  1960. // Committing the transaction before updating search index
  1961. $db->sql_transaction('commit');
  1962. // Delete draft if post was loaded...
  1963. $draft_id = $request->variable('draft_loaded', 0);
  1964. if ($draft_id)
  1965. {
  1966. $sql = 'DELETE FROM ' . DRAFTS_TABLE . "
  1967. WHERE draft_id = $draft_id
  1968. AND user_id = {$user->data['user_id']}";
  1969. $db->sql_query($sql);
  1970. }
  1971. // Index message contents
  1972. if ($update_search_index && $data_ary['enable_indexing'])
  1973. {
  1974. // Select the search method and do some additional checks to ensure it can actually be utilised
  1975. $search_type = $config['search_type'];
  1976. if (!class_exists($search_type))
  1977. {
  1978. trigger_error('NO_SUCH_SEARCH_MODULE');
  1979. }
  1980. $error = false;
  1981. $search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher);
  1982. if ($error)
  1983. {
  1984. trigger_error($error);
  1985. }
  1986. $search->index($mode, $data_ary['post_id'], $data_ary['message'], $subject, $poster_id, $data_ary['forum_id']);
  1987. }
  1988. // Topic Notification, do not change if moderator is changing other users posts...
  1989. if ($user->data['user_id'] == $poster_id)
  1990. {
  1991. if (!$data_ary['notify_set'] && $data_ary['notify'])
  1992. {
  1993. $sql = 'INSERT INTO ' . TOPICS_WATCH_TABLE . ' (user_id, topic_id)
  1994. VALUES (' . $user->data['user_id'] . ', ' . $data_ary['topic_id'] . ')';
  1995. $db->sql_query($sql);
  1996. }
  1997. else if (($config['email_enable'] || $config['jab_enable']) && $data_ary['notify_set'] && !$data_ary['notify'])
  1998. {
  1999. $sql = 'DELETE FROM ' . TOPICS_WATCH_TABLE . '
  2000. WHERE user_id = ' . $user->data['user_id'] . '
  2001. AND topic_id = ' . $data_ary['topic_id'];
  2002. $db->sql_query($sql);
  2003. }
  2004. }
  2005. if ($mode == 'post' || $mode == 'reply' || $mode == 'quote')
  2006. {
  2007. // Mark this topic as posted to
  2008. markread('post', $data_ary['forum_id'], $data_ary['topic_id']);
  2009. }
  2010. // Mark this topic as read
  2011. // We do not use post_time here, this is intended (post_time can have a date in the past if editing a message)
  2012. markread('topic', $data_ary['forum_id'], $data_ary['topic_id'], time());
  2013. //
  2014. if ($config['load_db_lastread'] && $user->data['is_registered'])
  2015. {
  2016. $sql = 'SELECT mark_time
  2017. FROM ' . FORUMS_TRACK_TABLE . '
  2018. WHERE user_id = ' . $user->data['user_id'] . '
  2019. AND forum_id = ' . $data_ary['forum_id'];
  2020. $result = $db->sql_query($sql);
  2021. $f_mark_time = (int) $db->sql_fetchfield('mark_time');
  2022. $db->sql_freeresult($result);
  2023. }
  2024. else if ($config['load_anon_lastread'] || $user->data['is_registered'])
  2025. {
  2026. $f_mark_time = false;
  2027. }
  2028. if (($config['load_db_lastread'] && $user->data['is_registered']) || $config['load_anon_lastread'] || $user->data['is_registered'])
  2029. {
  2030. // Update forum info
  2031. $sql = 'SELECT forum_last_post_time
  2032. FROM ' . FORUMS_TABLE . '
  2033. WHERE forum_id = ' . $data_ary['forum_id'];
  2034. $result = $db->sql_query($sql);
  2035. $forum_last_post_time = (int) $db->sql_fetchfield('forum_last_post_time');
  2036. $db->sql_freeresult($result);
  2037. update_forum_tracking_info($data_ary['forum_id'], $forum_last_post_time, $f_mark_time, false);
  2038. }
  2039. // If a username was supplied or the poster is a guest, we will use the supplied username.
  2040. // Doing it this way we can use "...post by guest-username..." in notifications when
  2041. // "guest-username" is supplied or ommit the username if it is not.
  2042. $username = ($username !== '' || !$user->data['is_registered']) ? $username : $user->data['username'];
  2043. // Send Notifications
  2044. $notification_data = array_merge($data_ary, array(
  2045. 'topic_title' => (isset($data_ary['topic_title'])) ? $data_ary['topic_title'] : $subject,
  2046. 'post_username' => $username,
  2047. 'poster_id' => $poster_id,
  2048. 'post_text' => $data_ary['message'],
  2049. 'post_time' => $current_time,
  2050. 'post_subject' => $subject,
  2051. ));
  2052. /**
  2053. * This event allows you to modify the notification data upon submission
  2054. *
  2055. * @event core.modify_submit_notification_data
  2056. * @var array notification_data The notification data to be inserted in to the database
  2057. * @var array data_ary The data array with a lot of the post submission data
  2058. * @var string mode The posting mode
  2059. * @var int poster_id The poster id
  2060. * @since 3.2.4-RC1
  2061. */
  2062. $vars = array('notification_data', 'data_ary', 'mode', 'poster_id');
  2063. extract($phpbb_dispatcher->trigger_event('core.modify_submit_notification_data', compact($vars)));
  2064. /* @var $phpbb_notifications \phpbb\notification\manager */
  2065. $phpbb_notifications = $phpbb_container->get('notification_manager');
  2066. if ($post_visibility == ITEM_APPROVED)
  2067. {
  2068. switch ($mode)
  2069. {
  2070. case 'post':
  2071. $phpbb_notifications->add_notifications(array(
  2072. 'notification.type.quote',
  2073. 'notification.type.topic',
  2074. ), $notification_data);
  2075. break;
  2076. case 'reply':
  2077. case 'quote':
  2078. $phpbb_notifications->add_notifications(array(
  2079. 'notification.type.quote',
  2080. 'notification.type.bookmark',
  2081. 'notification.type.post',
  2082. ), $notification_data);
  2083. break;
  2084. case 'edit_topic':
  2085. case 'edit_first_post':
  2086. case 'edit':
  2087. case 'edit_last_post':
  2088. if ($user->data['user_id'] == $poster_id)
  2089. {
  2090. $phpbb_notifications->update_notifications(array(
  2091. 'notification.type.quote',
  2092. ), $notification_data);
  2093. }
  2094. $phpbb_notifications->update_notifications(array(
  2095. 'notification.type.bookmark',
  2096. 'notification.type.topic',
  2097. 'notification.type.post',
  2098. ), $notification_data);
  2099. break;
  2100. }
  2101. }
  2102. else if ($post_visibility == ITEM_UNAPPROVED)
  2103. {
  2104. switch ($mode)
  2105. {
  2106. case 'post':
  2107. $phpbb_notifications->add_notifications('notification.type.topic_in_queue', $notification_data);
  2108. break;
  2109. case 'reply':
  2110. case 'quote':
  2111. $phpbb_notifications->add_notifications('notification.type.post_in_queue', $notification_data);
  2112. break;
  2113. case 'edit_topic':
  2114. case 'edit_first_post':
  2115. case 'edit':
  2116. case 'edit_last_post':
  2117. // Nothing to do here
  2118. break;
  2119. }
  2120. }
  2121. else if ($post_visibility == ITEM_REAPPROVE)
  2122. {
  2123. switch ($mode)
  2124. {
  2125. case 'edit_topic':
  2126. case 'edit_first_post':
  2127. $phpbb_notifications->add_notifications('notification.type.topic_in_queue', $notification_data);
  2128. // Delete the approve_post notification so we can notify the user again,
  2129. // when his post got reapproved
  2130. $phpbb_notifications->delete_notifications('notification.type.approve_post', $notification_data['post_id']);
  2131. break;
  2132. case 'edit':
  2133. case 'edit_last_post':
  2134. $phpbb_notifications->add_notifications('notification.type.post_in_queue', $notification_data);
  2135. // Delete the approve_post notification so we can notify the user again,
  2136. // when his post got reapproved
  2137. $phpbb_notifications->delete_notifications('notification.type.approve_post', $notification_data['post_id']);
  2138. break;
  2139. case 'post':
  2140. case 'reply':
  2141. case 'quote':
  2142. // Nothing to do here
  2143. break;
  2144. }
  2145. }
  2146. else if ($post_visibility == ITEM_DELETED)
  2147. {
  2148. switch ($mode)
  2149. {
  2150. case 'post':
  2151. case 'reply':
  2152. case 'quote':
  2153. case 'edit_topic':
  2154. case 'edit_first_post':
  2155. case 'edit':
  2156. case 'edit_last_post':
  2157. // Nothing to do here
  2158. break;
  2159. }
  2160. }
  2161. $params = $add_anchor = '';
  2162. if ($post_visibility == ITEM_APPROVED ||
  2163. ($auth->acl_get('m_softdelete', $data_ary['forum_id']) && $post_visibility == ITEM_DELETED) ||
  2164. ($auth->acl_get('m_approve', $data_ary['forum_id']) && in_array($post_visibility, array(ITEM_UNAPPROVED, ITEM_REAPPROVE))))
  2165. {
  2166. $params .= '&amp;t=' . $data_ary['topic_id'];
  2167. if ($mode != 'post')
  2168. {
  2169. $params .= '&amp;p=' . $data_ary['post_id'];
  2170. $add_anchor = '#p' . $data_ary['post_id'];
  2171. }
  2172. }
  2173. else if ($mode != 'post' && $post_mode != 'edit_first_post' && $post_mode != 'edit_topic')
  2174. {
  2175. $params .= '&amp;t=' . $data_ary['topic_id'];
  2176. }
  2177. $url = (!$params) ? "{$phpbb_root_path}viewforum.$phpEx" : "{$phpbb_root_path}viewtopic.$phpEx";
  2178. $url = append_sid($url, 'f=' . $data_ary['forum_id'] . $params) . $add_anchor;
  2179. $poll = $poll_ary;
  2180. $data = $data_ary;
  2181. /**
  2182. * This event is used for performing actions directly after a post or topic
  2183. * has been submitted. When a new topic is posted, the topic ID is
  2184. * available in the $data array.
  2185. *
  2186. * The only action that can be done by altering data made available to this
  2187. * event is to modify the return URL ($url).
  2188. *
  2189. * @event core.submit_post_end
  2190. * @var string mode Variable containing posting mode value
  2191. * @var string subject Variable containing post subject value
  2192. * @var string username Variable containing post author name
  2193. * @var int topic_type Variable containing topic type value
  2194. * @var array poll Array with the poll data for the post
  2195. * @var array data Array with the data for the post
  2196. * @var int post_visibility Variable containing up to date post visibility
  2197. * @var bool update_message Flag indicating if the post will be updated
  2198. * @var bool update_search_index Flag indicating if the search index will be updated
  2199. * @var string url The "Return to topic" URL
  2200. *
  2201. * @since 3.1.0-a3
  2202. * @changed 3.1.0-RC3 Added vars mode, subject, username, topic_type,
  2203. * poll, update_message, update_search_index
  2204. */
  2205. $vars = array(
  2206. 'mode',
  2207. 'subject',
  2208. 'username',
  2209. 'topic_type',
  2210. 'poll',
  2211. 'data',
  2212. 'post_visibility',
  2213. 'update_message',
  2214. 'update_search_index',
  2215. 'url',
  2216. );
  2217. extract($phpbb_dispatcher->trigger_event('core.submit_post_end', compact($vars)));
  2218. $data_ary = $data;
  2219. $poll_ary = $poll;
  2220. unset($data);
  2221. unset($poll);
  2222. return $url;
  2223. }
  2224. /**
  2225. * Handle topic bumping
  2226. * @param int $forum_id The ID of the forum the topic is being bumped belongs to
  2227. * @param int $topic_id The ID of the topic is being bumping
  2228. * @param array $post_data Passes some topic parameters:
  2229. * - 'topic_title'
  2230. * - 'topic_last_post_id'
  2231. * - 'topic_last_poster_id'
  2232. * - 'topic_last_post_subject'
  2233. * - 'topic_last_poster_name'
  2234. * - 'topic_last_poster_colour'
  2235. * @param int $bump_time The time at which topic was bumped, usually it is a current time as obtained via time().
  2236. * @return string An URL to the bumped topic, example: ./viewtopic.php?forum_id=1&amptopic_id=2&ampp=3#p3
  2237. */
  2238. function phpbb_bump_topic($forum_id, $topic_id, $post_data, $bump_time = false)
  2239. {
  2240. global $config, $db, $user, $phpEx, $phpbb_root_path, $phpbb_log;
  2241. if ($bump_time === false)
  2242. {
  2243. $bump_time = time();
  2244. }
  2245. // Begin bumping
  2246. $db->sql_transaction('begin');
  2247. // Update the topic's last post post_time
  2248. $sql = 'UPDATE ' . POSTS_TABLE . "
  2249. SET post_time = $bump_time
  2250. WHERE post_id = {$post_data['topic_last_post_id']}
  2251. AND topic_id = $topic_id";
  2252. $db->sql_query($sql);
  2253. // Sync the topic's last post time, the rest of the topic's last post data isn't changed
  2254. $sql = 'UPDATE ' . TOPICS_TABLE . "
  2255. SET topic_last_post_time = $bump_time,
  2256. topic_bumped = 1,
  2257. topic_bumper = " . $user->data['user_id'] . "
  2258. WHERE topic_id = $topic_id";
  2259. $db->sql_query($sql);
  2260. // Update the forum's last post info
  2261. $sql = 'UPDATE ' . FORUMS_TABLE . "
  2262. SET forum_last_post_id = " . $post_data['topic_last_post_id'] . ",
  2263. forum_last_poster_id = " . $post_data['topic_last_poster_id'] . ",
  2264. forum_last_post_subject = '" . $db->sql_escape($post_data['topic_last_post_subject']) . "',
  2265. forum_last_post_time = $bump_time,
  2266. forum_last_poster_name = '" . $db->sql_escape($post_data['topic_last_poster_name']) . "',
  2267. forum_last_poster_colour = '" . $db->sql_escape($post_data['topic_last_poster_colour']) . "'
  2268. WHERE forum_id = $forum_id";
  2269. $db->sql_query($sql);
  2270. // Update bumper's time of the last posting to prevent flood
  2271. $sql = 'UPDATE ' . USERS_TABLE . "
  2272. SET user_lastpost_time = $bump_time
  2273. WHERE user_id = " . $user->data['user_id'];
  2274. $db->sql_query($sql);
  2275. $db->sql_transaction('commit');
  2276. // Mark this topic as posted to
  2277. markread('post', $forum_id, $topic_id, $bump_time);
  2278. // Mark this topic as read
  2279. markread('topic', $forum_id, $topic_id, $bump_time);
  2280. // Update forum tracking info
  2281. if ($config['load_db_lastread'] && $user->data['is_registered'])
  2282. {
  2283. $sql = 'SELECT mark_time
  2284. FROM ' . FORUMS_TRACK_TABLE . '
  2285. WHERE user_id = ' . $user->data['user_id'] . '
  2286. AND forum_id = ' . $forum_id;
  2287. $result = $db->sql_query($sql);
  2288. $f_mark_time = (int) $db->sql_fetchfield('mark_time');
  2289. $db->sql_freeresult($result);
  2290. }
  2291. else if ($config['load_anon_lastread'] || $user->data['is_registered'])
  2292. {
  2293. $f_mark_time = false;
  2294. }
  2295. if (($config['load_db_lastread'] && $user->data['is_registered']) || $config['load_anon_lastread'] || $user->data['is_registered'])
  2296. {
  2297. // Update forum info
  2298. $sql = 'SELECT forum_last_post_time
  2299. FROM ' . FORUMS_TABLE . '
  2300. WHERE forum_id = ' . $forum_id;
  2301. $result = $db->sql_query($sql);
  2302. $forum_last_post_time = (int) $db->sql_fetchfield('forum_last_post_time');
  2303. $db->sql_freeresult($result);
  2304. update_forum_tracking_info($forum_id, $forum_last_post_time, $f_mark_time, false);
  2305. }
  2306. $phpbb_log->add('mod', $user->data['user_id'], $user->ip, 'LOG_BUMP_TOPIC', false, array(
  2307. 'forum_id' => $forum_id,
  2308. 'topic_id' => $topic_id,
  2309. $post_data['topic_title']
  2310. ));
  2311. $url = append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&amp;t=$topic_id&amp;p={$post_data['topic_last_post_id']}") . "#p{$post_data['topic_last_post_id']}";
  2312. return $url;
  2313. }
  2314. /**
  2315. * Show upload popup (progress bar)
  2316. */
  2317. function phpbb_upload_popup($forum_style = 0)
  2318. {
  2319. global $template, $user;
  2320. ($forum_style) ? $user->setup('posting', $forum_style) : $user->setup('posting');
  2321. page_header($user->lang['PROGRESS_BAR']);
  2322. $template->set_filenames(array(
  2323. 'popup' => 'posting_progress_bar.html')
  2324. );
  2325. $template->assign_vars(array(
  2326. 'PROGRESS_BAR' => $user->img('upload_bar', $user->lang['UPLOAD_IN_PROGRESS']))
  2327. );
  2328. $template->display('popup');
  2329. garbage_collection();
  2330. exit_handler();
  2331. }
  2332. /**
  2333. * Do the various checks required for removing posts as well as removing it
  2334. *
  2335. * @param int $forum_id The id of the forum
  2336. * @param int $topic_id The id of the topic
  2337. * @param int $post_id The id of the post
  2338. * @param array $post_data Array with the post data
  2339. * @param bool $is_soft The flag indicating whether it is the soft delete mode
  2340. * @param string $delete_reason Description for the post deletion reason
  2341. *
  2342. * @return null
  2343. */
  2344. function phpbb_handle_post_delete($forum_id, $topic_id, $post_id, &$post_data, $is_soft = false, $delete_reason = '')
  2345. {
  2346. global $user, $auth, $config, $request;
  2347. global $phpbb_root_path, $phpEx, $phpbb_log, $phpbb_dispatcher;
  2348. $force_delete_allowed = $force_softdelete_allowed = false;
  2349. $perm_check = ($is_soft) ? 'softdelete' : 'delete';
  2350. /**
  2351. * This event allows to modify the conditions for the post deletion
  2352. *
  2353. * @event core.handle_post_delete_conditions
  2354. * @var int forum_id The id of the forum
  2355. * @var int topic_id The id of the topic
  2356. * @var int post_id The id of the post
  2357. * @var array post_data Array with the post data
  2358. * @var bool is_soft The flag indicating whether it is the soft delete mode
  2359. * @var string delete_reason Description for the post deletion reason
  2360. * @var bool force_delete_allowed Allow the user to delete the post (all permissions and conditions are ignored)
  2361. * @var bool force_softdelete_allowed Allow the user to softdelete the post (all permissions and conditions are ignored)
  2362. * @var string perm_check The deletion mode softdelete|delete
  2363. * @since 3.1.11-RC1
  2364. */
  2365. $vars = array(
  2366. 'forum_id',
  2367. 'topic_id',
  2368. 'post_id',
  2369. 'post_data',
  2370. 'is_soft',
  2371. 'delete_reason',
  2372. 'force_delete_allowed',
  2373. 'force_softdelete_allowed',
  2374. 'perm_check',
  2375. );
  2376. extract($phpbb_dispatcher->trigger_event('core.handle_post_delete_conditions', compact($vars)));
  2377. // If moderator removing post or user itself removing post, present a confirmation screen
  2378. if ($force_delete_allowed || ($is_soft && $force_softdelete_allowed) || $auth->acl_get("m_$perm_check", $forum_id) || ($post_data['poster_id'] == $user->data['user_id'] && $user->data['is_registered'] && $auth->acl_get("f_$perm_check", $forum_id) && $post_id == $post_data['topic_last_post_id'] && !$post_data['post_edit_locked'] && ($post_data['post_time'] > time() - ($config['delete_time'] * 60) || !$config['delete_time'])))
  2379. {
  2380. $s_hidden_fields = array(
  2381. 'p' => $post_id,
  2382. 'f' => $forum_id,
  2383. 'mode' => ($is_soft) ? 'soft_delete' : 'delete',
  2384. );
  2385. if (confirm_box(true))
  2386. {
  2387. $data = array(
  2388. 'topic_first_post_id' => $post_data['topic_first_post_id'],
  2389. 'topic_last_post_id' => $post_data['topic_last_post_id'],
  2390. 'topic_posts_approved' => $post_data['topic_posts_approved'],
  2391. 'topic_posts_unapproved' => $post_data['topic_posts_unapproved'],
  2392. 'topic_posts_softdeleted' => $post_data['topic_posts_softdeleted'],
  2393. 'topic_visibility' => $post_data['topic_visibility'],
  2394. 'topic_type' => $post_data['topic_type'],
  2395. 'post_visibility' => $post_data['post_visibility'],
  2396. 'post_reported' => $post_data['post_reported'],
  2397. 'post_time' => $post_data['post_time'],
  2398. 'poster_id' => $post_data['poster_id'],
  2399. 'post_postcount' => $post_data['post_postcount'],
  2400. );
  2401. $next_post_id = delete_post($forum_id, $topic_id, $post_id, $data, $is_soft, $delete_reason);
  2402. $post_username = ($post_data['poster_id'] == ANONYMOUS && !empty($post_data['post_username'])) ? $post_data['post_username'] : $post_data['username'];
  2403. if ($next_post_id === false)
  2404. {
  2405. $phpbb_log->add('mod', $user->data['user_id'], $user->ip, (($is_soft) ? 'LOG_SOFTDELETE_TOPIC' : 'LOG_DELETE_TOPIC'), false, array(
  2406. 'forum_id' => $forum_id,
  2407. 'topic_id' => $topic_id,
  2408. $post_data['topic_title'],
  2409. $post_username,
  2410. $delete_reason
  2411. ));
  2412. $meta_info = append_sid("{$phpbb_root_path}viewforum.$phpEx", "f=$forum_id");
  2413. $message = $user->lang['POST_DELETED'];
  2414. }
  2415. else
  2416. {
  2417. $phpbb_log->add('mod', $user->data['user_id'], $user->ip, (($is_soft) ? 'LOG_SOFTDELETE_POST' : 'LOG_DELETE_POST'), false, array(
  2418. 'forum_id' => $forum_id,
  2419. 'topic_id' => $topic_id,
  2420. 'post_id' => $post_id,
  2421. $post_data['post_subject'],
  2422. $post_username,
  2423. $delete_reason
  2424. ));
  2425. $meta_info = append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&amp;t=$topic_id&amp;p=$next_post_id") . "#p$next_post_id";
  2426. $message = $user->lang['POST_DELETED'];
  2427. if (!$request->is_ajax())
  2428. {
  2429. $message .= '<br /><br />' . $user->lang('RETURN_TOPIC', '<a href="' . $meta_info . '">', '</a>');
  2430. }
  2431. }
  2432. meta_refresh(3, $meta_info);
  2433. if (!$request->is_ajax())
  2434. {
  2435. $message .= '<br /><br />' . $user->lang('RETURN_FORUM', '<a href="' . append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $forum_id) . '">', '</a>');
  2436. }
  2437. trigger_error($message);
  2438. }
  2439. else
  2440. {
  2441. global $template;
  2442. $can_delete = $force_delete_allowed || ($auth->acl_get('m_delete', $forum_id) || ($post_data['poster_id'] == $user->data['user_id'] && $user->data['is_registered'] && $auth->acl_get('f_delete', $forum_id)));
  2443. $can_softdelete = $force_softdelete_allowed || ($auth->acl_get('m_softdelete', $forum_id) || ($post_data['poster_id'] == $user->data['user_id'] && $user->data['is_registered'] && $auth->acl_get('f_softdelete', $forum_id)));
  2444. $template->assign_vars(array(
  2445. 'S_SOFTDELETED' => $post_data['post_visibility'] == ITEM_DELETED,
  2446. 'S_CHECKED_PERMANENT' => $request->is_set_post('delete_permanent') ? ' checked="checked"' : '',
  2447. 'S_ALLOWED_DELETE' => $can_delete,
  2448. 'S_ALLOWED_SOFTDELETE' => $can_softdelete,
  2449. ));
  2450. $l_confirm = 'DELETE_POST';
  2451. if ($post_data['post_visibility'] == ITEM_DELETED)
  2452. {
  2453. $l_confirm .= '_PERMANENTLY';
  2454. $s_hidden_fields['delete_permanent'] = '1';
  2455. }
  2456. else if (!$can_softdelete)
  2457. {
  2458. $s_hidden_fields['delete_permanent'] = '1';
  2459. }
  2460. confirm_box(false, [$l_confirm, 1], build_hidden_fields($s_hidden_fields), 'confirm_delete_body.html');
  2461. }
  2462. }
  2463. // If we are here the user is not able to delete - present the correct error message
  2464. if ($post_data['poster_id'] != $user->data['user_id'] && $auth->acl_get('f_delete', $forum_id))
  2465. {
  2466. trigger_error('DELETE_OWN_POSTS');
  2467. }
  2468. if ($post_data['poster_id'] == $user->data['user_id'] && $auth->acl_get('f_delete', $forum_id) && $post_id != $post_data['topic_last_post_id'])
  2469. {
  2470. trigger_error('CANNOT_DELETE_REPLIED');
  2471. }
  2472. trigger_error('USER_CANNOT_DELETE');
  2473. }