diff options
author | Jonas Kohl | 2024-09-15 19:55:12 +0200 |
---|---|---|
committer | Jonas Kohl | 2024-09-15 19:55:12 +0200 |
commit | cb9b87997993702131ca24d4d0e1fd45ef64805c (patch) | |
tree | 3ba1725028665f90b546703c461394b577b3e596 | |
parent | cc97f36b8c9a9522636d5b50fbcd2f52de06a01a (diff) |
Begun i18n work & more
-rw-r--r-- | .env.example | 4 | ||||
-rw-r--r-- | Dockerfile | 11 | ||||
-rw-r--r-- | src/application/i18n.php | 98 | ||||
-rw-r--r-- | src/application/messages/de.msg | 117 | ||||
-rw-r--r-- | src/application/messages/strings.mst | 111 | ||||
-rw-r--r-- | src/application/mystic/forum/Database.php | 23 | ||||
-rw-r--r-- | src/application/views/form_login.php | 2 | ||||
-rw-r--r-- | src/application/views/form_register.php | 5 | ||||
-rw-r--r-- | src/application/views/nav_guest.php | 4 | ||||
-rw-r--r-- | src/index.php | 159 | ||||
-rw-r--r-- | src/ui/site.css | 3 |
11 files changed, 477 insertions, 60 deletions
diff --git a/.env.example b/.env.example index 0eba495..7682611 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,10 @@ +SECRET= POSTGRES_USER=postgres POSTGRES_DB=postgres POSTGRES_PASSWORD=postgres +MAILER_DSN= +MAILER_FROM= +PUBLIC_URL= #MYSTIC_FORUM_THEME=default #REGISTRATION_ENABLED=1 #MYSTIC_FORUM_TITLE= @@ -10,13 +10,18 @@ RUN apt update && apt install -y \ libjpeg62-turbo-dev \ libxmp-dev \ libfreetype6-dev \ - libpq-dev + libpq-dev \ + libicu-dev \ + ; RUN docker-php-ext-configure gd \ --with-webp \ --with-jpeg \ --with-freetype -RUN docker-php-ext-install gd zip pgsql -#RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer +RUN docker-php-ext-install gd zip pgsql intl +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer FROM base COPY ./000-default.conf /etc/apache2/sites-available/000-default.conf +#COPY ./src /var/www/html +#WORKDIR /var/www/html +#RUN composer install diff --git a/src/application/i18n.php b/src/application/i18n.php new file mode 100644 index 0000000..964439f --- /dev/null +++ b/src/application/i18n.php @@ -0,0 +1,98 @@ +<?php + +const MESSAGE_DIR = __DIR__ . "/messages"; + +$__i18n_msg_store = []; +$__i18n_current_locale = null; + +function i18n_parse(string $contents, ?string $filename = null): array { + $syntax_error = fn(string $msg, ?int $line = null): never => + throw new Exception("i18n syntax error: $msg (in " . ($filename ?? "unknown") . ":" . ($line ?? 0) . ")"); + + $msgs = []; + $lines = explode("\n", $contents); + $currentId = ""; + $currentContext = ""; + $currentMessage = ""; + $isInMessage = false; + foreach ($lines as $i => $ln) { + $lnNum = $i + 1; + if (trim($ln) === "") + continue; + + switch ($ln[0]) { + case "#": + continue 2; + case ":": + if ($currentId !== "") { + if ($currentContext !== "") + $currentId = $currentContext . "\004" . $currentId; + $msgs[$currentId] ??= $currentMessage; + } + $currentId = json_decode(substr($ln, 2)); + $currentContext = ""; + $currentMessage = ""; + $isInMessage = false; + break; + case "=": + $isInMessage = true; + $currentMessage = json_decode(substr($ln, 2)); + break; + case "?": + if ($isInMessage) + $syntax_error("context must be defined before start of message", $lnNum); + $currentContext = json_decode(substr($ln, 2)); + break; + case " ": + if ($isInMessage) + $currentMessage .= json_decode(substr($ln, 2)); + else + $currentId .= json_decode(substr($ln, 2)); + break; + default: + $syntax_error("invalid start of line '" . $ln[0] . "' (0x" . str_pad(strtoupper(dechex(ord($ln[0]))), 2, "0", STR_PAD_LEFT) . ")", $lnNum); + break; + } + } + + if ($currentId !== "") { + if ($currentContext !== "") + $currentId = $currentContext . "\x7F" . $currentId; + $msgs[$currentId] ??= $currentMessage; + } + return $msgs; +} + +function i18n_locale(string $locale) { + global $__i18n_current_locale; + $__i18n_current_locale = $locale; +} + +function i18n_get(string $msgid, array $params = [], ?string $context = null): string { + global $__i18n_current_locale, $__i18n_msg_store; + + $key = $msgid; + if ($context !== null) + $key = $context . "\x7F" . $msgid; + + $msg = ($__i18n_msg_store[$__i18n_current_locale] ?? [])[$msgid] ?? $key; + + uksort($params, fn(string $a, string $b): int => strlen($b) <=> strlen($a)); + return str_replace( + array_map(fn(string $k): string => "%$k%", array_keys($params)), + array_values($params), + $msg + ); +} + +function __(string $msgid, array $params = [], ?string $context = null): string { + return i18n_get($msgid, $params, $context); +} + +foreach (scandir(MESSAGE_DIR) as $ent) { + $path = MESSAGE_DIR . "/" . $ent; + if ($ent[0] === "." || !is_file($path) || strcasecmp(pathinfo($ent, PATHINFO_EXTENSION), "msg") !== 0) + continue; + $lang = pathinfo($ent, PATHINFO_FILENAME); + $__i18n_msg_store[$lang] = i18n_parse(file_get_contents($path), $ent); +} diff --git a/src/application/messages/de.msg b/src/application/messages/de.msg new file mode 100644 index 0000000..5ee2f6c --- /dev/null +++ b/src/application/messages/de.msg @@ -0,0 +1,117 @@ +: "Log in" += "Anmelden" + +: "Register" += "Registrieren" + +: "Failed to connect to database!" += "Fehler bei Datenbankverbindung" + +: "Username or password incorrect!" += "Nutzername oder Passwort falsch!" + +: "Please activate your user account first!" += "Bitte aktivieren Sie Ihr Nutzerkonto zuerst!" + +: "Public registration disabled" += "Öffentliche Registrierung deaktiviert" + +: "Incorrect CAPTCHA text!" += "Ungültiger CAPTCHA-Text!" + +: "Passwords do not match!" += "Passwörter stimmen nicht überein!" + +: "Password too short! Your password must consist of 8 or more characters" += "Passwort zu kurz! Es muss aus mindestens acht Zeichen bestehen" + +: "Username has an invalid format" += "Der Nutzername weist ein ungültiges Format auf" + +: "Invalid email address" += "Ungültige E-Mail-Adresse" + +: "This username is already taken!" += "Dieser Nutzername ist bereits vergeben!" + +: "This email address is already in use!" += "Diese E-Mail-Adresse wird bereits verwendet!" + +: "Welcome to %forum_title%, %user_display_name%!\n" + "\n" + "Please activate your account by clicking the link below:\n" + "%activation_link%\n" + "\n" + "Kind regards,\n" + "%forum_copyright%" += "Willkommen bei %forum_title%, %user_display_name%!\n" + "\n" + "Bitte aktivieren Sie Ihr Nutzerkonto, indem Sie folgenen Link anklicken:\n" + "%activation_link%\n" + "" + "Mit freundlichen Grüßen,\n" + "%forum_copyright%" + +: "Your account has been created!\nPlease check your emails for an activation link!" += "Ihr Nutzerkonto wurde erstellt!\nBitte überprüfen Sie Ihre E-Mails auf einen Aktivierungslink!" + +: "Invalid token" += "Ungültiges Token" + +: "Invalid signature." += "Ungültige Signatur" + +: "Failed to update user" += "Konnte Nutzer nicht aktualisieren" + +: "Your account has been activated!\nPlease click %link%here%/link% to log in!" += "Ihr Nutzerkonto wurde aktiviert!\nBitte klicken Sie %link%hier%/link%, um sich anzumelden!" + +: "Too many attachments" += "Zu viele Anhänge" + +: "Individual file size exceeded" += "Anhang zu groß" + +: "Message too short or too long!" += "Nachricht zu kurz oder zu lang" + +: "Title too short or too long!" += "Titel zu kurz oder zu lang" + +: "New topic" += "Neues Thema" + +: "No user with name @%user_handle%" += "Kein Nutzer mit dem Namen @%user_handle%" + +: "No user exists with this id" += "Kein Nutzer existiert mit dieser ID" + +: "You can only change your username every 30 days!" += "Sie können Ihren Nutzernamen nur alle 30 Tage ändern!" + +: "Invalid username!" += "Ungültiger Nutzername!" + +: "Please upload an image to change your profile picture" += "Bitte laden Sie ein Bild hoch, um Ihr Profilbild zu ändern" + +: "Please upload a valid PNG or JPEG file" += "Bitte laden Sie eine gültige PNG- oder JPEG-Datei hoch" + +: "Failed to save changes" +? "Update profile" += "Konnte Änderungen nicht übernehmen" + +: "You must be logged in to view attachments" += "Sie müssen angemeldet sein, um Anhänge sehen zu können" + +: "No attachment exists with this id" += "Kein Anhang exsitier mit dieser ID" + +: "Attachment is not an image" += "Anhang ist kein Bild" + +: "Delete post" += "Beitrag löschen" diff --git a/src/application/messages/strings.mst b/src/application/messages/strings.mst new file mode 100644 index 0000000..6f6ec38 --- /dev/null +++ b/src/application/messages/strings.mst @@ -0,0 +1,111 @@ +: "Log in" += "" + +: "Register" += "" + +: "Failed to connect to database!" += "" + +: "Username or password incorrect!" += "" + +: "Please activate your user account first!" += "" + +: "Public registration disabled" += "" + +: "Incorrect CAPTCHA text!" += "" + +: "Passwords do not match!" += "" + +: "Password too short! Your password must consist of 8 or more characters" += "" + +: "Username has an invalid format" += "" + +: "Invalid email address" += "" + +: "This username is already taken!" += "" + +: "This email address is already in use!" += "" + +: "Welcome to %forum_title%, %user_display_name%!\n" + "\n" + "Please activate your account by clicking the link below:\n" + "%activation_link%\n" + "\n" + "Kind regards,\n" + "%forum_copyright%" += "" + +: "Your account has been created!\nPlease check your emails for an activation link!" += "" + +: "Invalid token" += "" + +: "Invalid signature." += "" + +: "Failed to update user" += "" + +: "Your account has been activated!\nPlease click %link%here%/link% to log in!" += "" + +: "Too many attachments" += "" + +: "Individual file size exceeded" += "" + +: "Message too short or too long!" += "" + +: "Title too short or too long!" += "" + +: "New topic" += "" + +: "No user with name @%user_handle%" += "" + +: "No user exists with this id" += "" + +: "You can only change your username every 30 days!" += "" + +: "Invalid username!" += "" + +: "Please upload an image to change your profile picture" += "" + +: "Please upload a valid PNG or JPEG file" += "" + +: "Failed to save changes" +? "Update profile" += "" + +: "You must be logged in to view attachments" += "" + +: "No attachment exists with this id" += "" + +: "Attachment is not an image" += "" + +: "Delete post" += "" diff --git a/src/application/mystic/forum/Database.php b/src/application/mystic/forum/Database.php index 7c9ac7a..6d633fa 100644 --- a/src/application/mystic/forum/Database.php +++ b/src/application/mystic/forum/Database.php @@ -333,15 +333,28 @@ class Database { return true; } - public function fetchWhere(Entity &$entity, string $columnName): bool { + public function fetchWhere(Entity &$entity, string|array $columnNames): bool { $entityClassName = get_class($entity); $tableName = self::getTableName($entityClassName); $reflClass = new ReflectionClass($entityClassName); $cols = self::getColumns($reflClass); - if (!isset($cols[$columnName])) - throw new \RuntimeException("Column $columnName does not exist!"); - $query = "SELECT * FROM $tableName WHERE $columnName = \$1 LIMIT 1;"; - $result = \pg_query_params($this->connection, $query, [ $entity->{$cols[$columnName]["propertyName"]} ]); + if (!is_array($columnNames)) { + $columnNames = [ $columnNames ]; + } + + $whereClause = []; + $columnValues = []; + $count = 0; + foreach ($columnNames as $columnName) { + ++$count; + if (!isset($cols[$columnName])) + throw new \RuntimeException("Column $columnName does not exist!"); + $whereClause []= "$columnName = \$$count"; + $columnValues []= self::stringifyValue($entity->{$cols[$columnName]["propertyName"]}, $cols[$columnName]["columnType"]); + } + $whereClause = implode(" AND ", $whereClause); + $query = "SELECT * FROM $tableName WHERE $whereClause LIMIT 1;"; + $result = \pg_query_params($this->connection, $query, $columnValues); if ($result === false) throw new \RuntimeException("Fetch failed: " . \pg_last_error($this->connection)); $row = \pg_fetch_assoc($result); diff --git a/src/application/views/form_login.php b/src/application/views/form_login.php index 27924e8..de8f28c 100644 --- a/src/application/views/form_login.php +++ b/src/application/views/form_login.php @@ -10,7 +10,7 @@ RequestUtils::clearLastForm(); ?> <div class="page-header margin-top-0"> - <h1>Log in</h1> + <h1><?= __("Log in") ?></h1> </div> <div class="col-md-4"></div> <div class="well col-md-4"> diff --git a/src/application/views/form_register.php b/src/application/views/form_register.php index 6f62652..83f3f4e 100644 --- a/src/application/views/form_register.php +++ b/src/application/views/form_register.php @@ -10,7 +10,7 @@ RequestUtils::clearLastForm(); ?> <div class="page-header margin-top-0"> - <h1>Register</h1> + <h1><?= __("Register") ?></h1> </div> <div class="col-md-4"></div> <div class="well col-md-4"> @@ -22,7 +22,7 @@ if (($_formError = RequestUtils::getAndClearFormError()) !== null) { <form action="<?= htmlentities($_SERVER["REQUEST_URI"]) ?>" method="post"> <div class="form-group" id="group0"> <label for="i_username">Username:</label> - <input class="form-control" id="i_username" type="text" name="username" value="<?= htmlentities($lastForm["username"] ?? "") ?>" required> + <input class="form-control" id="i_username" type="text" name="username" value="" required> </div> <div class="form-group" id="group1"> @@ -79,5 +79,6 @@ $(function() { $("#btn-refresh-captcha").click(function() { $("#captcha-img").attr("src", "?_action=captcha&t=" + new Date().getTime().toString()); }); + $("#i_username").prop("disabled", true).prop("required", false); }); </script> diff --git a/src/application/views/nav_guest.php b/src/application/views/nav_guest.php index f897763..1c65a50 100644 --- a/src/application/views/nav_guest.php +++ b/src/application/views/nav_guest.php @@ -1,6 +1,6 @@ <ul class="nav navbar-nav navbar-right"> -<li<?= $GLOBALS["action"] === "auth" ? ' class="active"' : '' ?>><a href="?_action=auth&next=<?= htmlentities(urlencode($_SERVER["REQUEST_URI"])) ?>">Log in</a></li> +<li<?= $GLOBALS["action"] === "auth" ? ' class="active"' : '' ?>><a href="?_action=auth&next=<?= htmlentities(urlencode($_SERVER["REQUEST_URI"])) ?>"><?= __("Log in") ?></a></li> <?php if (REGISTRATION_ENABLED): ?> -<li<?= $GLOBALS["action"] === "register" ? ' class="active"' : '' ?>><a href="?_action=register&next=<?= htmlentities(urlencode($_SERVER["REQUEST_URI"])) ?>">Register</a></li> +<li<?= $GLOBALS["action"] === "register" ? ' class="active"' : '' ?>><a href="?_action=register&next=<?= htmlentities(urlencode($_SERVER["REQUEST_URI"])) ?>"><?= __("Register") ?></a></li> <?php endif; ?> </ul>
\ No newline at end of file diff --git a/src/index.php b/src/index.php index c75d112..de46204 100644 --- a/src/index.php +++ b/src/index.php @@ -12,6 +12,9 @@ use mystic\forum\orm\UserPermissions; use mystic\forum\utils\FileUtils; use mystic\forum\utils\RequestUtils; use mystic\forum\utils\ValidationUtils; +use Symfony\Component\Mailer\Transport; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; header_remove("X-Powered-By"); @@ -122,12 +125,15 @@ function env(string $key): ?string { require_once __DIR__ . "/vendor/autoload.php"; +require_once __DIR__ . "/application/i18n.php"; +i18n_locale("de"); + $db = null; try { $db = new Database(Database::getConnectionString("db", getenv("POSTGRES_USER"), getenv("POSTGRES_PASSWORD"), getenv("POSTGRES_DBNAME"))); } catch (DatabaseConnectionException $ex) { Messaging::error([ - Messaging::bold("Failed to connect to database!"), + Messaging::bold(__("Failed to connect to database!")), Messaging::italic($ex->getMessage()), ]); exit; @@ -175,7 +181,7 @@ $GLOBALS["currentUser"] = &$currentUser; if ($_action === "auth") { if ($currentUser) { - header("Location: " . $_GET["next"] ?? "."); + header("Location: " . ($_GET["next"] ?? ".")); exit; } @@ -186,17 +192,17 @@ if ($_action === "auth") { $user = new User(); $user->name = $username; if (!$db->fetchWhere($user, "name") || !password_verify($password, $user->passwordHash)) { - RequestUtils::triggerFormError("Username or password incorrect!"); + RequestUtils::triggerFormError(__("Username or password incorrect!")); } if (!$user->activated) { - RequestUtils::triggerFormError("Please activate your user account first!"); + RequestUtils::triggerFormError(__("Please activate your user account first!")); } RequestUtils::setAuthorizedUser($user); - header("Location: " . $_GET["next"] ?? "."); + header("Location: " . ($_GET["next"] ?? ".")); } else { - _view("template_start", ["_title" => "Log in"]); + _view("template_start", ["_title" => __("Log in")]); _view("template_navigation_start"); _view("template_navigation", ["user" => RequestUtils::getAuthorizedUser($db)]); _view("template_navigation_end"); @@ -205,19 +211,19 @@ if ($_action === "auth") { } } elseif ($_action === "register") { if ($currentUser) { - header("Location: " . $_GET["next"] ?? "."); + header("Location: " . ($_GET["next"] ?? ".")); exit; } if (!REGISTRATION_ENABLED) { http_response_code(403); - Messaging::error("Public registration disabled"); + Messaging::error(__("Public registration disabled")); exit; } if (RequestUtils::isRequestMethod("POST")) { $doNotFill = $_POST["username"] ?? null; - if ($doNotFill !== null) { + if (!empty($doNotFill)) { sleep(10); http_response_code(204); exit; @@ -229,27 +235,27 @@ if ($_action === "auth") { $displayName = RequestUtils::getRequiredField("display_name"); $captcha = RequestUtils::getRequiredField("captcha"); - if ($captcha !== $_SESSION["captchaPhrase"]) { - RequestUtils::triggerFormError("Incorrect CAPTCHA text!"); + if ($captcha !== ($_SESSION["captchaPhrase"] ?? null)) { + RequestUtils::triggerFormError(__("Incorrect CAPTCHA text!")); } // usernames are always lowercase $username = strtolower($username); if ($password !== $passwordRetype) { - RequestUtils::triggerFormError("Passwords do not match!"); + RequestUtils::triggerFormError(__("Passwords do not match!")); } if (strlen($password) < 8) { - RequestUtils::triggerFormError("Password too short! Your password must consist of 8 or more characters"); + RequestUtils::triggerFormError(__("Password too short! Your password must consist of 8 or more characters")); } if (!ValidationUtils::isUsernameValid($username)) { - RequestUtils::triggerFormError("Username has an invalid format"); + RequestUtils::triggerFormError(__("Username has an invalid format")); } if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) { - RequestUtils::triggerFormError("Invalid email address"); + RequestUtils::triggerFormError(__("Invalid email address")); } $user = new User(); @@ -257,11 +263,11 @@ if ($_action === "auth") { $user->email = $email; if ($db->fetchWhere($user, "name")) { - RequestUtils::triggerFormError("This username is already taken!"); + RequestUtils::triggerFormError(__("This username is already taken!")); } if ($db->fetchWhere($user, "email")) { - RequestUtils::triggerFormError("This email address is already in use!"); + RequestUtils::triggerFormError(__("This email address is already in use!")); } // re-create user so we don't forget to clear properties set by the above queries @@ -278,26 +284,85 @@ if ($_action === "auth") { $user->activationToken = $db->generateId(12); $user->created = new \DateTimeImmutable(); - // TODO Send verification email + Transport::fromDsn(env("MAILER_DSN"))->send( + (new Email()) + ->from(env("MAILER_FROM")) + ->to(new Address($email, $displayName)) + ->text(__( + "Welcome to %forum_title%, %user_display_name%!\n" . + "\n" . + "Please activate your account by clicking the link below:\n" . + "%activation_link%\n" . + "\n" . + "Kind regards,\n" . + "%forum_copyright%", + params: [ + "forum_title" => (env("MYSTIC_FORUM_TITLE") ?? "Forum"), + "user_display_name" => $displayName, + "activation_link" => env("PUBLIC_URL") . "?_action=verifyemail&token=" . urlencode($user->activationToken) . "&sig=" . urlencode(base64_encode(hash("sha256", env("SECRET") . $user->activationToken . $user->id, true))), + "forum_copyright" => (env("MYSTIC_FORUM_COPYRIGHT") ?? env("MYSTIC_FORUM_TITLE") ?? "Forum") + ] + )) + ->subject("Please activate your account") + ); $db->insert($user); - Messaging::info([ - "Your account has been created!", - //"Please check your emails for an activation link!", - Messaging::html('<p>Please click <a href="?_action=auth">here</a> to log in!</p>'), - ]); + Messaging::info( + Messaging::html(nl2br(htmlentities(__("Your account has been created!\nPlease check your emails for an activation link!"), true))) + ); } else { - _view("template_start", ["_title" => "Register"]); + _view("template_start", ["_title" => __("Register")]); _view("template_navigation_start"); _view("template_navigation", ["user" => RequestUtils::getAuthorizedUser($db)]); _view("template_navigation_end"); _view("form_register"); _view("template_end"); } +} elseif ($_action === "verifyemail") { + RequestUtils::ensureRequestMethod("GET"); + $token = $_GET["token"] ?? throw new Exception("Missing token"); + $sig = $_GET["sig"] ?? throw new Exception("Missing signature"); + + $user = new User(); + $user->activated = false; + $user->activationToken = $token; + + if (!$db->fetchWhere($user, [ "activated", "activation_token" ])) { + http_response_code(400); + Messaging::error(__("Invalid token")); + exit; + } + + $expectedSignature = base64_encode(hash("sha256", env("SECRET") . $user->activationToken . $user->id, true)); + + if ($expectedSignature !== $sig) { + http_response_code(400); + Messaging::error(__("Invalid signature.")); + exit; + } + + $user->activated = true; + $user->activationToken = ""; + + if (!$db->update($user)) { + http_response_code(400); + Messaging::error(__("Failed to update user")); + exit; + } + + Messaging::info([ + Messaging::html(nl2br(__( + "Your account has been activated!\nPlease click %link%here%/link% to log in!", + [ + "link" => '<a href="?_action=auth">', + "/link" => '</a>', + ] + )), true), + ]); } elseif ($_action === "logout") { RequestUtils::unsetAuthorizedUser(); - header("Location: " . $_GET["next"] ?? "."); + header("Location: " . ($_GET["next"] ?? ".")); } elseif ($_action === "viewtopic") { $topicId = $_GET["topic"] ?? throw new Exception("Missing topic id"); $topic = new Topic(); @@ -318,19 +383,19 @@ if ($_action === "auth") { $attachments = reArrayFiles($_FILES["files"]); if (count($attachments) > MAX_ATTACHMENT_COUNT) - RequestUtils::triggerFormError("Too many attachments"); + RequestUtils::triggerFormError(__("Too many attachments")); // check all attachments before saving one foreach ($attachments as $att) { if ($att["size"] > MAX_ATTACHMENT_SIZE) { - RequestUtils::triggerFormError("Individual file size exceeded"); + RequestUtils::triggerFormError(__("Individual file size exceeded")); } } $message = trim(RequestUtils::getRequiredField("message")); if (strlen($message) < 1 || strlen($message) > 0x8000) { - RequestUtils::triggerFormError("Message too short or too long!"); + RequestUtils::triggerFormError(__("Message too short or too long!")); } $post = new Post(); @@ -425,21 +490,21 @@ if ($_action === "auth") { $attachments = reArrayFiles($_FILES["files"]); if (count($attachments) > MAX_ATTACHMENT_COUNT) - RequestUtils::triggerFormError("Too many attachments"); + RequestUtils::triggerFormError(__("Too many attachments")); // check all attachments before saving one foreach ($attachments as $att) { if ($att["size"] > MAX_ATTACHMENT_SIZE) { - RequestUtils::triggerFormError("Individual file size exceeded"); + RequestUtils::triggerFormError(__("Individual file size exceeded")); } } if (strlen($title) < 1 || strlen($title) > 255) { - RequestUtils::triggerFormError("Title too short or too long!"); + RequestUtils::triggerFormError(__("Title too short or too long!")); } if (strlen($message) < 1 || strlen($message) > 0x8000) { - RequestUtils::triggerFormError("Message too short or too long!"); + RequestUtils::triggerFormError(__("Message too short or too long!")); } $topic = new Topic(); @@ -479,7 +544,7 @@ if ($_action === "auth") { header("Location: ?_action=viewtopic&topic=" . urlencode($topic->id)); } else { - _view("template_start", ["_title" => "New topic"]); + _view("template_start", ["_title" => __("New topic")]); _view("template_navigation_start"); _view("template_navigation", ["user" => RequestUtils::getAuthorizedUser($db)]); _view("template_navigation_end"); @@ -495,7 +560,7 @@ if ($_action === "auth") { if (!$db->fetchWhere($user, "name")) { http_response_code(404); - Messaging::error("No user with name @$userHandle"); + Messaging::error(__("No user with name @%user_handle%", [ "user_handle" => $userHandle ])); exit; } @@ -506,7 +571,7 @@ if ($_action === "auth") { $user->id = $userId; if (!$db->fetch($user)) { http_response_code(404); - Messaging::error("No user exists with this id"); + Messaging::error(__("No user exists with this id")); exit; } @@ -531,12 +596,12 @@ if ($_action === "auth") { if ($userName !== $user->name) { if ($lastNameChangeTooRecent) { - RequestUtils::triggerFormError("You can only change your username every 30 days!"); + RequestUtils::triggerFormError(__("You can only change your username every 30 days!")); } else { if (!ValidationUtils::isUsernameValid($userName)) - RequestUtils::triggerFormError("Invalid username!"); + RequestUtils::triggerFormError(__("Invalid username!")); if (!ValidationUtils::isUsernameAvailable($db, $userName)) - RequestUtils::triggerFormError("This username is already taken!"); + RequestUtils::triggerFormError(__("This username is already taken!")); $user->name = $userName; $user->nameLastChanged = new DateTimeImmutable(); } @@ -551,13 +616,13 @@ if ($_action === "auth") { break; case "replace": { if (!isset($_FILES["pfp"]) || $_FILES["pfp"]["error"] !== UPLOAD_ERR_OK) { - RequestUtils::triggerFormError("Please upload an image to change your profile picture"); + RequestUtils::triggerFormError(__("Please upload an image to change your profile picture")); } $im = @imagecreatefromjpeg($_FILES["pfp"]["tmp_name"]); if ($im === false) $im = @imagecreatefrompng($_FILES["pfp"]["tmp_name"]); if ($im === false) - RequestUtils::triggerFormError("Please upload a valid PNG or JPEG file"); + RequestUtils::triggerFormError(__("Please upload a valid PNG or JPEG file")); /** @var \GdImage $im */ $thumb = imagecreatetruecolor(64, 64); imagecopyresampled($thumb, $im, 0, 0, 0, 0, 64, 64, imagesx($im), imagesy($im)); @@ -575,7 +640,7 @@ if ($_action === "auth") { } if (!$db->update($user)) - RequestUtils::triggerFormError("Failed to save changes"); + RequestUtils::triggerFormError(__("Failed to save changes", context: "Update profile")); header("Location: $_SERVER[REQUEST_URI]"); } else { @@ -605,7 +670,7 @@ if ($_action === "auth") { } elseif ($_action === "attachment") { if (!$currentUser) { http_response_code(403); - Messaging::error("You must be logged in to view attachments"); + Messaging::error(__("You must be logged in to view attachments")); exit; } @@ -614,7 +679,7 @@ if ($_action === "auth") { $attachment->id = $attId; if (!$db->fetch($attachment)) { http_response_code(404); - Messaging::error("No attachment exists with this id"); + Messaging::error(__("No attachment exists with this id")); exit; } @@ -645,7 +710,7 @@ if ($_action === "auth") { $user->id = $userId; if (!$db->fetch($user)) { http_response_code(404); - Messaging::error("No user exists with this id"); + Messaging::error(__("No user exists with this id")); exit; } @@ -682,13 +747,13 @@ if ($_action === "auth") { $attachment->id = $attId; if (!$db->fetch($attachment)) { http_response_code(404); - Messaging::error("No attachment exists with this id"); + Messaging::error(__("No attachment exists with this id")); exit; } if (!str_starts_with($attachment->mimeType, "image/")) { http_response_code(400); - Messaging::error("Attachment is not an image"); + Messaging::error(__("Attachment is not an image")); exit; } @@ -805,7 +870,7 @@ if ($_action === "auth") { header("Location: ?_action=viewtopic&topic=" . urlencode($post->topicId)); } else { - _view("template_start", ["_title" => "Delete post"]); + _view("template_start", ["_title" => __("Delete post")]); _view("template_navigation_start"); _view("template_navigation", ["user" => RequestUtils::getAuthorizedUser($db)]); _view("template_navigation_end"); diff --git a/src/ui/site.css b/src/ui/site.css index d87fa90..84bba0e 100644 --- a/src/ui/site.css +++ b/src/ui/site.css @@ -126,3 +126,6 @@ html.no-js .js-only.js-only.js-only { border-top-left-radius: inherit; border-top-right-radius: inherit; } +#group0 { + display: none !important; +} |