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 Request $request
63 * @param int $attachmentId
65 * @throws \Illuminate\Validation\ValidationException
67 public function uploadUpdate(Request $request, $attachmentId)
69 $this->validate($request, [
70 'uploaded_to' => 'required|integer|exists:pages,id',
71 'file' => 'required|file'
74 $pageId = $request->get('uploaded_to');
75 $page = $this->entityRepo->getById('page', $pageId, true);
76 $attachment = $this->attachment->findOrFail($attachmentId);
78 $this->checkOwnablePermission('page-update', $page);
79 $this->checkOwnablePermission('attachment-create', $attachment);
81 if (intval($pageId) !== intval($attachment->uploaded_to)) {
82 return $this->jsonError(trans('errors.attachment_page_mismatch'));
85 $uploadedFile = $request->file('file');
88 $attachment = $this->attachmentService->saveUpdatedUpload($uploadedFile, $attachment);
89 } catch (FileUploadException $e) {
90 return response($e->getMessage(), 500);
93 return response()->json($attachment);
97 * Update the details of an existing file.
98 * @param Request $request
99 * @param $attachmentId
100 * @return Attachment|mixed
101 * @throws \Illuminate\Validation\ValidationException
103 public function update(Request $request, $attachmentId)
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'
111 $pageId = $request->get('uploaded_to');
112 $page = $this->entityRepo->getById('page', $pageId, true);
113 $attachment = $this->attachment->findOrFail($attachmentId);
115 $this->checkOwnablePermission('page-update', $page);
116 $this->checkOwnablePermission('attachment-create', $attachment);
118 if (intval($pageId) !== intval($attachment->uploaded_to)) {
119 return $this->jsonError(trans('errors.attachment_page_mismatch'));
122 $attachment = $this->attachmentService->updateFile($attachment, $request->all());
123 return response()->json($attachment);
127 * Attach a link to a page.
128 * @param Request $request
131 public function attachLink(Request $request)
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'
139 $pageId = $request->get('uploaded_to');
140 $page = $this->entityRepo->getById('page', $pageId, true);
142 $this->checkPermission('attachment-create-all');
143 $this->checkOwnablePermission('page-update', $page);
145 $attachmentName = $request->get('name');
146 $link = $request->get('link');
147 $attachment = $this->attachmentService->saveNewFromLink($attachmentName, $link, $pageId);
149 return response()->json($attachment);
153 * Get the attachments for a specific page.
157 public function listForPage($pageId)
159 $page = $this->entityRepo->getById('page', $pageId, true);
160 $this->checkOwnablePermission('page-view', $page);
161 return response()->json($page->attachments);
165 * Update the attachment sorting.
166 * @param Request $request
169 * @throws \Illuminate\Validation\ValidationException
171 public function sortForPage(Request $request, $pageId)
173 $this->validate($request, [
174 'files' => 'required|array',
175 'files.*.id' => 'required|integer',
177 $page = $this->entityRepo->getById('page', $pageId);
178 $this->checkOwnablePermission('page-update', $page);
180 $attachments = $request->get('files');
181 $this->attachmentService->updateFileOrderWithinPage($attachments, $pageId);
182 return response()->json(['message' => trans('entities.attachments_order_updated')]);
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
192 public function get($attachmentId)
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'));
200 $this->checkOwnablePermission('page-view', $page);
202 if ($attachment->external) {
203 return redirect($attachment->path);
206 $attachmentContents = $this->attachmentService->getAttachmentFromStorage($attachment);
207 return $this->downloadResponse($attachmentContents, $attachment->getFileName());
211 * Delete a specific attachment in the system.
212 * @param $attachmentId
216 public function delete($attachmentId)
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')]);