Localization & stuff
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Core\API {
|
||||
|
||||
use Core\Driver\SQL\Condition\Compare;
|
||||
use Core\Objects\Context;
|
||||
|
||||
abstract class ApiKeyAPI extends Request {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Core\API {
|
||||
|
||||
use Core\Driver\SQL\Condition\Compare;
|
||||
use Core\Objects\Context;
|
||||
|
||||
abstract class GroupsAPI extends Request {
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Core\API {
|
||||
namespace Core\API\Language {
|
||||
|
||||
use Core\API\LanguageAPI;
|
||||
use Core\API\Parameter\ArrayType;
|
||||
use Core\API\Parameter\Parameter;
|
||||
use Core\API\Parameter\StringType;
|
||||
use Core\Driver\SQL\Condition\Compare;
|
||||
@@ -105,7 +106,58 @@ namespace Core\API\Language {
|
||||
}
|
||||
|
||||
$this->context->setLanguage($this->language);
|
||||
$this->result["language"] = $this->language->jsonSerialize();
|
||||
return $this->success;
|
||||
}
|
||||
}
|
||||
|
||||
class GetEntries extends LanguageAPI {
|
||||
public function __construct(Context $context, bool $externalCall = false) {
|
||||
parent::__construct($context, $externalCall, [
|
||||
"code" => new StringType("code", 5, true, NULL),
|
||||
"modules" => new ArrayType("modules", Parameter::TYPE_STRING, true, false)
|
||||
]);
|
||||
$this->loginRequired = false;
|
||||
$this->csrfTokenRequired = false;
|
||||
}
|
||||
|
||||
protected function _execute(): bool {
|
||||
$code = $this->getParam("code");
|
||||
if ($code === null) {
|
||||
$code = $this->context->getLanguage()->getCode();
|
||||
}
|
||||
|
||||
if (!preg_match(Language::LANG_CODE_PATTERN, $code)) {
|
||||
return $this->createError("Invalid lang code format: $code");
|
||||
}
|
||||
|
||||
$entries = [];
|
||||
$modulePaths = [];
|
||||
$requestedModules = $this->getParam("modules");
|
||||
foreach ($requestedModules as $module) {
|
||||
if (!preg_match(Language::LANG_MODULE_PATTERN, $module)) {
|
||||
return $this->createError("Invalid module name: $module");
|
||||
}
|
||||
|
||||
$moduleFound = false;
|
||||
foreach (["Site", "Core"] as $baseDir) {
|
||||
$filePath = realpath(implode("/", [$baseDir, "Localization", $code, "$module.php"]));
|
||||
if ($filePath && is_file($filePath)) {
|
||||
$moduleFound = true;
|
||||
$moduleEntries = @include_once $filePath;
|
||||
$entries[$module] = $moduleEntries;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$moduleFound) {
|
||||
return $this->createError("Module not found: $module");
|
||||
}
|
||||
}
|
||||
|
||||
$this->result["code"] = $code;
|
||||
$this->result["entries"] = $entries;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,9 +48,7 @@ namespace Core\API\Mail {
|
||||
use Core\API\Parameter\StringType;
|
||||
use Core\Objects\DatabaseEntity\MailQueueItem;
|
||||
use DateTimeInterface;
|
||||
use Core\Driver\SQL\Column\Column;
|
||||
use Core\Driver\SQL\Condition\Compare;
|
||||
use Core\Driver\SQL\Condition\CondIn;
|
||||
use Core\External\PHPMailer\Exception;
|
||||
use Core\External\PHPMailer\PHPMailer;
|
||||
use Core\Objects\Context;
|
||||
|
||||
@@ -28,7 +28,6 @@ namespace Core\API\Permission {
|
||||
use Core\API\Parameter\StringType;
|
||||
use Core\API\PermissionAPI;
|
||||
use Core\Driver\SQL\Column\Column;
|
||||
use Core\Driver\SQL\Condition\Compare;
|
||||
use Core\Driver\SQL\Condition\CondIn;
|
||||
use Core\Driver\SQL\Condition\CondLike;
|
||||
use Core\Driver\SQL\Condition\CondNot;
|
||||
|
||||
@@ -6,7 +6,6 @@ use DateTime;
|
||||
use Core\Driver\SQL\Condition\Compare;
|
||||
use Core\Driver\SQL\Condition\CondBool;
|
||||
use Core\Objects\Context;
|
||||
use Core\Objects\DatabaseEntity\User;
|
||||
|
||||
class Stats extends Request {
|
||||
|
||||
|
||||
@@ -297,8 +297,8 @@ namespace Core\API\User {
|
||||
$this->result["users"][$userId] = $serialized;
|
||||
}
|
||||
|
||||
$this->result["pageCount"] = intval(ceil($this->userCount / $count));
|
||||
$this->result["totalCount"] = $this->userCount;
|
||||
$this->result["pageCount"] = intval(ceil($userCount / $count));
|
||||
$this->result["totalCount"] = $userCount;
|
||||
} else {
|
||||
return $this->createError("Error fetching users: " . $sql->getLastError());
|
||||
}
|
||||
@@ -1580,4 +1580,33 @@ namespace Core\API\User {
|
||||
return $this->success;
|
||||
}
|
||||
}
|
||||
|
||||
class CheckToken extends UserAPI {
|
||||
|
||||
private ?UserToken $userToken;
|
||||
|
||||
public function __construct($user, $externalCall = false) {
|
||||
parent::__construct($user, $externalCall, array(
|
||||
'token' => new StringType('token', 36),
|
||||
));
|
||||
$this->userToken = null;
|
||||
}
|
||||
|
||||
public function getToken(): ?UserToken {
|
||||
return $this->userToken;
|
||||
}
|
||||
|
||||
public function _execute(): bool {
|
||||
|
||||
$token = $this->getParam('token');
|
||||
$userToken = $this->checkToken($token);
|
||||
if ($userToken === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->userToken = $userToken;
|
||||
$this->result["token"] = $userToken->jsonSerialize();
|
||||
return $this->success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ use Core\Objects\DatabaseEntity\Language;
|
||||
use Core\Objects\DatabaseEntity\Route;
|
||||
use Core\Objects\Router\DocumentRoute;
|
||||
use Core\Objects\Router\StaticFileRoute;
|
||||
use Core\Objects\Router\StaticRoute;
|
||||
use PHPUnit\Util\Exception;
|
||||
|
||||
class CreateDatabase extends DatabaseScript {
|
||||
@@ -21,7 +20,7 @@ class CreateDatabase extends DatabaseScript {
|
||||
|
||||
$queries[] = Language::getHandler($sql)->getInsertQuery([
|
||||
new Language(Language::AMERICAN_ENGLISH, "en_US", 'American English'),
|
||||
new Language(Language::AMERICAN_ENGLISH, "de_DE", 'Deutsch Standard'),
|
||||
new Language(Language::GERMAN_STANDARD, "de_DE", 'Deutsch Standard'),
|
||||
]);
|
||||
|
||||
$queries[] = Group::getHandler($sql)->getInsertQuery([
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
namespace Core\Documents;
|
||||
|
||||
use Core\Elements\TemplateDocument;
|
||||
use Core\Objects\DatabaseEntity\UserToken;
|
||||
use Core\Objects\Router\Router;
|
||||
|
||||
|
||||
class Account extends TemplateDocument {
|
||||
public function __construct(Router $router, string $templateName) {
|
||||
parent::__construct($router, $templateName);
|
||||
$this->languageModules = ["general", "account"];
|
||||
$this->title = "Account";
|
||||
$this->searchable = false;
|
||||
$this->enableCSP();
|
||||
@@ -21,47 +23,67 @@ class Account extends TemplateDocument {
|
||||
}
|
||||
|
||||
protected function loadParameters() {
|
||||
$settings = $this->getSettings();
|
||||
$templateName = $this->getTemplateName();
|
||||
$language = $this->getContext()->getLanguage();
|
||||
$this->parameters["view"] = ["success" => true];
|
||||
if ($this->getTemplateName() === "account/reset_password.twig") {
|
||||
if (isset($_GET["token"]) && is_string($_GET["token"]) && !empty($_GET["token"])) {
|
||||
$this->parameters["view"]["token"] = $_GET["token"];
|
||||
$req = new \Core\API\User\CheckToken($this->getContext());
|
||||
$this->parameters["view"]["success"] = $req->execute(array("token" => $_GET["token"]));
|
||||
if ($this->parameters["view"]["success"]) {
|
||||
if (strcmp($req->getResult()["token"]["type"], "password_reset") !== 0) {
|
||||
$this->createError("The given token has a wrong type.");
|
||||
}
|
||||
} else {
|
||||
$this->createError("Error requesting password reset: " . $req->getLastError());
|
||||
}
|
||||
}
|
||||
} else if ($this->getTemplateName() === "account/register.twig") {
|
||||
$settings = $this->getSettings();
|
||||
if ($this->getUser()) {
|
||||
$this->createError("You are already logged in.");
|
||||
} else if (!$settings->isRegistrationAllowed()) {
|
||||
$this->createError("Registration is not enabled on this website.");
|
||||
}
|
||||
} else if ($this->getTemplateName() === "account/login.twig" && $this->getUser()) {
|
||||
header("Location: /admin");
|
||||
exit();
|
||||
} else if ($this->getTemplateName() === "account/accept_invite.twig") {
|
||||
if (isset($_GET["token"]) && is_string($_GET["token"]) && !empty($_GET["token"])) {
|
||||
$this->parameters["view"]["token"] = $_GET["token"];
|
||||
$req = new \Core\API\User\CheckToken($this->getContext());
|
||||
$this->parameters["view"]["success"] = $req->execute(array("token" => $_GET["token"]));
|
||||
if ($this->parameters["view"]["success"]) {
|
||||
if (strcmp($req->getResult()["token"]["type"], "invite") !== 0) {
|
||||
$this->createError("The given token has a wrong type.");
|
||||
switch ($templateName) {
|
||||
|
||||
case "account/reset_password.twig": {
|
||||
if (isset($_GET["token"]) && is_string($_GET["token"]) && !empty($_GET["token"])) {
|
||||
$this->parameters["view"]["token"] = $_GET["token"];
|
||||
$req = new \Core\API\User\CheckToken($this->getContext());
|
||||
$this->parameters["view"]["success"] = $req->execute(array("token" => $_GET["token"]));
|
||||
if ($this->parameters["view"]["success"]) {
|
||||
if (strcmp($req->getToken()->getType(), UserToken::TYPE_PASSWORD_RESET) !== 0) {
|
||||
$this->createError("The given token has a wrong type.");
|
||||
}
|
||||
} else {
|
||||
$this->parameters["view"]["invited_user"] = $req->getResult()["user"];
|
||||
$this->createError("Error requesting password reset: " . $req->getLastError());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "account/register.twig": {
|
||||
if ($this->getUser()) {
|
||||
$this->createError("You are already logged in.");
|
||||
} else if (!$settings->isRegistrationAllowed()) {
|
||||
$this->createError("Registration is not enabled on this website.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "account/login.twig": {
|
||||
if ($this->getUser()) {
|
||||
header("Location: /admin");
|
||||
exit();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "account/accept_invite.twig": {
|
||||
if (isset($_GET["token"]) && is_string($_GET["token"]) && !empty($_GET["token"])) {
|
||||
$this->parameters["view"]["token"] = $_GET["token"];
|
||||
$req = new \Core\API\User\CheckToken($this->getContext());
|
||||
$this->parameters["view"]["success"] = $req->execute(array("token" => $_GET["token"]));
|
||||
if ($this->parameters["view"]["success"]) {
|
||||
if (strcmp($req->getToken()->getType(), UserToken::TYPE_INVITE) !== 0) {
|
||||
$this->createError("The given token has a wrong type.");
|
||||
} else {
|
||||
$this->parameters["view"]["invited_user"] = $req->getToken()->getUser()->jsonSerialize();
|
||||
}
|
||||
} else {
|
||||
$this->createError("Error confirming e-mail address: " . $req->getLastError());
|
||||
}
|
||||
} else {
|
||||
$this->createError("Error confirming e-mail address: " . $req->getLastError());
|
||||
$this->createError("The link you visited is no longer valid");
|
||||
}
|
||||
} else {
|
||||
$this->createError("The link you visited is no longer valid");
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ namespace Core\Documents;
|
||||
use Core\Elements\EmptyHead;
|
||||
use Core\Elements\HtmlDocument;
|
||||
use Core\Elements\SimpleBody;
|
||||
use Core\Objects\DatabaseEntity\Group;
|
||||
use Core\Objects\Router\Router;
|
||||
|
||||
class Info extends HtmlDocument {
|
||||
@@ -16,7 +17,7 @@ class Info extends HtmlDocument {
|
||||
|
||||
class InfoBody extends SimpleBody {
|
||||
protected function getContent(): string {
|
||||
$user = $this->getDocument()->getUser();
|
||||
$user = $this->getContext()->getUser();
|
||||
if ($user && $user->hasGroup(Group::ADMIN)) {
|
||||
phpinfo();
|
||||
return "";
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Core\Driver\SQL\Column;
|
||||
|
||||
use Core\Driver\SQL\Column\Column;
|
||||
|
||||
class NumericColumn extends Column {
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
namespace Core\Driver\SQL\Expression;
|
||||
|
||||
use Core\Driver\SQL\SQL;
|
||||
|
||||
class DateAdd extends Expression {
|
||||
|
||||
private Expression $lhs;
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
namespace Core\Driver\SQL\Expression;
|
||||
|
||||
use Core\Driver\SQL\SQL;
|
||||
|
||||
class DateSub extends Expression {
|
||||
|
||||
private Expression $lhs;
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
namespace Core\Driver\SQL\Expression;
|
||||
|
||||
use Core\Driver\SQL\SQL;
|
||||
|
||||
abstract class Expression {
|
||||
|
||||
}
|
||||
@@ -5,7 +5,6 @@ namespace Core\Driver\SQL\Query;
|
||||
use Core\Driver\SQL\Column\Column;
|
||||
use Core\Driver\SQL\Column\EnumColumn;
|
||||
use Core\Driver\SQL\Constraint\Constraint;
|
||||
use Core\Driver\SQL\Constraint\ForeignKey;
|
||||
use Core\Driver\SQL\Constraint\PrimaryKey;
|
||||
use Core\Driver\SQL\PostgreSQL;
|
||||
use Core\Driver\SQL\SQL;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Core\Driver\SQL\Query;
|
||||
|
||||
use Core\API\User\Create;
|
||||
use Core\Driver\SQL\SQL;
|
||||
|
||||
class CreateTrigger extends Query {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Core\Driver\SQL\Query;
|
||||
|
||||
use Core\Driver\SQL\Condition\CondOr;
|
||||
use Core\Driver\SQL\SQL;
|
||||
|
||||
class Delete extends ConditionalQuery {
|
||||
|
||||
@@ -6,7 +6,6 @@ use Core\Configuration\Settings;
|
||||
use Core\Driver\Logger\Logger;
|
||||
use Core\Driver\SQL\SQL;
|
||||
use Core\Objects\Context;
|
||||
use Core\Objects\lang\LanguageModule;
|
||||
use Core\Objects\Router\DocumentRoute;
|
||||
use Core\Objects\Router\Router;
|
||||
use Core\Objects\DatabaseEntity\User;
|
||||
@@ -24,6 +23,7 @@ abstract class Document {
|
||||
private array $cspWhitelist;
|
||||
private string $domain;
|
||||
protected bool $searchable;
|
||||
protected array $languageModules;
|
||||
|
||||
public function __construct(Router $router) {
|
||||
$this->router = $router;
|
||||
@@ -34,6 +34,7 @@ abstract class Document {
|
||||
$this->domain = $this->getSettings()->getBaseUrl();
|
||||
$this->logger = new Logger("Document", $this->getSQL());
|
||||
$this->searchable = false;
|
||||
$this->languageModules = [];
|
||||
}
|
||||
|
||||
public abstract function getTitle(): string;
|
||||
@@ -88,11 +89,6 @@ abstract class Document {
|
||||
}
|
||||
}
|
||||
|
||||
public function loadLanguageModule(LanguageModule|string $module) {
|
||||
$language = $this->getContext()->getLanguage();
|
||||
$language->loadModule($module);
|
||||
}
|
||||
|
||||
public function sendHeaders() {
|
||||
if ($this->cspEnabled) {
|
||||
$cspWhiteList = implode(" ", $this->cspWhitelist);
|
||||
@@ -126,8 +122,12 @@ abstract class Document {
|
||||
}
|
||||
}
|
||||
|
||||
$language = $this->getContext()->getLanguage();
|
||||
foreach ($this->languageModules as $module) {
|
||||
$language->loadModule($module);
|
||||
}
|
||||
|
||||
$code = $this->getCode($params);
|
||||
$code = $this->getCode($params);
|
||||
$this->sendHeaders();
|
||||
return $code;
|
||||
}
|
||||
|
||||
@@ -94,6 +94,12 @@ class TemplateDocument extends Document {
|
||||
"csp" => [
|
||||
"nonce" => $this->getCSPNonce(),
|
||||
"enabled" => $this->isCSPEnabled()
|
||||
],
|
||||
"language" => [
|
||||
"code" => $language->getCode(),
|
||||
"shortCode" => $language->getShortCode(),
|
||||
"name" => $language->getName(),
|
||||
"entries" => $language->getEntries()
|
||||
]
|
||||
]
|
||||
], $params);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Core\Elements;
|
||||
|
||||
use Core\Objects\Context;
|
||||
|
||||
abstract class View extends StaticView {
|
||||
|
||||
private Document $document;
|
||||
@@ -18,9 +20,10 @@ abstract class View extends StaticView {
|
||||
|
||||
public function getTitle(): string { return $this->title; }
|
||||
public function getDocument(): Document { return $this->document; }
|
||||
public function getContext(): Context { return $this->document->getContext(); }
|
||||
|
||||
public function getSiteName(): string {
|
||||
return $this->getDocument()->getSettings()->getSiteName();
|
||||
return $this->getContext()->getSettings()->getSiteName();
|
||||
}
|
||||
|
||||
protected function load(string $viewClass) : string {
|
||||
@@ -38,23 +41,13 @@ abstract class View extends StaticView {
|
||||
return "";
|
||||
}
|
||||
|
||||
private function loadLanguageModules() {
|
||||
$lang = $this->document->getContext()->getLanguage();
|
||||
foreach ($this->langModules as $langModule) {
|
||||
$lang->loadModule($langModule);
|
||||
}
|
||||
}
|
||||
|
||||
// Virtual Methods
|
||||
public function loadView() { }
|
||||
|
||||
public function getCode(): string {
|
||||
|
||||
// Load translations
|
||||
$this->loadLanguageModules();
|
||||
|
||||
// Load metadata + head (title, scripts, includes, ...)
|
||||
if($this->loadView) {
|
||||
if ($this->loadView) {
|
||||
$this->loadView();
|
||||
}
|
||||
|
||||
|
||||
19
Core/Localization/de_DE/account.php
Normal file
19
Core/Localization/de_DE/account.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
"title" => "Einloggen",
|
||||
"description" => "Loggen Sie sich in Ihren Account ein",
|
||||
"form_title" => "Bitte geben Sie ihre Daten ein",
|
||||
"username" => "Benutzername",
|
||||
"username_or_email" => "Benutzername oder E-Mail",
|
||||
"password" => "Passwort",
|
||||
"remember_me" => "Eingeloggt bleiben",
|
||||
"signing_in" => "Einloggen",
|
||||
"sign_in" => "Einloggen",
|
||||
"forgot_password" => "Passwort vergessen?",
|
||||
"passwords_do_not_match" => "Die Passwörter stimmen nicht überein",
|
||||
"register_text" => "Noch keinen Account? Jetzt registrieren",
|
||||
"6_digit_code" => "6-stelliger Code",
|
||||
"2fa_title" => "Es werden weitere Informationen zum Einloggen benötigt",
|
||||
"2fa_text" => "Stecke dein 2FA-Gerät ein. Möglicherweise wird noch eine Interaktion benötigt, z.B. durch Eingabe einer PIN oder durch Berühren des Geräts",
|
||||
];
|
||||
11
Core/Localization/de_DE/general.php
Normal file
11
Core/Localization/de_DE/general.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
"something_went_wrong" => "Etwas ist schief gelaufen",
|
||||
"retry" => "Erneut versuchen",
|
||||
"Go back" => "Zurück",
|
||||
"submitting" => "Übermittle",
|
||||
"submit" => "Absenden",
|
||||
"language" => "Sprache",
|
||||
"loading" => "Laden",
|
||||
];
|
||||
19
Core/Localization/en_US/account.php
Normal file
19
Core/Localization/en_US/account.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
"title" => "Sign In",
|
||||
"description" => "Sign In into your account",
|
||||
"form_title" => "Please fill with your details",
|
||||
"username" => "Username",
|
||||
"username_or_email" => "Username or E-Mail",
|
||||
"password" => "Password",
|
||||
"remember_me" => "Remember Me",
|
||||
"signing_in" => "Signing in",
|
||||
"sign_in" => "Sign In",
|
||||
"forgot_password" => "Forgot password?",
|
||||
"register_text" => "Don't have an account? Sign Up",
|
||||
"passwords_do_not_match" => "Your passwords did not match",
|
||||
"6_digit_code" => "6-Digit Code",
|
||||
"2fa_title" => "Additional information is required for logging in",
|
||||
"2fa_text" => "Plugin your 2FA-Device. Interaction might be required, e.g. typing in a PIN or touching it."
|
||||
];
|
||||
11
Core/Localization/en_US/general.php
Normal file
11
Core/Localization/en_US/general.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
"something_went_wrong" => "Something went wrong",
|
||||
"retry" => "Retry",
|
||||
"go_back" => "Go Back",
|
||||
"submitting" => "Submitting",
|
||||
"submit" => "Submit",
|
||||
"language" => "Language",
|
||||
"loading" => "Loading",
|
||||
];
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Core\Objects;
|
||||
|
||||
use Core\Objects\DatabaseEntity\Language;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
@@ -9,10 +10,16 @@ class CustomTwigFunctions extends AbstractExtension {
|
||||
public function getFunctions(): array {
|
||||
return [
|
||||
new TwigFunction('L', array($this, 'translate')),
|
||||
new TwigFunction('LoadLanguageModule', array($this, 'loadLanguageModule')),
|
||||
];
|
||||
}
|
||||
|
||||
public function translate(string $key): string {
|
||||
return L($key);
|
||||
}
|
||||
|
||||
public function loadLanguageModule(string $module): void {
|
||||
$language = Language::getInstance();
|
||||
$language->loadModule($module);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Core\Objects\DatabaseEntity\Controller;
|
||||
|
||||
use Core\Driver\SQL\Condition\Compare;
|
||||
use Core\Driver\SQL\Condition\Condition;
|
||||
use Core\Driver\SQL\SQL;
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
namespace Core\Objects\DatabaseEntity\Controller;
|
||||
|
||||
use Core\Driver\Logger\Logger;
|
||||
use Core\Driver\SQL\Condition\Condition;
|
||||
use Core\Driver\SQL\Join;
|
||||
use Core\Driver\SQL\Query\Select;
|
||||
use Core\Driver\SQL\SQL;
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace Core\Objects\DatabaseEntity {
|
||||
|
||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||
use Core\Objects\DatabaseEntity\Attribute\Transient;
|
||||
use Core\Objects\lang\LanguageModule;
|
||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
||||
|
||||
// TODO: language from cookie?
|
||||
@@ -14,11 +13,11 @@ namespace Core\Objects\DatabaseEntity {
|
||||
const GERMAN_STANDARD = 2;
|
||||
|
||||
const LANG_CODE_PATTERN = "/^[a-zA-Z]{2}_[a-zA-Z]{2}$/";
|
||||
const LANG_MODULE_PATTERN = "/[a-zA-Z0-9_-]/";
|
||||
|
||||
#[MaxLength(5)] private string $code;
|
||||
#[MaxLength(32)] private string $name;
|
||||
|
||||
#[Transient] private array $modules = [];
|
||||
#[Transient] protected array $entries = [];
|
||||
|
||||
public function __construct(int $id, string $code, string $name) {
|
||||
@@ -39,22 +38,6 @@ namespace Core\Objects\DatabaseEntity {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function loadModule(LanguageModule|string $module) {
|
||||
if (!is_object($module)) {
|
||||
$module = new $module();
|
||||
}
|
||||
|
||||
if (!in_array($module, $this->modules)) {
|
||||
$moduleEntries = $module->getEntries($this->code);
|
||||
$this->entries = array_merge($this->entries, $moduleEntries);
|
||||
$this->modules[] = $module;
|
||||
}
|
||||
}
|
||||
|
||||
public function translate(string $key): string {
|
||||
return $this->entries[$key] ?? $key;
|
||||
}
|
||||
|
||||
public function sendCookie(string $domain) {
|
||||
setcookie('lang', $this->code, 0, "/", $domain, false, false);
|
||||
}
|
||||
@@ -73,6 +56,11 @@ namespace Core\Objects\DatabaseEntity {
|
||||
$LANGUAGE = $this;
|
||||
}
|
||||
|
||||
public static function getInstance(): Language {
|
||||
global $LANGUAGE;
|
||||
return $LANGUAGE;
|
||||
}
|
||||
|
||||
public static function DEFAULT_LANGUAGE(bool $fromCookie = true): Language {
|
||||
if ($fromCookie && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
||||
$acceptLanguage = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
|
||||
@@ -94,16 +82,70 @@ namespace Core\Objects\DatabaseEntity {
|
||||
return new Language(1, "en_US", "American English");
|
||||
}
|
||||
|
||||
public function getEntries(): array {
|
||||
return $this->entries;
|
||||
public function getEntries(?string $module = null): ?array {
|
||||
if (!$module) {
|
||||
return $this->entries;
|
||||
} else {
|
||||
return $this->entries[$module] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
public function translate(string $key): string {
|
||||
if (preg_match("/(\w+)\.(\w+)/", $key, $matches)) {
|
||||
$module = $matches[1];
|
||||
$moduleKey = $matches[2];
|
||||
if ($this->hasModule($module) && array_key_exists($moduleKey, $this->entries[$module])) {
|
||||
return $this->entries[$module][$moduleKey];
|
||||
}
|
||||
}
|
||||
|
||||
return "[$key]";
|
||||
}
|
||||
|
||||
public function addModule(string $module, array $entries) {
|
||||
if ($this->hasModule($module)) {
|
||||
$this->entries[$module] = array_merge($this->entries[$module], $entries);
|
||||
} else {
|
||||
$this->entries[$module] = $entries;
|
||||
}
|
||||
}
|
||||
|
||||
public function loadModule(string $module, bool $forceReload=false): bool {
|
||||
if ($this->hasModule($module) && !$forceReload) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!preg_match(self::LANG_MODULE_PATTERN, $module)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!preg_match(self::LANG_CODE_PATTERN, $this->code)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (["Site", "Core"] as $baseDir) {
|
||||
$filePath = realpath(implode("/", [$baseDir, "Localization", $this->code, "$module.php"]));
|
||||
if ($filePath && is_file($filePath)) {
|
||||
$moduleEntries = @include_once $filePath;
|
||||
$this->addModule($module, $moduleEntries);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function hasModule(string $module): bool {
|
||||
return array_key_exists($module, $this->entries);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
function L($key) {
|
||||
if (!array_key_exists('LANGUAGE', $GLOBALS))
|
||||
return $key;
|
||||
function L(string $key): string {
|
||||
if (!array_key_exists('LANGUAGE', $GLOBALS)) {
|
||||
return "[$key]";
|
||||
}
|
||||
|
||||
global $LANGUAGE;
|
||||
return $LANGUAGE->translate($key);
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Core\Objects\lang;
|
||||
|
||||
abstract class LanguageModule {
|
||||
|
||||
public abstract function getEntries(string $langCode);
|
||||
}
|
||||
@@ -8,10 +8,13 @@
|
||||
<script src="/js/bootstrap.bundle.min.js" nonce="{{ site.csp.nonce }}"></script>
|
||||
<link rel="stylesheet" href="/css/fontawesome.min.css" nonce="{{ site.csp.nonce }}">
|
||||
<link rel="stylesheet" href="/css/account.css" nonce="{{ site.csp.nonce }}">
|
||||
<title>Account - {{ view_title }}</title>
|
||||
<title>Account - {{ L(view_title) }}</title>
|
||||
{% if site.recaptcha.enabled %}
|
||||
<script src="https://www.google.com/recaptcha/api.js?render={{ site.recaptcha.key }}" nonce="{{ site.csp.nonce }}"></script>
|
||||
{% endif %}
|
||||
<script nonce="{{ site.csp.nonce }}">
|
||||
window.languageEntries = {{ site.language.entries|json_encode()|raw }};
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
@@ -20,8 +23,8 @@
|
||||
<div class="col-md-3 py-5 bg-primary text-white text-center" style='border-top-left-radius:.4em;border-bottom-left-radius:.4em;margin-left: auto'>
|
||||
<div class="card-body">
|
||||
<i class="fas fa-{{ view_icon }} fa-3x"></i>
|
||||
<h2 class="py-3">{{ view_title }}</h2>
|
||||
<p>{{ view_description }}</p>
|
||||
<h2 class="py-3">{{ L(view_title) }}</h2>
|
||||
<p>{{ L(view_description) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-5 pt-5 pb-2 border border-info" style='border-top-right-radius:.4em;border-bottom-right-radius:.4em;margin-right:auto'>
|
||||
|
||||
@@ -1,32 +1,36 @@
|
||||
{% extends "account/account_base.twig" %}
|
||||
|
||||
{% set view_title = 'Sign In' %}
|
||||
{% set view_title = 'account.title' %}
|
||||
{% set view_icon = 'user-lock' %}
|
||||
{% set view_description = 'Sign In into your account' %}
|
||||
{% set view_description = 'account.description' %}
|
||||
|
||||
{% block view_content %}
|
||||
|
||||
<h4 class="pb-4">Please fill with your details</h4>
|
||||
<h4 class="pb-4">{{ L("account.form_title") }}</h4>
|
||||
<form>
|
||||
<div class="input-group">
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text"><i class="fas fa-hashtag"></i></span>
|
||||
</div>
|
||||
<input id="username" autocomplete='username' name="username" placeholder="Username or E-Mail" class="form-control" type="text" maxlength="32">
|
||||
<input id="username" autocomplete='username' name="username"
|
||||
placeholder="{{ L("account.username_or_email") }}" class="form-control" type="text" maxlength="32">
|
||||
</div>
|
||||
<div class="input-group mt-3">
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text"><i class="fas fa-key"></i></span>
|
||||
</div>
|
||||
<input type="password" autocomplete='password' name='password' id='password' class="form-control" placeholder="Password">
|
||||
<input type="password" autocomplete='password' name='password' id='password' class="form-control"
|
||||
placeholder={{ L("account.password") }}>
|
||||
</div>
|
||||
<div class="ml-2" style="line-height: 38px;">
|
||||
<a href="/resetPassword">Forgot Password?</a>
|
||||
<a href="/resetPassword">{{ L("account.forgot_password") }}</a>
|
||||
</div>
|
||||
<div class="input-group mt-3 mb-4">
|
||||
<button type="button" class="btn btn-primary" id='btnLogin'>Sign In</button>
|
||||
<button type="button" class="btn btn-primary" id='btnLogin'>{{ L("account.sign_in") }}</button>
|
||||
{% if site.registrationEnabled %}
|
||||
<div class="ml-2" style="line-height: 38px;">Don't have an account yet? <a href="/register">Click here</a> to register.</div>
|
||||
<div class="ml-2" style="line-height: 38px;">
|
||||
<a href="/register">{{ L("account.register_text") }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -9,4 +9,5 @@
|
||||
<noscript>You need Javascript enabled to run this app</noscript>
|
||||
<div class="wrapper" type="module" id="admin-panel"></div>
|
||||
<script src="/js/admin-panel/index.js" nonce="{{ site.csp.nonce }}"></script>
|
||||
<link rel="stylesheet" href="/js/admin-panel/index.css" nonce="{{ site.csp.nonce }}"></link>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user