Completed
Push — master ( d5fbc8...4c9094 )
by Erwan
03:26
created

qte   D

Complexity

Total Complexity 121

Size/Duplication

Total Lines 826
Duplicated Lines 13.08 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 3
Bugs 0 Features 1
Metric Value
wmc 121
c 3
b 0
f 1
lcom 1
cbo 0
dl 108
loc 826
rs 4.4444

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 16 1
A get_users_by_topic_id() 0 22 3
A get_attr_name_by_id() 0 4 1
A get_users_by_user_id() 0 18 2
C attr_select() 0 68 15
B attr_search() 0 47 6
C attr_sort() 54 54 10
B attr_default() 54 54 9
C attr_display() 0 27 8
B attr_title() 0 25 6
C attr_apply() 0 76 11
C mcp_attr_apply() 0 85 10
A getAttr() 0 4 1
F qte_group_select() 0 24 12
A attr_lng_key() 0 7 2
A attr_img_key() 0 4 4
A attr_colour() 0 10 3
B _check_auth_attribute() 0 13 8
B _check_auth_remove_attr() 0 26 6
B _get_attributes() 0 29 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like qte often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use qte, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 *
4
 * @package Quick Title Edition Extension
5
 * @copyright (c) 2015 ABDev
6
 * @copyright (c) 2015 PastisD
7
 * @copyright (c) 2015 Geolim4 <http://geolim4.com>
8
 * @copyright (c) 2015 Zoddo <[email protected]>
9
 * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
10
 *
11
 */
12
13
namespace ernadoo\qte;
14
15
class qte
16
{
17
	const KEEP = -2;
18
	const REMOVE = -1;
19
20
	/** @var \phpbb\request\request */
21
	protected $request;
22
23
	/** @var \phpbb\cache\driver\driver_interface */
24
	protected $cache;
25
26
	/** @var \phpbb\config\config */
27
	protected $config;
28
29
	/** @var \phpbb\db\driver\driver_interface */
30
	protected $db;
31
32
	/** @var \phpbb\template\template */
33
	protected $template;
34
35
	/** @var \phpbb\user */
36
	protected $user;
37
38
	/** @var \phpbb\log\log */
39
	protected $log;
40
41
	/** @var string */
42
	protected $root_path;
43
44
	/** @var string */
45
	protected $php_ext;
46
47
	/** @var string */
48
	protected $table_prefix;
49
50
	/** @var array */
51
	private $_attr = array();
52
53
	/** @var array */
54
	private $_name = array();
55
56
	/**
57
	* Constructor
58
	*
59
	* @param \phpbb\request\request					$request			Request object
60
	* @param \phpbb\cache\driver\driver_interface	$cache				Cache object
61
	* @param \phpbb\config\config					$config				Config object
62
	* @param \phpbb\db\driver\driver_interface 		$db					Database object
63
	* @param \phpbb\template\template				$template			Template object
64
	* @param \phpbb\user							$user				User object
65
	* @param \phpbb\log\log							$log				Log object
66
	* @param string									$root_path			phpBB root path
67
	* @param string									$php_ext   			phpEx
68
	* @param string									$table_prefix   	Prefix tables
69
	*/
70
	public function __construct(\phpbb\request\request $request, \phpbb\cache\driver\driver_interface $cache, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\template\template $template, \phpbb\user $user, \phpbb\log\log $log, $root_path, $php_ext, $table_prefix)
71
	{
72
		$this->request		= $request;
73
		$this->cache		= $cache;
74
		$this->config		= $config;
75
		$this->db			= $db;
76
		$this->template		= $template;
77
		$this->user			= $user;
78
		$this->log			= $log;
79
80
		$this->root_path	= $root_path;
81
		$this->php_ext		= $php_ext;
82
		$this->table_prefix = $table_prefix;
83
84
		$this->_get_attributes();
85
	}
86
87
	/**
88
	* Get topic attributes username
89
	*
90
	* @param	array	$topic_list	Topic ids
91
	*
92
	* @return	null
93
	*/
94
	public function get_users_by_topic_id($topic_list)
95
	{
96
		if (!empty($topic_list))
97
		{
98
			$sql = 'SELECT u.user_id, u.username, u.user_colour
99
				FROM ' . USERS_TABLE . ' u
100
				LEFT JOIN ' . TOPICS_TABLE . ' t ON (u.user_id = t.topic_attr_user)
101
				WHERE ' . $this->db->sql_in_set('t.topic_id', array_map('intval', $topic_list)) . '
102
					AND t.topic_attr_user <> ' . ANONYMOUS;
103
			$result = $this->db->sql_query($sql);
104
105
			while ($row = $this->db->sql_fetchrow($result))
106
			{
107
				$this->_name[$row['user_id']] = array(
108
					'user_id' => (int) $row['user_id'],
109
					'username' => $row['username'],
110
					'user_colour' => $row['user_colour'],
111
				);
112
			}
113
			$this->db->sql_freeresult();
114
		}
115
	}
116
117
	/**
118
	* Get attribute name
119
	*
120
	* @param	int		$attr_id	The attribute id
121
	*
122
	* @return	string
123
	*/
124
	public function get_attr_name_by_id($attr_id)
125
	{
126
		return $this->_attr[$attr_id]['attr_name'];
127
	}
128
129
	/**
130
	* Get attribute author
131
	*
132
	* @param	int		$user_id	User id
133
	*
134
	* @return	string
135
	*/
136
	public function get_users_by_user_id($user_id)
137
	{
138
		$sql = 'SELECT user_id, username, user_colour
139
			FROM ' . USERS_TABLE . '
140
			WHERE user_id = ' . (int) $user_id;
141
		$result = $this->db->sql_query($sql);
142
143
		$this->name = array();
0 ignored issues
show
Bug introduced by
The property name does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
144
		while ( $row = $this->db->sql_fetchrow($result) )
145
		{
146
			$this->name[$row['user_id']] = array(
147
				'user_id'		=> (int) $row['user_id'],
148
				'username'		=> $row['username'],
149
				'user_colour'	=> $row['user_colour'],
150
			);
151
		}
152
		$this->db->sql_freeresult();
153
	}
154
155
	/**
156
	* Generate a list of attributes based on permissions
157
	*
158
	* @param	int		$forum_id		Forum id
159
	* @param	int		$author_id		Topic author id
160
	* @param	int		$attribute_id	Current attribute id
161
	* @param	array	$hide_attr		Groups which can't delete attribute in this forum
162
	* @param	string	$viewtopic_url	Topic's url
163
	*
164
	* @return	null
165
	*/
166
	public function attr_select($forum_id = 0, $author_id = 0, $attribute_id = 0, $hide_attr = array(), $viewtopic_url = '')
167
	{
168
		// load language
169
		$this->user->add_lang_ext('ernadoo/qte', 'attributes');
170
171
		// get current time once !
172
		$current_time = time();
173
174
		$show_select = false;
175
		$user_groups = array();
176
		$show_remove = $this->_check_auth_remove_attr($user_groups, $hide_attr);
177
178
		foreach ($this->_attr as $attr)
179
		{
180
			if (empty($attr['attr_auths']))
181
			{
182
				$attr_auths = array(array(
183
					'forums_ids'	=> array(),
184
					'groups_ids'	=> array(),
185
					'author'		=> false,
186
				));
187
			}
188
			else
189
			{
190
				$attr_auths = json_decode($attr['attr_auths'], true);
191
			}
192
193
			foreach ($attr_auths as $attr_auth)
194
			{
195
				if (!$this->_check_auth_attribute($attr_auth, $forum_id, $user_groups, $author_id))
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_check_auth_attri...ser_groups, $author_id) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
196
				{
197
					continue;
198
				}
199
200
				// show the selector !
201
				$show_select = true;
202
203
				// parse the attribute name
204
				$attribute_name = str_replace(array('%mod%', '%date%'), array($this->user->data['username'], $this->user->format_date($current_time, $attr['attr_date'])), $this->attr_lng_key($attr['attr_name']));
205
206
				$this->template->assign_block_vars('attributes', array(
207
					'QTE_ID'		=> $attr['attr_id'],
208
					'QTE_TYPE'		=> $attr['attr_type'],
209
					'QTE_NAME'		=> $attribute_name,
210
					'QTE_DESC'		=> $this->attr_lng_key($attr['attr_desc']),
211
					'QTE_COLOUR'	=> $this->attr_colour($attr['attr_name'], $attr['attr_colour']),
212
213
					'IS_SELECTED'	=> (!empty($attribute_id) && ($attr['attr_id'] == $attribute_id)),
214
215
					'S_QTE_DESC'	=> !empty($attr['attr_desc']) ? true : false,
216
					'U_QTE_URL'		=> !empty($viewtopic_url) ? append_sid($viewtopic_url, array('attr_id' => $attr['attr_id'])) : false,
217
				));
218
			}
219
		}
220
221
		if ($show_select)
222
		{
223
			$this->template->assign_vars(array(
224
				'S_QTE_SELECT'		=> true,
225
				'S_QTE_REMOVE'		=> $show_remove,
226
				'S_QTE_EMPTY'		=> (empty($attribute_id) || ($attribute_id == -1) || ($attribute_id == -2)),
227
				'S_QTE_SELECTED'	=> ($show_remove && ($attribute_id == -1)),
228
229
				'L_QTE_SELECT'		=> $this->user->lang['QTE_ATTRIBUTE_' . (!empty($attribute_id) ? ($show_remove ? 'REMOVE' : 'RESTRICT') : 'ADD')],
230
				'U_QTE_URL'			=> !empty($viewtopic_url) ? append_sid($viewtopic_url, array('attr_id' => -1)) : false,
231
			));
232
		}
233
	}
234
235
	/**
236
	* Generate a list of all attributes for search page
237
	*
238
	* @return	null
239
	*/
240
	public function attr_search()
241
	{
242
		// load language
243
		$this->user->add_lang_ext('ernadoo/qte', array('attributes', 'attributes_acp'));
244
245
		$show_select = false;
246
247
		foreach ($this->_attr as $attr)
248
		{
249
			if (empty($attr['attr_auths']))
250
			{
251
				$attr_auths = array(array(
252
					'forums_ids'	=> array(),
253
					'groups_ids'	=> array(),
254
					'author'		=> false,
255
				));
256
			}
257
			else
258
			{
259
				$attr_auths = json_decode($attr['attr_auths'], true);
260
			}
261
262
			foreach ($attr_auths as $attr_auth)
263
			{
264
				// show the selector !
265
				$show_select = true;
266
267
				// parse the attribute name
268
				$attribute_name = str_replace(array('%mod%', '%date%'), array($this->user->lang['QTE_KEY_USERNAME'], $this->user->lang['QTE_KEY_DATE']), $this->attr_lng_key($attr['attr_name']));
269
270
				$this->template->assign_block_vars('attributes', array(
271
					'QTE_ID'		=> $attr['attr_id'],
272
					'QTE_TYPE'		=> $attr['attr_type'],
273
					'QTE_NAME'		=> $attribute_name,
274
					'QTE_DESC'		=> $this->attr_lng_key($attr['attr_desc']),
275
					'QTE_COLOUR'	=> $this->attr_colour($attr['attr_name'], $attr['attr_colour']),
276
277
					'S_QTE_DESC'	=> !empty($attr['attr_desc']) ? true : false,
278
				));
279
			}
280
		}
281
282
		if ($show_select)
283
		{
284
			$this->template->assign_var('S_QTE_SELECT', true);
285
		}
286
	}
287
288
	/**
289
	* Generate a list of attributes for viewforum page
290
	*
291
	* @param	int		$forum_id		Forum id
292
	* @param	int		$attribute_id	Current attribute id
293
	*
294
	* @return	null
295
	*/
296 View Code Duplication
	public function attr_sort($forum_id = 0, $attribute_id = 0)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in 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...
297
	{
298
		// load language
299
		$this->user->add_lang_ext('ernadoo/qte', array('attributes', 'attributes_acp'));
300
301
		$show_select = false;
302
303
		foreach ($this->_attr as $attr)
304
		{
305
			if (empty($attr['attr_auths']))
306
			{
307
				$attr_auths = array(array(
308
					'forums_ids'	=> array(),
309
					'groups_ids'	=> array(),
310
					'author'		=> false,
311
				));
312
			}
313
			else
314
			{
315
				$attr_auths = json_decode($attr['attr_auths'], true);
316
			}
317
318
			foreach ($attr_auths as $attr_auth)
319
			{
320
				$forum_ids = $attr_auth['forums_ids'];
321
322
				if (is_array($forum_ids) && in_array($forum_id, $forum_ids))
323
				{
324
					// show the selector !
325
					$show_select = true;
326
327
					// parse the attribute name
328
					$attribute_name = str_replace(array('%mod%', '%date%'), array($this->user->lang['QTE_KEY_USERNAME'], $this->user->lang['QTE_KEY_DATE']), $this->attr_lng_key($attr['attr_name']));
329
330
					$this->template->assign_block_vars('attributes', array(
331
						'QTE_ID'		=> $attr['attr_id'],
332
						'QTE_TYPE'		=> $attr['attr_type'],
333
						'QTE_NAME'		=> $attribute_name,
334
						'QTE_DESC'		=> $this->attr_lng_key($attr['attr_desc']),
335
						'QTE_COLOUR'	=> $this->attr_colour($attr['attr_name'], $attr['attr_colour']),
336
337
						'IS_SELECTED' => (!empty($attribute_id) && ($attr['attr_id'] == $attribute_id)) ? true : false,
338
339
						'S_QTE_DESC'	=> !empty($attr['attr_desc']) ? true : false,
340
					));
341
				}
342
			}
343
		}
344
345
		if ($show_select)
346
		{
347
			$this->template->assign_var('S_QTE_SELECT', true);
348
		}
349
	}
350
351
	/**
352
	* Generate a default attribute list for a forum
353
	*
354
	* @param	int		$forum_id		Forum id
355
	* @param	int		$attribute_id	Current attribute id
356
	*
357
	* @return	null
358
	*/
359 View Code Duplication
	public function attr_default($forum_id = 0, $attribute_id = 0)
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in 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...
360
	{
361
		// load language
362
		$this->user->add_lang_ext('ernadoo/qte', array('attributes', 'attributes_acp'));
363
364
		$show_select = false;
365
366
		foreach ($this->_attr as $attr)
367
		{
368
			if (empty($attr['attr_auths']))
369
			{
370
				$attr_auths = array(array(
371
					'forums_ids'	=> array(),
372
					'groups_ids'	=> array(),
373
					'author'		=> false,
374
				));
375
			}
376
			else
377
			{
378
				$attr_auths = json_decode($attr['attr_auths'], true);
379
			}
380
381
			foreach ($attr_auths as $attr_auth)
382
			{
383
				$forum_ids = $attr_auth['forums_ids'];
384
385
				if (is_array($forum_ids) && in_array($forum_id, $forum_ids))
386
				{
387
					// show the selector !
388
					$show_select = true;
389
390
					// parse the attribute name
391
					$attribute_name = str_replace(array('%mod%', '%date%'), array($this->user->lang['QTE_KEY_USERNAME'], $this->user->lang['QTE_KEY_DATE']), $this->attr_lng_key($attr['attr_name']));
392
393
					$this->template->assign_block_vars('attributes', array(
394
						'QTE_ID'		=> $attr['attr_id'],
395
						'QTE_TYPE'		=> $attr['attr_type'],
396
						'QTE_NAME'		=> $attribute_name,
397
						'QTE_DESC'		=> $this->attr_lng_key($attr['attr_desc']),
398
						'QTE_COLOUR'	=> $this->attr_colour($attr['attr_name'], $attr['attr_colour']),
399
400
						'IS_SELECTED'	=> (!empty($attribute_id) && ($attr['attr_id'] == $attribute_id)),
401
402
						'S_QTE_DESC'	=> !empty($attr['attr_desc']) ? true : false,
403
					));
404
				}
405
			}
406
		}
407
408
		if ($show_select)
409
		{
410
			$this->template->assign_var('S_QTE_SELECT', true);
411
		}
412
	}
413
414
	/**
415
	* Generate attribute for topic title
416
	*
417
	* @param	int		$attribute_id	Current attribute id
418
	* @param	int		$user_id		Current attribute user id
419
	* @param	int		$timestamp		Attribute timestamp
420
	*
421
	* @return	string					Attribute html code
422
	*/
423
	public function attr_display($attribute_id = 0, $user_id = 0, $timestamp = 0)
424
	{
425
		if (empty($attribute_id) || empty($user_id) || empty($timestamp))
426
		{
427
			return false;
428
		}
429
430
		if (isset($this->_attr[$attribute_id]))
431
		{
432
			$attribute_colour = $this->attr_colour($this->_attr[$attribute_id]['attr_name'], $this->_attr[$attribute_id]['attr_colour']);
433
434
			if (isset($this->_name[$user_id]['user_id']))
435
			{
436
				$attribute_username = get_username_string(($this->_attr[$attribute_id]['attr_user_colour'] ? 'no_profile' : 'username'), $this->_name[$user_id]['user_id'], $this->_name[$user_id]['username'], $this->_name[$user_id]['user_colour']);
437
			}
438
			else
439
			{
440
				$attribute_username = $this->user->lang['GUEST'];
441
			}
442
443
			$attribute_date = $this->user->format_date($timestamp, $this->_attr[$attribute_id]['attr_date']);
444
445
			$attribute_name = str_replace(array('%mod%', '%date%'), array($attribute_username, $attribute_date), $this->attr_lng_key($this->_attr[$attribute_id]['attr_name']));
446
447
			return !$this->_attr[$attribute_id]['attr_type'] ? '<span' . $attribute_colour . '>' . $attribute_name . '</span>' : $this->attr_img_key($this->_attr[$attribute_id]['attr_img'], $attribute_name);
448
		}
449
	}
450
451
	/**
452
	* Generate attribute for page title
453
	*
454
	* @param	int		$attribute_id	Current attribute id
455
	* @param	int		$user_id		Current attribute user id
456
	* @param	int		$timestamp		Attribute timestamp
457
	*
458
	* @return	string					attribute html code
459
	*/
460
	public function attr_title($attribute_id = 0, $user_id = 0, $timestamp = 0)
461
	{
462
		if (empty($attribute_id) || empty($user_id) || empty($timestamp))
463
		{
464
			return false;
465
		}
466
467
		if (isset($this->_attr[$attribute_id]))
468
		{
469
			if (isset($this->_name[$user_id]['user_id']))
470
			{
471
				$attribute_username = get_username_string('username', $this->_name[$user_id]['user_id'], $this->_name[$user_id]['username'], $this->_name[$user_id]['user_colour']);
472
			}
473
			else
474
			{
475
				$attribute_username = $this->user->lang['GUEST'];
476
			}
477
478
			$attribute_date = $this->user->format_date($timestamp, $this->_attr[$attribute_id]['attr_date']);
479
480
			$attribute_name = str_replace(array('%mod%', '%date%'), array($attribute_username, $attribute_date), $this->attr_lng_key($this->_attr[$attribute_id]['attr_name']));
481
482
			return $attribute_name;
483
		}
484
	}
485
486
487
	/**
488
	* Change topic attribute
489
	*
490
	* @param	int		$attribute_id		New attribute id
491
	* @param	int		$topic_id			The id of the topic
492
	* @param	int		$forum_id			The id of the forum
493
	* @param	int		$topic_attribute	Current attribute id
0 ignored issues
show
Documentation introduced by
Should the type for parameter $topic_attribute not be string|integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
494
	* @param	array	$hide_attr			Groups which can't delete attribute in this forum
495
	*
496
	* @return	null
497
	*/
498
	public function attr_apply($attribute_id = 0, $topic_id = 0, $forum_id = 0, $topic_attribute = '', $hide_attr = array())
499
	{
500
		if (empty($topic_id) || empty($forum_id) || empty($attribute_id))
501
		{
502
			return;
503
		}
504
505
		if ($attribute_id == \ernadoo\qte\qte::REMOVE && !$this->_check_auth_remove_attr($user_groups, $hide_attr))
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_check_auth_remov...ser_groups, $hide_attr) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
506
		{
507
			return;
508
		}
509
510
		// time !
511
		$current_time = time();
512
513
		if ($attribute_id == \ernadoo\qte\qte::REMOVE)
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
514
		{
515
			$fields = array(
516
				'topic_attr_id'		=> 0,
517
				'topic_attr_user'	=> 0,
518
				'topic_attr_time'	=> 0,
519
			);
520
		}
521
		else
522
		{
523
			$fields = array(
524
				'topic_attr_id'		=> $attribute_id,
525
				'topic_attr_user'	=> $this->user->data['user_id'],
526
				'topic_attr_time'	=> $current_time,
527
			);
528
		}
529
530
		$sql = 'UPDATE ' . TOPICS_TABLE . '
531
			SET ' . $this->db->sql_build_array('UPDATE', $fields) . '
532
			WHERE topic_id = ' . (int) $topic_id;
533
		$this->db->sql_query($sql);
534
535
		$sql = 'SELECT topic_id
536
			FROM ' . TOPICS_TABLE . '
537
			WHERE topic_moved_id = ' . (int) $topic_id;
538
		$result = $this->db->sql_query($sql);
539
		$shadow_topic_id = (int) $this->db->sql_fetchfield('topic_id');
540
		$this->db->sql_freeresult($result);
541
542
		if (!empty($shadow_topic_id))
543
		{
544
			$sql = 'UPDATE ' . TOPICS_TABLE . '
545
				SET ' . $this->db->sql_build_array('UPDATE', $fields) . '
546
				WHERE topic_id = ' . $shadow_topic_id;
547
			$this->db->sql_query($sql);
548
		}
549
550
		$meta_url = append_sid("{$this->root_path}viewtopic.$this->php_ext", "f=$forum_id&amp;t=$topic_id");
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $this instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $forum_id instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $topic_id instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
551
		meta_refresh(3, $meta_url);
552
553
		// load language
554
		$this->user->add_lang('posting');
555
		$this->user->add_lang_ext('ernadoo/qte', 'attributes');
556
557
		$message = $this->user->lang['QTE_ATTRIBUTE_' . ($attribute_id == -1 ? 'REMOVED' : (empty($topic_attribute) ? 'ADDED' : 'UPDATED'))] . '<br /><br />' . sprintf($this->user->lang['VIEW_MESSAGE'], '<a href="' . $meta_url . '">', '</a>');
558
		$message .= '<br /><br />' . sprintf($this->user->lang['RETURN_FORUM'], '<a href="' . append_sid("{$this->root_path}viewforum.$this->php_ext", 'f=' . $forum_id) . '">', '</a>');
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $this instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
559
560
		if ($this->request->is_ajax())
561
		{
562
			$json_response = new \phpbb\json_response;
563
			$json_response->send(array(
564
				'success' => true,
565
566
				'MESSAGE_TITLE'	=> $this->user->lang['INFORMATION'],
567
				'MESSAGE_TEXT'	=> $message,
568
				'NEW_ATTRIBUTE'	=> $this->attr_display($attribute_id, $this->user->data['user_id'], $current_time),
569
			));
570
		}
571
572
		trigger_error($message);
573
	}
574
575
	/**
576
	* Change topic attribute in mcp
577
	*
578
	* @param	int		$attribute_id		New attribute id
579
	* @param	array	$topic_ids			Topics ids
580
	*
581
	* @return	null
582
	*/
583
	public function mcp_attr_apply($attribute_id = 0, $topic_ids = array())
584
	{
585
		// load language
586
		$this->user->add_lang_ext('ernadoo/qte', 'attributes');
587
588
		if (!sizeof($topic_ids))
589
		{
590
			trigger_error('NO_TOPIC_SELECTED');
591
		}
592
593
		if (!phpbb_check_ids($topic_ids, TOPICS_TABLE, 'topic_id'))
594
		{
595
			return;
596
		}
597
598
		// time !
599
		$current_time = time();
600
601
		$sql = 'SELECT topic_id, forum_id, topic_title, topic_attr_id
602
			FROM ' . TOPICS_TABLE . '
603
			WHERE ' . $this->db->sql_in_set('topic_id', array_map('intval', $topic_ids));
604
		$result = $this->db->sql_query($sql);
605
606
		// log this action
607
		while ($row = $this->db->sql_fetchrow($result))
608
		{
609
			$message = ($attribute_id == -1) ? 'REMOVED' : (empty($row['topic_attr_id']) ? 'ADDED' : 'UPDATED');
610
			$additional_data = array(
611
				'forum_id'	=> $row['forum_id'],
612
				'topic_id'	=> $row['topic_id'],
613
				$row['topic_title'],
614
			);
615
			$this->log->add('mod', $this->user->data['user_id'], $this->user->ip, 'MCP_ATTRIBUTE_' . $message, $current_time, $additional_data);
616
		}
617
		$this->db->sql_freeresult($result);
618
619
		if ($attribute_id == -1)
620
		{
621
			$fields = array(
622
				'topic_attr_id'		=> 0,
623
				'topic_attr_user'	=> 0,
624
				'topic_attr_time'	=> 0,
625
			);
626
		}
627
		else
628
		{
629
			$fields = array(
630
				'topic_attr_id'		=> $attribute_id,
631
				'topic_attr_user'	=> $this->user->data['user_id'],
632
				'topic_attr_time'	=> $current_time,
633
			);
634
		}
635
636
		$sql = 'UPDATE ' . TOPICS_TABLE . '
637
			SET ' . $this->db->sql_build_array('UPDATE', $fields) . '
638
			WHERE ' . $this->db->sql_in_set('topic_id', array_map('intval', $topic_ids));
639
		$this->db->sql_query($sql);
640
641
		$sql = 'SELECT topic_id
642
			FROM ' . TOPICS_TABLE . '
643
			WHERE ' . $this->db->sql_in_set('topic_moved_id', array_map('intval', $topic_ids));
644
		$result = $this->db->sql_query($sql);
645
646
		$shadow_topic_ids = array();
647
		while ($row = $this->db->sql_fetchrow($result))
648
		{
649
			$shadow_topic_ids[] = (int) $row['topic_id'];
650
		}
651
		$this->db->sql_freeresult($result);
652
653
		if (sizeof($shadow_topic_ids))
654
		{
655
			$sql = 'UPDATE ' . TOPICS_TABLE . '
656
				SET ' . $this->db->sql_build_array('UPDATE', $fields) . '
657
				WHERE ' . $this->db->sql_in_set('topic_id', array_map('intval', $shadow_topic_ids));
658
			$this->db->sql_query($sql);
659
		}
660
661
		$redirect = $this->request->variable('redirect', $this->user->data['session_page']);
662
663
		meta_refresh(3, $redirect);
664
		trigger_error($this->user->lang['QTE_TOPIC' . (sizeof($topic_ids) == 1 ? '' : 'S') . '_ATTRIBUTE_' . $message] . '<br /><br />' . sprintf($this->user->lang['RETURN_PAGE'], '<a href="' . $redirect . '">', '</a>'));
0 ignored issues
show
Bug introduced by
The variable $message does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
665
666
		return;
667
	}
668
669
	/**
670
	* Getter...
671
	*
672
	* @return	array
673
	*/
674
	public function getAttr()
675
	{
676
		return $this->_attr;
677
	}
678
679
	/**
680
	* Generate list of groups
681
	*
682
	* @param int	$group_ids		The default groups id to mark as selected
683
	* @param array	$exclude_ids	The group ids to exclude from the list, false (default) if you whish to exclude no id
684
	* @param int	$manage_founder If set to false (default) all groups are returned, if 0 only those groups returned not being managed by founders only, if 1 only those groups returned managed by founders only.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $manage_founder not be false|integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
685
	*
686
	* @return string The list of options.
687
	*/
688
	public function qte_group_select($group_ids, $exclude_ids = array(), $manage_founder = false)
689
	{
690
		$exclude_sql = ($exclude_ids !== false && sizeof($exclude_ids)) ? 'WHERE ' . $this->db->sql_in_set('group_id', array_map('intval', $exclude_ids), true) : '';
691
		$sql_and = !$this->config['coppa_enable'] ? ($exclude_sql ? ' AND ' : ' WHERE ') . "group_name <> 'REGISTERED_COPPA'" : '';
692
		$sql_founder = ($manage_founder !== false) ? (($exclude_sql || $sql_and) ? ' AND ' : ' WHERE ') . 'group_founder_manage = ' . (int) $manage_founder : '';
693
694
		$sql = 'SELECT group_id, group_name, group_type
695
			FROM ' . GROUPS_TABLE . "
3 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $exclude_sql instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $sql_and instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $sql_founder instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
696
			$exclude_sql
697
			$sql_and
698
			$sql_founder
699
			ORDER BY group_type DESC, group_name ASC";
700
		$result = $this->db->sql_query($sql);
701
702
		$s_group_options = '';
703
		while ($row = $this->db->sql_fetchrow($result))
704
		{
705
			$selected = in_array($row['group_id'], $group_ids) ? ' selected="selected"' : '';
706
			$s_group_options .= '<option' . (($row['group_type'] == GROUP_SPECIAL) ? ' class="sep"' : '') . ' value="' . $row['group_id'] . '"' . $selected . '>' . (($row['group_type'] == GROUP_SPECIAL) ? $this->user->lang['G_' . $row['group_name']] : $row['group_name']) . '</option>';
707
		}
708
		$this->db->sql_freeresult($result);
709
710
		return $s_group_options;
711
	}
712
713
	/**
714
	* borrowed from "Categories Hierarchy" : used to check if a language key exists
715
	* @todo delete
716
	*/
717
	public function attr_lng_key($key)
718
	{
719
		// load language
720
		$this->user->add_lang_ext('ernadoo/qte', 'attributes');
721
722
		return isset($this->user->lang[$key]) ? $this->user->lang[$key] : $key;
723
	}
724
725
	// borrowed from "Categories Hierarchy" : used to check if a image key exists
726
	public function attr_img_key($key, $alt)
727
	{
728
		return empty($key) ? '' : (preg_match('#^[a-z0-9_-]+$#i', $key) ? $this->user->img($key, $alt) : '<img src="' . (preg_match('#^(ht|f)tp[s]?\://#i', $key) ? $key : $this->root_path . $key) . '" alt="' . $alt . '" title="' . $alt . '" />');
729
	}
730
731
	/**
732
	* Build class and style attribute
733
	*
734
	* @param	string	$a_name			Attribute name
735
	* @param	string	$a_colour		Attribute color
736
	* @return	string					html code
737
	*/
738
	public function attr_colour($a_name, $a_colour)
739
	{
740
		$a_name = preg_replace("#[^a-z0-9 _-]#", '', strtolower($a_name));
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal #[^a-z0-9 _-]# does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
741
		if (!empty($a_name))
742
		{
743
			$a_name .= '-qte';
744
		}
745
746
		return ' class="qte-attr ' . $a_name . '"' . (!empty($a_colour) ? ' style="color:#' . $a_colour . '; font-weight:bold;"' : '');
747
	}
748
749
	/**
750
	* Check if user can apply an attribute
751
	*
752
	* @param	array	$attr_auth		Forum auth
753
	* @param	int		$forum_id		Forum id
754
	* @param	array	$user_groups	User's groups
755
	* @param	int		$author_id		Topic author id
756
	* @return	bool
757
	*/
758
	private function _check_auth_attribute($attr_auth, $forum_id, $user_groups, $author_id)
759
	{
760
		$forum_ids = $attr_auth['forums_ids'];
761
		$group_ids = $attr_auth['groups_ids'];
762
763
		if (is_array($forum_ids) && in_array($forum_id, $forum_ids))
764
		{
765
			if (is_array($group_ids) && array_intersect($group_ids, $user_groups) || ($attr_auth['author'] && ($author_id == $this->user->data['user_id']) && ($this->user->data['user_id'] != ANONYMOUS)))
766
			{
767
				return true;
768
			}
769
		}
770
	}
771
772
	/**
773
	* Check if user can delete an attribute
774
	*
775
	* @param	array	$user_groups	User's groups
776
	* @param	array	$hide_attr		Groups which can't delete attribute in a forum
777
	* @return	bool
778
	*/
779
	private function _check_auth_remove_attr(&$user_groups, $hide_attr)
780
	{
781
		// include that file !
782
		if (!function_exists('group_memberships'))
783
		{
784
			include $this->root_path . 'includes/functions_user.' . $this->php_ext;
785
		}
786
787
		// get groups membership !
788
		$user_membership = group_memberships(false, $this->user->data['user_id']);
789
790
		$user_groups = array();
791
		if (!empty($user_membership))
792
		{
793
			foreach ($user_membership as $row)
794
			{
795
				$user_groups[$row['group_id']] = (int) $row['group_id'];
796
			}
797
		}
798
799
		$groups_removed = array_intersect($user_groups, $hide_attr);
800
		if (empty($hide_attr) || (count($groups_removed) < count($user_groups)))
801
		{
802
			return true;
803
		}
804
	}
805
806
	/**
807
	* Get attributes from database
808
	*
809
	* @return	null
810
	*/
811
	private function _get_attributes()
812
	{
813
		if (($this->_attr = $this->cache->get('_attr')) === false)
814
		{
815
			$sql = 'SELECT *
816
				FROM ' . $this->table_prefix . 'topics_attr
817
				ORDER BY left_id ASC';
818
			$result = $this->db->sql_query($sql);
819
820
			$this->_attr = array();
821
			while ($row = $this->db->sql_fetchrow($result))
822
			{
823
				$this->_attr[$row['attr_id']] = array(
824
					'attr_id'			=> (int) $row['attr_id'],
825
					'attr_type'			=> (bool) $row['attr_type'],
826
					'attr_name'			=> $row['attr_name'],
827
					'attr_desc'			=> $row['attr_desc'],
828
					'attr_img'			=> $row['attr_img'],
829
					'attr_colour'		=> $row['attr_colour'],
830
					'attr_date'			=> $row['attr_date'],
831
					'attr_user_colour'	=> (bool) $row['attr_user_colour'],
832
					'attr_auths'		=> $row['attr_auths'],
833
				);
834
			}
835
			$this->db->sql_freeresult();
836
837
			$this->cache->put('_attr', $this->_attr);
838
		}
839
	}
840
}
841