]> BookStack Code Mirror - bookstack/blob - app/Uploads/ImageRepo.php
Apply fixes from StyleCI
[bookstack] / app / Uploads / ImageRepo.php
1 <?php
2
3 namespace BookStack\Uploads;
4
5 use BookStack\Auth\Permissions\PermissionService;
6 use BookStack\Entities\Models\Page;
7 use BookStack\Exceptions\ImageUploadException;
8 use Exception;
9 use Illuminate\Database\Eloquent\Builder;
10 use Symfony\Component\HttpFoundation\File\UploadedFile;
11
12 class ImageRepo
13 {
14     protected $image;
15     protected $imageService;
16     protected $restrictionService;
17     protected $page;
18
19     /**
20      * ImageRepo constructor.
21      */
22     public function __construct(
23         Image $image,
24         ImageService $imageService,
25         PermissionService $permissionService,
26         Page $page
27     ) {
28         $this->image = $image;
29         $this->imageService = $imageService;
30         $this->restrictionService = $permissionService;
31         $this->page = $page;
32     }
33
34     /**
35      * Get an image with the given id.
36      */
37     public function getById($id): Image
38     {
39         return $this->image->findOrFail($id);
40     }
41
42     /**
43      * Execute a paginated query, returning in a standard format.
44      * Also runs the query through the restriction system.
45      */
46     private function returnPaginated($query, $page = 1, $pageSize = 24): array
47     {
48         $images = $query->orderBy('created_at', 'desc')->skip($pageSize * ($page - 1))->take($pageSize + 1)->get();
49         $hasMore = count($images) > $pageSize;
50
51         $returnImages = $images->take($pageSize);
52         $returnImages->each(function ($image) {
53             $this->loadThumbs($image);
54         });
55
56         return [
57             'images'   => $returnImages,
58             'has_more' => $hasMore,
59         ];
60     }
61
62     /**
63      * Fetch a list of images in a paginated format, filtered by image type.
64      * Can be filtered by uploaded to and also by name.
65      */
66     public function getPaginatedByType(
67         string $type,
68         int $page = 0,
69         int $pageSize = 24,
70         int $uploadedTo = null,
71         string $search = null,
72         callable $whereClause = null
73     ): array {
74         $imageQuery = $this->image->newQuery()->where('type', '=', strtolower($type));
75
76         if ($uploadedTo !== null) {
77             $imageQuery = $imageQuery->where('uploaded_to', '=', $uploadedTo);
78         }
79
80         if ($search !== null) {
81             $imageQuery = $imageQuery->where('name', 'LIKE', '%' . $search . '%');
82         }
83
84         // Filter by page access
85         $imageQuery = $this->restrictionService->filterRelatedEntity(Page::class, $imageQuery, 'images', 'uploaded_to');
86
87         if ($whereClause !== null) {
88             $imageQuery = $imageQuery->where($whereClause);
89         }
90
91         return $this->returnPaginated($imageQuery, $page, $pageSize);
92     }
93
94     /**
95      * Get paginated gallery images within a specific page or book.
96      */
97     public function getEntityFiltered(
98         string $type,
99         string $filterType = null,
100         int $page = 0,
101         int $pageSize = 24,
102         int $uploadedTo = null,
103         string $search = null
104     ): array {
105         $contextPage = $this->page->findOrFail($uploadedTo);
106         $parentFilter = null;
107
108         if ($filterType === 'book' || $filterType === 'page') {
109             $parentFilter = function (Builder $query) use ($filterType, $contextPage) {
110                 if ($filterType === 'page') {
111                     $query->where('uploaded_to', '=', $contextPage->id);
112                 } elseif ($filterType === 'book') {
113                     $validPageIds = $contextPage->book->pages()->visible()->get(['id'])->pluck('id')->toArray();
114                     $query->whereIn('uploaded_to', $validPageIds);
115                 }
116             };
117         }
118
119         return $this->getPaginatedByType($type, $page, $pageSize, null, $search, $parentFilter);
120     }
121
122     /**
123      * Save a new image into storage and return the new image.
124      *
125      * @throws ImageUploadException
126      */
127     public function saveNew(UploadedFile $uploadFile, string $type, int $uploadedTo = 0, int $resizeWidth = null, int $resizeHeight = null, bool $keepRatio = true): Image
128     {
129         $image = $this->imageService->saveNewFromUpload($uploadFile, $type, $uploadedTo, $resizeWidth, $resizeHeight, $keepRatio);
130         $this->loadThumbs($image);
131
132         return $image;
133     }
134
135     /**
136      * Save a new image from an existing image data string.
137      *
138      * @throws ImageUploadException
139      */
140     public function saveNewFromData(string $imageName, string $imageData, string $type, int $uploadedTo = 0)
141     {
142         $image = $this->imageService->saveNew($imageName, $imageData, $type, $uploadedTo);
143         $this->loadThumbs($image);
144
145         return $image;
146     }
147
148     /**
149      * Save a drawing the the database.
150      *
151      * @throws ImageUploadException
152      */
153     public function saveDrawing(string $base64Uri, int $uploadedTo): Image
154     {
155         $name = 'Drawing-' . strval(user()->id) . '-' . strval(time()) . '.png';
156
157         return $this->imageService->saveNewFromBase64Uri($base64Uri, $name, 'drawio', $uploadedTo);
158     }
159
160     /**
161      * Update the details of an image via an array of properties.
162      *
163      * @throws ImageUploadException
164      * @throws Exception
165      */
166     public function updateImageDetails(Image $image, $updateDetails): Image
167     {
168         $image->fill($updateDetails);
169         $image->save();
170         $this->loadThumbs($image);
171
172         return $image;
173     }
174
175     /**
176      * Destroys an Image object along with its revisions, files and thumbnails.
177      *
178      * @throws Exception
179      */
180     public function destroyImage(Image $image = null): bool
181     {
182         if ($image) {
183             $this->imageService->destroy($image);
184         }
185
186         return true;
187     }
188
189     /**
190      * Destroy all images of a certain type.
191      *
192      * @throws Exception
193      */
194     public function destroyByType(string $imageType)
195     {
196         $images = $this->image->where('type', '=', $imageType)->get();
197         foreach ($images as $image) {
198             $this->destroyImage($image);
199         }
200     }
201
202     /**
203      * Load thumbnails onto an image object.
204      *
205      * @throws Exception
206      */
207     public function loadThumbs(Image $image)
208     {
209         $image->thumbs = [
210             'gallery' => $this->getThumbnail($image, 150, 150, false),
211             'display' => $this->getThumbnail($image, 1680, null, true),
212         ];
213     }
214
215     /**
216      * Get the thumbnail for an image.
217      * If $keepRatio is true only the width will be used.
218      * Checks the cache then storage to avoid creating / accessing the filesystem on every check.
219      *
220      * @throws Exception
221      */
222     protected function getThumbnail(Image $image, ?int $width = 220, ?int $height = 220, bool $keepRatio = false): ?string
223     {
224         try {
225             return $this->imageService->getThumbnail($image, $width, $height, $keepRatio);
226         } catch (Exception $exception) {
227             return null;
228         }
229     }
230
231     /**
232      * Get the raw image data from an Image.
233      */
234     public function getImageData(Image $image): ?string
235     {
236         try {
237             return $this->imageService->getImageData($image);
238         } catch (Exception $exception) {
239             return null;
240         }
241     }
242
243     /**
244      * Get the user visible pages using the given image.
245      */
246     public function getPagesUsingImage(Image $image): array
247     {
248         $pages = Page::visible()
249             ->where('html', 'like', '%' . $image->url . '%')
250             ->get(['id', 'name', 'slug', 'book_id']);
251
252         foreach ($pages as $page) {
253             $page->url = $page->getUrl();
254         }
255
256         return $pages->all();
257     }
258 }