]> BookStack Code Mirror - bookstack/blob - app/Http/Controllers/AttachmentController.php
b6ce261d481aa6d8aad0bbdd5bb48d81676c3a37
[bookstack] / app / Http / Controllers / AttachmentController.php
1 <?php
2
3 namespace BookStack\Http\Controllers;
4
5 use BookStack\Entities\Repos\PageRepo;
6 use BookStack\Exceptions\FileUploadException;
7 use BookStack\Exceptions\NotFoundException;
8 use BookStack\Uploads\Attachment;
9 use BookStack\Uploads\AttachmentService;
10 use Exception;
11 use Illuminate\Contracts\Filesystem\FileNotFoundException;
12 use Illuminate\Http\Request;
13 use Illuminate\Support\MessageBag;
14 use Illuminate\Validation\ValidationException;
15
16 class AttachmentController extends Controller
17 {
18     public function __construct(
19         protected AttachmentService $attachmentService,
20         protected PageRepo $pageRepo
21     ) {
22     }
23
24     /**
25      * Endpoint at which attachments are uploaded to.
26      *
27      * @throws ValidationException
28      * @throws NotFoundException
29      */
30     public function upload(Request $request)
31     {
32         $this->validate($request, [
33             'uploaded_to' => ['required', 'integer', 'exists:pages,id'],
34             'file'        => array_merge(['required'], $this->attachmentService->getFileValidationRules()),
35         ]);
36
37         $pageId = $request->get('uploaded_to');
38         $page = $this->pageRepo->getById($pageId);
39
40         $this->checkPermission('attachment-create-all');
41         $this->checkOwnablePermission('page-update', $page);
42
43         $uploadedFile = $request->file('file');
44
45         try {
46             $attachment = $this->attachmentService->saveNewUpload($uploadedFile, $pageId);
47         } catch (FileUploadException $e) {
48             return response($e->getMessage(), 500);
49         }
50
51         return response()->json($attachment);
52     }
53
54     /**
55      * Update an uploaded attachment.
56      *
57      * @throws ValidationException
58      */
59     public function uploadUpdate(Request $request, $attachmentId)
60     {
61         $this->validate($request, [
62             'file' => array_merge(['required'], $this->attachmentService->getFileValidationRules()),
63         ]);
64
65         /** @var Attachment $attachment */
66         $attachment = Attachment::query()->findOrFail($attachmentId);
67         $this->checkOwnablePermission('view', $attachment->page);
68         $this->checkOwnablePermission('page-update', $attachment->page);
69         $this->checkOwnablePermission('attachment-create', $attachment);
70
71         $uploadedFile = $request->file('file');
72
73         try {
74             $attachment = $this->attachmentService->saveUpdatedUpload($uploadedFile, $attachment);
75         } catch (FileUploadException $e) {
76             return response($e->getMessage(), 500);
77         }
78
79         return response()->json($attachment);
80     }
81
82     /**
83      * Get the update form for an attachment.
84      */
85     public function getUpdateForm(string $attachmentId)
86     {
87         /** @var Attachment $attachment */
88         $attachment = Attachment::query()->findOrFail($attachmentId);
89
90         $this->checkOwnablePermission('page-update', $attachment->page);
91         $this->checkOwnablePermission('attachment-create', $attachment);
92
93         return view('attachments.manager-edit-form', [
94             'attachment' => $attachment,
95         ]);
96     }
97
98     /**
99      * Update the details of an existing file.
100      */
101     public function update(Request $request, string $attachmentId)
102     {
103         /** @var Attachment $attachment */
104         $attachment = Attachment::query()->findOrFail($attachmentId);
105
106         try {
107             $this->validate($request, [
108                 'attachment_edit_name' => ['required', 'string', 'min:1', 'max:255'],
109                 'attachment_edit_url'  => ['string', 'min:1', 'max:2000', 'safe_url'],
110             ]);
111         } catch (ValidationException $exception) {
112             return response()->view('attachments.manager-edit-form', array_merge($request->only(['attachment_edit_name', 'attachment_edit_url']), [
113                 'attachment' => $attachment,
114                 'errors'     => new MessageBag($exception->errors()),
115             ]), 422);
116         }
117
118         $this->checkOwnablePermission('page-view', $attachment->page);
119         $this->checkOwnablePermission('page-update', $attachment->page);
120         $this->checkOwnablePermission('attachment-update', $attachment);
121
122         $attachment = $this->attachmentService->updateFile($attachment, [
123             'name' => $request->get('attachment_edit_name'),
124             'link' => $request->get('attachment_edit_url'),
125         ]);
126
127         return view('attachments.manager-edit-form', [
128             'attachment' => $attachment,
129         ]);
130     }
131
132     /**
133      * Attach a link to a page.
134      *
135      * @throws NotFoundException
136      */
137     public function attachLink(Request $request)
138     {
139         $pageId = $request->get('attachment_link_uploaded_to');
140
141         try {
142             $this->validate($request, [
143                 'attachment_link_uploaded_to' => ['required', 'integer', 'exists:pages,id'],
144                 'attachment_link_name'        => ['required', 'string', 'min:1', 'max:255'],
145                 'attachment_link_url'         => ['required', 'string', 'min:1', 'max:2000', 'safe_url'],
146             ]);
147         } catch (ValidationException $exception) {
148             return response()->view('attachments.manager-link-form', array_merge($request->only(['attachment_link_name', 'attachment_link_url']), [
149                 'pageId' => $pageId,
150                 'errors' => new MessageBag($exception->errors()),
151             ]), 422);
152         }
153
154         $page = $this->pageRepo->getById($pageId);
155
156         $this->checkPermission('attachment-create-all');
157         $this->checkOwnablePermission('page-update', $page);
158
159         $attachmentName = $request->get('attachment_link_name');
160         $link = $request->get('attachment_link_url');
161         $this->attachmentService->saveNewFromLink($attachmentName, $link, intval($pageId));
162
163         return view('attachments.manager-link-form', [
164             'pageId' => $pageId,
165         ]);
166     }
167
168     /**
169      * Get the attachments for a specific page.
170      *
171      * @throws NotFoundException
172      */
173     public function listForPage(int $pageId)
174     {
175         $page = $this->pageRepo->getById($pageId);
176         $this->checkOwnablePermission('page-view', $page);
177
178         return view('attachments.manager-list', [
179             'attachments' => $page->attachments->all(),
180         ]);
181     }
182
183     /**
184      * Update the attachment sorting.
185      *
186      * @throws ValidationException
187      * @throws NotFoundException
188      */
189     public function sortForPage(Request $request, int $pageId)
190     {
191         $this->validate($request, [
192             'order' => ['required', 'array'],
193         ]);
194         $page = $this->pageRepo->getById($pageId);
195         $this->checkOwnablePermission('page-update', $page);
196
197         $attachmentOrder = $request->get('order');
198         $this->attachmentService->updateFileOrderWithinPage($attachmentOrder, $pageId);
199
200         return response()->json(['message' => trans('entities.attachments_order_updated')]);
201     }
202
203     /**
204      * Get an attachment from storage.
205      *
206      * @throws FileNotFoundException
207      * @throws NotFoundException
208      */
209     public function get(Request $request, string $attachmentId)
210     {
211         /** @var Attachment $attachment */
212         $attachment = Attachment::query()->findOrFail($attachmentId);
213
214         try {
215             $page = $this->pageRepo->getById($attachment->uploaded_to);
216         } catch (NotFoundException $exception) {
217             throw new NotFoundException(trans('errors.attachment_not_found'));
218         }
219
220         $this->checkOwnablePermission('page-view', $page);
221
222         if ($attachment->external) {
223             return redirect($attachment->path);
224         }
225
226         $fileName = $attachment->getFileName();
227         $attachmentStream = $this->attachmentService->streamAttachmentFromStorage($attachment);
228
229         if ($request->get('open') === 'true') {
230             return $this->download()->streamedInline($attachmentStream, $fileName);
231         }
232
233         return $this->download()->streamedDirectly($attachmentStream, $fileName);
234     }
235
236     /**
237      * Delete a specific attachment in the system.
238      *
239      * @throws Exception
240      */
241     public function delete(string $attachmentId)
242     {
243         /** @var Attachment $attachment */
244         $attachment = Attachment::query()->findOrFail($attachmentId);
245         $this->checkOwnablePermission('attachment-delete', $attachment);
246         $this->attachmentService->deleteFile($attachment);
247
248         return response()->json(['message' => trans('entities.attachments_deleted')]);
249     }
250 }