Failed Conditions
Branch release-2.1 (4e22cf)
by Rick
06:39
created

Subs-Boards.php ➔ createBoard()   C

Complexity

Conditions 10
Paths 16

Size

Total Lines 89
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 47
nc 16
nop 1
dl 0
loc 89
rs 5.1943
c 0
b 0
f 0

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
/**
4
 * This file is mainly concerned with minor tasks relating to boards, such as
5
 * marking them read, collapsing categories, or quick moderation.
6
 *
7
 * Simple Machines Forum (SMF)
8
 *
9
 * @package SMF
10
 * @author Simple Machines http://www.simplemachines.org
11
 * @copyright 2017 Simple Machines and individual contributors
12
 * @license http://www.simplemachines.org/about/smf/license.php BSD
13
 *
14
 * @version 2.1 Beta 4
15
 */
16
17
if (!defined('SMF'))
18
	die('No direct access...');
19
20
/**
21
 * Mark a board or multiple boards read.
22
 *
23
 * @param int|array $boards The ID of a single board or an array of boards
24
 * @param bool $unread Whether we're marking them as unread
25
 */
26
function markBoardsRead($boards, $unread = false)
27
{
28
	global $user_info, $modSettings, $smcFunc;
29
30
	// Force $boards to be an array.
31
	if (!is_array($boards))
32
		$boards = array($boards);
33
	else
34
		$boards = array_unique($boards);
35
36
	// No boards, nothing to mark as read.
37
	if (empty($boards))
38
		return;
39
40
	// Allow the user to mark a board as unread.
41
	if ($unread)
42
	{
43
		// Clear out all the places where this lovely info is stored.
44
		// @todo Maybe not log_mark_read?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
45
		$smcFunc['db_query']('', '
46
			DELETE FROM {db_prefix}log_mark_read
47
			WHERE id_board IN ({array_int:board_list})
48
				AND id_member = {int:current_member}',
49
			array(
50
				'current_member' => $user_info['id'],
51
				'board_list' => $boards,
52
			)
53
		);
54
		$smcFunc['db_query']('', '
55
			DELETE FROM {db_prefix}log_boards
56
			WHERE id_board IN ({array_int:board_list})
57
				AND id_member = {int:current_member}',
58
			array(
59
				'current_member' => $user_info['id'],
60
				'board_list' => $boards,
61
			)
62
		);
63
	}
64
	// Otherwise mark the board as read.
65
	else
66
	{
67
		$markRead = array();
68
		foreach ($boards as $board)
69
			$markRead[] = array($modSettings['maxMsgID'], $user_info['id'], $board);
70
71
		// Update log_mark_read and log_boards.
72
		$smcFunc['db_insert']('replace',
73
			'{db_prefix}log_mark_read',
74
			array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'),
75
			$markRead,
76
			array('id_board', 'id_member')
77
		);
78
79
		$smcFunc['db_insert']('replace',
80
			'{db_prefix}log_boards',
81
			array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'),
82
			$markRead,
83
			array('id_board', 'id_member')
84
		);
85
	}
86
87
	// Get rid of useless log_topics data, because log_mark_read is better for it - even if marking unread - I think so...
88
	// @todo look at this...
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
89
	// The call to markBoardsRead() in Display() used to be simply
90
	// marking log_boards (the previous query only)
91
	$result = $smcFunc['db_query']('', '
92
		SELECT MIN(id_topic)
93
		FROM {db_prefix}log_topics
94
		WHERE id_member = {int:current_member}',
95
		array(
96
			'current_member' => $user_info['id'],
97
		)
98
	);
99
	list ($lowest_topic) = $smcFunc['db_fetch_row']($result);
100
	$smcFunc['db_free_result']($result);
101
102
	if (empty($lowest_topic))
103
		return;
104
105
	// @todo SLOW This query seems to eat it sometimes.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
106
	$result = $smcFunc['db_query']('', '
107
		SELECT lt.id_topic
108
		FROM {db_prefix}log_topics AS lt
109
			INNER JOIN {db_prefix}topics AS t /*!40000 USE INDEX (PRIMARY) */ ON (t.id_topic = lt.id_topic
110
				AND t.id_board IN ({array_int:board_list}))
111
		WHERE lt.id_member = {int:current_member}
112
			AND lt.id_topic >= {int:lowest_topic}
113
			AND lt.unwatched != 1',
114
		array(
115
			'current_member' => $user_info['id'],
116
			'board_list' => $boards,
117
			'lowest_topic' => $lowest_topic,
118
		)
119
	);
120
	$topics = array();
121
	while ($row = $smcFunc['db_fetch_assoc']($result))
122
		$topics[] = $row['id_topic'];
123
	$smcFunc['db_free_result']($result);
124
125
	if (!empty($topics))
126
		$smcFunc['db_query']('', '
127
			DELETE FROM {db_prefix}log_topics
128
			WHERE id_member = {int:current_member}
129
				AND id_topic IN ({array_int:topic_list})',
130
			array(
131
				'current_member' => $user_info['id'],
132
				'topic_list' => $topics,
133
			)
134
		);
135
}
136
137
/**
138
 * Mark one or more boards as read.
139
 */
140
function MarkRead()
141
{
142
	global $board, $topic, $user_info, $board_info, $modSettings, $smcFunc;
143
144
	// No Guests allowed!
145
	is_not_guest();
146
147
	checkSession('get');
148
149
	if (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'all')
150
	{
151
		// Find all the boards this user can see.
152
		$result = $smcFunc['db_query']('', '
153
			SELECT b.id_board
154
			FROM {db_prefix}boards AS b
155
			WHERE {query_see_board}',
156
			array(
157
			)
158
		);
159
		$boards = array();
160
		while ($row = $smcFunc['db_fetch_assoc']($result))
161
			$boards[] = $row['id_board'];
162
		$smcFunc['db_free_result']($result);
163
164
		if (!empty($boards))
165
			markBoardsRead($boards, isset($_REQUEST['unread']));
166
167
		$_SESSION['id_msg_last_visit'] = $modSettings['maxMsgID'];
168 View Code Duplication
		if (!empty($_SESSION['old_url']) && strpos($_SESSION['old_url'], 'action=unread') !== false)
169
			redirectexit('action=unread');
170
171
		if (isset($_SESSION['topicseen_cache']))
172
			$_SESSION['topicseen_cache'] = array();
173
174
		redirectexit();
175
	}
176
	elseif (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'unreadreplies')
177
	{
178
		// Make sure all the topics are integers!
179
		$topics = array_map('intval', explode('-', $_REQUEST['topics']));
180
181
		$request = $smcFunc['db_query']('', '
182
			SELECT id_topic, unwatched
183
			FROM {db_prefix}log_topics
184
			WHERE id_topic IN ({array_int:selected_topics})
185
				AND id_member = {int:current_user}',
186
			array(
187
				'selected_topics' => $topics,
188
				'current_user' => $user_info['id'],
189
			)
190
		);
191
		$logged_topics = array();
192 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
193
			$logged_topics[$row['id_topic']] = $row['unwatched'];
194
		$smcFunc['db_free_result']($request);
195
196
		$markRead = array();
197 View Code Duplication
		foreach ($topics as $id_topic)
198
			$markRead[] = array($modSettings['maxMsgID'], $user_info['id'], $id_topic, (isset($logged_topics[$topic]) ? $logged_topics[$topic] : 0));
199
200
		$smcFunc['db_insert']('replace',
201
			'{db_prefix}log_topics',
202
			array('id_msg' => 'int', 'id_member' => 'int', 'id_topic' => 'int', 'unwatched' => 'int'),
203
			$markRead,
204
			array('id_member', 'id_topic')
205
		);
206
207
		if (isset($_SESSION['topicseen_cache']))
208
			$_SESSION['topicseen_cache'] = array();
209
210
		redirectexit('action=unreadreplies');
211
	}
212
213
	// Special case: mark a topic unread!
214
	elseif (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'topic')
215
	{
216
		// First, let's figure out what the latest message is.
217
		$result = $smcFunc['db_query']('', '
218
			SELECT t.id_first_msg, t.id_last_msg, COALESCE(lt.unwatched, 0) as unwatched
219
			FROM {db_prefix}topics as t
220
			LEFT JOIN {db_prefix}log_topics as lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
221
			WHERE t.id_topic = {int:current_topic}',
222
			array(
223
				'current_topic' => $topic,
224
				'current_member' => $user_info['id'],
225
			)
226
		);
227
		$topicinfo = $smcFunc['db_fetch_assoc']($result);
228
		$smcFunc['db_free_result']($result);
229
230
		if (!empty($_GET['t']))
231
		{
232
			// If they read the whole topic, go back to the beginning.
233
			if ($_GET['t'] >= $topicinfo['id_last_msg'])
234
				$earlyMsg = 0;
235
			// If they want to mark the whole thing read, same.
236
			elseif ($_GET['t'] <= $topicinfo['id_first_msg'])
237
				$earlyMsg = 0;
238
			// Otherwise, get the latest message before the named one.
239 View Code Duplication
			else
240
			{
241
				$result = $smcFunc['db_query']('', '
242
					SELECT MAX(id_msg)
243
					FROM {db_prefix}messages
244
					WHERE id_topic = {int:current_topic}
245
						AND id_msg >= {int:id_first_msg}
246
						AND id_msg < {int:topic_msg_id}',
247
					array(
248
						'current_topic' => $topic,
249
						'topic_msg_id' => (int) $_GET['t'],
250
						'id_first_msg' => $topicinfo['id_first_msg'],
251
					)
252
				);
253
				list ($earlyMsg) = $smcFunc['db_fetch_row']($result);
254
				$smcFunc['db_free_result']($result);
255
			}
256
		}
257
		// Marking read from first page?  That's the whole topic.
258
		elseif ($_REQUEST['start'] == 0)
259
			$earlyMsg = 0;
260 View Code Duplication
		else
261
		{
262
			$result = $smcFunc['db_query']('', '
263
				SELECT id_msg
264
				FROM {db_prefix}messages
265
				WHERE id_topic = {int:current_topic}
266
				ORDER BY id_msg
267
				LIMIT {int:start}, 1',
268
				array(
269
					'current_topic' => $topic,
270
					'start' => (int) $_REQUEST['start'],
271
				)
272
			);
273
			list ($earlyMsg) = $smcFunc['db_fetch_row']($result);
274
			$smcFunc['db_free_result']($result);
275
276
			$earlyMsg--;
277
		}
278
279
		// Blam, unread!
280
		$smcFunc['db_insert']('replace',
281
			'{db_prefix}log_topics',
282
			array('id_msg' => 'int', 'id_member' => 'int', 'id_topic' => 'int', 'unwatched' => 'int'),
283
			array($earlyMsg, $user_info['id'], $topic, $topicinfo['unwatched']),
284
			array('id_member', 'id_topic')
285
		);
286
287
		redirectexit('board=' . $board . '.0');
288
	}
289
	else
290
	{
291
		$categories = array();
292
		$boards = array();
293
294 View Code Duplication
		if (isset($_REQUEST['c']))
295
		{
296
			$_REQUEST['c'] = explode(',', $_REQUEST['c']);
297
			foreach ($_REQUEST['c'] as $c)
298
				$categories[] = (int) $c;
299
		}
300 View Code Duplication
		if (isset($_REQUEST['boards']))
301
		{
302
			$_REQUEST['boards'] = explode(',', $_REQUEST['boards']);
303
			foreach ($_REQUEST['boards'] as $b)
304
				$boards[] = (int) $b;
305
		}
306
		if (!empty($board))
307
			$boards[] = (int) $board;
308
309 View Code Duplication
		if (isset($_REQUEST['children']) && !empty($boards))
310
		{
311
			// They want to mark the entire tree starting with the boards specified
312
			// The easiest thing is to just get all the boards they can see, but since we've specified the top of tree we ignore some of them
313
314
			$request = $smcFunc['db_query']('', '
315
				SELECT b.id_board, b.id_parent
316
				FROM {db_prefix}boards AS b
317
				WHERE {query_see_board}
318
					AND b.child_level > {int:no_parents}
319
					AND b.id_board NOT IN ({array_int:board_list})
320
				ORDER BY child_level ASC
321
				',
322
				array(
323
					'no_parents' => 0,
324
					'board_list' => $boards,
325
				)
326
			);
327
			while ($row = $smcFunc['db_fetch_assoc']($request))
328
				if (in_array($row['id_parent'], $boards))
329
					$boards[] = $row['id_board'];
330
			$smcFunc['db_free_result']($request);
331
		}
332
333
		$clauses = array();
334
		$clauseParameters = array();
335
		if (!empty($categories))
336
		{
337
			$clauses[] = 'id_cat IN ({array_int:category_list})';
338
			$clauseParameters['category_list'] = $categories;
339
		}
340
		if (!empty($boards))
341
		{
342
			$clauses[] = 'id_board IN ({array_int:board_list})';
343
			$clauseParameters['board_list'] = $boards;
344
		}
345
346
		if (empty($clauses))
347
			redirectexit();
348
349
		$request = $smcFunc['db_query']('', '
350
			SELECT b.id_board
351
			FROM {db_prefix}boards AS b
352
			WHERE {query_see_board}
353
				AND b.' . implode(' OR b.', $clauses),
354
			array_merge($clauseParameters, array(
355
			))
356
		);
357
		$boards = array();
358
		while ($row = $smcFunc['db_fetch_assoc']($request))
359
			$boards[] = $row['id_board'];
360
		$smcFunc['db_free_result']($request);
361
362
		if (empty($boards))
363
			redirectexit();
364
365
		markBoardsRead($boards, isset($_REQUEST['unread']));
366
367
		foreach ($boards as $b)
368
		{
369
			if (isset($_SESSION['topicseen_cache'][$b]))
370
				$_SESSION['topicseen_cache'][$b] = array();
371
		}
372
373
		if (!isset($_REQUEST['unread']))
374
		{
375
			// Find all the boards this user can see.
376
			$result = $smcFunc['db_query']('', '
377
				SELECT b.id_board
378
				FROM {db_prefix}boards AS b
379
				WHERE b.id_parent IN ({array_int:parent_list})
380
					AND {query_see_board}',
381
				array(
382
					'parent_list' => $boards,
383
				)
384
			);
385
			if ($smcFunc['db_num_rows']($result) > 0)
386
			{
387
				$logBoardInserts = array();
388
				while ($row = $smcFunc['db_fetch_assoc']($result))
389
					$logBoardInserts[] = array($modSettings['maxMsgID'], $user_info['id'], $row['id_board']);
390
391
				$smcFunc['db_insert']('replace',
392
					'{db_prefix}log_boards',
393
					array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'),
394
					$logBoardInserts,
395
					array('id_member', 'id_board')
396
				);
397
			}
398
			$smcFunc['db_free_result']($result);
399
400
			if (empty($board))
401
				redirectexit();
402
			else
403
				redirectexit('board=' . $board . '.0');
404
		}
405
		else
406
		{
407
			if (empty($board_info['parent']))
408
				redirectexit();
409
			else
410
				redirectexit('board=' . $board_info['parent'] . '.0');
411
		}
412
	}
413
}
414
415
/**
416
 * Get the id_member associated with the specified message.
417
 * @param int $messageID The ID of the message
418
 * @return int The ID of the member associated with that post
419
 */
420
function getMsgMemberID($messageID)
421
{
422
	global $smcFunc;
423
424
	// Find the topic and make sure the member still exists.
425
	$result = $smcFunc['db_query']('', '
426
		SELECT COALESCE(mem.id_member, 0)
427
		FROM {db_prefix}messages AS m
428
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
429
		WHERE m.id_msg = {int:selected_message}
430
		LIMIT 1',
431
		array(
432
			'selected_message' => (int) $messageID,
433
		)
434
	);
435 View Code Duplication
	if ($smcFunc['db_num_rows']($result) > 0)
436
		list ($memberID) = $smcFunc['db_fetch_row']($result);
437
	// The message doesn't even exist.
438
	else
439
		$memberID = 0;
440
	$smcFunc['db_free_result']($result);
441
442
	return (int) $memberID;
443
}
444
445
/**
446
 * Modify the settings and position of a board.
447
 * Used by ManageBoards.php to change the settings of a board.
448
 *
449
 * @param int $board_id The ID of the board
450
 * @param array &$boardOptions An array of options related to the board
451
 */
452
function modifyBoard($board_id, &$boardOptions)
453
{
454
	global $cat_tree, $boards, $smcFunc;
455
456
	// Get some basic information about all boards and categories.
457
	getBoardTree();
458
459
	// Make sure given boards and categories exist.
460
	if (!isset($boards[$board_id]) || (isset($boardOptions['target_board']) && !isset($boards[$boardOptions['target_board']])) || (isset($boardOptions['target_category']) && !isset($cat_tree[$boardOptions['target_category']])))
461
		fatal_lang_error('no_board');
462
463
	$id = $board_id;
464
	call_integration_hook('integrate_pre_modify_board', array($id, &$boardOptions));
465
466
	// All things that will be updated in the database will be in $boardUpdates.
467
	$boardUpdates = array();
468
	$boardUpdateParameters = array();
469
470
	// In case the board has to be moved
471
	if (isset($boardOptions['move_to']))
472
	{
473
		// Move the board to the top of a given category.
474
		if ($boardOptions['move_to'] == 'top')
475
		{
476
			$id_cat = $boardOptions['target_category'];
477
			$child_level = 0;
478
			$id_parent = 0;
479
			$after = $cat_tree[$id_cat]['last_board_order'];
480
		}
481
482
		// Move the board to the bottom of a given category.
483
		elseif ($boardOptions['move_to'] == 'bottom')
484
		{
485
			$id_cat = $boardOptions['target_category'];
486
			$child_level = 0;
487
			$id_parent = 0;
488
			$after = 0;
489
			foreach ($cat_tree[$id_cat]['children'] as $id_board => $dummy)
490
				$after = max($after, $boards[$id_board]['order']);
491
		}
492
493
		// Make the board a child of a given board.
494
		elseif ($boardOptions['move_to'] == 'child')
495
		{
496
			$id_cat = $boards[$boardOptions['target_board']]['category'];
497
			$child_level = $boards[$boardOptions['target_board']]['level'] + 1;
498
			$id_parent = $boardOptions['target_board'];
499
500
			// People can be creative, in many ways...
501
			if (isChildOf($id_parent, $board_id))
502
				fatal_lang_error('mboards_parent_own_child_error', false);
503
			elseif ($id_parent == $board_id)
504
				fatal_lang_error('mboards_board_own_child_error', false);
505
506
			$after = $boards[$boardOptions['target_board']]['order'];
507
508
			// Check if there are already children and (if so) get the max board order.
509
			if (!empty($boards[$id_parent]['tree']['children']) && empty($boardOptions['move_first_child']))
510
				foreach ($boards[$id_parent]['tree']['children'] as $childBoard_id => $dummy)
511
					$after = max($after, $boards[$childBoard_id]['order']);
512
		}
513
514
		// Place a board before or after another board, on the same child level.
515
		elseif (in_array($boardOptions['move_to'], array('before', 'after')))
516
		{
517
			$id_cat = $boards[$boardOptions['target_board']]['category'];
518
			$child_level = $boards[$boardOptions['target_board']]['level'];
519
			$id_parent = $boards[$boardOptions['target_board']]['parent'];
520
			$after = $boards[$boardOptions['target_board']]['order'] - ($boardOptions['move_to'] == 'before' ? 1 : 0);
521
		}
522
523
		// Oops...?
524
		else
525
			trigger_error('modifyBoard(): The move_to value \'' . $boardOptions['move_to'] . '\' is incorrect', E_USER_ERROR);
526
527
		// Get a list of children of this board.
528
		$childList = array();
529
		recursiveBoards($childList, $boards[$board_id]['tree']);
530
531
		// See if there are changes that affect children.
532
		$childUpdates = array();
533
		$levelDiff = $child_level - $boards[$board_id]['level'];
0 ignored issues
show
Bug introduced by
The variable $child_level 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...
534
		if ($levelDiff != 0)
535
			$childUpdates[] = 'child_level = child_level ' . ($levelDiff > 0 ? '+ ' : '') . '{int:level_diff}';
536
		if ($id_cat != $boards[$board_id]['category'])
0 ignored issues
show
Bug introduced by
The variable $id_cat 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...
537
			$childUpdates[] = 'id_cat = {int:category}';
538
539
		// Fix the children of this board.
540
		if (!empty($childList) && !empty($childUpdates))
541
			$smcFunc['db_query']('', '
542
				UPDATE {db_prefix}boards
543
				SET ' . implode(',
544
					', $childUpdates) . '
545
				WHERE id_board IN ({array_int:board_list})',
546
				array(
547
					'board_list' => $childList,
548
					'category' => $id_cat,
549
					'level_diff' => $levelDiff,
550
				)
551
			);
552
553
		// Make some room for this spot.
554
		$smcFunc['db_query']('', '
555
			UPDATE {db_prefix}boards
556
			SET board_order = board_order + {int:new_order}
557
			WHERE board_order > {int:insert_after}
558
				AND id_board != {int:selected_board}',
559
			array(
560
				'insert_after' => $after,
0 ignored issues
show
Bug introduced by
The variable $after 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...
561
				'selected_board' => $board_id,
562
				'new_order' => 1 + count($childList),
563
			)
564
		);
565
566
		$boardUpdates[] = 'id_cat = {int:id_cat}';
567
		$boardUpdates[] = 'id_parent = {int:id_parent}';
568
		$boardUpdates[] = 'child_level = {int:child_level}';
569
		$boardUpdates[] = 'board_order = {int:board_order}';
570
		$boardUpdateParameters += array(
571
			'id_cat' => $id_cat,
572
			'id_parent' => $id_parent,
0 ignored issues
show
Bug introduced by
The variable $id_parent 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...
573
			'child_level' => $child_level,
574
			'board_order' => $after + 1,
575
		);
576
	}
577
578
	// This setting is a little twisted in the database...
579
	if (isset($boardOptions['posts_count']))
580
	{
581
		$boardUpdates[] = 'count_posts = {int:count_posts}';
582
		$boardUpdateParameters['count_posts'] = $boardOptions['posts_count'] ? 0 : 1;
583
	}
584
585
	// Set the theme for this board.
586
	if (isset($boardOptions['board_theme']))
587
	{
588
		$boardUpdates[] = 'id_theme = {int:id_theme}';
589
		$boardUpdateParameters['id_theme'] = (int) $boardOptions['board_theme'];
590
	}
591
592
	// Should the board theme override the user preferred theme?
593
	if (isset($boardOptions['override_theme']))
594
	{
595
		$boardUpdates[] = 'override_theme = {int:override_theme}';
596
		$boardUpdateParameters['override_theme'] = $boardOptions['override_theme'] ? 1 : 0;
597
	}
598
599
	// Who's allowed to access this board.
600
	if (isset($boardOptions['access_groups']))
601
	{
602
		$boardUpdates[] = 'member_groups = {string:member_groups}';
603
		$boardUpdateParameters['member_groups'] = implode(',', $boardOptions['access_groups']);
604
	}
605
606
	// And who isn't.
607 View Code Duplication
	if (isset($boardOptions['deny_groups']))
608
	{
609
		$boardUpdates[] = 'deny_member_groups = {string:deny_groups}';
610
		$boardUpdateParameters['deny_groups'] = implode(',', $boardOptions['deny_groups']);
611
	}
612
613
	if (isset($boardOptions['board_name']))
614
	{
615
		$boardUpdates[] = 'name = {string:board_name}';
616
		$boardUpdateParameters['board_name'] = $boardOptions['board_name'];
617
	}
618
619
	if (isset($boardOptions['board_description']))
620
	{
621
		$boardUpdates[] = 'description = {string:board_description}';
622
		$boardUpdateParameters['board_description'] = $boardOptions['board_description'];
623
	}
624
625
	if (isset($boardOptions['profile']))
626
	{
627
		$boardUpdates[] = 'id_profile = {int:profile}';
628
		$boardUpdateParameters['profile'] = (int) $boardOptions['profile'];
629
	}
630
631 View Code Duplication
	if (isset($boardOptions['redirect']))
632
	{
633
		$boardUpdates[] = 'redirect = {string:redirect}';
634
		$boardUpdateParameters['redirect'] = $boardOptions['redirect'];
635
	}
636
637
	if (isset($boardOptions['num_posts']))
638
	{
639
		$boardUpdates[] = 'num_posts = {int:num_posts}';
640
		$boardUpdateParameters['num_posts'] = (int) $boardOptions['num_posts'];
641
	}
642
643
	$id = $board_id;
644
	call_integration_hook('integrate_modify_board', array($id, $boardOptions, &$boardUpdates, &$boardUpdateParameters));
645
646
	// Do the updates (if any).
647
	if (!empty($boardUpdates))
648
		$smcFunc['db_query']('', '
649
			UPDATE {db_prefix}boards
650
			SET
651
				' . implode(',
652
				', $boardUpdates) . '
653
			WHERE id_board = {int:selected_board}',
654
			array_merge($boardUpdateParameters, array(
655
				'selected_board' => $board_id,
656
			))
657
		);
658
659
	// Set moderators of this board.
660
	if (isset($boardOptions['moderators']) || isset($boardOptions['moderator_string']) || isset($boardOptions['moderator_groups']) || isset($boardOptions['moderator_group_string']))
661
	{
662
		// Reset current moderators for this board - if there are any!
663
		$smcFunc['db_query']('', '
664
			DELETE FROM {db_prefix}moderators
665
			WHERE id_board = {int:board_list}',
666
			array(
667
				'board_list' => $board_id,
668
			)
669
		);
670
671
		// Validate and get the IDs of the new moderators.
672
		if (isset($boardOptions['moderator_string']) && trim($boardOptions['moderator_string']) != '')
673
		{
674
			// Divvy out the usernames, remove extra space.
675
			$moderator_string = strtr($smcFunc['htmlspecialchars']($boardOptions['moderator_string'], ENT_QUOTES), array('&quot;' => '"'));
676
			preg_match_all('~"([^"]+)"~', $moderator_string, $matches);
677
			$moderators = array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $moderator_string)));
678 View Code Duplication
			for ($k = 0, $n = count($moderators); $k < $n; $k++)
679
			{
680
				$moderators[$k] = trim($moderators[$k]);
681
682
				if (strlen($moderators[$k]) == 0)
683
					unset($moderators[$k]);
684
			}
685
686
			// Find all the id_member's for the member_name's in the list.
687
			if (empty($boardOptions['moderators']))
688
				$boardOptions['moderators'] = array();
689
			if (!empty($moderators))
690
			{
691
				$request = $smcFunc['db_query']('', '
692
					SELECT id_member
693
					FROM {db_prefix}members
694
					WHERE member_name IN ({array_string:moderator_list}) OR real_name IN ({array_string:moderator_list})
695
					LIMIT {int:limit}',
696
					array(
697
						'moderator_list' => $moderators,
698
						'limit' => count($moderators),
699
					)
700
				);
701
				while ($row = $smcFunc['db_fetch_assoc']($request))
702
					$boardOptions['moderators'][] = $row['id_member'];
703
				$smcFunc['db_free_result']($request);
704
			}
705
		}
706
707
		// Add the moderators to the board.
708
		if (!empty($boardOptions['moderators']))
709
		{
710
			$inserts = array();
711
			foreach ($boardOptions['moderators'] as $moderator)
712
				$inserts[] = array($board_id, $moderator);
713
714
			$smcFunc['db_insert']('insert',
715
				'{db_prefix}moderators',
716
				array('id_board' => 'int', 'id_member' => 'int'),
717
				$inserts,
718
				array('id_board', 'id_member')
719
			);
720
		}
721
722
		// Reset current moderator groups for this board - if there are any!
723
		$smcFunc['db_query']('', '
724
			DELETE FROM {db_prefix}moderator_groups
725
			WHERE id_board = {int:board_list}',
726
			array(
727
				'board_list' => $board_id,
728
			)
729
		);
730
731
		// Validate and get the IDs of the new moderator groups.
732
		if (isset($boardOptions['moderator_group_string']) && trim($boardOptions['moderator_group_string']) != '')
733
		{
734
			// Divvy out the group names, remove extra space.
735
			$moderator_group_string = strtr($smcFunc['htmlspecialchars']($boardOptions['moderator_group_string'], ENT_QUOTES), array('&quot;' => '"'));
736
			preg_match_all('~"([^"]+)"~', $moderator_group_string, $matches);
737
			$moderator_groups = array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $moderator_group_string)));
738 View Code Duplication
			for ($k = 0, $n = count($moderator_groups); $k < $n; $k++)
739
			{
740
				$moderator_groups[$k] = trim($moderator_groups[$k]);
741
742
				if (strlen($moderator_groups[$k]) == 0)
743
					unset($moderator_groups[$k]);
744
			}
745
746
			/* 	Find all the id_group's for all the group names in the list
747
				But skip any invalid ones (invisible/post groups/Administrator/Moderator) */
748
			if (empty($boardOptions['moderator_groups']))
749
				$boardOptions['moderator_groups'] = array();
750
			if (!empty($moderator_groups))
751
			{
752
				$request = $smcFunc['db_query']('', '
753
					SELECT id_group
754
					FROM {db_prefix}membergroups
755
					WHERE group_name IN ({array_string:moderator_group_list})
756
						AND hidden = {int:visible}
757
						AND min_posts = {int:negative_one}
758
						AND id_group NOT IN ({array_int:invalid_groups})
759
					LIMIT {int:limit}',
760
					array(
761
						'visible' => 0,
762
						'negative_one' => -1,
763
						'invalid_groups' => array(1, 3),
764
						'moderator_group_list' => $moderator_groups,
765
						'limit' => count($moderator_groups),
766
					)
767
				);
768
				while ($row = $smcFunc['db_fetch_assoc']($request))
769
				{
770
					$boardOptions['moderator_groups'][] = $row['id_group'];
771
				}
772
				$smcFunc['db_free_result']($request);
773
			}
774
		}
775
776
		// Add the moderator groups to the board.
777
		if (!empty($boardOptions['moderator_groups']))
778
		{
779
			$inserts = array();
780
			foreach ($boardOptions['moderator_groups'] as $moderator_group)
781
				$inserts[] = array($board_id, $moderator_group);
782
783
			$smcFunc['db_insert']('insert',
784
				'{db_prefix}moderator_groups',
785
				array('id_board' => 'int', 'id_group' => 'int'),
786
				$inserts,
787
				array('id_board', 'id_group')
788
			);
789
		}
790
791
		// Note that caches can now be wrong!
792
		updateSettings(array('settings_updated' => time()));
793
	}
794
795
	if (isset($boardOptions['move_to']))
796
		reorderBoards();
797
798
	clean_cache('data');
799
800
	if (empty($boardOptions['dont_log']))
801
		logAction('edit_board', array('board' => $board_id), 'admin');
802
}
803
804
/**
805
 * Create a new board and set its properties and position.
806
 * Allows (almost) the same options as the modifyBoard() function.
807
 * With the option inherit_permissions set, the parent board permissions
808
 * will be inherited.
809
 *
810
 * @param array $boardOptions An array of information for the new board
811
 * @return int The ID of the new board
812
 */
813
function createBoard($boardOptions)
814
{
815
	global $boards, $smcFunc;
816
817
	// Trigger an error if one of the required values is not set.
818
	if (!isset($boardOptions['board_name']) || trim($boardOptions['board_name']) == '' || !isset($boardOptions['move_to']) || !isset($boardOptions['target_category']))
819
		trigger_error('createBoard(): One or more of the required options is not set', E_USER_ERROR);
820
821
	if (in_array($boardOptions['move_to'], array('child', 'before', 'after')) && !isset($boardOptions['target_board']))
822
		trigger_error('createBoard(): Target board is not set', E_USER_ERROR);
823
824
	// Set every optional value to its default value.
825
	$boardOptions += array(
826
		'posts_count' => true,
827
		'override_theme' => false,
828
		'board_theme' => 0,
829
		'access_groups' => array(),
830
		'board_description' => '',
831
		'profile' => 1,
832
		'moderators' => '',
833
		'inherit_permissions' => true,
834
		'dont_log' => true,
835
	);
836
	$board_columns = array(
837
		'id_cat' => 'int', 'name' => 'string-255', 'description' => 'string', 'board_order' => 'int',
838
		'member_groups' => 'string', 'redirect' => 'string',
839
	);
840
	$board_parameters = array(
841
		$boardOptions['target_category'], $boardOptions['board_name'], '', 0,
842
		'-1,0', '',
843
	);
844
845
	call_integration_hook('integrate_create_board', array(&$boardOptions, &$board_columns, &$board_parameters));
846
847
	// Insert a board, the settings are dealt with later.
848
	$board_id = $smcFunc['db_insert']('',
849
		'{db_prefix}boards',
850
		$board_columns,
851
		$board_parameters,
852
		array('id_board'),
853
		1
854
	);
855
856
	if (empty($board_id))
857
		return 0;
858
859
	// Change the board according to the given specifications.
860
	modifyBoard($board_id, $boardOptions);
861
862
	// Do we want the parent permissions to be inherited?
863
	if ($boardOptions['inherit_permissions'])
864
	{
865
		getBoardTree();
866
867
		if (!empty($boards[$board_id]['parent']))
868
		{
869
			$request = $smcFunc['db_query']('', '
870
				SELECT id_profile
871
				FROM {db_prefix}boards
872
				WHERE id_board = {int:board_parent}
873
				LIMIT 1',
874
				array(
875
					'board_parent' => (int) $boards[$board_id]['parent'],
876
				)
877
			);
878
			list ($boardOptions['profile']) = $smcFunc['db_fetch_row']($request);
879
			$smcFunc['db_free_result']($request);
880
881
			$smcFunc['db_query']('', '
882
				UPDATE {db_prefix}boards
883
				SET id_profile = {int:new_profile}
884
				WHERE id_board = {int:current_board}',
885
				array(
886
					'new_profile' => $boardOptions['profile'],
887
					'current_board' => $board_id,
888
				)
889
			);
890
		}
891
	}
892
893
	// Clean the data cache.
894
	clean_cache('data');
895
896
	// Created it.
897
	logAction('add_board', array('board' => $board_id), 'admin');
898
899
	// Here you are, a new board, ready to be spammed.
900
	return $board_id;
901
}
902
903
/**
904
 * Remove one or more boards.
905
 * Allows to move the children of the board before deleting it
906
 * if moveChildrenTo is set to null, the child boards will be deleted.
907
 * Deletes:
908
 *   - all topics that are on the given boards;
909
 *   - all information that's associated with the given boards;
910
 * updates the statistics to reflect the new situation.
911
 *
912
 * @param array $boards_to_remove The boards to remove
913
 * @param int $moveChildrenTo The ID of the board to move the child boards to (null to remove the child boards, 0 to make them a top-level board)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $moveChildrenTo not be integer|null?

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...
914
 */
915
function deleteBoards($boards_to_remove, $moveChildrenTo = null)
916
{
917
	global $sourcedir, $boards, $smcFunc;
918
919
	// No boards to delete? Return!
920
	if (empty($boards_to_remove))
921
		return;
922
923
	getBoardTree();
924
925
	call_integration_hook('integrate_delete_board', array($boards_to_remove, &$moveChildrenTo));
926
927
	// If $moveChildrenTo is set to null, include the children in the removal.
928
	if ($moveChildrenTo === null)
929
	{
930
		// Get a list of the child boards that will also be removed.
931
		$child_boards_to_remove = array();
932
		foreach ($boards_to_remove as $board_to_remove)
933
			recursiveBoards($child_boards_to_remove, $boards[$board_to_remove]['tree']);
934
935
		// Merge the children with their parents.
936
		if (!empty($child_boards_to_remove))
937
			$boards_to_remove = array_unique(array_merge($boards_to_remove, $child_boards_to_remove));
938
	}
939
	// Move the children to a safe home.
940
	else
941
	{
942
		foreach ($boards_to_remove as $id_board)
943
		{
944
			// @todo Separate category?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
945
			if ($moveChildrenTo === 0)
946
				fixChildren($id_board, 0, 0);
947
			else
948
				fixChildren($id_board, $boards[$moveChildrenTo]['level'] + 1, $moveChildrenTo);
949
		}
950
	}
951
952
	// Delete ALL topics in the selected boards (done first so topics can't be marooned.)
953
	$request = $smcFunc['db_query']('', '
954
		SELECT id_topic
955
		FROM {db_prefix}topics
956
		WHERE id_board IN ({array_int:boards_to_remove})',
957
		array(
958
			'boards_to_remove' => $boards_to_remove,
959
		)
960
	);
961
	$topics = array();
962
	while ($row = $smcFunc['db_fetch_assoc']($request))
963
		$topics[] = $row['id_topic'];
964
	$smcFunc['db_free_result']($request);
965
966
	require_once($sourcedir . '/RemoveTopic.php');
967
	removeTopics($topics, false);
968
969
	// Delete the board's logs.
970
	$smcFunc['db_query']('', '
971
		DELETE FROM {db_prefix}log_mark_read
972
		WHERE id_board IN ({array_int:boards_to_remove})',
973
		array(
974
			'boards_to_remove' => $boards_to_remove,
975
		)
976
	);
977
	$smcFunc['db_query']('', '
978
		DELETE FROM {db_prefix}log_boards
979
		WHERE id_board IN ({array_int:boards_to_remove})',
980
		array(
981
			'boards_to_remove' => $boards_to_remove,
982
		)
983
	);
984
	$smcFunc['db_query']('', '
985
		DELETE FROM {db_prefix}log_notify
986
		WHERE id_board IN ({array_int:boards_to_remove})',
987
		array(
988
			'boards_to_remove' => $boards_to_remove,
989
		)
990
	);
991
992
	// Delete this board's moderators.
993
	$smcFunc['db_query']('', '
994
		DELETE FROM {db_prefix}moderators
995
		WHERE id_board IN ({array_int:boards_to_remove})',
996
		array(
997
			'boards_to_remove' => $boards_to_remove,
998
		)
999
	);
1000
1001
	// Delete this board's moderator groups.
1002
	$smcFunc['db_query']('', '
1003
		DELETE FROM {db_prefix}moderator_groups
1004
		WHERE id_board IN ({array_int:boards_to_remove})',
1005
		array(
1006
			'boards_to_remove' => $boards_to_remove,
1007
		)
1008
	);
1009
1010
	// Delete any extra events in the calendar.
1011
	$smcFunc['db_query']('', '
1012
		DELETE FROM {db_prefix}calendar
1013
		WHERE id_board IN ({array_int:boards_to_remove})',
1014
		array(
1015
			'boards_to_remove' => $boards_to_remove,
1016
		)
1017
	);
1018
1019
	// Delete any message icons that only appear on these boards.
1020
	$smcFunc['db_query']('', '
1021
		DELETE FROM {db_prefix}message_icons
1022
		WHERE id_board IN ({array_int:boards_to_remove})',
1023
		array(
1024
			'boards_to_remove' => $boards_to_remove,
1025
		)
1026
	);
1027
1028
	// Delete the boards.
1029
	$smcFunc['db_query']('', '
1030
		DELETE FROM {db_prefix}boards
1031
		WHERE id_board IN ({array_int:boards_to_remove})',
1032
		array(
1033
			'boards_to_remove' => $boards_to_remove,
1034
		)
1035
	);
1036
1037
	// Latest message/topic might not be there anymore.
1038
	updateStats('message');
1039
	updateStats('topic');
1040
	updateSettings(array(
1041
		'calendar_updated' => time(),
1042
	));
1043
1044
	// Plus reset the cache to stop people getting odd results.
1045
	updateSettings(array('settings_updated' => time()));
1046
1047
	// Clean the cache as well.
1048
	clean_cache('data');
1049
1050
	// Let's do some serious logging.
1051
	foreach ($boards_to_remove as $id_board)
1052
		logAction('delete_board', array('boardname' => $boards[$id_board]['name']), 'admin');
1053
1054
	reorderBoards();
1055
}
1056
1057
/**
1058
 * Put all boards in the right order and sorts the records of the boards table.
1059
 * Used by modifyBoard(), deleteBoards(), modifyCategory(), and deleteCategories() functions
1060
 */
1061
function reorderBoards()
1062
{
1063
	global $cat_tree, $boardList, $boards, $smcFunc;
1064
1065
	getBoardTree();
1066
1067
	// Set the board order for each category.
1068
	$board_order = 0;
1069
	foreach ($cat_tree as $catID => $dummy)
1070
	{
1071
		foreach ($boardList[$catID] as $boardID)
1072
			if ($boards[$boardID]['order'] != ++$board_order)
1073
				$smcFunc['db_query']('', '
1074
					UPDATE {db_prefix}boards
1075
					SET board_order = {int:new_order}
1076
					WHERE id_board = {int:selected_board}',
1077
					array(
1078
						'new_order' => $board_order,
1079
						'selected_board' => $boardID,
1080
					)
1081
				);
1082
	}
1083
1084
	// Empty the board order cache
1085
	cache_put_data('board_order', null, -3600);
1086
}
1087
1088
/**
1089
 * Fixes the children of a board by setting their child_levels to new values.
1090
 * Used when a board is deleted or moved, to affect its children.
1091
 *
1092
 * @param int $parent The ID of the parent board
1093
 * @param int $newLevel The new child level for each of the child boards
1094
 * @param int $newParent The ID of the new parent board
1095
 */
1096
function fixChildren($parent, $newLevel, $newParent)
1097
{
1098
	global $smcFunc;
1099
1100
	// Grab all children of $parent...
1101
	$result = $smcFunc['db_query']('', '
1102
		SELECT id_board
1103
		FROM {db_prefix}boards
1104
		WHERE id_parent = {int:parent_board}',
1105
		array(
1106
			'parent_board' => $parent,
1107
		)
1108
	);
1109
	$children = array();
1110
	while ($row = $smcFunc['db_fetch_assoc']($result))
1111
		$children[] = $row['id_board'];
1112
	$smcFunc['db_free_result']($result);
1113
1114
	// ...and set it to a new parent and child_level.
1115
	$smcFunc['db_query']('', '
1116
		UPDATE {db_prefix}boards
1117
		SET id_parent = {int:new_parent}, child_level = {int:new_child_level}
1118
		WHERE id_parent = {int:parent_board}',
1119
		array(
1120
			'new_parent' => $newParent,
1121
			'new_child_level' => $newLevel,
1122
			'parent_board' => $parent,
1123
		)
1124
	);
1125
1126
	// Recursively fix the children of the children.
1127
	foreach ($children as $child)
1128
		fixChildren($child, $newLevel + 1, $child);
1129
}
1130
1131
/**
1132
 * Tries to load up the entire board order and category very very quickly
1133
 * Returns an array with two elements, cats and boards
1134
 *
1135
 * @return array An array of categories and boards
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
1136
 */
1137
function getTreeOrder()
1138
{
1139
	global $smcFunc;
1140
1141
	static $tree_order = array(
1142
		'cats' => array(),
1143
		'boards' => array(),
1144
	);
1145
1146
	if (!empty($tree_order['boards']))
1147
		return $tree_order;
1148
1149
	if (($cached = cache_get_data('board_order', 86400)) !== null)
1150
	{
1151
		$tree_order = $cached;
1152
		return $cached;
1153
	}
1154
1155
	$request = $smcFunc['db_query']('', '
1156
		SELECT b.id_board, b.id_cat
1157
		FROM {db_prefix}boards AS b
1158
		ORDER BY b.board_order',
1159
		array()
1160
	);
1161
	while ($row = $smcFunc['db_fetch_assoc']($request))
1162
	{
1163
		if (!in_array($row['id_cat'], $tree_order['cats']))
1164
			$tree_order['cats'][] = $row['id_cat'];
1165
		$tree_order['boards'][] = $row['id_board'];
1166
	}
1167
	$smcFunc['db_free_result']($request);
1168
1169
	cache_put_data('board_order', $tree_order, 86400);
1170
1171
	return $tree_order;
1172
}
1173
1174
/**
1175
 * Takes a board array and sorts it
1176
 *
1177
 * @param array &$boards The boards
1178
 */
1179
function sortBoards(array &$boards)
1180
{
1181
	$tree = getTreeOrder();
1182
1183
	$ordered = array();
1184
	foreach ($tree['boards'] as $board)
1185
		if (!empty($boards[$board]))
1186
		{
1187
			$ordered[$board] = $boards[$board];
1188
1189 View Code Duplication
			if (is_array($ordered[$board]) && !empty($ordered[$board]['boards']))
1190
				sortBoards($ordered[$board]['boards']);
1191
1192 View Code Duplication
			if (is_array($ordered[$board]) && !empty($ordered[$board]['children']))
1193
				sortBoards($ordered[$board]['children']);
1194
		}
1195
1196
	$boards = $ordered;
1197
}
1198
1199
/**
1200
 * Takes a category array and sorts it
1201
 *
1202
 * @param array &$categories The categories
1203
 */
1204
function sortCategories(array &$categories)
1205
{
1206
	$tree = getTreeOrder();
1207
1208
	$ordered = array();
1209
	foreach ($tree['cats'] as $cat)
1210
		if (!empty($categories[$cat]))
1211
		{
1212
			$ordered[$cat] = $categories[$cat];
1213
			if (!empty($ordered[$cat]['boards']))
1214
				sortBoards($ordered[$cat]['boards']);
1215
		}
1216
1217
	$categories = $ordered;
1218
}
1219
1220
/**
1221
 * Returns the given board's moderators, with their names and links
1222
 *
1223
 * @param array $boards The boards to get moderators of
1224
 * @return array An array containing information about the moderators of each board
1225
 */
1226 View Code Duplication
function getBoardModerators(array $boards)
0 ignored issues
show
Duplication introduced by
This function 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...
1227
{
1228
	global $smcFunc, $scripturl, $txt;
1229
1230
	if (empty($boards))
1231
		return array();
1232
1233
	$request = $smcFunc['db_query']('', '
1234
		SELECT mem.id_member, mem.real_name, mo.id_board
1235
		FROM {db_prefix}moderators AS mo
1236
		  INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mo.id_member)
1237
		WHERE mo.id_board IN ({array_int:boards})',
1238
		array(
1239
			'boards' => $boards,
1240
		)
1241
	);
1242
	$moderators = array();
1243
	while ($row = $smcFunc['db_fetch_assoc']($request))
1244
	{
1245
		if (empty($moderators[$row['id_board']]))
1246
			$moderators[$row['id_board']] = array();
1247
1248
		$moderators[$row['id_board']][] = array(
1249
			'id' => $row['id_member'],
1250
			'name' => $row['real_name'],
1251
			'href' => $scripturl . '?action=profile;u=' . $row['id_member'],
1252
			'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '" title="' . $txt['board_moderator'] . '">' . $row['real_name'] . '</a>',
1253
		);
1254
	}
1255
	$smcFunc['db_free_result']($request);
1256
1257
	return $moderators;
1258
}
1259
1260
/**
1261
 * Returns board's moderator groups with their names and link
1262
 *
1263
 * @param array $boards The boards to get moderator groups of
1264
 * @return array An array containing information about the groups assigned to moderate each board
1265
 */
1266 View Code Duplication
function getBoardModeratorGroups(array $boards)
0 ignored issues
show
Duplication introduced by
This function 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...
1267
{
1268
	global $smcFunc, $scripturl, $txt;
1269
1270
	if (empty($boards))
1271
		return array();
1272
1273
	$request = $smcFunc['db_query']('', '
1274
		SELECT mg.id_group, mg.group_name, bg.id_board
1275
		FROM {db_prefix}moderator_groups AS bg
1276
		  INNER JOIN {db_prefix}membergroups AS mg ON (mg.id_group = bg.id_group)
1277
		WHERE bg.id_board IN ({array_int:boards})',
1278
		array(
1279
			'boards' => $boards,
1280
		)
1281
	);
1282
	$groups = array();
1283
	while ($row = $smcFunc['db_fetch_assoc']($request))
1284
	{
1285
		if (empty($groups[$row['id_board']]))
1286
			$groups[$row['id_board']] = array();
1287
1288
		$groups[$row['id_board']][] = array(
1289
			'id' => $row['id_group'],
1290
			'name' => $row['group_name'],
1291
			'href' => $scripturl . '?action=groups;sa=members;group=' . $row['id_group'],
1292
			'link' => '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row['id_group'] . '" title="' . $txt['board_moderator'] . '">' . $row['group_name'] . '</a>',
1293
		);
1294
	}
1295
1296
	return $groups;
1297
}
1298
1299
/**
1300
 * Load a lot of useful information regarding the boards and categories.
1301
 * The information retrieved is stored in globals:
1302
 *  $boards		properties of each board.
1303
 *  $boardList	a list of boards grouped by category ID.
1304
 *  $cat_tree	properties of each category.
1305
 */
1306
function getBoardTree()
1307
{
1308
	global $cat_tree, $boards, $boardList, $smcFunc;
1309
1310
	$boardColumns = array(
1311
		'COALESCE(b.id_board, 0) AS id_board', 'b.id_parent', 'b.name AS board_name',
1312
		'b.description', 'b.child_level', 'b.board_order', 'b.count_posts', 'b.member_groups',
1313
		'b.id_theme', 'b.override_theme', 'b.id_profile', 'b.redirect', 'b.num_posts',
1314
		'b.num_topics', 'b.deny_member_groups', 'c.id_cat', 'c.name AS cat_name',
1315
		'c.description AS cat_desc', 'c.cat_order', 'c.can_collapse',
1316
	);
1317
1318
	// Let mods add extra columns and parameters to the SELECT query
1319
	$extraBoardColumns = array();
1320
	$extraBoardParameters = array();
1321
	call_integration_hook('integrate_pre_boardtree', array(&$extraBoardColumns, &$extraBoardParameters));
1322
1323
	$boardColumns = array_unique(array_merge($boardColumns, $extraBoardColumns));
1324
	$boardParameters = array_unique($extraBoardParameters);
1325
1326
	// Getting all the board and category information you'd ever wanted.
1327
	$request = $smcFunc['db_query']('', '
1328
		SELECT
1329
			' . implode(', ', $boardColumns) . '
1330
		FROM {db_prefix}categories AS c
1331
			LEFT JOIN {db_prefix}boards AS b ON (b.id_cat = c.id_cat)
1332
		WHERE {query_see_board}
1333
		ORDER BY c.cat_order, b.child_level, b.board_order',
1334
		$boardParameters
1335
	);
1336
	$cat_tree = array();
1337
	$boards = array();
1338
	$last_board_order = 0;
1339
	while ($row = $smcFunc['db_fetch_assoc']($request))
1340
	{
1341
		if (!isset($cat_tree[$row['id_cat']]))
1342
		{
1343
			$cat_tree[$row['id_cat']] = array(
1344
				'node' => array(
1345
					'id' => $row['id_cat'],
1346
					'name' => $row['cat_name'],
1347
					'description' => $row['cat_desc'],
1348
					'order' => $row['cat_order'],
1349
					'can_collapse' => $row['can_collapse']
1350
				),
1351
				'is_first' => empty($cat_tree),
1352
				'last_board_order' => $last_board_order,
1353
				'children' => array()
1354
			);
1355
			$prevBoard = 0;
1356
			$curLevel = 0;
1357
		}
1358
1359
		if (!empty($row['id_board']))
1360
		{
1361
			if ($row['child_level'] != $curLevel)
0 ignored issues
show
Bug introduced by
The variable $curLevel 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...
1362
				$prevBoard = 0;
1363
1364
			$boards[$row['id_board']] = array(
1365
				'id' => $row['id_board'],
1366
				'category' => $row['id_cat'],
1367
				'parent' => $row['id_parent'],
1368
				'level' => $row['child_level'],
1369
				'order' => $row['board_order'],
1370
				'name' => $row['board_name'],
1371
				'member_groups' => explode(',', $row['member_groups']),
1372
				'deny_groups' => explode(',', $row['deny_member_groups']),
1373
				'description' => $row['description'],
1374
				'count_posts' => empty($row['count_posts']),
1375
				'posts' => $row['num_posts'],
1376
				'topics' => $row['num_topics'],
1377
				'theme' => $row['id_theme'],
1378
				'override_theme' => $row['override_theme'],
1379
				'profile' => $row['id_profile'],
1380
				'redirect' => $row['redirect'],
1381
				'prev_board' => $prevBoard
0 ignored issues
show
Bug introduced by
The variable $prevBoard 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...
1382
			);
1383
			$prevBoard = $row['id_board'];
1384
			$last_board_order = $row['board_order'];
1385
1386
			if (empty($row['child_level']))
1387
			{
1388
				$cat_tree[$row['id_cat']]['children'][$row['id_board']] = array(
1389
					'node' => &$boards[$row['id_board']],
1390
					'is_first' => empty($cat_tree[$row['id_cat']]['children']),
1391
					'children' => array()
1392
				);
1393
				$boards[$row['id_board']]['tree'] = &$cat_tree[$row['id_cat']]['children'][$row['id_board']];
1394
			}
1395
			else
1396
			{
1397
				// Parent doesn't exist!
1398
				if (!isset($boards[$row['id_parent']]['tree']))
1399
					fatal_lang_error('no_valid_parent', false, array($row['board_name']));
1400
1401
				// Wrong childlevel...we can silently fix this...
1402
				if ($boards[$row['id_parent']]['tree']['node']['level'] != $row['child_level'] - 1)
1403
					$smcFunc['db_query']('', '
1404
						UPDATE {db_prefix}boards
1405
						SET child_level = {int:new_child_level}
1406
						WHERE id_board = {int:selected_board}',
1407
						array(
1408
							'new_child_level' => $boards[$row['id_parent']]['tree']['node']['level'] + 1,
1409
							'selected_board' => $row['id_board'],
1410
						)
1411
					);
1412
1413
				$boards[$row['id_parent']]['tree']['children'][$row['id_board']] = array(
1414
					'node' => &$boards[$row['id_board']],
1415
					'is_first' => empty($boards[$row['id_parent']]['tree']['children']),
1416
					'children' => array()
1417
				);
1418
				$boards[$row['id_board']]['tree'] = &$boards[$row['id_parent']]['tree']['children'][$row['id_board']];
1419
			}
1420
		}
1421
1422
		// If mods want to do anything with this board before we move on, now's the time
1423
		call_integration_hook('integrate_boardtree_board', array($row));
1424
	}
1425
	$smcFunc['db_free_result']($request);
1426
1427
	// Get a list of all the boards in each category (using recursion).
1428
	$boardList = array();
1429
	foreach ($cat_tree as $catID => $node)
1430
	{
1431
		$boardList[$catID] = array();
1432
		recursiveBoards($boardList[$catID], $node);
1433
	}
1434
}
1435
1436
/**
1437
 * Recursively get a list of boards.
1438
 * Used by getBoardTree
1439
 *
1440
 * @param array &$_boardList The board list
1441
 * @param array &$_tree The board tree
1442
 */
1443
function recursiveBoards(&$_boardList, &$_tree)
1444
{
1445
	if (empty($_tree['children']))
1446
		return;
1447
1448
	foreach ($_tree['children'] as $id => $node)
1449
	{
1450
		$_boardList[] = $id;
1451
		recursiveBoards($_boardList, $node);
1452
	}
1453
}
1454
1455
/**
1456
 * Returns whether the child board id is actually a child of the parent (recursive).
1457
 * @param int $child The ID of the child board
1458
 * @param int $parent The ID of a parent board
1459
 * @return boolean Whether the specified child board is actually a child of the specified parent board.
1460
 */
1461
function isChildOf($child, $parent)
1462
{
1463
	global $boards;
1464
1465
	if (empty($boards[$child]['parent']))
1466
		return false;
1467
1468
	if ($boards[$child]['parent'] == $parent)
1469
		return true;
1470
1471
	return isChildOf($boards[$child]['parent'], $parent);
1472
}
1473
1474
?>