]> BookStack Code Mirror - bookstack/blob - app/Http/Controllers/Api/AttachmentApiController.php
Standardised laravel validation to be array based
[bookstack] / app / Http / Controllers / Api / AttachmentApiController.php
1 <?php
2
3 namespace BookStack\Http\Controllers\Api;
4
5 use BookStack\Entities\Models\Page;
6 use BookStack\Exceptions\FileUploadException;
7 use BookStack\Uploads\Attachment;
8 use BookStack\Uploads\AttachmentService;
9 use Exception;
10 use Illuminate\Contracts\Filesystem\FileNotFoundException;
11 use Illuminate\Http\Request;
12 use Illuminate\Validation\ValidationException;
13
14 class AttachmentApiController extends ApiController
15 {
16     protected $attachmentService;
17
18     protected $rules = [
19         'create' => [
20             'name'        => ['required', 'min:1', 'max:255', 'string'],
21             'uploaded_to' => ['required', 'integer', 'exists:pages,id'],
22             'file'        => ['required_without:link', 'file'],
23             'link'        => ['required_without:file', 'min:1', 'max:255', 'safe_url'],
24         ],
25         'update' => [
26             'name'        => ['min:1', 'max:255', 'string'],
27             'uploaded_to' => ['integer', 'exists:pages,id'],
28             'file'        => ['file'],
29             'link'        => ['min:1', 'max:255', 'safe_url'],
30         ],
31     ];
32
33     public function __construct(AttachmentService $attachmentService)
34     {
35         $this->attachmentService = $attachmentService;
36     }
37
38     /**
39      * Get a listing of attachments visible to the user.
40      * The external property indicates whether the attachment is simple a link.
41      * A false value for the external property would indicate a file upload.
42      */
43     public function list()
44     {
45         return $this->apiListingResponse(Attachment::visible(), [
46             'id', 'name', 'extension', 'uploaded_to', 'external', 'order', 'created_at', 'updated_at', 'created_by', 'updated_by',
47         ]);
48     }
49
50     /**
51      * Create a new attachment in the system.
52      * An uploaded_to value must be provided containing an ID of the page
53      * that this upload will be related to.
54      *
55      * If you're uploading a file the POST data should be provided via
56      * a multipart/form-data type request instead of JSON.
57      *
58      * @throws ValidationException
59      * @throws FileUploadException
60      */
61     public function create(Request $request)
62     {
63         $this->checkPermission('attachment-create-all');
64         $requestData = $this->validate($request, $this->rules['create']);
65
66         $pageId = $request->get('uploaded_to');
67         $page = Page::visible()->findOrFail($pageId);
68         $this->checkOwnablePermission('page-update', $page);
69
70         if ($request->hasFile('file')) {
71             $uploadedFile = $request->file('file');
72             $attachment = $this->attachmentService->saveNewUpload($uploadedFile, $page->id);
73         } else {
74             $attachment = $this->attachmentService->saveNewFromLink(
75                 $requestData['name'],
76                 $requestData['link'],
77                 $page->id
78             );
79         }
80
81         $this->attachmentService->updateFile($attachment, $requestData);
82
83         return response()->json($attachment);
84     }
85
86     /**
87      * Get the details & content of a single attachment of the given ID.
88      * The attachment link or file content is provided via a 'content' property.
89      * For files the content will be base64 encoded.
90      *
91      * @throws FileNotFoundException
92      */
93     public function read(string $id)
94     {
95         /** @var Attachment $attachment */
96         $attachment = Attachment::visible()
97             ->with(['createdBy', 'updatedBy'])
98             ->findOrFail($id);
99
100         $attachment->setAttribute('links', [
101             'html'     => $attachment->htmlLink(),
102             'markdown' => $attachment->markdownLink(),
103         ]);
104
105         if (!$attachment->external) {
106             $attachmentContents = $this->attachmentService->getAttachmentFromStorage($attachment);
107             $attachment->setAttribute('content', base64_encode($attachmentContents));
108         } else {
109             $attachment->setAttribute('content', $attachment->path);
110         }
111
112         return response()->json($attachment);
113     }
114
115     /**
116      * Update the details of a single attachment.
117      * As per the create endpoint, if a file is being provided as the attachment content
118      * the request should be formatted as a multipart/form-data request instead of JSON.
119      *
120      * @throws ValidationException
121      * @throws FileUploadException
122      */
123     public function update(Request $request, string $id)
124     {
125         $requestData = $this->validate($request, $this->rules['update']);
126         /** @var Attachment $attachment */
127         $attachment = Attachment::visible()->findOrFail($id);
128
129         $page = $attachment->page;
130         if ($requestData['uploaded_to'] ?? false) {
131             $pageId = $request->get('uploaded_to');
132             $page = Page::visible()->findOrFail($pageId);
133             $attachment->uploaded_to = $requestData['uploaded_to'];
134         }
135
136         $this->checkOwnablePermission('page-view', $page);
137         $this->checkOwnablePermission('page-update', $page);
138         $this->checkOwnablePermission('attachment-update', $attachment);
139
140         if ($request->hasFile('file')) {
141             $uploadedFile = $request->file('file');
142             $attachment = $this->attachmentService->saveUpdatedUpload($uploadedFile, $attachment);
143         }
144
145         $this->attachmentService->updateFile($attachment, $requestData);
146
147         return response()->json($attachment);
148     }
149
150     /**
151      * Delete an attachment of the given ID.
152      *
153      * @throws Exception
154      */
155     public function delete(string $id)
156     {
157         /** @var Attachment $attachment */
158         $attachment = Attachment::visible()->findOrFail($id);
159         $this->checkOwnablePermission('attachment-delete', $attachment);
160
161         $this->attachmentService->deleteFile($attachment);
162
163         return response('', 204);
164     }
165 }