Completed
Push — master ( faa58d...8e4bc7 )
by Bryan
03:57
created

topicsolved.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * This file is part of the phpBB Topic Solved extension package.
4
 *
5
 * @copyright (c) Bryan Petty
6
 * @license GNU General Public License, version 2 (GPL-2.0)
7
 *
8
 * @package tierra/topicsolved
9
 */
10
11
namespace tierra\topicsolved;
12
13
/**
14
 * Core topic solved functionality used throughout the extension.
15
 *
16
 * @package tierra/topicsolved
17
 */
18
class topicsolved
19
{
20
	/** No-one can mark topics as solved. */
21
	const TOPIC_SOLVED_NO = 0;
22
23
	/** Topic starter and moderators can mark topics as solved. */
24
	const TOPIC_SOLVED_YES = 1;
25
26
	/** Only moderators can mark topics as solved. */
27
	const TOPIC_SOLVED_MOD = 2;
28
29
	/** @var \phpbb\db\driver\driver_interface */
30
	protected $db;
31
32
	/** @var \phpbb\user */
33
	protected $user;
34
35
	/** @var \phpbb\auth\auth */
36
	protected $auth;
37
38
	/** @var \phpbb\event\dispatcher_interface */
39
	protected $dispatcher;
40
41
	/** @var string core.root_path */
42
	protected $root_path;
43
44
	/** @var string core.php_ext */
45
	protected $php_ext;
46
47
	/**
48
	 * Constructor
49
	 *
50
	 * @param \phpbb\db\driver\driver_interface $db Database object
51
	 * @param \phpbb\user $user
52
	 * @param \phpbb\auth\auth $auth
53
	 * @param \phpbb\event\dispatcher_interface $dispatcher
54
	 * @param string $root_path core.root_path
55
	 * @param string $php_ext core.php_ext
56
	 */
57 17
	public function __construct(
58
		\phpbb\db\driver\driver_interface $db,
59
		\phpbb\user $user,
60
		\phpbb\auth\auth $auth,
61
		\phpbb\event\dispatcher_interface $dispatcher,
62
		$root_path, $php_ext)
63
	{
64 17
		$this->db = $db;
65 17
		$this->user = $user;
66 17
		$this->auth = $auth;
67 17
		$this->dispatcher = $dispatcher;
68 17
		$this->root_path = $root_path;
69 17
		$this->php_ext = $php_ext;
70
71 17
		$this->user->add_lang_ext('tierra/topicsolved', 'common');
72 17
	}
73
74
	/**
75
	 * Determine if user is allowed to mark a post as solved or unsolved.
76
	 *
77
	 * @param string $solved Either "solved" or "unsolved".
78
	 * @param array $topic_data Topic to be solved or unsolved.
79
	 *
80
	 * @throws \phpbb\exception\runtime_exception
81
	 *         if an invalid solved parameter is specified.
82
	 *
83
	 * @return bool User is authorized to (un)solve topic.
84
	 */
85 1
	public function user_can_solve_post($solved, $topic_data)
86
	{
87
		// Disallow (un)solving topic if post is global.
88 1
		if ($topic_data['topic_type'] == POST_GLOBAL)
89 1
		{
90
			return false;
91
		}
92
93
		$forum_permission = array(
94 1
			'solved' => 'forum_allow_solve',
95 1
			'unsolved' => 'forum_allow_unsolve',
96 1
		);
97
98 1
		if (!array_key_exists($solved, $forum_permission))
99 1
		{
100
			throw new \phpbb\exception\runtime_exception(
101
				'BAD_METHOD_CALL', array('user_can_solve_post'));
102
		}
103
104 1
		if (($topic_data[$forum_permission[$solved]] == topicsolved::TOPIC_SOLVED_MOD ||
105 1
			$topic_data[$forum_permission[$solved]] == topicsolved::TOPIC_SOLVED_YES) &&
106 1
			$this->auth->acl_get('m_', $topic_data['forum_id']))
107 1
		{
108
			return true;
109
		}
110 1
		else if ($topic_data[$forum_permission[$solved]] == topicsolved::TOPIC_SOLVED_YES &&
111 1
			$topic_data['topic_poster'] == $this->user->data['user_id'] &&
112 1
			$topic_data['topic_status'] == ITEM_UNLOCKED)
113 1
		{
114 1
			return true;
115
		}
116
117
		return false;
118
	}
119
120
	/**
121
	 * Fetches all topic solved data related to the given post.
122
	 *
123
	 * @param int $post_id ID of post to fetch topic/forum data for.
124
	 *
125
	 * @return mixed topic data, or false if not found
126
	 */
127 1
	public function get_topic_data($post_id)
128
	{
129
		$select_sql_array = array(
130
			'SELECT' =>
131
				't.topic_id, t.topic_poster, t.topic_status, t.topic_type, t.topic_solved, ' .
132 1
				'f.forum_id, f.forum_allow_solve, f.forum_allow_unsolve, f.forum_lock_solved, ' .
133 1
				'p.post_id',
134
			'FROM' => array(
135 1
				FORUMS_TABLE => 'f',
136 1
				POSTS_TABLE => 'p',
137 1
				TOPICS_TABLE => 't',
138 1
			),
139
			'WHERE' =>
140 1
				'p.post_id = ' . (int) $post_id .
141 1
				' AND t.topic_id = p.topic_id AND f.forum_id = t.forum_id',
142 1
		);
143 1
		$select_sql = $this->db->sql_build_query('SELECT', $select_sql_array);
144 1
		$result = $this->db->sql_query($select_sql);
145 1
		$topic_data = $this->db->sql_fetchrow($result);
146 1
		$this->db->sql_freeresult($result);
147
148 1
		return $topic_data;
149
	}
150
151
	/**
152
	 * Update topic with the given data.
153
	 *
154
	 * @param int $topic_id Topic to update.
155
	 * @param array $data Topic data to update.
156
	 *
157
	 * @return mixed true if successful
158
	 */
159 3
	public function update_topic($topic_id, $data)
160
	{
161 3
		$update_sql = $this->db->sql_build_array('UPDATE', $data);
162 3
		$result = $this->db->sql_query('
163 3
			UPDATE ' . TOPICS_TABLE . '
164 3
			SET ' . $update_sql . '
165 3
			WHERE topic_id = ' . (int) $topic_id
166 3
		);
167
168 3
		return $result;
169
	}
170
171
	/**
172
	 * Marks a topic as solved.
173
	 *
174
	 * @param array $topic_data Topic to be marked as solved.
175
	 * @param int $post_id Post to mark as the solution.
176
	 */
177 2 View Code Duplication
	public function mark_solved($topic_data, $post_id)
178
	{
179
		// Database column values to set.
180 2
		$column_data = array('topic_solved' => $post_id);
181
182 2
		if ($topic_data['forum_lock_solved'] &&
183 1
			$this->user_can_lock_post($topic_data['forum_id']))
184 2
		{
185 1
			$column_data['topic_status'] = ITEM_LOCKED;
186 1
		}
187
188 2
		$this->update_topic($topic_data['topic_id'], $column_data);
189
190
		/**
191
		 * This event allows you to perform additional actions after a topic has been marked as solved.
192
		 *
193
		 * @event tierra.topicsolved.mark_solved_after
194
		 * @var	array	topic_data	Array with general topic data
195
		 * @var	array	column_data	Array with topic data that the database has been updated with
196
		 * @since 2.2.0
197
		 */
198
		$vars = array(
199 2
			'topic_data',
200 2
			'column_data',
201 2
		);
202 2
		extract($this->dispatcher->trigger_event('tierra.topicsolved.mark_solved_after', compact($vars)));
203 2
	}
204
205
	/**
206
	 * Marks a topic as unsolved.
207
	 *
208
	 * @param array $topic_data Topic to be marked as unsolved.
209
	 */
210 1 View Code Duplication
	public function mark_unsolved($topic_data)
211
	{
212
		// Database column values to set.
213 1
		$column_data = array('topic_solved' => 0);
214
215 1
		if ($topic_data['forum_lock_solved'] &&
216 1
			$this->auth->acl_get('m_lock', $topic_data['forum_id']))
217 1
		{
218 1
			$column_data['topic_status'] = ITEM_UNLOCKED;
219 1
		}
220
221 1
		$this->update_topic($topic_data['topic_id'], $column_data);
222
223
		/**
224
		 * This event allows you to perform additional actions after a topic has been marked as unsolved.
225
		 *
226
		 * @event tierra.topicsolved.mark_unsolved_after
227
		 * @var	array	topic_data	Array with general topic data
228
		 * @var	array	column_data	Array with topic data that the database has been updated with
229
		 * @since 2.2.0
230
		 */
231
		$vars = array(
232 1
			'topic_data',
233 1
			'column_data',
234 1
		);
235 1
		extract($this->dispatcher->trigger_event('tierra.topicsolved.mark_unsolved_after', compact($vars)));
236 1
	}
237
238
	/**
239
	 * Checks if the currently logged in user has permission to lock a post.
240
	 *
241
	 * Regular users won't have permission to solve any topics other than their
242
	 * own, and moderator permissions are forum based, so we only need to know
243
	 * the forum, not the post.
244
	 *
245
	 * @param int $forum_id Forum to check permissions on.
246
	 *
247
	 * @return bool true if user has permission to lock a post.
248
	 */
249 4
	public function user_can_lock_post($forum_id)
250
	{
251
		// Check if user is moderator with appropriate lock permission
252 4
		if ($this->auth->acl_get('m_lock', $forum_id))
253 4
		{
254 2
			return true;
255
		}
256
257
		// Check if user has "lock own posts" permission
258 2
		if ($this->auth->acl_get('f_user_lock', $forum_id))
259 2
		{
260 1
			return true;
261
		}
262
263 1
		return false;
264
	}
265
266
	/**
267
	 * Generate markup for the given solved indicator image.
268
	 *
269
	 * @param string $type One of "head", "list", or "post".
270
	 * @param string $alt Language code for title and alternative text.
271
	 * @param string $url Optional link to solved post.
272
	 *
273
	 * @return string HTML markup for image.
274
	 */
275 6
	public function image($type, $alt = '', $url = '')
276
	{
277 6
		$title = '';
278 6
		$markup = $this->user->img('icon_solved_' . $type, $alt);
279
280 6 View Code Duplication
		if (!empty($alt))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
281 6
		{
282 4
			$alt = $this->user->lang($alt);
283 4
			$title = ' title="' . htmlspecialchars($alt, ENT_QUOTES, 'UTF-8') . '"';
284 4
		}
285
286 6 View Code Duplication
		if (!empty($url))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
287 6
		{
288 6
			$markup = sprintf('<a href="%s"%s>%s</a>',
289 6
				htmlspecialchars($url, ENT_QUOTES, 'UTF-8'), $title, $markup);
290 6
		}
291
292 6
		return $markup;
293
	}
294
295
	/**
296
	 * Generate markup for the given solved indicator icon.
297
	 *
298
	 * @param string $color Color to use for the icon.
299
	 * @param string $alt Language code for title and alternative text.
300
	 * @param string $url Optional link to solved post.
301
	 *
302
	 * @return string HTML markup for icon.
303
	 */
304 5
	public function icon($color = '', $alt = '', $url = '')
305
	{
306 5
		$title = '';
307 5
		if (empty($color))
308 5
		{
309 3
			$color = '00BF00';
310 3
		}
311 5
		$classes = 'fa fa-check-circle fa-fw';
312
313
		/**
314
		 * This event makes it possible to customize the solved icon.
315
		 *
316
		 * @event tierra.topicsolved.render_icon
317
		 * @var	string	alt	Alternative text label for link if a URL was provided.
318
		 * @var	string	classes	CSS classes used for icon.
319
		 * @var	string	color	Color applied to the icon.
320
		 * @var	string	url	Link to the solved post.
321
		 * @since 2.3.0
322
		 */
323 5
		$vars = array('alt', 'classes', 'color', 'url');
324 5
		extract($this->dispatcher->trigger_event('tierra.topicsolved.render_icon', compact($vars)));
325
326 5
		$markup = sprintf(
327 5
			'<i class="%1s" style="color: #%2s" aria-hidden="true"></i>',
328 5
			$classes, $color
329 5
		);
330
331 5 View Code Duplication
		if (!empty($alt))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
332 5
		{
333 3
			$alt = $this->user->lang($alt);
334 3
			$title = ' title="' . htmlspecialchars($alt, ENT_QUOTES, 'UTF-8') . '"';
335 3
		}
336
337 5 View Code Duplication
		if (!empty($url))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
338 5
		{
339 5
			$markup = sprintf('<a href="%s"%s>%s</a>',
340 5
				htmlspecialchars($url, ENT_QUOTES, 'UTF-8'), $title, $markup);
341 5
		}
342
343 5
		return $markup;
344
	}
345
346
	/**
347
	 * Generate link to specific post (usually solution post).
348
	 *
349
	 * @param int $forum_id
350
	 * @param int $topic_id
351
	 * @param int $post_id
352
	 *
353
	 * @return string Relative URL to post
354
	 */
355 1
	public function get_link_to_post($forum_id, $topic_id, $post_id)
356
	{
357 1
		return append_sid("{$this->root_path}viewtopic.{$this->php_ext}",
358 1
			"f=$forum_id&t=$topic_id&p=$post_id") . '#p' . $post_id;
359
	}
360
}
361