getBoardList()   F
last analyzed

Complexity

Conditions 36
Paths > 20000

Size

Total Lines 191
Code Lines 92

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 40
CRAP Score 222.2522

Importance

Changes 0
Metric Value
cc 36
eloc 92
nc 124416
nop 2
dl 0
loc 191
ccs 40
cts 84
cp 0.4762
crap 222.2522
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1889
	$params = array_merge(array('wanna_see_board' => false, 'include_recycle' => true, 'include_redirects' => true), /** @scrutinizer ignore-type */ $params);
Loading history...
1890
1891
	$clauses = array();
1892
	$clauseParameters = array();
1893
1894
	// if $conditions wasn't set or is 'all', get all boards
1895
	if (!is_array($conditions) && $conditions === 'all')
1896
	{
1897 2
		// id_board, name, id_profile => used in admin/Reports.controller.php
1898
		$request = $db->query('', '
1899 2
			SELECT 
1900
				COUNT(*)
1901
			FROM {db_prefix}boards AS b',
1902 2
			array()
1903
		);
1904 2
	}
1905 2
	else
1906
	{
1907
		// only some categories?
1908 2
		if (!empty($conditions['categories']))
1909
		{
1910
			$clauses[] = 'id_cat IN ({array_int:category_list})';
1911 2
			$clauseParameters['category_list'] = is_array($conditions['categories']) ? $conditions['categories'] : array($conditions['categories']);
1912
		}
1913
1914
		// only a few boards, perhaps!
1915 2
		if (!empty($conditions['boards']))
1916
		{
1917
			$clauses[] = 'id_board IN ({array_int:board_list})';
1918
			$clauseParameters['board_list'] = is_array($conditions['boards']) ? $conditions['boards'] : array($conditions['boards']);
1919
		}
1920
1921
		$request = $db->query('', '
1922
			SELECT 
1923
				COUNT(*)
1924
			FROM {db_prefix}boards AS b
1925
			WHERE ' . ($params['wanna_see_board'] ? '{query_wanna_see_board}' : '{query_see_board}') . (!empty($clauses) ? '
1926
				AND b.' . implode(' OR b.', $clauses) : '') . ($params['include_recycle'] ? '' : '
1927
				AND b.id_board != {int:recycle_board}') . ($params['include_redirects'] ? '' : '
1928
				AND b.redirect = {string:empty_string}'),
1929
			array_merge($clauseParameters, array(
1930
				'recycle_board' => !empty($modSettings['recycle_board']) ? $modSettings['recycle_board'] : 0,
1931
				'empty_string' => '',
1932
			))
1933
		);
1934
	}
1935
1936
	list ($num_boards) = $request->fetch_row();
1937
	$request->free_result();
1938
1939
	return $num_boards;
1940
}
1941