Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 44 additions & 13 deletions Sources/Actions/Search.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
use SMF\ActionInterface;
use SMF\ActionRouter;
use SMF\ActionTrait;
use SMF\Board;
use SMF\Category;
use SMF\Config;
use SMF\Db\DatabaseApi as Db;
use SMF\ErrorHandler;
Expand Down Expand Up @@ -70,7 +70,6 @@ public function execute(): void
// Don't load this in XML mode.
if (!isset($_REQUEST['xml'])) {
Theme::loadTemplate('Search');
Theme::loadTemplate('GenericControls');
Theme::loadJavaScriptFile('suggest.js', ['defer' => false, 'minimize' => true], 'smf_suggest');
}

Expand Down Expand Up @@ -175,19 +174,51 @@ public function execute(): void
}
}

// If user selected some particular boards, is this one of them?
if (!empty(Utils::$context['search_params']['brd'])) {
$boards = Utils::$context['search_params']['brd'];
}
// User didn't select any boards, so select all except ignored and recycle boards.
elseif (!empty(Config::$modSettings['recycle_enable']) && !empty(Config::$modSettings['recycle_board'])) {
$boards = array_merge(User::$me->ignoreboards, [(int) Config::$modSettings['recycle_board']]);
} else {
$boards = User::$me->ignoreboards;
}
// Find all the boards this user is allowed to see.
Category::getTree();

Utils::$context['num_boards'] = 0;
Utils::$context['boards_check_all'] = true;
Utils::$context['categories'] = [];

foreach (Category::$loaded as $category) {
// Clone it so that we can edit it without touching the real data.
$cat = clone $category;

// Remove all redirect boards from the its children.
$cat->children = array_filter(
$cat->children,
fn($board) => empty($board->redirect),
);

// Skip empty categories.
if (empty($cat->children)) {
continue;
}

Utils::$context['categories'] = Board::getUserVisibleBoards($boards);
// Add the category to the list.
Utils::$context['categories'][$cat->id] = $cat;

// Figure out which boards to mark as selected.
foreach ($cat->children as $key => $board) {
Utils::$context['num_boards']++;

// If user selected some particular boards, is this one of them?
if (!empty(Utils::$context['search_params']['brd'])) {
$board->selected = \in_array($board->id, Utils::$context['search_params']['brd']);
}
// User didn't select any boards, so select all except ignored and recycle boards.
else {
$board->selected = !$board->recycle && !\in_array($board->id, User::$me->ignoreboards);
}

if (!$board->selected && !$board->recycle) {
Utils::$context['boards_check_all'] = false;
}
}
}

// Searching in a topic?
if (!empty($_REQUEST['topic'])) {
Utils::$context['search_params']['topic'] = (int) $_REQUEST['topic'];
Utils::$context['search_params']['show_complete'] = true;
Expand Down
82 changes: 26 additions & 56 deletions Sources/Board.php
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,23 @@ class Board implements \ArrayAccess, Routable
* @var bool
*
* Whether this board is the recycle bin board.
*/
public bool $recycle = false;
*
* For the sake of compatibility with \ArrayAccess it is possible to write
* to this property, but doing so is pointless because the value will be
* overwritten the next time the property is read.
*/
public bool $recycle {
// @todo Once \ArrayAccess compatibility is no longer required, change this hook to
// `get => !empty(Config::$modSettings['recycle_enable']) && $this->id == (Config::$modSettings['recycle_board'] ?? NAN);`
&get {
$this->recycle = (
!empty(Config::$modSettings['recycle_enable'])
&& $this->id == (Config::$modSettings['recycle_board'] ?? NAN)
);

return $this->recycle;
}
}

/**
* @var bool
Expand Down Expand Up @@ -326,6 +341,15 @@ class Board implements \ArrayAccess, Routable
*/
public string $error;

/**
* @var bool
*
* Whether this board is selected in the search form.
*
* Only used during search.
*/
public bool $selected = false;

/**************************
* Public static properties
**************************/
Expand Down Expand Up @@ -1108,59 +1132,6 @@ public static function getMsgMemberID(int $messageID): int
return (int) $memberID;
}

/**
* Fetches the list of boards the user is allowed to see and organizes them by categories.
*
* This function queries the database for boards visible to the current user, grouped by categories.
* It returns a structured array of categories and their associated boards.
*
* @param array $boards An array of board IDs to mark as selected.
* @return array The structured array of categories and boards.
*/
public static function getUserVisibleBoards(array $boards): array
{
// Query to fetch boards visible to the user.
$request = Db::$db->query(
'SELECT id_board, b.name, child_level, c.name AS cat_name, id_cat
FROM {db_prefix}boards AS b
JOIN {db_prefix}categories AS c USING (id_cat)
WHERE {query_see_board}
AND redirect = {string:empty_string}
ORDER BY board_order',
[
'empty_string' => '',
],
identifier: 'order_by_board_order',
);

$categories = [];
$categoryTracker = [];
$currentCategoryIndex = -1;

// Process the results and group boards by categories.
while ($row = Db::$db->fetch_assoc($request)) {
if (!isset($categoryTracker[$row['id_cat']])) {
$categories[++$currentCategoryIndex] = [
'id' => (int) $row['id_cat'],
'name' => $row['cat_name'],
'boards' => [],
];
$categoryTracker[$row['id_cat']] = true;
}

$categories[$currentCategoryIndex]['boards'][] = [
'id' => (int) $row['id_board'],
'name' => $row['name'],
'child_level' => (int) $row['child_level'],
'selected' => \in_array($row['id_board'], $boards),
];
}

Db::$db->free_result($request);

return $categories;
}

/**
* Modify the settings and position of a board.
* Used by ManageBoards.php to change the settings of a board.
Expand Down Expand Up @@ -2313,7 +2284,6 @@ protected function loadBoardInfo(): void

case 'id_board':
$props['id'] = (int) $value;
$props['recycle'] = !empty(Config::$modSettings['recycle_enable']) && !empty(Config::$modSettings['recycle_board']) && Config::$modSettings['recycle_board'] == $value;
break;

case 'id_cat':
Expand Down
22 changes: 20 additions & 2 deletions Sources/Category.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,24 @@ class Category implements \ArrayAccess
*/
public bool $show_unread;

/**
* @var array
*
* IDs of all boards in this category.
*
* For the sake of compatibility with \ArrayAccess it is possible to write
* to this property, but doing so is pointless because the value will be
* overwritten the next time the property is read.
*/
public array $child_ids {
&get {
$this->child_ids = [];
self::recursiveBoards($this->child_ids, $this);

return $this->child_ids;
}
}

/**************************
* Public static properties
**************************/
Expand Down Expand Up @@ -785,9 +803,9 @@ public static function getTree(): void
* Used by self::getTree().
*
* @param array &$list The board list
* @param \SMF\Category|\SMF\Board &$tree The board tree
* @param self|Board &$tree The board tree
*/
public static function recursiveBoards(array &$list, \SMF\Category|\SMF\Board &$tree): void
public static function recursiveBoards(array &$list, self|Board &$tree): void
{
if (empty($tree->children)) {
return;
Expand Down
2 changes: 1 addition & 1 deletion Themes/default/Search.template.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ function template_main()
<a href="javascript:void(0);" onclick="selectBoards([', !empty($category['child_ids']) ? implode(', ', $category['child_ids']) : '', '], \'searchform\'); return false;">', $category['name'], '</a>
<ul>';

$cat_boards = array_values($category['boards']);
$cat_boards = array_values($category['children']);

foreach ($cat_boards as $key => $board) {
echo '
Expand Down
Loading