| 
									
										
										
										
											2023-10-01 01:28:42 +08:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace BookStack\Uploads; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | use Illuminate\Filesystem\FilesystemManager; | 
					
						
							|  |  |  | use Illuminate\Support\Str; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ImageStorage | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     public function __construct( | 
					
						
							|  |  |  |         protected FilesystemManager $fileSystem, | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Get the storage disk for the given image type. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2023-10-01 02:12:22 +08:00
										 |  |  |     public function getDisk(string $imageType = ''): ImageStorageDisk | 
					
						
							| 
									
										
										
										
											2023-10-01 01:28:42 +08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-10-01 02:12:22 +08:00
										 |  |  |         $diskName = $this->getDiskName($imageType); | 
					
						
							| 
									
										
										
										
											2023-10-01 01:28:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-01 02:12:22 +08:00
										 |  |  |         return new ImageStorageDisk( | 
					
						
							|  |  |  |             $diskName, | 
					
						
							|  |  |  |             $this->fileSystem->disk($diskName), | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2023-10-01 01:28:42 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Check if "local secure restricted" (Fetched behind auth, with permissions enforced) | 
					
						
							|  |  |  |      * is currently active in the instance. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2023-10-01 02:12:22 +08:00
										 |  |  |     public function usingSecureRestrictedImages(): bool | 
					
						
							| 
									
										
										
										
											2023-10-01 01:28:42 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         return config('filesystems.images') === 'local_secure_restricted'; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Clean up an image file name to be both URL and storage safe. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function cleanImageFileName(string $name): string | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $name = str_replace(' ', '-', $name); | 
					
						
							|  |  |  |         $nameParts = explode('.', $name); | 
					
						
							|  |  |  |         $extension = array_pop($nameParts); | 
					
						
							|  |  |  |         $name = implode('-', $nameParts); | 
					
						
							|  |  |  |         $name = Str::slug($name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (strlen($name) === 0) { | 
					
						
							|  |  |  |             $name = Str::random(10); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $name . '.' . $extension; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Get the name of the storage disk to use. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function getDiskName(string $imageType): string | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-10-01 02:12:22 +08:00
										 |  |  |         $storageType = strtolower(config('filesystems.images')); | 
					
						
							| 
									
										
										
										
											2023-10-01 01:28:42 +08:00
										 |  |  |         $localSecureInUse = ($storageType === 'local_secure' || $storageType === 'local_secure_restricted'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Ensure system images (App logo) are uploaded to a public space
 | 
					
						
							|  |  |  |         if ($imageType === 'system' && $localSecureInUse) { | 
					
						
							|  |  |  |             return 'local'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Rename local_secure options to get our image specific storage driver which
 | 
					
						
							|  |  |  |         // is scoped to the relevant image directories.
 | 
					
						
							|  |  |  |         if ($localSecureInUse) { | 
					
						
							|  |  |  |             return 'local_secure_images'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $storageType; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Get a storage path for the given image URL. | 
					
						
							|  |  |  |      * Ensures the path will start with "uploads/images". | 
					
						
							|  |  |  |      * Returns null if the url cannot be resolved to a local URL. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function urlToPath(string $url): ?string | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $url = ltrim(trim($url), '/'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Handle potential relative paths
 | 
					
						
							|  |  |  |         $isRelative = !str_starts_with($url, 'http'); | 
					
						
							|  |  |  |         if ($isRelative) { | 
					
						
							|  |  |  |             if (str_starts_with(strtolower($url), 'uploads/images')) { | 
					
						
							|  |  |  |                 return trim($url, '/'); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return null; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Handle local images based on paths on the same domain
 | 
					
						
							|  |  |  |         $potentialHostPaths = [ | 
					
						
							|  |  |  |             url('uploads/images/'), | 
					
						
							|  |  |  |             $this->getPublicUrl('/uploads/images/'), | 
					
						
							|  |  |  |         ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         foreach ($potentialHostPaths as $potentialBasePath) { | 
					
						
							|  |  |  |             $potentialBasePath = strtolower($potentialBasePath); | 
					
						
							|  |  |  |             if (str_starts_with(strtolower($url), $potentialBasePath)) { | 
					
						
							|  |  |  |                 return 'uploads/images/' . trim(substr($url, strlen($potentialBasePath)), '/'); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Gets a public facing url for an image by checking relevant environment variables. | 
					
						
							|  |  |  |      * If s3-style store is in use it will default to guessing a public bucket URL. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function getPublicUrl(string $filePath): string | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $storageUrl = config('filesystems.url'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Get the standard public s3 url if s3 is set as storage type
 | 
					
						
							|  |  |  |         // Uses the nice, short URL if bucket name has no periods in otherwise the longer
 | 
					
						
							|  |  |  |         // region-based url will be used to prevent http issues.
 | 
					
						
							|  |  |  |         if (!$storageUrl && config('filesystems.images') === 's3') { | 
					
						
							|  |  |  |             $storageDetails = config('filesystems.disks.s3'); | 
					
						
							|  |  |  |             if (!str_contains($storageDetails['bucket'], '.')) { | 
					
						
							|  |  |  |                 $storageUrl = 'https://' . $storageDetails['bucket'] . '.s3.amazonaws.com'; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 $storageUrl = 'https://s3-' . $storageDetails['region'] . '.amazonaws.com/' . $storageDetails['bucket']; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $basePath = $storageUrl ?: url('/'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return rtrim($basePath, '/') . $filePath; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |