Completed
Pull Request — development (#3089)
by John
09:06
created

Boards.subs.php ➔ reorderBoards()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 45
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 22
nc 4
nop 0
dl 0
loc 45
ccs 0
cts 21
cp 0
crap 20
rs 8.5806
c 0
b 0
f 0
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
 * @name      ElkArte Forum
8
 * @copyright ElkArte Forum contributors
9
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
10
 *
11
 * This file contains code covered by:
12
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
13
 * license:  	BSD, See included LICENSE.TXT for terms and conditions.
14
 *
15
 * @version 2.0 dev
16
 *
17
 */
18
19
/**
20
 * Mark a board or multiple boards read.
21
 *
22
 * @package Boards
23
 * @param int[]|int $boards
24
 * @param bool $unread = false
25
 * @param bool $resetTopics = false
26
 */
27
function markBoardsRead($boards, $unread = false, $resetTopics = false)
28
{
29 1
	global $user_info, $modSettings;
30
31 1
	$db = database();
32
33
	// Force $boards to be an array.
34 1
	if (!is_array($boards))
35
		$boards = array($boards);
36
	else
37 1
		$boards = array_unique($boards);
38
39
	// No boards, nothing to mark as read.
40 1
	if (empty($boards))
41
		return;
42
43
	// Allow the user to mark a board as unread.
44 1
	if ($unread)
45
	{
46
		// Clear out all the places where this lovely info is stored.
47
		// @todo Maybe not log_mark_read?
48
		$db->query('', '
49
			DELETE FROM {db_prefix}log_mark_read
50
			WHERE id_board IN ({array_int:board_list})
51
				AND id_member = {int:current_member}',
52
			array(
53
				'current_member' => $user_info['id'],
54
				'board_list' => $boards,
55
			)
56
		);
57
		$db->query('', '
58
			DELETE FROM {db_prefix}log_boards
59
			WHERE id_board IN ({array_int:board_list})
60
				AND id_member = {int:current_member}',
61
			array(
62
				'current_member' => $user_info['id'],
63
				'board_list' => $boards,
64
			)
65
		);
66
	}
67
	// Otherwise mark the board as read.
68
	else
69
	{
70 1
		$markRead = array();
71 1
		foreach ($boards as $board)
72 1
			$markRead[] = array($modSettings['maxMsgID'], $user_info['id'], $board);
73
74 1
		$db->insert('replace',
75 1
			'{db_prefix}log_boards',
76 1
			array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'),
77 1
			$markRead,
78 1
			array('id_board', 'id_member')
79
		);
80
	}
81
82
	// Get rid of useless log_topics data, because log_mark_read is better for it - even if marking unread - I think so...
83
	// @todo look at this...
84
	// The call to markBoardsRead() in Display() used to be simply
85
	// marking log_boards (the previous query only)
86
	// I'm adding a bool to control the processing of log_topics. We might want to just dissociate it from boards,
87
	// and call the log_topics clear-up only from the controller that needs it..
88
89
	// Notes (for read/unread rework)
90
	// MessageIndex::action_messageindex() does not update log_topics at all (only the above).
91
	// Display controller needed only to update log_boards.
92
93 1
	if ($resetTopics)
94
	{
95
		// Update log_mark_read and log_boards.
96
		// @todo check this condition <= I think I did, but better double check
97
		if (!$unread && !empty($markRead))
98
			$db->insert('replace',
99
				'{db_prefix}log_mark_read',
100
				array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'),
101
				$markRead,
102
				array('id_board', 'id_member')
103
			);
104
105
		$result = $db->query('', '
106
			SELECT MIN(id_topic)
107
			FROM {db_prefix}log_topics
108
			WHERE id_member = {int:current_member}',
109
			array(
110
				'current_member' => $user_info['id'],
111
			)
112
		);
113
		list ($lowest_topic) = $db->fetch_row($result);
114
		$db->free_result($result);
115
116
		if (empty($lowest_topic))
117
			return;
118
119
		// @todo SLOW This query seems to eat it sometimes.
120
		$delete_topics = array();
121
		$update_topics = array();
122
		$db->fetchQueryCallback('
123
			SELECT lt.id_topic, lt.unwatched
124
			FROM {db_prefix}log_topics AS lt
125
				INNER JOIN {db_prefix}topics AS t /*!40000 USE INDEX (PRIMARY) */ ON (t.id_topic = lt.id_topic
126
					AND t.id_board IN ({array_int:board_list}))
127
			WHERE lt.id_member = {int:current_member}
128
				AND lt.id_topic >= {int:lowest_topic}',
129
			array(
130
				'current_member' => $user_info['id'],
131
				'board_list' => $boards,
132
				'lowest_topic' => $lowest_topic,
133
			),
134
			function ($row) use (&$delete_topics, &$update_topics, $user_info, $modSettings)
135
			{
136
				if (!empty($row['unwatched']))
137
					$update_topics[] = array(
138
						$user_info['id'],
139
						$modSettings['maxMsgID'],
140
						$row['id_topic'],
141
						1,
142
					);
143
				else
144
					$delete_topics[] = $row['id_topic'];
145
			}
146
		);
147
148
		if (!empty($update_topics))
149
			$db->insert('replace',
150
				'{db_prefix}log_topics',
151
				array(
152
					'id_member' => 'int',
153
					'id_msg' => 'int',
154
					'id_topic' => 'int',
155
					'unwatched' => 'int'
156
				),
157
				$update_topics,
158
				array('id_topic', 'id_member')
159
			);
160
161
		if (!empty($delete_topics))
162
			$db->query('', '
163
				DELETE FROM {db_prefix}log_topics
164
				WHERE id_member = {int:current_member}
165
					AND id_topic IN ({array_int:topic_list})',
166
				array(
167
					'current_member' => $user_info['id'],
168
					'topic_list' => $delete_topics,
169
				)
170
			);
171
	}
172 1
}
173
174
/**
175
 * Get the id_member associated with the specified message ID.
176
 *
177
 * @package Boards
178
 * @param int $messageID message ID
179
 * @return int the member id
180
 */
181
function getMsgMemberID($messageID)
182
{
183
	require_once(SUBSDIR . '/Messages.subs.php');
184
	$message_info = basicMessageInfo((int) $messageID, true);
185
186
	return empty($message_info['id_member']) ? 0 : (int) $message_info['id_member'];
187
}
188
189
/**
190
 * Modify the settings and position of a board.
191
 *
192
 * - Used by ManageBoards.controller.php to change the settings of a board.
193
 *
194
 * @package Boards
195
 *
196
 * @param int     $board_id
197
 * @param mixed[] $boardOptions
198
 *
199
 * @throws Elk_Exception no_board
200
 */
201
function modifyBoard($board_id, &$boardOptions)
202
{
203
	global $cat_tree, $boards;
204
205
	$db = database();
206
207
	// Get some basic information about all boards and categories.
208
	getBoardTree();
209
210
	// Make sure given boards and categories exist.
211
	if (!isset($boards[$board_id]) || (isset($boardOptions['target_board']) && !isset($boards[$boardOptions['target_board']])) || (isset($boardOptions['target_category']) && !isset($cat_tree[$boardOptions['target_category']])))
212
		throw new Elk_Exception('no_board');
213
214
	// All things that will be updated in the database will be in $boardUpdates.
215
	$boardUpdates = array();
216
	$boardUpdateParameters = array();
217
218
	// In case the board has to be moved
219
	if (isset($boardOptions['move_to']))
220
	{
221
		// Move the board to the top of a given category.
222
		if ($boardOptions['move_to'] == 'top')
223
		{
224
			$id_cat = $boardOptions['target_category'];
225
			$child_level = 0;
226
			$id_parent = 0;
227
			$after = $cat_tree[$id_cat]['last_board_order'];
228
		}
229
230
		// Move the board to the bottom of a given category.
231
		elseif ($boardOptions['move_to'] == 'bottom')
232
		{
233
			$id_cat = $boardOptions['target_category'];
234
			$child_level = 0;
235
			$id_parent = 0;
236
			$after = 0;
237
			foreach ($cat_tree[$id_cat]['children'] as $id_board => $dummy)
238
				$after = max($after, $boards[$id_board]['order']);
239
		}
240
241
		// Make the board a child of a given board.
242
		elseif ($boardOptions['move_to'] == 'child')
243
		{
244
			$id_cat = $boards[$boardOptions['target_board']]['category'];
245
			$child_level = $boards[$boardOptions['target_board']]['level'] + 1;
246
			$id_parent = $boardOptions['target_board'];
247
248
			// People can be creative, in many ways...
249
			if (isChildOf($id_parent, $board_id))
250
				throw new Elk_Exception('mboards_parent_own_child_error', false);
251
			elseif ($id_parent == $board_id)
252
				throw new Elk_Exception('mboards_board_own_child_error', false);
253
254
			$after = $boards[$boardOptions['target_board']]['order'];
255
256
			// Check if there are already children and (if so) get the max board order.
257
			if (!empty($boards[$id_parent]['tree']['children']) && empty($boardOptions['move_first_child']))
258
				foreach ($boards[$id_parent]['tree']['children'] as $childBoard_id => $dummy)
259
					$after = max($after, $boards[$childBoard_id]['order']);
260
		}
261
262
		// Place a board before or after another board, on the same child level.
263
		elseif (in_array($boardOptions['move_to'], array('before', 'after')))
264
		{
265
			$id_cat = $boards[$boardOptions['target_board']]['category'];
266
			$child_level = $boards[$boardOptions['target_board']]['level'];
267
			$id_parent = $boards[$boardOptions['target_board']]['parent'];
268
			$after = $boards[$boardOptions['target_board']]['order'] - ($boardOptions['move_to'] == 'before' ? 1 : 0);
269
		}
270
271
		// Oops...?
272
		else
273
			trigger_error('modifyBoard(): The move_to value \'' . $boardOptions['move_to'] . '\' is incorrect', E_USER_ERROR);
274
275
		// Get a list of children of this board.
276
		$childList = array();
277
		recursiveBoards($childList, $boards[$board_id]['tree']);
278
279
		// See if there are changes that affect children.
280
		$childUpdates = array();
281
		$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...
282
		if ($levelDiff != 0)
283
			$childUpdates[] = 'child_level = child_level ' . ($levelDiff > 0 ? '+ ' : '') . '{int:level_diff}';
284
		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...
285
			$childUpdates[] = 'id_cat = {int:category}';
286
287
		// Fix the children of this board.
288
		if (!empty($childList) && !empty($childUpdates))
289
			$db->query('', '
290
				UPDATE {db_prefix}boards
291
				SET ' . implode(',
292
					', $childUpdates) . '
293
				WHERE id_board IN ({array_int:board_list})',
294
				array(
295
					'board_list' => $childList,
296
					'category' => $id_cat,
297
					'level_diff' => $levelDiff,
298
				)
299
			);
300
301
		// Make some room for this spot.
302
		$db->query('', '
303
			UPDATE {db_prefix}boards
304
			SET board_order = board_order + {int:new_order}
305
			WHERE board_order > {int:insert_after}
306
				AND id_board != {int:selected_board}',
307
			array(
308
				'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...
309
				'selected_board' => $board_id,
310
				'new_order' => 1 + count($childList),
311
			)
312
		);
313
314
		$boardUpdates[] = 'id_cat = {int:id_cat}';
315
		$boardUpdates[] = 'id_parent = {int:id_parent}';
316
		$boardUpdates[] = 'child_level = {int:child_level}';
317
		$boardUpdates[] = 'board_order = {int:board_order}';
318
		$boardUpdateParameters += array(
319
			'id_cat' => $id_cat,
320
			'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...
321
			'child_level' => $child_level,
322
			'board_order' => $after + 1,
323
		);
324
	}
325
326
	// This setting is a little twisted in the database...
327
	if (isset($boardOptions['posts_count']))
328
	{
329
		$boardUpdates[] = 'count_posts = {int:count_posts}';
330
		$boardUpdateParameters['count_posts'] = $boardOptions['posts_count'] ? 0 : 1;
331
	}
332
333
	// Set the theme for this board.
334
	if (isset($boardOptions['board_theme']))
335
	{
336
		$boardUpdates[] = 'id_theme = {int:id_theme}';
337
		$boardUpdateParameters['id_theme'] = (int) $boardOptions['board_theme'];
338
	}
339
340
	// Should the board theme override the user preferred theme?
341
	if (isset($boardOptions['override_theme']))
342
	{
343
		$boardUpdates[] = 'override_theme = {int:override_theme}';
344
		$boardUpdateParameters['override_theme'] = $boardOptions['override_theme'] ? 1 : 0;
345
	}
346
347
	// Who's allowed to access this board.
348
	if (isset($boardOptions['access_groups']))
349
	{
350
		$boardUpdates[] = 'member_groups = {string:member_groups}';
351
		$boardUpdateParameters['member_groups'] = implode(',', $boardOptions['access_groups']);
352
	}
353
354
	// And who isn't.
355 View Code Duplication
	if (isset($boardOptions['deny_groups']))
356
	{
357
		$boardUpdates[] = 'deny_member_groups = {string:deny_groups}';
358
		$boardUpdateParameters['deny_groups'] = implode(',', $boardOptions['deny_groups']);
359
	}
360
361
	if (isset($boardOptions['board_name']))
362
	{
363
		$boardUpdates[] = 'name = {string:board_name}';
364
		$boardUpdateParameters['board_name'] = $boardOptions['board_name'];
365
	}
366
367
	if (isset($boardOptions['board_description']))
368
	{
369
		$boardUpdates[] = 'description = {string:board_description}';
370
		$boardUpdateParameters['board_description'] = $boardOptions['board_description'];
371
	}
372
373
	if (isset($boardOptions['profile']))
374
	{
375
		$boardUpdates[] = 'id_profile = {int:profile}';
376
		$boardUpdateParameters['profile'] = (int) $boardOptions['profile'];
377
	}
378
379 View Code Duplication
	if (isset($boardOptions['redirect']))
380
	{
381
		$boardUpdates[] = 'redirect = {string:redirect}';
382
		$boardUpdateParameters['redirect'] = $boardOptions['redirect'];
383
	}
384
385
	if (isset($boardOptions['num_posts']))
386
	{
387
		$boardUpdates[] = 'num_posts = {int:num_posts}';
388
		$boardUpdateParameters['num_posts'] = (int) $boardOptions['num_posts'];
389
	}
390
391
	call_integration_hook('integrate_modify_board', array($board_id, $boardOptions, &$boardUpdates, &$boardUpdateParameters));
392
393
	// Do the updates (if any).
394
	if (!empty($boardUpdates))
395
		$db->query('', '
396
			UPDATE {db_prefix}boards
397
			SET
398
				' . implode(',
399
				', $boardUpdates) . '
400
			WHERE id_board = {int:selected_board}',
401
			array_merge($boardUpdateParameters, array(
402
				'selected_board' => $board_id,
403
			))
404
		);
405
406
	// Set moderators of this board.
407
	if (isset($boardOptions['moderators']) || isset($boardOptions['moderator_string']))
408
	{
409
		// Reset current moderators for this board - if there are any!
410
		$db->query('', '
411
			DELETE FROM {db_prefix}moderators
412
			WHERE id_board = {int:board_list}',
413
			array(
414
				'board_list' => $board_id,
415
			)
416
		);
417
418
		// Validate and get the IDs of the new moderators.
419
		if (isset($boardOptions['moderator_string']) && trim($boardOptions['moderator_string']) != '')
420
		{
421
			// Divvy out the usernames, remove extra space.
422
			$moderator_string = strtr(Util::htmlspecialchars($boardOptions['moderator_string'], ENT_QUOTES), array('&quot;' => '"'));
423
			preg_match_all('~"([^"]+)"~', $moderator_string, $matches);
424
			$moderators = array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $moderator_string)));
425 View Code Duplication
			for ($k = 0, $n = count($moderators); $k < $n; $k++)
426
			{
427
				$moderators[$k] = trim($moderators[$k]);
428
429
				if (strlen($moderators[$k]) == 0)
430
					unset($moderators[$k]);
431
			}
432
433
			// Find all the id_member's for the member_name's in the list.
434
			if (empty($boardOptions['moderators']))
435
				$boardOptions['moderators'] = array();
436
			if (!empty($moderators))
437
			{
438
				$boardOptions['moderators'] = $db->fetchQueryCallback('
439
					SELECT id_member
440
					FROM {db_prefix}members
441
					WHERE member_name IN ({array_string:moderator_list}) OR real_name IN ({array_string:moderator_list})
442
					LIMIT ' . count($moderators),
443
					array(
444
						'moderator_list' => $moderators,
445
					),
446
					function ($row)
447
					{
448
						return $row['id_member'];
449
					}
450
				);
451
			}
452
		}
453
454
		// Add the moderators to the board.
455 View Code Duplication
		if (!empty($boardOptions['moderators']))
456
		{
457
			$inserts = array();
458
			foreach ($boardOptions['moderators'] as $moderator)
459
				$inserts[] = array($board_id, $moderator);
460
461
			$db->insert('insert',
462
				'{db_prefix}moderators',
463
				array('id_board' => 'int', 'id_member' => 'int'),
464
				$inserts,
465
				array('id_board', 'id_member')
466
			);
467
		}
468
469
		// Note that caches can now be wrong!
470
		updateSettings(array('settings_updated' => time()));
471
	}
472
473
	if (isset($boardOptions['move_to']))
474
		reorderBoards();
475
476
	clean_cache('data');
477
478
	if (empty($boardOptions['dont_log']))
479
		logAction('edit_board', array('board' => $board_id), 'admin');
480
}
481
482
/**
483
 * Create a new board and set its properties and position.
484
 *
485
 * - Allows (almost) the same options as the modifyBoard() function.
486
 * - With the option inherit_permissions set, the parent board permissions
487
 * will be inherited.
488
 *
489
 * @package Boards
490
 * @param mixed[] $boardOptions
491
 * @return int The new board id
492
 * @throws Elk_Exception
493
 */
494
function createBoard($boardOptions)
495
{
496
	global $boards;
497
498
	$db = database();
499
500
	// Trigger an error if one of the required values is not set.
501
	if (!isset($boardOptions['board_name']) || trim($boardOptions['board_name']) == '' || !isset($boardOptions['move_to']) || !isset($boardOptions['target_category']))
502
		trigger_error('createBoard(): One or more of the required options is not set', E_USER_ERROR);
503
504
	if (in_array($boardOptions['move_to'], array('child', 'before', 'after')) && !isset($boardOptions['target_board']))
505
		trigger_error('createBoard(): Target board is not set', E_USER_ERROR);
506
507
	// Set every optional value to its default value.
508
	$boardOptions += array(
509
		'posts_count' => true,
510
		'override_theme' => false,
511
		'board_theme' => 0,
512
		'access_groups' => array(),
513
		'board_description' => '',
514
		'profile' => 1,
515
		'moderators' => '',
516
		'inherit_permissions' => true,
517
		'dont_log' => true,
518
	);
519
	$board_columns = array(
520
		'id_cat' => 'int', 'name' => 'string-255', 'description' => 'string', 'board_order' => 'int',
521
		'member_groups' => 'string', 'redirect' => 'string',
522
	);
523
	$board_parameters = array(
524
		$boardOptions['target_category'], $boardOptions['board_name'], '', 0,
525
		'-1,0', '',
526
	);
527
528
	// Insert a board, the settings are dealt with later.
529
	$db->insert('',
530
		'{db_prefix}boards',
531
		$board_columns,
532
		$board_parameters,
533
		array('id_board')
534
	);
535
	$board_id = $db->insert_id('{db_prefix}boards', 'id_board');
536
537
	if (empty($board_id))
538
		return 0;
539
540
	// Change the board according to the given specifications.
541
	modifyBoard($board_id, $boardOptions);
542
543
	// Do we want the parent permissions to be inherited?
544
	if ($boardOptions['inherit_permissions'])
545
	{
546
		getBoardTree();
547
548
		if (!empty($boards[$board_id]['parent']))
549
		{
550
			$board_data = fetchBoardsInfo(array('boards' => $boards[$board_id]['parent']), array('selects' => 'permissions'));
551
552
			$db->query('', '
553
				UPDATE {db_prefix}boards
554
				SET id_profile = {int:new_profile}
555
				WHERE id_board = {int:current_board}',
556
				array(
557
					'new_profile' => $board_data[$boards[$board_id]['parent']]['id_profile'],
558
					'current_board' => $board_id,
559
				)
560
			);
561
		}
562
	}
563
564
	// Clean the data cache.
565
	clean_cache('data');
566
567
	// Created it.
568
	logAction('add_board', array('board' => $board_id), 'admin');
569
570
	// Here you are, a new board, ready to be spammed.
571
	return $board_id;
572
}
573
574
/**
575
 * Remove one or more boards.
576
 *
577
 * - Allows to move the children of the board before deleting it
578
 * - if moveChildrenTo is set to null, the sub-boards will be deleted.
579
 * - Deletes:
580
 *   - all topics that are on the given boards;
581
 *   - all information that's associated with the given boards;
582
 * - updates the statistics to reflect the new situation.
583
 *
584
 * @package Boards
585
 * @param int[] $boards_to_remove
586
 * @param int|null $moveChildrenTo = null
587
 * @throws Elk_Exception
588
 */
589
function deleteBoards($boards_to_remove, $moveChildrenTo = null)
590
{
591
	global $boards;
592
593
	$db = database();
594
595
	// No boards to delete? Return!
596
	if (empty($boards_to_remove))
597
		return;
598
599
	getBoardTree();
600
601
	call_integration_hook('integrate_delete_board', array($boards_to_remove, &$moveChildrenTo));
602
603
	// If $moveChildrenTo is set to null, include the children in the removal.
604
	if ($moveChildrenTo === null)
605
	{
606
		// Get a list of the sub-boards that will also be removed.
607
		$child_boards_to_remove = array();
608
		foreach ($boards_to_remove as $board_to_remove)
609
			recursiveBoards($child_boards_to_remove, $boards[$board_to_remove]['tree']);
610
611
		// Merge the children with their parents.
612
		if (!empty($child_boards_to_remove))
613
			$boards_to_remove = array_unique(array_merge($boards_to_remove, $child_boards_to_remove));
614
	}
615
	// Move the children to a safe home.
616
	else
617
	{
618
		foreach ($boards_to_remove as $id_board)
619
		{
620
			// @todo Separate category?
621
			if ($moveChildrenTo === 0)
622
				fixChildren($id_board, 0, 0);
623
			else
624
				fixChildren($id_board, $boards[$moveChildrenTo]['level'] + 1, $moveChildrenTo);
625
		}
626
	}
627
628
	// Delete ALL topics in the selected boards (done first so topics can't be marooned.)
629
	$topics = $db->fetchQuery('
630
		SELECT id_topic
631
		FROM {db_prefix}topics
632
		WHERE id_board IN ({array_int:boards_to_remove})',
633
		array(
634
			'boards_to_remove' => $boards_to_remove,
635
		)
636
	);
637
638
	require_once(SUBSDIR . '/Topic.subs.php');
639
	removeTopics($topics, false);
640
641
	// Delete the board's logs.
642
	$db->query('', '
643
		DELETE FROM {db_prefix}log_mark_read
644
		WHERE id_board IN ({array_int:boards_to_remove})',
645
		array(
646
			'boards_to_remove' => $boards_to_remove,
647
		)
648
	);
649
	$db->query('', '
650
		DELETE FROM {db_prefix}log_boards
651
		WHERE id_board IN ({array_int:boards_to_remove})',
652
		array(
653
			'boards_to_remove' => $boards_to_remove,
654
		)
655
	);
656
	$db->query('', '
657
		DELETE FROM {db_prefix}log_notify
658
		WHERE id_board IN ({array_int:boards_to_remove})',
659
		array(
660
			'boards_to_remove' => $boards_to_remove,
661
		)
662
	);
663
664
	// Delete this board's moderators.
665
	$db->query('', '
666
		DELETE FROM {db_prefix}moderators
667
		WHERE id_board IN ({array_int:boards_to_remove})',
668
		array(
669
			'boards_to_remove' => $boards_to_remove,
670
		)
671
	);
672
673
	// Delete any extra events in the calendar.
674
	$db->query('', '
675
		DELETE FROM {db_prefix}calendar
676
		WHERE id_board IN ({array_int:boards_to_remove})',
677
		array(
678
			'boards_to_remove' => $boards_to_remove,
679
		)
680
	);
681
682
	// Delete any message icons that only appear on these boards.
683
	$db->query('', '
684
		DELETE FROM {db_prefix}message_icons
685
		WHERE id_board IN ({array_int:boards_to_remove})',
686
		array(
687
			'boards_to_remove' => $boards_to_remove,
688
		)
689
	);
690
691
	// Delete the boards.
692
	$db->query('', '
693
		DELETE FROM {db_prefix}boards
694
		WHERE id_board IN ({array_int:boards_to_remove})',
695
		array(
696
			'boards_to_remove' => $boards_to_remove,
697
		)
698
	);
699
700
	// Latest message/topic might not be there anymore.
701
	require_once(SUBSDIR . '/Messages.subs.php');
702
	updateMessageStats();
703
	require_once(SUBSDIR . '/Topic.subs.php');
704
	updateTopicStats();
705
	updateSettings(array(
706
		'calendar_updated' => time(),
707
	));
708
709
	// Plus reset the cache to stop people getting odd results.
710
	updateSettings(array('settings_updated' => time()));
711
712
	// Clean the cache as well.
713
	clean_cache('data');
714
715
	// Let's do some serious logging.
716
	foreach ($boards_to_remove as $id_board)
717
		logAction('delete_board', array('boardname' => $boards[$id_board]['name']), 'admin');
718
719
	reorderBoards();
720
}
721
722
/**
723
 * Put all boards in the right order and sorts the records of the boards table.
724
 *
725
 * - Used by modifyBoard(), deleteBoards(), modifyCategory(), and deleteCategories() functions
726
 */
727
function reorderBoards()
728
{
729
	global $cat_tree, $boardList, $boards;
730
731
	$db = database();
732
	$update_query = '';
733
	$update_params = [];
734
735
	getBoardTree();
736
737
	// Set the board order for each category.
738
	$board_order = 0;
739
	foreach ($cat_tree as $catID => $dummy)
740
	{
741
		foreach ($boardList[$catID] as $boardID)
742
		{
743
			if ($boards[$boardID]['order'] != ++$board_order)
744
			{
745
				$update_query .= sprintf(
746
					'
747
				WHEN {int:selected_board%1$d} THEN {int:new_order%1$d}',
748
					$boardID
749
				);
750
751
				$update_params = array_merge(
752
					$update_params,
753
					[
754
						'new_order' . $boardID => $board_order,
755
						'selected_board' . $boardID => $boardID,
756
					]
757
				);
758
			}
759
		}
760
	}
761
762
	$db->query(
763
		'',
764
		'
765
		UPDATE {db_prefix}boards
766
			SET
767
				board_order = CASE id_board ' . $update_query . '
768
					END',
769
		$update_params
770
	);
771
}
772
773
/**
774
 * Fixes the children of a board by setting their child_levels to new values.
775
 *
776
 * - Used when a board is deleted or moved, to affect its children.
777
 *
778
 * @package Boards
779
 * @param int $parent
780
 * @param int $newLevel
781
 * @param int $newParent
782
 */
783
function fixChildren($parent, $newLevel, $newParent)
784
{
785
	$db = database();
786
787
	// Grab all children of $parent...
788
	$children = $db->fetchQueryCallback('
789
		SELECT id_board
790
		FROM {db_prefix}boards
791
		WHERE id_parent = {int:parent_board}',
792
		array(
793
			'parent_board' => $parent,
794
		),
795
		function ($row)
796
		{
797
			return $row['id_board'];
798
		}
799
	);
800
801
	// ...and set it to a new parent and child_level.
802
	$db->query('', '
803
		UPDATE {db_prefix}boards
804
		SET id_parent = {int:new_parent}, child_level = {int:new_child_level}
805
		WHERE id_parent = {int:parent_board}',
806
		array(
807
			'new_parent' => $newParent,
808
			'new_child_level' => $newLevel,
809
			'parent_board' => $parent,
810
		)
811
	);
812
813
	// Recursively fix the children of the children.
814
	foreach ($children as $child)
815
		fixChildren($child, $newLevel + 1, $child);
816
}
817
818
/**
819
 * Load a lot of useful information regarding the boards and categories.
820
 *
821
 * - The information retrieved is stored in globals:
822
 *   $boards:    properties of each board.
823
 *   $boardList: a list of boards grouped by category ID.
824
 *   $cat_tree:  properties of each category.
825
 *
826
 * @param array $query
827
 *
828
 * @throws Elk_Exception no_valid_parent
829
 * @package Boards
830
 */
831
function getBoardTree($query = array())
832
{
833
	global $cat_tree, $boards, $boardList;
834
835
	$db = database();
836
837
	// Addons may want to add their own information to the board table.
838
	call_integration_hook('integrate_board_tree_query', array(&$query));
839
840
	// Getting all the board and category information you'd ever wanted.
841
	$request = $db->query('', '
842
		SELECT
843
			COALESCE(b.id_board, 0) AS id_board, b.id_parent, b.name AS board_name, b.description, b.child_level,
844
			b.board_order, b.count_posts, b.member_groups, b.id_theme, b.override_theme, b.id_profile, b.redirect,
845
			b.num_posts, b.num_topics, b.deny_member_groups, c.id_cat, c.name AS cat_name, c.cat_order, c.can_collapse' . (!empty($query['select']) ?
846
			$query['select'] : '') . '
847
		FROM {db_prefix}categories AS c
848
			LEFT JOIN {db_prefix}boards AS b ON (b.id_cat = c.id_cat)' . (!empty($query['join']) ?
849
			$query['join'] : '') . '
850
		ORDER BY c.cat_order, b.child_level, b.board_order',
851
		array(
852
		)
853
	);
854
	$cat_tree = array();
855
	$boards = array();
856
	$last_board_order = 0;
857
	while ($row = $db->fetch_assoc($request))
858
	{
859
		if (!isset($cat_tree[$row['id_cat']]))
860
		{
861
			$cat_tree[$row['id_cat']] = array(
862
				'node' => array(
863
					'id' => $row['id_cat'],
864
					'name' => $row['cat_name'],
865
					'order' => $row['cat_order'],
866
					'can_collapse' => $row['can_collapse']
867
				),
868
				'is_first' => empty($cat_tree),
869
				'last_board_order' => $last_board_order,
870
				'children' => array()
871
			);
872
			$prevBoard = 0;
873
			$curLevel = 0;
874
		}
875
876
		if (!empty($row['id_board']))
877
		{
878
			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...
879
				$prevBoard = 0;
880
881
			$boards[$row['id_board']] = array(
882
				'id' => $row['id_board'],
883
				'category' => $row['id_cat'],
884
				'parent' => $row['id_parent'],
885
				'level' => $row['child_level'],
886
				'order' => $row['board_order'],
887
				'name' => $row['board_name'],
888
				'member_groups' => explode(',', $row['member_groups']),
889
				'deny_groups' => explode(',', $row['deny_member_groups']),
890
				'description' => $row['description'],
891
				'count_posts' => empty($row['count_posts']),
892
				'posts' => $row['num_posts'],
893
				'topics' => $row['num_topics'],
894
				'theme' => $row['id_theme'],
895
				'override_theme' => $row['override_theme'],
896
				'profile' => $row['id_profile'],
897
				'redirect' => $row['redirect'],
898
				'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...
899
			);
900
			$prevBoard = $row['id_board'];
901
			$last_board_order = $row['board_order'];
902
903
			if (empty($row['child_level']))
904
			{
905
				$cat_tree[$row['id_cat']]['children'][$row['id_board']] = array(
906
					'node' => &$boards[$row['id_board']],
907
					'is_first' => empty($cat_tree[$row['id_cat']]['children']),
908
					'children' => array()
909
				);
910
				$boards[$row['id_board']]['tree'] = &$cat_tree[$row['id_cat']]['children'][$row['id_board']];
911
			}
912
			else
913
			{
914
				// Parent doesn't exist!
915
				if (!isset($boards[$row['id_parent']]['tree']))
916
					throw new Elk_Exception('no_valid_parent', false, array($row['board_name']));
917
918
				// Wrong childlevel...we can silently fix this...
919
				if ($boards[$row['id_parent']]['tree']['node']['level'] != $row['child_level'] - 1)
920
					$db->query('', '
921
						UPDATE {db_prefix}boards
922
						SET child_level = {int:new_child_level}
923
						WHERE id_board = {int:selected_board}',
924
						array(
925
							'new_child_level' => $boards[$row['id_parent']]['tree']['node']['level'] + 1,
926
							'selected_board' => $row['id_board'],
927
						)
928
					);
929
930
				$boards[$row['id_parent']]['tree']['children'][$row['id_board']] = array(
931
					'node' => &$boards[$row['id_board']],
932
					'is_first' => empty($boards[$row['id_parent']]['tree']['children']),
933
					'children' => array()
934
				);
935
				$boards[$row['id_board']]['tree'] = &$boards[$row['id_parent']]['tree']['children'][$row['id_board']];
936
			}
937
		}
938
939
		// Let integration easily add data to $boards and $cat_tree
940
		call_integration_hook('integrate_board_tree', array($row));
941
	}
942
	$db->free_result($request);
943
944
	// Get a list of all the boards in each category (using recursion).
945
	$boardList = array();
946
	foreach ($cat_tree as $catID => $node)
947
	{
948
		$boardList[$catID] = array();
949
		recursiveBoards($boardList[$catID], $node);
950
	}
951
}
952
953
/**
954
 * Generates the query to determine the list of available boards for a user
955
 *
956
 * - Executes the query and returns the list
957
 *
958
 * @package Boards
959
 * @param mixed[] $boardListOptions
960
 * @param boolean $simple if true a simple array is returned containing some basic
961
 *                information regarding the board (id_board, board_name, child_level, id_cat, cat_name)
962
 *                if false the boards are returned in an array subdivided by categories including also
963
 *                additional data like the number of boards
964
 * @return array An array of boards sorted according to the normal boards order
965
 */
966
function getBoardList($boardListOptions = array(), $simple = false)
967
{
968 2
	global $modSettings;
969
970 2
	$db = database();
971
972 2
	if ((isset($boardListOptions['excluded_boards']) || isset($boardListOptions['allowed_to'])) && isset($boardListOptions['included_boards']))
973
		trigger_error('getBoardList(): Setting both excluded_boards and included_boards is not allowed.', E_USER_ERROR);
974
975 2
	$where = array();
976 2
	$join = array();
977 2
	$select = '';
978 2
	$where_parameters = array();
979
980
	// Any boards to exclude
981 2
	if (isset($boardListOptions['excluded_boards']))
982
	{
983
		$where[] = 'b.id_board NOT IN ({array_int:excluded_boards})';
984
		$where_parameters['excluded_boards'] = $boardListOptions['excluded_boards'];
985
	}
986
987
	// Get list of boards to which they have specific permissions
988 2
	if (isset($boardListOptions['allowed_to']))
989
	{
990
		$boardListOptions['included_boards'] = boardsAllowedTo($boardListOptions['allowed_to']);
991
		if (in_array(0, $boardListOptions['included_boards']))
992
			unset($boardListOptions['included_boards']);
993
	}
994
995
	// Just want to include certain boards in the query
996 2
	if (isset($boardListOptions['included_boards']))
997
	{
998
		$where[] = 'b.id_board IN ({array_int:included_boards})';
999
		$where_parameters['included_boards'] = $boardListOptions['included_boards'];
1000
	}
1001
1002
	// Determine if they can access a given board and return yea or nay in the results array
1003 2
	if (isset($boardListOptions['access']))
1004
	{
1005
		$select .= ',
1006
			FIND_IN_SET({string:current_group}, b.member_groups) != 0 AS can_access,
1007
			FIND_IN_SET({string:current_group}, b.deny_member_groups) != 0 AS cannot_access';
1008
		$where_parameters['current_group'] = $boardListOptions['access'];
1009
	}
1010
1011
	// Leave out the boards that the user may be ignoring
1012 2
	if (isset($boardListOptions['ignore']))
1013
	{
1014
		$select .= ',' . (!empty($boardListOptions['ignore']) ? 'b.id_board IN ({array_int:ignore_boards})' : '0') . ' AS is_ignored';
1015
		$where_parameters['ignore_boards'] = $boardListOptions['ignore'];
1016
	}
1017
1018
	// Want to check if the member is a moderators for any boards
1019 2
	if (isset($boardListOptions['moderator']))
1020
	{
1021
		$join[] = '
1022
			LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member})';
1023
		$select .= ', b.id_profile, b.member_groups, COALESCE(mods.id_member, 0) AS is_mod';
1024
		$where_parameters['current_member'] = $boardListOptions['moderator'];
1025
	}
1026
1027 2
	if (!empty($boardListOptions['ignore_boards']) && empty($boardListOptions['override_permissions']))
1028
		$where[] = '{query_wanna_see_board}';
1029
1030 2
	elseif (empty($boardListOptions['override_permissions']))
1031
		$where[] = '{query_see_board}';
1032
1033 2
	if (!empty($boardListOptions['not_redirection']))
1034
	{
1035 2
		$where[] = 'b.redirect = {string:blank_redirect}';
1036 2
		$where_parameters['blank_redirect'] = '';
1037
	}
1038
1039
	// Bring all the options together and make the query
1040 2
	$request = $db->query('', '
1041 2
		SELECT c.name AS cat_name, c.id_cat, b.id_board, b.name AS board_name, b.child_level' . $select . '
1042
		FROM {db_prefix}boards AS b
1043 2
			LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)' . (empty($join) ? '' : implode(' ', $join)) . (empty($where) ? '' : '
1044 2
		WHERE ' . implode('
1045 2
			AND ', $where)) . '
1046
		ORDER BY c.cat_order, b.board_order',
1047 2
		$where_parameters
1048
	);
1049
1050
	// Build our output arrays, simple or complete
1051 2
	if ($simple)
1052
	{
1053 2
		$return_value = array();
1054 2
		while ($row = $db->fetch_assoc($request))
1055
		{
1056 2
			$return_value[$row['id_board']] = array(
1057 2
				'id_cat' => $row['id_cat'],
1058 2
				'cat_name' => $row['cat_name'],
1059 2
				'id_board' => $row['id_board'],
1060 2
				'board_name' => $row['board_name'],
1061 2
				'child_level' => $row['child_level'],
1062
			);
1063
1064
			// Do we want access information?
1065 2
			if (isset($boardListOptions['access']) && $boardListOptions['access'] !== false)
1066
			{
1067
				$return_value[$row['id_board']]['allow'] = !(empty($row['can_access']) || $row['can_access'] == 'f');
1068
				$return_value[$row['id_board']]['deny'] = !(empty($row['cannot_access']) || $row['cannot_access'] == 'f');
1069
			}
1070
1071
			// Do we want moderation information?
1072 2 View Code Duplication
			if (!empty($boardListOptions['moderator']))
1073
			{
1074
				$return_value[$row['id_board']] += array(
1075
					'id_profile' => $row['id_profile'],
1076
					'member_groups' => $row['member_groups'],
1077
					'is_mod' => $row['is_mod'],
1078
				);
1079
			}
1080
		}
1081
	}
1082
	else
1083
	{
1084
		$return_value = array(
1085
			'num_boards' => $db->num_rows($request),
1086
			'boards_check_all' => true,
1087
			'boards_current_disabled' => true,
1088
			'categories' => array(),
1089
		);
1090
		while ($row = $db->fetch_assoc($request))
1091
		{
1092
			// This category hasn't been set up yet..
1093
			if (!isset($return_value['categories'][$row['id_cat']]))
1094
				$return_value['categories'][$row['id_cat']] = array(
1095
					'id' => $row['id_cat'],
1096
					'name' => $row['cat_name'],
1097
					'boards' => array(),
1098
				);
1099
1100
			// Shortcuts are useful to keep things simple
1101
			$this_cat = &$return_value['categories'][$row['id_cat']];
1102
1103
			$this_cat['boards'][$row['id_board']] = array(
1104
				'id' => $row['id_board'],
1105
				'name' => $row['board_name'],
1106
				'child_level' => $row['child_level'],
1107
				'allow' => false,
1108
				'deny' => false,
1109
				'selected' => isset($boardListOptions['selected_board']) && $boardListOptions['selected_board'] == $row['id_board'],
1110
			);
1111
			// Do we want access information?
1112
1113
			if (!empty($boardListOptions['access']))
1114
			{
1115
				$this_cat['boards'][$row['id_board']]['allow'] = !(empty($row['can_access']) || $row['can_access'] == 'f');
1116
				$this_cat['boards'][$row['id_board']]['deny'] = !(empty($row['cannot_access']) || $row['cannot_access'] == 'f');
1117
			}
1118
1119
			// If is_ignored is set, it means we could have to deselect a board
1120
			if (isset($row['is_ignored']))
1121
			{
1122
				$this_cat['boards'][$row['id_board']]['selected'] = $row['is_ignored'];
1123
1124
				// If a board wasn't checked that probably should have been ensure the board selection is selected, yo!
1125
				if (!empty($this_cat['boards'][$row['id_board']]['selected']) && (empty($modSettings['recycle_enable']) || $row['id_board'] != $modSettings['recycle_board']))
1126
					$return_value['boards_check_all'] = false;
1127
			}
1128
1129
			// Do we want moderation information?
1130 View Code Duplication
			if (!empty($boardListOptions['moderator']))
1131
			{
1132
				$this_cat['boards'][$row['id_board']] += array(
1133
					'id_profile' => $row['id_profile'],
1134
					'member_groups' => $row['member_groups'],
1135
					'is_mod' => $row['is_mod'],
1136
				);
1137
			}
1138
		}
1139
	}
1140
1141 2
	$db->free_result($request);
1142
1143 2
	return $return_value;
1144
}
1145
1146
/**
1147
 * Recursively get a list of boards.
1148
 *
1149
 * - Used by getBoardTree
1150
 *
1151
 * @package Boards
1152
 * @param int[] $_boardList The board list
1153
 * @param array $_tree the board tree
1154
 */
1155
function recursiveBoards(&$_boardList, &$_tree)
1156
{
1157
	if (empty($_tree['children']))
1158
		return;
1159
1160
	foreach ($_tree['children'] as $id => $node)
1161
	{
1162
		$_boardList[] = $id;
1163
		recursiveBoards($_boardList, $node);
1164
	}
1165
}
1166
1167
/**
1168
 * Returns whether the sub-board id is actually a child of the parent (recursive).
1169
 *
1170
 * @package Boards
1171
 * @param int $child The ID of the child board
1172
 * @param int $parent The ID of a parent board
1173
 *
1174
 * @return boolean if the specified child board is a child of the specified parent board.
1175
 */
1176
function isChildOf($child, $parent)
1177
{
1178
	global $boards;
1179
1180
	if (empty($boards[$child]['parent']))
1181
		return false;
1182
1183
	if ($boards[$child]['parent'] == $parent)
1184
		return true;
1185
1186
	return isChildOf($boards[$child]['parent'], $parent);
1187
}
1188
1189
/**
1190
 * Returns whether this member has notification turned on for the specified board.
1191
 *
1192
 * @param int $id_member the member id
1193
 * @param int $id_board the board to check
1194
 * @return bool if they have notifications turned on for the board
1195
 */
1196
function hasBoardNotification($id_member, $id_board)
1197
{
1198
	$db = database();
1199
1200
	// Find out if they have notification set for this board already.
1201
	$request = $db->query('', '
1202
		SELECT id_member
1203
		FROM {db_prefix}log_notify
1204
		WHERE id_member = {int:current_member}
1205
			AND id_board = {int:current_board}
1206
		LIMIT 1',
1207
		array(
1208
			'current_board' => $id_board,
1209
			'current_member' => $id_member,
1210
		)
1211
	);
1212
	$hasNotification = $db->num_rows($request) != 0;
1213
	$db->free_result($request);
1214
1215
	return $hasNotification;
1216
}
1217
1218
/**
1219
 * Set board notification on or off for the given member.
1220
 *
1221
 * @package Boards
1222
 * @param int $id_member
1223
 * @param int $id_board
1224
 * @param bool $on = false
1225
 */
1226 View Code Duplication
function setBoardNotification($id_member, $id_board, $on = false)
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
	$db = database();
1229
1230
	if ($on)
1231
	{
1232
		// Turn notification on.  (note this just blows smoke if it's already on.)
1233
		$db->insert('ignore',
1234
			'{db_prefix}log_notify',
1235
			array('id_member' => 'int', 'id_board' => 'int'),
1236
			array($id_member, $id_board),
1237
			array('id_member', 'id_board')
1238
		);
1239
	}
1240
	else
1241
	{
1242
		// Turn notification off for this board.
1243
		$db->query('', '
1244
			DELETE FROM {db_prefix}log_notify
1245
			WHERE id_member = {int:current_member}
1246
				AND id_board = {int:current_board}',
1247
			array(
1248
				'current_board' => $id_board,
1249
				'current_member' => $id_member,
1250
			)
1251
		);
1252
	}
1253
}
1254
1255
/**
1256
 * Reset sent status for board notifications.
1257
 *
1258
 * This function returns a boolean equivalent with hasBoardNotification().
1259
 * This is unexpected, but it's done this way to avoid any extra-query is executed on MessageIndex::action_messageindex().
1260
 * Just ignore the return value for normal use.
1261
 *
1262
 * @package Boards
1263
 * @param int $id_member
1264
 * @param int $id_board
1265
 * @param bool $check = true check if the user has notifications enabled for the board
1266
 * @return bool if the board was marked for notifications
1267
 */
1268
function resetSentBoardNotification($id_member, $id_board, $check = true)
1269
{
1270 1
	$db = database();
1271
1272
	// Check if notifications are enabled for this user on the board?
1273 1
	if ($check)
1274
	{
1275
		// check if the member has notifications enabled for this board
1276 1
		$request = $db->query('', '
1277
			SELECT sent
1278
			FROM {db_prefix}log_notify
1279
			WHERE id_board = {int:current_board}
1280
				AND id_member = {int:current_member}
1281
			LIMIT 1',
1282
			array(
1283 1
				'current_board' => $id_board,
1284 1
				'current_member' => $id_member,
1285
			)
1286
		);
1287
		// nothing to do
1288 1
		if ($db->num_rows($request) == 0)
1289 1
			return false;
1290
		$sent = $db->fetch_row($request);
1291
		$db->free_result($request);
1292
1293
		// not sent already? No need to stay around then
1294
		if (empty($sent))
1295
			return true;
1296
	}
1297
1298
	// Reset 'sent' status.
1299
	$db->query('', '
1300
		UPDATE {db_prefix}log_notify
1301
		SET sent = {int:is_sent}
1302
		WHERE id_board = {int:current_board}
1303
			AND id_member = {int:current_member}',
1304
		array(
1305
			'current_board' => $id_board,
1306
			'current_member' => $id_member,
1307
			'is_sent' => 0,
1308
		)
1309
	);
1310
	return true;
1311
}
1312
1313
/**
1314
 * Counts the board notification for a given member.
1315
 *
1316
 * @package Boards
1317
 * @param int $memID
1318
 * @return int
1319
 */
1320
function getBoardNotificationsCount($memID)
1321
{
1322
	global $user_info;
1323
1324
	$db = database();
1325
1326
	// All the boards that you have notification enabled
1327
	$request = $db->query('', '
1328
		SELECT COUNT(*)
1329
		FROM {db_prefix}log_notify AS ln
1330
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = ln.id_board)
1331
			LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member})
1332
		WHERE ln.id_member = {int:selected_member}
1333
			AND {query_see_board}',
1334
		array(
1335
			'current_member' => $user_info['id'],
1336
			'selected_member' => $memID,
1337
		)
1338
	);
1339
	list ($totalNotifications) = $db->fetch_row($request);
1340
	$db->free_result($request);
1341
1342
	return $totalNotifications;
1343
}
1344
1345
/**
1346
 * Returns all the boards accessible to the current user.
1347
 *
1348
 * - If $id_parents is given, return only the sub-boards of those boards.
1349
 * - If $id_boards is given, filters the boards to only those accessible.
1350
 * - The function doesn't guarantee the boards are properly sorted
1351
 *
1352
 * @package Boards
1353
 * @param int[]|null $id_parents array of ints representing board ids
1354
 * @param int[]|null $id_boards
1355
 */
1356
function accessibleBoards($id_boards = null, $id_parents = null)
1357
{
1358
	$db = database();
1359
1360
	$boards = array();
1361
	if (!empty($id_parents))
1362
	{
1363
		// Find all boards down from $id_parent
1364
		$request = $db->query('', '
1365
			SELECT b.id_board
1366
			FROM {db_prefix}boards AS b
1367
			WHERE b.id_parent IN ({array_int:parent_list})
1368
				AND {query_see_board}',
1369
			array(
1370
				'parent_list' => $id_parents,
1371
			)
1372
		);
1373
	}
1374
	elseif (!empty($id_boards))
1375
	{
1376
		// Find all the boards this user can see between those selected
1377
		$request = $db->query('', '
1378
			SELECT b.id_board
1379
			FROM {db_prefix}boards AS b
1380
			WHERE b.id_board IN ({array_int:board_list})
1381
				AND {query_see_board}',
1382
			array(
1383
				'board_list' => $id_boards,
1384
			)
1385
		);
1386
	}
1387
	else
1388
	{
1389
		// Find all the boards this user can see.
1390
		$request = $db->query('', '
1391
			SELECT b.id_board
1392
			FROM {db_prefix}boards AS b
1393
			WHERE {query_see_board}',
1394
			array(
1395
			)
1396
		);
1397
	}
1398
1399
	while ($row = $db->fetch_assoc($request))
1400
		$boards[] = $row['id_board'];
1401
	$db->free_result($request);
1402
1403
	return $boards;
1404
}
1405
1406
/**
1407
 * Returns the boards the current user wants to see.
1408
 *
1409
 * @package Boards
1410
 * @param string $see_board either 'query_see_board' or 'query_wanna_see_board'
1411
 * @param bool $hide_recycle is tru the recycle bin is not returned
1412
 */
1413
function wantedBoards($see_board, $hide_recycle = true)
1414
{
1415
	global $modSettings, $user_info;
1416
1417
	$db = database();
1418
	$allowed_see = array(
1419
		'query_see_board',
1420
		'query_wanna_see_board'
1421
	);
1422
1423
	// Find all boards down from $id_parent
1424
	return $db->fetchQueryCallback('
1425
		SELECT b.id_board
1426
		FROM {db_prefix}boards AS b
1427
		WHERE ' . $user_info[in_array($see_board, $allowed_see) ? $see_board : $allowed_see[0]] . ($hide_recycle && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
1428
			AND b.id_board != {int:recycle_board}' : ''),
1429
		array(
1430
			'recycle_board' => (int) $modSettings['recycle_board'],
1431
		),
1432
		function ($row)
1433
		{
1434
			return $row['id_board'];
1435
		}
1436
	);
1437
}
1438
1439
/**
1440
 * Returns the post count and name of a board
1441
 *
1442
 * - if supplied a topic id will also return the message subject
1443
 * - honors query_see_board to ensure a user can see the information
1444
 *
1445
 * @package Boards
1446
 * @param int $board_id
1447
 * @param int|null $topic_id
1448
 */
1449 View Code Duplication
function boardInfo($board_id, $topic_id = null)
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...
1450
{
1451 1
	$db = database();
1452
1453 1
	if (!empty($topic_id))
1454
	{
1455
		$request = $db->query('', '
1456
			SELECT b.count_posts, b.name, m.subject
1457
			FROM {db_prefix}boards AS b
1458
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
1459
				INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
1460
			WHERE {query_see_board}
1461
				AND b.id_board = {int:board}
1462
				AND b.redirect = {string:blank_redirect}
1463
			LIMIT 1',
1464
			array(
1465
				'current_topic' => $topic_id,
1466
				'board' => $board_id,
1467
				'blank_redirect' => '',
1468
			)
1469
		);
1470
	}
1471
	else
1472
	{
1473 1
		$request = $db->query('', '
1474
			SELECT b.count_posts, b.name
1475
			FROM {db_prefix}boards AS b
1476
			WHERE {query_see_board}
1477
				AND b.id_board = {int:board}
1478
				AND b.redirect = {string:blank_redirect}
1479
			LIMIT 1',
1480
			array(
1481 1
				'board' => $board_id,
1482 1
				'blank_redirect' => '',
1483
			)
1484
		);
1485
	}
1486
1487 1
	$returns = $db->fetch_assoc($request);
1488 1
	$db->free_result($request);
1489
1490 1
	return $returns;
1491
}
1492
1493
/**
1494
 * Loads properties from non-standard groups
1495
 *
1496
 * @package Boards
1497
 * @param int $curBoard
1498
 * @param boolean $new_board = false Whether this is a new board
1499
 * @return array
1500
 */
1501
function getOtherGroups($curBoard, $new_board = false)
1502
{
1503
	$db = database();
1504
1505
	$groups = array();
1506
1507
	// Load membergroups.
1508
	$request = $db->query('', '
1509
		SELECT group_name, id_group, min_posts
1510
		FROM {db_prefix}membergroups
1511
		WHERE id_group > {int:moderator_group} OR id_group = {int:global_moderator}
1512
		ORDER BY min_posts, id_group != {int:global_moderator}, group_name',
1513
		array(
1514
			'moderator_group' => 3,
1515
			'global_moderator' => 2,
1516
		)
1517
	);
1518
1519
	while ($row = $db->fetch_assoc($request))
1520
	{
1521
		if ($new_board && $row['min_posts'] == -1)
1522
			$curBoard['member_groups'][] = $row['id_group'];
1523
1524
		$groups[(int) $row['id_group']] = array(
1525
			'id' => $row['id_group'],
1526
			'name' => trim($row['group_name']),
1527
			'allow' => in_array($row['id_group'], $curBoard['member_groups']),
1528
			'deny' => in_array($row['id_group'], $curBoard['deny_groups']),
1529
			'is_post_group' => $row['min_posts'] != -1,
1530
		);
1531
		}
1532
	$db->free_result($request);
1533
1534
	return $groups;
1535
}
1536
1537
/**
1538
 * Get a list of moderators from a specific board
1539
 *
1540
 * @package Boards
1541
 * @param int $idboard
1542
 * @param bool $only_id return only the id of the moderators instead of id and name (default false)
1543
 * @return array
1544
 */
1545
function getBoardModerators($idboard, $only_id = false)
1546
{
1547
	$db = database();
1548
1549
	$moderators = array();
1550
1551
	if ($only_id)
1552
	{
1553
		$request = $db->query('', '
1554
			SELECT id_member
1555
			FROM {db_prefix}moderators
1556
			WHERE id_board = {int:current_board}',
1557
			array(
1558
				'current_board' => $idboard,
1559
			)
1560
		);
1561
		while ($row = $db->fetch_assoc($request))
1562
			$moderators[] = $row['id_member'];
1563
	}
1564
	else
1565
	{
1566
		$request = $db->query('', '
1567
			SELECT mem.id_member, mem.real_name
1568
			FROM {db_prefix}moderators AS mods
1569
				INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
1570
			WHERE mods.id_board = {int:current_board}',
1571
			array(
1572
				'current_board' => $idboard,
1573
			)
1574
		);
1575
1576
		while ($row = $db->fetch_assoc($request))
1577
			$moderators[$row['id_member']] = $row['real_name'];
1578
	}
1579
	$db->free_result($request);
1580
1581
	return $moderators;
1582
}
1583
1584
/**
1585
 * Get a list of all the board moderators (every board)
1586
 *
1587
 * @package Boards
1588
 * @param bool $only_id return array with key of id_member of the moderator(s)
1589
 * otherwise array with key of id_board id (default false)
1590
 * @return array
1591
 */
1592
function allBoardModerators($only_id = false)
1593
{
1594
	$db = database();
1595
1596
	$moderators = array();
1597
1598
	if ($only_id)
1599
		$request = $db->query('', '
1600
			SELECT id_board, id_member
1601
			FROM {db_prefix}moderators',
1602
			array(
1603
			)
1604
		);
1605
	else
1606
		$request = $db->query('', '
1607
			SELECT mods.id_board, mods.id_member, mem.real_name
1608
			FROM {db_prefix}moderators AS mods
1609
				INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)',
1610
			array(
1611
			)
1612
		);
1613
1614
	while ($row = $db->fetch_assoc($request))
1615
	{
1616
		if ($only_id)
1617
			$moderators[$row['id_member']][] = $row;
1618
		else
1619
			$moderators[$row['id_board']][] = $row;
1620
	}
1621
	$db->free_result($request);
1622
1623
	return $moderators;
1624
}
1625
1626
/**
1627
 * Get a list of all the board moderated by a certain user
1628
 *
1629
 * @package Boards
1630
 * @param int $id_member the id of a member
1631
 * @return array
1632
 */
1633
function boardsModerated($id_member)
1634
{
1635
	$db = database();
1636
1637
	return $db->fetchQueryCallback('
1638
		SELECT id_board
1639
		FROM {db_prefix}moderators
1640
		WHERE id_member = {int:current_member}',
1641
		array(
1642
			'current_member' => $id_member,
1643
		),
1644
		function ($row)
1645
		{
1646
			return $row['id_board'];
1647
		}
1648
	);
1649
}
1650
1651
/**
1652
 * Get all available themes
1653
 *
1654
 * @package Boards
1655
 * @return array
1656
 */
1657
function getAllThemes()
1658
{
1659
	$db = database();
1660
1661
	// Get all the themes...
1662
	return $db->fetchQuery('
1663
		SELECT id_theme AS id, value AS name
1664
		FROM {db_prefix}themes
1665
		WHERE variable = {string:name}',
1666
		array(
1667
			'name' => 'name',
1668
		)
1669
	);
1670
}
1671
1672
/**
1673
 * Gets redirect infos and post count from a selected board.
1674
 *
1675
 * @package Boards
1676
 * @param int $idboard
1677
 * @return array
1678
 */
1679
function getBoardProperties($idboard)
1680
{
1681
	$db = database();
1682
1683
	$properties = array();
1684
1685
	$request = $db->query('', '
1686
		SELECT redirect, num_posts
1687
		FROM {db_prefix}boards
1688
		WHERE id_board = {int:current_board}',
1689
		array(
1690
			'current_board' => $idboard,
1691
		)
1692
	);
1693
	list ($properties['oldRedirect'], $properties['numPosts']) = $db->fetch_row($request);
1694
	$db->free_result($request);
1695
1696
	return $properties;
1697
}
1698
1699
/**
1700
 * Fetch the number of posts in an array of boards based on board IDs or category IDs
1701
 *
1702
 * @package Boards
1703
 * @param int[]|null $boards an array of board IDs
1704
 * @param int[]|null $categories an array of category IDs
1705
 * @param bool $wanna_see_board if true uses {query_wanna_see_board}, otherwise {query_see_board}
1706
 * @param bool $include_recycle if false excludes any results from the recycle board (if enabled)
1707
 */
1708
function boardsPosts($boards, $categories, $wanna_see_board = false, $include_recycle = true)
1709
{
1710 1
	global $modSettings;
1711
1712 1
	$db = database();
1713
1714 1
	$clauses = array();
1715 1
	$removals = array();
1716 1
	$clauseParameters = array();
1717
1718 1
	if (!empty($categories))
1719
	{
1720
		$clauses[] = 'id_cat IN ({array_int:category_list})';
1721
		$clauseParameters['category_list'] = $categories;
1722
	}
1723
1724 1
	if (!empty($boards))
1725
	{
1726 1
		$clauses[] = 'id_board IN ({array_int:board_list})';
1727 1
		$clauseParameters['board_list'] = $boards;
1728
	}
1729
1730 1
	if (empty($include_recycle) && (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0))
1731
	{
1732
		$removals[] = 'id_board != {int:recycle_board}';
1733
		$clauseParameters['recycle_board'] = (int) $modSettings['recycle_board'];
1734
	}
1735
1736 1
	if (empty($clauses))
1737
		return array();
1738
1739 1
	$request = $db->query('', '
1740
		SELECT b.id_board, b.num_posts
1741
		FROM {db_prefix}boards AS b
1742 1
		WHERE ' . ($wanna_see_board ? '{query_wanna_see_board}' : '{query_see_board}') . '
1743 1
			AND b.' . implode(' OR b.', $clauses) . (!empty($removals) ? '
1744 1
			AND b.' . implode(' AND b.', $removals) : ''),
1745 1
		$clauseParameters
1746
	);
1747 1
	$return = array();
1748 1
	while ($row = $db->fetch_assoc($request))
1749 1
		$return[$row['id_board']] = $row['num_posts'];
1750 1
	$db->free_result($request);
1751
1752 1
	return $return;
1753
}
1754
1755
/**
1756
 * Returns the total sum of posts in the boards defined by query_wanna_see_board
1757
 * Excludes the count of any boards defined as a recycle board from the sum
1758
 */
1759 View Code Duplication
function sumRecentPosts()
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...
1760
{
1761 1
	$db = database();
1762
1763 1
	global $modSettings;
1764
1765 1
	$request = $db->query('', '
1766
		SELECT COALESCE(SUM(num_posts), 0)
1767
		FROM {db_prefix}boards as b
1768 1
		WHERE {query_wanna_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
1769 1
			AND b.id_board != {int:recycle_board}' : ''),
1770
		array(
1771 1
			'recycle_board' => $modSettings['recycle_board']
1772
		)
1773
	);
1774 1
	list ($result) = $db->fetch_row($request);
1775 1
	$db->free_result($request);
1776
1777 1
	return $result;
1778
}
1779
1780
/**
1781
 * Returns information of a set of boards based on board IDs or category IDs
1782
 *
1783
 * @package Boards
1784
 * @param mixed[]|string $conditions is an associative array that holds the board or the cat IDs
1785
 *              'categories' => an array of category IDs (it accepts a single ID too)
1786
 *              'boards' => an array of board IDs (it accepts a single ID too)
1787
 *              if conditions is set to 'all' (not an array) all the boards are queried
1788
 * @param mixed[] $params is an optional array that allows to control the results returned:
1789
 *              'sort_by' => (string) defines the sorting of the results (allowed: id_board, name)
1790
 *              'selects' => (string) determines what information are retrieved and returned
1791
 *                           Allowed values: 'name', 'posts', 'detailed', 'permissions', 'reports';
1792
 *                           default: 'name';
1793
 *                           see the function for details on the fields associated to each value
1794
 *              'override_permissions' => (bool) if true doesn't use neither {query_wanna_see_board} nor {query_see_board} (default false)
1795
 *              'wanna_see_board' => (bool) if true uses {query_wanna_see_board}, otherwise {query_see_board}
1796
 *              'include_recycle' => (bool) recycle board is included (default true)
1797
 *              'include_redirects' => (bool) redirects are included (default true)
1798
 *
1799
 * @todo unify the two queries?
1800
 */
1801
function fetchBoardsInfo($conditions = 'all', $params = array())
1802
{
1803
	global $modSettings;
1804
1805
	$db = database();
1806
1807
	// Ensure default values are set
1808
	$params = array_merge(array('override_permissions' => false, 'wanna_see_board' => false, 'include_recycle' => true, 'include_redirects' => true), $params);
1809
1810
	$clauses = array();
1811
	$clauseParameters = array();
1812
	$allowed_sort = array(
1813
		'id_board',
1814
		'name'
1815
	);
1816
1817
	if (!empty($params['sort_by']) && in_array($params['sort_by'], $allowed_sort))
1818
		$sort_by = 'ORDER BY ' . $params['sort_by'];
1819
	else
1820
		$sort_by = '';
1821
1822
	// @todo: memos for optimization
1823
	/*
1824
		id_board    => MergeTopic + MergeTopic + MessageIndex + Search + ScheduledTasks
1825
		name        => MergeTopic + ScheduledTasks + News
1826
		count_posts => MessageIndex
1827
		num_posts   => News
1828
	*/
1829
	$known_selects = array(
1830
		'name' => 'b.id_board, b.name',
1831
		'posts' => 'b.id_board, b.count_posts, b.num_posts',
1832
		'detailed' => 'b.id_board, b.name, b.count_posts, b.num_posts',
1833
		'permissions' => 'b.id_board, b.name, b.member_groups, b.id_profile',
1834
		'reports' => 'b.id_board, b.name, b.member_groups, b.id_profile, b.deny_member_groups',
1835
	);
1836
1837
	$select = $known_selects[empty($params['selects']) || !isset($known_selects[$params['selects']]) ? 'name' : $params['selects']];
1838
1839
	// If $conditions wasn't set or is 'all', get all boards
1840
	if (!is_array($conditions) && $conditions == 'all')
1841
	{
1842
		// id_board, name, id_profile => used in admin/Reports.controller.php
1843
		$request = $db->query('', '
1844
			SELECT ' . $select . '
1845
			FROM {db_prefix}boards AS b
1846
			' . $sort_by,
1847
			array()
1848
		);
1849
	}
1850
	else
1851
	{
1852
		// Only some categories?
1853 View Code Duplication
		if (!empty($conditions['categories']))
1854
		{
1855
			$clauses[] = 'id_cat IN ({array_int:category_list})';
1856
			$clauseParameters['category_list'] = is_array($conditions['categories']) ? $conditions['categories'] : array($conditions['categories']);
1857
		}
1858
1859
		// Only a few boards, perhaps!
1860 View Code Duplication
		if (!empty($conditions['boards']))
1861
		{
1862
			$clauses[] = 'id_board IN ({array_int:board_list})';
1863
			$clauseParameters['board_list'] = is_array($conditions['boards']) ? $conditions['boards'] : array($conditions['boards']);
1864
		}
1865
1866
		if ($params['override_permissions'])
1867
			$security = '1=1';
1868
		else
1869
			$security = $params['wanna_see_board'] ? '{query_wanna_see_board}' : '{query_see_board}';
1870
1871
		$request = $db->query('', '
1872
			SELECT ' . $select . '
1873
			FROM {db_prefix}boards AS b
1874
			WHERE ' . $security . (!empty($clauses) ? '
1875
				AND b.' . implode(' OR b.', $clauses) : '') . ($params['include_recycle'] ? '' : '
1876
				AND b.id_board != {int:recycle_board}') . ($params['include_redirects'] ? '' : '
1877
				AND b.redirect = {string:empty_string}
1878
			' . $sort_by),
1879
			array_merge($clauseParameters, array(
1880
				'recycle_board' => !empty($modSettings['recycle_board']) ? $modSettings['recycle_board'] : 0,
1881
				'empty_string' => '',
1882
			))
1883
		);
1884
	}
1885
	$return = array();
1886
	while ($row = $db->fetch_assoc($request))
1887
		$return[$row['id_board']] = $row;
1888
1889
	$db->free_result($request);
1890
1891
	return $return;
1892
}
1893
1894
/**
1895
 * Retrieve the all the sub-boards of an array of boards and add the ids to the same array
1896
 *
1897
 * @package Boards
1898
 * @param int[]|int $boards an array of board IDs (it accepts a single board too).
1899
 * NOTE: the $boards param is deprecated since 1.1 - The param is passed by ref in 1.0 and the result
1900
 * is returned through the param itself, starting from 1.1 the expected behaviour
1901
 * is that the result is returned.
1902
 * @return bool|int[]
1903
 */
1904
function addChildBoards($boards)
1905
{
1906
	$db = database();
1907
1908
	if (empty($boards))
1909
	{
1910
		return false;
1911
	}
1912
1913
	if (!is_array($boards))
1914
	{
1915
		$boards = array($boards);
1916
	}
1917
1918
	$request = $db->query('', '
1919
		SELECT
1920
			b.id_board, b.id_parent
1921
		FROM {db_prefix}boards AS b
1922
		WHERE {query_see_board}
1923
			AND b.child_level > {int:no_parents}
1924
			AND b.id_board NOT IN ({array_int:board_list})
1925
		ORDER BY child_level ASC
1926
		',
1927
		array(
1928
			'no_parents' => 0,
1929
			'board_list' => $boards,
1930
		)
1931
	);
1932
	while ($row = $db->fetch_assoc($request))
1933
	{
1934
		if (in_array($row['id_parent'], $boards))
1935
		{
1936
			$boards[] = $row['id_board'];
1937
		}
1938
	}
1939
	$db->free_result($request);
1940
1941
	return $boards;
1942
}
1943
1944
/**
1945
 * Increment a board stat field, for example num_posts.
1946
 *
1947
 * @package Boards
1948
 * @param int $id_board
1949
 * @param mixed[]|string $values an array of index => value of a string representing the index to increment
1950
 */
1951
function incrementBoard($id_board, $values)
1952
{
1953
	$db = database();
1954
1955
	$knownInts = array(
1956
		'child_level', 'board_order', 'num_topics', 'num_posts', 'count_posts',
1957
		'unapproved_posts', 'unapproved_topics'
1958
	);
1959
1960
	call_integration_hook('integrate_board_fields', array(&$knownInts));
1961
1962
	$set = array();
1963
	$params = array('id_board' => $id_board);
1964
	$values = is_array($values) ? $values : array($values => 1);
1965
1966
	foreach ($values as $key => $val)
1967
	{
1968
		if (in_array($key, $knownInts))
1969
		{
1970
			$set[] = $key . ' = ' . $key . ' + {int:' . $key . '}';
1971
			$params[$key] = $val;
1972
		}
1973
	}
1974
1975
	if (empty($set))
1976
		return;
1977
1978
	$db->query('', '
1979
		UPDATE {db_prefix}boards
1980
		SET
1981
			' . implode(',
1982
			', $set) . '
1983
		WHERE id_board = {int:id_board}',
1984
		$params
1985
	);
1986
}
1987
1988
/**
1989
 * Decrement a board stat field, for example num_posts.
1990
 *
1991
 * @package Boards
1992
 * @param int $id_board
1993
 * @param mixed[]|string $values an array of index => value of a string representing the index to decrement
1994
 */
1995
function decrementBoard($id_board, $values)
1996
{
1997
	$db = database();
1998
1999
	$knownInts = array(
2000
		'child_level', 'board_order', 'num_topics', 'num_posts', 'count_posts',
2001
		'unapproved_posts', 'unapproved_topics'
2002
	);
2003
2004
	call_integration_hook('integrate_board_fields', array(&$knownInts));
2005
2006
	$set = array();
2007
	$params = array('id_board' => $id_board);
2008
	$values = is_array($values) ? $values : array($values => 1);
2009
2010
	foreach ($values as $key => $val)
2011
	{
2012
		if (in_array($key, $knownInts))
2013
		{
2014
			$set[] = $key . ' = CASE WHEN {int:' . $key . '} > ' . $key . ' THEN 0 ELSE ' . $key . ' - {int:' . $key . '} END';
2015
			$params[$key] = $val;
2016
		}
2017
	}
2018
2019
	if (empty($set))
2020
		return;
2021
2022
	$db->query('', '
2023
		UPDATE {db_prefix}boards
2024
		SET
2025
			' . implode(',
2026
			', $set) . '
2027
		WHERE id_board = {int:id_board}',
2028
		$params
2029
	);
2030
}
2031
2032
/**
2033
 * Retrieve all the boards the user can see and their notification status:
2034
 *
2035
 * - if they're subscribed to notifications for new topics in each of them
2036
 * or they're not.
2037
 * - (used by createList() callbacks)
2038
 *
2039
 * @package Boards
2040
 * @param int $start The item to start with (for pagination purposes)
2041
 * @param int $items_per_page  The number of items to show per page
2042
 * @param string $sort A string indicating how to sort the results
2043
 * @param int $memID id_member
2044
 * @return array
2045
 */
2046
function boardNotifications($start, $items_per_page, $sort, $memID)
0 ignored issues
show
Unused Code introduced by
The parameter $start is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $items_per_page is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2047
{
2048
	global $scripturl, $user_info, $modSettings;
2049
2050
	$db = database();
2051
2052
	// All the boards that you have notification enabled
2053
	$notification_boards = $db->fetchQueryCallback('
2054
		SELECT b.id_board, b.name, COALESCE(lb.id_msg, 0) AS board_read, b.id_msg_updated
2055
		FROM {db_prefix}log_notify AS ln
2056
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = ln.id_board)
2057
			LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member})
2058
		WHERE ln.id_member = {int:selected_member}
2059
			AND {query_see_board}
2060
		ORDER BY ' . $sort,
2061
		array(
2062
			'current_member' => $user_info['id'],
2063
			'selected_member' => $memID,
2064
		),
2065
		function ($row) use ($scripturl)
2066
		{
2067
			return array(
2068
				'id' => $row['id_board'],
2069
				'name' => $row['name'],
2070
				'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
2071
				'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0"><strong>' . $row['name'] . '</strong></a>',
2072
				'new' => $row['board_read'] < $row['id_msg_updated'],
2073
				'checked' => 'checked="checked"',
2074
			);
2075
		}
2076
	);
2077
2078
	// and all the boards that you can see but don't have notify turned on for
2079
	$request = $db->query('', '
2080
		SELECT b.id_board, b.name, COALESCE(lb.id_msg, 0) AS board_read, b.id_msg_updated
2081
		FROM {db_prefix}boards AS b
2082
			LEFT JOIN {db_prefix}log_notify AS ln ON (ln.id_board = b.id_board AND ln.id_member = {int:selected_member})
2083
			LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member})
2084
		WHERE {query_see_board}
2085
			AND ln.id_board is null ' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
2086
			AND b.id_board != {int:recycle_board}' : '') . '
2087
		ORDER BY ' . $sort,
2088
		array(
2089
			'selected_member' => $memID,
2090
			'current_member' => $user_info['id'],
2091
			'recycle_board' => $modSettings['recycle_board'],
2092
		)
2093
	);
2094
	while ($row = $db->fetch_assoc($request))
2095
		$notification_boards[] = array(
2096
			'id' => $row['id_board'],
2097
			'name' => $row['name'],
2098
			'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
2099
			'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>',
2100
			'new' => $row['board_read'] < $row['id_msg_updated'],
2101
			'checked' => '',
2102
		);
2103
	$db->free_result($request);
2104
2105
	return $notification_boards;
2106
}
2107
2108
/**
2109
 * Count boards all or specific depending on argument, redirect boards excluded by default.
2110
 *
2111
 * @package Boards
2112
 * @param mixed[]|string $conditions is an associative array that holds the board or the cat IDs
2113
 *              'categories' => an array of category IDs (it accepts a single ID too)
2114
 *              'boards' => an array of board IDs (it accepts a single ID too)
2115
 *              if conditions is set to 'all' (not an array) all the boards are queried
2116
 * @param mixed[]|null $params is an optional array that allows to control the results returned if $conditions is not set to 'all':
2117
 *              'wanna_see_board' => (bool) if true uses {query_wanna_see_board}, otherwise {query_see_board}
2118
 *              'include_recycle' => (bool) recycle board is included (default true)
2119
 *              'include_redirects' => (bool) redirects are included (default true)
2120
 * @return int
2121
 */
2122
function countBoards($conditions = 'all', $params = array())
2123
{
2124
	global $modSettings;
2125
2126
	$db = database();
2127
2128
	// Ensure default values are set
2129
	$params = array_merge(array('wanna_see_board' => false, 'include_recycle' => true, 'include_redirects' => true), $params);
2130
2131
	$clauses = array();
2132
	$clauseParameters = array();
2133
2134
	// if $conditions wasn't set or is 'all', get all boards
2135
	if (!is_array($conditions) && $conditions == 'all')
2136
	{
2137
		// id_board, name, id_profile => used in admin/Reports.controller.php
2138
		$request = $db->query('', '
2139
			SELECT COUNT(*)
2140
			FROM {db_prefix}boards AS b',
2141
			array()
2142
		);
2143
	}
2144
	else
2145
	{
2146
		// only some categories?
2147 View Code Duplication
		if (!empty($conditions['categories']))
2148
		{
2149
			$clauses[] = 'id_cat IN ({array_int:category_list})';
2150
			$clauseParameters['category_list'] = is_array($conditions['categories']) ? $conditions['categories'] : array($conditions['categories']);
2151
		}
2152
2153
		// only a few boards, perhaps!
2154 View Code Duplication
		if (!empty($conditions['boards']))
2155
		{
2156
			$clauses[] = 'id_board IN ({array_int:board_list})';
2157
			$clauseParameters['board_list'] = is_array($conditions['boards']) ? $conditions['boards'] : array($conditions['boards']);
2158
		}
2159
2160
		$request = $db->query('', '
2161
			SELECT COUNT(*)
2162
			FROM {db_prefix}boards AS b
2163
			WHERE ' . ($params['wanna_see_board'] ? '{query_wanna_see_board}' : '{query_see_board}') . (!empty($clauses) ? '
2164
				AND b.' . implode(' OR b.', $clauses) : '') . ($params['include_recycle'] ? '' : '
2165
				AND b.id_board != {int:recycle_board}') . ($params['include_redirects'] ? '' : '
2166
				AND b.redirect = {string:empty_string}'),
2167
			array_merge($clauseParameters, array(
2168
				'recycle_board' => !empty($modSettings['recycle_board']) ? $modSettings['recycle_board'] : 0,
2169
				'empty_string' => '',
2170
			))
2171
		);
2172
	}
2173
2174
	list ($num_boards) = $db->fetch_row($request);
2175
	$db->free_result($request);
2176
2177
	return $num_boards;
2178
}
2179