Added user name history beginnings.
This commit is contained in:
parent
7f7e644069
commit
cc37b7cad3
6 changed files with 207 additions and 1 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
20250208
|
20250209
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
use Index\Db\DbConnection;
|
||||||
|
use Index\Db\Migration\DbMigration;
|
||||||
|
|
||||||
|
final class CreatedUserNameHistoryTable_20250209_005714 implements DbMigration {
|
||||||
|
public function migrate(DbConnection $conn): void {
|
||||||
|
$conn->execute(<<<SQL
|
||||||
|
CREATE TABLE msz_users_names_history (
|
||||||
|
history_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
user_id INT(10) UNSIGNED NULL DEFAULT NULL,
|
||||||
|
history_name_before VARCHAR(255) NOT NULL COLLATE 'utf8mb4_unicode_520_ci',
|
||||||
|
history_name_after VARCHAR(255) NOT NULL COLLATE 'utf8mb4_unicode_520_ci',
|
||||||
|
history_private TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
history_created TIMESTAMP NOT NULL DEFAULT current_timestamp(),
|
||||||
|
PRIMARY KEY (history_id),
|
||||||
|
INDEX users_names_history_users_foreign (user_id),
|
||||||
|
INDEX users_names_history_name_before_index (history_name_before),
|
||||||
|
INDEX users_names_history_created_index (history_created),
|
||||||
|
INDEX users_names_history_private_index (history_private),
|
||||||
|
CONSTRAINT users_names_history_users_foreign
|
||||||
|
FOREIGN KEY (user_id)
|
||||||
|
REFERENCES msz_users (user_id)
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
ON DELETE SET NULL
|
||||||
|
) COLLATE='utf8mb4_bin';
|
||||||
|
SQL);
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,12 @@ $viewerId = $viewingAsGuest ? '0' : $viewerInfo->id;
|
||||||
try {
|
try {
|
||||||
$userInfo = $msz->usersCtx->getUserInfo($userId, 'profile');
|
$userInfo = $msz->usersCtx->getUserInfo($userId, 'profile');
|
||||||
} catch(RuntimeException $ex) {
|
} catch(RuntimeException $ex) {
|
||||||
|
$userId = $msz->usersCtx->namesHistory->resolvePastUserName($userId);
|
||||||
|
if($userId !== null) {
|
||||||
|
header(sprintf('Location: %s', $msz->urls->format('user-profile', ['user' => $userId])));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
http_response_code(404);
|
http_response_code(404);
|
||||||
Template::render('profile.index', [
|
Template::render('profile.index', [
|
||||||
'profile_is_guest' => $viewingAsGuest,
|
'profile_is_guest' => $viewingAsGuest,
|
||||||
|
|
31
src/Users/UserNameHistoryInfo.php
Normal file
31
src/Users/UserNameHistoryInfo.php
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
namespace Misuzu\Users;
|
||||||
|
|
||||||
|
use Carbon\CarbonImmutable;
|
||||||
|
use Index\Db\DbResult;
|
||||||
|
|
||||||
|
class UserNameHistoryInfo {
|
||||||
|
public function __construct(
|
||||||
|
public private(set) string $id,
|
||||||
|
public private(set) string $userId,
|
||||||
|
public private(set) string $nameBefore,
|
||||||
|
public private(set) string $nameAfter,
|
||||||
|
public private(set) bool $private,
|
||||||
|
public private(set) int $createdTime
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public static function fromResult(DbResult $result): UserPasswordInfo {
|
||||||
|
return new UserPasswordInfo(
|
||||||
|
id: $result->getString(0),
|
||||||
|
userId: $result->getString(1),
|
||||||
|
nameBefore: $result->getString(2),
|
||||||
|
nameAfter: $result->getString(3),
|
||||||
|
private: $result->getBoolean(4),
|
||||||
|
createdTime: $result->getInteger(5),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CarbonImmutable $createdAt {
|
||||||
|
get => CarbonImmutable::createFromTimestampUTC($this->createdTime);
|
||||||
|
}
|
||||||
|
}
|
139
src/Users/UserNamesHistoryData.php
Normal file
139
src/Users/UserNamesHistoryData.php
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
<?php
|
||||||
|
namespace Misuzu\Users;
|
||||||
|
|
||||||
|
use RuntimeException;
|
||||||
|
use Index\Db\{DbConnection,DbStatementCache};
|
||||||
|
|
||||||
|
class UserNamesHistoryData {
|
||||||
|
private DbStatementCache $cache;
|
||||||
|
|
||||||
|
public function __construct(DbConnection $dbConn) {
|
||||||
|
$this->cache = new DbStatementCache($dbConn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return iterable<UserNameHistoryInfo> */
|
||||||
|
public function getUserNamesHistory(
|
||||||
|
UserInfo|string|null $userInfo = null,
|
||||||
|
?bool $private = null
|
||||||
|
): iterable {
|
||||||
|
$hasUserInfo = $userInfo !== null;
|
||||||
|
|
||||||
|
$args = 0;
|
||||||
|
$query = <<<SQL
|
||||||
|
SELECT history_id, user_id, history_name_before, history_name_after, history_private, UNIX_TIMESTAMP(history_created)
|
||||||
|
FROM msz_users_names_history
|
||||||
|
SQL;
|
||||||
|
if($hasUserInfo) {
|
||||||
|
++$args;
|
||||||
|
$query .= ' WHERE user_id = ?';
|
||||||
|
}
|
||||||
|
if($private !== null)
|
||||||
|
$query .= sprintf(' %s history_private %s 0', ++$args > 1 ? 'AND' : 'WHERE', $private ? '<>' : '=');
|
||||||
|
|
||||||
|
$stmt = $this->cache->get($query);
|
||||||
|
if($hasUserInfo)
|
||||||
|
$stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo);
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
return $stmt->getResultIterator(UserNameHistoryInfo::fromResult(...));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUserNameHistory(string $historyId): UserNameHistoryInfo {
|
||||||
|
$stmt = $this->cache->get(<<<SQL
|
||||||
|
SELECT history_id, user_id, history_name_before, history_name_after, history_private, UNIX_TIMESTAMP(history_created)
|
||||||
|
FROM msz_users_names_history
|
||||||
|
WHERE history_id = ?
|
||||||
|
SQL);
|
||||||
|
$stmt->nextParameter($historyId);
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
$result = $stmt->getResult();
|
||||||
|
if(!$result->next())
|
||||||
|
throw new RuntimeException('could not find that name history entry');
|
||||||
|
|
||||||
|
return UserNameHistoryInfo::fromResult($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteUserNameHistory(UserNameHistoryInfo|string $historyInfo): void {
|
||||||
|
$stmt = $this->cache->get(<<<SQL
|
||||||
|
DELETE FROM msz_users_names_history
|
||||||
|
WHERE history_id = ?
|
||||||
|
SQL);
|
||||||
|
$stmt->nextParameter($historyInfo instanceof UserNameHistoryInfo ? $historyInfo->id : $historyInfo);
|
||||||
|
$stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteUserNamesHistory(UserInfo|string $userInfo): void {
|
||||||
|
$stmt = $this->cache->get(<<<SQL
|
||||||
|
DELETE FROM msz_users_names_history
|
||||||
|
WHERE user_id = ?
|
||||||
|
SQL);
|
||||||
|
$stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo);
|
||||||
|
$stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUserNameHistoryVisibility(
|
||||||
|
UserNameHistoryInfo|string $historyInfo,
|
||||||
|
bool $private
|
||||||
|
): void {
|
||||||
|
$stmt = $this->cache->get(<<<SQL
|
||||||
|
UPDATE msz_users_names_history
|
||||||
|
SET history_private = ?
|
||||||
|
WHERE history_id = ?
|
||||||
|
SQL);
|
||||||
|
$stmt->nextParameter($private ? 1 : 0);
|
||||||
|
$stmt->nextParameter($historyInfo instanceof UserNameHistoryInfo ? $historyInfo->id : $historyInfo);
|
||||||
|
$stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
// this should probably let you specify a time period
|
||||||
|
public function setUserNamesHistoryVisibility(
|
||||||
|
UserInfo|string $userInfo,
|
||||||
|
bool $private
|
||||||
|
): void {
|
||||||
|
$stmt = $this->cache->get(<<<SQL
|
||||||
|
UPDATE msz_users_names_history
|
||||||
|
SET history_private = ?
|
||||||
|
WHERE user_id = ?
|
||||||
|
SQL);
|
||||||
|
$stmt->nextParameter($private ? 1 : 0);
|
||||||
|
$stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo);
|
||||||
|
$stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function recordUserNameChange(
|
||||||
|
UserInfo|string $userInfo,
|
||||||
|
string $nameBefore,
|
||||||
|
string $nameAfter,
|
||||||
|
bool $private = false
|
||||||
|
): UserNameHistoryInfo {
|
||||||
|
$stmt = $this->cache->get(<<<SQL
|
||||||
|
INSERT INTO msz_users_names_history (
|
||||||
|
user_id, history_name_before, history_name_after, history_private
|
||||||
|
) VALUES (?, ?, ?, ?)
|
||||||
|
SQL);
|
||||||
|
$stmt->nextParameter($userInfo instanceof UserInfo ? $userInfo->id : $userInfo);
|
||||||
|
$stmt->nextParameter($nameBefore);
|
||||||
|
$stmt->nextParameter($nameAfter);
|
||||||
|
$stmt->nextParameter($private ? 1 : 0);
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
return $this->getUserNameHistory($stmt->lastInsertId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resolvePastUserName(string $pastName): ?string {
|
||||||
|
$stmt = $this->cache->get(<<<SQL
|
||||||
|
SELECT IF(history_private, NULL, user_id)
|
||||||
|
FROM msz_users_names_history
|
||||||
|
WHERE user_id IS NOT NULL
|
||||||
|
AND history_name_before = ?
|
||||||
|
ORDER BY history_created DESC
|
||||||
|
LIMIT 1
|
||||||
|
SQL);
|
||||||
|
$stmt->nextParameter($pastName);
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
$result = $stmt->getResult();
|
||||||
|
return $result->next() ? $result->getStringOrNull(0) : null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ class UsersContext {
|
||||||
public private(set) UserPasswordsData $passwords;
|
public private(set) UserPasswordsData $passwords;
|
||||||
public private(set) UserTotpsData $totps;
|
public private(set) UserTotpsData $totps;
|
||||||
public private(set) UserBirthdatesData $birthdates;
|
public private(set) UserBirthdatesData $birthdates;
|
||||||
|
public private(set) UserNamesHistoryData $namesHistory;
|
||||||
|
|
||||||
/** @var array<string, UserInfo> */
|
/** @var array<string, UserInfo> */
|
||||||
private array $userInfos = [];
|
private array $userInfos = [];
|
||||||
|
@ -35,6 +36,7 @@ class UsersContext {
|
||||||
$this->passwords = new UserPasswordsData($dbConn);
|
$this->passwords = new UserPasswordsData($dbConn);
|
||||||
$this->totps = new UserTotpsData($dbConn);
|
$this->totps = new UserTotpsData($dbConn);
|
||||||
$this->birthdates = new UserBirthdatesData($dbConn);
|
$this->birthdates = new UserBirthdatesData($dbConn);
|
||||||
|
$this->namesHistory = new UserNamesHistoryData($dbConn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUserInfo(string $value, int|string|null $select = null): UserInfo {
|
public function getUserInfo(string $value, int|string|null $select = null): UserInfo {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue