Implement Account model; small style changes; Twig debug setting
This commit is contained in:
parent
7859ef77ee
commit
32add30c9d
10 changed files with 263 additions and 56 deletions
|
|
@ -16,6 +16,7 @@ use MailAccountAdmin\Repositories\DomainRepository;
|
|||
use PDO;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Slim\Views\Twig;
|
||||
use Twig\Extension\CoreExtension as TwigCoreExtension;
|
||||
|
||||
class Dependencies
|
||||
{
|
||||
|
|
@ -36,7 +37,17 @@ class Dependencies
|
|||
/** @var Settings $settings */
|
||||
$settings = $c->get(self::SETTINGS);
|
||||
|
||||
return Twig::create(self::TWIG_TEMPLATE_DIR, $settings->getTwigSettings());
|
||||
// Create Twig view
|
||||
$twig = Twig::create(self::TWIG_TEMPLATE_DIR, $settings->getTwigSettings());
|
||||
|
||||
// Set default date format
|
||||
/** @var TwigCoreExtension $coreExtension */
|
||||
$coreExtension = $twig->getEnvironment()->getExtension(TwigCoreExtension::class);
|
||||
$coreExtension->setDateFormat($settings->getDateFormat());
|
||||
$coreExtension->setTimezone($settings->getTimezone());
|
||||
|
||||
// Return Twig view
|
||||
return $twig;
|
||||
});
|
||||
|
||||
// Database connection
|
||||
|
|
|
|||
8
src/Exceptions/InformationUnknownException.php
Normal file
8
src/Exceptions/InformationUnknownException.php
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MailAccountAdmin\Exceptions;
|
||||
|
||||
class InformationUnknownException extends AppException
|
||||
{
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ namespace MailAccountAdmin\Frontend\Accounts;
|
|||
|
||||
use MailAccountAdmin\Common\UserHelper;
|
||||
use MailAccountAdmin\Frontend\BaseController;
|
||||
use MailAccountAdmin\Models\Account;
|
||||
use MailAccountAdmin\Repositories\AccountRepository;
|
||||
use MailAccountAdmin\Repositories\AliasRepository;
|
||||
use Psr\Http\Message\ResponseInterface as Response;
|
||||
|
|
@ -25,6 +26,9 @@ class AccountController extends BaseController
|
|||
$this->aliasRepository = $aliasRepository;
|
||||
}
|
||||
|
||||
|
||||
// -- /accounts - List all accounts
|
||||
|
||||
public function showAccounts(Request $request, Response $response): Response
|
||||
{
|
||||
// Parse query parameters for filters
|
||||
|
|
@ -39,21 +43,22 @@ class AccountController extends BaseController
|
|||
return $this->view->render($response, 'accounts.html.twig', $renderData);
|
||||
}
|
||||
|
||||
|
||||
// -- /accounts/{id} - Show account details
|
||||
|
||||
public function showAccountDetails(Request $request, Response $response, array $args): Response
|
||||
{
|
||||
// Parse URL arguments
|
||||
$accountId = (int)$args['id'];
|
||||
|
||||
// Get account data from database
|
||||
$accountData = $this->accountRepository->fetchAccountById($accountId);
|
||||
$accountUsername = $accountData['username'];
|
||||
$accountData['domain'] = explode('@', $accountUsername, 2)[1];
|
||||
$account = $this->accountRepository->fetchAccountById($accountId);
|
||||
|
||||
// Don't display the password hash, but at least the type of hash (used hash algorithm)
|
||||
if ($accountData['password'] === '') {
|
||||
if ($account->getPasswordHash() === '') {
|
||||
$passwordHashType = 'empty';
|
||||
} else {
|
||||
$passwordHashInfo = password_get_info($accountData['password']);
|
||||
$passwordHashInfo = password_get_info($account->getPasswordHash());
|
||||
$passwordHashType = $passwordHashInfo['algoName'] ?? 'unknown';
|
||||
}
|
||||
|
||||
|
|
@ -62,8 +67,8 @@ class AccountController extends BaseController
|
|||
|
||||
$renderData = [
|
||||
'id' => $accountId,
|
||||
'accountUsername' => $accountUsername,
|
||||
'accountData' => $accountData,
|
||||
'accountUsername' => $account->getUsername(),
|
||||
'account' => $account,
|
||||
'passwordHashType' => $passwordHashType,
|
||||
'aliases' => $aliases,
|
||||
];
|
||||
|
|
@ -71,26 +76,27 @@ class AccountController extends BaseController
|
|||
return $this->view->render($response, 'account_details.html.twig', $renderData);
|
||||
}
|
||||
|
||||
|
||||
// -- /accounts/new - Create new account
|
||||
|
||||
public function showAccountCreate(Request $request, Response $response): Response
|
||||
{
|
||||
return $this->showAccounts($request, $response);
|
||||
}
|
||||
|
||||
|
||||
// -- /accounts/{id}/edit - Edit account
|
||||
|
||||
public function showAccountEdit(Request $request, Response $response, array $args): Response
|
||||
{
|
||||
// Parse URL arguments
|
||||
$accountId = (int)$args['id'];
|
||||
|
||||
// Get account data from database
|
||||
$accountData = $this->accountRepository->fetchAccountById($accountId);
|
||||
$account = $this->accountRepository->fetchAccountById($accountId);
|
||||
|
||||
$renderData = [
|
||||
'id' => $accountId,
|
||||
'accountUsername' => $accountData['username'],
|
||||
'accountData' => $accountData,
|
||||
];
|
||||
|
||||
return $this->view->render($response, 'account_edit.html.twig', $renderData);
|
||||
// Render page
|
||||
return $this->renderEditPage($response, $account);
|
||||
}
|
||||
|
||||
public function editAccount(Request $request, Response $response, array $args): Response
|
||||
|
|
@ -100,18 +106,31 @@ class AccountController extends BaseController
|
|||
return $this->showAccountEdit($request, $response, $args);
|
||||
}
|
||||
|
||||
private function renderEditPage(Response $response, Account $account, array $extraRenderData = []): Response
|
||||
{
|
||||
$renderData = [
|
||||
'id' => $account->getId(),
|
||||
'accountUsername' => $account->getUsername(),
|
||||
'account' => $account,
|
||||
];
|
||||
|
||||
return $this->view->render($response, 'account_edit.html.twig', array_merge($renderData, $extraRenderData));
|
||||
}
|
||||
|
||||
// -- /accounts/{id}/delete - Delete account
|
||||
|
||||
public function showAccountDelete(Request $request, Response $response, array $args): Response
|
||||
{
|
||||
// Parse URL arguments
|
||||
$accountId = (int)$args['id'];
|
||||
|
||||
// Get account data and list of aliases from database
|
||||
$accountData = $this->accountRepository->fetchAccountById($accountId);
|
||||
$account = $this->accountRepository->fetchAccountById($accountId);
|
||||
$aliases = $this->aliasRepository->fetchAliasesForUserId($accountId);
|
||||
|
||||
$renderData = [
|
||||
'id' => $accountId,
|
||||
'accountUsername' => $accountData['username'],
|
||||
'accountUsername' => $account->getUsername(),
|
||||
'aliases' => $aliases,
|
||||
];
|
||||
|
||||
|
|
|
|||
138
src/Models/Account.php
Normal file
138
src/Models/Account.php
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MailAccountAdmin\Models;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use MailAccountAdmin\Exceptions\InformationUnknownException;
|
||||
|
||||
class Account
|
||||
{
|
||||
/** @var int */
|
||||
private $id;
|
||||
/** @var string */
|
||||
private $username;
|
||||
/** @var string */
|
||||
private $passwordHash;
|
||||
/** @var bool */
|
||||
private $active;
|
||||
/** @var string */
|
||||
private $homeDir;
|
||||
/** @var string */
|
||||
private $memo;
|
||||
/** @var DateTimeImmutable */
|
||||
private $createdAt;
|
||||
/** @var DateTimeImmutable */
|
||||
private $modifiedAt;
|
||||
|
||||
// Extra data that is not part of the data model
|
||||
|
||||
// Domain (extracted from username)
|
||||
/** @var string */
|
||||
private $domain;
|
||||
|
||||
// Number of aliases (not always available)
|
||||
/** @var int|null */
|
||||
private $aliasCount;
|
||||
|
||||
private function __construct(int $id, string $username, string $passwordHash, bool $active, string $homeDir, string $memo,
|
||||
DateTimeImmutable $createdAt, DateTimeImmutable $modifiedAt)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->username = $username;
|
||||
$this->passwordHash = $passwordHash;
|
||||
$this->active = $active;
|
||||
$this->homeDir = $homeDir;
|
||||
$this->memo = $memo;
|
||||
$this->createdAt = $createdAt;
|
||||
$this->modifiedAt = $modifiedAt;
|
||||
|
||||
// Extract domain from username
|
||||
$this->domain = explode('@', $this->username, 2)[1];
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
$account = new self(
|
||||
(int)$data['user_id'],
|
||||
$data['username'],
|
||||
$data['password'],
|
||||
$data['is_active'] === '1',
|
||||
$data['home_dir'],
|
||||
$data['memo'] !== null ? $data['memo'] : '',
|
||||
new DateTimeImmutable($data['created_at']),
|
||||
new DateTimeImmutable($data['modified_at']),
|
||||
);
|
||||
|
||||
if (isset($data['alias_count'])) {
|
||||
$account->setAliasCount((int)$data['alias_count']);
|
||||
}
|
||||
return $account;
|
||||
}
|
||||
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUsername(): string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
public function getPasswordHash(): string
|
||||
{
|
||||
return $this->passwordHash;
|
||||
}
|
||||
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
public function getHomeDir(): string
|
||||
{
|
||||
return $this->homeDir;
|
||||
}
|
||||
|
||||
public function getMemo(): string
|
||||
{
|
||||
return $this->memo;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function getModifiedAt(): DateTimeImmutable
|
||||
{
|
||||
return $this->modifiedAt;
|
||||
}
|
||||
|
||||
|
||||
// Getters and setters for extra data that is not part of the data model
|
||||
|
||||
public function getDomain(): string
|
||||
{
|
||||
return $this->domain;
|
||||
}
|
||||
|
||||
public function hasAliasCount(): bool
|
||||
{
|
||||
return $this->aliasCount !== null;
|
||||
}
|
||||
|
||||
public function getAliasCount(): int
|
||||
{
|
||||
if ($this->aliasCount === null) {
|
||||
throw new InformationUnknownException('Alias count is not set on this object.');
|
||||
}
|
||||
return $this->aliasCount;
|
||||
}
|
||||
|
||||
public function setAliasCount(int $newAliasCount): void
|
||||
{
|
||||
$this->aliasCount = $newAliasCount;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
namespace MailAccountAdmin\Repositories;
|
||||
|
||||
use MailAccountAdmin\Exceptions\AccountNotFoundException;
|
||||
use MailAccountAdmin\Models\Account;
|
||||
use PDO;
|
||||
|
||||
class AccountRepository extends BaseRepository
|
||||
|
|
@ -31,10 +32,17 @@ class AccountRepository extends BaseRepository
|
|||
|
||||
$statement = $this->pdo->prepare($query);
|
||||
$statement->execute($queryParams);
|
||||
return $statement->fetchAll(PDO::FETCH_ASSOC);
|
||||
$rows = $statement->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Create Account models from rows
|
||||
$accountList = [];
|
||||
foreach ($rows as $row) {
|
||||
$accountList[] = Account::createFromArray($row);
|
||||
}
|
||||
return $accountList;
|
||||
}
|
||||
|
||||
public function fetchAccountById(int $accountId): array
|
||||
public function fetchAccountById(int $accountId): Account
|
||||
{
|
||||
$statement = $this->pdo->prepare('SELECT * FROM mail_users WHERE user_id = :user_id LIMIT 1');
|
||||
$statement->execute(['user_id' => $accountId]);
|
||||
|
|
@ -43,6 +51,7 @@ class AccountRepository extends BaseRepository
|
|||
throw new AccountNotFoundException("Account with user ID '$accountId' was not found.");
|
||||
}
|
||||
|
||||
return $statement->fetch(PDO::FETCH_ASSOC);
|
||||
$row = $statement->fetch(PDO::FETCH_ASSOC);
|
||||
return Account::createFromArray($row);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,23 @@ class Settings
|
|||
return getenv('APP_DEBUG') === 'true';
|
||||
}
|
||||
|
||||
public function getTimezone(): string
|
||||
{
|
||||
return 'Europe/Berlin';
|
||||
}
|
||||
|
||||
public function getDateFormat(): string
|
||||
{
|
||||
// Default date format (https://www.php.net/manual/en/datetime.format.php)
|
||||
// RFC 2822 formatted date (Thu, 21 Dec 2000 16:01:07 +0200)
|
||||
return 'r';
|
||||
}
|
||||
|
||||
public function getTwigSettings(): array
|
||||
{
|
||||
return [
|
||||
'cache' => getenv('TWIG_CACHE_DIR') ?: false,
|
||||
'debug' => $this->isDebugMode(),
|
||||
'strict_variables' => getenv('TWIG_STRICT') === 'true',
|
||||
];
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue