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