| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-18 00:56:55 +08:00
										 |  |  | namespace BookStack\Uploads\Controllers; | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-08 05:58:27 +08:00
										 |  |  | use BookStack\Entities\Queries\PageQueries; | 
					
						
							| 
									
										
										
										
											2023-05-19 03:53:39 +08:00
										 |  |  | use BookStack\Http\ApiController; | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  | use BookStack\Uploads\Image; | 
					
						
							|  |  |  | use BookStack\Uploads\ImageRepo; | 
					
						
							| 
									
										
										
										
											2023-10-01 20:05:18 +08:00
										 |  |  | use BookStack\Uploads\ImageResizer; | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  | use Illuminate\Http\Request; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ImageGalleryApiController extends ApiController | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     protected array $fieldsToExpose = [ | 
					
						
							|  |  |  |         'id', 'name', 'url', 'path', 'type', 'uploaded_to', 'created_by', 'updated_by',  'created_at', 'updated_at', | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function __construct( | 
					
						
							| 
									
										
										
										
											2023-10-01 20:05:18 +08:00
										 |  |  |         protected ImageRepo $imageRepo, | 
					
						
							|  |  |  |         protected ImageResizer $imageResizer, | 
					
						
							| 
									
										
										
										
											2024-02-08 05:58:27 +08:00
										 |  |  |         protected PageQueries $pageQueries, | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  |     ) { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     protected function rules(): array | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return [ | 
					
						
							|  |  |  |             'create' => [ | 
					
						
							|  |  |  |                 'type'  => ['required', 'string', 'in:gallery,drawio'], | 
					
						
							| 
									
										
										
										
											2023-03-15 03:29:08 +08:00
										 |  |  |                 'uploaded_to' => ['required', 'integer'], | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  |                 'image' => ['required', 'file', ...$this->getImageValidationRules()], | 
					
						
							|  |  |  |                 'name'  => ['string', 'max:180'], | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |             'update' => [ | 
					
						
							|  |  |  |                 'name'  => ['string', 'max:180'], | 
					
						
							| 
									
										
										
										
											2023-05-29 22:06:17 +08:00
										 |  |  |                 'image' => ['file', ...$this->getImageValidationRules()], | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  |             ] | 
					
						
							|  |  |  |         ]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2023-03-15 19:37:03 +08:00
										 |  |  |      * Get a listing of images in the system. Includes gallery (page content) images and drawings. | 
					
						
							|  |  |  |      * Requires visibility of the page they're originally uploaded to. | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  |      */ | 
					
						
							|  |  |  |     public function list() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $images = Image::query()->scopes(['visible']) | 
					
						
							|  |  |  |             ->select($this->fieldsToExpose) | 
					
						
							|  |  |  |             ->whereIn('type', ['gallery', 'drawio']); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $this->apiListingResponse($images, [ | 
					
						
							|  |  |  |             ...$this->fieldsToExpose | 
					
						
							|  |  |  |         ]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Create a new image in the system. | 
					
						
							| 
									
										
										
										
											2023-06-21 06:44:39 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2023-03-15 19:37:03 +08:00
										 |  |  |      * Since "image" is expected to be a file, this needs to be a 'multipart/form-data' type request. | 
					
						
							|  |  |  |      * The provided "uploaded_to" should be an existing page ID in the system. | 
					
						
							| 
									
										
										
										
											2023-06-21 06:44:39 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2023-03-15 19:37:03 +08:00
										 |  |  |      * If the "name" parameter is omitted, the filename of the provided image file will be used instead. | 
					
						
							|  |  |  |      * The "type" parameter should be 'gallery' for page content images, and 'drawio' should only be used | 
					
						
							|  |  |  |      * when the file is a PNG file with diagrams.net image data embedded within. | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  |      */ | 
					
						
							|  |  |  |     public function create(Request $request) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-03-15 03:29:08 +08:00
										 |  |  |         $this->checkPermission('image-create-all'); | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  |         $data = $this->validate($request, $this->rules()['create']); | 
					
						
							| 
									
										
										
										
											2024-02-08 05:58:27 +08:00
										 |  |  |         $page = $this->pageQueries->findVisibleByIdOrFail($data['uploaded_to']); | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-08 05:58:27 +08:00
										 |  |  |         $image = $this->imageRepo->saveNew($data['image'], $data['type'], $page->id); | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-15 03:29:08 +08:00
										 |  |  |         if (isset($data['name'])) { | 
					
						
							|  |  |  |             $image->refresh(); | 
					
						
							|  |  |  |             $image->update(['name' => $data['name']]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  |         return response()->json($this->formatForSingleResponse($image)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * View the details of a single image. | 
					
						
							| 
									
										
										
										
											2023-03-15 19:37:03 +08:00
										 |  |  |      * The "thumbs" response property contains links to scaled variants that BookStack may use in its UI. | 
					
						
							|  |  |  |      * The "content" response property provides HTML and Markdown content, in the format that BookStack | 
					
						
							|  |  |  |      * would typically use by default to add the image in page content, as a convenience. | 
					
						
							|  |  |  |      * Actual image file data is not provided but can be fetched via the "url" response property. | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  |      */ | 
					
						
							|  |  |  |     public function read(string $id) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-03-15 03:29:08 +08:00
										 |  |  |         $image = Image::query()->scopes(['visible'])->findOrFail($id); | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return response()->json($this->formatForSingleResponse($image)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2023-03-15 19:37:03 +08:00
										 |  |  |      * Update the details of an existing image in the system. | 
					
						
							| 
									
										
										
										
											2023-05-29 22:06:17 +08:00
										 |  |  |      * Since "image" is expected to be a file, this needs to be a 'multipart/form-data' type request if providing a | 
					
						
							|  |  |  |      * new image file. Updated image files should be of the same file type as the original image. | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  |      */ | 
					
						
							|  |  |  |     public function update(Request $request, string $id) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $data = $this->validate($request, $this->rules()['update']); | 
					
						
							|  |  |  |         $image = $this->imageRepo->getById($id); | 
					
						
							|  |  |  |         $this->checkOwnablePermission('page-view', $image->getPage()); | 
					
						
							|  |  |  |         $this->checkOwnablePermission('image-update', $image); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->imageRepo->updateImageDetails($image, $data); | 
					
						
							| 
									
										
										
										
											2023-05-29 22:06:17 +08:00
										 |  |  |         if (isset($data['image'])) { | 
					
						
							|  |  |  |             $this->imageRepo->updateImageFile($image, $data['image']); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return response()->json($this->formatForSingleResponse($image)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Delete an image from the system. | 
					
						
							| 
									
										
										
										
											2023-03-15 19:37:03 +08:00
										 |  |  |      * Will also delete thumbnails for the image. | 
					
						
							|  |  |  |      * Does not check or handle image usage so this could leave pages with broken image references. | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  |      */ | 
					
						
							|  |  |  |     public function delete(string $id) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $image = $this->imageRepo->getById($id); | 
					
						
							|  |  |  |         $this->checkOwnablePermission('page-view', $image->getPage()); | 
					
						
							|  |  |  |         $this->checkOwnablePermission('image-delete', $image); | 
					
						
							|  |  |  |         $this->imageRepo->destroyImage($image); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return response('', 204); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Format the given image model for single-result display. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function formatForSingleResponse(Image $image): array | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-10-01 20:05:18 +08:00
										 |  |  |         $this->imageResizer->loadGalleryThumbnailsForImage($image, false); | 
					
						
							| 
									
										
										
										
											2023-06-10 17:52:39 +08:00
										 |  |  |         $data = $image->toArray(); | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  |         $data['created_by'] = $image->createdBy; | 
					
						
							|  |  |  |         $data['updated_by'] = $image->updatedBy; | 
					
						
							|  |  |  |         $data['content'] = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $escapedUrl = htmlentities($image->url); | 
					
						
							|  |  |  |         $escapedName = htmlentities($image->name); | 
					
						
							| 
									
										
										
										
											2023-10-01 20:05:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-14 20:19:19 +08:00
										 |  |  |         if ($image->type === 'drawio') { | 
					
						
							|  |  |  |             $data['content']['html'] = "<div drawio-diagram=\"{$image->id}\"><img src=\"{$escapedUrl}\"></div>"; | 
					
						
							|  |  |  |             $data['content']['markdown'] = $data['content']['html']; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             $escapedDisplayThumb = htmlentities($image->thumbs['display']); | 
					
						
							|  |  |  |             $data['content']['html'] = "<a href=\"{$escapedUrl}\" target=\"_blank\"><img src=\"{$escapedDisplayThumb}\" alt=\"{$escapedName}\"></a>"; | 
					
						
							|  |  |  |             $mdEscapedName = str_replace(']', '', str_replace('[', '', $image->name)); | 
					
						
							|  |  |  |             $mdEscapedThumb = str_replace(']', '', str_replace('[', '', $image->thumbs['display'])); | 
					
						
							|  |  |  |             $data['content']['markdown'] = ""; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $data; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |