translate($key, $language, $fallback); } function current_scheme() { if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) { return strtolower(trim(explode(',', $_SERVER['HTTP_X_FORWARDED_PROTO'])[0])); } return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; } function current_host() { return $_SERVER['HTTP_HOST'] ?? 'localhost'; } function bootstrap_link_for_token($token) { return current_scheme() . '://' . current_host() . '/bootstrap-racket?next=' . rawurlencode($token); } function post_value($name, $default = '') { return $_POST[$name] ?? $default; } function post_bool($name) { return isset($_POST[$name]) && $_POST[$name] === '1'; } function create_next_token($tokens, $ttlSeconds, $meta) { /* * Ondersteunt zowel: * create($ttl) * als: * create($ttl, $meta) */ $rm = new ReflectionMethod($tokens, 'create'); if ($rm->getNumberOfParameters() >= 2) { return $tokens->create($ttlSeconds, $meta); } return $tokens->create($ttlSeconds); } function clamp_bootstrap_ttl_minutes($ttlMinutes) { $ttlMinutes = (int)$ttlMinutes; if ($ttlMinutes < BOOTSTRAP_TTL_MIN_MINUTES) { return BOOTSTRAP_TTL_MIN_MINUTES; } if ($ttlMinutes > BOOTSTRAP_TTL_MAX_MINUTES) { return BOOTSTRAP_TTL_MAX_MINUTES; } return $ttlMinutes; } 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; } $currentUser = $auth->currentUser(); if ($currentUser === null) { header('Location: /login.php'); exit; } $language = resolve_user_language( $userSettings, $currentUser->id(), $languageStore->supportedLanguages() ); $languageStore->seedDefaults(array( 'app.title' => array('en' => 'Racket sandbox', 'nl' => 'Racket sandbox'), 'app.manage_prompts' => array('en' => 'Manage prompts', 'nl' => 'Prompts beheren'), 'app.logout' => array('en' => 'Logout', 'nl' => 'Uitloggen'), 'app.language' => array('en' => 'Language', 'nl' => 'Taal'), 'app.logged_in_as' => array('en' => 'Logged in as:', 'nl' => 'Ingelogd als:'), 'app.admin' => array('en' => 'admin', 'nl' => 'admin'), 'app.bootstrap_link' => array('en' => 'Bootstrap link', 'nl' => 'Bootstraplink'), 'app.generate_bootstrap_link' => array('en' => 'Generate bootstrap link', 'nl' => 'Bootstraplink genereren'), 'app.ttl_minutes' => array('en' => 'TTL in minutes', 'nl' => 'TTL in minuten'), 'app.ttl_range_help' => array('en' => 'Allowed range: 30 minutes to 8 hours.', 'nl' => 'Toegestaan bereik: 30 minuten tot 8 uur.'), 'app.generated_link' => array('en' => 'Generated link', 'nl' => 'Gegenereerde link'), 'app.copy' => array('en' => 'Copy', 'nl' => 'Kopieer'), 'app.copied' => array('en' => 'Copied', 'nl' => 'Gekopieerd'), 'app.generated_link_help' => array( 'en' => 'Give this link to the AI agent. The agent should start from this link and then only follow links from the generated HTML pages.', 'nl' => 'Geef deze link aan de AI-agent. De agent moet vanaf deze link starten en daarna alleen links volgen vanuit de gegenereerde HTML-paginas.' ), 'app.select_prompt' => array('en' => 'Select prompt', 'nl' => 'Prompt selecteren'), 'app.copy_full_prompt' => array('en' => 'Copy full prompt', 'nl' => 'Volledige prompt kopieren'), 'app.bootstrap_prompt_help' => array( 'en' => 'Choose one of your prompts. The placeholder {{bootstrap-racket-link}} is replaced by the generated bootstrap link.', 'nl' => 'Kies een van je prompts. De placeholder {{bootstrap-racket-link}} wordt vervangen door de gegenereerde bootstraplink.' ), 'app.no_bootstrap_prompts' => array( 'en' => 'No personal prompts are available for this language. Copy a default prompt first from prompt management.', 'nl' => 'Er zijn geen persoonlijke prompts beschikbaar voor deze taal. Kopieer eerst een standaardprompt vanuit promptbeheer.' ), 'app.user_management' => array('en' => 'User management', 'nl' => 'Gebruikersbeheer'), 'app.configuration' => array('en' => 'Configuration', 'nl' => 'Configuratie'), 'app.user_management_help' => array( 'en' => 'Users are registered manually by email address, full name and password. This page only manages existing users.', 'nl' => 'Gebruikers worden handmatig geregistreerd met e-mailadres, volledige naam en wachtwoord. Deze pagina beheert alleen bestaande gebruikers.' ), 'app.id' => array('en' => 'ID', 'nl' => 'ID'), 'app.full_name' => array('en' => 'Full name', 'nl' => 'Volledige naam'), 'app.email' => array('en' => 'Email', 'nl' => 'E-mail'), 'app.enabled' => array('en' => 'Enabled', 'nl' => 'Ingeschakeld'), 'app.created' => array('en' => 'Created', 'nl' => 'Gemaakt'), 'app.last_login' => array('en' => 'Last login', 'nl' => 'Laatste login'), 'app.actions' => array('en' => 'Actions', 'nl' => 'Acties'), 'app.yes' => array('en' => 'yes', 'nl' => 'ja'), 'app.no' => array('en' => 'no', 'nl' => 'nee'), 'app.save_flags' => array('en' => 'Save flags', 'nl' => 'Vlaggen opslaan'), 'app.new_password' => array('en' => 'New password', 'nl' => 'Nieuw wachtwoord'), 'app.change_password' => array('en' => 'Change password', 'nl' => 'Wachtwoord wijzigen'), 'app.delete_user' => array('en' => 'Delete user', 'nl' => 'Gebruiker verwijderen'), 'app.delete_user_confirm' => array('en' => 'Delete user', 'nl' => 'Gebruiker verwijderen'), 'app.cannot_delete_self' => array('en' => 'You cannot delete your own account.', 'nl' => 'Je kunt je eigen account niet verwijderen.'), 'app.bootstrap_link_issued' => array('en' => 'Bootstrap link issued.', 'nl' => 'Bootstraplink aangemaakt.'), 'app.password_changed_for' => array('en' => 'Password changed for:', 'nl' => 'Wachtwoord gewijzigd voor:'), 'app.user_flags_updated' => array('en' => 'User flags updated.', 'nl' => 'Gebruikersvlaggen bijgewerkt.'), 'app.user_deleted' => array('en' => 'User deleted.', 'nl' => 'Gebruiker verwijderd.'), 'app.admin_rights_required' => array('en' => 'Admin rights required.', 'nl' => 'Adminrechten vereist.'), 'app.cannot_remove_own_admin' => array('en' => 'You cannot remove your own admin rights.', 'nl' => 'Je kunt je eigen adminrechten niet verwijderen.'), 'app.cannot_disable_self' => array('en' => 'You cannot disable your own account.', 'nl' => 'Je kunt je eigen account niet uitschakelen.'), 'app.unknown_action' => array('en' => 'Unknown action:', 'nl' => 'Onbekende actie:'), )); if ($_SERVER['REQUEST_METHOD'] === 'POST') { $action = post_value('action'); try { if ($action === 'logout') { $auth->logout(); header('Location: /login.php'); exit; } $currentUser = $auth->currentUser(); if ($currentUser === null) { header('Location: /login.php'); exit; } if ($action === 'issue_bootstrap') { $ttlMinutes = clamp_bootstrap_ttl_minutes( post_value('ttl_minutes', (string)BOOTSTRAP_TTL_DEFAULT_MINUTES) ); $userSettings->set($currentUser->id(), 'bootstrap_ttl_minutes', (string)$ttlMinutes); $token = create_next_token( $tokens, $ttlMinutes * 60, array( 'user_id' => $currentUser->id(), 'email' => $currentUser->email(), 'full_name' => $currentUser->fullName(), 'purpose' => 'racket-bootstrap', ) ); $issuedLink = bootstrap_link_for_token($token); $message = t('app.bootstrap_link_issued', 'Bootstrap link issued.'); } else { if (!$currentUser->isAdmin()) { throw new Exception(t('app.admin_rights_required', 'Admin rights required.')); } if ($action === 'set_password') { $email = trim(post_value('email')); $password = (string)post_value('password'); $auth->setPassword($email, $password); $message = t('app.password_changed_for', 'Password changed for:') . ' ' . $email; } elseif ($action === 'set_flags') { $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->setAdmin($userId, $isAdmin); $auth->setEnabled($userId, $isEnabled); $message = t('app.user_flags_updated', 'User flags updated.'); } elseif ($action === 'delete_user') { $userId = (int)post_value('user_id'); if ($userId === $currentUser->id()) { throw new Exception('You cannot delete your own account.'); } $auth->deleteUser($userId); $message = t('app.user_deleted', 'User deleted.'); } elseif ($action !== '') { throw new Exception(t('app.unknown_action', 'Unknown action:') . ' ' . $action); } } } catch (Throwable $e) { $error = $e->getMessage(); } } $currentUser = $auth->currentUser(); if ($currentUser === null) { header('Location: /login.php'); exit; } $bootstrapTtlMinutes = clamp_bootstrap_ttl_minutes( $userSettings->get( $currentUser->id(), 'bootstrap_ttl_minutes', (string)BOOTSTRAP_TTL_DEFAULT_MINUTES ) ); foreach ($promptStore->listPrompts($currentUser->id(), $language) as $prompt) { $bootstrapPrompts[] = array( 'id' => $prompt->id(), 'name' => $prompt->name(), 'content' => $prompt->content(), ); } $headerLanguages = array(); foreach ($languageStore->supportedLanguages() as $lang) { $headerLanguages[$lang] = $languageStore->languageLabel($lang); } $headerNavItems = array( array( 'label' => t('app.manage_prompts', 'Manage prompts'), 'url' => '/prompts?lang=' . rawurlencode($language), ), ); if ($currentUser->isAdmin()) { $headerNavItems[] = array( 'label' => t('app.user_management', 'User management'), 'url' => '/users?lang=' . rawurlencode($language), 'separator_before' => true, ); $headerNavItems[] = array( 'label' => t('app.configuration', 'Configuration'), 'url' => '/admin-config?lang=' . rawurlencode($language), 'separator_before' => true, ); } header('Content-Type: text/html; charset=utf-8'); ?>