Cleanup + Login View
This commit is contained in:
parent
f46705cd84
commit
872ef4099a
20
cli.php
20
cli.php
@ -621,13 +621,29 @@ function onMail($argv) {
|
|||||||
|
|
||||||
function onImpersonate($argv) {
|
function onImpersonate($argv) {
|
||||||
if (count($argv) < 3) {
|
if (count($argv) < 3) {
|
||||||
_exit("Usage: cli.php impersonate <user_id>");
|
_exit("Usage: cli.php impersonate <user_id|user_name>");
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = getUser() or exit;
|
$user = getUser() or exit;
|
||||||
$user->createSession(intval($argv[2]));
|
|
||||||
|
$userId = $argv[2];
|
||||||
|
if (!is_numeric($userId)) {
|
||||||
|
$sql = $user->getSQL();
|
||||||
|
$res = $sql->select("uid")
|
||||||
|
->from("User")
|
||||||
|
->where(new Compare("name", $userId))
|
||||||
|
->execute();
|
||||||
|
if ($res === false) {
|
||||||
|
_exit("SQL error: " . $sql->getLastError());
|
||||||
|
} else {
|
||||||
|
$userId = $res[0]["uid"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$user->createSession(intval($userId));
|
||||||
$session = $user->getSession();
|
$session = $user->getSession();
|
||||||
$session->setData(["2faAuthenticated" => true]);
|
$session->setData(["2faAuthenticated" => true]);
|
||||||
|
$session->update(false);
|
||||||
echo "session=" . $session->getCookie() . PHP_EOL;
|
echo "session=" . $session->getCookie() . PHP_EOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +160,7 @@ class CreateDatabase extends DatabaseScript {
|
|||||||
->addRow("^/confirmEmail/?$", "dynamic", "\\Documents\\Account", "account/confirm_email.twig")
|
->addRow("^/confirmEmail/?$", "dynamic", "\\Documents\\Account", "account/confirm_email.twig")
|
||||||
->addRow("^/acceptInvite/?$", "dynamic", "\\Documents\\Account", "account/accept_invite.twig")
|
->addRow("^/acceptInvite/?$", "dynamic", "\\Documents\\Account", "account/accept_invite.twig")
|
||||||
->addRow("^/resetPassword/?$", "dynamic", "\\Documents\\Account", "account/reset_password.twig")
|
->addRow("^/resetPassword/?$", "dynamic", "\\Documents\\Account", "account/reset_password.twig")
|
||||||
|
->addRow("^/login/?$", "dynamic", "\\Documents\\Account", "account/login.twig")
|
||||||
->addRow("^/resendConfirmEmail/?$", "dynamic", "\\Documents\\Account", "account/resend_confirm_email.twig")
|
->addRow("^/resendConfirmEmail/?$", "dynamic", "\\Documents\\Account", "account/resend_confirm_email.twig")
|
||||||
->addRow("^/$", "static", "/static/welcome.html", NULL);
|
->addRow("^/$", "static", "/static/welcome.html", NULL);
|
||||||
|
|
||||||
|
@ -40,6 +40,9 @@ class Account extends TemplateDocument {
|
|||||||
} else if (!$settings->isRegistrationAllowed()) {
|
} else if (!$settings->isRegistrationAllowed()) {
|
||||||
$this->createError("Registration is not enabled on this website.");
|
$this->createError("Registration is not enabled on this website.");
|
||||||
}
|
}
|
||||||
|
} else if ($this->getTemplateName() === "account/login.twig" && $this->user->isLoggedIn()) {
|
||||||
|
header("Location: /admin");
|
||||||
|
exit();
|
||||||
} else if ($this->getTemplateName() === "account/accept_invite.twig") {
|
} else if ($this->getTemplateName() === "account/accept_invite.twig") {
|
||||||
if (isset($_GET["token"]) && is_string($_GET["token"]) && !empty($_GET["token"])) {
|
if (isset($_GET["token"]) && is_string($_GET["token"]) && !empty($_GET["token"])) {
|
||||||
$this->parameters["view"]["token"] = $_GET["token"];
|
$this->parameters["view"]["token"] = $_GET["token"];
|
||||||
|
@ -1,65 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Documents {
|
|
||||||
|
|
||||||
use Documents\Files\FilesBody;
|
|
||||||
use Documents\Files\FilesHead;
|
|
||||||
use Elements\Document;
|
|
||||||
use Objects\User;
|
|
||||||
|
|
||||||
class Files extends Document {
|
|
||||||
public function __construct(User $user, string $view = NULL) {
|
|
||||||
parent::__construct($user, FilesHead::class, FilesBody::class, $view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Documents\Files {
|
|
||||||
|
|
||||||
use Elements\Head;
|
|
||||||
use Elements\Link;
|
|
||||||
use Elements\Script;
|
|
||||||
use Elements\SimpleBody;
|
|
||||||
|
|
||||||
class FilesHead extends Head {
|
|
||||||
|
|
||||||
protected function initSources() {
|
|
||||||
$this->addCSS(Link::BOOTSTRAP);
|
|
||||||
$this->loadFontawesome();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function initMetas(): array {
|
|
||||||
return array(
|
|
||||||
array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'),
|
|
||||||
array('name' => 'format-detection', 'content' => 'telephone=yes'),
|
|
||||||
array('charset' => 'utf-8'),
|
|
||||||
array('http-equiv' => 'expires', 'content' => '0'),
|
|
||||||
array('name' => 'robots', 'content' => 'noarchive'),
|
|
||||||
array('name' => 'referrer', 'content' => 'origin')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function initRawFields(): array {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function initTitle(): string {
|
|
||||||
return "File Control Panel";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FilesBody extends SimpleBody {
|
|
||||||
|
|
||||||
public function __construct($document) {
|
|
||||||
parent::__construct($document);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getContent(): string {
|
|
||||||
$html = "<noscript>" . $this->createErrorText("Javascript is required for this site to render.") . "</noscript>";
|
|
||||||
$html .= "<div id=\"root\"></div>";
|
|
||||||
$html .= new Script(Script::MIME_TEXT_JAVASCRIPT, Script::FILES);
|
|
||||||
return $html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -58,6 +58,7 @@ class TemplateDocument extends Document {
|
|||||||
$params["site"] = [
|
$params["site"] = [
|
||||||
"name" => $settings->getSiteName(),
|
"name" => $settings->getSiteName(),
|
||||||
"baseUrl" => $settings->getBaseUrl(),
|
"baseUrl" => $settings->getBaseUrl(),
|
||||||
|
"registrationEnabled" => $settings->isRegistrationAllowed(),
|
||||||
"title" => $this->title,
|
"title" => $this->title,
|
||||||
"recaptcha" => [
|
"recaptcha" => [
|
||||||
"key" => $settings->isRecaptchaEnabled() ? $settings->getRecaptchaSiteKey() : null,
|
"key" => $settings->isRecaptchaEnabled() ? $settings->getRecaptchaSiteKey() : null,
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Objects;
|
|
||||||
|
|
||||||
class KeyBasedTwoFactorToken extends TwoFactorToken {
|
|
||||||
|
|
||||||
const TYPE = "fido2";
|
|
||||||
|
|
||||||
public function __construct(string $secret, ?int $id = null, bool $confirmed = false) {
|
|
||||||
parent::__construct(self::TYPE, $secret, $id, $confirmed);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Objects;
|
|
||||||
|
|
||||||
use Base32\Base32;
|
|
||||||
use chillerlan\QRCode\QRCode;
|
|
||||||
use chillerlan\QRCode\QROptions;
|
|
||||||
|
|
||||||
class TimeBasedTwoFactorToken extends TwoFactorToken {
|
|
||||||
|
|
||||||
const TYPE = "totp";
|
|
||||||
|
|
||||||
public function __construct(string $secret, ?int $id = null, bool $confirmed = false) {
|
|
||||||
parent::__construct(self::TYPE, $secret, $id, $confirmed);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getUrl(User $user): string {
|
|
||||||
$otpType = self::TYPE;
|
|
||||||
$name = rawurlencode($user->getUsername());
|
|
||||||
$settings = $user->getConfiguration()->getSettings();
|
|
||||||
$urlArgs = [
|
|
||||||
"secret" => $this->getSecret(),
|
|
||||||
"issuer" => $settings->getSiteName(),
|
|
||||||
];
|
|
||||||
|
|
||||||
$urlArgs = http_build_query($urlArgs);
|
|
||||||
return "otpauth://$otpType/$name?$urlArgs";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generateQRCode(User $user) {
|
|
||||||
$options = new QROptions(['outputType' => QRCode::OUTPUT_IMAGE_PNG, "imageBase64" => false]);
|
|
||||||
$qrcode = new QRCode($options);
|
|
||||||
return $qrcode->render($this->getUrl($user));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generate(?int $at = null, int $length = 6, int $period = 30): string {
|
|
||||||
if ($at === null) {
|
|
||||||
$at = time();
|
|
||||||
}
|
|
||||||
|
|
||||||
$seed = intval($at / $period);
|
|
||||||
$secret = Base32::decode($this->getSecret());
|
|
||||||
$hmac = hash_hmac('sha1', pack("J", $seed), $secret, true);
|
|
||||||
$offset = ord($hmac[-1]) & 0xF;
|
|
||||||
$code = (unpack("N", substr($hmac, $offset, 4))[1] & 0x7fffffff) % intval(pow(10, $length));
|
|
||||||
return substr(str_pad(strval($code), $length, "0", STR_PAD_LEFT), -1 * $length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function verify(string $code): bool {
|
|
||||||
return $this->generate() === $code;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Objects;
|
|
||||||
|
|
||||||
abstract class TwoFactorToken extends ApiObject {
|
|
||||||
|
|
||||||
private ?int $id;
|
|
||||||
private string $type;
|
|
||||||
private string $secret;
|
|
||||||
private bool $confirmed;
|
|
||||||
private bool $authenticated;
|
|
||||||
|
|
||||||
public function __construct(string $type, string $secret, ?int $id = null, bool $confirmed = false) {
|
|
||||||
$this->id = $id;
|
|
||||||
$this->type = $type;
|
|
||||||
$this->secret = $secret;
|
|
||||||
$this->confirmed = $confirmed;
|
|
||||||
$this->authenticated = $_SESSION["2faAuthenticated"] ?? false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function jsonSerialize(): array {
|
|
||||||
return [
|
|
||||||
"id" => $this->id,
|
|
||||||
"type" => $this->type,
|
|
||||||
"confirmed" => $this->confirmed,
|
|
||||||
"authenticated" => $this->authenticated,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function authenticate() {
|
|
||||||
$this->authenticated = true;
|
|
||||||
$_SESSION["2faAuthenticated"] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getType(): string {
|
|
||||||
return $this->type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSecret(): string {
|
|
||||||
return $this->secret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isConfirmed(): bool {
|
|
||||||
return $this->confirmed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getId(): int {
|
|
||||||
return $this->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function newInstance(string $type, string $secret, ?int $id = null, bool $confirmed = false) {
|
|
||||||
if ($type === TimeBasedTwoFactorToken::TYPE) {
|
|
||||||
return new TimeBasedTwoFactorToken($secret, $id, $confirmed);
|
|
||||||
} else {
|
|
||||||
// TODO: error message
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isAuthenticated(): bool {
|
|
||||||
return $this->authenticated;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
{% extends "base.twig" %}
|
|
||||||
|
|
||||||
{% block head %}
|
|
||||||
<script src="/js/jquery.min.js" nonce="{{ site.csp.nonce }}"></script>
|
|
||||||
<script src="/js/script.js" nonce="{{ site.csp.nonce }}"></script>
|
|
||||||
<script src="/js/account.js" nonce="{{ site.csp.nonce }}"></script>
|
|
||||||
<link rel="stylesheet" href="/css/bootstrap.min.css" nonce="{{ site.csp.nonce }}">
|
|
||||||
<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 - {{ title }}</title>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
31
core/Templates/account/login.twig
Normal file
31
core/Templates/account/login.twig
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{% extends "account.twig" %}
|
||||||
|
|
||||||
|
{% set view_title = 'Sign In' %}
|
||||||
|
{% set view_icon = 'user-lock' %}
|
||||||
|
{% set view_description = 'Sign In into your account' %}
|
||||||
|
|
||||||
|
{% block view_content %}
|
||||||
|
|
||||||
|
<h4 class="pb-4">Please fill with your details</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">
|
||||||
|
</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">
|
||||||
|
</div>
|
||||||
|
<div class="input-group mt-5 mb-4">
|
||||||
|
<button type="button" class="btn btn-primary" id='btnLogin'>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>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -1,8 +0,0 @@
|
|||||||
Hello {{ username }},<br><br>
|
|
||||||
You recently created an account on {{ site_name }}. Please click on the following link to confirm your email address and complete your registration.<br>
|
|
||||||
If you haven't registered an account, you can simply ignore this email. The link is valid for the next 48 hours:<br><br>
|
|
||||||
|
|
||||||
<a href="{{ link }}">{{ link }}</a><br><br>
|
|
||||||
|
|
||||||
Best Regards<br>
|
|
||||||
{{ site_name }} Administration
|
|
@ -1,89 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
|
|
||||||
namespace Views\Account;
|
|
||||||
|
|
||||||
|
|
||||||
use Elements\Document;
|
|
||||||
use Elements\View;
|
|
||||||
|
|
||||||
class AcceptInvite extends AccountView {
|
|
||||||
|
|
||||||
private bool $success;
|
|
||||||
private string $message;
|
|
||||||
private array $invitedUser;
|
|
||||||
|
|
||||||
public function __construct(Document $document, $loadView = true) {
|
|
||||||
parent::__construct($document, $loadView);
|
|
||||||
$this->title = "Invitation";
|
|
||||||
$this->description = "Finnish your account registration by choosing a password.";
|
|
||||||
$this->icon = "user-check";
|
|
||||||
$this->success = false;
|
|
||||||
$this->message = "No content";
|
|
||||||
$this->invitedUser = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function loadView() {
|
|
||||||
parent::loadView();
|
|
||||||
|
|
||||||
if (isset($_GET["token"]) && is_string($_GET["token"]) && !empty($_GET["token"])) {
|
|
||||||
$req = new \Api\User\CheckToken($this->getDocument()->getUser());
|
|
||||||
$this->success = $req->execute(array("token" => $_GET["token"]));
|
|
||||||
if ($this->success) {
|
|
||||||
if (strcmp($req->getResult()["token"]["type"], "invite") !== 0) {
|
|
||||||
$this->success = false;
|
|
||||||
$this->message = "The given token has a wrong type.";
|
|
||||||
} else {
|
|
||||||
$this->invitedUser = $req->getResult()["user"];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->message = "Error confirming e-mail address: " . $req->getLastError();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->success = false;
|
|
||||||
$this->message = "The link you visited is no longer valid";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getAccountContent() {
|
|
||||||
if (!$this->success) {
|
|
||||||
return $this->createErrorText($this->message);
|
|
||||||
}
|
|
||||||
|
|
||||||
$token = htmlspecialchars($_GET["token"], ENT_QUOTES);
|
|
||||||
$username = $this->invitedUser["name"];
|
|
||||||
$emailAddress = $this->invitedUser["email"];
|
|
||||||
|
|
||||||
return "<h4 class=\"pb-4\">Please fill with your details</h4>
|
|
||||||
<form>
|
|
||||||
<input name='token' id='token' type='hidden' value='$token'/>
|
|
||||||
<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\" name=\"username\" placeholder=\"Username\" class=\"form-control\" type=\"text\" maxlength=\"32\" value='$username' disabled>
|
|
||||||
</div>
|
|
||||||
<div class=\"input-group mt-3\">
|
|
||||||
<div class=\"input-group-append\">
|
|
||||||
<span class=\"input-group-text\"><i class=\"fas fa-at\"></i></span>
|
|
||||||
</div>
|
|
||||||
<input type=\"email\" name='email' id='email' class=\"form-control\" placeholder=\"Email\" maxlength=\"64\" value='$emailAddress' disabled>
|
|
||||||
</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='new-password' name='password' id='password' class=\"form-control\" placeholder=\"Password\">
|
|
||||||
</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='new-password' name='confirmPassword' id='confirmPassword' class=\"form-control\" placeholder=\"Confirm Password\">
|
|
||||||
</div>
|
|
||||||
<div class=\"input-group mt-3\">
|
|
||||||
<button type=\"button\" class=\"btn btn-success\" id='btnAcceptInvite'>Submit</button>
|
|
||||||
</div>
|
|
||||||
</form>";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Views\Account;
|
|
||||||
|
|
||||||
use Elements\Document;
|
|
||||||
use Elements\View;
|
|
||||||
|
|
||||||
abstract class AccountView extends View {
|
|
||||||
|
|
||||||
protected string $description;
|
|
||||||
protected string $icon;
|
|
||||||
|
|
||||||
public function __construct(Document $document, $loadView = true) {
|
|
||||||
parent::__construct($document, $loadView);
|
|
||||||
$this->description = "";
|
|
||||||
$this->icon = "image";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function loadView() {
|
|
||||||
parent::loadView();
|
|
||||||
|
|
||||||
$document = $this->getDocument();
|
|
||||||
$settings = $document->getUser()->getConfiguration()->getSettings();
|
|
||||||
if ($settings->isRecaptchaEnabled()) {
|
|
||||||
$document->getHead()->loadGoogleRecaptcha($settings->getRecaptchaSiteKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCode(): string {
|
|
||||||
$html = parent::getCode();
|
|
||||||
|
|
||||||
$content = $this->getAccountContent();
|
|
||||||
$icon = $this->createIcon($this->icon, "fas", "fa-3x");
|
|
||||||
|
|
||||||
$html .= "<div class=\"container mt-5\">
|
|
||||||
<div class=\"row\">
|
|
||||||
<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\">
|
|
||||||
$icon
|
|
||||||
<h2 class=\"py-3\">$this->title</h2>
|
|
||||||
<p>$this->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'>
|
|
||||||
$content
|
|
||||||
<div class='alert mt-2' style='display:none' id='alertMessage'></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>";
|
|
||||||
|
|
||||||
$settings = $this->getDocument()->getUser()->getConfiguration()->getSettings();
|
|
||||||
if ($settings->isRecaptchaEnabled()) {
|
|
||||||
$siteKey = $settings->getRecaptchaSiteKey();
|
|
||||||
$html .= "<input type='hidden' value='$siteKey' id='siteKey' />";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $html;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract function getAccountContent();
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
|
|
||||||
namespace Views\Account;
|
|
||||||
|
|
||||||
|
|
||||||
use Elements\Document;
|
|
||||||
use Elements\Script;
|
|
||||||
|
|
||||||
class ConfirmEmail extends AccountView {
|
|
||||||
|
|
||||||
public function __construct(Document $document, $loadView = true) {
|
|
||||||
parent::__construct($document, $loadView);
|
|
||||||
$this->title = "Confirm Email";
|
|
||||||
$this->description = "Request a password reset, once you got the e-mail address, you can choose a new password";
|
|
||||||
$this->icon = "user-check";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function loadView() {
|
|
||||||
parent::loadView();
|
|
||||||
$this->getDocument()->getHead()->addScript(Script::MIME_TEXT_JAVASCRIPT, "", '
|
|
||||||
$(document).ready(function() {
|
|
||||||
var token = jsCore.getParameter("token");
|
|
||||||
if (token) {
|
|
||||||
jsCore.apiCall("/user/confirmEmail", { token: token }, (res) => {
|
|
||||||
$("#confirm-status").removeClass("alert-info");
|
|
||||||
if (!res.success) {
|
|
||||||
$("#confirm-status").addClass("alert-danger");
|
|
||||||
$("#confirm-status").text("Error confirming e-mail address: " + res.msg);
|
|
||||||
} else {
|
|
||||||
$("#confirm-status").addClass("alert-success");
|
|
||||||
$("#confirm-status").text("Your e-mail address was successfully confirmed, you may now log in.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
$("#confirm-status").removeClass("alert-info");
|
|
||||||
$("#confirm-status").addClass("alert-danger");
|
|
||||||
$("#confirm-status").text("The link you visited is no longer valid");
|
|
||||||
}
|
|
||||||
});'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getAccountContent() {
|
|
||||||
|
|
||||||
$spinner = $this->createIcon("spinner");
|
|
||||||
$html = "<noscript><div class=\"alert alert-danger\">Javascript is required</div></noscript>
|
|
||||||
<div class=\"alert alert-info\" id=\"confirm-status\">
|
|
||||||
Confirming email… $spinner
|
|
||||||
</div>";
|
|
||||||
|
|
||||||
$html .= "<a href='/login'><button class='btn btn-primary' style='position: absolute; bottom: 10px' type='button'>Proceed to Login</button></a>";
|
|
||||||
return $html;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
|
|
||||||
namespace Views\Account;
|
|
||||||
|
|
||||||
use Elements\Document;
|
|
||||||
|
|
||||||
class Register extends AccountView {
|
|
||||||
|
|
||||||
public function __construct(Document $document, $loadView = true) {
|
|
||||||
parent::__construct($document, $loadView);
|
|
||||||
$this->title = "Registration";
|
|
||||||
$this->description = "Create a new account";
|
|
||||||
$this->icon = "user-plus";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAccountContent() {
|
|
||||||
|
|
||||||
$user = $this->getDocument()->getUser();
|
|
||||||
if ($user->isLoggedIn()) {
|
|
||||||
header(302);
|
|
||||||
header("Location: /");
|
|
||||||
die("You are already logged in.");
|
|
||||||
}
|
|
||||||
|
|
||||||
$settings = $user->getConfiguration()->getSettings();
|
|
||||||
if (!$settings->isRegistrationAllowed()) {
|
|
||||||
return $this->createErrorText(
|
|
||||||
"Registration is not enabled on this website. If you are an administrator,
|
|
||||||
goto <a href=\"/admin/settings\">/admin/settings</a>, to enable the user registration"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return "<h4 class=\"pb-4\">Please fill with your details</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\" 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-at\"></i></span>
|
|
||||||
</div>
|
|
||||||
<input type=\"email\" autocomplete='email' name='email' id='email' class=\"form-control\" placeholder=\"Email\" maxlength=\"64\">
|
|
||||||
</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='new-password' name='password' id='password' class=\"form-control\" placeholder=\"Password\">
|
|
||||||
</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='new-password' name='confirmPassword' id='confirmPassword' class=\"form-control\" placeholder=\"Confirm Password\">
|
|
||||||
</div>
|
|
||||||
<div class=\"input-group mt-3\">
|
|
||||||
<button type=\"button\" class=\"btn btn-primary\" id='btnRegister'>Submit</button>
|
|
||||||
<a href='/login' style='margin-left: 10px'>
|
|
||||||
<button class='btn btn-secondary' type='button'>
|
|
||||||
Back to Login
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</form>";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
|
|
||||||
namespace Views\Account;
|
|
||||||
|
|
||||||
|
|
||||||
use Elements\Document;
|
|
||||||
|
|
||||||
class ResendConfirmEmail extends AccountView {
|
|
||||||
|
|
||||||
public function __construct(Document $document, $loadView = true) {
|
|
||||||
parent::__construct($document, $loadView);
|
|
||||||
$this->title = "Resend Confirm Email";
|
|
||||||
$this->description = "Request a new confirmation email to finalize the account creation";
|
|
||||||
$this->icon = "envelope";
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getAccountContent() {
|
|
||||||
return "<p class='lead'>Enter your E-Mail address, to receive a new e-mail to confirm your registration.</p>
|
|
||||||
<form>
|
|
||||||
<div class=\"input-group\">
|
|
||||||
<div class=\"input-group-append\">
|
|
||||||
<span class=\"input-group-text\"><i class=\"fas fa-at\"></i></span>
|
|
||||||
</div>
|
|
||||||
<input id=\"email\" autocomplete='email' name=\"email\" placeholder=\"E-Mail address\" class=\"form-control\" type=\"email\" maxlength=\"64\" />
|
|
||||||
</div>
|
|
||||||
<div class=\"input-group mt-2\" style='position: absolute;bottom: 15px'>
|
|
||||||
<button id='btnResendConfirmEmail' class='btn btn-primary'>
|
|
||||||
Request
|
|
||||||
</button>
|
|
||||||
<a href='/login' style='margin-left: 10px'>
|
|
||||||
<button class='btn btn-secondary' type='button'>
|
|
||||||
Back to Login
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,99 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
|
|
||||||
namespace Views\Account;
|
|
||||||
|
|
||||||
|
|
||||||
use Elements\Document;
|
|
||||||
|
|
||||||
class ResetPassword extends AccountView {
|
|
||||||
|
|
||||||
private bool $success;
|
|
||||||
private string $message;
|
|
||||||
private ?string $token;
|
|
||||||
|
|
||||||
public function __construct(Document $document, $loadView = true) {
|
|
||||||
parent::__construct($document, $loadView);
|
|
||||||
$this->title = "Reset Password";
|
|
||||||
$this->description = "Request a password reset, once you got the e-mail address, you can choose a new password";
|
|
||||||
$this->icon = "user-lock";
|
|
||||||
$this->success = true;
|
|
||||||
$this->message = "";
|
|
||||||
$this->token = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function loadView() {
|
|
||||||
parent::loadView();
|
|
||||||
|
|
||||||
if (isset($_GET["token"]) && is_string($_GET["token"]) && !empty($_GET["token"])) {
|
|
||||||
$this->token = $_GET["token"];
|
|
||||||
$req = new \Api\User\CheckToken($this->getDocument()->getUser());
|
|
||||||
$this->success = $req->execute(array("token" => $_GET["token"]));
|
|
||||||
if ($this->success) {
|
|
||||||
if (strcmp($req->getResult()["token"]["type"], "password_reset") !== 0) {
|
|
||||||
$this->success = false;
|
|
||||||
$this->message = "The given token has a wrong type.";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->message = "Error requesting password reset: " . $req->getLastError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getAccountContent() {
|
|
||||||
if (!$this->success) {
|
|
||||||
$html = $this->createErrorText($this->message);
|
|
||||||
if ($this->token !== null) {
|
|
||||||
$html .= "<a href='/resetPassword' class='btn btn-primary'>Go back</a>";
|
|
||||||
}
|
|
||||||
return $html;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->token === null) {
|
|
||||||
return "<p class='lead'>Enter your E-Mail address, to receive a password reset token.</p>
|
|
||||||
<form>
|
|
||||||
<div class=\"input-group\">
|
|
||||||
<div class=\"input-group-append\">
|
|
||||||
<span class=\"input-group-text\"><i class=\"fas fa-at\"></i></span>
|
|
||||||
</div>
|
|
||||||
<input id=\"email\" autocomplete='email' name=\"email\" placeholder=\"E-Mail address\" class=\"form-control\" type=\"email\" maxlength=\"64\" />
|
|
||||||
</div>
|
|
||||||
<div class=\"input-group mt-2\" style='position: absolute;bottom: 15px'>
|
|
||||||
<button id='btnRequestPasswordReset' class='btn btn-primary'>
|
|
||||||
Request
|
|
||||||
</button>
|
|
||||||
<a href='/login' style='margin-left: 10px'>
|
|
||||||
<button class='btn btn-secondary' type='button'>
|
|
||||||
Back to Login
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
";
|
|
||||||
} else {
|
|
||||||
return "<h4 class=\"pb-4\">Choose a new password</h4>
|
|
||||||
<form>
|
|
||||||
<input name='token' id='token' type='hidden' value='$this->token'/>
|
|
||||||
<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='new-password' name='password' id='password' class=\"form-control\" placeholder=\"Password\">
|
|
||||||
</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='new-password' name='confirmPassword' id='confirmPassword' class=\"form-control\" placeholder=\"Confirm Password\">
|
|
||||||
</div>
|
|
||||||
<div class=\"input-group mt-3\">
|
|
||||||
<button type=\"button\" class=\"btn btn-primary\" id='btnResetPassword'>Submit</button>
|
|
||||||
<a href='/login' style='margin-left: 10px; display: none' id='backToLogin'>
|
|
||||||
<button class='btn btn-success' type='button'>
|
|
||||||
Back to Login
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</form>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Views\Admin;
|
|
||||||
|
|
||||||
use Elements\Body;
|
|
||||||
use Elements\Script;
|
|
||||||
|
|
||||||
class AdminDashboardBody extends Body {
|
|
||||||
|
|
||||||
public function __construct($document) {
|
|
||||||
parent::__construct($document);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCode(): string {
|
|
||||||
$html = parent::getCode();
|
|
||||||
$script = $this->getDocument()->createScript(Script::MIME_TEXT_JAVASCRIPT, "/js/admin.min.js");
|
|
||||||
$html .= "<body><div class=\"wrapper\" id=\"root\">$script</div></body>";
|
|
||||||
return $html;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Views\Admin;
|
|
||||||
|
|
||||||
use Elements\Body;
|
|
||||||
use Elements\Link;
|
|
||||||
use Elements\Script;
|
|
||||||
use Views\LanguageFlags;
|
|
||||||
|
|
||||||
class LoginBody extends Body {
|
|
||||||
|
|
||||||
public function __construct($document) {
|
|
||||||
parent::__construct($document);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function loadView() {
|
|
||||||
parent::loadView();
|
|
||||||
$head = $this->getDocument()->getHead();
|
|
||||||
$head->loadJQuery();
|
|
||||||
$head->loadBootstrap();
|
|
||||||
$head->addJS(Script::CORE);
|
|
||||||
$head->addCSS(Link::CORE);
|
|
||||||
$head->addJS(Script::ACCOUNT);
|
|
||||||
$head->addCSS(Link::ACCOUNT);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCode(): string {
|
|
||||||
$html = parent::getCode();
|
|
||||||
|
|
||||||
$username = L("Username");
|
|
||||||
$password = L("Password");
|
|
||||||
$login = L("Login");
|
|
||||||
$backToStartPage = L("Back to Start Page");
|
|
||||||
$stayLoggedIn = L("Stay logged in");
|
|
||||||
|
|
||||||
$flags = $this->load(LanguageFlags::class);
|
|
||||||
$iconBack = $this->createIcon("arrow-circle-left");
|
|
||||||
$domain = $_SERVER['HTTP_HOST'];
|
|
||||||
$protocol = getProtocol();
|
|
||||||
|
|
||||||
$html .= "
|
|
||||||
<body>
|
|
||||||
<div class=\"container mt-4\">
|
|
||||||
<div class=\"title text-center\">
|
|
||||||
<h2>Admin Control Panel</h2>
|
|
||||||
</div>
|
|
||||||
<div class=\"row\">
|
|
||||||
<div class=\"col-lg-6 col-12 m-auto\">
|
|
||||||
<form class=\"loginForm\">
|
|
||||||
<label for=\"username\">$username</label>
|
|
||||||
<input type=\"text\" class=\"form-control\" name=\"username\" id=\"username\" placeholder=\"$username\" required autofocus />
|
|
||||||
<label for=\"password\">$password</label>
|
|
||||||
<input type=\"password\" class=\"form-control\" name=\"password\" id=\"password\" placeholder=\"$password\" required />
|
|
||||||
<div class=\"form-check\">
|
|
||||||
<input type=\"checkbox\" class=\"form-check-input\" id=\"stayLoggedIn\" name=\"stayLoggedIn\">
|
|
||||||
<label class=\"form-check-label\" for=\"stayLoggedIn\">$stayLoggedIn</label>
|
|
||||||
</div>
|
|
||||||
<button class=\"btn btn-lg btn-primary btn-block\" id=\"btnLogin\" type=\"button\">$login</button>
|
|
||||||
<div class=\"alert alert-danger\" style='display:none' role=\"alert\" id=\"alertMessage\"></div>
|
|
||||||
<span class=\"flags position-absolute\">$flags</span>
|
|
||||||
</form>
|
|
||||||
<div class=\"p-1\">
|
|
||||||
<a href=\"$protocol://$domain\">$iconBack $backToStartPage</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>";
|
|
||||||
|
|
||||||
return $html;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Views;
|
|
||||||
|
|
||||||
use Elements\View;
|
|
||||||
|
|
||||||
class LanguageFlags extends View {
|
|
||||||
|
|
||||||
private array $languageFlags;
|
|
||||||
|
|
||||||
public function __construct($document) {
|
|
||||||
parent::__construct($document);
|
|
||||||
$this->languageFlags = array();
|
|
||||||
$this->searchable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function loadView() {
|
|
||||||
parent::loadView();
|
|
||||||
|
|
||||||
$request = new \Api\Language\Get($this->getDocument()->getUser());
|
|
||||||
if ($request->execute()) {
|
|
||||||
|
|
||||||
$requestUri = $_SERVER["REQUEST_URI"];
|
|
||||||
$queryString = $_SERVER['QUERY_STRING'];
|
|
||||||
|
|
||||||
$params = explode("&", $queryString);
|
|
||||||
$query = array();
|
|
||||||
foreach ($params as $param) {
|
|
||||||
$aParam = explode("=", $param);
|
|
||||||
$key = $aParam[0];
|
|
||||||
|
|
||||||
if ($key === "site" &&
|
|
||||||
(!startsWith($_SERVER["REQUEST_URI"], "/index.php") || $_SERVER["REQUEST_URI"] === "/")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$val = (isset($aParam[1]) ? $aParam[1] : "");
|
|
||||||
if (!empty($key)) {
|
|
||||||
$query[$key] = $val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$url = parse_url($requestUri, PHP_URL_PATH) . "?";
|
|
||||||
|
|
||||||
foreach ($request->getResult()['languages'] as $lang) {
|
|
||||||
$langCode = $lang['code'];
|
|
||||||
$langName = $lang['name'];
|
|
||||||
$query['lang'] = $langCode;
|
|
||||||
$queryString = http_build_query($query);
|
|
||||||
|
|
||||||
$this->languageFlags[] = $this->createLink(
|
|
||||||
"$url$queryString",
|
|
||||||
"<img class=\"p-1 clickable\" src=\"/img/icons/lang/$langCode.gif\" alt=\"$langName\" title=\"$langName\">"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCode(): string {
|
|
||||||
return implode('', $this->languageFlags);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Views;
|
|
||||||
|
|
||||||
use Elements\View;
|
|
||||||
|
|
||||||
class View404 extends View {
|
|
||||||
|
|
||||||
public function getCode(): string {
|
|
||||||
return parent::getCode() . "<b>Not found</b>";
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user