| 
									
										
										
										
											2018-09-25 19:30:50 +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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class AttachmentService extends UploadService | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-13 19:11:23 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Get the storage that will be used for storing files. | 
					
						
							|  |  |  |      * @return \Illuminate\Contracts\Filesystem\Filesystem | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function getStorage() | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-06-23 23:01:15 +08:00
										 |  |  |         $storageType = config('filesystems.attachments'); | 
					
						
							| 
									
										
										
										
											2018-01-13 19:11:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // Override default location if set to local public to ensure not visible.
 | 
					
						
							|  |  |  |         if ($storageType === 'local') { | 
					
						
							|  |  |  |             $storageType = 'local_secure'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 19:41:52 +08:00
										 |  |  |         return $this->fileSystem->disk($storageType); | 
					
						
							| 
									
										
										
										
											2018-01-13 19:11:23 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Get an attachment from storage. | 
					
						
							|  |  |  |      * @param Attachment $attachment | 
					
						
							|  |  |  |      * @return string | 
					
						
							| 
									
										
										
										
											2018-01-13 19:11:23 +08:00
										 |  |  |      * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |      */ | 
					
						
							|  |  |  |     public function getAttachmentFromStorage(Attachment $attachment) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2018-01-13 19:11:23 +08:00
										 |  |  |         return $this->getStorage()->get($attachment->path); | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Store a new attachment upon user upload. | 
					
						
							|  |  |  |      * @param UploadedFile $uploadedFile | 
					
						
							|  |  |  |      * @param int $page_id | 
					
						
							|  |  |  |      * @return Attachment | 
					
						
							|  |  |  |      * @throws FileUploadException | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function saveNewUpload(UploadedFile $uploadedFile, $page_id) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $attachmentName = $uploadedFile->getClientOriginalName(); | 
					
						
							| 
									
										
										
										
											2019-03-25 03:07:18 +08:00
										 |  |  |         $attachmentPath = $this->putFileInStorage($uploadedFile); | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |         $largestExistingOrder = Attachment::where('uploaded_to', '=', $page_id)->max('order'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $attachment = Attachment::forceCreate([ | 
					
						
							|  |  |  |             'name' => $attachmentName, | 
					
						
							|  |  |  |             'path' => $attachmentPath, | 
					
						
							|  |  |  |             'extension' => $uploadedFile->getClientOriginalExtension(), | 
					
						
							|  |  |  |             'uploaded_to' => $page_id, | 
					
						
							|  |  |  |             'created_by' => user()->id, | 
					
						
							|  |  |  |             'updated_by' => user()->id, | 
					
						
							|  |  |  |             'order' => $largestExistingOrder + 1 | 
					
						
							|  |  |  |         ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $attachment; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Store a upload, saving to a file and deleting any existing uploads | 
					
						
							|  |  |  |      * attached to that file. | 
					
						
							|  |  |  |      * @param UploadedFile $uploadedFile | 
					
						
							|  |  |  |      * @param Attachment $attachment | 
					
						
							|  |  |  |      * @return Attachment | 
					
						
							|  |  |  |      * @throws FileUploadException | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function saveUpdatedUpload(UploadedFile $uploadedFile, Attachment $attachment) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         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(); | 
					
						
							|  |  |  |         return $attachment; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Save a new File attachment from a given link and name. | 
					
						
							|  |  |  |      * @param string $name | 
					
						
							|  |  |  |      * @param string $link | 
					
						
							|  |  |  |      * @param int $page_id | 
					
						
							|  |  |  |      * @return Attachment | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function saveNewFromLink($name, $link, $page_id) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $largestExistingOrder = Attachment::where('uploaded_to', '=', $page_id)->max('order'); | 
					
						
							|  |  |  |         return Attachment::forceCreate([ | 
					
						
							|  |  |  |             'name' => $name, | 
					
						
							|  |  |  |             'path' => $link, | 
					
						
							|  |  |  |             'external' => true, | 
					
						
							|  |  |  |             'extension' => '', | 
					
						
							|  |  |  |             'uploaded_to' => $page_id, | 
					
						
							|  |  |  |             'created_by' => user()->id, | 
					
						
							|  |  |  |             'updated_by' => user()->id, | 
					
						
							|  |  |  |             'order' => $largestExistingOrder + 1 | 
					
						
							|  |  |  |         ]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Updates the file ordering for a listing of attached files. | 
					
						
							|  |  |  |      * @param array $attachmentList | 
					
						
							|  |  |  |      * @param $pageId | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function updateFileOrderWithinPage($attachmentList, $pageId) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         foreach ($attachmentList as $index => $attachment) { | 
					
						
							|  |  |  |             Attachment::where('uploaded_to', '=', $pageId)->where('id', '=', $attachment['id'])->update(['order' => $index]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Update the details of a file. | 
					
						
							|  |  |  |      * @param Attachment $attachment | 
					
						
							|  |  |  |      * @param $requestData | 
					
						
							|  |  |  |      * @return Attachment | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function updateFile(Attachment $attachment, $requestData) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $attachment->name = $requestData['name']; | 
					
						
							|  |  |  |         if (isset($requestData['link']) && trim($requestData['link']) !== '') { | 
					
						
							|  |  |  |             $attachment->path = $requestData['link']; | 
					
						
							|  |  |  |             if (!$attachment->external) { | 
					
						
							|  |  |  |                 $this->deleteFileInStorage($attachment); | 
					
						
							|  |  |  |                 $attachment->external = true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $attachment->save(); | 
					
						
							|  |  |  |         return $attachment; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Delete a File from the database and storage. | 
					
						
							|  |  |  |      * @param Attachment $attachment | 
					
						
							| 
									
										
										
										
											2018-01-13 19:11:23 +08:00
										 |  |  |      * @throws Exception | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |      */ | 
					
						
							|  |  |  |     public function deleteFile(Attachment $attachment) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if ($attachment->external) { | 
					
						
							|  |  |  |             $attachment->delete(); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         $this->deleteFileInStorage($attachment); | 
					
						
							|  |  |  |         $attachment->delete(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Delete a file from the filesystem it sits on. | 
					
						
							|  |  |  |      * Cleans any empty leftover folders. | 
					
						
							|  |  |  |      * @param Attachment $attachment | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function deleteFileInStorage(Attachment $attachment) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $storage = $this->getStorage(); | 
					
						
							| 
									
										
										
										
											2018-01-13 19:11:23 +08:00
										 |  |  |         $dirPath = dirname($attachment->path); | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-13 19:11:23 +08:00
										 |  |  |         $storage->delete($attachment->path); | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |         if (count($storage->allFiles($dirPath)) === 0) { | 
					
						
							|  |  |  |             $storage->deleteDirectory($dirPath); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Store a file in storage with the given filename | 
					
						
							|  |  |  |      * @param UploadedFile $uploadedFile | 
					
						
							|  |  |  |      * @return string | 
					
						
							|  |  |  |      * @throws FileUploadException | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-03-25 03:07:18 +08:00
										 |  |  |     protected function putFileInStorage(UploadedFile $uploadedFile) | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $attachmentData = file_get_contents($uploadedFile->getRealPath()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $storage = $this->getStorage(); | 
					
						
							| 
									
										
										
										
											2018-01-13 19:11:23 +08:00
										 |  |  |         $basePath = 'uploads/files/' . Date('Y-m-M') . '/'; | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-25 03:07:18 +08:00
										 |  |  |         $uploadFileName = str_random(16) . '.' . $uploadedFile->getClientOriginalExtension(); | 
					
						
							| 
									
										
										
										
											2018-01-13 19:11:23 +08:00
										 |  |  |         while ($storage->exists($basePath . $uploadFileName)) { | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |             $uploadFileName = str_random(3) . $uploadFileName; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-13 19:11:23 +08:00
										 |  |  |         $attachmentPath = $basePath . $uploadFileName; | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |         try { | 
					
						
							| 
									
										
										
										
											2018-01-13 19:11:23 +08:00
										 |  |  |             $storage->put($attachmentPath, $attachmentData); | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |         } catch (Exception $e) { | 
					
						
							| 
									
										
										
										
											2018-01-13 19:11:23 +08:00
										 |  |  |             throw new FileUploadException(trans('errors.path_not_writable', ['filePath' => $attachmentPath])); | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-01-13 19:11:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-12 22:12:26 +08:00
										 |  |  |         return $attachmentPath; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-29 00:58:52 +08:00
										 |  |  | } |