Files
www-data 475765e31f Move rendering into private templates
Add an explicit template renderer with HTML views and partials for the app, bootstrap, package, and catalog pages. Move shared reporting setup into config/reporting.php and relocate stylesheet assets under css/.
2026-05-26 12:50:26 +02:00

230 lines
8.0 KiB
PHP

<?php
/*
* users.php
*
* Admin user management.
*/
require_once __DIR__ . '/private/auth.php';
require_once __DIR__ . '/private/header.php';
require_once __DIR__ . '/private/languagestore.php';
require_once __DIR__ . '/private/usersettings.php';
require_once __DIR__ . '/private/viewdata.php';
require_once __DIR__ . '/config/reporting.php';
$DB_FILE = __DIR__ . '/data/racket-sandbox.sqlite';
$auth = new RacketSandboxAuth($DB_FILE);
$languageStore = new LanguageStore($DB_FILE);
$userSettings = new UserSettingsStore($DB_FILE);
$currentUser = $auth->requireAdminHtml();
$message = '';
$error = '';
function h($s)
{
return htmlspecialchars((string)$s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
function t($key, $fallback = null, $values = array())
{
global $languageStore, $language;
return $languageStore->translateFormat($key, $language, $values, $fallback);
}
function post_value($name, $default = '')
{
return $_POST[$name] ?? $default;
}
function post_bool($name)
{
return isset($_POST[$name]) && $_POST[$name] === '1';
}
function resolve_user_language($userSettings, $userId, $allowedLanguages)
{
$language = isset($_GET['lang'])
? (string)$_GET['lang']
: (string)$userSettings->get($userId, 'language', 'en');
if (!in_array($language, $allowedLanguages, true)) {
$language = 'en';
}
$userSettings->set($userId, 'language', $language);
return $language;
}
function fmt_time($ts)
{
if ($ts === null) {
return '-';
}
return date('Y-m-d H:i:s', (int)$ts);
}
$language = resolve_user_language(
$userSettings,
$currentUser->id(),
$languageStore->supportedLanguages()
);
seed_template_translations($languageStore, 'users.html');
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = post_value('action');
try {
if ($action === 'logout') {
$auth->logout();
header('Location: /login.php');
exit;
} elseif ($action === 'create_user') {
$auth->createUser(
post_value('email'),
post_value('full_name'),
post_value('password'),
post_bool('is_admin'),
post_bool('is_enabled')
);
$message = t('app.user_created', 'User created.');
} elseif ($action === 'update_user') {
$userId = (int)post_value('user_id');
$isAdmin = post_bool('is_admin');
$isEnabled = post_bool('is_enabled');
if ($userId === $currentUser->id() && !$isAdmin) {
throw new Exception(t('app.cannot_remove_own_admin', 'You cannot remove your own admin rights.'));
}
if ($userId === $currentUser->id() && !$isEnabled) {
throw new Exception(t('app.cannot_disable_self', 'You cannot disable your own account.'));
}
$auth->updateUser($userId, post_value('email'), post_value('full_name'));
$auth->setAdmin($userId, $isAdmin);
$auth->setEnabled($userId, $isEnabled);
$message = t('app.user_updated', 'User updated.');
} elseif ($action === 'set_password') {
$auth->setPassword(post_value('email'), post_value('password'));
$message = t('app.password_changed', 'Password changed.');
} elseif ($action === 'delete_user') {
$userId = (int)post_value('user_id');
if ($userId === $currentUser->id()) {
throw new Exception(t('app.cannot_delete_self', 'You cannot delete your own account.'));
}
$auth->deleteUser($userId);
$message = t('app.user_deleted', 'User deleted.');
}
} catch (Throwable $e) {
$error = $e->getMessage();
}
}
$users = $auth->listUsers();
$headerLanguages = array();
foreach ($languageStore->supportedLanguages() as $lang) {
$headerLanguages[$lang] = $languageStore->languageLabel($lang);
}
header('Content-Type: text/html; charset=utf-8');
$userRowsHtml = '';
foreach ($users as $managedUser) {
if ($managedUser->id() !== $currentUser->id()) {
$deleteHtml = RacketSandboxTemplate::renderFile('partials/user-delete-form.html', array(
'language_url' => rawurlencode($language),
'confirm_json' => json_encode(t('app.delete_user_confirm', 'Delete user {{email}}?', array(
'email' => $managedUser->email(),
)), JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT),
'email' => $managedUser->email(),
'user_id' => $managedUser->id(),
'delete_user_label' => t('app.delete_user', 'Delete user'),
));
} else {
$deleteHtml = RacketSandboxTemplate::renderFile('partials/user-self-note.html', array(
'cannot_delete_self' => t('app.cannot_delete_self', 'You cannot delete your own account.'),
));
}
$userRowsHtml .= RacketSandboxTemplate::renderFile('partials/user-row.html', array(
'language_url' => rawurlencode($language),
'user_id' => $managedUser->id(),
'full_name_label' => t('app.full_name', 'Full name'),
'full_name' => $managedUser->fullName(),
'email_label' => t('app.email', 'Email'),
'email' => $managedUser->email(),
'admin_label' => t('app.admin', 'Admin'),
'enabled_label' => t('app.enabled', 'Enabled'),
'is_admin_checked' => $managedUser->isAdmin() ? ' checked' : '',
'is_enabled_checked' => $managedUser->isEnabled() ? ' checked' : '',
'created_at' => fmt_time($managedUser->createdAt()),
'last_login_at' => fmt_time($managedUser->lastLoginAt()),
'update_user_label' => t('app.update_user', 'Update user'),
'new_password_label' => t('app.new_password', 'New password'),
'change_password_label' => t('app.change_password', 'Change password'),
'delete_html' => $deleteHtml,
)) . "\n";
}
$headerHtml = app_header_html(array(
'title' => t('app.user_management', 'User management'),
'nav_items' => array(
array('label' => t('app.back_to_sandbox', 'Back to Racket sandbox'), 'url' => '/?lang=' . rawurlencode($language)),
array(
'label' => t('app.manage_prompts', 'Manage prompts'),
'url' => '/prompts?lang=' . rawurlencode($language),
'separator_before' => true,
),
array(
'label' => t('app.user_management', 'User management'),
'url' => '/users?lang=' . rawurlencode($language),
'active' => true,
'separator_before' => true,
),
array(
'label' => t('app.configuration', 'Configuration'),
'url' => '/admin-config?lang=' . rawurlencode($language),
'separator_before' => true,
),
),
'user' => $currentUser,
'user_prefix' => t('app.logged_in_as', 'Logged in as:'),
'admin_label' => t('app.admin', 'Admin'),
'language_label' => t('app.language', 'Language'),
'language' => $language,
'languages' => $headerLanguages,
'language_action' => '/users',
'logout_action' => '/users?lang=' . rawurlencode($language),
'logout_label' => t('app.logout', 'Logout'),
'message' => $message,
'error' => $error,
));
echo RacketSandboxTemplate::renderFile('users.html', array(
'language' => $language,
'language_url' => rawurlencode($language),
'title' => t('app.user_management', 'User management'),
'header_html' => $headerHtml,
'create_user_label' => t('app.create_user', 'Create user'),
'full_name_label' => t('app.full_name', 'Full name'),
'email_label' => t('app.email', 'Email'),
'password_label' => t('app.password', 'Password'),
'admin_label' => t('app.admin', 'Admin'),
'enabled_label' => t('app.enabled', 'Enabled'),
'user_management_label' => t('app.user_management', 'User management'),
'created_label' => t('app.created', 'Created'),
'last_login_label' => t('app.last_login', 'Last login'),
'actions_label' => t('app.actions', 'Actions'),
'user_rows_html' => $userRowsHtml,
));