Sanitize import media filename lookup to match stored name

pull/7546/head
Your Name 1 month ago
parent 26cdd5ff6e
commit b206c8a251

@ -111,7 +111,7 @@ class TransformImports extends Command
$missingMedia = false;
foreach ($ip->media as $ipm) {
$fileName = last(explode('/', $ipm['uri']));
$fileName = $this->sanitizeFilename(last(explode('/', $ipm['uri'])));
$og = 'imports/'.$id.'/'.$fileName;
if (! $disk->exists($og)) {
$missingMedia = true;
@ -129,8 +129,7 @@ class TransformImports extends Command
$mediaRecords = [];
foreach ($ip->media as $ipm) {
$fileName = last(explode('/', $ipm['uri']));
$ext = last(explode('.', $fileName));
$fileName = $this->sanitizeFilename(last(explode('/', $ipm['uri'])));
$ext = strtolower(last(explode('.', $fileName)));
if (! in_array($ext, ['jpg', 'jpeg', 'png', 'webp', 'mp4'], true)) {
continue;
@ -287,4 +286,22 @@ class TransformImports extends Command
}
}
}
/**
* Sanitize a filename to match how ImportPostController::storeMedia wrote
* it to disk. The uploaded file is stored under a name with unsafe chars
* replaced by underscores, so a lookup built from the raw import URI (which
* may contain spaces/special chars) would miss the file and wrongly skip
* the post as "missing media".
*/
private function sanitizeFilename(string $filename): string
{
$parts = explode('.', $filename);
$extension = array_pop($parts);
$originalName = implode('.', $parts);
$safeFilename = preg_replace('/[^a-zA-Z0-9_.-]/', '_', $originalName);
return $safeFilename.'.'.$extension;
}
}

@ -0,0 +1,88 @@
<?php
use App\Models\ImportPost;
use App\Models\User;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Facades\Storage;
uses(LazilyRefreshDatabase::class);
/*
|--------------------------------------------------------------------------
| app:transform-imports must find media stored under its sanitized name
|--------------------------------------------------------------------------
|
| ImportPostController::storeMedia writes uploads under a sanitized filename
| (unsafe chars -> "_"), but TransformImports built the lookup path from the
| raw import URI. A filename with spaces/special chars therefore never matched
| on disk and the post was wrongly skipped as missing media. The lookup must
| apply the same sanitization.
|
*/
beforeEach(function () {
Storage::fake('local');
config(['filesystems.default' => 'local']);
});
it('imports a post whose media filename contains spaces/special chars', function () {
$user = User::factory()->create();
$user->refresh();
// The raw uri as stored in the import metadata (unsanitized).
$rawName = 'my photo (1).jpg';
// How storeMedia() actually wrote it to disk (sanitized).
$sanitized = 'my_photo__1_.jpg';
Storage::disk('local')->put('imports/'.$user->id.'/'.$sanitized, 'fake-image-bytes');
$ip = new ImportPost;
$ip->profile_id = $user->profile_id;
$ip->user_id = $user->id;
$ip->service = 'instagram';
$ip->filename = $sanitized;
$ip->media_count = 1;
$ip->post_type = 'photo';
$ip->caption = 'hello world';
$ip->media = [['uri' => 'media/posts/'.$rawName]];
$ip->creation_year = 21;
$ip->creation_month = 6;
$ip->creation_day = 15;
$ip->creation_date = now()->parse('2021-06-15 12:00:00');
$ip->skip_missing_media = false;
$ip->save();
$this->artisan('app:transform-imports')->assertExitCode(0);
$ip->refresh();
expect($ip->status_id)->not->toBeNull();
expect((bool) $ip->skip_missing_media)->toBeFalse();
});
it('still skips a post whose sanitized media file is genuinely absent', function () {
$user = User::factory()->create();
$user->refresh();
// No file placed on disk at all.
$ip = new ImportPost;
$ip->profile_id = $user->profile_id;
$ip->user_id = $user->id;
$ip->service = 'instagram';
$ip->filename = 'missing.jpg';
$ip->media_count = 1;
$ip->post_type = 'photo';
$ip->caption = 'hello world';
$ip->media = [['uri' => 'media/posts/missing.jpg']];
$ip->creation_year = 21;
$ip->creation_month = 6;
$ip->creation_day = 15;
$ip->creation_date = now()->parse('2021-06-15 12:00:00');
$ip->skip_missing_media = false;
$ip->save();
$this->artisan('app:transform-imports')->assertExitCode(0);
$ip->refresh();
expect($ip->status_id)->toBeNull();
expect((bool) $ip->skip_missing_media)->toBeTrue();
});
Loading…
Cancel
Save