PageRenderTime 60ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/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

Large files files are truncated, but you can click here to view the full file

  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_…

Large files files are truncated, but you can click here to view the full file