1 <?php namespace BookStack\Http\Controllers;
3 use BookStack\Exceptions\FileUploadException;
4 use BookStack\Attachment;
5 use BookStack\Repos\EntityRepo;
6 use BookStack\Repos\PageRepo;
7 use BookStack\Services\AttachmentService;
8 use Illuminate\Http\Request;
10 class AttachmentController extends Controller
12 protected $attachmentService;
13 protected $attachment;
15 protected $entityRepo;
18 * AttachmentController constructor.
19 * @param AttachmentService $attachmentService
20 * @param Attachment $attachment
21 * @param PageRepo $pageRepo
23 public function __construct(AttachmentService $attachmentService, Attachment $attachment, EntityRepo $entityRepo, PageRepo $pageRepo)
25 $this->attachmentService = $attachmentService;
26 $this->attachment = $attachment;
28 $this->pageRepo = $pageRepo;
29 $this->entityRepo = $entityRepo;
30 parent::__construct();
35 * Endpoint at which attachments are uploaded to.
36 * @param Request $request
37 * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\Response
39 public function upload(Request $request)
41 $this->validate($request, [
42 'uploaded_to' => 'required|integer|exists:pages,id',
43 'file' => 'required|file'
46 $pageId = $request->get('uploaded_to');
47 $page = $this->entityRepo->getById('page', $pageId, true);
49 $this->checkPermission('attachment-create-all');
50 $this->checkOwnablePermission('page-update', $page);
52 $uploadedFile = $request->file('file');
55 $attachment = $this->attachmentService->saveNewUpload($uploadedFile, $pageId);
56 } catch (FileUploadException $e) {
57 return response($e->getMessage(), 500);
60 return response()->json($attachment);
64 * Update an uploaded attachment.
65 * @param int $attachmentId
66 * @param Request $request
69 public function uploadUpdate($attachmentId, Request $request)
71 $this->validate($request, [
72 'uploaded_to' => 'required|integer|exists:pages,id',
73 'file' => 'required|file'
76 $pageId = $request->get('uploaded_to');
77 $page = $this->entityRepo->getById('page', $pageId, true);
78 $attachment = $this->attachment->findOrFail($attachmentId);
80 $this->checkOwnablePermission('page-update', $page);
81 $this->checkOwnablePermission('attachment-create', $attachment);
83 if (intval($pageId) !== intval($attachment->uploaded_to)) {
84 return $this->jsonError(trans('errors.attachment_page_mismatch'));
87 $uploadedFile = $request->file('file');
90 $attachment = $this->attachmentService->saveUpdatedUpload($uploadedFile, $attachment);
91 } catch (FileUploadException $e) {
92 return response($e->getMessage(), 500);
95 return response()->json($attachment);
99 * Update the details of an existing file.
100 * @param $attachmentId
101 * @param Request $request
102 * @return Attachment|mixed
104 public function update($attachmentId, Request $request)
106 $this->validate($request, [
107 'uploaded_to' => 'required|integer|exists:pages,id',
108 'name' => 'required|string|min:1|max:255',
109 'link' => 'url|min:1|max:255'
112 $pageId = $request->get('uploaded_to');
113 $page = $this->entityRepo->getById('page', $pageId, true);
114 $attachment = $this->attachment->findOrFail($attachmentId);
116 $this->checkOwnablePermission('page-update', $page);
117 $this->checkOwnablePermission('attachment-create', $attachment);
119 if (intval($pageId) !== intval($attachment->uploaded_to)) {
120 return $this->jsonError(trans('errors.attachment_page_mismatch'));
123 $attachment = $this->attachmentService->updateFile($attachment, $request->all());
124 return response()->json($attachment);
128 * Attach a link to a page.
129 * @param Request $request
132 public function attachLink(Request $request)
134 $this->validate($request, [
135 'uploaded_to' => 'required|integer|exists:pages,id',
136 'name' => 'required|string|min:1|max:255',
137 'link' => 'required|url|min:1|max:255'
140 $pageId = $request->get('uploaded_to');
141 $page = $this->entityRepo->getById('page', $pageId, true);
143 $this->checkPermission('attachment-create-all');
144 $this->checkOwnablePermission('page-update', $page);
146 $attachmentName = $request->get('name');
147 $link = $request->get('link');
148 $attachment = $this->attachmentService->saveNewFromLink($attachmentName, $link, $pageId);
150 return response()->json($attachment);
154 * Get the attachments for a specific page.
158 public function listForPage($pageId)
160 $page = $this->entityRepo->getById('page', $pageId, true);
161 $this->checkOwnablePermission('page-view', $page);
162 return response()->json($page->attachments);
166 * Update the attachment sorting.
168 * @param Request $request
171 public function sortForPage($pageId, Request $request)
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
190 public function get($attachmentId)
192 $attachment = $this->attachment->findOrFail($attachmentId);
193 $page = $this->entityRepo->getById('page', $attachment->uploaded_to);
194 $this->checkOwnablePermission('page-view', $page);
196 if ($attachment->external) {
197 return redirect($attachment->path);
200 $attachmentContents = $this->attachmentService->getAttachmentFromStorage($attachment);
201 return response($attachmentContents, 200, [
202 'Content-Type' => 'application/octet-stream',
203 'Content-Disposition' => 'attachment; filename="'. $attachment->getFileName() .'"'
208 * Delete a specific attachment in the system.
209 * @param $attachmentId
212 public function delete($attachmentId)
214 $attachment = $this->attachment->findOrFail($attachmentId);
215 $this->checkOwnablePermission('attachment-delete', $attachment);
216 $this->attachmentService->deleteFile($attachment);
217 return response()->json(['message' => trans('entities.attachments_deleted')]);