1 <?php namespace BookStack\Http\Controllers;
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;
10 class AttachmentController extends Controller
12 protected $attachmentService;
13 protected $attachment;
14 protected $entityRepo;
17 * AttachmentController constructor.
18 * @param \BookStack\Uploads\AttachmentService $attachmentService
19 * @param Attachment $attachment
20 * @param EntityRepo $entityRepo
22 public function __construct(AttachmentService $attachmentService, Attachment $attachment, EntityRepo $entityRepo)
24 $this->attachmentService = $attachmentService;
25 $this->attachment = $attachment;
26 $this->entityRepo = $entityRepo;
27 parent::__construct();
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
36 public function upload(Request $request)
38 $this->validate($request, [
39 'uploaded_to' => 'required|integer|exists:pages,id',
40 'file' => 'required|file'
43 $pageId = $request->get('uploaded_to');
44 $page = $this->entityRepo->getById('page', $pageId, true);
46 $this->checkPermission('attachment-create-all');
47 $this->checkOwnablePermission('page-update', $page);
49 $uploadedFile = $request->file('file');
52 $attachment = $this->attachmentService->saveNewUpload($uploadedFile, $pageId);
53 } catch (FileUploadException $e) {
54 return response($e->getMessage(), 500);
57 return response()->json($attachment);
61 * Update an uploaded attachment.
62 * @param int $attachmentId
63 * @param Request $request
66 public function uploadUpdate($attachmentId, Request $request)
68 $this->validate($request, [
69 'uploaded_to' => 'required|integer|exists:pages,id',
70 'file' => 'required|file'
73 $pageId = $request->get('uploaded_to');
74 $page = $this->entityRepo->getById('page', $pageId, true);
75 $attachment = $this->attachment->findOrFail($attachmentId);
77 $this->checkOwnablePermission('page-update', $page);
78 $this->checkOwnablePermission('attachment-create', $attachment);
80 if (intval($pageId) !== intval($attachment->uploaded_to)) {
81 return $this->jsonError(trans('errors.attachment_page_mismatch'));
84 $uploadedFile = $request->file('file');
87 $attachment = $this->attachmentService->saveUpdatedUpload($uploadedFile, $attachment);
88 } catch (FileUploadException $e) {
89 return response($e->getMessage(), 500);
92 return response()->json($attachment);
96 * Update the details of an existing file.
97 * @param $attachmentId
98 * @param Request $request
99 * @return Attachment|mixed
101 public function update($attachmentId, Request $request)
103 $this->validate($request, [
104 'uploaded_to' => 'required|integer|exists:pages,id',
105 'name' => 'required|string|min:1|max:255',
106 'link' => 'string|min:1|max:255'
109 $pageId = $request->get('uploaded_to');
110 $page = $this->entityRepo->getById('page', $pageId, true);
111 $attachment = $this->attachment->findOrFail($attachmentId);
113 $this->checkOwnablePermission('page-update', $page);
114 $this->checkOwnablePermission('attachment-create', $attachment);
116 if (intval($pageId) !== intval($attachment->uploaded_to)) {
117 return $this->jsonError(trans('errors.attachment_page_mismatch'));
120 $attachment = $this->attachmentService->updateFile($attachment, $request->all());
121 return response()->json($attachment);
125 * Attach a link to a page.
126 * @param Request $request
129 public function attachLink(Request $request)
131 $this->validate($request, [
132 'uploaded_to' => 'required|integer|exists:pages,id',
133 'name' => 'required|string|min:1|max:255',
134 'link' => 'required|string|min:1|max:255'
137 $pageId = $request->get('uploaded_to');
138 $page = $this->entityRepo->getById('page', $pageId, true);
140 $this->checkPermission('attachment-create-all');
141 $this->checkOwnablePermission('page-update', $page);
143 $attachmentName = $request->get('name');
144 $link = $request->get('link');
145 $attachment = $this->attachmentService->saveNewFromLink($attachmentName, $link, $pageId);
147 return response()->json($attachment);
151 * Get the attachments for a specific page.
155 public function listForPage($pageId)
157 $page = $this->entityRepo->getById('page', $pageId, true);
158 $this->checkOwnablePermission('page-view', $page);
159 return response()->json($page->attachments);
163 * Update the attachment sorting.
165 * @param Request $request
168 public function sortForPage($pageId, Request $request)
170 $this->validate($request, [
171 'files' => 'required|array',
172 'files.*.id' => 'required|integer',
174 $page = $this->entityRepo->getById('page', $pageId);
175 $this->checkOwnablePermission('page-update', $page);
177 $attachments = $request->get('files');
178 $this->attachmentService->updateFileOrderWithinPage($attachments, $pageId);
179 return response()->json(['message' => trans('entities.attachments_order_updated')]);
183 * Get an attachment from storage.
184 * @param $attachmentId
185 * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Symfony\Component\HttpFoundation\Response
186 * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
187 * @throws NotFoundException
189 public function get($attachmentId)
191 $attachment = $this->attachment->findOrFail($attachmentId);
192 $page = $this->entityRepo->getById('page', $attachment->uploaded_to);
193 if ($page === null) {
194 throw new NotFoundException(trans('errors.attachment_not_found'));
197 $this->checkOwnablePermission('page-view', $page);
199 if ($attachment->external) {
200 return redirect($attachment->path);
203 $attachmentContents = $this->attachmentService->getAttachmentFromStorage($attachment);
204 return $this->downloadResponse($attachmentContents, $attachment->getFileName());
208 * Delete a specific attachment in the system.
209 * @param $attachmentId
213 public function delete($attachmentId)
215 $attachment = $this->attachment->findOrFail($attachmentId);
216 $this->checkOwnablePermission('attachment-delete', $attachment);
217 $this->attachmentService->deleteFile($attachment);
218 return response()->json(['message' => trans('entities.attachments_deleted')]);