+ // Strip thumbnail element from path if existing
+ $originalPathSplit = array_filter(explode('/', $path), function(string $part) {
+ $resizedDir = (strpos($part, 'thumbs-') === 0 || strpos($part, 'scaled-') === 0);
+ $missingExtension = strpos($part, '.') === false;
+ return !($resizedDir && $missingExtension);
+ });
+
+ // Build a database-format image path and search for the image entry
+ $fullPath = '/uploads/images/' . ltrim(implode('/', $originalPathSplit), '/');
+ $image = Image::query()->where('path', '=', $fullPath)->first();
+
+ if (is_null($image)) {
+ return false;
+ }
+
+ $imageType = $image->type;
+
+ // Allow user or system (logo) images
+ // (No specific relation control but may still have access controlled by auth)
+ if ($imageType === 'user' || $imageType === 'system') {
+ return true;
+ }
+
+ if ($imageType === 'gallery' || $imageType === 'drawio') {
+ return Page::visible()->where('id', '=', $image->uploaded_to)->exists();
+ }
+
+ if ($imageType === 'cover_book') {
+ return Book::visible()->where('id', '=', $image->uploaded_to)->exists();
+ }
+
+ if ($imageType === 'cover_bookshelf') {
+ return Bookshelf::visible()->where('id', '=', $image->uploaded_to)->exists();
+ }
+
+ return false;
+ }
+
+ /**
+ * For the given path, if existing, provide a response that will stream the image contents.
+ */
+ public function streamImageFromStorageResponse(string $imageType, string $path): StreamedResponse
+ {
+ $disk = $this->getStorageDisk($imageType);
+
+ return $disk->response($path);
+ }
+
+ /**
+ * Check if the given image extension is supported by BookStack.
+ * The extension must not be altered in this function. This check should provide a guarantee
+ * that the provided extension is safe to use for the image to be saved.
+ */
+ public static function isExtensionSupported(string $extension): bool
+ {
+ return in_array($extension, static::$supportedExtensions);
+ }
+
+ /**
+ * 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.
+ */
+ private function imageUrlToStoragePath(string $url): ?string
+ {
+ $url = ltrim(trim($url), '/');
+
+ // Handle potential relative paths
+ $isRelative = strpos($url, 'http') !== 0;
+ if ($isRelative) {
+ if (strpos(strtolower($url), 'uploads/images') === 0) {
+ return trim($url, '/');
+ }
+