topic::main()   F
last analyzed

Complexity

Conditions 449
Paths 0

Duplication

Lines 0
Ratio 0 %

Size

Total Lines 1807
Code Lines 993

Importance

Changes 0
Metric Value
dl 0
loc 1807
rs 2
c 0
b 0
f 0
cc 449
eloc 993
nc 0
nop 3

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
* This file is part of the VinaBB.vn package.
4
*
5
* @copyright (c) VinaBB <vinabb.vn>
6
* @license GNU General Public License, version 2 (GPL-2.0)
7
*/
8
9
namespace vinabb\web\controllers\board;
10
11
use vinabb\web\includes\constants;
12
13
/**
14
* Controller for the topic page
15
*/
16
class topic implements topic_interface
17
{
18
	/** @var \phpbb\auth\auth $auth */
19
	protected $auth;
20
21
	/** @var \phpbb\cache\service $cache */
22
	protected $cache;
23
24
	/** @var \phpbb\config\config $config */
25
	protected $config;
26
27
	/** @var \phpbb\content_visibility $content_visibility */
28
	protected $content_visibility;
29
30
	/** @var \phpbb\db\driver\driver_interface $db */
31
	protected $db;
32
33
	/** @var \phpbb\language\language $language */
34
	protected $language;
35
36
	/** @var \vinabb\web\controllers\pagination $pagination */
37
	protected $pagination;
38
39
	/** @var \phpbb\profilefields\manager $profile_fields */
40
	protected $profile_fields;
41
42
	/** @var \phpbb\request\request $request */
43
	protected $request;
44
45
	/** @var \phpbb\template\template $template */
46
	protected $template;
47
48
	/** @var \phpbb\user $user */
49
	protected $user;
50
51
	/** @var \phpbb\controller\helper $helper */
52
	protected $helper;
53
54
	/** @var \vinabb\web\controllers\helper\helper_interface $ext_helper */
55
	protected $ext_helper;
56
57
	/** @var string $root_path */
58
	protected $root_path;
59
60
	/** @var string $php_ext */
61
	protected $php_ext;
62
63
	/**
64
	* Constructor
65
	*
66
	* @param \phpbb\auth\auth									$auth				Authentication object
67
	* @param \phpbb\cache\service								$cache				Cache service
68
	* @param \phpbb\config\config								$config				Config object
69
	* @param \phpbb\content_visibility							$content_visibility	Content visibility
70
	* @param \phpbb\db\driver\driver_interface					$db					Database object
71
	* @param \phpbb\language\language							$language			Language object
72
	* @param \vinabb\web\controllers\pagination					$pagination			Pagination object
73
	* @param \phpbb\profilefields\manager						$profile_fields		Profile field manager
74
	* @param \phpbb\request\request								$request			Request object
75
	* @param \phpbb\template\template							$template			Template object
76
	* @param \phpbb\user										$user				User object
77
	* @param \phpbb\controller\helper							$helper				Controller helper
78
	* @param \vinabb\web\controllers\helper\helper_interface	$ext_helper			Extension helper
79
	* @param string												$root_path			phpBB root path
80
	* @param string												$php_ext			PHP file extension
81
	*/
82
	public function __construct(
83
		\phpbb\auth\auth $auth,
84
		\phpbb\cache\service $cache,
85
		\phpbb\config\config $config,
86
		\phpbb\content_visibility $content_visibility,
87
		\phpbb\db\driver\driver_interface $db,
88
		\phpbb\language\language $language,
89
		\vinabb\web\controllers\pagination $pagination,
90
		\phpbb\profilefields\manager $profile_fields,
91
		\phpbb\request\request $request,
92
		\phpbb\template\template $template,
93
		\phpbb\user $user,
94
		\phpbb\controller\helper $helper,
95
		\vinabb\web\controllers\helper\helper_interface $ext_helper,
96
		$root_path,
97
		$php_ext
98
	)
99
	{
100
		$this->auth = $auth;
101
		$this->cache = $cache;
102
		$this->config = $config;
103
		$this->content_visibility = $content_visibility;
104
		$this->db = $db;
105
		$this->language = $language;
106
		$this->pagination = $pagination;
107
		$this->profile_fields = $profile_fields;
108
		$this->request = $request;
109
		$this->template = $template;
110
		$this->user = $user;
111
		$this->helper = $helper;
112
		$this->ext_helper = $ext_helper;
113
		$this->root_path = $root_path;
114
		$this->php_ext = $php_ext;
115
	}
116
117
	/**
118
	* Main method
119
	*
120
	* @param int	$forum_id	Forum ID
121
	* @param int	$topic_id	Topic ID
122
	* @param string	$page		Page number
123
	*
124
	* @return \Symfony\Component\HttpFoundation\Response
125
	*/
126
	public function main($forum_id, $topic_id, $page)
127
	{
128
		global $_SID, $_EXTRA_URL;
129
130
		// Common functions
131
		include "{$this->root_path}includes/functions_display.{$this->php_ext}";
132
		include "{$this->root_path}includes/bbcode.{$this->php_ext}";
133
		include "{$this->root_path}includes/functions_user.{$this->php_ext}";
134
135
		$forum_id = ($forum_id == constants::REWRITE_URL_FORUM_ZERO) ? 0 : $forum_id;
136
		$page = max(1, floor(str_replace(constants::REWRITE_URL_PAGE, '', $page)));
137
138
		// Initial var setup
139
		$post_id = $this->request->variable('p', 0);
140
		$voted_id = $this->request->variable('vote_id', ['' => 0]);
141
		$voted_id = (count($voted_id) > 1) ? array_unique($voted_id) : $voted_id;
142
143
		$start = floor(($page - 1) * $this->config['posts_per_page']);
144
		$view = $this->request->variable('view', '');
145
146
		$sort_days = $this->request->variable('st', $this->user->data['user_post_show_days']);
147
		$sort_key = $this->request->variable('sk', $this->user->data['user_post_sortby_type']);
148
		$sort_dir = $this->request->variable('sd', $this->user->data['user_post_sortby_dir']);
149
150
		$update = $this->request->variable('update', false);
151
152
		$s_can_vote = false;
153
		$hilit_words = $this->request->variable('hilit', '', true);
154
155
		// Do we have a topic or post id?
156
		if (!$topic_id && !$post_id)
157
		{
158
			trigger_error('NO_TOPIC');
159
		}
160
161
		// Find topic id if user requested a newer or older topic
162
		if ($view && !$post_id)
163
		{
164
			if (!$forum_id)
165
			{
166
				$sql = 'SELECT forum_id
167
					FROM ' . TOPICS_TABLE . "
168
					WHERE topic_id = $topic_id";
169
				$result = $this->db->sql_query($sql);
170
				$forum_id = (int) $this->db->sql_fetchfield('forum_id');
171
				$this->db->sql_freeresult($result);
172
173
				if (!$forum_id)
174
				{
175
					trigger_error('NO_TOPIC');
176
				}
177
			}
178
179
			if ($view == 'unread')
180
			{
181
				// Get topic tracking info
182
				$topic_tracking_info = get_complete_topic_tracking($forum_id, $topic_id);
183
				$topic_last_read = (isset($topic_tracking_info[$topic_id])) ? $topic_tracking_info[$topic_id] : 0;
184
185
				$sql = 'SELECT post_id, topic_id, forum_id
186
					FROM ' . POSTS_TABLE . "
187
					WHERE topic_id = $topic_id
188
						AND " . $this->content_visibility->get_visibility_sql('post', $forum_id) . "
189
						AND post_time > $topic_last_read
190
						AND forum_id = $forum_id
191
					ORDER BY post_time, post_id";
192
				$result = $this->db->sql_query_limit($sql, 1);
193
				$row = $this->db->sql_fetchrow($result);
194
				$this->db->sql_freeresult($result);
195
196
				if (!$row)
197
				{
198
					$sql = 'SELECT topic_last_post_id as post_id, topic_id, forum_id
199
						FROM ' . TOPICS_TABLE . '
200
						WHERE topic_id = ' . $topic_id;
201
					$result = $this->db->sql_query($sql);
202
					$row = $this->db->sql_fetchrow($result);
203
					$this->db->sql_freeresult($result);
204
				}
205
206
				if (!$row)
207
				{
208
					// Setup user environment so we can process lang string
209
					$this->language->add_lang('viewtopic');
210
211
					trigger_error('NO_TOPIC');
212
				}
213
214
				$post_id = $row['post_id'];
215
				$topic_id = $row['topic_id'];
216
			}
217
			else if ($view == 'next' || $view == 'previous')
218
			{
219
				$sql_condition = ($view == 'next') ? '>' : '<';
220
				$sql_ordering = ($view == 'next') ? 'ASC' : 'DESC';
221
222
				$sql = 'SELECT forum_id, topic_last_post_time
223
					FROM ' . TOPICS_TABLE . '
224
					WHERE topic_id = ' . $topic_id;
225
				$result = $this->db->sql_query($sql);
226
				$row = $this->db->sql_fetchrow($result);
227
				$this->db->sql_freeresult($result);
228
229
				if (!$row)
230
				{
231
					$this->language->add_lang('viewtopic');
232
233
					// OK, the topic doesn't exist. This error message is not helpful, but technically correct.
234
					trigger_error(($view == 'next') ? 'NO_NEWER_TOPICS' : 'NO_OLDER_TOPICS');
235
				}
236
				else
237
				{
238
					$sql = 'SELECT topic_id, forum_id
239
						FROM ' . TOPICS_TABLE . '
240
						WHERE forum_id = ' . $row['forum_id'] . "
241
							AND topic_moved_id = 0
242
							AND topic_last_post_time $sql_condition {$row['topic_last_post_time']}
243
							AND " . $this->content_visibility->get_visibility_sql('topic', $row['forum_id']) . "
244
						ORDER BY topic_last_post_time $sql_ordering, topic_last_post_id $sql_ordering";
245
					$result = $this->db->sql_query_limit($sql, 1);
246
					$row = $this->db->sql_fetchrow($result);
247
					$this->db->sql_freeresult($result);
248
249
					if (!$row)
250
					{
251
						$sql = 'SELECT forum_style
252
							FROM ' . FORUMS_TABLE . "
253
							WHERE forum_id = $forum_id";
254
						$result = $this->db->sql_query($sql);
255
						$forum_style = (int) $this->db->sql_fetchfield('forum_style');
256
						$this->db->sql_freeresult($result);
257
258
						$this->user->setup('viewtopic', $forum_style);
259
						trigger_error(($view == 'next') ? 'NO_NEWER_TOPICS' : 'NO_OLDER_TOPICS');
260
					}
261
					else
262
					{
263
						$topic_id = $row['topic_id'];
264
						$forum_id = $row['forum_id'];
265
					}
266
				}
267
			}
268
269
			if (isset($row) && $row['forum_id'])
270
			{
271
				$forum_id = $row['forum_id'];
272
			}
273
		}
274
275
		// This rather complex gaggle of code handles querying for topics but
276
		// also allows for direct linking to a post (and the calculation of which
277
		// page the post is on and the correct display of viewtopic)
278
		$sql_array = [
279
			'SELECT'	=> 't.*, f.*',
280
			'FROM'		=> [FORUMS_TABLE => 'f'],
281
		];
282
283
		// The FROM-Order is quite important here, else t.* columns can not be correctly bound.
284
		if ($post_id)
285
		{
286
			$sql_array['SELECT'] .= ', p.post_visibility, p.post_time, p.post_id';
287
			$sql_array['FROM'][POSTS_TABLE] = 'p';
288
		}
289
290
		// Topics table need to be the last in the chain
291
		$sql_array['FROM'][TOPICS_TABLE] = 't';
292
293
		if ($this->user->data['is_registered'])
294
		{
295
			$sql_array['SELECT'] .= ', tw.notify_status';
296
			$sql_array['LEFT_JOIN'] = [];
297
298
			$sql_array['LEFT_JOIN'][] = [
299
				'FROM'	=> [TOPICS_WATCH_TABLE => 'tw'],
300
				'ON'	=> 'tw.user_id = ' . $this->user->data['user_id'] . ' AND t.topic_id = tw.topic_id'
301
			];
302
303
			if ($this->config['allow_bookmarks'])
304
			{
305
				$sql_array['SELECT'] .= ', bm.topic_id as bookmarked';
306
				$sql_array['LEFT_JOIN'][] = [
307
					'FROM'	=> [BOOKMARKS_TABLE => 'bm'],
308
					'ON'	=> 'bm.user_id = ' . $this->user->data['user_id'] . ' AND t.topic_id = bm.topic_id'
309
				];
310
			}
311
312
			if ($this->config['load_db_lastread'])
313
			{
314
				$sql_array['SELECT'] .= ', tt.mark_time, ft.mark_time as forum_mark_time';
315
316
				$sql_array['LEFT_JOIN'][] = [
317
					'FROM'	=> [TOPICS_TRACK_TABLE => 'tt'],
318
					'ON'	=> 'tt.user_id = ' . $this->user->data['user_id'] . ' AND t.topic_id = tt.topic_id'
319
				];
320
321
				$sql_array['LEFT_JOIN'][] = [
322
					'FROM'	=> [FORUMS_TRACK_TABLE => 'ft'],
323
					'ON'	=> 'ft.user_id = ' . $this->user->data['user_id'] . ' AND t.forum_id = ft.forum_id'
324
				];
325
			}
326
		}
327
328
		if (!$post_id)
329
		{
330
			$sql_array['WHERE'] = "t.topic_id = $topic_id";
331
		}
332
		else
333
		{
334
			$sql_array['WHERE'] = "p.post_id = $post_id AND t.topic_id = p.topic_id";
335
		}
336
337
		$sql_array['WHERE'] .= ' AND f.forum_id = t.forum_id';
338
339
		$sql = $this->db->sql_build_query('SELECT', $sql_array);
340
		$result = $this->db->sql_query($sql);
341
		$topic_data = $this->db->sql_fetchrow($result);
342
		$this->db->sql_freeresult($result);
343
344
		// link to unapproved post or incorrect link
345
		if (!$topic_data)
346
		{
347
			// If post_id was submitted, we try at least to display the topic as a last resort...
348
			if ($post_id && $topic_id)
349
			{
350
				redirect($this->helper->route('vinabb_web_board_topic_route', ['forum_id' => (($forum_id) ? $forum_id : constants::REWRITE_URL_FORUM_ZERO), 'topic_id' => $topic_id]));
351
			}
352
353
			trigger_error('NO_TOPIC');
354
		}
355
356
		$forum_id = (int) $topic_data['forum_id'];
357
358
		// Now we know the forum_id and can check the permissions
359
		if ($topic_data['topic_visibility'] != ITEM_APPROVED && !$this->auth->acl_get('m_approve', $forum_id))
360
		{
361
			trigger_error('NO_TOPIC');
362
		}
363
364
		// This is for determining where we are (page)
365
		if ($post_id)
366
		{
367
			// Are we where we are supposed to be?
368
			if (($topic_data['post_visibility'] == ITEM_UNAPPROVED || $topic_data['post_visibility'] == ITEM_REAPPROVE) && !$this->auth->acl_get('m_approve', $topic_data['forum_id']))
369
			{
370
				// If post_id was submitted, we try at least to display the topic as a last resort...
371
				if ($topic_id)
372
				{
373
					redirect($this->helper->route('vinabb_web_board_topic_route', ['forum_id' => (($forum_id) ? $forum_id : constants::REWRITE_URL_FORUM_ZERO), 'topic_id' => $topic_id]));
374
				}
375
376
				trigger_error('NO_TOPIC');
377
			}
378
			if ($post_id == $topic_data['topic_first_post_id'] || $post_id == $topic_data['topic_last_post_id'])
379
			{
380
				$check_sort = ($post_id == $topic_data['topic_first_post_id']) ? 'd' : 'a';
381
382
				if ($sort_dir == $check_sort)
383
				{
384
					$topic_data['prev_posts'] = $this->content_visibility->get_count('topic_posts', $topic_data, $forum_id) - 1;
385
				}
386
				else
387
				{
388
					$topic_data['prev_posts'] = 0;
389
				}
390
			}
391
			else
392
			{
393
				$sql = 'SELECT COUNT(p.post_id) AS prev_posts
394
					FROM ' . POSTS_TABLE . " p
395
					WHERE p.topic_id = {$topic_data['topic_id']}
396
						AND " . $this->content_visibility->get_visibility_sql('post', $forum_id, 'p.');
397
398
				if ($sort_dir == 'd')
399
				{
400
					$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']}))";
401
				}
402
				else
403
				{
404
					$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']}))";
405
				}
406
407
				$result = $this->db->sql_query($sql);
408
				$row = $this->db->sql_fetchrow($result);
409
				$this->db->sql_freeresult($result);
410
411
				$topic_data['prev_posts'] = $row['prev_posts'] - 1;
412
			}
413
		}
414
415
		$topic_id = (int) $topic_data['topic_id'];
416
		$topic_replies = $this->content_visibility->get_count('topic_posts', $topic_data, $forum_id) - 1;
417
418
		// Check sticky/announcement time limit
419
		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())
420
		{
421
			$sql = 'UPDATE ' . TOPICS_TABLE . '
422
				SET topic_type = ' . POST_NORMAL . ', topic_time_limit = 0
423
				WHERE topic_id = ' . $topic_id;
424
			$this->db->sql_query($sql);
425
426
			$topic_data['topic_type'] = POST_NORMAL;
427
			$topic_data['topic_time_limit'] = 0;
428
		}
429
430
		// Setup look and feel
431
		$this->user->setup('viewtopic', $topic_data['forum_style']);
432
433
		$overrides_f_read_check = false;
434
		$overrides_forum_password_check = false;
435
		$topic_tracking_info = isset($topic_tracking_info) ? $topic_tracking_info : null;
436
437
		// Start auth check
438
		if (!$overrides_f_read_check && !$this->auth->acl_get('f_read', $forum_id))
439
		{
440
			if ($this->user->data['user_id'] != ANONYMOUS)
441
			{
442
				send_status_line(403, 'Forbidden');
443
				trigger_error('SORRY_AUTH_READ');
444
			}
445
446
			login_box('', $this->language->lang('LOGIN_VIEWFORUM'));
447
		}
448
449
		// Forum is passworded ... check whether access has been granted to this
450
		// user this session, if not show login box
451
		if (!$overrides_forum_password_check && $topic_data['forum_password'])
452
		{
453
			login_forum_box($topic_data);
454
		}
455
456
		// Redirect to login upon emailed notification links if user is not logged in.
457
		if (isset($_GET['e']) && $this->user->data['user_id'] == ANONYMOUS)
458
		{
459
			login_box(build_url('e') . '#unread', $this->language->lang('LOGIN_NOTIFY_TOPIC'));
460
		}
461
462
		// What is start equal to?
463
		if ($post_id)
464
		{
465
			$start = floor(($topic_data['prev_posts']) / $this->config['posts_per_page']) * $this->config['posts_per_page'];
466
		}
467
468
		// Get topic tracking info
469
		if (!isset($topic_tracking_info))
470
		{
471
			$topic_tracking_info = [];
472
473
			// Get topic tracking info
474
			if ($this->config['load_db_lastread'] && $this->user->data['is_registered'])
475
			{
476
				$tmp_topic_data = [$topic_id => $topic_data];
477
				$topic_tracking_info = get_topic_tracking($forum_id, $topic_id, $tmp_topic_data, [$forum_id => $topic_data['forum_mark_time']]);
478
479
				unset($tmp_topic_data);
480
			}
481
			else if ($this->config['load_anon_lastread'] || $this->user->data['is_registered'])
482
			{
483
				$topic_tracking_info = get_complete_topic_tracking($forum_id, $topic_id);
484
			}
485
		}
486
487
		// Post ordering options
488
		$limit_days = [
489
			0	=> $this->language->lang('ALL_POSTS'),
490
			1	=> $this->language->lang('1_DAY'),
491
			7	=> $this->language->lang('7_DAYS'),
492
			14	=> $this->language->lang('2_WEEKS'),
493
			30	=> $this->language->lang('1_MONTH'),
494
			90	=> $this->language->lang('3_MONTHS'),
495
			180	=> $this->language->lang('6_MONTHS'),
496
			365	=> $this->language->lang('1_YEAR')
497
		];
498
499
		$sort_by_text = [
500
			'a'	=> $this->language->lang('AUTHOR'),
501
			't'	=> $this->language->lang('POST_TIME'),
502
			's'	=> $this->language->lang('SUBJECT')
503
		];
504
505
		$sort_by_sql = [
506
			'a'	=> ['u.username_clean', 'p.post_id'],
507
			't'	=> ['p.post_time', 'p.post_id'],
508
			's'	=> ['p.post_subject', 'p.post_id']
509
		];
510
511
		$join_user_sql = ['a' => true, 't' => false, 's' => false];
512
		$s_limit_days = $s_sort_key = $s_sort_dir = $u_sort_param = '';
513
514
		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, $this->user->data['user_post_show_days'], $this->user->data['user_post_sortby_type'], $this->user->data['user_post_sortby_dir']);
515
516
		// Convert $u_sort_param from string to array
517
		$u_sort_param_ary = [];
518
519
		if ($u_sort_param != '')
520
		{
521
			$u_sort_param = htmlspecialchars_decode($u_sort_param);
522
			$u_sort_param_raw_ary = explode('&', $u_sort_param);
523
524
			foreach ($u_sort_param_raw_ary as $u_sort_param_raw)
525
			{
526
				list($u_sort_param_raw_key, $u_sort_param_raw_value) = explode('=', $u_sort_param_raw);
527
				$u_sort_param_ary[$u_sort_param_raw_key] = $u_sort_param_raw_value;
528
			}
529
		}
530
531
		// Obtain correct post count and ordering SQL if user has
532
		// requested anything different
533
		if ($sort_days)
534
		{
535
			$min_post_time = time() - ($sort_days * 86400);
536
537
			$sql = 'SELECT COUNT(post_id) AS num_posts
538
				FROM ' . POSTS_TABLE . "
539
				WHERE topic_id = $topic_id
540
					AND post_time >= $min_post_time
541
					AND " . $this->content_visibility->get_visibility_sql('post', $forum_id);
542
			$result = $this->db->sql_query($sql);
543
			$total_posts = (int) $this->db->sql_fetchfield('num_posts');
544
			$this->db->sql_freeresult($result);
545
546
			$limit_posts_time = "AND p.post_time >= $min_post_time ";
547
548
			if ($this->request->is_set_post('sort'))
549
			{
550
				$start = 0;
551
			}
552
		}
553
		else
554
		{
555
			$total_posts = $topic_replies + 1;
556
			$limit_posts_time = '';
557
		}
558
559
		// Was a highlight request part of the URI?
560
		$highlight_match = $highlight = '';
561
562
		if ($hilit_words)
563
		{
564
			$highlight_match = phpbb_clean_search_string($hilit_words);
565
			$highlight = urlencode($highlight_match);
566
			$highlight_match = str_replace('\*', '\w+?', preg_quote($highlight_match, '#'));
567
			$highlight_match = preg_replace('#(?<=^|\s)\\\\w\*\?(?=\s|$)#', '\w+?', $highlight_match);
568
			$highlight_match = str_replace(' ', '|', $highlight_match);
569
		}
570
571
		// Make sure $start is set to the last page if it exceeds the amount
572
		$start = $this->pagination->validate_start($start, $this->config['posts_per_page'], $total_posts);
573
574
		// General Viewtopic URL for return links
575
		$viewtopic_url = append_sid("{$this->root_path}viewtopic.{$this->php_ext}", "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" : ''));
576
577
		// Are we watching this topic?
578
		$s_watching_topic = [
579
			'link'			=> '',
580
			'link_toggle'	=> '',
581
			'title'			=> '',
582
			'title_toggle'	=> '',
583
			'is_watching'	=> false
584
		];
585
586
		if ($this->config['allow_topic_notify'])
587
		{
588
			$notify_status = (isset($topic_data['notify_status'])) ? $topic_data['notify_status'] : null;
589
			watch_topic_forum('topic', $s_watching_topic, $this->user->data['user_id'], $forum_id, $topic_id, $notify_status, $start, $topic_data['topic_title']);
590
591
			// Reset forum notification if forum notify is set
592
			if ($this->config['allow_forum_notify'] && $this->auth->acl_get('f_subscribe', $forum_id))
593
			{
594
				$s_watching_forum = $s_watching_topic;
595
				watch_topic_forum('forum', $s_watching_forum, $this->user->data['user_id'], $forum_id, 0);
596
			}
597
		}
598
599
		// Bookmarks
600
		if ($this->config['allow_bookmarks'] && $this->user->data['is_registered'] && $this->request->variable('bookmark', 0))
601
		{
602
			$this->bookmark($topic_id, $viewtopic_url, $topic_data['bookmarked']);
603
		}
604
605
		// Grab icons
606
		$icons = $this->cache->obtain_icons();
607
608
		// Forum rules listing
609
		gen_forum_auth_level('topic', $forum_id, $topic_data['forum_status']);
610
611
		// Quick mod tools
612
		$allow_change_type = ($this->auth->acl_get('m_', $forum_id) || ($this->user->data['is_registered'] && $this->user->data['user_id'] == $topic_data['topic_poster'])) ? true : false;
613
		$s_quickmod_action = $this->helper->route('vinabb_web_mcp_route', ['id' => 'main', 'mode' => 'quickmod', 'quickmod' => 1, 'f' => $forum_id, 't' => $topic_id, 'start' => $start, 'redirect' => urlencode(str_replace('&amp;', '&', $viewtopic_url))], true, $this->user->session_id);
614
615
		$quickmod_array = [
616
			'lock'			=> ['LOCK_TOPIC', ($topic_data['topic_status'] == ITEM_UNLOCKED) && ($this->auth->acl_get('m_lock', $forum_id) || ($this->auth->acl_get('f_user_lock', $forum_id) && $this->user->data['is_registered'] && $this->user->data['user_id'] == $topic_data['topic_poster']))],
617
			'unlock'		=> ['UNLOCK_TOPIC', ($topic_data['topic_status'] != ITEM_UNLOCKED) && ($this->auth->acl_get('m_lock', $forum_id))],
618
			'delete_topic'	=> ['DELETE_TOPIC', ($this->auth->acl_get('m_delete', $forum_id) || (($topic_data['topic_visibility'] != ITEM_DELETED) && $this->auth->acl_get('m_softdelete', $forum_id)))],
619
			'restore_topic'	=> ['RESTORE_TOPIC', (($topic_data['topic_visibility'] == ITEM_DELETED) && $this->auth->acl_get('m_approve', $forum_id))],
620
			'move'			=> ['MOVE_TOPIC', $this->auth->acl_get('m_move', $forum_id) && $topic_data['topic_status'] != ITEM_MOVED],
621
			'split'			=> ['SPLIT_TOPIC', $this->auth->acl_get('m_split', $forum_id)],
622
			'merge'			=> ['MERGE_POSTS', $this->auth->acl_get('m_merge', $forum_id)],
623
			'merge_topic'	=> ['MERGE_TOPIC', $this->auth->acl_get('m_merge', $forum_id)],
624
			'fork'			=> ['FORK_TOPIC', $this->auth->acl_get('m_move', $forum_id)],
625
			'make_normal'	=> ['MAKE_NORMAL', ($allow_change_type && $this->auth->acl_gets('f_sticky', 'f_announce', 'f_announce_global', $forum_id) && $topic_data['topic_type'] != POST_NORMAL)],
626
			'make_sticky'	=> ['MAKE_STICKY', ($allow_change_type && $this->auth->acl_get('f_sticky', $forum_id) && $topic_data['topic_type'] != POST_STICKY)],
627
			'make_announce'	=> ['MAKE_ANNOUNCE', ($allow_change_type && $this->auth->acl_get('f_announce', $forum_id) && $topic_data['topic_type'] != POST_ANNOUNCE)],
628
			'make_global'	=> ['MAKE_GLOBAL', ($allow_change_type && $this->auth->acl_get('f_announce_global', $forum_id) && $topic_data['topic_type'] != POST_GLOBAL)],
629
			'topic_logs'	=> ['VIEW_TOPIC_LOGS', $this->auth->acl_get('m_', $forum_id)]
630
		];
631
632
		foreach ($quickmod_array as $option => $qm_ary)
633
		{
634
			if (!empty($qm_ary[1]))
635
			{
636
				phpbb_add_quickmod_option($s_quickmod_action, $option, $qm_ary[0]);
637
			}
638
		}
639
640
		// Navigation links
641
		generate_forum_nav($topic_data);
642
643
		// Forum Rules
644
		generate_forum_rules($topic_data);
645
646
		// Moderators
647
		$forum_moderators = [];
648
649
		if ($this->config['load_moderators'])
650
		{
651
			get_moderators($forum_moderators, $forum_id);
652
		}
653
654
		// This is only used for print view so ...
655
		$server_path = (!$view) ? $this->root_path : generate_board_url() . '/';
656
657
		// Replace naughty words in title
658
		$topic_data['topic_title'] = censor_text($topic_data['topic_title']);
659
660
		$s_search_hidden_fields = [
661
			't'		=> $topic_id,
662
			'sf'	=> 'msgonly'
663
		];
664
665
		if ($_SID)
666
		{
667
			$s_search_hidden_fields['sid'] = $_SID;
668
		}
669
670
		if (!empty($_EXTRA_URL))
671
		{
672
			foreach ($_EXTRA_URL as $url_param)
673
			{
674
				$url_param = explode('=', $url_param, 2);
675
				$s_search_hidden_fields[$url_param[0]] = $url_param[1];
676
			}
677
		}
678
679
		$pagination_params = [
680
			'forum_id'	=> $forum_id,
681
			'topic_id'	=> $topic_id,
682
			'seo'		=> $topic_data['topic_title_seo'] . constants::REWRITE_URL_SEO
683
		];
684
685
		if (!empty($u_sort_param_ary))
686
		{
687
			$pagination_params = array_merge($pagination_params, $u_sort_param_ary);
688
		}
689
690
		// If we've got a hightlight set pass it on to pagination
691
		if ($highlight_match)
692
		{
693
			$pagination_params['hilit'] = $highlight;
694
		}
695
696
		$this->pagination->generate_template_pagination('vinabb_web_board_topic_route', $pagination_params, 'pagination', $total_posts, $this->config['posts_per_page'], $start);
697
698
		// Send vars to template
699
		$this->template->assign_vars([
700
				'FORUM_ID' 		=> $forum_id,
701
				'FORUM_NAME' 	=> $topic_data['forum_name'],
702
				'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']),
703
				'TOPIC_ID' 		=> $topic_id,
704
				'TOPIC_TITLE' 	=> $topic_data['topic_title'],
705
				'TOPIC_POSTER'	=> $topic_data['topic_poster'],
706
707
				'TOPIC_AUTHOR_FULL'		=> get_username_string('full', $topic_data['topic_poster'], $topic_data['topic_first_poster_name'], $topic_data['topic_first_poster_colour']),
708
				'TOPIC_AUTHOR_COLOUR'	=> get_username_string('colour', $topic_data['topic_poster'], $topic_data['topic_first_poster_name'], $topic_data['topic_first_poster_colour']),
709
				'TOPIC_AUTHOR'			=> get_username_string('username', $topic_data['topic_poster'], $topic_data['topic_first_poster_name'], $topic_data['topic_first_poster_colour']),
710
711
				'TOTAL_POSTS'	=> $this->language->lang('VIEW_TOPIC_POSTS', (int) $total_posts),
712
				'U_MCP' 		=> ($this->auth->acl_get('m_', $forum_id)) ? append_sid("{$this->root_path}mcp.{$this->php_ext}", "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, $this->user->session_id) : '',
713
				'MODERATORS'	=> !empty($forum_moderators[$forum_id]) ? implode($this->language->lang('COMMA_SEPARATOR'), $forum_moderators[$forum_id]) : '',
714
715
				'S_IS_LOCKED'			=> ($topic_data['topic_status'] == ITEM_UNLOCKED && $topic_data['forum_status'] == ITEM_UNLOCKED) ? false : true,
716
				'S_SELECT_SORT_DIR' 	=> $s_sort_dir,
717
				'S_SELECT_SORT_KEY' 	=> $s_sort_key,
718
				'S_SELECT_SORT_DAYS' 	=> $s_limit_days,
719
				'S_SINGLE_MODERATOR'	=> isset($forum_moderators[$forum_id]) && (count($forum_moderators[$forum_id]) == 1),
720
				'S_TOPIC_ACTION' 		=> append_sid("{$this->root_path}viewtopic.{$this->php_ext}", "f=$forum_id&amp;t=$topic_id" . (($start == 0) ? '' : "&amp;start=$start")),
721
				'S_MOD_ACTION' 			=> $s_quickmod_action,
722
723
				'L_RETURN_TO_FORUM'		=> $this->language->lang('RETURN_TO', $topic_data['forum_name']),
724
				'S_VIEWTOPIC'			=> true,
725
				'S_UNREAD_VIEW'			=> $view == 'unread',
726
				'S_DISPLAY_SEARCHBOX'	=> ($this->auth->acl_get('u_search') && $this->auth->acl_get('f_search', $forum_id) && $this->config['load_search']),
727
				'S_SEARCHBOX_ACTION'	=> append_sid("{$this->root_path}search.{$this->php_ext}"),
728
				'S_SEARCH_LOCAL_HIDDEN_FIELDS'	=> build_hidden_fields($s_search_hidden_fields),
729
730
				'S_DISPLAY_POST_INFO'	=> ($topic_data['forum_type'] == FORUM_POST && ($this->auth->acl_get('f_post', $forum_id) || $this->user->data['user_id'] == ANONYMOUS)),
731
				'S_DISPLAY_REPLY_INFO'	=> ($topic_data['forum_type'] == FORUM_POST && ($this->auth->acl_get('f_reply', $forum_id) || $this->user->data['user_id'] == ANONYMOUS)),
732
				'S_ENABLE_FEEDS_TOPIC'	=> ($this->config['feed_topic'] && !phpbb_optionget(FORUM_OPTION_FEED_EXCLUDE, $topic_data['forum_options'])),
733
734
				'U_TOPIC'				=> "{$server_path}viewtopic.{$this->php_ext}?f=$forum_id&amp;t=$topic_id",
735
				'U_FORUM'				=> $server_path,
736
				'U_VIEW_TOPIC' 			=> $viewtopic_url,
737
				'U_CANONICAL'			=> generate_board_url() . '/' . append_sid("viewtopic.{$this->php_ext}", "t=$topic_id" . (($start) ? "&amp;start=$start" : ''), true, ''),
738
				'U_VIEW_FORUM' 			=> $this->helper->route('vinabb_web_board_forum_route', ['forum_id' => $forum_id, 'seo' => $topic_data['forum_name_seo'] . constants::REWRITE_URL_SEO]),
739
				'U_VIEW_OLDER_TOPIC'	=> append_sid("{$this->root_path}viewtopic.{$this->php_ext}", "f=$forum_id&amp;t=$topic_id&amp;view=previous"),
740
				'U_VIEW_NEWER_TOPIC'	=> append_sid("{$this->root_path}viewtopic.{$this->php_ext}", "f=$forum_id&amp;t=$topic_id&amp;view=next"),
741
				'U_PRINT_TOPIC'			=> ($this->auth->acl_get('f_print', $forum_id)) ? $viewtopic_url . '&amp;view=print' : '',
742
				'U_EMAIL_TOPIC'			=> ($this->auth->acl_get('f_email', $forum_id) && $this->config['email_enable']) ? append_sid("{$this->root_path}memberlist.{$this->php_ext}", "mode=email&amp;t=$topic_id") : '',
743
744
				'U_WATCH_TOPIC'			=> $s_watching_topic['link'],
745
				'U_WATCH_TOPIC_TOGGLE'	=> $s_watching_topic['link_toggle'],
746
				'S_WATCH_TOPIC_TITLE'	=> $s_watching_topic['title'],
747
				'S_WATCH_TOPIC_TOGGLE'	=> $s_watching_topic['title_toggle'],
748
				'S_WATCHING_TOPIC'		=> $s_watching_topic['is_watching'],
749
750
				'U_BOOKMARK_TOPIC'		=> ($this->user->data['is_registered'] && $this->config['allow_bookmarks']) ? $viewtopic_url . '&amp;bookmark=1&amp;hash=' . generate_link_hash("topic_$topic_id") : '',
751
				'S_BOOKMARK_TOPIC'		=> ($this->user->data['is_registered'] && $this->config['allow_bookmarks'] && $topic_data['bookmarked']) ? $this->language->lang('BOOKMARK_TOPIC_REMOVE') : $this->language->lang('BOOKMARK_TOPIC'),
752
				'S_BOOKMARK_TOGGLE'		=> (!$this->user->data['is_registered'] || !$this->config['allow_bookmarks'] || !$topic_data['bookmarked']) ? $this->language->lang('BOOKMARK_TOPIC_REMOVE') : $this->language->lang('BOOKMARK_TOPIC'),
753
				'S_BOOKMARKED_TOPIC'	=> ($this->user->data['is_registered'] && $this->config['allow_bookmarks'] && $topic_data['bookmarked']),
754
755
				'U_POST_NEW_TOPIC' 		=> ($this->auth->acl_get('f_post', $forum_id) || $this->user->data['user_id'] == ANONYMOUS) ? append_sid("{$this->root_path}posting.{$this->php_ext}", "mode=post&amp;f=$forum_id") : '',
756
				'U_POST_REPLY_TOPIC' 	=> ($this->auth->acl_get('f_reply', $forum_id) || $this->user->data['user_id'] == ANONYMOUS) ? append_sid("{$this->root_path}posting.{$this->php_ext}", "mode=reply&amp;f=$forum_id&amp;t=$topic_id") : '',
757
				'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("{$this->root_path}posting.{$this->php_ext}", "mode=bump&amp;f=$forum_id&amp;t=$topic_id&amp;hash=" . generate_link_hash("topic_$topic_id")) : ''
758
		]);
759
760
		// Does this topic contain a poll?
761
		if (!empty($topic_data['poll_start']))
762
		{
763
			$sql = 'SELECT o.*, p.bbcode_bitfield, p.bbcode_uid
764
				FROM ' . POLL_OPTIONS_TABLE . ' o, ' . POSTS_TABLE . " p
765
				WHERE o.topic_id = $topic_id
766
					AND p.post_id = {$topic_data['topic_first_post_id']}
767
					AND p.topic_id = o.topic_id
768
				ORDER BY o.poll_option_id";
769
			$result = $this->db->sql_query($sql);
770
771
			$poll_info = $vote_counts = [];
772
			while ($row = $this->db->sql_fetchrow($result))
773
			{
774
				$poll_info[] = $row;
775
				$option_id = (int) $row['poll_option_id'];
776
				$vote_counts[$option_id] = (int) $row['poll_option_total'];
777
			}
778
			$this->db->sql_freeresult($result);
779
780
			$cur_voted_id = [];
781
			if ($this->user->data['is_registered'])
782
			{
783
				$sql = 'SELECT poll_option_id
784
					FROM ' . POLL_VOTES_TABLE . '
785
					WHERE topic_id = ' . $topic_id . '
786
						AND vote_user_id = ' . $this->user->data['user_id'];
787
				$result = $this->db->sql_query($sql);
788
789
				while ($row = $this->db->sql_fetchrow($result))
790
				{
791
					$cur_voted_id[] = $row['poll_option_id'];
792
				}
793
				$this->db->sql_freeresult($result);
794
			}
795
			else
796
			{
797
				// Cookie based guest tracking ... I don't like this but hum ho
798
				// it's oft requested. This relies on "nice" users who don't feel
799
				// the need to delete cookies to mess with results.
800
				if ($this->request->is_set($this->config['cookie_name'] . '_poll_' . $topic_id, \phpbb\request\request_interface::COOKIE))
801
				{
802
					$cur_voted_id = explode(',', $this->request->variable($this->config['cookie_name'] . '_poll_' . $topic_id, '', true, \phpbb\request\request_interface::COOKIE));
803
					$cur_voted_id = array_map('intval', $cur_voted_id);
804
				}
805
			}
806
807
			// Can not vote at all if no vote permission
808
			$s_can_vote = ($this->auth->acl_get('f_vote', $forum_id) &&
809
				(($topic_data['poll_length'] != 0 && $topic_data['poll_start'] + $topic_data['poll_length'] > time()) || $topic_data['poll_length'] == 0) &&
810
				$topic_data['topic_status'] != ITEM_LOCKED &&
811
				$topic_data['forum_status'] != ITEM_LOCKED &&
812
				(empty($cur_voted_id) ||
813
					($this->auth->acl_get('f_votechg', $forum_id) && $topic_data['poll_vote_change']))) ? true : false;
814
			$s_display_results = (!$s_can_vote || ($s_can_vote && !empty($cur_voted_id)) || $view == 'viewpoll') ? true : false;
815
816
			if ($update && $s_can_vote)
817
			{
818
				if (empty($voted_id) || count($voted_id) > $topic_data['poll_max_options'] || in_array(VOTE_CONVERTED, $cur_voted_id) || !check_form_key('posting'))
819
				{
820
					$redirect_url = append_sid("{$this->root_path}viewtopic.{$this->php_ext}", "f=$forum_id&amp;t=$topic_id" . (($start == 0) ? '' : "&amp;start=$start"));
821
822
					meta_refresh(5, $redirect_url);
823
					if (empty($voted_id))
824
					{
825
						$message = 'NO_VOTE_OPTION';
826
					}
827
					else if (count($voted_id) > $topic_data['poll_max_options'])
828
					{
829
						$message = 'TOO_MANY_VOTE_OPTIONS';
830
					}
831
					else if (in_array(VOTE_CONVERTED, $cur_voted_id))
832
					{
833
						$message = 'VOTE_CONVERTED';
834
					}
835
					else
836
					{
837
						$message = 'FORM_INVALID';
838
					}
839
840
					$message = $this->language->lang($message) . '<br><br>' . $this->language->lang('RETURN_TOPIC', '<a href="' . $redirect_url . '">', '</a>');
841
					trigger_error($message);
842
				}
843
844
				foreach ($voted_id as $option)
845
				{
846
					if (in_array($option, $cur_voted_id))
847
					{
848
						continue;
849
					}
850
851
					$sql = 'UPDATE ' . POLL_OPTIONS_TABLE . '
852
						SET poll_option_total = poll_option_total + 1
853
						WHERE poll_option_id = ' . (int) $option . '
854
							AND topic_id = ' . (int) $topic_id;
855
					$this->db->sql_query($sql);
856
857
					$vote_counts[$option]++;
858
859
					if ($this->user->data['is_registered'])
860
					{
861
						$sql_ary = [
862
							'topic_id'			=> (int) $topic_id,
863
							'poll_option_id'	=> (int) $option,
864
							'vote_user_id'		=> (int) $this->user->data['user_id'],
865
							'vote_user_ip'		=> (string) $this->user->ip
866
						];
867
868
						$sql = 'INSERT INTO ' . POLL_VOTES_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
869
						$this->db->sql_query($sql);
870
					}
871
				}
872
873
				foreach ($cur_voted_id as $option)
874
				{
875
					if (!in_array($option, $voted_id))
876
					{
877
						$sql = 'UPDATE ' . POLL_OPTIONS_TABLE . '
878
							SET poll_option_total = poll_option_total - 1
879
							WHERE poll_option_id = ' . (int) $option . '
880
								AND topic_id = ' . (int) $topic_id;
881
						$this->db->sql_query($sql);
882
883
						$vote_counts[$option]--;
884
885
						if ($this->user->data['is_registered'])
886
						{
887
							$sql = 'DELETE FROM ' . POLL_VOTES_TABLE . '
888
								WHERE topic_id = ' . (int) $topic_id . '
889
									AND poll_option_id = ' . (int) $option . '
890
									AND vote_user_id = ' . (int) $this->user->data['user_id'];
891
							$this->db->sql_query($sql);
892
						}
893
					}
894
				}
895
896
				if ($this->user->data['user_id'] == ANONYMOUS && !$this->user->data['is_bot'])
897
				{
898
					$this->user->set_cookie('poll_' . $topic_id, implode(',', $voted_id), time() + 31536000);
899
				}
900
901
				$sql = 'UPDATE ' . TOPICS_TABLE . '
902
					SET poll_last_vote = ' . time() . "
903
					WHERE topic_id = $topic_id";
904
				//, topic_last_post_time = ' . time() . " -- for bumping topics with new votes, ignore for now
905
				$this->db->sql_query($sql);
906
907
				$redirect_url = append_sid("{$this->root_path}viewtopic.{$this->php_ext}", "f=$forum_id&amp;t=$topic_id" . (($start == 0) ? '' : "&amp;start=$start"));
908
				$message = $this->language->lang('VOTE_SUBMITTED') . '<br><br>' . $this->language->lang('RETURN_TOPIC', '<a href="' . $redirect_url . '">', '</a>');
909
910
				if ($this->request->is_ajax())
911
				{
912
					// Filter out invalid options
913
					$valid_user_votes = array_intersect(array_keys($vote_counts), $voted_id);
914
915
					$data = [
916
						'NO_VOTES'		=> $this->language->lang('NO_VOTES'),
917
						'success'		=> true,
918
						'user_votes'	=> array_flip($valid_user_votes),
919
						'vote_counts'	=> $vote_counts,
920
						'total_votes'	=> array_sum($vote_counts),
921
						'can_vote'		=> empty($valid_user_votes) || ($this->auth->acl_get('f_votechg', $forum_id) && $topic_data['poll_vote_change'])
922
					];
923
924
					$json_response = new \phpbb\json_response();
925
					$json_response->send($data);
926
				}
927
928
				meta_refresh(5, $redirect_url);
929
				trigger_error($message);
930
			}
931
932
			$poll_total = 0;
933
			$poll_most = 0;
934
			foreach ($poll_info as $poll_option)
935
			{
936
				$poll_total += $poll_option['poll_option_total'];
937
				$poll_most = ($poll_option['poll_option_total'] >= $poll_most) ? $poll_option['poll_option_total'] : $poll_most;
938
			}
939
940
			$parse_flags = ($poll_info[0]['bbcode_bitfield'] ? OPTION_FLAG_BBCODE : 0) | OPTION_FLAG_SMILIES;
941
942
			for ($i = 0, $size = count($poll_info); $i < $size; $i++)
943
			{
944
				$poll_info[$i]['poll_option_text'] = generate_text_for_display($poll_info[$i]['poll_option_text'], $poll_info[$i]['bbcode_uid'], $poll_info['bbcode_bitfield'], $parse_flags, true);
945
			}
946
947
			$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);
948
949
			$poll_options_template_data = [];
950
			foreach ($poll_info as $poll_option)
951
			{
952
				$option_pct = ($poll_total > 0) ? $poll_option['poll_option_total'] / $poll_total : 0;
953
				$option_pct_txt = sprintf("%.1d%%", round($option_pct * 100));
954
				$option_pct_rel = ($poll_most > 0) ? $poll_option['poll_option_total'] / $poll_most : 0;
955
				$option_pct_rel_txt = sprintf("%.1d%%", round($option_pct_rel * 100));
956
				$option_most_votes = ($poll_option['poll_option_total'] > 0 && $poll_option['poll_option_total'] == $poll_most);
957
958
				$poll_options_template_data[] = [
959
					'POLL_OPTION_ID' 			=> $poll_option['poll_option_id'],
960
					'POLL_OPTION_CAPTION' 		=> $poll_option['poll_option_text'],
961
					'POLL_OPTION_RESULT' 		=> $poll_option['poll_option_total'],
962
					'POLL_OPTION_PERCENT' 		=> $option_pct_txt,
963
					'POLL_OPTION_PERCENT_REL' 	=> $option_pct_rel_txt,
964
					'POLL_OPTION_PCT'			=> round($option_pct * 100),
965
					'POLL_OPTION_WIDTH'     	=> round($option_pct * 250),
966
					'POLL_OPTION_VOTED'			=> in_array($poll_option['poll_option_id'], $cur_voted_id),
967
					'POLL_OPTION_MOST_VOTES'	=> $option_most_votes
968
				];
969
			}
970
971
			$poll_end = $topic_data['poll_length'] + $topic_data['poll_start'];
972
973
			$poll_template_data = [
974
				'POLL_QUESTION'		=> $topic_data['poll_title'],
975
				'TOTAL_VOTES' 		=> $poll_total,
976
				'POLL_LEFT_CAP_IMG'	=> $this->user->img('poll_left'),
977
				'POLL_RIGHT_CAP_IMG'=> $this->user->img('poll_right'),
978
979
				'L_MAX_VOTES'		=> $this->language->lang('MAX_OPTIONS_SELECT', (int) $topic_data['poll_max_options']),
980
				'L_POLL_LENGTH'		=> ($topic_data['poll_length']) ? $this->language->lang(($poll_end > time()) ? 'POLL_RUN_TILL' : 'POLL_ENDED_AT', $this->user->format_date($poll_end)) : '',
981
982
				'S_HAS_POLL'		=> true,
983
				'S_CAN_VOTE'		=> $s_can_vote,
984
				'S_DISPLAY_RESULTS'	=> $s_display_results,
985
				'S_IS_MULTI_CHOICE'	=> $topic_data['poll_max_options'] > 1,
986
				'S_POLL_ACTION'		=> $viewtopic_url,
987
988
				'U_VIEW_RESULTS'	=> $viewtopic_url . '&amp;view=viewpoll'
989
			];
990
991
			$this->template->assign_block_vars_array('poll_option', $poll_options_template_data);
992
			$this->template->assign_vars($poll_template_data);
993
994
			unset($poll_end, $poll_info, $poll_options_template_data, $poll_template_data, $voted_id);
995
		}
996
997
		// If the user is trying to reach the second half of the topic, fetch it starting from the end
998
		$store_reverse = false;
999
		$sql_limit = $this->config['posts_per_page'];
1000
1001
		if ($start > $total_posts / 2)
1002
		{
1003
			$store_reverse = true;
1004
1005
			// Select the sort order
1006
			$direction = (($sort_dir == 'd') ? 'ASC' : 'DESC');
1007
1008
			$sql_limit = $this->pagination->reverse_limit($start, $sql_limit, $total_posts);
1009
			$sql_start = $this->pagination->reverse_start($start, $sql_limit, $total_posts);
1010
		}
1011
		else
1012
		{
1013
			// Select the sort order
1014
			$direction = (($sort_dir == 'd') ? 'DESC' : 'ASC');
1015
			$sql_start = $start;
1016
		}
1017
1018
		if (is_array($sort_by_sql[$sort_key]))
1019
		{
1020
			$sql_sort_order = implode(' ' . $direction . ', ', $sort_by_sql[$sort_key]) . ' ' . $direction;
1021
		}
1022
		else
1023
		{
1024
			$sql_sort_order = $sort_by_sql[$sort_key] . ' ' . $direction;
1025
		}
1026
1027
		// Container for user details, only process once
1028
		$post_list = $user_cache = $id_cache = $attachments = $attach_list = $rowset = $update_count = $post_edit_list = $post_delete_list = [];
1029
		$has_unapproved_attachments = $has_approved_attachments = $display_notice = false;
1030
1031
		// Go ahead and pull all data for this topic
1032
		$sql = 'SELECT p.post_id
1033
			FROM ' . POSTS_TABLE . ' p' . (($join_user_sql[$sort_key]) ? ', ' . USERS_TABLE . ' u': '') . "
1034
			WHERE p.topic_id = $topic_id
1035
				AND " . $this->content_visibility->get_visibility_sql('post', $forum_id, 'p.') . "
1036
				" . (($join_user_sql[$sort_key]) ? 'AND u.user_id = p.poster_id': '') . "
1037
				$limit_posts_time
1038
			ORDER BY $sql_sort_order";
1039
		$result = $this->db->sql_query_limit($sql, $sql_limit, $sql_start);
1040
1041
		$i = ($store_reverse) ? $sql_limit - 1 : 0;
1042
		while ($row = $this->db->sql_fetchrow($result))
1043
		{
1044
			$post_list[$i] = (int) $row['post_id'];
1045
			($store_reverse) ? $i-- : $i++;
1046
		}
1047
		$this->db->sql_freeresult($result);
1048
1049
		if (empty($post_list))
1050
		{
1051
			if ($sort_days)
1052
			{
1053
				trigger_error('NO_POSTS_TIME_FRAME');
1054
			}
1055
			else
1056
			{
1057
				trigger_error('NO_TOPIC');
1058
			}
1059
		}
1060
1061
		// Holding maximum post time for marking topic read
1062
		// We need to grab it because we do reverse ordering sometimes
1063
		$max_post_time = 0;
1064
1065
		$sql_ary = [
1066
			'SELECT'	=> 'u.*, z.friend, z.foe, p.*',
1067
1068
			'FROM'		=> [
1069
				USERS_TABLE		=> 'u',
1070
				POSTS_TABLE		=> 'p'
1071
			],
1072
1073
			'LEFT_JOIN'	=> [
1074
				[
1075
					'FROM'	=> [ZEBRA_TABLE => 'z'],
1076
					'ON'	=> 'z.user_id = ' . $this->user->data['user_id'] . ' AND z.zebra_id = p.poster_id'
1077
				]
1078
			],
1079
1080
			'WHERE'		=> $this->db->sql_in_set('p.post_id', $post_list) . '
1081
				AND u.user_id = p.poster_id',
1082
		];
1083
		$sql = $this->db->sql_build_query('SELECT', $sql_ary);
1084
		$result = $this->db->sql_query($sql);
1085
1086
		$now = $this->user->create_datetime();
1087
		$now = phpbb_gmgetdate($now->getTimestamp() + $now->getOffset());
1088
1089
		// Posts are stored in the $rowset array while $attach_list, $user_cache
1090
		// and the global bbcode_bitfield are built
1091
		while ($row = $this->db->sql_fetchrow($result))
1092
		{
1093
			// Set max_post_time
1094
			if ($row['post_time'] > $max_post_time)
1095
			{
1096
				$max_post_time = $row['post_time'];
1097
			}
1098
1099
			$poster_id = (int) $row['poster_id'];
1100
1101
			// Does post have an attachment? If so, add it to the list
1102
			if ($row['post_attachment'] && $this->config['allow_attachments'])
1103
			{
1104
				$attach_list[] = (int) $row['post_id'];
1105
1106
				if ($row['post_visibility'] == ITEM_UNAPPROVED || $row['post_visibility'] == ITEM_REAPPROVE)
1107
				{
1108
					$has_unapproved_attachments = true;
1109
				}
1110
				else if ($row['post_visibility'] == ITEM_APPROVED)
1111
				{
1112
					$has_approved_attachments = true;
1113
				}
1114
			}
1115
1116
			$rowset_data = [
1117
				'hide_post'	=> (($row['foe'] || $row['post_visibility'] == ITEM_DELETED) && ($view != 'show' || $post_id != $row['post_id'])) ? true : false,
1118
1119
				'post_id'			=> $row['post_id'],
1120
				'post_time'			=> $row['post_time'],
1121
				'user_id'			=> $row['user_id'],
1122
				'username'			=> $row['username'],
1123
				'user_colour'		=> $row['user_colour'],
1124
				'topic_id'			=> $row['topic_id'],
1125
				'forum_id'			=> $row['forum_id'],
1126
				'post_subject'		=> $row['post_subject'],
1127
				'post_edit_count'	=> $row['post_edit_count'],
1128
				'post_edit_time'	=> $row['post_edit_time'],
1129
				'post_edit_reason'	=> $row['post_edit_reason'],
1130
				'post_edit_user'	=> $row['post_edit_user'],
1131
				'post_edit_locked'	=> $row['post_edit_locked'],
1132
				'post_delete_time'	=> $row['post_delete_time'],
1133
				'post_delete_reason'=> $row['post_delete_reason'],
1134
				'post_delete_user'	=> $row['post_delete_user'],
1135
1136
				// Make sure the icon actually exists
1137
				'icon_id'			=> (isset($icons[$row['icon_id']]['img'], $icons[$row['icon_id']]['height'], $icons[$row['icon_id']]['width'])) ? $row['icon_id'] : 0,
1138
				'post_attachment'	=> $row['post_attachment'],
1139
				'post_visibility'	=> $row['post_visibility'],
1140
				'post_reported'		=> $row['post_reported'],
1141
				'post_username'		=> $row['post_username'],
1142
				'post_text'			=> $row['post_text'],
1143
				'bbcode_uid'		=> $row['bbcode_uid'],
1144
				'bbcode_bitfield'	=> $row['bbcode_bitfield'],
1145
				'enable_smilies'	=> $row['enable_smilies'],
1146
				'enable_sig'		=> $row['enable_sig'],
1147
				'friend'			=> $row['friend'],
1148
				'foe'				=> $row['foe']
1149
			];
1150
1151
			$rowset[$row['post_id']] = $rowset_data;
1152
1153
			// Cache various user specific data ... so we don't have to recompute
1154
			// this each time the same user appears on this page
1155
			if (!isset($user_cache[$poster_id]))
1156
			{
1157
				if ($poster_id == ANONYMOUS)
1158
				{
1159
					$user_cache_data = [
1160
						'user_type'		=> USER_IGNORE,
1161
						'joined'		=> '',
1162
						'last_active'	=> '',
1163
						'posts'			=> '',
1164
1165
						'sig'					=> '',
1166
						'sig_bbcode_uid'		=> '',
1167
						'sig_bbcode_bitfield'	=> '',
1168
1169
						'online'			=> false,
1170
						'avatar'			=> ($this->user->optionget('viewavatars')) ? (($row['user_avatar_type'] == 'avatar.driver.gravatar') ? $this->ext_helper->get_gravatar_url($row) : phpbb_get_user_avatar($row)) : '',
1171
						'rank_title'		=> '',
1172
						'rank_image'		=> '',
1173
						'rank_image_src'	=> '',
1174
						'pm'				=> '',
1175
						'email'				=> '',
1176
						'jabber'			=> '',
1177
						'search'			=> '',
1178
						'age'				=> '',
1179
1180
						'username'		=> $row['username'],
1181
						'user_colour'	=> $row['user_colour'],
1182
						'contact_user'	=> '',
1183
1184
						'warnings'	=> 0,
1185
						'allow_pm'	=> 0
1186
					];
1187
1188
					$user_cache[$poster_id] = $user_cache_data;
1189
1190
					$user_rank_data = phpbb_get_user_rank($row, false);
1191
					$user_cache[$poster_id]['rank_title'] = $user_rank_data['title'];
1192
					$user_cache[$poster_id]['rank_image'] = $user_rank_data['img'];
1193
					$user_cache[$poster_id]['rank_image_src'] = $user_rank_data['img_src'];
1194
				}
1195
				else
1196
				{
1197
					$user_sig = '';
1198
1199
					// We add the signature to every posters entry because enable_sig is post dependent
1200
					if ($row['user_sig'] && $this->config['allow_sig'] && $this->user->optionget('viewsigs'))
1201
					{
1202
						$user_sig = $row['user_sig'];
1203
					}
1204
1205
					$id_cache[] = $poster_id;
1206
1207
					$user_cache_data = [
1208
						'user_type'				=> $row['user_type'],
1209
						'user_inactive_reason'	=> $row['user_inactive_reason'],
1210
1211
						'joined'		=> $this->user->format_date($row['user_regdate']),
1212
						'last_active'	=> $this->user->format_date($row['user_lastvisit']),
1213
						'posts'			=> $row['user_posts'],
1214
						'warnings'		=> (isset($row['user_warnings'])) ? $row['user_warnings'] : 0,
1215
1216
						'sig'					=> $user_sig,
1217
						'sig_bbcode_uid'		=> (!empty($row['user_sig_bbcode_uid'])) ? $row['user_sig_bbcode_uid'] : '',
1218
						'sig_bbcode_bitfield'	=> (!empty($row['user_sig_bbcode_bitfield'])) ? $row['user_sig_bbcode_bitfield'] : '',
1219
1220
						'viewonline'	=> $row['user_allow_viewonline'],
1221
						'allow_pm'		=> $row['user_allow_pm'],
1222
1223
						'avatar'	=> ($this->user->optionget('viewavatars')) ? (($row['user_avatar_type'] == 'avatar.driver.gravatar') ? $this->ext_helper->get_gravatar_url($row) : phpbb_get_user_avatar($row)) : '',
1224
						'age'		=> '',
1225
1226
						'rank_title'		=> '',
1227
						'rank_image'		=> '',
1228
						'rank_image_src'	=> '',
1229
1230
						'username'			=> $row['username'],
1231
						'user_colour'		=> $row['user_colour'],
1232
						'contact_user' 		=> $this->language->lang('CONTACT_USER', get_username_string('username', $poster_id, $row['username'], $row['user_colour'], $row['username'])),
1233
1234
						'online'	=> false,
1235
						'jabber'	=> ($this->config['jab_enable'] && $row['user_jabber'] && $this->auth->acl_get('u_sendim')) ? append_sid("{$this->root_path}memberlist.{$this->php_ext}", "mode=contact&amp;action=jabber&amp;u=$poster_id") : '',
1236
						'search'	=> ($this->config['load_search'] && $this->auth->acl_get('u_search')) ? append_sid("{$this->root_path}search.{$this->php_ext}", "author_id=$poster_id&amp;sr=posts") : '',
1237
1238
						'author_full'		=> get_username_string('full', $poster_id, $row['username'], $row['user_colour']),
1239
						'author_colour'		=> get_username_string('colour', $poster_id, $row['username'], $row['user_colour']),
1240
						'author_username'	=> get_username_string('username', $poster_id, $row['username'], $row['user_colour']),
1241
						'author_profile'	=> get_username_string('profile', $poster_id, $row['username'], $row['user_colour'])
1242
					];
1243
1244
					$user_cache[$poster_id] = $user_cache_data;
1245
1246
					$user_rank_data = phpbb_get_user_rank($row, $row['user_posts']);
1247
					$user_cache[$poster_id]['rank_title'] = $user_rank_data['title'];
1248
					$user_cache[$poster_id]['rank_image'] = $user_rank_data['img'];
1249
					$user_cache[$poster_id]['rank_image_src'] = $user_rank_data['img_src'];
1250
1251
					if ((!empty($row['user_allow_viewemail']) && $this->auth->acl_get('u_sendemail')) || $this->auth->acl_get('a_email'))
1252
					{
1253
						$user_cache[$poster_id]['email'] = ($this->config['board_email_form'] && $this->config['email_enable']) ? append_sid("{$this->root_path}memberlist.{$this->php_ext}", "mode=email&amp;u=$poster_id") : (($this->config['board_hide_emails'] && !$this->auth->acl_get('a_email')) ? '' : 'mailto:' . $row['user_email']);
1254
					}
1255
					else
1256
					{
1257
						$user_cache[$poster_id]['email'] = '';
1258
					}
1259
1260
					if ($this->config['allow_birthdays'] && !empty($row['user_birthday']))
1261
					{
1262
						list($bday_day, $bday_month, $bday_year) = array_map('intval', explode('-', $row['user_birthday']));
1263
1264
						if ($bday_year)
1265
						{
1266
							$diff = $now['mon'] - $bday_month;
1267
							if ($diff == 0)
1268
							{
1269
								$diff = ($now['mday'] - $bday_day < 0) ? 1 : 0;
1270
							}
1271
							else
1272
							{
1273