Refactored thumbs; fixed setting custom avatars
This commit is contained in:
parent
7e492e044c
commit
109aa1c39e
13 changed files with 159 additions and 22 deletions
1
data/.gitignore
vendored
1
data/.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
local.ini
|
local.ini
|
||||||
|
thumbnails
|
||||||
|
|
|
@ -61,7 +61,7 @@ final class UserAvatarController extends AbstractController
|
||||||
if (!$this->fileService->exists($file))
|
if (!$this->fileService->exists($file))
|
||||||
$file = $this->userService->getBlankAvatarSourcePath();
|
$file = $this->userService->getBlankAvatarSourcePath();
|
||||||
|
|
||||||
$sizedFile = $this->thumbnailService->generateFromFile($file, $size, $size);
|
$sizedFile = $this->thumbnailService->getOrGenerate($file, $size, $size);
|
||||||
$this->fileService->serve($sizedFile);
|
$this->fileService->serve($sizedFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,13 @@ class FileService
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function createFolders($target)
|
||||||
|
{
|
||||||
|
$finalTarget = $this->getFullPath($target);
|
||||||
|
if (!file_exists($finalTarget))
|
||||||
|
mkdir($finalTarget, 0777, true);
|
||||||
|
}
|
||||||
|
|
||||||
public function exists($source)
|
public function exists($source)
|
||||||
{
|
{
|
||||||
$finalSource = $this->getFullPath($source);
|
$finalSource = $this->getFullPath($source);
|
||||||
|
|
|
@ -3,5 +3,5 @@ namespace Szurubooru\Services\ThumbnailGenerators;
|
||||||
|
|
||||||
interface IThumbnailGenerator
|
interface IThumbnailGenerator
|
||||||
{
|
{
|
||||||
public function generateFromFile($srcPath, $dstPath, $width, $height);
|
public function generate($srcPath, $dstPath, $width, $height);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ class ImageGdThumbnailGenerator implements IThumbnailGenerator
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generateFromFile($srcPath, $dstPath, $width, $height)
|
public function generate($srcPath, $dstPath, $width, $height)
|
||||||
{
|
{
|
||||||
if (!file_exists($srcPath))
|
if (!file_exists($srcPath))
|
||||||
throw new \InvalidArgumentException($srcPath . ' does not exist');
|
throw new \InvalidArgumentException($srcPath . ' does not exist');
|
||||||
|
|
|
@ -10,7 +10,7 @@ class ImageImagickThumbnailGenerator implements IThumbnailGenerator
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generateFromFile($srcPath, $dstPath, $width, $height)
|
public function generate($srcPath, $dstPath, $width, $height)
|
||||||
{
|
{
|
||||||
if (!file_exists($srcPath))
|
if (!file_exists($srcPath))
|
||||||
throw new \InvalidArgumentException($srcPath . ' does not exist');
|
throw new \InvalidArgumentException($srcPath . ' does not exist');
|
||||||
|
|
|
@ -14,7 +14,7 @@ class ImageThumbnailGenerator implements IThumbnailGenerator
|
||||||
$this->imageGdThumbnailGenerator = $imageGdThumbnailGenerator;
|
$this->imageGdThumbnailGenerator = $imageGdThumbnailGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generateFromFile($srcPath, $dstPath, $width, $height)
|
public function generate($srcPath, $dstPath, $width, $height)
|
||||||
{
|
{
|
||||||
if (extension_loaded('imagick'))
|
if (extension_loaded('imagick'))
|
||||||
$strategy = $this->imageImagickThumbnailGenerator;
|
$strategy = $this->imageImagickThumbnailGenerator;
|
||||||
|
@ -23,6 +23,6 @@ class ImageThumbnailGenerator implements IThumbnailGenerator
|
||||||
else
|
else
|
||||||
throw new \Exception('Both imagick and gd extensions are disabled');
|
throw new \Exception('Both imagick and gd extensions are disabled');
|
||||||
|
|
||||||
return $strategy->generateFromFile($srcPath, $dstPath, $width, $height);
|
return $strategy->generate($srcPath, $dstPath, $width, $height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,17 +14,58 @@ class ThumbnailService
|
||||||
$this->thumbnailGenerator = $thumbnailGenerator;
|
$this->thumbnailGenerator = $thumbnailGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generateFromFile($source, $width, $height)
|
public function getOrGenerate($source, $width, $height)
|
||||||
{
|
{
|
||||||
$target = $source . '-thumb' . $width . 'x' . $height . '.jpg';
|
$target = $this->getPath($source, $width, $height);
|
||||||
|
|
||||||
if (!$this->fileService->exists($target))
|
if (!$this->fileService->exists($target))
|
||||||
{
|
$this->generate($source, $width, $height);
|
||||||
$fullSource = $this->fileService->getFullPath($source);
|
|
||||||
$fullTarget = $this->fileService->getFullPath($target);
|
|
||||||
$this->thumbnailGenerator->generateFromFile($fullSource, $fullTarget, $width, $height);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $target;
|
return $target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function deleteUsedThumbnails($source)
|
||||||
|
{
|
||||||
|
foreach ($this->getUsedThumbnailSizes() as $size)
|
||||||
|
{
|
||||||
|
list ($width, $height) = $size;
|
||||||
|
$target = $this->getPath($source, $width, $height);
|
||||||
|
if ($this->fileService->exists($target))
|
||||||
|
$this->fileService->delete($target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generate($source, $width, $height)
|
||||||
|
{
|
||||||
|
$target = $this->getPath($source, $width, $height);
|
||||||
|
|
||||||
|
$fullSource = $this->fileService->getFullPath($source);
|
||||||
|
$fullTarget = $this->fileService->getFullPath($target);
|
||||||
|
$this->fileService->createFolders(dirname($target));
|
||||||
|
$this->thumbnailGenerator->generate($fullSource, $fullTarget, $width, $height);
|
||||||
|
|
||||||
|
return $target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUsedThumbnailSizes()
|
||||||
|
{
|
||||||
|
foreach (glob($this->fileService->getFullPath('thumbnails') . DIRECTORY_SEPARATOR . '*x*') as $fn)
|
||||||
|
{
|
||||||
|
if (!is_dir($fn))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
preg_match('/(?P<width>\d+)x(?P<height>\d+)/', $fn, $matches);
|
||||||
|
if ($matches)
|
||||||
|
{
|
||||||
|
$width = intval($matches['width']);
|
||||||
|
$height = intval($matches['height']);
|
||||||
|
yield [$width, $height];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPath($source, $width, $height)
|
||||||
|
{
|
||||||
|
return 'thumbnails' . DIRECTORY_SEPARATOR . $width . 'x' . $height . DIRECTORY_SEPARATOR . $source;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ class UserService
|
||||||
private $passwordService;
|
private $passwordService;
|
||||||
private $emailService;
|
private $emailService;
|
||||||
private $fileService;
|
private $fileService;
|
||||||
|
private $thumbnailService;
|
||||||
private $timeService;
|
private $timeService;
|
||||||
private $tokenService;
|
private $tokenService;
|
||||||
|
|
||||||
|
@ -21,6 +22,7 @@ class UserService
|
||||||
\Szurubooru\Services\PasswordService $passwordService,
|
\Szurubooru\Services\PasswordService $passwordService,
|
||||||
\Szurubooru\Services\EmailService $emailService,
|
\Szurubooru\Services\EmailService $emailService,
|
||||||
\Szurubooru\Services\FileService $fileService,
|
\Szurubooru\Services\FileService $fileService,
|
||||||
|
\Szurubooru\Services\ThumbnailService $thumbnailService,
|
||||||
\Szurubooru\Services\TimeService $timeService,
|
\Szurubooru\Services\TimeService $timeService,
|
||||||
\Szurubooru\Services\TokenService $tokenService)
|
\Szurubooru\Services\TokenService $tokenService)
|
||||||
{
|
{
|
||||||
|
@ -31,6 +33,7 @@ class UserService
|
||||||
$this->passwordService = $passwordService;
|
$this->passwordService = $passwordService;
|
||||||
$this->emailService = $emailService;
|
$this->emailService = $emailService;
|
||||||
$this->fileService = $fileService;
|
$this->fileService = $fileService;
|
||||||
|
$this->thumbnailService = $thumbnailService;
|
||||||
$this->timeService = $timeService;
|
$this->timeService = $timeService;
|
||||||
$this->tokenService = $tokenService;
|
$this->tokenService = $tokenService;
|
||||||
}
|
}
|
||||||
|
@ -106,7 +109,11 @@ class UserService
|
||||||
{
|
{
|
||||||
$user->avatarStyle = \Szurubooru\Helpers\EnumHelper::avatarStyleFromString($formData->avatarStyle);
|
$user->avatarStyle = \Szurubooru\Helpers\EnumHelper::avatarStyleFromString($formData->avatarStyle);
|
||||||
if ($formData->avatarContent)
|
if ($formData->avatarContent)
|
||||||
$this->fileService->saveFromBase64($formData->avatarContent, $this->getCustomAvatarSourcePath($user));
|
{
|
||||||
|
$target = $this->getCustomAvatarSourcePath($user);
|
||||||
|
$this->fileService->saveFromBase64($formData->avatarContent, $target);
|
||||||
|
$this->thumbnailService->deleteUsedThumbnails($target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($formData->userName !== null and $formData->userName !== $user->name)
|
if ($formData->userName !== null and $formData->userName !== $user->name)
|
||||||
|
@ -155,7 +162,10 @@ class UserService
|
||||||
public function deleteUser(\Szurubooru\Entities\User $user)
|
public function deleteUser(\Szurubooru\Entities\User $user)
|
||||||
{
|
{
|
||||||
$this->userDao->deleteById($user->id);
|
$this->userDao->deleteById($user->id);
|
||||||
$this->fileService->delete($this->getCustomAvatarSourcePath($user));
|
|
||||||
|
$avatarSource = $this->getCustomAvatarSourcePath($user);
|
||||||
|
$this->fileService->delete($avatarSource);
|
||||||
|
$this->thumbnailService->deleteUsedThumbnails($avatarSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCustomAvatarSourcePath(\Szurubooru\Entities\User $user)
|
public function getCustomAvatarSourcePath(\Szurubooru\Entities\User $user)
|
||||||
|
|
|
@ -13,9 +13,12 @@ abstract class AbstractTestCase extends \PHPUnit_Framework_TestCase
|
||||||
return new ConfigMock();
|
return new ConfigMock();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTestDirectory()
|
public function createTestDirectory()
|
||||||
{
|
{
|
||||||
return __DIR__ . DIRECTORY_SEPARATOR . 'files';
|
$path = $this->getTestDirectoryPath();
|
||||||
|
if (!file_exists($path))
|
||||||
|
mkdir($path, 0777, true);
|
||||||
|
return $path;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function tearDown()
|
protected function tearDown()
|
||||||
|
@ -23,11 +26,31 @@ abstract class AbstractTestCase extends \PHPUnit_Framework_TestCase
|
||||||
$this->cleanTestDirectory();
|
$this->cleanTestDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getTestDirectoryPath()
|
||||||
|
{
|
||||||
|
return __DIR__ . DIRECTORY_SEPARATOR . 'files';
|
||||||
|
}
|
||||||
|
|
||||||
private function cleanTestDirectory()
|
private function cleanTestDirectory()
|
||||||
{
|
{
|
||||||
foreach (scandir($this->getTestDirectory()) as $fn)
|
if (!file_exists($this->getTestDirectoryPath()))
|
||||||
if ($fn{0} != '.')
|
return;
|
||||||
unlink($this->getTestDirectory() . DIRECTORY_SEPARATOR . $fn);
|
|
||||||
|
$dirIterator = new \RecursiveDirectoryIterator(
|
||||||
|
$this->getTestDirectoryPath(),
|
||||||
|
\RecursiveDirectoryIterator::SKIP_DOTS);
|
||||||
|
|
||||||
|
$files = new \RecursiveIteratorIterator(
|
||||||
|
$dirIterator,
|
||||||
|
\RecursiveIteratorIterator::CHILD_FIRST);
|
||||||
|
|
||||||
|
foreach ($files as $fileInfo)
|
||||||
|
{
|
||||||
|
if ($fileInfo->isDir())
|
||||||
|
rmdir($fileInfo->getRealPath());
|
||||||
|
else
|
||||||
|
unlink($fileInfo->getRealPath());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,13 @@ class FileServiceTest extends \Szurubooru\Tests\AbstractTestCase
|
||||||
{
|
{
|
||||||
public function testSaving()
|
public function testSaving()
|
||||||
{
|
{
|
||||||
|
$testDirectory = $this->createTestDirectory();
|
||||||
$httpHelper = $this->mock( \Szurubooru\Helpers\HttpHelper::class);
|
$httpHelper = $this->mock( \Szurubooru\Helpers\HttpHelper::class);
|
||||||
$fileService = new \Szurubooru\Services\FileService($this->getTestDirectory(), $httpHelper);
|
$fileService = new \Szurubooru\Services\FileService($testDirectory, $httpHelper);
|
||||||
$input = 'data:text/plain,YXdlc29tZSBkb2c=';
|
$input = 'data:text/plain,YXdlc29tZSBkb2c=';
|
||||||
$fileService->saveFromBase64($input, 'dog.txt');
|
$fileService->saveFromBase64($input, 'dog.txt');
|
||||||
$expected = 'awesome dog';
|
$expected = 'awesome dog';
|
||||||
$actual = file_get_contents($this->getTestDirectory() . DIRECTORY_SEPARATOR . 'dog.txt');
|
$actual = file_get_contents($testDirectory . DIRECTORY_SEPARATOR . 'dog.txt');
|
||||||
$this->assertEquals($expected, $actual);
|
$this->assertEquals($expected, $actual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
51
tests/Services/ThumbnailServiceTest.php
Normal file
51
tests/Services/ThumbnailServiceTest.php
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
namespace Szurubooru\Tests\Services;
|
||||||
|
|
||||||
|
class ThumbnailServiceTest extends \Szurubooru\Tests\AbstractTestCase
|
||||||
|
{
|
||||||
|
public function testDeleteUsedThumbnails()
|
||||||
|
{
|
||||||
|
define('DS', DIRECTORY_SEPARATOR);
|
||||||
|
|
||||||
|
$tempDirectory = $this->createTestDirectory();
|
||||||
|
mkdir($tempDirectory . DS . 'thumbnails');
|
||||||
|
mkdir($tempDirectory . DS . 'thumbnails' . DS . '5x5');
|
||||||
|
mkdir($tempDirectory . DS . 'thumbnails' . DS . '10x10');
|
||||||
|
touch($tempDirectory . DS . 'thumbnails' . DS . '5x5' . DS . 'remove');
|
||||||
|
touch($tempDirectory . DS . 'thumbnails' . DS . '5x5' . DS . 'keep');
|
||||||
|
touch($tempDirectory . DS . 'thumbnails' . DS . '10x10' . DS . 'remove');
|
||||||
|
|
||||||
|
$httpHelperMock = $this->mock(\Szurubooru\Helpers\HttpHelper::class);
|
||||||
|
$fileService = new \Szurubooru\Services\FileService($tempDirectory, $httpHelperMock);
|
||||||
|
$thumbnailGeneratorMock = $this->mock(\Szurubooru\Services\ThumbnailGenerators\SmartThumbnailGenerator::class);
|
||||||
|
|
||||||
|
$thumbnailService = new \Szurubooru\Services\ThumbnailService($fileService, $thumbnailGeneratorMock);
|
||||||
|
$thumbnailService->deleteUsedThumbnails('remove');
|
||||||
|
|
||||||
|
$this->assertFalse(file_exists($tempDirectory . DS . 'thumbnails' . DS . '5x5' . DS . 'remove'));
|
||||||
|
$this->assertTrue(file_exists($tempDirectory . DS . 'thumbnails' . DS . '5x5' . DS . 'keep'));
|
||||||
|
$this->assertFalse(file_exists($tempDirectory . DS . 'thumbnails' . DS . '10x10' . DS . 'remove'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetUsedThumbnailSizes()
|
||||||
|
{
|
||||||
|
$tempDirectory = $this->createTestDirectory();
|
||||||
|
mkdir($tempDirectory . DIRECTORY_SEPARATOR . '5x5');
|
||||||
|
mkdir($tempDirectory . DIRECTORY_SEPARATOR . '10x10');
|
||||||
|
mkdir($tempDirectory . DIRECTORY_SEPARATOR . 'something unexpected');
|
||||||
|
touch($tempDirectory . DIRECTORY_SEPARATOR . '15x15');
|
||||||
|
|
||||||
|
$fileServiceMock = $this->mock(\Szurubooru\Services\FileService::class);
|
||||||
|
$fileServiceMock->expects($this->once())->method('getFullPath')->willReturn($tempDirectory);
|
||||||
|
$thumbnailGeneratorMock = $this->mock(\Szurubooru\Services\ThumbnailGenerators\SmartThumbnailGenerator::class);
|
||||||
|
|
||||||
|
$thumbnailService = new \Szurubooru\Services\ThumbnailService($fileServiceMock, $thumbnailGeneratorMock);
|
||||||
|
|
||||||
|
$expected = [[5, 5], [10, 10]];
|
||||||
|
$actual = iterator_to_array($thumbnailService->getUsedThumbnailSizes());
|
||||||
|
|
||||||
|
$this->assertEquals(count($expected), count($actual));
|
||||||
|
foreach ($expected as $v)
|
||||||
|
$this->assertContains($v, $actual);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ final class UserServiceTest extends \Szurubooru\Tests\AbstractTestCase
|
||||||
private $passwordServiceMock;
|
private $passwordServiceMock;
|
||||||
private $emailServiceMock;
|
private $emailServiceMock;
|
||||||
private $fileServiceMock;
|
private $fileServiceMock;
|
||||||
|
private $thumbnailServiceMock;
|
||||||
private $timeServiceMock;
|
private $timeServiceMock;
|
||||||
private $tokenServiceMock;
|
private $tokenServiceMock;
|
||||||
|
|
||||||
|
@ -22,6 +23,7 @@ final class UserServiceTest extends \Szurubooru\Tests\AbstractTestCase
|
||||||
$this->passwordServiceMock = $this->mock(\Szurubooru\Services\PasswordService::class);
|
$this->passwordServiceMock = $this->mock(\Szurubooru\Services\PasswordService::class);
|
||||||
$this->emailServiceMock = $this->mock(\Szurubooru\Services\EmailService::class);
|
$this->emailServiceMock = $this->mock(\Szurubooru\Services\EmailService::class);
|
||||||
$this->fileServiceMock = $this->mock(\Szurubooru\Services\FileService::class);
|
$this->fileServiceMock = $this->mock(\Szurubooru\Services\FileService::class);
|
||||||
|
$this->thumbnailServiceMock = $this->mock(\Szurubooru\Services\ThumbnailService::class);
|
||||||
$this->timeServiceMock = $this->mock(\Szurubooru\Services\TimeService::class);
|
$this->timeServiceMock = $this->mock(\Szurubooru\Services\TimeService::class);
|
||||||
$this->tokenServiceMock = $this->mock(\Szurubooru\Services\TokenService::class);
|
$this->tokenServiceMock = $this->mock(\Szurubooru\Services\TokenService::class);
|
||||||
}
|
}
|
||||||
|
@ -179,6 +181,7 @@ final class UserServiceTest extends \Szurubooru\Tests\AbstractTestCase
|
||||||
$this->passwordServiceMock,
|
$this->passwordServiceMock,
|
||||||
$this->emailServiceMock,
|
$this->emailServiceMock,
|
||||||
$this->fileServiceMock,
|
$this->fileServiceMock,
|
||||||
|
$this->thumbnailServiceMock,
|
||||||
$this->timeServiceMock,
|
$this->timeServiceMock,
|
||||||
$this->tokenServiceMock);
|
$this->tokenServiceMock);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue