Implement option to replace existing alias when renaming an account; use database transactions when editing accounts
This commit is contained in:
parent
6ace072841
commit
36b8cfe8b1
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MailAccountAdmin\Exceptions;
|
||||
|
||||
class AliasNotFoundException extends AppException
|
||||
{
|
||||
}
|
||||
|
|
@ -73,26 +73,47 @@ class AccountHandler
|
|||
throw new InputValidationError('Account with ID ' . $accountId . ' does not exist!');
|
||||
}
|
||||
|
||||
// TODO: Use database transactions (beginTransaction/commit/rollBack in BaseRepository maybe?)
|
||||
|
||||
$newUsername = $editData->getUsername();
|
||||
if ($newUsername === $account->getUsername()) {
|
||||
// Username is unchanged
|
||||
$newUsername = null;
|
||||
}
|
||||
|
||||
// TODO: This feature is not supported yet. If the alias exists, the availability check (next) would fail anyway.
|
||||
if ($editData->getUsernameReplaceAlias()) {
|
||||
throw new InputValidationError('Replace alias: Not implemented yet.');
|
||||
}
|
||||
// This variable will be set to true if the user wants to change their username, has an existing alias with this username,
|
||||
// and checked the "replace existing alias" option.
|
||||
$aliasNeedsToBeReplaced = false;
|
||||
|
||||
// Check if new username is still available
|
||||
if ($newUsername !== null) {
|
||||
if (!$this->accountRepository->checkUsernameAvailable($newUsername) || !$this->aliasRepository->checkAliasAvailable($newUsername)) {
|
||||
$newUsernameAvailable = true;
|
||||
|
||||
// Check if account with this username already exists
|
||||
if (!$this->accountRepository->checkUsernameAvailable($newUsername)) {
|
||||
$newUsernameAvailable = false;
|
||||
}
|
||||
|
||||
// Check if alias with this username/address already exists
|
||||
if (!$this->aliasRepository->checkAliasAvailable($newUsername)) {
|
||||
$newUsernameAvailable = false;
|
||||
|
||||
// Alias already exists. If user wants to replace an existing alias, check if the alias belongs to this user.
|
||||
if ($editData->getUsernameReplaceAlias()) {
|
||||
$existingAlias = $this->aliasRepository->fetchAliasByAddress($newUsername);
|
||||
if ($existingAlias->getUserId() === $accountId) {
|
||||
$aliasNeedsToBeReplaced = true;
|
||||
$newUsernameAvailable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$newUsernameAvailable) {
|
||||
throw new InputValidationError("Username \"$newUsername\" is not available.");
|
||||
}
|
||||
}
|
||||
|
||||
// Start database transaction
|
||||
$this->accountRepository->beginTransaction();
|
||||
|
||||
// Create alias for old username (if wanted)
|
||||
if ($editData->getUsernameCreateAlias()) {
|
||||
$oldUsername = $account->getUsername();
|
||||
|
|
@ -119,9 +140,11 @@ class AccountHandler
|
|||
);
|
||||
|
||||
// Remove existing alias for new username (if wanted)
|
||||
// TODO: See above. This is the point where the alias should be deleted.
|
||||
// if ($editData->getUsernameReplaceAlias()) {
|
||||
// throw new InputValidationError('Replace alias: Not implemented yet.');
|
||||
// }
|
||||
if ($editData->getUsernameReplaceAlias() && $aliasNeedsToBeReplaced) {
|
||||
$this->aliasRepository->removeAlias($accountId, $newUsername);
|
||||
}
|
||||
|
||||
// Commit database transaction
|
||||
$this->accountRepository->commitTransaction();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MailAccountAdmin\Models;
|
||||
|
||||
use DateTimeImmutable;
|
||||
|
||||
class Alias
|
||||
{
|
||||
/** @var int */
|
||||
private $id;
|
||||
/** @var int */
|
||||
private $userId;
|
||||
/** @var string */
|
||||
private $mailAddress;
|
||||
/** @var DateTimeImmutable */
|
||||
private $createdAt;
|
||||
/** @var DateTimeImmutable */
|
||||
private $modifiedAt;
|
||||
|
||||
private function __construct(int $id, int $userId, string $mailAddress, DateTimeImmutable $createdAt, DateTimeImmutable $modifiedAt)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->userId = $userId;
|
||||
$this->mailAddress = $mailAddress;
|
||||
$this->createdAt = $createdAt;
|
||||
$this->modifiedAt = $modifiedAt;
|
||||
}
|
||||
|
||||
public static function createFromArray(array $data): self
|
||||
{
|
||||
return new self(
|
||||
(int)$data['alias_id'],
|
||||
(int)$data['user_id'],
|
||||
$data['mail_address'],
|
||||
new DateTimeImmutable($data['created_at']),
|
||||
new DateTimeImmutable($data['modified_at']),
|
||||
);
|
||||
}
|
||||
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUserId(): int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
public function getMailAddress(): string
|
||||
{
|
||||
return $this->mailAddress;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function getModifiedAt(): DateTimeImmutable
|
||||
{
|
||||
return $this->modifiedAt;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace MailAccountAdmin\Repositories;
|
||||
|
||||
use MailAccountAdmin\Exceptions\AliasNotFoundException;
|
||||
use MailAccountAdmin\Models\Alias;
|
||||
use PDO;
|
||||
|
||||
class AliasRepository extends BaseRepository
|
||||
|
|
@ -11,7 +13,27 @@ class AliasRepository extends BaseRepository
|
|||
{
|
||||
$statement = $this->pdo->prepare('SELECT * FROM mail_aliases WHERE user_id = :user_id ORDER BY mail_address');
|
||||
$statement->execute(['user_id' => $userId]);
|
||||
return $statement->fetchAll(PDO::FETCH_ASSOC);
|
||||
$rows = $statement->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Create Account models from rows
|
||||
$aliasList = [];
|
||||
foreach ($rows as $row) {
|
||||
$aliasList[] = Alias::createFromArray($row);
|
||||
}
|
||||
return $aliasList;
|
||||
}
|
||||
|
||||
public function fetchAliasByAddress(string $mailAddress): Alias
|
||||
{
|
||||
$statement = $this->pdo->prepare('SELECT * FROM mail_aliases WHERE mail_address = :mail_address LIMIT 1');
|
||||
$statement->execute(['mail_address' => $mailAddress]);
|
||||
|
||||
if ($statement->rowCount() < 1) {
|
||||
throw new AliasNotFoundException("Alias '$mailAddress' was not found.");
|
||||
}
|
||||
|
||||
$row = $statement->fetch(PDO::FETCH_ASSOC);
|
||||
return Alias::createFromArray($row);
|
||||
}
|
||||
|
||||
public function checkAliasAvailable(string $mailAddress): bool
|
||||
|
|
@ -29,4 +51,14 @@ class AliasRepository extends BaseRepository
|
|||
'mail_address' => $mailAddress,
|
||||
]);
|
||||
}
|
||||
|
||||
public function removeAlias(int $userId, string $mailAddress): void
|
||||
{
|
||||
// Check user ID in WHERE clause to make sure we don't accidentally delete someone else's alias
|
||||
$statement = $this->pdo->prepare('DELETE FROM mail_aliases WHERE user_id = :user_id AND mail_address = :mail_address LIMIT 1');
|
||||
$statement->execute([
|
||||
'user_id' => $userId,
|
||||
'mail_address' => $mailAddress,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,4 +14,24 @@ class BaseRepository
|
|||
{
|
||||
$this->pdo = $pdo;
|
||||
}
|
||||
|
||||
public function beginTransaction(): void
|
||||
{
|
||||
$this->pdo->beginTransaction();
|
||||
}
|
||||
|
||||
public function commitTransaction(): void
|
||||
{
|
||||
$this->pdo->commit();
|
||||
}
|
||||
|
||||
public function rollBackTransaction(): void
|
||||
{
|
||||
$this->pdo->rollBack();
|
||||
}
|
||||
|
||||
public function inTransaction(): bool
|
||||
{
|
||||
return $this->pdo->inTransaction();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
<p>You are about to delete the mail account "{{ accountUsername }}" including the following aliases:</p>
|
||||
<ul>
|
||||
{% for alias in aliases %}
|
||||
<li>{{ alias['mail_address'] }}</li>
|
||||
<li>{{ alias.getMailAddress() }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -73,8 +73,8 @@
|
|||
</tr>
|
||||
{% for alias in aliases %}
|
||||
<tr>
|
||||
<td>{{ alias['mail_address'] }}</td>
|
||||
<td>{{ alias['created_at'] }}</td>
|
||||
<td>{{ alias.getMailAddress() }}</td>
|
||||
<td>{{ alias.getCreatedAt() | date }}</td>
|
||||
<td><a href="#">Delete</a></td> {# TODO #}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
|||
Loading…
Reference in New Issue