PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 2.1.1
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v2.1.1
2.4.0 2.3.0 2.2.5 2.2.0 2.1.2 2.1.1 trunk 1.10.0 1.10.01 1.10.02 1.5.0 1.5.01 1.5.02 1.5.1 1.5.10 1.5.20 1.5.21 1.5.22 1.5.23 1.5.24 1.5.25 1.6.0 1.7.0 1.7.1 1.7.2 All 33 releases
fluent-booking / vendor / wpfluent / framework / src / WPFluent / Support / Media.php

Media.php in Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution 2.1.1, at vendor/wpfluent/framework/src/WPFluent/Support/Media.php

399 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBooking\Framework\Support;
4
5 use RuntimeException;
6 use InvalidArgumentException;
7
8 class Media
9 {
10 /**
11 * Turn off reading exif.
12 *
13 * @return $this
14 */
15 public function withoutExif()
16 {
17 add_filter('wp_read_image_metadata', '__return_empty_array');
18 return $this;
19 }
20
21 /**
22 * Turn off creating image sizes.
23 *
24 * @return $this
25 */
26 public function withoutSizes()
27 {
28 add_filter('intermediate_image_sizes_advanced', '__return_empty_array');
29 return $this;
30 }
31
32 /**
33 * Turn off reading exif and creating image sizes.
34 *
35 * @return $this
36 */
37 public function withoutExifAndSizes()
38 {
39 $this->withoutExif();
40 $this->withoutSizes();
41 return $this;
42 }
43
44 /**
45 * Sideload a local file array (e.g. from $_FILES) or a remote file URL.
46 *
47 * @param array|string $resource File array from $_FILES or remote file URL
48 * @param int|null $postId Optional post ID to attach the media to
49 * @param string|null $filename Optional file name for remote URL
50 *
51 * @return array
52 * @throws InvalidArgumentException
53 * @throws RuntimeException
54 */
55 public function upload($resource, $postId = 0, $filename = null)
56 {
57 [$data, $tmp] = $this->prepareMedia($resource, $filename);
58
59 $attachmentId = media_handle_sideload($data, $postId);
60
61 $this->cleanup($tmp, $attachmentId);
62
63 return [
64 'success' => true,
65 'id' => $attachmentId,
66 'url' => wp_get_attachment_url($attachmentId),
67 ];
68 }
69
70 /**
71 * Prepare the file array to use with media_handle_sideload.
72 *
73 * @param array|string $resource File array from $_FILES or remote file URL
74 * @param string|null $filename Optional file name for remote URL
75 *
76 * @return array
77 * @throws InvalidArgumentException
78 * @throws RuntimeException
79 */
80 protected function prepareMedia($resource, $filename = null)
81 {
82 $this->includeMediaFunctions();
83
84 if (is_array($resource)) {
85 if (empty($resource['tmp_name'])) {
86 throw new InvalidArgumentException(
87 'Invalid file array: tmp_name is required.'
88 );
89 }
90
91 return [[
92 'name' => $resource['name'] ?? 'file',
93 'tmp_name' => $resource['tmp_name'],
94 'type' => $resource['type'] ?? null,
95 'error' => $resource['error'] ?? 0,
96 'size' => $resource['size_in_bytes'] ?? $resource['size'] ?? null,
97 ], null];
98 }
99
100 if ($this->isBase64($resource)) {
101 $resource = $this->base64ToUrl($resource);
102 }
103
104 if (is_string($resource) && filter_var($resource, FILTER_VALIDATE_URL)) {
105 $tmp = download_url($resource);
106
107 if (is_wp_error($tmp)) {
108 throw new RuntimeException(
109 'Failed to download remote file: ' . $tmp->get_error_message()
110 );
111 }
112
113 return [[
114 'tmp_name' => $tmp,
115 'name' => $filename ?: basename(
116 parse_url($resource, PHP_URL_PATH)
117 ),
118 ], $tmp];
119 }
120
121 throw new InvalidArgumentException('Invalid file array or URL provided.');
122 }
123
124 /**
125 * Ensure required WordPress media functions are loaded.
126 *
127 * @return void
128 */
129 protected function includeMediaFunctions()
130 {
131 if (!function_exists('media_handle_sideload')) {
132 require_once ABSPATH . 'wp-admin/includes/image.php';
133 require_once ABSPATH . 'wp-admin/includes/file.php';
134 require_once ABSPATH . 'wp-admin/includes/media.php';
135 }
136 }
137
138 /**
139 * Checks if a string is base64 encoded.
140 *
141 * @param string $string
142 * @return boolean
143 */
144 protected function isBase64($string)
145 {
146 return is_string($string) && str_starts_with($string, 'data:image');
147 }
148
149 /**
150 * Saves a base64 encoded image to a file and returns the URL.
151 *
152 * @param string $imageData The base64 encoded image string.
153 * @param string $extension Optional. The image file extension.
154 * @return string The URL of the saved image on success.
155 * @throws RuntimeException If the image could not be saved.
156 */
157 public function base64ToUrl($imageData, $extension = 'png')
158 {
159 if (strpos($imageData, 'base64,') !== false) {
160 $imageData = explode('base64,', $imageData)[1];
161 }
162
163 $decoded = base64_decode($imageData);
164 if (!$decoded) {
165 throw new RuntimeException('Invalid base64 image data.');
166 }
167
168 $upload_dir = wp_upload_dir();
169
170 if (!wp_mkdir_p($upload_dir['path'])) {
171 throw new RuntimeException(
172 'Upload directory does not exist and could not be created.'
173 );
174 }
175
176 $filename = uniqid('wpfluent-') . '.' . $extension;
177 $file_path = trailingslashit($upload_dir['path']) . $filename;
178 $filUrl = trailingslashit($upload_dir['url']) . $filename;
179
180 if (file_put_contents($file_path, $decoded) === false) {
181 throw new RuntimeException('Failed to save image file.');
182 }
183
184 return $filUrl;
185 }
186
187 /**
188 * Delete the temporary file and throw an exception on failure.
189 *
190 * @param string $tmp
191 * @param int|\WP_Error $attachmentId
192 * @return void
193 */
194 protected function cleanup($tmp, $attachmentId)
195 {
196 if ($tmp && file_exists($tmp)) {
197 @unlink($tmp);
198 }
199
200 if (is_wp_error($attachmentId)) {
201 throw new RuntimeException(
202 'Failed to upload media: ' . $attachmentId->get_error_message()
203 );
204 }
205 }
206
207 /**
208 * Generate and save attachment metadata (image sizes, dimensions, etc.)
209 *
210 * @param int $attachmentId
211 * @return array
212 */
213 public function generateMetadata($attachmentId)
214 {
215 $this->includeMediaFunctions();
216
217 $metadata = wp_generate_attachment_metadata(
218 $attachmentId, get_attached_file($attachmentId)
219 );
220
221 wp_update_attachment_metadata(
222 $attachmentId, $metadata
223 );
224
225 return $metadata;
226 }
227
228 /**
229 * Update attachment title, caption, description, alt text, etc.
230 *
231 * @param int $attachmentId
232 * @param array $fields
233 * @return void
234 */
235 public function updateAttachmentFields($attachmentId, array $fields = [])
236 {
237 $defaults = [
238 'post_title' => '',
239 'post_content' => '',
240 'post_excerpt' => '',
241 'post_mime_type' => '',
242 ];
243
244 wp_update_post(array_merge([
245 'ID' => $attachmentId], array_intersect_key($fields, $defaults)
246 ));
247
248 if (!empty($fields['alt'])) {
249 update_post_meta(
250 $attachmentId, '_wp_attachment_image_alt',
251 sanitize_text_field($fields['alt'])
252 );
253 }
254 }
255
256 /**
257 * Get detailed info about an attachment.
258 *
259 * @param int $attachmentId
260 * @return array
261 */
262 public function getAttachmentArray($attachmentId)
263 {
264 return [
265 'id' => $attachmentId,
266 'url' => wp_get_attachment_url($attachmentId),
267 'path' => get_attached_file($attachmentId),
268 'type' => get_post_mime_type($attachmentId),
269 ];
270 }
271
272 /**
273 * Delete an attachment.
274 *
275 * @param int $attachmentId
276 * @param boolean $forceDelete
277 * @return \WP_Post|false|null Post data on success, false/null on failure.
278 */
279 public function delete($attachmentId, $forceDelete = false)
280 {
281 $this->includeMediaFunctions();
282
283 return wp_delete_attachment($attachmentId, $forceDelete);
284 }
285
286 /**
287 * Set an image as featured for a post.
288 *
289 * @param int $attachmentId
290 * @param int $postId
291 * @return int|bool
292 */
293 public function setAsFeaturedImage($attachmentId, $postId)
294 {
295 $this->includeMediaFunctions();
296
297 return set_post_thumbnail($postId, $attachmentId);
298 }
299
300 /**
301 * Find an attachment by its file name.
302 *
303 * @param string $name Filename to search for (can be partial).
304 * @return array|null Attachment data array or null if not found.
305 */
306 public function findByFilename($name)
307 {
308 global $wpdb;
309
310 $results = $wpdb->get_results($wpdb->prepare(
311 "SELECT ID FROM {$wpdb->posts}
312 WHERE post_type = 'attachment'
313 AND post_mime_type LIKE 'image/%'
314 AND guid LIKE %s
315 ORDER BY post_date DESC LIMIT 1",
316 '%' . $wpdb->esc_like($name) . '%'
317 ));
318
319 if (empty($results)) {
320 return null;
321 }
322
323 return $this->getAttachmentArray($results[0]->ID);
324 }
325
326 /**
327 * Get all media attachments associated with a specific post.
328 *
329 * @param int $postId
330 * @return array List of attachment data arrays.
331 */
332 public function findByPostId($postId)
333 {
334 $attachments = get_children([
335 'post_parent' => $postId,
336 'post_type' => 'attachment',
337 'post_status' => 'inherit',
338 'posts_per_page' => -1,
339 ]);
340
341 $results = [];
342
343 foreach ($attachments as $attachment) {
344 $results[] = $this->getAttachmentArray($attachment->ID);
345 }
346
347 return $results;
348 }
349
350 /**
351 * Find attachments by MIME type.
352 *
353 * @param string $mimeType e.g. 'image/jpeg', 'application/pdf', etc.
354 * @return array List of attachment data arrays.
355 */
356 public function findByMimeType($mimeType)
357 {
358 $attachments = get_posts([
359 'post_type' => 'attachment',
360 'post_status' => 'inherit',
361 'posts_per_page' => -1,
362 'post_mime_type' => $mimeType,
363 ]);
364
365 return array_map(
366 fn($attachment) => $this->getAttachmentArray($attachment->ID),
367 $attachments
368 );
369 }
370
371 /**
372 * Find attachments uploaded within a date range.
373 *
374 * @param string $startDate Format: 'YYYY-MM-DD'
375 * @param string $endDate Format: 'YYYY-MM-DD'
376 * @return array List of attachment data arrays.
377 */
378 public function findByDateRange($startDate, $endDate)
379 {
380 $attachments = get_posts([
381 'post_type' => 'attachment',
382 'post_status' => 'inherit',
383 'posts_per_page' => -1,
384 'date_query' => [
385 [
386 'after' => $startDate,
387 'before' => $endDate,
388 'inclusive' => true,
389 ]
390 ],
391 ]);
392
393 return array_map(
394 fn($attachment) => $this->getAttachmentArray($attachment->ID),
395 $attachments
396 );
397 }
398 }
399