yyxc
All checks were successful
Deploy / deploy (push) Successful in 56s

This commit is contained in:
2026-08-19 20:14:15 +02:00
parent c567d19deb
commit 39dfc97ae6
7 changed files with 147 additions and 7 deletions

View File

@@ -871,6 +871,10 @@ final class AccountPages
$listingCatalog->listCategoryReviewItems(),
static fn(array $item): bool => !empty($item['needs_review'])
)) : [];
$categorySearchQuery = $canManageCategories ? trim((string)($_GET['category_query'] ?? '')) : '';
$categorySearchResults = ($listingCatalog && $canManageCategories && $categorySearchQuery !== '')
? $listingCatalog->searchCategories($categorySearchQuery, 20)
: [];
$placeSuggestions = $listingCatalog ? $listingCatalog->listPlaceSuggestions(60) : [];
$eventLocationOptions = $listingCatalog ? $listingCatalog->listEventLocationOptions(120) : [];
$stmt = $pdo?->prepare(
@@ -1000,6 +1004,7 @@ final class AccountPages
'Themen und Antworten moderieren',
'Community-Sperren setzen und aufheben',
'Meldungen bearbeiten',
'Kategorien bestätigen und zusammenführen',
'Orte und Veranstaltungen freigeben',
],
],
@@ -1009,7 +1014,6 @@ final class AccountPages
'rights' => [
'Beinhaltet alle Rechte von Forum-Admin',
'Community-Admin-Bewerbungen bearbeiten',
'Kategorien bestätigen und zusammenführen',
'System-Einstellungen verwalten',
],
],
@@ -1057,6 +1061,8 @@ final class AccountPages
'editListing',
'listingCategories',
'categoryReviewItems',
'categorySearchQuery',
'categorySearchResults',
'placeSuggestions',
'eventLocationOptions',
'communityPoints',

View File

@@ -77,7 +77,7 @@ final class CommunityAccess
public function canManageCategories(int $userId, ?float $points = null): bool
{
if ($this->hasRole($userId, 'owner') || $this->hasRole($userId, 'site_admin')) {
if ($this->hasRole($userId, 'owner') || $this->hasRole($userId, 'site_admin') || $this->hasRole($userId, 'forum_admin')) {
return true;
}

View File

@@ -314,6 +314,68 @@ final class ListingCatalog
return $rows;
}
public function searchCategories(string $query, int $limit = 20): array
{
$this->ensureSchema();
$query = trim($query);
if ($query === '') {
return [];
}
$limit = max(1, min(100, $limit));
$tokens = array_values(array_filter(preg_split('/\s+/u', mb_strtolower($query)) ?: [], static fn(string $token): bool => $token !== ''));
if ($tokens === []) {
return [];
}
$conditions = [];
$params = [];
foreach ($tokens as $index => $token) {
$titleKey = ':title_' . $index;
$slugKey = ':slug_' . $index;
$conditions[] = '(LOWER(c.title) LIKE ' . $titleKey . ' OR LOWER(c.slug) LIKE ' . $slugKey . ')';
$params[$titleKey] = '%' . $token . '%';
$params[$slugKey] = '%' . $token . '%';
}
$sql = '
SELECT c.id, c.slug, c.title, c.category_group, c.sort_order,
(SELECT COUNT(*) FROM listing_category_map m WHERE m.category_id = c.id) AS listing_count,
(SELECT COUNT(*) FROM listing_places p WHERE p.place_kind = c.slug) AS place_count
FROM listing_categories c
WHERE ' . implode(' AND ', $conditions) . '
ORDER BY
CASE
WHEN LOWER(c.title) = :exactTitle THEN 0
WHEN LOWER(c.slug) = :exactSlug THEN 1
WHEN c.sort_order >= 900 THEN 2
ELSE 3
END,
c.sort_order ASC,
c.title ASC
LIMIT ' . $limit;
$params[':exactTitle'] = mb_strtolower($query);
$params[':exactSlug'] = $this->slugify($query);
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
$rows = $stmt->fetchAll(\PDO::FETCH_ASSOC) ?: [];
$eventStmt = $this->pdo->query('SELECT category_slug, COUNT(*) AS total FROM events WHERE category_slug IS NOT NULL AND category_slug <> "" GROUP BY category_slug');
$eventCounts = [];
foreach (($eventStmt ? $eventStmt->fetchAll(\PDO::FETCH_ASSOC) : []) as $row) {
$eventCounts[(string)$row['category_slug']] = (int)$row['total'];
}
foreach ($rows as &$row) {
$row['event_count'] = $eventCounts[(string)$row['slug']] ?? 0;
$row['needs_review'] = ((int)($row['sort_order'] ?? 0)) >= 900;
}
unset($row);
return $rows;
}
public function mergeCategories(string $sourceSlug, string $targetSlug): void
{
$this->ensureSchema();