diff --git a/extend.php b/extend.php
index 0f84140d..fe320078 100644
--- a/extend.php
+++ b/extend.php
@@ -25,12 +25,14 @@
use Flarum\Post\Event\Revised;
use Flarum\Settings\Event\Deserializing;
use Flarum\User\User;
+use FoF\Upload\Events\File\WasSaved;
use FoF\Upload\Events\File\WillBeUploaded;
use FoF\Upload\Exceptions\ExceptionHandler;
use FoF\Upload\Exceptions\InvalidUploadException;
use FoF\Upload\Extend\SvgSanitizer;
use FoF\Upload\Extenders\LoadFilesRelationship;
use FoF\Upload\Helpers\Util;
+use s9e\TextFormatter\Configurator;
return [
(new Extend\Frontend('admin'))
@@ -86,7 +88,8 @@
->listen(Deserializing::class, Listeners\AddAvailableOptionsInAdmin::class)
->listen(Posted::class, Listeners\LinkImageToPostOnSave::class)
->listen(Revised::class, Listeners\LinkImageToPostOnSave::class)
- ->listen(WillBeUploaded::class, Listeners\AddImageProcessor::class),
+ ->listen(WillBeUploaded::class, Listeners\AddImageProcessor::class)
+ ->listen(WasSaved::class, Listeners\AddImageMetadataProcessor::class),
(new Extend\Filesystem())
->disk('private-shared', Extenders\PrivateSharedDiskConfig::class),
@@ -108,6 +111,11 @@
->attributes(Extenders\AddUserAttributes::class),
(new Extend\Formatter())
+
+ ->configure(function (Configurator $config) {
+ // Remove the check that prevents dynamic content in CSS
+ $config->templateChecker->remove('DisallowUnsafeDynamicCSS');
+ })
->render(Formatter\ImagePreview\FormatImagePreview::class)
->render(Formatter\TextPreview\FormatTextPreview::class),
diff --git a/migrations/2025_08_06_000000_add_image_metadata.php b/migrations/2025_08_06_000000_add_image_metadata.php
new file mode 100644
index 00000000..cdf7fb4f
--- /dev/null
+++ b/migrations/2025_08_06_000000_add_image_metadata.php
@@ -0,0 +1,40 @@
+ function (Builder $schema) {
+ if ($schema->hasTable('fof_upload_image_metadata')) {
+ return;
+ }
+ $schema->create('fof_upload_image_metadata', function (Blueprint $table) {
+ $table->unsignedInteger('upload_id');
+ $table->uuid('file_id');
+ $table->unsignedInteger('image_width');
+ $table->unsignedInteger('image_height');
+
+ $table->primary('upload_id');
+
+ // Add foreign key constraint
+ $table->foreign('upload_id')
+ ->references('id')
+ ->on('flarum_fof_upload_files')
+ ->onDelete('cascade');
+ });
+ },
+ 'down' => function (Builder $schema) {
+ $schema->dropIfExists('fof_upload_image_metadata');
+ },
+];
+
diff --git a/resources/templates/image-preview.blade.php b/resources/templates/image-preview.blade.php
index bcb8d809..a80aeb64 100644
--- a/resources/templates/image-preview.blade.php
+++ b/resources/templates/image-preview.blade.php
@@ -1 +1,9 @@
-
+
diff --git a/src/File.php b/src/File.php
index b7cbe74b..61a1ca42 100644
--- a/src/File.php
+++ b/src/File.php
@@ -133,4 +133,12 @@ public function human_filesize(string $bytes, int $decimals = 0): string
return sprintf("%.{$decimals}f", (int) $bytes / pow(1024, $factor)).@$size[$factor];
}
+
+
+ public function imageMetadata(): \Illuminate\Database\Eloquent\Relations\HasOne
+ {
+ return $this->hasOne(ImageMetadata::class, 'upload_id', 'id');
+ }
+
+
}
diff --git a/src/Formatter/ImagePreview/FormatImagePreview.php b/src/Formatter/ImagePreview/FormatImagePreview.php
index b765597d..fcc23425 100644
--- a/src/Formatter/ImagePreview/FormatImagePreview.php
+++ b/src/Formatter/ImagePreview/FormatImagePreview.php
@@ -12,6 +12,7 @@
namespace FoF\Upload\Formatter\ImagePreview;
+use FoF\Upload\ImageMetadata;
use FoF\Upload\Repositories\FileRepository;
use Illuminate\Support\Arr;
use s9e\TextFormatter\Renderer;
@@ -57,6 +58,13 @@ public function __invoke(Renderer $renderer, $context, string $xml)
$attributes['title'] = $file->base_name;
}
+ // Add aspect ratio if image metadata exists
+ $imageMetadata = ImageMetadata::byFile($file);
+
+ if ($imageMetadata && $imageMetadata->image_width && $imageMetadata->image_height) {
+ $attributes['aspectRatio'] = $imageMetadata->image_width . "/" . $imageMetadata->image_height;
+ }
+
return $attributes;
});
}
diff --git a/src/ImageMetadata.php b/src/ImageMetadata.php
new file mode 100644
index 00000000..7f74c0bf
--- /dev/null
+++ b/src/ImageMetadata.php
@@ -0,0 +1,60 @@
+where('uuid', $uuid);
+ }
+
+ public static function byFile($file): Builder|\Illuminate\Database\Eloquent\Model|null
+ {
+ if (!$file) {
+ return null;
+ }
+
+ return static::query()->where('upload_id', $file->id)->first();
+ }
+
+ /**
+ * Relation to the File model
+ */
+ public function file(): \Illuminate\Database\Eloquent\Relations\BelongsTo
+ {
+ return $this->belongsTo(File::class, 'upload_id', 'id');
+ }
+
+ /**
+ * Static helper to find a metadata row by upload_id
+ */
+ public static function byUploadId(int $uploadId): \Illuminate\Database\Eloquent\Builder|null
+ {
+ return static::query()->where('upload_id', $uploadId)->first();
+ }
+}
diff --git a/src/Listeners/AddImageMetadataProcessor.php b/src/Listeners/AddImageMetadataProcessor.php
new file mode 100644
index 00000000..817a39de
--- /dev/null
+++ b/src/Listeners/AddImageMetadataProcessor.php
@@ -0,0 +1,41 @@
+validateMime($event->mime)) {
+ $this->processor->addMetadata($event->file, $event->uploadedFile, $event->mime);
+ }
+ }
+
+ protected function validateMime($mime): bool
+ {
+ return true;
+ if ($mime == 'image/jpeg' || $mime == 'image/png' || $mime == 'image/gif' || $mime == 'image/svg+xml') {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/Processors/ImageProcessor.php b/src/Processors/ImageProcessor.php
index 857c5970..13d9142c 100644
--- a/src/Processors/ImageProcessor.php
+++ b/src/Processors/ImageProcessor.php
@@ -92,4 +92,22 @@ protected function watermark(Image $image)
);
}
}
+
+ public function addMetadata(File &$file, UploadedFile &$upload, string &$mime): void
+ {
+ try {
+ $image = (new ImageManager())->make('assets/files'.DIRECTORY_SEPARATOR.$file->path);
+ } catch (NotReadableException $e) {
+ throw new ValidationException(['upload' => 'Corrupted image']);
+ }
+
+ $file->imageMetadata()->create([
+ 'upload_id' => $file->id,
+ 'file_id' => $file->uuid ?? '',
+ 'image_width' => $image->width(),
+ 'image_height' => $image->height(),
+ ]);
+ $file->save();
+
+ }
}