diff --git a/resources/js/form/components/fields/upload/Upload.vue b/resources/js/form/components/fields/upload/Upload.vue
index 7b60be9c5..1c3d6877c 100644
--- a/resources/js/form/components/fields/upload/Upload.vue
+++ b/resources/js/form/components/fields/upload/Upload.vue
@@ -477,20 +477,15 @@
-
+
- {{ value?.name }}
+ {{ value.name }}
@@ -527,16 +522,11 @@
-
+
{{ __('sharp::form.upload.download_link') }}
diff --git a/resources/js/show/components/fields/File.vue b/resources/js/show/components/fields/File.vue
index ec1b76830..485d5d75d 100644
--- a/resources/js/show/components/fields/File.vue
+++ b/resources/js/show/components/fields/File.vue
@@ -51,27 +51,27 @@
-
-
-
-
- {{ value.name }}
-
-
+
+
+
+
+
+ {{ value.name }}
+
+
-
- {{ __('sharp::form.upload.download_tooltip') }}
-
-
-
+
+ {{ __('sharp::form.upload.download_tooltip') }}
+
+
+
+
+
+ {{ value.name }}
+
@@ -87,27 +87,24 @@
-
-
-
-
-
-
- {{ __('sharp::show.file.download') }}
-
-
-
+
+
+
+
+
+
+
+ {{ __('sharp::show.file.download') }}
+
+
+
+
diff --git a/resources/js/types/generated.d.ts b/resources/js/types/generated.d.ts
index 105216c2b..35bb75464 100644
--- a/resources/js/types/generated.d.ts
+++ b/resources/js/types/generated.d.ts
@@ -663,6 +663,7 @@ export type FormUploadFieldValueData = {
thumbnail: string | null;
editable_thumbnail: string | null;
playable_preview_url: string | null;
+ download_url: string | null;
uploaded: boolean | null;
transformed: boolean | null;
not_found: boolean | null;
@@ -929,6 +930,7 @@ export type ShowFileFieldData = {
path: string;
thumbnail: string;
playable_preview_url: string;
+ download_url: string;
size: number;
mime_type: string;
};
diff --git a/src/Data/Form/Fields/FormUploadFieldValueData.php b/src/Data/Form/Fields/FormUploadFieldValueData.php
index f04d8f256..ae75bf95f 100644
--- a/src/Data/Form/Fields/FormUploadFieldValueData.php
+++ b/src/Data/Form/Fields/FormUploadFieldValueData.php
@@ -21,6 +21,7 @@ public function __construct(
public ?string $thumbnail,
public ?string $editable_thumbnail,
public ?string $playable_preview_url,
+ public ?string $download_url,
public ?bool $uploaded,
public ?bool $transformed,
public ?bool $not_found,
diff --git a/src/Data/Show/Fields/ShowFileFieldData.php b/src/Data/Show/Fields/ShowFileFieldData.php
index 217fa47a4..e9e5b95f9 100644
--- a/src/Data/Show/Fields/ShowFileFieldData.php
+++ b/src/Data/Show/Fields/ShowFileFieldData.php
@@ -20,6 +20,7 @@ final class ShowFileFieldData extends Data
'path' => 'string',
'thumbnail' => 'string',
'playable_preview_url' => 'string',
+ 'download_url' => 'string',
'size' => 'int',
'mime_type' => 'string',
])]
diff --git a/src/Form/Eloquent/Uploads/Transformers/SharpUploadModelFormAttributeTransformer.php b/src/Form/Eloquent/Uploads/Transformers/SharpUploadModelFormAttributeTransformer.php
index 542ee0dad..597e693d8 100644
--- a/src/Form/Eloquent/Uploads/Transformers/SharpUploadModelFormAttributeTransformer.php
+++ b/src/Form/Eloquent/Uploads/Transformers/SharpUploadModelFormAttributeTransformer.php
@@ -8,6 +8,7 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Support\Arr;
+use Illuminate\Support\Facades\URL;
use Illuminate\Support\Str;
use Intervention\Image\Exceptions\DecoderException;
@@ -79,6 +80,7 @@ public function apply($value, $instance = null, $attribute = null)
'disk',
'thumbnail',
'playable_preview_url',
+ 'download_url',
'size',
'mime_type',
'filters',
@@ -108,6 +110,15 @@ protected function transformUpload(SharpUploadModel $upload): array
'mime_type' => $upload->mime_type,
'thumbnail' => $this->getThumbnailUrl($upload),
'playable_preview_url' => $this->getPlayableMediaUrl($upload),
+ 'download_url' => URL::temporarySignedRoute(
+ 'code16.sharp.download.show',
+ now()->plus(minutes: config('session.lifetime')),
+ [
+ 'entityKey' => sharp()->context()->entityKey(),
+ 'instanceId' => sharp()->context()->instanceId(),
+ 'disk' => $upload->disk,
+ 'path' => $upload->file_name,
+ ]),
'size' => $upload->size,
]
: [],
diff --git a/src/Http/Controllers/Api/DownloadController.php b/src/Http/Controllers/Api/DownloadController.php
index e91ef3bc0..43ce06eeb 100644
--- a/src/Http/Controllers/Api/DownloadController.php
+++ b/src/Http/Controllers/Api/DownloadController.php
@@ -8,6 +8,10 @@ class DownloadController extends ApiController
{
public function show(string $globalFilter, string $entityKey, ?string $instanceId = null)
{
+ if (! request()->hasValidSignature()) {
+ abort(401);
+ }
+
$this->authorizationManager->check('view', $entityKey, $instanceId);
if (
diff --git a/tests/Http/Api/DownloadControllerTest.php b/tests/Http/Api/DownloadControllerTest.php
index 2be7c3757..fbb54915e 100644
--- a/tests/Http/Api/DownloadControllerTest.php
+++ b/tests/Http/Api/DownloadControllerTest.php
@@ -4,6 +4,7 @@
use Code16\Sharp\Utils\Entities\SharpEntityManager;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
+use Illuminate\Support\Facades\URL;
beforeEach(function () {
Storage::fake('local');
@@ -17,7 +18,7 @@
$this
->get(
- route('code16.sharp.download.show', [
+ URL::temporarySignedRoute('code16.sharp.download.show', now()->addMinutes(120), [
'entityKey' => 'person',
'instanceId' => 1,
'disk' => 'local',
@@ -35,7 +36,7 @@
$this
->get(
- route('code16.sharp.download.show', [
+ URL::temporarySignedRoute('code16.sharp.download.show', now()->addMinutes(120), [
'entityKey' => 'person',
'instanceId' => 1,
'disk' => 'local',
@@ -50,7 +51,7 @@
it('returns a 404 for a missing file', function () {
$this
->get(
- route('code16.sharp.download.show', [
+ URL::temporarySignedRoute('code16.sharp.download.show', now()->addMinutes(120), [
'entityKey' => 'person',
'instanceId' => 1,
'disk' => 'local',
@@ -60,6 +61,19 @@
->assertNotFound();
});
+it('returns a 401 for an invalid signature', function () {
+ $this
+ ->get(
+ route('code16.sharp.download.show', [
+ 'entityKey' => 'person',
+ 'instanceId' => 1,
+ 'disk' => 'local',
+ 'path' => '/files/test.jpg',
+ ]),
+ )
+ ->assertStatus(401);
+});
+
it('does not allow to download a file without authorization', function () {
app(SharpEntityManager::class)
->entityFor('person')
@@ -67,7 +81,7 @@
$this
->get(
- route('code16.sharp.download.show', [
+ URL::temporarySignedRoute('code16.sharp.download.show', now()->addMinutes(120), [
'entityKey' => 'person',
'instanceId' => 1,
'disk' => 'local',
@@ -85,7 +99,7 @@
$this
->get(
- route('code16.sharp.download.show', [
+ URL::temporarySignedRoute('code16.sharp.download.show', now()->addMinutes(120), [
'entityKey' => 'person',
'instanceId' => 1,
'disk' => 'local',
@@ -103,7 +117,7 @@
$this
->get(
- route('code16.sharp.download.show', [
+ URL::temporarySignedRoute('code16.sharp.download.show', now()->addMinutes(120), [
'entityKey' => 'person',
'instanceId' => 1,
'disk' => 'local',
diff --git a/tests/Unit/Form/Eloquent/Uploads/Transformers/SharpUploadModelFormAttributeTransformerTest.php b/tests/Unit/Form/Eloquent/Uploads/Transformers/SharpUploadModelFormAttributeTransformerTest.php
index c2e3c5be8..73f194efc 100644
--- a/tests/Unit/Form/Eloquent/Uploads/Transformers/SharpUploadModelFormAttributeTransformerTest.php
+++ b/tests/Unit/Form/Eloquent/Uploads/Transformers/SharpUploadModelFormAttributeTransformerTest.php
@@ -3,9 +3,13 @@
use Code16\Sharp\Form\Eloquent\Uploads\SharpUploadModel;
use Code16\Sharp\Form\Eloquent\Uploads\Transformers\SharpUploadModelFormAttributeTransformer;
use Code16\Sharp\Tests\Unit\Form\Eloquent\Uploads\Transformers\Fakes\FakePicturable;
+use Code16\Sharp\Tests\Unit\Utils\FakesBreadcrumb;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Storage;
+use Illuminate\Support\Facades\URL;
+
+uses(FakesBreadcrumb::class);
beforeEach(function () {
// sharp()->config()->configureUploadsThumbnailCreation(
@@ -18,65 +22,51 @@
});
it('transforms a single upload', function () {
- $upload = new SharpUploadModel([
- 'file_name' => createImage(),
- 'size' => 120,
- 'mime_type' => 'image/png',
- 'disk' => 'local',
- ]);
- $picturable = new FakePicturable(['id' => 1]);
- $picturable->setRelation('picture', $upload);
-
- $transformer = new SharpUploadModelFormAttributeTransformer();
-
- $this->assertEquals(
- [
- 'id' => $upload->id,
- 'name' => basename($upload->file_name),
- 'path' => $upload->file_name,
- 'disk' => 'local',
- 'size' => $upload->size,
- 'thumbnail' => $upload->thumbnail(200, 200),
- 'playable_preview_url' => null,
+ $this->freezeTime(function (Carbon $time) {
+ $upload = new SharpUploadModel([
+ 'file_name' => createImage(),
+ 'size' => 120,
'mime_type' => 'image/png',
- ],
- $transformer->apply('', $picturable, 'picture'),
- );
+ 'disk' => 'local',
+ ]);
+ $picturable = new FakePicturable(['id' => 1]);
+ $picturable->setRelation('picture', $upload);
+
+ $this->fakeBreadcrumbWithUrl('/sharp/root/s-list/person/s-show/person/1');
+
+ $transformer = new SharpUploadModelFormAttributeTransformer();
+
+ expect($transformer->apply('', $picturable, 'picture'))
+ ->toEqual([
+ 'id' => $upload->id,
+ 'name' => basename($upload->file_name),
+ 'path' => $upload->file_name,
+ 'disk' => 'local',
+ 'size' => $upload->size,
+ 'thumbnail' => $upload->thumbnail(200, 200),
+ 'playable_preview_url' => null,
+ 'mime_type' => 'image/png',
+ 'download_url' => URL::temporarySignedRoute(
+ 'code16.sharp.download.show',
+ $time->copy()->addMinutes(config('session.lifetime')),
+ [
+ 'entityKey' => 'person',
+ 'instanceId' => '1',
+ 'disk' => 'local',
+ 'path' => $upload->file_name,
+ ]
+ ),
+ ]);
+ });
});
it('transforms a single upload with transformations', function () {
- $upload = new SharpUploadModel([
- 'file_name' => createImage(),
- 'size' => 120,
- 'mime_type' => 'image/png',
- 'disk' => 'local',
- 'filters' => [
- 'crop' => [
- 'height' => .5,
- 'width' => .75,
- 'x' => .3,
- 'y' => .34,
- ],
- 'rotate' => [
- 'angle' => 45,
- ],
- ],
- ]);
- $picturable = new FakePicturable(['id' => 1]);
- $picturable->setRelation('picture', $upload);
-
- $transformer = new SharpUploadModelFormAttributeTransformer();
-
- $this->assertEquals(
- [
- 'id' => $upload->id,
- 'name' => basename($upload->file_name),
- 'path' => $upload->file_name,
- 'disk' => 'local',
- 'size' => $upload->size,
+ $this->freezeTime(function (Carbon $time) {
+ $upload = new SharpUploadModel([
+ 'file_name' => createImage(),
+ 'size' => 120,
'mime_type' => 'image/png',
- 'thumbnail' => $upload->thumbnail(200, 200),
- 'playable_preview_url' => null,
+ 'disk' => 'local',
'filters' => [
'crop' => [
'height' => .5,
@@ -88,31 +78,71 @@
'angle' => 45,
],
],
- ],
- $transformer->apply('', $picturable, 'picture'),
- );
+ ]);
+ $picturable = new FakePicturable(['id' => 1]);
+ $picturable->setRelation('picture', $upload);
+
+ $this->fakeBreadcrumbWithUrl('/sharp/root/s-list/person/s-show/person/1');
+
+ $transformer = new SharpUploadModelFormAttributeTransformer();
+
+ expect($transformer->apply('', $picturable, 'picture'))
+ ->toEqual([
+ 'id' => $upload->id,
+ 'name' => basename($upload->file_name),
+ 'path' => $upload->file_name,
+ 'disk' => 'local',
+ 'size' => $upload->size,
+ 'mime_type' => 'image/png',
+ 'thumbnail' => $upload->thumbnail(200, 200),
+ 'playable_preview_url' => null,
+ 'filters' => [
+ 'crop' => [
+ 'height' => .5,
+ 'width' => .75,
+ 'x' => .3,
+ 'y' => .34,
+ ],
+ 'rotate' => [
+ 'angle' => 45,
+ ],
+ ],
+ 'download_url' => URL::temporarySignedRoute(
+ 'code16.sharp.download.show',
+ $time->copy()->addMinutes(config('session.lifetime')),
+ [
+ 'entityKey' => 'person',
+ 'instanceId' => '1',
+ 'disk' => 'local',
+ 'path' => $upload->file_name,
+ ]
+ ),
+ ]);
+ });
});
it('transforms a list of upload', function () {
- $upload1 = new SharpUploadModel([
- 'file_name' => createImage(),
- 'size' => 120,
- 'mime_type' => 'image/png',
- 'disk' => 'local',
- ]);
- $upload2 = new SharpUploadModel([
- 'file_name' => createImage(),
- 'size' => 120,
- 'mime_type' => 'image/png',
- 'disk' => 'local',
- ]);
- $picturable = new FakePicturable(['id' => 1]);
- $picturable->setRelation('pictures', collect([$upload1, $upload2]));
-
- $transformer = new SharpUploadModelFormAttributeTransformer();
-
- $this->assertEquals(
- [
+ $this->freezeTime(function (Carbon $time) {
+ $upload1 = new SharpUploadModel([
+ 'file_name' => createImage(),
+ 'size' => 120,
+ 'mime_type' => 'image/png',
+ 'disk' => 'local',
+ ]);
+ $upload2 = new SharpUploadModel([
+ 'file_name' => createImage(),
+ 'size' => 120,
+ 'mime_type' => 'image/png',
+ 'disk' => 'local',
+ ]);
+ $picturable = new FakePicturable(['id' => 1]);
+ $picturable->setRelation('pictures', collect([$upload1, $upload2]));
+
+ $this->fakeBreadcrumbWithUrl('/sharp/root/s-list/person/s-show/person/1');
+
+ $transformer = new SharpUploadModelFormAttributeTransformer();
+
+ expect($transformer->apply('', $picturable, 'pictures'))->toEqual([
[
'file' => [
'name' => basename($upload1->file_name),
@@ -122,9 +152,20 @@
'thumbnail' => $upload1->thumbnail(200, 200),
'playable_preview_url' => null,
'mime_type' => 'image/png',
+ 'download_url' => URL::temporarySignedRoute(
+ 'code16.sharp.download.show',
+ $time->copy()->addMinutes(config('session.lifetime')),
+ [
+ 'entityKey' => 'person',
+ 'instanceId' => '1',
+ 'disk' => 'local',
+ 'path' => $upload1->file_name,
+ ]
+ ),
],
- 'id' => $upload1->id,
- ], [
+ 'id' => null,
+ ],
+ [
'file' => [
'name' => basename($upload2->file_name),
'path' => $upload2->file_name,
@@ -133,47 +174,58 @@
'thumbnail' => $upload2->thumbnail(200, 200),
'playable_preview_url' => null,
'mime_type' => 'image/png',
+ 'download_url' => URL::temporarySignedRoute(
+ 'code16.sharp.download.show',
+ $time->copy()->addMinutes(config('session.lifetime')),
+ [
+ 'entityKey' => 'person',
+ 'instanceId' => '1',
+ 'disk' => 'local',
+ 'path' => $upload2->file_name,
+ ]
+ ),
],
- 'id' => $upload2->id,
+ 'id' => null,
],
- ],
- $transformer->apply('', $picturable, 'pictures'),
- );
+ ]);
+ });
});
it('transforms a list of upload with transformations', function () {
- $filters = [
- 'crop' => [
- 'height' => .5,
- 'width' => .75,
- 'x' => .3,
- 'y' => .34,
- ],
- 'rotate' => [
- 'angle' => 45,
- ],
- ];
-
- $upload1 = new SharpUploadModel([
- 'file_name' => createImage(),
- 'size' => 120,
- 'mime_type' => 'image/png',
- 'disk' => 'local',
- 'filters' => $filters,
- ]);
- $upload2 = new SharpUploadModel([
- 'file_name' => createImage(),
- 'size' => 120,
- 'mime_type' => 'image/png',
- 'disk' => 'local',
- ]);
- $picturable = new FakePicturable(['id' => 1]);
- $picturable->setRelation('pictures', collect([$upload1, $upload2]));
-
- $transformer = new SharpUploadModelFormAttributeTransformer();
-
- expect($transformer->apply('', $picturable, 'pictures'))
- ->toEqual([
+ $this->freezeTime(function (Carbon $time) {
+ $filters = [
+ 'crop' => [
+ 'height' => .5,
+ 'width' => .75,
+ 'x' => .3,
+ 'y' => .34,
+ ],
+ 'rotate' => [
+ 'angle' => 45,
+ ],
+ ];
+
+ $upload1 = new SharpUploadModel([
+ 'file_name' => createImage(),
+ 'size' => 120,
+ 'mime_type' => 'image/png',
+ 'disk' => 'local',
+ 'filters' => $filters,
+ ]);
+ $upload2 = new SharpUploadModel([
+ 'file_name' => createImage(),
+ 'size' => 120,
+ 'mime_type' => 'image/png',
+ 'disk' => 'local',
+ ]);
+ $picturable = new FakePicturable(['id' => 1]);
+ $picturable->setRelation('pictures', collect([$upload1, $upload2]));
+
+ $this->fakeBreadcrumbWithUrl('/sharp/root/s-list/person/s-show/person/1');
+
+ $transformer = new SharpUploadModelFormAttributeTransformer();
+
+ expect($transformer->apply('', $picturable, 'pictures'))->toEqual([
[
'file' => [
'name' => basename($upload1->file_name),
@@ -184,9 +236,20 @@
'playable_preview_url' => null,
'filters' => $filters,
'mime_type' => 'image/png',
+ 'download_url' => URL::temporarySignedRoute(
+ 'code16.sharp.download.show',
+ $time->copy()->addMinutes(config('session.lifetime')),
+ [
+ 'entityKey' => 'person',
+ 'instanceId' => '1',
+ 'disk' => 'local',
+ 'path' => $upload1->file_name,
+ ]
+ ),
],
- 'id' => $upload1->id,
- ], [
+ 'id' => null,
+ ],
+ [
'file' => [
'name' => basename($upload2->file_name),
'path' => $upload2->file_name,
@@ -195,16 +258,29 @@
'thumbnail' => $upload2->thumbnail(200, 200),
'playable_preview_url' => null,
'mime_type' => 'image/png',
+ 'download_url' => URL::temporarySignedRoute(
+ 'code16.sharp.download.show',
+ $time->copy()->addMinutes(config('session.lifetime')),
+ [
+ 'entityKey' => 'person',
+ 'instanceId' => '1',
+ 'disk' => 'local',
+ 'path' => $upload2->file_name,
+ ]
+ ),
],
- 'id' => $upload2->id,
+ 'id' => null,
],
]);
+ });
});
it('transforms an upload with playable preview', function () {
$this->freezeTime(function (Carbon $time) {
+ $file = UploadedFile::fake()->create('video.mp4', 120, 'video/mp4');
+ $path = $file->storeAs('data', 'video.mp4', ['disk' => 'local']);
$upload = new SharpUploadModel([
- 'file_name' => UploadedFile::fake()->create('video.mp4', 120, 'video/mp4'),
+ 'file_name' => $path,
'size' => 120,
'mime_type' => 'video/mp4',
'disk' => 'local',
@@ -212,6 +288,8 @@
$picturable = new FakePicturable(['id' => 1]);
$picturable->setRelation('video', $upload);
+ $this->fakeBreadcrumbWithUrl('/sharp/root/s-list/person/s-show/person/1');
+
$transformer = new SharpUploadModelFormAttributeTransformer(withPlayablePreview: true);
expect($transformer->apply('', $picturable, 'video'))
@@ -222,16 +300,28 @@
'disk' => 'local',
'size' => $upload->size,
'thumbnail' => null,
- 'playable_preview_url' => $upload->file_name.'?expiration='.$time->addMinutes(30)->timestamp,
+ 'playable_preview_url' => '/'.$upload->file_name.'?expiration='.$time->copy()->addMinutes(30)->timestamp,
'mime_type' => 'video/mp4',
+ 'download_url' => URL::temporarySignedRoute(
+ 'code16.sharp.download.show',
+ $time->copy()->addMinutes(config('session.lifetime')),
+ [
+ 'entityKey' => 'person',
+ 'instanceId' => '1',
+ 'disk' => 'local',
+ 'path' => $upload->file_name,
+ ]
+ ),
]);
});
});
it('transforms a list of upload with playable preview', function () {
$this->freezeTime(function (Carbon $time) {
+ $file = UploadedFile::fake()->create('audio.mp3', 120, 'audio/mp3');
+ $path = $file->storeAs('data', 'audio.mp3', ['disk' => 'local']);
$upload1 = new SharpUploadModel([
- 'file_name' => UploadedFile::fake()->create('audio.mp3', 120, 'audio/mp3'),
+ 'file_name' => $path,
'size' => 120,
'mime_type' => 'audio/mp3',
'disk' => 'local',
@@ -239,6 +329,8 @@
$picturable = new FakePicturable(['id' => 1]);
$picturable->setRelation('songs', collect([$upload1]));
+ $this->fakeBreadcrumbWithUrl('/sharp/root/s-list/person/s-show/person/1');
+
$transformer = new SharpUploadModelFormAttributeTransformer(withPlayablePreview: true);
expect($transformer->apply('', $picturable, 'songs'))->toEqual([
@@ -249,8 +341,18 @@
'disk' => 'local',
'size' => $upload1->size,
'thumbnail' => null,
- 'playable_preview_url' => $upload1->file_name.'?expiration='.$time->addMinutes(30)->timestamp,
+ 'playable_preview_url' => '/'.$upload1->file_name.'?expiration='.$time->copy()->addMinutes(30)->timestamp,
'mime_type' => 'audio/mp3',
+ 'download_url' => URL::temporarySignedRoute(
+ 'code16.sharp.download.show',
+ $time->copy()->addMinutes(config('session.lifetime')),
+ [
+ 'entityKey' => 'person',
+ 'instanceId' => '1',
+ 'disk' => 'local',
+ 'path' => $upload1->file_name,
+ ]
+ ),
],
'id' => $upload1->id,
],
@@ -260,22 +362,24 @@
describe('dynamicInstance', function () {
it('allows to fake a sharpUpload and transform a single upload', function () {
- $file = createImage();
+ $this->freezeTime(function (Carbon $time) {
+ $file = createImage();
- $uploadData = [
- 'file_name' => $file,
- 'size' => 120,
- 'disk' => 'local',
- 'filters' => [],
- 'mime_type' => 'image/png',
- 'width' => 100,
- 'height' => 100,
- ];
+ $uploadData = [
+ 'file_name' => $file,
+ 'size' => 120,
+ 'disk' => 'local',
+ 'filters' => [],
+ 'mime_type' => 'image/png',
+ 'width' => 100,
+ 'height' => 100,
+ ];
- $transformer = (new SharpUploadModelFormAttributeTransformer())->dynamicInstance();
+ $this->fakeBreadcrumbWithUrl('/sharp/root/s-list/person/s-show/person/1');
- $this->assertEquals(
- [
+ $transformer = (new SharpUploadModelFormAttributeTransformer())->dynamicInstance();
+
+ expect($transformer->apply($uploadData, null, 'picture'))->toEqual([
'id' => null,
'name' => basename($file),
'path' => $file,
@@ -287,9 +391,18 @@
'mime_type' => 'image/png',
'width' => 100,
'height' => 100,
- ],
- $transformer->apply($uploadData, null, 'picture'),
- );
+ 'download_url' => URL::temporarySignedRoute(
+ 'code16.sharp.download.show',
+ $time->copy()->addMinutes(config('session.lifetime')),
+ [
+ 'entityKey' => 'person',
+ 'instanceId' => '1',
+ 'disk' => 'local',
+ 'path' => $file,
+ ]
+ ),
+ ]);
+ });
});
it('sends "uploaded" and "transformed" attributes if present', function () {
diff --git a/tests/Unit/Form/Fields/Formatters/EditorFormatterTest.php b/tests/Unit/Form/Fields/Formatters/EditorFormatterTest.php
index ab7cf5a58..03b258fac 100644
--- a/tests/Unit/Form/Fields/Formatters/EditorFormatterTest.php
+++ b/tests/Unit/Form/Fields/Formatters/EditorFormatterTest.php
@@ -5,10 +5,14 @@
use Code16\Sharp\Form\Fields\Formatters\EditorFormatter;
use Code16\Sharp\Form\Fields\SharpFormEditorField;
use Code16\Sharp\Tests\Unit\Form\Fields\Formatters\Fixtures\EditorFormatterTestEmbed;
+use Code16\Sharp\Tests\Unit\Utils\FakesBreadcrumb;
use Illuminate\Http\UploadedFile;
+use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
+uses(FakesBreadcrumb::class);
+
beforeEach(function () {
Storage::fake('local');
Storage::fake('public');
@@ -119,74 +123,98 @@
});
it('allows to format a text with uploads to front', function () {
- $formatter = new EditorFormatter();
- $field = SharpFormEditorField::make('md')
- ->allowUploads(SharpFormEditorUpload::make());
-
- UploadedFile::fake()->image('test.jpg', 600, 600)
- ->storeAs('data/Posts/1', 'image.jpg', ['disk' => 'local']);
- UploadedFile::fake()->create('doc.pdf')
- ->storeAs('data/Posts/1', 'doc.pdf', ['disk' => 'local']);
-
- $value = sprintf('',
- e(json_encode([
- 'file_name' => 'data/Posts/1/image.jpg',
- 'size' => 120,
- 'mime_type' => 'image/jpeg',
- 'disk' => 'local',
- 'width' => 600,
- 'height' => 600,
- ])),
- e(json_encode([
- 'file_name' => 'data/Posts/1/doc.pdf',
- 'size' => 120,
- 'mime_type' => 'application/pdf',
- 'disk' => 'local',
- ]))
- );
-
- expect($formatter->toFront($field, $value))->toEqual([
- 'text' => '',
- 'uploads' => (object) [
- '0' => [
- 'file' => [
- 'name' => 'image.jpg',
- 'path' => 'data/Posts/1/image.jpg',
- 'disk' => 'local',
- 'thumbnail' => sprintf(
- '/storage/thumbnails/data/Posts/1/200-200_q-90/image.jpg?%s',
- Storage::disk('public')->lastModified('/thumbnails/data/Posts/1/200-200_q-90/image.jpg')
- ),
- 'playable_preview_url' => null,
- 'size' => 120,
- 'mime_type' => 'image/jpeg',
- 'filters' => null,
- 'id' => null,
- 'width' => 600,
- 'height' => 600,
+ $this->freezeTime(function (Carbon $time) {
+ $formatter = new EditorFormatter();
+ $field = SharpFormEditorField::make('md')
+ ->allowUploads(SharpFormEditorUpload::make());
+
+ UploadedFile::fake()->image('test.jpg', 600, 600)
+ ->storeAs('data/Posts/1', 'image.jpg', ['disk' => 'local']);
+ UploadedFile::fake()->create('doc.pdf')
+ ->storeAs('data/Posts/1', 'doc.pdf', ['disk' => 'local']);
+
+ $value = sprintf('',
+ e(json_encode([
+ 'file_name' => 'data/Posts/1/image.jpg',
+ 'size' => 120,
+ 'mime_type' => 'image/jpeg',
+ 'disk' => 'local',
+ 'width' => 600,
+ 'height' => 600,
+ ])),
+ e(json_encode([
+ 'file_name' => 'data/Posts/1/doc.pdf',
+ 'size' => 120,
+ 'mime_type' => 'application/pdf',
+ 'disk' => 'local',
+ ]))
+ );
+
+ $this->fakeBreadcrumbWithUrl('/sharp/root/s-list/person/s-show/person/1/s-form/person/1');
+
+ expect($formatter->toFront($field, $value))->toEqual([
+ 'text' => '',
+ 'uploads' => (object) [
+ '0' => [
+ 'file' => [
+ 'name' => 'image.jpg',
+ 'path' => 'data/Posts/1/image.jpg',
+ 'disk' => 'local',
+ 'thumbnail' => sprintf(
+ '/storage/thumbnails/data/Posts/1/200-200_q-90/image.jpg?%s',
+ Storage::disk('public')->lastModified('/thumbnails/data/Posts/1/200-200_q-90/image.jpg')
+ ),
+ 'playable_preview_url' => null,
+ 'download_url' => URL::temporarySignedRoute(
+ 'code16.sharp.download.show',
+ $time->copy()->addMinutes(config('session.lifetime')),
+ [
+ 'entityKey' => 'person',
+ 'instanceId' => '1',
+ 'disk' => 'local',
+ 'path' => 'data/Posts/1/image.jpg',
+ ]
+ ),
+ 'size' => 120,
+ 'mime_type' => 'image/jpeg',
+ 'filters' => null,
+ 'id' => null,
+ 'width' => 600,
+ 'height' => 600,
+ ],
+ 'legend' => 'Legendary',
+ '_locale' => null,
],
- 'legend' => 'Legendary',
- '_locale' => null,
- ],
- [
- 'file' => [
- 'name' => 'doc.pdf',
- 'path' => 'data/Posts/1/doc.pdf',
- 'disk' => 'local',
- 'thumbnail' => null,
- 'playable_preview_url' => null,
- 'size' => 120,
- 'mime_type' => 'application/pdf',
- 'filters' => null,
- 'id' => null,
- 'width' => null,
- 'height' => null,
+ [
+ 'file' => [
+ 'name' => 'doc.pdf',
+ 'path' => 'data/Posts/1/doc.pdf',
+ 'disk' => 'local',
+ 'thumbnail' => null,
+ 'playable_preview_url' => null,
+ 'download_url' => URL::temporarySignedRoute(
+ 'code16.sharp.download.show',
+ $time->copy()->addMinutes(config('session.lifetime')),
+ [
+ 'entityKey' => 'person',
+ 'instanceId' => '1',
+ 'disk' => 'local',
+ 'path' => 'data/Posts/1/doc.pdf',
+ ]
+ ),
+ 'size' => 120,
+ 'mime_type' => 'application/pdf',
+ 'filters' => null,
+ 'id' => null,
+ 'width' => null,
+ 'height' => null,
+ ],
+ 'legend' => null,
+ '_locale' => null,
],
- 'legend' => null,
- '_locale' => null,
],
- ],
- ]);
+ ]);
+ });
});
it('allows to format text with uploads from front', function () {
@@ -262,60 +290,74 @@
});
it('allows to format embeds with uploads to front', function () {
- $formatter = (new EditorFormatter())->setInstanceId(1);
- $field = SharpFormEditorField::make('md')
- ->allowEmbeds([EditorFormatterTestEmbed::class]);
+ $this->freezeTime(function (Carbon $time) {
+ $formatter = (new EditorFormatter())->setInstanceId(1);
+ $field = SharpFormEditorField::make('md')
+ ->allowEmbeds([EditorFormatterTestEmbed::class]);
- $image = UploadedFile::fake()->image('test.jpg', 600, 600);
- $image->storeAs('data/Posts/1', 'image.jpg', ['disk' => 'local']);
+ $image = UploadedFile::fake()->image('test.jpg', 600, 600);
+ $image->storeAs('data/Posts/1', 'image.jpg', ['disk' => 'local']);
- $value = sprintf(<<<'HTML'
+ $value = sprintf(<<<'HTML'
My contentful content
HTML,
- e(json_encode([
- 'file_name' => 'data/Posts/1/image.jpg',
- 'size' => 120,
- 'mime_type' => 'image/jpeg',
- 'disk' => 'local',
- 'width' => 600,
- 'height' => 600,
- ]))
- );
-
- $data = $formatter->toFront($field, $value);
- $thumbnail = sprintf(
- '/storage/thumbnails/data/Posts/1/200-200_q-90/image.jpg?%s',
- Storage::disk('public')->lastModified('/thumbnails/data/Posts/1/200-200_q-90/image.jpg')
- );
-
- expect($data)->toEqual([
- 'text' => <<<'HTML'
+ e(json_encode([
+ 'file_name' => 'data/Posts/1/image.jpg',
+ 'size' => 120,
+ 'mime_type' => 'image/jpeg',
+ 'disk' => 'local',
+ 'width' => 600,
+ 'height' => 600,
+ ]))
+ );
+
+ $this->fakeBreadcrumbWithUrl('/sharp/root/s-list/person/s-show/person/1/s-form/person/1');
+
+ $data = $formatter->toFront($field, $value);
+ $thumbnail = sprintf(
+ '/storage/thumbnails/data/Posts/1/200-200_q-90/image.jpg?%s',
+ Storage::disk('public')->lastModified('/thumbnails/data/Posts/1/200-200_q-90/image.jpg')
+ );
+
+ expect($data)->toEqual([
+ 'text' => <<<'HTML'
HTML,
- 'embeds' => [
- (new EditorFormatterTestEmbed())->key() => (object) [
- '0' => [
- 'slot' => 'My contentful content',
- 'visual' => [
- 'name' => 'image.jpg',
- 'path' => 'data/Posts/1/image.jpg',
- 'disk' => 'local',
- 'thumbnail' => $thumbnail,
- 'playable_preview_url' => null,
- 'size' => 120,
- 'mime_type' => 'image/jpeg',
- 'filters' => null,
- 'id' => null,
- 'width' => 600,
- 'height' => 600,
+ 'embeds' => [
+ (new EditorFormatterTestEmbed())->key() => (object) [
+ '0' => [
+ 'slot' => 'My contentful content',
+ 'visual' => [
+ 'name' => 'image.jpg',
+ 'path' => 'data/Posts/1/image.jpg',
+ 'disk' => 'local',
+ 'thumbnail' => $thumbnail,
+ 'playable_preview_url' => null,
+ 'download_url' => URL::temporarySignedRoute(
+ 'code16.sharp.download.show',
+ $time->copy()->addMinutes(config('session.lifetime')),
+ [
+ 'entityKey' => 'person',
+ 'instanceId' => '1',
+ 'disk' => 'local',
+ 'path' => 'data/Posts/1/image.jpg',
+ ]
+ ),
+ 'size' => 120,
+ 'mime_type' => 'image/jpeg',
+ 'filters' => null,
+ 'id' => null,
+ 'width' => 600,
+ 'height' => 600,
+ ],
+ '_html' => sprintf('
My contentful content',
+ $thumbnail,
+ ),
],
- '_html' => sprintf('
My contentful content',
- $thumbnail,
- ),
],
],
- ],
- ]);
+ ]);
+ });
});
it('allows to format embeds with uploads from front', function () {
diff --git a/tests/Unit/Show/Fields/Formatters/TextFormatterTest.php b/tests/Unit/Show/Fields/Formatters/TextFormatterTest.php
index 094b94a74..8bc27aba5 100644
--- a/tests/Unit/Show/Fields/Formatters/TextFormatterTest.php
+++ b/tests/Unit/Show/Fields/Formatters/TextFormatterTest.php
@@ -3,10 +3,14 @@
use Code16\Sharp\Show\Fields\Formatters\TextFormatter;
use Code16\Sharp\Show\Fields\SharpShowTextField;
use Code16\Sharp\Tests\Unit\Form\Fields\Formatters\Fixtures\EditorFormatterTestEmbed;
+use Code16\Sharp\Tests\Unit\Utils\FakesBreadcrumb;
use Illuminate\Http\UploadedFile;
+use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
+uses(FakesBreadcrumb::class);
+
beforeEach(function () {
Storage::fake('local');
Storage::fake('public');
@@ -26,73 +30,97 @@
});
it('allows to format a text with uploads to front', function () {
- $formatter = new TextFormatter();
- $field = SharpShowTextField::make('md');
+ $this->freezeTime(function (Carbon $time) {
+ $formatter = new TextFormatter();
+ $field = SharpShowTextField::make('md');
- $image = UploadedFile::fake()->image('test.jpg', 600, 600);
- $image->storeAs('data/Posts/1', 'image.jpg', ['disk' => 'local']);
- $file = UploadedFile::fake()->create('doc.pdf');
- $file->storeAs('data/Posts/1', 'doc.pdf', ['disk' => 'local']);
+ $image = UploadedFile::fake()->image('test.jpg', 600, 600);
+ $image->storeAs('data/Posts/1', 'image.jpg', ['disk' => 'local']);
+ $file = UploadedFile::fake()->create('doc.pdf');
+ $file->storeAs('data/Posts/1', 'doc.pdf', ['disk' => 'local']);
- $value = sprintf('',
- e(json_encode([
- 'file_name' => 'data/Posts/1/image.jpg',
- 'size' => 120,
- 'mime_type' => 'image/jpeg',
- 'disk' => 'local',
- 'width' => 600,
- 'height' => 600,
- ])),
- e(json_encode([
- 'file_name' => 'data/Posts/1/doc.pdf',
- 'size' => 120,
- 'mime_type' => 'application/pdf',
- 'disk' => 'local',
- ]))
- );
+ $value = sprintf('',
+ e(json_encode([
+ 'file_name' => 'data/Posts/1/image.jpg',
+ 'size' => 120,
+ 'mime_type' => 'image/jpeg',
+ 'disk' => 'local',
+ 'width' => 600,
+ 'height' => 600,
+ ])),
+ e(json_encode([
+ 'file_name' => 'data/Posts/1/doc.pdf',
+ 'size' => 120,
+ 'mime_type' => 'application/pdf',
+ 'disk' => 'local',
+ ]))
+ );
+
+ $this->fakeBreadcrumbWithUrl('/sharp/root/s-list/person/s-show/person/1');
- expect($formatter->toFront($field, $value))->toEqual([
- 'text' => '',
- 'uploads' => (object) [
- '0' => [
- 'file' => [
- 'name' => 'image.jpg',
- 'path' => 'data/Posts/1/image.jpg',
- 'disk' => 'local',
- 'thumbnail' => sprintf(
- '/storage/thumbnails/data/Posts/1/200-200_q-90/image.jpg?%s',
- Storage::disk('public')->lastModified('/thumbnails/data/Posts/1/200-200_q-90/image.jpg')
- ),
- 'playable_preview_url' => null,
- 'size' => 120,
- 'mime_type' => 'image/jpeg',
- 'filters' => null,
- 'id' => null,
- 'width' => 600,
- 'height' => 600,
+ expect($formatter->toFront($field, $value))->toEqual([
+ 'text' => '',
+ 'uploads' => (object) [
+ '0' => [
+ 'file' => [
+ 'name' => 'image.jpg',
+ 'path' => 'data/Posts/1/image.jpg',
+ 'disk' => 'local',
+ 'thumbnail' => sprintf(
+ '/storage/thumbnails/data/Posts/1/200-200_q-90/image.jpg?%s',
+ Storage::disk('public')->lastModified('/thumbnails/data/Posts/1/200-200_q-90/image.jpg')
+ ),
+ 'playable_preview_url' => null,
+ 'download_url' => URL::temporarySignedRoute(
+ 'code16.sharp.download.show',
+ $time->copy()->addMinutes(config('session.lifetime')),
+ [
+ 'entityKey' => 'person',
+ 'instanceId' => '1',
+ 'disk' => 'local',
+ 'path' => 'data/Posts/1/image.jpg',
+ ]
+ ),
+ 'size' => 120,
+ 'mime_type' => 'image/jpeg',
+ 'filters' => null,
+ 'id' => null,
+ 'width' => 600,
+ 'height' => 600,
+ ],
+ 'legend' => 'Legendary',
+ '_locale' => null,
],
- 'legend' => 'Legendary',
- '_locale' => null,
- ],
- '1' => [
- 'file' => [
- 'name' => 'doc.pdf',
- 'path' => 'data/Posts/1/doc.pdf',
- 'disk' => 'local',
- 'thumbnail' => null,
- 'playable_preview_url' => null,
- 'size' => 120,
- 'mime_type' => 'application/pdf',
- 'filters' => null,
- 'id' => null,
- 'width' => null,
- 'height' => null,
+ '1' => [
+ 'file' => [
+ 'name' => 'doc.pdf',
+ 'path' => 'data/Posts/1/doc.pdf',
+ 'disk' => 'local',
+ 'thumbnail' => null,
+ 'playable_preview_url' => null,
+ 'download_url' => URL::temporarySignedRoute(
+ 'code16.sharp.download.show',
+ $time->copy()->addMinutes(config('session.lifetime')),
+ [
+ 'entityKey' => 'person',
+ 'instanceId' => '1',
+ 'disk' => 'local',
+ 'path' => 'data/Posts/1/doc.pdf',
+ ]
+ ),
+ 'size' => 120,
+ 'mime_type' => 'application/pdf',
+ 'filters' => null,
+ 'id' => null,
+ 'width' => null,
+ 'height' => null,
+ ],
+ 'legend' => null,
+ '_locale' => null,
],
- 'legend' => null,
- '_locale' => null,
],
- ],
- ]);
+ ]);
+ });
});
it('allows to format embeds with uploads to front', function () {