This commit is contained in:
@@ -46,7 +46,9 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter. Kernbereiche sind l
|
||||
- Eigene Events unterstützen optionalen Beschreibungstext, Ja/Nein-Angabe `Mit Kindern`, optionale Platzzahl, optionales Bild sowie eine Location aus Datenbank oder benutzerdefinierte Adresse.
|
||||
- `Profil-Levels` ist ein Owner-Bereich für Community-Level und die Übersicht der System-Level.
|
||||
- Community-Level sind jetzt über `system_settings.community_levels_json` editierbar und enthalten aktuell die Rechte `Hilfreiche Antworten hervorheben` und `Bewerbung als Forum-Admin`.
|
||||
- In `Profil-Levels` kann der Owner zusätzlich Benutzer suchen und Community-Punkte manuell mit Begründung erhöhen.
|
||||
- System-Level bleiben getrennt als Rollen `Forum-Admin`, `Site-Admin` und `SiteOwner`.
|
||||
- Die Vergabe von System-Leveln läuft im Bereich `Community-Admin` jetzt über eine Benutzersuche statt über rohe Benutzer-IDs; `Site-Admins` bleiben auf die bestehende Rollenlogik begrenzt.
|
||||
|
||||
## Technischer Rahmen
|
||||
- eigener Front-Controller in `public/index.php`
|
||||
|
||||
@@ -20,12 +20,13 @@ Stand: 2026-08-10
|
||||
- Event-Quellen aus Datenbank plus Speicherung der Event-Location-Herkunft: `partials/landing/account/dashboard.php`, `src/App/AccountPages.php`, `Internal/db/schema.sql`
|
||||
- Consent- und rechtliche Texte: `public/page/datenschutz.php`
|
||||
- System-Einstellungen: `src/App/SystemSettings.php`
|
||||
- Profil-Levels für Owner: `partials/landing/account/dashboard.php`, `src/App/AccountPages.php`, `src/App/Community.php`, `src/App/CommunityAccess.php`, `src/App/SystemSettings.php`
|
||||
- Profil-Levels für Owner inklusive Benutzersuche und manueller Community-Punktevergabe: `partials/landing/account/dashboard.php`, `src/App/AccountPages.php`, `src/App/Community.php`, `src/App/CommunityAccess.php`, `src/App/SystemSettings.php`
|
||||
- neue Listing-/Ort-Basis: `src/App/ListingCatalog.php`
|
||||
- Kalender-Export und abonnierbarer Feed: `src/App/CalendarSync.php` plus `public/page/calendar/export.php` und `public/page/calendar/feed.php`
|
||||
- UI für getrennte Bereiche `Events` und `Orte & Veranstaltungen` im Mitgliederbereich: `partials/landing/account/dashboard.php`
|
||||
- Preis-, Bild- und Ortseingabe für Orte und Veranstaltungen sowie reduzierte Event-Maske: `partials/landing/account/dashboard.php` plus Speicherung in `src/App/ListingCatalog.php`
|
||||
- Freigaben sowie Änderungs- und Löschanfragen für Orte und Veranstaltungen: `src/App/ListingCatalog.php`, `partials/landing/account/dashboard.php`, `partials/landing/account/community-admin.php`
|
||||
- Rollenvergabe per Benutzersuche im Community-Admin: `partials/landing/account/community-admin.php`, `src/App/CommunityAccess.php`
|
||||
- Legacy-Eigen-Events wurden erweitert in `src/App/AccountPages.php` und `Internal/db/schema.sql` um Kategorie- und Bildfelder
|
||||
- Kategorien-Prüfung und Zusammenführung für Seiten-Admins liegen ebenfalls in `partials/landing/account/dashboard.php` mit Logik in `src/App/ListingCatalog.php`
|
||||
|
||||
|
||||
@@ -46,7 +46,9 @@ Papa-Kind-Treff is a PHP-based platform for fathers. Core areas are local events
|
||||
- Own events support an optional description, a clear `with children` yes/no field, optional capacity, an optional image, and either a database-backed location or a custom address.
|
||||
- `Profile Levels` is an owner-only area for community levels and the overview of system levels.
|
||||
- Community levels are now editable through `system_settings.community_levels_json` and currently carry the rights `highlight helpful replies` and `apply for forum admin`.
|
||||
- Inside `Profile Levels`, the owner can additionally search users and manually increase community points with a required reason.
|
||||
- System levels remain separate as the roles `Forum Admin`, `Site Admin`, and `SiteOwner`.
|
||||
- System-level assignment in `Community Admin` now runs through user search instead of raw user IDs; site admins remain limited by the existing role rules.
|
||||
|
||||
## Technical Frame
|
||||
- custom front controller in `public/index.php`
|
||||
|
||||
@@ -20,12 +20,13 @@ Updated: 2026-08-10
|
||||
- database-backed event location sources plus persisted event location origin: `partials/landing/account/dashboard.php`, `src/App/AccountPages.php`, `Internal/db/schema.sql`
|
||||
- consent and legal texts: `public/page/datenschutz.php`
|
||||
- system settings: `src/App/SystemSettings.php`
|
||||
- owner profile-level management: `partials/landing/account/dashboard.php`, `src/App/AccountPages.php`, `src/App/Community.php`, `src/App/CommunityAccess.php`, `src/App/SystemSettings.php`
|
||||
- owner profile-level management including user search and manual community-point increases: `partials/landing/account/dashboard.php`, `src/App/AccountPages.php`, `src/App/Community.php`, `src/App/CommunityAccess.php`, `src/App/SystemSettings.php`
|
||||
- new listing/place base: `src/App/ListingCatalog.php`
|
||||
- calendar export and subscribable feed: `src/App/CalendarSync.php` plus `public/page/calendar/export.php` and `public/page/calendar/feed.php`
|
||||
- separated `Events` and `Places & Events` UI in member area: `partials/landing/account/dashboard.php`
|
||||
- pricing, image upload, and place/address input for places and event-style entries plus a reduced own-event form: `partials/landing/account/dashboard.php` with persistence in `src/App/ListingCatalog.php`
|
||||
- approvals as well as change and deletion requests for places and event-style entries: `src/App/ListingCatalog.php`, `partials/landing/account/dashboard.php`, `partials/landing/account/community-admin.php`
|
||||
- role assignment via user search in community admin: `partials/landing/account/community-admin.php`, `src/App/CommunityAccess.php`
|
||||
- legacy own events were extended in `src/App/AccountPages.php` and `Internal/db/schema.sql` with category and image fields
|
||||
|
||||
## Documentation Rule
|
||||
|
||||
@@ -38,6 +38,8 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter mit Fokus auf:
|
||||
- Hauptnavigation aktuell: `Home`, `Event Suche`, `Community`
|
||||
- Profil-Menü aktuell: `Profil`, `Kinder`, `Events`, `Orte & Veranstaltungen`, `Community`, `Einstellungen`, `Abmelden`
|
||||
- Für `SiteOwner` gibt es zusätzlich den Mitgliederbereichspunkt `Profil-Levels`
|
||||
- In `Profil-Levels` kann der `SiteOwner` Community-Level pflegen, Benutzer suchen und Community-Punkte manuell mit Begründung erhöhen
|
||||
- Im Bereich `Community-Admin` läuft die Rollenvergabe jetzt über eine Benutzersuche statt über rohe Benutzer-IDs; `Site-Admins` bleiben dabei auf die vorhandene Rollenlogik begrenzt
|
||||
- neue interne Grundstruktur: `listing_places`, `listings`, `listing_occurrences`, `listing_prices`, `listing_benefits`
|
||||
- Seiten-Admins haben zusätzlich eine System-Sektion für globale Wartungs- und Diensteschalter
|
||||
- Im Mitgliederbereich ist `Events` jetzt wieder auf echte eigene Events und Event-Teilnahmen beschränkt
|
||||
|
||||
@@ -25,6 +25,10 @@ $info = '';
|
||||
$canManageApplications = $access->canManageApplications((int)$userId) && $access->supportsApplications();
|
||||
$canManageRoles = $access->canManageRoles((int)$userId);
|
||||
$canReviewListings = $listingCatalog !== null && !$access->hasRole((int)$userId, 'owner');
|
||||
$roleUserSearchQuery = trim((string)($_GET['role_user_query'] ?? ''));
|
||||
$roleUserSearchResults = ($canManageRoles || $canManageApplications) && $roleUserSearchQuery !== ''
|
||||
? $access->searchUsers($roleUserSearchQuery, 12)
|
||||
: [];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = (string)($_POST['action'] ?? '');
|
||||
@@ -219,33 +223,64 @@ $migrationStatus = ($migration && $canManageApplications) ? $migration->status()
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php if ($canManageRoles): ?>
|
||||
<?php if ($canManageRoles || $canManageApplications): ?>
|
||||
<section class="forum-board">
|
||||
<div class="forum-board__head">
|
||||
<div>
|
||||
<h2>Rollen verwalten</h2>
|
||||
<p class="muted">Community-Rollen vergeben und entziehen.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding:24px; border-bottom:1px solid var(--color-border);">
|
||||
<form method="post" class="form-grid single">
|
||||
<input type="hidden" name="action" value="role_assign">
|
||||
<form method="get" class="form-grid single">
|
||||
<div class="stack gap-6">
|
||||
<label class="label" for="adminTargetUserId">Benutzer-ID</label>
|
||||
<input id="adminTargetUserId" name="target_user_id" class="input" type="number" min="1" required>
|
||||
</div>
|
||||
<div class="stack gap-6">
|
||||
<label class="label" for="adminRole">Rolle</label>
|
||||
<select id="adminRole" name="role" class="select">
|
||||
<option value="forum_admin">forum_admin</option>
|
||||
<option value="site_admin">site_admin</option>
|
||||
<option value="owner">owner</option>
|
||||
</select>
|
||||
<label class="label" for="adminRoleUserQuery">Benutzer suchen</label>
|
||||
<input id="adminRoleUserQuery" name="role_user_query" class="input" value="<?= htmlspecialchars($roleUserSearchQuery, ENT_QUOTES) ?>" placeholder="Name oder E-Mail">
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn" type="submit">Rolle vergeben</button>
|
||||
<button class="btn" type="submit">Suchen</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php if ($roleUserSearchResults): ?>
|
||||
<div class="stack gap-12" style="margin-top:18px;">
|
||||
<?php foreach ($roleUserSearchResults as $candidate): ?>
|
||||
<?php
|
||||
$candidateUserId = (int)($candidate['id'] ?? 0);
|
||||
$candidateRoles = $access->getUserRoles($candidateUserId);
|
||||
$assignableRoles = $canManageRoles ? ['forum_admin', 'site_admin', 'owner'] : ['forum_admin'];
|
||||
?>
|
||||
<div class="card" style="padding:14px;">
|
||||
<strong><?= htmlspecialchars((string)($candidate['display_name'] ?: trim(((string)($candidate['first_name'] ?? '')) . ' ' . ((string)($candidate['last_name'] ?? ''))) ?: 'Mitglied'), ENT_QUOTES) ?></strong>
|
||||
<div class="muted small" style="margin-top:6px;">
|
||||
ID: <?= $candidateUserId ?> · <?= htmlspecialchars((string)($candidate['email'] ?? ''), ENT_QUOTES) ?>
|
||||
</div>
|
||||
<?php if ($candidateRoles): ?>
|
||||
<div class="muted small" style="margin-top:6px;">Aktuelle Rollen: <?= htmlspecialchars(implode(', ', array_map('strval', $candidateRoles)), ENT_QUOTES) ?></div>
|
||||
<?php endif; ?>
|
||||
<div class="flex gap-8" style="margin-top:12px; flex-wrap:wrap;">
|
||||
<?php foreach ($assignableRoles as $roleKey): ?>
|
||||
<?php if (!in_array($roleKey, $candidateRoles, true)): ?>
|
||||
<form method="post">
|
||||
<input type="hidden" name="action" value="role_assign">
|
||||
<input type="hidden" name="target_user_id" value="<?= $candidateUserId ?>">
|
||||
<input type="hidden" name="role" value="<?= htmlspecialchars($roleKey, ENT_QUOTES) ?>">
|
||||
<button class="btn ghost" type="submit"><?= htmlspecialchars($roleKey, ENT_QUOTES) ?> vergeben</button>
|
||||
</form>
|
||||
<?php elseif ($canManageRoles): ?>
|
||||
<form method="post">
|
||||
<input type="hidden" name="action" value="role_revoke">
|
||||
<input type="hidden" name="target_user_id" value="<?= $candidateUserId ?>">
|
||||
<input type="hidden" name="role" value="<?= htmlspecialchars($roleKey, ENT_QUOTES) ?>">
|
||||
<button class="btn ghost" type="submit"><?= htmlspecialchars($roleKey, ENT_QUOTES) ?> entziehen</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php elseif ($roleUserSearchQuery !== ''): ?>
|
||||
<p class="muted small" style="margin-top:18px;">Keine passenden Benutzer gefunden.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="forum-admin-list">
|
||||
<?php foreach ($roleAssignments as $assignment): ?>
|
||||
|
||||
@@ -340,6 +340,64 @@ if (!empty($canManageSystemSettings)) {
|
||||
<h2>Profil-Levels</h2>
|
||||
</div>
|
||||
<div class="account-panel__body">
|
||||
<div class="card" style="margin-bottom:18px;">
|
||||
<strong>Benutzer suchen</strong>
|
||||
<form method="get" class="stack gap-12" style="margin-top:14px;">
|
||||
<input type="hidden" name="section" value="profile-levels">
|
||||
<div class="form-grid">
|
||||
<div class="stack gap-6">
|
||||
<label class="label" for="profileLevelUserQuery">Name oder E-Mail</label>
|
||||
<input id="profileLevelUserQuery" name="profile_level_user_query" class="input" value="<?= htmlspecialchars((string)($profileLevelUserSearchQuery ?? ''), ENT_QUOTES) ?>">
|
||||
</div>
|
||||
<div class="stack gap-6" style="justify-content:end;">
|
||||
<span class="label" aria-hidden="true"> </span>
|
||||
<button class="btn" type="submit">Suchen</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php if (!empty($profileLevelUserSearchResults)): ?>
|
||||
<div class="stack gap-12" style="margin-top:18px;">
|
||||
<?php foreach ($profileLevelUserSearchResults as $searchedUser): ?>
|
||||
<div class="card" style="padding:14px;">
|
||||
<div class="flex between center-y" style="gap:12px; flex-wrap:wrap;">
|
||||
<div>
|
||||
<strong><?= htmlspecialchars((string)($searchedUser['display_name'] ?: trim(((string)($searchedUser['first_name'] ?? '')) . ' ' . ((string)($searchedUser['last_name'] ?? ''))) ?: 'Mitglied'), ENT_QUOTES) ?></strong>
|
||||
<div class="muted small" style="margin-top:6px;">
|
||||
ID: <?= (int)($searchedUser['id'] ?? 0) ?> ·
|
||||
<?= htmlspecialchars((string)($searchedUser['email'] ?? ''), ENT_QUOTES) ?> ·
|
||||
Punkte: <?= number_format((float)($searchedUser['community_points'] ?? 0), 1, ',', '.') ?> ·
|
||||
Level: <?= htmlspecialchars(trim(((string)(($searchedUser['community_level']['icon'] ?? ''))) . ' ' . ((string)(($searchedUser['community_level']['label'] ?? '')))), ENT_QUOTES) ?>
|
||||
</div>
|
||||
<?php if (!empty($searchedUser['roles'])): ?>
|
||||
<div class="muted small" style="margin-top:6px;">System-Level: <?= htmlspecialchars(implode(', ', array_map('strval', (array)$searchedUser['roles'])), ENT_QUOTES) ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" class="stack gap-12" style="margin-top:14px;">
|
||||
<input type="hidden" name="action" value="community_points_adjust">
|
||||
<input type="hidden" name="target_user_id" value="<?= (int)($searchedUser['id'] ?? 0) ?>">
|
||||
<div class="form-grid">
|
||||
<div class="stack gap-6">
|
||||
<label class="label" for="communityPointsAmount<?= (int)($searchedUser['id'] ?? 0) ?>">Punkte erhöhen</label>
|
||||
<input id="communityPointsAmount<?= (int)($searchedUser['id'] ?? 0) ?>" name="points_amount" class="input" type="number" min="0.1" step="0.1" required>
|
||||
</div>
|
||||
<div class="stack gap-6">
|
||||
<label class="label" for="communityPointsReason<?= (int)($searchedUser['id'] ?? 0) ?>">Begründung</label>
|
||||
<input id="communityPointsReason<?= (int)($searchedUser['id'] ?? 0) ?>" name="points_reason" class="input" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-12" style="flex-wrap:wrap;">
|
||||
<button class="btn" type="submit">Punkte vergeben</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php elseif (($profileLevelUserSearchQuery ?? '') !== ''): ?>
|
||||
<p class="muted small" style="margin-top:14px;">Keine passenden Benutzer gefunden.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<strong>Community-Levels</strong>
|
||||
<div class="stack gap-12" style="margin-top:14px;">
|
||||
|
||||
@@ -707,6 +707,31 @@ final class AccountPages
|
||||
}
|
||||
$community->saveMembershipLevels($levels, $userId);
|
||||
$info = 'Community-Level gelöscht.';
|
||||
} elseif ($action === 'community_points_adjust') {
|
||||
if (!$canManageProfileLevels || !$community) {
|
||||
throw new \RuntimeException('Keine Berechtigung für Community-Punkte.');
|
||||
}
|
||||
|
||||
$targetUserId = (int)($_POST['target_user_id'] ?? 0);
|
||||
$amountRaw = trim((string)($_POST['points_amount'] ?? ''));
|
||||
$reason = trim((string)($_POST['points_reason'] ?? ''));
|
||||
if ($targetUserId <= 0) {
|
||||
throw new \RuntimeException('Bitte wähle einen Benutzer aus.');
|
||||
}
|
||||
if ($amountRaw === '' || !is_numeric($amountRaw)) {
|
||||
throw new \RuntimeException('Bitte gib eine gültige Punktzahl an.');
|
||||
}
|
||||
|
||||
$amount = (float)$amountRaw;
|
||||
if ($amount <= 0) {
|
||||
throw new \RuntimeException('Es können hier nur zusätzliche Punkte vergeben werden.');
|
||||
}
|
||||
if ($reason === '') {
|
||||
throw new \RuntimeException('Bitte gib eine Begründung für die Punktevergabe an.');
|
||||
}
|
||||
|
||||
$community->adjustPoints($targetUserId, $amount, $reason, $userId);
|
||||
$info = 'Community-Punkte wurden erhöht.';
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$error = $e->getMessage();
|
||||
@@ -913,6 +938,21 @@ final class AccountPages
|
||||
$communityLevelRightDefinitions = Community::membershipRightDefinitions();
|
||||
$communityRoles = $communityAccess ? $communityAccess->getUserRoles($userId) : [];
|
||||
$systemRoleAssignments = $communityAccess && $canManageProfileLevels ? $communityAccess->listRoleAssignments() : [];
|
||||
$profileLevelUserSearchQuery = '';
|
||||
$profileLevelUserSearchResults = [];
|
||||
if ($communityAccess && $community && $canManageProfileLevels) {
|
||||
$profileLevelUserSearchQuery = trim((string)($_GET['profile_level_user_query'] ?? ''));
|
||||
if ($profileLevelUserSearchQuery !== '') {
|
||||
foreach ($communityAccess->searchUsers($profileLevelUserSearchQuery, 12) as $searchRow) {
|
||||
$targetSearchUserId = (int)($searchRow['id'] ?? 0);
|
||||
$targetPoints = $community->computePoints($targetSearchUserId);
|
||||
$searchRow['community_points'] = $targetPoints;
|
||||
$searchRow['community_level'] = $community->membershipLevel($targetPoints);
|
||||
$searchRow['roles'] = $communityAccess->getUserRoles($targetSearchUserId);
|
||||
$profileLevelUserSearchResults[] = $searchRow;
|
||||
}
|
||||
}
|
||||
}
|
||||
$communityApplication = $communityAccess ? $communityAccess->getLatestApplication($userId) : null;
|
||||
$communityCanApply = $communityAccess ? $communityAccess->canApplyForForumAdmin($userId, $communityPoints) : false;
|
||||
$communityRestrictions = $communityAccess ? $communityAccess->getRestrictionState($userId) : [
|
||||
@@ -991,6 +1031,8 @@ final class AccountPages
|
||||
'communityLevelRightDefinitions',
|
||||
'communityRoles',
|
||||
'systemRoleAssignments',
|
||||
'profileLevelUserSearchQuery',
|
||||
'profileLevelUserSearchResults',
|
||||
'communityApplication',
|
||||
'communityCanApply',
|
||||
'communityRestrictions',
|
||||
|
||||
@@ -417,6 +417,36 @@ final class Community
|
||||
return $amount;
|
||||
}
|
||||
|
||||
public function adjustPoints(int $userId, float $amount, string $reason = '', ?int $actingUserId = null): float
|
||||
{
|
||||
if ($userId <= 0) {
|
||||
throw new \RuntimeException('Benutzer nicht gefunden.');
|
||||
}
|
||||
if ($amount == 0.0) {
|
||||
throw new \RuntimeException('Bitte gib eine Punkteänderung ungleich 0 an.');
|
||||
}
|
||||
|
||||
$meta = [
|
||||
'reason' => trim($reason),
|
||||
];
|
||||
if ($actingUserId !== null && $actingUserId > 0) {
|
||||
$meta['acting_user_id'] = $actingUserId;
|
||||
}
|
||||
|
||||
$stmt = $this->pdo->prepare('INSERT INTO user_points (user_id, action, amount, meta) VALUES (:uid, :action, :amount, :meta)');
|
||||
$stmt->execute([
|
||||
':uid' => $userId,
|
||||
':action' => 'manual.adjustment',
|
||||
':amount' => $amount,
|
||||
':meta' => json_encode($meta, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
]);
|
||||
|
||||
$stmt = $this->pdo->prepare('INSERT INTO user_points_totals (user_id, total) VALUES (:uid, :amt) ON DUPLICATE KEY UPDATE total = total + VALUES(total)');
|
||||
$stmt->execute([':uid' => $userId, ':amt' => $amount]);
|
||||
|
||||
return $amount;
|
||||
}
|
||||
|
||||
public function membershipLevel(float $points): array
|
||||
{
|
||||
$level = $this->membershipLevelMeta($points);
|
||||
|
||||
@@ -290,6 +290,53 @@ final class CommunityAccess
|
||||
return $this->emailStore()->decryptRowEmails($stmt->fetchAll(\PDO::FETCH_ASSOC) ?: [], 'user_id');
|
||||
}
|
||||
|
||||
public function searchUsers(string $query, int $limit = 12): array
|
||||
{
|
||||
$query = trim($query);
|
||||
if ($query === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$limit = max(1, min(50, $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) {
|
||||
$displayKey = ':display_' . $index;
|
||||
$nameKey = ':name_' . $index;
|
||||
$emailKey = ':email_' . $index;
|
||||
$conditions[] = '(LOWER(COALESCE(up.display_name, "")) LIKE ' . $displayKey . ' OR LOWER(CONCAT_WS(" ", COALESCE(up.first_name, ""), COALESCE(up.last_name, ""))) LIKE ' . $nameKey . ' OR LOWER(COALESCE(u.email, "")) LIKE ' . $emailKey . ')';
|
||||
$needle = '%' . $token . '%';
|
||||
$params[$displayKey] = $needle;
|
||||
$params[$nameKey] = $needle;
|
||||
$params[$emailKey] = $needle;
|
||||
}
|
||||
|
||||
$sql = '
|
||||
SELECT u.id, u.email, u.status, up.display_name, up.first_name, up.last_name
|
||||
FROM users u
|
||||
LEFT JOIN user_profiles up ON up.user_id = u.id
|
||||
WHERE ' . implode(' AND ', $conditions) . '
|
||||
ORDER BY
|
||||
CASE
|
||||
WHEN LOWER(COALESCE(up.display_name, "")) = :exactQuery THEN 0
|
||||
WHEN LOWER(COALESCE(u.email, "")) = :exactQuery THEN 1
|
||||
ELSE 2
|
||||
END,
|
||||
COALESCE(up.display_name, ""),
|
||||
u.id DESC
|
||||
LIMIT ' . $limit;
|
||||
$params[':exactQuery'] = mb_strtolower($query);
|
||||
|
||||
$stmt = $this->pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
return $this->emailStore()->decryptRowEmails($stmt->fetchAll(\PDO::FETCH_ASSOC) ?: [], 'id');
|
||||
}
|
||||
|
||||
public function setRestriction(int $actingUserId, int $targetUserId, string $type, string $reason): void
|
||||
{
|
||||
if (!$this->canModerateForum($actingUserId)) {
|
||||
|
||||
Reference in New Issue
Block a user