Completed
Push — development ( ac22bd...5236fa )
by Stephen
14:46
created

Boards.subs.php ➔ reorderBoards()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 25
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 12
nc 4
nop 0
dl 0
loc 25
ccs 0
cts 11
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 1.1
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 6
	global $user_info, $modSettings;
30
31 6
	$db = database();
32
33
	// Force $boards to be an array.
34 6
	if (!is_array($boards))
35 4
		$boards = array($boards);
36
	else
37 6
		$boards = array_unique($boards);
38
39
	// No boards, nothing to mark as read.
40 6
	if (empty($boards))
41 4
		return;
42
43
	// Allow the user to mark a board as unread.
44 2
	if ($unread)
45 4
	{
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 6
		$markRead = array();
71 6
		foreach ($boards as $board)
72 6
			$markRead[] = array($modSettings['maxMsgID'], $user_info['id'], $board);
73
74 6
		$db->insert('replace',
75 6
			'{db_prefix}log_boards',
76 6
			array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'),
77 4
			$markRead,
78 6
			array('id_board', 'id_member')
79 4
		);
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 2
	if ($resetTopics)
94 4
	{
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 6
}
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
	clean_cache('data');
474
475
	if (empty($boardOptions['dont_log']))
476
		logAction('edit_board', array('board' => $board_id), 'admin');
477
}
478
479
/**
480
 * Create a new board and set its properties and position.
481
 *
482
 * - Allows (almost) the same options as the modifyBoard() function.
483
 * - With the option inherit_permissions set, the parent board permissions
484
 * will be inherited.
485
 *
486
 * @package Boards
487
 * @param mixed[] $boardOptions
488
 * @return int The new board id
489
 * @throws Elk_Exception
490
 */
491
function createBoard($boardOptions)
492
{
493
	global $boards;
494
495
	$db = database();
496
497
	// Trigger an error if one of the required values is not set.
498
	if (!isset($boardOptions['board_name']) || trim($boardOptions['board_name']) == '' || !isset($boardOptions['move_to']) || !isset($boardOptions['target_category']))
499
		trigger_error('createBoard(): One or more of the required options is not set', E_USER_ERROR);
500
501
	if (in_array($boardOptions['move_to'], array('child', 'before', 'after')) && !isset($boardOptions['target_board']))
502
		trigger_error('createBoard(): Target board is not set', E_USER_ERROR);
503
504
	// Set every optional value to its default value.
505
	$boardOptions += array(
506
		'posts_count' => true,
507
		'override_theme' => false,
508
		'board_theme' => 0,
509
		'access_groups' => array(),
510
		'board_description' => '',
511
		'profile' => 1,
512
		'moderators' => '',
513
		'inherit_permissions' => true,
514
		'dont_log' => true,
515
	);
516
	$board_columns = array(
517
		'id_cat' => 'int', 'name' => 'string-255', 'description' => 'string', 'board_order' => 'int',
518
		'member_groups' => 'string', 'redirect' => 'string',
519
	);
520
	$board_parameters = array(
521
		$boardOptions['target_category'], $boardOptions['board_name'], '', 0,
522
		'-1,0', '',
523
	);
524
525
	// Insert a board, the settings are dealt with later.
526
	$db->insert('',
527
		'{db_prefix}boards',
528
		$board_columns,
529
		$board_parameters,
530
		array('id_board')
531
	);
532
	$board_id = $db->insert_id('{db_prefix}boards', 'id_board');
533
534
	if (empty($board_id))
535
		return 0;
536
537
	// Change the board according to the given specifications.
538
	modifyBoard($board_id, $boardOptions);
539
540
	// Do we want the parent permissions to be inherited?
541
	if ($boardOptions['inherit_permissions'])
542
	{
543
		getBoardTree();
544
545
		if (!empty($boards[$board_id]['parent']))
546
		{
547
			$board_data = fetchBoardsInfo(array('boards' => $boards[$board_id]['parent']), array('selects' => 'permissions'));
548
549
			$db->query('', '
550
				UPDATE {db_prefix}boards
551
				SET id_profile = {int:new_profile}
552
				WHERE id_board = {int:current_board}',
553
				array(
554
					'new_profile' => $board_data[$boards[$board_id]['parent']]['id_profile'],
555
					'current_board' => $board_id,
556
				)
557
			);
558
		}
559
	}
560
561
	// Clean the data cache.
562
	clean_cache('data');
563
564
	// Created it.
565
	logAction('add_board', array('board' => $board_id), 'admin');
566
567
	// Here you are, a new board, ready to be spammed.
568
	return $board_id;
569
}
570
571
/**
572
 * Remove one or more boards.
573
 *
574
 * - Allows to move the children of the board before deleting it
575
 * - if moveChildrenTo is set to null, the sub-boards will be deleted.
576
 * - Deletes:
577
 *   - all topics that are on the given boards;
578
 *   - all information that's associated with the given boards;
579
 * - updates the statistics to reflect the new situation.
580
 *
581
 * @package Boards
582
 * @param int[] $boards_to_remove
583
 * @param int|null $moveChildrenTo = null
584
 * @throws Elk_Exception
585
 */
586
function deleteBoards($boards_to_remove, $moveChildrenTo = null)
587
{
588
	global $boards;
589
590
	$db = database();
591
592
	// No boards to delete? Return!
593
	if (empty($boards_to_remove))
594
		return;
595
596
	getBoardTree();
597
598
	call_integration_hook('integrate_delete_board', array($boards_to_remove, &$moveChildrenTo));
599
600
	// If $moveChildrenTo is set to null, include the children in the removal.
601
	if ($moveChildrenTo === null)
602
	{
603
		// Get a list of the sub-boards that will also be removed.
604
		$child_boards_to_remove = array();
605
		foreach ($boards_to_remove as $board_to_remove)
606
			recursiveBoards($child_boards_to_remove, $boards[$board_to_remove]['tree']);
607
608
		// Merge the children with their parents.
609
		if (!empty($child_boards_to_remove))
610
			$boards_to_remove = array_unique(array_merge($boards_to_remove, $child_boards_to_remove));
611
	}
612
	// Move the children to a safe home.
613
	else
614
	{
615
		foreach ($boards_to_remove as $id_board)
616
		{
617
			// @todo Separate category?
618
			if ($moveChildrenTo === 0)
619
				fixChildren($id_board, 0, 0);
620
			else
621
				fixChildren($id_board, $boards[$moveChildrenTo]['level'] + 1, $moveChildrenTo);
622
		}
623
	}
624
625
	// Delete ALL topics in the selected boards (done first so topics can't be marooned.)
626
	$topics = $db->fetchQuery('
627
		SELECT id_topic
628
		FROM {db_prefix}topics
629
		WHERE id_board IN ({array_int:boards_to_remove})',
630
		array(
631
			'boards_to_remove' => $boards_to_remove,
632
		)
633
	);
634
635
	require_once(SUBSDIR . '/Topic.subs.php');
636
	removeTopics($topics, false);
637
638
	// Delete the board's logs.
639
	$db->query('', '
640
		DELETE FROM {db_prefix}log_mark_read
641
		WHERE id_board IN ({array_int:boards_to_remove})',
642
		array(
643
			'boards_to_remove' => $boards_to_remove,
644
		)
645
	);
646
	$db->query('', '
647
		DELETE FROM {db_prefix}log_boards
648
		WHERE id_board IN ({array_int:boards_to_remove})',
649
		array(
650
			'boards_to_remove' => $boards_to_remove,
651
		)
652
	);
653
	$db->query('', '
654
		DELETE FROM {db_prefix}log_notify
655
		WHERE id_board IN ({array_int:boards_to_remove})',
656
		array(
657
			'boards_to_remove' => $boards_to_remove,
658
		)
659
	);
660
661
	// Delete this board's moderators.
662
	$db->query('', '
663
		DELETE FROM {db_prefix}moderators
664
		WHERE id_board IN ({array_int:boards_to_remove})',
665
		array(
666
			'boards_to_remove' => $boards_to_remove,
667
		)
668
	);
669
670
	// Delete any extra events in the calendar.
671
	$db->query('', '
672
		DELETE FROM {db_prefix}calendar
673
		WHERE id_board IN ({array_int:boards_to_remove})',
674
		array(
675
			'boards_to_remove' => $boards_to_remove,
676
		)
677
	);
678
679
	// Delete any message icons that only appear on these boards.
680
	$db->query('', '
681
		DELETE FROM {db_prefix}message_icons
682
		WHERE id_board IN ({array_int:boards_to_remove})',
683
		array(
684
			'boards_to_remove' => $boards_to_remove,
685
		)
686
	);
687
688
	// Delete the boards.
689
	$db->query('', '
690
		DELETE FROM {db_prefix}boards
691
		WHERE id_board IN ({array_int:boards_to_remove})',
692
		array(
693
			'boards_to_remove' => $boards_to_remove,
694
		)
695
	);
696
697
	// Latest message/topic might not be there anymore.
698
	require_once(SUBSDIR . '/Messages.subs.php');
699
	updateMessageStats();
700
	require_once(SUBSDIR . '/Topic.subs.php');
701
	updateTopicStats();
702
	updateSettings(array(
703
		'calendar_updated' => time(),
704
	));
705
706
	// Plus reset the cache to stop people getting odd results.
707
	updateSettings(array('settings_updated' => time()));
708
709
	// Clean the cache as well.
710
	clean_cache('data');
711
712
	// Let's do some serious logging.
713
	foreach ($boards_to_remove as $id_board)
714
		logAction('delete_board', array('boardname' => $boards[$id_board]['name']), 'admin');
715
}
716
717
/**
718
 * Fixes the children of a board by setting their child_levels to new values.
719
 *
720
 * - Used when a board is deleted or moved, to affect its children.
721
 *
722
 * @package Boards
723
 * @param int $parent
724
 * @param int $newLevel
725
 * @param int $newParent
726
 */
727
function fixChildren($parent, $newLevel, $newParent)
728
{
729
	$db = database();
730
731
	// Grab all children of $parent...
732
	$children = $db->fetchQueryCallback('
733
		SELECT id_board
734
		FROM {db_prefix}boards
735
		WHERE id_parent = {int:parent_board}',
736
		array(
737
			'parent_board' => $parent,
738
		),
739
		function ($row)
740
		{
741
			return $row['id_board'];
742
		}
743
	);
744
745
	// ...and set it to a new parent and child_level.
746
	$db->query('', '
747
		UPDATE {db_prefix}boards
748
		SET id_parent = {int:new_parent}, child_level = {int:new_child_level}
749
		WHERE id_parent = {int:parent_board}',
750
		array(
751
			'new_parent' => $newParent,
752
			'new_child_level' => $newLevel,
753
			'parent_board' => $parent,
754
		)
755
	);
756
757
	// Recursively fix the children of the children.
758
	foreach ($children as $child)
759
		fixChildren($child, $newLevel + 1, $child);
760
}
761
762
/**
763
 * Load a lot of useful information regarding the boards and categories.
764
 *
765
 * - The information retrieved is stored in globals:
766
 *   $boards:    properties of each board.
767
 *   $boardList: a list of boards grouped by category ID.
768
 *   $cat_tree:  properties of each category.
769
 *
770
 * @param array $query
771
 *
772
 * @throws Elk_Exception no_valid_parent
773
 * @package Boards
774
 */
775
function getBoardTree($query = array())
776
{
777
	global $cat_tree, $boards, $boardList;
778
779
	$db = database();
780
781
	// Addons may want to add their own information to the board table.
782
	call_integration_hook('integrate_board_tree_query', array(&$query));
783
784
	// Getting all the board and category information you'd ever wanted.
785
	$request = $db->query('', '
786
		SELECT
787
			COALESCE(b.id_board, 0) AS id_board, b.id_parent, b.name AS board_name, b.description, b.child_level,
788
			b.board_order, b.count_posts, b.member_groups, b.id_theme, b.override_theme, b.id_profile, b.redirect,
789
			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']) ?
790
			$query['select'] : '') . '
791
		FROM {db_prefix}categories AS c
792
			LEFT JOIN {db_prefix}boards AS b ON (b.id_cat = c.id_cat)' . (!empty($query['join']) ?
793
			$query['join'] : '') . '
794
		ORDER BY c.cat_order, b.child_level, b.board_order',
795
		array(
796
		)
797
	);
798
	$cat_tree = array();
799
	$boards = array();
800
	$last_board_order = 0;
801
	while ($row = $db->fetch_assoc($request))
802
	{
803
		if (!isset($cat_tree[$row['id_cat']]))
804
		{
805
			$cat_tree[$row['id_cat']] = array(
806
				'node' => array(
807
					'id' => $row['id_cat'],
808
					'name' => $row['cat_name'],
809
					'order' => $row['cat_order'],
810
					'can_collapse' => $row['can_collapse']
811
				),
812
				'is_first' => empty($cat_tree),
813
				'last_board_order' => $last_board_order,
814
				'children' => array()
815
			);
816
			$prevBoard = 0;
817
			$curLevel = 0;
818
		}
819
820
		if (!empty($row['id_board']))
821
		{
822
			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...
823
				$prevBoard = 0;
824
825
			$boards[$row['id_board']] = array(
826
				'id' => $row['id_board'],
827
				'category' => $row['id_cat'],
828
				'parent' => $row['id_parent'],
829
				'level' => $row['child_level'],
830
				'order' => $row['board_order'],
831
				'name' => $row['board_name'],
832
				'member_groups' => explode(',', $row['member_groups']),
833
				'deny_groups' => explode(',', $row['deny_member_groups']),
834
				'description' => $row['description'],
835
				'count_posts' => empty($row['count_posts']),
836
				'posts' => $row['num_posts'],
837
				'topics' => $row['num_topics'],
838
				'theme' => $row['id_theme'],
839
				'override_theme' => $row['override_theme'],
840
				'profile' => $row['id_profile'],
841
				'redirect' => $row['redirect'],
842
				'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...
843
			);
844
			$prevBoard = $row['id_board'];
845
			$last_board_order = $row['board_order'];
846
847
			if (empty($row['child_level']))
848
			{
849
				$cat_tree[$row['id_cat']]['children'][$row['id_board']] = array(
850
					'node' => &$boards[$row['id_board']],
851
					'is_first' => empty($cat_tree[$row['id_cat']]['children']),
852
					'children' => array()
853
				);
854
				$boards[$row['id_board']]['tree'] = &$cat_tree[$row['id_cat']]['children'][$row['id_board']];
855
			}
856
			else
857
			{
858
				// Parent doesn't exist!
859
				if (!isset($boards[$row['id_parent']]['tree']))
860
					throw new Elk_Exception('no_valid_parent', false, array($row['board_name']));
861
862
				// Wrong childlevel...we can silently fix this...
863
				if ($boards[$row['id_parent']]['tree']['node']['level'] != $row['child_level'] - 1)
864
					$db->query('', '
865
						UPDATE {db_prefix}boards
866
						SET child_level = {int:new_child_level}
867
						WHERE id_board = {int:selected_board}',
868
						array(
869
							'new_child_level' => $boards[$row['id_parent']]['tree']['node']['level'] + 1,
870
							'selected_board' => $row['id_board'],
871
						)
872
					);
873
874
				$boards[$row['id_parent']]['tree']['children'][$row['id_board']] = array(
875
					'node' => &$boards[$row['id_board']],
876
					'is_first' => empty($boards[$row['id_parent']]['tree']['children']),
877
					'children' => array()
878
				);
879
				$boards[$row['id_board']]['tree'] = &$boards[$row['id_parent']]['tree']['children'][$row['id_board']];
880
			}
881
		}
882
883
		// Let integration easily add data to $boards and $cat_tree
884
		call_integration_hook('integrate_board_tree', array($row));
885
	}
886
	$db->free_result($request);
887
888
	// Get a list of all the boards in each category (using recursion).
889
	$boardList = array();
890
	foreach ($cat_tree as $catID => $node)
891
	{
892
		$boardList[$catID] = array();
893
		recursiveBoards($boardList[$catID], $node);
894
	}
895
}
896
897
/**
898
 * Generates the query to determine the list of available boards for a user
899
 *
900
 * - Executes the query and returns the list
901
 *
902
 * @package Boards
903
 * @param mixed[] $boardListOptions
904
 * @param boolean $simple if true a simple array is returned containing some basic
905
 *                information regarding the board (id_board, board_name, child_level, id_cat, cat_name)
906
 *                if false the boards are returned in an array subdivided by categories including also
907
 *                additional data like the number of boards
908
 * @return array An array of boards sorted according to the normal boards order
909
 */
910
function getBoardList($boardListOptions = array(), $simple = false)
911
{
912
	global $modSettings;
913
914
	$db = database();
915
916
	if ((isset($boardListOptions['excluded_boards']) || isset($boardListOptions['allowed_to'])) && isset($boardListOptions['included_boards']))
917
		trigger_error('getBoardList(): Setting both excluded_boards and included_boards is not allowed.', E_USER_ERROR);
918
919
	$where = array();
920
	$join = array();
921
	$select = '';
922
	$where_parameters = array();
923
924
	// Any boards to exclude
925
	if (isset($boardListOptions['excluded_boards']))
926
	{
927
		$where[] = 'b.id_board NOT IN ({array_int:excluded_boards})';
928
		$where_parameters['excluded_boards'] = $boardListOptions['excluded_boards'];
929
	}
930
931
	// Get list of boards to which they have specific permissions
932
	if (isset($boardListOptions['allowed_to']))
933
	{
934
		$boardListOptions['included_boards'] = boardsAllowedTo($boardListOptions['allowed_to']);
935
		if (in_array(0, $boardListOptions['included_boards']))
936
			unset($boardListOptions['included_boards']);
937
	}
938
939
	// Just want to include certain boards in the query
940
	if (isset($boardListOptions['included_boards']))
941
	{
942
		$where[] = 'b.id_board IN ({array_int:included_boards})';
943
		$where_parameters['included_boards'] = $boardListOptions['included_boards'];
944
	}
945
946
	// Determine if they can access a given board and return yea or nay in the results array
947
	if (isset($boardListOptions['access']))
948
	{
949
		$select .= ',
950
			FIND_IN_SET({string:current_group}, b.member_groups) != 0 AS can_access,
951
			FIND_IN_SET({string:current_group}, b.deny_member_groups) != 0 AS cannot_access';
952
		$where_parameters['current_group'] = $boardListOptions['access'];
953
	}
954
955
	// Leave out the boards that the user may be ignoring
956
	if (isset($boardListOptions['ignore']))
957
	{
958
		$select .= ',' . (!empty($boardListOptions['ignore']) ? 'b.id_board IN ({array_int:ignore_boards})' : '0') . ' AS is_ignored';
959 12
		$where_parameters['ignore_boards'] = $boardListOptions['ignore'];
960
	}
961 12
962
	// Want to check if the member is a moderators for any boards
963 12
	if (isset($boardListOptions['moderator']))
964 8
	{
965
		$join[] = '
966 12
			LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board AND mods.id_member = {int:current_member})';
967 12
		$select .= ', b.id_profile, b.member_groups, COALESCE(mods.id_member, 0) AS is_mod';
968 12
		$where_parameters['current_member'] = $boardListOptions['moderator'];
969 12
	}
970
971
	if (!empty($boardListOptions['ignore_boards']) && empty($boardListOptions['override_permissions']))
972 12
		$where[] = '{query_wanna_see_board}';
973 8
974
	elseif (empty($boardListOptions['override_permissions']))
975
		$where[] = '{query_see_board}';
976
977
	if (!empty($boardListOptions['not_redirection']))
978
	{
979 12
		$where[] = 'b.redirect = {string:blank_redirect}';
980 8
		$where_parameters['blank_redirect'] = '';
981
	}
982
983
	// Bring all the options together and make the query
984
	$request = $db->query('', '
985
		SELECT c.name AS cat_name, c.id_cat, b.id_board, b.name AS board_name, b.child_level' . $select . '
986
		FROM {db_prefix}boards AS b
987 12
			LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)' . (empty($join) ? '' : implode(' ', $join)) . (empty($where) ? '' : '
988 8
		WHERE ' . implode('
989
			AND ', $where)) . '
990
		ORDER BY c.cat_order, b.board_order',
991
		$where_parameters
992
	);
993
994 12
	// Build our output arrays, simple or complete
995 8
	if ($simple)
996
	{
997
		$return_value = array();
998
		while ($row = $db->fetch_assoc($request))
999
		{
1000
			$return_value[$row['id_board']] = array(
1001
				'id_cat' => $row['id_cat'],
1002
				'cat_name' => $row['cat_name'],
1003 12
				'id_board' => $row['id_board'],
1004 8
				'board_name' => $row['board_name'],
1005
				'child_level' => $row['child_level'],
1006
			);
1007
1008
			// Do we want access information?
1009
			if (isset($boardListOptions['access']) && $boardListOptions['access'] !== false)
1010 12
			{
1011 8
				$return_value[$row['id_board']]['allow'] = !(empty($row['can_access']) || $row['can_access'] == 'f');
1012
				$return_value[$row['id_board']]['deny'] = !(empty($row['cannot_access']) || $row['cannot_access'] == 'f');
1013
			}
1014
1015
			// Do we want moderation information?
1016 View Code Duplication
			if (!empty($boardListOptions['moderator']))
1017
			{
1018 12
				$return_value[$row['id_board']] += array(
1019 8
					'id_profile' => $row['id_profile'],
1020
					'member_groups' => $row['member_groups'],
1021 12
					'is_mod' => $row['is_mod'],
1022
				);
1023
			}
1024 12
		}
1025 8
	}
1026 12
	else
1027 12
	{
1028 8
		$return_value = array(
1029
			'num_boards' => $db->num_rows($request),
1030
			'boards_check_all' => true,
1031 12
			'boards_current_disabled' => true,
1032 12
			'categories' => array(),
1033
		);
1034 12
		while ($row = $db->fetch_assoc($request))
1035 12
		{
1036 12
			// This category hasn't been set up yet..
1037 8
			if (!isset($return_value['categories'][$row['id_cat']]))
1038 4
				$return_value['categories'][$row['id_cat']] = array(
1039 8
					'id' => $row['id_cat'],
1040
					'name' => $row['cat_name'],
1041
					'boards' => array(),
1042 4
				);
1043 8
1044 12
			// Shortcuts are useful to keep things simple
1045 12
			$this_cat = &$return_value['categories'][$row['id_cat']];
1046
1047 12
			$this_cat['boards'][$row['id_board']] = array(
1048 12
				'id' => $row['id_board'],
1049 12
				'name' => $row['board_name'],
1050 12
				'child_level' => $row['child_level'],
1051 12
				'allow' => false,
1052 12
				'deny' => false,
1053
				'selected' => isset($boardListOptions['selected_board']) && $boardListOptions['selected_board'] == $row['id_board'],
1054
			);
1055
			// Do we want access information?
1056 12
1057 8
			if (!empty($boardListOptions['access']))
1058
			{
1059
				$this_cat['boards'][$row['id_board']]['allow'] = !(empty($row['can_access']) || $row['can_access'] == 'f');
1060
				$this_cat['boards'][$row['id_board']]['deny'] = !(empty($row['cannot_access']) || $row['cannot_access'] == 'f');
1061
			}
1062
1063 12
			// If is_ignored is set, it means we could have to deselect a board
1064 8
			if (isset($row['is_ignored']))
1065
			{
1066
				$this_cat['boards'][$row['id_board']]['selected'] = $row['is_ignored'];
1067
1068
				// If a board wasn't checked that probably should have been ensure the board selection is selected, yo!
1069
				if (!empty($this_cat['boards'][$row['id_board']]['selected']) && (empty($modSettings['recycle_enable']) || $row['id_board'] != $modSettings['recycle_board']))
1070
					$return_value['boards_check_all'] = false;
1071 8
			}
1072 8
1073
			// Do we want moderation information?
1074 View Code Duplication
			if (!empty($boardListOptions['moderator']))
1075
			{
1076
				$this_cat['boards'][$row['id_board']] += array(
1077
					'id_profile' => $row['id_profile'],
1078
					'member_groups' => $row['member_groups'],
1079
					'is_mod' => $row['is_mod'],
1080
				);
1081
			}
1082
		}
1083
	}
1084
1085
	$db->free_result($request);
1086
1087
	return $return_value;
1088
}
1089
1090
/**
1091
 * Recursively get a list of boards.
1092
 *
1093
 * - Used by getBoardTree
1094
 *
1095
 * @package Boards
1096
 * @param int[] $_boardList The board list
1097
 * @param array $_tree the board tree
1098
 */
1099
function recursiveBoards(&$_boardList, &$_tree)
1100
{
1101
	if (empty($_tree['children']))
1102
		return;
1103
1104
	foreach ($_tree['children'] as $id => $node)
1105
	{
1106
		$_boardList[] = $id;
1107
		recursiveBoards($_boardList, $node);
1108
	}
1109
}
1110
1111
/**
1112
 * Returns whether the sub-board id is actually a child of the parent (recursive).
1113
 *
1114
 * @package Boards
1115
 * @param int $child The ID of the child board
1116
 * @param int $parent The ID of a parent board
1117
 *
1118
 * @return boolean if the specified child board is a child of the specified parent board.
1119
 */
1120
function isChildOf($child, $parent)
1121
{
1122
	global $boards;
1123
1124
	if (empty($boards[$child]['parent']))
1125
		return false;
1126
1127
	if ($boards[$child]['parent'] == $parent)
1128
		return true;
1129
1130
	return isChildOf($boards[$child]['parent'], $parent);
1131
}
1132 12
1133
/**
1134 12
 * Returns whether this member has notification turned on for the specified board.
1135
 *
1136
 * @param int $id_member the member id
1137
 * @param int $id_board the board to check
1138
 * @return bool if they have notifications turned on for the board
1139
 */
1140
function hasBoardNotification($id_member, $id_board)
1141
{
1142
	$db = database();
1143
1144
	// Find out if they have notification set for this board already.
1145
	$request = $db->query('', '
1146
		SELECT id_member
1147
		FROM {db_prefix}log_notify
1148
		WHERE id_member = {int:current_member}
1149
			AND id_board = {int:current_board}
1150
		LIMIT 1',
1151
		array(
1152
			'current_board' => $id_board,
1153
			'current_member' => $id_member,
1154
		)
1155
	);
1156
	$hasNotification = $db->num_rows($request) != 0;
1157
	$db->free_result($request);
1158
1159
	return $hasNotification;
1160
}
1161
1162
/**
1163
 * Set board notification on or off for the given member.
1164
 *
1165
 * @package Boards
1166
 * @param int $id_member
1167
 * @param int $id_board
1168
 * @param bool $on = false
1169
 */
1170 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...
1171
{
1172
	$db = database();
1173
1174
	if ($on)
1175
	{
1176
		// Turn notification on.  (note this just blows smoke if it's already on.)
1177
		$db->insert('ignore',
1178
			'{db_prefix}log_notify',
1179
			array('id_member' => 'int', 'id_board' => 'int'),
1180
			array($id_member, $id_board),
1181
			array('id_member', 'id_board')
1182
		);
1183
	}
1184
	else
1185
	{
1186
		// Turn notification off for this board.
1187
		$db->query('', '
1188
			DELETE FROM {db_prefix}log_notify
1189
			WHERE id_member = {int:current_member}
1190
				AND id_board = {int:current_board}',
1191
			array(
1192
				'current_board' => $id_board,
1193
				'current_member' => $id_member,
1194
			)
1195
		);
1196
	}
1197
}
1198
1199
/**
1200
 * Reset sent status for board notifications.
1201
 *
1202
 * This function returns a boolean equivalent with hasBoardNotification().
1203
 * This is unexpected, but it's done this way to avoid any extra-query is executed on MessageIndex::action_messageindex().
1204
 * Just ignore the return value for normal use.
1205
 *
1206
 * @package Boards
1207
 * @param int $id_member
1208
 * @param int $id_board
1209
 * @param bool $check = true check if the user has notifications enabled for the board
1210
 * @return bool if the board was marked for notifications
1211
 */
1212
function resetSentBoardNotification($id_member, $id_board, $check = true)
1213
{
1214
	$db = database();
1215
1216
	// Check if notifications are enabled for this user on the board?
1217
	if ($check)
1218
	{
1219
		// check if the member has notifications enabled for this board
1220
		$request = $db->query('', '
1221
			SELECT sent
1222
			FROM {db_prefix}log_notify
1223
			WHERE id_board = {int:current_board}
1224
				AND id_member = {int:current_member}
1225
			LIMIT 1',
1226
			array(
1227
				'current_board' => $id_board,
1228
				'current_member' => $id_member,
1229
			)
1230
		);
1231
		// nothing to do
1232
		if ($db->num_rows($request) == 0)
1233
			return false;
1234
		$sent = $db->fetch_row($request);
1235
		$db->free_result($request);
1236
1237
		// not sent already? No need to stay around then
1238
		if (empty($sent))
1239
			return true;
1240
	}
1241
1242
	// Reset 'sent' status.
1243
	$db->query('', '
1244
		UPDATE {db_prefix}log_notify
1245
		SET sent = {int:is_sent}
1246
		WHERE id_board = {int:current_board}
1247
			AND id_member = {int:current_member}',
1248
		array(
1249
			'current_board' => $id_board,
1250
			'current_member' => $id_member,
1251
			'is_sent' => 0,
1252
		)
1253
	);
1254
	return true;
1255
}
1256
1257
/**
1258
 * Counts the board notification for a given member.
1259
 *
1260
 * @package Boards
1261 6
 * @param int $memID
1262
 * @return int
1263
 */
1264 2
function getBoardNotificationsCount($memID)
1265 4
{
1266
	global $user_info;
1267 6
1268
	$db = database();
1269
1270
	// All the boards that you have notification enabled
1271
	$request = $db->query('', '
1272 4
		SELECT COUNT(*)
1273
		FROM {db_prefix}log_notify AS ln
1274 6
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = ln.id_board)
1275 6
			LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member})
1276
		WHERE ln.id_member = {int:selected_member}
1277 4
			AND {query_see_board}',
1278
		array(
1279 6
			'current_member' => $user_info['id'],
1280 6
			'selected_member' => $memID,
1281
		)
1282
	);
1283
	list ($totalNotifications) = $db->fetch_row($request);
1284
	$db->free_result($request);
1285
1286
	return $totalNotifications;
1287
}
1288
1289
/**
1290
 * Returns all the boards accessible to the current user.
1291
 *
1292
 * - If $id_parents is given, return only the sub-boards of those boards.
1293
 * - If $id_boards is given, filters the boards to only those accessible.
1294
 * - The function doesn't guarantee the boards are properly sorted
1295
 *
1296
 * @package Boards
1297
 * @param int[]|null $id_parents array of ints representing board ids
1298
 * @param int[]|null $id_boards
1299
 */
1300
function accessibleBoards($id_boards = null, $id_parents = null)
1301
{
1302
	$db = database();
1303
1304
	$boards = array();
1305
	if (!empty($id_parents))
1306
	{
1307
		// Find all boards down from $id_parent
1308
		$request = $db->query('', '
1309
			SELECT b.id_board
1310
			FROM {db_prefix}boards AS b
1311
			WHERE b.id_parent IN ({array_int:parent_list})
1312
				AND {query_see_board}',
1313
			array(
1314
				'parent_list' => $id_parents,
1315
			)
1316
		);
1317
	}
1318
	elseif (!empty($id_boards))
1319
	{
1320
		// Find all the boards this user can see between those selected
1321
		$request = $db->query('', '
1322
			SELECT b.id_board
1323
			FROM {db_prefix}boards AS b
1324
			WHERE b.id_board IN ({array_int:board_list})
1325
				AND {query_see_board}',
1326
			array(
1327
				'board_list' => $id_boards,
1328
			)
1329
		);
1330
	}
1331
	else
1332
	{
1333
		// Find all the boards this user can see.
1334
		$request = $db->query('', '
1335
			SELECT b.id_board
1336
			FROM {db_prefix}boards AS b
1337
			WHERE {query_see_board}',
1338
			array(
1339
			)
1340
		);
1341
	}
1342
1343
	while ($row = $db->fetch_assoc($request))
1344
		$boards[] = $row['id_board'];
1345
	$db->free_result($request);
1346
1347
	return $boards;
1348
}
1349
1350
/**
1351
 * Returns the boards the current user wants to see.
1352
 *
1353
 * @package Boards
1354
 * @param string $see_board either 'query_see_board' or 'query_wanna_see_board'
1355
 * @param bool $hide_recycle is tru the recycle bin is not returned
1356
 */
1357
function wantedBoards($see_board, $hide_recycle = true)
1358
{
1359
	global $modSettings, $user_info;
1360
1361
	$db = database();
1362
	$allowed_see = array(
1363
		'query_see_board',
1364
		'query_wanna_see_board'
1365
	);
1366
1367
	// Find all boards down from $id_parent
1368
	return $db->fetchQueryCallback('
1369
		SELECT b.id_board
1370
		FROM {db_prefix}boards AS b
1371
		WHERE ' . $user_info[in_array($see_board, $allowed_see) ? $see_board : $allowed_see[0]] . ($hide_recycle && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
1372
			AND b.id_board != {int:recycle_board}' : ''),
1373
		array(
1374
			'recycle_board' => (int) $modSettings['recycle_board'],
1375
		),
1376
		function ($row)
1377
		{
1378
			return $row['id_board'];
1379
		}
1380
	);
1381
}
1382
1383
/**
1384
 * Returns the post count and name of a board
1385
 *
1386
 * - if supplied a topic id will also return the message subject
1387
 * - honors query_see_board to ensure a user can see the information
1388
 *
1389
 * @package Boards
1390
 * @param int $board_id
1391
 * @param int|null $topic_id
1392
 */
1393 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...
1394
{
1395
	$db = database();
1396
1397
	if (!empty($topic_id))
1398
	{
1399
		$request = $db->query('', '
1400
			SELECT b.count_posts, b.name, m.subject
1401
			FROM {db_prefix}boards AS b
1402
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
1403
				INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
1404
			WHERE {query_see_board}
1405
				AND b.id_board = {int:board}
1406
				AND b.redirect = {string:blank_redirect}
1407
			LIMIT 1',
1408
			array(
1409
				'current_topic' => $topic_id,
1410
				'board' => $board_id,
1411
				'blank_redirect' => '',
1412
			)
1413
		);
1414
	}
1415
	else
1416
	{
1417
		$request = $db->query('', '
1418
			SELECT b.count_posts, b.name
1419
			FROM {db_prefix}boards AS b
1420
			WHERE {query_see_board}
1421
				AND b.id_board = {int:board}
1422
				AND b.redirect = {string:blank_redirect}
1423
			LIMIT 1',
1424
			array(
1425
				'board' => $board_id,
1426
				'blank_redirect' => '',
1427
			)
1428
		);
1429
	}
1430
1431
	$returns = $db->fetch_assoc($request);
1432
	$db->free_result($request);
1433
1434
	return $returns;
1435
}
1436
1437
/**
1438
 * Loads properties from non-standard groups
1439
 *
1440
 * @package Boards
1441
 * @param int $curBoard
1442 6
 * @param boolean $new_board = false Whether this is a new board
1443
 * @return array
1444 6
 */
1445 4
function getOtherGroups($curBoard, $new_board = false)
1446
{
1447
	$db = database();
1448
1449
	$groups = array();
1450
1451
	// Load membergroups.
1452
	$request = $db->query('', '
1453
		SELECT group_name, id_group, min_posts
1454
		FROM {db_prefix}membergroups
1455
		WHERE id_group > {int:moderator_group} OR id_group = {int:global_moderator}
1456
		ORDER BY min_posts, id_group != {int:global_moderator}, group_name',
1457
		array(
1458
			'moderator_group' => 3,
1459
			'global_moderator' => 2,
1460
		)
1461
	);
1462
1463
	while ($row = $db->fetch_assoc($request))
1464 6
	{
1465
		if ($new_board && $row['min_posts'] == -1)
1466
			$curBoard['member_groups'][] = $row['id_group'];
1467
1468
		$groups[(int) $row['id_group']] = array(
1469
			'id' => $row['id_group'],
1470 4
			'name' => trim($row['group_name']),
1471
			'allow' => in_array($row['id_group'], $curBoard['member_groups']),
1472 6
			'deny' => in_array($row['id_group'], $curBoard['deny_groups']),
1473 6
			'is_post_group' => $row['min_posts'] != -1,
1474
		);
1475 4
		}
1476
	$db->free_result($request);
1477
1478 6
	return $groups;
1479 6
}
1480
1481 6
/**
1482
 * Get a list of moderators from a specific board
1483
 *
1484
 * @package Boards
1485
 * @param int $idboard
1486
 * @param bool $only_id return only the id of the moderators instead of id and name (default false)
1487
 * @return array
1488
 */
1489
function getBoardModerators($idboard, $only_id = false)
1490
{
1491
	$db = database();
1492
1493
	$moderators = array();
1494
1495
	if ($only_id)
1496
	{
1497
		$request = $db->query('', '
1498
			SELECT id_member
1499
			FROM {db_prefix}moderators
1500
			WHERE id_board = {int:current_board}',
1501
			array(
1502
				'current_board' => $idboard,
1503
			)
1504
		);
1505
		while ($row = $db->fetch_assoc($request))
1506
			$moderators[] = $row['id_member'];
1507
	}
1508
	else
1509
	{
1510
		$request = $db->query('', '
1511
			SELECT mem.id_member, mem.real_name
1512
			FROM {db_prefix}moderators AS mods
1513
				INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
1514
			WHERE mods.id_board = {int:current_board}',
1515
			array(
1516
				'current_board' => $idboard,
1517
			)
1518
		);
1519
1520
		while ($row = $db->fetch_assoc($request))
1521
			$moderators[$row['id_member']] = $row['real_name'];
1522
	}
1523
	$db->free_result($request);
1524
1525
	return $moderators;
1526
}
1527
1528
/**
1529
 * Get a list of all the board moderators (every board)
1530
 *
1531
 * @package Boards
1532
 * @param bool $only_id return array with key of id_member of the moderator(s)
1533
 * otherwise array with key of id_board id (default false)
1534
 * @return array
1535
 */
1536
function allBoardModerators($only_id = false)
1537
{
1538
	$db = database();
1539
1540
	$moderators = array();
1541
1542
	if ($only_id)
1543
		$request = $db->query('', '
1544
			SELECT id_board, id_member
1545
			FROM {db_prefix}moderators',
1546
			array(
1547
			)
1548
		);
1549
	else
1550
		$request = $db->query('', '
1551
			SELECT mods.id_board, mods.id_member, mem.real_name
1552
			FROM {db_prefix}moderators AS mods
1553
				INNER JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)',
1554
			array(
1555
			)
1556
		);
1557
1558
	while ($row = $db->fetch_assoc($request))
1559
	{
1560
		if ($only_id)
1561
			$moderators[$row['id_member']][] = $row;
1562
		else
1563
			$moderators[$row['id_board']][] = $row;
1564
	}
1565
	$db->free_result($request);
1566
1567
	return $moderators;
1568
}
1569
1570
/**
1571
 * Get a list of all the board moderated by a certain user
1572
 *
1573
 * @package Boards
1574
 * @param int $id_member the id of a member
1575
 * @return array
1576
 */
1577
function boardsModerated($id_member)
1578
{
1579
	$db = database();
1580
1581
	return $db->fetchQueryCallback('
1582
		SELECT id_board
1583
		FROM {db_prefix}moderators
1584
		WHERE id_member = {int:current_member}',
1585
		array(
1586
			'current_member' => $id_member,
1587
		),
1588
		function ($row)
1589
		{
1590
			return $row['id_board'];
1591
		}
1592
	);
1593
}
1594
1595
/**
1596
 * Get all available themes
1597
 *
1598
 * @package Boards
1599
 * @return array
1600
 */
1601
function getAllThemes()
1602
{
1603
	$db = database();
1604
1605
	// Get all the themes...
1606
	return $db->fetchQuery('
1607
		SELECT id_theme AS id, value AS name
1608
		FROM {db_prefix}themes
1609
		WHERE variable = {string:name}',
1610
		array(
1611
			'name' => 'name',
1612
		)
1613
	);
1614
}
1615
1616
/**
1617
 * Gets redirect infos and post count from a selected board.
1618
 *
1619
 * @package Boards
1620
 * @param int $idboard
1621
 * @return array
1622
 */
1623
function getBoardProperties($idboard)
1624
{
1625
	$db = database();
1626
1627
	$properties = array();
1628
1629
	$request = $db->query('', '
1630
		SELECT redirect, num_posts
1631
		FROM {db_prefix}boards
1632
		WHERE id_board = {int:current_board}',
1633
		array(
1634
			'current_board' => $idboard,
1635
		)
1636
	);
1637
	list ($properties['oldRedirect'], $properties['numPosts']) = $db->fetch_row($request);
1638
	$db->free_result($request);
1639
1640
	return $properties;
1641
}
1642
1643
/**
1644
 * Fetch the number of posts in an array of boards based on board IDs or category IDs
1645
 *
1646
 * @package Boards
1647
 * @param int[]|null $boards an array of board IDs
1648
 * @param int[]|null $categories an array of category IDs
1649
 * @param bool $wanna_see_board if true uses {query_wanna_see_board}, otherwise {query_see_board}
1650
 * @param bool $include_recycle if false excludes any results from the recycle board (if enabled)
1651
 */
1652
function boardsPosts($boards, $categories, $wanna_see_board = false, $include_recycle = true)
1653
{
1654
	global $modSettings;
1655
1656
	$db = database();
1657
1658
	$clauses = array();
1659
	$removals = array();
1660
	$clauseParameters = array();
1661
1662
	if (!empty($categories))
1663
	{
1664
		$clauses[] = 'id_cat IN ({array_int:category_list})';
1665
		$clauseParameters['category_list'] = $categories;
1666
	}
1667
1668
	if (!empty($boards))
1669
	{
1670
		$clauses[] = 'id_board IN ({array_int:board_list})';
1671
		$clauseParameters['board_list'] = $boards;
1672
	}
1673
1674
	if (empty($include_recycle) && (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0))
1675
	{
1676
		$removals[] = 'id_board != {int:recycle_board}';
1677
		$clauseParameters['recycle_board'] = (int) $modSettings['recycle_board'];
1678
	}
1679
1680
	if (empty($clauses))
1681
		return array();
1682
1683
	$request = $db->query('', '
1684
		SELECT b.id_board, b.num_posts
1685
		FROM {db_prefix}boards AS b
1686
		WHERE ' . ($wanna_see_board ? '{query_wanna_see_board}' : '{query_see_board}') . '
1687
			AND b.' . implode(' OR b.', $clauses) . (!empty($removals) ? '
1688
			AND b.' . implode(' AND b.', $removals) : ''),
1689
		$clauseParameters
1690
	);
1691
	$return = array();
1692
	while ($row = $db->fetch_assoc($request))
1693
		$return[$row['id_board']] = $row['num_posts'];
1694
	$db->free_result($request);
1695
1696
	return $return;
1697
}
1698
1699
/**
1700
 * Returns the total sum of posts in the boards defined by query_wanna_see_board
1701 6
 * Excludes the count of any boards defined as a recycle board from the sum
1702
 */
1703 6 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...
1704
{
1705 6
	$db = database();
1706 6
1707 6
	global $modSettings;
1708
1709 6
	$request = $db->query('', '
1710 4
		SELECT COALESCE(SUM(num_posts), 0)
1711
		FROM {db_prefix}boards as b
1712
		WHERE {query_wanna_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
1713
			AND b.id_board != {int:recycle_board}' : ''),
1714
		array(
1715 6
			'recycle_board' => $modSettings['recycle_board']
1716 4
		)
1717 6
	);
1718 6
	list ($result) = $db->fetch_row($request);
1719 4
	$db->free_result($request);
1720
1721 6
	return $result;
1722 4
}
1723
1724
/**
1725
 * Returns information of a set of boards based on board IDs or category IDs
1726
 *
1727 6
 * @package Boards
1728 4
 * @param mixed[]|string $conditions is an associative array that holds the board or the cat IDs
1729
 *              'categories' => an array of category IDs (it accepts a single ID too)
1730 6
 *              'boards' => an array of board IDs (it accepts a single ID too)
1731
 *              if conditions is set to 'all' (not an array) all the boards are queried
1732
 * @param mixed[] $params is an optional array that allows to control the results returned:
1733 6
 *              'sort_by' => (string) defines the sorting of the results (allowed: id_board, name)
1734 6
 *              'selects' => (string) determines what information are retrieved and returned
1735 6
 *                           Allowed values: 'name', 'posts', 'detailed', 'permissions', 'reports';
1736 2
 *                           default: 'name';
1737 4
 *                           see the function for details on the fields associated to each value
1738 6
 *              'override_permissions' => (bool) if true doesn't use neither {query_wanna_see_board} nor {query_see_board} (default false)
1739 6
 *              'wanna_see_board' => (bool) if true uses {query_wanna_see_board}, otherwise {query_see_board}
1740 6
 *              'include_recycle' => (bool) recycle board is included (default true)
1741 6
 *              'include_redirects' => (bool) redirects are included (default true)
1742
 *
1743 6
 * @todo unify the two queries?
1744
 */
1745
function fetchBoardsInfo($conditions = 'all', $params = array())
1746
{
1747
	global $modSettings;
1748
1749
	$db = database();
1750
1751
	// Ensure default values are set
1752 6
	$params = array_merge(array('override_permissions' => false, 'wanna_see_board' => false, 'include_recycle' => true, 'include_redirects' => true), $params);
1753
1754 6
	$clauses = array();
1755
	$clauseParameters = array();
1756 6
	$allowed_sort = array(
1757
		'id_board',
1758
		'name'
1759 6
	);
1760 6
1761
	if (!empty($params['sort_by']) && in_array($params['sort_by'], $allowed_sort))
1762 6
		$sort_by = 'ORDER BY ' . $params['sort_by'];
1763 4
	else
1764 4
		$sort_by = '';
1765 6
1766 6
	// @todo: memos for optimization
1767
	/*
1768 6
		id_board    => MergeTopic + MergeTopic + MessageIndex + Search + ScheduledTasks
1769
		name        => MergeTopic + ScheduledTasks + News
1770
		count_posts => MessageIndex
1771
		num_posts   => News
1772
	*/
1773
	$known_selects = array(
1774
		'name' => 'b.id_board, b.name',
1775
		'posts' => 'b.id_board, b.count_posts, b.num_posts',
1776
		'detailed' => 'b.id_board, b.name, b.count_posts, b.num_posts',
1777
		'permissions' => 'b.id_board, b.name, b.member_groups, b.id_profile',
1778
		'reports' => 'b.id_board, b.name, b.member_groups, b.id_profile, b.deny_member_groups',
1779
	);
1780
1781
	$select = $known_selects[empty($params['selects']) || !isset($known_selects[$params['selects']]) ? 'name' : $params['selects']];
1782
1783
	// If $conditions wasn't set or is 'all', get all boards
1784
	if (!is_array($conditions) && $conditions == 'all')
1785
	{
1786
		// id_board, name, id_profile => used in admin/Reports.controller.php
1787
		$request = $db->query('', '
1788
			SELECT ' . $select . '
1789
			FROM {db_prefix}boards AS b
1790
			' . $sort_by,
1791
			array()
1792
		);
1793
	}
1794
	else
1795
	{
1796
		// Only some categories?
1797 View Code Duplication
		if (!empty($conditions['categories']))
1798
		{
1799
			$clauses[] = 'id_cat IN ({array_int:category_list})';
1800
			$clauseParameters['category_list'] = is_array($conditions['categories']) ? $conditions['categories'] : array($conditions['categories']);
1801
		}
1802
1803
		// Only a few boards, perhaps!
1804 View Code Duplication
		if (!empty($conditions['boards']))
1805
		{
1806
			$clauses[] = 'id_board IN ({array_int:board_list})';
1807
			$clauseParameters['board_list'] = is_array($conditions['boards']) ? $conditions['boards'] : array($conditions['boards']);
1808
		}
1809
1810
		if ($params['override_permissions'])
1811
			$security = '1=1';
1812
		else
1813
			$security = $params['wanna_see_board'] ? '{query_wanna_see_board}' : '{query_see_board}';
1814
1815
		$request = $db->query('', '
1816
			SELECT ' . $select . '
1817
			FROM {db_prefix}boards AS b
1818
			WHERE ' . $security . (!empty($clauses) ? '
1819
				AND b.' . implode(' OR b.', $clauses) : '') . ($params['include_recycle'] ? '' : '
1820
				AND b.id_board != {int:recycle_board}') . ($params['include_redirects'] ? '' : '
1821
				AND b.redirect = {string:empty_string}
1822
			' . $sort_by),
1823
			array_merge($clauseParameters, array(
1824
				'recycle_board' => !empty($modSettings['recycle_board']) ? $modSettings['recycle_board'] : 0,
1825
				'empty_string' => '',
1826
			))
1827
		);
1828
	}
1829
	$return = array();
1830
	while ($row = $db->fetch_assoc($request))
1831
		$return[$row['id_board']] = $row;
1832
1833
	$db->free_result($request);
1834
1835
	return $return;
1836
}
1837
1838
/**
1839
 * Retrieve the all the sub-boards of an array of boards and add the ids to the same array
1840
 *
1841
 * @package Boards
1842
 * @param int[]|int $boards an array of board IDs (it accepts a single board too).
1843
 * NOTE: the $boards param is deprecated since 1.1 - The param is passed by ref in 1.0 and the result
1844
 * is returned through the param itself, starting from 1.1 the expected behaviour
1845
 * is that the result is returned.
1846
 * @return bool|int[]
1847
 */
1848
function addChildBoards($boards)
1849
{
1850
	$db = database();
1851
1852
	if (empty($boards))
1853
	{
1854
		return false;
1855
	}
1856
1857
	if (!is_array($boards))
1858
	{
1859
		$boards = array($boards);
1860
	}
1861
1862
	$request = $db->query('', '
1863
		SELECT 
1864
			b.id_board, b.id_parent
1865
		FROM {db_prefix}boards AS b
1866
		WHERE {query_see_board}
1867
			AND b.child_level > {int:no_parents}
1868
			AND b.id_board NOT IN ({array_int:board_list})
1869
		ORDER BY child_level ASC
1870
		',
1871
		array(
1872
			'no_parents' => 0,
1873
			'board_list' => $boards,
1874
		)
1875
	);
1876
	while ($row = $db->fetch_assoc($request))
1877
	{
1878
		if (in_array($row['id_parent'], $boards))
1879
		{
1880
			$boards[] = $row['id_board'];
1881
		}
1882
	}
1883
	$db->free_result($request);
1884
1885
	return $boards;
1886
}
1887
1888
/**
1889
 * Increment a board stat field, for example num_posts.
1890
 *
1891
 * @package Boards
1892
 * @param int $id_board
1893
 * @param mixed[]|string $values an array of index => value of a string representing the index to increment
1894
 */
1895
function incrementBoard($id_board, $values)
1896
{
1897
	$db = database();
1898
1899
	$knownInts = array(
1900
		'child_level', 'board_order', 'num_topics', 'num_posts', 'count_posts',
1901
		'unapproved_posts', 'unapproved_topics'
1902
	);
1903
1904
	call_integration_hook('integrate_board_fields', array(&$knownInts));
1905
1906
	$set = array();
1907
	$params = array('id_board' => $id_board);
1908
	$values = is_array($values) ? $values : array($values => 1);
1909
1910
	foreach ($values as $key => $val)
1911
	{
1912
		if (in_array($key, $knownInts))
1913
		{
1914
			$set[] = $key . ' = ' . $key . ' + {int:' . $key . '}';
1915
			$params[$key] = $val;
1916
		}
1917
	}
1918
1919
	if (empty($set))
1920
		return;
1921
1922
	$db->query('', '
1923
		UPDATE {db_prefix}boards
1924
		SET
1925
			' . implode(',
1926
			', $set) . '
1927
		WHERE id_board = {int:id_board}',
1928
		$params
1929
	);
1930
}
1931
1932
/**
1933
 * Decrement a board stat field, for example num_posts.
1934
 *
1935
 * @package Boards
1936
 * @param int $id_board
1937
 * @param mixed[]|string $values an array of index => value of a string representing the index to decrement
1938
 */
1939
function decrementBoard($id_board, $values)
1940
{
1941
	$db = database();
1942
1943
	$knownInts = array(
1944
		'child_level', 'board_order', 'num_topics', 'num_posts', 'count_posts',
1945
		'unapproved_posts', 'unapproved_topics'
1946
	);
1947
1948
	call_integration_hook('integrate_board_fields', array(&$knownInts));
1949
1950
	$set = array();
1951
	$params = array('id_board' => $id_board);
1952
	$values = is_array($values) ? $values : array($values => 1);
1953
1954
	foreach ($values as $key => $val)
1955
	{
1956
		if (in_array($key, $knownInts))
1957
		{
1958
			$set[] = $key . ' = CASE WHEN {int:' . $key . '} > ' . $key . ' THEN 0 ELSE ' . $key . ' - {int:' . $key . '} END';
1959
			$params[$key] = $val;
1960
		}
1961
	}
1962
1963
	if (empty($set))
1964
		return;
1965
1966
	$db->query('', '
1967
		UPDATE {db_prefix}boards
1968
		SET
1969
			' . implode(',
1970
			', $set) . '
1971
		WHERE id_board = {int:id_board}',
1972
		$params
1973
	);
1974
}
1975
1976
/**
1977
 * Retrieve all the boards the user can see and their notification status:
1978
 *
1979
 * - if they're subscribed to notifications for new topics in each of them
1980
 * or they're not.
1981
 * - (used by createList() callbacks)
1982
 *
1983
 * @package Boards
1984
 * @param int $start The item to start with (for pagination purposes)
1985
 * @param int $items_per_page  The number of items to show per page
1986
 * @param string $sort A string indicating how to sort the results
1987
 * @param int $memID id_member
1988
 * @return array
1989
 */
1990
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...
1991
{
1992
	global $scripturl, $user_info, $modSettings;
1993
1994
	$db = database();
1995
1996
	// All the boards that you have notification enabled
1997
	$notification_boards = $db->fetchQueryCallback('
1998
		SELECT b.id_board, b.name, COALESCE(lb.id_msg, 0) AS board_read, b.id_msg_updated
1999
		FROM {db_prefix}log_notify AS ln
2000
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = ln.id_board)
2001
			LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member})
2002
		WHERE ln.id_member = {int:selected_member}
2003
			AND {query_see_board}
2004
		ORDER BY ' . $sort,
2005
		array(
2006
			'current_member' => $user_info['id'],
2007
			'selected_member' => $memID,
2008
		),
2009
		function ($row) use ($scripturl)
2010
		{
2011
			return array(
2012
				'id' => $row['id_board'],
2013
				'name' => $row['name'],
2014
				'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
2015
				'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0"><strong>' . $row['name'] . '</strong></a>',
2016
				'new' => $row['board_read'] < $row['id_msg_updated'],
2017
				'checked' => 'checked="checked"',
2018
			);
2019
		}
2020
	);
2021
2022
	// and all the boards that you can see but don't have notify turned on for
2023
	$request = $db->query('', '
2024
		SELECT b.id_board, b.name, COALESCE(lb.id_msg, 0) AS board_read, b.id_msg_updated
2025
		FROM {db_prefix}boards AS b
2026
			LEFT JOIN {db_prefix}log_notify AS ln ON (ln.id_board = b.id_board AND ln.id_member = {int:selected_member})
2027
			LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member})
2028
		WHERE {query_see_board}
2029
			AND ln.id_board is null ' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
2030
			AND b.id_board != {int:recycle_board}' : '') . '
2031
		ORDER BY ' . $sort,
2032
		array(
2033
			'selected_member' => $memID,
2034
			'current_member' => $user_info['id'],
2035
			'recycle_board' => $modSettings['recycle_board'],
2036
		)
2037
	);
2038
	while ($row = $db->fetch_assoc($request))
2039
		$notification_boards[] = array(
2040
			'id' => $row['id_board'],
2041
			'name' => $row['name'],
2042
			'href' => $scripturl . '?board=' . $row['id_board'] . '.0',
2043
			'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['name'] . '</a>',
2044
			'new' => $row['board_read'] < $row['id_msg_updated'],
2045
			'checked' => '',
2046
		);
2047
	$db->free_result($request);
2048
2049
	return $notification_boards;
2050
}
2051
2052
/**
2053
 * Count boards all or specific depending on argument, redirect boards excluded by default.
2054
 *
2055
 * @package Boards
2056
 * @param mixed[]|string $conditions is an associative array that holds the board or the cat IDs
2057
 *              'categories' => an array of category IDs (it accepts a single ID too)
2058
 *              'boards' => an array of board IDs (it accepts a single ID too)
2059
 *              if conditions is set to 'all' (not an array) all the boards are queried
2060
 * @param mixed[]|null $params is an optional array that allows to control the results returned if $conditions is not set to 'all':
2061
 *              'wanna_see_board' => (bool) if true uses {query_wanna_see_board}, otherwise {query_see_board}
2062
 *              'include_recycle' => (bool) recycle board is included (default true)
2063
 *              'include_redirects' => (bool) redirects are included (default true)
2064
 * @return int
2065
 */
2066
function countBoards($conditions = 'all', $params = array())
2067
{
2068
	global $modSettings;
2069
2070
	$db = database();
2071
2072
	// Ensure default values are set
2073
	$params = array_merge(array('wanna_see_board' => false, 'include_recycle' => true, 'include_redirects' => true), $params);
2074
2075
	$clauses = array();
2076
	$clauseParameters = array();
2077
2078
	// if $conditions wasn't set or is 'all', get all boards
2079
	if (!is_array($conditions) && $conditions == 'all')
2080
	{
2081
		// id_board, name, id_profile => used in admin/Reports.controller.php
2082
		$request = $db->query('', '
2083
			SELECT COUNT(*)
2084
			FROM {db_prefix}boards AS b',
2085
			array()
2086
		);
2087
	}
2088
	else
2089
	{
2090
		// only some categories?
2091 View Code Duplication
		if (!empty($conditions['categories']))
2092
		{
2093
			$clauses[] = 'id_cat IN ({array_int:category_list})';
2094
			$clauseParameters['category_list'] = is_array($conditions['categories']) ? $conditions['categories'] : array($conditions['categories']);
2095
		}
2096
2097
		// only a few boards, perhaps!
2098 View Code Duplication
		if (!empty($conditions['boards']))
2099
		{
2100
			$clauses[] = 'id_board IN ({array_int:board_list})';
2101
			$clauseParameters['board_list'] = is_array($conditions['boards']) ? $conditions['boards'] : array($conditions['boards']);
2102
		}
2103
2104
		$request = $db->query('', '
2105
			SELECT COUNT(*)
2106
			FROM {db_prefix}boards AS b
2107
			WHERE ' . ($params['wanna_see_board'] ? '{query_wanna_see_board}' : '{query_see_board}') . (!empty($clauses) ? '
2108
				AND b.' . implode(' OR b.', $clauses) : '') . ($params['include_recycle'] ? '' : '
2109
				AND b.id_board != {int:recycle_board}') . ($params['include_redirects'] ? '' : '
2110
				AND b.redirect = {string:empty_string}'),
2111
			array_merge($clauseParameters, array(
2112
				'recycle_board' => !empty($modSettings['recycle_board']) ? $modSettings['recycle_board'] : 0,
2113
				'empty_string' => '',
2114
			))
2115
		);
2116
	}
2117
2118
	list ($num_boards) = $db->fetch_row($request);
2119
	$db->free_result($request);
2120
2121
	return $num_boards;
2122
}
2123