Browse Source

Refactor and cleanup of the Movim Image class

Check for the required directories when the daemon is launched
Introduce the public/images/ path for upcoming persistent images storage
pull/1559/head
Timothée Jaussoin 4 months ago
parent
commit
fcdacc5e00
  1. 2
      app/MujiCallParticipant.php
  2. 14
      app/Post.php
  3. 2
      app/Presence.php
  4. 14
      app/Widgets/Avatar/Avatar.php
  5. 14
      app/Widgets/CommunityConfig/CommunityConfig.php
  6. 12
      app/Widgets/RoomsUtils/RoomsUtils.php
  7. 4
      app/Widgets/SpaceInfo/SpaceInfo.php
  8. 26
      src/Movim/Bootstrap.php
  9. 33
      src/Movim/Console/DaemonCommand.php
  10. 67
      src/Movim/Image.php

2
app/MujiCallParticipant.php

@ -43,6 +43,6 @@ class MujiCallParticipant extends Model
public function getConferencePictureAttribute(): string
{
return Image::getOrCreate($this->jid, 120) ?? avatarPlaceholder($this->jid);
return Image::getOrCreate($this->jid, width: 120) ?? avatarPlaceholder($this->jid);
}
}

14
app/Post.php

@ -764,13 +764,13 @@ class Post extends Model
new Promise(function () use ($url) {
\requestResolverWorker($url)->then(function ($extractor) {
try {
$atte = new Attachment;
$atte->rel = 'enclosure';
$atte->href = $extractor->image;
$atte->type = 'media/jpeg';
$atte->category = 'picture';
$atte->post_id = $this->id;
$atte->save();
$attachment = new Attachment;
$attachment->rel = 'enclosure';
$attachment->href = $extractor->image;
$attachment->type = 'media/jpeg';
$attachment->category = 'picture';
$attachment->post_id = $this->id;
$attachment->save();
} catch (\Throwable $th) {
//
}

2
app/Presence.php

@ -90,7 +90,7 @@ class Presence extends Model
public function getConferencePictureAttribute(): string
{
return Image::getOrCreate($this->mucjid, 120) ?? avatarPlaceholder($this->resource);
return Image::getOrCreate($this->mucjid, width: 120) ?? avatarPlaceholder($this->resource);
}
public function getAffiliationTxtAttribute(): ?string

14
app/Widgets/Avatar/Avatar.php

@ -95,20 +95,20 @@ class Avatar extends \Movim\Widget\Base
$key = $this->me->id . 'banner';
$p = new Image;
$p->fromBase64($banner->photobin->value);
$p->setKey($key);
$p->save(false, false, 'jpeg', 60);
$image = new Image;
$image->fromBase64($banner->photobin->value);
$image->setKey($key);
$image->save(format: 'jpeg', quality: 60);
// Reload
$p->load('jpeg');
$image->load('jpeg');
$r = $this->xmpp(new Set);
$r->setNode('urn:xmpp:movim-banner:0')
->setUrl(Image::getOrCreate($key, false, false, 'jpeg', true))
->setUrl(Image::getOrCreate($key, format: 'jpeg', noTime: true))
->setWidthMetadata(1280)
->setHeightMetadata(320)
->setData($p->toBase())
->setData($image->toBase())
->request();
}
}

14
app/Widgets/CommunityConfig/CommunityConfig.php

@ -80,19 +80,19 @@ class CommunityConfig extends Base
$key = $origin . $node . 'avatar';
$p = new Image;
$p->fromBase64($form->photobin->value);
$p->setKey($key);
$p->save(false, false, 'jpeg', 60);
$image = new Image;
$image->fromBase64($form->photobin->value);
$image->setKey($key);
$image->save(format: 'jpeg', quality: 60);
// Reload the freshly compressed picture
$p->load('jpeg');
$image->load('jpeg');
$r = $this->xmpp(new AvatarSet);
$r->setTo($origin)
->setNode($node)
->setUrl(Image::getOrCreate($key, false, false, 'jpeg', true))
->setData($p->toBase())
->setUrl(Image::getOrCreate($key, format: 'jpeg', noTime: true))
->setData($image->toBase())
->request();
}

12
app/Widgets/RoomsUtils/RoomsUtils.php

@ -270,18 +270,18 @@ class RoomsUtils extends Base
$tempKey = \generateKey(6);
$p = new Image;
$p->fromBase64($form->photobin->value);
$p->setKey($tempKey);
$p->save(false, false, 'jpeg', 60);
$image = new Image;
$image->fromBase64($form->photobin->value);
$image->setKey($tempKey);
$image->save(format: 'jpeg', quality: 60);
// Reload
$p->load('jpeg');
$image->load('jpeg');
$vcard = new \stdClass;
$vcard->photobin = new \stdClass;
$vcard->phototype = new \stdClass;
$vcard->photobin->value = $p->toBase();
$vcard->photobin->value = $image->toBase();
$vcard->phototype->value = 'image/jpeg';
$r = $this->xmpp(new VcardSet);

4
app/Widgets/SpaceInfo/SpaceInfo.php

@ -273,7 +273,7 @@ class SpaceInfo extends Base
$p = new Image;
$p->fromBase64($form->photobin->value);
$p->setKey($key);
$p->save(false, false, 'jpeg', 60);
$p->save(format: 'jpeg', quality: 60);
// Reload the freshly compressed picture
$p->load('jpeg');
@ -281,7 +281,7 @@ class SpaceInfo extends Base
$r = $this->xmpp(new AvatarSet);
$r->setTo($server)
->setNode($node)
->setUrl(Image::getOrCreate($key, false, false, 'jpeg', true))
->setUrl(Image::getOrCreate($key, format: 'jpeg', noTime: true))
->setData($p->toBase())
->request();
}

26
src/Movim/Bootstrap.php

@ -40,8 +40,6 @@ class Bootstrap
return null;
}
//Check if vital system need is OK
$this->checkSystem();
$session = $this->checkSession();
$this->loadTimezone($session);
$this->loadLanguage($session);
@ -49,21 +47,6 @@ class Bootstrap
return $session;
}
private function checkSystem()
{
if (!file_exists(CACHE_PATH) && !@mkdir(CACHE_PATH)) {
throw new \Exception('Couldn’t create cache directory');
}
if (!file_exists(PUBLIC_CACHE_PATH) && !@mkdir(PUBLIC_CACHE_PATH)) {
throw new \Exception('Couldn’t create public cache directory');
}
if (!file_exists(config('paths.log')) && !@mkdir(config('paths.log'))) {
throw new \Exception('Couldn’t create log directory');
}
}
private function setConstants()
{
if (file_exists(DOCUMENT_ROOT . '/.env')) {
@ -87,10 +70,13 @@ class Bootstrap
define('APP_PATH', DOCUMENT_ROOT . '/app/');
define('CONFIG_PATH', DOCUMENT_ROOT . '/config/');
define('LOCALES_PATH', DOCUMENT_ROOT . '/locales/');
define('PUBLIC_CACHE_PATH', DOCUMENT_ROOT . '/public/cache/');
define('PUBLIC_EMOJIS_PATH', DOCUMENT_ROOT . '/public/emojis/');
define('PUBLIC_PATH', DOCUMENT_ROOT . '/public/');
define('PUBLIC_STICKERS_PATH', DOCUMENT_ROOT . '/public/stickers/');
define('CACHE_DIR', 'cache/');
define('IMAGES_DIR', 'images/');
define('PUBLIC_CACHE_PATH', PUBLIC_PATH . CACHE_DIR);
define('PUBLIC_IMAGES_PATH', PUBLIC_PATH . IMAGES_DIR);
define('PUBLIC_EMOJIS_PATH', PUBLIC_PATH . 'emojis/');
define('PUBLIC_STICKERS_PATH', PUBLIC_PATH . 'stickers/');
define('VIEWS_PATH', DOCUMENT_ROOT . '/app/Views/');
define('WIDGETS_PATH', DOCUMENT_ROOT . '/app/Widgets/');
define('WORKERS_PATH', DOCUMENT_ROOT . '/workers/');

33
src/Movim/Console/DaemonCommand.php

@ -11,8 +11,6 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Respect\Validation\Validator;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
@ -85,6 +83,16 @@ class DaemonCommand extends Command
exit;
}
if ($paths = $this->checkDirectories()) {
foreach ($paths as $path) {
$output->writeln('<error>Can’t create ' . $path . ' directory</error>');
}
$output->writeln('<info>You can create them yourself or let the daemon create them for you.</info>');
$output->writeln('<info>In both case they should be writable by the same system user that is running the daemon.</info>');
exit;
}
if (User::where('admin', true)->count() == 0) {
$output->writeln('<comment>Please set at least one user as an admin once its account is logged in</comment>');
@ -160,7 +168,7 @@ class DaemonCommand extends Command
'baseuri' => $baseuri,
'DAEMON_DEBUG' => config('daemon.debug'),
'DAEMON_PORT' => config('daemon.port'),
'DAEMON_VERBOSE'=> config('daemon.verbose'),
'DAEMON_VERBOSE' => config('daemon.verbose'),
'DB_DATABASE' => config('database.database'),
'DB_DRIVER' => config('database.driver'),
'DB_HOST' => config('database.host'),
@ -178,4 +186,23 @@ class DaemonCommand extends Command
return Command::SUCCESS;
}
private function checkDirectories(): ?array
{
$paths = [
CACHE_PATH,
PUBLIC_CACHE_PATH,
PUBLIC_IMAGES_PATH,
config('paths.log')
];
$errors = [];
foreach ($paths as $path) {
if (!file_exists($path) && !@mkdir($path)) {
array_push($errors, $path);
}
}
return !empty($errors) ? $errors : null;
}
}

67
src/Movim/Image.php

@ -12,7 +12,6 @@ class Image
private $_im;
private $_inMemory = false;
public static $folder = 'cache/';
public static $formats = ['jpeg' => '.jpg', 'png' => '.png', 'webp' => '.webp', 'gif' => '.gif'];
public static $hash = 'sha256'; // Cache need to be cleared in a migration if changed
private static $originalType = '_o';
@ -50,11 +49,11 @@ class Image
/**
* @desc Load a bin picture from a path
*/
public function load(string $format = DEFAULT_PICTURE_FORMAT): bool
public function load(string $format = DEFAULT_PICTURE_FORMAT, ?string $directory = CACHE_DIR): bool
{
if (!empty($this->_key)) {
return $this->fromPath(
PUBLIC_CACHE_PATH .
PUBLIC_PATH . $directory .
hash(Image::$hash, $this->_key) .
self::$originalType .
self::$formats[$format]
@ -129,44 +128,43 @@ class Image
/**
* @desc Convert to a base64
*/
public function toBase(): string
public function toBase(): ?string
{
if ($this->_im) {
return base64_encode($this->toBin());
return base64_encode($this->_im->getImageBlob());
}
}
/**
* @desc Convert to a base64
*/
public function toBin(): string
{
if ($this->_im) {
return $this->_im->getImageBlob();
}
return null;
}
/**
* @desc Return the picture URL or create it if possible
*/
public static function getOrCreate(string $key, $width = false, $height = false, $format = DEFAULT_PICTURE_FORMAT, bool $noTime = false): ?string
{
public static function getOrCreate(
string $key,
?int $width = null,
?int $height = null,
?string $format = DEFAULT_PICTURE_FORMAT,
?bool $noTime = false,
?string $directory = CACHE_DIR,
): ?string {
if (!in_array($format, array_keys(self::$formats))) {
$format = DEFAULT_PICTURE_FORMAT;
}
$type = $width ? '_' . $width
$type = $width != null
? '_' . $width
: self::$originalType;
/**
* The file is in the cache and we can directly return it
*/
if (file_exists(
PUBLIC_CACHE_PATH . hash(Image::$hash, $key) .
PUBLIC_PATH . $directory . hash(Image::$hash, $key) .
$type . self::$formats[$format]
)) {
return urilize(
self::$folder . hash(Image::$hash, $key) . $type . self::$formats[$format],
$directory . hash(Image::$hash, $key) . $type . self::$formats[$format],
$noTime
);
}
@ -175,9 +173,9 @@ class Image
* The file is not in the cache but we do have the original to build the requested size
*/
elseif (
$width
$width != null
&& file_exists(
PUBLIC_CACHE_PATH . hash(Image::$hash, $key) .
PUBLIC_PATH . $directory . hash(Image::$hash, $key) .
self::$originalType . self::$formats[$format]
)
) {
@ -189,7 +187,7 @@ class Image
$im->save($width, $height, $format);
return urilize(
self::$folder . hash(Image::$hash, $key) . $type . self::$formats[$format],
$directory . hash(Image::$hash, $key) . $type . self::$formats[$format],
$noTime
);
}
@ -197,16 +195,21 @@ class Image
return null;
}
public function save($width = false, $height = false, $format = DEFAULT_PICTURE_FORMAT, $quality = DEFAULT_PICTURE_QUALITY)
{
public function save(
?int $width = null,
?int $height = null,
?string $format = DEFAULT_PICTURE_FORMAT,
?int $quality = DEFAULT_PICTURE_QUALITY,
?string $directory = CACHE_DIR
) {
if (!$this->_key && !$this->_inMemory) return;
$type = $width ? '_' . $width
$type = $width != null ? '_' . $width
: self::$originalType;
if (!$this->_inMemory) {
// Cleanup the existing files
$path = PUBLIC_CACHE_PATH . hash(Image::$hash, $this->_key) . $type . self::$formats[$format];
$path = PUBLIC_PATH . $directory . hash(Image::$hash, $this->_key) . $type . self::$formats[$format];
// If the file exists we replace it
if (file_exists($path)) {
@ -216,7 +219,7 @@ class Image
if ($width == false) {
foreach (
glob(
PUBLIC_CACHE_PATH .
PUBLIC_PATH . $directory .
hash(Image::$hash, $this->_key) .
'*' . self::$formats[$format],
GLOB_NOSORT
@ -279,11 +282,11 @@ class Image
$this->_im->setImageOrientation(\Imagick::ORIENTATION_TOPLEFT);
// Resize
if (!$height) {
if ($height == null) {
$height = $width;
}
if ($width && $height) {
if ($width != null && $height != null) {
$geo = $this->_im->getImageGeometry();
$this->_im->cropThumbnailImage($width, $height);
@ -300,7 +303,7 @@ class Image
$this->_im->clear();
}
} catch (\ImagickException $e) {
logError($this->_key . ' '. $e->getMessage());
logError($this->_key . ' ' . $e->getMessage());
}
}
@ -315,12 +318,12 @@ class Image
/**
* Remove the original
*/
public function remove(string $format = DEFAULT_PICTURE_FORMAT)
/*public function remove(string $format = DEFAULT_PICTURE_FORMAT)
{
$path = PUBLIC_CACHE_PATH . hash(Image::$hash, $this->_key) . self::$originalType . self::$formats[$format];
if (file_exists($path)) {
@unlink($path);
}
}
}*/
}
Loading…
Cancel
Save