v1.0.0.8
Publisium
- <?php
-
- declare(strict_types=1);
-
- require_once __DIR__ . '/includes/config.php';
- require_once __DIR__ . '/includes/database.php';
- require_once __DIR__ . '/includes/auth.php';
- require_once __DIR__ . '/includes/user-auth.php';
- require_once __DIR__ . '/includes/csrf.php';
- require_once __DIR__ . '/includes/avatar.php';
- require_once __DIR__ . '/includes/activity-log.php';
- require_once __DIR__ . '/includes/two-factor.php';
- require_once __DIR__ . '/includes/site-front.php';
- require_once __DIR__ . '/includes/indexnow.php';
-
- Auth::boot();
- Auth::requireRoleAtLeast(Auth::ROLE_EDITOR_IN_CHIEF);
-
- $currentUser = Auth::user();
- $currentRole = Auth::role();
- $isSuperAdmin = $currentRole === Auth::ROLE_SUPER_ADMIN;
-
- $assignableRoles = $isSuperAdmin
- ? [
- Auth::ROLE_SUPER_ADMIN,
- Auth::ROLE_EDITOR_IN_CHIEF,
- Auth::ROLE_MANAGING_EDITOR,
- Auth::ROLE_SENIOR_WRITER,
- Auth::ROLE_WRITER,
- Auth::ROLE_PROOFREADER,
- ]
- : [
- Auth::ROLE_MANAGING_EDITOR,
- Auth::ROLE_SENIOR_WRITER,
- Auth::ROLE_WRITER,
- Auth::ROLE_PROOFREADER,
- ];
-
- $canManageAccount = static fn(array $account): bool => $isSuperAdmin
- ? $account['role'] !== Auth::ROLE_SUPER_ADMIN
- : in_array($account['role'], $assignableRoles, true);
-
- $flashMessage = null;
- $flashType = 'success';
- $db = Database::site();
- $usersDb = Database::users();
- $activeTab = ($_GET['tab'] ?? 'team') === 'readers' ? 'readers' : 'team';
-
- if ($_SERVER['REQUEST_METHOD'] === 'POST') {
- if (!Csrf::verify($_POST['csrf_token'] ?? null)) {
- $flashMessage = 'Your session expired. Please try again.';
- $flashType = 'error';
- } else {
- $action = $_POST['action'] ?? '';
-
- if ($action === 'suspend_reader') {
- if (!$isSuperAdmin) {
- $flashMessage = 'Only the Super Admin can suspend reader accounts.';
- $flashType = 'error';
- } else {
- $readerId = (int) ($_POST['reader_id'] ?? 0);
- $update = $usersDb->prepare("UPDATE user_accounts SET status = 'suspended' WHERE id = :id");
- $update->execute(['id' => $readerId]);
- UserAuth::revokeAllRememberTokens($readerId);
- $flashMessage = 'Reader suspended. They can’t sign in until you reactivate them.';
- }
- $activeTab = 'readers';
- }
-
- if ($action === 'reactivate_reader') {
- if (!$isSuperAdmin) {
- $flashMessage = 'Only the Super Admin can reactivate reader accounts.';
- $flashType = 'error';
- } else {
- $readerId = (int) ($_POST['reader_id'] ?? 0);
- $update = $usersDb->prepare("UPDATE user_accounts SET status = 'active' WHERE id = :id");
- $update->execute(['id' => $readerId]);
- $flashMessage = 'Reader reactivated. They can sign in again.';
- }
- $activeTab = 'readers';
- }
-
- if ($action === 'create_account') {
- $username = trim((string) ($_POST['username'] ?? ''));
- $email = trim((string) ($_POST['email'] ?? ''));
- $displayName = trim((string) ($_POST['display_name'] ?? ''));
- $password = (string) ($_POST['password'] ?? '');
- $role = (string) ($_POST['role'] ?? '');
-
- if ($username === '' || $email === '' || $password === '' || !in_array($role, $assignableRoles, true)) {
- $flashMessage = 'Fill in every field and pick a role you’re allowed to give.';
- $flashType = 'error';
- } elseif ($role === Auth::ROLE_SUPER_ADMIN) {
- $flashMessage = 'There can only be one Super Admin, so you can’t create another one.';
- $flashType = 'error';
- } elseif (strlen($password) < 10) {
- $flashMessage = 'The password needs at least 10 characters.';
- $flashType = 'error';
- } else {
- $newAvatar = null;
- try {
- if (Avatar::hasUpload($_FILES['avatar'] ?? null)) {
- [$newAvatar, $avatarError] = Avatar::store($_FILES['avatar']);
- if ($avatarError !== null) {
- throw new InvalidArgumentException($avatarError);
- }
- }
-
- $insert = $db->prepare(
- 'INSERT INTO team_accounts (username, email, password_hash, display_name, role, status, avatar_path) VALUES (:username, :email, :hash, :display_name, :role, :status, :avatar)'
- );
- $insert->execute([
- 'username' => $username,
- 'email' => $email,
- 'hash' => password_hash($password, PASSWORD_DEFAULT),
- 'display_name' => $displayName !== '' ? $displayName : $username,
- 'role' => $role,
- 'status' => 'active',
- 'avatar' => $newAvatar,
- ]);
- $flashMessage = 'Account created.';
- } catch (InvalidArgumentException $exception) {
- $flashMessage = $exception->getMessage();
- $flashType = 'error';
- } catch (Throwable $exception) {
- Avatar::delete($newAvatar);
- $flashMessage = 'We couldn’t create the account. The username or email is probably already taken.';
- $flashType = 'error';
- }
- }
- }
-
- if ($action === 'update_account') {
- $targetId = (int) ($_POST['account_id'] ?? 0);
- $displayName = trim((string) ($_POST['display_name'] ?? ''));
- $role = (string) ($_POST['role'] ?? '');
- $status = ($_POST['status'] ?? 'active') === 'suspended' ? 'suspended' : 'active';
- $newPassword = (string) ($_POST['new_password'] ?? '');
-
- $targetStatement = $db->prepare('SELECT * FROM team_accounts WHERE id = :id LIMIT 1');
- $targetStatement->execute(['id' => $targetId]);
- $targetAccount = $targetStatement->fetch();
-
- $isSelf = $targetAccount && (int) $targetAccount['id'] === (int) $currentUser['id'];
- if ($isSelf) {
- $role = $targetAccount['role'];
- $status = 'active';
- }
-
- if (!$targetAccount) {
- $flashMessage = 'We couldn’t find this account.';
- $flashType = 'error';
- } elseif ($targetAccount['role'] === Auth::ROLE_SUPER_ADMIN && !$isSelf) {
- $flashMessage = 'Only the Super Admin can edit their own account.';
- $flashType = 'error';
- } elseif (!$isSelf && !$canManageAccount($targetAccount)) {
- $flashMessage = 'You can only edit accounts with a lower role than yours.';
- $flashType = 'error';
- } elseif ($role === Auth::ROLE_SUPER_ADMIN && $targetAccount['role'] !== Auth::ROLE_SUPER_ADMIN) {
- $flashMessage = 'The Super Admin role can’t be given to anyone else.';
- $flashType = 'error';
- } elseif (!$isSelf && !in_array($role, $assignableRoles, true)) {
- $flashMessage = 'You can’t give that role.';
- $flashType = 'error';
- } else {
- $fields = ['display_name = :display_name', 'role = :role', 'status = :status'];
- $params = [
- 'display_name' => $displayName !== '' ? $displayName : $targetAccount['display_name'],
- 'role' => $role,
- 'status' => $targetAccount['role'] === Auth::ROLE_SUPER_ADMIN ? 'active' : $status,
- 'id' => $targetId,
- ];
- $saveError = null;
- $newAvatar = null;
- $removeAvatar = isset($_POST['remove_avatar']);
-
- if ($newPassword !== '') {
- if (strlen($newPassword) < 10) {
- $saveError = 'The new password needs at least 10 characters.';
- } else {
- $fields[] = 'password_hash = :hash';
- $params['hash'] = password_hash($newPassword, PASSWORD_DEFAULT);
- }
- }
-
- if ($saveError === null && Avatar::hasUpload($_FILES['avatar'] ?? null)) {
- [$newAvatar, $saveError] = Avatar::store($_FILES['avatar']);
- }
-
- if ($saveError !== null) {
- $flashMessage = $saveError;
- $flashType = 'error';
- } else {
- if ($newAvatar !== null) {
- $fields[] = 'avatar_path = :avatar';
- $params['avatar'] = $newAvatar;
- } elseif ($removeAvatar) {
- $fields[] = 'avatar_path = NULL';
- }
-
- $resetTwoFactor = isset($_POST['reset_2fa']);
- if ($resetTwoFactor) {
- array_push($fields, 'totp_enabled = 0', 'totp_secret = NULL', 'totp_recovery = NULL', 'totp_last_step = NULL');
- }
-
- $update = $db->prepare('UPDATE team_accounts SET ' . implode(', ', $fields) . ' WHERE id = :id');
- $update->execute($params);
-
- if ($resetTwoFactor) {
- ActivityLog::record('security.2fa_reset', 'account', $targetId);
- }
-
- if ($newAvatar !== null || $removeAvatar) {
- Avatar::delete((string) ($targetAccount['avatar_path'] ?? ''));
- }
-
- $flashMessage = 'Changes saved.';
- }
- }
- }
-
- if ($action === 'delete_account') {
- $targetId = (int) ($_POST['account_id'] ?? 0);
-
- $targetStatement = $db->prepare('SELECT * FROM team_accounts WHERE id = :id LIMIT 1');
- $targetStatement->execute(['id' => $targetId]);
- $targetAccount = $targetStatement->fetch();
-
- if (!$targetAccount) {
- $flashMessage = 'We couldn’t find this account.';
- $flashType = 'error';
- } elseif ($targetAccount['role'] === Auth::ROLE_SUPER_ADMIN) {
- $flashMessage = 'The Super Admin account can’t be deleted.';
- $flashType = 'error';
- } elseif ((int) $targetAccount['id'] === (int) $currentUser['id']) {
- $flashMessage = 'You can’t delete your own account.';
- $flashType = 'error';
- } elseif (!$canManageAccount($targetAccount)) {
- $flashMessage = 'You can only delete accounts with a lower role than yours.';
- $flashType = 'error';
- } elseif (!in_array($_POST['content_action'] ?? '', ['keep', 'delete'], true)) {
- $flashMessage = 'Choose what to do with this person’s articles and pages.';
- $flashType = 'error';
- } else {
- $deleteContent = $_POST['content_action'] === 'delete';
- $authorName = mb_substr((string) $targetAccount['display_name'], 0, 120);
- $removedArticles = [];
-
- try {
- $db->beginTransaction();
-
- if ($deleteContent) {
- $articleStatement = $db->prepare('SELECT id, slug, status, deleted_at FROM articles WHERE author_id = :id');
- $articleStatement->execute(['id' => $targetId]);
- $removedArticles = $articleStatement->fetchAll();
-
- foreach ($removedArticles as $removedArticle) {
- $db->prepare('DELETE FROM article_versions WHERE article_id = :id')->execute(['id' => $removedArticle['id']]);
- $db->prepare('DELETE FROM article_tags WHERE article_id = :id')->execute(['id' => $removedArticle['id']]);
- $db->prepare("DELETE FROM content_translations WHERE entity_type = 'article' AND entity_id = :id")->execute(['id' => $removedArticle['id']]);
- $db->prepare('DELETE FROM articles WHERE id = :id')->execute(['id' => $removedArticle['id']]);
- }
-
- $pageStatement = $db->prepare('SELECT id FROM pages WHERE author_id = :id');
- $pageStatement->execute(['id' => $targetId]);
- foreach ($pageStatement->fetchAll(PDO::FETCH_COLUMN) as $removedPageId) {
- $db->prepare("DELETE FROM content_translations WHERE entity_type = 'page' AND entity_id = :id")->execute(['id' => $removedPageId]);
- $db->prepare('DELETE FROM pages WHERE id = :id')->execute(['id' => $removedPageId]);
- }
- }
-
- // Whatever is left is handed over to the system; only the author's name is kept.
- $db->prepare('UPDATE articles SET author_name = COALESCE(author_name, :name), author_id = NULL WHERE author_id = :id')
- ->execute(['name' => $authorName, 'id' => $targetId]);
- $db->prepare('UPDATE pages SET author_name = COALESCE(author_name, :name), author_id = NULL WHERE author_id = :id')
- ->execute(['name' => $authorName, 'id' => $targetId]);
- $db->prepare('UPDATE ads SET created_by = NULL WHERE created_by = :id')->execute(['id' => $targetId]);
- $db->prepare('UPDATE article_revisions SET editor_id = NULL WHERE editor_id = :id')->execute(['id' => $targetId]);
-
- $db->prepare('DELETE FROM team_accounts WHERE id = :id')->execute(['id' => $targetId]);
- $db->commit();
- } catch (PDOException $e) {
- if ($db->inTransaction()) {
- $db->rollBack();
- }
- error_log('Publisium: could not delete account ' . $targetId . ': ' . $e->getMessage());
- $flashMessage = 'We couldn’t delete the account, so nothing was changed. Please try again.';
- $flashType = 'error';
- }
-
- if ($flashType !== 'error') {
- Avatar::delete((string) ($targetAccount['avatar_path'] ?? ''));
- try {
- $db->prepare('DELETE FROM author_profiles WHERE account_id = :id')->execute(['id' => $targetId]);
- } catch (PDOException $e) {
- error_log('Publisium: could not remove author profile: ' . $e->getMessage());
- }
-
- foreach ($removedArticles as $removedArticle) {
- if ($removedArticle['status'] === 'published' && $removedArticle['deleted_at'] === null) {
- IndexNow::notifyRemoved((string) $removedArticle['slug'], (int) $removedArticle['id']);
- }
- }
-
- ActivityLog::record('account.delete', 'account', $targetId, $authorName . ($deleteContent ? ' [content deleted]' : ' [content kept by system]'));
- $flashMessage = $deleteContent
- ? 'Account deleted, along with its articles and pages.'
- : 'Account deleted. Their articles and pages stay on the site under the name "' . $authorName . '".';
- }
- }
- }
- }
- }
-
- $contentCounts = [];
- foreach (['articles' => 'articles', 'pages' => 'pages'] as $countKey => $countTable) {
- foreach ($db->query('SELECT author_id, COUNT(*) AS total FROM ' . $countTable . ' WHERE author_id IS NOT NULL GROUP BY author_id')->fetchAll() as $countRow) {
- $contentCounts[(int) $countRow['author_id']][$countKey] = (int) $countRow['total'];
- }
- }
-
- $accounts = $db->query('SELECT * FROM team_accounts ORDER BY FIELD(role, \'super_admin\',\'editor_in_chief\',\'managing_editor\',\'senior_writer\',\'writer\',\'proofreader\'), display_name ASC')->fetchAll();
-
- $readers = [];
- if ($isSuperAdmin) {
- $readers = $usersDb->query('SELECT * FROM user_accounts ORDER BY created_at DESC')->fetchAll();
- }
-
- $dashActivePage = 'admin';
- $dashPageTitle = 'Team';
-
- require __DIR__ . '/includes/dash-header.php';
-
- ?>
-
- <?php if ($flashMessage !== null): ?>
- <div class="dash-flash dash-flash-<?= htmlspecialchars($flashType) ?>"><?= Icons::icon($flashType === 'error' ? 'x' : 'check', 'icon icon-sm') ?><?= htmlspecialchars($flashMessage) ?></div>
- <?php endif; ?>
-
- <h1 class="dash-title"><?= Icons::icon("users", "icon icon-lg") ?>Accounts</h1>
-
- <?php if ($isSuperAdmin): ?>
- <div class="settings-tabs">
- <a href="admin.php?tab=team" class="<?= $activeTab === 'team' ? 'active' : '' ?>"><?= Icons::icon("team", "icon icon-sm") ?>Team</a>
- <a href="admin.php?tab=readers" class="<?= $activeTab === 'readers' ? 'active' : '' ?>"><?= Icons::icon("book", "icon icon-sm") ?>Readers</a>
- </div>
- <?php endif; ?>
-
- <?php if ($activeTab === 'readers' && $isSuperAdmin): ?>
-
- <table class="dash-table">
- <thead>
- <tr>
- <th><?= Icons::icon('heading', 'icon icon-sm') ?>Name</th>
- <th><?= Icons::icon('user', 'icon icon-sm') ?>Email</th>
- <th><?= Icons::icon('flag', 'icon icon-sm') ?>Status</th>
- <th><?= Icons::icon('stats', 'icon icon-sm') ?>Registered</th>
- <th><?= Icons::icon('eye', 'icon icon-sm') ?>Last login</th>
- <th></th>
- </tr>
- </thead>
- <tbody>
- <?php foreach ($readers as $reader): ?>
- <tr>
- <td><?= htmlspecialchars((string) ($reader['display_name'] ?? 'No name')) ?></td>
- <td><?= htmlspecialchars($reader['email']) ?></td>
- <td><span class="status-pill status-<?= $reader['status'] === 'active' ? 'published' : 'rejected' ?>"><?= htmlspecialchars($reader['status']) ?></span></td>
- <td><?= htmlspecialchars($reader['created_at']) ?></td>
- <td><?= htmlspecialchars((string) ($reader['last_login_at'] ?? 'Never')) ?></td>
- <td class="dash-table-actions">
- <?php if ($reader['status'] === 'active'): ?>
- <form method="post" style="display:inline;" onsubmit="return confirm('Suspend this reader? They’ll be signed out and won’t be able to sign in.');">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="suspend_reader">
- <input type="hidden" name="reader_id" value="<?= (int) $reader['id'] ?>">
- <button type="submit" class="dash-btn-small dash-btn-danger"><?= Icons::icon('lock', 'icon icon-sm') ?>Suspend</button>
- </form>
- <?php else: ?>
- <form method="post" style="display:inline;">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="reactivate_reader">
- <input type="hidden" name="reader_id" value="<?= (int) $reader['id'] ?>">
- <button type="submit" class="dash-btn-small dash-btn-success"><?= Icons::icon('check', 'icon icon-sm') ?>Reactivate</button>
- </form>
- <?php endif; ?>
- </td>
- </tr>
- <?php endforeach; ?>
- <?php if (empty($readers)): ?>
- <tr>
- <td colspan="6">No readers have signed up yet.</td>
- </tr>
- <?php endif; ?>
- </tbody>
- </table>
-
- <?php else: ?>
-
- <table class="dash-table">
- <thead>
- <tr>
- <th><?= Icons::icon('heading', 'icon icon-sm') ?>Name</th>
- <th><?= Icons::icon('user', 'icon icon-sm') ?>Username</th>
- <th><?= Icons::icon('hash', 'icon icon-sm') ?>Email</th>
- <th><?= Icons::icon('team', 'icon icon-sm') ?>Role</th>
- <th><?= Icons::icon('flag', 'icon icon-sm') ?>Status</th>
- <th><?= Icons::icon('lock', 'icon icon-sm') ?>2FA</th>
- <th><?= Icons::icon('eye', 'icon icon-sm') ?>Last login</th>
- <th></th>
- </tr>
- </thead>
- <tbody>
- <?php foreach ($accounts as $account): ?>
- <tr>
- <td><span class="dash-avatar-inline"><?= Avatar::html($account, 'dash-user-avatar') ?><?= htmlspecialchars($account['display_name']) ?></span></td>
- <td><?= htmlspecialchars($account['username']) ?></td>
- <td><?= htmlspecialchars((string) $account['email']) ?></td>
- <td><?= htmlspecialchars(Auth::roleLabel($account['role'])) ?></td>
- <td><?= htmlspecialchars($account['status']) ?></td>
- <td><?= TwoFactor::isEnabled($account) ? 'On' : 'Off' ?></td>
- <td><?= htmlspecialchars((string) ($account['last_login_at'] ?? 'Never')) ?></td>
- <td class="dash-table-actions">
- <?php if ($canManageAccount($account) || (int) $account['id'] === (int) $currentUser['id']): ?>
- <button type="button" class="dash-btn-small js-edit-account"
- data-id="<?= (int) $account['id'] ?>"
- data-display-name="<?= htmlspecialchars($account['display_name'], ENT_QUOTES) ?>"
- data-avatar="<?= htmlspecialchars(Avatar::isValidPath((string) ($account['avatar_path'] ?? '')) ? (string) $account['avatar_path'] : '', ENT_QUOTES) ?>"
- data-initial="<?= htmlspecialchars(Avatar::initial((string) $account['display_name']), ENT_QUOTES) ?>"
- data-role="<?= htmlspecialchars($account['role'], ENT_QUOTES) ?>"
- data-status="<?= htmlspecialchars($account['status'], ENT_QUOTES) ?>"><?= Icons::icon('edit', 'icon icon-sm') ?>Edit</button>
- <?php endif; ?>
- <?php if ($canManageAccount($account) && (int) $account['id'] !== (int) $currentUser['id']): ?>
- <button type="button" class="dash-btn-small dash-btn-danger js-delete-account"
- data-id="<?= (int) $account['id'] ?>"
- data-display-name="<?= htmlspecialchars($account['display_name'], ENT_QUOTES) ?>"
- data-articles="<?= (int) ($contentCounts[(int) $account['id']]['articles'] ?? 0) ?>"
- data-pages="<?= (int) ($contentCounts[(int) $account['id']]['pages'] ?? 0) ?>"><?= Icons::icon('trash', 'icon icon-sm') ?>Delete</button>
- <?php endif; ?>
- </td>
- </tr>
- <?php endforeach; ?>
- </tbody>
- </table>
-
- <h2 class="dash-subtitle"><?= Icons::icon('plus', 'icon icon-sm') ?>Create account</h2>
- <form method="post" enctype="multipart/form-data">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="create_account">
-
- <label><?= Icons::icon('user', 'icon icon-sm') ?>Username</label>
- <input type="text" name="username" required>
-
- <label><?= Icons::icon('hash', 'icon icon-sm') ?>Email</label>
- <input type="text" name="email" required>
-
- <label><?= Icons::icon('heading', 'icon icon-sm') ?>Display name</label>
- <input type="text" name="display_name">
-
- <label><?= Icons::icon('lock', 'icon icon-sm') ?>Password</label>
- <input type="password" name="password" minlength="10" required>
-
- <label><?= Icons::icon('user', 'icon icon-sm') ?>Profile photo (optional, JPG, PNG or WebP, up to 2 MB)</label>
- <input type="file" name="avatar" accept="image/jpeg,image/png,image/webp">
-
- <label><?= Icons::icon('team', 'icon icon-sm') ?>Role</label>
- <select name="role" required>
- <?php foreach ($assignableRoles as $role): ?>
- <?php if ($role === Auth::ROLE_SUPER_ADMIN) {
- continue;
- } ?>
- <option value="<?= htmlspecialchars($role) ?>"><?= htmlspecialchars(Auth::roleLabel($role)) ?></option>
- <?php endforeach; ?>
- </select>
-
- <button type="submit" class="dash-btn dash-btn-primary" style="margin-top:16px;"><?= Icons::icon('plus', 'icon icon-sm') ?>Create account</button>
- </form>
-
- <h2 class="dash-subtitle" id="edit-account-title" style="display:none;"><?= Icons::icon('edit', 'icon icon-sm') ?>Edit account</h2>
- <form method="post" id="edit-account-form" style="display:none;" enctype="multipart/form-data">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="update_account">
- <input type="hidden" name="account_id" id="edit_account_id">
-
- <label><?= Icons::icon('user', 'icon icon-sm') ?>Profile photo (JPG, PNG or WebP, up to 2 MB)</label>
- <div class="avatar-edit-preview">
- <div class="dash-user-avatar" id="edit_avatar_preview"></div>
- <input type="file" name="avatar" accept="image/jpeg,image/png,image/webp">
- </div>
- <label><input type="checkbox" name="remove_avatar" id="edit_remove_avatar"> Remove the photo (their first initial is shown instead)</label>
-
- <label><?= Icons::icon('heading', 'icon icon-sm') ?>Display name</label>
- <input type="text" name="display_name" id="edit_display_name">
-
- <label><?= Icons::icon('team', 'icon icon-sm') ?>Role</label>
- <select name="role" id="edit_role">
- <?php $editableRoleOptions = $isSuperAdmin ? array_merge($assignableRoles, [Auth::ROLE_SUPER_ADMIN]) : $assignableRoles; ?>
- <?php foreach (array_unique($editableRoleOptions) as $role): ?>
- <option value="<?= htmlspecialchars($role) ?>"><?= htmlspecialchars(Auth::roleLabel($role)) ?></option>
- <?php endforeach; ?>
- </select>
-
- <label><?= Icons::icon('flag', 'icon icon-sm') ?>Status</label>
- <select name="status" id="edit_status">
- <option value="active">Active</option>
- <option value="suspended">Suspended</option>
- </select>
-
- <label><?= Icons::icon('lock', 'icon icon-sm') ?>New password (leave empty to keep the current one)</label>
- <input type="password" name="new_password" minlength="10">
-
- <label><input type="checkbox" name="reset_2fa" id="edit_reset_2fa"> Reset two-factor login (if they lost their phone and their recovery codes)</label>
-
- <div class="publish-actions">
- <button type="submit" class="dash-btn dash-btn-primary"><?= Icons::icon('check', 'icon icon-sm') ?>Save changes</button>
- <button type="button" class="dash-btn" onclick="document.getElementById('edit-account-form').style.display='none';document.getElementById('edit-account-title').style.display='none';"><?= Icons::icon('x', 'icon icon-sm') ?>Cancel</button>
- </div>
- </form>
-
- <h2 class="dash-subtitle" id="delete-account-title" style="display:none;"><?= Icons::icon('trash', 'icon icon-sm') ?>Delete account: <span id="delete_account_name"></span></h2>
- <form method="post" id="delete-account-form" style="display:none;">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="delete_account">
- <input type="hidden" name="account_id" id="delete_account_id">
-
- <p id="delete_account_summary" style="margin:0 0 12px;"></p>
- <p style="margin:0 0 8px;font-weight:600;">Do you also want to delete everything this person wrote?</p>
-
- <label><input type="radio" name="content_action" value="keep" id="delete_content_keep" checked> No, keep their articles and pages on the site. Their name stays on everything they wrote.</label>
- <label><input type="radio" name="content_action" value="delete" id="delete_content_delete"> Yes, delete all their articles and pages for good, including anything in the trash.</label>
-
- <div class="publish-actions">
- <button type="submit" class="dash-btn dash-btn-danger"><?= Icons::icon('trash', 'icon icon-sm') ?>Delete account</button>
- <button type="button" class="dash-btn" onclick="document.getElementById('delete-account-form').style.display='none';document.getElementById('delete-account-title').style.display='none';"><?= Icons::icon('x', 'icon icon-sm') ?>Cancel</button>
- </div>
- </form>
-
- <script>
- document.querySelectorAll('.js-delete-account').forEach(function(button) {
- button.addEventListener('click', function() {
- var articles = parseInt(this.dataset.articles, 10) || 0;
- var pages = parseInt(this.dataset.pages, 10) || 0;
- document.getElementById('edit-account-form').style.display = 'none';
- document.getElementById('edit-account-title').style.display = 'none';
- document.getElementById('delete-account-title').style.display = '';
- document.getElementById('delete-account-form').style.display = '';
- document.getElementById('delete_account_id').value = this.dataset.id;
- document.getElementById('delete_account_name').textContent = this.dataset.displayName;
- document.getElementById('delete_account_summary').textContent = 'They have ' + articles + (articles === 1 ? ' article' : ' articles') + ' and ' + pages + (pages === 1 ? ' page' : ' pages') + '.';
- document.getElementById('delete_content_keep').checked = true;
- document.getElementById('delete-account-form').scrollIntoView({
- behavior: 'smooth'
- });
- });
- });
-
- document.getElementById('delete-account-form').addEventListener('submit', function(event) {
- var deleteContent = document.getElementById('delete_content_delete').checked;
- var message = deleteContent
- ? 'Delete this account and all of its articles and pages? You won’t be able to get them back.'
- : 'Delete this account? Their articles and pages stay on the site.';
- if (!window.confirm(message)) {
- event.preventDefault();
- }
- });
-
- document.querySelectorAll('.js-edit-account').forEach(function(button) {
- button.addEventListener('click', function() {
- document.getElementById('delete-account-form').style.display = 'none';
- document.getElementById('delete-account-title').style.display = 'none';
- document.getElementById('edit-account-title').style.display = '';
- document.getElementById('edit-account-form').style.display = '';
- document.getElementById('edit_account_id').value = this.dataset.id;
- document.getElementById('edit_display_name').value = this.dataset.displayName;
- document.getElementById('edit_role').value = this.dataset.role;
- document.getElementById('edit_status').value = this.dataset.status;
- document.getElementById('edit_remove_avatar').checked = false;
- document.getElementById('edit_reset_2fa').checked = false;
-
- var preview = document.getElementById('edit_avatar_preview');
- preview.textContent = '';
- preview.classList.toggle('has-image', this.dataset.avatar !== '');
- if (this.dataset.avatar !== '') {
- var image = document.createElement('img');
- image.src = this.dataset.avatar;
- image.alt = '';
- preview.appendChild(image);
- } else {
- preview.textContent = this.dataset.initial;
- }
- document.getElementById('edit-account-form').scrollIntoView({
- behavior: 'smooth'
- });
- });
- });
- </script>
-
- <?php endif; ?>
-
- <?php require __DIR__ . '/includes/dash-footer.php'; ?>