| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace BookStack\Uploads; | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | use BookStack\Exceptions\FileUploadException; | 
					
						
							|  |  |  | use Exception; | 
					
						
							|  |  |  | use Symfony\Component\HttpFoundation\File\UploadedFile; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-06 20:58:40 +08:00
										 |  |  | class AttachmentService | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-10-19 22:41:07 +08:00
										 |  |  |     public function __construct( | 
					
						
							| 
									
										
										
										
											2024-11-03 04:48:21 +08:00
										 |  |  |         protected FileStorage $storage, | 
					
						
							| 
									
										
										
										
											2024-10-19 22:41:07 +08:00
										 |  |  |     ) { | 
					
						
							| 
									
										
										
										
											2020-12-06 20:58:40 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-03 01:07:43 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Stream an attachment from storage. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2022-04-25 01:22:40 +08:00
										 |  |  |      * @return resource|null | 
					
						
							| 
									
										
										
										
											2022-04-03 01:07:43 +08:00
										 |  |  |      */ | 
					
						
							|  |  |  |     public function streamAttachmentFromStorage(Attachment $attachment) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2024-11-03 04:48:21 +08:00
										 |  |  |         return $this->storage->getReadStream($attachment->path); | 
					
						
							| 
									
										
										
										
											2022-04-03 01:07:43 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-07 22:03:13 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Read the file size of an attachment from storage, in bytes. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function getAttachmentFileSize(Attachment $attachment): int | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2024-11-03 04:48:21 +08:00
										 |  |  |         return $this->storage->getSize($attachment->path); | 
					
						
							| 
									
										
										
										
											2024-01-07 22:03:13 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Store a new attachment upon user upload. | 
					
						
							| 
									
										
										
										
											2021-10-09 05:23:17 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |      * @throws FileUploadException | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-10-19 00:46:55 +08:00
										 |  |  |     public function saveNewUpload(UploadedFile $uploadedFile, int $pageId): Attachment | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $attachmentName = $uploadedFile->getClientOriginalName(); | 
					
						
							| 
									
										
										
										
											2019-03-25 03:07:18 +08:00
										 |  |  |         $attachmentPath = $this->putFileInStorage($uploadedFile); | 
					
						
							| 
									
										
										
										
											2021-10-19 00:46:55 +08:00
										 |  |  |         $largestExistingOrder = Attachment::query()->where('uploaded_to', '=', $pageId)->max('order'); | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:47:14 +08:00
										 |  |  |         /** @var Attachment $attachment */ | 
					
						
							|  |  |  |         $attachment = Attachment::query()->forceCreate([ | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |             'name'        => $attachmentName, | 
					
						
							|  |  |  |             'path'        => $attachmentPath, | 
					
						
							|  |  |  |             'extension'   => $uploadedFile->getClientOriginalExtension(), | 
					
						
							| 
									
										
										
										
											2021-10-19 00:46:55 +08:00
										 |  |  |             'uploaded_to' => $pageId, | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |             'created_by'  => user()->id, | 
					
						
							|  |  |  |             'updated_by'  => user()->id, | 
					
						
							|  |  |  |             'order'       => $largestExistingOrder + 1, | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $attachment; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2021-10-09 00:47:14 +08:00
										 |  |  |      * Store an upload, saving to a file and deleting any existing uploads | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |      * attached to that file. | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |      * @throws FileUploadException | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-10-09 00:47:14 +08:00
										 |  |  |     public function saveUpdatedUpload(UploadedFile $uploadedFile, Attachment $attachment): Attachment | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         if (!$attachment->external) { | 
					
						
							|  |  |  |             $this->deleteFileInStorage($attachment); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $attachmentName = $uploadedFile->getClientOriginalName(); | 
					
						
							| 
									
										
										
										
											2019-03-25 03:07:18 +08:00
										 |  |  |         $attachmentPath = $this->putFileInStorage($uploadedFile); | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         $attachment->name = $attachmentName; | 
					
						
							|  |  |  |         $attachment->path = $attachmentPath; | 
					
						
							|  |  |  |         $attachment->external = false; | 
					
						
							|  |  |  |         $attachment->extension = $uploadedFile->getClientOriginalExtension(); | 
					
						
							|  |  |  |         $attachment->save(); | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |         return $attachment; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Save a new File attachment from a given link and name. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-10-31 23:01:52 +08:00
										 |  |  |     public function saveNewFromLink(string $name, string $link, int $page_id): Attachment | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $largestExistingOrder = Attachment::where('uploaded_to', '=', $page_id)->max('order'); | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |         return Attachment::forceCreate([ | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |             'name'        => $name, | 
					
						
							|  |  |  |             'path'        => $link, | 
					
						
							|  |  |  |             'external'    => true, | 
					
						
							|  |  |  |             'extension'   => '', | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |             'uploaded_to' => $page_id, | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |             'created_by'  => user()->id, | 
					
						
							|  |  |  |             'updated_by'  => user()->id, | 
					
						
							|  |  |  |             'order'       => $largestExistingOrder + 1, | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |         ]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2020-07-01 05:12:45 +08:00
										 |  |  |      * Updates the ordering for a listing of attached files. | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-07-01 05:12:45 +08:00
										 |  |  |     public function updateFileOrderWithinPage(array $attachmentOrder, string $pageId) | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-07-01 05:12:45 +08:00
										 |  |  |         foreach ($attachmentOrder as $index => $attachmentId) { | 
					
						
							|  |  |  |             Attachment::query()->where('uploaded_to', '=', $pageId) | 
					
						
							|  |  |  |                 ->where('id', '=', $attachmentId) | 
					
						
							|  |  |  |                 ->update(['order' => $index]); | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Update the details of a file. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-10-31 23:01:52 +08:00
										 |  |  |     public function updateFile(Attachment $attachment, array $requestData): Attachment | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2024-12-09 19:32:15 +08:00
										 |  |  |         if (isset($requestData['name'])) { | 
					
						
							|  |  |  |             $attachment->name = $requestData['name']; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-10-31 23:01:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-09 19:32:15 +08:00
										 |  |  |         $link = trim($requestData['link'] ?? ''); | 
					
						
							| 
									
										
										
										
											2021-10-19 00:46:55 +08:00
										 |  |  |         if (!empty($link)) { | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |             if (!$attachment->external) { | 
					
						
							|  |  |  |                 $this->deleteFileInStorage($attachment); | 
					
						
							|  |  |  |                 $attachment->external = true; | 
					
						
							| 
									
										
										
										
											2021-10-20 07:58:56 +08:00
										 |  |  |                 $attachment->extension = ''; | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2024-12-09 19:32:15 +08:00
										 |  |  |             $attachment->path = $link; | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-10-31 23:01:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |         $attachment->save(); | 
					
						
							| 
									
										
										
										
											2021-10-20 17:49:45 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-20 07:58:56 +08:00
										 |  |  |         return $attachment->refresh(); | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Delete a File from the database and storage. | 
					
						
							| 
									
										
										
										
											2021-10-09 05:23:17 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2018-01-13 19:11:23 +08:00
										 |  |  |      * @throws Exception | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |      */ | 
					
						
							|  |  |  |     public function deleteFile(Attachment $attachment) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2021-10-19 00:46:55 +08:00
										 |  |  |         if (!$attachment->external) { | 
					
						
							|  |  |  |             $this->deleteFileInStorage($attachment); | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |         $attachment->delete(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Delete a file from the filesystem it sits on. | 
					
						
							|  |  |  |      * Cleans any empty leftover folders. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2024-11-14 23:59:15 +08:00
										 |  |  |     public function deleteFileInStorage(Attachment $attachment): void | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2024-11-03 04:48:21 +08:00
										 |  |  |         $this->storage->delete($attachment->path); | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |      * Store a file in storage with the given filename. | 
					
						
							| 
									
										
										
										
											2021-10-09 05:23:17 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |      * @throws FileUploadException | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-10-09 00:47:14 +08:00
										 |  |  |     protected function putFileInStorage(UploadedFile $uploadedFile): string | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |         $basePath = 'uploads/files/' . date('Y-m-M') . '/'; | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-03 04:48:21 +08:00
										 |  |  |         return $this->storage->uploadFile( | 
					
						
							|  |  |  |             $uploadedFile, | 
					
						
							|  |  |  |             $basePath, | 
					
						
							|  |  |  |             $uploadedFile->getClientOriginalExtension(), | 
					
						
							|  |  |  |             '' | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-11-15 06:03:22 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Get the file validation rules for attachments. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2024-11-03 04:48:21 +08:00
										 |  |  |     public static function getFileValidationRules(): array | 
					
						
							| 
									
										
										
										
											2021-11-15 06:03:22 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         return ['file', 'max:' . (config('app.upload_limit') * 1000)]; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-29 00:58:52 +08:00
										 |  |  | } |