diff --git a/src/FormData/UserEditFormData.php b/src/FormData/UserEditFormData.php index f5b4cc4c..ef78d6b5 100644 --- a/src/FormData/UserEditFormData.php +++ b/src/FormData/UserEditFormData.php @@ -5,9 +5,10 @@ class UserEditFormData implements \Szurubooru\IValidatable { public $userName; public $email; - public $accessRank; public $password; + public $accessRank; public $avatarStyle; + public $avatarContent; public $browsingSettings; public function __construct($inputReader = null) diff --git a/src/Services/UserService.php b/src/Services/UserService.php index a9dc9e0e..1cf80d97 100644 --- a/src/Services/UserService.php +++ b/src/Services/UserService.php @@ -78,25 +78,17 @@ class UserService { $formData->validate($this->validator); - if ($formData->email and $this->userDao->getByEmail($formData->email)) - throw new \DomainException('User with this e-mail already exists.'); - - if ($this->userDao->getByName($formData->userName)) - throw new \DomainException('User with this name already exists.'); - $user = new \Szurubooru\Entities\User(); - $user->name = $formData->userName; - $user->emailUnconfirmed = $formData->email; - $user->passwordHash = $this->passwordService->getHash($formData->password); + $user->registrationTime = $this->timeService->getCurrentTime(); + $user->lastLoginTime = null; $user->accessRank = $this->userDao->hasAnyUsers() ? \Szurubooru\Entities\User::ACCESS_RANK_REGULAR_USER : \Szurubooru\Entities\User::ACCESS_RANK_ADMINISTRATOR; - $user->registrationTime = $this->timeService->getCurrentTime(); - $user->lastLoginTime = null; - $user->avatarStyle = \Szurubooru\Entities\User::AVATAR_STYLE_GRAVATAR; - - $user = $this->sendActivationEmailIfNeeded($user); + $this->updateUserName($user, $formData->userName); + $this->updateUserPassword($user, $formData->password); + $this->updateUserAvatarStyle($user, \Szurubooru\Entities\User::AVATAR_STYLE_GRAVATAR); + $this->updateUserEmail($user, $formData->email); return $this->userDao->save($user); } @@ -106,51 +98,79 @@ class UserService if ($formData->avatarStyle !== null) { - $user->avatarStyle = \Szurubooru\Helpers\EnumHelper::avatarStyleFromString($formData->avatarStyle); - if ($formData->avatarContent) - { - $target = $this->getCustomAvatarSourcePath($user); - $this->fileService->saveFromBase64($formData->avatarContent, $target); - $this->thumbnailService->deleteUsedThumbnails($target); - } + $this->updateUserAvatarStyle( + $user, + \Szurubooru\Helpers\EnumHelper::avatarStyleFromString($formData->avatarStyle)); } - if ($formData->userName !== null and $formData->userName !== $user->name) - { - $userWithThisEmail = $this->userDao->getByName($formData->userName); - if ($userWithThisEmail and $userWithThisEmail->id !== $user->id) - throw new \DomainException('User with this name already exists.'); + if ($formData->avatarContent !== null) + $this->updateUserAvatarContent($user, $formData->avatarContent); - $user->name = $formData->userName; - } + if ($formData->userName !== null) + $this->updateUserName($user, $formData->userName); if ($formData->password !== null) - { - $user->passwordHash = $this->passwordService->getHash($formData->password); - } + $this->updateUserPassword($user, $formData->password); - if ($formData->email !== null and $formData->email !== $user->email) - { - if ($this->userDao->getByEmail($formData->email)) - throw new \DomainException('User with this e-mail already exists.'); - - $user->emailUnconfirmed = $formData->email; - $user = $this->sendActivationEmailIfNeeded($user); - } + if ($formData->email !== null) + $this->updateUserEmail($user, $formData->email); if ($formData->accessRank !== null) - { - $user->accessRank = \Szurubooru\Helpers\EnumHelper::accessRankFromString($formData->accessRank); - } + $this->updateUserAccessRank($user, \Szurubooru\Helpers\EnumHelper::accessRankFromString($formData->accessRank)); if ($formData->browsingSettings !== null) - { - $user->browsingSettings = $formData->browsingSettings; - } + $this->updateUserBrowsingSettings($user, $formData->browsingSettings); return $this->userDao->save($user); } + public function updateUserAvatarStyle(\Szurubooru\Entities\User $user, $newAvatarStyle) + { + $user->avatarStyle = $newAvatarStyle; + } + + public function updateUserAvatarContent(\Szurubooru\Entities\User $user, $newAvatarContentInBase64) + { + $target = $this->getCustomAvatarSourcePath($user); + $this->fileService->saveFromBase64($newAvatarContentInBase64, $target); + $this->thumbnailService->deleteUsedThumbnails($target); + } + + public function updateUserName(\Szurubooru\Entities\User $user, $newName) + { + $this->assertNoUserWithThisName($user, $newName); + $user->name = $newName; + } + + public function updateUserPassword(\Szurubooru\Entities\User $user, $newPassword) + { + $user->passwordHash = $this->passwordService->getHash($newPassword); + } + + public function updateUserEmail(\Szurubooru\Entities\User $user, $newEmail) + { + if ($user->email === $newEmail) + { + $user->emailUnconfirmed = null; + } + else + { + $this->assertNoUserWithThisEmail($user, $newEmail); + $user->emailUnconfirmed = $newEmail; + $user = $this->sendActivationEmailIfNeeded($user); + } + } + + public function updateUserAccessRank(\Szurubooru\Entities\User $user, $newAccessRank) + { + $user->accessRank = $newAccessRank; + } + + public function updateUserBrowsingSettings(\Szurubooru\Entities\User $user, $newBrowsingSettings) + { + $user->browsingSettings = $newBrowsingSettings; + } + public function updateUserLastLoginTime(\Szurubooru\Entities\User $user) { $user->lastLoginTime = $this->timeService->getCurrentTime(); @@ -235,11 +255,27 @@ class UserService //5. two users share the same mail --> problem. //by checking here again for users with such mail, this problem is solved with first-come first-serve approach: //whoever confirms e-mail first, wins. - if ($this->userDao->getByEmail($user->emailUnconfirmed)) - throw new \DomainException('This e-mail was already confirmed by someone else in the meantime.'); + $this->assertNoUserWithThisEmail($user, $user->emailUnconfirmed); - $user->email = $user->emailUnconfirmed; - $user->emailUnconfirmed = null; + if ($user->emailUnconfirmed) + { + $user->email = $user->emailUnconfirmed; + $user->emailUnconfirmed = null; + } return $user; } + + private function assertNoUserWithThisName(\Szurubooru\Entities\User $owner, $nameToCheck) + { + $userWithThisName = $this->userDao->getByName($nameToCheck); + if ($userWithThisName and $userWithThisName->id !== $owner->id) + throw new \DomainException('User with this name already exists.'); + } + + private function assertNoUserWithThisEmail(\Szurubooru\Entities\User $owner, $emailToCheck) + { + $userWithThisEmail = $this->userDao->getByEmail($emailToCheck); + if ($userWithThisEmail and $userWithThisEmail->id !== $owner->id) + throw new \DomainException('User with this e-mail already exists.'); + } } diff --git a/tests/Services/UserServiceTest.php b/tests/Services/UserServiceTest.php index 978a0ebf..ae9ffd5b 100644 --- a/tests/Services/UserServiceTest.php +++ b/tests/Services/UserServiceTest.php @@ -31,7 +31,7 @@ final class UserServiceTest extends \Szurubooru\Tests\AbstractTestCase public function testGettingByName() { - $testUser = new \Szurubooru\Entities\User(); + $testUser = new \Szurubooru\Entities\User; $testUser->name = 'godzilla'; $this->userDaoMock->expects($this->once())->method('getByName')->willReturn($testUser); $userService = $this->getUserService(); @@ -49,7 +49,7 @@ final class UserServiceTest extends \Szurubooru\Tests\AbstractTestCase public function testGettingById() { - $testUser = new \Szurubooru\Entities\User(); + $testUser = new \Szurubooru\Entities\User; $testUser->name = 'godzilla'; $this->userDaoMock->expects($this->once())->method('getById')->willReturn($testUser); $userService = $this->getUserService(); @@ -67,7 +67,7 @@ final class UserServiceTest extends \Szurubooru\Tests\AbstractTestCase public function testGettingFilteredUsers() { - $mockUser = new \Szurubooru\Entities\User(); + $mockUser = new \Szurubooru\Entities\User; $mockUser->name = 'user'; $expected = [$mockUser]; $this->userSearchService->method('getFiltered')->willReturn($expected); @@ -121,7 +121,7 @@ final class UserServiceTest extends \Szurubooru\Tests\AbstractTestCase $this->userDaoMock->method('hasAnyUsers')->willReturn(true); $this->userDaoMock->method('save')->will($this->returnArgument(0)); - $testToken = new \Szurubooru\Entities\Token(); + $testToken = new \Szurubooru\Entities\Token; $this->tokenServiceMock->expects($this->once())->method('createAndSaveToken')->willReturn($testToken); $this->emailServiceMock->expects($this->once())->method('sendActivationEmail')->with( $this->anything(), @@ -162,8 +162,11 @@ final class UserServiceTest extends \Szurubooru\Tests\AbstractTestCase $formData->password = 'password'; $formData->email = 'email'; + $otherUser = new \Szurubooru\Entities\User; + $otherUser->id = 'yes, i exist in database'; + $this->userDaoMock->method('hasAnyUsers')->willReturn(true); - $this->userDaoMock->method('getByName')->willReturn(new \Szurubooru\Entities\User()); + $this->userDaoMock->method('getByName')->willReturn($otherUser); $this->userDaoMock->method('save')->will($this->returnArgument(0)); $userService = $this->getUserService(); @@ -172,6 +175,113 @@ final class UserServiceTest extends \Szurubooru\Tests\AbstractTestCase $savedUser = $userService->createUser($formData); } + public function testUpdatingName() + { + $testUser = new \Szurubooru\Entities\User; + $testUser->name = 'wojtek'; + + $formData = new \Szurubooru\FormData\UserEditFormData; + $formData->userName = 'sebastian'; + + $this->userDaoMock->method('save')->will($this->returnArgument(0)); + + $userService = $this->getUserService(); + $savedUser = $userService->updateUser($testUser, $formData); + $this->assertEquals('sebastian', $savedUser->name); + } + + public function testUpdatingNameToExisting() + { + $testUser = new \Szurubooru\Entities\User; + $testUser->name = 'wojtek'; + + $formData = new \Szurubooru\FormData\UserEditFormData; + $formData->userName = 'sebastian'; + + $otherUser = new \Szurubooru\Entities\User; + $otherUser->id = 'yes, i exist in database'; + $this->userDaoMock->method('getByName')->willReturn($otherUser); + $this->userDaoMock->method('save')->will($this->returnArgument(0)); + + $this->setExpectedException(\Exception::class, 'User with this name already exists'); + $userService = $this->getUserService(); + $savedUser = $userService->updateUser($testUser, $formData); + } + + public function testUpdatingEmailWithoutConfirmation() + { + $testUser = new \Szurubooru\Entities\User; + $this->configMock->set('security/needEmailActivationToRegister', false); + + $formData = new \Szurubooru\FormData\UserEditFormData; + $formData->email = 'hikari@geofront.gov'; + + $this->userDaoMock->method('save')->will($this->returnArgument(0)); + + $userService = $this->getUserService(); + $savedUser = $userService->updateUser($testUser, $formData); + $this->assertEquals('hikari@geofront.gov', $savedUser->email); + $this->assertNull($savedUser->emailUnconfirmed); + } + + public function testUpdatingEmailWithConfirmation() + { + $testUser = new \Szurubooru\Entities\User; + $this->configMock->set('security/needEmailActivationToRegister', true); + + $formData = new \Szurubooru\FormData\UserEditFormData; + $formData->email = 'hikari@geofront.gov'; + + $this->tokenServiceMock->method('createAndSaveToken')->willReturn(new \Szurubooru\Entities\Token()); + $this->userDaoMock->method('save')->will($this->returnArgument(0)); + + $userService = $this->getUserService(); + $savedUser = $userService->updateUser($testUser, $formData); + $this->assertNull($savedUser->email); + $this->assertEquals('hikari@geofront.gov', $savedUser->emailUnconfirmed); + } + + public function testUpdatingEmailWithConfirmationToExisting() + { + $testUser = new \Szurubooru\Entities\User; + $this->configMock->set('security/needEmailActivationToRegister', true); + + $formData = new \Szurubooru\FormData\UserEditFormData; + $formData->email = 'hikari@geofront.gov'; + + $otherUser = new \Szurubooru\Entities\User; + $otherUser->id = 'yes, i exist in database'; + $this->tokenServiceMock->method('createAndSaveToken')->willReturn(new \Szurubooru\Entities\Token()); + $this->userDaoMock->method('getByEmail')->willReturn($otherUser); + $this->userDaoMock->method('save')->will($this->returnArgument(0)); + + $this->setExpectedException(\Exception::class, 'User with this e-mail already exists'); + $userService = $this->getUserService(); + $userService->updateUser($testUser, $formData); + } + + public function testUpdatingEmailToAlreadyConfirmed() + { + $testUser = new \Szurubooru\Entities\User; + $testUser->email = 'hikari@geofront.gov'; + $testUser->emailUnconfirmed = 'coolcat32@sakura.ne.jp'; + $testUser->id = 5; + + $formData = new \Szurubooru\FormData\UserEditFormData; + $formData->email = 'hikari@geofront.gov'; + + $otherUser = new \Szurubooru\Entities\User; + $otherUser->id = 5; + $this->tokenServiceMock->method('createAndSaveToken')->willReturn(new \Szurubooru\Entities\Token()); + $this->userDaoMock->method('getByEmail')->willReturn($otherUser); + $this->userDaoMock->method('save')->will($this->returnArgument(0)); + + $userService = $this->getUserService(); + $savedUser = $userService->updateUser($testUser, $formData); + $this->assertEquals('hikari@geofront.gov', $savedUser->email); + $this->assertNull($savedUser->emailUnconfirmed); + } + private function getUserService() { return new \Szurubooru\Services\UserService(