PageRenderTime 55ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/phpBB/viewtopic.php

https://github.com/Jipem/phpbb
PHP | 2061 lines | 1508 code | 299 blank | 254 comment | 435 complexity | 44db9c9049fb449d6185e2de68a997dc MD5 | raw file
Possible License(s): 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. define('IN_PHPBB', true);
  17. $phpbb_root_path = (defined('PHPBB_ROOT_PATH')) ? PHPBB_ROOT_PATH : './';
  18. $phpEx = substr(strrchr(__FILE__, '.'), 1);
  19. include($phpbb_root_path . 'common.' . $phpEx);
  20. include($phpbb_root_path . 'includes/functions_display.' . $phpEx);
  21. include($phpbb_root_path . 'includes/bbcode.' . $phpEx);
  22. include($phpbb_root_path . 'includes/functions_user.' . $phpEx);
  23. // Start session management
  24. $user->session_begin();
  25. $auth->acl($user->data);
  26. // Initial var setup
  27. $forum_id = request_var('f', 0);
  28. $topic_id = request_var('t', 0);
  29. $post_id = request_var('p', 0);
  30. $voted_id = request_var('vote_id', array('' => 0));
  31. $voted_id = (sizeof($voted_id) > 1) ? array_unique($voted_id) : $voted_id;
  32. $start = request_var('start', 0);
  33. $view = request_var('view', '');
  34. $default_sort_days = (!empty($user->data['user_post_show_days'])) ? $user->data['user_post_show_days'] : 0;
  35. $default_sort_key = (!empty($user->data['user_post_sortby_type'])) ? $user->data['user_post_sortby_type'] : 't';
  36. $default_sort_dir = (!empty($user->data['user_post_sortby_dir'])) ? $user->data['user_post_sortby_dir'] : 'a';
  37. $sort_days = request_var('st', $default_sort_days);
  38. $sort_key = request_var('sk', $default_sort_key);
  39. $sort_dir = request_var('sd', $default_sort_dir);
  40. $update = request_var('update', false);
  41. $pagination = $phpbb_container->get('pagination');
  42. $s_can_vote = false;
  43. /**
  44. * @todo normalize?
  45. */
  46. $hilit_words = request_var('hilit', '', true);
  47. // Do we have a topic or post id?
  48. if (!$topic_id && !$post_id)
  49. {
  50. trigger_error('NO_TOPIC');
  51. }
  52. $phpbb_content_visibility = $phpbb_container->get('content.visibility');
  53. // Find topic id if user requested a newer or older topic
  54. if ($view && !$post_id)
  55. {
  56. if (!$forum_id)
  57. {
  58. $sql = 'SELECT forum_id
  59. FROM ' . TOPICS_TABLE . "
  60. WHERE topic_id = $topic_id";
  61. $result = $db->sql_query($sql);
  62. $forum_id = (int) $db->sql_fetchfield('forum_id');
  63. $db->sql_freeresult($result);
  64. if (!$forum_id)
  65. {
  66. trigger_error('NO_TOPIC');
  67. }
  68. }
  69. if ($view == 'unread')
  70. {
  71. // Get topic tracking info
  72. $topic_tracking_info = get_complete_topic_tracking($forum_id, $topic_id);
  73. $topic_last_read = (isset($topic_tracking_info[$topic_id])) ? $topic_tracking_info[$topic_id] : 0;
  74. $sql = 'SELECT post_id, topic_id, forum_id
  75. FROM ' . POSTS_TABLE . "
  76. WHERE topic_id = $topic_id
  77. AND " . $phpbb_content_visibility->get_visibility_sql('post', $forum_id) . "
  78. AND post_time > $topic_last_read
  79. AND forum_id = $forum_id
  80. ORDER BY post_time ASC";
  81. $result = $db->sql_query_limit($sql, 1);
  82. $row = $db->sql_fetchrow($result);
  83. $db->sql_freeresult($result);
  84. if (!$row)
  85. {
  86. $sql = 'SELECT topic_last_post_id as post_id, topic_id, forum_id
  87. FROM ' . TOPICS_TABLE . '
  88. WHERE topic_id = ' . $topic_id;
  89. $result = $db->sql_query($sql);
  90. $row = $db->sql_fetchrow($result);
  91. $db->sql_freeresult($result);
  92. }
  93. if (!$row)
  94. {
  95. // Setup user environment so we can process lang string
  96. $user->setup('viewtopic');
  97. trigger_error('NO_TOPIC');
  98. }
  99. $post_id = $row['post_id'];
  100. $topic_id = $row['topic_id'];
  101. }
  102. else if ($view == 'next' || $view == 'previous')
  103. {
  104. $sql_condition = ($view == 'next') ? '>' : '<';
  105. $sql_ordering = ($view == 'next') ? 'ASC' : 'DESC';
  106. $sql = 'SELECT forum_id, topic_last_post_time
  107. FROM ' . TOPICS_TABLE . '
  108. WHERE topic_id = ' . $topic_id;
  109. $result = $db->sql_query($sql);
  110. $row = $db->sql_fetchrow($result);
  111. $db->sql_freeresult($result);
  112. if (!$row)
  113. {
  114. $user->setup('viewtopic');
  115. // OK, the topic doesn't exist. This error message is not helpful, but technically correct.
  116. trigger_error(($view == 'next') ? 'NO_NEWER_TOPICS' : 'NO_OLDER_TOPICS');
  117. }
  118. else
  119. {
  120. $sql = 'SELECT topic_id, forum_id
  121. FROM ' . TOPICS_TABLE . '
  122. WHERE forum_id = ' . $row['forum_id'] . "
  123. AND topic_moved_id = 0
  124. AND topic_last_post_time $sql_condition {$row['topic_last_post_time']}
  125. AND " . $phpbb_content_visibility->get_visibility_sql('topic', $row['forum_id']) . "
  126. ORDER BY topic_last_post_time $sql_ordering";
  127. $result = $db->sql_query_limit($sql, 1);
  128. $row = $db->sql_fetchrow($result);
  129. $db->sql_freeresult($result);
  130. if (!$row)
  131. {
  132. $sql = 'SELECT forum_style
  133. FROM ' . FORUMS_TABLE . "
  134. WHERE forum_id = $forum_id";
  135. $result = $db->sql_query($sql);
  136. $forum_style = (int) $db->sql_fetchfield('forum_style');
  137. $db->sql_freeresult($result);
  138. $user->setup('viewtopic', $forum_style);
  139. trigger_error(($view == 'next') ? 'NO_NEWER_TOPICS' : 'NO_OLDER_TOPICS');
  140. }
  141. else
  142. {
  143. $topic_id = $row['topic_id'];
  144. $forum_id = $row['forum_id'];
  145. }
  146. }
  147. }
  148. if (isset($row) && $row['forum_id'])
  149. {
  150. $forum_id = $row['forum_id'];
  151. }
  152. }
  153. // This rather complex gaggle of code handles querying for topics but
  154. // also allows for direct linking to a post (and the calculation of which
  155. // page the post is on and the correct display of viewtopic)
  156. $sql_array = array(
  157. 'SELECT' => 't.*, f.*',
  158. 'FROM' => array(FORUMS_TABLE => 'f'),
  159. );
  160. // The FROM-Order is quite important here, else t.* columns can not be correctly bound.
  161. if ($post_id)
  162. {
  163. $sql_array['SELECT'] .= ', p.post_visibility, p.post_time, p.post_id';
  164. $sql_array['FROM'][POSTS_TABLE] = 'p';
  165. }
  166. // Topics table need to be the last in the chain
  167. $sql_array['FROM'][TOPICS_TABLE] = 't';
  168. if ($user->data['is_registered'])
  169. {
  170. $sql_array['SELECT'] .= ', tw.notify_status';
  171. $sql_array['LEFT_JOIN'] = array();
  172. $sql_array['LEFT_JOIN'][] = array(
  173. 'FROM' => array(TOPICS_WATCH_TABLE => 'tw'),
  174. 'ON' => 'tw.user_id = ' . $user->data['user_id'] . ' AND t.topic_id = tw.topic_id'
  175. );
  176. if ($config['allow_bookmarks'])
  177. {
  178. $sql_array['SELECT'] .= ', bm.topic_id as bookmarked';
  179. $sql_array['LEFT_JOIN'][] = array(
  180. 'FROM' => array(BOOKMARKS_TABLE => 'bm'),
  181. 'ON' => 'bm.user_id = ' . $user->data['user_id'] . ' AND t.topic_id = bm.topic_id'
  182. );
  183. }
  184. if ($config['load_db_lastread'])
  185. {
  186. $sql_array['SELECT'] .= ', tt.mark_time, ft.mark_time as forum_mark_time';
  187. $sql_array['LEFT_JOIN'][] = array(
  188. 'FROM' => array(TOPICS_TRACK_TABLE => 'tt'),
  189. 'ON' => 'tt.user_id = ' . $user->data['user_id'] . ' AND t.topic_id = tt.topic_id'
  190. );
  191. $sql_array['LEFT_JOIN'][] = array(
  192. 'FROM' => array(FORUMS_TRACK_TABLE => 'ft'),
  193. 'ON' => 'ft.user_id = ' . $user->data['user_id'] . ' AND t.forum_id = ft.forum_id'
  194. );
  195. }
  196. }
  197. if (!$post_id)
  198. {
  199. $sql_array['WHERE'] = "t.topic_id = $topic_id";
  200. }
  201. else
  202. {
  203. $sql_array['WHERE'] = "p.post_id = $post_id AND t.topic_id = p.topic_id";
  204. }
  205. $sql_array['WHERE'] .= ' AND f.forum_id = t.forum_id';
  206. $sql = $db->sql_build_query('SELECT', $sql_array);
  207. $result = $db->sql_query($sql);
  208. $topic_data = $db->sql_fetchrow($result);
  209. $db->sql_freeresult($result);
  210. // link to unapproved post or incorrect link
  211. if (!$topic_data)
  212. {
  213. // If post_id was submitted, we try at least to display the topic as a last resort...
  214. if ($post_id && $topic_id)
  215. {
  216. redirect(append_sid("{$phpbb_root_path}viewtopic.$phpEx", "t=$topic_id" . (($forum_id) ? "&amp;f=$forum_id" : '')));
  217. }
  218. trigger_error('NO_TOPIC');
  219. }
  220. $forum_id = (int) $topic_data['forum_id'];
  221. // Now we know the forum_id and can check the permissions
  222. if ($topic_data['topic_visibility'] != ITEM_APPROVED && !$auth->acl_get('m_approve', $forum_id))
  223. {
  224. trigger_error('NO_TOPIC');
  225. }
  226. // This is for determining where we are (page)
  227. if ($post_id)
  228. {
  229. // are we where we are supposed to be?
  230. if (($topic_data['post_visibility'] == ITEM_UNAPPROVED || $topic_data['post_visibility'] == ITEM_REAPPROVE) && !$auth->acl_get('m_approve', $topic_data['forum_id']))
  231. {
  232. // If post_id was submitted, we try at least to display the topic as a last resort...
  233. if ($topic_id)
  234. {
  235. redirect(append_sid("{$phpbb_root_path}viewtopic.$phpEx", "t=$topic_id" . (($forum_id) ? "&amp;f=$forum_id" : '')));
  236. }
  237. trigger_error('NO_TOPIC');
  238. }
  239. if ($post_id == $topic_data['topic_first_post_id'] || $post_id == $topic_data['topic_last_post_id'])
  240. {
  241. $check_sort = ($post_id == $topic_data['topic_first_post_id']) ? 'd' : 'a';
  242. if ($sort_dir == $check_sort)
  243. {
  244. $topic_data['prev_posts'] = $phpbb_content_visibility->get_count('topic_posts', $topic_data, $forum_id) - 1;
  245. }
  246. else
  247. {
  248. $topic_data['prev_posts'] = 0;
  249. }
  250. }
  251. else
  252. {
  253. $sql = 'SELECT COUNT(p.post_id) AS prev_posts
  254. FROM ' . POSTS_TABLE . " p
  255. WHERE p.topic_id = {$topic_data['topic_id']}
  256. AND " . $phpbb_content_visibility->get_visibility_sql('post', $forum_id, 'p.');
  257. if ($sort_dir == 'd')
  258. {
  259. $sql .= " AND (p.post_time > {$topic_data['post_time']} OR (p.post_time = {$topic_data['post_time']} AND p.post_id >= {$topic_data['post_id']}))";
  260. }
  261. else
  262. {
  263. $sql .= " AND (p.post_time < {$topic_data['post_time']} OR (p.post_time = {$topic_data['post_time']} AND p.post_id <= {$topic_data['post_id']}))";
  264. }
  265. $result = $db->sql_query($sql);
  266. $row = $db->sql_fetchrow($result);
  267. $db->sql_freeresult($result);
  268. $topic_data['prev_posts'] = $row['prev_posts'] - 1;
  269. }
  270. }
  271. $topic_id = (int) $topic_data['topic_id'];
  272. $topic_replies = $phpbb_content_visibility->get_count('topic_posts', $topic_data, $forum_id) - 1;
  273. // Check sticky/announcement time limit
  274. if (($topic_data['topic_type'] == POST_STICKY || $topic_data['topic_type'] == POST_ANNOUNCE) && $topic_data['topic_time_limit'] && ($topic_data['topic_time'] + $topic_data['topic_time_limit']) < time())
  275. {
  276. $sql = 'UPDATE ' . TOPICS_TABLE . '
  277. SET topic_type = ' . POST_NORMAL . ', topic_time_limit = 0
  278. WHERE topic_id = ' . $topic_id;
  279. $db->sql_query($sql);
  280. $topic_data['topic_type'] = POST_NORMAL;
  281. $topic_data['topic_time_limit'] = 0;
  282. }
  283. // Setup look and feel
  284. $user->setup('viewtopic', $topic_data['forum_style']);
  285. // Start auth check
  286. if (!$auth->acl_get('f_read', $forum_id))
  287. {
  288. if ($user->data['user_id'] != ANONYMOUS)
  289. {
  290. trigger_error('SORRY_AUTH_READ');
  291. }
  292. login_box('', $user->lang['LOGIN_VIEWFORUM']);
  293. }
  294. // Forum is passworded ... check whether access has been granted to this
  295. // user this session, if not show login box
  296. if ($topic_data['forum_password'])
  297. {
  298. login_forum_box($topic_data);
  299. }
  300. // Redirect to login upon emailed notification links if user is not logged in.
  301. if (isset($_GET['e']) && $user->data['user_id'] == ANONYMOUS)
  302. {
  303. login_box(build_url('e') . '#unread', $user->lang['LOGIN_NOTIFY_TOPIC']);
  304. }
  305. // What is start equal to?
  306. if ($post_id)
  307. {
  308. $start = floor(($topic_data['prev_posts']) / $config['posts_per_page']) * $config['posts_per_page'];
  309. }
  310. // Get topic tracking info
  311. if (!isset($topic_tracking_info))
  312. {
  313. $topic_tracking_info = array();
  314. // Get topic tracking info
  315. if ($config['load_db_lastread'] && $user->data['is_registered'])
  316. {
  317. $tmp_topic_data = array($topic_id => $topic_data);
  318. $topic_tracking_info = get_topic_tracking($forum_id, $topic_id, $tmp_topic_data, array($forum_id => $topic_data['forum_mark_time']));
  319. unset($tmp_topic_data);
  320. }
  321. else if ($config['load_anon_lastread'] || $user->data['is_registered'])
  322. {
  323. $topic_tracking_info = get_complete_topic_tracking($forum_id, $topic_id);
  324. }
  325. }
  326. // Post ordering options
  327. $limit_days = array(0 => $user->lang['ALL_POSTS'], 1 => $user->lang['1_DAY'], 7 => $user->lang['7_DAYS'], 14 => $user->lang['2_WEEKS'], 30 => $user->lang['1_MONTH'], 90 => $user->lang['3_MONTHS'], 180 => $user->lang['6_MONTHS'], 365 => $user->lang['1_YEAR']);
  328. $sort_by_text = array('a' => $user->lang['AUTHOR'], 't' => $user->lang['POST_TIME'], 's' => $user->lang['SUBJECT']);
  329. $sort_by_sql = array('a' => array('u.username_clean', 'p.post_id'), 't' => 'p.post_time', 's' => array('p.post_subject', 'p.post_id'));
  330. $join_user_sql = array('a' => true, 't' => false, 's' => false);
  331. $s_limit_days = $s_sort_key = $s_sort_dir = $u_sort_param = '';
  332. gen_sort_selects($limit_days, $sort_by_text, $sort_days, $sort_key, $sort_dir, $s_limit_days, $s_sort_key, $s_sort_dir, $u_sort_param, $default_sort_days, $default_sort_key, $default_sort_dir);
  333. // Obtain correct post count and ordering SQL if user has
  334. // requested anything different
  335. if ($sort_days)
  336. {
  337. $min_post_time = time() - ($sort_days * 86400);
  338. $sql = 'SELECT COUNT(post_id) AS num_posts
  339. FROM ' . POSTS_TABLE . "
  340. WHERE topic_id = $topic_id
  341. AND post_time >= $min_post_time
  342. AND " . $phpbb_content_visibility->get_visibility_sql('post', $forum_id);
  343. $result = $db->sql_query($sql);
  344. $total_posts = (int) $db->sql_fetchfield('num_posts');
  345. $db->sql_freeresult($result);
  346. $limit_posts_time = "AND p.post_time >= $min_post_time ";
  347. if (isset($_POST['sort']))
  348. {
  349. $start = 0;
  350. }
  351. }
  352. else
  353. {
  354. $total_posts = $topic_replies + 1;
  355. $limit_posts_time = '';
  356. }
  357. // Was a highlight request part of the URI?
  358. $highlight_match = $highlight = '';
  359. if ($hilit_words)
  360. {
  361. $highlight_match = phpbb_clean_search_string($hilit_words);
  362. $highlight = urlencode($highlight_match);
  363. $highlight_match = str_replace('\*', '\w+?', preg_quote($highlight_match, '#'));
  364. $highlight_match = preg_replace('#(?<=^|\s)\\\\w\*\?(?=\s|$)#', '\w+?', $highlight_match);
  365. $highlight_match = str_replace(' ', '|', $highlight_match);
  366. }
  367. // Make sure $start is set to the last page if it exceeds the amount
  368. $start = $pagination->validate_start($start, $config['posts_per_page'], $total_posts);
  369. // General Viewtopic URL for return links
  370. $viewtopic_url = append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&amp;t=$topic_id" . (($start == 0) ? '' : "&amp;start=$start") . ((strlen($u_sort_param)) ? "&amp;$u_sort_param" : '') . (($highlight_match) ? "&amp;hilit=$highlight" : ''));
  371. // Are we watching this topic?
  372. $s_watching_topic = array(
  373. 'link' => '',
  374. 'link_toggle' => '',
  375. 'title' => '',
  376. 'title_toggle' => '',
  377. 'is_watching' => false,
  378. );
  379. if (($config['email_enable'] || $config['jab_enable']) && $config['allow_topic_notify'])
  380. {
  381. $notify_status = (isset($topic_data['notify_status'])) ? $topic_data['notify_status'] : null;
  382. watch_topic_forum('topic', $s_watching_topic, $user->data['user_id'], $forum_id, $topic_id, $notify_status, $start, $topic_data['topic_title']);
  383. // Reset forum notification if forum notify is set
  384. if ($config['allow_forum_notify'] && $auth->acl_get('f_subscribe', $forum_id))
  385. {
  386. $s_watching_forum = $s_watching_topic;
  387. watch_topic_forum('forum', $s_watching_forum, $user->data['user_id'], $forum_id, 0);
  388. }
  389. }
  390. // Bookmarks
  391. if ($config['allow_bookmarks'] && $user->data['is_registered'] && request_var('bookmark', 0))
  392. {
  393. if (check_link_hash(request_var('hash', ''), "topic_$topic_id"))
  394. {
  395. if (!$topic_data['bookmarked'])
  396. {
  397. $sql = 'INSERT INTO ' . BOOKMARKS_TABLE . ' ' . $db->sql_build_array('INSERT', array(
  398. 'user_id' => $user->data['user_id'],
  399. 'topic_id' => $topic_id,
  400. ));
  401. $db->sql_query($sql);
  402. }
  403. else
  404. {
  405. $sql = 'DELETE FROM ' . BOOKMARKS_TABLE . "
  406. WHERE user_id = {$user->data['user_id']}
  407. AND topic_id = $topic_id";
  408. $db->sql_query($sql);
  409. }
  410. $message = (($topic_data['bookmarked']) ? $user->lang['BOOKMARK_REMOVED'] : $user->lang['BOOKMARK_ADDED']);
  411. if (!$request->is_ajax())
  412. {
  413. $message .= '<br /><br />' . $user->lang('RETURN_TOPIC', '<a href="' . $viewtopic_url . '">', '</a>');
  414. }
  415. }
  416. else
  417. {
  418. $message = $user->lang['BOOKMARK_ERR'];
  419. if (!$request->is_ajax())
  420. {
  421. $message .= '<br /><br />' . $user->lang('RETURN_TOPIC', '<a href="' . $viewtopic_url . '">', '</a>');
  422. }
  423. }
  424. meta_refresh(3, $viewtopic_url);
  425. trigger_error($message);
  426. }
  427. // Grab ranks
  428. $ranks = $cache->obtain_ranks();
  429. // Grab icons
  430. $icons = $cache->obtain_icons();
  431. // Grab extensions
  432. $extensions = array();
  433. if ($topic_data['topic_attachment'])
  434. {
  435. $extensions = $cache->obtain_attach_extensions($forum_id);
  436. }
  437. // Forum rules listing
  438. $s_forum_rules = '';
  439. gen_forum_auth_level('topic', $forum_id, $topic_data['forum_status']);
  440. // Quick mod tools
  441. $allow_change_type = ($auth->acl_get('m_', $forum_id) || ($user->data['is_registered'] && $user->data['user_id'] == $topic_data['topic_poster'])) ? true : false;
  442. $quickmod_array = array(
  443. // 'key' => array('LANG_KEY', $userHasPermissions),
  444. 'lock' => array('LOCK_TOPIC', ($topic_data['topic_status'] == ITEM_UNLOCKED) && ($auth->acl_get('m_lock', $forum_id) || ($auth->acl_get('f_user_lock', $forum_id) && $user->data['is_registered'] && $user->data['user_id'] == $topic_data['topic_poster'] && $topic_data['topic_status'] == ITEM_UNLOCKED))),
  445. 'unlock' => array('UNLOCK_TOPIC', ($topic_data['topic_status'] != ITEM_UNLOCKED) && ($auth->acl_get('m_lock', $forum_id) || ($auth->acl_get('f_user_lock', $forum_id) && $user->data['is_registered'] && $user->data['user_id'] == $topic_data['topic_poster'] && $topic_data['topic_status'] == ITEM_UNLOCKED))),
  446. 'delete_topic' => array('DELETE_TOPIC', ($auth->acl_get('m_delete', $forum_id) || (($topic_data['topic_visibility'] != ITEM_DELETED) && $auth->acl_get('m_softdelete', $forum_id)))),
  447. 'restore_topic' => array('RESTORE_TOPIC', (($topic_data['topic_visibility'] == ITEM_DELETED) && $auth->acl_get('m_approve', $forum_id))),
  448. 'move' => array('MOVE_TOPIC', $auth->acl_get('m_move', $forum_id) && $topic_data['topic_status'] != ITEM_MOVED),
  449. 'split' => array('SPLIT_TOPIC', $auth->acl_get('m_split', $forum_id)),
  450. 'merge' => array('MERGE_POSTS', $auth->acl_get('m_merge', $forum_id)),
  451. 'merge_topic' => array('MERGE_TOPIC', $auth->acl_get('m_merge', $forum_id)),
  452. 'fork' => array('FORK_TOPIC', $auth->acl_get('m_move', $forum_id)),
  453. 'make_normal' => array('MAKE_NORMAL', ($allow_change_type && $auth->acl_gets('f_sticky', 'f_announce', $forum_id) && $topic_data['topic_type'] != POST_NORMAL)),
  454. 'make_sticky' => array('MAKE_STICKY', ($allow_change_type && $auth->acl_get('f_sticky', $forum_id) && $topic_data['topic_type'] != POST_STICKY)),
  455. 'make_announce' => array('MAKE_ANNOUNCE', ($allow_change_type && $auth->acl_get('f_announce', $forum_id) && $topic_data['topic_type'] != POST_ANNOUNCE)),
  456. 'make_global' => array('MAKE_GLOBAL', ($allow_change_type && $auth->acl_get('f_announce', $forum_id) && $topic_data['topic_type'] != POST_GLOBAL)),
  457. 'topic_logs' => array('VIEW_TOPIC_LOGS', $auth->acl_get('m_', $forum_id)),
  458. );
  459. foreach($quickmod_array as $option => $qm_ary)
  460. {
  461. if (!empty($qm_ary[1]))
  462. {
  463. phpbb_add_quickmod_option($option, $qm_ary[0]);
  464. }
  465. }
  466. // Navigation links
  467. generate_forum_nav($topic_data);
  468. // Forum Rules
  469. generate_forum_rules($topic_data);
  470. // Moderators
  471. $forum_moderators = array();
  472. if ($config['load_moderators'])
  473. {
  474. get_moderators($forum_moderators, $forum_id);
  475. }
  476. // This is only used for print view so ...
  477. $server_path = (!$view) ? $phpbb_root_path : generate_board_url() . '/';
  478. // Replace naughty words in title
  479. $topic_data['topic_title'] = censor_text($topic_data['topic_title']);
  480. $s_search_hidden_fields = array(
  481. 't' => $topic_id,
  482. 'sf' => 'msgonly',
  483. );
  484. if ($_SID)
  485. {
  486. $s_search_hidden_fields['sid'] = $_SID;
  487. }
  488. if (!empty($_EXTRA_URL))
  489. {
  490. foreach ($_EXTRA_URL as $url_param)
  491. {
  492. $url_param = explode('=', $url_param, 2);
  493. $s_search_hidden_fields[$url_param[0]] = $url_param[1];
  494. }
  495. }
  496. // If we've got a hightlight set pass it on to pagination.
  497. $base_url = append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&amp;t=$topic_id" . ((strlen($u_sort_param)) ? "&amp;$u_sort_param" : '') . (($highlight_match) ? "&amp;hilit=$highlight" : ''));
  498. $pagination->generate_template_pagination($base_url, 'pagination', 'start', $total_posts, $config['posts_per_page'], $start);
  499. // Send vars to template
  500. $template->assign_vars(array(
  501. 'FORUM_ID' => $forum_id,
  502. 'FORUM_NAME' => $topic_data['forum_name'],
  503. 'FORUM_DESC' => generate_text_for_display($topic_data['forum_desc'], $topic_data['forum_desc_uid'], $topic_data['forum_desc_bitfield'], $topic_data['forum_desc_options']),
  504. 'TOPIC_ID' => $topic_id,
  505. 'TOPIC_TITLE' => $topic_data['topic_title'],
  506. 'TOPIC_POSTER' => $topic_data['topic_poster'],
  507. 'TOPIC_AUTHOR_FULL' => get_username_string('full', $topic_data['topic_poster'], $topic_data['topic_first_poster_name'], $topic_data['topic_first_poster_colour']),
  508. 'TOPIC_AUTHOR_COLOUR' => get_username_string('colour', $topic_data['topic_poster'], $topic_data['topic_first_poster_name'], $topic_data['topic_first_poster_colour']),
  509. 'TOPIC_AUTHOR' => get_username_string('username', $topic_data['topic_poster'], $topic_data['topic_first_poster_name'], $topic_data['topic_first_poster_colour']),
  510. 'TOTAL_POSTS' => $user->lang('VIEW_TOPIC_POSTS', (int) $total_posts),
  511. 'U_MCP' => ($auth->acl_get('m_', $forum_id)) ? append_sid("{$phpbb_root_path}mcp.$phpEx", "i=main&amp;mode=topic_view&amp;f=$forum_id&amp;t=$topic_id" . (($start == 0) ? '' : "&amp;start=$start") . ((strlen($u_sort_param)) ? "&amp;$u_sort_param" : ''), true, $user->session_id) : '',
  512. 'MODERATORS' => (isset($forum_moderators[$forum_id]) && sizeof($forum_moderators[$forum_id])) ? implode($user->lang['COMMA_SEPARATOR'], $forum_moderators[$forum_id]) : '',
  513. 'POST_IMG' => ($topic_data['forum_status'] == ITEM_LOCKED) ? $user->img('button_topic_locked', 'FORUM_LOCKED') : $user->img('button_topic_new', 'POST_NEW_TOPIC'),
  514. 'QUOTE_IMG' => $user->img('icon_post_quote', 'REPLY_WITH_QUOTE'),
  515. 'REPLY_IMG' => ($topic_data['forum_status'] == ITEM_LOCKED || $topic_data['topic_status'] == ITEM_LOCKED) ? $user->img('button_topic_locked', 'TOPIC_LOCKED') : $user->img('button_topic_reply', 'REPLY_TO_TOPIC'),
  516. 'EDIT_IMG' => $user->img('icon_post_edit', 'EDIT_POST'),
  517. 'DELETE_IMG' => $user->img('icon_post_delete', 'DELETE_POST'),
  518. 'DELETED_IMG' => $user->img('icon_topic_deleted', 'POST_DELETED_RESTORE'),
  519. 'INFO_IMG' => $user->img('icon_post_info', 'VIEW_INFO'),
  520. 'PROFILE_IMG' => $user->img('icon_user_profile', 'READ_PROFILE'),
  521. 'SEARCH_IMG' => $user->img('icon_user_search', 'SEARCH_USER_POSTS'),
  522. 'PM_IMG' => $user->img('icon_contact_pm', 'SEND_PRIVATE_MESSAGE'),
  523. 'EMAIL_IMG' => $user->img('icon_contact_email', 'SEND_EMAIL'),
  524. 'JABBER_IMG' => $user->img('icon_contact_jabber', 'JABBER') ,
  525. 'REPORT_IMG' => $user->img('icon_post_report', 'REPORT_POST'),
  526. 'REPORTED_IMG' => $user->img('icon_topic_reported', 'POST_REPORTED'),
  527. 'UNAPPROVED_IMG' => $user->img('icon_topic_unapproved', 'POST_UNAPPROVED'),
  528. 'WARN_IMG' => $user->img('icon_user_warn', 'WARN_USER'),
  529. 'S_IS_LOCKED' => ($topic_data['topic_status'] == ITEM_UNLOCKED && $topic_data['forum_status'] == ITEM_UNLOCKED) ? false : true,
  530. 'S_SELECT_SORT_DIR' => $s_sort_dir,
  531. 'S_SELECT_SORT_KEY' => $s_sort_key,
  532. 'S_SELECT_SORT_DAYS' => $s_limit_days,
  533. 'S_SINGLE_MODERATOR' => (!empty($forum_moderators[$forum_id]) && sizeof($forum_moderators[$forum_id]) > 1) ? false : true,
  534. 'S_TOPIC_ACTION' => append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&amp;t=$topic_id" . (($start == 0) ? '' : "&amp;start=$start")),
  535. 'S_MOD_ACTION' => append_sid("{$phpbb_root_path}mcp.$phpEx", "f=$forum_id&amp;t=$topic_id" . (($start == 0) ? '' : "&amp;start=$start") . "&amp;quickmod=1&amp;redirect=" . urlencode(str_replace('&amp;', '&', $viewtopic_url)), true, $user->session_id),
  536. 'L_RETURN_TO_FORUM' => $user->lang('RETURN_TO', $topic_data['forum_name']),
  537. 'S_VIEWTOPIC' => true,
  538. 'S_UNREAD_VIEW' => $view == 'unread',
  539. 'S_DISPLAY_SEARCHBOX' => ($auth->acl_get('u_search') && $auth->acl_get('f_search', $forum_id) && $config['load_search']) ? true : false,
  540. 'S_SEARCHBOX_ACTION' => append_sid("{$phpbb_root_path}search.$phpEx"),
  541. 'S_SEARCH_LOCAL_HIDDEN_FIELDS' => build_hidden_fields($s_search_hidden_fields),
  542. 'S_DISPLAY_POST_INFO' => ($topic_data['forum_type'] == FORUM_POST && ($auth->acl_get('f_post', $forum_id) || $user->data['user_id'] == ANONYMOUS)) ? true : false,
  543. 'S_DISPLAY_REPLY_INFO' => ($topic_data['forum_type'] == FORUM_POST && ($auth->acl_get('f_reply', $forum_id) || $user->data['user_id'] == ANONYMOUS)) ? true : false,
  544. 'S_ENABLE_FEEDS_TOPIC' => ($config['feed_topic'] && !phpbb_optionget(FORUM_OPTION_FEED_EXCLUDE, $topic_data['forum_options'])) ? true : false,
  545. 'U_TOPIC' => "{$server_path}viewtopic.$phpEx?f=$forum_id&amp;t=$topic_id",
  546. 'U_FORUM' => $server_path,
  547. 'U_VIEW_TOPIC' => $viewtopic_url,
  548. 'U_CANONICAL' => generate_board_url() . '/' . append_sid("viewtopic.$phpEx", "t=$topic_id" . ((strlen($u_sort_param)) ? "&amp;$u_sort_param" : '') . (($start) ? "&amp;start=$start" : ''), true, ''),
  549. 'U_VIEW_FORUM' => append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $forum_id),
  550. 'U_VIEW_OLDER_TOPIC' => append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&amp;t=$topic_id&amp;view=previous"),
  551. 'U_VIEW_NEWER_TOPIC' => append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&amp;t=$topic_id&amp;view=next"),
  552. 'U_PRINT_TOPIC' => ($auth->acl_get('f_print', $forum_id)) ? $viewtopic_url . '&amp;view=print' : '',
  553. 'U_EMAIL_TOPIC' => ($auth->acl_get('f_email', $forum_id) && $config['email_enable']) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", "mode=email&amp;t=$topic_id") : '',
  554. 'U_WATCH_TOPIC' => $s_watching_topic['link'],
  555. 'U_WATCH_TOPIC_TOGGLE' => $s_watching_topic['link_toggle'],
  556. 'S_WATCH_TOPIC_TITLE' => $s_watching_topic['title'],
  557. 'S_WATCH_TOPIC_TOGGLE' => $s_watching_topic['title_toggle'],
  558. 'S_WATCHING_TOPIC' => $s_watching_topic['is_watching'],
  559. 'U_BOOKMARK_TOPIC' => ($user->data['is_registered'] && $config['allow_bookmarks']) ? $viewtopic_url . '&amp;bookmark=1&amp;hash=' . generate_link_hash("topic_$topic_id") : '',
  560. 'S_BOOKMARK_TOPIC' => ($user->data['is_registered'] && $config['allow_bookmarks'] && $topic_data['bookmarked']) ? $user->lang['BOOKMARK_TOPIC_REMOVE'] : $user->lang['BOOKMARK_TOPIC'],
  561. 'S_BOOKMARK_TOGGLE' => (!$user->data['is_registered'] || !$config['allow_bookmarks'] || !$topic_data['bookmarked']) ? $user->lang['BOOKMARK_TOPIC_REMOVE'] : $user->lang['BOOKMARK_TOPIC'],
  562. 'S_BOOKMARKED_TOPIC' => ($user->data['is_registered'] && $config['allow_bookmarks'] && $topic_data['bookmarked']) ? true : false,
  563. 'U_POST_NEW_TOPIC' => ($auth->acl_get('f_post', $forum_id) || $user->data['user_id'] == ANONYMOUS) ? append_sid("{$phpbb_root_path}posting.$phpEx", "mode=post&amp;f=$forum_id") : '',
  564. 'U_POST_REPLY_TOPIC' => ($auth->acl_get('f_reply', $forum_id) || $user->data['user_id'] == ANONYMOUS) ? append_sid("{$phpbb_root_path}posting.$phpEx", "mode=reply&amp;f=$forum_id&amp;t=$topic_id") : '',
  565. 'U_BUMP_TOPIC' => (bump_topic_allowed($forum_id, $topic_data['topic_bumped'], $topic_data['topic_last_post_time'], $topic_data['topic_poster'], $topic_data['topic_last_poster_id'])) ? append_sid("{$phpbb_root_path}posting.$phpEx", "mode=bump&amp;f=$forum_id&amp;t=$topic_id&amp;hash=" . generate_link_hash("topic_$topic_id")) : '')
  566. );
  567. // Does this topic contain a poll?
  568. if (!empty($topic_data['poll_start']))
  569. {
  570. $sql = 'SELECT o.*, p.bbcode_bitfield, p.bbcode_uid
  571. FROM ' . POLL_OPTIONS_TABLE . ' o, ' . POSTS_TABLE . " p
  572. WHERE o.topic_id = $topic_id
  573. AND p.post_id = {$topic_data['topic_first_post_id']}
  574. AND p.topic_id = o.topic_id
  575. ORDER BY o.poll_option_id";
  576. $result = $db->sql_query($sql);
  577. $poll_info = $vote_counts = array();
  578. while ($row = $db->sql_fetchrow($result))
  579. {
  580. $poll_info[] = $row;
  581. $option_id = (int) $row['poll_option_id'];
  582. $vote_counts[$option_id] = (int) $row['poll_option_total'];
  583. }
  584. $db->sql_freeresult($result);
  585. $cur_voted_id = array();
  586. if ($user->data['is_registered'])
  587. {
  588. $sql = 'SELECT poll_option_id
  589. FROM ' . POLL_VOTES_TABLE . '
  590. WHERE topic_id = ' . $topic_id . '
  591. AND vote_user_id = ' . $user->data['user_id'];
  592. $result = $db->sql_query($sql);
  593. while ($row = $db->sql_fetchrow($result))
  594. {
  595. $cur_voted_id[] = $row['poll_option_id'];
  596. }
  597. $db->sql_freeresult($result);
  598. }
  599. else
  600. {
  601. // Cookie based guest tracking ... I don't like this but hum ho
  602. // it's oft requested. This relies on "nice" users who don't feel
  603. // the need to delete cookies to mess with results.
  604. if ($request->is_set($config['cookie_name'] . '_poll_' . $topic_id, \phpbb\request\request_interface::COOKIE))
  605. {
  606. $cur_voted_id = explode(',', $request->variable($config['cookie_name'] . '_poll_' . $topic_id, '', true, \phpbb\request\request_interface::COOKIE));
  607. $cur_voted_id = array_map('intval', $cur_voted_id);
  608. }
  609. }
  610. // Can not vote at all if no vote permission
  611. $s_can_vote = ($auth->acl_get('f_vote', $forum_id) &&
  612. (($topic_data['poll_length'] != 0 && $topic_data['poll_start'] + $topic_data['poll_length'] > time()) || $topic_data['poll_length'] == 0) &&
  613. $topic_data['topic_status'] != ITEM_LOCKED &&
  614. $topic_data['forum_status'] != ITEM_LOCKED &&
  615. (!sizeof($cur_voted_id) ||
  616. ($auth->acl_get('f_votechg', $forum_id) && $topic_data['poll_vote_change']))) ? true : false;
  617. $s_display_results = (!$s_can_vote || ($s_can_vote && sizeof($cur_voted_id)) || $view == 'viewpoll') ? true : false;
  618. if ($update && $s_can_vote)
  619. {
  620. if (!sizeof($voted_id) || sizeof($voted_id) > $topic_data['poll_max_options'] || in_array(VOTE_CONVERTED, $cur_voted_id) || !check_form_key('posting'))
  621. {
  622. $redirect_url = append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&amp;t=$topic_id" . (($start == 0) ? '' : "&amp;start=$start"));
  623. meta_refresh(5, $redirect_url);
  624. if (!sizeof($voted_id))
  625. {
  626. $message = 'NO_VOTE_OPTION';
  627. }
  628. else if (sizeof($voted_id) > $topic_data['poll_max_options'])
  629. {
  630. $message = 'TOO_MANY_VOTE_OPTIONS';
  631. }
  632. else if (in_array(VOTE_CONVERTED, $cur_voted_id))
  633. {
  634. $message = 'VOTE_CONVERTED';
  635. }
  636. else
  637. {
  638. $message = 'FORM_INVALID';
  639. }
  640. $message = $user->lang[$message] . '<br /><br />' . sprintf($user->lang['RETURN_TOPIC'], '<a href="' . $redirect_url . '">', '</a>');
  641. trigger_error($message);
  642. }
  643. foreach ($voted_id as $option)
  644. {
  645. if (in_array($option, $cur_voted_id))
  646. {
  647. continue;
  648. }
  649. $sql = 'UPDATE ' . POLL_OPTIONS_TABLE . '
  650. SET poll_option_total = poll_option_total + 1
  651. WHERE poll_option_id = ' . (int) $option . '
  652. AND topic_id = ' . (int) $topic_id;
  653. $db->sql_query($sql);
  654. $vote_counts[$option]++;
  655. if ($user->data['is_registered'])
  656. {
  657. $sql_ary = array(
  658. 'topic_id' => (int) $topic_id,
  659. 'poll_option_id' => (int) $option,
  660. 'vote_user_id' => (int) $user->data['user_id'],
  661. 'vote_user_ip' => (string) $user->ip,
  662. );
  663. $sql = 'INSERT INTO ' . POLL_VOTES_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
  664. $db->sql_query($sql);
  665. }
  666. }
  667. foreach ($cur_voted_id as $option)
  668. {
  669. if (!in_array($option, $voted_id))
  670. {
  671. $sql = 'UPDATE ' . POLL_OPTIONS_TABLE . '
  672. SET poll_option_total = poll_option_total - 1
  673. WHERE poll_option_id = ' . (int) $option . '
  674. AND topic_id = ' . (int) $topic_id;
  675. $db->sql_query($sql);
  676. $vote_counts[$option]--;
  677. if ($user->data['is_registered'])
  678. {
  679. $sql = 'DELETE FROM ' . POLL_VOTES_TABLE . '
  680. WHERE topic_id = ' . (int) $topic_id . '
  681. AND poll_option_id = ' . (int) $option . '
  682. AND vote_user_id = ' . (int) $user->data['user_id'];
  683. $db->sql_query($sql);
  684. }
  685. }
  686. }
  687. if ($user->data['user_id'] == ANONYMOUS && !$user->data['is_bot'])
  688. {
  689. $user->set_cookie('poll_' . $topic_id, implode(',', $voted_id), time() + 31536000);
  690. }
  691. $sql = 'UPDATE ' . TOPICS_TABLE . '
  692. SET poll_last_vote = ' . time() . "
  693. WHERE topic_id = $topic_id";
  694. //, topic_last_post_time = ' . time() . " -- for bumping topics with new votes, ignore for now
  695. $db->sql_query($sql);
  696. $redirect_url = append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&amp;t=$topic_id" . (($start == 0) ? '' : "&amp;start=$start"));
  697. $message = $user->lang['VOTE_SUBMITTED'] . '<br /><br />' . sprintf($user->lang['RETURN_TOPIC'], '<a href="' . $redirect_url . '">', '</a>');
  698. if ($request->is_ajax())
  699. {
  700. // Filter out invalid options
  701. $valid_user_votes = array_intersect(array_keys($vote_counts), $voted_id);
  702. $data = array(
  703. 'NO_VOTES' => $user->lang['NO_VOTES'],
  704. 'success' => true,
  705. 'user_votes' => array_flip($valid_user_votes),
  706. 'vote_counts' => $vote_counts,
  707. 'total_votes' => array_sum($vote_counts),
  708. 'can_vote' => !sizeof($valid_user_votes) || ($auth->acl_get('f_votechg', $forum_id) && $topic_data['poll_vote_change']),
  709. );
  710. $json_response = new \phpbb\json_response();
  711. $json_response->send($data);
  712. }
  713. meta_refresh(5, $redirect_url);
  714. trigger_error($message);
  715. }
  716. $poll_total = 0;
  717. $poll_most = 0;
  718. foreach ($poll_info as $poll_option)
  719. {
  720. $poll_total += $poll_option['poll_option_total'];
  721. $poll_most = ($poll_option['poll_option_total'] >= $poll_most) ? $poll_option['poll_option_total'] : $poll_most;
  722. }
  723. $parse_flags = ($poll_info[0]['bbcode_bitfield'] ? OPTION_FLAG_BBCODE : 0) | OPTION_FLAG_SMILIES;
  724. for ($i = 0, $size = sizeof($poll_info); $i < $size; $i++)
  725. {
  726. $poll_info[$i]['poll_option_text'] = generate_text_for_display($poll_info[$i]['poll_option_text'], $poll_info[$i]['bbcode_uid'], $poll_option['bbcode_bitfield'], $parse_flags, true);
  727. }
  728. $topic_data['poll_title'] = generate_text_for_display($topic_data['poll_title'], $poll_info[0]['bbcode_uid'], $poll_info[0]['bbcode_bitfield'], $parse_flags, true);
  729. foreach ($poll_info as $poll_option)
  730. {
  731. $option_pct = ($poll_total > 0) ? $poll_option['poll_option_total'] / $poll_total : 0;
  732. $option_pct_txt = sprintf("%.1d%%", round($option_pct * 100));
  733. $option_pct_rel = ($poll_most > 0) ? $poll_option['poll_option_total'] / $poll_most : 0;
  734. $option_pct_rel_txt = sprintf("%.1d%%", round($option_pct_rel * 100));
  735. $option_most_votes = ($poll_option['poll_option_total'] > 0 && $poll_option['poll_option_total'] == $poll_most) ? true : false;
  736. $template->assign_block_vars('poll_option', array(
  737. 'POLL_OPTION_ID' => $poll_option['poll_option_id'],
  738. 'POLL_OPTION_CAPTION' => $poll_option['poll_option_text'],
  739. 'POLL_OPTION_RESULT' => $poll_option['poll_option_total'],
  740. 'POLL_OPTION_PERCENT' => $option_pct_txt,
  741. 'POLL_OPTION_PERCENT_REL' => $option_pct_rel_txt,
  742. 'POLL_OPTION_PCT' => round($option_pct * 100),
  743. 'POLL_OPTION_WIDTH' => round($option_pct * 250),
  744. 'POLL_OPTION_VOTED' => (in_array($poll_option['poll_option_id'], $cur_voted_id)) ? true : false,
  745. 'POLL_OPTION_MOST_VOTES' => $option_most_votes,
  746. ));
  747. }
  748. $poll_end = $topic_data['poll_length'] + $topic_data['poll_start'];
  749. $template->assign_vars(array(
  750. 'POLL_QUESTION' => $topic_data['poll_title'],
  751. 'TOTAL_VOTES' => $poll_total,
  752. 'POLL_LEFT_CAP_IMG' => $user->img('poll_left'),
  753. 'POLL_RIGHT_CAP_IMG'=> $user->img('poll_right'),
  754. 'L_MAX_VOTES' => $user->lang('MAX_OPTIONS_SELECT', (int) $topic_data['poll_max_options']),
  755. 'L_POLL_LENGTH' => ($topic_data['poll_length']) ? sprintf($user->lang[($poll_end > time()) ? 'POLL_RUN_TILL' : 'POLL_ENDED_AT'], $user->format_date($poll_end)) : '',
  756. 'S_HAS_POLL' => true,
  757. 'S_CAN_VOTE' => $s_can_vote,
  758. 'S_DISPLAY_RESULTS' => $s_display_results,
  759. 'S_IS_MULTI_CHOICE' => ($topic_data['poll_max_options'] > 1) ? true : false,
  760. 'S_POLL_ACTION' => $viewtopic_url,
  761. 'U_VIEW_RESULTS' => $viewtopic_url . '&amp;view=viewpoll',
  762. ));
  763. unset($poll_end, $poll_info, $voted_id);
  764. }
  765. // If the user is trying to reach the second half of the topic, fetch it starting from the end
  766. $store_reverse = false;
  767. $sql_limit = $config['posts_per_page'];
  768. $sql_sort_order = $direction = '';
  769. if ($start > $total_posts / 2)
  770. {
  771. $store_reverse = true;
  772. // Select the sort order
  773. $direction = (($sort_dir == 'd') ? 'ASC' : 'DESC');
  774. $sql_limit = $pagination->reverse_limit($start, $sql_limit, $total_posts);
  775. $sql_start = $pagination->reverse_start($start, $sql_limit, $total_posts);
  776. }
  777. else
  778. {
  779. // Select the sort order
  780. $direction = (($sort_dir == 'd') ? 'DESC' : 'ASC');
  781. $sql_start = $start;
  782. }
  783. if (is_array($sort_by_sql[$sort_key]))
  784. {
  785. $sql_sort_order = implode(' ' . $direction . ', ', $sort_by_sql[$sort_key]) . ' ' . $direction;
  786. }
  787. else
  788. {
  789. $sql_sort_order = $sort_by_sql[$sort_key] . ' ' . $direction;
  790. }
  791. // Container for user details, only process once
  792. $post_list = $user_cache = $id_cache = $attachments = $attach_list = $rowset = $update_count = $post_edit_list = $post_delete_list = array();
  793. $has_unapproved_attachments = $has_approved_attachments = $display_notice = false;
  794. $bbcode_bitfield = '';
  795. $i = $i_total = 0;
  796. // Go ahead and pull all data for this topic
  797. $sql = 'SELECT p.post_id
  798. FROM ' . POSTS_TABLE . ' p' . (($join_user_sql[$sort_key]) ? ', ' . USERS_TABLE . ' u': '') . "
  799. WHERE p.topic_id = $topic_id
  800. AND " . $phpbb_content_visibility->get_visibility_sql('post', $forum_id, 'p.') . "
  801. " . (($join_user_sql[$sort_key]) ? 'AND u.user_id = p.poster_id': '') . "
  802. $limit_posts_time
  803. ORDER BY $sql_sort_order";
  804. $result = $db->sql_query_limit($sql, $sql_limit, $sql_start);
  805. $i = ($store_reverse) ? $sql_limit - 1 : 0;
  806. while ($row = $db->sql_fetchrow($result))
  807. {
  808. $post_list[$i] = (int) $row['post_id'];
  809. ($store_reverse) ? $i-- : $i++;
  810. }
  811. $db->sql_freeresult($result);
  812. if (!sizeof($post_list))
  813. {
  814. if ($sort_days)
  815. {
  816. trigger_error('NO_POSTS_TIME_FRAME');
  817. }
  818. else
  819. {
  820. trigger_error('NO_TOPIC');
  821. }
  822. }
  823. // Holding maximum post time for marking topic read
  824. // We need to grab it because we do reverse ordering sometimes
  825. $max_post_time = 0;
  826. $sql_ary = array(
  827. 'SELECT' => 'u.*, z.friend, z.foe, p.*',
  828. 'FROM' => array(
  829. USERS_TABLE => 'u',
  830. POSTS_TABLE => 'p',
  831. ),
  832. 'LEFT_JOIN' => array(
  833. array(
  834. 'FROM' => array(ZEBRA_TABLE => 'z'),
  835. 'ON' => 'z.user_id = ' . $user->data['user_id'] . ' AND z.zebra_id = p.poster_id',
  836. ),
  837. ),
  838. 'WHERE' => $db->sql_in_set('p.post_id', $post_list) . '
  839. AND u.user_id = p.poster_id',
  840. );
  841. /**
  842. * Event to modify the SQL query before the post and poster data is retrieved
  843. *
  844. * @event core.viewtopic_get_post_data
  845. * @var int forum_id Forum ID
  846. * @var int topic_id Topic ID
  847. * @var array topic_data Array with topic data
  848. * @var array post_list Array with post_ids we are going to retrieve
  849. * @var int sort_days Display posts of previous x days
  850. * @var string sort_key Key the posts are sorted by
  851. * @var string sort_dir Direction the posts are sorted by
  852. * @var int start Pagination information
  853. * @var array sql_ary The SQL array to get the data of posts and posters
  854. * @since 3.1.0-a1
  855. * @change 3.1.0-a2 Added vars forum_id, topic_id, topic_data, post_list, sort_days, sort_key, sort_dir, start
  856. */
  857. $vars = array(
  858. 'forum_id',
  859. 'topic_id',
  860. 'topic_data',
  861. 'post_list',
  862. 'sort_days',
  863. 'sort_key',
  864. 'sort_dir',
  865. 'start',
  866. 'sql_ary',
  867. );
  868. extract($phpbb_dispatcher->trigger_event('core.viewtopic_get_post_data', compact($vars)));
  869. $sql = $db->sql_build_query('SELECT', $sql_ary);
  870. $result = $db->sql_query($sql);
  871. $now = $user->create_datetime();
  872. $now = phpbb_gmgetdate($now->getTimestamp() + $now->getOffset());
  873. // Posts are stored in the $rowset array while $attach_list, $user_cache
  874. // and the global bbcode_bitfield are built
  875. while ($row = $db->sql_fetchrow($result))
  876. {
  877. // Set max_post_time
  878. if ($row['post_time'] > $max_post_time)
  879. {
  880. $max_post_time = $row['post_time'];
  881. }
  882. $poster_id = (int) $row['poster_id'];
  883. // Does post have an attachment? If so, add it to the list
  884. if ($row['post_attachment'] && $config['allow_attachments'])
  885. {
  886. $attach_list[] = (int) $row['post_id'];
  887. if ($row['post_visibility'] == ITEM_UNAPPROVED || $row['post_visibility'] == ITEM_REAPPROVE)
  888. {
  889. $has_unapproved_attachments = true;
  890. }
  891. else if ($row['post_visibility'] == ITEM_APPROVED)
  892. {
  893. $has_approved_attachments = true;
  894. }
  895. }
  896. $rowset_data = array(
  897. 'hide_post' => (($row['foe'] || $row['post_visibility'] == ITEM_DELETED) && ($view != 'show' || $post_id != $row['post_id'])) ? true : false,
  898. 'post_id' => $row['post_id'],
  899. 'post_time' => $row['post_time'],
  900. 'user_id' => $row['user_id'],
  901. 'username' => $row['username'],
  902. 'user_colour' => $row['user_colour'],
  903. 'topic_id' => $row['topic_id'],
  904. 'forum_id' => $row['forum_id'],
  905. 'post_subject' => $row['post_subject'],
  906. 'post_edit_count' => $row['post_edit_count'],
  907. 'post_edit_time' => $row['post_edit_time'],
  908. 'post_edit_reason' => $row['post_edit_reason'],
  909. 'post_edit_user' => $row['post_edit_user'],
  910. 'post_edit_locked' => $row['post_edit_locked'],
  911. 'post_delete_time' => $row['post_delete_time'],
  912. 'post_delete_reason'=> $row['post_delete_reason'],
  913. 'post_delete_user' => $row['post_delete_user'],
  914. // Make sure the icon actually exists
  915. 'icon_id' => (isset($icons[$row['icon_id']]['img'], $icons[$row['icon_id']]['height'], $icons[$row['icon_id']]['width'])) ? $row['icon_id'] : 0,
  916. 'post_attachment' => $row['post_attachment'],
  917. 'post_visibility' => $row['post_visibility'],
  918. 'post_reported' => $row['post_reported'],
  919. 'post_username' => $row['post_username'],
  920. 'post_text' => $row['post_text'],
  921. 'bbcode_uid' => $row['bbcode_uid'],
  922. 'bbcode_bitfield' => $row['bbcode_bitfield'],
  923. 'enable_smilies' => $row['enable_smilies'],
  924. 'enable_sig' => $row['enable_sig'],
  925. 'friend' => $row['friend'],
  926. 'foe' => $row['foe'],
  927. );
  928. /**
  929. * Modify the post rowset containing data to be displayed with posts
  930. *
  931. * @event core.viewtopic_post_rowset_data
  932. * @var array rowset_data Array with the rowset data for this post
  933. * @var array row Array with original user and post data
  934. * @since 3.1.0-a1
  935. */
  936. $vars = array('rowset_data', 'row');
  937. extract($phpbb_dispatcher->trigger_event('core.viewtopic_post_rowset_data', compact($vars)));
  938. $rowset[$row['post_id']] = $rowset_data;
  939. // Define the global bbcode bitfield, will be used to load bbcodes
  940. $bbcode_bitfield = $bbcode_bitfield | base64_decode($row['bbcode_bitfield']);
  941. // Is a signature attached? Are we going to display it?
  942. if ($row['enable_sig'] && $config['allow_sig'] && $user->optionget('viewsigs'))
  943. {
  944. $bbcode_bitfield = $bbcode_bitfield | base64_decode($row['user_sig_bbcode_bitfield']);
  945. }
  946. // Cache various user specific data ... so we don't have to recompute
  947. // this each time the same user appears on this page
  948. if (!isset($user_cache[$poster_id]))
  949. {
  950. if ($poster_id == ANONYMOUS)
  951. {
  952. $user_cache_data = array(
  953. 'user_type' => USER_IGNORE,
  954. 'joined' => '',
  955. 'posts' => '',
  956. 'sig' => '',
  957. 'sig_bbcode_uid' => '',
  958. 'sig_bbcode_bitfield' => '',
  959. 'online' => false,
  960. 'avatar' => ($user->optionget('viewavatars')) ? phpbb_get_user_avatar($row) : '',
  961. 'rank_title' => '',
  962. 'rank_image' => '',
  963. 'rank_image_src' => '',
  964. 'sig' => '',
  965. 'pm' => '',
  966. 'email' => '',
  967. 'jabber' => '',
  968. 'search' => '',
  969. 'age' => '',
  970. 'username' => $row['username'],
  971. 'user_colour' => $row['user_colour'],
  972. 'warnings' => 0,
  973. 'allow_pm' => 0,
  974. );
  975. /**
  976. * Modify the guest user's data displayed with the posts
  977. *
  978. * @event core.viewtopic_cache_guest_data
  979. * @var array user_cache_data Array with the user's data
  980. * @var int poster_id Poster's user id
  981. * @var array row Array with original user and post data
  982. * @since 3.1.0-a1
  983. */
  984. $vars = array('user_cache_data', 'poster_id', 'row');
  985. extract($phpbb_dispatcher->trigger_event('core.viewtopic_cache_guest_data', compact($vars)));
  986. $user_cache[$poster_id] = $user_cache_data;
  987. get_user_rank($row['user_rank'], false, $user_cache[$poster_id]['rank_title'], $user_cache[$poster_id]['rank_image'], $user_cache[$poster_id]['rank_image_src']);
  988. }
  989. else
  990. {
  991. $user_sig = '';
  992. // We add the signature to every posters entry because enable_sig is post dependent
  993. if ($row['user_sig'] && $config['allow_sig'] && $user->optionget('viewsigs'))
  994. {
  995. $user_sig = $row['user_sig'];
  996. }
  997. $id_cache[] = $poster_id;
  998. $user_cache_data = array(
  999. 'user_type' => $row['user_type'],
  1000. 'user_inactive_reason' => $row['user_inactive_reason'],
  1001. 'joined' => $user->format_date($row['user_regdate']),
  1002. 'posts' => $row['user_posts'],
  1003. 'warnings' => (isset($row['user_warnings'])) ? $row['user_warnings'] : 0,
  1004. 'sig' => $user_sig,
  1005. 'sig_bbcode_uid' => (!empty($row['user_sig_bbcode_uid'])) ? $row['user_sig_bbcode_uid'] : '',
  1006. 'sig_bbcode_bitfield' => (!empty($row['user_sig_bbcode_bitfield'])) ? $row['user_sig_bbcode_bitfield'] : '',
  1007. 'viewonline' => $row['user_allow_viewonline'],
  1008. 'allow_pm' => $row['user_allow_pm'],
  1009. 'avatar' => ($user->optionget('viewavatars')) ? phpbb_get_user_avatar($row) : '',
  1010. 'age' => '',
  1011. 'rank_title' => '',
  1012. 'rank_image' => '',
  1013. 'rank_image_src' => '',
  1014. 'username' => $row['username'],
  1015. 'user_colour' => $row['user_colour'],
  1016. 'online' => false,
  1017. 'jabber' => ($row['user_jabber'] && $auth->acl_get('u_sendim')) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", "mode=contact&amp;action=jabber&amp;u=$poster_id") : '',
  1018. 'search' => ($auth->acl_get('u_search')) ? append_sid("{$phpbb_root_path}search.$phpEx", "author_id=$poster_id&amp;sr=posts") : '',
  1019. 'author_full' => get_username_string('full', $poster_id, $row['username'], $row['user_colour']),
  1020. 'author_colour' => get_username_string('colour', $poster_id, $row['username'], $row['user_colour']),
  1021. 'author_username' => get_username_string('username', $poster_id, $row['username'], $row['user_colour']),
  1022. 'author_profile' => get_username_string('profile', $poster_id, $row['username'], $row['user_colour']),
  1023. );
  1024. /**
  1025. * Modify the users' data displayed with their posts
  1026. *
  1027. * @event core.viewtopic_cache_user_data
  1028. * @var array user_cache_data Array with the user's data
  1029. * @var int poster_id Poster's user id
  1030. * @var array row Array with original user and post data
  1031. * @since 3.1.0-a1
  1032. */
  1033. $vars = array('user_cache_data', 'poster_id', 'row');
  1034. extract($phpbb_dispatcher->trigger_event('core.viewtopic_cache_user_data', compact($vars)));
  1035. $user_cache[$poster_id] = $user_cache_data;
  1036. get_user_rank($row['user_rank'], $row['user_posts'], $user_cache[$poster_id]['rank_title'], $user_cache[$poster_id]['rank_image'], $user_cache[$poster_id]['rank_image_src']);
  1037. if ((!empty($row['user_allow_viewemail']) && $auth->acl_get('u_sendemail')) || $auth->acl_get('a_email'))
  1038. {
  1039. $user_cache[$poster_id]['email'] = ($config['board_email_form'] && $config['email_enable']) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", "mode=email&amp;u=$poster_id") : (($config['board_hide_emails'] && !$auth->acl_get('a_email')) ? '' : 'mailto:' . $row['user_email']);
  1040. }
  1041. else
  1042. {
  1043. $user_cache[$poster_id]['email'] = '';
  1044. }
  1045. if ($config['allow_birthdays'] && !empty($row['user_birthday']))
  1046. {
  1047. list($bday_day, $bday_month, $bday_year) = array_map('intval', explode('-', $row['user_birthday']));
  1048. if ($bday_year)
  1049. {
  1050. $diff = $now['mon'] - $bday_month;
  1051. if ($diff == 0)
  1052. {
  1053. $diff = ($now['mday'] - $bday_day < 0) ? 1 : 0;
  1054. }
  1055. else
  1056. {
  1057. $diff = ($diff < 0) ? 1 : 0;
  1058. }
  1059. $user_cache[$poster_id]['age'] = (int) ($now['year'] - $bday_year - $diff);
  1060. }
  1061. }
  1062. }
  1063. }
  1064. }
  1065. $db->sql_freeresult($result);
  1066. // Load custom profile fields
  1067. if ($config['load_cpf_viewtopic'])
  1068. {
  1069. $cp = $phpbb_container->get('profilefields.manager');
  1070. // Grab all profile fields from users in id cache for later use - similar to the poster cache
  1071. $profile_fields_tmp = $cp->grab_profile_fields_data($id_cache);
  1072. // filter out fields not to be displayed on viewtopic. Yes, it's a hack, but this shouldn't break any MODs.
  1073. $profile_fields_cache = array();
  1074. foreach ($profile_fields_tmp as $profile_user_id => $profile_fields)
  1075. {
  1076. $profile_fields_cache[$profile_user_id] = array();
  1077. foreach ($profile_fields as $used_ident => $profile_field)
  1078. {
  1079. if ($profile_field['data']['field_show_on_vt'])
  1080. {
  1081. $profile_fields_cache[$profile_user_id][$used_ident] = $profile_field;
  1082. }
  1083. }
  1084. }
  1085. unset($profile_fields_tmp);
  1086. }
  1087. // Generate online information for user
  1088. if ($config['load_onlinetrack'] && sizeof($id_cache))
  1089. {
  1090. $sql = 'SELECT session_user_id, MAX(session_time) as online_time, MIN(session_viewonline) AS viewonline
  1091. FROM ' . SESSIONS_TABLE . '
  1092. WHERE ' . $db->sql_in_set('session_user_id', $id_cache) . '
  1093. GROUP BY session_user_id';
  1094. $result = $db->sql_query($sql);
  1095. $update_time = $config['load_online_time'] * 60;
  1096. while ($row = $db->sql_fetchrow($result))
  1097. {
  1098. $user_cache[$row['session_user_id']]['online'] = (time() - $update_time < $row['online_time'] && (($row['viewonline']) || $auth->acl_get('u_viewonline'))) ? true : false;
  1099. }
  1100. $db->sql_freeresult($result);
  1101. }
  1102. unset($id_cache);
  1103. // Pull attachment data
  1104. if (sizeof($attach_list))
  1105. {
  1106. if ($auth->acl_get('u_download') && $auth->acl_get('f_download', $forum_id))
  1107. {
  1108. $sql = 'SELECT *
  1109. FROM ' . ATTACHMENTS_TABLE . '
  1110. WHERE ' . $db->sql_in_set('post_msg_id', $attach_list) . '
  1111. AND in_message = 0
  1112. ORDER BY filetime DESC, post_msg_id ASC';
  1113. $result = $db->sql_query($sql);
  1114. while ($row = $db->sql_fetchrow($result))
  1115. {
  1116. $attachments[$row['post_msg_id']][] = $row;
  1117. }
  1118. $db->sql_freeresult($result);
  1119. // No attachments exist, but post table thinks they do so go ahead and reset post_attach flags
  1120. if (!sizeof($attachments))
  1121. {
  1122. $sql = 'UPDATE ' . POSTS_TABLE . '
  1123. SET post_attachment = 0
  1124. WHERE ' . $db->sql_in_set('post_id', $attach_list);
  1125. $db->sql_query($sql);
  1126. // We need to update the topic indicator too if the complete topic is now without an attachment
  1127. if (sizeof($rowset) != $total_posts)
  1128. {
  1129. // Not all posts are displayed so we query the db to find if there's any attachment for this topic
  1130. $sql = 'SELECT a.post_msg_id as post_id
  1131. FROM ' . ATTACHMENTS_TABLE . ' a, ' . POSTS_TABLE . " p
  1132. WHERE p.topic_id = $topic_id
  1133. AND p.post_visibility = " . ITEM_APPROVED . '
  1134. AND p.topic_id = a.topic_id';
  1135. $result = $db->sql_query_limit($sql, 1);
  1136. $row = $db->sql_fetchrow($result);
  1137. $db->sql_freeresult($result);
  1138. if (!$row)
  1139. {
  1140. $sql = 'UPDATE ' . TOPICS_TABLE . "
  1141. SET topic_attachment = 0
  1142. WHERE topic_id = $topic_id";
  1143. $db->sql_query($sql);
  1144. }
  1145. }
  1146. else
  1147. {
  1148. $sql = 'UPDATE ' . TOPICS_TABLE . "
  1149. SET topic_attachment = 0
  1150. WHERE topic_id = $topic_id";
  1151. $db->sql_query($sql);
  1152. }
  1153. }
  1154. else if ($has_approved_attachments && !$topic_data['topic_attachment'])
  1155. {
  1156. // Topic has approved attachments but its flag is wrong
  1157. $sql = 'UPDATE ' . TOPICS_TABLE . "
  1158. SET topic_attachment = 1
  1159. WHERE topic_id = $topic_id";
  1160. $db->sql_query($sql);
  1161. $topic_data['topic_attachment'] = 1;
  1162. }
  1163. else if ($has_unapproved_attachments && !$topic_data['topic_attachment'])
  1164. {
  1165. // Topic has only unapproved attachments but we have the right to see and download them
  1166. $topic_data['topic_attachment'] = 1;
  1167. }
  1168. }
  1169. else
  1170. {
  1171. $display_notice = true;
  1172. }
  1173. }
  1174. $methods = phpbb_gen_download_links('topic_id', $topic_id, $phpbb_root_path, $phpEx);
  1175. foreach ($methods as $method)
  1176. {
  1177. $template->assign_block_vars('dl_method', $method);
  1178. }
  1179. $template->assign_vars(array(
  1180. 'S_HAS_ATTACHMENTS' => $topic_data['topic_attachment'],
  1181. 'U_DOWNLOAD_ALL_ATTACHMENTS' => $methods[0]['LINK'],
  1182. ));
  1183. // Instantiate BBCode if need be
  1184. if ($bbcode_bitfield !== '')
  1185. {
  1186. $bbcode = new bbcode(base64_encode($bbcode_bitfield));
  1187. }
  1188. // Get the list of users who can receive private messages
  1189. $can_receive_pm_list = $auth->acl_get_list(array_keys($user_cache), 'u_readpm');
  1190. $can_receive_pm_list = (empty($can_receive_pm_list) || !isset($can_receive_pm_list[0]['u_readpm'])) ? array() : $can_receive_pm_list[0]['u_readpm'];
  1191. // Get the list of permanently banned users
  1192. $permanently_banned_users = phpbb_get_banned_user_ids(array_keys($user_cache), false);
  1193. $i_total = sizeof($rowset) - 1;
  1194. $prev_post_id = '';
  1195. $template->assign_vars(array(
  1196. 'S_NUM_POSTS' => sizeof($post_list))
  1197. );
  1198. // Output the posts
  1199. $first_unread = $post_unread = false;
  1200. for ($i = 0, $end = sizeof($post_list); $i < $end; ++$i)
  1201. {
  1202. // A non-existing rowset only happens if there was no user present for the entered poster_id
  1203. // This could be a broken posts table.
  1204. if (!isset($rowset[$post_list[$i]]))
  1205. {
  1206. continue;
  1207. }
  1208. $row = $rowset[$post_list[$i]];
  1209. $poster_id = $row['user_id'];
  1210. // End signature parsing, only if needed
  1211. if ($user_cache[$poster_id]['sig'] && $row['enable_sig'] && empty($user_cache[$poster_id]['sig_parsed']))
  1212. {
  1213. $parse_flags = ($user_cache[$poster_id]['sig_bbcode_bitfield'] ? OPTION_FLAG_BBCODE : 0) | OPTION_FLAG_SMILIES;
  1214. $user_cache[$poster_id]['sig'] = generate_text_for_display($user_cache[$poster_id]['sig'], $user_cache[$poster_id]['sig_bbcode_uid'], $user_cache[$poster_id]['sig_bbcode_bitfield'], $parse_flags, true);
  1215. }
  1216. // Parse the message and subject
  1217. $parse_flags = ($row['bbcode_bitfield'] ? OPTION_FLAG_BBCODE : 0) | OPTION_FLAG_SMILIES;
  1218. $message = generate_text_for_display($row['post_text'], $row['bbcode_uid'], $row['bbcode_bitfield'], $parse_flags, true);
  1219. if (!empty($attachments[$row['post_id']]))
  1220. {
  1221. parse_attachments($forum_id, $message, $attachments[$row['post_id']], $update_count);
  1222. }
  1223. // Replace naughty words such as farty pants
  1224. $row['post_subject'] = censor_text($row['post_subject']);
  1225. // Highlight active words (primarily for search)
  1226. if ($highlight_match)
  1227. {
  1228. $message = preg_replace('#(?!<.*)(?<!\w)(' . $highlight_match . ')(?!\w|[^<>]*(?:</s(?:cript|tyle))?>)#is', '<span class="posthilit">\1</span>', $message);
  1229. $row['post_subject'] = preg_replace('#(?!<.*)(?<!\w)(' . $highlight_match . ')(?!\w|[^<>]*(?:</s(?:cript|tyle))?>)#is', '<span class="posthilit">\1</span>', $row['post_subject']);
  1230. }
  1231. // Editing information
  1232. if (($row['post_edit_count'] && $config['display_last_edited']) || $row['post_edit_reason'])
  1233. {
  1234. // Get usernames for all following posts if not already stored
  1235. if (!sizeof($post_edit_list) && ($row['post_edit_reason'] || ($row['post_edit_user'] && !isset($user_cache[$row['post_edit_user']]))))
  1236. {
  1237. // Remove all post_ids already parsed (we do not have to check them)
  1238. $post_storage_list = (!$store_reverse) ? array_slice($post_list, $i) : array_slice(array_reverse($post_list), $i);
  1239. $sql = 'SELECT DISTINCT u.user_id, u.username, u.user_colour
  1240. FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u
  1241. WHERE ' . $db->sql_in_set('p.post_id', $post_storage_list) . '
  1242. AND p.post_edit_count <> 0
  1243. AND p.post_edit_user <> 0
  1244. AND p.post_edit_user = u.user_id';
  1245. $result2 = $db->sql_query($sql);
  1246. while ($user_edit_row = $db->sql_fetchrow($result2))
  1247. {
  1248. $post_edit_list[$user_edit_row['user_id']] = $user_edit_row;
  1249. }
  1250. $db->sql_freeresult($result2);
  1251. unset($post_storage_list);
  1252. }
  1253. if ($row['post_edit_reason'])
  1254. {
  1255. // User having edited the post also being the post author?
  1256. if (!$row['post_edit_user'] || $row['post_edit_user'] == $poster_id)
  1257. {
  1258. $display_username = get_username_string('full', $poster_id, $row['username'], $row['user_colour'], $row['post_username']);
  1259. }
  1260. else
  1261. {
  1262. $display_username = get_username_string('full', $row['post_edit_user'], $post_edit_list[$row['post_edit_user']]['username'], $post_edit_list[$row['post_edit_user']]['user_colour']);
  1263. }
  1264. $l_edited_by = $user->lang('EDITED_TIMES_TOTAL', (int) $row['post_edit_count'], $display_username, $user->format_date($row['post_edit_time'], false, true));
  1265. }
  1266. else
  1267. {
  1268. if ($row['post_edit_user'] && !isset($user_cache[$row['post_edit_user']]))
  1269. {
  1270. $user_cache[$row['post_edit_user']] = $post_edit_list[$row['post_edit_user']];
  1271. }
  1272. // User having edited the post also being the post author?
  1273. if (!$row['post_edit_user'] || $row['post_edit_user'] == $poster_id)
  1274. {
  1275. $display_username = get_username_string('full', $poster_id, $row['username'], $row['user_colour'], $row['post_username']);
  1276. }
  1277. else
  1278. {
  1279. $display_username = get_username_string('full', $row['post_edit_user'], $user_cache[$row['post_edit_user']]['username'], $user_cache[$row['post_edit_user']]['user_colour']);
  1280. }
  1281. $l_edited_by = $user->lang('EDITED_TIMES_TOTAL', (int) $row['post_edit_count'], $display_username, $user->format_date($row['post_edit_time'], false, true));
  1282. }
  1283. }
  1284. else
  1285. {
  1286. $l_edited_by = '';
  1287. }
  1288. // Deleting information
  1289. if ($row['post_visibility'] == ITEM_DELETED && $row['post_delete_user'])
  1290. {
  1291. // Get usernames for all following posts if not already stored
  1292. if (!sizeof($post_delete_list) && ($row['post_delete_reason'] || ($row['post_delete_user'] && !isset($user_cache[$row['post_delete_user']]))))
  1293. {
  1294. // Remove all post_ids already parsed (we do not have to check them)
  1295. $post_storage_list = (!$store_reverse) ? array_slice($post_list, $i) : array_slice(array_reverse($post_list), $i);
  1296. $sql = 'SELECT DISTINCT u.user_id, u.username, u.user_colour
  1297. FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u
  1298. WHERE ' . $db->sql_in_set('p.post_id', $post_storage_list) . '
  1299. AND p.post_delete_user <> 0
  1300. AND p.post_delete_user = u.user_id';
  1301. $result2 = $db->sql_query($sql);
  1302. while ($user_delete_row = $db->sql_fetchrow($result2))
  1303. {
  1304. $post_delete_list[$user_delete_row['user_id']] = $user_delete_row;
  1305. }
  1306. $db->sql_freeresult($result2);
  1307. unset($post_storage_list);
  1308. }
  1309. if ($row['post_delete_user'] && !isset($user_cache[$row['post_delete_user']]))
  1310. {
  1311. $user_cache[$row['post_delete_user']] = $post_delete_list[$row['post_delete_user']];
  1312. }
  1313. $display_postername = get_username_string('full', $poster_id, $row['username'], $row['user_colour'], $row['post_username']);
  1314. // User having deleted the post also being the post author?
  1315. if (!$row['post_delete_user'] || $row['post_delete_user'] == $poster_id)
  1316. {
  1317. $display_username = $display_postername;
  1318. }
  1319. else
  1320. {
  1321. $display_username = get_username_string('full', $row['post_delete_user'], $user_cache[$row['post_delete_user']]['username'], $user_cache[$row['post_delete_user']]['user_colour']);
  1322. }
  1323. if ($row['post_delete_reason'])
  1324. {
  1325. $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']);
  1326. }
  1327. else
  1328. {
  1329. $l_deleted_message = $user->lang('POST_DELETED_BY', $display_postername, $display_username, $user->format_date($row['post_delete_time'], false, true));
  1330. }
  1331. $l_deleted_by = $user->lang('DELETED_INFORMATION', $display_username, $user->format_date($row['post_delete_time'], false, true));
  1332. }
  1333. else
  1334. {
  1335. $l_deleted_by = $l_deleted_message = '';
  1336. }
  1337. // Bump information
  1338. if ($topic_data['topic_bumped'] && $row['post_id'] == $topic_data['topic_last_post_id'] && isset($user_cache[$topic_data['topic_bumper']]) )
  1339. {
  1340. // It is safe to grab the username from the user cache array, we are at the last
  1341. // post and only the topic poster and last poster are allowed to bump.
  1342. // Admins and mods are bound to the above rules too...
  1343. $l_bumped_by = sprintf($user->lang['BUMPED_BY'], $user_cache[$topic_data['topic_bumper']]['username'], $user->format_date($topic_data['topic_last_post_time'], false, true));
  1344. }
  1345. else
  1346. {
  1347. $l_bumped_by = '';
  1348. }
  1349. $cp_row = array();
  1350. //
  1351. if ($config['load_cpf_viewtopic'])
  1352. {
  1353. $cp_row = (isset($profile_fields_cache[$poster_id])) ? $cp->generate_profile_fields_template_data($profile_fields_cache[$poster_id]) : array();
  1354. }
  1355. $post_unread = (isset($topic_tracking_info[$topic_id]) && $row['post_time'] > $topic_tracking_info[$topic_id]) ? true : false;
  1356. $s_first_unread = false;
  1357. if (!$first_unread && $post_unread)
  1358. {
  1359. $s_first_unread = $first_unread = true;
  1360. }
  1361. $force_edit_allowed = $force_delete_allowed = false;
  1362. $s_cannot_edit = !$auth->acl_get('f_edit', $forum_id) || $user->data['user_id'] != $poster_id;
  1363. $s_cannot_edit_time = $config['edit_time'] && $row['post_time'] <= time() - ($config['edit_time'] * 60);
  1364. $s_cannot_edit_locked = $topic_data['topic_status'] == ITEM_LOCKED || $row['post_edit_locked'];
  1365. $s_cannot_delete = $user->data['user_id'] != $poster_id || (
  1366. !$auth->acl_get('f_delete', $forum_id) &&
  1367. (!$auth->acl_get('f_softdelete', $forum_id) || $row['post_visibility'] == ITEM_DELETED)
  1368. );
  1369. $s_cannot_delete_lastpost = $topic_data['topic_last_post_id'] != $row['post_id'];
  1370. $s_cannot_delete_time = $config['delete_time'] && $row['post_time'] <= time() - ($config['delete_time'] * 60);
  1371. // we do not want to allow removal of the last post if a moderator locked it!
  1372. $s_cannot_delete_locked = $topic_data['topic_status'] == ITEM_LOCKED || $row['post_edit_locked'];
  1373. /**
  1374. * This event allows you to modify the conditions for the "can edit post" and "can delete post" checks
  1375. *
  1376. * @event core.viewtopic_modify_post_action_conditions
  1377. * @var array row Array with post data
  1378. * @var array topic_data Array with topic data
  1379. * @var bool force_edit_allowed Allow the user to edit the post (all permissions and conditions are ignored)
  1380. * @var bool s_cannot_edit User can not edit the post because it's not his
  1381. * @var bool s_cannot_edit_locked User can not edit the post because it's locked
  1382. * @var bool s_cannot_edit_time User can not edit the post because edit_time has passed
  1383. * @var bool force_delete_allowed Allow the user to delete the post (all permissions and conditions are ignored)
  1384. * @var bool s_cannot_delete User can not delete the post because it's not his
  1385. * @var bool s_cannot_delete_lastpost User can not delete the post because it's not the last post of the topic
  1386. * @var bool s_cannot_delete_locked User can not delete the post because it's locked
  1387. * @var bool s_cannot_delete_time User can not delete the post because edit_time has passed
  1388. * @since 3.1.0-b4
  1389. */
  1390. $vars = array(
  1391. 'row',
  1392. 'topic_data',
  1393. 'force_edit_allowed',
  1394. 's_cannot_edit',
  1395. 's_cannot_edit_locked',
  1396. 's_cannot_edit_time',
  1397. 'force_delete_allowed',
  1398. 's_cannot_delete',
  1399. 's_cannot_delete_lastpost',
  1400. 's_cannot_delete_locked',
  1401. 's_cannot_delete_time',
  1402. );
  1403. extract($phpbb_dispatcher->trigger_event('core.viewtopic_modify_post_action_conditions', compact($vars)));
  1404. $edit_allowed = $force_edit_allowed || ($user->data['is_registered'] && ($auth->acl_get('m_edit', $forum_id) || (
  1405. !$s_cannot_edit &&
  1406. !$s_cannot_edit_time &&
  1407. !$s_cannot_edit_locked
  1408. )));
  1409. $quote_allowed = $auth->acl_get('m_edit', $forum_id) || ($topic_data['topic_status'] != ITEM_LOCKED &&
  1410. ($user->data['user_id'] == ANONYMOUS || $auth->acl_get('f_reply', $forum_id))
  1411. );
  1412. $delete_allowed = $force_delete_allowed || ($user->data['is_registered'] && (
  1413. ($auth->acl_get('m_delete', $forum_id) || ($auth->acl_get('m_softdelete', $forum_id) && $row['post_visibility'] != ITEM_DELETED)) ||
  1414. (!$s_cannot_delete && !$s_cannot_delete_lastpost && !$s_cannot_delete_time && !$s_cannot_delete_locked)
  1415. ));
  1416. // Can this user receive a Private Message?
  1417. $can_receive_pm = (
  1418. // They must be a "normal" user
  1419. $user_cache[$poster_id]['user_type'] != USER_IGNORE &&
  1420. // They must not be deactivated by the administrator
  1421. ($user_cache[$poster_id]['user_type'] != USER_INACTIVE || $user_cache[$poster_id]['user_inactive_reason'] != INACTIVE_MANUAL) &&
  1422. // They must be able to read PMs
  1423. in_array($poster_id, $can_receive_pm_list) &&
  1424. // They must not be permanently banned
  1425. !in_array($poster_id, $permanently_banned_users) &&
  1426. // They must allow users to contact via PM
  1427. (($auth->acl_gets('a_', 'm_') || $auth->acl_getf_global('m_')) || $user_cache[$poster_id]['allow_pm'])
  1428. );
  1429. $u_pm = '';
  1430. if ($config['allow_privmsg'] && $auth->acl_get('u_sendpm') && $can_receive_pm)
  1431. {
  1432. $u_pm = append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;mode=compose&amp;action=quotepost&amp;p=' . $row['post_id']);
  1433. }
  1434. //
  1435. $post_row = array(
  1436. 'POST_AUTHOR_FULL' => ($poster_id != ANONYMOUS) ? $user_cache[$poster_id]['author_full'] : get_username_string('full', $poster_id, $row['username'], $row['user_colour'], $row['post_username']),
  1437. 'POST_AUTHOR_COLOUR' => ($poster_id != ANONYMOUS) ? $user_cache[$poster_id]['author_colour'] : get_username_string('colour', $poster_id, $row['username'], $row['user_colour'], $row['post_username']),
  1438. 'POST_AUTHOR' => ($poster_id != ANONYMOUS) ? $user_cache[$poster_id]['author_username'] : get_username_string('username', $poster_id, $row['username'], $row['user_colour'], $row['post_username']),
  1439. 'U_POST_AUTHOR' => ($poster_id != ANONYMOUS) ? $user_cache[$poster_id]['author_profile'] : get_username_string('profile', $poster_id, $row['username'], $row['user_colour'], $row['post_username']),
  1440. 'RANK_TITLE' => $user_cache[$poster_id]['rank_title'],
  1441. 'RANK_IMG' => $user_cache[$poster_id]['rank_image'],
  1442. 'RANK_IMG_SRC' => $user_cache[$poster_id]['rank_image_src'],
  1443. 'POSTER_JOINED' => $user_cache[$poster_id]['joined'],
  1444. 'POSTER_POSTS' => $user_cache[$poster_id]['posts'],
  1445. 'POSTER_AVATAR' => $user_cache[$poster_id]['avatar'],
  1446. 'POSTER_WARNINGS' => $auth->acl_get('m_warn') ? $user_cache[$poster_id]['warnings'] : '',
  1447. 'POSTER_AGE' => $user_cache[$poster_id]['age'],
  1448. 'POST_DATE' => $user->format_date($row['post_time'], false, ($view == 'print') ? true : false),
  1449. 'POST_SUBJECT' => $row['post_subject'],
  1450. 'MESSAGE' => $message,
  1451. 'SIGNATURE' => ($row['enable_sig']) ? $user_cache[$poster_id]['sig'] : '',
  1452. 'EDITED_MESSAGE' => $l_edited_by,
  1453. 'EDIT_REASON' => $row['post_edit_reason'],
  1454. 'DELETED_MESSAGE' => $l_deleted_by,
  1455. 'DELETE_REASON' => $row['post_delete_reason'],
  1456. 'BUMPED_MESSAGE' => $l_bumped_by,
  1457. 'MINI_POST_IMG' => ($post_unread) ? $user->img('icon_post_target_unread', 'UNREAD_POST') : $user->img('icon_post_target', 'POST'),
  1458. 'POST_ICON_IMG' => ($topic_data['enable_icons'] && !empty($row['icon_id'])) ? $icons[$row['icon_id']]['img'] : '',
  1459. 'POST_ICON_IMG_WIDTH' => ($topic_data['enable_icons'] && !empty($row['icon_id'])) ? $icons[$row['icon_id']]['width'] : '',
  1460. 'POST_ICON_IMG_HEIGHT' => ($topic_data['enable_icons'] && !empty($row['icon_id'])) ? $icons[$row['icon_id']]['height'] : '',
  1461. 'ONLINE_IMG' => ($poster_id == ANONYMOUS || !$config['load_onlinetrack']) ? '' : (($user_cache[$poster_id]['online']) ? $user->img('icon_user_online', 'ONLINE') : $user->img('icon_user_offline', 'OFFLINE')),
  1462. 'S_ONLINE' => ($poster_id == ANONYMOUS || !$config['load_onlinetrack']) ? false : (($user_cache[$poster_id]['online']) ? true : false),
  1463. 'U_EDIT' => ($edit_allowed) ? append_sid("{$phpbb_root_path}posting.$phpEx", "mode=edit&amp;f=$forum_id&amp;p={$row['post_id']}") : '',
  1464. 'U_QUOTE' => ($quote_allowed) ? append_sid("{$phpbb_root_path}posting.$phpEx", "mode=quote&amp;f=$forum_id&amp;p={$row['post_id']}") : '',
  1465. 'U_INFO' => ($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) : '',
  1466. 'U_DELETE' => ($delete_allowed) ? append_sid("{$phpbb_root_path}posting.$phpEx", "mode=delete&amp;f=$forum_id&amp;p={$row['post_id']}") : '',
  1467. 'U_SEARCH' => $user_cache[$poster_id]['search'],
  1468. 'U_PM' => $u_pm,
  1469. 'U_EMAIL' => $user_cache[$poster_id]['email'],
  1470. 'U_JABBER' => $user_cache[$poster_id]['jabber'],
  1471. 'U_APPROVE_ACTION' => append_sid("{$phpbb_root_path}mcp.$phpEx", "i=queue&amp;p={$row['post_id']}&amp;f=$forum_id&amp;redirect=" . urlencode(str_replace('&amp;', '&', $viewtopic_url . '&amp;p=' . $row['post_id'] . '#p' . $row['post_id']))),
  1472. 'U_REPORT' => ($auth->acl_get('f_report', $forum_id)) ? append_sid("{$phpbb_root_path}report.$phpEx", 'f=' . $forum_id . '&amp;p=' . $row['post_id']) : '',
  1473. 'U_MCP_REPORT' => ($auth->acl_get('m_report', $forum_id)) ? append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=reports&amp;mode=report_details&amp;f=' . $forum_id . '&amp;p=' . $row['post_id'], true, $user->session_id) : '',
  1474. 'U_MCP_APPROVE' => ($auth->acl_get('m_approve', $forum_id)) ? append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=queue&amp;mode=approve_details&amp;f=' . $forum_id . '&amp;p=' . $row['post_id'], true, $user->session_id) : '',
  1475. 'U_MCP_RESTORE' => ($auth->acl_get('m_approve', $forum_id)) ? append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=queue&amp;mode=' . (($topic_data['topic_visibility'] != ITEM_DELETED) ? 'deleted_posts' : 'deleted_topics') . '&amp;f=' . $forum_id . '&amp;p=' . $row['post_id'], true, $user->session_id) : '',
  1476. 'U_MINI_POST' => append_sid("{$phpbb_root_path}viewtopic.$phpEx", 'p=' . $row['post_id']) . '#p' . $row['post_id'],
  1477. 'U_NEXT_POST_ID' => ($i < $i_total && isset($rowset[$post_list[$i + 1]])) ? $rowset[$post_list[$i + 1]]['post_id'] : '',
  1478. 'U_PREV_POST_ID' => $prev_post_id,
  1479. 'U_NOTES' => ($auth->acl_getf_global('m_')) ? append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=notes&amp;mode=user_notes&amp;u=' . $poster_id, true, $user->session_id) : '',
  1480. 'U_WARN' => ($auth->acl_get('m_warn') && $poster_id != $user->data['user_id'] && $poster_id != ANONYMOUS) ? append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=warn&amp;mode=warn_post&amp;f=' . $forum_id . '&amp;p=' . $row['post_id'], true, $user->session_id) : '',
  1481. 'POST_ID' => $row['post_id'],
  1482. 'POST_NUMBER' => $i + $start + 1,
  1483. 'POSTER_ID' => $poster_id,
  1484. 'S_HAS_ATTACHMENTS' => (!empty($attachments[$row['post_id']])) ? true : false,
  1485. 'S_MULTIPLE_ATTACHMENTS' => !empty($attachments[$row['post_id']]) && sizeof($attachments[$row['post_id']]) > 1,
  1486. 'S_POST_UNAPPROVED' => ($row['post_visibility'] == ITEM_UNAPPROVED || $row['post_visibility'] == ITEM_REAPPROVE) ? true : false,
  1487. 'S_POST_DELETED' => ($row['post_visibility'] == ITEM_DELETED) ? true : false,
  1488. 'L_POST_DELETED_MESSAGE' => $l_deleted_message,
  1489. 'S_POST_REPORTED' => ($row['post_reported'] && $auth->acl_get('m_report', $forum_id)) ? true : false,
  1490. 'S_DISPLAY_NOTICE' => $display_notice && $row['post_attachment'],
  1491. 'S_FRIEND' => ($row['friend']) ? true : false,
  1492. 'S_UNREAD_POST' => $post_unread,
  1493. 'S_FIRST_UNREAD' => $s_first_unread,
  1494. 'S_CUSTOM_FIELDS' => (isset($cp_row['row']) && sizeof($cp_row['row'])) ? true : false,
  1495. 'S_TOPIC_POSTER' => ($topic_data['topic_poster'] == $poster_id) ? true : false,
  1496. 'S_IGNORE_POST' => ($row['foe']) ? true : false,
  1497. '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'])) : '',
  1498. 'S_POST_HIDDEN' => $row['hide_post'],
  1499. 'L_POST_DISPLAY' => ($row['hide_post']) ? $user->lang('POST_DISPLAY', '<a class="display_post" data-post-id="' . $row['post_id'] . '" href="' . $viewtopic_url . "&amp;p={$row['post_id']}&amp;view=show#p{$row['post_id']}" . '">', '</a>') : '',
  1500. );
  1501. $user_poster_data = $user_cache[$poster_id];
  1502. $current_row_number = $i;
  1503. /**
  1504. * Modify the posts template block
  1505. *
  1506. * @event core.viewtopic_modify_post_row
  1507. * @var int start Start item of this page
  1508. * @var int current_row_number Number of the post on this page
  1509. * @var int end Number of posts on this page
  1510. * @var int total_posts Total posts count
  1511. * @var array row Array with original post and user data
  1512. * @var array cp_row Custom profile field data of the poster
  1513. * @var array attachments List of attachments
  1514. * @var array user_poster_data Poster's data from user cache
  1515. * @var array post_row Template block array of the post
  1516. * @var array topic_data Array with topic data
  1517. * @since 3.1.0-a1
  1518. * @change 3.1.0-a3 Added vars start, current_row_number, end, attachments
  1519. * @change 3.1.0-b3 Added topic_data array, total_posts
  1520. */
  1521. $vars = array(
  1522. 'start',
  1523. 'current_row_number',
  1524. 'end',
  1525. 'total_posts',
  1526. 'row',
  1527. 'cp_row',
  1528. 'attachments',
  1529. 'user_poster_data',
  1530. 'post_row',
  1531. 'topic_data',
  1532. );
  1533. extract($phpbb_dispatcher->trigger_event('core.viewtopic_modify_post_row', compact($vars)));
  1534. $i = $current_row_number;
  1535. if (isset($cp_row['row']) && sizeof($cp_row['row']))
  1536. {
  1537. $post_row = array_merge($post_row, $cp_row['row']);
  1538. }
  1539. // Dump vars into template
  1540. $template->assign_block_vars('postrow', $post_row);
  1541. $contact_fields = array(
  1542. array(
  1543. 'ID' => 'pm',
  1544. 'NAME' => $user->lang['SEND_PRIVATE_MESSAGE'],
  1545. 'U_CONTACT' => $u_pm,
  1546. ),
  1547. array(
  1548. 'ID' => 'email',
  1549. 'NAME' => $user->lang['SEND_EMAIL'],
  1550. 'U_CONTACT' => $user_cache[$poster_id]['email'],
  1551. ),
  1552. array(
  1553. 'ID' => 'jabber',
  1554. 'NAME' => $user->lang['JABBER'],
  1555. 'U_CONTACT' => $user_cache[$poster_id]['jabber'],
  1556. ),
  1557. );
  1558. foreach ($contact_fields as $field)
  1559. {
  1560. if ($field['U_CONTACT'])
  1561. {
  1562. $template->assign_block_vars('postrow.contact', $field);
  1563. }
  1564. }
  1565. if (!empty($cp_row['blockrow']))
  1566. {
  1567. foreach ($cp_row['blockrow'] as $field_data)
  1568. {
  1569. $template->assign_block_vars('postrow.custom_fields', $field_data);
  1570. if ($field_data['S_PROFILE_CONTACT'])
  1571. {
  1572. $template->assign_block_vars('postrow.contact', array(
  1573. 'ID' => $field_data['PROFILE_FIELD_IDENT'],
  1574. 'NAME' => $field_data['PROFILE_FIELD_NAME'],
  1575. 'U_CONTACT' => $field_data['PROFILE_FIELD_CONTACT'],
  1576. ));
  1577. }
  1578. }
  1579. }
  1580. // Display not already displayed Attachments for this post, we already parsed them. ;)
  1581. if (!empty($attachments[$row['post_id']]))
  1582. {
  1583. foreach ($attachments[$row['post_id']] as $attachment)
  1584. {
  1585. $template->assign_block_vars('postrow.attachment', array(
  1586. 'DISPLAY_ATTACHMENT' => $attachment)
  1587. );
  1588. }
  1589. $methods = phpbb_gen_download_links('post_id', $row['post_id'], $phpbb_root_path, $phpEx);
  1590. foreach ($methods as $method)
  1591. {
  1592. $template->assign_block_vars('postrow.dl_method', $method);
  1593. }
  1594. }
  1595. $current_row_number = $i;
  1596. /**
  1597. * Event after the post data has been assigned to the template
  1598. *
  1599. * @event core.viewtopic_post_row_after
  1600. * @var int start Start item of this page
  1601. * @var int current_row_number Number of the post on this page
  1602. * @var int end Number of posts on this page
  1603. * @var int total_posts Total posts count
  1604. * @var array row Array with original post and user data
  1605. * @var array cp_row Custom profile field data of the poster
  1606. * @var array attachments List of attachments
  1607. * @var array user_poster_data Poster's data from user cache
  1608. * @var array post_row Template block array of the post
  1609. * @var array topic_data Array with topic data
  1610. * @since 3.1.0-a3
  1611. * @change 3.1.0-b3 Added topic_data array, total_posts
  1612. */
  1613. $vars = array(
  1614. 'start',
  1615. 'current_row_number',
  1616. 'end',
  1617. 'total_posts',
  1618. 'row',
  1619. 'cp_row',
  1620. 'attachments',
  1621. 'user_poster_data',
  1622. 'post_row',
  1623. 'topic_data',
  1624. );
  1625. extract($phpbb_dispatcher->trigger_event('core.viewtopic_post_row_after', compact($vars)));
  1626. $i = $current_row_number;
  1627. $prev_post_id = $row['post_id'];
  1628. unset($rowset[$post_list[$i]]);
  1629. unset($attachments[$row['post_id']]);
  1630. }
  1631. unset($rowset, $user_cache);
  1632. // Update topic view and if necessary attachment view counters ... but only for humans and if this is the first 'page view'
  1633. if (isset($user->data['session_page']) && !$user->data['is_bot'] && (strpos($user->data['session_page'], '&t=' . $topic_id) === false || isset($user->data['session_created'])))
  1634. {
  1635. $sql = 'UPDATE ' . TOPICS_TABLE . '
  1636. SET topic_views = topic_views + 1, topic_last_view_time = ' . time() . "
  1637. WHERE topic_id = $topic_id";
  1638. $db->sql_query($sql);
  1639. // Update the attachment download counts
  1640. if (sizeof($update_count))
  1641. {
  1642. $sql = 'UPDATE ' . ATTACHMENTS_TABLE . '
  1643. SET download_count = download_count + 1
  1644. WHERE ' . $db->sql_in_set('attach_id', array_unique($update_count));
  1645. $db->sql_query($sql);
  1646. }
  1647. }
  1648. // Only mark topic if it's currently unread. Also make sure we do not set topic tracking back if earlier pages are viewed.
  1649. if (isset($topic_tracking_info[$topic_id]) && $topic_data['topic_last_post_time'] > $topic_tracking_info[$topic_id] && $max_post_time > $topic_tracking_info[$topic_id])
  1650. {
  1651. markread('topic', $forum_id, $topic_id, $max_post_time);
  1652. // Update forum info
  1653. $all_marked_read = update_forum_tracking_info($forum_id, $topic_data['forum_last_post_time'], (isset($topic_data['forum_mark_time'])) ? $topic_data['forum_mark_time'] : false, false);
  1654. }
  1655. else
  1656. {
  1657. $all_marked_read = true;
  1658. }
  1659. // If there are absolutely no more unread posts in this forum
  1660. // and unread posts shown, we can safely show the #unread link
  1661. if ($all_marked_read)
  1662. {
  1663. if ($post_unread)
  1664. {
  1665. $template->assign_vars(array(
  1666. 'U_VIEW_UNREAD_POST' => '#unread',
  1667. ));
  1668. }
  1669. else if (isset($topic_tracking_info[$topic_id]) && $topic_data['topic_last_post_time'] > $topic_tracking_info[$topic_id])
  1670. {
  1671. $template->assign_vars(array(
  1672. 'U_VIEW_UNREAD_POST' => append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&amp;t=$topic_id&amp;view=unread") . '#unread',
  1673. ));
  1674. }
  1675. }
  1676. else if (!$all_marked_read)
  1677. {
  1678. $last_page = ((floor($start / $config['posts_per_page']) + 1) == max(ceil($total_posts / $config['posts_per_page']), 1)) ? true : false;
  1679. // What can happen is that we are at the last displayed page. If so, we also display the #unread link based in $post_unread
  1680. if ($last_page && $post_unread)
  1681. {
  1682. $template->assign_vars(array(
  1683. 'U_VIEW_UNREAD_POST' => '#unread',
  1684. ));
  1685. }
  1686. else if (!$last_page)
  1687. {
  1688. $template->assign_vars(array(
  1689. 'U_VIEW_UNREAD_POST' => append_sid("{$phpbb_root_path}viewtopic.$phpEx", "f=$forum_id&amp;t=$topic_id&amp;view=unread") . '#unread',
  1690. ));
  1691. }
  1692. }
  1693. // let's set up quick_reply
  1694. $s_quick_reply = false;
  1695. if ($user->data['is_registered'] && $config['allow_quick_reply'] && ($topic_data['forum_flags'] & FORUM_FLAG_QUICK_REPLY) && $auth->acl_get('f_reply', $forum_id))
  1696. {
  1697. // Quick reply enabled forum
  1698. $s_quick_reply = (($topic_data['forum_status'] == ITEM_UNLOCKED && $topic_data['topic_status'] == ITEM_UNLOCKED) || $auth->acl_get('m_edit', $forum_id)) ? true : false;
  1699. }
  1700. if ($s_can_vote || $s_quick_reply)
  1701. {
  1702. add_form_key('posting');
  1703. if ($s_quick_reply)
  1704. {
  1705. $s_attach_sig = $config['allow_sig'] && $user->optionget('attachsig') && $auth->acl_get('f_sigs', $forum_id) && $auth->acl_get('u_sig');
  1706. $s_smilies = $config['allow_smilies'] && $user->optionget('smilies') && $auth->acl_get('f_smilies', $forum_id);
  1707. $s_bbcode = $config['allow_bbcode'] && $user->optionget('bbcode') && $auth->acl_get('f_bbcode', $forum_id);
  1708. $s_notify = $config['allow_topic_notify'] && ($user->data['user_notify'] || $s_watching_topic['is_watching']);
  1709. $qr_hidden_fields = array(
  1710. 'topic_cur_post_id' => (int) $topic_data['topic_last_post_id'],
  1711. 'lastclick' => (int) time(),
  1712. 'topic_id' => (int) $topic_data['topic_id'],
  1713. 'forum_id' => (int) $forum_id,
  1714. );
  1715. // Originally we use checkboxes and check with isset(), so we only provide them if they would be checked
  1716. (!$s_bbcode) ? $qr_hidden_fields['disable_bbcode'] = 1 : true;
  1717. (!$s_smilies) ? $qr_hidden_fields['disable_smilies'] = 1 : true;
  1718. (!$config['allow_post_links']) ? $qr_hidden_fields['disable_magic_url'] = 1 : true;
  1719. ($s_attach_sig) ? $qr_hidden_fields['attach_sig'] = 1 : true;
  1720. ($s_notify) ? $qr_hidden_fields['notify'] = 1 : true;
  1721. ($topic_data['topic_status'] == ITEM_LOCKED) ? $qr_hidden_fields['lock_topic'] = 1 : true;
  1722. $template->assign_vars(array(
  1723. 'S_QUICK_REPLY' => true,
  1724. 'U_QR_ACTION' => append_sid("{$phpbb_root_path}posting.$phpEx", "mode=reply&amp;f=$forum_id&amp;t=$topic_id"),
  1725. 'QR_HIDDEN_FIELDS' => build_hidden_fields($qr_hidden_fields),
  1726. 'SUBJECT' => 'Re: ' . censor_text($topic_data['topic_title']),
  1727. ));
  1728. }
  1729. }
  1730. // now I have the urge to wash my hands :(
  1731. // We overwrite $_REQUEST['f'] if there is no forum specified
  1732. // to be able to display the correct online list.
  1733. // One downside is that the user currently viewing this topic/post is not taken into account.
  1734. if (!request_var('f', 0))
  1735. {
  1736. $request->overwrite('f', $forum_id);
  1737. }
  1738. // We need to do the same with the topic_id. See #53025.
  1739. if (!request_var('t', 0) && !empty($topic_id))
  1740. {
  1741. $request->overwrite('t', $topic_id);
  1742. }
  1743. $page_title = $topic_data['topic_title'] . ($start ? ' - ' . sprintf($user->lang['PAGE_TITLE_NUMBER'], $pagination->get_on_page($config['posts_per_page'], $start)) : '');
  1744. /**
  1745. * You can use this event to modify the page title of the viewtopic page
  1746. *
  1747. * @event core.viewtopic_modify_page_title
  1748. * @var string page_title Title of the viewtopic page
  1749. * @var array topic_data Array with topic data
  1750. * @var int forum_id Forum ID of the topic
  1751. * @var int start Start offset used to calculate the page
  1752. * @since 3.1.0-a1
  1753. */
  1754. $vars = array('page_title', 'topic_data', 'forum_id', 'start');
  1755. extract($phpbb_dispatcher->trigger_event('core.viewtopic_modify_page_title', compact($vars)));
  1756. // Output the page
  1757. page_header($page_title, true, $forum_id);
  1758. $template->set_filenames(array(
  1759. 'body' => ($view == 'print') ? 'viewtopic_print.html' : 'viewtopic_body.html')
  1760. );
  1761. make_jumpbox(append_sid("{$phpbb_root_path}viewforum.$phpEx"), $forum_id);
  1762. page_footer();