Guest User

Untitled

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