Skip to content

Commit

Permalink
feat: add MediaCollection enum and implement media download permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentauger committed Nov 28, 2024
1 parent 550afd7 commit 0a1e7a8
Show file tree
Hide file tree
Showing 18 changed files with 330 additions and 73 deletions.
10 changes: 10 additions & 0 deletions app/Enums/MediaCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace App\Enums;

enum MediaCollection: string
{
case MANUSCRIPT = 'manuscript';
case SUPPLEMENTARY_FILE = 'supplementary_file';
case PUBLICATION = 'publication';
}
20 changes: 7 additions & 13 deletions app/Enums/SupplementaryFileType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,11 @@

enum SupplementaryFileType: string
{
const MANUSCRIPT_RECORD_FORM = 'manuscript_record_form';

const AUTHOR_AGREEMENT = 'author_agreement';

const JOINT_COPYRIGHT_AGREEMENT = 'joint_copyright_agreement';

const PREPRINT = 'preprint';

const AUTHORS_ACCEPTED_MANUSCRIPT = 'authors_accepted_manuscript';

const ERRATA = 'errata';

const OTHER = 'other';
case MANUSCRIPT_RECORD_FORM = 'manuscript_record_form';
case AUTHOR_AGREEMENT = 'author_agreement';
case JOINT_COPYRIGHT_AGREEMENT = 'joint_copyright_agreement';
case PREPRINT = 'preprint';
case AUTHORS_ACCEPTED_MANUSCRIPT = 'authors_accepted_manuscript';
case ERRATA = 'errata';
case OTHER = 'other';
}
4 changes: 4 additions & 0 deletions app/Http/Controllers/ManuscriptRecordFileController.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ public function index(Request $request, ManuscriptRecord $manuscriptRecord)

$media = $manuscriptRecord->getManuscriptFiles();

// get manuscript model and all required relations to avoid n+1
$manuscriptRecord->load('manuscriptAuthors.author', 'managementReviewSteps', 'shareables');
$media->each(fn ($media) => $media->setRelation('model', $manuscriptRecord));

return MediaResource::collection($media->sortBy('created_at', SORT_REGULAR, true));
}

Expand Down
15 changes: 13 additions & 2 deletions app/Http/Controllers/PublicationFileController.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ public function index(Publication $publication)

$media = $publication->getMedia('publication');

// get publication model and all required relations to avoid n+1
$publication->load([
'manuscriptRecord' => [
'manuscriptAuthors.author',
'managementReviewSteps',
'shareables',
],
'publicationAuthors.author',
]);
$media->each(fn ($media) => $media->setRelation('model', $publication));

return MediaResource::collection($media->sortBy('created_at', SORT_REGULAR, true));
}

Expand Down Expand Up @@ -53,10 +64,10 @@ public function show(Request $request, Publication $publication, string $uuid)
{
Gate::authorize('view', $publication);

$media = $publication->getMedia('publication')->where('uuid', $uuid)->first();
$media = $publication->getPublicationFile($uuid);

$download = $request->query('download', false);
if ($download && Gate::allows('viewPdf', $publication)) {
if ($download && Gate::allows('downloadMedia', [$publication,$media])) {
return $media;
}

Expand Down
88 changes: 88 additions & 0 deletions app/Http/Controllers/PublicationSupplementaryFileController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

namespace App\Http\Controllers;

use App\Enums\SupplementaryFileType;
use App\Http\Resources\MediaResource;
use App\Models\Publication;
use Exception;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class PublicationSupplementaryFileController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Publication $publication)
{
Gate::authorize('view', $publication);

$media = $publication->getMedia('supplementary_file');

return MediaResource::collection($media->sortBy('created_at', SORT_REGULAR, true));
}

/**
* Store a newly created resource in storage.
*/
public function store(Request $request, Publication $publication)
{
Gate::authorize('update', $publication);

$validated = $request->validate([
'pdf' => 'required|file|mimes:pdf|max:50000',
'supplementary_file_type' => ['required', Rule::enum(SupplementaryFileType::class)],
'description' => 'nullable|string',
]);

$media = $publication->addSupplementaryFile($validated['pdf'], $validated['supplementary_file_type'], $validated['description'] ?? null);

activity()
->performedOn($publication)
->withProperties($media->toArray())
->causedBy($request->user())
->log('publication.file.uploaded');

return new MediaResource($media);
}

/**
* Display the specified resource.
*/
public function show(Request $request, Publication $publication, string $uuid)
{
Gate::authorize('view', $publication);

$media = $publication->getSupplementaryFile($uuid);

$download = $request->query('download', false);
if ($download && Gate::allow('viewSupplementaryPdf', $publication, $media)) {
return $media;
}

return MediaResource::make($media);

}

/**
* Remove the specified resource from storage.
*/
public function destroy(Publication $publication, string $uuid)
{
Gate::authorize('update', $publication);

try {
$publication->deleteSupplementaryFile($uuid);
} catch (FileNotFoundException $e) {
throw new NotFoundHttpException('File not found.');
} catch (Exception $e) {
return response()->json([
'message' => 'This file is locked.',
], 403);
}
}
}
28 changes: 18 additions & 10 deletions app/Http/Resources/MediaResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Enums\SensitivityLabel;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Gate;

class MediaResource extends JsonResource
{
Expand All @@ -16,16 +17,23 @@ class MediaResource extends JsonResource
public function toArray($request)
{
return [
'uuid' => $this->uuid,
'file_name' => $this->file_name,
'size_bytes' => $this->size,
'created_at' => $this->created_at,
'collection_name' => $this->collection_name,
'mime_type' => $this->mime_type,
'locked' => $this->getCustomProperty('locked', false),
'sensitivity_label' => $this->getCustomProperty('sensitivity_label', SensitivityLabel::Unclassified),
'supplementary_file_type' => $this->when($this->hasCustomProperty('supplementary_file_type'), $this->getCustomProperty('supplementary_file_type')),
'description' => $this->when($this->hasCustomProperty('description'), $this->getCustomProperty('description')),
'data' => [
'uuid' => $this->uuid,
'file_name' => $this->file_name,
'size_bytes' => $this->size,
'created_at' => $this->created_at,
'collection_name' => $this->collection_name,
'mime_type' => $this->mime_type,
'locked' => $this->getCustomProperty('locked', false),
'sensitivity_label' => $this->getCustomProperty('sensitivity_label', SensitivityLabel::Unclassified),
'supplementary_file_type' => $this->when($this->hasCustomProperty('supplementary_file_type'), $this->getCustomProperty('supplementary_file_type')),
'description' => $this->when($this->hasCustomProperty('description'), $this->getCustomProperty('description')),
],
'can' => [
'update' => Gate::allows('update', $this->resource),
'delete' => Gate::allows('delete', $this->resource),
'download' => Gate::allows('download', $this->resource),
],
];
}
}
16 changes: 9 additions & 7 deletions app/Models/ManuscriptRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Contracts\Fundable;
use App\Enums\ManuscriptRecordStatus;
use App\Enums\ManuscriptRecordType;
use App\Enums\MediaCollection;
use App\Enums\SensitivityLabel;
use App\Models\Concerns\HasUlid;
use App\Traits\FundableTrait;
Expand All @@ -18,6 +19,7 @@
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Validator;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Traits\LogsActivity;
use Spatie\MediaLibrary\HasMedia;
Expand Down Expand Up @@ -153,7 +155,7 @@ public function publication(): HasOne
*/
public function registerMediaCollections(): void
{
$this->addMediaCollection('manuscript')
$this->addMediaCollection(MediaCollection::MANUSCRIPT->value)
->acceptsMimeTypes(['application/pdf']);
}

Expand All @@ -162,15 +164,15 @@ public function registerMediaCollections(): void
*/
public function getLastManuscriptFile()
{
return $this->getMedia('manuscript')->last();
return $this->getMedia(MediaCollection::MANUSCRIPT->value)->last();
}

/**
* Get manuscript files media model.
*/
public function getManuscriptFiles()
{
return $this->getMedia('manuscript');
return $this->getMedia(MediaCollection::MANUSCRIPT->value);
}

/**
Expand All @@ -186,21 +188,21 @@ public function addManuscriptFile($file, $preserveOriginal = false)
'sensitivity_label' => SensitivityLabel::ProtectedA,
])
->preservingOriginal($preserveOriginal)
->toMediaCollection('manuscript');
->toMediaCollection(MediaCollection::MANUSCRIPT->value);

}

public function getManuscriptFile($uuid)
{
return $this->getMedia('manuscript')->where('uuid', $uuid)->first();
return $this->getMedia(MediaCollection::MANUSCRIPT->value)->where('uuid', $uuid)->first();
}

/**
* Delete a manuscript file
*/
public function deleteManuscriptFile($uuid, $force = false)
{
$media = $this->getMedia('manuscript')->where('uuid', $uuid)->first();
$media = $this->getMedia(MediaCollection::MANUSCRIPT->value)->where('uuid', $uuid)->first();
if (! $media) {
throw new FileNotFoundException('File not found.');
}
Expand Down Expand Up @@ -236,7 +238,7 @@ public function lockManuscriptFiles()
*/
public function validateIsFilled(bool $noExceptions = false): bool
{
$validator = \Validator::make($this->toArray(), [
$validator = Validator::make($this->toArray(), [
'title' => 'required',
'abstract' => 'required',
'pls' => 'required',
Expand Down
62 changes: 59 additions & 3 deletions app/Models/Publication.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
namespace App\Models;

use App\Contracts\Fundable;
use App\Enums\MediaCollection;
use App\Enums\PublicationStatus;
use App\Enums\SupplementaryFileType;
use App\Traits\FundableTrait;
use Exception;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
Expand All @@ -17,6 +19,7 @@
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Symfony\Component\HttpFoundation\File\UploadedFile;

class Publication extends Model implements Fundable, HasMedia
{
Expand Down Expand Up @@ -83,23 +86,29 @@ public function publicationAuthors(): HasMany
// media
public function registerMediaCollections(): void
{
$this->addMediaCollection('publication')
$this->addMediaCollection(MediaCollection::PUBLICATION->value)
->acceptsMimeTypes(['application/pdf']);

$this->addMediaCollection(MediaCollection::SUPPLEMENTARY_FILE->value)
->acceptsMimeTypes(['application/pdf']);
}

/**
* Add publication file media model.
*/
public function addPublicationFile($file, $preserveOriginal = false): Media
{
return $this->addMedia($file)
->preservingOriginal($preserveOriginal)
->toMediaCollection('publication');
->toMediaCollection(MediaCollection::PUBLICATION->value);
}

/**
* Get publication file media model.
*/
public function getPublicationFile($uuid)
{
return $this->getMedia('publication')->where('uuid', $uuid)->first();
return $this->getMedia(MediaCollection::PUBLICATION->value)->where('uuid', $uuid)->first();
}

/**
Expand All @@ -122,6 +131,53 @@ public function deletePublicationFile($uuid, $force = false): void
$media->delete();
}

/**
* Add supplementary file media model.
*/
public function addSupplementaryFile(string|UploadedFile $file, SupplementaryFileType $type, ?string $description = null, $preserveOriginal = false): Media
{

$properties = [
'supplementary_file_type' => $type->value,
];
if ($description) {
$properties['description'] = $description;
}

return $this->addMedia($file)
->preservingOriginal($preserveOriginal)
->withCustomProperties($properties)
->toMediaCollection(MediaCollection::SUPPLEMENTARY_FILE->value);
}

/**
* Get supplementary file media model.
*/
public function getSupplementaryFile($uuid)
{
return $this->getMedia(MediaCollection::SUPPLEMENTARY_FILE->value)->where('uuid', $uuid)->first();
}

/**
* Delete supplementary file media model.
*/
public function deleteSupplementaryFile($uuid, $force = false): void
{
$media = $this->getSupplementaryFile($uuid);
if (! $media) {
throw new FileNotFoundException('File not found.');
}
if ($force) {
$media->delete();

return;
}
if ($media->getCustomProperty('locked', false)) {
throw new Exception('Cannot delete locked file.');
}
$media->delete();
}

/** Is the publication under embargo? */
public function isUnderEmbargo(): bool
{
Expand Down
Loading

0 comments on commit 0a1e7a8

Please sign in to comment.